Compare commits

...

112 Commits

Author SHA1 Message Date
Eloy Lafuente (stronk7) 4cef723c22 Moodle release 3.0rc1 2015-11-03 19:00:58 +01:00
Eloy Lafuente (stronk7) 561413695d Merge branch 'install_master' of https://git.in.moodle.com/amosbot/moodle-install 2015-11-03 19:00:41 +01:00
Eloy Lafuente (stronk7) 217c3e0fc9 Merge branch 'wip-mdl-52007' of https://github.com/rajeshtaneja/moodle 2015-11-03 17:56:23 +01:00
Eloy Lafuente (stronk7) 836c6d7883 Merge branch 'wip-mdl-52020' of https://github.com/rajeshtaneja/moodle 2015-11-03 17:56:22 +01:00
Dan Poltawski 728754de6b Merge branch 'MDL-52018-master-enfix' of git://github.com/mudrd8mz/moodle 2015-11-03 17:56:21 +01:00
Andrew Nicols 40a44cdd52 Merge branch 'MDL-51993-master' of git://github.com/cameron1729/moodle 2015-11-03 17:56:21 +01:00
AMOS bot 52617af3c9 Automatically generated installer lang files 2015-11-04 00:04:21 +08:00
rajesh Taneja 180fdb2f7c MDL-52007 behat: Use extended timeout for site admin expansion 2015-11-03 16:08:27 +08:00
Cameron Ball c47b6f97d7 MDL-51993 course: Set valid default for section_edit_control_menu
Thank you to Gareth Barnard for originally providing this patch.
2015-11-03 10:29:38 +08:00
Andrew Nicols 80ad34e720 Merge branch 'MDL-51914' of git://github.com/timhunt/moodle 2015-11-03 09:42:04 +08:00
rajesh Taneja f497fdcc21 MDL-52020 behat: Removed text checking to ensure window switch
Comment question window closes after 2 sec.
which crashes win-FF as it's slow to check for
"Changes saved" text. As after switching it is
checked the changes are saved, this step can be
avoided to ensure robustness of the scenario
2015-11-03 09:40:33 +08:00
Tim Hunt 0b4b0a7eea MDL-51914 qtype_ddmarker: fix checking points on the boundary
All points on the boundary are now considered in the shape, on the
grounds that it is better to grade someone right for an edge case.
2015-11-02 22:06:23 +00:00
David Mudrák e8b75e6292 MDL-52018 lang: Fix behat tests to use the new strings 2015-11-02 22:30:46 +01:00
Helen Foster da6ecfd8c1 MDL-52018 lang: Merge English strings from the en_fix language pack 2015-11-02 22:12:06 +01:00
Eloy Lafuente (stronk7) 2cc040c6c0 Merge branch 'MDL-52014-master' of git://github.com/danpoltawski/moodle 2015-11-02 20:02:16 +01:00
Eloy Lafuente (stronk7) e88100f673 Merge branch 'wip-MDL-51999-master' of https://github.com/marinaglancy/moodle 2015-11-02 19:59:01 +01:00
Eloy Lafuente (stronk7) 373748277a Merge branch 'MDL-51829-master' of git://github.com/dpalou/moodle 2015-11-02 19:40:16 +01:00
Dan Poltawski 5294052875 MDL-52014 quiz: do not log preview attempt deletions
This happens automatically by the system.
2015-11-02 16:17:22 +00:00
Dan Poltawski 28aadc2b12 Merge branch 'MDL-51128-master' of git://github.com/junpataleta/moodle 2015-11-02 14:07:10 +00:00
Dan Poltawski 2088c22e41 Merge branch 'wip-MDL-50633-master2' of https://github.com/marinaglancy/moodle 2015-11-02 14:04:44 +00:00
Marina Glancy 57ee89569a MDL-50633 dml: deprecated var replaced in mysql 5.7
Thanks to Charles Verge for providing a patch
2015-11-02 21:05:20 +08:00
Dan Poltawski deec056650 Merge branch 'MDL-51854-master' of https://github.com/pauln/moodle 2015-11-02 09:08:01 +00:00
Andrew Nicols 693ed49c10 Merge branch 'MDL-51707-m' of https://github.com/micaherne/moodle 2015-11-02 14:36:10 +08:00
Andrew Nicols df8095156e Merge branch 'MDL-47073-master' of git://github.com/junpataleta/moodle 2015-11-02 11:54:21 +08:00
Marina Glancy bb0ac1fdc2 MDL-51999 user: remove unnecessary continue 2015-11-02 10:53:43 +08:00
Andrew Nicols 117dc470bc Merge branch 'MDL-51964-master' of git://github.com/damyon/moodle 2015-11-02 10:52:37 +08:00
Marina Glancy 9dfe870449 MDL-51888 mod_resource: cache file details for performance
If file details (size, type, date) are configured to be displayed we cache them
in course cache raw and build the display string in user language/timezone when
displaying. Also changed behat test not to fail in 2016
2015-11-02 10:38:26 +08:00
Damyon Wiese be9b036a33 MDL-51964 autocomplete: IE11 fires spurious "input" events
We need to check for "real" changes to the input value because
ie11 is just firing events willy nilly.
2015-11-02 10:11:13 +08:00
AMOS bot e0b2ad1f35 Automatically generated installer lang files 2015-11-01 00:07:45 +08:00
Jun Pataleta bbbdfc8610 MDL-47073 lib: Replace backslashes with slashes in jquery plugin urls
This patch replaces backslash characters in the generated jQuery
plugin URL to forward slashes when slasharguments is disabled and
 the Moodle server is running on IIS.
Thanks to Ryan Panning <Ryan.Panning@district196.org> for
reporting this issue and for suggesting a fix.
2015-10-30 17:05:23 -05:00
Dan Poltawski 821ab27c19 on-demand release 3.0beta+ 2015-10-30 21:09:21 +00:00
Dan Poltawski 999aa8ca45 Merge branch 'install_master' of https://git.in.moodle.com/amosbot/moodle-install 2015-10-30 21:09:20 +00:00
Eloy Lafuente (stronk7) d708bf59e4 Merge branch 'wip-mdl-51981' of https://github.com/rajeshtaneja/moodle 2015-10-30 10:23:52 +01:00
rajesh Taneja 4b274a10c2 MDL-51981 behat: No need for JS tag
This scenario crashes win-chrome and probably
other comninations. Looking at this, it seems
js tag is not needed, so removing js tag to make
it pass on all OS
2015-10-30 11:29:52 +08:00
Andrew Nicols ee0546291a Merge branch 'wip-MDL-51552-master' of git://github.com/abgreeve/moodle 2015-10-30 11:01:00 +08:00
Adrian Greeve 42b3a58c47 MDL-51552 gradebook: Update to singleview behat test. 2015-10-30 10:08:02 +08:00
Andrew Nicols 22d0a8f11e MDL-51966 forms: Typo correction 2015-10-30 07:56:25 +08:00
Andrew Nicols 63a03a8b05 Merge branch 'wip-MDL-51966-master' of https://github.com/marinaglancy/moodle 2015-10-30 07:31:28 +08:00
AMOS bot 1a9d1593e4 Automatically generated installer lang files 2015-10-30 00:04:09 +08:00
Dan Poltawski ca9daf0fb0 Merge branch 'MDL-51955' of git://github.com/stronk7/moodle 2015-10-29 14:22:28 +00:00
Dan Poltawski 02315eb2bb Merge branch 'MDL-51939-master' of git://github.com/andrewnicols/moodle 2015-10-29 13:54:56 +00:00
Eloy Lafuente (stronk7) 1f0f002e1d MDL-51939 mod_forum: all participants discussions are viewed by students
Added scenario to verify that "all participants" discussions, under
separate-groups mode are, by default, visible for students.
2015-10-29 21:10:20 +08:00
Andrew Nicols 7774cf96f8 MDL-51939 mod_forum: Fix groupid data on upgrade
This upgrade step addresses issues identified in MDL-51939 where the
groupid was incorrectly set. The issue itself is not present in 2.9, but
this upgrade step is required to correct any incorrect data.
2015-10-29 21:10:11 +08:00
Eloy Lafuente (stronk7) aa4a2716ef MDL-51955 testing: Exported files may be smaller than expected
Open a bit the allowed size expectation to accomodate oracle
producing smaller files when outputing decimal numbers.
2015-10-29 13:19:36 +01:00
Dan Poltawski 3adcb91d55 Merge branch 'MDL-50811-master' of git://github.com/cameron1729/moodle 2015-10-29 10:53:40 +00:00
Dan Poltawski ab0ad70a83 Merge branch 'MDL-51921-master' of git://github.com/junpataleta/moodle 2015-10-29 10:35:03 +00:00
Adrian Greeve 81e3251512 MDL-51552 gradebook: Single view bulk insert fix.
Single view now doesn't overwrite non-empty grades.

A thank you to Sam Anderson and Robert Russo for providing a patch.
2015-10-29 16:00:46 +08:00
Marina Glancy ecbc2a2f25 MDL-51966 forms: case-insensitive search in autocomplete 2015-10-29 15:10:20 +08:00
Andrew Nicols b5df2c1db0 Merge branch 'MDL-51923-master-2' of git://github.com/xow/moodle 2015-10-29 14:48:25 +08:00
Andrew Nicols 37d0021369 Merge branch 'MDL-51915' of git://github.com/timhunt/moodle 2015-10-29 14:41:45 +08:00
Andrew Nicols a72f3add06 Merge branch 'MDL-51799-master' of git://github.com/ryanwyllie/moodle 2015-10-29 13:16:15 +08:00
Ryan Wyllie f8f477ece5 MDL-51799 javascript: dialogue accessibility
Moved setting the aria visiblity of the dialogues from the show
and hide functions into the visibility change handler to stop
multiple calls.

Also made the visibility checker just continue to buffer the elements
it hides, rather than clearning them, on multiple calls.
2015-10-29 05:10:10 +00:00
Andrew Nicols 68881fb620 Merge branch 'MDL-51956-master' of git://github.com/ryanwyllie/moodle 2015-10-29 11:06:18 +08:00
Ryan Wyllie 1c53bc813e MDL-51956 atto_table: fix bg colour bug
The table editor will now set the backgorund colour on the
table in Firefox version 34 and below.

Also expanded the dialogue slightly because the colour choices
were wrapping and I added some styling to put a gap in for the
metric markers for border width and table width.
2015-10-29 02:51:06 +00:00
Dan Poltawski de342562cc Merge branch 'wip-MDL-51919-master' of git://github.com/abgreeve/moodle 2015-10-28 12:48:18 +00:00
Dan Poltawski bb63c18443 Merge branch 'MDL-51924-master' of git://github.com/barbararamiro/moodle 2015-10-28 12:04:20 +00:00
Dan Poltawski b2aaab60f3 Merge branch 'MDL-51838-master' of git://github.com/damyon/moodle 2015-10-28 11:53:53 +00:00
Dan Poltawski 413c321a18 Merge branch 'MDL-51914' of git://github.com/timhunt/moodle 2015-10-28 11:52:03 +00:00
Tim Hunt 2d391d1b10 MDL-51914 qtype_ddmarker: precise grading round the edges of zones
There was a previous change to the CSS (7px -> 3px) to make things
look prettier, which acutally broke the grading by a few pixes.
I fixed this by:

* Changing the offset of the cross hairs back to the correct value,
  and adding a comment to point out the imporance of not changing
  that CSS.

* Achieved the nice layout of the label relative to the cross-hairs
  in a different way.

* Added a similar clafirying comment in the JavaScript.

* Improved how the grid on the editing form is aligned with the
  background image.

* Added rounding to the grading code, to cope better now that
  browsers do sub-pixel positioning.
2015-10-28 10:53:00 +00:00
Jun Pataleta 9f555a7e77 MDL-51921 files: Fixes for file manager issues
Fixed the issue where the file path select box goes beyond the
container with very long folder names. Instead of setting to
"width: auto", I set it the select box's "width: 100%" so that it will
just fill up its container's width.
Fixed the issue where markups are being shown on file aliases.
2015-10-28 04:48:25 -05:00
Rajneel Totaram 494fec0071 MDL-51923 enrol: Enrolment list filter 'No Group' shows incorrect count
With contributions from John Okely
2015-10-28 17:34:16 +08:00
Adrian Greeve 527bde6e0f MDL-51919 forms lib: Autocomplete doesn't hide prematurely.
Quickly navigating via the keyboard to an autocomplete
element and hitting down would result in the selections
being displayed and then quickly removed. This has now
been fixed.
2015-10-28 14:15:26 +08:00
Andrew Nicols d412f8552d on-demand release 3.0beta+ 2015-10-28 12:59:31 +08:00
Andrew Nicols 6fb6859945 Merge branch 'install_master' of https://git.in.moodle.com/amosbot/moodle-install 2015-10-28 12:59:25 +08:00
Andrew Nicols af39ce0f8d MDL-51880 atto_table: Coding style fix 2015-10-28 11:39:17 +08:00
Andrew Nicols 7dea2c1707 Merge branch 'MDL-51880-master' of git://github.com/ryanwyllie/moodle 2015-10-28 11:37:17 +08:00
Ryan Wyllie f45937e17d MDL-51880 atto/plugins/table: improve table borders
* Removed the "No borders" option from general borders setting
  since it's a style choice.
* Added inherit, initial and unset as border style options.
* Made the border settings disabled if "Theme default" is selected.
* Changed the transparent colour to be "Theme default" which means
  apply no colour at all.
2015-10-28 03:28:36 +00:00
Dan Poltawski e360ff5db6 Merge branch 'MDL-51893' of git://github.com/stronk7/moodle 2015-10-27 14:43:00 +00:00
Dan Poltawski 61257ae417 Merge branch 'MDL-51863' of git://github.com/stronk7/moodle 2015-10-27 11:57:20 +00:00
Dan Poltawski 101df9a634 Merge branch 'MDL-51906-master-fixes' of git://github.com/andrewnicols/moodle 2015-10-27 10:49:10 +00:00
Andrew Nicols 68b35af353 MDL-51906 testing: Hardcode branch name 2015-10-27 18:31:23 +08:00
Eloy Lafuente (stronk7) 7c549a17f4 MDL-51893 core_date: Stop using Pacific/Norfolk as 11.5 replacement
Norfolk Island's standard time (NFT) was on UTC+11:30 until 4 October
2015, when it was changed to UTC+11:00. So it's not suitable for 11.5
bad mapping replacement anymore.

It seems that there isn't any other location using such 11.5 timezone
so completely getting rid of it for conversions.
2015-10-27 10:17:05 +01:00
Barbara Ramiro 39f360ae38 MDL-51924 Using box-sizing mixin to prevent blocks overlap on profile page 2015-10-27 16:13:43 +08:00
Corey Wallis 346ba4f443 MDL-50811 mod_forum: Add completion tracking for emailed replies 2015-10-27 15:53:42 +08:00
Andrew Nicols 10a8d040dc Merge branch 'MDL-51905-master' of git://github.com/xow/moodle 2015-10-27 14:50:55 +08:00
John Okely 161ffc5605 MDL-51905 block_activity_results: Default to the current activity
Due to an incorrect condition, the activity results block would
incorrectly identify some blocks as not being gradeable.
This meant that edit form could not determine what activity the block
was added to. This commit fixes the condition, allowing the current
block to be determined correctly
2015-10-27 13:47:58 +08:00
Andrew Nicols 53759a0c91 Merge branch 'wip-MDL-51840-master' of git://github.com/abgreeve/moodle 2015-10-27 11:13:44 +08:00
Andrew Nicols f5e587c1db Merge branch 'wip-MDL-51841-master' of git://github.com/abgreeve/moodle 2015-10-27 11:03:43 +08:00
Andrew Nicols 4727a4a207 Merge branch 'MDL-51864-master' of git://github.com/junpataleta/moodle 2015-10-27 10:57:45 +08:00
Andrew Nicols d891a6dcb7 Merge branch 'MDL-51127-master' of git://github.com/lazydaisy/moodle 2015-10-27 10:47:48 +08:00
Eloy Lafuente (stronk7) 70ae75674d MDL-51863 packer: ensure empty zip files behavior remains consistent
With PHP bug #70322 fixed, ZipArchive::close() did start returning false
and throwing PHP Warnings with recent PHP versions (5.6.14 and up).
Previously (5.6.13 verified) it was returning true, and false in older
versions (5.4.x verified).

This change does silent the 2 "hacky" calls to close() that we perform
in core leaving the 3rd one (used for files having files) unmodified.

A new unit test has been created to cover the close() behavior, ideally
supporting both old and new PHP versions without harcoding any PHP
version.

Note that we don't use to rely much on results coming from close(), and
that's a good thing given the buggy behavior commented above. This just
keeps empty zips working like they were before.
2015-10-26 21:16:54 +01:00
Tim Hunt 42406bc413 MDL-51915 lib tests: Extra unit test for html_to_text. 2015-10-26 18:30:21 +00:00
Dan Poltawski d3cd5f1f8f Merge branch 'wip-MDL-51834-master' of https://github.com/kwiliarty/moodle 2015-10-26 13:54:41 +00:00
Dan Poltawski 3ec49c6de3 Merge branch 'MDL-51906-master' of git://github.com/andrewnicols/moodle 2015-10-26 13:49:32 +00:00
Dan Poltawski ff1543e0df Merge branch 'MDL-51894-master' of git://github.com/andrewnicols/moodle 2015-10-26 12:40:15 +00:00
Dan Poltawski eb154d9c14 Merge branch 'MDL-51855-master' of git://github.com/lameze/moodle 2015-10-26 10:20:02 +00:00
Eloy Lafuente (stronk7) f7d8c61ae7 Merge branch 'MDL-51824-master' of git://github.com/lameze/moodle 2015-10-26 09:59:21 +01:00
Simey Lameze 0bc41cfcec MDL-51824 auth: include required CLI library 2015-10-26 15:52:11 +08:00
Adrian Greeve d304952b43 MDL-51840 forms lib: keyboard navigation with ajax works.
Ajax autocomplete forms can now be navigated properly with
the keyboard.
2015-10-26 15:03:42 +08:00
Adrian Greeve adebc069bd MDL-51841 forms lib: Autocomplete fields work with spaces. 2015-10-26 15:02:52 +08:00
Adrian Greeve abfaf50217 MDL-51841 forms lib: Autocomplete fields work with spaces. 2015-10-26 14:55:19 +08:00
Damyon Wiese 81c471e2a9 MDL-51821 forms: Be more explicit in autocomplete if no suggestions 2015-10-26 14:20:25 +08:00
Damyon Wiese bdd60287e5 MDL-51821 forms: Better handling for deselecting items in autocomplete
Fixes 3 problems with deselecting items in an autocomplete field:

1. Keep track of whether items were in the offical suggestion list, or are newly created
   tags and remove the "newly created tags" from the suggestion list when they are
   deselected
2. Change the aria-role for the selected items list when items cannot be deselected (and
   do not treat it like a multiselect list).
3. When leaving and returning focus to the selected items list, remember the last
   active-descendant.
2015-10-26 14:20:25 +08:00
Andrew Nicols 3365244fda Merge branch 'MDL-51824-master' of git://github.com/lameze/moodle 2015-10-26 13:56:49 +08:00
Simey Lameze 37e51a5daa MDL-51824 core: initial deprecation of auth sync CLI scripts
This commit does the initial deprecation of the CLI scripts of CAS and LDAP authentication plugins.
2015-10-26 13:36:27 +08:00
Damyon Wiese 32f3de567f MDL-51838 autocomplete: Scroll the suggestions list
Keep the active item "visible" when using keyboard nav on a long list of suggestions.
2015-10-26 12:20:33 +08:00
Andrew Nicols fd424b99f2 MDL-51894 inboundmessage: Ensure that all mailboxes exist 2015-10-26 12:17:15 +08:00
Andrew Nicols 5459e754c6 MDL-51906 testing: Fix travis builds for beta/rc
These were previously checking the release string for presence of the 'dev'
string. On friday we moved to beta, and will shortly move to RC.

We should be checking the version maturity instead.

This change switches to checking for MATURITY_STABLE in the maturity
string. If it is found, the branch behaviour follows the $branch version.
If it is not found, we use master.

This also adds a check to ensure that if the branch is not found, we exit
early instead of performing CI Tests over the entirety of Moodle.
2015-10-26 12:03:51 +08:00
Simey Lameze abc2491211 MDL-51855 mod_question: fix offset error during upgrade 2015-10-26 11:51:18 +08:00
Andrew Nicols 3953d0b7a7 Merge branch 'MDL-51842-master' of git://github.com/xow/moodle 2015-10-26 11:29:02 +08:00
John Okely 0fc2bccd60 MDL-51842 theme_bootstrapbase: Fix alignment of parallel buttons 2015-10-26 10:49:26 +08:00
Andrew Nicols 3363cbe55b Merge branch 'MDL-51779_master' of https://github.com/Syxton/moodle 2015-10-26 09:46:38 +08:00
Eloy Lafuente (stronk7) 8b3090c13b MDL-51903 behat: move guest enrolment to new UI
With MDL-30674 we missed to change this use from the
old (course edition) UI to the new (enrolment methods) one.
2015-10-25 21:13:08 +01:00
Jun Pataleta 3dbdeb5ccb MDL-51864 blocks: Fixed title display for block_course_summary 2015-10-24 00:03:39 -05:00
lazydaisy b395f7b599 MDL-51127 theme_bootstrapbase: Re-aligned RTL context header button. 2015-10-23 18:28:20 +01:00
AMOS bot 1bc3f19a5b Automatically generated installer lang files 2015-10-24 00:04:00 +08:00
Syxton 626d833543 MDL-51779 assign: PDF annotate quality
Fixes the quality of pdf content when annotating.
2015-10-23 09:22:38 -04:00
Kevin Wiliarty b71890687a MDL-51834 auth,profile: locks custom fields based on auth settings 2015-10-22 23:39:49 -04:00
Paul Nicholls ca50279d4e MDL-51854 atto_image: Allow other plugins to handle drop events
The drop handler should only return false if it has actually handled the
drop event (i.e. it was an image) - otherwise, Atto plugins loaded after
this one don't get an opportunity to handle other drop events.
2015-10-22 11:58:57 +13:00
Dani Palou dbcade703b MDL-51829 admin: Set userquota setting as PARAM_INT 2015-10-20 10:01:11 +02:00
Jun Pataleta cef93f97ae MDL-51128 course: Add startdate for generated course
Added course start date for the course generated by the course
generator in order to prevent incorrect date calculations
(i.e. in assignment due dates) when the generated course is reset.
2015-10-12 21:48:27 -05:00
Michael Aherne 1dc462baa5 MDL-51707 cron: Prevent finished tasks being returned 2015-10-08 14:43:56 +01:00
117 changed files with 1547 additions and 940 deletions
+2 -12
View File
@@ -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`
+2 -1
View File
@@ -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).
*
+11 -4
View File
@@ -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);
+11 -4
View File
@@ -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);
+2 -2
View File
@@ -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"
+10 -2
View File
@@ -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 &amp; Vehicles';
@@ -42,7 +42,7 @@ $string['numberofvideos'] = 'Number of videos';
$string['peopleblogs'] = 'People &amp; Blogs';
$string['petsanimals'] = 'Pets &amp; 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 &amp; Tech';
$string['sports'] = 'Sports';
$string['tag_youtube:addinstance'] = 'Add a new YouTube block';
+1 -1
View File
@@ -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
View File
@@ -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);
}
+2 -2
View File
@@ -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 |
-3
View File
@@ -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"
+1 -1
View File
@@ -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';
+1
View File
@@ -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)';
+1
View File
@@ -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)';
+1
View File
@@ -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)';
+1 -1
View File
@@ -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
View File
@@ -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.';
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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';
+1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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';
+1 -1
View File
@@ -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';
File diff suppressed because one or more lines are too long
+126 -43
View File
@@ -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);
-1
View File
@@ -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) {
+6
View File
@@ -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 {
+3 -3
View File
@@ -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();
@@ -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;
},
File diff suppressed because one or more lines are too long
@@ -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;
},
+1 -1
View File
@@ -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 %)';
+13 -2
View File
@@ -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',
+14 -10
View File
@@ -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,
+3
View File
@@ -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)"
@@ -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();
},
File diff suppressed because one or more lines are too long
@@ -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
View File
@@ -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();
},
+67
View File
@@ -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
*/
+2 -2
View File
@@ -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;
+9 -2
View File
@@ -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();
}
+5 -1
View File
@@ -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
+2
View File
@@ -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">
+20 -34
View File
@@ -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();
}
/**
-2
View File
@@ -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();
}
-1
View File
@@ -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'));
+8
View File
@@ -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));
}
}
+38
View File
@@ -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);
}
}
@@ -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,
File diff suppressed because one or more lines are too long
@@ -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
View File
@@ -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,
+1 -1
View File
@@ -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);
+2 -2
View File
@@ -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;
}
+7
View File
@@ -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"
+1 -1
View File
@@ -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
View File
@@ -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.
+22 -2
View File
@@ -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.
*
+2 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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';
+2
View File
@@ -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();
+55 -137
View File
@@ -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);
}
}
+3 -3
View File
@@ -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' => '',
+30 -8
View File
@@ -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)));
}
}
@@ -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];
},
@@ -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];
},
@@ -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'));
}
@@ -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"]});
@@ -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