{
"coverage": false,
- "lint": "config"
+ "lint": "config",
+ "clean": true
}
admin_externalpage_setup('ssoaccesscontrol');
-echo $OUTPUT->header();
-
if (!extension_loaded('openssl')) {
print_error('requiresopenssl', 'mnet');
}
if (mnet_update_sso_access_control($idrec->username, $idrec->mnet_host_id, $accessctrl)) {
if ($accessctrl == 'allow') {
- redirect('access_control.php', get_string('ssl_acl_allow','mnet', array('uset'=>$idrec->username, 'host'=>$mnethosts[$idrec->mnet_host_id])));
- } elseif ($accessctrl == 'deny') {
- redirect('access_control.php', get_string('ssl_acl_deny','mnet', array('user'=>$idrec->username, 'host'=>$mnethosts[$idrec->mnet_host_id])));
+ redirect('access_control.php', get_string('ssl_acl_allow','mnet', array('user' => $idrec->username,
+ 'host' => $mnethosts[$idrec->mnet_host_id])));
+ } else if ($accessctrl == 'deny') {
+ redirect('access_control.php', get_string('ssl_acl_deny','mnet', array('user' => $idrec->username,
+ 'host' => $mnethosts[$idrec->mnet_host_id])));
}
}
break;
exit;
}
+echo $OUTPUT->header();
+
// Explain
echo $OUTPUT->box(get_string('ssoacldescr','mnet'));
// Are the needed bits enabled?
get_string('displayname', 'core_plugin'),
get_string('source', 'core_plugin'),
get_string('version', 'core_plugin'),
+ get_string('release', 'core_plugin'),
get_string('availability', 'core_plugin'),
get_string('actions', 'core_plugin'),
get_string('notes','core_plugin'),
);
- $table->headspan = array(1, 1, 1, 1, 2, 1);
+ $table->headspan = array(1, 1, 1, 1, 1, 2, 1);
$table->colclasses = array(
- 'pluginname', 'source', 'version', 'availability', 'settings', 'uninstall', 'notes'
+ 'pluginname', 'source', 'version', 'release', 'availability', 'settings', 'uninstall', 'notes'
);
foreach ($plugininfo as $type => $plugins) {
}
$version = new html_table_cell($plugin->versiondb);
+ $release = new html_table_cell($plugin->release);
$isenabled = $plugin->is_enabled();
if (is_null($isenabled)) {
$notes = new html_table_cell($requiredby.$updateinfo);
$row->cells = array(
- $pluginname, $source, $version, $availability, $settings, $uninstall, $notes
+ $pluginname, $source, $version, $release, $availability, $settings, $uninstall, $notes
);
$table->data[] = $row;
}
// This file defines settingpages and externalpages under the "appearance" category
-if ($hassiteconfig) { // speedup for non-admins, add all caps used on this page
+$capabilities = array(
+ 'moodle/my:configsyspages',
+ 'moodle/tag:manage'
+);
+
+if ($hassiteconfig or has_any_capability($capabilities, $systemcontext)) { // speedup for non-admins, add all caps used on this page
$ADMIN->add('appearance', new admin_category('themes', new lang_string('themes')));
// "themesettings" settingpage
$temp->add(new admin_setting_configcheckbox('doctonewwindow', new lang_string('doctonewwindow', 'admin'), new lang_string('configdoctonewwindow', 'admin'), 0));
$ADMIN->add('appearance', $temp);
- $temp = new admin_externalpage('mypage', new lang_string('mypage', 'admin'), $CFG->wwwroot . '/my/indexsys.php');
+ $temp = new admin_externalpage('mypage', new lang_string('mypage', 'admin'), $CFG->wwwroot . '/my/indexsys.php',
+ 'moodle/my:configsyspages');
$ADMIN->add('appearance', $temp);
- $temp = new admin_externalpage('profilepage', new lang_string('myprofile', 'admin'), $CFG->wwwroot . '/user/profilesys.php');
+ $temp = new admin_externalpage('profilepage', new lang_string('myprofile', 'admin'), $CFG->wwwroot . '/user/profilesys.php',
+ 'moodle/my:configsyspages');
$ADMIN->add('appearance', $temp);
// coursecontact is the person responsible for course - usually manages enrolments, receives notification, etc.
$ADMIN->add('appearance', $temp);
// link to tag management interface
- $ADMIN->add('appearance', new admin_externalpage('managetags', new lang_string('managetags', 'tag'), "$CFG->wwwroot/tag/manage.php"));
+ $ADMIN->add('appearance', new admin_externalpage('managetags', new lang_string('managetags', 'tag'), $CFG->wwwroot.'/tag/manage.php', 'moodle/tag:manage'));
$temp = new admin_settingpage('additionalhtml', new lang_string('additionalhtml', 'admin'));
$temp->add(new admin_setting_heading('additionalhtml_heading', new lang_string('additionalhtml_heading', 'admin'), new lang_string('additionalhtml_desc', 'admin')));
$temp->add(new admin_setting_configcheckbox('enabletgzbackups',
new lang_string('enabletgzbackups', 'admin'),
new lang_string('enabletgzbackups_desc', 'admin'), 0));
+ $temp->add(new admin_setting_php_extension_enabled('zlibenabled',
+ get_string('zlibenabled', 'admin'),
+ get_string('enabletgzbackups_nozlib', 'admin'), 'zlib'));
$ADMIN->add('experimental', $temp);
// Add common settings page
$temp = new admin_settingpage('managerepositoriescommon', new lang_string('commonrepositorysettings', 'repository'));
- $temp->add(new admin_setting_configtext('repositorycacheexpire', new lang_string('cacheexpire', 'repository'), new lang_string('configcacheexpire', 'repository'), 120));
+ $temp->add(new admin_setting_configtext('repositorycacheexpire', new lang_string('cacheexpire', 'repository'), new lang_string('configcacheexpire', 'repository'), 120, PARAM_INT));
+ $temp->add(new admin_setting_configtext('repositorygetfiletimeout', new lang_string('getfiletimeout', 'repository'), new lang_string('configgetfiletimeout', 'repository'), 30, PARAM_INT));
+ $temp->add(new admin_setting_configtext('repositorysyncfiletimeout', new lang_string('syncfiletimeout', 'repository'), new lang_string('configsyncfiletimeout', 'repository'), 1, PARAM_INT));
+ $temp->add(new admin_setting_configtext('repositorysyncimagetimeout', new lang_string('syncimagetimeout', 'repository'), new lang_string('configsyncimagetimeout', 'repository'), 3, PARAM_INT));
$temp->add(new admin_setting_configcheckbox('repositoryallowexternallinks', new lang_string('allowexternallinks', 'repository'), new lang_string('configallowexternallinks', 'repository'), 1));
$temp->add(new admin_setting_configcheckbox('legacyfilesinnewcourses', new lang_string('legacyfilesinnewcourses', 'admin'), new lang_string('legacyfilesinnewcourses_help', 'admin'), 0));
$temp->add(new admin_setting_configcheckbox('legacyfilesaddallowed', new lang_string('legacyfilesaddallowed', 'admin'), new lang_string('legacyfilesaddallowed_help', 'admin'), 1));
}
}
+// Add Calendar type settings.
+if ($hassiteconfig) {
+ $ADMIN->add('modules', new admin_category('calendartype', new lang_string('calendartypes', 'calendar')));
+ foreach (core_component::get_plugin_list_with_file('calendartype', 'settings.php') as $plugin => $settingspath) {
+ $settings = new admin_settingpage('calendartype_' . $plugin . '_settings', new lang_string('pluginname', 'calendartype_' . $plugin), 'moodle/site:config');
+ include($settingspath);
+ $ADMIN->add('calendartype', $settings);
+ }
+}
+
/// Add all local plugins - must be always last!
if ($hassiteconfig) {
$ADMIN->add('modules', new admin_category('localplugins', new lang_string('localplugins')));
$this->getSession()->wait($timeout, $javascript);
}
}
-
- /**
- * Goes to notification page ensuring site admin navigation is loaded.
- *
- * @Given /^I go to notifications page$/
- * @return Given[]
- */
- public function i_go_to_notifications_page() {
- if ($this->running_javascript()) {
- return array(
- new Given('I expand "' . get_string('administrationsite') . '" node'),
- new Given('I follow "' . get_string('notifications') . '"')
- );
- } else {
- return array(
- new Given('I follow "' . get_string('administrationsite') . '"')
- );
- }
- }
}
And I should not see "C_shortname Course fullname"
Scenario: Courses list with extended course names
- Given I go to notifications page
+ Given I expand "Site administration" node
+ And I expand "Appearance" node
And I click on "Courses" "link" in the "//div[@id='settingsnav']/descendant::li[contains(concat(' ', normalize-space(@class), ' '), ' type_setting ')][contains(., 'Appearance')]" "xpath_element"
And I check "Display extended course names"
When I press "Save changes"
// Getting $CFG data.
require_once(__DIR__ . '/../../../../config.php');
-// CFG->behat_prefix must be set and with value different than CFG->prefix and phpunit_prefix.
-if (empty($CFG->behat_prefix) ||
- ($CFG->behat_prefix == $CFG->prefix) ||
- (!empty($CFG->phpunit_prefix) && $CFG->behat_prefix == $CFG->phpunit_prefix)) {
- behat_error(BEHAT_EXITCODE_CONFIG,
- 'Define $CFG->behat_prefix in config.php with a value different than $CFG->prefix and $CFG->phpunit_prefix');
-}
-
-// CFG->behat_dataroot must be set and with value different than CFG->dataroot and phpunit_dataroot.
-if (empty($CFG->behat_dataroot) ||
- ($CFG->behat_dataroot == $CFG->dataroot) ||
- (!empty($CFG->phpunit_dataroot) && $CFG->behat_dataroot == $CFG->phpunit_dataroot)) {
- behat_error(BEHAT_EXITCODE_CONFIG,
- 'Define $CFG->behat_dataroot in config.php with a value different than $CFG->dataroot and $CFG->phpunit_dataroot');
-}
+// When we use the utilities we don't know how the site
+// will be accessed, so if neither $CFG->behat_switchcompletely or
+// $CFG->behat_wwwroot are set we must think that the site will
+// be accessed using the built-in server which is set by default
+// to localhost:8000. We need to do this to prevent uses of the production
+// wwwroot when the site is being installed / dropped...
+$CFG->behat_wwwroot = behat_get_wwwroot();
+
+// Checking the integrity of the provided $CFG->behat_* vars
+// to prevent conflicts with production and phpunit environments.
+behat_check_config_vars();
// Create behat_dataroot if it doesn't exists.
if (!file_exists($CFG->behat_dataroot)) {
$string['stepsdefinitionsfilters'] = 'Steps definitions';
$string['stepsdefinitionstype'] = 'Type';
$string['theninfo'] = 'Then. Checkings to ensure the outcomes are the expected ones';
-$string['unknownexceptioninfo'] = 'There was a problem with Selenium or the browser, try to upgrade Selenium to the latest version. Error: ';
+$string['unknownexceptioninfo'] = 'There was a problem with Selenium or your browser. Please ensure you are using the latest version of Selenium. Error:';
$string['viewsteps'] = 'Filter';
$string['wheninfo'] = 'When. Actions that provokes an event';
$string['wrongbehatsetup'] = 'Something is wrong with behat setup, ensure:<ul>
When I follow "Overview"
And I wait until the page is ready
And I wait "2" seconds
- And I hover ".region-content .generaltable td span" "css_element"
+ And I hover "#region-main .generaltable td span" "css_element"
Then I should see "I'm the description"
And "Grouping" "select" in the "region-main" "region" should be visible
And "Group" "select" should be visible
And "Change password" "link" should not be visible
And I should see "Filter groups by"
And I should not see "Filter groupssss by"
- And I should see "Group members" in the ".region-content table th.c1" "css_element"
- And I should not see "Group membersssss" in the ".region-content table th.c1" "css_element"
+ And I should see "Group members" in the "#region-main table th.c1" "css_element"
+ And I should not see "Group membersssss" in the "#region-main table th.c1" "css_element"
And I follow "Groups"
And the "#groupeditform #showcreateorphangroupform" "css_element" should be enabled
And the "#groupeditform #showeditgroupsettingsform" "css_element" should be disabled
-@tool @tool_behat
+@tool @tool_behat @_only_local
Feature: Set up contextual data for tests
In order to write tests quickly
As a developer
| fullname | shortname |
| Course 1 | C1 |
And the following "activities" exists:
- | activity | name | intro | course | idnumber |
- | assign | Test assignment name | Test assignment description | C1 | assign1 |
- | data | Test database name | Test database description | C1 | data1 |
+ | activity | name | intro | course | idnumber |
+ | assign | Test assignment name | Test assignment description | C1 | assign1 |
+ | assignment | Test assignment22 name | Test assignment22 description | C1 | assignment1 |
+ | book | Test book name | Test book description | C1 | book1 |
+ | chat | Test chat name | Test chat description | C1 | chat1 |
+ | choice | Test choice name | Test choice description | C1 | choice1 |
+ | data | Test database name | Test database description | C1 | data1 |
+ | feedback | Test feedback name | Test feedback description | C1 | feedback1 |
+ | folder | Test folder name | Test folder description | C1 | folder1 |
+ | forum | Test forum name | Test forum description | C1 | forum1 |
+ | glossary | Test glossary name | Test glossary description | C1 | glossary1 |
+ | imscp | Test imscp name | Test imscp description | C1 | imscp1 |
+ | label | Test label name | Test label description | C1 | label1 |
+ | lesson | Test lesson name | Test lesson description | C1 | lesson1 |
+ | lti | Test lti name | Test lti description | C1 | lti1 |
+ | page | Test page name | Test page description | C1 | page1 |
+ | quiz | Test quiz name | Test quiz description | C1 | quiz1 |
+ | resource | Test resource name | Test resource description | C1 | resource1 |
+ | scorm | Test scorm name | Test scorm description | C1 | scorm1 |
+ | survey | Test survey name | Test survey description | C1 | survey1 |
+ | url | Test url name | Test url description | C1 | url1 |
+ | wiki | Test wiki name | Test wiki description | C1 | wiki1 |
+ | workshop | Test workshop name | Test workshop description | C1 | workshop1 |
When I log in as "admin"
And I follow "Course 1"
Then I should see "Test assignment name"
+ # Assignment 2.2 module type is disabled by default
+ # And I should see "Test assignment22 name"
+ And I should see "Test book name"
+ And I should see "Test chat name"
+ And I should see "Test choice name"
And I should see "Test database name"
+ # Feedback module type is disabled by default
+ # And I should see "Test feedback name"
+ And I should see "Test folder name"
+ And I should see "Test forum name"
+ And I should see "Test glossary name"
+ And I should see "Test imscp name"
+ # We don't see label name, we see only description:
+ And I should see "Test label description"
+ And I should see "Test lesson name"
+ And I should see "Test lti name"
+ And I should see "Test page name"
+ And I should see "Test quiz name"
+ And I should see "Test resource name"
+ And I should see "Test scorm name"
+ And I should see "Test survey name"
+ And I should see "Test url name"
+ And I should see "Test wiki name"
+ And I should see "Test workshop name"
And I follow "Test assignment name"
And I should see "Test assignment description"
Then I should see "Close the quiz"
And I should see "Group mode"
And I should see "Grouping"
- And I should not see "Show more..." in the "region-main-box" "region"
+ And I should not see "Show more..." in the "region-main" "region"
And I should see "Show less..."
defined('MOODLE_INTERNAL') || die();
-$plugin->version = 2013110500;
+$plugin->version = 2013110501;
$plugin->requires = 2013110500; // Requires Moodle 2.5.
$plugin->component = 'tool_behat';
// Put the contexts into a tree structure.
foreach ($contexts as $conid => $con) {
$context = context::instance_by_id($conid);
- $parentcontext = $context->get_parent_context();
- if ($parentcontext) {
- $contexts[$parentcontext->id]->children[] = $conid;
+ try {
+ $parentcontext = $context->get_parent_context();
+ if ($parentcontext) { // Will be false if $context is the system context.
+ $contexts[$parentcontext->id]->children[] = $conid;
+ }
+ } catch (dml_missing_record_exception $e) {
+ // Ignore corrupt context tree structure here. Don't let it break
+ // showing the rest of the report.
+ continue;
}
}
$table->attributes['class'] = 'comparisontable';
$table->head = array(' ');
foreach ($roles as $role) {
- $url = new moodle_url('/admin/roles/override.php', array('contextid' => $contextid, 'roleid' => $role->id));
+ $url = new moodle_url('/admin/roles/define.php', array('action' => 'view', 'roleid' => $role->id));
$table->head[] = html_writer::div(html_writer::link($url, $role->localname));
}
$table->data = array();
// Put any upgrade step following this.
+ // Moodle v2.6.0 release upgrade line.
+ // Put any upgrade step following this.
+
return true;
}
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
<stringProp name="HTTPSampler.path">${sitepath}/mod/forum/post.php</stringProp>
<stringProp name="HTTPSampler.method">POST</stringProp>
- <boolProp name="HTTPSampler.follow_redirects">true</boolProp>
+ <boolProp name="HTTPSampler.follow_redirects">false</boolProp>
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
<boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
$string['pluginname'] = 'Language packs';
$string['purgestringcaches'] = 'Purge string caches';
$string['remotelangnotavailable'] = 'Because Moodle cannot connect to download.moodle.org, it is not possible for language packs to be installed automatically. Please download the appropriate ZIP file(s) from <a href="http://download.moodle.org/langpack/">download.moodle.org/langpack</a>, copy them to your {$a} directory and unzip them manually.';
-$string['uninstall'] = 'Uninstall selected language pack(s)';
+$string['uninstall'] = 'Uninstall selected language pack';
$string['uninstallconfirm'] = 'You are about to completely uninstall language pack {$a}, are you sure?';
$string['updatelangs'] = 'Update all installed language packs';
$conditions6 = '( '.implode(' OR ', $keywordfull6).' )';
$conditions7 = '( '.implode(' OR ', $keywordfull7).' )';
- $sql = "SELECT * FROM {user} WHERE deleted = 0 AND id <> :userid AND $conditions"; // Exclude oneself
- $sql2 = "SELECT u.*, p.summary FROM {user} AS u, {post} AS p WHERE $conditions2 AND u.deleted = 0 AND u.id=p.userid AND u.id <> :userid";
- $sql3 = "SELECT u.*, p.subject as postsubject FROM {user} AS u, {post} AS p WHERE $conditions3 AND u.deleted = 0 AND u.id=p.userid AND u.id <> :userid";
- $sql4 = "SELECT u.*, c.content FROM {user} AS u, {comments} AS c WHERE $conditions4 AND u.deleted = 0 AND u.id=c.userid AND u.id <> :userid";
- $sql5 = "SELECT u.*, m.fullmessage FROM {user} AS u, {message} AS m WHERE $conditions5 AND u.deleted = 0 AND u.id=m.useridfrom AND u.id <> :userid";
- $sql6 = "SELECT u.*, fp.message FROM {user} AS u, {forum_posts} AS fp WHERE $conditions6 AND u.deleted = 0 AND u.id=fp.userid AND u.id <> :userid";
- $sql7 = "SELECT u.*, fp.subject FROM {user} AS u, {forum_posts} AS fp WHERE $conditions7 AND u.deleted = 0 AND u.id=fp.userid AND u.id <> :userid";
+ $sql = "SELECT *
+ FROM {user}
+ WHERE deleted = 0
+ AND id <> :userid
+ AND $conditions"; // Exclude oneself
+ $sql2 = "SELECT u.*, p.summary
+ FROM {user} u, {post} p
+ WHERE $conditions2
+ AND u.deleted = 0
+ AND u.id=p.userid
+ AND u.id <> :userid";
+ $sql3 = "SELECT u.*, p.subject AS postsubject
+ FROM {user} u, {post} p
+ WHERE $conditions3
+ AND u.deleted = 0
+ AND u.id=p.userid
+ AND u.id <> :userid";
+ $sql4 = "SELECT u.*, c.content
+ FROM {user} u, {comments} c
+ WHERE $conditions4
+ AND u.deleted = 0
+ AND u.id=c.userid
+ AND u.id <> :userid";
+ $sql5 = "SELECT u.*, m.fullmessage
+ FROM {user} u, {message} m
+ WHERE $conditions5
+ AND u.deleted = 0
+ AND u.id=m.useridfrom
+ AND u.id <> :userid";
+ $sql6 = "SELECT u.*, fp.message
+ FROM {user} u, {forum_posts} fp
+ WHERE $conditions6
+ AND u.deleted = 0
+ AND u.id=fp.userid
+ AND u.id <> :userid";
+ $sql7 = "SELECT u.*, fp.subject
+ FROM {user} u, {forum_posts} fp
+ WHERE $conditions7
+ AND u.deleted = 0
+ AND u.id=fp.userid
+ AND u.id <> :userid";
$spamusers_desc = $DB->get_recordset_sql($sql, $params);
$spamusers_blog = $DB->get_recordset_sql($sql2, $params);
tool_uploadcourse_processor::MODE_UPDATE_ONLY => get_string('updateonly', 'tool_uploadcourse')
);
$mform->addElement('select', 'options[mode]', get_string('mode', 'tool_uploadcourse'), $choices);
+ $mform->addHelpButton('options[mode]', 'mode', 'tool_uploadcourse');
$choices = array(
tool_uploadcourse_processor::UPDATE_NOTHING => get_string('nochanges', 'tool_uploadcourse'),
$mform->setDefault('options[updatemode]', tool_uploadcourse_processor::UPDATE_NOTHING);
$mform->disabledIf('options[updatemode]', 'options[mode]', 'eq', tool_uploadcourse_processor::MODE_CREATE_NEW);
$mform->disabledIf('options[updatemode]', 'options[mode]', 'eq', tool_uploadcourse_processor::MODE_CREATE_ALL);
+ $mform->addHelpButton('options[updatemode]', 'updatemode', 'tool_uploadcourse');
$mform->addElement('selectyesno', 'options[allowdeletes]', get_string('allowdeletes', 'tool_uploadcourse'));
$mform->setDefault('options[allowdeletes]', 0);
$mform->disabledIf('options[allowdeletes]', 'options[mode]', 'eq', tool_uploadcourse_processor::MODE_CREATE_NEW);
$mform->disabledIf('options[allowdeletes]', 'options[mode]', 'eq', tool_uploadcourse_processor::MODE_CREATE_ALL);
+ $mform->addHelpButton('options[allowdeletes]', 'allowdeletes', 'tool_uploadcourse');
$mform->addElement('selectyesno', 'options[allowrenames]', get_string('allowrenames', 'tool_uploadcourse'));
$mform->setDefault('options[allowrenames]', 0);
$mform->disabledIf('options[allowrenames]', 'options[mode]', 'eq', tool_uploadcourse_processor::MODE_CREATE_NEW);
$mform->disabledIf('options[allowrenames]', 'options[mode]', 'eq', tool_uploadcourse_processor::MODE_CREATE_ALL);
+ $mform->addHelpButton('options[allowrenames]', 'allowrenames', 'tool_uploadcourse');
$mform->addElement('selectyesno', 'options[allowresets]', get_string('allowresets', 'tool_uploadcourse'));
$mform->setDefault('options[allowresets]', 0);
$mform->disabledIf('options[allowresets]', 'options[mode]', 'eq', tool_uploadcourse_processor::MODE_CREATE_NEW);
$mform->disabledIf('options[allowresets]', 'options[mode]', 'eq', tool_uploadcourse_processor::MODE_CREATE_ALL);
+ $mform->addHelpButton('options[allowresets]', 'allowresets', 'tool_uploadcourse');
}
}
$mform->addElement('header', 'generalhdr', get_string('general'));
- $mform->addElement('filepicker', 'coursefile', get_string('file'));
+ $mform->addElement('filepicker', 'coursefile', get_string('coursefile', 'tool_uploadcourse'));
$mform->addRule('coursefile', null, 'required');
+ $mform->addHelpButton('coursefile', 'coursefile', 'tool_uploadcourse');
$choices = csv_import_reader::get_delimiter_list();
$mform->addElement('select', 'delimiter_name', get_string('csvdelimiter', 'tool_uploadcourse'), $choices);
} else {
$mform->setDefault('delimiter_name', 'comma');
}
+ $mform->addHelpButton('delimiter_name', 'csvdelimiter', 'tool_uploadcourse');
$choices = core_text::get_encodings();
$mform->addElement('select', 'encoding', get_string('encoding', 'tool_uploadcourse'), $choices);
$mform->setDefault('encoding', 'UTF-8');
+ $mform->addHelpButton('encoding', 'encoding', 'tool_uploadcourse');
$choices = array('10' => 10, '20' => 20, '100' => 100, '1000' => 1000, '100000' => 100000);
$mform->addElement('select', 'previewrows', get_string('rowpreviewnum', 'tool_uploadcourse'), $choices);
$mform->setType('previewrows', PARAM_INT);
+ $mform->addHelpButton('previewrows', 'rowpreviewnum', 'tool_uploadcourse');
$this->add_import_options();
$mform->disabledIf('options[reset]', 'options[mode]', 'eq', tool_uploadcourse_processor::MODE_CREATE_NEW);
$mform->disabledIf('options[reset]', 'options[mode]', 'eq', tool_uploadcourse_processor::MODE_CREATE_ALL);
$mform->disabledIf('options[reset]', 'options[allowresets]', 'eq', 0);
+ $mform->addHelpButton('options[reset]', 'reset', 'tool_uploadcourse');
// Default values.
$mform->addElement('header', 'defaultheader', get_string('defaultvalues', 'tool_uploadcourse'));
*/
$string['allowdeletes'] = 'Allow deletes';
+$string['allowdeletes_help'] = 'Whether the delete field is accepted or not.';
$string['allowrenames'] = 'Allow renames';
+$string['allowrenames_help'] = 'Whether the rename field is accepted or not.';
$string['allowresets'] = 'Allow resets';
+$string['allowresets_help'] = 'Whether the reset field is accepted or not.';
$string['cachedef_helper'] = 'Helper caching';
$string['cannotdeletecoursenotexist'] = 'Cannot delete a course that does not exist';
$string['cannotgenerateshortnameupdatemode'] = 'Cannot generate a shortname when updates are allowed';
$string['coursedeletionnotallowed'] = 'Course deletion is not allowed';
$string['coursedoesnotexistandcreatenotallowed'] = 'The course does not exist and creating course is not allowed';
$string['courseexistsanduploadnotallowed'] = 'The course exists and update is not allowed';
+$string['coursefile'] = 'File';
+$string['coursefile_help'] = 'This file must be a CSV file.';
$string['courseidnumberincremented'] = 'Course ID number incremented {$a->from} -> {$a->to}';
$string['courseprocess'] = 'Course process';
$string['courserenamed'] = 'Course renamed';
$string['createnew'] = 'Create new courses only, skip existing ones';
$string['createorupdate'] = 'Create new courses, or update existing ones';
$string['csvdelimiter'] = 'CSV delimiter';
+$string['csvdelimiter_help'] = 'CSV delimiter of the CSV file.';
$string['csvfileerror'] = 'There is something wrong with the format of the CSV file. Please check the number of headings and columns match, and that the delimiter and file encoding are correct: {$a}';
$string['csvline'] = 'Line';
$string['defaultvalues'] = 'Default course values';
$string['encoding'] = 'Encoding';
+$string['encoding_help'] = 'Encoding of the CSV file.';
$string['errorwhilerestoringcourse'] = 'Error while restoring the course';
$string['errorwhiledeletingcourse'] = 'Error while deleting the course';
$string['generatedshortnameinvalid'] = 'The generated shortname is invalid';
$string['missingmandatoryfields'] = 'Missing value for mandatory fields: {$a}';
$string['missingshortnamenotemplate'] = 'Missing shortname and shortname template not set';
$string['mode'] = 'Upload mode';
+$string['mode_help'] = 'This allows you to specify if courses can be created and/or updated.';
$string['nochanges'] = 'No changes';
$string['pluginname'] = 'Course upload';
$string['preview'] = 'Preview';
$string['reset'] = 'Reset course after upload';
+$string['reset_help'] = 'Whether to reset the course after creating/updating it.';
$string['result'] = 'Result';
$string['restoreafterimport'] = 'Restore after import';
$string['rowpreviewnum'] = 'Preview rows';
+$string['rowpreviewnum_help'] = 'Number of rows from the CSV file that will be previewed in the next page. This option exists in
+order to limit the next page size.';
$string['shortnametemplate'] = 'Template to generate a shortname';
$string['shortnametemplate_help'] = 'The short name of the course is displayed in the navigation. You may use template syntax here (%f = fullname, %i = idnumber), or enter an initial value that is incremented.';
$string['templatefile'] = 'Restore from this file after upload';
$string['unknownimportmode'] = 'Unknown import mode';
$string['updatemissing'] = 'Fill in missing items from CSV data and defaults';
$string['updatemode'] = 'Update mode';
+$string['updatemode_help'] = 'If you allow courses to be updated, you also have to tell the tool what to update the courses with.';
$string['updatemodedoessettonothing'] = 'Update mode does not allow anything to be updated';
$string['updateonly'] = 'Only update existing courses';
$string['updatewithdataordefaults'] = 'Update with CSV data and defaults';
* Each line of the file contains one record
* Each record is a series of data separated by commas (or other delimiters)
* The first record contains a list of fieldnames defining the format of the rest of the file
-* Required fieldnames are shortname, fullname, summary and category';
+* Required fieldnames are shortname, fullname, and category';
$string['uploadcoursespreview'] = 'Upload courses preview';
$string['uploadcoursesresult'] = 'Upload courses results';
$content = $mform1->get_file_content('userfile');
$readcount = $cir->load_csv_content($content, $formdata->encoding, $formdata->delimiter_name);
+ $csvloaderror = $cir->get_error();
unset($content);
- if ($readcount === false) {
- print_error('csvloaderror', '', $returnurl);
- } else if ($readcount == 0) {
- print_error('csvemptyfile', 'error', $returnurl);
+ if (!is_null($csvloaderror)) {
+ print_error('csvloaderror', '', $returnurl, $csvloaderror);
}
// test if columns ok
$filecolumns = uu_validate_user_upload_columns($cir, $STD_FIELDS, $PRF_FIELDS, $returnurl);
$user->$key['text'] = $value;
$user->$key['format'] = FORMAT_MOODLE;
} else {
- $user->$key = $value;
+ $user->$key = trim($value);
}
} else {
- $user->$key = $value;
+ $user->$key = trim($value);
}
if (in_array($key, $upt->columns)) {
continue;
}
if (!property_exists($user, $column) or !property_exists($existinguser, $column)) {
- // this should never happen
- debugging("Could not find $column on the user objects", DEBUG_DEVELOPER);
continue;
}
if ($updatetype == UU_UPDATE_MISSING) {
$status = null;
if (isset($user->{'enrolstatus'.$i})) {
- $enrolstatus = trim($user->{'enrolstatus'.$i});
+ $enrolstatus = $user->{'enrolstatus'.$i};
if ($enrolstatus == '') {
$status = null;
} else if ($enrolstatus === (string)ENROL_USER_ACTIVE) {
$rowcols = array();
$rowcols['line'] = $linenum;
foreach($fields as $key => $field) {
- $rowcols[$filecolumns[$key]] = s($field);
+ $rowcols[$filecolumns[$key]] = s(trim($field));
}
$rowcols['status'] = array();
}
if (isset($rowcols['city'])) {
- $rowcols['city'] = trim($rowcols['city']);
+ $rowcols['city'] = $rowcols['city'];
}
// Check if rowcols have custom profile field with correct data and update error state.
$noerror = uu_check_custom_profile_data($rowcols) && $noerror;
'noviolatedforeignkeysfound' => 'tool_xmldb',
'violatedforeignkeysfound' => 'tool_xmldb',
'violations' => 'tool_xmldb',
+ 'unknowntable' => 'tool_xmldb',
+ 'unknownfield' => 'tool_xmldb',
));
}
}
$o.=' <li>' . $this->str['key'] . ': ' . $xmldb_key->readableInfo() . ' ';
+ $reftable = $xmldb_key->getRefTable();
+ if (!$dbman->table_exists($reftable)) {
+ $o.='<font color="red">' . $this->str['unknowntable'] . '</font>';
+ // Add the missing index to the list
+ $violation = new stdClass();
+ $violation->string = 'fkunknowntable';
+ $violation->table = $xmldb_table;
+ $violation->key = $xmldb_key;
+ $violation->reftable = $reftable;
+ $violatedkeys[] = $violation;
+ continue;
+ }
+
// Work out the SQL to find key violations.
$keyfields = $xmldb_key->getFields();
$reffields = $xmldb_key->getRefFields();
$nullnessconditions = array();
$params = array();
foreach ($keyfields as $i => $field) {
+ if (!$dbman->field_exists($reftable, $reffields[$i])) {
+ $o.='<font color="red">' . $this->str['unknownfield'] . '</font>';
+ // Add the missing index to the list
+ $violation = new stdClass();
+ $violation->string = 'fkunknownfield';
+ $violation->table = $xmldb_table;
+ $violation->key = $xmldb_key;
+ $violation->reftable = $reftable;
+ $violation->reffield = $reffields[$i];
+ $violatedkeys[] = $violation;
+ continue 2;
+ }
+
$joinconditions[] = 't1.' . $field . ' = t2.' . $reffields[$i];
$xmldb_field = $xmldb_table->getField($field);
$default = $xmldb_field->getDefault();
}
$nullnessconditions[] = 't2.id IS NULL';
$sql = 'SELECT count(1) FROM {' . $xmldb_table->getName() .
- '} t1 LEFT JOIN {' . $xmldb_key->getRefTable() . '} t2 ON ' .
+ '} t1 LEFT JOIN {' . $reftable . '} t2 ON ' .
implode(' AND ', $joinconditions) . ' WHERE ' .
implode(' AND ', $nullnessconditions);
$o.='<font color="red">' . $this->str['violations'] . '</font>';
// Add the missing index to the list
$violation = new stdClass;
+ $violation->string = 'fkviolationdetails';
$violation->table = $xmldb_table;
$violation->key = $xmldb_key;
$violation->numviolations = $violations;
$violation->tablename = $violation->table->getName();
$violation->keyname = $violation->key->getName();
- $r.= ' <li>' .get_string('fkviolationdetails', 'tool_xmldb', $violation) .
- '<pre>' . s($violation->sql) . '; ' . s($violation->sqlparams) . '</pre></li>';
+ $r.= ' <li>' .get_string($violation->string, 'tool_xmldb', $violation);
+ if (!empty($violation->sql)) {
+ $r.= '<pre>' . s($violation->sql) . '; ' . s($violation->sqlparams) . '</pre>';
+ }
+ $r.= '</li>';
}
$r.= ' </ul>';
} else {
$string['fieldsusedinkey'] = 'This field is used as key.';
$string['filemodifiedoutfromeditor'] = 'Warning: File locally modified while using the XMLDB Editor. Saving will overwrite local changes.';
$string['filenotwriteable'] = 'File not writeable';
+$string['fkunknownfield'] = 'Foreign key {$a->keyname} on table {$a->tablename} points to a non-existent field {$a->reffield} in referenced table {$a->reftable}.';
+$string['fkunknowntable'] = 'Foreign key {$a->keyname} on table {$a->tablename} points to a non-existent table {$a->reftable}.';
$string['fkviolationdetails'] = 'Foreign key {$a->keyname} on table {$a->tablename} is violated by {$a->numviolations} out of {$a->numrows} rows.';
$string['floatincorrectdecimals'] = 'Incorrect number of decimals for float field';
$string['floatincorrectlength'] = 'Incorrect length for float field';
$string['table'] = 'Table';
$string['tablenameempty'] = 'The table name cannot be empty';
$string['tables'] = 'Tables';
+$string['unknownfield'] = 'Refers to an unknown field';
+$string['unknowntable'] = 'Refers to an unknown table';
$string['unload'] = 'Unload';
$string['up'] = 'Up';
$string['view'] = 'View';
upgrade_plugin_savepoint(true, 2013091700, 'auth', 'cas');
}
+ // Moodle v2.6.0 release upgrade line.
+ // Put any upgrade step following this.
+
return true;
}
* @return moodle_url
*/
function change_password_url() {
- if ($this->is_internal()) {
+ if ($this->is_internal() || empty($this->config->changepasswordurl)) {
// Standard form.
return null;
} else {
* @return moodle_url
*/
function change_password_url() {
- return new moodle_url($this->config->changepasswordurl);
+ if (!empty($this->config->changepasswordurl)) {
+ return new moodle_url($this->config->changepasswordurl);
+ } else {
+ return null;
+ }
}
/**
*/
function change_password_url() {
if (empty($this->config->stdchangepassword)) {
- return new moodle_url($this->config->changepasswordurl);
+ if (!empty($this->config->changepasswordurl)) {
+ return new moodle_url($this->config->changepasswordurl);
+ } else {
+ return null;
+ }
} else {
return null;
}
upgrade_plugin_savepoint(true, 2013052100, 'auth', 'ldap');
}
+ // Moodle v2.6.0 release upgrade line.
+ // Put any upgrade step following this.
+
return true;
}
// Put any upgrade step following this.
+ // Moodle v2.6.0 release upgrade line.
+ // Put any upgrade step following this.
+
return true;
}
// Put any upgrade step following this.
+ // Moodle v2.6.0 release upgrade line.
+ // Put any upgrade step following this.
+
return true;
}
* @return moodle_url
*/
function change_password_url() {
- return new moodle_url($this->config->changepasswordurl);
+ if (!empty($this->config->changepasswordurl)) {
+ return new moodle_url($this->config->changepasswordurl);
+ } else {
+ return null;
+ }
}
/**
This files describes API changes in /auth/* - plugins,
information provided here is intended especially for developers.
+=== 2.7 ===
+
+* If you are returning a url in method change_password_url() from config, please make sure it is set before trying to use it.
+
=== 2.6 ===
* can_be_manually_set() - This function was introduced in the base class and returns false by default. If overriden by
const OPERATION_RESTORE ='restore';// We are performing one restore
// Version (to keep CFG->backup_version (and release) updated automatically)
- const VERSION = 2013110500;
- const RELEASE = '2.6';
+ const VERSION = 2013111800;
+ const RELEASE = '2.7';
}
/*
} else {
$params['instance'] = 0;
}
- $sql = 'SELECT id FROM {event} WHERE name = ? AND courseid = ? AND
- repeatid = ? AND modulename = ? AND timestart = ? AND timeduration =?
- AND ' . $DB->sql_compare_text('description', 255) . ' = ' . $DB->sql_compare_text('?', 255);
+ $sql = "SELECT id
+ FROM {event}
+ WHERE " . $DB->sql_compare_text('name', 255) . " = " . $DB->sql_compare_text('?', 255) . "
+ AND courseid = ?
+ AND repeatid = ?
+ AND modulename = ?
+ AND timestart = ?
+ AND timeduration = ?
+ AND " . $DB->sql_compare_text('description', 255) . " = " . $DB->sql_compare_text('?', 255);
$arg = array ($params['name'], $params['courseid'], $params['repeatid'], $params['modulename'], $params['timestart'], $params['timeduration'], $params['description']);
$result = $DB->record_exists_sql($sql, $arg);
if (empty($result)) {
$this->currents[] = 0;
$this->parentcounts[] = $parentcount;
$this->update_progress();
- $lastprogresstime = $this->get_time();
}
/**
*/
function __construct(base_ui_stage $uistage, $action=null, $customdata=null, $method='post', $target='', $attributes=null, $editable=true) {
$this->uistage = $uistage;
+ // Add a class to the attributes to prevent the default collapsible behaviour.
+ if (!$attributes) {
+ $attributes = array();
+ }
+ $attributes['class'] = 'unresponsive';
+ if (!isset($attributes['enctype'])) {
+ $attributes['enctype'] = 'application/x-www-form-urlencoded'; // Enforce compatibility with our max_input_vars hack.
+ }
parent::__construct($action, $customdata, $method, $target, $attributes, $editable);
}
/**
$html .= html_writer::start_tag('form', array(
'action' => $url->out_omit_querystring(),
'class' => 'backup-restore',
+ 'enctype' => 'application/x-www-form-urlencoded', // Enforce compatibility with our max_input_vars hack.
'method' => 'post'));
foreach ($url->params() as $name => $value) {
$html .= html_writer::empty_tag('input', array(
| Description | Test database description |
And I open "Test database name" actions menu
When I click on "Duplicate" "link" in the "Test database name" activity
+ And I open "Test database name" actions menu
And I click on "Edit settings" "link" in the "Test database name" activity
And I fill the moodle form with:
| Name | Original database name |
* @param object $badge A badge which is notified about.
*/
function badge_assemble_notification(stdClass $badge) {
- global $CFG, $DB;
-
- $admin = get_admin();
- $userfrom = new stdClass();
- $userfrom->id = $admin->id;
- $userfrom->email = !empty($CFG->badges_defaultissuercontact) ? $CFG->badges_defaultissuercontact : $admin->email;
- $userfrom->firstname = !empty($CFG->badges_defaultissuername) ? $CFG->badges_defaultissuername : $admin->firstname;
- $userfrom->lastname = !empty($CFG->badges_defaultissuername) ? '' : $admin->lastname;
+ global $DB;
+
+ $userfrom = core_user::get_noreply_user();
$userfrom->maildisplay = true;
if ($msgs = $DB->get_records_select('badge_issued', 'issuernotified IS NULL AND badgeid = ?', array($badge->id))) {
// Create a message object.
$eventdata = new stdClass();
$eventdata->component = 'moodle';
- $eventdata->name = 'instantmessage';
+ $eventdata->name = 'badgecreatornotice';
$eventdata->userfrom = $userfrom;
$eventdata->userto = $creator;
$eventdata->notification = 1;
$eventdata->subject = $creatorsubject;
- $eventdata->fullmessage = $creatormessage;
+ $eventdata->fullmessage = format_text_email($creatormessage, FORMAT_HTML);
$eventdata->fullmessageformat = FORMAT_PLAIN;
- $eventdata->fullmessagehtml = format_text($creatormessage, FORMAT_HTML);
- $eventdata->smallmessage = '';
+ $eventdata->fullmessagehtml = $creatormessage;
+ $eventdata->smallmessage = $creatorsubject;
message_send($eventdata);
}
$mform->addElement('advcheckbox', 'attachment', get_string('attachment', 'badges'), '', null, array(0, 1));
$mform->addHelpButton('attachment', 'attachment', 'badges');
+ if (empty($CFG->allowattachments)) {
+ $mform->freeze('attachment');
+ }
$options = array(
BADGE_MESSAGE_NEVER => get_string('never'),
}
public function test_badge_awards() {
+ $this->preventResetByRollback(); // Messaging is not compatible with transactions.
$badge = new badge($this->badgeid);
$user1 = $this->getDataGenerator()->create_user();
* Test badges observer when course module completion event id fired.
*/
public function test_badges_observer_course_module_criteria_review() {
+ $this->preventResetByRollback(); // Messaging is not compatible with transactions.
$badge = new badge($this->coursebadge);
$this->assertFalse($badge->is_issued($this->user->id));
* Test badges observer when course_completed event is fired.
*/
public function test_badges_observer_course_criteria_review() {
+ $this->preventResetByRollback(); // Messaging is not compatible with transactions.
$badge = new badge($this->coursebadge);
$this->assertFalse($badge->is_issued($this->user->id));
* Test badges observer when user_updated event is fired.
*/
public function test_badges_observer_profile_criteria_review() {
+ $this->preventResetByRollback(); // Messaging is not compatible with transactions.
$badge = new badge($this->coursebadge);
$this->assertFalse($badge->is_issued($this->user->id));
* Test badges assertion generated when a badge is issued.
*/
public function test_badges_assertion() {
+ $this->preventResetByRollback(); // Messaging is not compatible with transactions.
$badge = new badge($this->coursebadge);
$this->assertFalse($badge->is_issued($this->user->id));
--- /dev/null
+@block @block_activity_modules @_only_local
+Feature: Block activity modules
+ In order to overview activity modules in a course
+ As a manager
+ I can add activities block in a course or on the frontpage
+
+ Background:
+ Given I log in as "admin"
+ And I expand "Site administration" node
+ And I expand "Plugins" node
+ And I expand "Activity modules" node
+ And I follow "Manage activities"
+ And I click on "//a[@title=\"Show\"]" "xpath_element" in the "Feedback" "table_row"
+ And I click on "//a[@title=\"Show\"]" "xpath_element" in the "Assignment (2.2)" "table_row"
+
+ Scenario: Add activities block on the frontpage
+ And the following "activities" exists:
+ | activity | name | intro | course | idnumber |
+ | assign | Frontpage assignment name | Frontpage assignment description | Acceptance test site | assign0 |
+ | assignment | Frontpage assignment22 name | Frontpage assignment22 description | Acceptance test site | assignment0 |
+ | book | Frontpage book name | Frontpage book description | Acceptance test site | book0 |
+ | chat | Frontpage chat name | Frontpage chat description | Acceptance test site | chat0 |
+ | choice | Frontpage choice name | Frontpage choice description | Acceptance test site | choice0 |
+ | data | Frontpage database name | Frontpage database description | Acceptance test site | data0 |
+ | feedback | Frontpage feedback name | Frontpage feedback description | Acceptance test site | feedback0 |
+ | forum | Frontpage forum name | Frontpage forum description | Acceptance test site | forum0 |
+ | label | Frontpage label name | Frontpage label description | Acceptance test site | label0 |
+ | lti | Frontpage lti name | Frontpage lti description | Acceptance test site | lti0 |
+ | page | Frontpage page name | Frontpage page description | Acceptance test site | page0 |
+ | quiz | Frontpage quiz name | Frontpage quiz description | Acceptance test site | quiz0 |
+ | resource | Frontpage resource name | Frontpage resource description | Acceptance test site | resource0 |
+ | imscp | Frontpage imscp name | Frontpage imscp description | Acceptance test site | imscp0 |
+ | folder | Frontpage folder name | Frontpage folder description | Acceptance test site | folder0 |
+ | glossary | Frontpage glossary name | Frontpage glossary description | Acceptance test site | glossary0 |
+ | scorm | Frontpage scorm name | Frontpage scorm description | Acceptance test site | scorm0 |
+ | lesson | Frontpage lesson name | Frontpage lesson description | Acceptance test site | lesson0 |
+ | survey | Frontpage survey name | Frontpage survey description | Acceptance test site | survey0 |
+ | url | Frontpage url name | Frontpage url description | Acceptance test site | url0 |
+ | wiki | Frontpage wiki name | Frontpage wiki description | Acceptance test site | wiki0 |
+ | workshop | Frontpage workshop name | Frontpage workshop description | Acceptance test site | workshop0 |
+
+ And I am on homepage
+ When I follow "Turn editing on"
+ And I add the "Activities" block
+ And I click on "Assignments" "link" in the "Activities" "block"
+ Then I should see "Frontpage assignment name"
+ And I am on homepage
+ And I click on "Assignments (2.2)" "link" in the "Activities" "block"
+ And I should see "Frontpage assignment22 name"
+ And I am on homepage
+ And I click on "Chats" "link" in the "Activities" "block"
+ And I should see "Frontpage chat name"
+ And I am on homepage
+ And I click on "Choices" "link" in the "Activities" "block"
+ And I should see "Frontpage choice name"
+ And I am on homepage
+ And I click on "Databases" "link" in the "Activities" "block"
+ And I should see "Frontpage database name"
+ And I am on homepage
+ And I click on "Feedback" "link" in the "Activities" "block"
+ And I should see "Frontpage feedback name"
+ And I am on homepage
+ And I click on "Forums" "link" in the "Activities" "block"
+ And I should see "Frontpage forum name"
+ And I am on homepage
+ And I click on "External Tools" "link" in the "Activities" "block"
+ And I should see "Frontpage lti name"
+ And I am on homepage
+ And I click on "Quizzes" "link" in the "Activities" "block"
+ And I should see "Frontpage quiz name"
+ And I am on homepage
+ And I click on "Glossaries" "link" in the "Activities" "block"
+ And I should see "Frontpage glossary name"
+ And I am on homepage
+ And I click on "SCORM packages" "link" in the "Activities" "block"
+ And I should see "Frontpage scorm name"
+ And I am on homepage
+ And I click on "Lessons" "link" in the "Activities" "block"
+ And I should see "Frontpage lesson name"
+ And I am on homepage
+ And I click on "Wikis" "link" in the "Activities" "block"
+ And I should see "Frontpage wiki name"
+ And I am on homepage
+ And I click on "Workshop" "link" in the "Activities" "block"
+ And I should see "Frontpage workshop name"
+ And I am on homepage
+ And I click on "Resources" "link" in the "Activities" "block"
+ And I should see "Frontpage book name"
+ And I should see "Frontpage page name"
+ And I should see "Frontpage resource name"
+ And I should see "Frontpage imscp name"
+ And I should see "Frontpage folder name"
+ And I should see "Frontpage url name"
+
+ Scenario: Add activities block in a course
+ Given the following "courses" exists:
+ | fullname | shortname | format |
+ | Course 1 | C1 | topics |
+ And the following "activities" exists:
+ | activity | name | intro | course | idnumber |
+ | assign | Test assignment name | Test assignment description | C1 | assign1 |
+ | assignment | Test assignment22 name | Test assignment22 description | C1 | assignment1 |
+ | book | Test book name | Test book description | C1 | book1 |
+ | chat | Test chat name | Test chat description | C1 | chat1 |
+ | choice | Test choice name | Test choice description | C1 | choice1 |
+ | data | Test database name | Test database description | C1 | data1 |
+ | feedback | Test feedback name | Test feedback description | C1 | feedback1 |
+ | folder | Test folder name | Test folder description | C1 | folder1 |
+ | forum | Test forum name | Test forum description | C1 | forum1 |
+ | glossary | Test glossary name | Test glossary description | C1 | glossary1 |
+ | imscp | Test imscp name | Test imscp description | C1 | imscp1 |
+ | label | Test label name | Test label description | C1 | label1 |
+ | lesson | Test lesson name | Test lesson description | C1 | lesson1 |
+ | lti | Test lti name | Test lti description | C1 | lti1 |
+ | page | Test page name | Test page description | C1 | page1 |
+ | quiz | Test quiz name | Test quiz description | C1 | quiz1 |
+ | resource | Test resource name | Test resource description | C1 | resource1 |
+ | scorm | Test scorm name | Test scorm description | C1 | scorm1 |
+ | survey | Test survey name | Test survey description | C1 | survey1 |
+ | url | Test url name | Test url description | C1 | url1 |
+ | wiki | Test wiki name | Test wiki description | C1 | wiki1 |
+ | workshop | Test workshop name | Test workshop description | C1 | workshop1 |
+
+ When I follow "Courses"
+ And I follow "Course 1"
+ And I turn editing mode on
+ And I add the "Activities" block
+ And I click on "Assignments" "link" in the "Activities" "block"
+ Then I should see "Test assignment name"
+ And I follow "Course 1"
+ And I click on "Assignments (2.2)" "link" in the "Activities" "block"
+ And I should see "Test assignment22 name"
+ And I follow "Course 1"
+ And I click on "Chats" "link" in the "Activities" "block"
+ And I should see "Test chat name"
+ And I follow "Course 1"
+ And I click on "Choices" "link" in the "Activities" "block"
+ And I should see "Test choice name"
+ And I follow "Course 1"
+ And I click on "Databases" "link" in the "Activities" "block"
+ And I should see "Test database name"
+ And I follow "Course 1"
+ And I click on "Feedback" "link" in the "Activities" "block"
+ And I should see "Test feedback name"
+ And I follow "Course 1"
+ And I click on "Forums" "link" in the "Activities" "block"
+ And I should see "Test forum name"
+ And I follow "Course 1"
+ And I click on "External Tools" "link" in the "Activities" "block"
+ And I should see "Test lti name"
+ And I follow "Course 1"
+ And I click on "Quizzes" "link" in the "Activities" "block"
+ And I should see "Test quiz name"
+ And I follow "Course 1"
+ And I click on "Glossaries" "link" in the "Activities" "block"
+ And I should see "Test glossary name"
+ And I follow "Course 1"
+ And I click on "SCORM packages" "link" in the "Activities" "block"
+ And I should see "Test scorm name"
+ And I follow "Course 1"
+ And I click on "Lessons" "link" in the "Activities" "block"
+ And I should see "Test lesson name"
+ And I follow "Course 1"
+ And I click on "Wikis" "link" in the "Activities" "block"
+ And I should see "Test wiki name"
+ And I follow "Course 1"
+ And I click on "Workshop" "link" in the "Activities" "block"
+ And I should see "Test workshop name"
+ And I follow "Course 1"
+ And I click on "Resources" "link" in the "Activities" "block"
+ And I should see "Test book name"
+ And I should see "Test page name"
+ And I should see "Test resource name"
+ And I should see "Test imscp name"
+ And I should see "Test folder name"
+ And I should see "Test url name"
// Put any upgrade step following this.
+ // Moodle v2.6.0 release upgrade line.
+ // Put any upgrade step following this.
+
return true;
}
// Put any upgrade step following this.
+ // Moodle v2.6.0 release upgrade line.
+ // Put any upgrade step following this.
+
return true;
}
\ No newline at end of file
if (empty($CFG->disablemycourses) and isloggedin() and !isguestuser() and
!(has_capability('moodle/course:update', context_system::instance()) and $adminseesall)) { // Just print My Courses
- if ($courses = enrol_get_my_courses(NULL, 'visible DESC, fullname ASC')) {
+ // As this is producing navigation sort order should default to $CFG->navsortmycoursessort instead
+ // of using the default.
+ if (!empty($CFG->navsortmycoursessort)) {
+ $sortorder = 'visible DESC, ' . $CFG->navsortmycoursessort . ' ASC';
+ } else {
+ $sortorder = 'visible DESC, sortorder ASC';
+ }
+ if ($courses = enrol_get_my_courses(NULL, $sortorder)) {
foreach ($courses as $course) {
$coursecontext = context_course::instance($course->id);
$linkcss = $course->visible ? "" : " class=\"dimmed\" ";
// Put any upgrade step following this.
+ // Moodle v2.6.0 release upgrade line.
+ // Put any upgrade step following this.
+
return true;
}
\ No newline at end of file
// Put any upgrade step following this.
+ // Moodle v2.6.0 release upgrade line.
+ // Put any upgrade step following this.
+
return true;
}
return $this->content;
}
+
+ /**
+ * Returns true if the block can be docked.
+ * The mentees block can only be docked if it has a non-empty title.
+ * @return bool
+ */
+ public function instance_can_be_docked() {
+ return parent::instance_can_be_docked() && isset($this->config->title) && !empty($this->config->title);
+ }
}
// Put any upgrade step following this.
+ // Moodle v2.6.0 release upgrade line.
+ // Put any upgrade step following this.
+
return true;
}
\ No newline at end of file
}
return val;
}
+ },
+ /**
+ * The navigation tree block instance.
+ */
+ instance : {
+ value : false,
+ setter : function(val) {
+ return parseInt(val);
+ }
}
}
});
} else {
e.stopPropagation();
}
- if (e.type === 'actionkey' && e.action === 'enter' && e.target.test('A')) {
+ if ((e.type === 'actionkey' && e.action === 'enter') || e.target.test('a')) {
// No ajaxLoad for enter.
this.node.setAttribute('data-expandable', '0');
this.node.setAttribute('data-loaded', '1');
this.node.setAttribute('data-loaded', '1');
try {
var object = Y.JSON.parse(outcome.responseText);
+ if (object.error) {
+ Y.use('moodle-core-notification-ajaxException', function () {
+ return new M.core.ajaxException(object).show();
+ });
+ return false;
+ }
if (object.children && object.children.length > 0) {
var coursecount = 0;
for (var i in object.children) {
return true;
}
Y.log('AJAX loading complete but there were no children.', 'note', 'moodle-block_navigation');
- } catch (ex) {
- // If we got here then there was an error parsing the result.
- Y.log('Error parsing AJAX response or adding branches to the navigation tree', 'error', 'moodle-block_navigation');
+ } catch (error) {
+ if (outcome && outcome.status && outcome.status > 0) {
+ // If we got here then there was an error parsing the result.
+ Y.log('Error parsing AJAX response or adding branches to the navigation tree', 'error', 'moodle-block_navigation');
+ Y.use('moodle-core-notification-exception', function () {
+ return new M.core.exception(error).show();
+ });
+ }
+
+ return false;
}
// The branch is empty so class it accordingly
this.node.replaceClass('branch', 'emptybranch');
if($this->config->display_description && !empty($description)){
- $description = break_up_long_words($description, 30);
-
$formatoptions = new stdClass();
$formatoptions->para = false;
$r.= html_writer::start_tag('div',array('class'=>'description'));
- $r.= format_text($description, FORMAT_HTML, $formatoptions, $this->page->course->id);
+ $description = format_text($description, FORMAT_HTML, $formatoptions, $this->page->course->id);
+ $description = break_up_long_words($description, 30);
+ $r.= $description;
$r.= html_writer::end_tag('div');
}
$r.= html_writer::end_tag('li');
// Put any upgrade step following this
+ // Moodle v2.6.0 release upgrade line.
+ // Put any upgrade step following this.
+
return true;
}
// Put any upgrade step following this.
+ // Moodle v2.6.0 release upgrade line.
+ // Put any upgrade step following this.
+
return true;
}
\ No newline at end of file
// Put any upgrade step following this.
+ // Moodle v2.6.0 release upgrade line.
+ // Put any upgrade step following this.
+
return true;
}
\ No newline at end of file
}
if (!$ismoving) {
$actions = course_get_cm_edit_actions($mod, -1);
+
+ // Add the action move.
+ $modcontext = context_module::instance($mod->id);
+ $hasmanageactivities = has_capability('moodle/course:manageactivities', $modcontext);
+ if ($hasmanageactivities) {
+ $baseurl = new moodle_url('/course/mod.php', array('sesskey' => sesskey()));
+ $actions['move'] = new action_menu_link_primary(
+ new moodle_url($baseurl, array('copy' => $mod->id)),
+ new pix_icon('t/move', get_string('move'), 'moodle', array('class' => 'iconsmall', 'title' => '')),
+ null,
+ array('title' => get_string('move'))
+ );
+ }
+
$editbuttons = html_writer::tag('div',
$courserenderer->course_section_cm_edit_actions($actions, $mod, array('donotenhance' => true)),
array('class' => 'buttons')
}
$this->content = new stdClass;
+ $this->content->text = '';
$this->content->footer = '';
// Get a list of tags.
return $steps;
}
+ /**
+ * Opens a block's actions menu if it is not already opened.
+ *
+ * @Given /^I open the "(?P<block_name_string>(?:[^"]|\\")*)" blocks action menu$/
+ * @throws DriverException The step is not available when Javascript is disabled
+ * @param string $blockname
+ * @return Given
+ */
+ public function i_open_the_blocks_action_menu($blockname) {
+
+ if (!$this->running_javascript()) {
+ throw new DriverException('Blocks action menu not available when Javascript is disabled');
+ }
+
+ // If it is already opened we do nothing.
+ $blocknode = $this->get_block_node($blockname);
+ $classes = array_flip(explode(' ', $blocknode->getAttribute('class')));
+ if (!empty($classes['action-menu-shown'])) {
+ return;
+ }
+
+ return new Given('I click on "a[role=\'menuitem\']" "css_element" in the "' . $this->escape($blockname) . '" "block"');
+ }
+
+ /**
+ * Returns the DOM node of the block from <div>.
+ *
+ * @throws ElementNotFoundException Thrown by behat_base::find
+ * @param string $blockname The block name
+ * @return NodeElement
+ */
+ protected function get_block_node($blockname) {
+
+ $blockname = $this->getSession()->getSelectorsHandler()->xpathLiteral($blockname);
+ $xpath = "//div[contains(concat(' ', normalize-space(@class), ' '), ' block ')][contains(., $blockname)]";
+
+ return $this->find('xpath', $xpath);
+ }
+
}
And I log in as "manager1"
And I follow "Turn editing on"
And I add the "Comments" block
- And I click on "Actions" "link" in the "Comments" "block"
+ And I open the "Comments" blocks action menu
And I follow "Configure Comments block"
And I fill the moodle form with:
| Page contexts | Display throughout the entire site |
Then I should see "Comments" in the "Comments" "block"
And I should see "Save comment" in the "Comments" "block"
And I am on homepage
- And I click on "Actions" "link" in the "Comments" "block"
+ And I open the "Comments" blocks action menu
And I follow "Configure Comments block"
And I fill the moodle form with:
| Default weight | -10 (first) |
And I press "Save changes"
And I follow "Course 1"
# The first block matching the pattern should be top-left block
- And I should see "Comments" in the "//*[@id='region-pre']/descendant::div[contains(concat(' ', normalize-space(@class), ' '), ' block ')]" "xpath_element"
+ And I should see "Comments" in the "//*[@id='region-pre' or @id='block-region-side-pre']/descendant::div[contains(concat(' ', normalize-space(@class), ' '), ' block ')]" "xpath_element"
And I follow "Course 1"
And I follow "Turn editing on"
And I add the "Comments" block
+ And I open the "Comments" blocks action menu
And I follow "Configure Comments block"
And I fill the moodle form with:
| Display on page types | Any page |
When I follow "Test survey name"
Then I should see "Comments" in the "Comments" "block"
And I follow "Course 1"
+ And I open the "Comments" blocks action menu
And I follow "Configure Comments block"
And I fill the moodle form with:
| Display on page types | Any course page |
@javascript
Scenario: Block settings can be modified so that a block can be hidden or moved
When I follow "Test book name"
+ And I open the "Comments" blocks action menu
And I follow "Configure Comments block"
And I fill the moodle form with:
| Visible | No |
Then I should not see "Comments"
And I expand "Course administration" node
And I follow "Turn editing on"
+ And I open the "Comments" blocks action menu
And I follow "Configure Comments block"
And I fill the moodle form with:
| Visible | Yes |
| Region | Right |
And I press "Save changes"
- And I should see "Comments" in the "#region-post" "css_element"
+ And I should see "Comments" in the "//*[@id='region-post' or @id='block-region-side-post']" "xpath_element"
And I add the "Tags" block
Then I should see "Tags" in the "Tags" "block"
And I click on "Participants" "link" in the "//li[p/span[contains(normalize-space(string(.)), 'Current course')]]" "xpath_element"
- And I click on "Actions" "link" in the "Tags" "block"
+ And I open the "Tags" blocks action menu
And I follow "Configure Tags block"
And I fill the moodle form with:
| Display on page types | Any page |
| Assignment name | Assignment1 |
| Description | Description |
And I follow "Assignment1"
- And I click on "Actions" "link" in the "Tags" "block"
+ And I open the "Tags" blocks action menu
And I follow "Configure Tags block"
And I fill the moodle form with:
| Display on page types | Any assignment module page |
| Description | Description |
And I follow "Assignment2"
And I should see "Tags" in the "Tags" "block"
- And I click on "Actions" "link" in the "Tags" "block"
+ And I open the "Tags" blocks action menu
And I follow "Configure Tags block"
And I fill the moodle form with:
| Display on page types | Any page |
//correct tagid if a text tag is provided as a param
if (!empty($tag)) {
- if ($tagrec = $DB->get_record_sql("SELECT * FROM {tag} WHERE ". $DB->sql_like('name', '?', false), array("%$tag%"))) {
+ if ($tagrec = $DB->get_record('tag', array('name' => $tag))) {
$tagid = $tagrec->id;
} else {
unset($tagid);
$bloglisting->print_entries();
echo $OUTPUT->footer();
-
-add_to_log($courseid, 'blog', 'view', 'index.php?entryid='.$entryid.'&tagid='.@$tagid.'&tag='.$tag, 'view blog entry');
+$eventparams = array(
+ 'other' => array('entryid' => $entryid, 'tagid' => $tagid, 'userid' => $userid, 'modid' => $modid, 'groupid' => $groupid,
+ 'search' => $search, 'fromstart' => $start)
+);
+if (!empty($userid)) {
+ $eventparams['relateduserid'] = $userid;
+}
+$eventparams['other']['courseid'] = ($courseid === SITEID) ? 0 : $courseid;
+$event = \core\event\blog_entries_viewed::create($eventparams);
+$event->trigger();
$tagrec = $DB->get_record('tag', array('id'=>$tagid));
$PAGE->navbar->add($tagrec->name, $blogurl);
} elseif (!empty($tag)) {
- $blogurl->param('tag', $tag);
- $PAGE->navbar->add(get_string('tagparam', 'blog', $tag), $blogurl);
+ if ($tagrec = $DB->get_record('tag', array('name' => $tag))) {
+ $tagid = $tagrec->id;
+ $headers['filters']['tag'] = $tagid;
+ $blogurl->param('tag', $tag);
+ $PAGE->navbar->add(get_string('tagparam', 'blog', $tag), $blogurl);
+ }
}
// Append Search info
}
/**
- * function to add all context associations to an entry
- * @param int entry - data object processed to include all 'entry' fields and extra data from the edit_form object
+ * Function to add all context associations to an entry.
+ * TODO : Remove $action in 2.9 (MDL-41330)
+ *
+ * @param string $action - This does nothing, do not use it. This is present only for Backward compatibility.
*/
- public function add_associations($action='add') {
- global $DB, $USER;
+ public function add_associations($action = null) {
+
+ if (!empty($action)) {
+ debugging('blog_entry->add_associations() does not accept any argument', DEBUG_DEVELOPER);
+ }
$this->remove_associations();
if (!empty($this->courseassoc)) {
- $this->add_association($this->courseassoc, $action);
+ $this->add_association($this->courseassoc);
}
if (!empty($this->modassoc)) {
- $this->add_association($this->modassoc, $action);
+ $this->add_association($this->modassoc);
}
}
/**
- * add a single association for a blog entry
- * @param int contextid - id of context to associate with the blog entry
+ * Add a single association for a blog entry
+ * TODO : Remove $action in 2.9 (MDL-41330)
+ *
+ * @param int $contextid - id of context to associate with the blog entry.
+ * @param string $action - This does nothing, do not use it. This is present only for Backward compatibility.
*/
- public function add_association($contextid, $action='add') {
- global $DB, $USER;
+ public function add_association($contextid, $action = null) {
+ global $DB;
+
+ if (!empty($action)) {
+ debugging('blog_entry->add_association() accepts only one argument', DEBUG_DEVELOPER);
+ }
$assocobject = new StdClass;
$assocobject->contextid = $contextid;
$assocobject->blogid = $this->id;
- $DB->insert_record('blog_association', $assocobject);
+ $id = $DB->insert_record('blog_association', $assocobject);
+ // Trigger an association created event.
$context = context::instance_by_id($contextid);
- $courseid = null;
-
+ $eventparam = array(
+ 'objectid' => $id,
+ 'other' => array('associateid' => $context->instanceid, 'subject' => $this->subject, 'blogid' => $this->id),
+ 'relateduserid' => $this->userid
+ );
if ($context->contextlevel == CONTEXT_COURSE) {
- $courseid = $context->instanceid;
- add_to_log($courseid, 'blog', $action, 'index.php?userid='.$this->userid.'&entryid='.$this->id, $this->subject);
+ $eventparam['other']['associatetype'] = 'course';
+
} else if ($context->contextlevel == CONTEXT_MODULE) {
- $cm = $DB->get_record('course_modules', array('id' => $context->instanceid));
- $modulename = $DB->get_field('modules', 'name', array('id' => $cm->module));
- add_to_log($cm->course, 'blog', $action, 'index.php?userid='.$this->userid.'&entryid='.$this->id, $this->subject, $cm->id, $this->userid);
+ $eventparam['other']['associatetype'] = 'coursemodule';
}
+ $event = \core\event\blog_association_created::create($eventparam);
+ $event->trigger();
}
/**
$this->assertEventLegacyLogData($arr, $event);
$this->assertEventLegacyData($blog, $event);
}
+
+
+ /**
+ * Tests for event blog_association_created.
+ */
+ public function test_blog_association_created_event() {
+ global $USER;
+
+ $this->setAdminUser();
+ $this->resetAfterTest();
+ $sitecontext = context_system::instance();
+ $coursecontext = context_course::instance($this->courseid);
+ $contextmodule = context_module::instance($this->cmid);
+
+ // Add blog associations with a course.
+ $blog = new blog_entry($this->postid);
+ $sink = $this->redirectEvents();
+ $blog->add_association($coursecontext->id);
+ $events = $sink->get_events();
+ $event = reset($events);
+ $sink->close();
+
+ // Validate event data.
+ $this->assertInstanceOf('\core\event\blog_association_created', $event);
+ $this->assertEquals($sitecontext->id, $event->contextid);
+ $this->assertEquals($blog->id, $event->other['blogid']);
+ $this->assertEquals($this->courseid, $event->other['associateid']);
+ $this->assertEquals('course', $event->other['associatetype']);
+ $this->assertEquals($blog->subject, $event->other['subject']);
+ $this->assertEquals($USER->id, $event->userid);
+ $this->assertEquals($this->userid, $event->relateduserid);
+ $this->assertEquals('blog_association', $event->objecttable);
+ $arr = array(SITEID, 'blog', 'add association', 'index.php?userid=' . $this->userid . '&entryid=' . $blog->id,
+ $blog->subject, 0, $this->userid);
+ $this->assertEventLegacyLogData($arr, $event);
+
+ // Add blog associations with a module.
+ $blog = new blog_entry($this->postid);
+ $sink = $this->redirectEvents();
+ $blog->add_association($contextmodule->id);
+ $events = $sink->get_events();
+ $event = reset($events);
+ $sink->close();
+
+ // Validate event data.
+ $this->assertEquals($blog->id, $event->other['blogid']);
+ $this->assertEquals($this->cmid, $event->other['associateid']);
+ $this->assertEquals('coursemodule', $event->other['associatetype']);
+ $arr = array(SITEID, 'blog', 'add association', 'index.php?userid=' . $this->userid . '&entryid=' . $blog->id,
+ $blog->subject, $this->cmid, $this->userid);
+ $this->assertEventLegacyLogData($arr, $event);
+ }
+
+ /**
+ * Tests for event blog_association_created validations.
+ */
+ public function test_blog_association_created_event_validations() {
+
+ $this->resetAfterTest();
+
+ // Make sure associatetype validations work.
+ try {
+ \core\event\blog_association_created::create(array(
+ 'contextid' => 1,
+ 'objectid' => 3,
+ 'other' => array('associateid' => 2 , 'blogid' => 3, 'subject' => 'blog subject')));
+ } catch (coding_exception $e) {
+ $this->assertContains('Invalid associatetype', $e->getMessage());
+ }
+ try {
+ \core\event\blog_association_created::create(array(
+ 'contextid' => 1,
+ 'objectid' => 3,
+ 'other' => array('associateid' => 2 , 'blogid' => 3, 'associatetype' => 'random', 'subject' => 'blog subject')));
+ } catch (coding_exception $e) {
+ $this->assertContains('Invalid associatetype', $e->getMessage());
+ }
+ // Make sure associateid validations work.
+ try {
+ \core\event\blog_association_created::create(array(
+ 'contextid' => 1,
+ 'objectid' => 3,
+ 'other' => array('blogid' => 3, 'associatetype' => 'course', 'subject' => 'blog subject')));
+ } catch (coding_exception $e) {
+ $this->assertContains('Associate id must be set', $e->getMessage());
+ }
+ // Make sure blogid validations work.
+ try {
+ \core\event\blog_association_created::create(array(
+ 'contextid' => 1,
+ 'objectid' => 3,
+ 'other' => array('associateid' => 3, 'associatetype' => 'course', 'subject' => 'blog subject')));
+ } catch (coding_exception $e) {
+ $this->assertContains('Blog id must be set', $e->getMessage());
+ }
+ // Make sure blogid validations work.
+ try {
+ \core\event\blog_association_created::create(array(
+ 'contextid' => 1,
+ 'objectid' => 3,
+ 'other' => array('blogid' => 3, 'associateid' => 3, 'associatetype' => 'course')));
+ } catch (coding_exception $e) {
+ $this->assertContains('Subject must be set', $e->getMessage());
+ }
+ }
+
+ /**
+ * Tests for event blog_entries_viewed.
+ */
+ public function test_blog_entries_viewed_event() {
+
+ $this->setAdminUser();
+ $this->resetAfterTest();
+ $other = array('entryid' => $this->postid, 'tagid' => $this->tagid, 'userid' => $this->userid, 'modid' => $this->cmid,
+ 'groupid' => $this->groupid, 'courseid' => $this->courseid, 'search' => 'search', 'fromstart' => 2);
+
+ // Trigger event.
+ $sink = $this->redirectEvents();
+ $eventparams = array('other' => $other);
+ $eventinst = \core\event\blog_entries_viewed::create($eventparams);
+ $eventinst->trigger();
+ $events = $sink->get_events();
+ $event = reset($events);
+ $sink->close();
+
+ // Validate event data.
+ $url = new moodle_url('/blog/index.php', $other);
+ $url2 = new moodle_url('index.php', $other);
+ $this->assertEquals($url, $event->get_url());
+ $arr = array(SITEID, 'blog', 'view', $url2->out(), 'view blog entry');
+ $this->assertEventLegacyLogData($arr, $event);
+ }
}
--- /dev/null
+=== 2.7 ===
+
+* blog_entry->add_association() does not accept any params.
+* blog_entry->add_associations() accepts only one param.
\ No newline at end of file
$PAGE->set_title($title);
$PAGE->set_heading($SITE->fullname);
+/* @var core_cache_renderer $renderer */
$renderer = $PAGE->get_renderer('core_cache');
echo $renderer->header();
public function has_required_identifiers() {
return (count($this->requireidentifiers) > 0);
}
+
+ /**
+ * Returns the possible sharing options that can be used with this defintion.
+ *
+ * @return int
+ */
+ public function get_sharing_options() {
+ return $this->sharingoptions;
+ }
+
+ /**
+ * Returns the user entered sharing key for this definition.
+ *
+ * @return string
+ */
+ public function get_user_input_sharing_key() {
+ return $this->userinputsharingkey;
+ }
+
+ /**
+ * Returns the user selected sharing option for this definition.
+ *
+ * @return int
+ */
+ public function get_selected_sharing_option() {
+ return $this->selectedsharingoption;
+ }
}
*/
public function create_cache(cache_definition $definition) {
$class = $definition->get_cache_class();
- if ($this->is_initialising()) {
- // Do nothing we just want the dummy store.
- $stores = array();
- } else {
- $stores = cache_helper::get_cache_stores($definition);
- }
+ $stores = cache_helper::get_stores_suitable_for_definition($definition);
if (count($stores) === 0) {
- // Hmm no stores, better provide a dummy store to mimick functionality. The dev will be none the wiser.
+ // Hmm still no stores, better provide a dummy store to mimic functionality. The dev will be none the wiser.
$stores[] = $this->create_dummy_store($definition);
}
$loader = null;
*
* @param array $stores
* @param cache_definition $definition
- * @return array
+ * @return cache_store[]
*/
protected static function initialise_cachestore_instances(array $stores, cache_definition $definition) {
$return = array();
/**
* Record a cache hit in the stats for the given store and definition.
*
+ * @internal
* @param string $store
* @param string $definition
+ * @param int $hits The number of hits to record (by default 1)
*/
- public static function record_cache_hit($store, $definition) {
+ public static function record_cache_hit($store, $definition, $hits = 1) {
self::ensure_ready_for_stats($store, $definition);
- self::$stats[$definition][$store]['hits']++;
+ self::$stats[$definition][$store]['hits'] += $hits;
}
/**
* Record a cache miss in the stats for the given store and definition.
*
+ * @internal
* @param string $store
* @param string $definition
+ * @param int $misses The number of misses to record (by default 1)
*/
- public static function record_cache_miss($store, $definition) {
+ public static function record_cache_miss($store, $definition, $misses = 1) {
self::ensure_ready_for_stats($store, $definition);
- self::$stats[$definition][$store]['misses']++;
+ self::$stats[$definition][$store]['misses'] += $misses;
}
/**
* Record a cache set in the stats for the given store and definition.
*
+ * @internal
* @param string $store
* @param string $definition
+ * @param int $sets The number of sets to record (by default 1)
*/
- public static function record_cache_set($store, $definition) {
+ public static function record_cache_set($store, $definition, $sets = 1) {
self::ensure_ready_for_stats($store, $definition);
- self::$stats[$definition][$store]['sets']++;
+ self::$stats[$definition][$store]['sets'] += $sets;
}
/**
}
}
}
+
+ /**
+ * Returns an array of stores that would meet the requirements for every definition.
+ *
+ * These stores would be 100% suitable to map as defaults for cache modes.
+ *
+ * @return array[] An array of stores, keys are the store names.
+ */
+ public static function get_stores_suitable_for_mode_default() {
+ $factory = cache_factory::instance();
+ $config = $factory->create_config_instance();
+ $requirements = 0;
+ foreach ($config->get_definitions() as $definition) {
+ $definition = cache_definition::load($definition['component'].'/'.$definition['area'], $definition);
+ $requirements = $requirements | $definition->get_requirements_bin();
+ }
+ $stores = array();
+ foreach ($config->get_all_stores() as $name => $store) {
+ if (!empty($store['features']) && ($store['features'] & $requirements)) {
+ $stores[$name] = $store;
+ }
+ }
+ return $stores;
+ }
+
+ /**
+ * Returns stores suitable for use with a given definition.
+ *
+ * @param cache_definition $definition
+ * @return cache_store[]
+ */
+ public static function get_stores_suitable_for_definition(cache_definition $definition) {
+ $factory = cache_factory::instance();
+ $stores = array();
+ if ($factory->is_initialising() || $factory->stores_disabled()) {
+ // No suitable stores here.
+ return $stores;
+ } else {
+ $stores = self::get_cache_stores($definition);
+ if (count($stores) === 0) {
+ // No suitable stores we found for the definition. We need to come up with a sensible default.
+ // If this has happened we can be sure that the user has mapped custom stores to either the
+ // mode of the definition. The first alternative to try is the system default for the mode.
+ // e.g. the default file store instance for application definitions.
+ $config = $factory->create_config_instance();
+ foreach ($config->get_stores($definition->get_mode()) as $name => $details) {
+ if (!empty($details['default'])) {
+ $stores[] = $factory->create_store_from_config($name, $details, $definition);
+ break;
+ }
+ }
+ }
+ }
+ return $stores;
+ }
}
}
}
+ if ($this->perfdebug) {
+ $hits = 0;
+ $misses = 0;
+ foreach ($fullresult as $value) {
+ if ($value === false) {
+ $misses++;
+ } else {
+ $hits++;
+ }
+ }
+ cache_helper::record_cache_hit($this->storetype, $this->definition->get_id(), $hits);
+ cache_helper::record_cache_miss($this->storetype, $this->definition->get_id(), $misses);
+ }
+
// Return the result. Phew!
return $fullresult;
}
$this->static_acceleration_set($data[$key]['key'], $value);
}
}
- if ($this->perfdebug) {
- cache_helper::record_cache_set($this->storetype, $this->definition->get_id());
+ $successfullyset = $this->store->set_many($data);
+ if ($this->perfdebug && $successfullyset) {
+ cache_helper::record_cache_set($this->storetype, $this->definition->get_id(), $successfullyset);
}
- return $this->store->set_many($data);
+ return $successfullyset;
}
/**
if ($hasmissingkeys && $strictness === MUST_EXIST) {
throw new coding_exception('Requested key did not exist in any cache stores and could not be loaded.');
}
-
+ if ($this->perfdebug) {
+ $hits = 0;
+ $misses = 0;
+ foreach ($return as $value) {
+ if ($value === false) {
+ $misses++;
+ } else {
+ $hits++;
+ }
+ }
+ cache_helper::record_cache_hit($this->storetype, $this->get_definition()->get_id(), $hits);
+ cache_helper::record_cache_miss($this->storetype, $this->get_definition()->get_id(), $misses);
+ }
return $return;
}
'value' => $value
);
}
- if ($this->perfdebug) {
- cache_helper::record_cache_set($this->storetype, $definitionid);
+ $successfullyset = $this->get_store()->set_many($data);
+ if ($this->perfdebug && $successfullyset) {
+ cache_helper::record_cache_set($this->storetype, $definitionid, $successfullyset);
}
- return $this->get_store()->set_many($data);
+ return $successfullyset;
}
/**
* @return array
*/
public static function get_definition_summaries() {
- $instance = cache_config::instance();
- $definitions = $instance->get_definitions();
-
+ $factory = cache_factory::instance();
+ $config = $factory->create_config_instance();
$storenames = array();
- foreach ($instance->get_all_stores() as $key => $store) {
+ foreach ($config->get_all_stores() as $key => $store) {
if (!empty($store['default'])) {
$storenames[$key] = new lang_string('store_'.$key, 'cache');
- }
- }
-
- $modemappings = array();
- foreach ($instance->get_mode_mappings() as $mapping) {
- $mode = $mapping['mode'];
- if (!array_key_exists($mode, $modemappings)) {
- $modemappings[$mode] = array();
- }
- if (array_key_exists($mapping['store'], $storenames)) {
- $modemappings[$mode][] = $storenames[$mapping['store']];
} else {
- $modemappings[$mode][] = $mapping['store'];
+ $storenames[$store['name']] = $store['name'];
}
}
-
- $definitionmappings = array();
- foreach ($instance->get_definition_mappings() as $mapping) {
- $definition = $mapping['definition'];
- if (!array_key_exists($definition, $definitionmappings)) {
- $definitionmappings[$definition] = array();
- }
- if (array_key_exists($mapping['store'], $storenames)) {
- $definitionmappings[$definition][] = $storenames[$mapping['store']];
- } else {
- $definitionmappings[$definition][] = $mapping['store'];
- }
+ /* @var cache_definition[] $definitions */
+ $definitions = array();
+ foreach ($config->get_definitions() as $key => $definition) {
+ $definitions[$key] = cache_definition::load($definition['component'].'/'.$definition['area'], $definition);
}
-
- $return = array();
-
foreach ($definitions as $id => $definition) {
-
$mappings = array();
- if (array_key_exists($id, $definitionmappings)) {
- $mappings = $definitionmappings[$id];
- } else if (empty($definition['mappingsonly'])) {
- $mappings = $modemappings[$definition['mode']];
+ foreach (cache_helper::get_stores_suitable_for_definition($definition) as $store) {
+ $mappings[] = $storenames[$store->my_name()];
}
-
$return[$id] = array(
'id' => $id,
- 'name' => cache_helper::get_definition_name($definition),
- 'mode' => $definition['mode'],
- 'component' => $definition['component'],
- 'area' => $definition['area'],
+ 'name' => $definition->get_name(),
+ 'mode' => $definition->get_mode(),
+ 'component' => $definition->get_component(),
+ 'area' => $definition->get_area(),
'mappings' => $mappings,
- 'sharingoptions' => self::get_definition_sharing_options($definition['sharingoptions'], false),
- 'selectedsharingoption' => self::get_definition_sharing_options($definition['selectedsharingoption'], true),
- 'userinputsharingkey' => $definition['userinputsharingkey']
+ 'sharingoptions' => self::get_definition_sharing_options($definition->get_sharing_options(), false),
+ 'selectedsharingoption' => self::get_definition_sharing_options($definition->get_selected_sharing_option(), true),
+ 'userinputsharingkey' => $definition->get_user_input_sharing_key()
);
}
return $return;
* @return array An array containing sub-arrays, one for each mode.
*/
public static function get_default_mode_stores() {
+ global $OUTPUT;
$instance = cache_config::instance();
+ $adequatestores = cache_helper::get_stores_suitable_for_mode_default();
+ $icon = new pix_icon('i/warning', new lang_string('inadequatestoreformapping', 'cache'));
$storenames = array();
foreach ($instance->get_all_stores() as $key => $store) {
if (!empty($store['default'])) {
} else {
$modemappings[$mode][$mapping['store']] = $mapping['store'];
}
+ if (!array_key_exists($mapping['store'], $adequatestores)) {
+ $modemappings[$mode][$mapping['store']] = $modemappings[$mode][$mapping['store']].' '.$OUTPUT->render($icon);
+ }
}
return $modemappings;
}
$lines = explode("\n", $data->servers);
$servers = array();
foreach ($lines as $line) {
- $line = trim($line, ':');
+ // Trim surrounding colons and default whitespace.
+ $line = trim(trim($line), ":");
+ // Skip blank lines.
+ if ($line === '') {
+ continue;
+ }
$servers[] = explode(':', $line, 3);
}
return array(
$lines = explode("\n", $data->servers);
$servers = array();
foreach ($lines as $line) {
- $line = trim($line, ':');
+ // Trim surrounding colons and default whitespace.
+ $line = trim(trim($line), ":");
+ // Skip blank lines.
+ if ($line === '') {
+ continue;
+ }
$servers[] = explode(':', $line, 3);
}
return array(
global $CFG;
$event = calendar_add_event_metadata($event);
+ $context = $event->context;
$anchor = html_writer::tag('a', '', array('name'=>'event_'.$event->id));
if (!empty($event->referer)) {
$table->data[0]->cells[1]->text .= html_writer::tag('div', $event->referer, array('class'=>'referer'));
} else {
- $table->data[0]->cells[1]->text .= html_writer::tag('div', $event->name, array('class'=>'name'));
+ $table->data[0]->cells[1]->text .= html_writer::tag('div', format_string($event->name, false, array('context' => $context)), array('class'=>'name'));
}
if (!empty($event->courselink)) {
$table->data[0]->cells[1]->text .= html_writer::tag('div', $event->courselink, array('class'=>'course'));
$table->data[1]->cells[0] = new html_table_cell(' ');
$table->data[1]->cells[0]->attributes['class'] .= 'side';
- $table->data[1]->cells[1] = new html_table_cell($event->description);
+ $table->data[1]->cells[1] = new html_table_cell(format_text($event->description, $event->format, array('context' => $context)));
$table->data[1]->cells[1]->attributes['class'] .= ' description';
if (isset($event->cssclass)) {
$table->data[1]->cells[1]->attributes['class'] .= ' '.$event->cssclass;
$userid = $DB->get_field('user', 'id', array('username' => $username));
$steps = array(
- new Given('I click on "' . get_string('assign', 'cohort') . '" "link" in the "' . $this->escape($cohortidnumber) . '" table row'),
+ new Given('I click on "' . get_string('assign', 'cohort') . '" "link" in the "' . $this->escape($cohortidnumber) . '" "table_row"'),
new Given('I select "' . $userid . '" from "' . get_string('potusers', 'cohort') . '"'),
new Given('I press "' . get_string('add') . '"'),
new Given('I press "' . get_string('backtocohorts', 'cohort') . '"')
And I press "Upload users"
And I press "Continue"
And I follow "Cohorts"
- And I click on "Assign" "link" in the "Cohort 1" table row
+ And I click on "Assign" "link" in the "Cohort 1" "table_row"
Then the "Current users" select box should contain "Tom Jones (tomjones@example.com)"
And the "Current users" select box should contain "Bob Jones (bobjones@example.com)"
And I press "Back to cohorts"
- And I click on "Assign" "link" in the "Cohort 2" table row
+ And I click on "Assign" "link" in the "Cohort 2" "table_row"
And the "Current users" select box should contain "Mary Smith (marysmith@example.com)"
And the "Current users" select box should contain "Alice Smith (alicesmith@example.com)"
And I am on homepage
"require-dev": {
"phpunit/phpunit": "3.7.*",
"phpunit/dbUnit": "1.2.*",
- "moodlehq/behat-extension": "1.26.*"
+ "moodlehq/behat-extension": "1.27.0"
}
}
// $CFG->behat_prefix = 'bht_';
// $CFG->behat_dataroot = '/home/example/bht_moodledata';
//
-// Behat uses http://localhost:8000 as default URL to run
-// the acceptance tests, you can override this value.
+// To set a seperate wwwroot for Behat to use, use $CFG->behat_wwwroot; this is set automatically
+// to http://localhost:8000 as it is the proposed PHP built-in server URL. Instead of that you can,
+// for example, use an alias, add a host to /etc/hosts or add a new virtual host having a URL
+// poiting to your production site and another one poiting to your test site. Note that you need
+// to ensure that this URL is not accessible from the www as the behat test site uses "sugar"
+// credentials (admin/admin) and can be easily hackable.
+//
// Example:
// $CFG->behat_wwwroot = 'http://192.168.1.250:8000';
+// $CFG->behat_wwwroot = 'http://localhost/moodlesitetesting';
//
// You can override default Moodle configuration for Behat and add your own
// params; here you can add more profiles, use different Mink drivers than Selenium...
// Example:
// $CFG->behat_extraallowedsettings = array('logsql', 'dblogerror');
//
+// You should explicitly allow the usage of the deprecated behat steps, otherwise an exception will
+// be thrown when using them. The setting is disabled by default.
+// Example:
+// $CFG->behat_usedeprecated = true;
+//
//=========================================================================
// 12. DEVELOPER DATA GENERATOR
//=========================================================================
$categoryid = required_param('categoryid', PARAM_INT);
/* @var core_course_management_renderer $renderer */
$renderer = $PAGE->get_renderer('core_course', 'management');
- $outcome->html = html_writer::start_tag('ul', array('class' => 'ml'));
+ $outcome->html = html_writer::start_tag('ul',
+ array('class' => 'ml', 'role' => 'group', 'id' => 'subcategoriesof'.$categoryid));
$coursecat = coursecat::get($categoryid);
foreach ($coursecat->get_children() as $subcat) {
$outcome->html .= $renderer->category_listitem($subcat, array(), $subcat->get_children_count());
if (!$category->can_change_visibility()) {
throw new \moodle_exception('permissiondenied', 'error', '', null, 'coursecat::can_change_visbility');
}
- if ((int)$category->get_parent_coursecat()->visible === 0) {
- // You cannot mark a category visible if its parent is hidden.
- return false;
- }
$category->show();
return true;
}
public function enhance_management_interface() {
$this->page->requires->yui_module('moodle-course-management', 'M.course.management.init');
$this->page->requires->strings_for_js(
- array('show', 'hide', 'expand', 'collapse', 'confirmcoursemove', 'move', 'cancel', 'confirm'),
+ array(
+ 'show',
+ 'showcategory',
+ 'hide',
+ 'expand',
+ 'expandcategory',
+ 'collapse',
+ 'collapsecategory',
+ 'confirmcoursemove',
+ 'move',
+ 'cancel',
+ 'confirm'
+ ),
'moodle'
);
}
$html .= $this->heading($heading);
}
if ($viewmode !== null) {
+ $html .= html_writer::start_div();
+ $html .= $this->view_mode_selector(\core_course\management\helper::get_management_viewmodes(), $viewmode);
if ($viewmode === 'courses') {
$categories = coursecat::make_categories_list(array('moodle/category:manage', 'moodle/course:create'));
$nothing = false;
$select = new single_select($this->page->url, 'categoryid', $categories, $categoryid, $nothing);
$html .= $this->render($select);
}
- $html .= $this->view_mode_selector(\core_course\management\helper::get_management_viewmodes(), $viewmode);
+ $html .= html_writer::end_div();
}
$html .= html_writer::end_div();
return $html;
'aria-expanded' => $isexpanded ? 'true' : 'false'
);
$text = $category->get_formatted_name();
+ if ($category->parent) {
+ $a = new stdClass;
+ $a->category = $text;
+ $a->parentcategory = $category->get_parent_coursecat()->get_formatted_name();
+ $textlabel = get_string('categorysubcategoryof', 'moodle', $a);
+ }
$courseicon = $this->output->pix_icon('i/course', get_string('courses'));
- $bcatinput = array('type' => 'checkbox', 'name' => 'bcat[]', 'value' => $category->id, 'class' => 'bulk-action-checkbox');
+ $bcatinput = array(
+ 'type' => 'checkbox',
+ 'name' => 'bcat[]',
+ 'value' => $category->id,
+ 'class' => 'bulk-action-checkbox',
+ 'aria-label' => get_string('bulkactionselect', 'moodle', $text),
+ 'data-action' => 'select'
+ );
if (!$category->can_resort_subcategories() && !$category->has_manage_capability()) {
// Very very hardcoded here.
$viewcaturl = new moodle_url('/course/management.php', array('categoryid' => $category->id));
if ($isexpanded) {
- $icon = $this->output->pix_icon('t/switch_minus', get_string('collapse'), 'moodle', array('class' => 'tree-icon'));
- $icon = html_writer::link($viewcaturl, $icon, array('class' => 'float-left', 'data-action' => 'collapse'));
+ $icon = $this->output->pix_icon('t/switch_minus', get_string('collapse'), 'moodle', array('class' => 'tree-icon', 'title' => ''));
+ $icon = html_writer::link(
+ $viewcaturl,
+ $icon,
+ array(
+ 'class' => 'float-left',
+ 'data-action' => 'collapse',
+ 'title' => get_string('collapsecategory', 'moodle', $text),
+ 'aria-controls' => 'subcategoryof'.$category->id
+ )
+ );
} else if ($isexpandable) {
- $icon = $this->output->pix_icon('t/switch_plus', get_string('expand'), 'moodle', array('class' => 'tree-icon'));
- $icon = html_writer::link($viewcaturl, $icon, array('class' => 'float-left', 'data-action' => 'expand'));
+ $icon = $this->output->pix_icon('t/switch_plus', get_string('expand'), 'moodle', array('class' => 'tree-icon', 'title' => ''));
+ $icon = html_writer::link(
+ $viewcaturl,
+ $icon,
+ array(
+ 'class' => 'float-left',
+ 'data-action' => 'expand',
+ 'title' => get_string('expandcategory', 'moodle', $text)
+ )
+ );
} else {
- $icon = $this->output->pix_icon('i/navigationitem', '', 'moodle', array('class' => 'tree-icon'));
- $icon = html_writer::link($viewcaturl, $icon, array('class' => 'float-left'));
+ $icon = $this->output->pix_icon(
+ 'i/navigationitem',
+ '',
+ 'moodle',
+ array('class' => 'tree-icon', 'title' => get_string('showcategory', 'moodle', $text))
+ );
+ $icon = html_writer::span($icon, 'float-left');
}
$actions = \core_course\management\helper::get_category_listitem_actions($category);
$hasactions = !empty($actions) || $category->can_create_course();
$html .= html_writer::end_div();
$html .= $icon;
if ($hasactions) {
- $html .= html_writer::link($viewcaturl, $text, array('class' => 'float-left categoryname'));
+ $textattributes = array('class' => 'float-left categoryname');
} else {
- $html .= html_writer::link($viewcaturl, $text, array('class' => 'float-left categoryname without-actions'));
+ $textattributes = array('class' => 'float-left categoryname without-actions');
+ }
+ if (isset($textlabel)) {
+ $textattributes['aria-label'] = $textlabel;
}
+ $html .= html_writer::link($viewcaturl, $text, $textattributes);
$html .= html_writer::start_div('float-right');
if ($category->idnumber) {
$html .= html_writer::tag('span', s($category->idnumber), array('class' => 'dimmed idnumber'));
$html .= $this->category_listitem_actions($category, $actions);
}
$countid = 'course-count-'.$category->id;
- $html .= html_writer::span(get_string('courses'), 'accesshide', array('id' => $countid));
$html .= html_writer::span(
- html_writer::span($category->get_courses_count()).$courseicon,
+ &