php:
# We only run the highest and lowest supported versions to reduce the load on travis-ci.org.
- 7.3
- - 7.1.30 # Make this sticky because current default version (7.1.11) has a bug with redis-extension output (MDL-66062)
+ - 7.2
addons:
postgresql: "9.6"
default:
break;
}
+$new = implode(',', $activeantiviruses);
+add_to_config_log('antiviruses', $CFG->antiviruses, $new, 'core');
+set_config('antiviruses', $new);
-set_config('antiviruses', implode(',', $activeantiviruses));
core_plugin_manager::reset_caches();
-redirect ($returnurl);
\ No newline at end of file
+redirect ($returnurl);
$return = optional_param('return','', PARAM_ALPHA);
$adminediting = optional_param('adminedit', -1, PARAM_BOOL);
-require_admin();
+require_login(0, false);
$PAGE->set_context(context_system::instance());
$PAGE->set_url('/admin/category.php', array('category' => $category));
$PAGE->set_pagetype('admin-setting-' . $category);
require_once($CFG->libdir.'/cronlib.php');
// now get cli options
-list($options, $unrecognized) = cli_get_params(array('help'=>false),
- array('h'=>'help'));
+list($options, $unrecognized) = cli_get_params(
+ array(
+ 'help' => false,
+ 'stop' => false,
+ ),
+ array(
+ 'h' => 'help',
+ 's' => 'stop',
+ )
+);
if ($unrecognized) {
$unrecognized = implode("\n ", $unrecognized);
Options:
-h, --help Print out this help
+-s, --stop Notify all other running cron processes to stop after the current task
Example:
\$sudo -u www-data /usr/bin/php admin/cli/cron.php
die;
}
+if ($options['stop']) {
+ // By clearing the caches this signals to other running processes
+ // to exit after finishing the current task.
+ \core\task\manager::clear_static_caches();
+ die;
+}
+
cron_run();
</CUSTOM_CHECK>
</CUSTOM_CHECKS>
</MOODLE>
+ <MOODLE version="3.9" requires="3.5">
+ <UNICODE level="required">
+ <FEEDBACK>
+ <ON_ERROR message="unicoderequired" />
+ </FEEDBACK>
+ </UNICODE>
+ <DATABASE level="required">
+ <VENDOR name="mariadb" version="10.2.29" />
+ <VENDOR name="mysql" version="5.6" />
+ <VENDOR name="postgres" version="9.5" />
+ <VENDOR name="mssql" version="11.0" />
+ <VENDOR name="oracle" version="11.2" />
+ </DATABASE>
+ <PHP version="7.2.0" 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="required">
+ <FEEDBACK>
+ <ON_ERROR message="mbstringrequired" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="curl" level="required">
+ <FEEDBACK>
+ <ON_ERROR message="curlrequired" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="openssl" level="required">
+ <FEEDBACK>
+ <ON_ERROR message="opensslrequired" />
+ </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="required">
+ <FEEDBACK>
+ <ON_ERROR message="intlrequired" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="json" level="required">
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="hash" level="required"/>
+ <PHP_EXTENSION name="fileinfo" 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_CHECK file="lib/upgradelib.php" function="check_libcurl_version" level="optional">
+ <FEEDBACK>
+ <ON_CHECK message="libcurlwarning" />
+ </FEEDBACK>
+ </CUSTOM_CHECK>
+ <CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_file_format" level="required">
+ <FEEDBACK>
+ <ON_ERROR message="unsupporteddbfileformat" />
+ </FEEDBACK>
+ </CUSTOM_CHECK>
+ <CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_file_per_table" level="required">
+ <FEEDBACK>
+ <ON_ERROR message="unsupporteddbfilepertable" />
+ </FEEDBACK>
+ </CUSTOM_CHECK>
+ <CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_large_prefix" level="required">
+ <FEEDBACK>
+ <ON_ERROR message="unsupporteddblargeprefix" />
+ </FEEDBACK>
+ </CUSTOM_CHECK>
+ <CUSTOM_CHECK file="lib/upgradelib.php" function="check_is_https" level="optional">
+ <FEEDBACK>
+ <ON_CHECK message="ishttpswarning" />
+ </FEEDBACK>
+ </CUSTOM_CHECK>
+ <CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_incomplete_unicode_support" level="optional">
+ <FEEDBACK>
+ <ON_CHECK message="incompleteunicodesupport" />
+ </FEEDBACK>
+ </CUSTOM_CHECK>
+ <CUSTOM_CHECK file="lib/upgradelib.php" function="check_sixtyfour_bits" level="optional">
+ <FEEDBACK>
+ <ON_CHECK message="sixtyfourbitswarning" />
+ </FEEDBACK>
+ </CUSTOM_CHECK>
+ </CUSTOM_CHECKS>
+ </MOODLE>
</COMPATIBILITY_MATRIX>
$processors = array_filter($allprocessors, function($processor) {
return $processor->enabled;
});
+$disabledprocessors = array_filter($allprocessors, function($processor) {
+ return !$processor->enabled;
+});
+
// Fetch message providers.
$providers = get_message_providers();
// Fetch the manage message outputs interface.
$preferences = get_message_output_default_preferences();
if (($form = data_submitted()) && confirm_sesskey()) {
- $preferences = array();
+ $newpreferences = array();
// Prepare default message outputs settings.
foreach ($providers as $provider) {
$componentproviderbase = $provider->component.'_'.$provider->name;
$providerdisabled = false;
if (!isset($form->$disableprovidersetting)) {
$providerdisabled = true;
- $preferences[$disableprovidersetting] = 1;
+ $newpreferences[$disableprovidersetting] = 1;
} else {
- $preferences[$disableprovidersetting] = 0;
+ $newpreferences[$disableprovidersetting] = 0;
}
foreach (array('permitted', 'loggedin', 'loggedoff') as $setting) {
$form->{$componentproviderbase.'_loggedoff'}[$processor->name] = 1;
}
// Record the site preference.
- $preferences[$processor->name.'_provider_'.$componentprovidersetting] = $value;
+ $newpreferences[$processor->name.'_provider_'.$componentprovidersetting] = $value;
}
- } else if (property_exists($form, $componentprovidersetting)) {
- // We must be processing loggedin or loggedoff checkboxes. Store
- // defained comma-separated processors as setting value.
- // Using array_filter eliminates elements set to 0 above.
- $value = join(',', array_keys(array_filter($form->{$componentprovidersetting})));
+ } else {
+ $newsettings = array();
+ if (array_key_exists($componentprovidersetting, $form)) {
+ // We must be processing loggedin or loggedoff checkboxes.
+ // Store defained comma-separated processors as setting value.
+ // Using array_filter eliminates elements set to 0 above.
+ $newsettings = array_keys(array_filter($form->{$componentprovidersetting}));
+ }
+
+ // Let's join existing setting values for disabled processors.
+ $property = 'message_provider_'.$componentprovidersetting;
+ if (property_exists($preferences, $property)) {
+ $existingsetting = $preferences->$property;
+ foreach ($disabledprocessors as $disable) {
+ if (strpos($existingsetting, $disable->name) > -1) {
+ $newsettings[] = $disable->name;
+ }
+ }
+ }
+
+ $value = join(',', $newsettings);
if (empty($value)) {
$value = null;
}
}
if ($setting != 'permitted') {
// We have already recoded site preferences for 'permitted' type.
- $preferences['message_provider_'.$componentprovidersetting] = $value;
+ $newpreferences['message_provider_'.$componentprovidersetting] = $value;
}
}
}
\core_message\api::update_processor_status($processor, $enabled);
}
- foreach ($preferences as $name => $value) {
+ foreach ($newpreferences as $name => $value) {
set_config($name, $value, 'message');
}
$transaction->allow_commit();
if ($mnet_peer && !empty($mnet_peer->deleted)) {
$radioarray = array();
$radioarray[] = $mform->createElement('static', 'deletedinfo', '',
- $OUTPUT->container(get_string('deletedhostinfo', 'mnet'), 'deletedhostinfo'));
+ $OUTPUT->container(get_string('deletedhostinfo', 'mnet'), 'alert alert-warning'));
$radioarray[] = $mform->createElement('radio', 'deleted', '', get_string('yes'), 1);
$radioarray[] = $mform->createElement('radio', 'deleted', '', get_string('no'), 0);
$mform->addGroup($radioarray, 'radioar', get_string('deleted'), array(' ', ' '), false);
if ($version) {
$row[] = $version;
} else {
- $row[] = html_writer::tag('span', get_string('nodatabase', 'admin'), array('class' => 'disabled'));
+ $row[] = html_writer::tag('span', get_string('nodatabase', 'admin'), array('class' => 'text-muted'));
}
// Other question types required by this one.
if ($version) {
$row[] = $version;
} else {
- $row[] = html_writer::tag('span', get_string('nodatabase', 'admin'), array('class' => 'disabled'));
+ $row[] = html_writer::tag('span', get_string('nodatabase', 'admin'), array('class' => 'text-muted'));
}
// Other question types required by this one.
$out .= $this->output->container(get_string('cancelinstallinfodir', 'core_plugin', $pluginfo->rootdir));
if ($repotype = $pluginman->plugin_external_source($pluginfo->component)) {
$out .= $this->output->container(get_string('uninstalldeleteconfirmexternal', 'core_plugin', $repotype),
- 'uninstalldeleteconfirmexternal');
+ 'alert alert-warning mt-2');
}
}
if ($repotype = $pluginman->plugin_external_source($pluginfo->component)) {
$confirm .= $this->output->container(get_string('uninstalldeleteconfirmexternal', 'core_plugin', $repotype),
- 'uninstalldeleteconfirmexternal');
+ 'alert alert-warning mt-2');
}
// After any uninstall we must execute full upgrade to finish the cleanup!
* @return string HTML to output.
*/
protected function warning($message, $type = 'warning') {
- return $this->box($message, 'generalbox admin' . $type);
+ return $this->box($message, 'generalbox alert alert-' . $type);
}
/**
return $this->warning(get_string('datarootsecuritywarning', 'admin', $CFG->dataroot));
} else if ($insecuredataroot == INSECURE_DATAROOT_ERROR) {
- return $this->warning(get_string('datarootsecurityerror', 'admin', $CFG->dataroot), 'error');
+ return $this->warning(get_string('datarootsecurityerror', 'admin', $CFG->dataroot), 'danger');
} else {
return '';
if ($devlibdir) {
$moreinfo = new moodle_url('/report/security/index.php');
$warning = get_string('devlibdirpresent', 'core_admin', ['moreinfourl' => $moreinfo->out()]);
- return $this->warning($warning, 'error');
+ return $this->warning($warning, 'danger');
} else {
return '';
return $this->warning(
$this->container(get_string('maturitycorewarning', 'admin', $maturitylevel)) .
$this->container($this->doc_link('admin/versions', get_string('morehelp'))),
- 'error');
+ 'danger');
}
/*
}
$warning = (get_string('testsiteupgradewarning', 'admin', $testsite));
- return $this->warning($warning, 'error');
+ return $this->warning($warning, 'danger');
}
/**
$level = 'warning';
if ($maturity == MATURITY_ALPHA) {
- $level = 'error';
+ $level = 'danger';
}
$maturitylevel = get_string('maturity' . $maturity, 'admin');
protected function release_notes_link() {
$releasenoteslink = get_string('releasenoteslink', 'admin', 'http://docs.moodle.org/dev/Releases');
$releasenoteslink = str_replace('target="_blank"', 'onclick="this.target=\'_blank\'"', $releasenoteslink); // extremely ugly validation hack
- return $this->box($releasenoteslink, 'generalbox releasenoteslink');
+ return $this->box($releasenoteslink, 'generalbox alert alert-info');
}
/**
get_string('status'),
);
$servertable->colclasses = array('centeralign name', 'centeralign info', 'leftalign report', 'leftalign plugin', 'centeralign status');
- $servertable->attributes['class'] = 'admintable environmenttable generaltable';
+ $servertable->attributes['class'] = 'admintable environmenttable generaltable table-sm';
$servertable->id = 'serverstatus';
$serverdata = array('ok'=>array(), 'warn'=>array(), 'error'=>array());
get_string('status'),
);
$othertable->colclasses = array('aligncenter info', 'alignleft report', 'alignleft plugin', 'aligncenter status');
- $othertable->attributes['class'] = 'admintable environmenttable generaltable';
+ $othertable->attributes['class'] = 'admintable environmenttable generaltable table-sm';
$othertable->id = 'otherserverstatus';
$otherdata = array('ok'=>array(), 'warn'=>array(), 'error'=>array());
protected $id;
/** Added to the class="" attribute on output. */
- protected $classes = array('rolecap');
+ protected $classes = array('rolecap table-hover');
/** Default number of capabilities in the table for the search UI to be shown. */
const NUM_CAPS_FOR_SEARCH = 12;
$this->contextname = $contextname;
$this->stryes = get_string('yes');
$this->strno = get_string('no');
+ $this->add_classes(['table-striped']);
}
protected function add_header_cells() {
foreach ($levels as $level => $classname) {
$this->allcontextlevels[$level] = context_helper::get_level_name($level);
}
+ $this->add_classes(['table-striped']);
}
protected function load_current_permissions() {
$rowattributes = parent::get_row_attributes($capability);
if ($this->permissions[$capability->name] !== 0) {
if (empty($rowattributes['class'])) {
- $rowattributes['class'] = "overriddenpermission";
+ $rowattributes['class'] = "overriddenpermission table-warning";
} else {
- $rowattributes['class'] .= " overriddenpermission";
+ $rowattributes['class'] .= " overriddenpermission table-warning";
}
}
return $rowattributes;
<label {{#labelfor}}for="{{labelfor}}"{{/labelfor}}>
{{{title}}}
{{#override}}
- <div class="form-overridden">{{override}}</div>
+ <div class="alert alert-info">{{override}}</div>
{{/override}}
{{#warning}}
- <div class="form-warning">{{warning}}</div>
+ <div class="alert alert-warning">{{warning}}</div>
{{/warning}}
</label>
<span class="form-shortname d-block small text-muted">{{{name}}}</span>
<h3 class="adminpagetitle"><a href="{{url}}">{{{title}}}</a></h3>
<ul class="adminpagepath" aria-label="{{#str}} pagepath, core {{/str}}">
{{#path}}
- <li>{{.}}</li>
+ <li class="small text-muted">{{.}}</li>
{{/path}}
</ul>
<fieldset class="adminsettings">
Background:
Given the following "users" exist:
- | username | firstname | lastname | email | auth | confirmed | lastip |
- | user1 | User | One | one@example.com | manual | 0 | 127.0.1.1 |
- | user2 | User | Two | two@example.com | ldap | 1 | 0.0.0.0 |
- | user3 | User | Three | three@example.com | manual | 1 | 0.0.0.0 |
- | user4 | User | Four | four@example.com | ldap | 0 | 127.0.1.2 |
+ | username | firstname | lastname | email | auth | confirmed | lastip | institution | department |
+ | user1 | User | One | one@example.com | manual | 0 | 127.0.1.1 | moodle | red |
+ | user2 | User | Two | two@example.com | ldap | 1 | 0.0.0.0 | moodle | blue |
+ | user3 | User | Three | three@example.com | manual | 1 | 0.0.0.0 | | |
+ | user4 | User | Four | four@example.com | ldap | 0 | 127.0.1.2 | | |
And the following "cohorts" exist:
| name | idnumber |
| Cohort 1 | CH1 |
And I should see "User Two"
And I should see "User Three"
And I should see "User Four"
+
+ Scenario: Filter users by institution and department
+ When I set the field "id_institution" to "moodle"
+ And I press "Add filter"
+ Then I should see "User One"
+ And I should see "User Two"
+ And I should not see "User Three"
+ And I should not see "User Four"
+ And I set the field "id_department" to "red"
+ And I press "Add filter"
+ And I should see "User One"
+ And I should not see "User Two"
\ No newline at end of file
$form->addElement('select', 'roles', get_string('roleslabel', 'tool_capability'), $roles, $attributes);
$form->setType('roles', PARAM_TEXT);
+ $form->addElement('checkbox', 'onlydiff',
+ get_string('filters', 'tool_capability'),
+ get_string('onlydiff', 'tool_capability'));
+ $form->setType('onlydiff', PARAM_BOOL);
+
$form->addElement('submit', 'submitbutton', get_string('getreport', 'tool_capability'));
}
$rolestoshow = array();
$roleids = array('0');
$cleanedroleids = array();
+$onlydiff = false;
if ($data = $form->get_data()) {
$roleids = array();
}
}
}
+
+ if (isset($data->onlydiff)) {
+ $onlydiff = $data->onlydiff;
+ }
}
\tool_capability\event\report_viewed::create()->trigger();
// If we have a capability, generate the report.
if (count($capabilities) && count($rolestoshow)) {
/* @var tool_capability_renderer $renderer */
- echo $renderer->capability_comparison_table($capabilities, $context->id, $rolestoshow);
+ echo $renderer->capability_comparison_table($capabilities, $context->id, $rolestoshow, $onlydiff);
}
// Footer.
// If there are any role overrides here, print them.
if (!empty($contexts[$contextid]->rolecapabilities)) {
$rowcounter = 0;
- echo '<table class="generaltable rolecaps"><tbody>';
+ echo '<table class="generaltable table-striped"><tbody>';
foreach ($allroles as $role) {
if (isset($contexts[$contextid]->rolecapabilities[$role->id])) {
$permission = $contexts[$contextid]->rolecapabilities[$role->id];
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
+$string['onlydiff'] = 'Show differences only';
$string['capabilitylabel'] = 'Capability:';
$string['capabilityreport'] = 'Capability overview';
$string['eventreportviewed'] = 'Report viewed';
+$string['filters'] = 'Filter results';
$string['forroles'] = 'For roles {$a}';
$string['getreport'] = 'Get the overview';
$string['changeoverrides'] = 'Change overrides in this context';
$string['changeroles'] = 'Change role definitions';
$string['intro'] = 'This report shows, for a particular capability, what permission that capability has in the definition of every role (or a selection of roles), and everywhere in the site where that capability is overridden.';
$string['pluginname'] = 'Capability overview';
+$string['nodifferences'] = 'There are no differences to show between selected roles in this context';
$string['reportforcapability'] = 'Report for capability \'{$a}\'';
$string['reportsettings'] = 'Report settings';
$string['roleslabel'] = 'Roles:';
* @param array $capabilities An array of capabilities to show comparison for.
* @param int $contextid The context we are displaying for.
* @param array $roles An array of roles to show comparison for.
+ * @param bool $onlydiff show only different permissions
* @return string
*/
- public function capability_comparison_table(array $capabilities, $contextid, array $roles) {
+ public function capability_comparison_table(array $capabilities, $contextid, array $roles, $onlydiff=false) {
$strpermissions = $this->get_permission_strings();
$permissionclasses = $this->get_permission_classes();
$row = new html_table_row(array($captitle));
+ $permissiontypes = array();
foreach ($roles as $role) {
if (isset($contexts[$contextid]->rolecapabilities[$role->id])) {
$permission = $contexts[$contextid]->rolecapabilities[$role->id];
} else {
$permission = CAP_INHERIT;
}
+ if (!in_array($permission, $permissiontypes)) {
+ $permissiontypes[] = $permission;
+ }
$cell = new html_table_cell($strpermissions[$permission]);
$cell->attributes['class'] = $permissionclasses[$permission];
$row->cells[] = $cell;
}
-
- $table->data[] = $row;
+ if (!$onlydiff || count($permissiontypes) > 1) {
+ $table->data[] = $row;
+ }
}
// Start the list item, and print the context name as a link to the place to make changes.
$title = get_string('permissionsincontext', 'core_role', $context->get_context_name());
$html = $this->output->heading(html_writer::link($url, $title), 3);
- $html .= html_writer::table($table);
+ if (!empty($table->data)) {
+ $html .= html_writer::table($table);
+ } else {
+ $html .= html_writer::tag('p', get_string('nodifferences', 'tool_capability'));
+ }
// If there are any child contexts, print them recursively.
if (!empty($contexts[$contextid]->children)) {
foreach ($contexts[$contextid]->children as $childcontextid) {
- $html .= $this->capability_comparison_table($capabilities, $childcontextid, $roles, true);
+ $html .= $this->capability_comparison_table($capabilities, $childcontextid, $roles, $onlydiff);
}
}
return $html;
--- /dev/null
+@tool @tool_capability
+Feature: show capabilities for selected roles
+ In order to check roles capabilities
+ As an admin
+ I need to be able to customize capabilities report viewing only specific roles and capabilities
+
+ Background:
+ Given the following "roles" exist:
+ | shortname | name | archetype |
+ | studenteq | Studenteq | student |
+ | studentdf | Studentdf | student |
+ And the following "permission overrides" exist:
+ | capability | permission | role | contextlevel | reference |
+ | moodle/course:changefullname | Allow | studentdf | System | |
+ | moodle/course:changeshortname | Prohibit | studentdf | System | |
+ | moodle/course:changeidnumber | Prevent | studentdf | System | |
+ And I log in as "admin"
+ And I navigate to "Users > Permissions > Capability overview" in site administration
+
+ Scenario: visualize capabilities table with a limited number of capabilities
+ When I set the following fields to these values:
+ | Capability: | moodle/course:changefullname, moodle/course:changeshortname |
+ | Roles: | Studentdf |
+ And I click on "Get the overview" "button"
+ Then I should see "moodle/course:changefullname" in the "comparisontable" "table"
+ And I should see "moodle/course:changeshortname" in the "comparisontable" "table"
+ And I should not see "moodle/course:changecategory" in the "comparisontable" "table"
+
+ Scenario: visualize an allow capability
+ When I set the following fields to these values:
+ | Capability: | moodle/course:changefullname |
+ | Roles: | Studentdf |
+ And I click on "Get the overview" "button"
+ Then I should see "Allow" in the "comparisontable" "table"
+ And I should not see "Prevent" in the "comparisontable" "table"
+ And I should not see "Prohibit" in the "comparisontable" "table"
+ And I should not see "Not set" in the "comparisontable" "table"
+
+ Scenario: visualize a prohibit capability
+ When I set the following fields to these values:
+ | Capability: | moodle/course:changeshortname |
+ | Roles: | Studentdf |
+ And I click on "Get the overview" "button"
+ Then I should not see "Allow" in the "comparisontable" "table"
+ And I should not see "Prevent" in the "comparisontable" "table"
+ And I should see "Prohibit" in the "comparisontable" "table"
+ And I should not see "Not set" in the "comparisontable" "table"
+
+ Scenario: visualize a not set capability
+ When I set the following fields to these values:
+ | Capability: | moodle/course:changecategory |
+ | Roles: | Studentdf |
+ And I click on "Get the overview" "button"
+ Then I should not see "Allow" in the "comparisontable" "table"
+ And I should not see "Prevent" in the "comparisontable" "table"
+ And I should not see "Prohibit" in the "comparisontable" "table"
+ And I should see "Not set" in the "comparisontable" "table"
+
+ Scenario: visualize more than one role
+ When I set the following fields to these values:
+ | Capability: | moodle/course:changecategory |
+ | Roles: | Student, Studentdf |
+ And I click on "Get the overview" "button"
+ Then I should see "Student" in the "comparisontable" "table"
+ And I should see "Studentdf" in the "comparisontable" "table"
+ And I should not see "Teacher" in the "comparisontable" "table"
+
+ Scenario: visualize all roles without selecting any role
+ When I set the following fields to these values:
+ | Capability: | moodle/course:changecategory |
+ And I click on "Get the overview" "button"
+ Then I should see "Student" in the "comparisontable" "table"
+ And I should see "Studentdf" in the "comparisontable" "table"
+ And I should see "Teacher" in the "comparisontable" "table"
+
+ Scenario: visualize all roles by selecting All option
+ When I set the following fields to these values:
+ | Capability: | moodle/course:changecategory |
+ | Roles: | All |
+ And I click on "Get the overview" "button"
+ Then I should see "Student" in the "comparisontable" "table"
+ And I should see "Studentdf" in the "comparisontable" "table"
+ And I should see "Teacher" in the "comparisontable" "table"
+
+ @javascript
+ Scenario: filter capability list using javascript
+ Given I should see "moodle/site:config" in the "Capability" "field"
+ And I should see "moodle/course:change" in the "Capability" "field"
+ When I wait until the page is ready
+ And I set the field "capabilitysearch" to "moodle/course:change"
+ Then I should see "moodle/course:change" in the "Capability" "field"
+ And I should not see "moodle/site:config" in the "Capability" "field"
+
+ @javascript
+ Scenario: selecting capabilities using filters
+ Given I should see "moodle/course:change" in the "Capability" "field"
+ When I wait until the page is ready
+ And I set the field "capabilitysearch" to "moodle/course:change"
+ When I set the following fields to these values:
+ | Capability: | moodle/course:changecategory |
+ | Roles: | Student |
+ And I set the field "capabilitysearch" to ""
+ And I click on "Get the overview" "button"
+ Then I should see "moodle/course:changecategory" in the "comparisontable" "table"
--- /dev/null
+@tool @tool_capability
+Feature: show only differences between roles for selected capabilities
+ In order to check roles capabilities
+ As an admin
+ I need to be able to filter capabilities report viewing only role differences
+
+ Background:
+ Given the following "roles" exist:
+ | shortname | name | archetype |
+ | studenteq | Studenteq | student |
+ | studentdf | Studentdf | student |
+ And the following "permission overrides" exist:
+ | capability | permission | role | contextlevel | reference |
+ | moodle/course:changefullname | Allow | studentdf | System | |
+ | moodle/course:changeshortname | Prohibit | studentdf | System | |
+ And I log in as "admin"
+ And I navigate to "Users > Permissions > Capability overview" in site administration
+
+ Scenario: Compare identical roles
+ When I set the following fields to these values:
+ | Capability: | moodle/course:changefullname, moodle/course:changeshortname, moodle/course:changeidnumber, moodle/course:changesummary |
+ | Roles: | Student, Studenteq |
+ And I set the field "Show differences only" to "1"
+ And I click on "Get the overview" "button"
+ Then I should see "There are no differences to show between selected roles in this context"
+
+ Scenario: Compare different roles
+ When I set the following fields to these values:
+ | Capability: | moodle/course:changefullname, moodle/course:changeshortname, moodle/course:changeidnumber, moodle/course:changesummary |
+ | Roles: | Student, Studentdf |
+ And I set the field "Show differences only" to "1"
+ And I click on "Get the overview" "button"
+ Then I should not see "There are no differences to show between selected roles in this context"
+ And I should see "moodle/course:changefullname" in the "comparisontable" "table"
+ And I should see "moodle/course:changeshortname" in the "comparisontable" "table"
+ And I should not see "moodle/course:changesummary" in the "comparisontable" "table"
+
+ Scenario: Compare different roles but comparing capabilities that are equals on both
+ When I set the following fields to these values:
+ | Capability: | moodle/course:changeidnumber, moodle/course:changesummary |
+ | Roles: | Student, Studentdf |
+ And I set the field "Show differences only" to "1"
+ And I click on "Get the overview" "button"
+ Then I should see "There are no differences to show between selected roles in this context"
+
+ Scenario: Compare all roles without selecting specific role
+ When I set the following fields to these values:
+ | Capability: | moodle/course:changefullname, moodle/site:config |
+ And I set the field "Show differences only" to "1"
+ And I click on "Get the overview" "button"
+ Then I should not see "moodle/site:config" in the "comparisontable" "table"
+ And I should see "moodle/course:changefullname" in the "comparisontable" "table"
+
+ Scenario: Compare all roles without selecting specific role on not defined capability
+ When I set the following fields to these values:
+ | Capability: | moodle/site:config |
+ And I set the field "Show differences only" to "1"
+ And I click on "Get the overview" "button"
+ Then I should see "There are no differences to show between selected roles in this context"
+
+ Scenario: Comparing only one role
+ When I set the following fields to these values:
+ | Capability: | moodle/course:changefullname, moodle/course:changeshortname, moodle/course:changeidnumber, moodle/course:changesummary |
+ | Roles: | Student |
+ And I set the field "Show differences only" to "1"
+ And I click on "Get the overview" "button"
+ Then I should see "There are no differences to show between selected roles in this context"
this.button = this.form.all('input[type=submit]');
this.lastsearch = this.form.one('input[name=search]');
- var div = Y.Node.create('<div id="capabilitysearchui"></div>'),
+ var div = Y.Node.create('<div id="capabilitysearchui" data-fieldtype="text"></div>'),
label = Y.Node.create('<label for="capabilitysearch">' + this.get('strsearch') + '</label>');
this.input = Y.Node.create('<input type="text" id="capabilitysearch" />');
$rolesadded = array();
$rolesremoved = array();
+ // Remove any cohort role mappings for roles which have been deleted.
+ // The role assignments are not a consideration because these will have been removed when the role was.
+ $DB->delete_records_select('tool_cohortroles', "roleid NOT IN (SELECT id FROM {role})");
+
// Get all cohort role assignments and group them by user and role.
$all = cohort_role_assignment::get_records(array(), 'userid, roleid');
// We build an better structure to loop on.
function xmldb_tool_customlang_upgrade($oldversion) {
global $CFG;
- // Automatically generated Moodle v3.3.0 release upgrade line.
- // Put any upgrade step following this.
-
- // Automatically generated Moodle v3.4.0 release upgrade line.
- // Put any upgrade step following this.
-
// Automatically generated Moodle v3.5.0 release upgrade line.
// Put any upgrade step following this.
*
* @param string $component the name of the component
* @param array $strings
+ * @return void
*/
protected static function dump_strings($lang, $component, $strings) {
global $CFG;
if ($lang !== clean_param($lang, PARAM_LANG)) {
- debugging('Unable to dump local strings for non-installed language pack .'.s($lang));
- return false;
+ throw new moodle_exception('Unable to dump local strings for non-installed language pack .'.s($lang));
}
if ($component !== clean_param($component, PARAM_COMPONENT)) {
throw new coding_exception('Incorrect component name');
}
if (!$filename = self::get_component_filename($component)) {
- debugging('Unable to find the filename for the component '.s($component));
- return false;
+ throw new moodle_exception('Unable to find the filename for the component '.s($component));
}
if ($filename !== clean_param($filename, PARAM_FILE)) {
throw new coding_exception('Incorrect file name '.s($filename));
}
if (!$f = fopen($filepath, 'w')) {
- debugging('Unable to write '.s($filepath));
- return false;
+ throw new moodle_exception('Unable to write '.s($filepath));
}
fwrite($f, <<<EOF
<?php
'requestedby' => $requestedby->fullname,
'requesttype' => $typetext,
'requestdate' => userdate($requestdata->timecreated),
- 'requestorigin' => $SITE->fullname,
+ 'requestorigin' => format_string($SITE->fullname, true, ['context' => context_system::instance()]),
'requestoriginurl' => new moodle_url('/'),
'requestcomments' => $requestdata->messagehtml,
'datarequestsurl' => $datarequestsurl
use action_link;
use coding_exception;
+use context_system;
use core\message\message;
use core\task\adhoc_task;
use core_user;
$message->contexturl = $datarequestsurl;
$message->contexturlname = get_string('datarequests', 'tool_dataprivacy');
// Message to the recipient.
- $messagetextdata['message'] = get_string('resultdownloadready', 'tool_dataprivacy', $SITE->fullname);
+ $messagetextdata['message'] = get_string('resultdownloadready', 'tool_dataprivacy',
+ format_string($SITE->fullname, true, ['context' => context_system::instance()]));
// Prepare download link.
$downloadurl = moodle_url::make_pluginfile_url($usercontext->id, 'tool_dataprivacy', 'export', $thing->get_itemid(),
$thing->get_filepath(), $thing->get_filename(), true);
// No point notifying a deleted user in Moodle.
$message->notification = 0;
// Message to the recipient.
- $messagetextdata['message'] = get_string('resultdeleted', 'tool_dataprivacy', $SITE->fullname);
+ $messagetextdata['message'] = get_string('resultdeleted', 'tool_dataprivacy',
+ format_string($SITE->fullname, true, ['context' => context_system::instance()]));
// Message will be sent to the deleted user via email only.
$emailonly = true;
break;
<hr />
<div class="p-l-3">
<dl class="row">
- <dt class="col-xs-3">
+ <dt class="col-3">
{{#link}}
<a href="#{{name}}"><strong style="word-wrap:break-word">{{name}}</strong></a>
{{/link}}
{{/link}}
<div class="small text-muted" style="word-wrap:break-word">{{type}}</div>
</dt>
- <dd class="col-xs-9">{{summary}}</dd>
+ <dd class="col-9">{{summary}}</dd>
</dl>
<dl>
{{#fields}}
<div class="row">
- <dt class="col-xs-3 font-weight-normal" style="word-wrap:break-word">{{field_name}}</dt>
- <dd class="col-xs-9">{{field_summary}}</dd>
+ <dt class="col-3 font-weight-normal" style="word-wrap:break-word">{{field_name}}</dt>
+ <dd class="col-9">{{field_summary}}</dd>
</div>
{{/fields}}
</dl>
<hr />
<div class="p-l-3">
<div class="row">
- <div class="col-xs-12">
+ <div class="col-12">
{{nullprovider}}
</div>
</div>
$out = $this->heading(get_string('pluginname', 'tool_filetypes'));
if ($restricted) {
$out .= html_writer::div(
- html_writer::div(get_string('configoverride', 'admin'), 'form-overridden'),
+ html_writer::div(get_string('configoverride', 'admin'), 'alert alert-info'),
'', array('id' => 'adminsettings'));
}
if (count($combined) > 1) {
.path-admin-tool-filetypes .generaltable .nonstandard {
font-weight: bold;
}
-
-/* Spacing around the 'Defined in config.php' stripe */
-.path-admin-tool-filetypes .form-overridden {
- display: inline-block;
- margin-bottom: 1em;
- padding: 4px 6px;
-}
function xmldb_tool_log_upgrade($oldversion) {
global $CFG;
- // Automatically generated Moodle v3.3.0 release upgrade line.
- // Put any upgrade step following this.
-
- // Automatically generated Moodle v3.4.0 release upgrade line.
- // Put any upgrade step following this.
-
// Automatically generated Moodle v3.5.0 release upgrade line.
// Put any upgrade step following this.
function xmldb_logstore_database_upgrade($oldversion) {
global $CFG;
- // Automatically generated Moodle v3.3.0 release upgrade line.
- // Put any upgrade step following this.
-
- // Automatically generated Moodle v3.4.0 release upgrade line.
- // Put any upgrade step following this.
-
// Automatically generated Moodle v3.5.0 release upgrade line.
// Put any upgrade step following this.
function xmldb_logstore_standard_upgrade($oldversion) {
global $CFG;
- // Automatically generated Moodle v3.3.0 release upgrade line.
- // Put any upgrade step following this.
-
- // Automatically generated Moodle v3.4.0 release upgrade line.
- // Put any upgrade step following this.
-
// Automatically generated Moodle v3.5.0 release upgrade line.
// Put any upgrade step following this.
+++ /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/>.
-
-/**
- * This file contains renamed classes mappings.
- *
- * @package tool_lp
- * @copyright 2016 Frédéric Massart - FMCorz.net
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-defined('MOODLE_INTERNAL') || die();
-
-$renamedclasses = array(
- 'tool_lp\\external\\cohort_summary_exporter' => 'core_cohort\\external\\cohort_summary_exporter',
- 'tool_lp\\external\\course_module_summary_exporter' => 'core_course\\external\\course_module_summary_exporter',
- 'tool_lp\\external\\course_summary_exporter' => 'core_course\\external\\course_summary_exporter',
- 'tool_lp\\form\\persistent' => 'core\\form\\persistent',
-);
function xmldb_tool_monitor_upgrade($oldversion) {
global $CFG, $DB;
- if ($oldversion < 2017021300) {
-
- // Delete "orphaned" subscriptions.
- $sql = "SELECT DISTINCT s.courseid
- FROM {tool_monitor_subscriptions} s
- LEFT OUTER JOIN {course} c ON c.id = s.courseid
- WHERE s.courseid <> 0 and c.id IS NULL";
- $deletedcourses = $DB->get_field_sql($sql);
- if ($deletedcourses) {
- list($sql, $params) = $DB->get_in_or_equal($deletedcourses);
- $DB->execute("DELETE FROM {tool_monitor_subscriptions} WHERE courseid " . $sql, $params);
- }
-
- // Monitor savepoint reached.
- upgrade_plugin_savepoint(true, 2017021300, 'tool', 'monitor');
- }
-
- // Automatically generated Moodle v3.3.0 release upgrade line.
- // Put any upgrade step following this.
-
- // Automatically generated Moodle v3.4.0 release upgrade line.
- // Put any upgrade step following this.
-
// Automatically generated Moodle v3.5.0 release upgrade line.
// Put any upgrade step following this.
[
'execute' => false,
'help' => false,
+ 'keep-alive' => 0,
'showsql' => false,
'showdebugging' => false,
+ 'ignorelimits' => false,
], [
'h' => 'help',
+ 'e' => 'execute',
+ 'k' => 'keep-alive',
+ 'i' => 'ignorelimits',
]
);
}
if ($options['help'] or empty($options['execute'])) {
- $help =
-"Scheduled cron tasks.
+ $help = <<<EOT
+Ad hoc cron tasks.
Options:
---showsql Show sql queries before they are executed
---showdebugging Show developer level debugging information
---execute Run all queued adhoc tasks
--h, --help Print out this help
+ -h, --help Print out this help
+ --showsql Show sql queries before they are executed
+ --showdebugging Show developer level debugging information
+ -e, --execute Run all queued adhoc tasks
+ -k, --keep-alive=N Keep this script alive for N seconds and poll for new adhoc tasks
+ -i --ignorelimits Ignore task_adhoc_concurrency_limit and task_adhoc_max_runtime limits
Example:
\$sudo -u www-data /usr/bin/php admin/tool/task/cli/adhoc_task.php --execute
-";
+EOT;
echo $help;
die;
if (empty($options['execute'])) {
exit(0);
}
+if (empty($options['keep-alive'])) {
+ $options['keep-alive'] = 0;
+}
if (!empty($CFG->showcronsql)) {
$DB->set_debug(true);
set_debugging(DEBUG_DEVELOPER, true);
}
+$checklimits = empty($options['ignorelimits']);
+
core_php_time_limit::raise();
-$starttime = microtime();
// Increase memory limit.
raise_memory_limit(MEMORY_EXTRA);
-// Emulate normal session - we use admin accoutn by default.
+// Emulate normal session - we use admin account by default.
cron_setup_user();
-// Start output log.
-$timenow = time();
-$humantimenow = date('r', $timenow);
-mtrace("Server Time: {$humantimenow}\n");
+$humantimenow = date('r', time());
+$keepalive = (int)$options['keep-alive'];
-// Run all adhoc tasks.
-$taskcount = 0;
-while (!\core\task\manager::static_caches_cleared_since($timenow) &&
- $task = \core\task\manager::get_next_adhoc_task($timenow)) {
- cron_run_inner_adhoc_task($task);
- $taskcount++;
- unset($task);
-}
-mtrace("Ran {$taskcount} adhoc tasks found at {$humantimenow}");
+mtrace("Server Time: {$humantimenow}\n");
+cron_run_adhoc_tasks(time(), $keepalive, $checklimits);
$string['resettasktodefaults'] = 'Reset task schedule to defaults';
$string['resettasktodefaults_help'] = 'This will discard any local changes and revert the schedule for this task back to its original settings.';
$string['runnow'] = 'Run now';
+$string['runagain'] = 'Run again';
$string['runnow_confirm'] = 'Are you sure you want to run this task \'{$a}\' now? The task will run on the web server and may take some time to complete.';
$string['runpattern'] = 'Run pattern';
$string['scheduledtasks'] = 'Scheduled tasks';
echo html_writer::end_tag('pre');
$output = $PAGE->get_renderer('tool_task');
+
+// Re-run the specified task (this will output an error if it doesn't exist).
+echo $OUTPUT->single_button(new moodle_url('/admin/tool/task/schedule_task.php',
+ array('task' => $taskname, 'confirm' => 1, 'sesskey' => sesskey())),
+ get_string('runagain', 'tool_task'));
echo $output->link_back();
echo $OUTPUT->footer();
$string['invalidencoding'] = 'Invalid encoding';
$string['invalidmode'] = 'Invalid mode selected';
$string['invalideupdatemode'] = 'Invalid update mode selected';
-$string['invalidvisibilitymode'] = 'Invalid visibility mode given';
+$string['invalidvisibilitymode'] = 'Invalid visible mode';
$string['invalidroles'] = 'Invalid role names: {$a}';
$string['invalidshortname'] = 'Invalid shortname';
$string['missingmandatoryfields'] = 'Missing value for mandatory fields: {$a}';
// Is this the first step?
if (this.isFirstStep(stepConfig.stepNumber)) {
- template.find('[data-role="previous"]').prop('disabled', true);
+ template.find('[data-role="previous"]').hide();
} else {
template.find('[data-role="previous"]').prop('disabled', false);
}
// Is this the final step?
if (this.isLastStep(stepConfig.stepNumber)) {
- template.find('[data-role="next"]').prop('disabled', true);
+ template.find('[data-role="next"]').hide();
+ template.find('[data-role="end"]').removeClass("btn-secondary").addClass("btn-primary");
} else {
template.find('[data-role="next"]').prop('disabled', false);
}
--- /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/>.
+
+/**
+ * Access Date filter.
+ *
+ * @package tool_usertours
+ * @copyright 2019 Tom Dickman <tomdickman@catalyst-au.net>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+namespace tool_usertours\local\filter;
+
+defined('MOODLE_INTERNAL') || die();
+
+use context;
+use tool_usertours\tour;
+
+/**
+ * Access date filter. Used to determine if USER should see a tour based on a particular access date.
+ *
+ * @copyright 2019 Tom Dickman <tomdickman@catalyst-au.net>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class accessdate extends base {
+
+ /**
+ * Access date filtering constant for setting base date as account creation date.
+ */
+ const FILTER_ACCOUNT_CREATION = 'tool_usertours_accountcreation';
+
+ /**
+ * Access date filtering constant for setting base date as account first login date.
+ */
+ const FILTER_FIRST_LOGIN = 'tool_usertours_firstlogin';
+
+ /**
+ * Access date filtering constant for setting base date as account last login date.
+ */
+ const FILTER_LAST_LOGIN = 'tool_usertours_lastlogin';
+
+ /**
+ * Default this filter to not be enabled.
+ */
+ const FILTER_ENABLED_DEFAULT = 0;
+
+ /**
+ * The name of the filter.
+ *
+ * @return string
+ */
+ public static function get_filter_name() {
+ return 'accessdate';
+ }
+
+ /**
+ * Retrieve the list of available filter options.
+ *
+ * @return array An array whose keys are the valid options
+ * And whose values are the values to display
+ * @throws \coding_exception
+ */
+ public static function get_filter_options() {
+
+ return array(
+ self::FILTER_ACCOUNT_CREATION => get_string('filter_date_account_creation', 'tool_usertours'),
+ self::FILTER_FIRST_LOGIN => get_string('filter_date_first_login', 'tool_usertours'),
+ self::FILTER_LAST_LOGIN => get_string('filter_date_last_login', 'tool_usertours'),
+ );
+
+ }
+
+ /**
+ * Add the form elements for the filter to the supplied form.
+ *
+ * @param \MoodleQuickForm $mform The form to add filter settings to.
+ *
+ * @throws \coding_exception
+ */
+ public static function add_filter_to_form(\MoodleQuickForm &$mform) {
+
+ $filtername = static::get_filter_name();
+ $key = "filter_{$filtername}";
+ $range = "{$key}_range";
+ $enabled = "{$key}_enabled";
+
+ $mform->addElement('advcheckbox', $enabled, get_string($key, 'tool_usertours'),
+ get_string('filter_accessdate_enabled', 'tool_usertours'), null, array(0, 1));
+ $mform->addHelpButton($enabled, $enabled, 'tool_usertours');
+
+ $mform->addElement('select', $key, ' ', self::get_filter_options());
+ $mform->setDefault($key, self::FILTER_ACCOUNT_CREATION);
+ $mform->hideIf($key, $enabled, 'notchecked');
+
+ $mform->addElement('duration', $range, null, [
+ 'optional' => false,
+ 'defaultunit' => DAYSECS,
+ ]);
+ $mform->setDefault($range, 90 * DAYSECS);
+ $mform->hideIf($range, $enabled, 'notchecked');
+
+ }
+
+ /**
+ * Prepare the filter values for the form.
+ *
+ * @param tour $tour The tour to prepare values from
+ * @param stdClass $data The data value
+ * @return stdClass
+ */
+ public static function prepare_filter_values_for_form(tour $tour, \stdClass $data) {
+ $filtername = static::get_filter_name();
+
+ $key = "filter_{$filtername}";
+ $range = "{$key}_range";
+ $enabled = "{$key}_enabled";
+
+ $values = $tour->get_filter_values($filtername);
+
+ // Prepare the advanced checkbox value and prepare filter values based on previously set values.
+ if (!empty($values)) {
+ $data->$enabled = $values->$enabled ? $values->$enabled : self::FILTER_ENABLED_DEFAULT;
+ if ($data->$enabled) {
+ if (isset($values->$key)) {
+ $data->$key = $values->$key;
+ }
+ if (isset($values->$range)) {
+ $data->$range = $values->$range;
+ }
+ }
+ } else {
+ $data->$enabled = self::FILTER_ENABLED_DEFAULT;
+ }
+ return $data;
+ }
+
+ /**
+ * Save the filter values from the form to the tour.
+ *
+ * @param tour $tour The tour to save values to
+ * @param \stdClass $data The data submitted in the form
+ */
+ public static function save_filter_values_from_form(tour $tour, \stdClass $data) {
+ $filtername = static::get_filter_name();
+ $key = "filter_{$filtername}";
+ $range = "{$key}_range";
+ $enabled = "{$key}_enabled";
+
+ $savedata = [];
+ $savedata[$key] = $data->$key;
+ $savedata[$range] = $data->$range;
+ $savedata[$enabled] = $data->$enabled;
+
+ $tour->set_filter_values($filtername, $savedata);
+ }
+
+ /**
+ * Check whether the filter matches the specified tour and/or context.
+ *
+ * @param tour $tour The tour to check
+ * @param context $context The context to check
+ * @return boolean
+ */
+ public static function filter_matches(tour $tour, context $context) {
+ global $USER;
+
+ $filtername = static::get_filter_name();
+ $key = "filter_{$filtername}";
+ $range = "{$key}_range";
+ $enabled = "{$key}_enabled";
+
+ // Default behaviour is to match filter.
+ $result = true;
+ $values = (array) $tour->get_filter_values(self::get_filter_name());
+
+ // If the access date filter is not enabled, end here.
+ if (empty($values[$enabled])) {
+ return $result;
+ }
+
+ if (!empty($values[$key])) {
+ switch ($values[$key]) {
+ case (self::FILTER_ACCOUNT_CREATION):
+ $filterbasedate = (int) $USER->timecreated;
+ break;
+ case (self::FILTER_FIRST_LOGIN):
+ $filterbasedate = (int) $USER->firstaccess;
+ break;
+ case (self::FILTER_LAST_LOGIN):
+ $filterbasedate = (int) $USER->lastlogin;
+ break;
+ default:
+ // Use account creation as default.
+ $filterbasedate = (int) $USER->timecreated;
+ break;
+ }
+ // If the base date has no value because a user hasn't accessed Moodle yet, default to account creation.
+ if (empty($filterbasedate)) {
+ $filterbasedate = (int) $USER->timecreated;
+ }
+
+ if (!empty($values[$range])) {
+ $filterrange = (int) $values[$range];
+ } else {
+ $filterrange = 90 * DAYSECS;
+ }
+
+ // If we're outside the set range from the set base date, filter out tour.
+ if ((time() > ($filterbasedate + $filterrange))) {
+ $result = false;
+ }
+ }
+ return $result;
+ }
+}
\ No newline at end of file
function xmldb_tool_usertours_upgrade($oldversion) {
global $CFG, $DB;
- // Automatically generated Moodle v3.3.0 release upgrade line.
- // Put any upgrade step following this.
-
- // Automatically generated Moodle v3.4.0 release upgrade line.
- // Put any upgrade step following this.
-
// Automatically generated Moodle v3.5.0 release upgrade line.
// Put any upgrade step following this.
$string['event_tour_ended'] = 'Tour ended';
$string['event_step_shown'] = 'Step shown';
$string['exporttour'] = 'Export tour';
+$string['filter_accessdate'] = 'Access date';
+$string['filter_accessdate_enabled'] = 'Enable access date filter';
+$string['filter_accessdate_enabled_help'] = 'Only show the tour to new users or users who have accessed the site recently.';
$string['filter_category'] = 'Category';
$string['filter_category_help'] = 'Show the tour on a page that is associated with a course in the selected category.';
$string['filter_course'] = 'Courses';
$string['filter_courseformat_help'] = 'Show the tour on a page that is associated with a course using the selected course format.';
$string['filter_header'] = 'Tour filters';
$string['filter_help'] = 'Select the conditions under which the tour will be shown. All of the filters must match for a tour to be shown to a user.';
+$string['filter_date_account_creation'] = 'User account creation date within';
+$string['filter_date_first_login'] = 'User\'s first access date within';
+$string['filter_date_last_login'] = 'User\'s last access date within';
$string['filter_theme'] = 'Theme';
$string['filter_theme_help'] = 'Show the tour when the user is using one of the selected themes.';
$string['filter_role'] = 'Role';
--- /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/>.
+
+/**
+ * Tests for time filter.
+ *
+ * @package tool_usertours
+ * @copyright 2019 Tom Dickman <tomdickman@catalyst-au.net>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+use tool_usertours\tour;
+use tool_usertours\local\filter\accessdate;
+
+/**
+ * Tests for time filter.
+ *
+ * @package tool_usertours
+ * @copyright 2019 Tom Dickman <tomdickman@catalyst-au.net>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class tool_usertours_accessdate_filter_test extends advanced_testcase {
+
+ public function setUp() {
+ $this->resetAfterTest(true);
+ }
+
+ /**
+ * Data Provider for filter_matches method.
+ *
+ * @return array
+ */
+ public function filter_matches_provider() {
+ return [
+ 'No config set; Matches' => [
+ [],
+ [],
+ true,
+ ],
+ 'Filter is not enabled; Match' => [
+ ['filter_accessdate' => accessdate::FILTER_ACCOUNT_CREATION, 'filter_accessdate_range' => 90 * DAYSECS,
+ 'filter_accessdate_enabled' => 0],
+ ['timecreated' => time() - (89 * DAYSECS)],
+ true,
+ ],
+ 'Filter is not enabled (tour would not be displayed if it was); Match' => [
+ ['filter_accessdate' => accessdate::FILTER_ACCOUNT_CREATION, 'filter_accessdate_range' => 90 * DAYSECS,
+ 'filter_accessdate_enabled' => 0],
+ ['timecreated' => time() - (91 * DAYSECS)],
+ true,
+ ],
+ 'Inside range of account creation date; Match' => [
+ ['filter_accessdate' => accessdate::FILTER_ACCOUNT_CREATION, 'filter_accessdate_range' => 90 * DAYSECS,
+ 'filter_accessdate_enabled' => 1],
+ ['timecreated' => time() - (89 * DAYSECS)],
+ true,
+ ],
+ 'Outside range of account creation date; No match' => [
+ ['filter_accessdate' => accessdate::FILTER_ACCOUNT_CREATION, 'filter_accessdate_range' => 90 * DAYSECS,
+ 'filter_accessdate_enabled' => 1],
+ ['timecreated' => time() - (91 * DAYSECS)],
+ false,
+ ],
+ 'Inside range of first login date; Match' => [
+ ['filter_accessdate' => accessdate::FILTER_FIRST_LOGIN, 'filter_accessdate_range' => 90 * DAYSECS,
+ 'filter_accessdate_enabled' => 1],
+ ['firstaccess' => time() - (89 * DAYSECS)],
+ true,
+ ],
+ 'Outside range of first login date; No match' => [
+ ['filter_accessdate' => accessdate::FILTER_FIRST_LOGIN, 'filter_accessdate_range' => 90 * DAYSECS,
+ 'filter_accessdate_enabled' => 1],
+ ['firstaccess' => time() - (91 * DAYSECS)],
+ false,
+ ],
+ 'Inside range of last login date; Match' => [
+ ['filter_accessdate' => accessdate::FILTER_LAST_LOGIN, 'filter_accessdate_range' => 90 * DAYSECS,
+ 'filter_accessdate_enabled' => 1],
+ ['lastlogin' => time() - (89 * DAYSECS)],
+ true,
+ ],
+ 'Outside range of last login date; No match' => [
+ ['filter_accessdate' => accessdate::FILTER_LAST_LOGIN, 'filter_accessdate_range' => 90 * DAYSECS,
+ 'filter_accessdate_enabled' => 1],
+ ['lastlogin' => time() - (91 * DAYSECS)],
+ false,
+ ],
+ 'User has never logged in, but tour should be visible; Match' => [
+ ['filter_accessdate' => accessdate::FILTER_LAST_LOGIN, 'filter_accessdate_range' => 90 * DAYSECS,
+ 'filter_accessdate_enabled' => 1],
+ ['lastlogin' => 0, 'timecreated' => time() - (89 * DAYSECS)],
+ true,
+ ],
+ 'User has never logged in, and tour should not be visible; No match' => [
+ ['filter_accessdate' => accessdate::FILTER_LAST_LOGIN, 'filter_accessdate_range' => 90 * DAYSECS,
+ 'filter_accessdate_enabled' => 1],
+ ['lastlogin' => 0, 'timecreated' => time() - (91 * DAYSECS)],
+ false,
+ ],
+ ];
+ }
+
+ /**
+ * Test filter matches.
+ *
+ * @dataProvider filter_matches_provider
+ *
+ * @param array $filtervalues the filter values set.
+ * @param array $userstate any user state required for test.
+ * @param bool $expected result expected.
+ */
+ public function test_filter_matches($filtervalues, $userstate, $expected) {
+ $course = $this->getDataGenerator()->create_course();
+ $context = \context_course::instance($course->id);
+
+ $user = $this->getDataGenerator()->create_user($userstate);
+ $this->setUser($user);
+
+ $tour = new tour();
+ $tour->set_filter_values('accessdate', $filtervalues);
+
+ $this->assertEquals($expected, accessdate::filter_matches($tour, $context));
+ }
+
+}
defined('MOODLE_INTERNAL') || die();
-$plugin->version = 2019111800; // The current module version (Date: YYYYMMDDXX).
+$plugin->version = 2019120400; // The current module version (Date: YYYYMMDDXX).
$plugin->requires = 2019111200; // Requires this Moodle version.
$plugin->component = 'tool_usertours'; // Full name of the plugin (used for diagnostics).
$result = $this->launch('get_db_directories');
// Display list of DB directories if everything is ok
if ($result && !empty($XMLDB->dbdirs)) {
- $o .= '<table id="listdirectories" border="0" cellpadding="5" cellspacing="1" class="admintable generaltable">';
+ $o .= '<table id="listdirectories" border="0" cellpadding="5" cellspacing="1"' .
+ ' class="table-striped table-sm admintable generaltable">';
$row = 0;
foreach ($XMLDB->dbdirs as $key => $dbdir) {
// Detect if this is the lastused dir
This files describes API changes in /admin/*.
+=== 3.9 ===
+
+* The following functions, previously used (exclusively) by upgrade steps are not available anymore because of the upgrade cleanup performed for this version. See MDL-65809 for more info:
+ - upgrade_fix_block_instance_configuration()
+ - upgrade_theme_is_from_family()
+ - upgrade_find_theme_location()
+ - linkcoursesectionsupgradescriptwasrun setting
+ - upgrade_block_positions()
+
=== 3.8 ===
* Admin setting "Open to Google" (opentogoogle) has been renamed to the more generic "Open to search engines" (opentowebcrawlers).
$table->head = array ();
$table->colclasses = array();
$table->head[] = $fullnamedisplay;
- $table->attributes['class'] = 'admintable generaltable';
+ $table->attributes['class'] = 'admintable generaltable table-sm';
foreach ($extracolumns as $field) {
$table->head[] = ${$field};
}
function xmldb_auth_cas_upgrade($oldversion) {
global $CFG;
- if ($oldversion < 2017020700) {
- // Convert info in config plugins from auth/cas to auth_cas.
- upgrade_fix_config_auth_plugin_names('cas');
- upgrade_fix_config_auth_plugin_defaults('cas');
- upgrade_plugin_savepoint(true, 2017020700, 'auth', 'cas');
- }
-
- // Automatically generated Moodle v3.3.0 release upgrade line.
- // Put any upgrade step following this.
-
- // Automatically generated Moodle v3.4.0 release upgrade line.
- // Put any upgrade step following this.
-
// Automatically generated Moodle v3.5.0 release upgrade line.
// Put any upgrade step following this.
function xmldb_auth_db_upgrade($oldversion) {
global $CFG, $DB;
- if ($oldversion < 2017032800) {
- // Convert info in config plugins from auth/db to auth_db
- upgrade_fix_config_auth_plugin_names('db');
- upgrade_fix_config_auth_plugin_defaults('db');
- upgrade_plugin_savepoint(true, 2017032800, 'auth', 'db');
- }
-
- // Automatically generated Moodle v3.3.0 release upgrade line.
- // Put any upgrade step following this.
-
- // Automatically generated Moodle v3.4.0 release upgrade line.
- // Put any upgrade step following this.
-
// Automatically generated Moodle v3.5.0 release upgrade line.
// Put any upgrade step following this.
function xmldb_auth_email_upgrade($oldversion) {
global $CFG, $DB;
- if ($oldversion < 2017020700) {
- // Convert info in config plugins from auth/email to auth_email.
- upgrade_fix_config_auth_plugin_names('email');
- upgrade_fix_config_auth_plugin_defaults('email');
- upgrade_plugin_savepoint(true, 2017020700, 'auth', 'email');
- }
-
- // Automatically generated Moodle v3.3.0 release upgrade line.
- // Put any upgrade step following this.
-
- // Automatically generated Moodle v3.4.0 release upgrade line.
- // Put any upgrade step following this.
-
// Automatically generated Moodle v3.5.0 release upgrade line.
// Put any upgrade step following this.
function xmldb_auth_ldap_upgrade($oldversion) {
global $CFG;
- if ($oldversion < 2017020700) {
- // Convert info in config plugins from auth/ldap to auth_ldap.
- upgrade_fix_config_auth_plugin_names('ldap');
- upgrade_fix_config_auth_plugin_defaults('ldap');
- upgrade_plugin_savepoint(true, 2017020700, 'auth', 'ldap');
- }
-
- // Automatically generated Moodle v3.3.0 release upgrade line.
- // Put any upgrade step following this.
-
- if ($oldversion < 2017080100) {
- // The "auth_ldap/coursecreators" setting was replaced with "auth_ldap/coursecreatorcontext" (created
- // dynamically from system-assignable roles) - so migrate any existing value to the first new slot.
- if ($ldapcontext = get_config('auth_ldap', 'creators')) {
- // Get info about the role that the old coursecreators setting would apply.
- $creatorrole = get_archetype_roles('coursecreator');
- $creatorrole = array_shift($creatorrole); // We can only use one, let's use the first.
-
- // Create new setting.
- set_config($creatorrole->shortname . 'context', $ldapcontext, 'auth_ldap');
-
- // Delete old setting.
- set_config('creators', null, 'auth_ldap');
-
- upgrade_plugin_savepoint(true, 2017080100, 'auth', 'ldap');
- }
- }
-
- // Automatically generated Moodle v3.4.0 release upgrade line.
- // Put any upgrade step following this.
-
// Automatically generated Moodle v3.5.0 release upgrade line.
// Put any upgrade step following this.
function xmldb_auth_manual_upgrade($oldversion) {
global $CFG;
- if ($oldversion < 2017020700) {
- // Convert info in config plugins from auth/manual to auth_manual.
- upgrade_fix_config_auth_plugin_names('manual');
- upgrade_fix_config_auth_plugin_defaults('manual');
- upgrade_plugin_savepoint(true, 2017020700, 'auth', 'manual');
- }
-
- // Automatically generated Moodle v3.3.0 release upgrade line.
- // Put any upgrade step following this.
-
- // Automatically generated Moodle v3.4.0 release upgrade line.
- // Put any upgrade step following this.
-
// Automatically generated Moodle v3.5.0 release upgrade line.
// Put any upgrade step following this.
function xmldb_auth_mnet_upgrade($oldversion) {
global $CFG;
- if ($oldversion < 2017020700) {
- // Convert info in config plugins from auth/mnet to auth_mnet.
- upgrade_fix_config_auth_plugin_names('mnet');
- upgrade_fix_config_auth_plugin_defaults('mnet');
- upgrade_plugin_savepoint(true, 2017020700, 'auth', 'mnet');
- }
-
- // Automatically generated Moodle v3.3.0 release upgrade line.
- // Put any upgrade step following this.
-
- // Automatically generated Moodle v3.4.0 release upgrade line.
- // Put any upgrade step following this.
-
// Automatically generated Moodle v3.5.0 release upgrade line.
// Put any upgrade step following this.
function xmldb_auth_none_upgrade($oldversion) {
global $CFG, $DB;
- if ($oldversion < 2017020700) {
- // Convert info in config plugins from auth/none to auth_none.
- upgrade_fix_config_auth_plugin_names('none');
- upgrade_fix_config_auth_plugin_defaults('none');
- upgrade_plugin_savepoint(true, 2017020700, 'auth', 'none');
- }
-
- // Automatically generated Moodle v3.3.0 release upgrade line.
- // Put any upgrade step following this.
-
- // Automatically generated Moodle v3.4.0 release upgrade line.
- // Put any upgrade step following this.
-
// Automatically generated Moodle v3.5.0 release upgrade line.
// Put any upgrade step following this.
$dbman = $DB->get_manager();
- // Automatically generated Moodle v3.3.0 release upgrade line.
- // Put any upgrade step following this.
-
- // Automatically generated Moodle v3.4.0 release upgrade line.
- // Put any upgrade step following this.
-
// Automatically generated Moodle v3.5.0 release upgrade line.
// Put any upgrade step following this.
function xmldb_auth_shibboleth_upgrade($oldversion) {
global $CFG, $DB;
- if ($oldversion < 2017020700) {
- // Convert info in config plugins from auth/shibboleth to auth_shibboleth.
- upgrade_fix_config_auth_plugin_names('shibboleth');
- upgrade_fix_config_auth_plugin_defaults('shibboleth');
- upgrade_plugin_savepoint(true, 2017020700, 'auth', 'shibboleth');
- }
-
- // Automatically generated Moodle v3.3.0 release upgrade line.
- // Put any upgrade step following this.
-
- // Automatically generated Moodle v3.4.0 release upgrade line.
- // Put any upgrade step following this.
-
// Automatically generated Moodle v3.5.0 release upgrade line.
// Put any upgrade step following this.
This files describes API changes in /auth/* - plugins,
information provided here is intended especially for developers.
+=== 3.9 ===
+
+* The following functions, previously used (exclusively) by upgrade steps are not available anymore because of the upgrade cleanup performed for this version. See MDL-65809 for more info:
+ - upgrade_fix_config_auth_plugin_names()
+ - upgrade_fix_config_auth_plugin_defaults()
+
=== 3.7 ===
* get_password_change_info() method is added to the base class and returns an array containing the subject and body of the message
// show the course selector
echo $OUTPUT->header();
- echo $renderer->import_course_selector($url, $search);
+ $backup = new import_ui(false, array());
+ echo $renderer->progress_bar($backup->get_progress_bar());
+ $html = $renderer->import_course_selector($url, $search);
+ echo $html;
echo $OUTPUT->footer();
die();
}
// First execute the backup
$backup->execute();
+ // Before destroying the backup object, we still need to generate the progress bar.
+ $progressbar = $renderer->progress_bar($backup->get_progress_bar());
$backup->destroy();
unset($backup);
echo html_writer::end_tag('ul');
echo $OUTPUT->box_end();
}
+ echo $progressbar;
echo $OUTPUT->notification(get_string('importsuccess', 'backup'), 'notifysuccess');
echo $OUTPUT->continue_button(new moodle_url('/course/view.php', array('id'=>$course->id)));
--- /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/>.
+
+/**
+ * Automated backup tests.
+ *
+ * @package core_backup
+ * @copyright 2019 John Yao <johnyao@catalyst-au.net>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+global $CFG;
+require_once($CFG->dirroot . '/backup/util/helper/backup_cron_helper.class.php');
+require_once($CFG->libdir.'/cronlib.php');
+require_once($CFG->libdir . '/completionlib.php');
+
+/**
+ * Automated backup tests.
+ *
+ * @copyright 2019 John Yao <johnyao@catalyst-au.net>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class core_backup_automated_backup_testcase extends advanced_testcase {
+ /**
+ * @var \backup_cron_automated_helper
+ */
+ protected $backupcronautomatedhelper;
+
+ /**
+ * @var stdClass $course
+ */
+ protected $course;
+
+ protected function setUp() {
+ global $DB, $CFG;
+
+ $this->resetAfterTest(true);
+ $this->setAdminUser();
+ $CFG->enableavailability = true;
+ $CFG->enablecompletion = true;
+
+ // Getting a testable backup_cron_automated_helper class.
+ $this->backupcronautomatedhelper = new test_backup_cron_automated_helper();
+
+ $generator = $this->getDataGenerator();
+ $this->course = $generator->create_course(
+ array('format' => 'topics', 'numsections' => 3,
+ 'enablecompletion' => COMPLETION_ENABLED),
+ array('createsections' => true));
+ $forum = $generator->create_module('forum', array(
+ 'course' => $this->course->id));
+ $forum2 = $generator->create_module('forum', array(
+ 'course' => $this->course->id, 'completion' => COMPLETION_TRACKING_MANUAL));
+
+ // We need a grade, easiest is to add an assignment.
+ $assignrow = $generator->create_module('assign', array(
+ 'course' => $this->course->id));
+ $assign = new assign(context_module::instance($assignrow->cmid), false, false);
+ $item = $assign->get_grade_item();
+
+ // Make a test grouping as well.
+ $grouping = $generator->create_grouping(array('courseid' => $this->course->id,
+ 'name' => 'Grouping!'));
+
+ $availability = '{"op":"|","show":false,"c":[' .
+ '{"type":"completion","cm":' . $forum2->cmid .',"e":1},' .
+ '{"type":"grade","id":' . $item->id . ',"min":4,"max":94},' .
+ '{"type":"grouping","id":' . $grouping->id . '}' .
+ ']}';
+ $DB->set_field('course_modules', 'availability', $availability, array(
+ 'id' => $forum->cmid));
+ $DB->set_field('course_sections', 'availability', $availability, array(
+ 'course' => $this->course->id, 'section' => 1));
+ }
+
+ /**
+ * Tests the automated backup run when the there is course backup should be skipped.
+ */
+ public function test_automated_backup_skipped_run() {
+ global $DB;
+
+ // Enable automated back up.
+ set_config('backup_auto_active', true, 'backup');
+ set_config('backup_auto_weekdays', '1111111', 'backup');
+
+ // Start backup process.
+ $admin = get_admin();
+
+ // Backup entry should not exist.
+ $backupcourse = $DB->get_record('backup_courses', array('courseid' => $this->course->id));
+ $this->assertFalse($backupcourse);
+ $this->assertInstanceOf(
+ backup_cron_automated_helper::class,
+ $this->backupcronautomatedhelper->return_this()
+ );
+
+ $classobject = $this->backupcronautomatedhelper->return_this();
+
+ $method = new ReflectionMethod('\backup_cron_automated_helper', 'get_courses');
+ $method->setAccessible(true); // Allow accessing of private method.
+ $courses = $method->invoke($classobject);
+
+ $method = new ReflectionMethod('\backup_cron_automated_helper', 'check_and_push_automated_backups');
+ $method->setAccessible(true); // Allow accessing of private method.
+ $emailpending = $method->invokeArgs($classobject, [$courses, $admin]);
+
+ $coursename = $this->course->fullname;
+ $this->expectOutputRegex("/Skipping $coursename \(Not scheduled for backup until/");
+ $this->assertFalse($emailpending);
+
+ $backupcourse = $DB->get_record('backup_courses', array('courseid' => $this->course->id));
+ $this->assertNotNull($backupcourse->laststatus);
+ }
+
+ /**
+ * Tests the automated backup run when the there is course backup can be pushed to adhoc task.
+ */
+ public function test_automated_backup_push_run() {
+ global $DB;
+
+ // Enable automated back up.
+ set_config('backup_auto_active', true, 'backup');
+ set_config('backup_auto_weekdays', '1111111', 'backup');
+
+ $admin = get_admin();
+
+ $classobject = $this->backupcronautomatedhelper->return_this();
+
+ $method = new ReflectionMethod('\backup_cron_automated_helper', 'get_courses');
+ $method->setAccessible(true); // Allow accessing of private method.
+ $courses = $method->invoke($classobject);
+
+ // Create this backup course.
+ $backupcourse = new stdClass;
+ $backupcourse->courseid = $this->course->id;
+ $backupcourse->laststatus = backup_cron_automated_helper::BACKUP_STATUS_NOTYETRUN;
+ $DB->insert_record('backup_courses', $backupcourse);
+ $backupcourse = $DB->get_record('backup_courses', array('courseid' => $this->course->id));
+
+ // We now manually trigger a backup pushed to adhoc task.
+ // Make sure is in the past, which means should run now.
+ $backupcourse->nextstarttime = time() - 10;
+ $DB->update_record('backup_courses', $backupcourse);
+
+ $method = new ReflectionMethod('\backup_cron_automated_helper', 'check_and_push_automated_backups');
+ $method->setAccessible(true); // Allow accessing of private method.
+ $emailpending = $method->invokeArgs($classobject, [$courses, $admin]);
+ $this->assertTrue($emailpending);
+
+ $coursename = $this->course->fullname;
+ $this->expectOutputRegex("/Putting backup of $coursename in adhoc task queue/");
+
+ $backupcourse = $DB->get_record('backup_courses', array('courseid' => $this->course->id));
+ // Now this backup course status should be queued.
+ $this->assertEquals(backup_cron_automated_helper::BACKUP_STATUS_QUEUED, $backupcourse->laststatus);
+ }
+
+ /**
+ * Tests the automated backup inactive run.
+ */
+ public function test_inactive_run() {
+ backup_cron_automated_helper::run_automated_backup();
+ $this->expectOutputString("Checking automated backup status...INACTIVE\n");
+ }
+
+ /**
+ * Tests the invisible course being skipped.
+ */
+ public function test_should_skip_invisible_course() {
+ global $DB;
+
+ set_config('backup_auto_active', true, 'backup');
+ set_config('backup_auto_skip_hidden', true, 'backup');
+ set_config('backup_auto_weekdays', '1111111', 'backup');
+ // Create this backup course.
+ $backupcourse = new stdClass;
+ $backupcourse->courseid = $this->course->id;
+ // This is the status we believe last run was OK.
+ $backupcourse->laststatus = backup_cron_automated_helper::BACKUP_STATUS_SKIPPED;
+ $DB->insert_record('backup_courses', $backupcourse);
+ $backupcourse = $DB->get_record('backup_courses', array('courseid' => $this->course->id));
+
+ $this->assertTrue(course_change_visibility($this->course->id, false));
+ $course = $DB->get_record('course', array('id' => $this->course->id));
+ $this->assertEquals('0', $course->visible);
+ $classobject = $this->backupcronautomatedhelper->return_this();
+ $nextstarttime = backup_cron_automated_helper::calculate_next_automated_backup(null, time());
+
+ $method = new ReflectionMethod('\backup_cron_automated_helper', 'should_skip_course_backup');
+ $method->setAccessible(true); // Allow accessing of private method.
+ $skipped = $method->invokeArgs($classobject, [$backupcourse, $course, $nextstarttime]);
+
+ $this->assertTrue($skipped);
+ $this->expectOutputRegex("/Skipping $course->fullname \(Not visible\)/");
+ }
+
+ /**
+ * Tests the not modified course being skipped.
+ */
+ public function test_should_skip_not_modified_course_in_days() {
+ global $DB;
+
+ set_config('backup_auto_active', true, 'backup');
+ // Skip if not modified in two days.
+ set_config('backup_auto_skip_modif_days', 2, 'backup');
+ set_config('backup_auto_weekdays', '1111111', 'backup');
+
+ // Create this backup course.
+ $backupcourse = new stdClass;
+ $backupcourse->courseid = $this->course->id;
+ // This is the status we believe last run was OK.
+ $backupcourse->laststatus = backup_cron_automated_helper::BACKUP_STATUS_SKIPPED;
+ $backupcourse->laststarttime = time() - 2 * DAYSECS;
+ $backupcourse->lastendtime = time() - 1 * DAYSECS;
+ $DB->insert_record('backup_courses', $backupcourse);
+ $backupcourse = $DB->get_record('backup_courses', array('courseid' => $this->course->id));
+ $course = $DB->get_record('course', array('id' => $this->course->id));
+
+ $course->timemodified = time() - 2 * DAYSECS - 1;
+
+ $classobject = $this->backupcronautomatedhelper->return_this();
+ $nextstarttime = backup_cron_automated_helper::calculate_next_automated_backup(null, time());
+
+ $method = new ReflectionMethod('\backup_cron_automated_helper', 'should_skip_course_backup');
+ $method->setAccessible(true); // Allow accessing of private method.
+ $skipped = $method->invokeArgs($classobject, [$backupcourse, $course, $nextstarttime]);
+
+ $this->assertTrue($skipped);
+ $this->expectOutputRegex("/Skipping $course->fullname \(Not modified in the past 2 days\)/");
+ }
+
+ /**
+ * Tests the backup not modified course being skipped.
+ */
+ public function test_should_skip_not_modified_course_since_prev() {
+ global $DB;
+
+ set_config('backup_auto_active', true, 'backup');
+ // Skip if not modified in two days.
+ set_config('backup_auto_skip_modif_prev', 2, 'backup');
+ set_config('backup_auto_weekdays', '1111111', 'backup');
+
+ // Create this backup course.
+ $backupcourse = new stdClass;
+ $backupcourse->courseid = $this->course->id;
+ // This is the status we believe last run was OK.
+ $backupcourse->laststatus = backup_cron_automated_helper::BACKUP_STATUS_SKIPPED;
+ $backupcourse->laststarttime = time() - 2 * DAYSECS;
+ $backupcourse->lastendtime = time() - 1 * DAYSECS;
+ $DB->insert_record('backup_courses', $backupcourse);
+ $backupcourse = $DB->get_record('backup_courses', array('courseid' => $this->course->id));
+ $course = $DB->get_record('course', array('id' => $this->course->id));
+
+ $course->timemodified = time() - 2 * DAYSECS - 1;
+
+ $classobject = $this->backupcronautomatedhelper->return_this();
+ $nextstarttime = backup_cron_automated_helper::calculate_next_automated_backup(null, time());
+
+ $method = new ReflectionMethod('\backup_cron_automated_helper', 'should_skip_course_backup');
+ $method->setAccessible(true); // Allow accessing of private method.
+ $skipped = $method->invokeArgs($classobject, [$backupcourse, $course, $nextstarttime]);
+
+ $this->assertTrue($skipped);
+ $this->expectOutputRegex("/Skipping $course->fullname \(Not modified since previous backup\)/");
+ }
+}
+
+/**
+ * New backup_cron_automated_helper class for testing.
+ *
+ * This class extends the helper backup_cron_automated_helper class
+ * in order to utilise abstract class for testing.
+ *
+ * @package core
+ * @copyright 2019 John Yao <johnyao@catalyst-au.net>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class test_backup_cron_automated_helper extends backup_cron_automated_helper {
+ /**
+ * Returning this for testing.
+ */
+ public function return_this() {
+ return $this;
+ }
+}
<?php
-
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
const BACKUP_STATUS_WARNING = 4;
/** Course automated backup has yet to be run */
const BACKUP_STATUS_NOTYETRUN = 5;
+ /** Course automated backup has been added to adhoc task queue */
+ const BACKUP_STATUS_QUEUED = 6;
/** Run if required by the schedule set in config. Default. **/
const RUN_ON_SCHEDULE = 0;
/**
* Runs the automated backups if required
*
- * @global moodle_database $DB
+ * @param bool $rundirective
*/
public static function run_automated_backup($rundirective = self::RUN_ON_SCHEDULE) {
- global $CFG, $DB;
-
- $status = true;
- $emailpending = false;
$now = time();
- $config = get_config('backup');
-
- mtrace("Checking automated backup status",'...');
- $state = backup_cron_automated_helper::get_automated_backup_state($rundirective);
- if ($state === backup_cron_automated_helper::STATE_DISABLED) {
- mtrace('INACTIVE');
- return $state;
- } else if ($state === backup_cron_automated_helper::STATE_RUNNING) {
- mtrace('RUNNING');
- if ($rundirective == self::RUN_IMMEDIATELY) {
- mtrace('Automated backups are already running. If this script is being run by cron this constitues an error. You will need to increase the time between executions within cron.');
- } else {
- mtrace("automated backup are already running. Execution delayed");
- }
- return $state;
- } else {
- mtrace('OK');
- }
- backup_cron_automated_helper::set_state_running();
- mtrace("Getting admin info");
- $admin = get_admin();
- if (!$admin) {
- mtrace("Error: No admin account was found");
- $state = false;
+ $lock = self::get_automated_backup_lock($rundirective);
+ if (!$lock) {
+ return;
}
- if ($status) {
+ try {
mtrace("Checking courses");
mtrace("Skipping deleted courses", '...');
- mtrace(sprintf("%d courses", backup_cron_automated_helper::remove_deleted_courses_from_schedule()));
- }
-
- if ($status) {
-
+ mtrace(sprintf("%d courses", self::remove_deleted_courses_from_schedule()));
mtrace('Running required automated backups...');
cron_trace_time_and_memory();
- // This could take a while!
- core_php_time_limit::raise();
- raise_memory_limit(MEMORY_EXTRA);
-
- $nextstarttime = backup_cron_automated_helper::calculate_next_automated_backup(null, $now);
- $showtime = "undefined";
- if ($nextstarttime > 0) {
- $showtime = date('r', $nextstarttime);
+ mtrace("Getting admin info");
+ $admin = get_admin();
+ if (!$admin) {
+ mtrace("Error: No admin account was found");
+ return;
}
$rs = self::get_courses($now); // Get courses to backup.
- foreach ($rs as $course) {
- $backupcourse = $DB->get_record('backup_courses', array('courseid' => $course->id));
- if (!$backupcourse) {
- $backupcourse = new stdClass;
- $backupcourse->courseid = $course->id;
- $backupcourse->laststatus = self::BACKUP_STATUS_NOTYETRUN;
- $DB->insert_record('backup_courses', $backupcourse);
- $backupcourse = $DB->get_record('backup_courses', array('courseid' => $course->id));
- }
+ $emailpending = self::check_and_push_automated_backups($rs, $admin);
+ $rs->close();
- // The last backup is considered as successful when OK or SKIPPED.
- $lastbackupwassuccessful = ($backupcourse->laststatus == self::BACKUP_STATUS_SKIPPED ||
- $backupcourse->laststatus == self::BACKUP_STATUS_OK) && (
- $backupcourse->laststarttime > 0 && $backupcourse->lastendtime > 0);
+ // Send email to admin if necessary.
+ if ($emailpending) {
+ self::send_backup_status_to_admin($admin);
+ }
+ } finally {
+ // Everything is finished release lock.
+ $lock->release();
+ mtrace('Automated backups complete.');
+ }
+ }
- // Assume that we are not skipping anything.
- $skipped = false;
- $skippedmessage = '';
+ /**
+ * Gets the results from the last automated backup that was run based upon
+ * the statuses of the courses that were looked at.
+ *
+ * @return array
+ */
+ public static function get_backup_status_array() {
+ global $DB;
- // Check if we are going to be running the backup now.
- $shouldrunnow = (($backupcourse->nextstarttime > 0 && $backupcourse->nextstarttime < $now)
- || $rundirective == self::RUN_IMMEDIATELY);
+ $result = array(
+ self::BACKUP_STATUS_ERROR => 0,
+ self::BACKUP_STATUS_OK => 0,
+ self::BACKUP_STATUS_UNFINISHED => 0,
+ self::BACKUP_STATUS_SKIPPED => 0,
+ self::BACKUP_STATUS_WARNING => 0,
+ self::BACKUP_STATUS_NOTYETRUN => 0,
+ self::BACKUP_STATUS_QUEUED => 0,
+ );
- // If config backup_auto_skip_hidden is set to true, skip courses that are not visible.
- if ($shouldrunnow && $config->backup_auto_skip_hidden) {
- $skipped = ($config->backup_auto_skip_hidden && !$course->visible);
- $skippedmessage = 'Not visible';
- }
+ $statuses = $DB->get_records_sql('SELECT DISTINCT bc.laststatus,
+ COUNT(bc.courseid) AS statuscount
+ FROM {backup_courses} bc
+ GROUP BY bc.laststatus');
- // If config backup_auto_skip_modif_days is set to true, skip courses
- // that have not been modified since the number of days defined.
- if ($shouldrunnow && !$skipped && $lastbackupwassuccessful && $config->backup_auto_skip_modif_days) {
- $timenotmodifsincedays = $now - ($config->backup_auto_skip_modif_days * DAYSECS);
- // Check log if there were any modifications to the course content.
- $logexists = self::is_course_modified($course->id, $timenotmodifsincedays);
- $skipped = ($course->timemodified <= $timenotmodifsincedays && !$logexists);
- $skippedmessage = 'Not modified in the past '.$config->backup_auto_skip_modif_days.' days';
- }
+ foreach ($statuses as $status) {
+ if (empty($status->statuscount)) {
+ $status->statuscount = 0;
+ }
+ $result[(int)$status->laststatus] += $status->statuscount;
+ }
- // If config backup_auto_skip_modif_prev is set to true, skip courses
- // that have not been modified since previous backup.
- if ($shouldrunnow && !$skipped && $lastbackupwassuccessful && $config->backup_auto_skip_modif_prev) {
- // Check log if there were any modifications to the course content.
- $logexists = self::is_course_modified($course->id, $backupcourse->laststarttime);
- $skipped = ($course->timemodified <= $backupcourse->laststarttime && !$logexists);
- $skippedmessage = 'Not modified since previous backup';
- }
+ return $result;
+ }
- // Check if the course is not scheduled to run right now.
- if (!$shouldrunnow) {
- $backupcourse->nextstarttime = $nextstarttime;
- $DB->update_record('backup_courses', $backupcourse);