Merge branch 'MOODLE_25_STABLE' into install_25_STABLE

This commit is contained in:
AMOS bot
2013-07-27 00:43:10 +00:00
98 changed files with 825 additions and 380 deletions
+2
View File
@@ -957,6 +957,8 @@
<ON_ERROR message="ziprequired" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="zlib" level="optional">
</PHP_EXTENSION>
<PHP_EXTENSION name="gd" level="required">
<FEEDBACK>
<ON_ERROR message="gdrequired" />
+7 -3
View File
@@ -62,7 +62,7 @@ class behat_admin extends behat_base {
}
// Search by label.
$searchbox = $this->find_field('Search in settings');
$searchbox = $this->find_field(get_string('searchinsettings', 'admin'));
$searchbox->setValue($label);
$submitsearch = $this->find('css', 'form.adminsearchform input[type=submit]');
$submitsearch->press();
@@ -72,8 +72,12 @@ class behat_admin extends behat_base {
// Admin settings does not use the same DOM structure than other moodle forms
// but we also need to use lib/behat/form_field/* to deal with the different moodle form elements.
$exception = new ElementNotFoundException($this->getSession(), '"' . $label . '" administration setting ');
// The argument should be converted to an xpath literal.
$label = $this->getSession()->getSelectorsHandler()->xpathLiteral($label);
$fieldxpath = "//*[self::input | self::textarea | self::select][not(./@type = 'submit' or ./@type = 'image' or ./@type = 'hidden')]" .
"[@id=//label[contains(normalize-space(string(.)), '" . $label . "')]/@for]";
"[@id=//label[contains(normalize-space(.), $label)]/@for]";
$fieldnode = $this->find('xpath', $fieldxpath, $exception);
$formfieldtypenode = $this->find('xpath', $fieldxpath . "/ancestor::div[@class='form-setting']" .
"/child::div[contains(concat(' ', @class, ' '), ' form-')]/child::*/parent::div");
@@ -90,7 +94,7 @@ class behat_admin extends behat_base {
$field = behat_field_manager::get_field_instance($type, $fieldnode, $this->getSession());
$field->set_value($value);
$this->find_button('Save changes')->press();
$this->find_button(get_string('savechanges'))->press();
}
}
@@ -15,7 +15,7 @@ Feature: Display extended course names
And I should not see "C_shortname Course fullname"
Scenario: Courses list with extended course names
Given I click on "Courses" "link" in the "//div[@id='settingsnav']//descendant::li[contains(concat(' ', @class, ' '), ' type_setting ')][not(contains(., 'Site administration'))][contains(., 'Appearance')]" "xpath_element"
Given I click on "Courses" "link" in the "//div[@id='settingsnav']/descendant::li[contains(concat(' ', normalize-space(@class), ' '), ' type_setting ')][not(contains(., 'Site administration'))][contains(., 'Appearance')]" "xpath_element"
And I check "Display extended course names"
When I press "Save changes"
And I am on homepage
+3
View File
@@ -142,6 +142,9 @@ foreach ($vars as $var) {
$CFG->{$var} = $CFG->{'behat_' . $var};
}
// Clean $CFG extra values before performing any action.
behat_clean_init_config();
$CFG->noemailever = true;
$CFG->passwordsaltmain = 'moodle';
+1 -2
View File
@@ -41,6 +41,5 @@ $string['theninfo'] = 'Then. Checkings to ensure the outcomes are the expected o
$string['viewsteps'] = 'Filter';
$string['wheninfo'] = 'When. Actions that provokes an event';
$string['wrongbehatsetup'] = 'Something is wrong with behat setup, ensure:<ul>
<li>You ran "curl http://getcomposer.org/installer | php"</li>
<li>You ran "php composer.phar install --dev"</li>
<li>You ran "php admin/tool/behat/cli/init.php" from your moodle root directory</li>
<li>vendor/bin/behat file has execution permissions</li></ul>';
@@ -37,7 +37,7 @@ Feature: Page contents assertions
And I follow "Course 1"
When I click on "Move this to the dock" "button" in the ".block_settings" "css_element"
Then I should not see "Question bank"
And I click on "//div[@id='dock']/descendant::*[contains(., 'Administration')]/h2" "xpath_element"
And I click on "//div[@id='dock']/descendant::h2[normalize-space(.)='Administration']" "xpath_element"
@javascript
Scenario: Locators inside specific DOM nodes using XPath
@@ -45,5 +45,5 @@ Feature: Page contents assertions
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And I log in as "admin"
When I click on "Move this to the dock" "button" in the "//*[contains(concat(' ', normalize-space(@class), ' '), ' block_settings ')]" "xpath_element"
When I click on "Move this to the dock" "button" in the "//div[contains(concat(' ', normalize-space(@class), ' '), ' block_settings ')]" "xpath_element"
Then I should not see "Turn editing on"
@@ -30,17 +30,17 @@ Feature: Set up contextual data for tests
Then I should see "Course 1"
And I should see "Course 2"
And I should see "Course 3"
When I go to the courses management page
And I go to the courses management page
And I follow "Cat 1"
Then I should see "Cat 2"
And I should see "Cat 2"
And I should see "Cat 3"
When I follow "Cat 3"
Then I should see "Course 1"
And I follow "Cat 3"
And I should see "Course 1"
And I should see "Course 2"
When I select "Cat 2" from "Course categories:"
Then I should see "No courses in this category"
When I select "Miscellaneous" from "Course categories:"
Then I should see "Course 3"
And I select "Cat 1 / Cat 2" from "Course categories:"
And I should see "No courses in this category"
And I select "Miscellaneous" from "Course categories:"
And I should see "Course 3"
@javascript
Scenario: Add a bunch of groups and groupings
+4 -6
View File
@@ -74,12 +74,11 @@ if (isset($script)) {
$prevreferences = $DB->get_records_select('profiling',
'url = ? AND runreference = 1 AND timecreated < ?',
array($run->url, $run->timecreated),
'timecreated DESC', 'runid', 0, 1);
$prevrunid = $prevreferences ? reset($prevreferences)->runid : false;
'timecreated DESC', 'runid, runcomment, timecreated', 0, 10);
echo $OUTPUT->box_start('generalbox boxwidthwide boxaligncenter');
$header = get_string('lastrunof', 'tool_profiling', $script);
echo $OUTPUT->heading($header);
$table = profiling_print_run($run, $prevrunid);
$table = profiling_print_run($run, $prevreferences);
echo $table;
echo $OUTPUT->box_end();
@@ -126,12 +125,11 @@ if (isset($script)) {
$prevreferences = $DB->get_records_select('profiling',
'url = ? AND runreference = 1 AND timecreated < ?',
array($run->url, $run->timecreated),
'timecreated DESC', 'runid', 0, 1);
$prevrunid = $prevreferences ? reset($prevreferences)->runid : false;
'timecreated DESC', 'runid, runcomment, timecreated', 0, 10);
echo $OUTPUT->box_start('generalbox boxwidthwide boxaligncenter');
$header = get_string('summaryof', 'tool_profiling', $run->url);
echo $OUTPUT->heading($header);
$table = profiling_print_run($run, $prevrunid);
$table = profiling_print_run($run, $prevreferences);
echo $table;
echo $OUTPUT->box_end();
@@ -51,5 +51,5 @@ $string['referencerun'] = 'Reference run/comment';
$string['runid'] = 'Run ID';
$string['summaryof'] = 'Summary of {$a}';
$string['viewdetails'] = 'View profiling details';
$string['viewdiff'] = 'View profiling differences with last reference run';
$string['viewdiff'] = 'View profiling differences with:';
$string['viewdiffdetails'] = 'View profiling diff details';
+5 -5
View File
@@ -49,10 +49,10 @@ class behat_auth extends behat_base {
public function i_log_in_as($username) {
return array(new Given('I am on homepage'),
new Given('I follow "Login"'),
new Given('I fill in "Username" with "'.$username.'"'),
new Given('I fill in "Password" with "'.$username.'"'),
new Given('I press "Login"')
new Given('I follow "' . get_string('login') . '"'),
new Given('I fill in "' . get_string('username') . '" with "' . $this->escape($username) . '"'),
new Given('I fill in "' . get_string('password') . '" with "'. $this->escape($username) . '"'),
new Given('I press "' . get_string('login') . '"')
);
}
@@ -62,7 +62,7 @@ class behat_auth extends behat_base {
* @Given /^I log out$/
*/
public function i_log_out() {
return new When('I follow "Logout"');
return new When('I follow "' . get_string('logout') . '"');
}
}
+19 -4
View File
@@ -1,5 +1,4 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
@@ -24,16 +23,32 @@
require_once($CFG->libdir.'/formslib.php');
class backup_files_edit_form extends moodleform {
function definition() {
/**
* Form definition.
*/
public function definition() {
$mform =& $this->_form;
$contextid = $this->_customdata['contextid'];
$options = array('subdirs'=>0, 'maxfiles'=>-1, 'accepted_types'=>'*', 'return_types'=>FILE_INTERNAL | FILE_REFERENCE);
$options = array('subdirs' => 0, 'maxfiles' => -1, 'accepted_types' => '*', 'return_types' => FILE_INTERNAL | FILE_REFERENCE);
$mform->addElement('filemanager', 'files_filemanager', get_string('files'), null, $options);
$mform->addElement('hidden', 'contextid', $this->_customdata['contextid']);
$mform->setType('contextid', PARAM_INT);
$mform->addElement('hidden', 'currentcontext', $this->_customdata['currentcontext']);
$mform->setType('currentcontext', PARAM_INT);
$mform->addElement('hidden', 'filearea', $this->_customdata['filearea']);
$mform->setType('filearea', PARAM_AREA);
$mform->addElement('hidden', 'component', $this->_customdata['component']);
$mform->setType('component', PARAM_COMPONENT);
$mform->addElement('hidden', 'returnurl', $this->_customdata['returnurl']);
$mform->setType('returnurl', PARAM_URL);
$this->add_action_buttons(true, get_string('savechanges'));
$this->set_data($this->_customdata['data']);
}
+13 -6
View File
@@ -614,13 +614,20 @@ abstract class restore_dbops {
} else {
self::set_backup_ids_record($restoreid, 'question_category', $category->id, $matchcat->id, $targetcontext->id);
$questions = self::restore_get_questions($restoreid, $category->id);
// Collect all the questions for this category into memory so we only talk to the DB once.
$questioncache = $DB->get_records_sql_menu("SELECT ".$DB->sql_concat('stamp', "' '", 'version').", id
FROM {question}
WHERE category = ?", array($matchcat->id));
foreach ($questions as $question) {
$matchq = $DB->get_record('question', array(
'category' => $matchcat->id,
'stamp' => $question->stamp,
'version' => $question->version));
if (isset($questioncache[$question->stamp." ".$question->version])) {
$matchqid = $questioncache[$question->stamp." ".$question->version];
} else {
$matchqid = false;
}
// 5a) No match, check if user can add q
if (!$matchq) {
if (!$matchqid) {
// 6a) User can, mark the q to be created
if ($canadd) {
// Nothing to mark, newitemid means create
@@ -645,7 +652,7 @@ abstract class restore_dbops {
// 5b) Match, mark q to be mapped
} else {
self::set_backup_ids_record($restoreid, 'question', $question->id, $matchq->id);
self::set_backup_ids_record($restoreid, 'question', $question->id, $matchqid);
}
}
}
@@ -31,7 +31,7 @@ Feature: Backup Moodle courses
And I should not see "Section 3"
And I press "Continue"
And I click on "Continue" "button" in the ".bcs-current-course" "css_element"
And I click on "//div[contains(concat(' ', @class, ' '), ' fitem ')][contains(., 'Include calendar events')]/descendant::img" "xpath_element"
And I click on "setting_root_logs" "checkbox" in the "//div[contains(@class, 'fitem')][contains(., 'Include course logs')]" "xpath_element"
And "//div[contains(concat(' ', normalize-space(@class), ' '), ' fitem ')][contains(., 'Include calendar events')]/descendant::img" "xpath_element" should exists
And I check "Include course logs"
And I press "Cancel"
And I click on "Cancel" "button" in the ".confirmation-dialogue" "css_element"
+46 -33
View File
@@ -61,25 +61,25 @@ class behat_backup extends behat_base {
$this->find_link($backupcourse)->click();
// Click the backup link.
$this->find_link('Backup')->click();
$this->find_link(get_string('backup'))->click();
// Initial settings.
$this->fill_backup_restore_form($options);
$this->find_button('Next')->press();
$this->find_button(get_string('backupstage1action', 'backup'))->press();
// Schema settings.
$this->fill_backup_restore_form($options);
$this->find_button('Next')->press();
$this->find_button(get_string('backupstage2action', 'backup'))->press();
// Confirmation and review, backup filename can also be specified.
$this->fill_backup_restore_form($options);
$this->find_button('Perform backup')->press();
$this->find_button(get_string('backupstage4action', 'backup'))->press();
// Waiting for it to finish.
$this->wait(10);
// Last backup continue button.
$this->find_button('Continue')->press();
$this->find_button(get_string('backupstage16action', 'backup'))->press();
}
/**
@@ -105,36 +105,37 @@ class behat_backup extends behat_base {
// Click the course link.
$this->find_link($tocourse)->click();
// Click the backup link.
$this->find_link('Import')->click();
// Click the import link.
$this->find_link(get_string('import'))->click();
// Select the course.
$exception = new ExpectationException('"' . $fromcourse . '" course not found in the list of courses to import from', $this->getSession());
$fromcourse = str_replace("'", "\'", $fromcourse);
$xpath = "//div[contains(concat(' ', @class, ' '), ' ics-results ')]" .
"/descendant::tr[contains(., '" . $fromcourse . "')]" .
// The argument should be converted to an xpath literal.
$fromcourse = $this->getSession()->getSelectorsHandler()->xpathLiteral($fromcourse);
$xpath = "//div[contains(concat(' ', normalize-space(@class), ' '), ' ics-results ')]" .
"/descendant::tr[contains(., $fromcourse)]" .
"/descendant::input[@type='radio']";
$radionode = $this->find('xpath', $xpath, $exception);
$radionode->check();
$radionode->click();
$this->find_button('Continue')->press();
$this->find_button(get_string('continue'))->press();
// Initial settings.
$this->fill_backup_restore_form($options);
$this->find_button('Next')->press();
$this->find_button(get_string('importbackupstage1action', 'backup'))->press();
// Schema settings.
$this->fill_backup_restore_form($options);
$this->find_button('Next')->press();
$this->find_button(get_string('importbackupstage2action', 'backup'))->press();
// Run it.
$this->find_button('Perform import')->press();
$this->find_button(get_string('importbackupstage4action', 'backup'))->press();
$this->wait();
// Continue and redirect to 'to' course.
$this->find_button('Continue')->press();
$this->find_button(get_string('continue'))->press();
}
/**
@@ -150,17 +151,20 @@ class behat_backup extends behat_base {
// Confirm restore.
$this->select_backup($backupfilename);
// The argument should be converted to an xpath literal.
$existingcourse = $this->getSession()->getSelectorsHandler()->xpathLiteral($existingcourse);
// Selecting the specified course (we can not call behat_forms::select_radio here as is in another behat subcontext).
$existingcourse = str_replace("'", "\'", $existingcourse);
$radionode = $this->find('xpath', "//div[contains(@class, 'bcs-existing-course')]" .
$radionode = $this->find('xpath', "//div[contains(concat(' ', normalize-space(@class), ' '), ' bcs-existing-course ')]" .
"/descendant::div[@class='restore-course-search']" .
"/descendant::tr[contains(., '" . $existingcourse . "')]" .
"/descendant::tr[contains(., $existingcourse)]" .
"/descendant::input[@type='radio']");
$radionode->check();
$radionode->click();
// Pressing the continue button of the restore into an existing course section.
$continuenode = $this->find('xpath', "//div[contains(@class, 'bcs-existing-course')]/descendant::input[@type='submit'][@value='Continue']");
$continuenode = $this->find('xpath', "//div[contains(concat(' ', normalize-space(@class), ' '), ' bcs-existing-course ')]" .
"/descendant::input[@type='submit'][@value='" . get_string('continue') . "']");
$continuenode->click();
$this->wait();
@@ -181,14 +185,15 @@ class behat_backup extends behat_base {
$this->select_backup($backupfilename);
// The first category in the list.
$radionode = $this->find('xpath', "//div[contains(@class, 'bcs-new-course')]" .
$radionode = $this->find('xpath', "//div[contains(concat(' ', normalize-space(@class), ' '), ' bcs-new-course ')]" .
"/descendant::div[@class='restore-course-search']" .
"/descendant::input[@type='radio']");
$radionode->check();
$radionode->click();
// Pressing the continue button of the restore into an existing course section.
$continuenode = $this->find('xpath', "//div[contains(@class, 'bcs-new-course')]/descendant::input[@type='submit'][@value='Continue']");
$continuenode = $this->find('xpath', "//div[contains(concat(' ', normalize-space(@class), ' '), ' bcs-new-course ')]" .
"/descendant::input[@type='submit'][@value='" . get_string('continue') . "']");
$continuenode->click();
$this->wait();
@@ -209,14 +214,14 @@ class behat_backup extends behat_base {
$this->select_backup($backupfilename);
// Merge without deleting radio option.
$radionode = $this->find('xpath', "//div[contains(@class, 'bcs-current-course')]" .
$radionode = $this->find('xpath', "//div[contains(concat(' ', normalize-space(@class), ' '), 'bcs-current-course')]" .
"/descendant::input[@type='radio'][@name='target'][@value='1']");
$radionode->check();
$radionode->click();
// Pressing the continue button of the restore merging section.
$continuenode = $this->find('xpath', "//div[contains(@class, 'bcs-current-course')]" .
"/descendant::input[@type='submit'][@value='Continue']");
$continuenode = $this->find('xpath', "//div[contains(concat(' ', normalize-space(@class), ' '), 'bcs-current-course')]" .
"/descendant::input[@type='submit'][@value='" . get_string('continue') . "']");
$continuenode->click();
$this->wait();
@@ -237,14 +242,14 @@ class behat_backup extends behat_base {
$this->select_backup($backupfilename);
// Delete contents radio option.
$radionode = $this->find('xpath', "//div[contains(@class, 'bcs-current-course')]" .
$radionode = $this->find('xpath', "//div[contains(concat(' ', normalize-space(@class), ' '), 'bcs-current-course')]" .
"/descendant::input[@type='radio'][@name='target'][@value='0']");
$radionode->check();
$radionode->click();
// Pressing the continue button of the restore merging section.
$continuenode = $this->find('xpath', "//div[contains(@class, 'bcs-current-course')]" .
"/descendant::input[@type='submit'][@value='Continue']");
$continuenode = $this->find('xpath', "//div[contains(concat(' ', normalize-space(@class), ' '), 'bcs-current-course')]" .
"/descendant::input[@type='submit'][@value='" . get_string('continue') . "']");
$continuenode->click();
$this->wait();
@@ -263,12 +268,16 @@ class behat_backup extends behat_base {
// Using xpath as there are other restore links before this one.
$exception = new ExpectationException('The "' . $backupfilename . '" backup file can not be found in this page', $this->getSession());
$xpath = "//tr[contains(., '" . $backupfilename . "')]/descendant::a[contains(., 'Restore')]";
// The argument should be converted to an xpath literal.
$backupfilename = $this->getSession()->getSelectorsHandler()->xpathLiteral($backupfilename);
$xpath = "//tr[contains(., $backupfilename)]/descendant::a[contains(., '" . get_string('restore') . "')]";
$restorelink = $this->find('xpath', $xpath, $exception);
$restorelink->click();
// Confirm the backup contents.
$restore = $this->find_button('Continue')->press();
$restore = $this->find_button(get_string('continue'))->press();
}
/**
@@ -284,18 +293,18 @@ class behat_backup extends behat_base {
// Settings.
$this->fill_backup_restore_form($options);
$this->find_button('Next')->press();
$this->find_button(get_string('restorestage4action', 'backup'))->press();
// Schema.
$this->fill_backup_restore_form($options);
$this->find_button('Next')->press();
$this->find_button(get_string('restorestage8action', 'backup'))->press();
// Review, no options here.
$this->find_button('Perform restore')->press();
$this->find_button(get_string('restorestage16action', 'backup'))->press();
$this->wait(10);
// Last restore continue button, redirected to restore course after this.
$this->find_button('Continue')->press();
$this->find_button(get_string('restorestage32action', 'backup'))->press();
}
/**
@@ -339,6 +348,10 @@ class behat_backup extends behat_base {
*/
protected function wait($timeout = false) {
if (!$this->running_javascript()) {
return;
}
if (!$timeout) {
$timeout = self::TIMEOUT;
}
@@ -43,6 +43,18 @@ abstract class grouped_parser_processor extends simplified_parser_processor {
protected $groupedpaths; // Paths we are requesting grouped
protected $currentdata; // Where we'll be acummulating data
/**
* Keep cache of parent directory paths for XML parsing.
* @var array
*/
protected $parentcache = array();
/**
* Remaining space for parent directory paths.
* @var integer
*/
protected $parentcacheavailablesize = 2048;
public function __construct(array $paths = array()) {
$this->groupedpaths = array();
$this->currentdata = null;
@@ -65,7 +77,7 @@ abstract class grouped_parser_processor extends simplified_parser_processor {
$a->child = $found;
throw new progressive_parser_exception('xml_grouped_child_found', $a);
}
$this->groupedpaths[] = $path;
$this->groupedpaths[$path] = true;
}
parent::add_path($path);
}
@@ -141,7 +153,7 @@ abstract class grouped_parser_processor extends simplified_parser_processor {
}
protected function path_is_grouped($path) {
return in_array($path, $this->groupedpaths);
return isset($this->groupedpaths[$path]);
}
/**
@@ -150,16 +162,41 @@ abstract class grouped_parser_processor extends simplified_parser_processor {
* false if not
*/
protected function grouped_parent_exists($path) {
$parentpath = progressive_parser::dirname($path);
$parentpath = $this->get_parent_path($path);
while ($parentpath != '/') {
if ($this->path_is_grouped($parentpath)) {
return $parentpath;
}
$parentpath = progressive_parser::dirname($parentpath);
$parentpath = $this->get_parent_path($parentpath);
}
return false;
}
/**
* Get the parent path using a local cache for performance.
*
* @param $path string The pathname you wish to obtain the parent name for.
* @return string The parent pathname.
*/
protected function get_parent_path($path) {
if (!isset($this->parentcache[$path])) {
$this->parentcache[$path] = progressive_parser::dirname($path);
$this->parentcacheavailablesize--;
if ($this->parentcacheavailablesize < 0) {
// Older first is cheaper than LRU. We use 10% as items are grouped together and the large quiz
// restore from MDL-40585 used only 600 parent paths. This is an XML heirarchy, so common paths
// are grouped near each other. eg; /question_bank/question_category/question/element. After keeping
// question_bank paths in the cache when we move to another area and the question_bank cache is not
// useful any longer.
$this->parentcache = array_slice($this->parentcache, 200, null, true);
$this->parentcacheavailablesize += 200;
}
}
return $this->parentcache[$path];
}
/**
* Function that will look for any grouped
* child for the given path, returning it if found,
@@ -167,7 +204,7 @@ abstract class grouped_parser_processor extends simplified_parser_processor {
*/
protected function grouped_child_exists($path) {
$childpath = $path . '/';
foreach ($this->groupedpaths as $groupedpath) {
foreach ($this->groupedpaths as $groupedpath => $set) {
if (strpos($groupedpath, $childpath) === 0) {
return $groupedpath;
}
+14 -1
View File
@@ -2,7 +2,20 @@
* Push badges to backpack.
*/
function addtobackpack(event, args) {
OpenBadges.issue([args.assertion], function(errors, successes) { });
var badgetable = Y.one('#issued-badge-table');
var errordiv = Y.one('#addtobackpack-error');
var errortext = M.util.get_string('error:backpackproblem', 'badges');
var errorhtml = '<div id="addtobackpack-error" class="box boxaligncenter notifyproblem">' + errortext + '</div>';
if (typeof OpenBadges !== 'undefined') {
OpenBadges.issue([args.assertion], function(errors, successes) { });
} else {
// Add error div if it doesn't exist yet.
if (!errordiv) {
var badgerror = Y.Node.create(errorhtml);
badgetable.insert(badgerror, 'before');
}
}
}
/**
+2 -4
View File
@@ -56,10 +56,8 @@ if (isloggedin()) {
navigation_node::override_active_url($url);
}
// TODO: Better way of pushing badges to Mozilla backpack?
if (!empty($CFG->badges_allowexternalbackpack)) {
$PAGE->requires->js(new moodle_url('http://backpack.openbadges.org/issuer.js'), true);
}
// Include JS files for backpack support.
badges_setup_backpack_js();
echo $OUTPUT->header();
+2 -5
View File
@@ -90,11 +90,8 @@ $PAGE->set_title($title);
$PAGE->set_heading($title);
$PAGE->set_pagelayout('mydashboard');
// TODO: Better way of pushing badges to Mozilla backpack?
if (!empty($CFG->badges_allowexternalbackpack)) {
$PAGE->requires->js(new moodle_url('http://backpack.openbadges.org/issuer.js'), true);
$PAGE->requires->js('/badges/backpack.js', true);
}
// Include JS files for backpack support.
badges_setup_backpack_js();
$output = $PAGE->get_renderer('core', 'badges');
$badges = badges_get_user_badges($USER->id);
+7 -4
View File
@@ -282,6 +282,7 @@ class core_badges_renderer extends plugin_renderer_base {
$today = strtotime($today_date);
$table = new html_table();
$table->id = 'issued-badge-table';
$imagetable = new html_table();
$imagetable->attributes = array('class' => 'clearfix badgeissuedimage');
@@ -294,11 +295,13 @@ class core_badges_renderer extends plugin_renderer_base {
$expiration = isset($issued['expires']) ? strtotime($issued['expires']) : $today + 1;
if (!empty($CFG->badges_allowexternalbackpack) && ($expiration > $today) && badges_user_has_backpack($USER->id)) {
$assertion = new moodle_url('/badges/assertion.php', array('b' => $ibadge->hash));
$action = new component_action('click', 'addtobackpack', array('assertion' => $assertion->out(false)));
$attributes = array(
'type' => 'button',
'value' => get_string('addtobackpack', 'badges'),
'onclick' => 'OpenBadges.issue(["' . $assertion->out(false) . '"], function(errors, successes) { })');
'type' => 'button',
'id' => 'addbutton',
'value' => get_string('addtobackpack', 'badges'));
$tobackpack = html_writer::tag('input', '', $attributes);
$this->output->add_action_handler($action, 'addbutton');
$imagetable->data[] = array($tobackpack);
}
}
@@ -459,7 +462,7 @@ class core_badges_renderer extends plugin_renderer_base {
get_string('downloadall'), 'POST', array('class' => 'activatebadge'));
// Local badges.
$localhtml = html_writer::start_tag('fieldset', array('class' => 'generalbox'));
$localhtml = html_writer::start_tag('fieldset', array('id' => 'issued-badge-table', 'class' => 'generalbox'));
$localhtml .= html_writer::tag('legend',
$this->output->heading_with_help(get_string('localbadges', 'badges', $SITE->fullname), 'localbadgesh', 'badges'));
if ($badges->badges) {
@@ -63,7 +63,7 @@ class behat_block_comments extends behat_base {
$commentstextarea = $this->find('css', '.comment-area textarea', $exception);
$commentstextarea->setValue($comment);
$this->find_link('Save comment')->click();
$this->find_link(get_string('savecomment'))->click();
// Wait for the AJAX request.
$this->getSession()->wait(4 * 1000, false);
@@ -91,8 +91,11 @@ class behat_block_comments extends behat_base {
$exception = new ElementNotFoundException($this->getSession(), '"' . $comment . '" comment ');
$commentxpath = "//div[contains(concat(' ', @class, ' '), ' block_comments ')]" .
"/descendant::div[@class='comment-message'][contains(., '" . $comment . "')]";
// Using xpath liternal to avoid possible problems with comments containing quotes.
$commentliteral = $this->getSession()->getSelectorsHandler()->xpathLiteral($comment);
$commentxpath = "//div[contains(concat(' ', normalize-space(@class), ' '), ' block_comments ')]" .
"/descendant::div[@class='comment-message'][contains(., $commentliteral)]";
$commentnode = $this->find('xpath', $commentxpath, $exception);
// Click on delete icon.
@@ -101,7 +104,7 @@ class behat_block_comments extends behat_base {
$deleteicon->click();
// Yes confirm.
$confirmnode = $this->find('xpath', "//div[@class='comment-delete-confirm']/descendant::a[contains(., 'Yes')]");
$confirmnode = $this->find('xpath', "//div[@class='comment-delete-confirm']/descendant::a[contains(., '" . get_string('yes') . "')]");
$confirmnode->click();
// Wait for the AJAX request.
+3 -3
View File
@@ -103,10 +103,10 @@ class block_course_overview_renderer extends plugin_renderer_base {
// No need to pass title through s() here as it will be done automatically by html_writer.
$attributes = array('title' => $course->fullname);
if (empty($course->visible)) {
$attributes['class'] = 'dimmed';
}
if ($course->id > 0) {
if (empty($course->visible)) {
$attributes['class'] = 'dimmed';
}
$courseurl = new moodle_url('/course/view.php', array('id' => $course->id));
$coursefullname = format_string($course->fullname, true, $course->id);
$link = html_writer::link($courseurl, $coursefullname, $attributes);
+2 -2
View File
@@ -46,13 +46,13 @@ class behat_blocks extends behat_base {
* @param string $blockname
*/
public function i_add_the_block($blockname) {
$steps = new Given('I select "' . $blockname . '" from "bui_addblock"');
$steps = new Given('I select "' . $this->escape($blockname) . '" from "bui_addblock"');
// If we are running without javascript we need to submit the form.
if (!$this->running_javascript()) {
$steps = array(
$steps,
new Given('I click on "Go" "button" in the "#add_block" "css_element"')
new Given('I click on "' . get_string('go') . '" "button" in the "#add_block" "css_element"')
);
}
return $steps;
@@ -32,4 +32,4 @@ Feature: Add and configure blocks throughout the site
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(' ', @class, ' '), ' block ')]" "xpath_element"
And I should see "Comments" in the "//*[@id='region-pre']/descendant::div[contains(concat(' ', normalize-space(@class), ' '), ' block ')]" "xpath_element"
+2 -1
View File
@@ -49,7 +49,8 @@ class cachestore_memcache_addinstance_form extends cachestore_addinstance_form {
$form->addElement('text', 'prefix', get_string('prefix', 'cachestore_memcache'),
array('maxlength' => 5, 'size' => 5));
$form->addHelpButton('prefix', 'prefix', 'cachestore_memcache');
$form->setType('prefix', PARAM_ALPHAEXT);
$form->setType('prefix', PARAM_TEXT); // We set to text but we have a rule to limit to alphanumext.
$form->setDefault('prefix', 'mdl_');
$form->addRule('prefix', get_string('prefixinvalid', 'cachestore_memcache'), 'regex', '#^[a-zA-Z0-9\-_]+$#');
}
}
+1
View File
@@ -31,6 +31,7 @@ $string['prefix'] = 'Key prefix';
$string['prefix_help'] = 'This prefix is used for all key names on the memcache server.
* If you only have one Moodle instance using this server, you can leave this value default.
* Due to key length restrictions, a maximum of 5 characters is permitted.';
$string['prefixinvalid'] = 'Invalid prefix. You can only use a-z A-Z 0-9-_.';
$string['servers'] = 'Servers';
$string['servers_help'] = 'This sets the servers that should be utilised by this memcache adapter.
Servers should be defined one per line and consist of a server address and optionally a port and weight.
+1 -1
View File
@@ -404,7 +404,7 @@ class cachestore_memcache extends cache_store implements cache_is_configurable {
* Generates an instance of the cache store that can be used for testing.
*
* @param cache_definition $definition
* @return false
* @return cachestore_memcache|false
*/
public static function initialise_test_instance(cache_definition $definition) {
if (!self::are_requirements_met()) {
+32 -1
View File
@@ -59,4 +59,35 @@ class cachestore_memcache_test extends cachestore_tests {
protected function get_class_name() {
return 'cachestore_memcache';
}
}
/**
* Tests the valid keys to ensure they work.
*/
public function test_valid_keys() {
$definition = cache_definition::load_adhoc(cache_store::MODE_APPLICATION, 'cachestore_memcache', 'phpunit_test');
$instance = cachestore_memcache::initialise_test_instance($definition);
if (!$instance) { // Something prevented memcache store to be inited (extension, TEST_CACHESTORE_MEMCACHE_TESTSERVERS...).
$this->markTestSkipped();
}
$keys = array(
// Alphanumeric.
'abc', 'ABC', '123', 'aB1', '1aB',
// Hyphens.
'a-1', '1-a', '-a1', 'a1-',
// Underscores.
'a_1', '1_a', '_a1', 'a1_'
);
foreach ($keys as $key) {
$this->assertTrue($instance->set($key, $key), "Failed to set key `$key`");
}
foreach ($keys as $key) {
$this->assertEquals($key, $instance->get($key), "Failed to get key `$key`");
}
$values = $instance->get_many($keys);
foreach ($values as $key => $value) {
$this->assertEquals($key, $value);
}
}
}
+4 -3
View File
@@ -57,11 +57,12 @@ class cachestore_memcached_addinstance_form extends cachestore_addinstance_form
$form->addElement('select', 'serialiser', get_string('useserialiser', 'cachestore_memcached'), $serialiseroptions);
$form->addHelpButton('serialiser', 'useserialiser', 'cachestore_memcached');
$form->setDefault('serialiser', Memcached::SERIALIZER_PHP);
$form->setType('serialiser', PARAM_NUMBER);
$form->setType('serialiser', PARAM_INT);
$form->addElement('text', 'prefix', get_string('prefix', 'cachestore_memcached'), array('size' => 16));
$form->setType('prefix', PARAM_ALPHANUM);
$form->setType('prefix', PARAM_TEXT); // We set to text but we have a rule to limit to alphanumext.
$form->addHelpButton('prefix', 'prefix', 'cachestore_memcached');
$form->addRule('prefix', get_string('prefixinvalid', 'cachestore_memcached'), 'regex', '#^[a-zA-Z0-9\-_]+$#');
$hashoptions = cachestore_memcached::config_get_hash_options();
$form->addElement('select', 'hash', get_string('hash', 'cachestore_memcached'), $hashoptions);
@@ -74,4 +75,4 @@ class cachestore_memcached_addinstance_form extends cachestore_addinstance_form
$form->setDefault('bufferwrites', 0);
$form->setType('bufferwrites', PARAM_BOOL);
}
}
}
@@ -42,6 +42,7 @@ $string['hash_murmur'] = 'Murmur';
$string['pluginname'] = 'Memcached';
$string['prefix'] = 'Prefix key';
$string['prefix_help'] = 'This can be used to create a "domain" for your item keys allowing you to create multiple memcached stores on a single memcached installation. It cannot be longer than 16 characters in order to ensure key length issues are not encountered.';
$string['prefixinvalid'] = 'Invalid prefix. You can only use a-z A-Z 0-9-_.';
$string['serialiser_igbinary'] = 'The igbinary serializer.';
$string['serialiser_json'] = 'The JSON serializer.';
$string['serialiser_php'] = 'The default PHP serializer.';
+2 -2
View File
@@ -339,7 +339,7 @@ class cachestore_memcached extends cache_store implements cache_is_configurable
$options[Memcached::SERIALIZER_JSON] = get_string('serialiser_json', 'cachestore_memcached');
}
if (Memcached::HAVE_IGBINARY) {
$options[Memcached::SERIALIZER_IGBINARY] = get_string('serialiser_php', 'cachestore_memcached');
$options[Memcached::SERIALIZER_IGBINARY] = get_string('serialiser_igbinary', 'cachestore_memcached');
}
return $options;
}
@@ -444,7 +444,7 @@ class cachestore_memcached extends cache_store implements cache_is_configurable
* Generates an instance of the cache store that can be used for testing.
*
* @param cache_definition $definition
* @return false
* @return cachestore_memcached|false
*/
public static function initialise_test_instance(cache_definition $definition) {
+32 -1
View File
@@ -59,4 +59,35 @@ class cachestore_memcached_test extends cachestore_tests {
protected function get_class_name() {
return 'cachestore_memcached';
}
}
/**
* Tests the valid keys to ensure they work.
*/
public function test_valid_keys() {
$definition = cache_definition::load_adhoc(cache_store::MODE_APPLICATION, 'cachestore_memcached', 'phpunit_test');
$instance = cachestore_memcached::initialise_test_instance($definition);
if (!$instance) { // Something prevented memcached store to be inited (extension, TEST_CACHESTORE_MEMCACHED_TESTSERVERS...).
$this->markTestSkipped();
}
$keys = array(
// Alphanumeric.
'abc', 'ABC', '123', 'aB1', '1aB',
// Hyphens.
'a-1', '1-a', '-a1', 'a1-',
// Underscores.
'a_1', '1_a', '_a1', 'a1_'
);
foreach ($keys as $key) {
$this->assertTrue($instance->set($key, $key), "Failed to set key `$key`");
}
foreach ($keys as $key) {
$this->assertEquals($key, $instance->get($key), "Failed to get key `$key`");
}
$values = $instance->get_many($keys);
foreach ($values as $key => $value) {
$this->assertEquals($key, $value);
}
}
}
+2 -2
View File
@@ -62,7 +62,7 @@ abstract class cachestore_tests extends advanced_testcase {
}
}
if ($modes & cache_store::MODE_SESSION) {
$definition = cache_definition::load_adhoc(cache_store::MODE_APPLICATION, $class, 'phpunit_test');
$definition = cache_definition::load_adhoc(cache_store::MODE_SESSION, $class, 'phpunit_test');
$instance = $class::initialise_test_instance($definition);
if (!$instance) {
$this->markTestSkipped('Could not test '.$class.'. No test instance configured for session caches.');
@@ -71,7 +71,7 @@ abstract class cachestore_tests extends advanced_testcase {
}
}
if ($modes & cache_store::MODE_REQUEST) {
$definition = cache_definition::load_adhoc(cache_store::MODE_APPLICATION, $class, 'phpunit_test');
$definition = cache_definition::load_adhoc(cache_store::MODE_REQUEST, $class, 'phpunit_test');
$instance = $class::initialise_test_instance($definition);
if (!$instance) {
$this->markTestSkipped('Could not test '.$class.'. No test instance configured for request caches.');
+9 -9
View File
@@ -54,10 +54,10 @@ class behat_cohort extends behat_base {
$userid = $DB->get_field('user', 'id', array('username' => $username));
$steps = array(
new Given('I click on "Assign" "link" in the "//table[@id=\'cohorts\']//tr[contains(., \'' . $cohortidnumber . '\')]" "xpath_element"'),
new Given('I select "' . $userid . '" from "Potential users"'),
new Given('I press "Add"'),
new Given('I press "Back to cohorts"')
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') . '"')
);
// If we are not in the cohorts management we should move there before anything else.
@@ -65,11 +65,11 @@ class behat_cohort extends behat_base {
$steps = array_merge(
array(
new Given('I am on homepage'),
new Given('I collapse "Front page settings" node'),
new Given('I expand "Site administration" node'),
new Given('I expand "Users" node'),
new Given('I expand "Accounts" node'),
new Given('I follow "Cohorts"')
new Given('I collapse "' . get_string('frontpagesettings', 'admin') . '" node'),
new Given('I expand "' . get_string('administrationsite') . '" node'),
new Given('I expand "' . get_string('users', 'admin') . '" node'),
new Given('I expand "' . get_string('accounts', 'admin') . '" node'),
new Given('I follow "' . get_string('cohorts', 'cohort') . '"')
),
$steps
);
@@ -32,11 +32,11 @@ Feature: Upload users to a cohort
And I press "Upload users"
And I press "Continue"
And I follow "Cohorts"
And I click on "Assign" "link" in the "//table[@id='cohorts']//tr[contains(., 'Cohort 1')]" "xpath_element"
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 "//table[@id='cohorts']//tr[contains(., 'Cohort 2')]" "xpath_element"
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
+12 -9
View File
@@ -50,12 +50,13 @@ class behat_completion extends behat_base {
public function user_has_completed_activity($userfullname, $activityname) {
// Will throw an exception if the element can not be hovered.
$titleliteral = $this->getSession()->getSelectorsHandler()->xpathLiteral($userfullname . ", " . $activityname . ": Completed");
$xpath = "//table[@id='completion-progress']" .
"/descendant::img[contains(@title, '" . $userfullname . ", " . $activityname . ": Completed')]";
"/descendant::img[contains(@title, $titleliteral)]";
return array(
new Given('I go to the current course activity completion report'),
new Given('I hover "' . $xpath . '" "xpath_element"')
new Given('I hover "' . $this->escape($xpath) . '" "xpath_element"')
);
}
@@ -68,11 +69,13 @@ class behat_completion extends behat_base {
*/
public function user_has_not_completed_activity($userfullname, $activityname) {
// Will throw an exception if the element can not be hovered.
$titleliteral = $this->getSession()->getSelectorsHandler()->xpathLiteral($userfullname . ", " . $activityname . ": Not completed");
$xpath = "//table[@id='completion-progress']" .
"/descendant::img[contains(@title, '" . $userfullname . ", " . $activityname . ": Not completed')]";
"/descendant::img[contains(@title, $titleliteral)]";
return array(
new Given('I go to the current course activity completion report'),
new Given('I hover "' . $xpath . '" "xpath_element"')
new Given('I hover "' . $this->escape($xpath) . '" "xpath_element"')
);
return $steps;
@@ -89,15 +92,15 @@ class behat_completion extends behat_base {
// Expand reports node if we can't see the link.
try {
$this->find('xpath', "//*[@id='settingsnav']" .
$this->find('xpath', "//div[@id='settingsnav']" .
"/descendant::li" .
"/descendant::li[not(contains(@class,'collapsed'))]" .
"/descendant::p[contains(., 'Activity completion')]");
"/descendant::li[not(contains(concat(' ', normalize-space(@class), ' '), ' collapsed '))]" .
"/descendant::p[contains(., '" . get_string('pluginname', 'report_progress') . "')]");
} catch (ElementNotFoundException $e) {
$steps[] = new Given('I expand "Reports" node');
$steps[] = new Given('I expand "' . get_string('reports') . '" node');
}
$steps[] = new Given('I follow "Activity completion"');
$steps[] = new Given('I follow "' . get_string('pluginname', 'report_progress') . '"');
return $steps;
}
+2 -3
View File
@@ -670,7 +670,6 @@ class core_course_renderer extends plugin_renderer_base {
//Accessibility: for files get description via icon, this is very ugly hack!
$instancename = $mod->get_formatted_name();
$altname = '';
$altname = $mod->modfullname;
// Avoid unnecessary duplication: if e.g. a forum name already
// includes the word forum (or Forum, etc) then it is unhelpful
@@ -706,7 +705,7 @@ class core_course_renderer extends plugin_renderer_base {
}
if ($mod->uservisible) {
// show accessibility note only if user can access the module himself
$accesstext = get_accesshide(get_string('hiddenfromstudents').': ');
$accesstext = get_accesshide(get_string('hiddenfromstudents').':'. $mod->modfullname);
}
}
@@ -723,7 +722,7 @@ class core_course_renderer extends plugin_renderer_base {
// Display link itself.
$activitylink = html_writer::empty_tag('img', array('src' => $mod->get_icon_url(),
'class' => 'iconlarge activityicon', 'alt' => $mod->modfullname)) . $accesstext .
'class' => 'iconlarge activityicon', 'alt' => ' ', 'role' => 'presentation')) . $accesstext .
html_writer::tag('span', $instancename . $altname, array('class' => 'instancename'));
if ($mod->uservisible) {
$output .= html_writer::link($url, $activitylink, array('class' => $linkclasses, 'onclick' => $onclick)) .
@@ -27,21 +27,21 @@ Feature: Toggle activities groups mode from the course page
| Force group mode | No |
When I press "Save changes"
Then "No groups (Click to change)" "link" should exists
And ".//a//img[contains(@src, 'groupn')]" "xpath_element" should exists
And "//a/child::img[contains(@src, 'groupn')]" "xpath_element" should exists
And I click on "No groups (Click to change)" "link" in the "Test forum name" activity
And I wait "3" seconds
And "Separate groups (Click to change)" "link" should exists
And ".//a//img[contains(@src, 'groups')]" "xpath_element" should exists
And "//a/child::img[contains(@src, 'groups')]" "xpath_element" should exists
And I reload the page
And "Separate groups (Click to change)" "link" should exists
And ".//a//img[contains(@src, 'groups')]" "xpath_element" should exists
And "//a/child::img[contains(@src, 'groups')]" "xpath_element" should exists
And I click on "Separate groups (Click to change)" "link" in the "Test forum name" activity
And I wait "3" seconds
And "Visible groups (Click to change)" "link" should exists
And ".//a//img[contains(@src, 'groupv')]" "xpath_element" should exists
And "//a/child::img[contains(@src, 'groupv')]" "xpath_element" should exists
And I reload the page
And "Visible groups (Click to change)" "link" should exists
And ".//a//img[contains(@src, 'groupv')]" "xpath_element" should exists
And "//a/child::img[contains(@src, 'groupv')]" "xpath_element" should exists
And I click on "Visible groups (Click to change)" "link" in the "Test forum name" activity
And "No groups (Click to change)" "link" should exists
And ".//a//img[contains(@src, 'groupn')]" "xpath_element" should exists
And "//a/child::img[contains(@src, 'groupn')]" "xpath_element" should exists
@@ -26,7 +26,7 @@ Feature: Indent items on the course page
When I indent right "Test glossary name" activity
Then "#section-1 li.glossary div.mod-indent-1" "css_element" should exists
And I indent right "Test glossary name" activity
And "//*[@id='section-1']/descendant::li[contains(concat(' ', @class, ' '), ' glossary ')]/descendant::a[@title='Move left']" "xpath_element" should exists
And "//li[@id='section-1']/descendant::li[contains(concat(' ', @class, ' '), ' glossary ')]/descendant::a[@title='Move left']" "xpath_element" should exists
And "#section-1 li.glossary div.mod-indent-2" "css_element" should exists
And I reload the page
And "#section-1 li.glossary div.mod-indent-2" "css_element" should exists
@@ -34,4 +34,4 @@ Feature: Indent items on the course page
And I indent left "Test glossary name" activity
And "#section-1 li.glossary div.mod-indent-2" "css_element" should not exists
And "#section-1 li.glossary div.mod-indent-1" "css_element" should not exists
And "//*[@id='section-1']/descendant::li[contains(concat(' ', @class, ' '), ' glossary ')]/descendant::a[@title='Move left']" "xpath_element" should not exists
And "//li[@id='section-1']/descendant::li[contains(concat(' ', @class, ' '), ' glossary ')]/descendant::a[@title='Move left']" "xpath_element" should not exists
+46 -42
View File
@@ -48,7 +48,7 @@ class behat_course extends behat_base {
* @Given /^I turn editing mode on$/
*/
public function i_turn_editing_mode_on() {
return new Given('I press "Turn editing on"');
return new Given('I press "' . get_string('turneditingon') . '"');
}
/**
@@ -56,7 +56,7 @@ class behat_course extends behat_base {
* @Given /^I turn editing mode off$/
*/
public function i_turn_editing_mode_off() {
return new Given('I press "Turn editing off"');
return new Given('I press "' . get_string('turneditingoff') . '"');
}
/**
@@ -68,9 +68,9 @@ class behat_course extends behat_base {
public function i_create_a_course_with(TableNode $table) {
return array(
new Given('I go to the courses management page'),
new Given('I press "Add a new course"'),
new Given('I press "' . get_string('addnewcourse') . '"'),
new Given('I fill the moodle form with:', $table),
new Given('I press "Save changes"')
new Given('I press "' . get_string('savechanges') . '"')
);
}
@@ -83,9 +83,9 @@ class behat_course extends behat_base {
return array(
new Given('I am on homepage'),
new Given('I expand "Site administration" node'),
new Given('I expand "Courses" node'),
new Given('I follow "Add/edit courses"'),
new Given('I expand "' . get_string('administrationsite') . '" node'),
new Given('I expand "' . get_string('courses', 'admin') . '" node'),
new Given('I follow "' . get_string('coursemgmt', 'admin') . '"'),
);
}
@@ -100,9 +100,9 @@ class behat_course extends behat_base {
public function i_add_to_section_and_i_fill_the_form_with($activity, $section, TableNode $data) {
return array(
new Given('I add a "'.$activity.'" to section "'.$section.'"'),
new Given('I add a "' . $this->escape($activity) . '" to section "' . $this->escape($section) . '"'),
new Given('I fill the moodle form with:', $data),
new Given('I press "Save and return to course"')
new Given('I press "' . get_string('savechangesandreturntocourse') . '"')
);
}
@@ -116,7 +116,9 @@ class behat_course extends behat_base {
*/
public function i_add_to_section($activity, $section) {
$sectionxpath = "//*[@id='section-" . $section . "']";
$sectionxpath = "//li[@id='section-" . $section . "']";
$activityliteral = $this->getSession()->getSelectorsHandler()->xpathLiteral(ucfirst($activity));
if ($this->running_javascript()) {
@@ -126,9 +128,9 @@ class behat_course extends behat_base {
$sectionnode->click();
// Clicks the selected activity if it exists.
$activity = ucfirst($activity);
$activityxpath = "//div[@id='chooseform']/descendant::label" .
"/descendant::span[contains(concat(' ', @class, ' '), ' typename ')][contains(.,'" . $activity . "')]" .
"/descendant::span[contains(concat(' ', normalize-space(@class), ' '), ' typename ')]" .
"[contains(., $activityliteral)]" .
"/parent::label/child::input";
$activitynode = $this->find('xpath', $activityxpath);
$activitynode->doubleClick();
@@ -137,8 +139,8 @@ class behat_course extends behat_base {
// Without Javascript.
// Selecting the option from the select box which contains the option.
$selectxpath = $sectionxpath . "/descendant::div[contains(concat(' ', @class, ' '), ' section_add_menus ')]" .
"/descendant::select[contains(., '" . $activity . "')]";
$selectxpath = $sectionxpath . "/descendant::div[contains(concat(' ', normalize-space(@class), ' '), ' section_add_menus ')]" .
"/descendant::select[contains(., $activityliteral)]";
$selectnode = $this->find('xpath', $selectxpath);
$selectnode->selectOption($activity);
@@ -162,7 +164,7 @@ class behat_course extends behat_base {
$xpath = $this->section_exists($sectionnumber);
return array(
new Given('I click on "' . get_string('markthistopic') . '" "link" in the "' . $xpath . '" "xpath_element"'),
new Given('I click on "' . get_string('markthistopic') . '" "link" in the "' . $this->escape($xpath) . '" "xpath_element"'),
new Given('I wait "2" seconds')
);
}
@@ -179,7 +181,7 @@ class behat_course extends behat_base {
$xpath = $this->section_exists($sectionnumber);
return array(
new Given('I click on "' . get_string('markedthistopic') . '" "link" in the "' . $xpath . '" "xpath_element"'),
new Given('I click on "' . get_string('markedthistopic') . '" "link" in the "' . $this->escape($xpath) . '" "xpath_element"'),
new Given('I wait "2" seconds')
);
}
@@ -268,7 +270,7 @@ class behat_course extends behat_base {
// Section should be hidden.
$exception = new ExpectationException('The section is not hidden', $this->getSession());
$this->find('xpath', $sectionxpath . "[contains(concat(' ', @class, ' '), ' hidden ')]", $exception);
$this->find('xpath', $sectionxpath . "[contains(concat(' ', normalize-space(@class), ' '), ' hidden ')]", $exception);
// The checking are different depending on user permissions.
if ($this->is_course_editor()) {
@@ -284,8 +286,8 @@ class behat_course extends behat_base {
foreach ($activities as $activity) {
// Dimmed.
$this->find('xpath', "//div[contains(concat(' ', @class, ' '), ' activityinstance ')]" .
"/a[contains(concat(' ', @class, ' '), ' dimmed ')]", $dimmedexception, $activity);
$this->find('xpath', "//div[contains(concat(' ', normalize-space(@class), ' '), ' activityinstance ')]" .
"/a[contains(concat(' ', normalize-space(@class), ' '), ' dimmed ')]", $dimmedexception, $activity);
// Non-JS browsers can not click on img elements.
if ($this->running_javascript()) {
@@ -319,7 +321,8 @@ class behat_course extends behat_base {
$sectionxpath = $this->section_exists($sectionnumber);
// Section should not be hidden.
if (!$this->getSession()->getPage()->find('xpath', $sectionxpath . "[not(contains(concat(' ', @class, ' '), ' hidden '))]")) {
$xpath = $sectionxpath . "[not(contains(concat(' ', normalize-space(@class), ' '), ' hidden '))]";
if (!$this->getSession()->getPage()->find('xpath', $xpath)) {
throw new ExpectationException('The section is hidden', $this->getSession());
}
@@ -449,10 +452,11 @@ class behat_course extends behat_base {
// JS enabled.
if ($this->running_javascript()) {
$destinationxpath = $sectionxpath . "/descendant::ul[contains(@class, 'yui3-dd-drop')]";
$destinationxpath = $sectionxpath . "/descendant::ul[contains(concat(' ', normalize-space(@class), ' '), ' yui3-dd-drop ')]";
return array(
new Given('I drag "' . $activitynode->getXpath() . '" "xpath_element" and I drop it in "' . $destinationxpath . '" "xpath_element"'),
new Given('I drag "' . $this->escape($activitynode->getXpath()) . '" "xpath_element" ' .
'and I drop it in "' . $this->escape($destinationxpath) . '" "xpath_element"'),
);
} else {
@@ -460,8 +464,8 @@ class behat_course extends behat_base {
// Moving to the fist spot of the section (before all other section's activities).
return array(
new Given('I click on "a.editing_move" "css_element" in the "' . $activityname . '" activity'),
new Given('I click on "li.movehere a" "css_element" in the "' . $sectionxpath . '" "xpath_element"'),
new Given('I click on "a.editing_move" "css_element" in the "' . $this->escape($activityname) . '" activity'),
new Given('I click on "li.movehere a" "css_element" in the "' . $this->escape($sectionxpath) . '" "xpath_element"'),
);
}
}
@@ -482,8 +486,8 @@ class behat_course extends behat_base {
// Adding chr(10) to save changes.
return array(
new Given('I click on "' . get_string('edittitle') . '" "link" in the "' . $activityname .'" activity'),
new Given('I fill in "title" with "' . $newactivityname . chr(10) . '"'),
new Given('I click on "' . get_string('edittitle') . '" "link" in the "' . $this->escape($activityname) .'" activity'),
new Given('I fill in "title" with "' . $this->escape($newactivityname) . chr(10) . '"'),
new Given('I wait "2" seconds')
);
}
@@ -497,7 +501,7 @@ class behat_course extends behat_base {
public function i_indent_right_activity($activityname) {
$steps = array(
new Given('I click on "' . get_string('moveright') . '" "link" in the "' . $activityname . '" activity')
new Given('I click on "' . get_string('moveright') . '" "link" in the "' . $this->escape($activityname) . '" activity')
);
if ($this->running_javascript()) {
@@ -516,7 +520,7 @@ class behat_course extends behat_base {
public function i_indent_left_activity($activityname) {
$steps = array(
new Given('I click on "' . get_string('moveleft') . '" "link" in the "' . $activityname . '" activity')
new Given('I click on "' . get_string('moveleft') . '" "link" in the "' . $this->escape($activityname) . '" activity')
);
if ($this->running_javascript()) {
@@ -553,7 +557,7 @@ class behat_course extends behat_base {
// With JS disabled.
$steps = array(
new Given('I click on "' . $deletestring . '" "link" in the "' . $activityname . '" activity'),
new Given('I click on "' . $this->escape($deletestring) . '" "link" in the "' . $this->escape($activityname) . '" activity'),
new Given('I press "' . get_string('yes') . '"')
);
@@ -569,7 +573,7 @@ class behat_course extends behat_base {
*/
public function i_duplicate_activity($activityname) {
return array(
new Given('I click on "' . get_string('duplicate') . '" "link" in the "' . $activityname . '" activity'),
new Given('I click on "' . get_string('duplicate') . '" "link" in the "' . $this->escape($activityname) . '" activity'),
new Given('I press "' . get_string('continue') .'"'),
new Given('I press "' . get_string('duplicatecontcourse') .'"')
);
@@ -584,7 +588,7 @@ class behat_course extends behat_base {
*/
public function i_duplicate_activity_editing_the_new_copy_with($activityname, TableNode $data) {
return array(
new Given('I click on "' . get_string('duplicate') . '" "link" in the "' . $activityname . '" activity'),
new Given('I click on "' . get_string('duplicate') . '" "link" in the "' . $this->escape($activityname) . '" activity'),
new Given('I press "' . get_string('continue') .'"'),
new Given('I press "' . get_string('duplicatecontedit') . '"'),
new Given('I fill the moodle form with:', $data),
@@ -657,9 +661,9 @@ class behat_course extends behat_base {
$courseformat = $this->get_course_format();
// Checking the show button alt text and show icon.
$showtext = get_string('showfromothers', $courseformat);
$linkxpath = $xpath . "/descendant::a[@title='". $showtext ."']";
$imgxpath = $linkxpath . "/descendant::img[@alt='". $showtext ."'][contains(@src, 'show')]";
$showtext = $this->getSession()->getSelectorsHandler()->xpathLiteral(get_string('showfromothers', $courseformat));
$linkxpath = $xpath . "/descendant::a[@title=$showtext]";
$imgxpath = $linkxpath . "/descendant::img[@alt=$showtext][contains(@src, 'show')]";
$exception = new ElementNotFoundException($this->getSession(), 'Show section icon ');
$this->find('xpath', $imgxpath, $exception);
@@ -684,9 +688,9 @@ class behat_course extends behat_base {
$courseformat = $this->get_course_format();
// Checking the hide button alt text and hide icon.
$hidetext = get_string('hidefromothers', $courseformat);
$linkxpath = $xpath . "/descendant::a[@title='" . $hidetext . "']";
$imgxpath = $linkxpath . "/descendant::img[@alt='" . $hidetext ."'][contains(@src, 'hide')]";
$hidetext = $this->getSession()->getSelectorsHandler()->xpathLiteral(get_string('hidefromothers', $courseformat));
$linkxpath = $xpath . "/descendant::a[@title=$hidetext]";
$imgxpath = $linkxpath . "/descendant::img[@alt=$hidetext][contains(@src, 'hide')]";
$exception = new ElementNotFoundException($this->getSession(), 'Hide section icon ');
$this->find('xpath', $imgxpath, $exception);
@@ -730,7 +734,7 @@ class behat_course extends behat_base {
*/
protected function get_section_activities($sectionxpath) {
$xpath = $sectionxpath . "/descendant::li[contains(concat(' ', @class, ' '), ' activity ')]";
$xpath = $sectionxpath . "/descendant::li[contains(concat(' ', normalize-space(@class), ' '), ' activity ')]";
// We spin here, as activities usually require a lot of time to load.
try {
@@ -751,8 +755,8 @@ class behat_course extends behat_base {
*/
protected function get_activity_node($activityname) {
$activityname = str_replace("'", "\'", $activityname);
$xpath = "//li[contains(concat(' ', @class, ' '), ' activity ')][contains(., '" .$activityname. "')]";
$activityname = $this->getSession()->getSelectorsHandler()->xpathLiteral($activityname);
$xpath = "//li[contains(concat(' ', normalize-space(@class), ' '), ' activity ')][contains(., $activityname)]";
return $this->find('xpath', $xpath);
}
@@ -765,8 +769,8 @@ class behat_course extends behat_base {
protected function is_course_editor() {
// We don't need to behat_base::spin() here as all is already loaded.
if (!$this->getSession()->getPage()->findButton('Turn editing off') &&
!$this->getSession()->getPage()->findButton('Turn editing on')) {
if (!$this->getSession()->getPage()->findButton(get_string('turneditingoff')) &&
!$this->getSession()->getPage()->findButton(get_string('turneditingon'))) {
return false;
}
+15 -15
View File
@@ -28,11 +28,11 @@ Feature: Force group mode in a course
| Group mode | Separate groups |
| Force group mode | Yes |
When I press "Save changes"
Then ".//a//img[contains(./@alt, 'Separate groups (forced mode)')]" "xpath_element" should not exists
And ".//img[contains(./@alt, 'Separate groups (forced mode)')]" "xpath_element" should exists
And I click on "//img[contains(./@alt, 'Separate groups (forced mode)')]" "xpath_element" in the "li.activity.chat" "css_element"
And ".//a//img[contains(./@alt, 'Separate groups (forced mode)')]" "xpath_element" should not exists
And ".//img[contains(./@alt, 'Separate groups (forced mode)')]" "xpath_element" should exists
Then "//a/child::img[contains(@alt, 'Separate groups (forced mode)')]" "xpath_element" should not exists
And "//img[contains(@alt, 'Separate groups (forced mode)')]" "xpath_element" should exists
And I click on "//img[contains(@alt, 'Separate groups (forced mode)')]" "xpath_element" in the "li.activity.chat" "css_element"
And "//a/child::img[contains(@alt, 'Separate groups (forced mode)')]" "xpath_element" should not exists
And "//img[contains(@alt, 'Separate groups (forced mode)')]" "xpath_element" should exists
@javascript
Scenario: Forced group mode using visible groups
@@ -40,11 +40,11 @@ Feature: Force group mode in a course
| Group mode | Visible groups |
| Force group mode | Yes |
And I press "Save changes"
Then ".//a//img[contains(./@alt, 'Visible groups (forced mode)')]" "xpath_element" should not exists
And ".//img[contains(./@alt, 'Visible groups (forced mode)')]" "xpath_element" should exists
And I click on "//img[contains(./@alt, 'Visible groups (forced mode)')]" "xpath_element" in the "li.activity.chat" "css_element"
And ".//a//img[contains(./@alt, 'Visible groups (forced mode)')]" "xpath_element" should not exists
And ".//img[contains(./@alt, 'Visible groups (forced mode)')]" "xpath_element" should exists
Then "//a/child::img[contains(@alt, 'Visible groups (forced mode)')]" "xpath_element" should not exists
And "//img[contains(@alt, 'Visible groups (forced mode)')]" "xpath_element" should exists
And I click on "//img[contains(@alt, 'Visible groups (forced mode)')]" "xpath_element" in the "li.activity.chat" "css_element"
And "//a/child::img[contains(@alt, 'Visible groups (forced mode)')]" "xpath_element" should not exists
And "//img[contains(@alt, 'Visible groups (forced mode)')]" "xpath_element" should exists
@javascript
Scenario: Forced group mode without groups
@@ -52,9 +52,9 @@ Feature: Force group mode in a course
| Group mode | No groups |
| Force group mode | Yes |
And I press "Save changes"
Then ".//a//img[contains(./@alt, 'No groups (forced mode)')]" "xpath_element" should not exists
And ".//img[contains(./@alt, 'No groups (forced mode)')]" "xpath_element" should exists
And I click on "//img[contains(./@alt, 'No groups (forced mode)')]" "xpath_element" in the "li.activity.chat" "css_element"
And ".//a//img[contains(./@alt, 'No groups (forced mode)')]" "xpath_element" should not exists
And ".//img[contains(./@alt, 'No groups (forced mode)')]" "xpath_element" should exists
Then "//a/child::img[contains(@alt, 'No groups (forced mode)')]" "xpath_element" should not exists
And "//img[contains(@alt, 'No groups (forced mode)')]" "xpath_element" should exists
And I click on "//img[contains(@alt, 'No groups (forced mode)')]" "xpath_element" in the "li.activity.chat" "css_element"
And "//a/child::img[contains(@alt, 'No groups (forced mode)')]" "xpath_element" should not exists
And "//img[contains(@alt, 'No groups (forced mode)')]" "xpath_element" should exists
+4 -4
View File
@@ -50,11 +50,11 @@ class behat_enrol extends behat_base {
public function i_add_enrolment_method_with($enrolmethod, TableNode $table) {
return array(
new Given('I expand "Users" node'),
new Given('I follow "Enrolment methods"'),
new Given('I select "' . $enrolmethod . '" from "Add method"'),
new Given('I expand "' . get_string('users', 'admin') . '" node'),
new Given('I follow "' . get_string('type_enrol_plural', 'plugin') . '"'),
new Given('I select "' . $this->escape($enrolmethod) . '" from "' . get_string('addinstance', 'enrol') . '"'),
new Given('I fill the moodle form with:', $table),
new Given('I press "Add method"')
new Given('I press "' . get_string('addinstance', 'enrol') . '"')
);
}
+7 -4
View File
@@ -820,10 +820,9 @@ class gradingform_guide_instance extends gradingform_instance {
/**
* Calculates the grade to be pushed to the gradebook
*
* @return int the valid grade from $this->get_controller()->get_grade_range()
* @return float|int the valid grade from $this->get_controller()->get_grade_range()
*/
public function get_grade() {
global $DB, $USER;
$grade = $this->get_guide_filling();
if (!($scores = $this->get_controller()->get_min_max_score()) || $scores['maxscore'] <= $scores['minscore']) {
@@ -842,8 +841,12 @@ class gradingform_guide_instance extends gradingform_instance {
foreach ($grade['criteria'] as $record) {
$curscore += $record['score'];
}
return round(($curscore-$scores['minscore'])/($scores['maxscore']-$scores['minscore'])*
($maxgrade-$mingrade), 0) + $mingrade;
$gradeoffset = ($curscore-$scores['minscore'])/($scores['maxscore']-$scores['minscore'])*
($maxgrade-$mingrade);
if ($this->get_controller()->get_allow_grade_decimals()) {
return $gradeoffset + $mingrade;
}
return round($gradeoffset, 0) + $mingrade;
}
/**
+2 -2
View File
@@ -25,6 +25,6 @@
defined('MOODLE_INTERNAL') || die();
$plugin->component = 'gradingform_guide';
$plugin->version = 2013050100;
$plugin->requires = 2013050100;
$plugin->version = 2013051401.05;
$plugin->requires = 2013051401.05;
$plugin->maturity = MATURITY_STABLE;
+31 -5
View File
@@ -73,6 +73,9 @@ abstract class gradingform_controller {
/** @var array graderange array of valid grades for this area. Use set_grade_range and get_grade_range to access this */
private $graderange = null;
/** @var bool if decimal values are allowed as grades. */
private $allowgradedecimals = false;
/** @var boolean|null cached result of function has_active_instances() */
protected $hasactiveinstances = null;
@@ -622,13 +625,23 @@ abstract class gradingform_controller {
/**
* Sets the range of grades used in this area. This is usually either range like 0-100
* or the scale where keys start from 1. Typical use:
* $controller->set_grade_range(make_grades_menu($gradingtype));
* or the scale where keys start from 1.
*
* @param array $graderange
* Typically modules will call it:
* $controller->set_grade_range(make_grades_menu($gradingtype), $gradingtype > 0);
* Negative $gradingtype means that scale is used and the grade must be rounded
* to the nearest int. Positive $gradingtype means that range 0..$gradingtype
* is used for the grades and in this case grade does not have to be rounded.
*
* Sometimes modules always expect grade to be rounded (like mod_assignment does).
*
* @param array $graderange array where first _key_ is the minimum grade and the
* last key is the maximum grade.
* @param bool $allowgradedecimals if decimal values are allowed as grades.
*/
public final function set_grade_range(array $graderange) {
public final function set_grade_range(array $graderange, $allowgradedecimals = false) {
$this->graderange = $graderange;
$this->allowgradedecimals = $allowgradedecimals;
}
/**
@@ -643,6 +656,15 @@ abstract class gradingform_controller {
return $this->graderange;
}
/**
* Returns if decimal values are allowed as grades
*
* @return bool
*/
public final function get_allow_grade_decimals() {
return $this->allowgradedecimals;
}
/**
* Overridden by sub classes that wish to make definition details available to web services.
* When not overridden, only definition data common to all grading methods is made available.
@@ -866,7 +888,11 @@ abstract class gradingform_instance {
/**
* Calculates the grade to be pushed to the gradebook
*
* @return int the valid grade from $this->get_controller()->get_grade_range()
* Returned grade must be in range $this->get_controller()->get_grade_range()
* Plugins must returned grade converted to int unless
* $this->get_controller()->get_allow_grade_decimals() is true.
*
* @return float|int
*/
abstract public function get_grade();
+6 -3
View File
@@ -814,10 +814,9 @@ class gradingform_rubric_instance extends gradingform_instance {
/**
* Calculates the grade to be pushed to the gradebook
*
* @return int the valid grade from $this->get_controller()->get_grade_range()
* @return float|int the valid grade from $this->get_controller()->get_grade_range()
*/
public function get_grade() {
global $DB, $USER;
$grade = $this->get_rubric_filling();
if (!($scores = $this->get_controller()->get_min_max_score()) || $scores['maxscore'] <= $scores['minscore']) {
@@ -836,7 +835,11 @@ class gradingform_rubric_instance extends gradingform_instance {
foreach ($grade['criteria'] as $id => $record) {
$curscore += $this->get_controller()->get_definition()->rubric_criteria[$id]['levels'][$record['levelid']]['score'];
}
return round(($curscore-$scores['minscore'])/($scores['maxscore']-$scores['minscore'])*($maxgrade-$mingrade), 0) + $mingrade;
$gradeoffset = ($curscore-$scores['minscore'])/($scores['maxscore']-$scores['minscore'])*($maxgrade-$mingrade);
if ($this->get_controller()->get_allow_grade_decimals()) {
return $gradeoffset + $mingrade;
}
return round($gradeoffset, 0) + $mingrade;
}
/**
+2 -2
View File
@@ -80,7 +80,7 @@
.gradingform_rubric .plainvalue.empty {font-style: italic; color: #AAA;}
.gradingform_rubric.editor .criterion .levels .level .delete {position:absolute;right:0;bottom:0;}
.gradingform_rubric.editor .criterion .levels .level .delete {position:absolute;right:0;}
.gradingform_rubric .criterion .levels .level .score {font-style:italic;color:#575;font-weight: bold;margin-top:5px;white-space:nowrap;}
.gradingform_rubric .criterion .levels .level .score .scorevalue {padding-right:5px;}
@@ -106,7 +106,7 @@
.gradingform_rubric.editor .addcriterion input,
.gradingform_rubric.editor .addlevel input {background: transparent url([[pix:t/add]]) no-repeat top left ;display:block;color:#555555;font-weight:bold;text-decoration:none;}
.gradingform_rubric.editor .addcriterion input {background-position: 5px 8px;height:30px;line-height:29px;margin-bottom:14px;padding-left:20px;padding-right:10px;}
.gradingform_rubric.editor .addlevel input {background-position: 5px 5px;height:25px;line-height:24px;margin-bottom:10px;padding-left:18px;padding-right:8px;}
.gradingform_rubric.editor .addlevel input {background-position: 5px 5px;height:25px;line-height:24px;margin-bottom:45px;padding-left:18px;padding-right:8px;}
.gradingform_rubric .options .optionsheading {font-weight:bold;font-size:1.1em;padding-bottom:5px;}
.gradingform_rubric .options .option {padding-bottom:2px;}
+2 -2
View File
@@ -25,8 +25,8 @@
defined('MOODLE_INTERNAL') || die();
$plugin->component = 'gradingform_rubric';
$plugin->version = 2013050100;
$plugin->version = 2013051401.05;
$plugin->requires = 2013050100;
$plugin->requires = 2013051401.05;
$plugin->maturity = MATURITY_STABLE;
+9
View File
@@ -0,0 +1,9 @@
This files describes API changes in /grade/grading/form/* - Advanced grading methods
information provided here is intended especially for developers.
=== 2.5.2 ===
* Grading methods now can return grade with decimals. See API functions
gradingform_controller::set_grade_range() and
gradingform_controller::get_allow_grade_decimals(), and also examples
in gradingform_rubric_instance::get_grade().
+4
View File
@@ -226,6 +226,10 @@ class grade_report_grader extends grade_report {
$changedgrades = true;
} else if ($datatype === 'feedback') {
// If quick grading is on, feedback needs to be compared without line breaks.
if ($this->get_pref('quickgrading')) {
$oldvalue->feedback = preg_replace("/\r\n|\r|\n/", "", $oldvalue->feedback);
}
if (($oldvalue->feedback === $postedvalue) or ($oldvalue->feedback === NULL and empty($postedvalue))) {
continue;
}
+9 -6
View File
@@ -51,36 +51,39 @@ class behat_groups extends behat_base {
global $DB;
$user = $DB->get_record('user', array('username' => $username));
$userfullname = fullname($user);
$userfullname = $this->getSession()->getSelectorsHandler()->xpathLiteral(fullname($user));
// Using a xpath liternal to avoid problems with quotes and double quotes.
$groupname = $this->getSession()->getSelectorsHandler()->xpathLiteral($groupname);
// We don't know the option text as it contains the number of users in the group.
$select = $this->find_field('groups');
$xpath = "//select[@id='groups']/descendant::option[contains(., '" . $groupname . "')]";
$xpath = "//select[@id='groups']/descendant::option[contains(., $groupname)]";
$groupoption = $this->find('xpath', $xpath);
$fulloption = $groupoption->getText();
$select->selectOption($fulloption);
// Here we don't need to wait for the AJAX response.
$this->find_button('Add/remove users')->click();
$this->find_button(get_string('adduserstogroup', 'group'))->click();
// Wait for add/remove members page to be loaded.
$this->getSession()->wait(self::TIMEOUT, '(document.readyState === "complete")');
// Getting the option and selecting it.
$select = $this->find_field('addselect');
$xpath = "//select[@id='addselect']/descendant::option[contains(., '" . $userfullname . "')]";
$xpath = "//select[@id='addselect']/descendant::option[contains(., $userfullname)]";
$memberoption = $this->find('xpath', $xpath);
$fulloption = $memberoption->getText();
$select->selectOption($fulloption);
// Click add button.
$this->find_button('Add')->click();
$this->find_button(get_string('add'))->click();
// Wait for the page to load.
$this->getSession()->wait(self::TIMEOUT, '(document.readyState === "complete")');
// Returning to the main groups page.
$this->find_button('Back to groups')->click();
$this->find_button(get_string('backtogroups', 'group'))->click();
}
}
+1
View File
@@ -198,6 +198,7 @@ $string['donotaward'] = 'Currently, this badge is not active, so it cannot be aw
$string['editsettings'] = 'Edit settings';
$string['enablebadges'] = 'Enable badges';
$string['error:backpacknotavailable'] = 'Your site is not accessible from the Internet, so any badges issued from this site cannot be verified by external backpack services.';
$string['error:backpackproblem'] = 'There was a problem connecting to your backpack service provider. Please try again later.';
$string['error:cannotact'] = 'Cannot activate the badge. ';
$string['error:cannotawardbadge'] = 'Cannot award badge to a user.';
$string['error:clone'] = 'Cannot clone the badge.';
+16
View File
@@ -1348,3 +1348,19 @@ function badges_handle_course_deletion($courseid) {
$DB->update_record('badge', $toupdate);
}
}
/**
* Loads JS files required for backpack support.
*
* @uses $CFG, $PAGE
* @return void
*/
function badges_setup_backpack_js() {
global $CFG, $PAGE;
if (!empty($CFG->badges_allowexternalbackpack)) {
$PAGE->requires->string_for_js('error:backpackproblem', 'badges');
$protocol = (strpos($CFG->wwwroot, 'https://') === 0) ? 'https://' : 'http://';
$PAGE->requires->js(new moodle_url($protocol . 'backpack.openbadges.org/issuer.js'), true);
$PAGE->requires->js('/badges/backpack.js', true);
}
}
+25 -17
View File
@@ -64,10 +64,12 @@ class behat_files extends behat_base {
$exception = new ExpectationException('"' . $filepickerelement . '" filepicker can not be found', $this->getSession());
// Gets the ffilemanager node specified by the locator which contains the filepicker container.
$filepickerelement = $this->getSession()->getSelectorsHandler()->xpathLiteral($filepickerelement);
$filepickercontainer = $this->find(
'xpath',
"//input[./@id = //label[contains(normalize-space(string(.)), '" . $filepickerelement . "')]/@for]
//ancestor::div[contains(concat(' ', normalize-space(@class), ' '), ' ffilemanager ') or contains(concat(' ', normalize-space(@class), ' '), ' ffilepicker ')]",
"//input[./@id = //label[normalize-space(.)=$filepickerelement]/@for]" .
"//ancestor::div[contains(concat(' ', normalize-space(@class), ' '), ' ffilemanager ') or " .
"contains(concat(' ', normalize-space(@class), ' '), ' ffilepicker ')]",
$exception
);
@@ -117,17 +119,20 @@ class behat_files extends behat_base {
$exception = new ExpectationException($exceptionmsg, $this->getSession());
// Avoid quote-related problems.
$name = $this->getSession()->getSelectorsHandler()->xpathLiteral($name);
// Get a filepicker element (folder or file).
try {
// First we look at the folder as we need to click on the contextual menu otherwise it would be opened.
$node = $this->find(
'xpath',
"//div[@class='fp-content']
//descendant::*[self::div | self::a][contains(concat(' ', normalize-space(@class), ' '), ' fp-file ')]
[contains(concat(' ', normalize-space(@class), ' '), ' fp-folder ')][contains(normalize-space(string(.)), '" . $name . "')]
//descendant::a[contains(concat(' ', normalize-space(@class), ' '), ' fp-contextmenu ')]
",
"//div[@class='fp-content']" .
"//descendant::*[self::div | self::a][contains(concat(' ', normalize-space(@class), ' '), ' fp-file ')]" .
"[contains(concat(' ', normalize-space(@class), ' '), ' fp-folder ')]" .
"[normalize-space(.)=$name]" .
"//descendant::a[contains(concat(' ', normalize-space(@class), ' '), ' fp-contextmenu ')]",
$exception,
$containernode
);
@@ -137,10 +142,10 @@ class behat_files extends behat_base {
// Here the contextual menu is hidden, we click on the thumbnail.
$node = $this->find(
'xpath',
"//div[@class='fp-content']
//descendant::*[self::div | self::a][contains(concat(' ', normalize-space(@class), ' '), ' fp-file ')][contains(normalize-space(string(.)), '" . $name . "')]
//descendant::div[contains(concat(' ', normalize-space(@class), ' '), ' fp-thumbnail ')]
",
"//div[@class='fp-content']" .
"//descendant::*[self::div | self::a][contains(concat(' ', normalize-space(@class), ' '), ' fp-file ')]" .
"[normalize-space(.)=$name]" .
"//descendant::div[contains(concat(' ', normalize-space(@class), ' '), ' fp-thumbnail ')]",
false,
$containernode
);
@@ -176,12 +181,15 @@ class behat_files extends behat_base {
// Getting the repository link and opening it.
$repoexception = new ExpectationException('The "' . $repositoryname . '" repository has not been found', $this->getSession());
// Avoid problems with both double and single quotes in the same string.
$repositoryname = $this->getSession()->getSelectorsHandler()->xpathLiteral($repositoryname);
// Here we don't need to look inside the selected filepicker because there can only be one modal window.
$repositorylink = $this->find(
'xpath',
"//div[contains(concat(' ', normalize-space(@class), ' '), ' fp-repo-area ')]
//descendant::span[contains(concat(' ', normalize-space(@class), ' '), ' fp-repo-name ')]
[contains(normalize-space(string(.)), '" . $repositoryname . "')]",
"//div[contains(concat(' ', normalize-space(@class), ' '), ' fp-repo-area ')]" .
"//descendant::span[contains(concat(' ', normalize-space(@class), ' '), ' fp-repo-name ')]" .
"[normalize-space(.)=$repositoryname]",
$repoexception
);
@@ -226,10 +234,10 @@ class behat_files extends behat_base {
// only used when accessing the filepicker, there is no filemanager-loading after selecting the file.
$this->find(
'xpath',
"//div[contains(concat(' ', @class, ' '), ' filemanager ')]" .
"[not(contains(concat(' ', @class, ' '), ' fm-updating '))]" .
"//div[contains(concat(' ', normalize-space(@class), ' '), ' filemanager ')]" .
"[not(contains(concat(' ', normalize-space(@class), ' '), ' fm-updating '))]" .
"|" .
"//div[contains(concat(' ', @class, ' '), ' filemanager-loading ')]" .
"//div[contains(concat(' ', normalize-space(@class), ' '), ' filemanager-loading ')]" .
"[contains(@style, 'display: none;')]",
$exception,
$filepickernode
+4 -1
View File
@@ -58,10 +58,13 @@ class behat_form_select extends behat_form_field {
// Single select needs an extra click in the option.
if (!$this->field->hasAttribute('multiple')) {
$value = $this->session->getSelectorsHandler()->xpathLiteral($value);
// Using the driver direcly because Element methods are messy when dealing
// with elements inside containers.
$optionxpath = $this->field->getXpath() .
"/descendant::option[(./@value = '" . $value . "' or contains(normalize-space(string(.)), '" . $value . "'))]";
"/descendant::option[(./@value=$value or normalize-space(.)=$value)]";
$optionnodes = $this->session->getDriver()->find($optionxpath);
if ($optionnodes) {
current($optionnodes)->click();
+26
View File
@@ -141,3 +141,29 @@ function behat_error_handler($errno, $errstr, $errfile, $errline, $errcontext) {
// Also use the internal error handler so we keep the usual behaviour.
return false;
}
/**
* Restrict the config.php settings allowed.
*
* When running the behat features the config.php
* settings should not affect the results.
*
* @return void
*/
function behat_clean_init_config() {
global $CFG;
$allowed = array_flip(array(
'wwwroot', 'dataroot', 'dirroot', 'admin', 'directorypermissions', 'filepermissions',
'dbtype', 'dblibrary', 'dbhost', 'dbname', 'dbuser', 'dbpass', 'prefix', 'dboptions',
'proxyhost', 'proxyport', 'proxytype', 'proxyuser', 'proxypassword', 'proxybypass'
));
// Also allowing behat_ prefixed attributes.
foreach ($CFG as $key => $value) {
if (!isset($allowed[$key]) && strpos($key, 'behat_') !== 0) {
unset($CFG->{$key});
}
}
}
@@ -28,25 +28,18 @@ require('../../../../../config.php');
// General settings
$engine = get_config('tinymce_spellchecker', 'spellengine');
if (!$engine) {
$engine = 'GoogleSpell';
if (!$engine or $engine === 'GoogleSpell') {
$engine = 'PSpell';
}
$config['general.engine'] = $engine;
// GoogleSpell settings
$config['GoogleSpell.proxyhost'] = isset($CFG->proxyhost) ? $CFG->proxyhost : '';
$config['GoogleSpell.proxyport'] = isset($CFG->proxyport) ? $CFG->proxyport : '';
$config['GoogleSpell.proxytype'] = isset($CFG->proxytype) ? $CFG->proxytype : 'HTML';
$config['GoogleSpell.proxyuser'] = isset($CFG->proxyuser) ? $CFG->proxyuser : '';
$config['GoogleSpell.proxypassword'] = isset($CFG->proxypassword) ? $CFG->proxypassword : '';
if ($config['general.engine'] === 'PSpell' || $config['general.engine'] === 'PSpellShell') {
if ($config['general.engine'] === 'PSpell') {
// PSpell settings
$config['PSpell.mode'] = PSPELL_FAST;
$config['PSpell.spelling'] = "";
$config['PSpell.jargon'] = "";
$config['PSpell.encoding'] = "";
} else if ($config['general.engine'] === 'PSpellShell') {
// PSpellShell settings
$config['PSpellShell.mode'] = PSPELL_FAST;
$config['PSpellShell.aspell'] = $CFG->aspellpath;
@@ -36,6 +36,12 @@ class tinymce_spellchecker extends editor_tinymce_plugin {
return;
}
// Check some speller is configured.
$engine = $this->get_config('spellengine', '');
if (!$engine or $engine === 'GoogleSpell') {
return;
}
// Check at least one language is supported.
$spelllanguagelist = $this->get_config('spelllanguagelist', '');
if ($spelllanguagelist !== '') {
@@ -26,11 +26,11 @@ defined('MOODLE_INTERNAL') || die;
if ($ADMIN->fulltree) {
$options = array(
''=>get_string('none'),
'PSpell'=>'PSpell',
'GoogleSpell'=>'Google Spell',
'PSpellShell'=>'PSpellShell');
$settings->add(new admin_setting_configselect('tinymce_spellchecker/spellengine',
get_string('spellengine', 'admin'), '', 'GoogleSpell', $options));
get_string('spellengine', 'admin'), '', '', $options));
$settings->add(new admin_setting_configtextarea('tinymce_spellchecker/spelllanguagelist',
get_string('spelllanguagelist', 'admin'), '',
'+English=en,Danish=da,Dutch=nl,Finnish=fi,French=fr,German=de,Italian=it,Polish=pl,' .
+2 -1
View File
@@ -1266,7 +1266,8 @@ class global_navigation extends navigation_node {
// Remove any empty root nodes
foreach ($this->rootnodes as $node) {
// Dont remove the home node
if ($node->key !== 'home' && !$node->has_children()) {
/** @var navigation_node $node */
if ($node->key !== 'home' && !$node->has_children() && !$node->isactive) {
$node->remove();
}
}
+4
View File
@@ -131,6 +131,10 @@ if (!defined('BEHAT_SITE_RUNNING') && !empty($CFG->behat_dataroot) &&
// actions like reset the site or use data generators.
define('BEHAT_SITE_RUNNING', true);
// Clean extra config.php settings.
require_once(__DIR__ . '/../lib/behat/lib.php');
behat_clean_init_config();
$CFG->wwwroot = $CFG->behat_wwwroot;
$CFG->passwordsaltmain = 'moodle';
$CFG->prefix = $CFG->behat_prefix;
+1
View File
@@ -1686,6 +1686,7 @@ class table_xhtml_export_format extends table_default_export_format_parent {
<html xmlns="http://www.w3.org/1999/xhtml"
xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<style type="text/css">/*<![CDATA[*/
.flexible th {
+21 -21
View File
@@ -110,34 +110,31 @@ class behat_forms extends behat_base {
// behat_base::find() throws an exception if there are no elements, we should not fail a test because of this.
try {
// Expand fieldsets.
$fieldsets = $this->find_all('css', 'fieldset.collapsed a.fheader');
// We are supposed to have fieldsets here, otherwise exception.
// Funny thing about this, with find_all() we specify a pattern and each element matching the pattern is added to the array
// with of xpaths with a [0], [1]... sufix, but when we click on an element it does not matches the specified xpath
// anymore (is not collapsed) so [1] becomes [0], that's why we always click on the first XPath match, will be always the next one.
$iterations = count($fieldsets);
for ($i = 0; $i < $iterations; $i++) {
$fieldsets[0]->click();
}
// Expand fieldsets link.
$collapseexpandlink = $this->find('xpath', "//div[@class='collapsible-actions']" .
"/descendant::a[contains(concat(' ', @class, ' '), ' collapseexpand ')]" .
"[not(contains(concat(' ', @class, ' '), ' collapse-all '))]"
);
$collapseexpandlink->click();
} catch (ElementNotFoundException $e) {
// We continue if there are not expanded fields.
// We continue if there are not expandable fields.
}
// Different try & catch as we can have expanded fieldsets with advanced fields on them.
try {
// Show all fields.
$showmorestr = get_string('showmore', 'form');
$showmores = $this->find_all('xpath', "//a[contains(concat(' ', normalize-space(.), ' '), '" . $showmorestr . "')]" .
"[contains(concat(' ', normalize-space(@class), ' '), ' moreless-toggler')]");
// Expand all fields xpath.
$showmorexpath = "//a[normalize-space(.)='" . get_string('showmore', 'form') . "']" .
"[contains(concat(' ', normalize-space(@class), ' '), ' moreless-toggler')]";
// We are supposed to have 'show more's here, otherwise exception.
// We don't wait here as we already waited when getting the expand fieldsets links.
$showmores = $this->getSession()->getPage()->findAll('xpath', $showmorexpath);
// Same funny case, after clicking on the element the [1] showmore link becomes the [0].
// Funny thing about this, with findAll() we specify a pattern and each element matching the pattern is added to the array
// with of xpaths with a [0], [1]... sufix, but when we click on an element it does not matches the specified xpath
// anymore (now is a "Show less..." link) so [1] becomes [0], that's why we always click on the first XPath match,
// will be always the next one.
$iterations = count($showmores);
for ($i = 0; $i < $iterations; $i++) {
$showmores[0]->click();
@@ -186,9 +183,12 @@ class behat_forms extends behat_base {
return;
}
// Single select needs an extra click in the option.
if (!$selectnode->hasAttribute('multiple')) {
// Single select needs an extra click in the option.
$xpath = ".//option[(./@value = '" . $option . "' or contains(normalize-space(string(.)), '" . $option . "'))]";
// Avoid quotes problems.
$option = $this->getSession()->getSelectorsHandler()->xpathLiteral($option);
$xpath = "//option[(./@value=$option or normalize-space(.)=$option)]";
$optionnode = $this->find('xpath', $xpath, false, $selectnode);
$optionnode->click();
} else {
+4 -4
View File
@@ -242,8 +242,8 @@ class behat_general extends behat_base {
// The table row container.
$nocontainerexception = new ElementNotFoundException($this->getSession(), '"' . $tablerowtext . '" row text ');
$tablerowtext = str_replace("'", "\'", $tablerowtext);
$rownode = $this->find('xpath', "//tr[contains(., '" . $tablerowtext . "')]", $nocontainerexception);
$tablerowtext = $this->getSession()->getSelectorsHandler()->xpathLiteral($tablerowtext);
$rownode = $this->find('xpath', "//tr[contains(., $tablerowtext)]", $nocontainerexception);
// Looking for the element DOM node inside the specified row.
list($selector, $locator) = $this->transform_selector($selectortype, $element);
@@ -285,7 +285,7 @@ class behat_general extends behat_base {
public function assert_page_contains_text($text) {
$xpathliteral = $this->getSession()->getSelectorsHandler()->xpathLiteral($text);
$xpath = "/descendant::*[contains(., " . $xpathliteral. ")]";
$xpath = "/descendant::*[contains(., $xpathliteral)]";
// Wait until it finds the text, otherwise custom exception.
try {
@@ -305,7 +305,7 @@ class behat_general extends behat_base {
public function assert_page_not_contains_text($text) {
$xpathliteral = $this->getSession()->getSelectorsHandler()->xpathLiteral($text);
$xpath = "/descendant::*[not(contains(., " . $xpathliteral. "))]";
$xpath = "/descendant::*[not(contains(., $xpathliteral))]";
// Wait until it finds the text, otherwise custom exception.
try {
+23 -4
View File
@@ -244,7 +244,26 @@ class behat_hooks extends behat_base {
try {
// Exceptions.
if ($errormsg = $this->getSession()->getPage()->find('css', '.errorbox p.errormessage')) {
$exceptionsxpath = "//*[contains(concat(' ', normalize-space(@class), ' '), ' errorbox ')]" .
"/descendant::p[contains(concat(' ', normalize-space(@class), ' '), ' errormessage ')]";
// Debugging messages.
$debuggingxpath = "//*[contains(concat(' ', normalize-space(@class), ' '), ' debuggingmessage ')]";
// PHP debug messages.
$phperrorxpath = "//*[contains(concat(' ', normalize-space(@class), ' '), ' phpdebugmessage ')]";
// Any other backtrace.
$othersxpath = "(//*[contains(., ': call to ')])[1]";
$xpaths = array($exceptionsxpath, $debuggingxpath, $phperrorxpath, $othersxpath);
$joinedxpath = implode(' | ', $xpaths);
// Joined xpath expression. Most of the time there will be no exceptions, so this pre-check
// is faster than to send the 4 xpath queries for each step.
if (!$this->getSession()->getDriver()->find($joinedxpath)) {
return;
}
// Exceptions.
if ($errormsg = $this->getSession()->getPage()->find('xpath', $exceptionsxpath)) {
// Getting the debugging info and the backtrace.
$errorinfoboxes = $this->getSession()->getPage()->findAll('css', 'div.notifytiny');
@@ -256,7 +275,7 @@ class behat_hooks extends behat_base {
}
// Debugging messages.
if ($debuggingmessages = $this->getSession()->getPage()->findAll('css', '.debuggingmessage')) {
if ($debuggingmessages = $this->getSession()->getPage()->findAll('xpath', $debuggingxpath)) {
$msgs = array();
foreach ($debuggingmessages as $debuggingmessage) {
$msgs[] = $this->get_debug_text($debuggingmessage->getHtml());
@@ -266,7 +285,7 @@ class behat_hooks extends behat_base {
}
// PHP debug messages.
if ($phpmessages = $this->getSession()->getPage()->findAll('css', '.phpdebugmessage')) {
if ($phpmessages = $this->getSession()->getPage()->findAll('xpath', $phperrorxpath)) {
$msgs = array();
foreach ($phpmessages as $phpmessage) {
@@ -279,7 +298,7 @@ class behat_hooks extends behat_base {
// Any other backtrace.
// First looking through xpath as it is faster than get and parse the whole page contents,
// we get the contents and look for matches once we found something to suspect that there is a backtrace.
if ($this->getSession()->getDriver()->find("(//html/descendant::*[contains(., ': call to ')])[1]")) {
if ($this->getSession()->getDriver()->find($othersxpath)) {
$backtracespattern = '/(line [0-9]* of [^:]*: call to [\->&;:a-zA-Z_\x7f-\xff][\->&;:a-zA-Z0-9_\x7f-\xff]*)/';
if (preg_match_all($backtracespattern, $this->getSession()->getPage()->getContent(), $backtraces)) {
$msgs = array();
+17 -11
View File
@@ -56,16 +56,19 @@ class behat_navigation extends behat_base {
return false;
}
// Avoid problems with quotes.
$nodetextliteral = $this->getSession()->getSelectorsHandler()->xpathLiteral($nodetext);
$xpath = "//ul[contains(concat(' ', normalize-space(@class), ' '), ' block_tree ')]" .
"/child::li[contains(concat(' ', normalize-space(@class), ' '), ' collapsed ')]" .
"/child::p[contains(concat(' ', normalize-space(@class), ' '), ' branch')]" .
"/child::span[contains(concat(' ', normalize-space(.), ' '), '" . $nodetext . "')]" .
"/child::p[contains(concat(' ', normalize-space(@class), ' '), ' branch ')]" .
"/child::span[normalize-space(.)=$nodetextliteral]" .
"|" .
"//ul[contains(concat(' ', normalize-space(@class), ' '), ' block_tree ')]" .
"/descendant::li[not(contains(concat(' ', normalize-space(@class), ' '), ' collapsed'))]" .
"/descendant::li[contains(concat(' ', normalize-space(@class), ' '), ' collapsed')]" .
"/child::p[contains(concat(' ', normalize-space(@class), ' '), ' branch')]" .
"/child::span[contains(concat(' ', normalize-space(.), ' '), '" . $nodetext . "')]";
"/descendant::li[not(contains(concat(' ', normalize-space(@class), ' '), ' collapsed '))]" .
"/descendant::li[contains(concat(' ', normalize-space(@class), ' '), ' collapsed ')]" .
"/child::p[contains(concat(' ', normalize-space(@class), ' '), ' branch ')]" .
"/child::span[normalize-space(.)=$nodetextliteral]";
$exception = new ExpectationException('The "' . $nodetext . '" node can not be expanded', $this->getSession());
$node = $this->find('xpath', $xpath, $exception);
@@ -86,15 +89,18 @@ class behat_navigation extends behat_base {
return false;
}
// Avoid problems with quotes.
$nodetextliteral = $this->getSession()->getSelectorsHandler()->xpathLiteral($nodetext);
$xpath = "//ul[contains(concat(' ', normalize-space(@class), ' '), ' block_tree ')]" .
"/child::li[not(contains(concat(' ', normalize-space(@class), ' '), ' collapsed '))]" .
"/child::p[contains(concat(' ', normalize-space(@class), ' '), ' branch')]" .
"/child::span[contains(concat(' ', normalize-space(.), ' '), '" . $nodetext . "')]" .
"/child::p[contains(concat(' ', normalize-space(@class), ' '), ' branch ')]" .
"/child::span[normalize-space(.)=$nodetextliteral]" .
"|" .
"//ul[contains(concat(' ', normalize-space(@class), ' '), ' block_tree ')]" .
"/descendant::li[not(contains(concat(' ', normalize-space(@class), ' '), ' collapsed'))]" .
"/child::p[contains(concat(' ', normalize-space(@class), ' '), ' branch')]" .
"/child::span[contains(concat(' ', normalize-space(.), ' '), '" . $nodetext . "')]";
"/descendant::li[not(contains(concat(' ', normalize-space(@class), ' '), ' collapsed '))]" .
"/child::p[contains(concat(' ', normalize-space(@class), ' '), ' branch ')]" .
"/child::span[normalize-space(.)=$nodetextliteral]";
$exception = new ExpectationException('The "' . $nodetext . '" node can not be collapsed', $this->getSession());
$node = $this->find('xpath', $xpath, $exception);
+11 -11
View File
@@ -52,14 +52,14 @@ class behat_permissions extends behat_base {
return array(
new Given('I am on homepage'),
new Given('I collapse "Front page settings" node'),
new Given('I expand "Site administration" node'),
new Given('I expand "Users" node'),
new Given('I expand "Permissions" node'),
new Given('I follow "Define roles"'),
new Given('I collapse "' . get_string('frontpagesettings', 'admin') . '" node'),
new Given('I expand "' . get_string('administrationsite') . '" node'),
new Given('I expand "' . get_string('users', 'admin') . '" node'),
new Given('I expand "' . get_string('permissions', 'role') . '" node'),
new Given('I follow "' . get_string('defineroles', 'role') . '"'),
new Given('I follow "Edit ' . $this->escape($rolename) . ' role"'),
new Given('I fill the capabilities form with the following permissions:', $table),
new Given('I press "Save changes"')
new Given('I press "' . get_string('savechanges') . '"')
);
}
@@ -73,12 +73,12 @@ class behat_permissions extends behat_base {
public function i_override_the_system_permissions_of_role_with($rolename, $table) {
// We don't know the number of overrides so we have to get it to match the option contents.
$roleoption = $this->find('xpath', '//select[@name="roleid"]/option[contains(text(),"' . $this->escape($rolename) . '")]');
$roleoption = $this->find('xpath', '//select[@name="roleid"]/option[contains(.,"' . $this->escape($rolename) . '")]');
return array(
new Given('I select "' . $this->escape($roleoption->getText()) . '" from "Advanced role override"'),
new Given('I select "' . $this->escape($roleoption->getText()) . '" from "' . get_string('advancedoverride', 'role') . '"'),
new Given('I fill the capabilities form with the following permissions:', $table),
new Given('I press "Save changes"')
new Given('I press "' . get_string('savechanges') . '"')
);
}
@@ -93,9 +93,9 @@ class behat_permissions extends behat_base {
// Ensure we are using the advanced view.
// Wrapped in a try/catch to capture the exception and continue execution, we don't know if advanced mode was already enabled.
try {
$advancedtoggle = $this->find_button('Show advanced');
$advancedtoggle = $this->find_button(get_string('showadvanced', 'form'));
if ($advancedtoggle) {
$this->getSession()->getPage()->pressButton('Show advanced');
$this->getSession()->getPage()->pressButton(get_string('showadvanced', 'form'));
}
} catch (Exception $e) {
// We already are in advanced mode.
+30 -8
View File
@@ -263,7 +263,14 @@ function profiling_urls($report, $runid, $runid2 = null) {
return $url;
}
function profiling_print_run($run, $prevrunid = null) {
/**
* Generate the output to print a profiling run including further actions you can then take.
*
* @param object $run The profiling run object we are going to display.
* @param array $prevreferences A list of run objects to list as comparison targets.
* @return string The output to display on the screen for this run.
*/
function profiling_print_run($run, $prevreferences = null) {
global $CFG, $OUTPUT;
$output = '';
@@ -297,13 +304,28 @@ function profiling_print_run($run, $prevrunid = null) {
// Add link to details
$strviewdetails = get_string('viewdetails', 'tool_profiling');
$url = profiling_urls('run', $run->runid);
$output.=$OUTPUT->heading('<a href="' . $url . '" onclick="javascript:window.open(' . "'" . $url . "'" . ');' .
'return false;"' . ' title="">' . $strviewdetails . '</a>', 3, 'main profilinglink');
// If there is one previous run marked as reference, add link to diff
if ($prevrunid) {
$strviewdiff = get_string('viewdiff', 'tool_profiling');
$url = 'index.php?runid=' . $run->runid . '&amp;runid2=' . $prevrunid . '&amp;listurl=' . urlencode($run->url);
$output.=$OUTPUT->heading('<a href="' . $url . '" title="">' . $strviewdiff . '</a>', 3, 'main profilinglink');
$output .= $OUTPUT->heading('<a href="' . $url . '" onclick="javascript:window.open(' . "'" . $url . "'" . ');' .
'return false;"' . ' title="">' . $strviewdetails . '</a>', 3, 'main profilinglink');
// If there are previous run(s) marked as reference, add link to diff.
if ($prevreferences) {
$table = new html_table();
$table->align = array('left', 'left');
$table->head = array(get_string('date'), get_string('runid', 'tool_profiling'), get_string('comment', 'tool_profiling'));
$table->tablealign = 'center';
$table->attributes['class'] = 'flexible generaltable generalbox';
$table->colclasses = array('value', 'value', 'value');
$table->data = array();
$output .= $OUTPUT->heading(get_string('viewdiff', 'tool_profiling'), 3, 'main profilinglink');
foreach ($prevreferences as $reference) {
$url = 'index.php?runid=' . $run->runid . '&amp;runid2=' . $reference->runid . '&amp;listurl=' . urlencode($run->url);
$row = array(userdate($reference->timecreated), '<a href="' . $url . '" title="">'.$reference->runid.'</a>', $reference->runcomment);
$table->data[] = $row;
}
$output .= $OUTPUT->box(html_writer::table($table), 'profilingrunbox', 'profiling_diffs', true);
}
// Add link to export this run.
$strexport = get_string('exportthis', 'tool_profiling');
+5 -5
View File
@@ -63,14 +63,14 @@ class behat_message extends behat_base {
$steps[] = new Given('I am on homepage');
if ($this->running_javascript()) {
$steps[] = new Given('I expand "My profile" node');
$steps[] = new Given('I expand "' . get_string('myprofile') . '" node');
}
$steps[] = new Given('I follow "Messages"');
$steps[] = new Given('I fill in "' . get_string('searchcombined', 'message') . '" with "' . $tofullname . '"');
$steps[] = new Given('I follow "' . get_string('messages', 'message') . '"');
$steps[] = new Given('I fill in "' . get_string('searchcombined', 'message') . '" with "' . $this->escape($tofullname) . '"');
$steps[] = new Given('I press "' . get_string('searchcombined', 'message') . '"');
$steps[] = new Given('I follow "' . get_string('sendmessageto', 'message', $tofullname) . '"');
$steps[] = new Given('I fill in "id_message" with "' . $messagecontent . '"');
$steps[] = new Given('I follow "' . $this->escape(get_string('sendmessageto', 'message', $tofullname)) . '"');
$steps[] = new Given('I fill in "id_message" with "' . $this->escape($messagecontent) . '"');
$steps[] = new Given('I press "' . get_string('sendmessage', 'message') . '"');
return $steps;
+4 -3
View File
@@ -3394,7 +3394,7 @@ class assign {
// Only show the grade if it is not hidden in gradebook.
if (!empty($gradebookgrade->grade) && ($cangrade || !$gradebookgrade->hidden)) {
if ($controller = $gradingmanager->get_active_controller()) {
$controller->set_grade_range(make_grades_menu($this->get_instance()->grade));
$controller->set_grade_range(make_grades_menu($this->get_instance()->grade), $this->get_instance()->grade > 0);
$gradefordisplay = $controller->render_grade($PAGE,
$grade->id,
$gradingitem,
@@ -3491,7 +3491,7 @@ class assign {
// Now get the gradefordisplay.
if ($controller) {
$controller->set_grade_range(make_grades_menu($this->get_instance()->grade));
$controller->set_grade_range(make_grades_menu($this->get_instance()->grade), $this->get_instance()->grade > 0);
$grade->gradefordisplay = $controller->render_grade($PAGE,
$grade->id,
$gradingitem,
@@ -4880,6 +4880,7 @@ class assign {
global $CFG, $USER;
$grademenu = make_grades_menu($this->get_instance()->grade);
$allowgradedecimals = $this->get_instance()->grade > 0;
$advancedgradingwarning = false;
$gradingmanager = get_grading_manager($this->context, 'mod_assign', 'submissions');
@@ -4904,7 +4905,7 @@ class assign {
}
}
if ($gradinginstance) {
$gradinginstance->get_controller()->set_grade_range($grademenu);
$gradinginstance->get_controller()->set_grade_range($grademenu, $allowgradedecimals);
}
return $gradinginstance;
}
+4
View File
@@ -11,11 +11,15 @@
#input-message {background:#FFFFFF url(input.png) repeat-x scroll 0 0;padding:0 9px;border: 1px solid #999;border-radius: 9px;-moz-border-radius: 9px;}
.mdl-chat-entry,
.mdl-chat-my-entry {clear:both;}
.dir-rtl .mdl-chat-entry,
.dir-rtl .mdl-chat-my-entry {height: 90px;}
.chat-message .chat-message-meta {padding-top:15px;}
.chat-message .picture {vertical-align:middle;}
.chat-message .right {text-align:right;}
.chat-message .left {text-align:left;}
.dir-rtl .chat-message .right,
.dir-rtl .chat-message .left {direction:ltr;}
#chat-input-area,
#chat-input-area table.generaltable,
+1 -1
View File
@@ -52,7 +52,7 @@ class behat_mod_choice extends behat_base {
return array(
new Given('I follow "' . $this->escape($choiceactivity) . '"'),
new Given('I select "' . $this->escape($option) . '" radio button'),
new Given('I press "Save my choice"')
new Given('I press "' . get_string('savemychoice', 'choice') . '"')
);
}
+1 -1
View File
@@ -45,7 +45,7 @@ $string['page-mod-folder-view'] = 'Folder module main page';
$string['pluginadministration'] = 'Folder administration';
$string['pluginname'] = 'Folder';
$string['display'] = 'Display folder contents';
$string['display_help'] = 'If you choose to display the folder contents on a course page, there will be no link to a separate page and the title will not be displayed.
$string['display_help'] = 'If you choose to display the folder contents on a course page, there will be no link to a separate page.
The description will be displayed only if "Display description on course page" is checked.<br />
Also note that participants view actions can not be logged in this case.';
$string['displaypage'] = 'On a separate page';
+6 -1
View File
@@ -58,7 +58,12 @@ class mod_folder_renderer extends plugin_renderer_base {
}
}
$output .= $this->output->box($this->render(new folder_tree($folder, $cm)),
$foldertree = new folder_tree($folder, $cm);
if ($folder->display == FOLDER_DISPLAY_INLINE) {
// Display module name as the name of the root directory.
$foldertree->dir['dirname'] = $cm->get_formatted_name();
}
$output .= $this->output->box($this->render($foldertree),
'generalbox foldertree');
// Do not append the edit button on the course page.
+4 -4
View File
@@ -51,9 +51,9 @@ class behat_mod_forum extends behat_base {
// Escaping $forumname as it has been stripped automatically by the transformer.
return array(
new Given('I follow "' . $this->escape($forumname) . '"'),
new Given('I press "' . get_string('addanewdiscussion', 'mod_forum') . '"'),
new Given('I press "' . get_string('addanewdiscussion', 'forum') . '"'),
new Given('I fill the moodle form with:', $table),
new Given('I press "' . get_string('posttoforum', 'mod_forum') . '"'),
new Given('I press "' . get_string('posttoforum', 'forum') . '"'),
new Given('I wait to be redirected')
);
}
@@ -71,9 +71,9 @@ class behat_mod_forum extends behat_base {
return array(
new Given('I follow "' . $this->escape($forumname) . '"'),
new Given('I follow "' . $this->escape($postsubject) . '"'),
new Given('I follow "' . get_string('reply', 'mod_forum') . '"'),
new Given('I follow "' . get_string('reply', 'forum') . '"'),
new Given('I fill the moodle form with:', $table),
new Given('I press "' . get_string('posttoforum', 'mod_forum') . '"'),
new Given('I press "' . get_string('posttoforum', 'forum') . '"'),
new Given('I wait to be redirected')
);
@@ -38,7 +38,7 @@ Feature: Set a certain number of discussions as a completion condition for a for
And I log out
And I log in as "student1"
And I follow "Course 1"
Then I hover "//li[contains(concat(' ', @class, ' '), ' modtype_forum ')]/descendant::img[@alt='Not completed: Test forum name']" "xpath_element"
Then I hover "//li[contains(concat(' ', normalize-space(@class), ' '), ' modtype_forum ')]/descendant::img[@alt='Not completed: Test forum name']" "xpath_element"
And I add a new discussion to "Test forum name" forum with:
| Subject | Post 1 subject |
| Message | Body 1 content |
@@ -46,7 +46,7 @@ Feature: Set a certain number of discussions as a completion condition for a for
| Subject | Post 2 subject |
| Message | Body 2 content |
And I follow "Course 1"
And I hover "//li[contains(concat(' ', @class, ' '), ' modtype_forum ')]/descendant::img[contains(@alt, 'Completed: Test forum name')]" "xpath_element"
And I hover "//li[contains(concat(' ', normalize-space(@class), ' '), ' modtype_forum ')]/descendant::img[contains(@alt, 'Completed: Test forum name')]" "xpath_element"
And I log out
And I log in as "teacher1"
And I follow "Course 1"
@@ -50,7 +50,7 @@ class behat_mod_glossary extends behat_base {
return array(
new Given('I press "' . get_string('addentry', 'mod_glossary') . '"'),
new Given('I fill the moodle form with:', $data),
new Given('I press "Save changes"')
new Given('I press "' . get_string('savechanges') . '"')
);
}
@@ -65,8 +65,8 @@ class behat_mod_glossary extends behat_base {
return array(
new Given('I follow "' . get_string('categoryview', 'mod_glossary') . '"'),
new Given('I press "' . get_string('editcategories', 'mod_glossary') . '"'),
new Given('I press "Add Category"'),
new Given('I fill in "name" with "' . $categoryname . '"'),
new Given('I press "' . get_string('add').' '.get_string('category', 'glossary') . '"'),
new Given('I fill in "name" with "' . $this->escape($categoryname) . '"'),
new Given('I press "' . get_string('savechanges') . '"'),
new Given('I press "' . get_string('back', 'mod_glossary') . '"')
);
@@ -34,7 +34,7 @@ Feature: A teacher can choose whether to provide a printer-friendly glossary ent
| Concept | Just a test concept |
| Definition | Concept definition |
Then "Printer-friendly version" "link" should exists
And "//*[contains(concat(' ', @class, ' '), ' printicon ')]/descendant::a[contains(@href, 'print.php')]" "xpath_element" should exists
And "//span[contains(concat(' ', normalize-space(@class), ' '), ' printicon ')]/descendant::a[contains(@href, 'print.php')]" "xpath_element" should exists
And I follow "Printer-friendly version"
And I should see "Just a test concept"
@@ -51,4 +51,4 @@ Feature: A teacher can choose whether to provide a printer-friendly glossary ent
| Concept | Just a test concept |
| Definition | Concept definition |
Then "Printer-friendly version" "link" should not exists
And "//*[contains(concat(' ', @class, ' '), ' printicon ')]/descendant::a[contains(@href, 'print.php')]" "xpath_element" should not exists
And "//span[contains(concat(' ', normalize-space(@class), ' '), ' printicon ')]/descendant::a[contains(@href, 'print.php')]" "xpath_element" should not exists
@@ -40,7 +40,7 @@ Feature: In a lesson activity, students can navigate through a series of pages i
| id_jumpto_1 | Next page |
And I press "Save page"
And I follow "Expanded"
And I click on "Add a question page here" "link" in the "//div[contains(concat(' ', @class, ' '), ' addlinks ')][3]" "xpath_element"
And I click on "Add a question page here" "link" in the "//div[contains(concat(' ', normalize-space(@class), ' '), ' addlinks ')][3]" "xpath_element"
And I select "Numerical" from "Select a question type"
And I press "Add a question page"
And I fill the moodle form with:
@@ -66,7 +66,7 @@ class quiz_statistics_question_table extends flexible_table {
$this->define_baseurl($reporturl->out());
$this->collapsible(false);
$this->set_attribute('class', 'generaltable generalbox boxaligncenter');
$this->set_attribute('class', 'generaltable generalbox boxaligncenter quizresponseanalysis');
// Define the table columns.
$columns = array();
+8
View File
@@ -416,6 +416,14 @@ table.quizreviewsummary td.cell {
#page-mod-quiz-report fieldset.felement.fgroup {
margin: 0;
}
#page-mod-quiz-report table th {
white-space: normal;
}
#page-mod-quiz-report table#attempts td,
#page-mod-quiz-report table.quizresponseanalysis td {
word-wrap: break-word;
max-width: 20em;
}
#page-mod-quiz-report table.titlesleft td.c0 {
font-weight: bold;
}
+9 -3
View File
@@ -2169,11 +2169,17 @@ class page_wiki_confirmrestore extends page_wiki_save {
class page_wiki_prettyview extends page_wiki {
function print_header() {
global $CFG, $PAGE, $OUTPUT;
function __construct($wiki, $subwiki, $cm) {
global $PAGE;
$PAGE->set_pagelayout('embedded');
echo $OUTPUT->header();
parent::__construct($wiki, $subwiki, $cm);
}
function print_header() {
global $OUTPUT;
$this->set_url();
echo $OUTPUT->header();
echo '<h1 id="wiki_printable_title">' . format_string($this->title) . '</h1>';
}
+12 -6
View File
@@ -51,16 +51,18 @@ class behat_question extends behat_base {
*/
public function i_add_a_question_filling_the_form_with($questiontypename, TableNode $questiondata) {
$questiontypexpath = "//span[@class='qtypename'][.='" . $questiontypename . "']" .
// Using xpath literal to avoid quotes problems.
$questiontypename = $this->getSession()->getSelectorsHandler()->xpathLiteral($questiontypename);
$questiontypexpath = "//span[@class='qtypename'][normalize-space(.)=$questiontypename]" .
"/ancestor::div[@class='qtypeoption']/descendant::input";
return array(
new Given('I follow "' . get_string('questionbank', 'question') . '"'),
new Given('I press "' . get_string('createnewquestion', 'question') . '"'),
new Given('I click on "' . $questiontypexpath . '" "xpath_element"'),
new Given('I click on "' . $this->escape($questiontypexpath) . '" "xpath_element"'),
new Given('I click on "Next" "button" in the "#qtypechoicecontainer" "css_element"'),
new Given('I fill the moodle form with:', $questiondata),
new Given('I press "Save changes"')
new Given('I press "' . get_string('savechanges') . '"')
);
}
@@ -75,14 +77,18 @@ class behat_question extends behat_base {
*/
public function the_state_of_question_is_shown_as($questiondescription, $state) {
// Using xpath literal to avoid quotes problems.
$questiondescriptionliteral = $this->getSession()->getSelectorsHandler()->xpathLiteral($questiondescription);
$stateliteral = $this->getSession()->getSelectorsHandler()->xpathLiteral($state);
// Split in two checkings to give more feedback in case of exception.
$exception = new ElementNotFoundException($this->getSession(), 'Question "' . $questiondescription . '" ');
$questionxpath = "//div[contains(concat(' ', @class, ' '), ' qtext ')][contains(., '" . $questiondescription . "')]";
$questionxpath = "//div[contains(concat(' ', normalize-space(@class), ' '), ' qtext ')][contains(., $questiondescriptionliteral)]";
$this->find('xpath', $questionxpath, $exception);
$exception = new ExpectationException('Question "' . $questiondescription . '" state is not "' . $state . '"', $this->getSession());
$xpath = $questionxpath . "/ancestor::div[contains(concat(' ', @class, ' '), ' que ')]" .
"/descendant::div[@class='state'][contains(., '" . $state . "')]";
$xpath = $questionxpath . "/ancestor::div[contains(concat(' ', normalize-space(@class), ' '), ' que ')]" .
"/descendant::div[@class='state'][contains(., $stateliteral)]";
$this->find('xpath', $xpath, $exception);
}
+1
View File
@@ -94,6 +94,7 @@ if ($requestedqtype) {
JOIN {question_categories} qc ON q.category = qc.id
JOIN {context} con ON con.id = qc.contextid
$sqlqtypetest
AND (q.parent = 0 OR q.parent = q.id)
GROUP BY qc.contextid, $ctxgroupby
ORDER BY numquestions DESC, numhidden ASC, con.contextlevel ASC, con.id ASC", $params);
@@ -56,7 +56,7 @@ class behat_repository_recent extends behat_files {
// Opening the specified file contextual menu from the modal window.
$this->open_element_contextual_menu($filename);
$this->find_button('Select this file')->click();
$this->find_button(get_string('getfile', 'repository'))->click();
// Ensure the file has been selected and we returned to the form page.
$this->wait_until_return_to_form();
+6 -4
View File
@@ -63,7 +63,7 @@ class behat_filepicker extends behat_files {
$dialoginput = $this->find('css', '.fp-mkdir-dlg-text input');
$dialoginput->setValue($foldername);
$this->getSession()->getPage()->pressButton('Create folder');
$this->getSession()->getPage()->pressButton(get_string('makeafolder'));
// Wait until the process finished and modal windows are hidden.
$this->wait_until_return_to_form();
@@ -92,6 +92,8 @@ class behat_filepicker extends behat_files {
// Just in case there is any contents refresh in progress.
$this->wait_until_contents_are_updated($fieldnode);
$folderliteral = $this->getSession()->getSelectorsHandler()->xpathLiteral($foldername);
// We look both in the pathbar and in the contents.
try {
@@ -99,8 +101,8 @@ class behat_filepicker extends behat_files {
$folder = $this->find(
'xpath',
"//div[contains(concat(' ', normalize-space(@class), ' '), ' fp-folder ')]" .
"/descendant::div[contains(concat(' ', @class, ' '), ' fp-filename ')]" .
"[normalize-space(.)='" . $foldername . "']",
"/descendant::div[contains(concat(' ', normalize-space(@class), ' '), ' fp-filename ')]" .
"[normalize-space(.)=$folderliteral]",
$exception,
$fieldnode
);
@@ -110,7 +112,7 @@ class behat_filepicker extends behat_files {
$folder = $this->find(
'xpath',
"//a[contains(concat(' ', normalize-space(@class), ' '), ' fp-path-folder-name ')]" .
"[normalize-space(.)='" . $foldername . "']",
"[normalize-space(.)=$folderliteral]",
$exception,
$fieldnode
);
@@ -24,7 +24,7 @@ Feature: A selected file can be cancelled
And I upload "lib/tests/fixtures/upload_users.csv" file to "Files" filepicker
And I click on "#fitem_id_files .fp-btn-add a" "css_element"
And I click on "Recent files" "link" in the ".fp-repo-area" "css_element"
And I click on "//a[contains(concat(' ', @class, ' '), ' fp-file ')][contains(., 'empty.txt')]" "xpath_element"
And I click on "//a[contains(concat(' ', normalize-space(@class), ' '), ' fp-file ')][normalize-space(.)='empty.txt']" "xpath_element"
And I click on ".yui3-panel-focused .fp-select .fp-select-cancel" "css_element"
And I click on ".yui3-panel-focused .file-picker button.yui3-button-close" "css_element"
And I press "Save and display"
@@ -65,10 +65,10 @@ class behat_repository_upload extends behat_files {
$noformexception = new ExpectationException('The upload file form is not ready', $this->getSession());
$this->find(
'xpath',
"//div[contains(concat(' ', @class, ' '), ' file-picker ')]" .
"[contains(concat(' ', @class, ' '), ' repository_upload ')]" .
"//div[contains(concat(' ', normalize-space(@class), ' '), ' file-picker ')]" .
"[contains(concat(' ', normalize-space(@class), ' '), ' repository_upload ')]" .
"/descendant::div[@class='fp-content']" .
"/descendant::div[contains(concat(' ', @class, ' '), ' fp-upload-form ')]" .
"/descendant::div[contains(concat(' ', normalize-space(@class), ' '), ' fp-upload-form ')]" .
"/descendant::form",
$noformexception
);
+4 -1
View File
@@ -418,7 +418,10 @@ img.iconsmall {
margin-right: 0.5em;
display: inline;
}
.dir-rtl .form-item .form-setting .defaultsnext {
margin-left: 0.5em;
margin-right: 0;
}
.form-item .form-setting .locked-checkbox {
margin-right: 0.2em;
margin-left: 0.5em;
+2 -2
View File
@@ -1330,7 +1330,7 @@ audio.mediaplugin_html5audio {
background-position: 0 -8866px;
}
.dir-rtl.yui-skin-sam .yui-panel .hd {
text-align: left;
text-align: right;
}
.dir-rtl .yui-skin-sam .yui-layout .yui-layout-unit div.yui-layout-bd {
text-align: right;
@@ -1905,4 +1905,4 @@ div.badge .expireimage {
}
.breadcrumb-nav .breadcrumb {
margin: 0;
}
}
@@ -12,6 +12,32 @@ table.flexible,
.generaltable {
.table-striped;
}
.dir-rtl {
table#explaincaps,
table#defineroletable,
table.grading-report,
table#listdirectories,
table.rolecaps,
table.userenrolment,
table#form,
form#movecourses table,
.forumheaderlist,
table.flexible,
.generaltable,
.generaltable thead:first-child tr:first-child {
td , th {
text-align:right;
}
}
}
#page-admin-course-index.dir-rtl .editcourse {
td , th {
text-align:right;
}
}
#page-report-loglive-index .generaltable,
#page-admin-report-log-index .generaltable,
#page-report-log-user .generaltable,
@@ -432,6 +432,7 @@
width: 17px;
height: 22px;
}
.dir-rtl .filemanager .ygtvtn,
.dir-rtl .file-picker .ygtvtn {
background: url('[[pix:moodle|y/tn_rtl]]') 0 0 no-repeat;
width: 17px;
@@ -150,6 +150,12 @@ input#id_externalurl {
.mform .fitem div.fitemtitle {
.form-horizontal .control-label
}
.dir-rtl {
.form-item .form-label,
.mform .fitem div.fitemtitle {
float:right;
}
}
.form-defaultinfo,
.form-label .form-shortname {
.muted;
@@ -341,6 +347,9 @@ input[type="checkbox"] {
.form-item .form-label label {
margin-bottom: 0;
}
.dir-rtl .form-item .form-label label {
text-align:left;
}
.felement.ffilepicker {
margin-top: 5px;
}
+15 -1
View File
@@ -548,7 +548,12 @@ table#user-grades tr.controls td,
.path-grade-report-grader table td.userfield {
text-align:left;
}
.path-grade-report-grader.dir-rtl {
table th.user,
table td.userfield {
text-align:right;
}
}
.path-grade-report-grader .usersuspended a:link,
.path-grade-report-grader .usersuspended a:visited {
.muted
@@ -592,3 +597,12 @@ table#user-grades tr.controls td,
.path-grade-report-grader #hiddentooltiproot, .tooltipDiv {
display: none;
}
.gradingform_rubric.editor .criteria .definition textarea,
.gradingform_rubric.editor .criteria .scorevalue input {
width: auto;
}
.gradingform_rubric.editor .criteria .scorevalue input {
float: left;
}
@@ -26,6 +26,10 @@
#chooseqtype .qtypename img {
padding: 0 0.3em;
}
#chooseqtype .qtypename {
display: inline-table;
width: 16em;
}
#chooseqtype .qtypesummary {
display: block;
margin: 0 2em;
@@ -60,7 +64,7 @@
}
//#qtypechoicecontainer
#chooseqtype {
width: 35em;
width: 40em;
}
#chooseqtypehead h3 {
margin: 0;
@@ -95,6 +99,7 @@
margin: 0;
padding: 1.5em 1.6em;
background-color: #fff;
overflow-y: auto;
}
#chooseqtype .instruction,
#chooseqtype .selected .qtypesummary {
@@ -33,6 +33,19 @@
.form-horizontal .controls {
margin-left: @horizontalComponentOffset980;
}
.dir-rtl {
.form-item .form-setting,
.form-item .form-description,
.mform .fitem .felement,
.mform .fdescription.required,
.userprofile dl.list dd,
.form-horizontal .controls {
margin-right: @horizontalComponentOffset980;
}
}
#page-mod-forum-search.dir-lrt .c1 {
margin-right: @horizontalComponentOffset980;
}
.path-admin .buttons,
.form-buttons {
padding-left: @horizontalComponentOffset980;
@@ -66,10 +79,29 @@
.form-horizontal .controls {
margin-left: @horizontalComponentOffset1200;
}
.dir-rtl {
.form-item .form-setting,
.form-item .form-description,
.mform .fitem .felement,
.mform .fdescription.required,
.userprofile dl.list dd,
.form-horizontal .controls {
margin-right: @horizontalComponentOffset1200;
}
#page-mod-forum-search .c1 {
margin-right: @horizontalComponentOffset1200;
}
}
.path-admin .buttons,
.form-buttons {
padding-left: @horizontalComponentOffset1200;
}
.dir-rtl {
.path-admin .buttons,
.form-buttons {
padding-right: @horizontalComponentOffset1200;
}
}
}
File diff suppressed because one or more lines are too long
+13 -9
View File
@@ -97,11 +97,11 @@ if (!empty($CFG->enabledevicedetection) && empty($device)) {
$table->head = array(get_string('devicetype', 'admin'), get_string('currenttheme', 'admin'), get_string('info'));
$devices = get_device_type_list();
foreach ($devices as $device) {
foreach ($devices as $thedevice) {
$headingthemename = ''; // To output the picked theme name when needed
$themename = get_selected_theme_for_device_type($device);
if (!$themename && $device == 'default') {
$themename = get_selected_theme_for_device_type($thedevice);
if (!$themename && $thedevice == 'default') {
$themename = theme_config::DEFAULT_THEME;
}
@@ -112,7 +112,7 @@ if (!empty($CFG->enabledevicedetection) && empty($device)) {
$themename = clean_param($themename, PARAM_THEME);
if (empty($themename)) {
// Likely the theme has been deleted
unset_config(get_device_cfg_var_name($device));
unset_config(get_device_cfg_var_name($thedevice));
} else {
$strthemename = get_string('pluginname', 'theme_'.$themename);
// link to the screenshot, now mandatory - the image path is hardcoded because we need image from other themes, not the current one
@@ -123,19 +123,19 @@ if (!empty($CFG->enabledevicedetection) && empty($device)) {
$headingthemename = $OUTPUT->heading($strthemename, 3);
}
// If not default device then show option to unset theme.
if ($device != 'default') {
if ($thedevice != 'default') {
$unsetthemestr = get_string('unsettheme', 'admin');
$unsetthemeurl = new moodle_url('/theme/index.php', array('device' => $device, 'sesskey' => sesskey(), 'unsettheme' => true));
$unsetthemeurl = new moodle_url('/theme/index.php', array('device' => $thedevice, 'sesskey' => sesskey(), 'unsettheme' => true));
$unsetthemebutton = new single_button($unsetthemeurl, $unsetthemestr, 'get');
$unsetthemebutton = $OUTPUT->render($unsetthemebutton);
}
}
$deviceurl = new moodle_url('/theme/index.php', array('device' => $device, 'sesskey' => sesskey()));
$deviceurl = new moodle_url('/theme/index.php', array('device' => $thedevice, 'sesskey' => sesskey()));
$select = new single_button($deviceurl, $strthemeselect, 'get');
$table->data[] = array(
$OUTPUT->heading(ucfirst($device), 3),
$OUTPUT->heading(ucfirst($thedevice), 3),
$screenshotcell,
$headingthemename . $OUTPUT->render($select) . $unsetthemebutton
);
@@ -217,7 +217,11 @@ if (!empty($CFG->enabledevicedetection) && empty($device)) {
echo $OUTPUT->header('themeselector');
echo $OUTPUT->heading($heading);
echo $OUTPUT->single_button(new moodle_url('index.php', array('sesskey' => sesskey(), 'reset' => 1, 'device' => $device)), get_string('themeresetcaches', 'admin'));
$params = array('sesskey' => sesskey(), 'reset' => 1);
if (!empty($device)) {
$params['device'] = $device;
}
echo $OUTPUT->single_button(new moodle_url('index.php', $params), get_string('themeresetcaches', 'admin'));
echo html_writer::table($table);
+2 -2
View File
@@ -29,11 +29,11 @@
defined('MOODLE_INTERNAL') || die();
$version = 2013051401.04; // 20130514 = branching date YYYYMMDD - do not modify!
$version = 2013051401.06; // 20130514 = branching date YYYYMMDD - do not modify!
// RR = release increments - 00 in DEV branches
// .XX = incremental changes
$release = '2.5.1+ (Build: 20130719)'; // Human-friendly version name
$release = '2.5.1+ (Build: 20130726)'; // Human-friendly version name
$branch = '25'; // this version's branch
$maturity = MATURITY_STABLE; // this version's maturity level