},
'rules': {
// See http://eslint.org/docs/rules/ for all rules and explanations of all
- // rules. Commented out rules with 'DEFINE POLICY' are rules Dan P has flagged
- // for discussion and possible enable soon.
+ // rules.
+
// === Possible Errors ===
- // DEFINE POLICY: 'comma-dangle': ['off', 'always'],
+ 'comma-dangle': 'off',
'no-cond-assign': 'error',
'no-console': 'error',
'no-constant-condition': 'error',
'no-dupe-args': 'error',
'no-dupe-keys': 'error',
'no-duplicate-case': 'error',
- // Disabled for YUI rollups, enabled by grunt for AMD: 'no-empty': 'error',
+ 'no-empty': 'warn',
'no-empty-character-class': 'error',
'no-ex-assign': 'error',
'no-extra-boolean-cast': 'error',
// === Best Practices ===
// (these mostly match our jshint config)
+ 'array-callback-return': 'warn',
+ 'block-scoped-var': 'warn',
+ 'complexity': 'warn',
+ 'consistent-return': 'warn',
'curly': 'error',
'dot-notation': 'warn',
'no-alert': 'warn',
'no-caller': 'error',
'no-case-declarations': 'error',
+ 'no-div-regex': 'error',
'no-empty-pattern': 'error',
'no-empty-function': 'warn',
- //DEFINE POLICY: 'no-eq-null': 'warn',
+ 'no-eq-null': 'error',
'no-eval': 'error',
- //DEFINE POLICY: 'no-extra-bind': 'warn',
+ 'no-extend-native': 'error',
+ 'no-extra-bind': 'warn',
'no-fallthrough': 'error',
- //DEFINE POLICY: 'no-implicit-globals': 'warn',
+ 'no-floating-decimal': 'warn',
+ // Enabled by grunt for AMD modules: 'no-implicit-globals': 'error',
'no-implied-eval': 'error',
'no-invalid-this': 'error',
'no-iterator': 'error',
'no-native-reassign': 'warn',
'no-new-func': 'error',
'no-new-wrappers': 'error',
- // DEFINE POLICY: no-octal: "error"
- // DEFINE POLICY: no-octal-escape: "error"
+ 'no-octal': 'error',
+ 'no-octal-escape': 'error',
'no-proto': 'error',
'no-redeclare': 'warn',
'no-return-assign': 'error',
'no-script-url': 'error',
'no-self-assign': 'error',
'no-self-compare': 'error',
+ 'no-sequences': 'warn',
+ 'no-throw-literal': 'warn',
'no-unmodified-loop-condition': 'error',
- // Disabled for YUI rollups, enabled by grunt for AMD: 'no-unused-expressions': 'error',
+ 'no-unused-expressions': 'error',
'no-unused-labels': 'error',
- //DEFINE POLICY: 'no-useless-call': 'error',
+ 'no-useless-call': 'warn',
'no-useless-escape': 'warn',
- //DEFINE POLICY: 'no-with': 'error',
+ 'no-with': 'error',
'wrap-iife': ['error', 'any'],
// === Variables ===
'no-delete-var': 'error',
- // Disabled for YUI rollups, enabled by grunt for AMD: 'no-undef': 'off',
- //DEFINE POLICY: 'no-undef-init': 'error',
- // Disabled for YUI rollups, enabled by grunt for AMD: 'no-unused-vars': 'error',
+ 'no-undef': 'error',
+ 'no-undef-init': 'error',
+ 'no-unused-vars': ['error', { 'caughtErrors': 'none', 'argsIgnorePattern': "(e|event)" }],
// === Stylistic Issues ===
'array-bracket-spacing': 'warn',
'lines-around-comment': 'off',
'max-len': ['error', 132],
'max-lines': 'off',
- // DEFINE POLICY: turn on some of these max values?
- 'max-depth': 'off',
- 'max-nested-callbacks': 'off',
+ 'max-depth': 'warn',
+ 'max-nested-callbacks': ['warn', 5],
'max-params': 'off',
'max-statements': 'off',
- 'max-statements-per-line': 'off',
+ 'max-statements-per-line': ['warn', { max: 2 }],
'new-cap': ['warn', { 'properties': false }],
'new-parens': 'warn',
'newline-after-var': 'off',
'newline-before-return': 'off',
- // REVIST POLICY: 'newline-per-chained-call': 'warn',
+ 'newline-per-chained-call': 'off',
'no-array-constructor': 'off',
'no-bitwise': 'error',
'no-continue': 'off',
'no-ternary': 'off',
'no-trailing-spaces': 'error',
'no-underscore-dangle': 'off',
- // DEFINE POLICY: 'no-unneeded-ternary': 'off',
+ 'no-unneeded-ternary': 'off',
'no-whitespace-before-property': 'warn',
- // DEFINE POLICY: 'object-curly-newline': 'off,
- // DEFINE POLICY: 'object-curly-spacing': 'off',
- // DEFINE POLICY: 'object-property-newline': 'off',
+ 'object-curly-newline': 'off',
+ 'object-curly-spacing': 'warn',
+ 'object-property-newline': 'off',
'one-var': 'off',
- // DEFINE POLICY: 'one-var-declaration-per-line': 'off',
+ 'one-var-declaration-per-line': ['warn', 'initializations'],
'operator-assignment': 'off',
'operator-linebreak': 'off',
'padded-blocks': 'off',
- // DEFINE POLICY: 'quote-props': 'off',
+ 'quote-props': ['warn', 'as-needed', {'unnecessary': false, 'keywords': true, 'numbers': true}],
'quotes': 'off',
'require-jsdoc': 'warn',
'semi': 'error',
+// NOTE: We use eslint now. This file is used only by shifter. We keep the configuration
+// here because shifter uses jshint after modules have been concating. Eslint can't
+// currently do this.
{
"asi": false,
"bitwise": true,
php:
# We only run the highest and lowest supported versions to reduce the load on travis-ci.org.
- 7.0
- # - 5.6
- # - 5.5
- - 5.4
+ - 5.6
env:
# Although we want to run these jobs and see failures as quickly as possible, we also want to get the slowest job to
# start first so that the total run time is not too high.
#
- # We only run MySQL on PHP 5.6, so run that first.
+ # We only run MySQL on PHP 7.0, so run that first.
# CI Tests should be second-highest in priority as these only take <= 60 seconds to run under normal circumstances.
# Postgres is significantly is pretty reasonable in its run-time.
# Perform an upgrade test too.
- DB=pgsql TASK=UPGRADE
- # Run a check for unbuilt files with Grunt.
- - DB=none TASK=GRUNT
-
matrix:
# Enable fast finish.
# This will fail the build if a single job fails (except those in allow_failures).
# It will not stop the jobs from running.
fast_finish: true
+ include:
+ # Run grunt/npm install on lowest supported npm version
+ - php: 7
+ env: DB=none TASK=GRUNT NVM_VERSION='4'
+ # Run grunt/npm install on highest version ('node' is an alias for the latest node.js version.)
+ - php: 7
+ env: DB=none TASK=GRUNT NVM_VERSION='node'
+
exclude:
# MySQL - it's just too slow.
# Exclude it on all versions except for 7.0
- # - env: DB=mysqli TASK=PHPUNIT
- # php: 5.6
- #
- # - env: DB=mysqli TASK=PHPUNIT
- # php: 5.5
- env: DB=mysqli TASK=PHPUNIT
- php: 5.4
-
- - env: DB=none TASK=GRUNT
- php: 5.4
+ php: 5.6
# Moodle 2.7 is not compatible with PHP 7 for the upgrade test.
- env: DB=pgsql TASK=UPGRADE
# Disable xdebug. We aren't generating code coverage, and it has a huge impact upon test performance.
- rm /home/travis/.phpenv/versions/$(phpenv version-name)/etc/conf.d/xdebug.ini
- # Set the encrypted GITHUB_TOKEN if it's available to raise the API limit.
- - if [ -n "$GITHUB_APITOKEN" ]; then composer config github-oauth.github.com $GITHUB_APITOKEN; fi
- - echo 'auth.json' >> .git/info/exclude
+ - >
+ if [ "$TASK" = 'PHPUNIT' ];
+ then
+ if [ -n "$GITHUB_APITOKEN" ]; then
+ composer config github-oauth.github.com $GITHUB_APITOKEN;
+ echo 'auth.json' >> .git/info/exclude
+ fi
+
+ # Install composer dependencies.
+ # We need --no-interaction in case we hit API limits for composer. This causes it to fall back to a standard clone.
+ # Typically it should be able to use the Composer cache if any other job has already completed before we started here.
+ travis_retry composer install --prefer-dist --no-interaction;
+ fi
- # Install composer dependencies.
- # We need --no-interaction in case we hit API limits for composer. This causes it to fall back to a standard clone.
- # Typically it should be able to use the Composer cache if any other job has already completed before we started here.
- - travis_retry composer install --prefer-dist --no-interaction
+ - >
+ if [ "$TASK" = 'GRUNT' ];
+ then
+ nvm install $NVM_VERSION ;
+ nvm use $NVM_VERSION ;
+ fi
before_script:
- >
- >
if [ "$TASK" = 'GRUNT' ];
then
- npm install ;
- npm install -g grunt ;
- grunt ;
+ npm install --no-spin;
+ npm install --no-spin -g grunt ;
fi
########################################################################
- >
if [ "$TASK" = 'GRUNT' ];
then
+ grunt ;
# Add all files to the git index and then run diff --cached to see all changes.
# This ensures that we get the status of all files, including new files.
git add . ;
// Project configuration.
grunt.initConfig({
- jshint: {
- options: {jshintrc: '.jshintrc'},
- amd: { src: amdSrc }
- },
eslint: {
// Even though warnings dont stop the build we don't display warnings by default because
// at this moment we've got too many core warnings.
- options: { quiet: !grunt.option('show-lint-warnings') },
- // Check AMD files. We add some stricter rules which we can't apply to the default configuration due
- // to YUI rollups.
+ options: {quiet: !grunt.option('show-lint-warnings')},
amd: {
src: amdSrc,
- options: {
- rules: {'no-undef': 'error', 'no-unused-vars': 'error', 'no-empty': 'error', 'no-unused-expressions': 'error'}
+ // Check AMD with some slightly stricter rules.
+ rules: {
+ 'no-unused-vars': 'error',
+ 'no-implicit-globals': 'error'
}
},
// Check YUI module source files.
yui: {
src: ['**/yui/src/**/*.js', '!*/**/yui/src/*/meta/*.js'],
+ options: {
+ // Disable some rules which we can't safely define for YUI rollups.
+ rules: {
+ 'no-undef': 'off',
+ 'no-unused-vars': 'off',
+ 'no-unused-expressions': 'off'
+ }
+ }
}
},
uglify: {
expand: true,
src: amdSrc,
rename: uglifyRename
- }]
+ }],
+ options: {report: 'none'}
}
},
less: {
var changedFiles = Object.create(null);
var onChange = grunt.util._.debounce(function() {
var files = Object.keys(changedFiles);
- grunt.config('jshint.amd.src', files);
+ grunt.config('eslint.amd.src', files);
+ grunt.config('eslint.yui.src', files);
grunt.config('uglify.amd.files', [{ expand: true, src: files, rename: uglifyRename }]);
grunt.config('shifter.options.paths', files);
changedFiles = Object.create(null);
// Register NPM tasks.
grunt.loadNpmTasks('grunt-contrib-uglify');
- grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-less');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-eslint');
grunt.registerTask('shifter', 'Run Shifter against the current directory', tasks.shifter);
grunt.registerTask('ignorefiles', 'Generate ignore files for linters', tasks.ignorefiles);
grunt.registerTask('yui', ['eslint:yui', 'shifter']);
- grunt.registerTask('amd', ['eslint:amd', 'jshint', 'uglify']);
+ grunt.registerTask('amd', ['eslint:amd', 'uglify']);
grunt.registerTask('js', ['amd', 'yui']);
// Register CSS taks.
define('IGNORE_COMPONENT_CACHE', true);
// Check that PHP is of a sufficient version
-if (version_compare(phpversion(), "5.4.4") < 0) {
+if (version_compare(phpversion(), "5.6.5") < 0) {
$phpversion = phpversion();
// do NOT localise - lang strings would not work here and we CAN NOT move it after installib
- fwrite(STDERR, "Moodle 2.7 or later requires at least PHP 5.4.4 (currently using version $phpversion).\n");
+ fwrite(STDERR, "Moodle 3.2 or later requires at least PHP 5.6.5 (currently using version $phpversion).\n");
fwrite(STDERR, "Please upgrade your server software or install older Moodle version.\n");
exit(1);
}
";
// Check that PHP is of a sufficient version
-if (version_compare(phpversion(), "5.4.4") < 0) {
+if (version_compare(phpversion(), "5.6.5") < 0) {
$phpversion = phpversion();
// do NOT localise - lang strings would not work here and we CAN NOT move it after installib
- fwrite(STDERR, "Moodle 2.7 or later requires at least PHP 5.4.4 (currently using version $phpversion).\n");
+ fwrite(STDERR, "Moodle 3.2 or later requires at least PHP 5.6.5 (currently using version $phpversion).\n");
fwrite(STDERR, "Please upgrade your server software or install older Moodle version.\n");
exit(1);
}
require(__DIR__.'/../../config.php');
require_once($CFG->libdir.'/clilib.php'); // cli only functions
+// Define the input options.
+$longparams = array(
+ 'help' => false,
+ 'username' => '',
+ 'password' => '',
+ 'ignore-password-policy' => false
+);
+
+$shortparams = array(
+ 'h' => 'help',
+ 'u' => 'username',
+ 'p' => 'password',
+ 'i' => 'ignore-password-policy'
+);
// now get cli options
-list($options, $unrecognized) = cli_get_params(array('help'=>false),
- array('h'=>'help'));
+list($options, $unrecognized) = cli_get_params($longparams, $shortparams);
if ($unrecognized) {
$unrecognized = implode("\n ", $unrecognized);
execute this file may execute any PHP too.
Options:
--h, --help Print out this help
+-h, --help Print out this help
+-u, --username=username Specify username to change
+-p, --password=newpassword Specify new password
+--ignore-password-policy Ignore password policy when setting password
Example:
\$sudo -u www-data /usr/bin/php admin/cli/reset_password.php
+\$sudo -u www-data /usr/bin/php admin/cli/reset_password.php --username=rosaura --password=jiu3jiu --ignore-password-policy
"; //TODO: localize - to be translated later when everything is finished
echo $help;
die;
}
-cli_heading('Password reset'); // TODO: localize
-$prompt = "enter username (manual authentication only)"; // TODO: localize
-$username = cli_input($prompt);
+if ($options['username'] == '' ) {
+ cli_heading('Password reset'); // TODO: localize.
+ $prompt = "enter username (manual authentication only)"; // TODO: localize.
+ $username = cli_input($prompt);
+} else {
+ $username = $options['username'];
+}
if (!$user = $DB->get_record('user', array('auth'=>'manual', 'username'=>$username, 'mnethostid'=>$CFG->mnet_localhost_id))) {
cli_error("Can not find user '$username'");
}
-$prompt = "Enter new password"; // TODO: localize
-$password = cli_input($prompt);
+if ($options['password'] == '' ) {
+ $prompt = "Enter new password"; // TODO: localize.
+ $password = cli_input($prompt);
+} else {
+ $password = $options['password'];
+}
$errmsg = '';//prevent eclipse warning
-if (!check_password_policy($password, $errmsg)) {
- cli_error($errmsg);
+if (!$options['ignore-password-policy'] ) {
+ if (!check_password_policy($password, $errmsg)) {
+ cli_error($errmsg);
+ }
}
$hashedpassword = hash_internal_user_password($password);
echo "Password changed\n";
-exit(0); // 0 means success
\ No newline at end of file
+exit(0); // 0 means success.
</CUSTOM_CHECK>
</CUSTOM_CHECKS>
</MOODLE>
+ <MOODLE version="3.2" requires="2.7">
+ <UNICODE level="required">
+ <FEEDBACK>
+ <ON_ERROR message="unicoderequired" />
+ </FEEDBACK>
+ </UNICODE>
+ <DATABASE level="required">
+ <VENDOR name="mariadb" version="5.5.31" />
+ <VENDOR name="mysql" version="5.5.31" />
+ <VENDOR name="postgres" version="9.1" />
+ <VENDOR name="mssql" version="10.0" />
+ <VENDOR name="oracle" version="10.2" />
+ </DATABASE>
+ <PHP version="5.6.5" level="required">
+ </PHP>
+ <PCREUNICODE level="optional">
+ <FEEDBACK>
+ <ON_CHECK message="pcreunicodewarning" />
+ </FEEDBACK>
+ </PCREUNICODE>
+ <PHP_EXTENSIONS>
+ <PHP_EXTENSION name="iconv" level="required">
+ <FEEDBACK>
+ <ON_ERROR message="iconvrequired" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="mbstring" level="optional">
+ <FEEDBACK>
+ <ON_CHECK message="mbstringrecommended" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="curl" level="required">
+ <FEEDBACK>
+ <ON_ERROR message="curlrequired" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="openssl" level="optional">
+ <FEEDBACK>
+ <ON_CHECK message="opensslrecommended" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="tokenizer" level="optional">
+ <FEEDBACK>
+ <ON_CHECK message="tokenizerrecommended" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="xmlrpc" level="optional">
+ <FEEDBACK>
+ <ON_CHECK message="xmlrpcrecommended" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="soap" level="optional">
+ <FEEDBACK>
+ <ON_CHECK message="soaprecommended" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="ctype" level="required">
+ <FEEDBACK>
+ <ON_ERROR message="ctyperequired" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="zip" level="required">
+ <FEEDBACK>
+ <ON_ERROR message="ziprequired" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="zlib" level="required">
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="gd" level="required">
+ <FEEDBACK>
+ <ON_ERROR message="gdrequired" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="simplexml" level="required">
+ <FEEDBACK>
+ <ON_ERROR message="simplexmlrequired" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="spl" level="required">
+ <FEEDBACK>
+ <ON_ERROR message="splrequired" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="pcre" level="required">
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="dom" level="required">
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="xml" level="required">
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="xmlreader" level="required">
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="intl" level="optional">
+ <FEEDBACK>
+ <ON_CHECK message="intlrecommended" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="json" level="required">
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="hash" level="required"/>
+ </PHP_EXTENSIONS>
+ <PHP_SETTINGS>
+ <PHP_SETTING name="memory_limit" value="96M" level="required">
+ <FEEDBACK>
+ <ON_ERROR message="settingmemorylimit" />
+ </FEEDBACK>
+ </PHP_SETTING>
+ <PHP_SETTING name="file_uploads" value="1" level="optional">
+ <FEEDBACK>
+ <ON_CHECK message="settingfileuploads" />
+ </FEEDBACK>
+ </PHP_SETTING>
+ <PHP_SETTING name="opcache.enable" value="1" level="optional">
+ <FEEDBACK>
+ <ON_CHECK message="opcacherecommended" />
+ </FEEDBACK>
+ </PHP_SETTING>
+ </PHP_SETTINGS>
+ <CUSTOM_CHECKS>
+ <CUSTOM_CHECK file="lib/upgradelib.php" function="check_database_storage_engine" level="required">
+ <FEEDBACK>
+ <ON_ERROR message="unsupporteddbstorageengine" />
+ </FEEDBACK>
+ </CUSTOM_CHECK>
+ <CUSTOM_CHECK file="question/engine/upgrade/upgradelib.php" function="quiz_attempts_upgraded" level="required">
+ <FEEDBACK>
+ <ON_ERROR message="quizattemptsupgradedmessage" />
+ </FEEDBACK>
+ </CUSTOM_CHECK>
+ <CUSTOM_CHECK file="lib/upgradelib.php" function="check_slasharguments" level="optional">
+ <FEEDBACK>
+ <ON_CHECK message="slashargumentswarning" />
+ </FEEDBACK>
+ </CUSTOM_CHECK>
+ <CUSTOM_CHECK file="lib/upgradelib.php" function="check_database_tables_row_format" level="optional">
+ <FEEDBACK>
+ <ON_CHECK message="unsupporteddbtablerowformat" />
+ </FEEDBACK>
+ </CUSTOM_CHECK>
+ <CUSTOM_CHECK file="lib/upgradelib.php" function="check_unoconv_version" level="optional">
+ <FEEDBACK>
+ <ON_CHECK message="unoconvwarning" />
+ </FEEDBACK>
+ </CUSTOM_CHECK>
+ </CUSTOM_CHECKS>
+ </MOODLE>
</COMPATIBILITY_MATRIX>
}
// Check that PHP is of a sufficient version as soon as possible
-if (version_compare(phpversion(), '5.4.4') < 0) {
+if (version_compare(phpversion(), '5.6.5') < 0) {
$phpversion = phpversion();
// do NOT localise - lang strings would not work here and we CAN NOT move it to later place
- echo "Moodle 2.7 or later requires at least PHP 5.4.4 (currently using version $phpversion).<br />";
+ echo "Moodle 3.2 or later requires at least PHP 5.6.5 (currently using version $phpversion).<br />";
echo "Please upgrade your server software or install older Moodle version.";
die();
}
require_capability('moodle/role:review', $context);
require_sesskey();
+$OUTPUT->header();
+
list($overridableroles, $overridecounts, $nameswithcounts) = get_overridable_roles($context,
ROLENAME_BOTH, true);
--- /dev/null
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Manage global search areas.
+ *
+ * @package core_search
+ * @copyright 2016 Dan Poltawski <dan@moodle.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+require_once(__DIR__ . '/../config.php');
+require_once($CFG->libdir . '/adminlib.php');
+
+admin_externalpage_setup('searchareas');
+
+$areaid = optional_param('areaid', null, PARAM_ALPHAEXT);
+$action = optional_param('action', null, PARAM_ALPHA);
+
+try {
+ $searchmanager = \core_search\manager::instance();
+} catch (core_search\engine_exception $searchmanagererror) {
+ // Continue, we return an error later depending on the requested action.
+}
+
+echo $OUTPUT->header();
+
+if ($action) {
+ require_sesskey();
+
+ if ($areaid) {
+ // We need to check that the area exists.
+ $area = \core_search\manager::get_search_area($areaid);
+ if ($area === false) {
+ throw new moodle_exception('invalidrequest');
+ }
+ }
+
+ // All actions but enable/disable need the search engine to be ready.
+ if ($action !== 'enable' && $action !== 'disable') {
+ if (!empty($searchmanagererror)) {
+ throw $searchmanagererror;
+ }
+ }
+
+ switch ($action) {
+ case 'enable':
+ $area->set_enabled(true);
+ echo $OUTPUT->notification(get_string('searchareaenabled', 'admin'), \core\output\notification::NOTIFY_SUCCESS);
+ break;
+ case 'disable':
+ $area->set_enabled(false);
+ echo $OUTPUT->notification(get_string('searchareadisabled', 'admin'), \core\output\notification::NOTIFY_SUCCESS);
+ break;
+ case 'delete':
+ $search = \core_search\manager::instance();
+ $search->delete_index($areaid);
+ echo $OUTPUT->notification(get_string('searchindexdeleted', 'admin'), \core\output\notification::NOTIFY_SUCCESS);
+ break;
+ case 'indexall':
+ $searchmanager->index();
+ echo $OUTPUT->notification(get_string('searchindexupdated', 'admin'), \core\output\notification::NOTIFY_SUCCESS);
+ break;
+ case 'reindexall':
+ $searchmanager->index(true);
+ echo $OUTPUT->notification(get_string('searchreindexed', 'admin'), \core\output\notification::NOTIFY_SUCCESS);
+ break;
+ case 'deleteall':
+ $searchmanager->delete_index();
+ echo $OUTPUT->notification(get_string('searchalldeleted', 'admin'), \core\output\notification::NOTIFY_SUCCESS);
+ break;
+ default:
+ throw new moodle_exception('invalidaction');
+ break;
+ }
+}
+
+$searchareas = \core_search\manager::get_search_areas_list();
+if (empty($searchmanagererror)) {
+ $areasconfig = $searchmanager->get_areas_config($searchareas);
+} else {
+ $areasconfig = false;
+}
+
+if (!empty($searchmanagererror)) {
+ $errorstr = get_string($searchmanagererror->errorcode, $searchmanagererror->module);
+ echo $OUTPUT->notification($errorstr, \core\output\notification::NOTIFY_ERROR);
+} else {
+ echo $OUTPUT->notification(get_string('indexinginfo', 'admin'), \core\output\notification::NOTIFY_INFO);
+}
+
+$table = new html_table();
+$table->id = 'core-search-areas';
+
+$table->head = array(get_string('searcharea', 'search'), get_string('enable'), get_string('newestdocindexed', 'admin'),
+ get_string('searchlastrun', 'admin'), get_string('searchindexactions', 'admin'));
+
+foreach ($searchareas as $area) {
+ $areaid = $area->get_area_id();
+ $columns = array(new html_table_cell($area->get_visible_name()));
+
+ if ($area->is_enabled()) {
+ $columns[] = $OUTPUT->action_icon(admin_searcharea_action_url('disable', $areaid),
+ new pix_icon('t/hide', get_string('disable'), 'moodle', array('title' => '', 'class' => 'iconsmall')),
+ null, array('title' => get_string('disable')));
+
+ if ($areasconfig) {
+ $columns[] = $areasconfig[$areaid]->lastindexrun;
+
+ if ($areasconfig[$areaid]->indexingstart) {
+ $timediff = $areasconfig[$areaid]->indexingend - $areasconfig[$areaid]->indexingstart;
+ $laststatus = $timediff . ' , ' .
+ $areasconfig[$areaid]->docsprocessed . ' , ' .
+ $areasconfig[$areaid]->recordsprocessed . ' , ' .
+ $areasconfig[$areaid]->docsignored;
+ } else {
+ $laststatus = '';
+ }
+ $columns[] = $laststatus;
+ $columns[] = html_writer::link(admin_searcharea_action_url('delete', $areaid), 'Delete index');
+
+ } else {
+ $blankrow = new html_table_cell(get_string('searchnotavailable', 'admin'));
+ $blankrow->colspan = 3;
+ $columns[] = $blankrow;
+ }
+
+ } else {
+ $columns[] = $OUTPUT->action_icon(admin_searcharea_action_url('enable', $areaid),
+ new pix_icon('t/show', get_string('enable'), 'moodle', array('title' => '', 'class' => 'iconsmall')),
+ null, array('title' => get_string('enable')));
+
+ $blankrow = new html_table_cell(get_string('searchareadisabled', 'admin'));
+ $blankrow->colspan = 3;
+ $columns[] = $blankrow;
+ }
+ $row = new html_table_row($columns);
+ $table->data[] = $row;
+}
+
+// Cross-search area tasks.
+$options = array();
+if (!empty($searchmanagererror)) {
+ $options['disabled'] = true;
+}
+echo $OUTPUT->box_start('search-areas-actions');
+echo $OUTPUT->single_button(admin_searcharea_action_url('indexall'), get_string('searchupdateindex', 'admin'), 'get', $options);
+echo $OUTPUT->single_button(admin_searcharea_action_url('reindexall'), get_string('searchreindexindex', 'admin'), 'get', $options);
+echo $OUTPUT->single_button(admin_searcharea_action_url('deleteall'), get_string('searchdeleteindex', 'admin'), 'get', $options);
+echo $OUTPUT->box_end();
+
+echo html_writer::table($table);
+echo $OUTPUT->footer();
+
+/**
+ * Helper for generating url for management actions.
+ *
+ * @param string $action
+ * @param string $areaid
+ * @return moodle_url
+ */
+function admin_searcharea_action_url($action, $areaid = false) {
+ $params = array('action' => $action, 'sesskey' => sesskey());
+ if ($areaid) {
+ $params['areaid'] = $areaid;
+ }
+ return new moodle_url('/admin/searchareas.php', $params);
+}
$temp->add(new admin_setting_configselect('searchengine',
new lang_string('selectsearchengine', 'admin'), '', 'solr', $engines));
- // Enable search areas.
- $temp->add(new admin_setting_heading('searchareasheading', new lang_string('availablesearchareas', 'admin'), ''));
- $searchareas = \core_search\manager::get_search_areas_list();
- foreach ($searchareas as $areaid => $searcharea) {
- list($componentname, $varname) = $searcharea->get_config_var_name();
- $temp->add(new admin_setting_configcheckbox($componentname . '/' . $varname . '_enabled', $searcharea->get_visible_name(true),
- '', 1, 1, 0));
- }
$ADMIN->add('searchplugins', $temp);
+ $ADMIN->add('searchplugins', new admin_externalpage('searchareas', new lang_string('searchareas', 'admin'),
+ new moodle_url('/admin/searchareas.php')));
core_collator::asort_objects_by_property($pages, 'visiblename');
foreach ($pages as $page) {
self._selectedValue = $("input[type='radio']:checked").val();
self._find('[data-action="action-selector-confirm"]').removeAttr('disabled');
self._refresh.bind(self);
- }.bind(self));
+ });
// Add listener for cancel.
self._find('[data-action="action-selector-cancel"]').click(function(e) {
e.preventDefault();
self.close();
- }.bind(self));
+ });
// Add listener for confirm.
self._find('[data-action="action-selector-confirm"]').click(function(e) {
if (!self._selectedValue.length) {
return;
}
- self._trigger('save', { action: self._selectedValue });
+ self._trigger('save', {action: self._selectedValue});
self.close();
- }.bind(self));
+ });
};
/**
html,
self._afterRender.bind(self)
);
- }.bind(self)).fail(Notification.exception);
+ }).fail(Notification.exception);
};
/**
return self._render().then(function(html) {
self._find('[data-region="action-selector"]').replaceWith(html);
self._afterRender();
- }.bind(self));
+ });
};
/**
function(movestring) {
dragdrop.dragdrop('movecompetency',
movestring,
- { identifier: 'movecompetency', component: 'tool_lp'},
- { identifier: 'movecompetencyafter', component: 'tool_lp'},
+ {identifier: 'movecompetency', component: 'tool_lp'},
+ {identifier: 'movecompetencyafter', component: 'tool_lp'},
'drag-samenode',
'drag-parentnode',
'drag-handlecontainer',
function(drag, drop) {
- localthis.handleDrop.call(localthis, drag, drop);
+ localthis.handleDrop(drag, drop);
});
}
).fail(notification.exception);
requests = ajax.call([
{
methodname: 'core_competency_reorder_course_competency',
- args: { courseid: localthis.itemid, competencyidfrom: fromid, competencyidto: toid }
+ args: {courseid: localthis.itemid, competencyidfrom: fromid, competencyidto: toid}
}
]);
} else if (localthis.itemtype == 'template') {
requests = ajax.call([
{
methodname: 'core_competency_reorder_template_competency',
- args: { templateid: localthis.itemid, competencyidfrom: fromid, competencyidto: toid }
+ args: {templateid: localthis.itemid, competencyidfrom: fromid, competencyidto: toid}
}
]);
} else if (localthis.itemtype == 'plan') {
requests = ajax.call([
{
methodname: 'core_competency_reorder_plan_competency',
- args: { planid: localthis.itemid, competencyidfrom: fromid, competencyidto: toid }
+ args: {planid: localthis.itemid, competencyidfrom: fromid, competencyidto: toid}
}
]);
} else {
$.each(compIds, function(index, compId) {
requests.push({
methodname: 'core_competency_add_competency_to_course',
- args: { courseid: self.itemid, competencyid: compId }
+ args: {courseid: self.itemid, competencyid: compId}
});
});
requests.push({
methodname: 'tool_lp_data_for_course_competencies_page',
- args: { courseid: self.itemid }
+ args: {courseid: self.itemid}
});
pagerender = 'tool_lp/course_competencies_page';
$.each(compIds, function(index, compId) {
requests.push({
methodname: 'core_competency_add_competency_to_template',
- args: { templateid: self.itemid, competencyid: compId }
+ args: {templateid: self.itemid, competencyid: compId}
});
});
requests.push({
methodname: 'tool_lp_data_for_template_competencies_page',
- args: { templateid: self.itemid, pagecontext: { contextid: self.pageContextId }}
+ args: {templateid: self.itemid, pagecontext: {contextid: self.pageContextId}}
});
pagerender = 'tool_lp/template_competencies_page';
pageregion = 'templatecompetenciespage';
$.each(compIds, function(index, compId) {
requests.push({
methodname: 'core_competency_add_competency_to_plan',
- args: { planid: self.itemid, competencyid: compId }
+ args: {planid: self.itemid, competencyid: compId}
});
});
requests.push({
methodname: 'tool_lp_data_for_plan_page',
- args: { planid: self.itemid}
+ args: {planid: self.itemid}
});
pagerender = 'tool_lp/plan_page';
pageregion = 'plan-page';
// Delete the link and reload the page template.
if (localthis.itemtype == 'course') {
requests = ajax.call([
- { methodname: 'core_competency_remove_competency_from_course',
- args: { courseid: localthis.itemid, competencyid: deleteid } },
- { methodname: 'tool_lp_data_for_course_competencies_page',
- args: { courseid: localthis.itemid } }
+ {methodname: 'core_competency_remove_competency_from_course',
+ args: {courseid: localthis.itemid, competencyid: deleteid}},
+ {methodname: 'tool_lp_data_for_course_competencies_page',
+ args: {courseid: localthis.itemid}}
]);
pagerender = 'tool_lp/course_competencies_page';
pageregion = 'coursecompetenciespage';
} else if (localthis.itemtype == 'template') {
requests = ajax.call([
- { methodname: 'core_competency_remove_competency_from_template',
- args: { templateid: localthis.itemid, competencyid: deleteid } },
- { methodname: 'tool_lp_data_for_template_competencies_page',
- args: { templateid: localthis.itemid, pagecontext: { contextid: localthis.pageContextId } } }
+ {methodname: 'core_competency_remove_competency_from_template',
+ args: {templateid: localthis.itemid, competencyid: deleteid}},
+ {methodname: 'tool_lp_data_for_template_competencies_page',
+ args: {templateid: localthis.itemid, pagecontext: {contextid: localthis.pageContextId}}}
]);
pagerender = 'tool_lp/template_competencies_page';
pageregion = 'templatecompetenciespage';
} else if (localthis.itemtype == 'plan') {
requests = ajax.call([
- { methodname: 'core_competency_remove_competency_from_plan',
- args: { planid: localthis.itemid, competencyid: deleteid } },
- { methodname: 'tool_lp_data_for_plan_page',
- args: { planid: localthis.itemid } }
+ {methodname: 'core_competency_remove_competency_from_plan',
+ args: {planid: localthis.itemid, competencyid: deleteid}},
+ {methodname: 'tool_lp_data_for_plan_page',
+ args: {planid: localthis.itemid}}
]);
pagerender = 'tool_lp/plan_page';
pageregion = 'plan-page';
requests = ajax.call([{
methodname: 'core_competency_read_competency',
- args: { id: deleteid }
+ args: {id: deleteid}
}]);
requests[0].done(function(competency) {
str.get_strings([
- { key: 'confirm', component: 'moodle' },
- { key: message, component: 'tool_lp', param: competency.shortname },
- { key: 'confirm', component: 'moodle' },
- { key: 'cancel', component: 'moodle' }
+ {key: 'confirm', component: 'moodle'},
+ {key: message, component: 'tool_lp', param: competency.shortname},
+ {key: 'confirm', component: 'moodle'},
+ {key: 'cancel', component: 'moodle'}
]).done(function(strings) {
notification.confirm(
strings[0], // Confirm.
var coursecompetencyid = $(e.target).data('id');
var ruleoutcome = $(e.target).val();
requests = ajax.call([
- { methodname: 'core_competency_set_course_competency_ruleoutcome',
- args: { coursecompetencyid: coursecompetencyid, ruleoutcome: ruleoutcome } },
- { methodname: 'tool_lp_data_for_course_competencies_page',
- args: { courseid: localthis.itemid } }
+ {methodname: 'core_competency_set_course_competency_ruleoutcome',
+ args: {coursecompetencyid: coursecompetencyid, ruleoutcome: ruleoutcome}},
+ {methodname: 'tool_lp_data_for_course_competencies_page',
+ args: {courseid: localthis.itemid}}
]);
requests[1].done(function(context) {
getAll: function() {
var self = this;
return Str.get_strings([
- { key: 'competencyoutcome_none', component: 'tool_lp' },
- { key: 'competencyoutcome_evidence', component: 'tool_lp' },
- { key: 'competencyoutcome_recommend', component: 'tool_lp' },
- { key: 'competencyoutcome_complete', component: 'tool_lp' },
+ {key: 'competencyoutcome_none', component: 'tool_lp'},
+ {key: 'competencyoutcome_evidence', component: 'tool_lp'},
+ {key: 'competencyoutcome_recommend', component: 'tool_lp'},
+ {key: 'competencyoutcome_complete', component: 'tool_lp'},
]).then(function(strings) {
var outcomes = {};
- outcomes[self.NONE] = { code: self.NONE, name: strings[0] };
- outcomes[self.EVIDENCE] = { code: self.EVIDENCE, name: strings[1] };
- outcomes[self.RECOMMEND] = { code: self.RECOMMEND, name: strings[2] };
- outcomes[self.COMPLETE] = { code: self.COMPLETE, name: strings[3] };
+ outcomes[self.NONE] = {code: self.NONE, name: strings[0]};
+ outcomes[self.EVIDENCE] = {code: self.EVIDENCE, name: strings[1]};
+ outcomes[self.RECOMMEND] = {code: self.RECOMMEND, name: strings[2]};
+ outcomes[self.COMPLETE] = {code: self.COMPLETE, name: strings[3]};
return outcomes;
});
},
children = this._tree.getChildren(this._competency.id),
context,
config = {
- base: { points: 2 },
+ base: {points: 2},
competencies: []
};
if (parent !== null && treeModel.hasRule(parent.id)) {
str.get_strings([
- { key: 'confirm', component: 'moodle' },
- { key: 'addingcompetencywillresetparentrule', component: 'tool_lp', param: parent.shortname },
- { key: 'yes', component: 'core' },
- { key: 'no', component: 'core' }
+ {key: 'confirm', component: 'moodle'},
+ {key: 'addingcompetencywillresetparentrule', component: 'tool_lp', param: parent.shortname},
+ {key: 'yes', component: 'core'},
+ {key: 'no', component: 'core'}
]).done(function(strings) {
notification.confirm(
strings[0],
var frameworkid = $('[data-region="filtercompetencies"]').data('frameworkid');
var requests = ajax.call([{
methodname: 'core_competency_set_parent_competency',
- args: { competencyid: moveSource, parentid: moveTarget }
+ args: {competencyid: moveSource, parentid: moveTarget}
}, {
methodname: 'tool_lp_data_for_competencies_manage_page',
- args: { competencyframeworkid: frameworkid,
- search: $('[data-region="filtercompetencies"] input').val() }
+ args: {competencyframeworkid: frameworkid,
+ search: $('[data-region="filtercompetencies"] input').val()}
}]);
requests[1].done(reloadPage).fail(notification.exception);
};
// Show confirm, and/or do the things.
if (showConfirm) {
str.get_strings([
- { key: 'confirm', component: 'moodle' },
- { key: confirmMessage, component: 'tool_lp' },
- { key: 'yes', component: 'moodle' },
- { key: 'no', component: 'moodle' }
+ {key: 'confirm', component: 'moodle'},
+ {key: confirmMessage, component: 'tool_lp'},
+ {key: 'yes', component: 'moodle'},
+ {key: 'no', component: 'moodle'}
]).done(function(strings) {
notification.confirm(
strings[0], // Confirm.
$.when.apply(null, requests).done(function(competencies, framework) {
// Expand the list of competencies into a tree.
- var i, competenciestree = [];
+ var i;
+ var competenciestree = [];
for (i = 0; i < competencies.length; i++) {
var onecompetency = competencies[i];
if (onecompetency.parentid == "0") {
}
str.get_strings([
- { key: 'movecompetency', component: 'tool_lp', param: competency.shortname },
- { key: 'move', component: 'tool_lp' },
- { key: 'cancel', component: 'moodle' }
+ {key: 'movecompetency', component: 'tool_lp', param: competency.shortname},
+ {key: 'move', component: 'tool_lp'},
+ {key: 'cancel', component: 'moodle'}
]).done(function(strings) {
var context = {
var requests = ajax.call([{
methodname: 'tool_lp_data_for_competencies_manage_page',
- args: { competencyframeworkid: frameworkid,
- search: $('[data-region="filtercompetencies"] input').val() }
+ args: {competencyframeworkid: frameworkid,
+ search: $('[data-region="filtercompetencies"] input').val()}
}]);
requests[0].done(reloadPage).fail(notification.exception);
};
var competency = $('[data-region="competencyactions"]').data('competency');
var requests = ajax.call([{
methodname: 'core_competency_move_up_competency',
- args: { id: competency.id }
+ args: {id: competency.id}
}, {
methodname: 'tool_lp_data_for_competencies_manage_page',
- args: { competencyframeworkid: competency.competencyframeworkid,
- search: $('[data-region="filtercompetencies"] input').val() }
+ args: {competencyframeworkid: competency.competencyframeworkid,
+ search: $('[data-region="filtercompetencies"] input').val()}
}]);
requests[1].done(reloadPage).fail(notification.exception);
};
var competency = $('[data-region="competencyactions"]').data('competency');
var requests = ajax.call([{
methodname: 'core_competency_move_down_competency',
- args: { id: competency.id }
+ args: {id: competency.id}
}, {
methodname: 'tool_lp_data_for_competencies_manage_page',
- args: { competencyframeworkid: competency.competencyframeworkid,
- search: $('[data-region="filtercompetencies"] input').val() }
+ args: {competencyframeworkid: competency.competencyframeworkid,
+ search: $('[data-region="filtercompetencies"] input').val()}
}]);
requests[1].done(reloadPage).fail(notification.exception);
};
var requests = ajax.call([{
methodname: 'tool_lp_list_courses_using_competency',
- args: { id: competency.id }
+ args: {id: competency.id}
}]);
requests[0].done(function(courses) {
$.each(compIds, function(index, value) {
calls.push({
methodname: 'core_competency_add_related_competency',
- args: { competencyid: value, relatedcompetencyid: relatedTarget.id }
+ args: {competencyid: value, relatedcompetencyid: relatedTarget.id}
});
});
calls.push({
methodname: 'tool_lp_data_for_related_competencies_section',
- args: { competencyid: relatedTarget.id }
+ args: {competencyid: relatedTarget.id}
});
var promises = ajax.call(calls);
};
var promise = ajax.call([{
methodname: 'core_competency_update_competency',
- args: { competency: update }
+ args: {competency: update}
}]);
promise[0].then(function(result) {
if (result) {
var competency = $('[data-region="competencyactions"]').data('competency');
var requests = ajax.call([{
methodname: 'core_competency_delete_competency',
- args: { id: competency.id }
+ args: {id: competency.id}
}, {
methodname: 'tool_lp_data_for_competencies_manage_page',
- args: { competencyframeworkid: competency.competencyframeworkid,
- search: $('[data-region="filtercompetencies"] input').val() }
+ args: {competencyframeworkid: competency.competencyframeworkid,
+ search: $('[data-region="filtercompetencies"] input').val()}
}]);
requests[0].done(function(success) {
if (success === false) {
str.get_strings([
- { key: 'competencycannotbedeleted', component: 'tool_lp', param: competency.shortname },
- { key: 'cancel', component: 'moodle' }
+ {key: 'competencycannotbedeleted', component: 'tool_lp', param: competency.shortname},
+ {key: 'cancel', component: 'moodle'}
]).done(function(strings) {
notification.alert(
null,
}
str.get_strings([
- { key: 'confirm', component: 'moodle' },
- { key: confirmMessage, component: 'tool_lp', param: competency.shortname },
- { key: 'delete', component: 'moodle' },
- { key: 'cancel', component: 'moodle' }
+ {key: 'confirm', component: 'moodle'},
+ {key: confirmMessage, component: 'tool_lp', param: competency.shortname},
+ {key: 'delete', component: 'moodle'},
+ {key: 'cancel', component: 'moodle'}
]).done(function(strings) {
notification.confirm(
strings[0], // Confirm.
var relatedid = this.id.substr(11);
var competency = $('[data-region="competencyactions"]').data('competency');
var removeRelated = ajax.call([
- { methodname: 'core_competency_remove_related_competency',
- args: { relatedcompetencyid: relatedid, competencyid: competency.id } },
- { methodname: 'tool_lp_data_for_related_competencies_section',
- args: { competencyid: competency.id } }
+ {methodname: 'core_competency_remove_related_competency',
+ args: {relatedcompetencyid: relatedid, competencyid: competency.id}},
+ {methodname: 'tool_lp_data_for_related_competencies_section',
+ args: {competencyid: competency.id}}
]);
removeRelated[1].done(function(context) {
templates.render('tool_lp/related_competencies', context).done(function(html) {
$('[data-region="relatedcompetencies"]').replaceWith(html);
updatedRelatedCompetencies();
- }.bind(this)).fail(notification.exception);
- }.bind(this)).fail(notification.exception);
+ }).fail(notification.exception);
+ }).fail(notification.exception);
};
/**
selectedCompetencyId = competency.id;
ajax.call([{
methodname: 'core_competency_competency_viewed',
- args: { id: competency.id }
+ args: {id: competency.id}
}]);
}
};
}).done(function() {
ajax.call([{
methodname: 'tool_lp_data_for_related_competencies_section',
- args: { competencyid: competency.id },
+ args: {competencyid: competency.id},
done: function(context) {
return templates.render('tool_lp/related_competencies', context).done(function(html, js) {
$('[data-region="relatedcompetencies"]').replaceWith(html);
Competencydialogue.prototype.triggerCompetencyViewedEvent = function(competencyId) {
ajax.call([{
methodname: 'core_competency_competency_viewed',
- args: { id: competencyId }
+ args: {id: competencyId}
}]);
};
Competencydialogue.prototype.getCompetencyDataPromise = function(competencyid, options) {
var requests = ajax.call([
- { methodname: 'tool_lp_data_for_competency_summary',
- args: { competencyid: competencyid,
+ {methodname: 'tool_lp_data_for_competency_summary',
+ args: {competencyid: competencyid,
includerelated: options.includerelated || false,
includecourses: options.includecourses || false
}
// Instantiate the one instance and delegate event on the body.
instance = new Competencydialogue();
- $('body').delegate('[data-action="competency-dialogue"]', 'click', { compdialogue: instance },
+ $('body').delegate('[data-action="competency-dialogue"]', 'click', {compdialogue: instance},
instance.clickEventHandler.bind(instance));
}
};
if (valid) {
validIds.push(compId);
}
- }.bind(self));
+ });
self._selectedCompetencies = validIds;
} else {
self._find('[data-region="competencylinktree"] [data-action="add"]').removeAttr('disabled');
}
- }.bind(self));
+ });
// Add listener for framework change.
if (!self._singleFramework) {
self._find('[data-action="chooseframework"]').change(function(e) {
self._frameworkId = $(e.target).val();
self._loadCompetencies().then(self._refresh.bind(self));
- }.bind(self));
+ });
}
// Add listener for search.
return self._refresh().always(function() {
$(e.target).removeAttr('disabled');
});
- }.bind(self));
+ });
// Add listener for cancel.
self._find('[data-region="competencylinktree"] [data-action="cancel"]').click(function(e) {
e.preventDefault();
self.close();
- }.bind(self));
+ });
// Add listener for add.
self._find('[data-region="competencylinktree"] [data-action="add"]').click(function(e) {
}
if (self._multiSelect) {
- self._trigger('save', { competencyIds: self._selectedCompetencies });
+ self._trigger('save', {competencyIds: self._selectedCompetencies});
} else {
// We checked above that the array has at least one value.
- self._trigger('save', { competencyId: self._selectedCompetencies[0] });
+ self._trigger('save', {competencyId: self._selectedCompetencies[0]});
}
self.close();
- }.bind(self));
+ });
// The list of selected competencies will be modified while looping (because of the listeners above).
var currentItems = self._selectedCompetencies.slice(0);
tree.toggleItem(node);
tree.updateFocus(node);
}
- }.bind(self));
+ });
};
html,
self._afterRender.bind(self)
);
- }.bind(self));
- }.bind(self)).fail(Notification.exception);
+ });
+ }).fail(Notification.exception);
};
/**
var self = this;
return Ajax.call([
- { methodname: 'core_competency_search_competencies', args: {
+ {methodname: 'core_competency_search_competencies', args: {
searchtext: searchText,
competencyframeworkid: frameworkId
}}
}
// Expand the list of competencies into a tree.
- var i, tree = [], comp;
+ var i, comp;
+ var tree = [];
for (i = 0; i < competencies.length; i++) {
comp = competencies[i];
if (comp.parentid == "0") { // Loose check for now, because WS returns a string.
self._competencies = tree;
- }.bind(self)).fail(Notification.exception);
+ }).fail(Notification.exception);
};
/**
$.each(this._frameworks, function(i, f) {
if (f.id == fid) {
frm = f;
- return false;
+ return;
}
});
return frm;
if (self._singleFramework) {
promise = Ajax.call([
- { methodname: 'core_competency_read_competency_framework', args: {
+ {methodname: 'core_competency_read_competency_framework', args: {
id: this._frameworkId
}}
])[0].then(function(framework) {
});
} else {
promise = Ajax.call([
- { methodname: 'core_competency_list_competency_frameworks', args: {
+ {methodname: 'core_competency_list_competency_frameworks', args: {
sort: 'shortname',
- context: { contextid: self._pageContextId },
+ context: {contextid: self._pageContextId},
includes: self._pageContextIncludes,
onlyvisible: self._onlyVisible
}}
}
return self._loadCompetencies();
- }.bind(self));
+ });
};
/**
return self._render().then(function(html) {
self._find('[data-region="competencylinktree"]').replaceWith(html);
self._afterRender();
- }.bind(self));
+ });
};
/**
};
return Templates.render('tool_lp/competency_picker', context);
- }.bind(self));
+ });
};
/**
self._find('[data-action="chooseplan"]').change(function(e) {
self._planId = $(e.target).val();
self._loadCompetencies().then(self._refresh.bind(self));
- }.bind(self));
+ });
}
};
var self = this;
return Ajax.call([
- { methodname: 'core_competency_list_plan_competencies', args: {
+ {methodname: 'core_competency_list_plan_competencies', args: {
id: planId
}}
])[0].done(function(competencies) {
// Expand the list of competencies into a fake tree.
- var i, tree = [], comp;
+ var i, comp;
+ var tree = [];
for (i = 0; i < competencies.length; i++) {
comp = competencies[i].competency;
if (comp.shortname.toLowerCase().indexOf(searchText.toLowerCase()) < 0) {
$.each(this._plans, function(i, f) {
if (f.id == id) {
plan = f;
- return false;
+ return;
}
});
return plan;
if (self._singlePlan) {
promise = Ajax.call([
- { methodname: 'core_competency_read_plan', args: {
+ {methodname: 'core_competency_read_plan', args: {
id: this._planId
}}
])[0].then(function(plan) {
});
} else {
promise = Ajax.call([
- { methodname: 'core_competency_list_user_plans', args: {
+ {methodname: 'core_competency_list_user_plans', args: {
userid: self._userId
}}
])[0];
}
return self._loadCompetencies();
- }.bind(self));
+ });
};
/**
};
return Templates.render('tool_lp/competency_picker_user_plans', context);
- }.bind(self));
+ });
};
return /** @alias module:tool_lp/competencypicker_user_plans */ Picker;
$.each(this._rules, function(index, rule) {
if (rule.canConfig()) {
can = true;
- return false;
+ return;
}
});
return can;
RuleConfig.prototype.display = function() {
var self = this;
if (!self._competency) {
- return;
+ return false;
}
return self._render().then(function(html) {
return Str.get_string('competencyrule', 'tool_lp').then(function(title) {
$.each(this._rules, function(index, rule) {
if (rule.getType() == type) {
result = rule;
- return false;
+ return;
}
});
$.each(self._rulesModules, function(index, modInfo) {
if (modInfo.type == type) {
name = modInfo.name;
- return false;
+ return;
}
});
return name;
var currentValue = $(e.target).closest('a').data('pushratingstouserplans');
var context = {
courseid: courseid,
- settings: { pushratingstouserplans: currentValue }
+ settings: {pushratingstouserplans: currentValue}
};
e.preventDefault();
var newValue = this._find('input[name="pushratingstouserplans"]:checked').val();
var courseId = this._find('input[name="courseid"]').val();
- var settings = { pushratingstouserplans: newValue };
+ var settings = {pushratingstouserplans: newValue};
ajax.call([
- { methodname: 'core_competency_update_course_competency_settings',
- args: { courseid: courseId, settings: settings } }
+ {methodname: 'core_competency_update_course_competency_settings',
+ args: {courseid: courseId, settings: settings}}
])[0].done(function() {
this.refreshCourseCompetenciesPage();
}.bind(this)).fail(notification.exception);
var courseId = this._find('input[name="courseid"]').val();
ajax.call([
- { methodname: 'tool_lp_data_for_course_competencies_page',
- args: { courseid: courseId } }
+ {methodname: 'tool_lp_data_for_course_competencies_page',
+ args: {courseid: courseId}}
])[0].done(function(context) {
templates.render('tool_lp/course_competencies_page', context).done(function(html, js) {
$('[data-region="coursecompetenciespage"]').replaceWith(html);
// Here we are wrapping YUI. This allows us to start transitioning, but
// wait for a good alternative without having inconsistent UIs.
str.get_strings([
- { key: 'emptydragdropregion', component: 'moodle' },
- { key: 'movecontent', component: 'moodle' },
- { key: 'tocontent', component: 'moodle' },
+ {key: 'emptydragdropregion', component: 'moodle'},
+ {key: 'movecontent', component: 'moodle'},
+ {key: 'tocontent', component: 'moodle'},
]).done(function() {
Y.use('moodle-tool_lp-dragdrop-reorder', function() {
e.stopPropagation();
Str.get_strings([
- { key: 'confirm', component: 'moodle' },
- { key: 'areyousure', component: 'moodle' },
- { key: 'delete', component: 'moodle' },
- { key: 'cancel', component: 'moodle' }
+ {key: 'confirm', component: 'moodle'},
+ {key: 'areyousure', component: 'moodle'},
+ {key: 'delete', component: 'moodle'},
+ {key: 'cancel', component: 'moodle'}
]).done(function(strings) {
Notification.confirm(
strings[0], // Confirm.
methodname: 'tool_lp_search_cohorts',
args: {
query: query,
- context: { contextid: contextid },
+ context: {contextid: contextid},
includes: includes
}
}]);
// We are chaining ajax requests here.
var requests = ajax.call([{
methodname: 'core_competency_duplicate_competency_framework',
- args: { id: frameworkid }
+ args: {id: frameworkid}
}, {
methodname: 'tool_lp_data_for_competency_frameworks_manage_page',
args: {
// We are chaining ajax requests here.
var requests = ajax.call([{
methodname: 'core_competency_delete_competency_framework',
- args: { id: frameworkid }
+ args: {id: frameworkid}
}, {
methodname: 'tool_lp_data_for_competency_frameworks_manage_page',
args: {
if (success === false) {
var req = ajax.call([{
methodname: 'core_competency_read_competency_framework',
- args: { id: frameworkid }
+ args: {id: frameworkid}
}]);
req[0].done(function(framework) {
str.get_strings([
- { key: 'frameworkcannotbedeleted', component: 'tool_lp', param: framework.shortname },
- { key: 'cancel', component: 'moodle' }
+ {key: 'frameworkcannotbedeleted', component: 'tool_lp', param: framework.shortname},
+ {key: 'cancel', component: 'moodle'}
]).done(function(strings) {
notification.alert(
null,
var requests = ajax.call([{
methodname: 'core_competency_read_competency_framework',
- args: { id: frameworkid }
+ args: {id: frameworkid}
}]);
requests[0].done(function(framework) {
str.get_strings([
- { key: 'confirm', component: 'moodle' },
- { key: 'deletecompetencyframework', component: 'tool_lp', param: framework.shortname },
- { key: 'delete', component: 'moodle' },
- { key: 'cancel', component: 'moodle' }
+ {key: 'confirm', component: 'moodle'},
+ {key: 'deletecompetencyframework', component: 'tool_lp', param: framework.shortname},
+ {key: 'delete', component: 'moodle'},
+ {key: 'cancel', component: 'moodle'}
]).done(function(strings) {
notification.confirm(
strings[0], // Confirm.
methodname: self._methodName,
args: args,
done: function(evidence) {
- self._trigger('competencyupdated', { args: args, evidence: evidence });
- }.bind(self),
+ self._trigger('competencyupdated', {args: args, evidence: evidence});
+ },
fail: notification.exception
}]);
- }.bind(self));
+ });
}).fail(notification.exception);
};
if (this.handlers) {
$.each(this.handlers, function(selector, handler) {
if (eventHandled) {
- return false;
+ return;
}
if (item.find(selector).length > 0) {
var callable = $.proxy(handler, anchor);
if (data.competencyId !== 0) {
ajax.call([
- { methodname: 'core_competency_read_competency', args: {
+ {methodname: 'core_competency_read_competency', args: {
id: data.competencyId
}}
])[0].done(function(competency) {
};
return Templates.render('tool_lp/competency_picker_competencyform', context);
- }.bind(self));
+ });
};
// On selected competency.
picker.on('save', function(e, data) {
self.setParent(data);
- }.bind(self));
+ });
picker.display();
});
.done(function(newhtml, newjs) {
$(self._region).replaceWith(newhtml);
templates.runTemplateJS(newjs);
- }.bind(self))
+ })
.fail(notification.exception);
};
// Apply all the promises, and refresh when the last one is resolved.
return $.when.apply($.when, ajax.call(calls))
.then(function() {
- self._renderView.call(self, arguments[arguments.length - 1]);
+ self._renderView(arguments[arguments.length - 1]);
})
.fail(notification.exception);
};
var self = this,
calls = [{
methodname: 'core_competency_delete_plan',
- args: { id: planData.id }
+ args: {id: planData.id}
}];
self._callAndRefresh(calls, planData);
};
requests = ajax.call([{
methodname: 'core_competency_read_plan',
- args: { id: planData.id }
+ args: {id: planData.id}
}]);
requests[0].done(function(plan) {
str.get_strings([
- { key: 'confirm', component: 'moodle' },
- { key: 'deleteplan', component: 'tool_lp', param: plan.name },
- { key: 'delete', component: 'moodle' },
- { key: 'cancel', component: 'moodle' }
+ {key: 'confirm', component: 'moodle'},
+ {key: 'deleteplan', component: 'tool_lp', param: plan.name},
+ {key: 'delete', component: 'moodle'},
+ {key: 'cancel', component: 'moodle'}
]).done(function(strings) {
notification.confirm(
strings[0], // Confirm.
strings[3], // Cancel.
function() {
self._doDelete(planData);
- }.bind(self)
+ }
);
}).fail(notification.exception);
}).fail(notification.exception);
var self = this,
calls = [{
methodname: 'core_competency_reopen_plan',
- args: { planid: planData.id}
+ args: {planid: planData.id}
}];
self._callAndRefresh(calls, planData);
};
var self = this,
requests = ajax.call([{
methodname: 'core_competency_read_plan',
- args: { id: planData.id }
+ args: {id: planData.id}
}]);
requests[0].done(function(plan) {
str.get_strings([
- { key: 'confirm', component: 'moodle' },
- { key: 'reopenplanconfirm', component: 'tool_lp', param: plan.name },
- { key: 'reopenplan', component: 'tool_lp' },
- { key: 'cancel', component: 'moodle' }
+ {key: 'confirm', component: 'moodle'},
+ {key: 'reopenplanconfirm', component: 'tool_lp', param: plan.name},
+ {key: 'reopenplan', component: 'tool_lp'},
+ {key: 'cancel', component: 'moodle'}
]).done(function(strings) {
notification.confirm(
strings[0], // Confirm.
strings[3], // Cancel.
function() {
self._doReopenPlan(planData);
- }.bind(self)
+ }
);
}).fail(notification.exception);
}).fail(notification.exception);
var self = this,
calls = [{
methodname: 'core_competency_complete_plan',
- args: { planid: planData.id}
+ args: {planid: planData.id}
}];
self._callAndRefresh(calls, planData);
};
var self = this,
requests = ajax.call([{
methodname: 'core_competency_read_plan',
- args: { id: planData.id }
+ args: {id: planData.id}
}]);
requests[0].done(function(plan) {
str.get_strings([
- { key: 'confirm', component: 'moodle' },
- { key: 'completeplanconfirm', component: 'tool_lp', param: plan.name },
- { key: 'completeplan', component: 'tool_lp' },
- { key: 'cancel', component: 'moodle' }
+ {key: 'confirm', component: 'moodle'},
+ {key: 'completeplanconfirm', component: 'tool_lp', param: plan.name},
+ {key: 'completeplan', component: 'tool_lp'},
+ {key: 'cancel', component: 'moodle'}
]).done(function(strings) {
notification.confirm(
strings[0], // Confirm.
strings[3], // Cancel.
function() {
self._doCompletePlan(planData);
- }.bind(self)
+ }
);
}).fail(notification.exception);
}).fail(notification.exception);
var self = this,
calls = [{
methodname: 'core_competency_unlink_plan_from_template',
- args: { planid: planData.id}
+ args: {planid: planData.id}
}];
self._callAndRefresh(calls, planData);
};
var self = this,
requests = ajax.call([{
methodname: 'core_competency_read_plan',
- args: { id: planData.id }
+ args: {id: planData.id}
}]);
requests[0].done(function(plan) {
str.get_strings([
- { key: 'confirm', component: 'moodle' },
- { key: 'unlinkplantemplateconfirm', component: 'tool_lp', param: plan.name },
- { key: 'unlinkplantemplate', component: 'tool_lp' },
- { key: 'cancel', component: 'moodle' }
+ {key: 'confirm', component: 'moodle'},
+ {key: 'unlinkplantemplateconfirm', component: 'tool_lp', param: plan.name},
+ {key: 'unlinkplantemplate', component: 'tool_lp'},
+ {key: 'cancel', component: 'moodle'}
]).done(function(strings) {
notification.confirm(
strings[0], // Confirm.
strings[3], // Cancel.
function() {
self._doUnlinkPlan(planData);
- }.bind(self)
+ }
);
}).fail(notification.exception);
}).fail(notification.exception);
var competencyid = $(e.target).data('id');
var requests = ajax.call([{
methodname: 'tool_lp_list_courses_using_competency',
- args: { id: competencyid }
+ args: {id: competencyid}
}]);
requests[0].done(function(courses) {
}.bind(this));
body.on('click', '[data-action="cancel"]', function() {
popup.close();
- }.bind(this));
+ });
};
/**
ScaleConfig.prototype.setScaleConfig = function() {
var body = $(this.popup.getContent());
// Get the data.
- var data = [{ scaleid: this.scaleid}];
+ var data = [{scaleid: this.scaleid}];
this.scalevalues.forEach(function(value) {
var scaledefault = 0;
var proficient = 0;
// We are chaining ajax requests here.
var requests = ajax.call([{
methodname: 'core_competency_delete_template',
- args: { id: templateid,
- deleteplans: deleteplans }
+ args: {id: templateid,
+ deleteplans: deleteplans}
}, {
methodname: 'tool_lp_data_for_templates_manage_page',
args: {
// We are chaining ajax requests here.
var requests = ajax.call([{
methodname: 'core_competency_duplicate_template',
- args: { id: templateid }
+ args: {id: templateid}
}, {
methodname: 'tool_lp_data_for_templates_manage_page',
args: {
var requests = ajax.call([{
methodname: 'core_competency_read_template',
- args: { id: templateid }
+ args: {id: templateid}
}, {
methodname: 'core_competency_template_has_related_data',
- args: { id: templateid }
+ args: {id: templateid}
}]);
requests[0].done(function(template) {
requests[1].done(function(templatehasrelateddata) {
if (templatehasrelateddata) {
str.get_strings([
- { key: 'deletetemplate', component: 'tool_lp', param: template.shortname },
- { key: 'deletetemplatewithplans', component: 'tool_lp' },
- { key: 'deleteplans', component: 'tool_lp' },
- { key: 'unlinkplanstemplate', component: 'tool_lp' },
- { key: 'confirm', component: 'moodle' },
- { key: 'cancel', component: 'moodle' }
+ {key: 'deletetemplate', component: 'tool_lp', param: template.shortname},
+ {key: 'deletetemplatewithplans', component: 'tool_lp'},
+ {key: 'deleteplans', component: 'tool_lp'},
+ {key: 'unlinkplanstemplate', component: 'tool_lp'},
+ {key: 'confirm', component: 'moodle'},
+ {key: 'cancel', component: 'moodle'}
]).done(function(strings) {
var actions = [{'text': strings[2], 'value': 'delete'},
{'text': strings[3], 'value': 'unlink'}];
}).fail(notification.exception);
} else {
str.get_strings([
- { key: 'confirm', component: 'moodle' },
- { key: 'deletetemplate', component: 'tool_lp', param: template.shortname },
- { key: 'delete', component: 'moodle' },
- { key: 'cancel', component: 'moodle' }
+ {key: 'confirm', component: 'moodle'},
+ {key: 'deletetemplate', component: 'tool_lp', param: template.shortname},
+ {key: 'delete', component: 'moodle'},
+ {key: 'cancel', component: 'moodle'}
]).done(function(strings) {
notification.confirm(
strings[0], // Confirm.
if (!this.multiSelect) {
allSelected = allSelected.first();
}
- this.treeRoot.trigger('selectionchanged', { selected: allSelected });
+ this.treeRoot.trigger('selectionchanged', {selected: allSelected});
};
/**
* @param {Event} e The event.
* @return {Boolean}
*/
+ // This function should be simplified. In the meantime..
+ // eslint-disable-next-line complexity
Tree.prototype.handleKeyDown = function(item, e) {
var currentIndex = this.visibleItems.index(item);
var newItem = null;
if (this._planId) {
this._methodName = 'tool_lp_data_for_user_competency_summary_in_plan';
- this._args = { competencyid: this._competencyId, planid: this._planId };
+ this._args = {competencyid: this._competencyId, planid: this._planId};
this._templateName = 'tool_lp/user_competency_summary_in_plan';
} else if (this._courseId) {
this._methodName = 'tool_lp_data_for_user_competency_summary_in_course';
- this._args = { userid: this._userId, competencyid: this._competencyId, courseid: this._courseId };
+ this._args = {userid: this._userId, competencyid: this._competencyId, courseid: this._courseId};
this._templateName = 'tool_lp/user_competency_summary_in_course';
} else {
this._methodName = 'tool_lp_data_for_user_competency_summary';
- this._args = { userid: this._userId, competencyid: this._competencyId };
+ this._args = {userid: this._userId, competencyid: this._competencyId};
this._templateName = 'tool_lp/user_competency_summary';
}
};
var requests = ajax.call([{
methodname: 'tool_lp_data_for_user_competency_summary_in_plan',
- args: { competencyid: competencyId, planid: planId },
+ args: {competencyid: competencyId, planid: planId},
done: this._contextLoaded.bind(this),
fail: notification.exception
}]);
ajax.call([{
methodname: 'tool_lp_data_for_plan_page',
- args: { planid: planId},
+ args: {planid: planId},
done: this._pageContextLoaded.bind(this),
fail: notification.exception
}]);
templates.render(self._template, context)
.done(function(newhtml, newjs) {
templates.replaceNode($(self._region), newhtml, newjs);
- }.bind(self))
+ })
.fail(notification.exception);
};
// Apply all the promises, and refresh when the last one is resolved.
return $.when.apply($.when, ajax.call(calls))
.then(function() {
- self._renderView.call(self, arguments[arguments.length - 1]);
+ self._renderView(arguments[arguments.length - 1]);
})
.fail(notification.exception);
};
var self = this,
calls = [{
methodname: 'core_competency_delete_user_evidence',
- args: { id: evidenceData.id }
+ args: {id: evidenceData.id}
}];
self._callAndRefresh(calls, evidenceData);
};
requests = ajax.call([{
methodname: 'core_competency_read_user_evidence',
- args: { id: evidenceData.id }
+ args: {id: evidenceData.id}
}]);
requests[0].done(function(evidence) {
str.get_strings([
- { key: 'confirm', component: 'moodle' },
- { key: 'deleteuserevidence', component: 'tool_lp', param: evidence.name },
- { key: 'delete', component: 'moodle' },
- { key: 'cancel', component: 'moodle' }
+ {key: 'confirm', component: 'moodle'},
+ {key: 'deleteuserevidence', component: 'tool_lp', param: evidence.name},
+ {key: 'delete', component: 'moodle'},
+ {key: 'cancel', component: 'moodle'}
]).done(function(strings) {
notification.confirm(
strings[0], // Confirm.
strings[3], // Cancel.
function() {
self._doDelete(evidenceData);
- }.bind(self)
+ }
);
}).fail(notification.exception);
}).fail(notification.exception);
picker.on('save', function(e, data) {
var competencyIds = data.competencyIds;
self._doCreateUserEvidenceCompetency(evidenceData, competencyIds, data.requestReview);
- }.bind(self));
+ });
picker.display();
};
var self = this,
calls = [{
methodname: 'core_competency_request_review_of_user_evidence_linked_competencies',
- args: { id: evidenceData.id }
+ args: {id: evidenceData.id}
}];
self._callAndRefresh(calls, evidenceData);
};
requests = ajax.call([{
methodname: 'core_competency_read_user_evidence',
- args: { id: evidenceData.id }
+ args: {id: evidenceData.id}
}]);
requests[0].done(function(evidence) {
str.get_strings([
- { key: 'confirm', component: 'moodle' },
- { key: 'sendallcompetenciestoreview', component: 'tool_lp', param: evidence.name },
- { key: 'confirm', component: 'moodle' },
- { key: 'cancel', component: 'moodle' }
+ {key: 'confirm', component: 'moodle'},
+ {key: 'sendallcompetenciestoreview', component: 'tool_lp', param: evidence.name},
+ {key: 'confirm', component: 'moodle'},
+ {key: 'cancel', component: 'moodle'}
]).done(function(strings) {
notification.confirm(
strings[0], // Confirm.
strings[3], // Cancel.
function() {
self._doReviewUserEvidenceCompetencies(evidenceData);
- }.bind(self)
+ }
);
}).fail(notification.exception);
}).fail(notification.exception);
list($title, $subtitle) = \tool_lp\page_helper::setup_for_course($url, $course);
$output = $PAGE->get_renderer('tool_lp');
+$page = new \tool_lp\output\course_competencies_page($course->id);
+
echo $output->header();
echo $output->heading($title);
-
-$page = new \tool_lp\output\course_competencies_page($course->id);
echo $output->render($page);
echo $output->footer();
return;
}
+ // Check access to the course and competencies page.
+ $capabilities = array('moodle/competency:coursecompetencyview', 'moodle/competency:coursecompetencymanage');
+ $context = context_course::instance($course->id);
+ if (!has_any_capability($capabilities, $context) || !can_access_course($course)) {
+ return;
+ }
+
// Just a link to course competency.
$title = get_string('competencies', 'core_competency');
$path = new moodle_url("/admin/tool/lp/coursecompetencies.php", array('courseid' => $course->id));
use core_component;
use core_plugin_manager;
+use context_system;
/**
* API exposed by tool_mobile
return $pluginsinfo;
}
+ /**
+ * Returns a list of the site public settings, those not requiring authentication.
+ *
+ * @return array with the settings and warnings
+ */
+ public static function get_site_public_settings() {
+ global $CFG, $SITE, $PAGE;
+
+ $context = context_system::instance();
+ // We need this to make work the format text functions.
+ $PAGE->set_context($context);
+
+ $settings = array(
+ 'wwwroot' => $CFG->wwwroot,
+ 'httpswwwroot' => $CFG->httpswwwroot,
+ 'sitename' => external_format_string($SITE->fullname, $context->id, true),
+ 'guestlogin' => $CFG->guestloginbutton,
+ 'rememberusername' => $CFG->rememberusername,
+ 'authloginviaemail' => $CFG->authloginviaemail,
+ 'registerauth' => $CFG->registerauth,
+ 'forgottenpasswordurl' => $CFG->forgottenpasswordurl,
+ 'authinstructions' => format_text($CFG->auth_instructions),
+ 'authnoneenabled' => (int) is_enabled_auth('none'),
+ 'enablewebservices' => $CFG->enablewebservices,
+ 'enablemobilewebservice' => $CFG->enablemobilewebservice,
+ 'maintenanceenabled' => $CFG->maintenance_enabled,
+ 'maintenancemessage' => format_text($CFG->maintenance_message),
+ );
+ return $settings;
+ }
+
}
);
}
+ /**
+ * Returns description of get_site_public_settings() parameters.
+ *
+ * @return external_function_parameters
+ * @since Moodle 3.2
+ */
+ public static function get_site_public_settings_parameters() {
+ return new external_function_parameters(array());
+ }
+
+ /**
+ * Returns a list of the site public settings, those not requiring authentication.
+ *
+ * @return array with the settings and warnings
+ * @since Moodle 3.2
+ */
+ public static function get_site_public_settings() {
+ $result = api::get_site_public_settings();
+ $result['warnings'] = array();
+ return $result;
+ }
+
+ /**
+ * Returns description of get_site_public_settings() result value.
+ *
+ * @return external_description
+ * @since Moodle 3.2
+ */
+ public static function get_site_public_settings_returns() {
+ return new external_single_structure(
+ array(
+ 'wwwroot' => new external_value(PARAM_RAW, 'Site URL.'),
+ 'httpswwwroot' => new external_value(PARAM_RAW, 'Site https URL (if httpslogin is enabled).'),
+ 'sitename' => new external_value(PARAM_TEXT, 'Site name.'),
+ 'guestlogin' => new external_value(PARAM_INT, 'Whether guest login is enabled.'),
+ 'rememberusername' => new external_value(PARAM_INT, 'Values: 0 for No, 1 for Yes, 2 for optional.'),
+ 'authloginviaemail' => new external_value(PARAM_INT, 'Whether log in via email is enabled.'),
+ 'registerauth' => new external_value(PARAM_PLUGIN, 'Authentication method for user registration.'),
+ 'forgottenpasswordurl' => new external_value(PARAM_URL, 'Forgotten password URL.'),
+ 'authinstructions' => new external_value(PARAM_RAW, 'Authentication instructions.'),
+ 'authnoneenabled' => new external_value(PARAM_INT, 'Whether auth none is enabled.'),
+ 'enablewebservices' => new external_value(PARAM_INT, 'Whether Web Services are enabled.'),
+ 'enablemobilewebservice' => new external_value(PARAM_INT, 'Whether the Mobile service is enabled.'),
+ 'maintenanceenabled' => new external_value(PARAM_INT, 'Whether site maintenance is enabled.'),
+ 'maintenancemessage' => new external_value(PARAM_RAW, 'Maintenance message.'),
+ 'warnings' => new external_warnings(),
+ )
+ );
+ }
+
}
'description' => 'Returns a list of Moodle plugins supporting the mobile app.',
'type' => 'read',
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
+ ),
+
+ 'tool_mobile_get_site_public_settings' => array(
+ 'classname' => 'tool_mobile\external',
+ 'methodname' => 'get_site_public_settings',
+ 'description' => 'Returns a list of the site public settings, those not requiring authentication.',
+ 'type' => 'read',
+ 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE),
+ 'ajax' => true,
+ 'loginrequired' => false,
)
);
$this->assertTrue(is_array($result['plugins']));
}
+ public function test_get_site_public_settings() {
+ global $CFG, $SITE;
+
+ $this->resetAfterTest(true);
+ $result = external::get_site_public_settings();
+ $result = external_api::clean_returnvalue(external::get_site_public_settings_returns(), $result);
+
+ // Test default values.
+ $context = context_system::instance();
+ $expected = array(
+ 'wwwroot' => $CFG->wwwroot,
+ 'httpswwwroot' => $CFG->httpswwwroot,
+ 'sitename' => external_format_string($SITE->fullname, $context->id, true),
+ 'guestlogin' => $CFG->guestloginbutton,
+ 'rememberusername' => $CFG->rememberusername,
+ 'authloginviaemail' => $CFG->authloginviaemail,
+ 'registerauth' => $CFG->registerauth,
+ 'forgottenpasswordurl' => $CFG->forgottenpasswordurl,
+ 'authinstructions' => format_text($CFG->auth_instructions),
+ 'authnoneenabled' => (int) is_enabled_auth('none'),
+ 'enablewebservices' => $CFG->enablewebservices,
+ 'enablemobilewebservice' => $CFG->enablemobilewebservice,
+ 'maintenanceenabled' => $CFG->maintenance_enabled,
+ 'maintenancemessage' => format_text($CFG->maintenance_message),
+ 'warnings' => array()
+ );
+ $this->assertEquals($expected, $result);
+
+ // Change a value.
+ set_config('registerauth', 'email');
+ $authinstructions = 'Something with <b>html tags</b>';
+ set_config('auth_instructions', $authinstructions);
+
+ $expected['registerauth'] = 'email';
+ $expected['authinstructions'] = format_text($authinstructions);
+
+ $result = external::get_site_public_settings();
+ $result = external_api::clean_returnvalue(external::get_site_public_settings_returns(), $result);
+ $this->assertEquals($expected, $result);
+ }
+
}
*/
defined('MOODLE_INTERNAL') || die();
-$plugin->version = 2016052300; // The current plugin version (Date: YYYYMMDDXX).
+$plugin->version = 2016052301; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2016051900; // Requires this Moodle version.
$plugin->component = 'tool_mobile'; // Full name of the plugin (used for diagnostics).
$subscriptions = subscription_manager::get_subscriptions_by_event($eventobj);
$idstosend = array();
foreach ($subscriptions as $subscription) {
+ // Only proceed to fire events and notifications if the subscription is active.
+ if (!subscription_manager::subscription_is_active($subscription)) {
+ continue;
+ }
$starttime = $now - $subscription->timewindow;
$starttime = ($starttime > $subscription->lastnotificationsent) ? $starttime : $subscription->lastnotificationsent;
if ($subscription->courseid == 0) {
* Magic get method.
*
* @param string $prop property to get.
- *
* @return mixed
* @throws \coding_exception
*/
public function __get($prop) {
- if (property_exists($this->subscription, $prop)) {
+ if (isset($this->subscription->$prop)) {
return $this->subscription->$prop;
}
throw new \coding_exception('Property "' . $prop . '" doesn\'t exist');
}
+ /**
+ * Magic isset method.
+ *
+ * @param string $prop the property to get.
+ * @return bool true if the property is set, false otherwise.
+ */
+ public function __isset($prop) {
+ return property_exists($this->subscription, $prop);
+ }
+
/**
* Get a human readable name for instances associated with this subscription.
*
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class subscription_manager {
+
+ /** @const Period of time, in days, after which an inactive subscription will be removed completely.*/
+ const INACTIVE_SUBSCRIPTION_LIFESPAN_IN_DAYS = 30;
+
/**
* Subscribe a user to a given rule.
*
return false;
}
+
+ /**
+ * Activates a group of subscriptions based on an input array of ids.
+ *
+ * @since 3.2.0
+ * @param array $ids of subscription ids.
+ * @return bool true if the operation was successful, false otherwise.
+ */
+ public static function activate_subscriptions(array $ids) {
+ global $DB;
+ if (!empty($ids)) {
+ list($sql, $params) = $DB->get_in_or_equal($ids);
+ $success = $DB->set_field_select('tool_monitor_subscriptions', 'inactivedate', '0', 'id ' . $sql, $params);
+ return $success;
+ }
+ return false;
+ }
+
+ /**
+ * Deactivates a group of subscriptions based on an input array of ids.
+ *
+ * @since 3.2.0
+ * @param array $ids of subscription ids.
+ * @return bool true if the operation was successful, false otherwise.
+ */
+ public static function deactivate_subscriptions(array $ids) {
+ global $DB;
+ if (!empty($ids)) {
+ $inactivedate = time();
+ list($sql, $params) = $DB->get_in_or_equal($ids);
+ $success = $DB->set_field_select('tool_monitor_subscriptions', 'inactivedate', $inactivedate, 'id ' . $sql,
+ $params);
+ return $success;
+ }
+ return false;
+ }
+
+ /**
+ * Deletes subscriptions which have been inactive for a period of time.
+ *
+ * @since 3.2.0
+ * @param int $userid if provided, only this user's stale subscriptions will be deleted.
+ * @return bool true if the operation was successful, false otherwise.
+ */
+ public static function delete_stale_subscriptions($userid = 0) {
+ global $DB;
+ // Get the expiry duration, in days.
+ $cutofftime = strtotime("-" . self::INACTIVE_SUBSCRIPTION_LIFESPAN_IN_DAYS . " days", time());
+
+ if (!empty($userid)) {
+ // Remove any stale subscriptions for the desired user only.
+ $success = $DB->delete_records_select('tool_monitor_subscriptions',
+ 'userid = ? AND inactivedate < ? AND inactivedate <> 0',
+ array($userid, $cutofftime));
+
+ } else {
+ // Remove all stale subscriptions.
+ $success = $DB->delete_records_select('tool_monitor_subscriptions',
+ 'inactivedate < ? AND inactivedate <> 0',
+ array($cutofftime));
+ }
+ return $success;
+ }
+
+ /**
+ * Check whether a subscription is active.
+ *
+ * @since 3.2.0
+ * @param \tool_monitor\subscription $subscription instance.
+ * @return bool true if the subscription is active, false otherwise.
+ */
+ public static function subscription_is_active(subscription $subscription) {
+ return empty($subscription->inactivedate);
+ }
}
--- /dev/null
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+namespace tool_monitor\task;
+use tool_monitor\subscription;
+use tool_monitor\subscription_manager;
+
+/**
+ * Simple task class responsible for activating, deactivating and removing subscriptions.
+ *
+ * Activation/deactivation is managed by looking at the same access rules used to determine whether a user can
+ * subscribe to the rule in the first place.
+ *
+ * Removal occurs when a subscription has been inactive for a period of time exceeding the lifespan, as set by
+ * subscription_manager::get_inactive_subscription_lifespan().
+ *
+ * I.e.
+ * - Activation: If a user can subscribe currently, then an existing subscription should be made active.
+ * - Deactivation: If a user cannot subscribe currently, then an existing subscription should be made inactive.
+ * - Removal: If a user has a subscription that has been inactive for longer than the prescribed period, then
+ * delete the subscription entirely.
+ *
+ * @since 3.2.0
+ * @package tool_monitor
+ * @copyright 2016 Jake Dallimore <jrhdallimore@gmail.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class check_subscriptions extends \core\task\scheduled_task {
+
+ /** @var array 1d static cache, indexed by userid, storing whether or not the user has been fully set up.*/
+ protected $userssetupcache = array();
+
+ /** @var array 2d static cache, indexed by courseid and userid, storing whether a user can access the course with
+ * the 'tool/monitor:subscribe' capability.
+ */
+ protected $courseaccesscache = array();
+
+ /**
+ * Get a descriptive name for this task.
+ *
+ * @since 3.2.0
+ * @return string name of the task.
+ */
+ public function get_name() {
+ return get_string('taskchecksubscriptions', 'tool_monitor');
+ }
+
+ /**
+ * Checks all course-level rule subscriptions and activates/deactivates based on current course access.
+ *
+ * The ordering of checks within the task is important for optimisation purposes. The aim is to be able to make a decision
+ * about whether to activate/deactivate each subscription without making unnecessary checks. The ordering roughly follows the
+ * context model, starting with system and user checks and moving down to course and course-module only when necessary.
+ *
+ * For example, if the user is suspended, then any active subscription is made inactive right away. I.e. there is no need to
+ * check site-level, course-level or course-module-level permissions. Likewise, if a subscriptions is site-level, there is no
+ * need to check course-level and course-module-level permissions.
+ *
+ * The task performs the following checks, in this order:
+ * 1. Check for a suspended user, breaking if suspended.
+ * 2. Check for an incomplete (not set up) user, breaking if not fully set up.
+ * 3. Check for the required capability in the relevant context, breaking if the capability is not found.
+ * 4. Check whether the subscription is site-context, breaking if true.
+ * 5. Check whether the user has course access, breaking only if the subscription is not also course-module-level.
+ * 6. Check whether the user has course-module access.
+ *
+ * @since 3.2.0
+ */
+ public function execute() {
+ global $DB;
+
+ if (!get_config('tool_monitor', 'enablemonitor')) {
+ return; // The tool is disabled. Nothing to do.
+ }
+
+ $toactivate = array(); // Store the ids of subscriptions to be activated upon completion.
+ $todeactivate = array(); // Store the ids of subscriptions to be deactivated upon completion.
+
+ // Resultset rows are ordered by userid and courseid to work nicely with get_fast_modinfo() caching.
+ $sql = "SELECT u.id AS userid, u.firstname AS userfirstname, u.lastname AS userlastname, u.suspended AS usersuspended,
+ u.email AS useremail, c.visible as coursevisible, c.cacherev as coursecacherev, s.courseid AS subcourseid,
+ s.userid AS subuserid, s.cmid AS subcmid, s.inactivedate AS subinactivedate, s.id AS subid
+ FROM {user} u
+ JOIN {tool_monitor_subscriptions} s ON (s.userid = u.id)
+ LEFT JOIN {course} c ON (c.id = s.courseid)
+ WHERE u.id = s.userid
+ ORDER BY s.userid, s.courseid";
+ $rs = $DB->get_recordset_sql($sql);
+
+ foreach ($rs as $row) {
+ // Create skeleton records from the result. This should be enough to use in subsequent access calls and avoids DB hits.
+ $sub = $this->get_subscription_from_rowdata($row);
+ $sub = new subscription($sub);
+ if (!isset($user) || $user->id != $sub->userid) {
+ $user= $this->get_user_from_rowdata($row);
+ }
+ if ((!isset($course) || $course->id != $sub->courseid) && !empty($sub->courseid)) {
+ $course = $this->get_course_from_rowdata($row);
+ }
+
+ // The user is suspended at site level, so deactivate any active subscriptions.
+ if ($user->suspended) {
+ if (subscription_manager::subscription_is_active($sub)) {
+ $todeactivate[] = $sub->id;
+ }
+ continue;
+ }
+
+ // Is the user fully set up? As per require_login on the subscriptions page.
+ if (!$this->is_user_setup($user)) {
+ if (subscription_manager::subscription_is_active($sub)) {
+ $todeactivate[] = $sub->id;
+ }
+ continue;
+ }
+
+ // Determine the context, based on the subscription course id.
+ $sitelevelsubscription = false;
+ if (empty($sub->courseid)) {
+ $context = \context_system::instance();
+ $sitelevelsubscription = true;
+ } else {
+ $context = \context_course::instance($sub->courseid);
+ }
+
+ // Check capability in the context.
+ if (!has_capability('tool/monitor:subscribe', $context, $user)) {
+ if (subscription_manager::subscription_is_active($sub)) {
+ $todeactivate[] = $sub->id;
+ }
+ continue;
+ }
+
+ // If the subscription is site-level, then we've run all the checks required to make an access decision.
+ if ($sitelevelsubscription) {
+ if (!subscription_manager::subscription_is_active($sub)) {
+ $toactivate[] = $sub->id;
+ }
+ continue;
+ }
+
+ // Check course access.
+ if (!$this->user_can_access_course($user, $course, 'tool/monitor:subscribe')) {
+ if (subscription_manager::subscription_is_active($sub)) {
+ $todeactivate[] = $sub->id;
+ }
+ continue;
+ }
+
+ // If the subscription has no course module relationship.
+ if (empty($sub->cmid)) {
+ if (!subscription_manager::subscription_is_active($sub)) {
+ $toactivate[] = $sub->id;
+ }
+ continue;
+ }
+
+ // Otherwise, check the course module info. We use the same checks as on the subscription page.
+ $modinfo = get_fast_modinfo($course, $sub->userid);
+ $cm = $modinfo->get_cm($sub->cmid);
+ if (!$cm || !$cm->uservisible || !$cm->available) {
+ if (subscription_manager::subscription_is_active($sub)) {
+ $todeactivate[] = $sub->id;
+ }
+ continue;
+ }
+
+ // The course module is available and visible, so make a decision.
+ if (!subscription_manager::subscription_is_active($sub)) {
+ $toactivate[] = $sub->id;
+ }
+ }
+ $rs->close();
+
+ // Activate/deactivate/delete relevant subscriptions.
+ subscription_manager::activate_subscriptions($toactivate);
+ subscription_manager::deactivate_subscriptions($todeactivate);
+ subscription_manager::delete_stale_subscriptions();
+ }
+
+ /**
+ * Determines whether a user is fully set up, using cached results where possible.
+ *
+ * @since 3.2.0
+ * @param \stdClass $user the user record.
+ * @return bool true if the user is fully set up, false otherwise.
+ */
+ protected function is_user_setup($user) {
+ if (!isset($this->userssetupcache[$user->id])) {
+ $this->userssetupcache[$user->id] = !user_not_fully_set_up($user);
+ }
+ return $this->userssetupcache[$user->id];
+ }
+
+ /**
+ * Determines a user's access to a course with a given capability, using cached results where possible.
+ *
+ * @since 3.2.0
+ * @param \stdClass $user the user record.
+ * @param \stdClass $course the course record.
+ * @param string $capability the capability to check.
+ * @return bool true if the user can access the course with the specified capability, false otherwise.
+ */
+ protected function user_can_access_course($user, $course, $capability) {
+ if (!isset($this->courseaccesscache[$course->id][$user->id][$capability])) {
+ $this->courseaccesscache[$course->id][$user->id][$capability] = can_access_course($course, $user, $capability, true);
+ }
+ return $this->courseaccesscache[$course->id][$user->id][$capability];
+ }
+
+ /**
+ * Returns a partial subscription record, created from properties of the supplied recordset row object.
+ * Intended to return a minimal record for specific use within this class and in subsequent access control calls only.
+ *
+ * @since 3.2.0
+ * @param \stdClass $rowdata the row object.
+ * @return \stdClass a partial subscription record.
+ */
+ protected function get_subscription_from_rowdata($rowdata) {
+ $sub = new \stdClass();
+ $sub->id = $rowdata->subid;
+ $sub->userid = $rowdata->subuserid;
+ $sub->courseid = $rowdata->subcourseid;
+ $sub->cmid = $rowdata->subcmid;
+ $sub->inactivedate = $rowdata->subinactivedate;
+ return $sub;
+ }
+
+ /**
+ * Returns a partial course record, created from properties of the supplied recordset row object.
+ * Intended to return a minimal record for specific use within this class and in subsequent access control calls only.
+ *
+ * @since 3.2.0
+ * @param \stdClass $rowdata the row object.
+ * @return \stdClass a partial course record.
+ */
+ protected function get_course_from_rowdata($rowdata) {
+ $course = new \stdClass();
+ $course->id = $rowdata->subcourseid;
+ $course->visible = $rowdata->coursevisible;
+ $course->cacherev = $rowdata->coursecacherev;
+ return $course;
+ }
+
+ /**
+ * Returns a partial user record, created from properties of the supplied recordset row object.
+ * Intended to return a minimal record for specific use within this class and in subsequent access control calls only.
+ *
+ * @since 3.2.0
+ * @param \stdClass $rowdata the row object.
+ * @return \stdClass a partial user record.
+ */
+ protected function get_user_from_rowdata($rowdata) {
+ $user = new \stdClass();
+ $user->id = $rowdata->userid;
+ $user->firstname = $rowdata->userfirstname;
+ $user->lastname = $rowdata->userlastname;
+ $user->email = $rowdata->useremail;
+ $user->suspended = $rowdata->usersuspended;
+ return $user;
+ }
+}
<FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="User id of the subscriber"/>
<FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="Timestamp of when this subscription was created"/>
<FIELD NAME="lastnotificationsent" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Timestamp of the time when a notification was last sent for this subscription."/>
+ <FIELD NAME="inactivedate" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
'day' => '*',
'dayofweek' => '*',
'month' => '*'
+ ),
+ array(
+ 'classname' => 'tool_monitor\task\check_subscriptions',
+ 'blocking' => 0,
+ 'minute' => 'R',
+ 'hour' => 'R',
+ 'day' => '*',
+ 'dayofweek' => '*',
+ 'month' => '*'
)
);
// Moodle v3.1.0 release upgrade line.
// Put any upgrade step following this.
+ if ($oldversion < 2016052305) {
+
+ // Define field inactivedate to be added to tool_monitor_subscriptions.
+ $table = new xmldb_table('tool_monitor_subscriptions');
+ $field = new xmldb_field('inactivedate', XMLDB_TYPE_INTEGER, '10', null, true, null, 0, 'lastnotificationsent');
+
+ // Conditionally launch add field inactivedate.
+ if (!$dbman->field_exists($table, $field)) {
+ $dbman->add_field($table, $field);
+ }
+
+ // Monitor savepoint reached.
+ upgrade_plugin_savepoint(true, 2016052305, 'tool', 'monitor');
+ }
+
return true;
}
$string['subhelp_help'] = 'This subscription listens for when the event \'{$a->eventname}\' has been triggered in \'{$a->moduleinstance}\' {$a->frequency} time(s) in {$a->minutes} minute(s).';
$string['subscribeto'] = 'Subscribe to rule "{$a}"';
$string['taskcleanevents'] = 'Removes any unnecessary event monitor events';
+$string['taskchecksubscriptions'] = 'Activate/deactivate invalid rule subscriptions';
$string['unsubscribe'] = 'Unsubscribe';
--- /dev/null
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+defined('MOODLE_INTERNAL') || exit();
+
+/**
+ * Unit tests for the subscription class.
+ * @since 3.2.0
+ *
+ * @package tool_monitor
+ * @category test
+ * @copyright 2016 Jake Dallimore <jrhdallimore@gmail.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class tool_monitor_subscription_testcase extends advanced_testcase {
+
+ /**
+ * @var \tool_monitor\subscription $subscription object.
+ */
+ private $subscription;
+
+ /**
+ * Test set up.
+ */
+ public function setUp() {
+ $this->resetAfterTest(true);
+
+ // Create the mock subscription.
+ $sub = new stdClass();
+ $sub->id = 100;
+ $sub->name = 'My test rule';
+ $sub->courseid = 20;
+ $this->subscription = $this->getMock('\tool_monitor\subscription',null, array($sub));
+ }
+
+ /**
+ * Test for the magic __isset method.
+ */
+ public function test_magic_isset() {
+ $this->assertEquals(true, isset($this->subscription->name));
+ $this->assertEquals(true, isset($this->subscription->courseid));
+ $this->assertEquals(false, isset($this->subscription->ruleid));
+ }
+
+ /**
+ * Test for the magic __get method.
+ */
+ public function test_magic_get() {
+ $this->assertEquals(20, $this->subscription->courseid);
+ $this->setExpectedException('coding_exception');
+ $this->subscription->ruleid;
+ }
+}
--- /dev/null
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+defined('MOODLE_INTERNAL') || exit();
+
+/**
+ * Unit tests for the tool_monitor clean events task.
+ * @since 3.2.0
+ *
+ * @package tool_monitor
+ * @category test
+ * @copyright 2016 Jake Dallimore <jrhdallimore@gmail.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class tool_monitor_task_check_subscriptions_testcase extends advanced_testcase {
+
+ private $course;
+ private $user;
+ private $rule;
+ private $subscription;
+ private $teacherrole;
+ private $studentrole;
+
+ /**
+ * Test set up.
+ */
+ public function setUp() {
+ global $DB;
+ set_config('enablemonitor', 1, 'tool_monitor');
+ $this->resetAfterTest(true);
+
+ // All tests defined herein need a user, course, rule and subscription, so set these up.
+ $this->user = $this->getDataGenerator()->create_user();
+ $this->course = $this->getDataGenerator()->create_course();
+
+ $rule = new stdClass();
+ $rule->userid = 2; // Rule created by admin.
+ $rule->courseid = $this->course->id;
+ $rule->plugin = 'mod_book';
+ $rule->eventname = '\mod_book\event\course_module_viewed';
+ $rule->timewindow = 500;
+ $monitorgenerator = $this->getDataGenerator()->get_plugin_generator('tool_monitor');
+ $this->rule = $monitorgenerator->create_rule($rule);
+
+ $sub = new stdClass();
+ $sub->courseid = $this->course->id;
+ $sub->userid = $this->user->id;
+ $sub->ruleid = $this->rule->id;
+ $this->subscription = $monitorgenerator->create_subscription($sub);
+
+ // Also set up a student and a teacher role for use in some tests.
+ $this->teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
+ $this->studentrole = $DB->get_record('role', array('shortname' => 'student'));
+ }
+
+ /**
+ * Reloads the subscription object from the DB.
+ *
+ * @return void.
+ */
+ private function reload_subscription() {
+ global $DB;
+ $sub = $DB->get_record('tool_monitor_subscriptions', array('id' => $this->subscription->id));
+ $this->subscription = new \tool_monitor\subscription($sub);
+ }
+
+ /**
+ * Test to confirm the task is named correctly.
+ */
+ public function test_task_name() {
+ $task = new \tool_monitor\task\check_subscriptions();
+ $this->assertEquals(get_string('taskchecksubscriptions', 'tool_monitor'), $task->get_name());
+ }
+
+ /**
+ * Test to confirm that site level subscriptions are activated and deactivated according to system capabilities.
+ */
+ public function test_site_level_subscription() {
+ // Create a site level subscription.
+ $monitorgenerator = $this->getDataGenerator()->get_plugin_generator('tool_monitor');
+ $sub = new stdClass();
+ $sub->userid = $this->user->id;
+ $sub->ruleid = $this->rule->id;
+ $this->subscription = $monitorgenerator->create_subscription($sub);
+
+ // Run the task.
+ $task = new \tool_monitor\task\check_subscriptions();
+ $task->execute();
+
+ // The subscription should be inactive as the user doesn't have the capability. Pass in the id only to refetch the data.
+ $this->reload_subscription();
+ $this->assertEquals(false, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
+
+ // Now, assign the user as a teacher role at system context.
+ $this->getDataGenerator()->role_assign($this->teacherrole->id, $this->user->id, context_system::instance());
+
+ // Run the task.
+ $task = new \tool_monitor\task\check_subscriptions();
+ $task->execute();
+
+ // The subscription should be active now. Pass in the id only to refetch the data.
+ $this->reload_subscription();
+ $this->assertEquals(true, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
+ }
+
+ /**
+ * Test to confirm that if the module is disabled, no changes are made to active subscriptions.
+ */
+ public function test_module_disabled() {
+ set_config('enablemonitor', 0, 'tool_monitor');
+
+ // Subscription should be active to start with.
+ $this->assertEquals(true, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
+
+ // Run the task. Note, we never enrolled the user.
+ $task = new \tool_monitor\task\check_subscriptions();
+ $task->execute();
+
+ // The subscription should still be active. Pass in the id only to refetch the data.
+ $this->reload_subscription();
+ $this->assertEquals(true, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
+ }
+
+ /**
+ * Test to confirm an active, valid subscription stays active once the scheduled task is run.
+ */
+ public function test_active_unaffected() {
+ // Enrol the user as a teacher. This role should have the required capability.
+ $this->getDataGenerator()->enrol_user($this->user->id, $this->course->id, $this->teacherrole->id);
+
+ // Subscription should be active to start with.
+ $this->assertEquals(true, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
+
+ // Run the task.
+ $task = new \tool_monitor\task\check_subscriptions();
+ $task->execute();
+
+ // The subscription should still be active. Pass in the id only to refetch the data.
+ $this->reload_subscription();
+ $this->assertEquals(true, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
+ }
+
+ /**
+ * Test to confirm that a subscription for a user without an enrolment to the course is made inactive.
+ */
+ public function test_course_enrolment() {
+ // Subscription should be active until deactivated by the scheduled task. Remember, by default the test setup
+ // doesn't enrol the user, so the first run of the task should deactivate it.
+ $this->assertEquals(true, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
+
+ // Run the task.
+ $task = new \tool_monitor\task\check_subscriptions();
+ $task->execute();
+
+ // The subscription should NOT be active. Pass in the id only to refetch the data.
+ $this->reload_subscription();
+ $this->assertEquals(false, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
+
+ // Enrol the user.
+ $this->getDataGenerator()->enrol_user($this->user->id, $this->course->id, $this->teacherrole->id);
+
+ // Run the task.
+ $task = new \tool_monitor\task\check_subscriptions();
+ $task->execute();
+
+ // Subscription should now be active again.
+ $this->reload_subscription();
+ $this->assertEquals(true, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
+ }
+
+ /**
+ * Test to confirm that subscriptions for enrolled users without the required capability are made inactive.
+ */
+ public function test_enrolled_user_with_no_capability() {
+ // Enrol the user. By default, students won't have the required capability.
+ $this->getDataGenerator()->enrol_user($this->user->id, $this->course->id, $this->studentrole->id);
+
+ // The subscription should be active to start with. Pass in the id only to refetch the data.
+ $this->assertEquals(true, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
+
+ // Run the task.
+ $task = new \tool_monitor\task\check_subscriptions();
+ $task->execute();
+
+ // The subscription should NOT be active. Pass in the id only to refetch the data.
+ $this->reload_subscription();
+ $this->assertEquals(false, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
+ }
+
+ /**
+ * Test to confirm that subscriptions for users who fail can_access_course(), are deactivated.
+ */
+ public function test_can_access_course() {
+ // Enrol the user as a teacher. This role should have the required capability.
+ $this->getDataGenerator()->enrol_user($this->user->id, $this->course->id, $this->teacherrole->id);
+
+ // Strip the ability to see hidden courses, so we'll fail the check_subscriptions->user_can_access_course call.
+ $context = \context_course::instance($this->course->id);
+ assign_capability('moodle/course:viewhiddencourses', CAP_PROHIBIT, $this->teacherrole->id, $context);
+
+ // Subscription should be active to start with.
+ $this->assertEquals(true, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
+
+ // Hide the course.
+ course_change_visibility($this->course->id, false);
+
+ // Run the task.
+ $task = new \tool_monitor\task\check_subscriptions();
+ $task->execute();
+
+ // The subscription should be inactive. Pass in the id only to refetch the data.
+ $this->reload_subscription();
+ $this->assertEquals(false, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
+ }
+
+ /**
+ * Test to confirm that subscriptions for enrolled users who don't have CM access, are deactivated.
+ */
+ public function test_cm_access() {
+ // Enrol the user as a student but grant to ability to subscribe. Students cannot view hidden activities.
+ $context = \context_course::instance($this->course->id);
+ assign_capability('tool/monitor:subscribe', CAP_ALLOW, $this->studentrole->id, $context);
+ $this->getDataGenerator()->enrol_user($this->user->id, $this->course->id, $this->studentrole->id);
+
+ // Generate a course module.
+ $book = $this->getDataGenerator()->create_module('book', array('course' => $this->course->id));
+
+ // And add a subscription to it.
+ $sub = new stdClass();
+ $sub->courseid = $this->course->id;
+ $sub->userid = $this->user->id;
+ $sub->ruleid = $this->rule->id;
+ $sub->cmid = $book->cmid;
+ $monitorgenerator = $this->getDataGenerator()->get_plugin_generator('tool_monitor');
+ $this->subscription = $monitorgenerator->create_subscription($sub);
+
+ // The subscription should be active to start with. Pass in the id only to refetch the data.
+ $this->assertEquals(true, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
+
+ // Run the task.
+ $task = new \tool_monitor\task\check_subscriptions();
+ $task->execute();
+
+ // The subscription should still be active. Pass in the id only to refetch the data.
+ $this->reload_subscription();
+ $this->assertEquals(true, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
+
+ // Make the course module invisible, which should in turn make the subscription inactive.
+ set_coursemodule_visible($book->cmid, false);
+
+ // Run the task.
+ $task = new \tool_monitor\task\check_subscriptions();
+ $task->execute();
+
+ // The subscription should NOT be active. Pass in the id only to refetch the data.
+ $this->reload_subscription();
+ $this->assertEquals(false, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
+
+ // Make the course module visible again.
+ set_coursemodule_visible($book->cmid, true);
+
+ // Run the task.
+ $task = new \tool_monitor\task\check_subscriptions();
+ $task->execute();
+
+ // The subscription should be active. Pass in the id only to refetch the data.
+ $this->reload_subscription();
+ $this->assertEquals(true, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
+ }
+
+ /**
+ * Test to confirm that long term inactive subscriptions are removed entirely.
+ */
+ public function test_stale_subscription_removal() {
+ global $DB;
+ // Manually set the inactivedate to 1 day older than the limit allowed.
+ $daysold = 1 + \tool_monitor\subscription_manager::INACTIVE_SUBSCRIPTION_LIFESPAN_IN_DAYS;
+
+ $inactivedate = strtotime("-$daysold days", time());
+ $DB->set_field('tool_monitor_subscriptions', 'inactivedate', $inactivedate, array('id' => $this->subscription->id));
+
+ // Subscription should be inactive to start with.
+ $this->reload_subscription();
+ $this->assertEquals(false, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
+
+ // Run the task.
+ $task = new \tool_monitor\task\check_subscriptions();
+ $task->execute();
+
+ // Subscription should now not exist at all.
+ $this->assertEquals(false, $DB->record_exists('tool_monitor_subscriptions', array('id' => $this->subscription->id)));
+ }
+
+ /**
+ * Test to confirm that subscriptions for a partially set up user are deactivated.
+ */
+ public function test_user_not_fully_set_up() {
+ global $DB;
+
+ // Enrol the user as a teacher.
+ $this->getDataGenerator()->enrol_user($this->user->id, $this->course->id, $this->teacherrole->id);
+
+ // The subscription should be active to start.
+ $this->assertEquals(true, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
+
+ // Unset the user's email address, so we fail the check_subscriptions->is_user_setup() call.
+ $DB->set_field('user', 'email', '', array('id' => $this->user->id));
+
+ // Run the task.
+ $task = new \tool_monitor\task\check_subscriptions();
+ $task->execute();
+
+ // The subscription should now be inactive.
+ $this->reload_subscription();
+ $this->assertEquals(false, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
+ }
+
+ /**
+ * Test to confirm that a suspended user's subscriptions are deactivated properly.
+ */
+ public function test_suspended_user() {
+ global $DB;
+
+ // Enrol the user as a teacher. This role should have the required capability.
+ $this->getDataGenerator()->enrol_user($this->user->id, $this->course->id, $this->teacherrole->id);
+
+ // Subscription should be active to start with.
+ $this->assertEquals(true, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
+
+ // Suspend the user.
+ $DB->set_field('user', 'suspended', '1', array('id' => $this->user->id));
+
+ // Run the task.
+ $task = new \tool_monitor\task\check_subscriptions();
+ $task->execute();
+
+ // The subscription should now be inactive.
+ $this->reload_subscription();
+ $this->assertEquals(false, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
+
+ // Unsuspend the user.
+ $DB->set_field('user', 'suspended', '0', array('id' => $this->user->id));
+
+ // Run the task.
+ $task = new \tool_monitor\task\check_subscriptions();
+ $task->execute();
+
+ // The subscription should now be active again.
+ $this->reload_subscription();
+ $this->assertEquals(true, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
+ }
+}
defined('MOODLE_INTERNAL') || die;
-$plugin->version = 2016052300; // The current plugin version (Date: YYYYMMDDXX).
+$plugin->version = 2016052305; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2016051900; // Requires this Moodle version.
$plugin->component = 'tool_monitor'; // Full name of the plugin (used for diagnostics).
* @param {String[]} templateList List of template ids.
*/
var reloadListTemplate = function(templateList) {
- templates.render('tool_templatelibrary/search_results', { templates: templateList })
+ templates.render('tool_templatelibrary/search_results', {templates: templateList})
.done(function(result, js) {
templates.replaceNode($('[data-region="searchresults"]'), result, js);
}).fail(notification.exception);
// Trigger the search.
ajax.call([
- { methodname: 'tool_templatelibrary_list_templates',
- args: { component: componentStr, search: searchStr },
+ {methodname: 'tool_templatelibrary_list_templates',
+ args: {component: componentStr, search: searchStr},
done: reloadListTemplate,
- fail: notification.exception }
+ fail: notification.exception}
], true, false);
};
$o.= ' <input type="hidden" name ="name" value="' . s($field->getName()) .'" />';
$o.= ' <tr valign="top"><td>Name:</td><td colspan="2">' . s($field->getName()) . '</td></tr>';
} else {
- $o.= ' <tr valign="top"><td><label for="name" accesskey="n">Name:</label></td><td colspan="2"><input name="name" type="text" size="30" maxlength="30" id="name" value="' . s($field->getName()) . '" /></td></tr>';
+ $o.= ' <tr valign="top"><td><label for="name" accesskey="n">Name:</label></td><td colspan="2"><input name="name" type="text" size="'.xmldb_field::NAME_MAX_LENGTH.'" maxlength="'.xmldb_field::NAME_MAX_LENGTH.'" id="name" value="' . s($field->getName()) . '" /></td></tr>';
}
// XMLDB field comment
$o.= ' <tr valign="top"><td><label for="comment" accesskey="c">Comment:</label></td><td colspan="2"><textarea name="comment" rows="3" cols="80" id="comment">' . s($field->getComment()) . '</textarea></td></tr>';
if ($structure->getIndexUses($table->getName(), $index->getName())) {
$disabled = ' disabled="disabled " ';
}
- $o.= ' <tr valign="top"><td><label for="name" accesskey="n">Name:</label></td><td colspan="2"><input name="name" type="text" size="30" id="name"' . $disabled . ' value="' . s($index->getName()) . '" /></td></tr>';
+ $o.= ' <tr valign="top"><td><label for="name" accesskey="n">Name:</label></td><td colspan="2"><input name="name" type="text" size="'.xmldb_field::NAME_MAX_LENGTH.'" id="name"' . $disabled . ' value="' . s($index->getName()) . '" /></td></tr>';
// XMLDB key comment
$o.= ' <tr valign="top"><td><label for="comment" accesskey="c">Comment:</label></td><td colspan="2"><textarea name="comment" rows="3" cols="80" id="comment">' . s($index->getComment()) . '</textarea></td></tr>';
// xmldb_index Type
if ($structure->getKeyUses($table->getName(), $key->getName())) {
$disabled = ' disabled="disabled " ';
}
- $o.= ' <tr valign="top"><td><label for="name" accesskey="n">Name:</label></td><td colspan="2"><input name="name" type="text" size="30" id="name"' . $disabled . ' value="' . s($key->getName()) . '" /></td></tr>';
+ $o.= ' <tr valign="top"><td><label for="name" accesskey="n">Name:</label></td><td colspan="2"><input name="name" type="text" size="'.xmldb_field::NAME_MAX_LENGTH.'" id="name"' . $disabled . ' value="' . s($key->getName()) . '" /></td></tr>';
// XMLDB key comment
$o.= ' <tr valign="top"><td><label for="comment" accesskey="c">Comment:</label></td><td colspan="2"><textarea name="comment" rows="3" cols="80" id="comment">' . s($key->getComment()) . '</textarea></td></tr>';
// xmldb_key Type
if ($structure->getTableUses($table->getName())) {
$o.= ' <tr valign="top"><td>Name:</td><td><input type="hidden" name ="name" value="' . s($table->getName()) . '" />' . s($table->getName()) .'</td></tr>';
} else {
- $o.= ' <tr valign="top"><td><label for="name" accesskey="p">Name:</label></td><td><input name="name" type="text" size="28" maxlength="28" id="name" value="' . s($table->getName()) . '" /></td></tr>';
+ $o.= ' <tr valign="top"><td><label for="name" accesskey="p">Name:</label></td><td><input name="name" type="text" size="'.xmldb_table::NAME_MAX_LENGTH.'" maxlength="'.xmldb_table::NAME_MAX_LENGTH.'" id="name" value="' . s($table->getName()) . '" /></td></tr>';
}
$o.= ' <tr valign="top"><td><label for="comment" accesskey="c">Comment:</label></td><td><textarea name="comment" rows="3" cols="80" id="comment">' . s($table->getComment()) . '</textarea></td></tr>';
$o.= ' <tr valign="top"><td> </td><td><input type="submit" value="' .$this->str['change'] . '" /></td></tr>';
// Find obsolete users.
if (count($userlist)) {
- list($notin_sql, $params) = $DB->get_in_or_equal($userlist, SQL_PARAMS_NAMED, 'u', false);
- $params['authtype'] = $this->authtype;
- $sql = "SELECT u.*
+ $remove_users = array();
+ // All the drivers can cope with chunks of 10,000. See line 4491 of lib/dml/tests/dml_est.php
+ $userlistchunks = array_chunk($userlist , 10000);
+ foreach($userlistchunks as $userlistchunk) {
+ list($notin_sql, $params) = $DB->get_in_or_equal($userlistchunk, SQL_PARAMS_NAMED, 'u', false);
+ $params['authtype'] = $this->authtype;
+ $sql = "SELECT u.id, u.username
FROM {user} u
WHERE u.auth=:authtype AND u.deleted=0 AND u.mnethostid=:mnethostid $suspendselect AND u.username $notin_sql";
+ $params['mnethostid'] = $CFG->mnet_localhost_id;
+ $remove_users = $remove_users + $DB->get_records_sql($sql, $params);
+ }
} else {
- $sql = "SELECT u.*
+ $sql = "SELECT u.id, u.username
FROM {user} u
WHERE u.auth=:authtype AND u.deleted=0 AND u.mnethostid=:mnethostid $suspendselect";
$params = array();
$params['authtype'] = $this->authtype;
+ $params['mnethostid'] = $CFG->mnet_localhost_id;
+ $remove_users = $DB->get_records_sql($sql, $params);
}
- $params['mnethostid'] = $CFG->mnet_localhost_id;
- $remove_users = $DB->get_records_sql($sql, $params);
if (!empty($remove_users)) {
$trace->output(get_string('auth_dbuserstoremove','auth_db', count($remove_users)));
// Only go ahead if we actually have fields to update locally.
if (!empty($updatekeys)) {
- list($in_sql, $params) = $DB->get_in_or_equal($userlist, SQL_PARAMS_NAMED, 'u', true);
- $params['authtype'] = $this->authtype;
- $sql = "SELECT u.id, u.username
+ $update_users = array();
+ // All the drivers can cope with chunks of 10,000. See line 4491 of lib/dml/tests/dml_est.php
+ $userlistchunks = array_chunk($userlist , 10000);
+ foreach($userlistchunks as $userlistchunk) {
+ list($in_sql, $params) = $DB->get_in_or_equal($userlistchunk, SQL_PARAMS_NAMED, 'u', true);
+ $params['authtype'] = $this->authtype;
+ $params['mnethostid'] = $CFG->mnet_localhost_id;
+ $sql = "SELECT u.id, u.username
FROM {user} u
- WHERE u.auth=:authtype AND u.deleted=0 AND u.username {$in_sql}";
- if ($update_users = $DB->get_records_sql($sql, $params)) {
+ WHERE u.auth = :authtype AND u.deleted = 0 AND u.mnethostid = :mnethostid AND u.username {$in_sql}";
+ $update_users = $update_users + $DB->get_records_sql($sql, $params);
+ }
+
+ if ($update_users) {
$trace->output("User entries to update: ".count($update_users));
foreach ($update_users as $user) {
$this->title = get_string('pluginname', 'block_activity_results');
}
+ /**
+ * Allow the block to have a configuration page
+ *
+ * @return boolean
+ */
+ public function has_config() {
+ return true;
+ }
+
/**
* Core function, specifies where the block can be used.
* @return array
protected function specific_definition($mform) {
global $DB;
+ // Load defaults.
+ $blockconfig = get_config('block_activity_results');
+
// Fields for editing activity_results block title and contents.
$mform->addElement('header', 'configheader', get_string('blocksettings', 'block'));
$mform->addElement('text', 'config_showbest',
get_string('config_show_best', 'block_activity_results'), array('size' => 3));
- $mform->setDefault('config_showbest', 3);
+ $mform->setDefault('config_showbest', $blockconfig->config_showbest);
$mform->setType('config_showbest', PARAM_INT);
+ if ($blockconfig->config_showbest_locked) {
+ $mform->freeze('config_showbest');
+ }
$mform->addElement('text', 'config_showworst',
get_string('config_show_worst', 'block_activity_results'), array('size' => 3));
- $mform->setDefault('config_showworst', 0);
+ $mform->setDefault('config_showworst', $blockconfig->config_showworst);
$mform->setType('config_showworst', PARAM_INT);
+ if ($blockconfig->config_showworst_locked) {
+ $mform->freeze('config_showworst');
+ }
$mform->addElement('selectyesno', 'config_usegroups', get_string('config_use_groups', 'block_activity_results'));
+ $mform->setDefault('config_usegroups', $blockconfig->config_usegroups);
+ if ($blockconfig->config_usegroups_locked) {
+ $mform->freeze('config_usegroups');
+ }
$nameoptions = array(
B_ACTIVITYRESULTS_NAME_FORMAT_FULL => get_string('config_names_full', 'block_activity_results'),
);
$mform->addElement('select', 'config_nameformat',
get_string('config_name_format', 'block_activity_results'), $nameoptions);
- $mform->setDefault('config_nameformat', B_ACTIVITYRESULTS_NAME_FORMAT_FULL);
+ $mform->setDefault('config_nameformat', $blockconfig->config_nameformat);
+ if ($blockconfig->config_nameformat_locked) {
+ $mform->freeze('config_nameformat');
+ }
$gradeeoptions = array(
B_ACTIVITYRESULTS_GRADE_FORMAT_PCT => get_string('config_format_percentage', 'block_activity_results'),
);
$mform->addElement('select', 'config_gradeformat',
get_string('config_grade_format', 'block_activity_results'), $gradeeoptions);
- $mform->setDefault('config_gradeformat', B_ACTIVITYRESULTS_GRADE_FORMAT_PCT);
+ $mform->setDefault('config_gradeformat', $blockconfig->config_gradeformat);
+ if ($blockconfig->config_gradeformat_locked) {
+ $mform->freeze('config_gradeformat');
+ }
$options = array();
for ($i = 0; $i <= 5; $i++) {
}
$mform->addElement('select', 'config_decimalpoints', get_string('config_decimalplaces', 'block_activity_results'),
$options);
- $mform->setDefault('config_decimalpoints', 2);
+ $mform->setDefault('config_decimalpoints', $blockconfig->config_decimalpoints);
$mform->setType('config_decimalpoints', PARAM_INT);
+ if ($blockconfig->config_decimalpoints_locked) {
+ $mform->freeze('config_decimalpoints');
+ }
}
}
\ No newline at end of file
$string['config_show_worst'] = 'How many of the lowest grades should be shown (0 to disable)?';
$string['configuredtoshownothing'] = 'This block\'s configuration currently does not allow it to show any results.';
$string['config_use_groups'] = 'Show groups instead of students (only if the activity supports groups)?';
+$string['defaulthighestgrades'] = 'Default highest grades shown';
+$string['defaulthighestgrades_desc'] = 'How many of the highest grades should be shown by default?';
+$string['defaultlowestgrades'] = 'Default lowest grades shown';
+$string['defaultlowestgrades_desc'] = 'How many of the lowest grades should be shown by default?';
+$string['defaultshowgroups'] = 'Default show groups';
+$string['defaultnameoptions'] = 'Privacy of results';
+$string['defaultnameoptions_desc'] = 'How should the students be identified by default?';
+$string['defaultshowgroups_desc'] = 'Show groups instead of students by default (only if the activity supports groups)';
+$string['defaultgradedisplay'] = 'Display grades as';
+$string['defaultgradedisplay_desc'] = 'How should the grades be displayed by default?';
+$string['defaultdecimalplaces'] = 'Decimal places';
+$string['defaultdecimalplaces_desc'] = 'Number of decimal places to display by default';
$string['error_emptyactivityid'] = 'Please configure this block and select which activity it should display results from.';
$string['error_emptyactivityrecord'] = 'Error: the selected activity does not exist in the database.';
$string['error_nogroupsexist'] = 'Error: the block is set to display grades in group mode, but there are no groups defined.';
--- /dev/null
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Defines the form for editing activity results block instances.
+ *
+ * @package block_activity_results
+ * @copyright 2016 Stephen Bourget
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die;
+
+if ($ADMIN->fulltree) {
+
+ // Default high scores.
+ $setting = new admin_setting_configtext('block_activity_results/config_showbest',
+ new lang_string('defaulthighestgrades', 'block_activity_results'),
+ new lang_string('defaulthighestgrades_desc', 'block_activity_results'), 3, PARAM_INT);
+ $setting->set_locked_flag_options(admin_setting_flag::ENABLED, false);
+ $settings->add($setting);
+
+ // Default low scores.
+ $setting = new admin_setting_configtext('block_activity_results/config_showworst',
+ new lang_string('defaultlowestgrades', 'block_activity_results'),
+ new lang_string('defaultlowestgrades_desc', 'block_activity_results'), 0, PARAM_INT);
+ $setting->set_locked_flag_options(admin_setting_flag::ENABLED, false);
+ $settings->add($setting);
+
+ // Default group display.
+ $yesno = array(0 => get_string('no'), 1 => get_string('yes'));
+ $setting = new admin_setting_configselect('block_activity_results/config_usegroups',
+ new lang_string('defaultshowgroups', 'block_activity_results'),
+ new lang_string('defaultshowgroups_desc', 'block_activity_results'), 0, $yesno);
+ $setting->set_locked_flag_options(admin_setting_flag::ENABLED, false);
+ $settings->add($setting);
+
+ // Default privacy settings.
+ $nameoptions = array(
+ B_ACTIVITYRESULTS_NAME_FORMAT_FULL => get_string('config_names_full', 'block_activity_results'),
+ B_ACTIVITYRESULTS_NAME_FORMAT_ID => get_string('config_names_id', 'block_activity_results'),
+ B_ACTIVITYRESULTS_NAME_FORMAT_ANON => get_string('config_names_anon', 'block_activity_results')
+ );
+ $setting = new admin_setting_configselect('block_activity_results/config_nameformat',
+ new lang_string('defaultnameoptions', 'block_activity_results'),
+ new lang_string('defaultnameoptions_desc', 'block_activity_results'), B_ACTIVITYRESULTS_NAME_FORMAT_FULL, $nameoptions);
+ $setting->set_locked_flag_options(admin_setting_flag::ENABLED, false);
+ $settings->add($setting);
+
+ // Default grade display settings.
+ $gradeoptions = array(
+ B_ACTIVITYRESULTS_GRADE_FORMAT_PCT => get_string('config_format_percentage', 'block_activity_results'),
+ B_ACTIVITYRESULTS_GRADE_FORMAT_FRA => get_string('config_format_fraction', 'block_activity_results'),
+ B_ACTIVITYRESULTS_GRADE_FORMAT_ABS => get_string('config_format_absolute', 'block_activity_results')
+ );
+ $setting = new admin_setting_configselect('block_activity_results/config_gradeformat',