Compare commits
112 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4cef723c22 | |||
| 561413695d | |||
| 217c3e0fc9 | |||
| 836c6d7883 | |||
| 728754de6b | |||
| 40a44cdd52 | |||
| 52617af3c9 | |||
| 180fdb2f7c | |||
| c47b6f97d7 | |||
| 80ad34e720 | |||
| f497fdcc21 | |||
| 0b4b0a7eea | |||
| e8b75e6292 | |||
| da6ecfd8c1 | |||
| 2cc040c6c0 | |||
| e88100f673 | |||
| 373748277a | |||
| 5294052875 | |||
| 28aadc2b12 | |||
| 2088c22e41 | |||
| 57ee89569a | |||
| deec056650 | |||
| 693ed49c10 | |||
| df8095156e | |||
| bb0ac1fdc2 | |||
| 117dc470bc | |||
| 9dfe870449 | |||
| be9b036a33 | |||
| e0b2ad1f35 | |||
| bbbdfc8610 | |||
| 821ab27c19 | |||
| 999aa8ca45 | |||
| d708bf59e4 | |||
| 4b274a10c2 | |||
| ee0546291a | |||
| 42b3a58c47 | |||
| 22d0a8f11e | |||
| 63a03a8b05 | |||
| 1a9d1593e4 | |||
| ca9daf0fb0 | |||
| 02315eb2bb | |||
| 1f0f002e1d | |||
| 7774cf96f8 | |||
| aa4a2716ef | |||
| 3adcb91d55 | |||
| ab0ad70a83 | |||
| 81e3251512 | |||
| ecbc2a2f25 | |||
| b5df2c1db0 | |||
| 37d0021369 | |||
| a72f3add06 | |||
| f8f477ece5 | |||
| 68881fb620 | |||
| 1c53bc813e | |||
| de342562cc | |||
| bb63c18443 | |||
| b2aaab60f3 | |||
| 413c321a18 | |||
| 2d391d1b10 | |||
| 9f555a7e77 | |||
| 494fec0071 | |||
| 527bde6e0f | |||
| d412f8552d | |||
| 6fb6859945 | |||
| af39ce0f8d | |||
| 7dea2c1707 | |||
| f45937e17d | |||
| e360ff5db6 | |||
| 61257ae417 | |||
| 101df9a634 | |||
| 68b35af353 | |||
| 7c549a17f4 | |||
| 39f360ae38 | |||
| 346ba4f443 | |||
| 10a8d040dc | |||
| 161ffc5605 | |||
| 53759a0c91 | |||
| f5e587c1db | |||
| 4727a4a207 | |||
| d891a6dcb7 | |||
| 70ae75674d | |||
| 42406bc413 | |||
| d3cd5f1f8f | |||
| 3ec49c6de3 | |||
| ff1543e0df | |||
| eb154d9c14 | |||
| f7d8c61ae7 | |||
| 0bc41cfcec | |||
| d304952b43 | |||
| adebc069bd | |||
| abfaf50217 | |||
| 81c471e2a9 | |||
| bdd60287e5 | |||
| 3365244fda | |||
| 37e51a5daa | |||
| 32f3de567f | |||
| fd424b99f2 | |||
| 5459e754c6 | |||
| abc2491211 | |||
| 3953d0b7a7 | |||
| 0fc2bccd60 | |||
| 3363cbe55b | |||
| 8b3090c13b | |||
| 3dbdeb5ccb | |||
| b395f7b599 | |||
| 1bc3f19a5b | |||
| 626d833543 | |||
| b71890687a | |||
| ca50279d4e | |||
| dbcade703b | |||
| cef93f97ae | |||
| 1dc462baa5 |
+2
-12
@@ -165,20 +165,10 @@ script:
|
||||
# The local_ci repository does the actual checking.
|
||||
git clone https://github.com/moodlehq/moodle-local_ci.git local/ci
|
||||
|
||||
# Determine the branch
|
||||
grep '^$release.*dev' version.php > /dev/null
|
||||
if [ $? -eq 0 ];
|
||||
then
|
||||
# This is master
|
||||
export branchname='master';
|
||||
else
|
||||
# This is a stable branch. Use the version from version.php to determine which one.
|
||||
export branchname="MOODLE_`grep '^$branch' version.php | sed "s/^.*'\([0-9]*\)'.*$/\1/"`_STABLE";
|
||||
fi
|
||||
|
||||
# We need the official upstream for comparison
|
||||
git remote add upstream https://github.com/moodle/moodle.git;
|
||||
git fetch upstream "$branchname";
|
||||
|
||||
git fetch upstream master;
|
||||
export GIT_PREVIOUS_COMMIT="`git merge-base FETCH_HEAD $TRAVIS_COMMIT`";
|
||||
export GIT_COMMIT="$TRAVIS_COMMIT";
|
||||
export UPSTREAM_FETCH_HEAD=`git rev-parse FETCH_HEAD`
|
||||
|
||||
@@ -35,7 +35,8 @@ if ($hassiteconfig) { // speedup for non-admins, add all caps used on this page
|
||||
$params = new stdClass();
|
||||
$params->bytes = $defaultuserquota;
|
||||
$params->displaysize = display_size($defaultuserquota);
|
||||
$temp->add(new admin_setting_configtext('userquota', new lang_string('userquota', 'admin'), new lang_string('configuserquota', 'admin', $params), $defaultuserquota));
|
||||
$temp->add(new admin_setting_configtext('userquota', new lang_string('userquota', 'admin'),
|
||||
new lang_string('configuserquota', 'admin', $params), $defaultuserquota, PARAM_INT, 30));
|
||||
|
||||
$temp->add(new admin_setting_configcheckbox('allowobjectembed', new lang_string('allowobjectembed', 'admin'), new lang_string('configallowobjectembed', 'admin'), 0));
|
||||
$temp->add(new admin_setting_configcheckbox('enabletrusttext', new lang_string('enabletrusttext', 'admin'), new lang_string('configenabletrusttext', 'admin'), 0));
|
||||
|
||||
@@ -253,7 +253,8 @@ class tool_generator_course_backend extends tool_generator_backend {
|
||||
$courserecord = array(
|
||||
'shortname' => $this->shortname,
|
||||
'fullname' => $this->fullname,
|
||||
'numsections' => self::$paramsections[$this->size]
|
||||
'numsections' => self::$paramsections[$this->size],
|
||||
'startdate' => usergetmidnight(time())
|
||||
);
|
||||
if (strlen($this->summary) > 0) {
|
||||
$courserecord['summary'] = $this->summary;
|
||||
|
||||
@@ -37,7 +37,7 @@ $string['installfromzip'] = 'Install plugin from ZIP file';
|
||||
$string['installfromzip_help'] = 'An alternative to installing a plugin directly from the Moodle plugins directory is to upload a ZIP package of the plugin. The ZIP package should have the same structure as a package downloaded from the Moodle plugins directory.';
|
||||
$string['installfromzipfile'] = 'ZIP package';
|
||||
$string['installfromzipfile_help'] = 'The plugin ZIP package must contain just one directory, named to match the plugin name. The ZIP will be extracted into an appropriate location for the plugin type. If the package has been downloaded from the Moodle plugins directory then it will have this structure.';
|
||||
$string['installfromzipinvalid'] = 'The plugin ZIP package must contain just one directory, named to match the plugin name. Provided file is not a valid plugin ZIP package.';
|
||||
$string['installfromzipinvalid'] = 'The plugin ZIP package must contain just one directory, named to match the plugin name. The file provided is not a valid plugin ZIP package.';
|
||||
$string['installfromziprootdir'] = 'Rename the root directory';
|
||||
$string['installfromziprootdir_help'] = 'Some ZIP packages, such as those generated by Github, may contain an incorrect root directory name. If so, the correct name may be entered here.';
|
||||
$string['installfromzipsubmit'] = 'Install plugin from the ZIP file';
|
||||
|
||||
@@ -39,12 +39,12 @@ $string['langpackupdatedevent'] = 'Language pack updated';
|
||||
$string['langupdatecomplete'] = 'Language pack update completed';
|
||||
$string['missingcfglangotherroot'] = 'Missing configuration value $CFG->langotherroot';
|
||||
$string['missinglangparent'] = 'Missing parent language <em>{$a->parent}</em> of <em>{$a->lang}</em>.';
|
||||
$string['noenglishuninstall'] = 'English language pack can not be uninstalled';
|
||||
$string['noenglishuninstall'] = 'The English language pack cannot be uninstalled.';
|
||||
$string['nolangupdateneeded'] = 'All your language packs are up to date, no update is needed';
|
||||
$string['pluginname'] = 'Language packs';
|
||||
$string['purgestringcaches'] = 'Purge string caches';
|
||||
$string['remotelangnotavailable'] = 'Because Moodle cannot connect to download.moodle.org, it is not possible for language packs to be installed automatically. Please download the appropriate ZIP file(s) from <a href="https://download.moodle.org/langpack/">download.moodle.org/langpack</a>, copy them to your {$a} directory and unzip them manually.';
|
||||
$string['selectlangs'] = 'Select languages to unistall!';
|
||||
$string['selectlangs'] = 'Select languages to uninstall';
|
||||
$string['uninstall'] = 'Uninstall selected language pack(s)';
|
||||
$string['uninstallconfirm'] = 'You are about to completely uninstall these language packs: <strong>{$a}</strong>. Are you sure?';
|
||||
$string['updatelangs'] = 'Update all installed language packs';
|
||||
|
||||
@@ -53,7 +53,7 @@ Feature: Manage language packs
|
||||
And I navigate to "Language packs" node in "Site administration > Language"
|
||||
When I set the field "Installed language packs" to "English (en)"
|
||||
And I press "Uninstall selected language pack(s)"
|
||||
Then I should see "English language pack can not be uninstalled"
|
||||
Then I should see "The English language pack cannot be uninstalled."
|
||||
And I navigate to "Live logs" node in "Site administration > Reports"
|
||||
And I should not see "Language pack uninstalled"
|
||||
And I log out
|
||||
|
||||
@@ -102,6 +102,10 @@ class manager {
|
||||
try {
|
||||
$this->client->login();
|
||||
mtrace("Connection established.");
|
||||
|
||||
// Ensure that mailboxes exist.
|
||||
$this->ensure_mailboxes_exist();
|
||||
|
||||
return true;
|
||||
|
||||
} catch (\Horde_Imap_Client_Exception $e) {
|
||||
@@ -741,6 +745,27 @@ class manager {
|
||||
return in_array($flag, $flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that all mailboxes exist.
|
||||
*/
|
||||
private function ensure_mailboxes_exist() {
|
||||
$requiredmailboxes = array(
|
||||
self::MAILBOX,
|
||||
self::CONFIRMATIONFOLDER,
|
||||
);
|
||||
|
||||
$existingmailboxes = $this->client->listMailboxes($requiredmailboxes);
|
||||
foreach ($requiredmailboxes as $mailbox) {
|
||||
if (isset($existingmailboxes[$mailbox])) {
|
||||
// This mailbox was found.
|
||||
continue;
|
||||
}
|
||||
|
||||
mtrace("Unable to find the '{$mailbox}' mailbox - creating it.");
|
||||
$this->client->createMailbox($mailbox);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to determine whether this message is a bulk message (e.g. automated reply).
|
||||
*
|
||||
|
||||
@@ -20,10 +20,6 @@
|
||||
* This script is meant to be called from a cronjob to sync moodle with the CAS
|
||||
* backend in those setups where the CAS backend acts as 'master'.
|
||||
*
|
||||
* Sample cron entry:
|
||||
* # 5 minutes past 4am
|
||||
* 5 4 * * * $sudo -u www-data /usr/bin/php /var/www/moodle/auth/cas/cli/sync_users.php
|
||||
*
|
||||
* Notes:
|
||||
* - it is required to use the web server account when executing PHP CLI scripts
|
||||
* - you need to change the "www-data" to match the apache user account
|
||||
@@ -40,12 +36,15 @@
|
||||
* @package auth_cas
|
||||
* @copyright 2007 Jerome Gutierrez - based on code by Martin Langhoff
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @deprecated since Moodle 3.0 MDL-51824 - please do not use this CLI script any more, use scheduled task instead.
|
||||
* @todo MDL-50264 This will be deleted in Moodle 3.2.
|
||||
*/
|
||||
|
||||
define('CLI_SCRIPT', true);
|
||||
|
||||
require(dirname(dirname(dirname(dirname(__FILE__)))).'/config.php');
|
||||
require_once($CFG->dirroot.'/course/lib.php');
|
||||
require_once($CFG->libdir.'/clilib.php');
|
||||
|
||||
// Ensure errors are well explained
|
||||
set_debugging(DEBUG_DEVELOPER, true);
|
||||
@@ -55,6 +54,14 @@ if (!is_enabled_auth('cas')) {
|
||||
die;
|
||||
}
|
||||
|
||||
cli_problem('[AUTH CAS] The sync users cron has been deprecated. Please use the scheduled task instead.');
|
||||
|
||||
// Abort execution of the CLI script if the auth_cas\task\sync_task is enabled.
|
||||
$task = \core\task\manager::get_scheduled_task('auth_cas\task\sync_task');
|
||||
if (!$task->get_disabled()) {
|
||||
cli_error('[AUTH CAS] The scheduled task sync_task is enabled, the cron execution has been aborted.');
|
||||
}
|
||||
|
||||
$casauth = get_auth_plugin('cas');
|
||||
$casauth->sync_users(true);
|
||||
|
||||
|
||||
@@ -20,10 +20,6 @@
|
||||
* This script is meant to be called from a cronjob to sync moodle with the LDAP
|
||||
* backend in those setups where the LDAP backend acts as 'master'.
|
||||
*
|
||||
* Sample cron entry:
|
||||
* # 5 minutes past 4am
|
||||
* 5 4 * * * $sudo -u www-data /usr/bin/php /var/www/moodle/auth/ldap/cli/sync_users.php
|
||||
*
|
||||
* Notes:
|
||||
* - it is required to use the web server account when executing PHP CLI scripts
|
||||
* - you need to change the "www-data" to match the apache user account
|
||||
@@ -44,12 +40,15 @@
|
||||
* @package auth_ldap
|
||||
* @copyright 2004 Martin Langhoff
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @deprecated since Moodle 3.0 MDL-51824 - please do not use this CLI script any more, use scheduled task instead.
|
||||
* @todo MDL-50264 This will be deleted in Moodle 3.2.
|
||||
*/
|
||||
|
||||
define('CLI_SCRIPT', true);
|
||||
|
||||
require(dirname(dirname(dirname(dirname(__FILE__)))).'/config.php'); // global moodle config file.
|
||||
require_once($CFG->dirroot.'/course/lib.php');
|
||||
require_once($CFG->libdir.'/clilib.php');
|
||||
|
||||
// Ensure errors are well explained
|
||||
set_debugging(DEBUG_DEVELOPER, true);
|
||||
@@ -59,6 +58,14 @@ if (!is_enabled_auth('ldap')) {
|
||||
die;
|
||||
}
|
||||
|
||||
cli_problem('[AUTH LDAP] The users sync cron has been deprecated. Please use the scheduled task instead.');
|
||||
|
||||
// Abort execution of the CLI script if the auth_ldap\task\sync_task is enabled.
|
||||
$taskdisabled = \core\task\manager::get_scheduled_task('auth_ldap\task\sync_task');
|
||||
if (!$taskdisabled->get_disabled()) {
|
||||
cli_error('[AUTH LDAP] The scheduled task sync_task is enabled, the cron execution has been aborted.');
|
||||
}
|
||||
|
||||
$ldapauth = get_auth_plugin('ldap');
|
||||
$ldapauth->sync_users(true);
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ $string['auth_ldap_expiration_desc'] = 'Select No to disable expired password ch
|
||||
$string['auth_ldap_expiration_key'] = 'Expiration';
|
||||
$string['auth_ldap_expiration_warning_desc'] = 'Number of days before password expiration warning is issued.';
|
||||
$string['auth_ldap_expiration_warning_key'] = 'Expiration warning';
|
||||
$string['auth_ldap_expireattr_desc'] = 'Optional: overrides ldap-attribute that stores password expiration time';
|
||||
$string['auth_ldap_expireattr_desc'] = 'Optional: Overrides the LDAP attribute that stores password expiration time.';
|
||||
$string['auth_ldap_expireattr_key'] = 'Expiration attribute';
|
||||
$string['auth_ldapextrafields'] = 'These fields are optional. You can choose to pre-fill some Moodle user fields with information from the <b>LDAP fields</b> that you specify here. <p>If you leave these fields blank, then nothing will be transferred from LDAP and Moodle defaults will be used instead.</p><p>In either case, the user will be able to edit all of these fields after they log in.</p>';
|
||||
$string['auth_ldap_graceattr_desc'] = 'Optional: Overrides gracelogin attribute';
|
||||
@@ -71,7 +71,7 @@ $string['auth_ldap_noconnect_all'] = 'LDAP-module cannot connect to any servers:
|
||||
$string['auth_ldap_noextension'] = '<em>The PHP LDAP module does not seem to be present. Please ensure it is installed and enabled if you want to use this authentication plugin.</em>';
|
||||
$string['auth_ldap_no_mbstring'] = 'You need the mbstring extension to create users in Active Directory.';
|
||||
$string['auth_ldapnotinstalled'] = 'Cannot use LDAP authentication. The PHP LDAP module is not installed.';
|
||||
$string['auth_ldap_objectclass'] = 'Optional: Overrides objectClass used to name/search users on ldap_user_type. Usually you dont need to chage this.';
|
||||
$string['auth_ldap_objectclass'] = 'Optional: Overrides objectClass used to name/search users on ldap_user_type. Usually you don\'t need to change this.';
|
||||
$string['auth_ldap_objectclass_key'] = 'Object class';
|
||||
$string['auth_ldap_opt_deref'] = 'Determines how aliases are handled during search. Select one of the following values: "No" (LDAP_DEREF_NEVER) or "Yes" (LDAP_DEREF_ALWAYS)';
|
||||
$string['auth_ldap_opt_deref_key'] = 'Dereference aliases';
|
||||
|
||||
@@ -92,7 +92,7 @@ class block_activity_results extends block_base {
|
||||
return $result;
|
||||
}
|
||||
// See if it is a gradable activity.
|
||||
if (($rec->gradetype != GRADE_TYPE_VALUE) || ($rec->gradetype != GRADE_TYPE_SCALE)) {
|
||||
if (($rec->gradetype != GRADE_TYPE_VALUE) && ($rec->gradetype != GRADE_TYPE_SCALE)) {
|
||||
return $result;
|
||||
}
|
||||
return $rec;
|
||||
|
||||
@@ -28,31 +28,44 @@ Feature: The activity results block displays student scores
|
||||
And I follow "Course 1"
|
||||
And I turn editing mode on
|
||||
And I add a "Assignment" to section "1" and I fill the form with:
|
||||
| Assignment name | Test assignment |
|
||||
| Assignment name | Test assignment 1 |
|
||||
| Description | Offline text |
|
||||
| assignsubmission_file_enabled | 0 |
|
||||
And I follow "Course 1"
|
||||
And I add a "Assignment" to section "1" and I fill the form with:
|
||||
| Assignment name | Test assignment 2 |
|
||||
| Description | Offline text |
|
||||
| assignsubmission_file_enabled | 0 |
|
||||
And I follow "Course 1"
|
||||
And I add a "Assignment" to section "1" and I fill the form with:
|
||||
| Assignment name | Test assignment 3 |
|
||||
| Description | Offline text |
|
||||
| assignsubmission_file_enabled | 0 |
|
||||
And I follow "Course 1"
|
||||
And I add a "Page" to section "1"
|
||||
And I set the following fields to these values:
|
||||
| Name | Test page name |
|
||||
| Description | Test page description |
|
||||
| Page content | This is a page |
|
||||
And I press "Save and return to course"
|
||||
And I follow "Course 1"
|
||||
And I should see "Test page name"
|
||||
And I navigate to "Grades" node in "Course administration"
|
||||
And I turn editing mode on
|
||||
And I give the grade "90.00" to the user "Student 1" for the grade item "Test assignment"
|
||||
And I give the grade "80.00" to the user "Student 2" for the grade item "Test assignment"
|
||||
And I give the grade "70.00" to the user "Student 3" for the grade item "Test assignment"
|
||||
And I give the grade "60.00" to the user "Student 4" for the grade item "Test assignment"
|
||||
And I give the grade "50.00" to the user "Student 5" for the grade item "Test assignment"
|
||||
And I give the grade "90.00" to the user "Student 1" for the grade item "Test assignment 1"
|
||||
And I give the grade "80.00" to the user "Student 2" for the grade item "Test assignment 1"
|
||||
And I give the grade "70.00" to the user "Student 3" for the grade item "Test assignment 1"
|
||||
And I give the grade "60.00" to the user "Student 4" for the grade item "Test assignment 1"
|
||||
And I give the grade "50.00" to the user "Student 5" for the grade item "Test assignment 1"
|
||||
And I press "Save changes"
|
||||
And I follow "Course 1"
|
||||
|
||||
Scenario: Configure the block on a non-graded activity to show 3 high scores
|
||||
Given I add a "Page" to section "1"
|
||||
Given I follow "Test page name"
|
||||
And I add the "Activity results" block
|
||||
When I configure the "Activity results" block
|
||||
And I set the following fields to these values:
|
||||
| Name | Test page name |
|
||||
| Description | Test page description |
|
||||
| page | This is a page |
|
||||
And I press "Save and display"
|
||||
When I add the "Activity results" block
|
||||
And I configure the "Activity results" block
|
||||
And I set the following fields to these values:
|
||||
| id_config_activitygradeitemid | Test assignment |
|
||||
| id_config_activitygradeitemid | Test assignment 1 |
|
||||
| id_config_showbest | 3 |
|
||||
| id_config_showworst | 0 |
|
||||
| id_config_gradeformat | Absolute numbers |
|
||||
@@ -63,4 +76,25 @@ Scenario: Configure the block on a non-graded activity to show 3 high scores
|
||||
And I should see "Student 2" in the "Activity results" "block"
|
||||
And I should see "80.00" in the "Activity results" "block"
|
||||
And I should see "Student 3" in the "Activity results" "block"
|
||||
And I should see "70.00" in the "Activity results" "block"
|
||||
And I should see "70.00" in the "Activity results" "block"
|
||||
|
||||
Scenario: Block should select current activity by default
|
||||
Given I follow "Test assignment 1"
|
||||
When I add the "Activity results" block
|
||||
And I configure the "Activity results" block
|
||||
Then the field "id_config_activitygradeitemid" matches value "Test assignment 1"
|
||||
And I follow "Course 1"
|
||||
And I follow "Test assignment 2"
|
||||
And I add the "Activity results" block
|
||||
And I configure the "Activity results" block
|
||||
And the field "id_config_activitygradeitemid" matches value "Test assignment 2"
|
||||
And I follow "Course 1"
|
||||
And I follow "Test assignment 3"
|
||||
And I add the "Activity results" block
|
||||
And I configure the "Activity results" block
|
||||
And the field "id_config_activitygradeitemid" matches value "Test assignment 3"
|
||||
And I follow "Course 1"
|
||||
And I follow "Test page name"
|
||||
And I add the "Activity results" block
|
||||
And I configure the "Activity results" block
|
||||
And the field "id_config_activitygradeitemid" does not match value "Test page name"
|
||||
|
||||
@@ -23,6 +23,12 @@
|
||||
*/
|
||||
|
||||
class block_course_summary extends block_base {
|
||||
|
||||
/**
|
||||
* @var bool Flag to indicate whether the header should be hidden or not.
|
||||
*/
|
||||
private $headerhidden = true;
|
||||
|
||||
function init() {
|
||||
$this->title = get_string('pluginname', 'block_course_summary');
|
||||
}
|
||||
@@ -32,8 +38,10 @@ class block_course_summary extends block_base {
|
||||
}
|
||||
|
||||
function specialization() {
|
||||
if($this->page->pagetype == PAGE_COURSE_VIEW && $this->page->course->id != SITEID) {
|
||||
// Page type starts with 'course-view' and the page's course ID is not equal to the site ID.
|
||||
if (strpos($this->page->pagetype, PAGE_COURSE_VIEW) === 0 && $this->page->course->id != SITEID) {
|
||||
$this->title = get_string('coursesummary', 'block_course_summary');
|
||||
$this->headerhidden = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +79,7 @@ class block_course_summary extends block_base {
|
||||
}
|
||||
|
||||
function hide_header() {
|
||||
return true;
|
||||
return $this->headerhidden;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,19 +25,22 @@ Feature: Course summary block used in a course
|
||||
Scenario: Student can view course summary
|
||||
When I log in as "student1"
|
||||
And I follow "Course 1"
|
||||
Then "Course/site summary" "block" should exist
|
||||
And I should see "Proved the course summary block works!" in the "Course/site summary" "block"
|
||||
Then "Course summary" "block" should exist
|
||||
And I should see "Course summary" in the "Course summary" "block"
|
||||
And I should see "Proved the course summary block works!" in the "Course summary" "block"
|
||||
|
||||
Scenario: Teacher can see an edit icon when edit mode is on and follow it to the course edit page
|
||||
When I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I turn editing mode on
|
||||
Then I should see "Proved the course summary block works!" in the "Course/site summary" "block"
|
||||
And I click on "Edit" "link" in the "Course/site summary" "block"
|
||||
Then I should see "Proved the course summary block works!" in the "Course summary" "block"
|
||||
And I should see "Course summary" in the "Course summary" "block"
|
||||
And I click on "Edit" "link" in the "Course summary" "block"
|
||||
Then I should see "Edit course settings" in the "h2" "css_element"
|
||||
|
||||
Scenario: Teacher can not see edit icon when edit mode is off
|
||||
When I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
Then I should see "Proved the course summary block works!" in the "Course/site summary" "block"
|
||||
And "Edit" "link" should not exist in the "Course/site summary" "block"
|
||||
Then I should see "Proved the course summary block works!" in the "Course summary" "block"
|
||||
And I should see "Course summary" in the "Course summary" "block"
|
||||
And "Edit" "link" should not exist in the "Course summary" "block"
|
||||
|
||||
@@ -17,6 +17,7 @@ Feature: Course summary block used on the frontpage
|
||||
Scenario: Guest can view site summary
|
||||
When I am on site homepage
|
||||
Then "Course/site summary" "block" should exist
|
||||
And I should not see "Course summary" in the "Course/site summary" "block"
|
||||
And I should see "Proved the summary block works!" in the "Course/site summary" "block"
|
||||
|
||||
Scenario: Admin can see an edit icon when edit mode is on and follow it to the front page settings
|
||||
@@ -24,6 +25,7 @@ Feature: Course summary block used on the frontpage
|
||||
And I am on site homepage
|
||||
And I follow "Turn editing on"
|
||||
Then I should see "Proved the summary block works!" in the "Course/site summary" "block"
|
||||
And I should not see "Course summary" in the "Course/site summary" "block"
|
||||
And I click on "Edit" "link" in the "Course/site summary" "block"
|
||||
Then I should see "Front page settings" in the "h2" "css_element"
|
||||
|
||||
@@ -31,4 +33,5 @@ Feature: Course summary block used on the frontpage
|
||||
When I log in as "admin"
|
||||
And I am on site homepage
|
||||
Then I should see "Proved the summary block works!" in the "Course/site summary" "block"
|
||||
And I should not see "Course summary" in the "Course/site summary" "block"
|
||||
And "Edit" "link" should not exist in the "Course/site summary" "block"
|
||||
|
||||
@@ -39,11 +39,11 @@ Feature: Expand the courses nodes within the navigation block
|
||||
And I log in as "admin"
|
||||
And I am on site homepage
|
||||
And I follow "Course 2"
|
||||
And I turn editing mode on
|
||||
And I click on "Edit settings" "link" in the "Administration" "block"
|
||||
And I navigate to "Enrolment methods" node in "Course administration > Users"
|
||||
And I click on "Edit" "link" in the "Guest access" "table_row"
|
||||
And I set the following fields to these values:
|
||||
| Allow guest access | Yes |
|
||||
And I press "Save and display"
|
||||
And I press "Save changes"
|
||||
And I log out
|
||||
|
||||
@javascript
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
*/
|
||||
|
||||
$string['anycategory'] = 'Any category';
|
||||
$string['apierror'] = 'The Youtube API key is not set. Contact your administrator.';
|
||||
$string['apierror'] = 'The YouTube API key is not set. Contact your administrator.';
|
||||
$string['apikey'] = 'API key';
|
||||
$string['apikeyinfo'] = 'Get a <a href="https://developers.google.com/youtube/v3/getting-started">Google API key</a> for your Moodle site.';
|
||||
$string['autosvehicles'] = 'Autos & Vehicles';
|
||||
@@ -42,7 +42,7 @@ $string['numberofvideos'] = 'Number of videos';
|
||||
$string['peopleblogs'] = 'People & Blogs';
|
||||
$string['petsanimals'] = 'Pets & Animals';
|
||||
$string['pluginname'] = 'YouTube';
|
||||
$string['requesterror'] = 'Data could not be obtained from the server. Contact your administrator if the problem persist.';
|
||||
$string['requesterror'] = 'Data could not be obtained from the server. Contact your administrator if the problem persists.';
|
||||
$string['scienceandtech'] = 'Science & Tech';
|
||||
$string['sports'] = 'Sports';
|
||||
$string['tag_youtube:addinstance'] = 'Add a new YouTube block';
|
||||
|
||||
@@ -104,7 +104,7 @@ abstract class format_section_renderer_base extends plugin_renderer_base {
|
||||
$url = empty($value['url']) ? '' : $value['url'];
|
||||
$icon = empty($value['icon']) ? '' : $value['icon'];
|
||||
$name = empty($value['name']) ? '' : $value['name'];
|
||||
$attr = empty($value['attr']) ? '' : $value['attr'];
|
||||
$attr = empty($value['attr']) ? array() : $value['attr'];
|
||||
$class = empty($item['pixattr']['class']) ? '' : $item['pixattr']['class'];
|
||||
$alt = empty($item['pixattr']['alt']) ? '' : $item['pixattr']['alt'];
|
||||
$al = new action_menu_link_secondary(
|
||||
|
||||
+5
-1
@@ -172,7 +172,11 @@ class course_enrolment_manager {
|
||||
FROM {user} u
|
||||
JOIN {user_enrolments} ue ON (ue.userid = u.id AND ue.enrolid $instancessql)
|
||||
JOIN {enrol} e ON (e.id = ue.enrolid)
|
||||
LEFT JOIN {groups_members} gm ON u.id = gm.userid
|
||||
LEFT JOIN {groups_members} gm ON u.id = gm.userid AND gm.groupid IN (
|
||||
SELECT g.id
|
||||
FROM {groups} g
|
||||
WHERE g.courseid = e.courseid
|
||||
)
|
||||
WHERE $filtersql";
|
||||
$this->totalusers = (int)$DB->count_records_sql($sqltotal, $params);
|
||||
}
|
||||
|
||||
@@ -101,9 +101,9 @@ $string['sendcoursewelcomemessage'] = 'Send course welcome message';
|
||||
$string['sendcoursewelcomemessage_help'] = 'If enabled, users receive a welcome message via email when they self-enrol in a course.';
|
||||
$string['showhint'] = 'Show hint';
|
||||
$string['showhint_desc'] = 'Show first letter of the guest access key.';
|
||||
$string['status'] = 'Enable existing enrolments';
|
||||
$string['status'] = 'Allow existing enrolments';
|
||||
$string['status_desc'] = 'Enable self enrolment method in new courses.';
|
||||
$string['status_help'] = 'If disabled all existing self enrolments are suspended and new users can not enrol.';
|
||||
$string['status_help'] = 'If enabled together with \'Allow new enrolments\' disabled, only users who self enrolled previously can access the course. If disabled, this self enrolment method is effectively disabled, since all existing self enrolments are suspended and new users cannot self enrol.';
|
||||
$string['unenrol'] = 'Unenrol user';
|
||||
$string['unenrolselfconfirm'] = 'Do you really want to unenrol yourself from course "{$a}"?';
|
||||
$string['unenroluser'] = 'Do you really want to unenrol "{$a->user}" from course "{$a->course}"?';
|
||||
|
||||
@@ -27,7 +27,7 @@ Feature: Users can auto-enrol themself in courses where self enrolment is allowe
|
||||
And I log out
|
||||
When I follow "Course 1"
|
||||
And I press "Log in as a guest"
|
||||
Then I should see "Guests can not access this course, please try to log in."
|
||||
Then I should see "Guests cannot access this course. Please log in."
|
||||
And I press "Continue"
|
||||
And I should see "Log in"
|
||||
|
||||
|
||||
@@ -50,11 +50,12 @@ Feature: Enrolled users can be filtered by group
|
||||
And I should see "<expected3>"
|
||||
And I should not see "<notexpected1>"
|
||||
And I should not see "<notexpected2>"
|
||||
And I should see "<expected4>"
|
||||
|
||||
# Note the 'XX-IGNORE-XX' elements are for when there is less than 2 'not expected' items.
|
||||
Examples:
|
||||
| group | expected1 | expected2 | expected3 | notexpected1 | notexpected2 |
|
||||
| All participants | Student 1 | Student 2 | Student 3 | XX-IGNORE-XX | XX-IGNORE-XX |
|
||||
| No group | Student 1 | | | Student 2 | Student 3 |
|
||||
| Group 1 | Student 2 | | | Student 1 | Student 3 |
|
||||
| Group 2 | Student 2 | Student 3 | | Student 1 | XX-IGNORE-XX |
|
||||
| group | expected1 | expected2 | expected3 | expected4 | notexpected1 | notexpected2 |
|
||||
| All participants | Student 1 | Student 2 | Student 3 | 4 enrolled users | XX-IGNORE-XX | XX-IGNORE-XX |
|
||||
| No group | Student 1 | | | 2 enrolled users | Student 2 | Student 3 |
|
||||
| Group 1 | Student 2 | | | 1 enrolled users | Student 1 | Student 3 |
|
||||
| Group 2 | Student 2 | Student 3 | | 2 enrolled users | Student 1 | XX-IGNORE-XX |
|
||||
|
||||
@@ -3142,9 +3142,6 @@ abstract class grade_helper {
|
||||
JOIN {user_info_category} c ON f.categoryid=c.id
|
||||
WHERE f.shortname $wherefields
|
||||
ORDER BY c.sortorder ASC, f.sortorder ASC", $whereparams);
|
||||
if (!is_array($customfields)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($customfields as $field) {
|
||||
// Make sure we can display this custom field
|
||||
|
||||
@@ -333,19 +333,13 @@ class grade extends tablelike implements selectable_items, filterable_items {
|
||||
|
||||
$data->$field = empty($grade) ? $null : $grade->finalgrade;
|
||||
$data->{"old$field"} = $data->$field;
|
||||
|
||||
preg_match('/_(\d+)_(\d+)/', $field, $oldoverride);
|
||||
$oldoverride = 'oldoverride' . $oldoverride[0];
|
||||
if (empty($data->$oldoverride)) {
|
||||
$data->$field = (!isset($grade->rawgrade)) ? $null : $grade->rawgrade;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($data as $varname => $value) {
|
||||
if (preg_match('/^oldoverride_(\d+)_(\d+)/', $varname, $matches)) {
|
||||
// If we've selected override or overriding all grades.
|
||||
if (!empty($data->$matches[0]) || $filter == 'all') {
|
||||
// If we've selected overriding all grades.
|
||||
if ($filter == 'all') {
|
||||
$override = "override_{$matches[1]}_{$matches[2]}";
|
||||
$data->$override = '1';
|
||||
}
|
||||
|
||||
@@ -336,29 +336,32 @@ class user extends tablelike implements selectable_items {
|
||||
}
|
||||
|
||||
$oldfinalgradefield = "oldfinalgrade_{$gradeitem->id}_{$this->itemid}";
|
||||
if (!empty($data->$oldfinalgradefield)) {
|
||||
// Bulk grade changes for all grades need to be processed and shouldn't be skipped if they had a previous grade.
|
||||
if ($gradeitem->is_course_item() || ($filter != 'all' && !empty($data->$oldfinalgradefield))) {
|
||||
if ($gradeitem->is_course_item()) {
|
||||
// The course total should not be overridden.
|
||||
unset($data->$field);
|
||||
unset($data->oldfinalgradefield);
|
||||
$oldoverride = "oldoverride_{$gradeitem->id}_{$this->itemid}";
|
||||
unset($data->$oldoverride);
|
||||
$oldfeedback = "oldfeedback_{$gradeitem->id}_{$this->itemid}";
|
||||
unset($data->$oldfeedback);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
$grade = grade_grade::fetch(array(
|
||||
'itemid' => $this->itemid,
|
||||
'itemid' => $gradeitemid,
|
||||
'userid' => $userid
|
||||
));
|
||||
|
||||
$data->$field = empty($grade) ? $null : $grade->finalgrade;
|
||||
$data->{"old$field"} = $data->$field;
|
||||
|
||||
preg_match('/_(\d+)_(\d+)/', $field, $oldoverride);
|
||||
$oldoverride = 'oldoverride' . $oldoverride[0];
|
||||
if (empty($data->$oldoverride)) {
|
||||
$data->$field = (!isset($grade->rawgrade)) ? $null : $grade->rawgrade;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
foreach ($data as $varname => $value) {
|
||||
if (preg_match('/^oldoverride_(\d+)_(\d+)/', $varname, $matches)) {
|
||||
// If we've selected override or overriding all grades.
|
||||
if (!empty($data->$matches[0]) || $filter == 'all') {
|
||||
// If we've selected overriding all grades.
|
||||
if ($filter == 'all') {
|
||||
$override = "override_{$matches[1]}_{$matches[2]}";
|
||||
$data->$override = '1';
|
||||
}
|
||||
|
||||
@@ -47,14 +47,7 @@ class finalgrade extends grade_attribute_format implements unique_value, be_disa
|
||||
public function get_value() {
|
||||
$this->label = $this->grade->grade_item->itemname;
|
||||
|
||||
$isoverridden = $this->grade->is_overridden();
|
||||
// If the grade is overridden or the grade type is not an activity then use finalgrade.
|
||||
if (!empty($isoverridden) || $this->grade->grade_item->itemtype != 'mod') {
|
||||
$val = $this->grade->finalgrade;
|
||||
} else {
|
||||
$val = $this->grade->rawgrade;
|
||||
}
|
||||
|
||||
$val = $this->grade->finalgrade;
|
||||
if ($this->grade->grade_item->scaleid) {
|
||||
return $val ? (int)$val : -1;
|
||||
} else {
|
||||
|
||||
@@ -105,7 +105,7 @@ Feature: We can use Single view
|
||||
And I set the field "Insert value" to "1.0"
|
||||
And I click on "Perform bulk insert" "checkbox"
|
||||
And I press "Save"
|
||||
Then I should see "Grades were set for 9 items"
|
||||
Then I should see "Grades were set for 8 items"
|
||||
|
||||
Scenario: Navigation works in the Single view.
|
||||
Given I follow "Single view for Student 1"
|
||||
|
||||
@@ -90,5 +90,5 @@ $string['welcomep50'] = 'L\'ús de totes les aplicacions d\'aquest paquet és go
|
||||
<a href="http://www.opensource.org/docs/definition_plain.html">codi font obert</a> i es distribueix
|
||||
sota llicència <a href="http://www.gnu.org/copyleft/gpl.html">GPL</a>.';
|
||||
$string['welcomep60'] = 'Les pàgines següents us guiaran per una sèrie de passos fàcils de seguir per configurar <strong>Moodle</strong> en el vostre ordinador. Podeu acceptar els paràmetres per defecte o, opcionalment, modificar-los perquè s\'ajustin a les vostres necessitats.';
|
||||
$string['welcomep70'] = 'Feu clic en el botó "Següent" per continuar la configuració de <strong>Moodle</strong>.';
|
||||
$string['welcomep70'] = 'Feu clic al botó «Següent» per continuar amb la configuració de <strong>Moodle</strong>.';
|
||||
$string['wwwroot'] = 'Adreça web';
|
||||
|
||||
@@ -42,3 +42,4 @@ Použijte volbu --help pro získání nápovědy';
|
||||
$string['cliyesnoprompt'] = 'zadejte a (pro "ano") nebo n (pro "ne")';
|
||||
$string['environmentrequireinstall'] = 'musí být nainstalováno a povoleno';
|
||||
$string['environmentrequireversion'] = 'vyžadována je verze {$a->needed}, nyní používáte verzi {$a->current}';
|
||||
$string['upgradekeyset'] = 'Klíč pro upgrade (ponechte prázdné, nechcete-li nenastavil)';
|
||||
|
||||
@@ -42,3 +42,4 @@ $string['cliunknowoption'] = 'Opzioni non riconosciute:
|
||||
$string['cliyesnoprompt'] = 'inserisci s (per Si) o n (per No)';
|
||||
$string['environmentrequireinstall'] = 'E\' necessaria l\'installazione e attivazione';
|
||||
$string['environmentrequireversion'] = 'Versione minima necessaria: {$a->needed}. Versione in uso: {$a->current}';
|
||||
$string['upgradekeyset'] = 'Chiave per l\'aggiornamento (lasciare vuoto se non impostata)';
|
||||
|
||||
@@ -42,3 +42,4 @@ Vennligst bruk Hjelp.';
|
||||
$string['cliyesnoprompt'] = 'Tast j (betyr JA) eller n (betyr NEI)';
|
||||
$string['environmentrequireinstall'] = 'er påkrevd at du installerer/tillater';
|
||||
$string['environmentrequireversion'] = 'Du må ha versjon {$a->needed}. Du kjører {$a->current}';
|
||||
$string['upgradekeyset'] = 'Oppgraderingsnøkkel (la være tom for å kreve noen)';
|
||||
|
||||
@@ -27,7 +27,7 @@ $string['addrestriction'] = 'Add restriction...';
|
||||
$string['and'] = 'and';
|
||||
$string['condition_group'] = 'Restriction set';
|
||||
$string['condition_group_info'] = 'Add a set of nested restrictions to apply complex logic.';
|
||||
$string['enableavailability'] = 'Enable conditional access';
|
||||
$string['enableavailability'] = 'Enable restricted access';
|
||||
$string['enableavailability_desc'] = 'When enabled, this lets you set conditions (based on date, grade, or completion) that control whether an activity or resource can be accessed.';
|
||||
$string['error_list_nochildren'] = 'Restriction sets should contain at least one condition.';
|
||||
$string['hidden_marker'] = '(hidden otherwise)';
|
||||
|
||||
+1
-1
@@ -106,7 +106,7 @@ $string['badgeprivacysetting_help'] = 'Badges you earn can be displayed on your
|
||||
|
||||
You can still control individual badge privacy settings on your badges page.';
|
||||
$string['badgeprivacysetting_str'] = 'Automatically show badges I earn on my profile page';
|
||||
$string['badgesalt'] = 'Salt for hashing the recepient\'s email address';
|
||||
$string['badgesalt'] = 'Salt for hashing the recipient\'s email address';
|
||||
$string['badgesalt_desc'] = 'Using a hash allows backpack services to confirm the badge earner without having to expose their email address. This setting should only use numbers and letters.
|
||||
|
||||
Note: For recipient verification purposes, please avoid changing this setting once you start issuing badges.';
|
||||
|
||||
@@ -41,7 +41,7 @@ $string['completed'] = 'Completed';
|
||||
$string['completedunlocked'] = 'Completion options unlocked';
|
||||
$string['completedunlockedtext'] = 'When you save changes, completion state for all students will be erased. If you change your mind about this, do not save the form.';
|
||||
$string['completedwarning'] = 'Completion options locked';
|
||||
$string['completedwarningtext'] = 'One or more students ({$a}) has already marked this activity as completed. Changing completion options will erase their completion state and may cause confusion. Thus the options have been locked and should not be unlocked unless absolutely necessary.';
|
||||
$string['completedwarningtext'] = 'This activity has already been marked as completed for {$a} participant(s). Changing completion options will erase their completion state and may cause confusion. Thus the options have been locked and should not be unlocked unless absolutely necessary.';
|
||||
$string['completion'] = 'Completion tracking';
|
||||
$string['completion-alt-auto-enabled'] = 'The system marks this item complete according to conditions: {$a}';
|
||||
$string['completion-alt-auto-fail'] = 'Completed: {$a} (did not achieve pass grade)';
|
||||
|
||||
+1
-1
@@ -98,7 +98,7 @@ $string['migratetomanual'] = 'Migrate to manual enrolments';
|
||||
$string['nochange'] = 'No change';
|
||||
$string['noexistingparticipants'] = 'No existing participants';
|
||||
$string['nogroup'] = 'No group';
|
||||
$string['noguestaccess'] = 'Guests can not access this course, please try to log in.';
|
||||
$string['noguestaccess'] = 'Guests cannot access this course. Please log in.';
|
||||
$string['none'] = 'None';
|
||||
$string['notenrollable'] = 'You can not enrol yourself in this course.';
|
||||
$string['notenrolledusers'] = 'Other users';
|
||||
|
||||
+2
-2
@@ -304,7 +304,7 @@ $string['invalidcourselevel'] = 'Incorrect context level';
|
||||
$string['invalidcoursemodule'] = 'Invalid course module ID';
|
||||
$string['invalidcoursenameshort'] = 'Invalid short course name';
|
||||
$string['invaliddata'] = 'Data submitted is invalid';
|
||||
$string['invaliddatarootpermissions'] = 'Invalid permissions detected in $CFG->dataroot directory, administrator has to fix permissions.';
|
||||
$string['invaliddatarootpermissions'] = 'Invalid permissions detected when trying to create a directory. Turn debugging on for further details.';
|
||||
$string['invaliddevicetype'] = 'Invalid device type';
|
||||
$string['invalidelementid'] = 'Incorrect element id!';
|
||||
$string['invalidentry'] = 'This is not valid entry!';
|
||||
@@ -388,7 +388,7 @@ $string['movecatcontentstoroot'] = 'Moving the category content to root is not a
|
||||
$string['movecategorynotpossible'] = 'You cannot move category \'{$a}\' into the selected category.';
|
||||
$string['movecategoryownparent'] = 'You cannot make category \'{$a}\' a parent of itself.';
|
||||
$string['movecategoryparentconflict'] = 'You cannot make category \'{$a}\' a subcategory of one of its own subcategories.';
|
||||
$string['mssqlrcsmodemissing'] = 'The database is not using the expected READ_COMMITTED_SNAPSHOT mode which can lead to wrong results, especially under high concurrency scenarios. Please enable it for correct behaviour. You can find more information in the <a href="https://docs.moodle.org/en/Installing_MSSQL_for_PHP#Configuration">Moodle Docs</a>.';
|
||||
$string['mssqlrcsmodemissing'] = 'The database is not using the expected READ_COMMITTED_SNAPSHOT mode which can lead to wrong results, especially under high concurrency scenarios. Please enable it for correct behaviour. For further information, see the documentation <a href="https://docs.moodle.org/en/Installing_MSSQL_for_PHP">Installing MSSQL for PHP</a>.';
|
||||
$string['multiplerecordsfound'] = 'Multiple records found, only one record expected.';
|
||||
$string['multiplerestorenotallow'] = 'Multiple restore execution not allowed!';
|
||||
$string['mustbeloggedin'] = 'You must be logged in to do this';
|
||||
|
||||
@@ -50,6 +50,7 @@ $string['mustbeoverriden'] = 'Abstract form_definition() method in class {$a} mu
|
||||
$string['nomethodforaddinghelpbutton'] = 'There is no method for adding a help button to form element {$a->name} (class {$a->classname})';
|
||||
$string['nonexistentformelements'] = 'Trying to add help buttons to non-existent form elements : {$a}';
|
||||
$string['noselection'] = 'No selection';
|
||||
$string['nosuggestions'] = 'No suggestions';
|
||||
$string['optional'] = 'Optional';
|
||||
$string['othersettings'] = 'Other settings';
|
||||
$string['requiredelement'] = 'Required field';
|
||||
|
||||
+3
-1
@@ -240,7 +240,9 @@ $string['gradeanalysis'] = 'Grade analysis';
|
||||
$string['gradebook'] = 'Gradebook';
|
||||
$string['gradebookcalculationsuptodate'] = 'The calculations in the gradebook are up to date. You may need to reload this page to see changes.';
|
||||
$string['gradebookcalculationsfixbutton'] = 'Accept grade changes and fix calculation errors';
|
||||
$string['gradebookcalculationswarning'] = 'Note: Some errors have been detected in calculating the grades displayed in the gradebook. It is recommended that the errors are fixed by clicking the button below, though this will result in some grades being changed. For details, see the changes between versions {$a->gradebookversion} and {$a->currentversion} in <a href="{$a->url}">Gradebook calculation changes</a>.';
|
||||
$string['gradebookcalculationswarning'] = 'Note: Some errors have been detected in calculating the grades displayed in the gradebook. If your course has not started or is in progress, it is recommended that the errors are fixed by clicking the button below, though this will result in some grades being changed. If your course has already ended and grades have been submitted, you probably do not want to fix this issue.
|
||||
|
||||
The newer version is {$a->currentversion}; you are using gradebook version {$a->gradebookversion}. You can see a list of changes in <a href="{$a->url}">Gradebook calculation changes</a>.';
|
||||
$string['gradebookhiddenerror'] = 'The gradebook is currently set to hide everything from students.';
|
||||
$string['gradebookhistories'] = 'Grade histories';
|
||||
$string['gradebooksetup'] = 'Gradebook setup';
|
||||
|
||||
+1
-1
@@ -1002,7 +1002,7 @@ $string['institution'] = 'Institution';
|
||||
$string['instudentview'] = 'in student view';
|
||||
$string['interests'] = 'Interests';
|
||||
$string['interestslist'] = 'List of interests';
|
||||
$string['interestslist_help'] = 'Enter your interests. Your interests will be displayed on your profile page as tags.';
|
||||
$string['interestslist_help'] = 'Enter your interests, one by one, which will then be displayed on your profile page as tags.';
|
||||
$string['invalidemail'] = 'Invalid email address';
|
||||
$string['invalidlogin'] = 'Invalid login, please try again';
|
||||
$string['invalidusername'] = 'The username can only contain alphanumeric lowercase characters, underscore (_), hyphen (-), period (.) or at symbol (@)';
|
||||
|
||||
+2
-2
@@ -30,7 +30,7 @@ $string['availability'] = 'Availability';
|
||||
$string['cancelinstallall'] = 'Cancel new installations ({$a})';
|
||||
$string['cancelinstallone'] = 'Cancel this installation';
|
||||
$string['cancelinstallhead'] = 'Cancelling installation of plugins';
|
||||
$string['cancelinstallinfo'] = 'Following plugins are not fully installed yet and their installation can be cancelled. To do so, the plugin folder must be removed from your server now. Make sure that is really what you want to prevent accidental data loss (such as your own code modifications).';
|
||||
$string['cancelinstallinfo'] = 'The following plugins are not fully installed yet so their installation may be cancelled. To do so, the plugin folder must be removed from the server now. Make sure this is really what you want to prevent accidental data loss (such as your own code modifications).';
|
||||
$string['cancelinstallinfodir'] = 'Folder to be deleted: {$a}';
|
||||
$string['cancelupgradeall'] = 'Cancel upgrades ({$a})';
|
||||
$string['cancelupgradehead'] = 'Restoring previous version of plugins';
|
||||
@@ -67,7 +67,7 @@ $string['notdownloadable'] = 'Can not download the package';
|
||||
$string['notdownloadable_help'] = 'ZIP package with the update can not be downloaded automatically. Please refer to the documentation page for more help.';
|
||||
$string['notdownloadable_link'] = 'admin/mdeploy/notdownloadable';
|
||||
$string['notwritable'] = 'Plugin files not writable';
|
||||
$string['notwritable_help'] = 'Plugin files are not writable by the web server. The web server process has to have write access to the plugin folder and all its contents. Write access to the root folder of the given plugin type may be required, too.';
|
||||
$string['notwritable_help'] = 'Plugin files are not writable by the web server. The web server process must have write access to the plugin folder and all its contents. Write access to the root folder of the given plugin type may also be required.';
|
||||
$string['otherplugin'] = '{$a->component}';
|
||||
$string['otherpluginversion'] = '{$a->component} ({$a->version})';
|
||||
$string['overviewall'] = 'All plugins';
|
||||
|
||||
@@ -117,7 +117,7 @@ $string['iprestriction'] = 'IP restriction';
|
||||
$string['iprestriction_help'] = 'The user will need to call the web service from the listed IPs (separated by commas).';
|
||||
$string['key'] = 'Key';
|
||||
$string['keyshelp'] = 'The keys are used to access your Moodle account from external applications.';
|
||||
$string['loginrequired'] = 'Restricted to logged in users';
|
||||
$string['loginrequired'] = 'Restricted to logged-in users';
|
||||
$string['manageprotocols'] = 'Manage protocols';
|
||||
$string['managetokens'] = 'Manage tokens';
|
||||
$string['missingcaps'] = 'Missing capabilities';
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
@@ -92,6 +92,10 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
|
||||
originalSelect.children('option').each(function(index, ele) {
|
||||
if ($(ele).attr('value') == selectedItemValue) {
|
||||
$(ele).prop('selected', false);
|
||||
// We remove newly created custom tags from the suggestions list when they are deselected.
|
||||
if ($(ele).attr('data-iscustom')) {
|
||||
$(ele).remove();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -133,6 +137,15 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
|
||||
element.attr('aria-selected', true).attr('id', itemId);
|
||||
// Tell the input field it has a new active descendant so the item is announced.
|
||||
inputElement.attr('aria-activedescendant', itemId);
|
||||
|
||||
// Scroll it into view.
|
||||
var scrollPos = element.offset().top
|
||||
- suggestionsElement.offset().top
|
||||
+ suggestionsElement.scrollTop()
|
||||
- (suggestionsElement.height() / 2);
|
||||
suggestionsElement.animate({
|
||||
scrollTop: scrollPos
|
||||
}, 100);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -246,8 +259,9 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
|
||||
* @param {JQuery} originalSelect The JQuery object matching the hidden select list.
|
||||
* @param {Boolean} multiple Are multiple items allowed to be selected?
|
||||
* @param {Boolean} tags Are we allowed to create new items on the fly?
|
||||
* @param {Boolean} caseSensitive - If search has to be made case sensitive.
|
||||
*/
|
||||
var updateSuggestions = function(query, inputId, suggestionsId, originalSelect, multiple, tags) {
|
||||
var updateSuggestions = function(query, inputId, suggestionsId, originalSelect, multiple, tags, caseSensitive) {
|
||||
// Find the elements in the DOM.
|
||||
var inputElement = $(document.getElementById(inputId));
|
||||
var suggestionsElement = $(document.getElementById(suggestionsId));
|
||||
@@ -263,6 +277,7 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
|
||||
});
|
||||
|
||||
// Re-render the list of suggestions.
|
||||
var searchquery = caseSensitive ? query : query.toLocaleLowerCase();
|
||||
templates.render(
|
||||
'core/form_autocomplete_suggestions',
|
||||
{ inputId: inputId, suggestionsId: suggestionsId, options: options, multiple: multiple}
|
||||
@@ -276,7 +291,8 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
|
||||
// For each option in the list, hide it if it doesn't match the query.
|
||||
suggestionsElement.children().each(function(index, node) {
|
||||
node = $(node);
|
||||
if (node.text().indexOf(query) > -1) {
|
||||
if ((caseSensitive && node.text().indexOf(searchquery) > -1) ||
|
||||
(!caseSensitive && node.text().toLocaleLowerCase().indexOf(searchquery) > -1)) {
|
||||
node.show().attr('aria-hidden', false);
|
||||
matchingElements = true;
|
||||
} else {
|
||||
@@ -284,8 +300,8 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
|
||||
}
|
||||
});
|
||||
// If we found any matches, show the list.
|
||||
inputElement.attr('aria-expanded', true);
|
||||
if (matchingElements) {
|
||||
inputElement.attr('aria-expanded', true);
|
||||
// We only activate the first item in the list if tags is false,
|
||||
// because otherwise "Enter" would select the first item, instead of
|
||||
// creating a new tag.
|
||||
@@ -293,10 +309,10 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
|
||||
activateItem(0, inputId, suggestionsId);
|
||||
}
|
||||
} else {
|
||||
// Abort - nothing matches. Hide the suggestions properly.
|
||||
suggestionsElement.hide();
|
||||
suggestionsElement.attr('aria-hidden', true);
|
||||
inputElement.attr('aria-expanded', false);
|
||||
// Nothing matches. Tell them that.
|
||||
str.get_string('nosuggestions', 'form').done(function(nosuggestionsstr) {
|
||||
suggestionsElement.html(nosuggestionsstr);
|
||||
});
|
||||
}
|
||||
}).fail(notification.exception);
|
||||
|
||||
@@ -341,34 +357,18 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
|
||||
option.attr('value', tag);
|
||||
originalSelect.append(option);
|
||||
option.prop('selected', true);
|
||||
// We mark newly created custom options as we handle them differently if they are "deselected".
|
||||
option.attr('data-iscustom', true);
|
||||
}
|
||||
}
|
||||
});
|
||||
// Get the selection element.
|
||||
var newSelection = $(document.getElementById(selectionId));
|
||||
// Build up a valid context to re-render the selection.
|
||||
var items = [];
|
||||
originalSelect.children('option').each(function(index, ele) {
|
||||
if ($(ele).prop('selected')) {
|
||||
items.push( { label: $(ele).html(), value: $(ele).attr('value') } );
|
||||
}
|
||||
});
|
||||
var context = {
|
||||
selectionId: selectionId,
|
||||
items: items,
|
||||
multiple: multiple
|
||||
};
|
||||
// Re-render the selection.
|
||||
templates.render('core/form_autocomplete_selection', context).done(function(newHTML) {
|
||||
// Update the page.
|
||||
newSelection.empty().append($(newHTML).html());
|
||||
}).fail(notification.exception);
|
||||
|
||||
updateSelectionList(selectionId, inputId, originalSelect, multiple);
|
||||
|
||||
// Clear the input field.
|
||||
inputElement.val('');
|
||||
// Close the suggestions list.
|
||||
closeSuggestions(inputId, suggestionsId, selectionId);
|
||||
// Trigger a change event so that the mforms javascript can check for required fields etc.
|
||||
originalSelect.change();
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -385,6 +385,12 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
|
||||
// Build up a valid context to re-render the template.
|
||||
var items = [];
|
||||
var newSelection = $(document.getElementById(selectionId));
|
||||
var activeId = newSelection.attr('aria-activedescendant');
|
||||
var activeValue = false;
|
||||
|
||||
if (activeId) {
|
||||
activeValue = $(document.getElementById(activeId)).attr('data-value');
|
||||
}
|
||||
originalSelect.children('option').each(function(index, ele) {
|
||||
if ($(ele).prop('selected')) {
|
||||
items.push( { label: $(ele).html(), value: $(ele).attr('value') } );
|
||||
@@ -399,6 +405,15 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
|
||||
templates.render('core/form_autocomplete_selection', context).done(function(newHTML) {
|
||||
// Add it to the page.
|
||||
newSelection.empty().append($(newHTML).html());
|
||||
|
||||
if (activeValue !== false) {
|
||||
// Reselect any previously selected item.
|
||||
newSelection.children('[aria-selected=true]').each(function(index, ele) {
|
||||
if ($(ele).attr('data-value') === activeValue) {
|
||||
activateSelection(index, selectionId);
|
||||
}
|
||||
});
|
||||
}
|
||||
}).fail(notification.exception);
|
||||
// Because this function get's called after changing the selection, this is a good place
|
||||
// to trigger a change notification.
|
||||
@@ -457,8 +472,9 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
|
||||
* @param {Boolean} multiple Are multiple items allowed to be selected?
|
||||
* @param {Boolean} tags Are we allowed to create new items on the fly?
|
||||
* @param {Object} ajaxHandler This is a module that does the ajax fetch and translates the results.
|
||||
* @param {Boolean} caseSensitive - If search has to be made case sensitive.
|
||||
*/
|
||||
var updateAjax = function(e, selector, inputId, suggestionsId, originalSelect, multiple, tags, ajaxHandler) {
|
||||
var updateAjax = function(e, selector, inputId, suggestionsId, originalSelect, multiple, tags, ajaxHandler, caseSensitive) {
|
||||
// Get the query to pass to the ajax function.
|
||||
var query = $(e.currentTarget).val();
|
||||
// Call the transport function to do the ajax (name taken from Select2).
|
||||
@@ -486,7 +502,7 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
|
||||
}
|
||||
});
|
||||
// Update the list of suggestions now from the new values in the select list.
|
||||
updateSuggestions('', inputId, suggestionsId, originalSelect, multiple, tags);
|
||||
updateSuggestions('', inputId, suggestionsId, originalSelect, multiple, tags, caseSensitive);
|
||||
}, notification.exception);
|
||||
};
|
||||
|
||||
@@ -502,8 +518,21 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
|
||||
* @param {JQuery} originalSelect The JQuery object matching the hidden select list.
|
||||
* @param {Boolean} multiple Are multiple items allowed to be selected?
|
||||
* @param {Boolean} tags Are we allowed to create new items on the fly?
|
||||
* @param {String} selector The selector for this select list.
|
||||
* @param {String} ajax Name of an AMD module to handle ajax requests. If specified, the AMD
|
||||
* module must expose 2 functions "transport" and "processResults".
|
||||
* @param {Boolean} caseSensitive - If search has to be made case sensitive.
|
||||
*/
|
||||
var addNavigation = function(inputId, suggestionsId, downArrowId, selectionId, originalSelect, multiple, tags) {
|
||||
var addNavigation = function(inputId,
|
||||
suggestionsId,
|
||||
downArrowId,
|
||||
selectionId,
|
||||
originalSelect,
|
||||
multiple,
|
||||
tags,
|
||||
selector,
|
||||
ajax,
|
||||
caseSensitive) {
|
||||
// Start with the input element.
|
||||
var inputElement = $(document.getElementById(inputId));
|
||||
// Add keyboard nav with keydown.
|
||||
@@ -514,8 +543,29 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
|
||||
if (inputElement.attr('aria-expanded') === "true") {
|
||||
activateNextItem(inputId, suggestionsId);
|
||||
} else {
|
||||
// Else - open the suggestions list.
|
||||
updateSuggestions(inputElement.val(), inputId, suggestionsId, originalSelect, multiple, tags);
|
||||
// Handle ajax population of suggestions.
|
||||
if (!inputElement.val() && ajax) {
|
||||
require([ajax], function(ajaxHandler) {
|
||||
updateAjax(e,
|
||||
selector,
|
||||
inputId,
|
||||
suggestionsId,
|
||||
originalSelect,
|
||||
multiple,
|
||||
tags,
|
||||
ajaxHandler,
|
||||
caseSensitive);
|
||||
});
|
||||
} else {
|
||||
// Else - open the suggestions list.
|
||||
updateSuggestions(inputElement.val(),
|
||||
inputId,
|
||||
suggestionsId,
|
||||
originalSelect,
|
||||
multiple,
|
||||
tags,
|
||||
caseSensitive);
|
||||
}
|
||||
}
|
||||
// We handled this event, so prevent it.
|
||||
e.preventDefault();
|
||||
@@ -572,10 +622,12 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
|
||||
window.clearTimeout(closeSuggestionsTimer);
|
||||
}
|
||||
closeSuggestionsTimer = window.setTimeout(function() {
|
||||
if ((e.type == 'blur') && tags) {
|
||||
createItem(inputId, suggestionsId, selectionId, multiple, originalSelect);
|
||||
if (e.type == 'blur') {
|
||||
if (tags) {
|
||||
createItem(inputId, suggestionsId, selectionId, multiple, originalSelect);
|
||||
}
|
||||
closeSuggestions(inputId, suggestionsId, selectionId);
|
||||
}
|
||||
closeSuggestions(inputId, suggestionsId, selectionId);
|
||||
}, 500);
|
||||
});
|
||||
var arrowElement = $(document.getElementById(downArrowId));
|
||||
@@ -586,7 +638,7 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
|
||||
window.clearTimeout(closeSuggestionsTimer);
|
||||
}
|
||||
// Show the suggestions list.
|
||||
updateSuggestions(inputElement.val(), inputId, suggestionsId, originalSelect, multiple, tags);
|
||||
updateSuggestions(inputElement.val(), inputId, suggestionsId, originalSelect, multiple, tags, caseSensitive);
|
||||
});
|
||||
|
||||
var suggestionsElement = $(document.getElementById(suggestionsId));
|
||||
@@ -603,14 +655,14 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
|
||||
});
|
||||
var selectionElement = $(document.getElementById(selectionId));
|
||||
// Handle clicks on the selected items (will unselect an item).
|
||||
selectionElement.parent().on('click', '[role=listitem]', function(e) {
|
||||
selectionElement.on('click', '[role=listitem]', function(e) {
|
||||
// Get the item that was clicked.
|
||||
var item = $(e.currentTarget);
|
||||
// Remove it from the selection.
|
||||
deselectItem(inputId, selectionId, item, originalSelect, multiple);
|
||||
});
|
||||
// Keyboard navigation for the selection list.
|
||||
selectionElement.parent().on('keydown', function(e) {
|
||||
selectionElement.on('keydown', function(e) {
|
||||
switch (e.keyCode) {
|
||||
case KEYS.DOWN:
|
||||
// Choose the next selection item.
|
||||
@@ -641,7 +693,17 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
|
||||
// Whenever the input field changes, update the suggestion list.
|
||||
inputElement.on('input', function(e) {
|
||||
var query = $(e.currentTarget).val();
|
||||
updateSuggestions(query, inputId, suggestionsId, originalSelect, multiple, tags);
|
||||
var last = $(e.currentTarget).data('last-value');
|
||||
if (typeof last === 'undefined') {
|
||||
last = query;
|
||||
}
|
||||
// IE11 fires many more input events than required - even when the value has not changed.
|
||||
// We need to only do this for real value changed events or the suggestions will be
|
||||
// unclickable on IE11 (because they will be rebuilt before the click event fires).
|
||||
if (last != query) {
|
||||
updateSuggestions(query, inputId, suggestionsId, originalSelect, multiple, tags, caseSensitive);
|
||||
$(e.currentTarget).data('last-value', query);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -657,8 +719,9 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
|
||||
* module must expose 2 functions "transport" and "processResults".
|
||||
* These are modeled on Select2 see: https://select2.github.io/options.html#ajax
|
||||
* @param {String} placeholder - The text to display before a selection is made.
|
||||
* @param {Boolean} caseSensitive - If search has to be made case sensitive.
|
||||
*/
|
||||
enhance: function(selector, tags, ajax, placeholder) {
|
||||
enhance: function(selector, tags, ajax, placeholder, caseSensitive) {
|
||||
// Set some default values.
|
||||
if (typeof tags === "undefined") {
|
||||
tags = false;
|
||||
@@ -666,6 +729,9 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
|
||||
if (typeof ajax === "undefined") {
|
||||
ajax = false;
|
||||
}
|
||||
if (typeof caseSensitive === "undefined") {
|
||||
caseSensitive = false;
|
||||
}
|
||||
|
||||
// Look for the select element.
|
||||
var originalSelect = $(selector);
|
||||
@@ -719,7 +785,16 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
|
||||
// Update the form label to point to the text input.
|
||||
originalLabel.attr('for', inputId);
|
||||
// Add the event handlers.
|
||||
addNavigation(inputId, suggestionsId, downArrowId, selectionId, originalSelect, multiple, tags);
|
||||
addNavigation(inputId,
|
||||
suggestionsId,
|
||||
downArrowId,
|
||||
selectionId,
|
||||
originalSelect,
|
||||
multiple,
|
||||
tags,
|
||||
selector,
|
||||
ajax,
|
||||
caseSensitive);
|
||||
|
||||
var inputElement = $(document.getElementById(inputId));
|
||||
var suggestionsElement = $(document.getElementById(suggestionsId));
|
||||
@@ -730,7 +805,15 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
|
||||
if (ajax) {
|
||||
require([ajax], function(ajaxHandler) {
|
||||
var handler = function(e) {
|
||||
updateAjax(e, selector, inputId, suggestionsId, originalSelect, multiple, tags, ajaxHandler);
|
||||
updateAjax(e,
|
||||
selector,
|
||||
inputId,
|
||||
suggestionsId,
|
||||
originalSelect,
|
||||
multiple,
|
||||
tags,
|
||||
ajaxHandler,
|
||||
caseSensitive);
|
||||
};
|
||||
// Trigger an ajax update after the text field value changes.
|
||||
inputElement.on("input keypress", handler);
|
||||
|
||||
@@ -537,7 +537,6 @@ class core_date {
|
||||
self::$badzones['5.5'] = 'Asia/Kolkata';
|
||||
self::$badzones['6.5'] = 'Asia/Rangoon';
|
||||
self::$badzones['9.5'] = 'Australia/Darwin';
|
||||
self::$badzones['11.5'] = 'Pacific/Norfolk';
|
||||
|
||||
// Remove bad zones that are elsewhere.
|
||||
foreach (self::$bczones as $zone => $unused) {
|
||||
|
||||
@@ -491,6 +491,12 @@ class manager {
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure the task data is unchanged.
|
||||
if (!$DB->record_exists('task_scheduled', (array) $record)) {
|
||||
$lock->release();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$task->is_blocking()) {
|
||||
$cronlock->release();
|
||||
} else {
|
||||
|
||||
@@ -180,13 +180,13 @@ class mysqli_native_moodle_database extends moodle_database {
|
||||
return $engine;
|
||||
}
|
||||
|
||||
// get the default database engine
|
||||
$sql = "SELECT @@storage_engine";
|
||||
// Get the default database engine.
|
||||
$sql = "SELECT @@default_storage_engine engine";
|
||||
$this->query_start($sql, NULL, SQL_QUERY_AUX);
|
||||
$result = $this->mysqli->query($sql);
|
||||
$this->query_end($result);
|
||||
if ($rec = $result->fetch_assoc()) {
|
||||
$engine = $rec['@@storage_engine'];
|
||||
$engine = $rec['engine'];
|
||||
}
|
||||
$result->close();
|
||||
|
||||
|
||||
Vendored
+1
-1
@@ -333,8 +333,8 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
};
|
||||
xhr.open("POST", M.cfg.wwwroot + '/repository/repository_ajax.php?action=upload', true);
|
||||
xhr.send(formData);
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
|
||||
},
|
||||
|
||||
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
@@ -333,8 +333,8 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
};
|
||||
xhr.open("POST", M.cfg.wwwroot + '/repository/repository_ajax.php?action=upload', true);
|
||||
xhr.send(formData);
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
|
||||
},
|
||||
|
||||
|
||||
@@ -331,8 +331,8 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
};
|
||||
xhr.open("POST", M.cfg.wwwroot + '/repository/repository_ajax.php?action=upload', true);
|
||||
xhr.send(formData);
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
|
||||
},
|
||||
|
||||
|
||||
@@ -28,13 +28,13 @@ $string['all'] = 'Around each cell';
|
||||
$string['allowbackgroundcolour'] = 'Allow background colour';
|
||||
$string['allowbackgroundcolour_desc'] = 'Allow users to set the background colour for the table';
|
||||
$string['allowborder'] = 'Allow borders';
|
||||
$string['allowborder_desc'] = 'If checked, users will be able to customize borders when creating and editing a table';
|
||||
$string['allowborder_desc'] = 'If enabled, table and cell borders can be customised. The default border is a solid, 1px, black line.';
|
||||
$string['allowborderstyle'] = 'Allow border styling';
|
||||
$string['allowborderstyle_desc'] = 'Allow users to style borders on tables. If this is not checked, but allowborder is checked, the default style is solid.<br>NOTE that this setting will not have an impact unless allowborder is checked';
|
||||
$string['allowborderstyle_desc'] = 'This setting has no effect unless \'Allow borders\' is enabled.';
|
||||
$string['allowbordersize'] = 'Allow border size control';
|
||||
$string['allowbordersize_desc'] = 'Allow users to set the size of borders on tables. If this is not checked, but allowborder is checked, the default size is 1px.<br>NOTE that this setting will not have an impact unless allowborder is checked';
|
||||
$string['allowbordersize_desc'] = 'This setting has no effect unless \'Allow borders\' is enabled.';
|
||||
$string['allowbordercolour'] = 'Allow border colour';
|
||||
$string['allowbordercolour_desc'] = 'Allow users to set the colour of the borders on tables. If this is not checked, but allowborder is checked, the default colour is black.<br>NOTE that this setting will not have an impact unless allowborder is checked';
|
||||
$string['allowbordercolour_desc'] = 'This setting has no effect unless \'Allow borders\' is enabled.';
|
||||
$string['allowwidth'] = 'Allow width';
|
||||
$string['allowwidth_desc'] = 'Allow users to set the width of the tables.';
|
||||
$string['appearance'] = 'Appearance';
|
||||
@@ -45,15 +45,22 @@ $string['borders'] = 'Borders';
|
||||
$string['bordersize'] = 'Size of borders';
|
||||
$string['borderstyles'] = 'Style of borders';
|
||||
$string['borderstyles_desc'] = 'Allowed border styles, separated by commas';
|
||||
$string['borderstyles_default'] = 'solid,dashed,dotted';
|
||||
$string['caption'] = 'Caption';
|
||||
$string['captionposition'] = 'Caption position';
|
||||
$string['columns'] = 'Columns';
|
||||
$string['createtable'] = 'Create table';
|
||||
$string['deletecolumn'] = 'Delete column';
|
||||
$string['deleterow'] = 'Delete row';
|
||||
$string['dashed'] = 'Dashed';
|
||||
$string['dotted'] = 'Dotted';
|
||||
$string['double'] = 'Double';
|
||||
$string['edittable'] = 'Edit table';
|
||||
$string['groove'] = 'Groove';
|
||||
$string['headers'] = 'Define headers on';
|
||||
$string['hidden'] = 'Hidden';
|
||||
$string['inherit'] = 'Inherit';
|
||||
$string['initial'] = 'Initial';
|
||||
$string['inset'] = 'Inset';
|
||||
$string['movecolumnleft'] = 'Move column left';
|
||||
$string['movecolumnright'] = 'Move column right';
|
||||
$string['moverowdown'] = 'Move row down';
|
||||
@@ -63,10 +70,14 @@ $string['none'] = 'None';
|
||||
$string['numberofcolumns'] = 'Number of columns';
|
||||
$string['numberofrows'] = 'Number of rows';
|
||||
$string['outer'] = 'Around table';
|
||||
$string['outset'] = 'Outset';
|
||||
$string['pluginname'] = 'Table';
|
||||
$string['ridge'] = 'Ridge';
|
||||
$string['rows'] = 'Rows';
|
||||
$string['settings'] = 'Table settings';
|
||||
$string['updatetable'] = 'Update table';
|
||||
$string['width'] = 'Table width (in %)';
|
||||
$string['solid'] = 'Solid';
|
||||
$string['themedefault'] = 'Theme default';
|
||||
$string['transparent'] = 'Transparent';
|
||||
$string['unset'] = 'Unset';
|
||||
$string['updatetable'] = 'Update table';
|
||||
$string['width'] = 'Table width (in %)';
|
||||
|
||||
@@ -60,8 +60,19 @@ function atto_table_strings_for_js() {
|
||||
'width',
|
||||
'outer',
|
||||
'noborder',
|
||||
'transparent',
|
||||
'themedefault'),
|
||||
'inherit',
|
||||
'themedefault',
|
||||
'initial',
|
||||
'unset',
|
||||
'hidden',
|
||||
'dotted',
|
||||
'dashed',
|
||||
'solid',
|
||||
'double',
|
||||
'groove',
|
||||
'ridge',
|
||||
'inset',
|
||||
'outset'),
|
||||
'atto_table');
|
||||
|
||||
$PAGE->requires->strings_for_js(array('top',
|
||||
|
||||
@@ -50,16 +50,20 @@ if ($ADMIN->fulltree) {
|
||||
|
||||
$name = new lang_string('borderstyles', 'atto_table');
|
||||
$desc = new lang_string('borderstyles_desc', 'atto_table');
|
||||
$default = ['none' => 'none',
|
||||
'hidden' => 'hidden',
|
||||
'dotted' => 'dotted',
|
||||
'dashed' => 'dashed',
|
||||
'solid' => 'solid',
|
||||
'double' => 'double',
|
||||
'groove' => 'groove',
|
||||
'ridge' => 'ridge',
|
||||
'inset' => 'inset',
|
||||
'outset' => 'outset'];
|
||||
$default = array(
|
||||
'initial' => new lang_string('initial', 'atto_table'),
|
||||
'unset' => new lang_string('unset', 'atto_table'),
|
||||
'none' => new lang_string('none', 'atto_table'),
|
||||
'hidden' => new lang_string('hidden', 'atto_table'),
|
||||
'dotted' => new lang_string('dotted', 'atto_table'),
|
||||
'dashed' => new lang_string('dashed', 'atto_table'),
|
||||
'solid' => new lang_string('solid', 'atto_table'),
|
||||
'double' => new lang_string('double', 'atto_table'),
|
||||
'groove' => new lang_string('groove', 'atto_table'),
|
||||
'ridge' => new lang_string('ridge', 'atto_table'),
|
||||
'inset' => new lang_string('inset', 'atto_table'),
|
||||
'outset' => new lang_string('outset', 'atto_table'),
|
||||
);
|
||||
|
||||
$setting = new admin_setting_configmultiselect('atto_table/borderstyles',
|
||||
$name,
|
||||
|
||||
@@ -34,3 +34,6 @@ div.availablecolors label input[type="radio"] {
|
||||
margin-left: -15px;
|
||||
}
|
||||
|
||||
input[name="bordersize"], input[name="width"] {
|
||||
margin-right: 0.3em;
|
||||
}
|
||||
|
||||
@@ -35,6 +35,71 @@ Feature: Atto tables
|
||||
And I press "Save changes"
|
||||
Then ".blog_entry table caption" "css_element" should be visible
|
||||
|
||||
@javascript
|
||||
Scenario: Check table setting defaults on new table
|
||||
Given the following config values are set as admin:
|
||||
| config | value | plugin |
|
||||
| allowborders | 1 | atto_table |
|
||||
| allowborderstyles | 1 | atto_table |
|
||||
| allowbordersize | 1 | atto_table |
|
||||
| allowbordercolour | 1 | atto_table |
|
||||
| allowbackgroundcolour | 1 | atto_table |
|
||||
| allowwidth | 1 | atto_table |
|
||||
And I log in as "admin"
|
||||
And I follow "Profile" in the user menu
|
||||
And I follow "Blog entries"
|
||||
And I follow "Add a new entry"
|
||||
And I set the field "Entry title" to "How to make a table"
|
||||
And I set the field "Blog entry body" to "Table test"
|
||||
And I select the text in the "Blog entry body" Atto editor
|
||||
And I click on "Show more buttons" "button"
|
||||
When I click on "Table" "button"
|
||||
Then the ".moodle-dialogue-base .atto_form .borderstyle" "css_element" should be disabled
|
||||
And the ".moodle-dialogue-base .atto_form .bordersize" "css_element" should be disabled
|
||||
And the "borderColour" "radio" should be disabled
|
||||
And the field "borders" matches value "default"
|
||||
And the field "Theme default" matches value "1"
|
||||
And the field "Table width (in %)" matches value ""
|
||||
|
||||
@javascript
|
||||
Scenario: Edit a table restores settings
|
||||
Given the following config values are set as admin:
|
||||
| config | value | plugin |
|
||||
| allowborders | 1 | atto_table |
|
||||
| allowborderstyles | 1 | atto_table |
|
||||
| allowbordersize | 1 | atto_table |
|
||||
| allowbordercolour | 1 | atto_table |
|
||||
| allowbackgroundcolour | 1 | atto_table |
|
||||
| allowwidth | 1 | atto_table |
|
||||
And I log in as "admin"
|
||||
And I follow "Profile" in the user menu
|
||||
And I follow "Blog entries"
|
||||
And I follow "Add a new entry"
|
||||
And I set the field "Entry title" to "How to make a table"
|
||||
And I set the field "Blog entry body" to "<table><tr><td>Cell</td></tr></table>"
|
||||
And I select the text in the "Blog entry body" Atto editor
|
||||
And I click on "Show more buttons" "button"
|
||||
And I click on "Table" "button"
|
||||
And I click on "Edit table" "link"
|
||||
And I click on "#FFFFFF" "radio" in the ".moodle-dialogue-base .atto_form .backgroundcolour" "css_element"
|
||||
And I set the field "Table width (in %)" to "100"
|
||||
And I set the field "Borders" to "Around table"
|
||||
And I set the field "Style of borders" to "Dashed"
|
||||
And I set the field "Size of borders" to "2"
|
||||
And I click on "#FFFFFF" "radio" in the ".moodle-dialogue-base .atto_form .bordercolour" "css_element"
|
||||
And I press "Update table"
|
||||
And I select the text in the "Blog entry body" Atto editor
|
||||
And I click on "Table" "button"
|
||||
When I click on "Edit table" "link"
|
||||
# Check that the background colour is set correctly.
|
||||
Then the field with xpath "//label/input[@name='backgroundColour' and @value='#FFFFFF']" matches value "1"
|
||||
And the field "Table width (in %)" matches value "100"
|
||||
And the field "Borders" matches value "Around table"
|
||||
And the field "Style of borders" matches value "dashed"
|
||||
And the field "Size of borders" matches value "2"
|
||||
# Check that the border colour is set correctly.
|
||||
And the field with xpath "//label/input[@name='borderColour' and @value='#FFFFFF']" matches value "1"
|
||||
|
||||
@javascript
|
||||
Scenario: Create a table with background colour and width with border settings off
|
||||
Given the following config values are set as admin:
|
||||
@@ -67,7 +132,8 @@ Feature: Atto tables
|
||||
And I follow "Edit"
|
||||
And I click on "Show more buttons" "button"
|
||||
And I click on "HTML" "button"
|
||||
Then I should see "style=\"background-color:#FFFFFF;width:100%;\""
|
||||
Then I should see "background-color:rgb(255,255,255);"
|
||||
And I should see "width:100%;"
|
||||
|
||||
@javascript
|
||||
Scenario: Edit a table with background colour and width with border settings off
|
||||
@@ -102,7 +168,8 @@ Feature: Atto tables
|
||||
And I follow "Edit"
|
||||
And I click on "Show more buttons" "button"
|
||||
And I click on "HTML" "button"
|
||||
Then I should see "style=\"width:100%;background-color:rgb(255,255,255);\""
|
||||
Then I should see "background-color:rgb(255,255,255);"
|
||||
And I should see "width:100%;"
|
||||
|
||||
@javascript
|
||||
Scenario: Create a table with background colour and width with borders on
|
||||
@@ -137,7 +204,8 @@ Feature: Atto tables
|
||||
And I follow "Edit"
|
||||
And I click on "Show more buttons" "button"
|
||||
And I click on "HTML" "button"
|
||||
Then I should see "style=\"border:1px solid #FFFFFF;background-color:#FFFFFF;width:100%;\""
|
||||
Then I should see "background-color:rgb(255,255,255);"
|
||||
And I should see "width:100%;"
|
||||
|
||||
@javascript
|
||||
Scenario: Edit a table with background colour and width with borders on
|
||||
@@ -167,13 +235,14 @@ Feature: Atto tables
|
||||
Then ".moodle-dialogue-base .atto_form .customwidth" "css_element" should exist
|
||||
And I click on "#FFFFFF" "radio" in the ".moodle-dialogue-base .atto_form .backgroundcolour" "css_element"
|
||||
And I set the field "Table width (in %)" to "100"
|
||||
And I set the field "Borders" to "Around table"
|
||||
And I set the field "Borders" to "Around each cell"
|
||||
And I press "Update table"
|
||||
And I press "Save changes"
|
||||
And I follow "Edit"
|
||||
And I click on "Show more buttons" "button"
|
||||
And I click on "HTML" "button"
|
||||
Then I should see "style=\"border:1px solid rgb(255,255,255);width:100%;background-color:rgb(255,255,255);\""
|
||||
Then I should see "background-color:rgb(255,255,255);"
|
||||
And I should see "width:100%;"
|
||||
|
||||
@javascript
|
||||
Scenario: Create a table with background colour and width with borders and border styling on
|
||||
@@ -203,13 +272,15 @@ Feature: Atto tables
|
||||
And I click on "#FFFFFF" "radio" in the ".moodle-dialogue-base .atto_form .backgroundcolour" "css_element"
|
||||
And I set the field "Table width (in %)" to "100"
|
||||
And I set the field "Borders" to "Around table"
|
||||
And I set the field "Style of borders" to "dashed"
|
||||
And I set the field "Style of borders" to "Dashed"
|
||||
And I press "Create table"
|
||||
And I press "Save changes"
|
||||
And I follow "Edit"
|
||||
And I click on "Show more buttons" "button"
|
||||
And I click on "HTML" "button"
|
||||
Then I should see "style=\"border:1px dashed #FFFFFF;background-color:#FFFFFF;width:100%;\""
|
||||
Then I should see "background-color:rgb(255,255,255);"
|
||||
And I should see "width:100%;"
|
||||
And I should see "border-style:dashed;"
|
||||
|
||||
@javascript
|
||||
Scenario: Edit a table with background colour and width with borders and border styling on
|
||||
@@ -240,13 +311,15 @@ Feature: Atto tables
|
||||
And I click on "#FFFFFF" "radio" in the ".moodle-dialogue-base .atto_form .backgroundcolour" "css_element"
|
||||
And I set the field "Table width (in %)" to "100"
|
||||
And I set the field "Borders" to "Around table"
|
||||
And I set the field "Style of borders" to "dashed"
|
||||
And I set the field "Style of borders" to "Dashed"
|
||||
And I press "Update table"
|
||||
And I press "Save changes"
|
||||
And I follow "Edit"
|
||||
And I click on "Show more buttons" "button"
|
||||
And I click on "HTML" "button"
|
||||
Then I should see "style=\"border:1px dashed rgb(255,255,255);width:100%;background-color:rgb(255,255,255);\""
|
||||
Then I should see "background-color:rgb(255,255,255);"
|
||||
And I should see "width:100%;"
|
||||
And I should see "border-style:dashed;"
|
||||
|
||||
@javascript
|
||||
Scenario: Create a table with background colour and width with borders, border styling, and border size on
|
||||
@@ -276,14 +349,17 @@ Feature: Atto tables
|
||||
And I click on "#FFFFFF" "radio" in the ".moodle-dialogue-base .atto_form .backgroundcolour" "css_element"
|
||||
And I set the field "Table width (in %)" to "100"
|
||||
And I set the field "Borders" to "Around table"
|
||||
And I set the field "Style of borders" to "dashed"
|
||||
And I set the field "Style of borders" to "Dashed"
|
||||
And I set the field "Size of borders" to "2"
|
||||
And I press "Create table"
|
||||
And I press "Save changes"
|
||||
And I follow "Edit"
|
||||
And I click on "Show more buttons" "button"
|
||||
And I click on "HTML" "button"
|
||||
Then I should see "style=\"border:2px dashed #FFFFFF;background-color:#FFFFFF;width:100%;\""
|
||||
Then I should see "background-color:rgb(255,255,255);"
|
||||
And I should see "width:100%;"
|
||||
And I should see "border-style:dashed;"
|
||||
And I should see "border-width:2px;"
|
||||
|
||||
@javascript
|
||||
Scenario: Edit a table with background colour and width with borders, border styling, and border size on
|
||||
@@ -314,14 +390,17 @@ Feature: Atto tables
|
||||
And I click on "#FFFFFF" "radio" in the ".moodle-dialogue-base .atto_form .backgroundcolour" "css_element"
|
||||
And I set the field "Table width (in %)" to "100"
|
||||
And I set the field "Borders" to "Around table"
|
||||
And I set the field "Style of borders" to "dashed"
|
||||
And I set the field "Style of borders" to "Dashed"
|
||||
And I set the field "Size of borders" to "2"
|
||||
And I press "Update table"
|
||||
And I press "Save changes"
|
||||
And I follow "Edit"
|
||||
And I click on "Show more buttons" "button"
|
||||
And I click on "HTML" "button"
|
||||
Then I should see "style=\"border:2px dashed rgb(255,255,255);width:100%;background-color:rgb(255,255,255);\""
|
||||
Then I should see "background-color:rgb(255,255,255);"
|
||||
And I should see "width:100%;"
|
||||
And I should see "border-style:dashed;"
|
||||
And I should see "border-width:2px;"
|
||||
|
||||
@javascript
|
||||
Scenario: Create a table with all settings on
|
||||
@@ -351,7 +430,7 @@ Feature: Atto tables
|
||||
And I click on "#FFFFFF" "radio" in the ".moodle-dialogue-base .atto_form .backgroundcolour" "css_element"
|
||||
And I set the field "Table width (in %)" to "100"
|
||||
And I set the field "Borders" to "Around table"
|
||||
And I set the field "Style of borders" to "dashed"
|
||||
And I set the field "Style of borders" to "Dashed"
|
||||
And I set the field "Size of borders" to "2"
|
||||
And I click on "#FFFFFF" "radio" in the ".moodle-dialogue-base .atto_form .bordercolour" "css_element"
|
||||
And I press "Create table"
|
||||
@@ -359,7 +438,11 @@ Feature: Atto tables
|
||||
And I follow "Edit"
|
||||
And I click on "Show more buttons" "button"
|
||||
And I click on "HTML" "button"
|
||||
Then I should see "style=\"border:2px dashed #FFFFFF;background-color:#FFFFFF;width:100%;\""
|
||||
Then I should see "background-color:rgb(255,255,255);"
|
||||
And I should see "width:100%;"
|
||||
And I should see "dashed"
|
||||
And I should see "2px"
|
||||
And I should see "rgb(255,255,255)"
|
||||
|
||||
@javascript
|
||||
Scenario: Edit a table with background colour and width with borders, border styling, and border size on
|
||||
@@ -387,15 +470,19 @@ Feature: Atto tables
|
||||
Then ".moodle-dialogue-base .atto_form .bordercolour" "css_element" should exist
|
||||
Then ".moodle-dialogue-base .atto_form .backgroundcolour" "css_element" should exist
|
||||
Then ".moodle-dialogue-base .atto_form .customwidth" "css_element" should exist
|
||||
And I click on "#FFFFFF" "radio" in the ".moodle-dialogue-base .atto_form .backgroundcolour" "css_element"
|
||||
And I click on "FFFFFF" "radio" in the ".moodle-dialogue-base .atto_form .backgroundcolour" "css_element"
|
||||
And I set the field "Table width (in %)" to "100"
|
||||
And I set the field "Borders" to "Around table"
|
||||
And I set the field "Style of borders" to "dashed"
|
||||
And I set the field "Style of borders" to "Dashed"
|
||||
And I set the field "Size of borders" to "2"
|
||||
And I click on "#FFFFFF" "radio" in the ".moodle-dialogue-base .atto_form .bordercolour" "css_element"
|
||||
And I click on "FFFFFF" "radio" in the ".moodle-dialogue-base .atto_form .bordercolour" "css_element"
|
||||
And I press "Update table"
|
||||
And I press "Save changes"
|
||||
And I follow "Edit"
|
||||
And I click on "Show more buttons" "button"
|
||||
And I click on "HTML" "button"
|
||||
Then I should see "style=\"border:2px dashed rgb(255,255,255);width:100%;background-color:rgb(255,255,255);\""
|
||||
Then I should see "background-color:rgb(255,255,255);"
|
||||
And I should see "width:100%;"
|
||||
And I should see "dashed"
|
||||
And I should see "2px"
|
||||
And I should see "rgb(255,255,255)"
|
||||
|
||||
Vendored
+158
-123
@@ -35,11 +35,11 @@ YUI.add('moodle-atto_table-button', function (Y, NAME) {
|
||||
|
||||
var COMPONENT = 'atto_table',
|
||||
DEFAULT = {
|
||||
BORDERSTYLE: 'solid',
|
||||
BORDERSTYLE: 'inherit',
|
||||
BORDERWIDTH: '1'
|
||||
},
|
||||
DIALOGUE = {
|
||||
WIDTH: '450px'
|
||||
WIDTH: '480px'
|
||||
},
|
||||
TEMPLATE = '' +
|
||||
'<form class="{{CSS.FORM}}">' +
|
||||
@@ -78,9 +78,8 @@ var COMPONENT = 'atto_table',
|
||||
'<legend class="mdl-align">{{get_string "appearance" component}}</legend>' +
|
||||
'{{#if allowBorders}}' +
|
||||
'<label for="{{elementid}}_atto_table_borders" class="sameline">{{get_string "borders" component}}</label>' +
|
||||
'<select class="{{CSS.BORDERS}}" id="{{elementid}}_atto_table_borders">' +
|
||||
'<select name="borders" class="{{CSS.BORDERS}}" id="{{elementid}}_atto_table_borders">' +
|
||||
'<option value="default">{{get_string "themedefault" component}}' + '</option>' +
|
||||
'<option value="none">{{get_string "noborder" component}}' + '</option>' +
|
||||
'<option value="outer">{{get_string "outer" component}}' + '</option>' +
|
||||
'<option value="all">{{get_string "all" component}}' + '</option>' +
|
||||
'</select>' +
|
||||
@@ -88,9 +87,10 @@ var COMPONENT = 'atto_table',
|
||||
'{{#if allowBorderStyles}}' +
|
||||
'<label for="{{elementid}}_atto_table_borderstyle" class="sameline">' +
|
||||
'{{get_string "borderstyles" component}}</label>' +
|
||||
'<select class="{{CSS.BORDERSTYLE}}" id="{{elementid}}_atto_table_borderstyle">' +
|
||||
'<select name="borderstyles" class="{{CSS.BORDERSTYLE}}" id="{{elementid}}_atto_table_borderstyle">' +
|
||||
'<option value="inherit">{{get_string "inherit" component}}</option>' +
|
||||
'{{#each borderStyles}}' +
|
||||
'<option value="' + '{{this}}' + '">' + '{{this}}' + '</option>' +
|
||||
'<option value="' + '{{this}}' + '">' + '{{get_string this ../component}}' + '</option>' +
|
||||
'{{/each}}' +
|
||||
'</select>' +
|
||||
'<br>' +
|
||||
@@ -98,7 +98,7 @@ var COMPONENT = 'atto_table',
|
||||
'{{#if allowBorderSize}}' +
|
||||
'<label for="{{elementid}}_atto_table_bordersize" class="sameline">' +
|
||||
'{{get_string "bordersize" component}}</label>' +
|
||||
'<input id="{{elementid}}_atto_table_bordersize" class="{{CSS.BORDERSIZE}}"' +
|
||||
'<input name="bordersize" id="{{elementid}}_atto_table_bordersize" class="{{CSS.BORDERSIZE}}"' +
|
||||
'type="number" value="1" size="8" min="1" max="50"/>' +
|
||||
'<label style="display: inline-block;">{{CSS.BORDERSIZEUNIT}}</label>' +
|
||||
'<br>' +
|
||||
@@ -112,10 +112,10 @@ var COMPONENT = 'atto_table',
|
||||
'style="background-color:transparent;color:transparent">' +
|
||||
|
||||
'<input id="{{../elementid}}_atto_table_bordercolour_-1"' +
|
||||
'type="radio" name="borderColour" value="transparent" checked="checked"'+
|
||||
'title="{{get_string "transparent" component}}"></input>' +
|
||||
'type="radio" name="borderColour" value="none" checked="checked"'+
|
||||
'title="{{get_string "themedefault" component}}"></input>' +
|
||||
|
||||
'{{get_string "transparent" component}}' +
|
||||
'{{get_string "themedefault" component}}' +
|
||||
'</label>' +
|
||||
'{{#each availableColours}}' +
|
||||
'<label for="{{../elementid}}_atto_table_bordercolour_{{@index}}"' +
|
||||
@@ -140,10 +140,10 @@ var COMPONENT = 'atto_table',
|
||||
'style="background-color:transparent;color:transparent">' +
|
||||
|
||||
'<input id="{{../elementid}}_atto_table_backgroundcolour_-1"' +
|
||||
'type="radio" name="backgroundColour" value="transparent" checked="checked"'+
|
||||
'title="{{get_string "transparent" component}}"></input>' +
|
||||
'type="radio" name="backgroundColour" value="none" checked="checked"'+
|
||||
'title="{{get_string "themedefault" component}}"></input>' +
|
||||
|
||||
'{{get_string "transparent" component}}' +
|
||||
'{{get_string "themedefault" component}}' +
|
||||
'</label>' +
|
||||
|
||||
'{{#each availableColours}}' +
|
||||
@@ -162,7 +162,7 @@ var COMPONENT = 'atto_table',
|
||||
'{{#if allowWidth}}' +
|
||||
'<label for="{{elementid}}_atto_table_width" class="sameline">' +
|
||||
'{{get_string "width" component}}</label>' +
|
||||
'<input id="{{elementid}}_atto_table_width" class="{{CSS.WIDTH}}" size="8" type="number" min="0" max="100"/>' +
|
||||
'<input name="width" id="{{elementid}}_atto_table_width" class="{{CSS.WIDTH}}" size="8" type="number" min="0" max="100"/>' +
|
||||
'<label style="display: inline-block;">{{CSS.WIDTHUNIT}}</label>' +
|
||||
'<br>' +
|
||||
'{{/if}}' +
|
||||
@@ -289,6 +289,8 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
// Set the dialogue content, and then show the dialogue.
|
||||
dialogue.set('bodyContent', this._getDialogueContent(false))
|
||||
.show();
|
||||
|
||||
this._updateAvailableSettings();
|
||||
}
|
||||
},
|
||||
|
||||
@@ -334,7 +336,7 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
*/
|
||||
_getDialogueContent: function(edit) {
|
||||
var template = Y.Handlebars.compile(TEMPLATE);
|
||||
var availableColours = this.get('availableColors');
|
||||
var allowBorders = this.get('allowBorders');
|
||||
|
||||
this._content = Y.Node.create(template({
|
||||
CSS: CSS,
|
||||
@@ -343,13 +345,13 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
edit: edit,
|
||||
nonedit: !edit,
|
||||
allowStyling: this.get('allowStyling'),
|
||||
allowBorders: this.get('allowBorders'),
|
||||
allowBorders: allowBorders,
|
||||
allowBorderStyles: this.get('allowBorderStyles'),
|
||||
borderStyles: this.get('borderStyles'),
|
||||
allowBorderSize: this.get('allowBorderSize'),
|
||||
allowBorderColour: this.get('allowBorderColour'),
|
||||
allowBackgroundColour: this.get('allowBackgroundColour'),
|
||||
availableColours: availableColours,
|
||||
availableColours: this.get('availableColors'),
|
||||
allowWidth: this.get('allowWidth')
|
||||
}));
|
||||
|
||||
@@ -360,9 +362,48 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
this._content.one('.submit').on('click', this._setTable, this);
|
||||
}
|
||||
|
||||
if (allowBorders) {
|
||||
this._content.one('[name="borders"]').on('change', this._updateAvailableSettings, this);
|
||||
}
|
||||
|
||||
return this._content;
|
||||
},
|
||||
|
||||
/**
|
||||
* Disables options within the dialogue if they shouldn't be available.
|
||||
* E.g.
|
||||
* If borders are set to "Theme default" then the border size, style and
|
||||
* colour options are disabled.
|
||||
*
|
||||
* @method _updateAvailableSettings
|
||||
* @private
|
||||
*/
|
||||
_updateAvailableSettings: function() {
|
||||
var tableForm = this._content,
|
||||
enableBorders = tableForm.one('[name="borders"]'),
|
||||
borderStyle = tableForm.one('[name="borderstyles"]'),
|
||||
borderSize = tableForm.one('[name="bordersize"]'),
|
||||
borderColour = tableForm.all('[name="borderColour"]'),
|
||||
disabledValue = 'removeAttribute';
|
||||
|
||||
if (enableBorders.get('value') === 'default') {
|
||||
disabledValue = 'setAttribute';
|
||||
}
|
||||
|
||||
if (borderStyle) {
|
||||
borderStyle[disabledValue]('disabled');
|
||||
}
|
||||
|
||||
if (borderSize) {
|
||||
borderSize[disabledValue]('disabled');
|
||||
}
|
||||
|
||||
if (borderColour) {
|
||||
borderColour[disabledValue]('disabled');
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Given the current selection, return a table cell suitable for table editing
|
||||
* purposes, i.e. the first table cell selected, or the first cell in the table
|
||||
@@ -436,10 +477,6 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
borderstyle,
|
||||
bordercolour,
|
||||
backgroundcolour,
|
||||
backgroundcolourvalue = '#FFFFFF',
|
||||
borderSizeValue = '1',
|
||||
borderStyleValue = 'solid',
|
||||
borderhex = '#FFFFFF',
|
||||
table,
|
||||
width,
|
||||
captionnode;
|
||||
@@ -461,51 +498,16 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
backgroundcolour = e.currentTarget.ancestor(SELECTORS.FORM).one(SELECTORS.SELECTEDBACKGROUNDCOLOUR);
|
||||
width = e.currentTarget.ancestor(SELECTORS.FORM).one(SELECTORS.WIDTH);
|
||||
|
||||
if (bordercolour) {
|
||||
borderhex = bordercolour.get('value');
|
||||
}
|
||||
|
||||
if (bordersize) {
|
||||
borderSizeValue = bordersize.get('value');
|
||||
}
|
||||
|
||||
if (borderstyle) {
|
||||
borderStyleValue = borderstyle.get('value');
|
||||
}
|
||||
|
||||
if (backgroundcolour) {
|
||||
backgroundcolourvalue = backgroundcolour.get('value');
|
||||
}
|
||||
|
||||
table = this._lastTarget.ancestor('table');
|
||||
// Clear the inline border styling
|
||||
table.removeAttribute('style');
|
||||
table.all('td, th').each(function() {
|
||||
this.removeAttribute('style');
|
||||
this._setAppearance(table, {
|
||||
width: width,
|
||||
borders: borders,
|
||||
borderColour: bordercolour,
|
||||
borderSize: bordersize,
|
||||
borderStyle: borderstyle,
|
||||
backgroundColour: backgroundcolour
|
||||
});
|
||||
|
||||
if (borders) {
|
||||
if (borders.get('value') === 'outer') {
|
||||
table.setStyle('border', borderSizeValue + CSS.BORDERSIZEUNIT + ' ' +
|
||||
borderStyleValue + ' ' + borderhex);
|
||||
} else if (borders.get('value') === 'all') {
|
||||
table.all('td, th').each(function() {
|
||||
this.setStyle('border', borderSizeValue + CSS.BORDERSIZEUNIT + ' ' +
|
||||
borderStyleValue + ' ' + borderhex);
|
||||
});
|
||||
} else if (borders.get('value') === 'none') {
|
||||
table.setStyle('border', 'none');
|
||||
}
|
||||
}
|
||||
|
||||
if (width && width.get('value')) {
|
||||
table.setStyle('width', width.get('value') + CSS.WIDTHUNIT);
|
||||
}
|
||||
|
||||
if (backgroundcolourvalue !== '') {
|
||||
table.setStyle('background-color', backgroundcolourvalue);
|
||||
}
|
||||
|
||||
captionnode = table.one('caption');
|
||||
if (!captionnode) {
|
||||
captionnode = Y.Node.create('<caption></caption>');
|
||||
@@ -589,14 +591,9 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
var caption,
|
||||
captionposition,
|
||||
borders,
|
||||
bordertable = '',
|
||||
bordercell = '',
|
||||
bordersize,
|
||||
borderstyle,
|
||||
bordercolour,
|
||||
borderSizeValue = '1',
|
||||
borderStyleValue = 'solid',
|
||||
borderhex = '#FFFFFF',
|
||||
rows,
|
||||
cols,
|
||||
headers,
|
||||
@@ -624,52 +621,13 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
headers = e.currentTarget.ancestor(SELECTORS.FORM).one(SELECTORS.HEADERS);
|
||||
width = e.currentTarget.ancestor(SELECTORS.FORM).one(SELECTORS.WIDTH);
|
||||
|
||||
if (bordercolour) {
|
||||
borderhex = bordercolour.get('value');
|
||||
}
|
||||
|
||||
if (bordersize) {
|
||||
borderSizeValue = bordersize.get('value');
|
||||
}
|
||||
|
||||
if (borderstyle) {
|
||||
borderStyleValue = borderstyle.get('value');
|
||||
}
|
||||
|
||||
if (borders) {
|
||||
if (borders.get('value') === 'outer') {
|
||||
bordertable = ' style="border: ' + borderSizeValue + CSS.BORDERSIZEUNIT + ' ' +
|
||||
borderStyleValue + ' ' + borderhex + ';"';
|
||||
} else if (borders.get('value') === 'all') {
|
||||
bordercell = ' style="border: ' + borderSizeValue + CSS.BORDERSIZEUNIT + ' ' +
|
||||
borderStyleValue + ' ' + borderhex + ';"';
|
||||
}
|
||||
}
|
||||
|
||||
if (backgroundcolour) {
|
||||
if (bordertable !== '') {
|
||||
bordertable = bordertable.substring(0, bordertable.length-1) +
|
||||
'background-color: ' + backgroundcolour.get('value') + ';"';
|
||||
} else {
|
||||
bordertable = ' style="background-color: ' + backgroundcolour.get('value') + ';"';
|
||||
}
|
||||
}
|
||||
|
||||
if (width && width.get('value')) {
|
||||
if (bordertable !== '') {
|
||||
bordertable = bordertable.substring(0, bordertable.length-1) + 'width: ' +
|
||||
width.get('value') + CSS.WIDTHUNIT + ';"';
|
||||
} else {
|
||||
bordertable = ' style="width: ' + width.get('value') + CSS.WIDTHUNIT + ';"';
|
||||
}
|
||||
}
|
||||
|
||||
// Set the selection.
|
||||
this.get('host').setSelection(this._currentSelection);
|
||||
|
||||
// Note there are some spaces inserted in the cells and before and after, so that users have somewhere to click.
|
||||
var nl = "\n";
|
||||
tablehtml = '<br/>' + nl + '<table' + bordertable + '>' + nl;
|
||||
var tableId = Y.guid();
|
||||
tablehtml = '<br/>' + nl + '<table id="' + tableId + '">' + nl;
|
||||
|
||||
var captionstyle = '';
|
||||
if (captionposition.get('value')) {
|
||||
@@ -681,7 +639,7 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
i = 1;
|
||||
tablehtml += '<thead>' + nl + '<tr>' + nl;
|
||||
for (j = 0; j < parseInt(cols.get('value'), 10); j++) {
|
||||
tablehtml += '<th scope="col"' + bordercell + '></th>' + nl;
|
||||
tablehtml += '<th scope="col"></th>' + nl;
|
||||
}
|
||||
tablehtml += '</tr>' + nl + '</thead>' + nl;
|
||||
}
|
||||
@@ -690,9 +648,9 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
tablehtml += '<tr>' + nl;
|
||||
for (j = 0; j < parseInt(cols.get('value'), 10); j++) {
|
||||
if (j === 0 && (headers.get('value') === 'rows' || headers.get('value') === 'both')) {
|
||||
tablehtml += '<th scope="row"' + bordercell + '></th>' + nl;
|
||||
tablehtml += '<th scope="row"></th>' + nl;
|
||||
} else {
|
||||
tablehtml += '<td ' + bordercell + '></td>' + nl;
|
||||
tablehtml += '<td ></td>' + nl;
|
||||
}
|
||||
}
|
||||
tablehtml += '</tr>' + nl;
|
||||
@@ -702,6 +660,17 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
|
||||
this.get('host').insertContentAtFocusPoint(tablehtml);
|
||||
|
||||
var tableNode = Y.one('#' + tableId);
|
||||
this._setAppearance(tableNode, {
|
||||
width: width,
|
||||
borders: borders,
|
||||
borderColour: bordercolour,
|
||||
borderSize: bordersize,
|
||||
borderStyle: borderstyle,
|
||||
backgroundColour: backgroundcolour
|
||||
});
|
||||
tableNode.removeAttribute('id');
|
||||
|
||||
// Mark the content as updated.
|
||||
this.markUpdated();
|
||||
},
|
||||
@@ -1148,6 +1117,73 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the appropriate styles on the given table node according to
|
||||
* the provided configuration.
|
||||
*
|
||||
* @method _setAppearance
|
||||
* @param {Node} The table node to be modified.
|
||||
* @param {Object} Configuration object (associative array) containing the form nodes for
|
||||
* border styling.
|
||||
* @private
|
||||
*/
|
||||
_setAppearance: function(tableNode, configuration) {
|
||||
var borderhex,
|
||||
borderSizeValue,
|
||||
borderStyleValue,
|
||||
backgroundcolourvalue;
|
||||
|
||||
if (configuration.borderColour) {
|
||||
borderhex = configuration.borderColour.get('value');
|
||||
}
|
||||
|
||||
if (configuration.borderSize) {
|
||||
borderSizeValue = configuration.borderSize.get('value');
|
||||
}
|
||||
|
||||
if (configuration.borderStyle) {
|
||||
borderStyleValue = configuration.borderStyle.get('value');
|
||||
}
|
||||
|
||||
if (configuration.backgroundColour) {
|
||||
backgroundcolourvalue = configuration.backgroundColour.get('value');
|
||||
}
|
||||
|
||||
// Clear the inline border styling
|
||||
tableNode.removeAttribute('style');
|
||||
tableNode.all('td, th').each(function(cell) {
|
||||
cell.removeAttribute('style');
|
||||
}, this);
|
||||
|
||||
if (configuration.borders) {
|
||||
if (configuration.borders.get('value') === 'outer') {
|
||||
tableNode.setStyle('borderWidth', borderSizeValue + CSS.BORDERSIZEUNIT);
|
||||
tableNode.setStyle('borderStyle', borderStyleValue);
|
||||
|
||||
if (borderhex !== 'none') {
|
||||
tableNode.setStyle('borderColor', borderhex);
|
||||
}
|
||||
} else if (configuration.borders.get('value') === 'all') {
|
||||
tableNode.all('td, th').each(function(cell) {
|
||||
cell.setStyle('borderWidth', borderSizeValue + CSS.BORDERSIZEUNIT);
|
||||
cell.setStyle('borderStyle', borderStyleValue);
|
||||
|
||||
if (borderhex !== 'none') {
|
||||
cell.setStyle('borderColor', borderhex);
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
}
|
||||
|
||||
if (backgroundcolourvalue !== 'none') {
|
||||
tableNode.setStyle('backgroundColor', backgroundcolourvalue);
|
||||
}
|
||||
|
||||
if (configuration.width && configuration.width.get('value')) {
|
||||
tableNode.setStyle('width', configuration.width.get('value') + CSS.WIDTHUNIT);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Edit table (show the dialogue).
|
||||
*
|
||||
@@ -1174,7 +1210,9 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
backgroundcolours = node.all(SELECTORS.BACKGROUNDCOLOURS),
|
||||
width = node.one(SELECTORS.WIDTH),
|
||||
table = this._lastTarget.ancestor('table'),
|
||||
captionnode = table.one('caption');
|
||||
captionnode = table.one('caption'),
|
||||
hexColour,
|
||||
matchedInput;
|
||||
|
||||
if (captionnode) {
|
||||
captioninput.set('value', captionnode.getHTML());
|
||||
@@ -1193,9 +1231,9 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
captionpositioninput.set('value', '');
|
||||
}
|
||||
|
||||
if (table.getStyle('background-color') !== 'transparent' && this.get('allowBackgroundColour')) {
|
||||
var hexColour = Y.Color.toHex(table.getStyle('background-color'));
|
||||
var matchedInput = backgroundcolours.filter('[value="' + hexColour + '"]');
|
||||
if (table.getStyle('backgroundColor') && this.get('allowBackgroundColour')) {
|
||||
hexColour = Y.Color.toHex(table.getStyle('backgroundColor'));
|
||||
matchedInput = backgroundcolours.filter('[value="' + hexColour + '"]');
|
||||
|
||||
if (matchedInput) {
|
||||
matchedInput.set("checked", true);
|
||||
@@ -1207,11 +1245,7 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
borderConfiguration = this._getBorderConfiguration(table);
|
||||
|
||||
if (borderConfiguration) {
|
||||
if (borderConfiguration.borderStyle && borderConfiguration.borderStyle === 'none') {
|
||||
borderValue = 'none';
|
||||
} else {
|
||||
borderValue = 'outer';
|
||||
}
|
||||
borderValue = 'outer';
|
||||
} else {
|
||||
borderConfiguration = this._getBorderConfiguration(table.one('td'));
|
||||
if (borderConfiguration) {
|
||||
@@ -1226,8 +1260,8 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
bordersize.set('value', borderSize);
|
||||
borderinput.set('value', borderValue);
|
||||
|
||||
var hexColour = borderConfiguration.borderColor;
|
||||
var matchedInput = bordercolours.filter('[value="' + hexColour + '"]');
|
||||
hexColour = borderConfiguration.borderColor;
|
||||
matchedInput = bordercolours.filter('[value="' + hexColour + '"]');
|
||||
|
||||
if (matchedInput) {
|
||||
matchedInput.set("checked", true);
|
||||
@@ -1244,6 +1278,7 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
}
|
||||
headersinput.set('value', headersvalue);
|
||||
dialogue.set('bodyContent', node).show();
|
||||
this._updateAvailableSettings();
|
||||
},
|
||||
|
||||
|
||||
|
||||
Vendored
+4
-4
File diff suppressed because one or more lines are too long
Vendored
+158
-123
@@ -35,11 +35,11 @@ YUI.add('moodle-atto_table-button', function (Y, NAME) {
|
||||
|
||||
var COMPONENT = 'atto_table',
|
||||
DEFAULT = {
|
||||
BORDERSTYLE: 'solid',
|
||||
BORDERSTYLE: 'inherit',
|
||||
BORDERWIDTH: '1'
|
||||
},
|
||||
DIALOGUE = {
|
||||
WIDTH: '450px'
|
||||
WIDTH: '480px'
|
||||
},
|
||||
TEMPLATE = '' +
|
||||
'<form class="{{CSS.FORM}}">' +
|
||||
@@ -78,9 +78,8 @@ var COMPONENT = 'atto_table',
|
||||
'<legend class="mdl-align">{{get_string "appearance" component}}</legend>' +
|
||||
'{{#if allowBorders}}' +
|
||||
'<label for="{{elementid}}_atto_table_borders" class="sameline">{{get_string "borders" component}}</label>' +
|
||||
'<select class="{{CSS.BORDERS}}" id="{{elementid}}_atto_table_borders">' +
|
||||
'<select name="borders" class="{{CSS.BORDERS}}" id="{{elementid}}_atto_table_borders">' +
|
||||
'<option value="default">{{get_string "themedefault" component}}' + '</option>' +
|
||||
'<option value="none">{{get_string "noborder" component}}' + '</option>' +
|
||||
'<option value="outer">{{get_string "outer" component}}' + '</option>' +
|
||||
'<option value="all">{{get_string "all" component}}' + '</option>' +
|
||||
'</select>' +
|
||||
@@ -88,9 +87,10 @@ var COMPONENT = 'atto_table',
|
||||
'{{#if allowBorderStyles}}' +
|
||||
'<label for="{{elementid}}_atto_table_borderstyle" class="sameline">' +
|
||||
'{{get_string "borderstyles" component}}</label>' +
|
||||
'<select class="{{CSS.BORDERSTYLE}}" id="{{elementid}}_atto_table_borderstyle">' +
|
||||
'<select name="borderstyles" class="{{CSS.BORDERSTYLE}}" id="{{elementid}}_atto_table_borderstyle">' +
|
||||
'<option value="inherit">{{get_string "inherit" component}}</option>' +
|
||||
'{{#each borderStyles}}' +
|
||||
'<option value="' + '{{this}}' + '">' + '{{this}}' + '</option>' +
|
||||
'<option value="' + '{{this}}' + '">' + '{{get_string this ../component}}' + '</option>' +
|
||||
'{{/each}}' +
|
||||
'</select>' +
|
||||
'<br>' +
|
||||
@@ -98,7 +98,7 @@ var COMPONENT = 'atto_table',
|
||||
'{{#if allowBorderSize}}' +
|
||||
'<label for="{{elementid}}_atto_table_bordersize" class="sameline">' +
|
||||
'{{get_string "bordersize" component}}</label>' +
|
||||
'<input id="{{elementid}}_atto_table_bordersize" class="{{CSS.BORDERSIZE}}"' +
|
||||
'<input name="bordersize" id="{{elementid}}_atto_table_bordersize" class="{{CSS.BORDERSIZE}}"' +
|
||||
'type="number" value="1" size="8" min="1" max="50"/>' +
|
||||
'<label style="display: inline-block;">{{CSS.BORDERSIZEUNIT}}</label>' +
|
||||
'<br>' +
|
||||
@@ -112,10 +112,10 @@ var COMPONENT = 'atto_table',
|
||||
'style="background-color:transparent;color:transparent">' +
|
||||
|
||||
'<input id="{{../elementid}}_atto_table_bordercolour_-1"' +
|
||||
'type="radio" name="borderColour" value="transparent" checked="checked"'+
|
||||
'title="{{get_string "transparent" component}}"></input>' +
|
||||
'type="radio" name="borderColour" value="none" checked="checked"'+
|
||||
'title="{{get_string "themedefault" component}}"></input>' +
|
||||
|
||||
'{{get_string "transparent" component}}' +
|
||||
'{{get_string "themedefault" component}}' +
|
||||
'</label>' +
|
||||
'{{#each availableColours}}' +
|
||||
'<label for="{{../elementid}}_atto_table_bordercolour_{{@index}}"' +
|
||||
@@ -140,10 +140,10 @@ var COMPONENT = 'atto_table',
|
||||
'style="background-color:transparent;color:transparent">' +
|
||||
|
||||
'<input id="{{../elementid}}_atto_table_backgroundcolour_-1"' +
|
||||
'type="radio" name="backgroundColour" value="transparent" checked="checked"'+
|
||||
'title="{{get_string "transparent" component}}"></input>' +
|
||||
'type="radio" name="backgroundColour" value="none" checked="checked"'+
|
||||
'title="{{get_string "themedefault" component}}"></input>' +
|
||||
|
||||
'{{get_string "transparent" component}}' +
|
||||
'{{get_string "themedefault" component}}' +
|
||||
'</label>' +
|
||||
|
||||
'{{#each availableColours}}' +
|
||||
@@ -162,7 +162,7 @@ var COMPONENT = 'atto_table',
|
||||
'{{#if allowWidth}}' +
|
||||
'<label for="{{elementid}}_atto_table_width" class="sameline">' +
|
||||
'{{get_string "width" component}}</label>' +
|
||||
'<input id="{{elementid}}_atto_table_width" class="{{CSS.WIDTH}}" size="8" type="number" min="0" max="100"/>' +
|
||||
'<input name="width" id="{{elementid}}_atto_table_width" class="{{CSS.WIDTH}}" size="8" type="number" min="0" max="100"/>' +
|
||||
'<label style="display: inline-block;">{{CSS.WIDTHUNIT}}</label>' +
|
||||
'<br>' +
|
||||
'{{/if}}' +
|
||||
@@ -289,6 +289,8 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
// Set the dialogue content, and then show the dialogue.
|
||||
dialogue.set('bodyContent', this._getDialogueContent(false))
|
||||
.show();
|
||||
|
||||
this._updateAvailableSettings();
|
||||
}
|
||||
},
|
||||
|
||||
@@ -334,7 +336,7 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
*/
|
||||
_getDialogueContent: function(edit) {
|
||||
var template = Y.Handlebars.compile(TEMPLATE);
|
||||
var availableColours = this.get('availableColors');
|
||||
var allowBorders = this.get('allowBorders');
|
||||
|
||||
this._content = Y.Node.create(template({
|
||||
CSS: CSS,
|
||||
@@ -343,13 +345,13 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
edit: edit,
|
||||
nonedit: !edit,
|
||||
allowStyling: this.get('allowStyling'),
|
||||
allowBorders: this.get('allowBorders'),
|
||||
allowBorders: allowBorders,
|
||||
allowBorderStyles: this.get('allowBorderStyles'),
|
||||
borderStyles: this.get('borderStyles'),
|
||||
allowBorderSize: this.get('allowBorderSize'),
|
||||
allowBorderColour: this.get('allowBorderColour'),
|
||||
allowBackgroundColour: this.get('allowBackgroundColour'),
|
||||
availableColours: availableColours,
|
||||
availableColours: this.get('availableColors'),
|
||||
allowWidth: this.get('allowWidth')
|
||||
}));
|
||||
|
||||
@@ -360,9 +362,48 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
this._content.one('.submit').on('click', this._setTable, this);
|
||||
}
|
||||
|
||||
if (allowBorders) {
|
||||
this._content.one('[name="borders"]').on('change', this._updateAvailableSettings, this);
|
||||
}
|
||||
|
||||
return this._content;
|
||||
},
|
||||
|
||||
/**
|
||||
* Disables options within the dialogue if they shouldn't be available.
|
||||
* E.g.
|
||||
* If borders are set to "Theme default" then the border size, style and
|
||||
* colour options are disabled.
|
||||
*
|
||||
* @method _updateAvailableSettings
|
||||
* @private
|
||||
*/
|
||||
_updateAvailableSettings: function() {
|
||||
var tableForm = this._content,
|
||||
enableBorders = tableForm.one('[name="borders"]'),
|
||||
borderStyle = tableForm.one('[name="borderstyles"]'),
|
||||
borderSize = tableForm.one('[name="bordersize"]'),
|
||||
borderColour = tableForm.all('[name="borderColour"]'),
|
||||
disabledValue = 'removeAttribute';
|
||||
|
||||
if (enableBorders.get('value') === 'default') {
|
||||
disabledValue = 'setAttribute';
|
||||
}
|
||||
|
||||
if (borderStyle) {
|
||||
borderStyle[disabledValue]('disabled');
|
||||
}
|
||||
|
||||
if (borderSize) {
|
||||
borderSize[disabledValue]('disabled');
|
||||
}
|
||||
|
||||
if (borderColour) {
|
||||
borderColour[disabledValue]('disabled');
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Given the current selection, return a table cell suitable for table editing
|
||||
* purposes, i.e. the first table cell selected, or the first cell in the table
|
||||
@@ -436,10 +477,6 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
borderstyle,
|
||||
bordercolour,
|
||||
backgroundcolour,
|
||||
backgroundcolourvalue = '#FFFFFF',
|
||||
borderSizeValue = '1',
|
||||
borderStyleValue = 'solid',
|
||||
borderhex = '#FFFFFF',
|
||||
table,
|
||||
width,
|
||||
captionnode;
|
||||
@@ -461,51 +498,16 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
backgroundcolour = e.currentTarget.ancestor(SELECTORS.FORM).one(SELECTORS.SELECTEDBACKGROUNDCOLOUR);
|
||||
width = e.currentTarget.ancestor(SELECTORS.FORM).one(SELECTORS.WIDTH);
|
||||
|
||||
if (bordercolour) {
|
||||
borderhex = bordercolour.get('value');
|
||||
}
|
||||
|
||||
if (bordersize) {
|
||||
borderSizeValue = bordersize.get('value');
|
||||
}
|
||||
|
||||
if (borderstyle) {
|
||||
borderStyleValue = borderstyle.get('value');
|
||||
}
|
||||
|
||||
if (backgroundcolour) {
|
||||
backgroundcolourvalue = backgroundcolour.get('value');
|
||||
}
|
||||
|
||||
table = this._lastTarget.ancestor('table');
|
||||
// Clear the inline border styling
|
||||
table.removeAttribute('style');
|
||||
table.all('td, th').each(function() {
|
||||
this.removeAttribute('style');
|
||||
this._setAppearance(table, {
|
||||
width: width,
|
||||
borders: borders,
|
||||
borderColour: bordercolour,
|
||||
borderSize: bordersize,
|
||||
borderStyle: borderstyle,
|
||||
backgroundColour: backgroundcolour
|
||||
});
|
||||
|
||||
if (borders) {
|
||||
if (borders.get('value') === 'outer') {
|
||||
table.setStyle('border', borderSizeValue + CSS.BORDERSIZEUNIT + ' ' +
|
||||
borderStyleValue + ' ' + borderhex);
|
||||
} else if (borders.get('value') === 'all') {
|
||||
table.all('td, th').each(function() {
|
||||
this.setStyle('border', borderSizeValue + CSS.BORDERSIZEUNIT + ' ' +
|
||||
borderStyleValue + ' ' + borderhex);
|
||||
});
|
||||
} else if (borders.get('value') === 'none') {
|
||||
table.setStyle('border', 'none');
|
||||
}
|
||||
}
|
||||
|
||||
if (width && width.get('value')) {
|
||||
table.setStyle('width', width.get('value') + CSS.WIDTHUNIT);
|
||||
}
|
||||
|
||||
if (backgroundcolourvalue !== '') {
|
||||
table.setStyle('background-color', backgroundcolourvalue);
|
||||
}
|
||||
|
||||
captionnode = table.one('caption');
|
||||
if (!captionnode) {
|
||||
captionnode = Y.Node.create('<caption></caption>');
|
||||
@@ -589,14 +591,9 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
var caption,
|
||||
captionposition,
|
||||
borders,
|
||||
bordertable = '',
|
||||
bordercell = '',
|
||||
bordersize,
|
||||
borderstyle,
|
||||
bordercolour,
|
||||
borderSizeValue = '1',
|
||||
borderStyleValue = 'solid',
|
||||
borderhex = '#FFFFFF',
|
||||
rows,
|
||||
cols,
|
||||
headers,
|
||||
@@ -624,52 +621,13 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
headers = e.currentTarget.ancestor(SELECTORS.FORM).one(SELECTORS.HEADERS);
|
||||
width = e.currentTarget.ancestor(SELECTORS.FORM).one(SELECTORS.WIDTH);
|
||||
|
||||
if (bordercolour) {
|
||||
borderhex = bordercolour.get('value');
|
||||
}
|
||||
|
||||
if (bordersize) {
|
||||
borderSizeValue = bordersize.get('value');
|
||||
}
|
||||
|
||||
if (borderstyle) {
|
||||
borderStyleValue = borderstyle.get('value');
|
||||
}
|
||||
|
||||
if (borders) {
|
||||
if (borders.get('value') === 'outer') {
|
||||
bordertable = ' style="border: ' + borderSizeValue + CSS.BORDERSIZEUNIT + ' ' +
|
||||
borderStyleValue + ' ' + borderhex + ';"';
|
||||
} else if (borders.get('value') === 'all') {
|
||||
bordercell = ' style="border: ' + borderSizeValue + CSS.BORDERSIZEUNIT + ' ' +
|
||||
borderStyleValue + ' ' + borderhex + ';"';
|
||||
}
|
||||
}
|
||||
|
||||
if (backgroundcolour) {
|
||||
if (bordertable !== '') {
|
||||
bordertable = bordertable.substring(0, bordertable.length-1) +
|
||||
'background-color: ' + backgroundcolour.get('value') + ';"';
|
||||
} else {
|
||||
bordertable = ' style="background-color: ' + backgroundcolour.get('value') + ';"';
|
||||
}
|
||||
}
|
||||
|
||||
if (width && width.get('value')) {
|
||||
if (bordertable !== '') {
|
||||
bordertable = bordertable.substring(0, bordertable.length-1) + 'width: ' +
|
||||
width.get('value') + CSS.WIDTHUNIT + ';"';
|
||||
} else {
|
||||
bordertable = ' style="width: ' + width.get('value') + CSS.WIDTHUNIT + ';"';
|
||||
}
|
||||
}
|
||||
|
||||
// Set the selection.
|
||||
this.get('host').setSelection(this._currentSelection);
|
||||
|
||||
// Note there are some spaces inserted in the cells and before and after, so that users have somewhere to click.
|
||||
var nl = "\n";
|
||||
tablehtml = '<br/>' + nl + '<table' + bordertable + '>' + nl;
|
||||
var tableId = Y.guid();
|
||||
tablehtml = '<br/>' + nl + '<table id="' + tableId + '">' + nl;
|
||||
|
||||
var captionstyle = '';
|
||||
if (captionposition.get('value')) {
|
||||
@@ -681,7 +639,7 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
i = 1;
|
||||
tablehtml += '<thead>' + nl + '<tr>' + nl;
|
||||
for (j = 0; j < parseInt(cols.get('value'), 10); j++) {
|
||||
tablehtml += '<th scope="col"' + bordercell + '></th>' + nl;
|
||||
tablehtml += '<th scope="col"></th>' + nl;
|
||||
}
|
||||
tablehtml += '</tr>' + nl + '</thead>' + nl;
|
||||
}
|
||||
@@ -690,9 +648,9 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
tablehtml += '<tr>' + nl;
|
||||
for (j = 0; j < parseInt(cols.get('value'), 10); j++) {
|
||||
if (j === 0 && (headers.get('value') === 'rows' || headers.get('value') === 'both')) {
|
||||
tablehtml += '<th scope="row"' + bordercell + '></th>' + nl;
|
||||
tablehtml += '<th scope="row"></th>' + nl;
|
||||
} else {
|
||||
tablehtml += '<td ' + bordercell + '></td>' + nl;
|
||||
tablehtml += '<td ></td>' + nl;
|
||||
}
|
||||
}
|
||||
tablehtml += '</tr>' + nl;
|
||||
@@ -702,6 +660,17 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
|
||||
this.get('host').insertContentAtFocusPoint(tablehtml);
|
||||
|
||||
var tableNode = Y.one('#' + tableId);
|
||||
this._setAppearance(tableNode, {
|
||||
width: width,
|
||||
borders: borders,
|
||||
borderColour: bordercolour,
|
||||
borderSize: bordersize,
|
||||
borderStyle: borderstyle,
|
||||
backgroundColour: backgroundcolour
|
||||
});
|
||||
tableNode.removeAttribute('id');
|
||||
|
||||
// Mark the content as updated.
|
||||
this.markUpdated();
|
||||
},
|
||||
@@ -1148,6 +1117,73 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the appropriate styles on the given table node according to
|
||||
* the provided configuration.
|
||||
*
|
||||
* @method _setAppearance
|
||||
* @param {Node} The table node to be modified.
|
||||
* @param {Object} Configuration object (associative array) containing the form nodes for
|
||||
* border styling.
|
||||
* @private
|
||||
*/
|
||||
_setAppearance: function(tableNode, configuration) {
|
||||
var borderhex,
|
||||
borderSizeValue,
|
||||
borderStyleValue,
|
||||
backgroundcolourvalue;
|
||||
|
||||
if (configuration.borderColour) {
|
||||
borderhex = configuration.borderColour.get('value');
|
||||
}
|
||||
|
||||
if (configuration.borderSize) {
|
||||
borderSizeValue = configuration.borderSize.get('value');
|
||||
}
|
||||
|
||||
if (configuration.borderStyle) {
|
||||
borderStyleValue = configuration.borderStyle.get('value');
|
||||
}
|
||||
|
||||
if (configuration.backgroundColour) {
|
||||
backgroundcolourvalue = configuration.backgroundColour.get('value');
|
||||
}
|
||||
|
||||
// Clear the inline border styling
|
||||
tableNode.removeAttribute('style');
|
||||
tableNode.all('td, th').each(function(cell) {
|
||||
cell.removeAttribute('style');
|
||||
}, this);
|
||||
|
||||
if (configuration.borders) {
|
||||
if (configuration.borders.get('value') === 'outer') {
|
||||
tableNode.setStyle('borderWidth', borderSizeValue + CSS.BORDERSIZEUNIT);
|
||||
tableNode.setStyle('borderStyle', borderStyleValue);
|
||||
|
||||
if (borderhex !== 'none') {
|
||||
tableNode.setStyle('borderColor', borderhex);
|
||||
}
|
||||
} else if (configuration.borders.get('value') === 'all') {
|
||||
tableNode.all('td, th').each(function(cell) {
|
||||
cell.setStyle('borderWidth', borderSizeValue + CSS.BORDERSIZEUNIT);
|
||||
cell.setStyle('borderStyle', borderStyleValue);
|
||||
|
||||
if (borderhex !== 'none') {
|
||||
cell.setStyle('borderColor', borderhex);
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
}
|
||||
|
||||
if (backgroundcolourvalue !== 'none') {
|
||||
tableNode.setStyle('backgroundColor', backgroundcolourvalue);
|
||||
}
|
||||
|
||||
if (configuration.width && configuration.width.get('value')) {
|
||||
tableNode.setStyle('width', configuration.width.get('value') + CSS.WIDTHUNIT);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Edit table (show the dialogue).
|
||||
*
|
||||
@@ -1174,7 +1210,9 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
backgroundcolours = node.all(SELECTORS.BACKGROUNDCOLOURS),
|
||||
width = node.one(SELECTORS.WIDTH),
|
||||
table = this._lastTarget.ancestor('table'),
|
||||
captionnode = table.one('caption');
|
||||
captionnode = table.one('caption'),
|
||||
hexColour,
|
||||
matchedInput;
|
||||
|
||||
if (captionnode) {
|
||||
captioninput.set('value', captionnode.getHTML());
|
||||
@@ -1193,9 +1231,9 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
captionpositioninput.set('value', '');
|
||||
}
|
||||
|
||||
if (table.getStyle('background-color') !== 'transparent' && this.get('allowBackgroundColour')) {
|
||||
var hexColour = Y.Color.toHex(table.getStyle('background-color'));
|
||||
var matchedInput = backgroundcolours.filter('[value="' + hexColour + '"]');
|
||||
if (table.getStyle('backgroundColor') && this.get('allowBackgroundColour')) {
|
||||
hexColour = Y.Color.toHex(table.getStyle('backgroundColor'));
|
||||
matchedInput = backgroundcolours.filter('[value="' + hexColour + '"]');
|
||||
|
||||
if (matchedInput) {
|
||||
matchedInput.set("checked", true);
|
||||
@@ -1207,11 +1245,7 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
borderConfiguration = this._getBorderConfiguration(table);
|
||||
|
||||
if (borderConfiguration) {
|
||||
if (borderConfiguration.borderStyle && borderConfiguration.borderStyle === 'none') {
|
||||
borderValue = 'none';
|
||||
} else {
|
||||
borderValue = 'outer';
|
||||
}
|
||||
borderValue = 'outer';
|
||||
} else {
|
||||
borderConfiguration = this._getBorderConfiguration(table.one('td'));
|
||||
if (borderConfiguration) {
|
||||
@@ -1226,8 +1260,8 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
bordersize.set('value', borderSize);
|
||||
borderinput.set('value', borderValue);
|
||||
|
||||
var hexColour = borderConfiguration.borderColor;
|
||||
var matchedInput = bordercolours.filter('[value="' + hexColour + '"]');
|
||||
hexColour = borderConfiguration.borderColor;
|
||||
matchedInput = bordercolours.filter('[value="' + hexColour + '"]');
|
||||
|
||||
if (matchedInput) {
|
||||
matchedInput.set("checked", true);
|
||||
@@ -1244,6 +1278,7 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
}
|
||||
headersinput.set('value', headersvalue);
|
||||
dialogue.set('bodyContent', node).show();
|
||||
this._updateAvailableSettings();
|
||||
},
|
||||
|
||||
|
||||
|
||||
+158
-123
@@ -33,11 +33,11 @@
|
||||
|
||||
var COMPONENT = 'atto_table',
|
||||
DEFAULT = {
|
||||
BORDERSTYLE: 'solid',
|
||||
BORDERSTYLE: 'inherit',
|
||||
BORDERWIDTH: '1'
|
||||
},
|
||||
DIALOGUE = {
|
||||
WIDTH: '450px'
|
||||
WIDTH: '480px'
|
||||
},
|
||||
TEMPLATE = '' +
|
||||
'<form class="{{CSS.FORM}}">' +
|
||||
@@ -76,9 +76,8 @@ var COMPONENT = 'atto_table',
|
||||
'<legend class="mdl-align">{{get_string "appearance" component}}</legend>' +
|
||||
'{{#if allowBorders}}' +
|
||||
'<label for="{{elementid}}_atto_table_borders" class="sameline">{{get_string "borders" component}}</label>' +
|
||||
'<select class="{{CSS.BORDERS}}" id="{{elementid}}_atto_table_borders">' +
|
||||
'<select name="borders" class="{{CSS.BORDERS}}" id="{{elementid}}_atto_table_borders">' +
|
||||
'<option value="default">{{get_string "themedefault" component}}' + '</option>' +
|
||||
'<option value="none">{{get_string "noborder" component}}' + '</option>' +
|
||||
'<option value="outer">{{get_string "outer" component}}' + '</option>' +
|
||||
'<option value="all">{{get_string "all" component}}' + '</option>' +
|
||||
'</select>' +
|
||||
@@ -86,9 +85,10 @@ var COMPONENT = 'atto_table',
|
||||
'{{#if allowBorderStyles}}' +
|
||||
'<label for="{{elementid}}_atto_table_borderstyle" class="sameline">' +
|
||||
'{{get_string "borderstyles" component}}</label>' +
|
||||
'<select class="{{CSS.BORDERSTYLE}}" id="{{elementid}}_atto_table_borderstyle">' +
|
||||
'<select name="borderstyles" class="{{CSS.BORDERSTYLE}}" id="{{elementid}}_atto_table_borderstyle">' +
|
||||
'<option value="inherit">{{get_string "inherit" component}}</option>' +
|
||||
'{{#each borderStyles}}' +
|
||||
'<option value="' + '{{this}}' + '">' + '{{this}}' + '</option>' +
|
||||
'<option value="' + '{{this}}' + '">' + '{{get_string this ../component}}' + '</option>' +
|
||||
'{{/each}}' +
|
||||
'</select>' +
|
||||
'<br>' +
|
||||
@@ -96,7 +96,7 @@ var COMPONENT = 'atto_table',
|
||||
'{{#if allowBorderSize}}' +
|
||||
'<label for="{{elementid}}_atto_table_bordersize" class="sameline">' +
|
||||
'{{get_string "bordersize" component}}</label>' +
|
||||
'<input id="{{elementid}}_atto_table_bordersize" class="{{CSS.BORDERSIZE}}"' +
|
||||
'<input name="bordersize" id="{{elementid}}_atto_table_bordersize" class="{{CSS.BORDERSIZE}}"' +
|
||||
'type="number" value="1" size="8" min="1" max="50"/>' +
|
||||
'<label style="display: inline-block;">{{CSS.BORDERSIZEUNIT}}</label>' +
|
||||
'<br>' +
|
||||
@@ -110,10 +110,10 @@ var COMPONENT = 'atto_table',
|
||||
'style="background-color:transparent;color:transparent">' +
|
||||
|
||||
'<input id="{{../elementid}}_atto_table_bordercolour_-1"' +
|
||||
'type="radio" name="borderColour" value="transparent" checked="checked"'+
|
||||
'title="{{get_string "transparent" component}}"></input>' +
|
||||
'type="radio" name="borderColour" value="none" checked="checked"'+
|
||||
'title="{{get_string "themedefault" component}}"></input>' +
|
||||
|
||||
'{{get_string "transparent" component}}' +
|
||||
'{{get_string "themedefault" component}}' +
|
||||
'</label>' +
|
||||
'{{#each availableColours}}' +
|
||||
'<label for="{{../elementid}}_atto_table_bordercolour_{{@index}}"' +
|
||||
@@ -138,10 +138,10 @@ var COMPONENT = 'atto_table',
|
||||
'style="background-color:transparent;color:transparent">' +
|
||||
|
||||
'<input id="{{../elementid}}_atto_table_backgroundcolour_-1"' +
|
||||
'type="radio" name="backgroundColour" value="transparent" checked="checked"'+
|
||||
'title="{{get_string "transparent" component}}"></input>' +
|
||||
'type="radio" name="backgroundColour" value="none" checked="checked"'+
|
||||
'title="{{get_string "themedefault" component}}"></input>' +
|
||||
|
||||
'{{get_string "transparent" component}}' +
|
||||
'{{get_string "themedefault" component}}' +
|
||||
'</label>' +
|
||||
|
||||
'{{#each availableColours}}' +
|
||||
@@ -160,7 +160,7 @@ var COMPONENT = 'atto_table',
|
||||
'{{#if allowWidth}}' +
|
||||
'<label for="{{elementid}}_atto_table_width" class="sameline">' +
|
||||
'{{get_string "width" component}}</label>' +
|
||||
'<input id="{{elementid}}_atto_table_width" class="{{CSS.WIDTH}}" size="8" type="number" min="0" max="100"/>' +
|
||||
'<input name="width" id="{{elementid}}_atto_table_width" class="{{CSS.WIDTH}}" size="8" type="number" min="0" max="100"/>' +
|
||||
'<label style="display: inline-block;">{{CSS.WIDTHUNIT}}</label>' +
|
||||
'<br>' +
|
||||
'{{/if}}' +
|
||||
@@ -287,6 +287,8 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
// Set the dialogue content, and then show the dialogue.
|
||||
dialogue.set('bodyContent', this._getDialogueContent(false))
|
||||
.show();
|
||||
|
||||
this._updateAvailableSettings();
|
||||
}
|
||||
},
|
||||
|
||||
@@ -332,7 +334,7 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
*/
|
||||
_getDialogueContent: function(edit) {
|
||||
var template = Y.Handlebars.compile(TEMPLATE);
|
||||
var availableColours = this.get('availableColors');
|
||||
var allowBorders = this.get('allowBorders');
|
||||
|
||||
this._content = Y.Node.create(template({
|
||||
CSS: CSS,
|
||||
@@ -341,13 +343,13 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
edit: edit,
|
||||
nonedit: !edit,
|
||||
allowStyling: this.get('allowStyling'),
|
||||
allowBorders: this.get('allowBorders'),
|
||||
allowBorders: allowBorders,
|
||||
allowBorderStyles: this.get('allowBorderStyles'),
|
||||
borderStyles: this.get('borderStyles'),
|
||||
allowBorderSize: this.get('allowBorderSize'),
|
||||
allowBorderColour: this.get('allowBorderColour'),
|
||||
allowBackgroundColour: this.get('allowBackgroundColour'),
|
||||
availableColours: availableColours,
|
||||
availableColours: this.get('availableColors'),
|
||||
allowWidth: this.get('allowWidth')
|
||||
}));
|
||||
|
||||
@@ -358,9 +360,48 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
this._content.one('.submit').on('click', this._setTable, this);
|
||||
}
|
||||
|
||||
if (allowBorders) {
|
||||
this._content.one('[name="borders"]').on('change', this._updateAvailableSettings, this);
|
||||
}
|
||||
|
||||
return this._content;
|
||||
},
|
||||
|
||||
/**
|
||||
* Disables options within the dialogue if they shouldn't be available.
|
||||
* E.g.
|
||||
* If borders are set to "Theme default" then the border size, style and
|
||||
* colour options are disabled.
|
||||
*
|
||||
* @method _updateAvailableSettings
|
||||
* @private
|
||||
*/
|
||||
_updateAvailableSettings: function() {
|
||||
var tableForm = this._content,
|
||||
enableBorders = tableForm.one('[name="borders"]'),
|
||||
borderStyle = tableForm.one('[name="borderstyles"]'),
|
||||
borderSize = tableForm.one('[name="bordersize"]'),
|
||||
borderColour = tableForm.all('[name="borderColour"]'),
|
||||
disabledValue = 'removeAttribute';
|
||||
|
||||
if (enableBorders.get('value') === 'default') {
|
||||
disabledValue = 'setAttribute';
|
||||
}
|
||||
|
||||
if (borderStyle) {
|
||||
borderStyle[disabledValue]('disabled');
|
||||
}
|
||||
|
||||
if (borderSize) {
|
||||
borderSize[disabledValue]('disabled');
|
||||
}
|
||||
|
||||
if (borderColour) {
|
||||
borderColour[disabledValue]('disabled');
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Given the current selection, return a table cell suitable for table editing
|
||||
* purposes, i.e. the first table cell selected, or the first cell in the table
|
||||
@@ -434,10 +475,6 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
borderstyle,
|
||||
bordercolour,
|
||||
backgroundcolour,
|
||||
backgroundcolourvalue = '#FFFFFF',
|
||||
borderSizeValue = '1',
|
||||
borderStyleValue = 'solid',
|
||||
borderhex = '#FFFFFF',
|
||||
table,
|
||||
width,
|
||||
captionnode;
|
||||
@@ -459,51 +496,16 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
backgroundcolour = e.currentTarget.ancestor(SELECTORS.FORM).one(SELECTORS.SELECTEDBACKGROUNDCOLOUR);
|
||||
width = e.currentTarget.ancestor(SELECTORS.FORM).one(SELECTORS.WIDTH);
|
||||
|
||||
if (bordercolour) {
|
||||
borderhex = bordercolour.get('value');
|
||||
}
|
||||
|
||||
if (bordersize) {
|
||||
borderSizeValue = bordersize.get('value');
|
||||
}
|
||||
|
||||
if (borderstyle) {
|
||||
borderStyleValue = borderstyle.get('value');
|
||||
}
|
||||
|
||||
if (backgroundcolour) {
|
||||
backgroundcolourvalue = backgroundcolour.get('value');
|
||||
}
|
||||
|
||||
table = this._lastTarget.ancestor('table');
|
||||
// Clear the inline border styling
|
||||
table.removeAttribute('style');
|
||||
table.all('td, th').each(function() {
|
||||
this.removeAttribute('style');
|
||||
this._setAppearance(table, {
|
||||
width: width,
|
||||
borders: borders,
|
||||
borderColour: bordercolour,
|
||||
borderSize: bordersize,
|
||||
borderStyle: borderstyle,
|
||||
backgroundColour: backgroundcolour
|
||||
});
|
||||
|
||||
if (borders) {
|
||||
if (borders.get('value') === 'outer') {
|
||||
table.setStyle('border', borderSizeValue + CSS.BORDERSIZEUNIT + ' ' +
|
||||
borderStyleValue + ' ' + borderhex);
|
||||
} else if (borders.get('value') === 'all') {
|
||||
table.all('td, th').each(function() {
|
||||
this.setStyle('border', borderSizeValue + CSS.BORDERSIZEUNIT + ' ' +
|
||||
borderStyleValue + ' ' + borderhex);
|
||||
});
|
||||
} else if (borders.get('value') === 'none') {
|
||||
table.setStyle('border', 'none');
|
||||
}
|
||||
}
|
||||
|
||||
if (width && width.get('value')) {
|
||||
table.setStyle('width', width.get('value') + CSS.WIDTHUNIT);
|
||||
}
|
||||
|
||||
if (backgroundcolourvalue !== '') {
|
||||
table.setStyle('background-color', backgroundcolourvalue);
|
||||
}
|
||||
|
||||
captionnode = table.one('caption');
|
||||
if (!captionnode) {
|
||||
captionnode = Y.Node.create('<caption></caption>');
|
||||
@@ -587,14 +589,9 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
var caption,
|
||||
captionposition,
|
||||
borders,
|
||||
bordertable = '',
|
||||
bordercell = '',
|
||||
bordersize,
|
||||
borderstyle,
|
||||
bordercolour,
|
||||
borderSizeValue = '1',
|
||||
borderStyleValue = 'solid',
|
||||
borderhex = '#FFFFFF',
|
||||
rows,
|
||||
cols,
|
||||
headers,
|
||||
@@ -622,52 +619,13 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
headers = e.currentTarget.ancestor(SELECTORS.FORM).one(SELECTORS.HEADERS);
|
||||
width = e.currentTarget.ancestor(SELECTORS.FORM).one(SELECTORS.WIDTH);
|
||||
|
||||
if (bordercolour) {
|
||||
borderhex = bordercolour.get('value');
|
||||
}
|
||||
|
||||
if (bordersize) {
|
||||
borderSizeValue = bordersize.get('value');
|
||||
}
|
||||
|
||||
if (borderstyle) {
|
||||
borderStyleValue = borderstyle.get('value');
|
||||
}
|
||||
|
||||
if (borders) {
|
||||
if (borders.get('value') === 'outer') {
|
||||
bordertable = ' style="border: ' + borderSizeValue + CSS.BORDERSIZEUNIT + ' ' +
|
||||
borderStyleValue + ' ' + borderhex + ';"';
|
||||
} else if (borders.get('value') === 'all') {
|
||||
bordercell = ' style="border: ' + borderSizeValue + CSS.BORDERSIZEUNIT + ' ' +
|
||||
borderStyleValue + ' ' + borderhex + ';"';
|
||||
}
|
||||
}
|
||||
|
||||
if (backgroundcolour) {
|
||||
if (bordertable !== '') {
|
||||
bordertable = bordertable.substring(0, bordertable.length-1) +
|
||||
'background-color: ' + backgroundcolour.get('value') + ';"';
|
||||
} else {
|
||||
bordertable = ' style="background-color: ' + backgroundcolour.get('value') + ';"';
|
||||
}
|
||||
}
|
||||
|
||||
if (width && width.get('value')) {
|
||||
if (bordertable !== '') {
|
||||
bordertable = bordertable.substring(0, bordertable.length-1) + 'width: ' +
|
||||
width.get('value') + CSS.WIDTHUNIT + ';"';
|
||||
} else {
|
||||
bordertable = ' style="width: ' + width.get('value') + CSS.WIDTHUNIT + ';"';
|
||||
}
|
||||
}
|
||||
|
||||
// Set the selection.
|
||||
this.get('host').setSelection(this._currentSelection);
|
||||
|
||||
// Note there are some spaces inserted in the cells and before and after, so that users have somewhere to click.
|
||||
var nl = "\n";
|
||||
tablehtml = '<br/>' + nl + '<table' + bordertable + '>' + nl;
|
||||
var tableId = Y.guid();
|
||||
tablehtml = '<br/>' + nl + '<table id="' + tableId + '">' + nl;
|
||||
|
||||
var captionstyle = '';
|
||||
if (captionposition.get('value')) {
|
||||
@@ -679,7 +637,7 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
i = 1;
|
||||
tablehtml += '<thead>' + nl + '<tr>' + nl;
|
||||
for (j = 0; j < parseInt(cols.get('value'), 10); j++) {
|
||||
tablehtml += '<th scope="col"' + bordercell + '></th>' + nl;
|
||||
tablehtml += '<th scope="col"></th>' + nl;
|
||||
}
|
||||
tablehtml += '</tr>' + nl + '</thead>' + nl;
|
||||
}
|
||||
@@ -688,9 +646,9 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
tablehtml += '<tr>' + nl;
|
||||
for (j = 0; j < parseInt(cols.get('value'), 10); j++) {
|
||||
if (j === 0 && (headers.get('value') === 'rows' || headers.get('value') === 'both')) {
|
||||
tablehtml += '<th scope="row"' + bordercell + '></th>' + nl;
|
||||
tablehtml += '<th scope="row"></th>' + nl;
|
||||
} else {
|
||||
tablehtml += '<td ' + bordercell + '></td>' + nl;
|
||||
tablehtml += '<td ></td>' + nl;
|
||||
}
|
||||
}
|
||||
tablehtml += '</tr>' + nl;
|
||||
@@ -700,6 +658,17 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
|
||||
this.get('host').insertContentAtFocusPoint(tablehtml);
|
||||
|
||||
var tableNode = Y.one('#' + tableId);
|
||||
this._setAppearance(tableNode, {
|
||||
width: width,
|
||||
borders: borders,
|
||||
borderColour: bordercolour,
|
||||
borderSize: bordersize,
|
||||
borderStyle: borderstyle,
|
||||
backgroundColour: backgroundcolour
|
||||
});
|
||||
tableNode.removeAttribute('id');
|
||||
|
||||
// Mark the content as updated.
|
||||
this.markUpdated();
|
||||
},
|
||||
@@ -1146,6 +1115,73 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the appropriate styles on the given table node according to
|
||||
* the provided configuration.
|
||||
*
|
||||
* @method _setAppearance
|
||||
* @param {Node} The table node to be modified.
|
||||
* @param {Object} Configuration object (associative array) containing the form nodes for
|
||||
* border styling.
|
||||
* @private
|
||||
*/
|
||||
_setAppearance: function(tableNode, configuration) {
|
||||
var borderhex,
|
||||
borderSizeValue,
|
||||
borderStyleValue,
|
||||
backgroundcolourvalue;
|
||||
|
||||
if (configuration.borderColour) {
|
||||
borderhex = configuration.borderColour.get('value');
|
||||
}
|
||||
|
||||
if (configuration.borderSize) {
|
||||
borderSizeValue = configuration.borderSize.get('value');
|
||||
}
|
||||
|
||||
if (configuration.borderStyle) {
|
||||
borderStyleValue = configuration.borderStyle.get('value');
|
||||
}
|
||||
|
||||
if (configuration.backgroundColour) {
|
||||
backgroundcolourvalue = configuration.backgroundColour.get('value');
|
||||
}
|
||||
|
||||
// Clear the inline border styling
|
||||
tableNode.removeAttribute('style');
|
||||
tableNode.all('td, th').each(function(cell) {
|
||||
cell.removeAttribute('style');
|
||||
}, this);
|
||||
|
||||
if (configuration.borders) {
|
||||
if (configuration.borders.get('value') === 'outer') {
|
||||
tableNode.setStyle('borderWidth', borderSizeValue + CSS.BORDERSIZEUNIT);
|
||||
tableNode.setStyle('borderStyle', borderStyleValue);
|
||||
|
||||
if (borderhex !== 'none') {
|
||||
tableNode.setStyle('borderColor', borderhex);
|
||||
}
|
||||
} else if (configuration.borders.get('value') === 'all') {
|
||||
tableNode.all('td, th').each(function(cell) {
|
||||
cell.setStyle('borderWidth', borderSizeValue + CSS.BORDERSIZEUNIT);
|
||||
cell.setStyle('borderStyle', borderStyleValue);
|
||||
|
||||
if (borderhex !== 'none') {
|
||||
cell.setStyle('borderColor', borderhex);
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
}
|
||||
|
||||
if (backgroundcolourvalue !== 'none') {
|
||||
tableNode.setStyle('backgroundColor', backgroundcolourvalue);
|
||||
}
|
||||
|
||||
if (configuration.width && configuration.width.get('value')) {
|
||||
tableNode.setStyle('width', configuration.width.get('value') + CSS.WIDTHUNIT);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Edit table (show the dialogue).
|
||||
*
|
||||
@@ -1172,7 +1208,9 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
backgroundcolours = node.all(SELECTORS.BACKGROUNDCOLOURS),
|
||||
width = node.one(SELECTORS.WIDTH),
|
||||
table = this._lastTarget.ancestor('table'),
|
||||
captionnode = table.one('caption');
|
||||
captionnode = table.one('caption'),
|
||||
hexColour,
|
||||
matchedInput;
|
||||
|
||||
if (captionnode) {
|
||||
captioninput.set('value', captionnode.getHTML());
|
||||
@@ -1191,9 +1229,9 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
captionpositioninput.set('value', '');
|
||||
}
|
||||
|
||||
if (table.getStyle('background-color') !== 'transparent' && this.get('allowBackgroundColour')) {
|
||||
var hexColour = Y.Color.toHex(table.getStyle('background-color'));
|
||||
var matchedInput = backgroundcolours.filter('[value="' + hexColour + '"]');
|
||||
if (table.getStyle('backgroundColor') && this.get('allowBackgroundColour')) {
|
||||
hexColour = Y.Color.toHex(table.getStyle('backgroundColor'));
|
||||
matchedInput = backgroundcolours.filter('[value="' + hexColour + '"]');
|
||||
|
||||
if (matchedInput) {
|
||||
matchedInput.set("checked", true);
|
||||
@@ -1205,11 +1243,7 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
borderConfiguration = this._getBorderConfiguration(table);
|
||||
|
||||
if (borderConfiguration) {
|
||||
if (borderConfiguration.borderStyle && borderConfiguration.borderStyle === 'none') {
|
||||
borderValue = 'none';
|
||||
} else {
|
||||
borderValue = 'outer';
|
||||
}
|
||||
borderValue = 'outer';
|
||||
} else {
|
||||
borderConfiguration = this._getBorderConfiguration(table.one('td'));
|
||||
if (borderConfiguration) {
|
||||
@@ -1224,8 +1258,8 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
bordersize.set('value', borderSize);
|
||||
borderinput.set('value', borderValue);
|
||||
|
||||
var hexColour = borderConfiguration.borderColor;
|
||||
var matchedInput = bordercolours.filter('[value="' + hexColour + '"]');
|
||||
hexColour = borderConfiguration.borderColor;
|
||||
matchedInput = bordercolours.filter('[value="' + hexColour + '"]');
|
||||
|
||||
if (matchedInput) {
|
||||
matchedInput.set("checked", true);
|
||||
@@ -1242,6 +1276,7 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
}
|
||||
headersinput.set('value', headersvalue);
|
||||
dialogue.set('bodyContent', node).show();
|
||||
this._updateAvailableSettings();
|
||||
},
|
||||
|
||||
|
||||
|
||||
@@ -376,6 +376,73 @@ class core_files_zip_packer_testcase extends advanced_testcase implements file_p
|
||||
unlink($archive);
|
||||
}
|
||||
|
||||
public function test_close_archive() {
|
||||
global $CFG;
|
||||
|
||||
$this->resetAfterTest(true);
|
||||
|
||||
$archive = "$CFG->tempdir/archive.zip";
|
||||
$textfile = "$CFG->tempdir/textfile.txt";
|
||||
touch($textfile);
|
||||
|
||||
$this->assertFileNotExists($archive);
|
||||
$this->assertFileExists($textfile);
|
||||
|
||||
// Create archive and close it without files.
|
||||
// (returns true, without any warning).
|
||||
$zip_archive = new zip_archive();
|
||||
$result = $zip_archive->open($archive, file_archive::CREATE);
|
||||
$this->assertTrue($result);
|
||||
$result = $zip_archive->close();
|
||||
$this->assertTrue($result);
|
||||
unlink($archive);
|
||||
|
||||
// Create archive and close it with files.
|
||||
// (returns true, without any warning).
|
||||
$zip_archive = new zip_archive();
|
||||
$result = $zip_archive->open($archive, file_archive::CREATE);
|
||||
$this->assertTrue($result);
|
||||
$result = $zip_archive->add_file_from_string('test.txt', 'test');
|
||||
$this->assertTrue($result);
|
||||
$result = $zip_archive->add_file_from_pathname('test2.txt', $textfile);
|
||||
$result = $zip_archive->close();
|
||||
$this->assertTrue($result);
|
||||
unlink($archive);
|
||||
|
||||
// Create archive and close if forcing error.
|
||||
// (returns true for old PHP versions and
|
||||
// false with warnings for new PHP versions). MDL-51863.
|
||||
$zip_archive = new zip_archive();
|
||||
$result = $zip_archive->open($archive, file_archive::CREATE);
|
||||
$this->assertTrue($result);
|
||||
$result = $zip_archive->add_file_from_string('test.txt', 'test');
|
||||
$this->assertTrue($result);
|
||||
$result = $zip_archive->add_file_from_pathname('test2.txt', $textfile);
|
||||
$this->assertTrue($result);
|
||||
// Delete the file before closing does force close() to fail.
|
||||
unlink($textfile);
|
||||
// Behavior is different between old PHP versions and new ones. Let's detect it.
|
||||
$result = false;
|
||||
try {
|
||||
// Old PHP versions were not printing any warning.
|
||||
$result = $zip_archive->close();
|
||||
} catch (Exception $e) {
|
||||
// New PHP versions print PHP Warning.
|
||||
$this->assertInstanceOf('PHPUnit_Framework_Error_Warning', $e);
|
||||
$this->assertContains('ZipArchive::close', $e->getMessage());
|
||||
}
|
||||
// This is crazy, but it shows how some PHP versions do return true.
|
||||
try {
|
||||
// And some PHP versions do return correctly false (5.4.25, 5.6.14...)
|
||||
$this->assertFalse($result);
|
||||
} catch (Exception $e) {
|
||||
// But others do insist into returning true (5.6.13...). Only can accept them.
|
||||
$this->assertInstanceOf('PHPUnit_Framework_ExpectationFailedException', $e);
|
||||
$this->assertTrue($result);
|
||||
}
|
||||
$this->assertFileNotExists($archive);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends test_add_files
|
||||
*/
|
||||
|
||||
@@ -191,7 +191,7 @@ class zip_archive extends file_archive {
|
||||
}
|
||||
|
||||
if ($this->emptyziphack) {
|
||||
$this->za->close();
|
||||
@$this->za->close();
|
||||
$this->za = null;
|
||||
$this->mode = null;
|
||||
$this->namelookup = null;
|
||||
@@ -202,7 +202,7 @@ class zip_archive extends file_archive {
|
||||
|
||||
} else if ($this->za->numFiles == 0) {
|
||||
// PHP can not create empty archives, so let's fake it.
|
||||
$this->za->close();
|
||||
@$this->za->close();
|
||||
$this->za = null;
|
||||
$this->mode = null;
|
||||
$this->namelookup = null;
|
||||
|
||||
@@ -46,6 +46,8 @@ class MoodleQuickForm_autocomplete extends MoodleQuickForm_select {
|
||||
protected $ajax = '';
|
||||
/** @var string $placeholder Placeholder text for an empty list. */
|
||||
protected $placeholder = '';
|
||||
/** @var bool $casesensitive Whether the search has to be case-sensitive. */
|
||||
protected $casesensitive = false;
|
||||
|
||||
/**
|
||||
* constructor
|
||||
@@ -54,7 +56,7 @@ class MoodleQuickForm_autocomplete extends MoodleQuickForm_select {
|
||||
* @param mixed $elementLabel Label(s) for the select
|
||||
* @param mixed $options Data to be used to populate options
|
||||
* @param mixed $attributes Either a typical HTML attribute string or an associative array. Special options
|
||||
* "tags", "placeholder", "ajax", "multiple" are supported.
|
||||
* "tags", "placeholder", "ajax", "multiple", "casesensitive" are supported.
|
||||
*/
|
||||
function MoodleQuickForm_autocomplete($elementName=null, $elementLabel=null, $options=null, $attributes=null) {
|
||||
// Even if the constructor gets called twice we do not really want 2x options (crazy forms!).
|
||||
@@ -75,6 +77,10 @@ class MoodleQuickForm_autocomplete extends MoodleQuickForm_select {
|
||||
$this->ajax = $attributes['ajax'];
|
||||
unset($attributes['ajax']);
|
||||
}
|
||||
if (isset($attributes['casesensitive'])) {
|
||||
$this->casesensitive = $attributes['casesensitive'] ? true : false;
|
||||
unset($attributes['casesensitive']);
|
||||
}
|
||||
parent::HTML_QuickForm_select($elementName, $elementLabel, $options, $attributes);
|
||||
|
||||
$this->_type = 'autocomplete';
|
||||
@@ -91,7 +97,8 @@ class MoodleQuickForm_autocomplete extends MoodleQuickForm_select {
|
||||
// Enhance the select with javascript.
|
||||
$this->_generateId();
|
||||
$id = $this->getAttribute('id');
|
||||
$PAGE->requires->js_call_amd('core/form-autocomplete', 'enhance', $params = array('#' . $id, $this->tags, $this->ajax, $this->placeholder));
|
||||
$PAGE->requires->js_call_amd('core/form-autocomplete', 'enhance', $params = array('#' . $id, $this->tags, $this->ajax,
|
||||
$this->placeholder, $this->casesensitive));
|
||||
|
||||
return parent::toHTML();
|
||||
}
|
||||
|
||||
@@ -960,8 +960,12 @@ M.form_filemanager.init = function(Y, options) {
|
||||
for (var i in attrs) {
|
||||
if (selectnode.one('.fp-'+attrs[i])) {
|
||||
var value = (node[attrs[i]+'_f']) ? node[attrs[i]+'_f'] : (node[attrs[i]] ? node[attrs[i]] : '');
|
||||
// Escape if the attribute being evaluated is not for the list of reference files.
|
||||
if (attrs[i] !== 'reflist') {
|
||||
value = Y.Escape.html(value);
|
||||
}
|
||||
selectnode.one('.fp-'+attrs[i]).addClassIf('fp-unknown', ''+value == '')
|
||||
.one('.fp-value').setContent(Y.Escape.html(value));
|
||||
.one('.fp-value').setContent(value);
|
||||
}
|
||||
}
|
||||
// display thumbnail
|
||||
|
||||
@@ -556,6 +556,8 @@ class page_requirements_manager {
|
||||
$path = realpath("$componentdir/jquery/$file");
|
||||
if (strpos($path, $CFG->dirroot) === 0) {
|
||||
$url = $CFG->httpswwwroot.preg_replace('/^'.preg_quote($CFG->dirroot, '/').'/', '', $path);
|
||||
// Replace all occurences of backslashes characters in url to forward slashes.
|
||||
$url = str_replace('\\', '/', $url);
|
||||
$url = new moodle_url($url);
|
||||
} else {
|
||||
// Bad luck, fix your server!
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
{ "label": "Another item label with <strong>tags</strong>", "value": "4" }
|
||||
]}
|
||||
}}
|
||||
<div class="form-autocomplete-selection {{#multiple}}form-autocomplete-multiple{{/multiple}}" id="{{selectionId}}" role="list" aria-atomic="true" tabindex="0" aria-multiselectable="true">
|
||||
<div class="form-autocomplete-selection {{#multiple}}form-autocomplete-multiple{{/multiple}}" id="{{selectionId}}" role="list" aria-atomic="true" {{#multiple}}tabindex="0" aria-multiselectable="true"{{/multiple}}>
|
||||
<span class="accesshide">{{#str}}selecteditems, form{{/str}}</span>
|
||||
{{#items}}
|
||||
<span role="listitem" data-value="{{value}}" aria-selected="true" class="label label-info">
|
||||
|
||||
@@ -246,40 +246,30 @@ class behat_navigation extends behat_base {
|
||||
}
|
||||
}
|
||||
|
||||
// Expand first node, and get it.
|
||||
// Get top level node.
|
||||
$node = $this->get_top_navigation_node($parentnodes[0]);
|
||||
|
||||
// Expand parent, sub-parent nodes in navigation if js enabled.
|
||||
if ($node->hasClass('collapsed') || ($node->hasAttribute('data-loaded') && $node->getAttribute('data-loaded') == 0)) {
|
||||
$xpath = "/p[contains(concat(' ', normalize-space(@class), ' '), ' tree_item ')]/span";
|
||||
$nodetoexpand = $node->find('xpath', $xpath);
|
||||
// Expand all nodes.
|
||||
for ($i = 0; $i < $countparentnode; $i++) {
|
||||
if ($i > 0) {
|
||||
// Sub nodes within top level node.
|
||||
$node = $this->get_navigation_node($parentnodes[$i], $node);
|
||||
}
|
||||
|
||||
// Keep expanding all sub-parents if js enabled.
|
||||
if ($this->running_javascript() && $node->hasClass('collapsed')) {
|
||||
$xpath = "/p[contains(concat(' ', normalize-space(@class), ' '), ' tree_item ')]";
|
||||
$nodetoexpand = $node->find('xpath', $xpath);
|
||||
|
||||
if ($this->running_javascript()) {
|
||||
$this->ensure_node_is_visible($nodetoexpand);
|
||||
$nodetoexpand->click();
|
||||
|
||||
// Site administration node needs to be expanded.
|
||||
if ($parentnodes[0] === $siteadminstr) {
|
||||
$this->getSession()->wait(self::TIMEOUT * 1000, self::PAGE_READY_JS);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Wait for node to load, if not loaded before.
|
||||
if ($nodetoexpand->hasAttribute('data-loaded') && $nodetoexpand->getAttribute('data-loaded') == 0) {
|
||||
$jscondition = '(document.evaluate("' . $nodetoexpand->getXpath() . '", document, null, '.
|
||||
'XPathResult.ANY_TYPE, null).iterateNext().getAttribute(\'data-loaded\') == 1)';
|
||||
|
||||
// If sub-parent nodes then get to the last one.
|
||||
if ($countparentnode > 1) {
|
||||
for ($i = 1; $i < $countparentnode; $i++) {
|
||||
$node = $this->get_navigation_node($parentnodes[$i], $node);
|
||||
|
||||
// Keep expanding all sub-parents if js enabled.
|
||||
if ($this->running_javascript()) {
|
||||
$xpath = "/p[contains(concat(' ', normalize-space(@class), ' '), ' tree_item ')]";
|
||||
if ($node->hasClass('collapsed')) {
|
||||
$nodetoexpand = $node->find('xpath', $xpath);
|
||||
if ($this->running_javascript()) {
|
||||
$this->ensure_node_is_visible($nodetoexpand);
|
||||
$nodetoexpand->click();
|
||||
}
|
||||
}
|
||||
$this->getSession()->wait(self::EXTENDED_TIMEOUT * 1000, $jscondition);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -288,19 +278,15 @@ class behat_navigation extends behat_base {
|
||||
$nodetextliteral = $this->getSession()->getSelectorsHandler()->xpathLiteral($nodetext);
|
||||
$xpath = "/ul/li/p[contains(concat(' ', normalize-space(@class), ' '), ' tree_item ')]" .
|
||||
"/a[normalize-space(.)=" . $nodetextliteral . "]";
|
||||
$node = $node->find('xpath', $xpath);
|
||||
$nodetoclick = $node->find('xpath', $xpath);
|
||||
|
||||
// Throw exception if no node found.
|
||||
if (!$node) {
|
||||
if (!$nodetoclick) {
|
||||
throw new ExpectationException('Navigation node "' . $nodetext . '" not found under "' .
|
||||
implode($parentnodes, ' > ') . '"', $this->getSession());
|
||||
}
|
||||
|
||||
if ($this->running_javascript()) {
|
||||
$this->ensure_node_is_visible($node);
|
||||
}
|
||||
|
||||
$node->click();
|
||||
$nodetoclick->click();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -154,8 +154,6 @@ class core_date_legacy_testcase extends advanced_testcase {
|
||||
$this->assertEquals(60 * 60 * 6.5, get_timezone_offset('Asia/Rangoon'));
|
||||
$this->assertEquals(60 * 60 * 9.5, get_timezone_offset('9.5'));
|
||||
$this->assertEquals(60 * 60 * 9.5, get_timezone_offset('Australia/Darwin'));
|
||||
$this->assertEquals(60 * 60 * 11.5, get_timezone_offset('11.5'));
|
||||
$this->assertEquals(60 * 60 * 11.5, get_timezone_offset('Pacific/Norfolk'));
|
||||
|
||||
$this->resetDebugging();
|
||||
}
|
||||
|
||||
@@ -83,7 +83,6 @@ class core_date_testcase extends advanced_testcase {
|
||||
$this->assertSame('Asia/Kolkata', core_date::normalise_timezone(5.5));
|
||||
$this->assertSame('Asia/Rangoon', core_date::normalise_timezone(6.5));
|
||||
$this->assertSame('Australia/Darwin', core_date::normalise_timezone('9.5'));
|
||||
$this->assertSame('Pacific/Norfolk', core_date::normalise_timezone('11.5'));
|
||||
|
||||
$this->setTimezone('99', 'Pacific/Auckland');
|
||||
$this->assertSame('Pacific/Auckland', core_date::normalise_timezone('Pacific/Auckland'));
|
||||
|
||||
@@ -180,4 +180,12 @@ What would happen if a non-terminated string were input to this function?
|
||||
|
||||
$this->assertSame($strconv, html_to_text($strorig));
|
||||
}
|
||||
|
||||
/**
|
||||
* Scripts should be stripped.
|
||||
*/
|
||||
public function test_strip_scripts() {
|
||||
$this->assertSame('Interesting text',
|
||||
html_to_text('Interesting <script type="text/javascript">var what_a_mess = "Yuck!";</script> text', 0));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,4 +63,42 @@ class core_outputrequirementslib_testcase extends advanced_testcase {
|
||||
$this->assertTrue($secondpage->requires->should_create_one_time_item_now('test_item'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for the jquery_plugin method.
|
||||
*
|
||||
* Test to make sure that backslashes are not generated with either slasharguments set to on or off.
|
||||
*/
|
||||
public function test_jquery_plugin() {
|
||||
global $CFG;
|
||||
|
||||
$this->resetAfterTest();
|
||||
|
||||
// With slasharguments on.
|
||||
$CFG->slasharguments = 1;
|
||||
|
||||
$page = new moodle_page();
|
||||
$requirements = $page->requires;
|
||||
// Assert successful method call.
|
||||
$this->assertTrue($requirements->jquery_plugin('jquery'));
|
||||
$this->assertTrue($requirements->jquery_plugin('ui'));
|
||||
|
||||
// Get the code containing the required jquery plugins.
|
||||
$requirecode = $requirements->get_top_of_body_code();
|
||||
// Make sure that the generated code does not contain backslashes.
|
||||
$this->assertFalse(strpos($requirecode, '\\'), "Output contains backslashes: " . $requirecode);
|
||||
|
||||
// With slasharguments off.
|
||||
$CFG->slasharguments = 0;
|
||||
|
||||
$page = new moodle_page();
|
||||
$requirements = $page->requires;
|
||||
// Assert successful method call.
|
||||
$this->assertTrue($requirements->jquery_plugin('jquery'));
|
||||
$this->assertTrue($requirements->jquery_plugin('ui'));
|
||||
|
||||
// Get the code containing the required jquery plugins.
|
||||
$requirecode = $requirements->get_top_of_body_code();
|
||||
// Make sure that the generated code does not contain backslashes.
|
||||
$this->assertFalse(strpos($requirecode, '\\'), "Output contains backslashes: " . $requirecode);
|
||||
}
|
||||
}
|
||||
|
||||
+30
-17
@@ -109,6 +109,16 @@ Y.extend(DIALOGUE, Y.Panel, {
|
||||
*/
|
||||
_originalPosition: null,
|
||||
|
||||
/**
|
||||
* The list of elements that have been aria hidden when displaying
|
||||
* this dialogue.
|
||||
*
|
||||
* @property _hiddenSiblings
|
||||
* @protected
|
||||
* @type Array
|
||||
*/
|
||||
_hiddenSiblings: null,
|
||||
|
||||
/**
|
||||
* Initialise the dialogue.
|
||||
*
|
||||
@@ -117,6 +127,9 @@ Y.extend(DIALOGUE, Y.Panel, {
|
||||
initializer : function() {
|
||||
var bb;
|
||||
|
||||
// Initialise the element cache.
|
||||
this._hiddenSiblings = [];
|
||||
|
||||
if (this.get('render')) {
|
||||
this.render();
|
||||
}
|
||||
@@ -243,6 +256,7 @@ Y.extend(DIALOGUE, Y.Panel, {
|
||||
var titlebar, bb;
|
||||
if (e.attrName === 'visible') {
|
||||
this.get('maskNode').addClass(CSS.LIGHTBOX);
|
||||
// Going from visible to hidden.
|
||||
if (e.prevVal && !e.newVal) {
|
||||
bb = this.get('boundingBox');
|
||||
if (this._resizeevent) {
|
||||
@@ -254,7 +268,13 @@ Y.extend(DIALOGUE, Y.Panel, {
|
||||
this._orientationevent = null;
|
||||
}
|
||||
bb.detach('key', this.keyDelegation);
|
||||
|
||||
if (this.get('modal')) {
|
||||
// Hide this dialogue from screen readers.
|
||||
this.setAccessibilityHidden();
|
||||
}
|
||||
}
|
||||
// Going from hidden to visible.
|
||||
if (!e.prevVal && e.newVal) {
|
||||
// This needs to be done each time the dialog is shown as new dialogs may have been opened.
|
||||
this.applyZIndex();
|
||||
@@ -268,6 +288,13 @@ Y.extend(DIALOGUE, Y.Panel, {
|
||||
}
|
||||
}
|
||||
this.keyDelegation();
|
||||
|
||||
// Only do accessibility hiding for modals because the ARIA spec
|
||||
// says that all ARIA dialogues should be modal.
|
||||
if (this.get('modal')) {
|
||||
// Make this dialogue visible to screen readers.
|
||||
this.setAccessibilityVisible();
|
||||
}
|
||||
}
|
||||
if (this.get('center') && !e.prevVal && e.newVal) {
|
||||
this.centerDialogue();
|
||||
@@ -367,13 +394,6 @@ Y.extend(DIALOGUE, Y.Panel, {
|
||||
this.lockScroll.enableScrollLock(this.shouldResizeFullscreen());
|
||||
}
|
||||
|
||||
// Only do accessibility hiding for modals because the ARIA spec
|
||||
// says that all ARIA dialogues should be modal.
|
||||
if (this.get('modal')) {
|
||||
// Make this dialogue visible to screen readers.
|
||||
this.setAccessibilityVisible();
|
||||
}
|
||||
|
||||
// Try and find a node to focus on using the focusOnShowSelector attribute.
|
||||
if (focusSelector !== null) {
|
||||
focusNode = this.get('boundingBox').one(focusSelector);
|
||||
@@ -401,11 +421,6 @@ Y.extend(DIALOGUE, Y.Panel, {
|
||||
}
|
||||
}
|
||||
|
||||
if (this.get('modal')) {
|
||||
// Hide this dialogue from screen readers.
|
||||
this.setAccessibilityHidden();
|
||||
}
|
||||
|
||||
// Unlock scroll if the plugin is present.
|
||||
if (this.lockScroll) {
|
||||
this.lockScroll.disableScrollLock();
|
||||
@@ -463,8 +478,6 @@ Y.extend(DIALOGUE, Y.Panel, {
|
||||
// Get the element that contains this dialogue because we need it
|
||||
// to filter out from the document.body child elements.
|
||||
var container = this.get(BASE);
|
||||
// Keep a record of any elements we change so that they can be reverted later.
|
||||
this.hiddenSiblings = [];
|
||||
|
||||
// We need to get a list containing each sibling element and the shallowest
|
||||
// non-ancestral nodes in the DOM. We can shortcut this a little by leveraging
|
||||
@@ -479,7 +492,7 @@ Y.extend(DIALOGUE, Y.Panel, {
|
||||
if (hidden !== 'true') {
|
||||
// Save their current state.
|
||||
node.setData('previous-aria-hidden', hidden);
|
||||
this.hiddenSiblings.push(node);
|
||||
this._hiddenSiblings.push(node);
|
||||
|
||||
// Hide this node from screen readers.
|
||||
node.set('aria-hidden', 'true');
|
||||
@@ -503,7 +516,7 @@ Y.extend(DIALOGUE, Y.Panel, {
|
||||
container.set('aria-hidden', 'true');
|
||||
|
||||
// Restore the sibling nodes back to their original values.
|
||||
Y.Array.each(this.hiddenSiblings, function(node) {
|
||||
Y.Array.each(this._hiddenSiblings, function(node) {
|
||||
var previousValue = node.getData('previous-aria-hidden');
|
||||
// If the element didn't previously have an aria-hidden attribute
|
||||
// then we can just remove the one we set.
|
||||
@@ -516,7 +529,7 @@ Y.extend(DIALOGUE, Y.Panel, {
|
||||
});
|
||||
|
||||
// Clear the cache. No longer need to store these.
|
||||
this.hiddenSiblings = [];
|
||||
this._hiddenSiblings = [];
|
||||
}
|
||||
}, {
|
||||
NAME : DIALOGUE_NAME,
|
||||
|
||||
+2
-2
File diff suppressed because one or more lines are too long
+30
-17
@@ -109,6 +109,16 @@ Y.extend(DIALOGUE, Y.Panel, {
|
||||
*/
|
||||
_originalPosition: null,
|
||||
|
||||
/**
|
||||
* The list of elements that have been aria hidden when displaying
|
||||
* this dialogue.
|
||||
*
|
||||
* @property _hiddenSiblings
|
||||
* @protected
|
||||
* @type Array
|
||||
*/
|
||||
_hiddenSiblings: null,
|
||||
|
||||
/**
|
||||
* Initialise the dialogue.
|
||||
*
|
||||
@@ -117,6 +127,9 @@ Y.extend(DIALOGUE, Y.Panel, {
|
||||
initializer : function() {
|
||||
var bb;
|
||||
|
||||
// Initialise the element cache.
|
||||
this._hiddenSiblings = [];
|
||||
|
||||
if (this.get('render')) {
|
||||
this.render();
|
||||
}
|
||||
@@ -243,6 +256,7 @@ Y.extend(DIALOGUE, Y.Panel, {
|
||||
var titlebar, bb;
|
||||
if (e.attrName === 'visible') {
|
||||
this.get('maskNode').addClass(CSS.LIGHTBOX);
|
||||
// Going from visible to hidden.
|
||||
if (e.prevVal && !e.newVal) {
|
||||
bb = this.get('boundingBox');
|
||||
if (this._resizeevent) {
|
||||
@@ -254,7 +268,13 @@ Y.extend(DIALOGUE, Y.Panel, {
|
||||
this._orientationevent = null;
|
||||
}
|
||||
bb.detach('key', this.keyDelegation);
|
||||
|
||||
if (this.get('modal')) {
|
||||
// Hide this dialogue from screen readers.
|
||||
this.setAccessibilityHidden();
|
||||
}
|
||||
}
|
||||
// Going from hidden to visible.
|
||||
if (!e.prevVal && e.newVal) {
|
||||
// This needs to be done each time the dialog is shown as new dialogs may have been opened.
|
||||
this.applyZIndex();
|
||||
@@ -268,6 +288,13 @@ Y.extend(DIALOGUE, Y.Panel, {
|
||||
}
|
||||
}
|
||||
this.keyDelegation();
|
||||
|
||||
// Only do accessibility hiding for modals because the ARIA spec
|
||||
// says that all ARIA dialogues should be modal.
|
||||
if (this.get('modal')) {
|
||||
// Make this dialogue visible to screen readers.
|
||||
this.setAccessibilityVisible();
|
||||
}
|
||||
}
|
||||
if (this.get('center') && !e.prevVal && e.newVal) {
|
||||
this.centerDialogue();
|
||||
@@ -367,13 +394,6 @@ Y.extend(DIALOGUE, Y.Panel, {
|
||||
this.lockScroll.enableScrollLock(this.shouldResizeFullscreen());
|
||||
}
|
||||
|
||||
// Only do accessibility hiding for modals because the ARIA spec
|
||||
// says that all ARIA dialogues should be modal.
|
||||
if (this.get('modal')) {
|
||||
// Make this dialogue visible to screen readers.
|
||||
this.setAccessibilityVisible();
|
||||
}
|
||||
|
||||
// Try and find a node to focus on using the focusOnShowSelector attribute.
|
||||
if (focusSelector !== null) {
|
||||
focusNode = this.get('boundingBox').one(focusSelector);
|
||||
@@ -401,11 +421,6 @@ Y.extend(DIALOGUE, Y.Panel, {
|
||||
}
|
||||
}
|
||||
|
||||
if (this.get('modal')) {
|
||||
// Hide this dialogue from screen readers.
|
||||
this.setAccessibilityHidden();
|
||||
}
|
||||
|
||||
// Unlock scroll if the plugin is present.
|
||||
if (this.lockScroll) {
|
||||
this.lockScroll.disableScrollLock();
|
||||
@@ -463,8 +478,6 @@ Y.extend(DIALOGUE, Y.Panel, {
|
||||
// Get the element that contains this dialogue because we need it
|
||||
// to filter out from the document.body child elements.
|
||||
var container = this.get(BASE);
|
||||
// Keep a record of any elements we change so that they can be reverted later.
|
||||
this.hiddenSiblings = [];
|
||||
|
||||
// We need to get a list containing each sibling element and the shallowest
|
||||
// non-ancestral nodes in the DOM. We can shortcut this a little by leveraging
|
||||
@@ -479,7 +492,7 @@ Y.extend(DIALOGUE, Y.Panel, {
|
||||
if (hidden !== 'true') {
|
||||
// Save their current state.
|
||||
node.setData('previous-aria-hidden', hidden);
|
||||
this.hiddenSiblings.push(node);
|
||||
this._hiddenSiblings.push(node);
|
||||
|
||||
// Hide this node from screen readers.
|
||||
node.set('aria-hidden', 'true');
|
||||
@@ -503,7 +516,7 @@ Y.extend(DIALOGUE, Y.Panel, {
|
||||
container.set('aria-hidden', 'true');
|
||||
|
||||
// Restore the sibling nodes back to their original values.
|
||||
Y.Array.each(this.hiddenSiblings, function(node) {
|
||||
Y.Array.each(this._hiddenSiblings, function(node) {
|
||||
var previousValue = node.getData('previous-aria-hidden');
|
||||
// If the element didn't previously have an aria-hidden attribute
|
||||
// then we can just remove the one we set.
|
||||
@@ -516,7 +529,7 @@ Y.extend(DIALOGUE, Y.Panel, {
|
||||
});
|
||||
|
||||
// Clear the cache. No longer need to store these.
|
||||
this.hiddenSiblings = [];
|
||||
this._hiddenSiblings = [];
|
||||
}
|
||||
}, {
|
||||
NAME : DIALOGUE_NAME,
|
||||
|
||||
+30
-17
@@ -80,6 +80,16 @@ Y.extend(DIALOGUE, Y.Panel, {
|
||||
*/
|
||||
_originalPosition: null,
|
||||
|
||||
/**
|
||||
* The list of elements that have been aria hidden when displaying
|
||||
* this dialogue.
|
||||
*
|
||||
* @property _hiddenSiblings
|
||||
* @protected
|
||||
* @type Array
|
||||
*/
|
||||
_hiddenSiblings: null,
|
||||
|
||||
/**
|
||||
* Initialise the dialogue.
|
||||
*
|
||||
@@ -88,6 +98,9 @@ Y.extend(DIALOGUE, Y.Panel, {
|
||||
initializer : function() {
|
||||
var bb;
|
||||
|
||||
// Initialise the element cache.
|
||||
this._hiddenSiblings = [];
|
||||
|
||||
if (this.get('render')) {
|
||||
this.render();
|
||||
}
|
||||
@@ -214,6 +227,7 @@ Y.extend(DIALOGUE, Y.Panel, {
|
||||
var titlebar, bb;
|
||||
if (e.attrName === 'visible') {
|
||||
this.get('maskNode').addClass(CSS.LIGHTBOX);
|
||||
// Going from visible to hidden.
|
||||
if (e.prevVal && !e.newVal) {
|
||||
bb = this.get('boundingBox');
|
||||
if (this._resizeevent) {
|
||||
@@ -225,7 +239,13 @@ Y.extend(DIALOGUE, Y.Panel, {
|
||||
this._orientationevent = null;
|
||||
}
|
||||
bb.detach('key', this.keyDelegation);
|
||||
|
||||
if (this.get('modal')) {
|
||||
// Hide this dialogue from screen readers.
|
||||
this.setAccessibilityHidden();
|
||||
}
|
||||
}
|
||||
// Going from hidden to visible.
|
||||
if (!e.prevVal && e.newVal) {
|
||||
// This needs to be done each time the dialog is shown as new dialogs may have been opened.
|
||||
this.applyZIndex();
|
||||
@@ -239,6 +259,13 @@ Y.extend(DIALOGUE, Y.Panel, {
|
||||
}
|
||||
}
|
||||
this.keyDelegation();
|
||||
|
||||
// Only do accessibility hiding for modals because the ARIA spec
|
||||
// says that all ARIA dialogues should be modal.
|
||||
if (this.get('modal')) {
|
||||
// Make this dialogue visible to screen readers.
|
||||
this.setAccessibilityVisible();
|
||||
}
|
||||
}
|
||||
if (this.get('center') && !e.prevVal && e.newVal) {
|
||||
this.centerDialogue();
|
||||
@@ -338,13 +365,6 @@ Y.extend(DIALOGUE, Y.Panel, {
|
||||
this.lockScroll.enableScrollLock(this.shouldResizeFullscreen());
|
||||
}
|
||||
|
||||
// Only do accessibility hiding for modals because the ARIA spec
|
||||
// says that all ARIA dialogues should be modal.
|
||||
if (this.get('modal')) {
|
||||
// Make this dialogue visible to screen readers.
|
||||
this.setAccessibilityVisible();
|
||||
}
|
||||
|
||||
// Try and find a node to focus on using the focusOnShowSelector attribute.
|
||||
if (focusSelector !== null) {
|
||||
focusNode = this.get('boundingBox').one(focusSelector);
|
||||
@@ -372,11 +392,6 @@ Y.extend(DIALOGUE, Y.Panel, {
|
||||
}
|
||||
}
|
||||
|
||||
if (this.get('modal')) {
|
||||
// Hide this dialogue from screen readers.
|
||||
this.setAccessibilityHidden();
|
||||
}
|
||||
|
||||
// Unlock scroll if the plugin is present.
|
||||
if (this.lockScroll) {
|
||||
this.lockScroll.disableScrollLock();
|
||||
@@ -434,8 +449,6 @@ Y.extend(DIALOGUE, Y.Panel, {
|
||||
// Get the element that contains this dialogue because we need it
|
||||
// to filter out from the document.body child elements.
|
||||
var container = this.get(BASE);
|
||||
// Keep a record of any elements we change so that they can be reverted later.
|
||||
this.hiddenSiblings = [];
|
||||
|
||||
// We need to get a list containing each sibling element and the shallowest
|
||||
// non-ancestral nodes in the DOM. We can shortcut this a little by leveraging
|
||||
@@ -450,7 +463,7 @@ Y.extend(DIALOGUE, Y.Panel, {
|
||||
if (hidden !== 'true') {
|
||||
// Save their current state.
|
||||
node.setData('previous-aria-hidden', hidden);
|
||||
this.hiddenSiblings.push(node);
|
||||
this._hiddenSiblings.push(node);
|
||||
|
||||
// Hide this node from screen readers.
|
||||
node.set('aria-hidden', 'true');
|
||||
@@ -474,7 +487,7 @@ Y.extend(DIALOGUE, Y.Panel, {
|
||||
container.set('aria-hidden', 'true');
|
||||
|
||||
// Restore the sibling nodes back to their original values.
|
||||
Y.Array.each(this.hiddenSiblings, function(node) {
|
||||
Y.Array.each(this._hiddenSiblings, function(node) {
|
||||
var previousValue = node.getData('previous-aria-hidden');
|
||||
// If the element didn't previously have an aria-hidden attribute
|
||||
// then we can just remove the one we set.
|
||||
@@ -487,7 +500,7 @@ Y.extend(DIALOGUE, Y.Panel, {
|
||||
});
|
||||
|
||||
// Clear the cache. No longer need to store these.
|
||||
this.hiddenSiblings = [];
|
||||
this._hiddenSiblings = [];
|
||||
}
|
||||
}, {
|
||||
NAME : DIALOGUE_NAME,
|
||||
|
||||
@@ -445,7 +445,7 @@ class pdf extends \FPDI {
|
||||
$filename = \escapeshellarg($this->filename);
|
||||
$pagenoinc = \escapeshellarg($pageno + 1);
|
||||
$command = "$gsexec -q -sDEVICE=png16m -dSAFER -dBATCH -dNOPAUSE -r$imageres -dFirstPage=$pagenoinc -dLastPage=$pagenoinc ".
|
||||
"-dGraphicsAlphaBits=4 -dTextAlphaBits=4 -sOutputFile=$imagefilearg $filename";
|
||||
"-dDOINTERPOLATE -dGraphicsAlphaBits=4 -dTextAlphaBits=4 -sOutputFile=$imagefilearg $filename";
|
||||
|
||||
$output = null;
|
||||
$result = exec($command, $output);
|
||||
|
||||
@@ -208,7 +208,7 @@ $string['invalidurl'] = 'The URL you just entered is not valid';
|
||||
$string['jstemplate'] = 'Javascript template';
|
||||
$string['latitude'] = 'Latitude';
|
||||
$string['latlong'] = 'Latitude/longitude';
|
||||
$string['latlongboth'] = 'Both the Latitude and the Longitude must be filled in.';
|
||||
$string['latlongboth'] = 'Both latitude and longitude are required.';
|
||||
$string['latlongdownloadallhint'] = 'Download link for all entries as KML';
|
||||
$string['latlongkmllabelling'] = 'How to label items in KML files (Google Earth)';
|
||||
$string['latlonglinkservicesdisplayed'] = 'Link-out services to display';
|
||||
@@ -217,7 +217,7 @@ $string['list'] = 'View list';
|
||||
$string['listtemplate'] = 'List template';
|
||||
$string['longitude'] = 'Longitude';
|
||||
$string['manageapproved'] = 'Allow editing of approved entries';
|
||||
$string['manageapproved_help'] = 'If disabled, approved entries are not editable and deletable by its owner. This setting only takes effect if approval required is set to yes. Default is yes.';
|
||||
$string['manageapproved_help'] = 'If disabled, approved entries are no longer editable or deletable by the user who added them. This setting has no effect unless \'Approval required\' is enabled.';
|
||||
$string['mapexistingfield'] = 'Map to {$a}';
|
||||
$string['mapnewfield'] = 'Create a new field';
|
||||
$string['mappingwarning'] = 'All old fields not mapped to a new field will be lost and all data in that field will be removed.';
|
||||
|
||||
@@ -29,6 +29,7 @@ defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->dirroot . '/mod/forum/lib.php');
|
||||
require_once($CFG->dirroot . '/repository/lib.php');
|
||||
require_once($CFG->libdir . '/completionlib.php');
|
||||
|
||||
/**
|
||||
* A Handler to process replies to forum posts.
|
||||
@@ -244,6 +245,14 @@ class reply_handler extends \core\message\inbound\handler {
|
||||
$event->add_record_snapshot('forum_discussions', $discussion);
|
||||
$event->trigger();
|
||||
|
||||
// Update completion state.
|
||||
$completion = new \completion_info($course);
|
||||
if ($completion->is_enabled($cm) && ($forum->completionreplies || $forum->completionposts)) {
|
||||
$completion->update_state($cm, COMPLETION_COMPLETE);
|
||||
|
||||
mtrace("--> Updating completion status for user {$USER->id} in forum {$forum->id} for post {$addpost->id}.");
|
||||
}
|
||||
|
||||
mtrace("--> Created a post {$addpost->id} in {$discussion->id}.");
|
||||
return $addpost;
|
||||
}
|
||||
|
||||
@@ -242,6 +242,13 @@ function xmldb_forum_upgrade($oldversion) {
|
||||
|
||||
// Moodle v2.9.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
if ($oldversion < 2015102900) {
|
||||
// Groupid = 0 is never valid.
|
||||
$DB->set_field('forum_discussions', 'groupid', -1, array('groupid' => 0));
|
||||
|
||||
// Forum savepoint reached.
|
||||
upgrade_mod_savepoint(true, 2015102900, 'forum');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -239,3 +239,16 @@ Feature: Posting to all groups in a separate group discussion is restricted to u
|
||||
And the "Group" select box should contain "Group A"
|
||||
And the "Group" select box should contain "Group B"
|
||||
And I should see "Post a copy to all groups"
|
||||
|
||||
Scenario: Students can view all participants discussions in separate groups mode
|
||||
Given I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
When I add a new discussion to "Standard forum name" forum with:
|
||||
| Subject | Forum post to all participants |
|
||||
| Message | This is the body |
|
||||
| Group | All participants |
|
||||
And I log out
|
||||
And I log in as "student1"
|
||||
And I follow "Course 1"
|
||||
And I follow "Standard forum name"
|
||||
Then I should see "Forum post to all participants"
|
||||
|
||||
@@ -24,6 +24,6 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->version = 2015051103; // The current module version (Date: YYYYMMDDXX)
|
||||
$plugin->version = 2015102900; // The current module version (Date: YYYYMMDDXX)
|
||||
$plugin->requires = 2015050500; // Requires this Moodle version
|
||||
$plugin->component = 'mod_forum'; // Full name of the plugin (used for diagnostics)
|
||||
|
||||
+14
-12
@@ -420,18 +420,20 @@ function quiz_delete_attempt($attempt, $quiz) {
|
||||
question_engine::delete_questions_usage_by_activity($attempt->uniqueid);
|
||||
$DB->delete_records('quiz_attempts', array('id' => $attempt->id));
|
||||
|
||||
// Log the deletion of the attempt.
|
||||
$params = array(
|
||||
'objectid' => $attempt->id,
|
||||
'relateduserid' => $attempt->userid,
|
||||
'context' => context_module::instance($quiz->cmid),
|
||||
'other' => array(
|
||||
'quizid' => $quiz->id
|
||||
)
|
||||
);
|
||||
$event = \mod_quiz\event\attempt_deleted::create($params);
|
||||
$event->add_record_snapshot('quiz_attempts', $attempt);
|
||||
$event->trigger();
|
||||
// Log the deletion of the attempt if not a preview.
|
||||
if (!$attempt->preview) {
|
||||
$params = array(
|
||||
'objectid' => $attempt->id,
|
||||
'relateduserid' => $attempt->userid,
|
||||
'context' => context_module::instance($quiz->cmid),
|
||||
'other' => array(
|
||||
'quizid' => $quiz->id
|
||||
)
|
||||
);
|
||||
$event = \mod_quiz\event\attempt_deleted::create($params);
|
||||
$event->add_record_snapshot('quiz_attempts', $attempt);
|
||||
$event->trigger();
|
||||
}
|
||||
|
||||
// Search quiz_attempts for other instances by this user.
|
||||
// If none, then delete record for this quiz, this user from quiz_grades
|
||||
|
||||
@@ -54,7 +54,6 @@ Feature: Teachers can override the grade for any question
|
||||
And I should see "That is not a valid number."
|
||||
And I set the field "Mark" to "10.0"
|
||||
And I press "Save"
|
||||
And I should see "Changes saved"
|
||||
And I switch to the main window
|
||||
And I should see "Complete" in the "Manually graded 10 with comment: " "table_row"
|
||||
# This time is same as time the window is open. So wait for it to close before proceeding.
|
||||
|
||||
@@ -38,7 +38,12 @@ require_once($CFG->dirroot . '/mod/quiz/attemptlib.php');
|
||||
*/
|
||||
class mod_quiz_events_testcase extends advanced_testcase {
|
||||
|
||||
protected function prepare_quiz_data() {
|
||||
/**
|
||||
* Setup some convenience test data with a single attempt.
|
||||
*
|
||||
* @param bool $ispreview Make the attempt a preview attempt when true.
|
||||
*/
|
||||
protected function prepare_quiz_data($ispreview = false) {
|
||||
|
||||
$this->resetAfterTest(true);
|
||||
|
||||
@@ -75,7 +80,7 @@ class mod_quiz_events_testcase extends advanced_testcase {
|
||||
$quba->set_preferred_behaviour($quizobj->get_quiz()->preferredbehaviour);
|
||||
|
||||
$timenow = time();
|
||||
$attempt = quiz_create_attempt($quizobj, 1, false, $timenow);
|
||||
$attempt = quiz_create_attempt($quizobj, 1, false, $timenow, $ispreview);
|
||||
quiz_start_new_attempt($quizobj, $quba, $attempt, 1, $timenow);
|
||||
quiz_attempt_save_started($quizobj, $quba, $attempt);
|
||||
|
||||
@@ -283,6 +288,21 @@ class mod_quiz_events_testcase extends advanced_testcase {
|
||||
$this->assertEventContextNotUsed($event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that preview attempt deletions are not logged.
|
||||
*/
|
||||
public function test_preview_attempt_deleted() {
|
||||
// Create quiz with preview attempt.
|
||||
list($quizobj, $quba, $previewattempt) = $this->prepare_quiz_data(true);
|
||||
|
||||
// Delete a preview attempt, capturing events.
|
||||
$sink = $this->redirectEvents();
|
||||
quiz_delete_attempt($previewattempt, $quizobj->get_quiz());
|
||||
|
||||
// Verify that no events were generated.
|
||||
$this->assertEmpty($sink->get_events());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the report viewed event.
|
||||
*
|
||||
|
||||
@@ -63,7 +63,7 @@ $string['framesize'] = 'Frame height';
|
||||
$string['legacyfiles'] = 'Migration of old course file';
|
||||
$string['legacyfilesactive'] = 'Active';
|
||||
$string['legacyfilesdone'] = 'Finished';
|
||||
$string['modifieddate'] = 'Modified on {$a}';
|
||||
$string['modifieddate'] = 'Modified {$a}';
|
||||
$string['modulename'] = 'File';
|
||||
$string['modulename_help'] = 'The file module enables a teacher to provide a file as a course resource. Where possible, the file will be displayed within the course interface; otherwise students will be prompted to download it. The file may include supporting files, for example an HTML page may have embedded images or Flash objects.
|
||||
|
||||
@@ -115,4 +115,4 @@ $string['showtype_help'] = 'Displays the type of the file, such as \'Word docume
|
||||
If there are multiple files in this resource, the start file type is displayed.
|
||||
|
||||
If the file type is not known to the system, it will not display.';
|
||||
$string['uploadeddate'] = 'Uploaded on {$a}';
|
||||
$string['uploadeddate'] = 'Uploaded {$a}';
|
||||
|
||||
+10
-2
@@ -237,8 +237,16 @@ function resource_get_coursemodule_info($coursemodule) {
|
||||
|
||||
}
|
||||
|
||||
// If any optional extra details are turned on, store in custom data
|
||||
$info->customdata = $resource->displayoptions;
|
||||
// If any optional extra details are turned on, store in custom data,
|
||||
// add some file details as well to be used later by resource_get_optional_details() without retriving.
|
||||
// Do not store filedetails if this is a reference - they will still need to be retrieved every time.
|
||||
if (($filedetails = resource_get_file_details($resource, $coursemodule)) && empty($filedetails['isref'])) {
|
||||
$displayoptions = @unserialize($resource->displayoptions);
|
||||
$displayoptions['filedetails'] = $filedetails;
|
||||
$info->customdata = serialize($displayoptions);
|
||||
} else {
|
||||
$info->customdata = $resource->displayoptions;
|
||||
}
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
+86
-35
@@ -270,13 +270,75 @@ function resource_print_heading($resource, $cm, $course, $notused = false) {
|
||||
echo $OUTPUT->heading(format_string($resource->name), 2);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets details of the file to cache in course cache to be displayed using {@link resource_get_optional_details()}
|
||||
*
|
||||
* @param object $resource Resource table row (only property 'displayoptions' is used here)
|
||||
* @param object $cm Course-module table row
|
||||
* @return string Size and type or empty string if show options are not enabled
|
||||
*/
|
||||
function resource_get_file_details($resource, $cm) {
|
||||
$options = empty($resource->displayoptions) ? array() : @unserialize($resource->displayoptions);
|
||||
$filedetails = array();
|
||||
if (!empty($options['showsize']) || !empty($options['showtype']) || !empty($options['showdate'])) {
|
||||
$context = context_module::instance($cm->id);
|
||||
$fs = get_file_storage();
|
||||
$files = $fs->get_area_files($context->id, 'mod_resource', 'content', 0, 'sortorder DESC, id ASC', false);
|
||||
// For a typical file resource, the sortorder is 1 for the main file
|
||||
// and 0 for all other files. This sort approach is used just in case
|
||||
// there are situations where the file has a different sort order.
|
||||
$mainfile = $files ? reset($files) : null;
|
||||
if (!empty($options['showsize'])) {
|
||||
$filedetails['size'] = 0;
|
||||
foreach ($files as $file) {
|
||||
// This will also synchronize the file size for external files if needed.
|
||||
$filedetails['size'] += $file->get_filesize();
|
||||
if ($file->get_repository_id()) {
|
||||
// If file is a reference the 'size' attribute can not be cached.
|
||||
$filedetails['isref'] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!empty($options['showtype'])) {
|
||||
if ($mainfile) {
|
||||
$filedetails['type'] = get_mimetype_description($mainfile);
|
||||
// Only show type if it is not unknown.
|
||||
if ($filedetails['type'] === get_mimetype_description('document/unknown')) {
|
||||
$filedetails['type'] = '';
|
||||
}
|
||||
} else {
|
||||
$filedetails['type'] = '';
|
||||
}
|
||||
}
|
||||
if (!empty($options['showdate'])) {
|
||||
if ($mainfile) {
|
||||
// Modified date may be up to several minutes later than uploaded date just because
|
||||
// teacher did not submit the form promptly. Give teacher up to 5 minutes to do it.
|
||||
if ($mainfile->get_timemodified() > $mainfile->get_timecreated() + 5 * MINSECS) {
|
||||
$filedetails['modifieddate'] = $mainfile->get_timemodified();
|
||||
} else {
|
||||
$filedetails['uploadeddate'] = $mainfile->get_timecreated();
|
||||
}
|
||||
if ($mainfile->get_repository_id()) {
|
||||
// If main file is a reference the 'date' attribute can not be cached.
|
||||
$filedetails['isref'] = true;
|
||||
}
|
||||
} else {
|
||||
$filedetails['uploadeddate'] = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
return $filedetails;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets optional details for a resource, depending on resource settings.
|
||||
*
|
||||
* Result may include the file size and type if those settings are chosen,
|
||||
* or blank if none.
|
||||
*
|
||||
* @param object $resource Resource table row
|
||||
* @param object $resource Resource table row (only property 'displayoptions' is used here)
|
||||
* @param object $cm Course-module table row
|
||||
* @return string Size and type or empty string if show options are not enabled
|
||||
*/
|
||||
@@ -285,50 +347,39 @@ function resource_get_optional_details($resource, $cm) {
|
||||
|
||||
$details = '';
|
||||
|
||||
$options = empty($resource->displayoptions) ? array() : unserialize($resource->displayoptions);
|
||||
$options = empty($resource->displayoptions) ? array() : @unserialize($resource->displayoptions);
|
||||
if (!empty($options['showsize']) || !empty($options['showtype']) || !empty($options['showdate'])) {
|
||||
$context = context_module::instance($cm->id);
|
||||
if (!array_key_exists('filedetails', $options)) {
|
||||
$filedetails = resource_get_file_details($resource, $cm);
|
||||
} else {
|
||||
$filedetails = $options['filedetails'];
|
||||
}
|
||||
$size = '';
|
||||
$type = '';
|
||||
$date = '';
|
||||
$langstring = '';
|
||||
$infodisplayed = 0;
|
||||
$fs = get_file_storage();
|
||||
$files = $fs->get_area_files($context->id, 'mod_resource', 'content', 0, 'sortorder DESC, id ASC', false);
|
||||
if (!empty($options['showsize']) && count($files)) {
|
||||
$sizebytes = 0;
|
||||
foreach ($files as $file) {
|
||||
// this will also synchronize the file size for external files if needed
|
||||
$sizebytes += $file->get_filesize();
|
||||
if (!empty($options['showsize'])) {
|
||||
if (!empty($filedetails['size'])) {
|
||||
$size = display_size($filedetails['size']);
|
||||
$langstring .= 'size';
|
||||
$infodisplayed += 1;
|
||||
}
|
||||
if ($sizebytes) {
|
||||
$size = display_size($sizebytes);
|
||||
}
|
||||
$langstring .= 'size';
|
||||
$infodisplayed += 1;
|
||||
}
|
||||
if (!empty($options['showtype']) && count($files)) {
|
||||
// For a typical file resource, the sortorder is 1 for the main file
|
||||
// and 0 for all other files. This sort approach is used just in case
|
||||
// there are situations where the file has a different sort order
|
||||
$mainfile = reset($files);
|
||||
$type = get_mimetype_description($mainfile);
|
||||
// Only show type if it is not unknown
|
||||
if ($type === get_mimetype_description('document/unknown')) {
|
||||
$type = '';
|
||||
if (!empty($options['showtype'])) {
|
||||
if (!empty($filedetails['type'])) {
|
||||
$type = $filedetails['type'];
|
||||
$langstring .= 'type';
|
||||
$infodisplayed += 1;
|
||||
}
|
||||
$langstring .= 'type';
|
||||
$infodisplayed += 1;
|
||||
}
|
||||
if (!empty($options['showdate'])) {
|
||||
$mainfile = reset($files);
|
||||
$uploaddate = $mainfile->get_timecreated();
|
||||
$modifieddate = $mainfile->get_timemodified();
|
||||
|
||||
if ($modifieddate > $uploaddate) {
|
||||
$date = get_string('modifieddate', 'mod_resource', userdate($modifieddate));
|
||||
} else {
|
||||
$date = get_string('uploadeddate', 'mod_resource', userdate($uploaddate));
|
||||
if (!empty($options['showdate']) && (!empty($filedetails['modifieddate']) || !empty($filedetails['uploadeddate']))) {
|
||||
if (!empty($filedetails['modifieddate'])) {
|
||||
$date = get_string('modifieddate', 'mod_resource', userdate($filedetails['modifieddate'],
|
||||
get_string('strftimedatetimeshort', 'langconfig')));
|
||||
} else if (!empty($filedetails['uploadeddate'])) {
|
||||
$date = get_string('uploadeddate', 'mod_resource', userdate($filedetails['uploadeddate'],
|
||||
get_string('strftimedatetimeshort', 'langconfig')));
|
||||
}
|
||||
$langstring .= 'date';
|
||||
$infodisplayed += 1;
|
||||
|
||||
@@ -48,11 +48,11 @@ Feature: Teacher can specify different display options for the resource
|
||||
And I press "Save and display"
|
||||
Then I <seesize> see "6 bytes" in the ".resourcedetails" "css_element"
|
||||
And I <seetype> see "Text file" in the ".resourcedetails" "css_element"
|
||||
And I <seedate> see "2015" in the ".resourcedetails" "css_element"
|
||||
And I <seedate> see "Uploaded" in the ".resourcedetails" "css_element"
|
||||
And I follow "Course 1"
|
||||
And I <seesize> see "6 bytes" in the ".activity.resource .resourcelinkdetails" "css_element"
|
||||
And I <seetype> see "Text file" in the ".activity.resource .resourcelinkdetails" "css_element"
|
||||
And I <seedate> see "2015" in the ".activity.resource .resourcelinkdetails" "css_element"
|
||||
And I <seedate> see "Uploaded" in the ".activity.resource .resourcelinkdetails" "css_element"
|
||||
And I log out
|
||||
|
||||
Examples:
|
||||
|
||||
@@ -36,30 +36,30 @@ $string['draggableword'] = 'Draggable text';
|
||||
$string['dropbackground'] = 'Background image for dragging markers onto';
|
||||
$string['dropzone'] = 'Drop zone {$a}';
|
||||
$string['dropzoneheader'] = 'Drop zones';
|
||||
$string['formerror_disallowedtags'] = 'You have used html tags here that are not allowed in a draggable text drag item type.';
|
||||
$string['formerror_noallowedtags'] = 'No html tags are allowed in this text which is the alt text for a draggable image';
|
||||
$string['formerror_noytop'] = 'You must provide a value for the y coords for the top left corner of this drop area. You can drag and drop the drop area above to set the coordinates or enter them manually here.';
|
||||
$string['formerror_noxleft'] = 'You must provide a value for the x coords for the top left corner of this drop area. You can drag and drop the drop area above to set the coordinates or enter them manually here.';
|
||||
$string['formerror_disallowedtags'] = 'Sorry, HTML tags are not allowed in draggable text.';
|
||||
$string['formerror_noallowedtags'] = 'HTML tags are not allowed in this text which is the alt text for a draggable image.';
|
||||
$string['formerror_noytop'] = 'You must provide a value for the y coordinate for the top left corner of this drop area. You can drag and drop the drop area above to set the coordinates or enter them manually here.';
|
||||
$string['formerror_noxleft'] = 'You must provide a value for the x coordinate for the top left corner of this drop area. You can drag and drop the drop area above to set the coordinates or enter them manually here.';
|
||||
$string['formerror_nofile'] = 'You need to upload or select a file to use here.';
|
||||
$string['formerror_nofile3'] = 'You need to select an image file here, or delete the associated label and uncheck the infinite checkbox.';
|
||||
$string['formerror_notintytop'] = 'The y coords must be an integer.';
|
||||
$string['formerror_notintxleft'] = 'The x coords must be an integer.';
|
||||
$string['formerror_multipledraginstance'] = 'You have selected this image {$a} more than once as the correct choice for a drop zone but it is not marked as being an infinite drag item.';
|
||||
$string['formerror_multipledraginstance2'] = 'You have selected this image more than once as the correct choice for a drop zone but it is not marked as being an infinite drag item.';
|
||||
$string['formerror_nofile3'] = 'You need to select an image file here, or delete the associated label and uncheck the unlimited checkbox.';
|
||||
$string['formerror_notintytop'] = 'The y coordinate must be an integer.';
|
||||
$string['formerror_notintxleft'] = 'The x coordinate must be an integer.';
|
||||
$string['formerror_multipledraginstance'] = 'You have selected this image {$a} more than once as the correct choice for a drop zone but it is not marked as being an unlimited drag item.';
|
||||
$string['formerror_multipledraginstance2'] = 'You have selected this image more than once as the correct choice for a drop zone but it is not marked as being an unlimited drag item.';
|
||||
$string['formerror_noimageselected'] = 'You need to select a drag item to be the correct choice for this drop zone.';
|
||||
$string['formerror_nobgimage'] = 'You need to select an image to use as the background for the drag and drop area.';
|
||||
$string['infinite'] = 'Infinite';
|
||||
$string['infinite'] = 'Unlimited';
|
||||
$string['label'] = 'Text';
|
||||
$string['nolabel'] = 'No label text';
|
||||
$string['pleasedraganimagetoeachdropregion'] = 'Your answer is not complete, please drag an item to each drop region.';
|
||||
$string['pleasedraganimagetoeachdropregion'] = 'Your answer is not complete; please drag an item to each drop region.';
|
||||
$string['pluginname'] = 'Drag and drop onto image';
|
||||
$string['pluginname_help'] = 'Select a background image file, select draggable images or enter text and define the drop zones on the background image to which they must be dragged.';
|
||||
$string['pluginname_help'] = 'Drag and drop onto image questions require the respondent to drag images or text labels and drop them into defined drop zones on a background image. Draggable items may be grouped so that all items in a particular group are coloured the same as the drop zones. Draggable items marked as unlimited may be dropped into more than one zone.';
|
||||
$string['pluginname_link'] = 'question/type/ddimageortext';
|
||||
$string['pluginnameadding'] = 'Adding drag and drop onto image';
|
||||
$string['pluginnameediting'] = 'Editing drag and drop onto image';
|
||||
$string['pluginnamesummary'] = 'Images or text labels are dragged and dropped into drop zones on a background image.';
|
||||
$string['previewareaheader'] = 'Preview';
|
||||
$string['previewareamessage'] = 'Select a background image file and select draggable images or just enter text that will be made draggable. Then choose a drag item for each \'drop zone\', and drag the drag item to where the student should drag it to.';
|
||||
$string['previewareamessage'] = 'Select a background image, specify draggable items and define drop zones on the background image into which they must be dragged.';
|
||||
$string['refresh'] = 'Refresh preview';
|
||||
$string['shuffleimages'] = 'Shuffle drag items each time question is attempted';
|
||||
$string['summarisechoice'] = '{$a->no}. {$a->text}';
|
||||
|
||||
@@ -25,7 +25,7 @@ $string['addmoreitems'] = 'Blanks for {no} more markers';
|
||||
$string['alttext'] = 'Alt text';
|
||||
$string['answer'] = 'Answer';
|
||||
$string['bgimage'] = 'Background image';
|
||||
$string['coords'] = 'Coords';
|
||||
$string['coords'] = 'Coordinates';
|
||||
$string['correctansweris'] = 'The correct answer is: {$a}';
|
||||
$string['draggableimage'] = 'Draggable image';
|
||||
$string['draggableitem'] = 'Draggable item';
|
||||
@@ -36,7 +36,7 @@ $string['dropbackground'] = 'Background image for dragging markers onto';
|
||||
$string['dropzone'] = 'Drop zone {$a}';
|
||||
$string['dropzoneheader'] = 'Drop zones';
|
||||
$string['dropzones'] = 'Drop zones';
|
||||
$string['dropzones_help'] = 'The drop zones are defined by typing co-ordinates. As you type, the preview above is immediately updated, so you can position things by trial and error.
|
||||
$string['dropzones_help'] = 'The drop zones are defined by typing coordinates. As you type, the preview above is immediately updated, so you can position things by trial and improvement.
|
||||
|
||||
* Circle: centre_x, centre_y; radius<br>for example: <code>80, 100; 50</code>
|
||||
* Polygon: x1, y1; x2, y2; ...; xn, yn<br>for example: <code>20, 60; 100, 60; 20, 100</code>
|
||||
@@ -44,25 +44,25 @@ $string['dropzones_help'] = 'The drop zones are defined by typing co-ordinates.
|
||||
$string['followingarewrong'] = 'The following markers have been placed in the wrong area : {$a}.';
|
||||
$string['followingarewrongandhighlighted'] = 'The following markers were incorrectly placed : {$a}. Highlighted marker(s) are now shown with the correct placement(s).<br /> Click on the marker to highlight the allowed area.';
|
||||
$string['formerror_nobgimage'] = 'You need to select an image to use as the background for the drag and drop area.';
|
||||
$string['formerror_noitemselected'] = 'You have specified a drop zone but not chosen a marker that must be dragged to the zone';
|
||||
$string['formerror_noitemselected'] = 'You have specified a drop zone but not chosen a marker that must be dragged to the zone.';
|
||||
$string['formerror_nosemicolons'] = 'There are no semicolons in your coordinates string. Your coordinates for a {$a->shape} should be expressed as - {$a->coordsstring}.';
|
||||
$string['formerror_onlysometagsallowed'] = 'Only "{$a}" tags are allowed in the label for a marker';
|
||||
$string['formerror_onlyusewholepositivenumbers'] = 'Please use only whole positive numbers to specify x,y coords and/or width and height of shapes. Your coordinates for a {$a->shape} should be expressed as - {$a->coordsstring}.';
|
||||
$string['formerror_onlysometagsallowed'] = 'Only "{$a}" tags are allowed in the label for a marker.';
|
||||
$string['formerror_onlyusewholepositivenumbers'] = 'Please use only whole positive numbers to specify x,y coordinates and/or width and height of shapes. Your coordinates for a {$a->shape} should be expressed as - {$a->coordsstring}.';
|
||||
$string['formerror_polygonmusthaveatleastthreepoints'] = 'For a polygon shape you need to specify at least 3 points. Your coordinates for a {$a->shape} should be expressed as - {$a->coordsstring}.';
|
||||
$string['formerror_repeatedpoint'] = 'You have given the same point twice. Please remove the duplication. Your coordinates for a {$a->shape} should be expressed as - {$a->coordsstring}.';
|
||||
$string['formerror_shapeoutsideboundsofbgimage'] = 'The shape you have defined goes out of the bounds of the background image';
|
||||
$string['formerror_toomanysemicolons'] = 'There are too many semi colon separated parts to the coordinates you have specified. Your coordinates for a {$a->shape} should be expressed as - {$a->coordsstring}.';
|
||||
$string['formerror_unrecognisedwidthheightpart'] = 'We do not recognise the width and height you have specified. Your coordinates for a {$a->shape} should be expressed as - {$a->coordsstring}.';
|
||||
$string['formerror_unrecognisedxypart'] = 'We do not recognise the x,y coordinates you have specified. Your coordinates for a {$a->shape} should be expressed as - {$a->coordsstring}.';
|
||||
$string['formerror_repeatedpoint'] = 'You have entered the same coordinates twice. Each point must be unique. Your coordinates for a {$a->shape} should be expressed as - {$a->coordsstring}.';
|
||||
$string['formerror_shapeoutsideboundsofbgimage'] = 'The shape you have defined goes out of the bounds of the background image.';
|
||||
$string['formerror_toomanysemicolons'] = 'There are too many semicolon-separated parts to the coordinates you have specified. Your coordinates for a {$a->shape} should be expressed as - {$a->coordsstring}.';
|
||||
$string['formerror_unrecognisedwidthheightpart'] = 'The width and height that you have specified are unrecognisable. Your coordinates for a {$a->shape} should be expressed as - {$a->coordsstring}.';
|
||||
$string['formerror_unrecognisedxypart'] = 'The x,y coordinates that you have specified are unrecognisable. Your coordinates for a {$a->shape} should be expressed as - {$a->coordsstring}.';
|
||||
$string['infinite'] = 'Infinite';
|
||||
$string['marker'] = 'Marker';
|
||||
$string['marker_n'] = 'Marker {no}';
|
||||
$string['markers'] = 'Markers';
|
||||
$string['nolabel'] = 'No label text';
|
||||
$string['noofdrags'] = 'Number';
|
||||
$string['pleasedragatleastonemarker'] = 'Your answer is not complete, you must place at least one marker on the image.';
|
||||
$string['pleasedragatleastonemarker'] = 'Your answer is not complete; you must place at least one marker on the image.';
|
||||
$string['pluginname'] = 'Drag and drop markers';
|
||||
$string['pluginname_help'] = 'select a background image file, enter text labels for markers and define the drop zones on the background image to which they must be dragged.';
|
||||
$string['pluginname_help'] = 'Drag and drop markers require the respondent to drag text labels and drop them into defined drop zones on a background image.';
|
||||
$string['pluginname_link'] = 'question/type/ddmarker';
|
||||
$string['pluginnameadding'] = 'Adding drag and drop markers';
|
||||
$string['pluginnameediting'] = 'Editing drag and drop markers';
|
||||
@@ -74,13 +74,13 @@ $string['clearwrongparts'] = 'Move incorrectly placed markers back to default st
|
||||
$string['shape'] = 'Shape';
|
||||
$string['shape_circle'] = 'Circle';
|
||||
$string['shape_circle_lowercase'] = 'circle';
|
||||
$string['shape_circle_coords'] = 'x,y;r (where x,y are the xy coordinates of the centre of the circle and r is the radius)';
|
||||
$string['shape_circle_coords'] = 'x,y;r (where x,y are the coordinates of the centre of the circle and r is the radius)';
|
||||
$string['shape_rectangle'] = 'Rectangle';
|
||||
$string['shape_rectangle_lowercase'] = 'rectangle';
|
||||
$string['shape_rectangle_coords'] = 'x,y;w,h (where x,y are the xy coordinates of the top left corner of the rectangle and w and h are the width and height of the rectangle)';
|
||||
$string['shape_rectangle_coords'] = 'x,y;w,h (where x,y are the coordinates of the top left corner of the rectangle and w and h are the width and height of the rectangle)';
|
||||
$string['shape_polygon'] = 'Polygon';
|
||||
$string['shape_polygon_lowercase'] = 'polygon';
|
||||
$string['shape_polygon_coords'] = 'x1,y1;x2,y2;x3,y3;x4,y4....(where x1, y1 are the x,y coordinates of the first vertex, x2, y2 are the x,y coordinates of the second, etc. You do not need to repeat the coordinates for the first vertex to close the polygon)';
|
||||
$string['shape_polygon_coords'] = 'x1,y1;x2,y2;x3,y3;x4,y4... (where x1, y1 are the coordinates of the first vertex, x2, y2 are the coordinates of the second etc. There is no need to repeat the coordinates for the first vertex to close the polygon.)';
|
||||
$string['showmisplaced'] = 'Highlight drop zones which have not had the correct marker dropped on them';
|
||||
$string['shuffleimages'] = 'Shuffle drag items each time question is attempted';
|
||||
$string['stateincorrectlyplaced'] = 'State which markers are incorrectly placed';
|
||||
|
||||
@@ -214,6 +214,8 @@ class qtype_ddmarker_question extends qtype_ddtoimage_question_base {
|
||||
continue;
|
||||
}
|
||||
$pointxy = explode(',', $coord);
|
||||
$pointxy[0] = round($pointxy[0]);
|
||||
$pointxy[1] = round($pointxy[1]);
|
||||
if ($place->drop_hit($pointxy)) {
|
||||
if (!isset($hits[$placeno])) {
|
||||
$hits[$placeno] = array();
|
||||
|
||||
@@ -82,13 +82,13 @@ abstract class qtype_ddmarker_shape {
|
||||
* @return bool
|
||||
*/
|
||||
protected function is_point_in_bounding_box($pointxy, $xleftytop, $xrightybottom) {
|
||||
if ($pointxy[0] <= $xleftytop[0]) {
|
||||
if ($pointxy[0] < $xleftytop[0]) {
|
||||
return false;
|
||||
} else if ($pointxy[0] >= $xrightybottom[0]) {
|
||||
} else if ($pointxy[0] > $xrightybottom[0]) {
|
||||
return false;
|
||||
} else if ($pointxy[1] <= $xleftytop[1]) {
|
||||
} else if ($pointxy[1] < $xleftytop[1]) {
|
||||
return false;
|
||||
} else if ($pointxy[1] >= $xrightybottom[1]) {
|
||||
} else if ($pointxy[1] > $xrightybottom[1]) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -300,7 +300,7 @@ class qtype_ddmarker_shape_circle extends qtype_ddmarker_shape {
|
||||
|
||||
public function is_point_in_shape($xy) {
|
||||
$distancefromcentre = sqrt(pow(($xy[0] - $this->xcentre), 2) + pow(($xy[1] - $this->ycentre), 2));
|
||||
return $distancefromcentre < $this->radius;
|
||||
return $distancefromcentre <= $this->radius;
|
||||
}
|
||||
|
||||
public function center_point() {
|
||||
@@ -380,84 +380,69 @@ class qtype_ddmarker_shape_polygon extends qtype_ddmarker_shape {
|
||||
}
|
||||
|
||||
public function is_point_in_shape($xy) {
|
||||
$pointatinfinity = new qtype_ddmarker_point(-1000000, $xy[1] + 1);
|
||||
$pointtotest = new qtype_ddmarker_point($xy[0], $xy[1]);
|
||||
$testsegment = new qtype_ddmarker_segment($pointatinfinity, $pointtotest);
|
||||
// This code is based on the winding number algorithm from
|
||||
// http://geomalgorithms.com/a03-_inclusion.html
|
||||
// which comes with the following copyright notice:
|
||||
|
||||
// Copyright 2000 softSurfer, 2012 Dan Sunday
|
||||
// This code may be freely used, distributed and modified for any purpose
|
||||
// providing that this copyright notice is included with it.
|
||||
// SoftSurfer makes no warranty for this code, and cannot be held
|
||||
// liable for any real or imagined damage resulting from its use.
|
||||
// Users of this code must verify correctness for their application.
|
||||
|
||||
$point = new qtype_ddmarker_point($xy[0], $xy[1]);
|
||||
$windingnumber = 0;
|
||||
foreach ($this->coords as $index => $coord) {
|
||||
if ($index != 0) {
|
||||
$a = new qtype_ddmarker_point($this->coords[$index - 1][0],
|
||||
$this->coords[$index - 1][1]);
|
||||
$start = new qtype_ddmarker_point($this->coords[$index][0], $this->coords[$index][1]);
|
||||
if ($index < count($this->coords) - 1) {
|
||||
$endindex = $index + 1;
|
||||
} else {
|
||||
$a = new qtype_ddmarker_point($this->coords[count($this->coords) - 1][0],
|
||||
$this->coords[count($this->coords) - 1][1]);
|
||||
$endindex = 0;
|
||||
}
|
||||
$b = new qtype_ddmarker_point($this->coords[$index][0],
|
||||
$this->coords[$index][1]);
|
||||
$segment = new qtype_ddmarker_segment($a, $b);
|
||||
$intersects = $segment->intersects($testsegment);
|
||||
if ($intersects === null) {
|
||||
list($perturbedsegment, $testsegment) = $this->perturb($segment, $testsegment);
|
||||
if ($index !== 0) {
|
||||
$this->coords[$index - 1][0] = $perturbedsegment->a->x;
|
||||
$this->coords[$index - 1][1] = $perturbedsegment->a->y;
|
||||
} else {
|
||||
$this->coords[count($this->coords) - 1][0] = $perturbedsegment->a->x;
|
||||
$this->coords[count($this->coords) - 1][1] = $perturbedsegment->a->y;
|
||||
$end = new qtype_ddmarker_point($this->coords[$endindex][0], $this->coords[$endindex][1]);
|
||||
|
||||
if ($start->y <= $point->y) {
|
||||
if ($end->y >= $point->y) { // An upward crossing.
|
||||
$isleft = $this->is_left($start, $end, $point);
|
||||
if ($isleft == 0) {
|
||||
return true; // The point is on the line.
|
||||
} else if ($isleft > 0) {
|
||||
// A valid up intersect.
|
||||
$windingnumber += 1;
|
||||
}
|
||||
}
|
||||
$this->coords[$index][0] = $perturbedsegment->b->x;
|
||||
$this->coords[$index][1] = $perturbedsegment->b->y;
|
||||
$intersects = $perturbedsegment->intersects($testsegment);
|
||||
if ($intersects === null) {
|
||||
throw new coding_exception('Polygon hit test code failed '.
|
||||
'- Still touching end point after perturbation');
|
||||
} else if ($intersects) {
|
||||
$windingnumber++;
|
||||
} else {
|
||||
if ($end->y <= $point->y) { // A downward crossing.
|
||||
$isleft = $this->is_left($start, $end, $point);
|
||||
if ($isleft == 0) {
|
||||
return true; // The point is on the line.
|
||||
} else if ($this->is_left($start, $end, $point) < 0) {
|
||||
// A valid down intersect.
|
||||
$windingnumber -= 1;
|
||||
}
|
||||
}
|
||||
} else if ($intersects) {
|
||||
$windingnumber++;
|
||||
}
|
||||
}
|
||||
return ($windingnumber % 2) ? true : false;
|
||||
return $windingnumber != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* $v segment and this touch, move one of them slightly.
|
||||
* @param qtype_ddmarker_segment $v
|
||||
* @param int $ua
|
||||
* @param int $ub
|
||||
* Tests if a point is left / on / right of an infinite line.
|
||||
*
|
||||
* @param qtype_ddmarker_point $start first of two points on the infinite line.
|
||||
* @param qtype_ddmarker_point $end second of two points on the infinite line.
|
||||
* @param qtype_ddmarker_point $point the oint to test.
|
||||
* @return number > 0 if the point is left of the line.
|
||||
* = 0 if the point is on the line.
|
||||
* < 0 if the point is right of the line.
|
||||
*/
|
||||
public function perturb($p, $q) {
|
||||
list(, $ua, $ub) = $p->intersection_point($q);
|
||||
$pt = 0.00001; // Perturbation factor.
|
||||
$h = $p->a->dist($p->b);
|
||||
if ($ua == 0) {
|
||||
// ... q1, q2 intersects p1 exactly, move vertex p1 closer to p2.
|
||||
$a = ($pt * $p->a->dist(new qtype_ddmarker_point($p->b->x, $p->a->y))) / $h;
|
||||
$b = ($pt * $p->b->dist(new qtype_ddmarker_point($p->b->x, $p->a->y))) / $h;
|
||||
$p->a->x = $p->a->x + $a;
|
||||
$p->a->y = $p->a->y + $b;
|
||||
} else if ($ua == 1) {
|
||||
// ... q1, q2 intersects p2 exactly, move vertex p2 closer to p1.
|
||||
$a = ($pt * $p->a->dist(new qtype_ddmarker_point($p->b->x, $p->a->y))) / $h;
|
||||
$b = ($pt * $p->b->dist(new qtype_ddmarker_point($p->b->x, $p->a->y))) / $h;
|
||||
$p->b->x = $p->b->x - $a;
|
||||
$p->b->y = $p->b->y - $b;
|
||||
} else if ($ub == 0) {
|
||||
// ... p1, p2 intersects q1 exactly, move vertex q1 closer to q2.
|
||||
$a = ($pt * $q->a->dist(new qtype_ddmarker_point($q->b->x, $q->a->y))) / $h;
|
||||
$b = ($pt * $q->b->dist(new qtype_ddmarker_point($q->b->x, $q->a->y))) / $h;
|
||||
$q->a->x = $q->a->x + $a;
|
||||
$q->a->y = $q->a->y + $b;
|
||||
} else if ($ub == 1) {
|
||||
// ... p1, p2 intersects q2 exactly, move vertex q2 closer to q1.
|
||||
$a = ($pt * $q->a->dist(new qtype_ddmarker_point($q->b->x, $q->a->y))) / $h;
|
||||
$b = ($pt * $q->b->dist(new qtype_ddmarker_point($q->b->x, $q->a->y))) / $h;
|
||||
$q->b->x = $q->b->x - $a;
|
||||
$q->b->y = $q->b->y - $b;
|
||||
}
|
||||
return array($p, $q);
|
||||
protected function is_left(qtype_ddmarker_point $start, qtype_ddmarker_point $end,
|
||||
qtype_ddmarker_point $point) {
|
||||
return ($end->x - $start->x) * ($point->y - $start->y)
|
||||
- ($point->x - $start->x) * ($end->y - $start->y);
|
||||
}
|
||||
|
||||
public function center_point() {
|
||||
$center = array(round(($this->minxy[0] + $this->maxxy[0]) / 2),
|
||||
round(($this->minxy[1] + $this->maxxy[1]) / 2));
|
||||
@@ -466,7 +451,6 @@ class qtype_ddmarker_shape_polygon extends qtype_ddmarker_shape {
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -495,69 +479,3 @@ class qtype_ddmarker_point {
|
||||
return sqrt(pow($this->x - $other->x, 2) + pow($this->y - $other->y, 2));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Defines a segment between two end points a and b.
|
||||
*
|
||||
* @copyright 2012 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class qtype_ddmarker_segment {
|
||||
/** @var object First point */
|
||||
public $a;
|
||||
|
||||
/** @var object Second point */
|
||||
public $b;
|
||||
|
||||
public function __construct(qtype_ddmarker_point $a, qtype_ddmarker_point $b) {
|
||||
$this->a = $a;
|
||||
$this->b = $b;
|
||||
}
|
||||
/**
|
||||
* Find if this segment intersects another segment $v.
|
||||
* @param segment $v
|
||||
* @return boolean does it intersect?
|
||||
*/
|
||||
public function intersects(qtype_ddmarker_segment $v) {
|
||||
// Algorithm from: http://astronomy.swin.edu.au/~pbourke/geometry/lineline2d/
|
||||
// $this is P1 to P2 and $v is P3 to P4.
|
||||
list($d, $ua, $ub) = $this->intersection_point($v);
|
||||
if ($d !== 0) { // The lines intersect at a point somewhere
|
||||
// The values of $ua and $ub tell us where the intersection occurred.
|
||||
if ( (($ua == 0 || $ua == 1 )&&($ub >= 0 && $ub <= 1))
|
||||
|| (($ub == 0 || $ub == 1) && ($ua >= 0 && $ua <= 1))) {
|
||||
// A value of exactly 0 or 1 means the intersection occurred right at the
|
||||
// start or end of the line segment. For our purposes we will consider this
|
||||
// NOT to be an intersection away from the intersecting line.
|
||||
// Degenerate case - segment exactly touches a line.
|
||||
return null;
|
||||
} else if (($ua > 0 && $ua < 1) && ($ub > 0 && $ub < 1)) {
|
||||
// A value between 0 and 1 means the intersection occurred within the
|
||||
// line segment.
|
||||
// Intersection occurs on both line segments.
|
||||
return true;
|
||||
} else {
|
||||
// The lines do not intersect within the line segments.
|
||||
return false;
|
||||
}
|
||||
} else { // The lines do not intersect.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function intersection_point(qtype_ddmarker_segment $v) {
|
||||
$d = (($v->b->y - $v->a->y) * ($this->b->x - $this->a->x)) -
|
||||
(($v->b->x - $v->a->x) * ($this->b->y - $this->a->y));
|
||||
if ($d != 0) { // The lines intersect at a point somewhere.
|
||||
$ua = (($v->b->x - $v->a->x) * ($this->a->y - $v->a->y) -
|
||||
($v->b->y - $v->a->y) * ($this->a->x - $v->a->x)) / $d;
|
||||
$ub = (($this->b->x - $this->a->x) * ($this->a->y - $v->a->y) -
|
||||
($this->b->y - $this->a->y) * ($this->a->x - $v->a->x)) / $d;
|
||||
} else {
|
||||
$ua = null;
|
||||
$ub = null;
|
||||
}
|
||||
return array($d, $ua, $ub);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ form.mform fieldset#id_previewareaheader div.draghome, form.mform fieldset#id_pr
|
||||
.que.ddmarker div.dragitems span.markertext,
|
||||
.que.ddmarker div.markertexts span.markertext,
|
||||
form.mform fieldset#id_previewareaheader div.markertexts span.markertext {
|
||||
margin: 5px;
|
||||
margin: 0 5px;
|
||||
z-index: 3;
|
||||
background-color: white;
|
||||
border: 2px solid black;
|
||||
@@ -70,8 +70,8 @@ form.mform fieldset#id_previewareaheader div.markertexts span.markertext {
|
||||
}
|
||||
.que.ddmarker div.dragitems img.target {
|
||||
position: absolute;
|
||||
left: -7px;
|
||||
top: -3px;
|
||||
left: -7px; /* This must be half the size of the target image, minus 0.5. */
|
||||
top: -7px; /* In other words, this works for a 15x15 cross-hair. */
|
||||
}
|
||||
.que.ddmarker div.dragitems div.draghome img.target {
|
||||
display: none;
|
||||
|
||||
@@ -51,7 +51,7 @@ Feature: Test creating a drag and drop markers question
|
||||
|
||||
# Try to submit without setting the last marker.
|
||||
And I press "id_submitbutton"
|
||||
Then I should see "You have specified a drop zone but not chosen a marker that must be dragged to the zone"
|
||||
Then I should see "You have specified a drop zone but not chosen a marker that must be dragged to the zone."
|
||||
|
||||
# Set the last marker and submit again.
|
||||
And I set the field "id_drops_3_choice" to "2"
|
||||
|
||||
@@ -130,11 +130,11 @@ class qtype_ddmarker_question_test extends basic_testcase {
|
||||
|
||||
// The second returned param in array is the max of correct choices or
|
||||
// the actual number of items dragged.
|
||||
$response1 = array('c1' => '50,50', 'c2' => '100,100', 'c3' => '100,100;200,200');
|
||||
$response1 = array('c1' => '50,50', 'c2' => '110,110', 'c3' => '90,90;210,210');
|
||||
$this->assertEquals(array(1, 4), $dd->get_num_parts_right($response1));
|
||||
$response2 = array('c1' => '50,50;150,50;50,150',
|
||||
'c2' => '100,100',
|
||||
'c3' => '100,100;200,200');
|
||||
'c2' => '110,110',
|
||||
'c3' => '90,90;210,210');
|
||||
$this->assertEquals(array(1, 6), $dd->get_num_parts_right($response2));
|
||||
$response3 = array('c1' => '50,50;150,50;50,150',
|
||||
'c2' => '',
|
||||
|
||||
@@ -75,6 +75,16 @@ class qtype_ddmarker_shapes_test extends basic_testcase {
|
||||
$this->assertTrue($shape->is_point_in_shape(array(11, 11)));
|
||||
$this->assertTrue($shape->is_point_in_shape(array(19, 19)));
|
||||
|
||||
// Test points right on the edge are in.
|
||||
$this->assertTrue($shape->is_point_in_shape(array(10, 10)));
|
||||
$this->assertTrue($shape->is_point_in_shape(array(10, 20)));
|
||||
$this->assertTrue($shape->is_point_in_shape(array(20, 20)));
|
||||
$this->assertTrue($shape->is_point_in_shape(array(20, 10)));
|
||||
$this->assertTrue($shape->is_point_in_shape(array(10, 15)));
|
||||
$this->assertTrue($shape->is_point_in_shape(array(15, 10)));
|
||||
$this->assertTrue($shape->is_point_in_shape(array(20, 15)));
|
||||
$this->assertTrue($shape->is_point_in_shape(array(15, 20)));
|
||||
|
||||
// Should accept closed polygon coords or unclosed and it will model a closed polygon.
|
||||
$shape = new qtype_ddmarker_shape_polygon('10, 10; 20, 10; 20, 20; 10, 20; 10, 10');
|
||||
$this->assertTrue($shape->is_point_in_shape(array(15, 15)));
|
||||
@@ -114,8 +124,13 @@ class qtype_ddmarker_shapes_test extends basic_testcase {
|
||||
public function test_circle_hit_test() {
|
||||
$shape = new qtype_ddmarker_shape_circle('10, 10; 10');
|
||||
$this->assertTrue($shape->is_point_in_shape(array(19, 10)));
|
||||
$this->assertFalse($shape->is_point_in_shape(array(20, 10)));
|
||||
$this->assertTrue($shape->is_point_in_shape(array(20, 10)));
|
||||
$this->assertFalse($shape->is_point_in_shape(array(21, 10)));
|
||||
|
||||
$this->assertTrue($shape->is_point_in_shape(array(10, 1)));
|
||||
$this->assertTrue($shape->is_point_in_shape(array(10, 0)));
|
||||
$this->assertFalse($shape->is_point_in_shape(array(10, -1)));
|
||||
|
||||
$this->assertFalse($shape->is_point_in_shape(array(15, 25)));
|
||||
$this->assertFalse($shape->is_point_in_shape(array(25, 15)));
|
||||
$this->assertTrue($shape->is_point_in_shape(array(11, 11)));
|
||||
@@ -123,6 +138,9 @@ class qtype_ddmarker_shapes_test extends basic_testcase {
|
||||
$this->assertTrue($shape->is_point_in_shape(array(17, 17)));
|
||||
$this->assertTrue($shape->is_point_in_shape(array(3, 3)));
|
||||
$this->assertFalse($shape->is_point_in_shape(array(2, 2)));
|
||||
|
||||
// Should be exactly on the boundary - 3, 4, 5 right-angled triangle.
|
||||
$this->assertTrue($shape->is_point_in_shape(array(16, 18)));
|
||||
}
|
||||
|
||||
public function test_rectangle_valdiation_test() {
|
||||
@@ -132,12 +150,16 @@ class qtype_ddmarker_shapes_test extends basic_testcase {
|
||||
|
||||
public function test_rectangle_hit_test() {
|
||||
$shape = new qtype_ddmarker_shape_rectangle('1000, 4000; 500, 400');
|
||||
$this->assertTrue($shape->is_point_in_shape(array(1001, 4001)));
|
||||
$this->assertFalse($shape->is_point_in_shape(array(1000, 4000)));
|
||||
$this->assertFalse($shape->is_point_in_shape(array(501, 3601)));
|
||||
$this->assertTrue($shape->is_point_in_shape(array(1499, 4399)));
|
||||
$this->assertFalse($shape->is_point_in_shape(array(25, 15)));
|
||||
$this->assertTrue($shape->is_point_in_shape(array(1001, 4399)));
|
||||
$this->assertTrue($shape->is_point_in_shape(array(1499, 4001)));
|
||||
$this->assertFalse($shape->is_point_in_shape(array(999, 4200)));
|
||||
$this->assertTrue($shape->is_point_in_shape(array(1000, 4200)));
|
||||
$this->assertTrue($shape->is_point_in_shape(array(1001, 4200)));
|
||||
$this->assertTrue($shape->is_point_in_shape(array(1499, 4200)));
|
||||
$this->assertTrue($shape->is_point_in_shape(array(1500, 4200)));
|
||||
$this->assertFalse($shape->is_point_in_shape(array(1501, 4200)));
|
||||
|
||||
$this->assertFalse($shape->is_point_in_shape(array(1250, 3999)));
|
||||
$this->assertTrue($shape->is_point_in_shape(array(1250, 4000)));
|
||||
$this->assertTrue($shape->is_point_in_shape(array(1250, 4400)));
|
||||
$this->assertFalse($shape->is_point_in_shape(array(1250, 4401)));
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+2
@@ -133,6 +133,8 @@ Y.extend(DDMARKER_DD, Y.Base, {
|
||||
return colour;
|
||||
},
|
||||
convert_to_window_xy : function (bgimgxy) {
|
||||
// The +1 seems rather odd, but seems to give the best results in
|
||||
// the three main browsers at a range of zoom levels.
|
||||
return [Number(bgimgxy[0]) + this.doc.bg_img().getX() + 1,
|
||||
Number(bgimgxy[1]) + this.doc.bg_img().getY() + 1];
|
||||
},
|
||||
|
||||
+2
@@ -133,6 +133,8 @@ Y.extend(DDMARKER_DD, Y.Base, {
|
||||
return colour;
|
||||
},
|
||||
convert_to_window_xy : function (bgimgxy) {
|
||||
// The +1 seems rather odd, but seems to give the best results in
|
||||
// the three main browsers at a range of zoom levels.
|
||||
return [Number(bgimgxy[0]) + this.doc.bg_img().getX() + 1,
|
||||
Number(bgimgxy[1]) + this.doc.bg_img().getY() + 1];
|
||||
},
|
||||
|
||||
Vendored
+1
-1
@@ -98,7 +98,7 @@ Y.extend(DDMARKER_FORM, M.qtype_ddmarker.dd_base_class, {
|
||||
}
|
||||
if (this.doc.bg_img()) {
|
||||
Y.one('div.ddarea .grid')
|
||||
.setXY(this.doc.bg_img().getXY())
|
||||
.setXY(this.convert_to_window_xy([0, 0]))
|
||||
.setStyle('width', this.doc.bg_img().get('width'))
|
||||
.setStyle('height', this.doc.bg_img().get('height'));
|
||||
}
|
||||
|
||||
Vendored
+1
-1
@@ -1 +1 @@
|
||||
YUI.add("moodle-qtype_ddmarker-form",function(e,t){var n="moodle-qtype_ddmarker-form",r=function(){r.superclass.constructor.apply(this,arguments)};e.extend(r,M.qtype_ddmarker.dd_base_class,{fp:null,initializer:function(){var t="qtype_ddmarker-form-"+Math.random().toString(36).slice(2);M.util.js_pending(t),this.fp=this.file_pickers();var n=e.one(this.get("topnode"));n.one("div.fcontainer").append('<div class="ddarea"><div class="markertexts"></div><div class="droparea"></div><div class="dropzones"></div><div class="grid"></div></div>'),this.doc=this.doc_structure(this),this.stop_selector_events(),this.set_options_for_drag_item_selectors(),this.setup_form_events(),e.later(500,this,this.update_drop_zones,[t],!0),e.after(this.load_bg_image,M.form_filepicker,"callback",this),this.load_bg_image()},load_bg_image:function(){var t=this.fp.file("bgimage").href;if(t!==null){this.doc.load_bg_img(t);var n=new e.DD.Drop({node:this.doc.bg_img()});n.on("drop:hit",function(e){e.drag.get("node").setData("gooddrop",!0)}),this.afterimageloaddone=!1,this.doc.bg_img().on("load",this.constrain_image_size,this)}},constrain_image_size:function(e){var t=this.get("maxsizes").bgimage,n=Math.max(e.target.get("width")/t.width,e.target.get("height")/t.height);n>1&&e.target.set("width",Math.floor(e.target.get("width")/n)),e.target.addClass("constrained"),e.target.detach("load",this.constrain_image_size)},update_drop_zones:function(t){this.graphics!==null&&this.graphics.destroy(),this.restart_colours(),this.graphics=new e.Graphic({render:"div.ddarea div.dropzones"});var n=this.form.get_form_value("nodropzone",[]);for(var r=0;r<n;r++){var i=this.form.get_form_value("drops",[r,"choice"]),s=this.get_marker_text(i),o=this.form.get_form_value("drops",[r,"shape"]),u=this.get_coords(r),a=this.get_next_colour();e.one("input#id_drops_"+r+"_coords").setStyle("background-color",a),this.draw_drop_zone(r,s,o,u,a,!1)}this.doc.bg_img()&&e.one("div.ddarea .grid").setXY(this.doc.bg_img().getXY()).setStyle("width",this.doc.bg_img().get("width")).setStyle("height",this.doc.bg_img().get("height")),M.util.js_complete(t)},get_coords:function(e){var t=this.form.get_form_value("drops",[e,"coords"]);return t.replace(new RegExp("\\s*","g"),"")},get_marker_text:function(e){if(Number(e)!==0){var t=this.form.get_form_value("drags",[e-1,"label"]);return t.replace(new RegExp("^\\s*(.*)\\s*$"),"$1")}return""},set_options_for_drag_item_selectors:function(){var t={0:""};for(var n=1;n<=this.form.get_form_value("noitems",[]);n++){var r=this.get_marker_text(n);r!==""&&(t[n]=e.Escape.html(r))}var i=[],s;for(n=0;n<this.form.get_form_value("nodropzone",[]);n++)s=e.one("#id_drops_"+n+"_choice"),i[n]=Number(s.get("value"));for(n=0;n<this.form.get_form_value("nodropzone",[]);n++){s=e.one("#id_drops_"+n+"_choice"),s.all("option").remove(!0);for(var o in t){o=Number(o);var u='<option value="'+o+'">'+t[o]+"</option>";s.append(u);var a=s.one('option[value="'+o+'"]');if(o===i[n])a.set("selected",!0);else if(o!==0){var f=this.form.get_form_value("drags",[o-1,"noofdrags"]);if(Number(f)!==0)for(var l in i)if(Number(i[l])===o){if(Number(f)===1){a.set("disabled",!0);break}f--}}}}},stop_selector_events:function(){e.all("fieldset#id_dropzoneheader select").detachAll()},setup_form_events:function(){e.all("fieldset#id_draggableitemheader input").on("change",function(){this.set_options_for_drag_item_selectors()},this),e.all("fieldset#id_draggableitemheader select").on("change",function(){this.set_options_for_drag_item_selectors()},this),e.all("fieldset#id_dropzoneheader select").on("change",function(){this.set_options_for_drag_item_selectors()},this)},form:{to_name_with_index:function(e,t){var n=e;for(var r=0;r<t.length;r++)n=n+"["+t[r]+"]";return n},get_el:function(e,t){var n=document.getElementById("mform1");return n.elements[this.to_name_with_index(e,t)]},get_form_value:function(e,t){var n=this.get_el(e,t);return n.type==="checkbox"?n.checked:n.value},set_form_value:function(e,t,n){var r=this.get_el(e,t);r.type==="checkbox"?r.checked=n:r.value=n},from_name_with_index:function(e){var t={};t.indexes=[];var n=e.indexOf("[");t.name=e.substring(0,n);while(n!==-1){var r=e.indexOf("]",n+1);t.indexes.push(e.substring(n+1,r)),n=e.indexOf("[",r+1)}return t}},file_pickers:function(){var t,n;if(t===undefined){t={},n={};var r=e.all("form.mform input.filepickerhidden");r.each(function(e){t[e.get("value")]=e.get("name"),n[e.get("name")]=e.get("parentNode")},this)}var i={file:function(e){var t=n[e],r=t.one("div.filepicker-filelist a");return r?{href:r.get("href"),name:r.get("innerHTML")}:{href:null,name:null}},name:function(e){return t[e]}};return i}},{NAME:n,ATTRS:{maxsizes:{value:null}}}),M.qtype_ddmarker=M.qtype_ddmarker||{},M.qtype_ddmarker.init_form=function(e){return new r(e)}},"@VERSION@",{requires:["moodle-qtype_ddmarker-dd","form_filepicker","graphics","escape"]});
|
||||
YUI.add("moodle-qtype_ddmarker-form",function(e,t){var n="moodle-qtype_ddmarker-form",r=function(){r.superclass.constructor.apply(this,arguments)};e.extend(r,M.qtype_ddmarker.dd_base_class,{fp:null,initializer:function(){var t="qtype_ddmarker-form-"+Math.random().toString(36).slice(2);M.util.js_pending(t),this.fp=this.file_pickers();var n=e.one(this.get("topnode"));n.one("div.fcontainer").append('<div class="ddarea"><div class="markertexts"></div><div class="droparea"></div><div class="dropzones"></div><div class="grid"></div></div>'),this.doc=this.doc_structure(this),this.stop_selector_events(),this.set_options_for_drag_item_selectors(),this.setup_form_events(),e.later(500,this,this.update_drop_zones,[t],!0),e.after(this.load_bg_image,M.form_filepicker,"callback",this),this.load_bg_image()},load_bg_image:function(){var t=this.fp.file("bgimage").href;if(t!==null){this.doc.load_bg_img(t);var n=new e.DD.Drop({node:this.doc.bg_img()});n.on("drop:hit",function(e){e.drag.get("node").setData("gooddrop",!0)}),this.afterimageloaddone=!1,this.doc.bg_img().on("load",this.constrain_image_size,this)}},constrain_image_size:function(e){var t=this.get("maxsizes").bgimage,n=Math.max(e.target.get("width")/t.width,e.target.get("height")/t.height);n>1&&e.target.set("width",Math.floor(e.target.get("width")/n)),e.target.addClass("constrained"),e.target.detach("load",this.constrain_image_size)},update_drop_zones:function(t){this.graphics!==null&&this.graphics.destroy(),this.restart_colours(),this.graphics=new e.Graphic({render:"div.ddarea div.dropzones"});var n=this.form.get_form_value("nodropzone",[]);for(var r=0;r<n;r++){var i=this.form.get_form_value("drops",[r,"choice"]),s=this.get_marker_text(i),o=this.form.get_form_value("drops",[r,"shape"]),u=this.get_coords(r),a=this.get_next_colour();e.one("input#id_drops_"+r+"_coords").setStyle("background-color",a),this.draw_drop_zone(r,s,o,u,a,!1)}this.doc.bg_img()&&e.one("div.ddarea .grid").setXY(this.convert_to_window_xy([0,0])).setStyle("width",this.doc.bg_img().get("width")).setStyle("height",this.doc.bg_img().get("height")),M.util.js_complete(t)},get_coords:function(e){var t=this.form.get_form_value("drops",[e,"coords"]);return t.replace(new RegExp("\\s*","g"),"")},get_marker_text:function(e){if(Number(e)!==0){var t=this.form.get_form_value("drags",[e-1,"label"]);return t.replace(new RegExp("^\\s*(.*)\\s*$"),"$1")}return""},set_options_for_drag_item_selectors:function(){var t={0:""};for(var n=1;n<=this.form.get_form_value("noitems",[]);n++){var r=this.get_marker_text(n);r!==""&&(t[n]=e.Escape.html(r))}var i=[],s;for(n=0;n<this.form.get_form_value("nodropzone",[]);n++)s=e.one("#id_drops_"+n+"_choice"),i[n]=Number(s.get("value"));for(n=0;n<this.form.get_form_value("nodropzone",[]);n++){s=e.one("#id_drops_"+n+"_choice"),s.all("option").remove(!0);for(var o in t){o=Number(o);var u='<option value="'+o+'">'+t[o]+"</option>";s.append(u);var a=s.one('option[value="'+o+'"]');if(o===i[n])a.set("selected",!0);else if(o!==0){var f=this.form.get_form_value("drags",[o-1,"noofdrags"]);if(Number(f)!==0)for(var l in i)if(Number(i[l])===o){if(Number(f)===1){a.set("disabled",!0);break}f--}}}}},stop_selector_events:function(){e.all("fieldset#id_dropzoneheader select").detachAll()},setup_form_events:function(){e.all("fieldset#id_draggableitemheader input").on("change",function(){this.set_options_for_drag_item_selectors()},this),e.all("fieldset#id_draggableitemheader select").on("change",function(){this.set_options_for_drag_item_selectors()},this),e.all("fieldset#id_dropzoneheader select").on("change",function(){this.set_options_for_drag_item_selectors()},this)},form:{to_name_with_index:function(e,t){var n=e;for(var r=0;r<t.length;r++)n=n+"["+t[r]+"]";return n},get_el:function(e,t){var n=document.getElementById("mform1");return n.elements[this.to_name_with_index(e,t)]},get_form_value:function(e,t){var n=this.get_el(e,t);return n.type==="checkbox"?n.checked:n.value},set_form_value:function(e,t,n){var r=this.get_el(e,t);r.type==="checkbox"?r.checked=n:r.value=n},from_name_with_index:function(e){var t={};t.indexes=[];var n=e.indexOf("[");t.name=e.substring(0,n);while(n!==-1){var r=e.indexOf("]",n+1);t.indexes.push(e.substring(n+1,r)),n=e.indexOf("[",r+1)}return t}},file_pickers:function(){var t,n;if(t===undefined){t={},n={};var r=e.all("form.mform input.filepickerhidden");r.each(function(e){t[e.get("value")]=e.get("name"),n[e.get("name")]=e.get("parentNode")},this)}var i={file:function(e){var t=n[e],r=t.one("div.filepicker-filelist a");return r?{href:r.get("href"),name:r.get("innerHTML")}:{href:null,name:null}},name:function(e){return t[e]}};return i}},{NAME:n,ATTRS:{maxsizes:{value:null}}}),M.qtype_ddmarker=M.qtype_ddmarker||{},M.qtype_ddmarker.init_form=function(e){return new r(e)}},"@VERSION@",{requires:["moodle-qtype_ddmarker-dd","form_filepicker","graphics","escape"]});
|
||||
|
||||
+1
-1
@@ -98,7 +98,7 @@ Y.extend(DDMARKER_FORM, M.qtype_ddmarker.dd_base_class, {
|
||||
}
|
||||
if (this.doc.bg_img()) {
|
||||
Y.one('div.ddarea .grid')
|
||||
.setXY(this.doc.bg_img().getXY())
|
||||
.setXY(this.convert_to_window_xy([0, 0]))
|
||||
.setStyle('width', this.doc.bg_img().get('width'))
|
||||
.setStyle('height', this.doc.bg_img().get('height'));
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user