Compare commits

..

395 Commits

Author SHA1 Message Date
Eloy Lafuente (stronk7) 3ed9d1c176 Moodle release 2.8.11 2016-03-12 00:37:54 +01:00
Eloy Lafuente (stronk7) 488087cb69 Merge branch 'install_28_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_28_STABLE 2016-03-12 00:37:50 +01:00
Eloy Lafuente (stronk7) 4e5732e7fe MDL-51167 tool_monitor: fix too strict condition 2016-03-10 00:14:11 +01:00
Simey Lameze d9d8e9c3fe MDL-50705 auth_db: apply standard cleaning to all fields
Also unit tests were added to cover the new clean_data() method.
2016-03-09 18:29:09 +01:00
Simey Lameze 4ee7394c8b MDL-50705 core_user: introduce new fill_properties_cache()
Also the get_property_definition() was created to get the property without retrieve the whole definition cache and
    unit tests were created to tests those new methods.
2016-03-09 18:29:07 +01:00
Simey Lameze a0cd21cd5c MDL-53031 mod_assign: add session check on assignment plugins management 2016-03-08 19:54:43 +01:00
Jun Pataleta a0034bb017 MDL-52433 user: Remove unnecessary capability check 2016-03-08 12:24:46 +01:00
Simey Lameze 89b97390d0 MDL-51167 tool_monitor: check course visibility and user 2016-03-08 03:09:57 +01:00
Cameron Ball 190757854d MDL-52651 htmlpurifier: Append rel=noreferrer to links.
Thank you to Zachary Durber for originally working on this issue.
2016-03-08 02:10:17 +01:00
Krista Koivisto dc8421575f MDL-52774 ajax: Require authentication when getting nav branch
When getting the navigation branch data through AJAX, require
login if forcelogin is set.
2016-03-08 00:38:52 +01:00
Juan Leyva 0f989a4c70 MDL-52901 mod_assign: Check due dates in external save_submission 2016-03-08 00:14:31 +01:00
David Monllao b74d0f8404 MDL-52378 singleview: Using is_disabled instead of is_locked 2016-03-07 23:01:35 +01:00
Travis Noll 71beedee8c MDL-52378 Singleview: Exclude Grade Attribute Permissions
Singleview users must have Manage Grade permission to modify the
Exclude grade attribute.
2016-03-07 23:01:25 +01:00
Juan Leyva d63ac148b9 MDL-52808 calendar: Do not return events for hidden activities 2016-03-07 22:31:49 +01:00
David Mudrák 8f95eac163 MDL-52727 mod_data: Improve output of the form fields values
This issue mostly affects the search form fields. Submitted values for
these fields are typically obtained via optional_param() with
PARAM_NOTAGS specified as the parameter type - see parse_search_field()
methods. Such values themselves are not safe enough to be printed back
directly into the HTML as they might contain malicious code.

While working on the patch, some other places with weak protection were
detected and fixed.

In case of the itemid parameters, the s() seems to be unnecessary but it
was added anyway as an extra protection (just in case the code flow
changes or the parts of the code are re-used elsewhere).
2016-03-07 22:07:17 +01:00
AMOS bot 8e12ce7e28 Automatically generated installer lang files 2016-03-03 22:24:34 +08:00
AMOS bot 3703791954 Automatically generated installer lang files 2016-03-03 22:24:33 +08:00
AMOS bot a457f23484 Automatically generated installer lang files 2016-03-03 00:04:39 +08:00
AMOS bot 608beccda3 Automatically generated installer lang files 2016-02-28 00:04:35 +08:00
AMOS bot 51eb1032de Automatically generated installer lang files 2016-02-25 00:12:05 +08:00
AMOS bot b7d5dffc77 Automatically generated installer lang files 2016-02-24 00:04:51 +08:00
Dan Poltawski d3142bcdbe weekly release 2.8.10+ 2016-02-22 09:10:51 +00:00
Dan Poltawski 75066aabb0 Merge branch 'install_28_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_28_STABLE 2016-02-22 09:10:48 +00:00
AMOS bot 16fce86c8b Automatically generated installer lang files 2016-02-13 00:04:48 +08:00
Eloy Lafuente (stronk7) 22277cfabf MDL-51580 environment: 3.1 requirements added
Only important change is that Moodle 2.7 installed is
required to be able to upgrade to 3.1. See the issue
for opinions, votes and agreed outcomes.
2016-02-10 18:28:53 +01:00
AMOS bot 02d953d1b2 Automatically generated installer lang files 2016-02-10 00:04:34 +08:00
AMOS bot 03fb8a8769 Automatically generated installer lang files 2016-02-06 00:04:34 +08:00
AMOS bot 962bf40a1e Automatically generated installer lang files 2016-02-04 00:04:41 +08:00
AMOS bot 00c165d8f3 Automatically generated installer lang files 2016-01-28 00:04:47 +08:00
AMOS bot 54ba33e91b Automatically generated installer lang files 2016-01-26 00:04:31 +08:00
AMOS bot e96ea8d846 Automatically generated installer lang files 2016-01-25 00:04:33 +08:00
AMOS bot 3edce27167 Automatically generated installer lang files 2016-01-23 00:04:43 +08:00
AMOS bot ac60c71cb6 Automatically generated installer lang files 2016-01-19 00:05:16 +08:00
AMOS bot 63e9112aec Automatically generated installer lang files 2016-01-13 00:04:26 +08:00
Eloy Lafuente (stronk7) 2e8e92b0f1 Moodle release 2.8.10 2016-01-09 00:49:45 +01:00
Eloy Lafuente (stronk7) cd654c06a7 Merge branch 'install_28_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_28_STABLE 2016-01-09 00:49:41 +01:00
AMOS bot b25f428173 Automatically generated installer lang files 2016-01-06 00:04:28 +08:00
Juan Leyva 535fd9678f MDL-52072 enrol: Fix course visibility checks in external functions 2016-01-05 10:31:39 +00:00
Dan Poltawski 627e723096 MDL-52552 course management: insufficient cleaning of search output 2016-01-05 10:51:43 +08:00
AMOS bot 81f444886a Automatically generated installer lang files 2016-01-04 00:04:48 +08:00
AMOS bot fe3db63c77 Automatically generated installer lang files 2015-12-25 00:04:31 +08:00
AMOS bot 0b08576ea7 Automatically generated installer lang files 2015-12-20 00:05:42 +08:00
Dan Poltawski 71d630fd59 weekly release 2.8.9+ 2015-12-18 12:27:29 +00:00
Dan Poltawski ae9a34b2d4 Merge branch 'install_28_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_28_STABLE 2015-12-18 12:27:27 +00:00
Marina Glancy e0db7da9e2 MDL-52438 core: allow Moodle 3.0 to be used with PHP7 2015-12-10 17:58:01 +08:00
AMOS bot 32ea431a2f Automatically generated installer lang files 2015-12-05 00:04:59 +08:00
Eloy Lafuente (stronk7) 56068f1aeb weekly release 2.8.9+ 2015-12-03 19:41:02 +01:00
Eloy Lafuente (stronk7) bd696650b7 Merge branch 'install_28_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_28_STABLE 2015-12-03 19:40:54 +01:00
AMOS bot 9157b95733 Automatically generated installer lang files 2015-12-04 00:04:59 +08:00
AMOS bot 025ed8a332 Automatically generated installer lang files 2015-11-28 00:05:17 +08:00
John Okely 397f50e376 MDL-52260 user: Fix error when saving profile with locked field 2015-11-27 11:03:12 +08:00
AMOS bot 4a702ed500 Automatically generated installer lang files 2015-11-25 00:04:45 +08:00
Dan Poltawski b6e2489fd3 weekly release 2.8.9+ 2015-11-19 09:20:36 +00:00
Dan Poltawski fc542633e8 Merge branch 'wip-MDL-52194-m28' of https://github.com/marinaglancy/moodle into MOODLE_28_STABLE 2015-11-19 06:49:55 +00:00
Marina Glancy f988f63bc6 MDL-52194 flowplayer: do not check request
$_REQUEST variable may contain cookies in some php configurations. See also MDL-48085
2015-11-18 15:38:04 +08:00
David Monllao f0f739f4be weekly release 2.8.9+ 2015-11-13 15:27:46 +08:00
David Monllao df2527eb4a Merge branch 'install_28_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_28_STABLE 2015-11-13 15:27:44 +08:00
Eloy Lafuente (stronk7) ade73d44bf Merge branch 'MDL-51495-m28' of https://github.com/NeillM/moodle into MOODLE_28_STABLE 2015-11-11 16:17:26 +00:00
David Monllao 0fae5133a0 Merge branch 'MDL-51913_statistics_dup_1st_column_MDL28' of git://github.com/barrysspace/moodle into MOODLE_28_STABLE 2015-11-11 16:17:25 +00:00
David Monllao ff65861526 Merge branch 'MDL-51935-28' of git://github.com/dpalou/moodle into MOODLE_28_STABLE 2015-11-11 16:17:24 +00:00
Eloy Lafuente (stronk7) 7920d1f08b Merge branch 'wip-mdl-52077-m28' of https://github.com/rajeshtaneja/moodle into MOODLE_28_STABLE 2015-11-11 16:17:24 +00:00
Eloy Lafuente (stronk7) 6193543174 Merge branch 'wip-mdl-52066-m28' of https://github.com/rajeshtaneja/moodle into MOODLE_28_STABLE 2015-11-11 16:17:23 +00:00
Eloy Lafuente (stronk7) 097fe73bab Merge branch 'MDL-51686-28' of git://github.com/damyon/moodle into MOODLE_28_STABLE 2015-11-11 16:17:23 +00:00
Eloy Lafuente (stronk7) b4f7f20a74 Merge branch 'MDL-50781-28' of git://github.com/danpoltawski/moodle into MOODLE_28_STABLE 2015-11-11 16:17:22 +00:00
Eloy Lafuente (stronk7) 9856eadb1b Merge branch 'MDL-51720-28' of git://github.com/damyon/moodle into MOODLE_28_STABLE 2015-11-11 16:17:22 +00:00
AMOS bot 027baac82e Automatically generated installer lang files 2015-11-12 00:04:25 +08:00
Neill Magill 8102ded93a MDL-51495 calendar: E_WARNING in core_calendar_get_calendar_events
If a user does not have the 'moodle/calendar:manageentries' capability then an E_WARNING
can be generated by the core_calendar_external::get_calendar_events() method, that an
expected array is null

This patch creates an array that is missing when a user does not have the capability.
2015-11-11 15:23:13 +00:00
Neill Magill 7cd705334e MDL-51495 calendar: Unit test to get an individual course event
If is user requests a single event on a course via the web service they should be able to retrieve it.
2015-11-11 15:22:47 +00:00
Eloy Lafuente (stronk7) 86eaf83174 weekly release 2.8.9+ 2015-11-10 10:49:58 +01:00
Eloy Lafuente (stronk7) e8f0c133eb Merge branch 'install_28_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_28_STABLE 2015-11-10 10:49:50 +01:00
Dan Poltawski 8535cc180e MDL-52063 forum: Moved notification is not a problem
The default behaviour of $OUTPUT->notification() is to indiciate a
problem.
2015-11-10 09:38:23 +00:00
Dan Poltawski e74ff55ff5 Merge branch 'MDL-52063-28' of git://github.com/xow/moodle into MOODLE_28_STABLE 2015-11-10 09:21:33 +00:00
Dani Palou 5d2e3c1b97 MDL-51935 wiki: Add missing strings in wiki 2015-11-10 10:11:19 +01:00
Rajesh Taneja 120b3ad637 MDL-52077 unittest: Fixed string typo 2015-11-10 13:54:46 +08:00
Rajesh Taneja ed611437a6 MDL-52066 unittest: Use ics file from external repo 2015-11-10 13:43:40 +08:00
John Okely c1c1dc7a58 MDL-52063 forum: Display message when moving forum 2015-11-10 10:24:29 +08:00
AMOS bot 7511a99d0b Automatically generated installer lang files 2015-11-10 00:04:28 +08:00
Dan Poltawski 3afbdcb211 Merge branch 'MDL-51837-28' of git://github.com/junpataleta/moodle into MOODLE_28_STABLE 2015-11-09 11:33:03 +00:00
Dan Poltawski 4d47398bdc Merge branch 'MDL-51628-28' of git://github.com/damyon/moodle into MOODLE_28_STABLE 2015-11-09 11:01:43 +00:00
David Monllao f8c0d18110 Merge branch 'MDL-40245_28' of git://github.com/SWiT/moodle into MOODLE_28_STABLE 2015-11-09 16:13:46 +08:00
David Monllao d84ddfda00 Merge branch 'MDL-49473-28' of git://github.com/danpoltawski/moodle into MOODLE_28_STABLE 2015-11-09 15:13:22 +08:00
Eloy Lafuente (stronk7) ae1854f55c Moodle release 2.8.9 2015-11-06 20:51:38 +01:00
Dan Poltawski 394cba177e weekly release 2.8.8+ 2015-11-06 11:56:38 +00:00
Rajesh Taneja 9d9cfb8736 MDL-51685 unittest: Test functionality individually 2015-11-06 11:24:41 +01:00
Eloy Lafuente (stronk7) 8e2cd6b91a MDL-51861 enrol: added missing external cleanup for formal compliance 2015-11-06 10:05:56 +00:00
Dan Poltawski 6b9c205a99 MDL-51569 mod_choice: Fix use of hardcoded ids
(Previous approach of making up ids longer works now we are validating
them..)
2015-11-06 08:12:57 +00:00
Dani Palou 512633461a MDL-51861 enrol: Don't get all parts in get_enrolled_users with groups 2015-11-06 15:12:11 +08:00
Eloy Lafuente (stronk7) 61ecc0d5f0 MDL-51861 enrol: new unit test to verify all conditions and filters
This unit test does not verify contents, details, but only
that the returned users for every group mode, for every
filtering (active, groupid and withcapability) together
with permissions are correct.
2015-11-06 15:12:07 +08:00
John Okely 02d8c8ca39 MDL-51569 mod_choice: Prevent users from updating choices with curl 2015-11-06 14:53:26 +08:00
Andrew Nicols 11d4f442bb MDL-51685-master Fix unit tests following change
These unit tests were modifying the PAGE and other globals in the middle of
tests and were failing as a result.

Broke the tests up into separate tests where the functionality is now
tested individually.
2015-11-06 10:37:04 +08:00
Damyon Wiese 5c16db4fc5 MDL-51569 mod_choice: Validate the submitted optionid
Make sure any submitted choice options actually belong to the current choice
module.
2015-11-05 10:48:51 +00:00
Damyon Wiese c03882584d MDL-51720 assign: Always fallback on userid as a default sort
Even when the table has no userid column (this is normally the edit column).
2015-11-05 15:36:30 +08:00
Andrew Nicols 720d88779e Merge branch 'MDL-51395-28' of git://github.com/junpataleta/moodle into MOODLE_28_STABLE 2015-11-05 14:03:27 +08:00
Jun Pataleta 5dd381d25c MDL-51395 blog: Fix output of strings for blog comments 2015-11-04 23:47:59 -06:00
Andrew Nicols cb2220bfde Merge branch 'MDL-26429-28' of git://github.com/cameron1729/moodle into MOODLE_28_STABLE 2015-11-05 12:17:44 +08:00
Andrew Nicols c9d8b0d29a MDL-51899 mod_forum: Add test for allowing 0 grades 2015-11-05 12:08:54 +08:00
Cameron Ball 29d6115e65 MDL-26429 usability: Add course completion icons.
This patch adds a method to the course completion criteria classes
which allows them to specify an icon for display in the course
completion report table.

There is a default implementation which simply looks for an icon
in pix/i with a name matching the completion criteria type.
2015-11-05 11:41:49 +08:00
Barbara Ramiro 2dffff79c7 MDL-26429 Course completion icons for self, duration, and role. 2015-11-05 11:41:48 +08:00
Andrew Nicols 9be0bf1d10 Merge branch 'MDL-51899_28' of git://github.com/jacac/moodle into MOODLE_28_STABLE 2015-11-05 11:27:37 +08:00
Andrew Nicols 9ba8006955 Merge branch 'MDL-51685-28' of git://github.com/dpalou/moodle into MOODLE_28_STABLE 2015-11-05 10:42:29 +08:00
rajesh Taneja fe9bd2b8bb MDL-50837 mod_scorm: include locallib to access function 2015-11-05 10:25:05 +08:00
Matt Switlik d7b80c7dd4 MDL-40245 assignment: download all submissions raise time limit 2015-11-04 16:29:25 -05:00
Jakob 57d281f554 MDL-51899 mod/assign: Allow 0.00 grades while editing a grade.
The function unformat_float can return 0 and false.
We need to do strict comparison for the return value on line 92 to accept grades 0.00.
2015-11-04 11:08:06 -08:00
David Monllao d41fa94a69 MDL-51684 badges: Make sure 'moodle/badges:viewbadges' is respected
Credit to Yuliya Bozhko.
2015-11-04 13:02:44 +00:00
Juan Leyva d28eedd536 MDL-50837 mod_scorm: Fix availability checks 2015-11-04 12:56:11 +01:00
Juan Leyva 72885a5725 MDL-51569 mod_choice: Check choice availability prior to do any action 2015-11-04 11:08:51 +00:00
Cameron Ball 7bf5c6a542 MDL-51091 core_registration: session key check in registration. 2015-11-04 18:46:26 +08:00
Simey Lameze b4f4232e1c MDL-49940 mod_survey: Fix XSS on survey module 2015-11-04 18:46:26 +08:00
Hugh Davenport f03ec4ce85 MDL-49940 mod_survey: Escape user comments
This patch fixes a XSS vulnerability with surveys where a student user
    could inject arbitrary HTML in a comment on the survey. The fix is to
    escape the comment before displaying it to the teacher.
2015-11-04 18:46:26 +08:00
David Monllao 50632a0681 MDL-51000 editor_atto: No autosave for guests 2015-11-04 18:46:26 +08:00
Simey Lameze dcb42c9ed1 MDL-48109 mod_lesson: prevent CSRF on lesson
This commit add a new session key hidden field on the lesson password form
and confirm if the session key is valid on related pages to prevent CSRF on
password protected lessons.
2015-11-04 18:46:26 +08:00
Petr Skoda 7db05ae3b2 MDL-48085 flowplayer: prevent flash parameters via url 2015-11-04 10:38:34 +00:00
Andrew Nicols 8e093acc9d Merge branch 'MDL-45814-28' of git://github.com/danpoltawski/moodle into MOODLE_28_STABLE 2015-11-04 14:05:19 +08:00
Andrew Nicols 5bd1977645 Merge branch 'MDL-52004-28' of git://github.com/damyon/moodle into MOODLE_28_STABLE 2015-11-04 13:47:10 +08:00
Andrew Nicols 63d70b1eca Merge branch 'wip-MDL-51486-28' of git://github.com/abgreeve/moodle into MOODLE_28_STABLE 2015-11-04 11:50:45 +08:00
Andrew Nicols 2c6d1dd02d Merge branch 'MDL-51918-28-navigable' of https://github.com/pauln/moodle into MOODLE_28_STABLE 2015-11-04 11:30:51 +08:00
Adrian Greeve 5c31064d5e MDL-51486 mod_data: Unit test update for item ratings.
Additional checks that access is available for item ratings
on records that have a group id of zero (all participants).
2015-11-04 10:17:08 +08:00
Adrian Greeve 6dfb7f0eca MDL-51486 mod_data: Can view rating information.
When students are given the appropriate permissions
to view ratings, in the situation where there are
separate groups. The students can view the ratings
of an entry that is in the all participants group.
2015-11-04 10:16:52 +08:00
Paul Nicholls 80ef82e3f3 MDL-51918 gradereport: Fix AJAX mode keyboard navigation
Keyboard navigation (ctrl+up/down/left/right) with AJAX enabled ceased
functioning with editing mode enabled after MDL-36606, as it was looking
for cells with a "clickable" class - but was only applying that class when
editing mode is NOT enabled.  This patch uses a new "gbnavigable" class to
control keyboard navigation, whether editing mode is on or not.  It also
addresses some browser compatibility and minor behavioural issues.

# Conflicts:
#	grade/report/grader/module.js
2015-11-04 11:53:57 +13:00
Eloy Lafuente (stronk7) 336f2e073b weekly release 2.8.8+ 2015-11-03 18:46:14 +01:00
Eloy Lafuente (stronk7) 27f5d818de Merge branch 'install_28_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_28_STABLE 2015-11-03 18:45:55 +01:00
Eloy Lafuente (stronk7) c2da1ffea9 Merge branch 'wip-mdl-52007-m28' of https://github.com/rajeshtaneja/moodle into MOODLE_28_STABLE 2015-11-03 18:00:50 +01:00
Eloy Lafuente (stronk7) 7f50bc1d2f Merge branch 'wip-mdl-52020-m28' of https://github.com/rajeshtaneja/moodle into MOODLE_28_STABLE 2015-11-03 18:00:50 +01:00
Dan Poltawski 12f93caf45 Merge branch 'MDL-52018-28-enfix' of git://github.com/mudrd8mz/moodle into MOODLE_28_STABLE 2015-11-03 18:00:49 +01:00
AMOS bot 170bb2e118 Automatically generated installer lang files 2015-11-04 00:04:42 +08:00
Dan Poltawski 56272bb56d MDL-45814 login: don't prompt guest user to change password 2015-11-03 14:49:43 +00:00
Dan Poltawski 992591e0e1 MDL-49473 logs: ensure exported logs contain year 2015-11-03 11:38:37 +00:00
Dan Poltawski efb26ed011 MDL-50781 user: do not print useless table cell
Thanks to Maurício Severo da Silva for the patch
2015-11-03 10:53:22 +00:00
rajesh Taneja 3c5a90d52d 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 16:58:23 +08:00
rajesh Taneja fc3ef232d1 MDL-52007 behat: Use extended timeout for site admin expansion 2015-11-03 16:09:45 +08:00
David Mudrák b4ccb13855 MDL-52018 lang: Fix behat test to use the new string 2015-11-02 22:52:20 +01:00
Helen Foster 9e0a879c0a MDL-52018 lang: Merge English strings from the en_fix language pack 2015-11-02 22:48:28 +01:00
Eloy Lafuente (stronk7) ca2d074d95 Merge branch 'MDL-52014-28' of git://github.com/danpoltawski/moodle into MOODLE_28_STABLE 2015-11-02 20:02:32 +01:00
Marina Glancy 5a22246905 MDL-51999 user: remove unnecessary continue 2015-11-02 19:59:45 +01:00
Eloy Lafuente (stronk7) 262289493d Merge branch 'MDL-51829-28' of git://github.com/dpalou/moodle into MOODLE_28_STABLE 2015-11-02 19:40:59 +01:00
Dan Poltawski 1dcf6b339a MDL-52014 quiz: do not log preview attempt deletions
This happens automatically by the system.
2015-11-02 16:19:19 +00:00
Marina Glancy 6dce2f64b8 MDL-50633 dml: deprecated var replaced in mysql 5.7
Thanks to Charles Verge for providing a patch
2015-11-02 14:05:04 +00:00
Dan Poltawski 29f19e7e3e Merge branch 'MDL-51128-28' of git://github.com/junpataleta/moodle into MOODLE_28_STABLE 2015-11-02 10:54:54 +00:00
Damyon Wiese b43abbdccc MDL-52004 assign: Set unique urls when grading so autosaves work
The Atto autosave feature uses the $PAGE->url to generate unique hashes for
each page. Assignment uses a MUC cache to persist ordering of lists - the result
is that the urls are not unique (they depend on the cached filter).

The fix is to spoof a unique url that will grade only that student with no
active grading list (no next/prev buttons).
2015-11-02 16:46:41 +08:00
Andrew Nicols 1af0fa4d1c Merge branch 'MDL-51707-m28' of https://github.com/micaherne/moodle into MOODLE_28_STABLE 2015-11-02 14:51:49 +08:00
Jun Pataleta 1a5b8567c8 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:06:19 -05:00
Dan Poltawski c12979c7df weekly release 2.8.8+ 2015-10-30 21:08:41 +00:00
Andrew Nicols 1d8090a7a6 Merge branch 'wip-MDL-51552-28' of git://github.com/abgreeve/moodle into MOODLE_28_STABLE 2015-10-30 11:03:42 +08:00
Dan Poltawski 318f7454e0 Merge branch 'MDL-51939-28' of git://github.com/andrewnicols/moodle into MOODLE_28_STABLE 2015-10-29 13:55:06 +00:00
Eloy Lafuente (stronk7) 2d0a9d0255 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:07:44 +08:00
Dan Poltawski 15dc45e953 Merge branch 'MDL-50811-28' of git://github.com/cameron1729/moodle into MOODLE_28_STABLE 2015-10-29 10:53:44 +00:00
Dan Poltawski eb1e78be45 Merge branch 'MDL-51921-28' of git://github.com/junpataleta/moodle into MOODLE_28_STABLE 2015-10-29 10:35:41 +00:00
Adrian Greeve 9323277c03 MDL-51552 gradebook: Update to bulk update behat test. 2015-10-29 16:16:00 +08:00
Adrian Greeve 017c71cfcf 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:16:00 +08:00
Ryan Wyllie c07b265f77 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:41:59 +00:00
Jun Pataleta 4b573f4d42 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:51:12 -05:00
Andrew Nicols ba7fcebb79 MDL-51939 mod_forum: Correct empty groupinfo check 2015-10-28 14:04:44 +08:00
Andrew Nicols 53378d63e2 weekly release 2.8.8+ 2015-10-28 12:57:35 +08:00
Eloy Lafuente (stronk7) 2ff58a3b05 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-27 11:57:00 +00:00
Corey Wallis 7fa0d47cda MDL-50811 mod_forum: Add completion tracking for emailed replies 2015-10-27 15:55:29 +08:00
Andrew Nicols 6f3a4fb5c3 Merge branch 'MDL-51864-28' of git://github.com/junpataleta/moodle into MOODLE_28_STABLE 2015-10-27 10:58:47 +08:00
Barry Oosthuizen 9486c3fe5c MDL-51913 report_stats: Make first column unique 2015-10-26 17:27:52 +00:00
Kevin Wiliarty 5daffb32f3 MDL-51834 auth,profile: locks custom fields based on auth settings 2015-10-26 14:24:06 +00:00
Dan Poltawski 3561600a37 Merge branch 'MDL-51894-28' of git://github.com/andrewnicols/moodle into MOODLE_28_STABLE 2015-10-26 12:40:31 +00:00
Andrew Nicols d784ad13e8 MDL-51894 inboundmessage: Ensure that all mailboxes exist 2015-10-26 12:31:20 +08:00
Andrew Nicols a6cb45542d Merge branch 'MDL-51779_28' of https://github.com/Syxton/moodle into MOODLE_28_STABLE 2015-10-26 09:54:20 +08:00
Jun Pataleta c4350c40f5 MDL-51864 blocks: Fixed title display for block_course_summary 2015-10-24 00:04:42 -05:00
Matt Davidson cbb94a638e MDL-51779 assign: PDF annotate quality
Fixes the quality of pdf content when annotating.
2015-10-23 09:31:20 -04:00
Dan Poltawski 7eb88da0af weekly release 2.8.8+ 2015-10-23 12:51:47 +01:00
Andrew Nicols 5631e50ced Merge branch 'MDL-51822_28' of git://github.com/dmonllao/moodle into MOODLE_28_STABLE 2015-10-22 13:22:24 +08:00
Dan Poltawski 23eb923bcc weekly release 2.8.8+ 2015-10-20 09:56:35 +01:00
Dani Palou 80bea94ffe MDL-51829 admin: Set userquota setting as PARAM_INT 2015-10-20 10:02:52 +02:00
Mark Nelson 1430b4dc8f MDL-51782 mod_forum: fixed unit test failing on certain DBs 2015-10-20 14:46:09 +08:00
Andrew Nicols 388efc9233 Merge branch 'MDL-51782_m28' of git://github.com/markn86/moodle into MOODLE_28_STABLE 2015-10-20 11:53:00 +08:00
Mark Nelson 9383deb075 MDL-51782 mod_forum: altered unit tests for the post_deleted event
This was done to ensure we are actually testing the API, and not
just creating and triggering the event manually.
2015-10-19 20:14:05 -07:00
Mark Nelson 102ffb8e18 MDL-51782 mod_forum: moved post_deleted event into function 2015-10-19 20:14:04 -07:00
Jun Pataleta 37ccc13b0f MDL-51837 user: Check if user email is set on validation
Add a check if the user's email is set before performing validation on
the user's email address. This is to prevent the warning messages to
be shown in the case of a pending email change which causes the user's
email field to be not included in the Edit profile form.
2015-10-19 16:52:57 -05:00
Eloy Lafuente (stronk7) 6cf4677e78 Merge branch 'wip-mdl-48117-m28' of https://github.com/rajeshtaneja/moodle into MOODLE_28_STABLE 2015-10-19 16:58:59 +02:00
Dan Poltawski e5ec55cbdd Merge branch 'm28_MDL-51554_Apache_PHP-FPM_Broken_File_Serving' of https://github.com/scara/moodle into MOODLE_28_STABLE 2015-10-19 15:19:38 +01:00
David Monllao 4ae6340719 Merge branch 'MDL-50483-28' of git://github.com/lameze/moodle into MOODLE_28_STABLE 2015-10-19 17:06:59 +08:00
Eloy Lafuente (stronk7) 140bd7b3ca MDL-48117 behat: Use form field to check checkbox
On Mac-Chrome click checkbox is not generating
proper events and hence failing. Use behat_form_checkbox
to ensure checkbox is checked
2015-10-19 16:59:57 +08:00
David Monllao 1b851d1b1f Merge branch 'MDL-51771_28' of git://github.com/timhunt/moodle into MOODLE_28_STABLE 2015-10-19 16:28:17 +08:00
David Monllao 99482eb0bc MDL-51822 timezones: Update to IANA 2015g 2015-10-19 15:00:25 +08:00
Andrew Nicols 2ed5192a84 Merge branch 'MDL-51673-28' of git://github.com/junpataleta/moodle into MOODLE_28_STABLE 2015-10-19 13:50:31 +08:00
Andrew Nicols e1f8a35f27 Merge branch 'MDL-51772-28' of git://github.com/jleyva/moodle into MOODLE_28_STABLE 2015-10-19 13:35:34 +08:00
David Monllao 03f0dec4a4 Merge branch 'MDL-51759-28-2' of git://github.com/andrewnicols/moodle into MOODLE_28_STABLE 2015-10-19 13:31:17 +08:00
Simey Lameze 2ecb8fdea7 MDL-50483 gradereport_user: fix the fatal error of no enrolled users 2015-10-19 09:16:29 +08:00
Jinu Abraham 200522e7c6 MDL-50483 gradereport_user: fix the fatal error of no enrolled users 2015-10-19 09:15:50 +08:00
Andrew Nicols 131679eddc Merge branch 'MDL-50412-28' of git://github.com/danpoltawski/moodle into MOODLE_28_STABLE 2015-10-19 08:05:21 +08:00
Tim Hunt fb33a080dc MDL-51771 question engine: fix typo in SQL
Thanks to Pete at learningpool.com for finding thsi bug.
2015-10-18 16:05:41 +01:00
Jun Pataleta 61ba945d8d MDL-51673 messages: Added codes for the unit tests 2015-10-16 16:07:11 -05:00
Ben Kelada 0429a76370 MDL-51673 messages: remove strict type check of int vs string 2015-10-16 16:07:10 -05:00
Matteo Scaramuccia 0f0f997e61 MDL-51554 Files: Fix broken file serving under Apache + PHP-FPM 2015-10-16 22:06:51 +02:00
Eloy Lafuente (stronk7) e82ed9333b weekly release 2.8.8+ 2015-10-16 12:51:20 +02:00
Andrew Nicols 52b58e7cea Merge branch 'MDL-51565-28' of git://github.com/jleyva/moodle into MOODLE_28_STABLE 2015-10-16 09:37:15 +08:00
Andrew Nicols dff755c3e2 Merge branch 'MDL-51651-28' of git://github.com/danpoltawski/moodle into MOODLE_28_STABLE 2015-10-16 09:27:16 +08:00
Andrew Nicols 70f315edbc Merge branch 'MDL-50182_28' of git://github.com/stronk7/moodle into MOODLE_28_STABLE 2015-10-16 08:14:46 +08:00
Andrew Nicols d1bffa672f MDL-51759 theme: Ensure that non-svg images are cached and returned 2015-10-16 07:53:59 +08:00
Dan Poltawski 764057b0c3 Merge branch 'MDL-51703-28' of git://github.com/jleyva/moodle into MOODLE_28_STABLE 2015-10-15 13:58:20 +01:00
Dan Poltawski 2aea111faa Merge branch 'MDL-51500-28' of git://github.com/merrill-oakland/moodle into MOODLE_28_STABLE 2015-10-15 13:52:48 +01:00
Dan Poltawski db4c084a63 Merge branch 'MDL-51563-28' of git://github.com/jleyva/moodle into MOODLE_28_STABLE 2015-10-15 13:50:13 +01:00
Dan Poltawski ec7af5aa0e Merge branch 'MDL-51652-m28' of https://github.com/sammarshallou/moodle into MOODLE_28_STABLE 2015-10-15 13:46:14 +01:00
Dan Poltawski 60e3abff94 Merge branch 'MOODLE_28_STABLE-MDL-51679-groupmode-ajax' of https://github.com/Dagefoerde/moodle into MOODLE_28_STABLE 2015-10-15 11:44:50 +01:00
Dan Poltawski 5e90e70643 MDL-51651 lang: add consitency for application of firstdayofweek
1) Set the site-wide default based on language pack
2) Use the site/user preference for js popups, like we do in /calendar/
2015-10-15 10:17:20 +01:00
Dan Poltawski 62897237f7 MDL-51651 lang: the first day of the week is Monday 2015-10-15 10:17:19 +01:00
Dan Poltawski d55e3b1fc5 Merge branch 'wip-mdl-50940-m28' of https://github.com/rajeshtaneja/moodle into MOODLE_28_STABLE 2015-10-15 08:57:52 +01:00
Andrew Nicols a6e1c0afa7 Merge branch 'MDL-51756-28' of git://github.com/damyon/moodle into MOODLE_28_STABLE 2015-10-15 15:25:24 +08:00
Andrew Nicols 9a9bfb41e2 MDL-51756 tests: Refactor test to use data-provider
The benefit is that this becomes one test per external function instead of one
test testing them all - so more information is shown on failure and we get lots
more dots (we love dots!).
2015-10-15 15:19:05 +08:00
Damyon Wiese 19ab1865e3 MDL-51756 externallib: Show debugging when optional params are wrong
Optional params are not allowed as the top level value for external_function_parameters.
This is because stricter protocols (xmlrpc and soap) cannot handle optional parameters, only
optional properties in a structure.
2015-10-15 14:20:25 +08:00
Ryan Wyllie d058095357 MDL-49545 backup: Restore does not respect some capabilities
Thanks to Daniel Neis for the original patch that formed the
basis of these changes.
2015-10-15 11:55:47 +08:00
Dan Poltawski f7393e447c Merge branch 'MDL-51399_28' of https://github.com/andrewhancox/moodle into MOODLE_28_STABLE 2015-10-14 15:02:47 +01:00
Dan Poltawski ae4e4e11ff Merge branch 'MDL-51467-m28' of https://github.com/sammarshallou/moodle into MOODLE_28_STABLE 2015-10-14 13:41:03 +01:00
Jan C. Dageförde 0da8db7426 MDL-51679 course: Update groupmode CSS class
Changing groupmode of an activity via AJAX did not change the button's CSS class accordingly.
2015-10-14 14:35:47 +02:00
Juan Leyva 2b6e9e40dc MDL-51772 webservice: Export add_user_private_files as Web Service 2015-10-14 14:09:14 +02:00
Dan Poltawski 5f21490c11 weekly release 2.8.8+ 2015-10-14 10:49:05 +01:00
Dan Poltawski 2148eb566c NOBUG: Fixed SVG browser compatibility 2015-10-14 10:49:04 +01:00
Dan Poltawski 2b18ca5e3f Merge branch 'MDL-40710-28' of git://github.com/barbararamiro/moodle into MOODLE_28_STABLE 2015-10-13 15:35:28 +01:00
Dan Poltawski 45d6ea0832 Merge branch 'MDL-51435-m28' of https://github.com/NeillM/moodle into MOODLE_28_STABLE 2015-10-13 11:43:52 +01:00
Dan Poltawski a8f9db70c6 MDL-47494 grade: even more complete copyright fix.
With thanks to Tim!
2015-10-13 09:55:18 +01:00
Dan Poltawski a7f765a7a9 MDL-47494 grade: fix incorrect copyright statement
(From copy/paste error, blame Tim :P)
2015-10-13 09:39:17 +01:00
Damyon Wiese 206e677358 MDL-48861 assign: Make the requires grading filter consistent
Consistently include submissions with a null grade (see MDL-47694).
2015-10-13 14:54:47 +08:00
Damyon Wiese 2d4cf584ca MDL-48861 assign: Fix unit tests and behat tests
The tests were not working with the change to the needs grading filter.
2015-10-13 13:06:48 +08:00
Andrew Nicols f58afe07e0 Merge branch 'MDL-51302-28' of git://github.com/danpoltawski/moodle into MOODLE_28_STABLE 2015-10-13 11:35:38 +08:00
Jun Pataleta c07f0799ed 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 22:12:13 -05:00
Andrew Nicols 7914b0a2cf Merge branch 'MDL-51427-28' of git://github.com/marinaglancy/moodle into MOODLE_28_STABLE 2015-10-13 10:15:35 +08:00
Andrew Nicols 1cf1b279da Merge branch 'MDL-40442-28' of git://github.com/danpoltawski/moodle into MOODLE_28_STABLE 2015-10-13 09:49:14 +08:00
Andrew Nicols 182a393e06 Merge branch 'MDL-50079-28' of git://github.com/danpoltawski/moodle into MOODLE_28_STABLE 2015-10-13 09:33:38 +08:00
Andrew Nicols ee8072e2a1 Merge branch 'MDL-50788-28' of git://github.com/danpoltawski/moodle into MOODLE_28_STABLE 2015-10-13 08:54:42 +08:00
Eloy Lafuente (stronk7) bf0e51d1a1 MDL-50182 core: add 3.0 environmental requirements
Only change from 2.9 is the modification of the slasharguments
admin setting string to recommend always to keep the setting enabled
and fix the problem in the server, warning about the drawbacks
of disabling it.

Also, there is a little phpdoc note added to one custom check
to have clearly specified when we can get rid of it.
2015-10-13 00:35:55 +02:00
sam marshall 378f34a469 MDL-51467 Availability: Course reset should change restrict dates 2015-10-12 17:36:00 +01:00
Dan Poltawski 2c9c7b1130 Merge branch 'MDL-51375_28' of git://github.com/timhunt/moodle into MOODLE_28_STABLE 2015-10-12 17:01:44 +01:00
Dan Poltawski a78d3fa10b Merge branch 'MDL-48861-28' of git://github.com/damyon/moodle into MOODLE_28_STABLE 2015-10-12 16:53:21 +01:00
Juan Leyva 43654c4872 MDL-51563 core_user: Fix invalid passed parameteres in get_user 2015-10-12 13:54:27 +02:00
Dan Poltawski 989298d309 Merge branch 'MDL-36606-28' of git://github.com/merrill-oakland/moodle into MOODLE_28_STABLE 2015-10-12 12:41:30 +01:00
Juan Leyva 8eb8681b5e MDL-51565 webservice: Fix return types definitions 2015-10-12 13:30:17 +02:00
Dan Poltawski 025b4cb5e9 Merge branch 'MDL-51586-28' of git://github.com/xow/moodle into MOODLE_28_STABLE 2015-10-12 10:47:17 +01:00
sam marshall 0273933079 MDL-51652 Availability: OR conditions fail when filtering user lists
When filtering a user list, if there is an OR tree where one of the
conditions does not restrict user lists such as a date condition,
then the overall result should include all users (not be filtered).
This was not working correctly.
2015-10-12 10:26:48 +01:00
David Monllao 28e4326cf1 Merge branch 'MDL-51083-28' of git://github.com/xow/moodle into MOODLE_28_STABLE 2015-10-12 16:58:13 +08:00
Andrew Nicols fb48641b5f MDL-46497 atto: Whitespace/coding style fix 2015-10-12 13:44:18 +08:00
Andrew Nicols 907093e131 Merge branch 'MDL-46497_M28' of https://github.com/nadavkav/moodle into MOODLE_28_STABLE 2015-10-12 13:43:33 +08:00
Andrew Nicols ce4c237787 Merge branch 'MDL-51451-m28' of https://github.com/sammarshallou/moodle into MOODLE_28_STABLE 2015-10-12 10:15:31 +08:00
Lior Gil 284ef7be8f MDL-46497 Atto: Fix adding redundant p elements in rtl/ltr actions 2015-10-10 01:34:07 +03:00
Juan Leyva cc595febe4 MDL-51703 mod_survey: Trigger response_submitted event in correct place 2015-10-09 16:27:20 +02:00
Dan Poltawski 6ff47714ac MDL-50412 admin: prevent fatal errors on reports admin page 2015-10-09 14:47:39 +01:00
Barbara Ramiro c3ece30f18 MDL-40710 Icon: More identifiable backpack icon 2015-10-09 18:40:19 +08:00
Dan Poltawski 1670fc84a7 MDL-50079 atto_link: respect new window option when creating from repo
Previously users had to go back and edit their link to set the new
window option.
2015-10-09 11:29:27 +01:00
Dani Palou b961b1c142 MDL-51685 user: Support gravatar images in user_get_user_details 2015-10-09 11:51:19 +02:00
Dan Poltawski d6e6b308c5 MDL-50788 classloader: Prevent cryptic errors on unreadable classes dir
Do not attempt to read an unreadable directory because previously the generated
errors were hard to fathom.

Thanks to James McQuillan for the basis of the patch.
2015-10-09 10:37:32 +01:00
Dan Poltawski 66ce9e9f1c MDL-51302 scorm: do not use invalid course urls
Previous behaviour of setting an empty sectionid was incorrect and we
were doing work which course_get_url() does for us.
2015-10-09 09:09:37 +01:00
Neill Magill c21fca27fd MDL-51435 Web Services: E_WARNING in core_webservice_get_site_info
$plugin was not being set before the version file was included.
This caused an E_WARNING: Creating default object from empty value

This change creates the plugin object before the version file sets its parameters.
2015-10-09 09:06:53 +01:00
John Okely 9b132c5b23 MDL-51586 enrol: Enrolment table displays fullname properly.
When the alternatefullnamedisplay was set to something that
didn't contain 'firstname' then nothing would be displayed
for the user's name.
2015-10-09 15:35:23 +08:00
Damyon Wiese 16ef0ac033 MDL-48861 mod_assign: use >= for requires grading filter
If the grade and submission were modified at exactly the same time,
assume that this means the grade was automatically created for the submission
and is not a real grade from a teacher interaction.
2015-10-09 15:10:46 +08:00
Damyon Wiese cce2034d5e MDL-51628 assign: invalid hardcoded capability check
Use existing function to do the check instead.
2015-10-09 15:01:08 +08:00
Mifsud Gaël f1afb82337 MDL-51083 admin: Prevent password autofill in authentication settings 2015-10-09 14:34:02 +08:00
Damyon Wiese 8ffc94ff69 MDL-51686 assign: Apply admin default for comment inline 2015-10-09 13:42:29 +08:00
Michael Aherne 545163440b MDL-51707 cron: Prevent finished tasks being returned 2015-10-08 16:44:08 +01:00
Eric Merrill 5c66e986ee MDL-36606 gradereport: Update AJAX grader to understand gradeless cells 2015-10-08 10:51:04 -04:00
Barbara Ramiro e4f2fb2665 MDL-40442 Icon: Improved svg download icon and png version 2015-10-08 13:31:46 +01:00
Dan Poltawski 6d8dbfee53 weekly release 2.8.8+ 2015-10-08 11:36:41 +01:00
Adam Olley 41c8edf46d MDL-51285 mod_forum: Posts all need to have different stamps 2015-10-08 12:23:31 +08:00
Eric Merrill 287e6a9772 MDL-51500 navigation: Compute links with destination context
We should calculate the links based on the destination context,
not the current course. Could show links that are not accessible.
2015-10-07 10:03:13 -04:00
sam marshall 735ef8ba92 MDL-51451 Moodle dialogues: Lock scroll causes background jump
When you add a module or otherwise cause a Moodle dialogue to appear, this
enables the 'lock scroll' feature (the scrollbar disappears).

On some browsers this causes the width of the viewport to change, which can
cause centered/right-aligned theme elements to move distractingly.

This change temporarily sets body max-width while lock scroll is active.
2015-10-07 10:32:59 +01:00
David Monllao a112d4ebfb Merge branch 'wip-mdl-51285-m28' of git://github.com/rajeshtaneja/moodle into MOODLE_28_STABLE 2015-10-07 11:23:32 +08:00
Andrew Nicols 797ac16e88 Merge branch 'MDL-41042-28' of git://github.com/marinaglancy/moodle into MOODLE_28_STABLE 2015-10-07 10:27:47 +08:00
David Monllao 1ce643eaad Merge branch 'MDL-51525-28' of git://github.com/ryanwyllie/moodle into MOODLE_28_STABLE 2015-10-07 09:23:24 +08:00
Dan Poltawski 936d11f137 Merge branch 'MDL-51588-28' of git://github.com/FMCorz/moodle into MOODLE_28_STABLE 2015-10-06 11:42:58 +01:00
David Monllao c4dc0748a4 Merge branch 'MDL-51656_m28' of git://github.com/markn86/moodle into MOODLE_28_STABLE 2015-10-06 15:25:15 +08:00
Andrew Nicols cee7167b07 Merge branch 'MDL-51228-m28-master' of https://github.com/dthies/moodle into MOODLE_28_STABLE 2015-10-06 13:49:36 +08:00
Tim Hunt e8da65449a MDL-51375 excel output: more work to avoid invalid sheet names 2015-10-06 05:45:16 +01:00
Andrew Nicols 77712faa11 Merge branch 'MDL-44707-28' of git://github.com/marinaglancy/moodle into MOODLE_28_STABLE 2015-10-06 11:36:30 +08:00
Andrew Nicols 71175baff9 Merge branch 'MDL-50805_MOODLE_28_STABLE' of https://github.com/crazyserver/moodle into MOODLE_28_STABLE 2015-10-06 10:36:32 +08:00
David Monllao 6641b6fb8a Merge branch 'MOODLE_28_STABLE-MDL-49750' of git://github.com/jojoob/moodle into MOODLE_28_STABLE 2015-10-06 09:30:04 +08:00
Dan Poltawski 4fbad85763 Merge branch 'wip-MDL-49189-MOODLE_28_STABLE' of https://github.com/kwiliarty/moodle into MOODLE_28_STABLE 2015-10-05 15:03:18 +01:00
Johannes Burk e33152d912 MDL-49750 mod_data: fix updating file/picture fields breaks references
Use file_save_draft_area_files on file/picture update and
set content to null if file was removed.
2015-10-05 11:45:18 +02:00
David Monllao 758b2d9b29 Merge branch 'MDL-51514_m28' of git://github.com/markn86/moodle into MOODLE_28_STABLE 2015-10-05 16:00:12 +08:00
rajesh Taneja b5b3a37049 MDL-50940 behat: Added support for field matching with xpath 2015-10-05 14:04:02 +08:00
David Monllao 06cb6e421b Merge branch 'MDL-51408-28' of git://github.com/marinaglancy/moodle into MOODLE_28_STABLE 2015-10-05 13:50:34 +08:00
Andrew Hancox 08544e3bdb MDL-50940 behat: Added ability to check field value by xpath 2015-10-05 13:47:04 +08:00
David Monllao 192509762b MDL-50613 webservices: Upgrade info about riskbitmask 2015-10-05 12:19:07 +08:00
David Monllao f2ac95dfdf Merge branch 'MDL-50613-28' of git://github.com/jleyva/moodle into MOODLE_28_STABLE 2015-10-05 12:13:14 +08:00
Sam Chaffee f0d7264769 MDL-51514 gradebook: Optimize set_usedinaggregation query for MySQL 2015-10-04 13:20:47 -07:00
Kevin Wiliarty 36d44b206a MDL-49189 auth: Map data to profile fields with uppercase shortnames 2015-10-03 08:25:06 -04:00
Mark Nelson 2bec647d91 MDL-51656 auth_radius: fixed colon location 2015-10-02 16:35:41 -07:00
Dan Poltawski dc3850fc20 weekly release 2.8.8+ 2015-10-02 10:57:33 +01:00
Dan Poltawski 35f4f19770 Merge branch 'install_28_STABLE' of https://git.in.moodle.com/amosbot/moodle-install into MOODLE_28_STABLE 2015-10-02 10:57:31 +01:00
Pau Ferrer Ocaña 3262330d95 MDL-50805 Messages: Add index to message_read to speed up message crons 2015-10-01 10:37:16 +02:00
David Monllao 00f1a102ca MDL-46710 mod_lti: Removing duplicated generated events
Event generation and completion should be generated only once.
2015-10-01 12:07:07 +08:00
Eloy Lafuente (stronk7) 474b1e0040 Merge branch 'MDL-51227-m28-master' of https://github.com/dthies/moodle into MOODLE_28_STABLE 2015-10-01 00:35:25 +02:00
Andrew Robert Nicols 68dcf2dded MDL-36606 gradereport: Correctly handle ungraded assessment ajax grading 2015-09-30 15:04:12 -04:00
Eloy Lafuente (stronk7) 9194f5911b Merge branch 'wip-mdl-51266-m28' of https://github.com/rajeshtaneja/moodle into MOODLE_28_STABLE 2015-09-30 20:34:59 +02:00
Andrew Hancox 4143c33d27 MDL-51399 report_outline: Hide blog column if blogs are disabled. 2015-09-30 17:04:59 +01:00
Juan Leyva ccefec7360 MDL-50613 webservice: Remove risk bit masks for Web Service protocols
This will prevent to show a 'Critical' status in security overview
report when mobile services are enabled
2015-09-30 11:18:01 +02:00
Tim Lock b68783c870 MDL-51440 gradeimport_direct: fix config require statement 2015-09-30 10:53:51 +08:00
Eloy Lafuente (stronk7) 9c46e281cb Merge branch 'MDL-49143-28' of git://github.com/cameron1729/moodle into MOODLE_28_STABLE
Note: Added small fix to unit test.
2015-09-30 03:21:07 +02:00
Dan Poltawski 773582db11 Merge branch 'MDL-51402_28' of git://github.com/timhunt/moodle into MOODLE_28_STABLE 2015-09-29 20:54:44 +01:00
Dan Poltawski 964c02c3fe Merge branch 'MDL-51424-m28-master' of https://github.com/dthies/moodle into MOODLE_28_STABLE 2015-09-29 20:33:30 +01:00
Dan Poltawski 868fb3adf9 Merge branch 'MDL-42639_28' of git://github.com/dmonllao/moodle into MOODLE_28_STABLE 2015-09-29 10:20:15 +01:00
David Monllao 0b54d6d971 MDL-42639 user: user:viewalldetails enough to view some fields
The fields are:
- username
- idnumber
- institution
- department
2015-09-29 16:56:26 +08:00
Ryan Wyllie a6bd6b3cad MDL-51525 libraries: added graphlib to thirdpartylibs.xml 2015-09-29 07:32:57 +00:00
Frederic Massart 46067fbd30 MDL-51588 webservice: Allow CORS in webservice/pluginfile.php 2015-09-29 14:41:28 +08:00
Andrew Nicols acd86d0cf2 Merge branch 'MDL-43594-28' of git://github.com/junpataleta/moodle into MOODLE_28_STABLE 2015-09-29 14:01:33 +08:00
Jun Pataleta 365644eecf MDL-43594 mod_assign: Fix calendar update on course reset
This patch fixes the updating of calendar events when the course
is reset and its start date is changed.
2015-09-29 13:47:06 +08:00
Andrew Nicols 51d6415ef3 Merge branch 'MDL-35918-28' of git://github.com/ryanwyllie/moodle into MOODLE_28_STABLE 2015-09-29 13:43:32 +08:00
Eloy Lafuente (stronk7) c022a86c38 MDL-49143 LTI: Test resource_link_id in lti_build_request() 2015-09-29 13:42:43 +08:00
Cameron Ball fa3652ef9c MDL-49143 LTI: Fix missing resource id 2015-09-29 13:42:43 +08:00
Ryan Wyllie 08db4b5843 MDL-35918 javascript: dialogues screen reader support
The YUI notification dialogues will now set the appropriate
aria attributes when they are shown/hidden to allow screen
readers to navigate them correctly.

When the dialogue is shown all background elements will be marked
as hidden while the dialogue is marked as visible to a screen reader.

When the dialogue is closed the modified elements will be reverted
to their previous state and the dialogue will be marked as hidden to
a screen reader.
2015-09-29 04:53:23 +00:00
Andrew Nicols 3497350e2f Merge branch 'wip-MDL-51241-28' of git://github.com/abgreeve/moodle into MOODLE_28_STABLE 2015-09-29 12:47:20 +08:00
Andrew Nicols 396f954b05 Merge branch 'MDL-51476_28' of git://github.com/timhunt/moodle into MOODLE_28_STABLE 2015-09-29 12:07:25 +08:00
Dan Poltawski 997e7a966b Merge branch 'MDL-51305_28_too_many_gradeitem_fetches' of https://github.com/colin-umn/moodle into MOODLE_28_STABLE 2015-09-28 16:25:57 +01:00
Dan Poltawski 94249aa198 Merge branch 'mdl46710-moodle28' of https://github.com/tlock/moodle into MOODLE_28_STABLE 2015-09-28 14:56:58 +01:00
Dan Poltawski 1916b2863c Merge branch 'MDL-51390_28' of https://github.com/totara/openbadges into MOODLE_28_STABLE 2015-09-28 13:27:17 +01:00
Mark Nielsen 0c2ddb687a MDL-51498 core_grades: Prevent unnecessary grade record fetching
All of these changes make use of already
fetched grade data.  Without these changes,
the gradebook regrade process does not scale
well with very large courses because it fetches
many grade records, one at a time.
2015-09-28 11:12:59 +01:00
Dan Poltawski 8ae33bc947 Merge branch 'MDL-49482-28' of https://github.com/blckct/moodle into MOODLE_28_STABLE 2015-09-28 10:31:50 +01:00
Yuliya Bozhko 8589530727 MDL-51390 badges: Switch backpack URL to use HTTPS
Signed-off-by: Yuliya Bozhko <yuliya.bozhko@totaralms.com>
2015-09-28 10:08:00 +01:00
AMOS bot b9e92d05e1 Automatically generated installer lang files 2015-09-28 00:04:12 +08:00
Daniel Thies 45e591a85a MDL-51424 editor_atto: Save selection and editor contents after change
Conflicts:
	lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-min.js
2015-09-25 09:50:06 -05:00
Daniel Thies a59f24b904 MDL-51227 editor_atto: Stop Chrome losing selection when formating
Conflicts:
	lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-min.js
2015-09-25 09:38:48 -05:00
Tim Hunt 58d7de73ac MDL-51476 questions: manual grading validation for ungraded Qs
The change in MDL-51090 broke manually commenting on questions for which
no mark is given (max mark == 0). This fixes it, with unit tests.

Thanks to Nick Phillips for the original suggestion of the fix.
2015-09-25 14:02:32 +01:00
Tim Lock e87c74e9f5 MDL-46710 mod_lti: fix logging and completion tracking in new windows 2015-09-25 06:28:28 +09:30
Eloy Lafuente (stronk7) d8edd556ab MDL-49482 sqlsrv: Implement support of Offset/Fetch in SQL Server 2012+ 2015-09-24 20:13:17 +02:00
Jakub Kania 68629452fa MDL-49482 mssql: Implement support of Offset/Fetch in SQL Server 2012+. 2015-09-24 20:13:17 +02:00
Eloy Lafuente (stronk7) 3377cbc3ba weekly release 2.8.8+ 2015-09-24 16:07:14 +02:00
rajesh Taneja 07f3c02ec7 MDL-49973 behat: Fixed string change from Continue to Delete 2015-09-24 10:35:37 +08:00
Eloy Lafuente (stronk7) 837c4dab86 Merge branch 'wip-MDL-51259-28' of git://github.com/abgreeve/moodle into MOODLE_28_STABLE 2015-09-23 16:29:06 +02:00
rajesh Taneja 551dce86ba MDL-51266 unittest: replaced low level phpunit_util calls 2015-09-23 15:44:57 +08:00
David Monllao f7666835c7 MDL-50332 enrol_self: Setting form url for guests
Credit to Andrew Nicols.
2015-09-23 11:53:12 +08:00
Adrian Greeve f9dfe738aa MDL-51241 cohorts: Update to cohort query.
This improves the speed of returning a list of
cohorts. Thanks to François Gannaz for providing
this patch.
2015-09-23 11:38:46 +08:00
Dan Poltawski 3bf8439e3a Merge branch 'MDL-49973-28' of git://github.com/cameron1729/moodle into MOODLE_28_STABLE 2015-09-22 09:44:46 +01:00
Dan Poltawski c6a6ce7914 Merge branch 'MDL-46157-28' of git://github.com/cameron1729/moodle into MOODLE_28_STABLE 2015-09-22 08:15:02 +01:00
Andrew Nicols 599eeea2a5 Merge branch 'MDL-51190-28' of https://github.com/pauln/moodle into MOODLE_28_STABLE 2015-09-22 08:32:18 +08:00
Ferenc Lengyel bfd6727b42 MDL-51402 quiz: notification emails should respect grouping
In the case where teachers do not have the accessallgroups capability,
we only send "an attempt has been sumbitted "notification to teachers
in the same group as the student who submitted.

At the moment this considers all groups in the course. If this
particular quiz has a grouping set, then the logic should only consider
the groups in that grouping.
2015-09-21 13:31:15 +01:00
Dan Poltawski 606cf274a7 Merge branch 'MDL-51052_28' of https://github.com/dmonllao/moodle into MOODLE_28_STABLE 2015-09-21 10:53:30 +01:00
Dan Poltawski 0dbd2594e8 Merge branch 'MDL-48165-28-dimgrades' of git://github.com/mudrd8mz/moodle into MOODLE_28_STABLE 2015-09-21 10:14:50 +01:00
Andrew Nicols 0c73e5335c Merge branch 'MDL-50332_28' of git://github.com/dmonllao/moodle into MOODLE_28_STABLE 2015-09-21 15:47:07 +08:00
David Monllao 18fd195086 MDL-50332 enrol_self: Formatting unavailable instances error messages
Many thanks to Andrew Nicols as this patch is based on his
proposal. Thanks Andrew.
2015-09-21 15:44:29 +08:00
Andrew Nicols 05c252e9b0 Merge branch 'MDL-51431_2_8' of https://github.com/andrewhancox/moodle into MOODLE_28_STABLE 2015-09-21 08:13:10 +08:00
David Monllao a23c44cddf MDL-51052 dml: More info about mysql and case & accent sensitiveness 2015-09-18 11:49:26 +08:00
Charles Fulton f8e0b4ece1 MDL-51052 dml: support for sql_like() under utf8_bin 2015-09-18 11:49:25 +08:00
Adrian Greeve 76ebf76be1 MDL-51259 mod_data: ratings of items not activated.
When selecting a rating method, the restrict rating
to items with dates would be automatically enabled if
not checked. This code brings the data module inline
with forum and glossary.
2015-09-18 07:28:56 +08:00
Eloy Lafuente (stronk7) a91caeefc7 weekly release 2.8.8+ 2015-09-17 18:01:22 +02:00
Simey Lameze e1efcbd1db MDL-50959 gradebook: fix XML grade export 2015-09-17 10:37:09 +08:00
Andrew Hancox 210349f647 MDL-51431 enrol_flatfile: Fix for startdate when updating users 2015-09-16 10:56:55 +01:00
Marina Glancy 193f5d4029 MDL-46705 admin: Validate unique shortname for frontpage course 2015-09-16 14:48:03 +08:00
Marina Glancy 489db61206 MDL-51427 user: correctly use strings for phone1 and phone2
Thanks to Olumuyiwa Taiwo for original patch
2015-09-16 13:36:54 +08:00
David Monllao 685aa0b7c9 Merge branch 'MDL-49561_m28' of git://github.com/markn86/moodle into MOODLE_28_STABLE
Conflicts:
	lib/bennu/readme_moodle.txt
2015-09-16 09:59:04 +08:00
David Monllao 57caaace44 Merge branch 'MDL-46705-28' of git://github.com/dkns/moodle into MOODLE_28_STABLE 2015-09-16 09:46:13 +08:00
Eloy Lafuente (stronk7) 42ba6f011e Merge branch 'MDL-50685_28' of git://github.com/dmonllao/moodle into MOODLE_28_STABLE 2015-09-16 02:16:38 +02:00
Eloy Lafuente (stronk7) 8ada99201a Merge branch 'MDL-49032_28' of https://github.com/stronk7/moodle into MOODLE_28_STABLE 2015-09-16 00:58:56 +02:00
Eloy Lafuente (stronk7) 2fab7166b5 MDL-49032 lib/bennu: update readme to document the change 2015-09-16 00:54:15 +02:00
Joby Harding f27c022e5c MDL-49032 lib/bennu: use HTAB only when folding lines 2015-09-16 00:45:38 +02:00
Eloy Lafuente (stronk7) 16eedce0c6 Merge branch 'MDL-50959-28' of git://github.com/lameze/moodle into MOODLE_28_STABLE 2015-09-15 23:36:31 +02:00
Eloy Lafuente (stronk7) 5d4bccd775 Merge branch 'MDL-50892-28' of git://github.com/marinaglancy/moodle into MOODLE_28_STABLE 2015-09-15 23:35:23 +02:00
David Monllao 2648522e3d Merge branch 'MDL-50884-28' of git://github.com/lameze/moodle into MOODLE_28_STABLE 2015-09-15 17:16:30 +08:00
Cameron Ball e13126f85a MDL-49973 behat: Update delete button names
Conflicts:
	course/format/topics/tests/behat/edit_delete_sections.feature
	course/format/weeks/tests/behat/edit_delete_sections.feature
2015-09-15 15:39:32 +08:00
Andrew Davis 1237ba1962 MDL-49973 changed continue to delete on delete confirmations
Conflicts:
	course/editsection.php
2015-09-15 15:38:08 +08:00
David Monllao 3e08b5c04e Merge branch 'MDL-50921-28' of git://github.com/ryanwyllie/moodle into MOODLE_28_STABLE 2015-09-15 14:27:32 +08:00
David Monllao 978ef29e47 MDL-51048 backup: Removing unnecessary var 2015-09-15 13:44:36 +08:00
David Monllao 5c03acf518 Merge branch 'MDL-51048_28' of git://github.com/timhunt/moodle into MOODLE_28_STABLE 2015-09-15 13:44:32 +08:00
Andrew Nicols 8d5092cd84 Merge branch 'MDL-51290-28' of git://github.com/cameron1729/moodle into MOODLE_28_STABLE 2015-09-15 13:06:54 +08:00
Paul Nicholls add6322e5a MDL-51190 javascript: Make "maximised embed" compatible with MS Edge
MS Edge exhibits strange behaviour when M.util.init_maximised_embed hides
the embedded document by setting its width and height to 0px.  Toggling
display:none instead achieves the intended goal without breaking Edge.
2015-09-15 16:28:42 +12:00
David Monllao c87b932f1c Merge branch 'MDL-51008-28' of git://github.com/marinaglancy/moodle into MOODLE_28_STABLE 2015-09-15 11:22:16 +08:00
Marina Glancy 78eb3ee407 MDL-51408 badges: fix criteria for custom profile fields 2015-09-15 11:17:42 +08:00
David Monllao 38e0d68f0a Merge branch 'MDL-51147_28' of git://github.com/timhunt/moodle into MOODLE_28_STABLE 2015-09-15 10:09:52 +08:00
David Monllao 6c974ef376 MDL-51363 mod_scorm: Removing whitespaces 2015-09-15 10:03:27 +08:00
David Monllao 17c975874f Merge branch 'm28_MDL-51363_Fix_Default_Settings_1x_Restore' of git://github.com/scara/moodle into MOODLE_28_STABLE 2015-09-15 10:02:19 +08:00
Eloy Lafuente (stronk7) 07d52ad818 Merge branch 'MDL-46239-28-2' of git://github.com/xow/moodle into MOODLE_28_STABLE 2015-09-15 01:03:08 +02:00
Matteo Scaramuccia 3f121ac514 MDL-51363 SCORM: fixed default settings on restoring 1.x backups 2015-09-15 00:01:33 +02:00
Cameron Ball eddc63f4e8 MDL-51290 user: Expand picture section in profile by default 2015-09-14 17:54:06 +08:00
David Monllao 3f72e79ee1 Merge branch 'wip-mdl-51101-m28' of git://github.com/rajeshtaneja/moodle into MOODLE_28_STABLE 2015-09-14 16:18:55 +08:00
David Monllao 000be9c016 Merge branch 'MDL-51170-28' of git://github.com/andrewnicols/moodle into MOODLE_28_STABLE 2015-09-14 14:14:48 +08:00
David Monllao 8a6c5244c4 Merge branch 'MDL-51191-moodle28' of git://github.com/StudiUM/moodle into MOODLE_28_STABLE 2015-09-14 13:59:40 +08:00
David Monllao 5319119569 Merge branch 'MDL-51253_MOODLE_28' of git://github.com/BenKelada/moodle into MOODLE_28_STABLE 2015-09-14 13:09:18 +08:00
Ben Kelada 6dd7f1d452 MDL-51253 forum: fix urls for exceptions and redirects 2015-09-14 14:20:43 +10:00
Simey Lameze 59d26c45d9 MDL-50884 auth_shibboleth: fix logout handler url generation
This patch fixes the shibboleth redirect url generation, that can create invalid url
if the shibboleth logout_handler setting has a parameter generating two parameters
with (?) instead of (&).

Thanks to Matteo Boni for the proposed solution.
2015-09-14 09:52:20 +08:00
David Monllao 596950852c Merge branch 'MDL-51232_28' of git://github.com/jinuabraham/moodle into MOODLE_28_STABLE 2015-09-14 09:39:30 +08:00
Simey Lameze 6deeba6bcc MDL-50959 gradebook: fix XML grade publishing score 2015-09-14 09:05:49 +08:00
Daniel Thies 85cf55ebaf MDL-51228 atto_editor: Add documententation for inlineFormat flag 2015-09-13 14:26:56 -05:00
Colin Campbell a03a01f2ad MDL-51305 grade: Fix slow grade operations by setting grade_item early.
Exports were fetching the grade item for every item-user combination.
Setting the grade_item object on the grade_item early prevents that.
2015-09-11 15:23:06 -05:00
Jean-Philippe Gaudreau 80764a301f MDL-51191 assign: Optimizing slow SQL query for MySQL 2015-09-11 11:12:24 -04:00
Jetha Chan 596e4603ae MDL-50685 core_form: disable hidden options, don't just hide 2015-09-11 15:09:29 +08:00
Marina Glancy 355e943c4b MDL-50892 mod: allow activity to have name '0' 2015-09-11 10:52:35 +08:00
Rajesh Taneja 8102007398 MDL-51101 behat: Fixed wrong hover step usage 2015-09-10 14:56:24 +08:00
Tim Hunt d881f12cde MDL-51147 qtype_match: stats should cope even if Q edited badly
The numbers become pretty meaningless, but we need to avoid fatal
errors.
2015-09-09 16:48:39 +01:00
Cameron Ball 7b027ea79c MDL-46157 drag and drop: Improve item labels
Ignore the edit menu when determining the region to drag.

Conflicts:
	lib/yui/build/moodle-core-dragdrop/moodle-core-dragdrop-min.js
2015-09-08 11:46:37 +08:00
Marina Glancy adb65a80f9 MDL-51008 grades: show correct default value in overview report 2015-09-08 10:11:47 +08:00
Corey Wallis f558ddcc4f MDL-51048 IMS-CC: Fix import & export of true/false questions 2015-09-07 16:06:00 +01:00
Marina Glancy f19717481d MDL-50921 message: allow popup messages from noreply user
Thanks Gary Harris for the patch
2015-09-07 08:15:10 +00:00
John Okely 8f4379b3d5 MDL-46239 task: Improve error message when timezone does not exist
Credit to Mark Nielsen for initial patch.
2015-09-07 15:51:27 +08:00
Mark Nelson dbeeebc0f6 MDL-49561 core_lib: added info to bennu readme
Also did some minor tweaks to existing code.
2015-09-04 19:40:02 -07:00
Lars Anderson e4d7edbf4f MDL-49561 core_calendar: Fix ical export for events with no duration 2015-09-04 19:40:01 -07:00
Marina Glancy 0e0c8c0dd7 MDL-41042 coursecat: better handle changes in coursecontact 2015-09-04 11:54:33 +08:00
Damyon Wiese f44ac5d3bf MDL-48861 assign: Fix the "needs grading" filter
When auto-creating a new grade record - do not set the timemodified > than the submission timemodified
2015-09-04 11:07:06 +08:00
rajesh Taneja f3661a7ea8 MDL-51285 mod_forum: Discussion post should not have same time stamp
Discussion post are sorted by timemodified
if they have same timemodified then results
will be random
2015-09-03 15:34:33 +08:00
David Mudrák fa36f1d1ab MDL-48165 workshop: Validate the rubric editing form 2015-09-01 23:39:43 +02:00
David Mudrák 235d8a4196 MDL-48165 workshop: Add support for assessment forms validation 2015-09-01 23:39:41 +02:00
Jinu Abraham 12259f01ce MDL-51232 mod_book: Filter name header 2015-08-31 12:06:29 +09:30
Marina Glancy 82c123c722 MDL-44707 block_html: copy files when copying instance 2015-08-28 16:04:27 +08:00
Daniel Thies 981379ffc2 MDL-51228 editor_atto: Apply style in plugins if selection is collapsed 2015-08-27 12:33:01 -05:00
Andrew Nicols 61662e6896 MDL-51170 core: Scheme section of URL should be case insensitive 2015-08-21 12:27:16 +08:00
Daniel Kosinski 5d9bc0acc3 MDL-46705 admin: Check if course with same shortname already exists 2015-03-24 23:36:28 +01:00
358 changed files with 7698 additions and 3680 deletions
+4 -2
View File
@@ -64,6 +64,8 @@ echo "<form id=\"authmenu\" method=\"post\" action=\"auth_config.php\">\n";
echo "<div>\n";
echo "<input type=\"hidden\" name=\"sesskey\" value=\"".sesskey()."\" />\n";
echo "<input type=\"hidden\" name=\"auth\" value=\"".$auth."\" />\n";
// HACK to prevent browsers from automatically inserting the user's password into the wrong fields.
echo prevent_form_autofill_password();
// auth plugin description
echo $OUTPUT->box_start();
@@ -146,8 +148,8 @@ function print_auth_lock_options($auth, $user_fields, $helptext, $retrieveopts,
// If custom field then pick name from database.
$fieldshortname = str_replace('profile_field_', '', $fieldname);
$fieldname = $customfieldname[$fieldshortname]->name;
} elseif (preg_match('/^(.+?)(\d+)$/', $fieldname, $matches)) {
$fieldname = get_string($matches[1]) . ' ' . $matches[2];
} elseif ($fieldname == 'phone1') {
$fieldname = get_string('phone');
} elseif ($fieldname == 'url') {
$fieldname = get_string('webpage');
} else {
+276
View File
@@ -1420,4 +1420,280 @@
</CUSTOM_CHECK>
</CUSTOM_CHECKS>
</MOODLE>
<MOODLE version="3.0" requires="2.2">
<UNICODE level="required">
<FEEDBACK>
<ON_ERROR message="unicoderequired" />
</FEEDBACK>
</UNICODE>
<DATABASE level="required">
<VENDOR name="mariadb" version="5.5.31" />
<VENDOR name="mysql" version="5.5.31" />
<VENDOR name="postgres" version="9.1" />
<VENDOR name="mssql" version="10.0" />
<VENDOR name="oracle" version="10.2" />
</DATABASE>
<PHP version="5.4.4" level="required">
</PHP>
<PCREUNICODE level="optional">
<FEEDBACK>
<ON_CHECK message="pcreunicodewarning" />
</FEEDBACK>
</PCREUNICODE>
<PHP_EXTENSIONS>
<PHP_EXTENSION name="iconv" level="required">
<FEEDBACK>
<ON_ERROR message="iconvrequired" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="mbstring" level="optional">
<FEEDBACK>
<ON_CHECK message="mbstringrecommended" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="curl" level="required">
<FEEDBACK>
<ON_ERROR message="curlrequired" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="openssl" level="optional">
<FEEDBACK>
<ON_CHECK message="opensslrecommended" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="tokenizer" level="optional">
<FEEDBACK>
<ON_CHECK message="tokenizerrecommended" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="xmlrpc" level="optional">
<FEEDBACK>
<ON_CHECK message="xmlrpcrecommended" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="soap" level="optional">
<FEEDBACK>
<ON_CHECK message="soaprecommended" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="ctype" level="required">
<FEEDBACK>
<ON_ERROR message="ctyperequired" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="zip" level="required">
<FEEDBACK>
<ON_ERROR message="ziprequired" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="zlib" level="required">
</PHP_EXTENSION>
<PHP_EXTENSION name="gd" level="required">
<FEEDBACK>
<ON_ERROR message="gdrequired" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="simplexml" level="required">
<FEEDBACK>
<ON_ERROR message="simplexmlrequired" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="spl" level="required">
<FEEDBACK>
<ON_ERROR message="splrequired" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="pcre" level="required">
</PHP_EXTENSION>
<PHP_EXTENSION name="dom" level="required">
</PHP_EXTENSION>
<PHP_EXTENSION name="xml" level="required">
</PHP_EXTENSION>
<PHP_EXTENSION name="intl" level="optional">
<FEEDBACK>
<ON_CHECK message="intlrecommended" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="json" level="required">
</PHP_EXTENSION>
<PHP_EXTENSION name="hash" level="required"/>
</PHP_EXTENSIONS>
<PHP_SETTINGS>
<PHP_SETTING name="memory_limit" value="96M" level="required">
<FEEDBACK>
<ON_ERROR message="settingmemorylimit" />
</FEEDBACK>
</PHP_SETTING>
<PHP_SETTING name="file_uploads" value="1" level="optional">
<FEEDBACK>
<ON_CHECK message="settingfileuploads" />
</FEEDBACK>
</PHP_SETTING>
<PHP_SETTING name="opcache.enable" value="1" level="optional">
<FEEDBACK>
<ON_CHECK message="opcacherecommended" />
</FEEDBACK>
</PHP_SETTING>
</PHP_SETTINGS>
<CUSTOM_CHECKS>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_database_storage_engine" level="required">
<FEEDBACK>
<ON_ERROR message="unsupporteddbstorageengine" />
</FEEDBACK>
</CUSTOM_CHECK>
<CUSTOM_CHECK file="question/engine/upgrade/upgradelib.php" function="quiz_attempts_upgraded" level="required">
<FEEDBACK>
<ON_ERROR message="quizattemptsupgradedmessage" />
</FEEDBACK>
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_slasharguments" level="optional">
<FEEDBACK>
<ON_CHECK message="slashargumentswarning" />
</FEEDBACK>
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_database_tables_row_format" level="optional">
<FEEDBACK>
<ON_CHECK message="unsupporteddbtablerowformat" />
</FEEDBACK>
</CUSTOM_CHECK>
</CUSTOM_CHECKS>
</MOODLE>
<MOODLE version="3.1" requires="2.7">
<UNICODE level="required">
<FEEDBACK>
<ON_ERROR message="unicoderequired" />
</FEEDBACK>
</UNICODE>
<DATABASE level="required">
<VENDOR name="mariadb" version="5.5.31" />
<VENDOR name="mysql" version="5.5.31" />
<VENDOR name="postgres" version="9.1" />
<VENDOR name="mssql" version="10.0" />
<VENDOR name="oracle" version="10.2" />
</DATABASE>
<PHP version="5.4.4" level="required">
</PHP>
<PCREUNICODE level="optional">
<FEEDBACK>
<ON_CHECK message="pcreunicodewarning" />
</FEEDBACK>
</PCREUNICODE>
<PHP_EXTENSIONS>
<PHP_EXTENSION name="iconv" level="required">
<FEEDBACK>
<ON_ERROR message="iconvrequired" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="mbstring" level="optional">
<FEEDBACK>
<ON_CHECK message="mbstringrecommended" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="curl" level="required">
<FEEDBACK>
<ON_ERROR message="curlrequired" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="openssl" level="optional">
<FEEDBACK>
<ON_CHECK message="opensslrecommended" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="tokenizer" level="optional">
<FEEDBACK>
<ON_CHECK message="tokenizerrecommended" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="xmlrpc" level="optional">
<FEEDBACK>
<ON_CHECK message="xmlrpcrecommended" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="soap" level="optional">
<FEEDBACK>
<ON_CHECK message="soaprecommended" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="ctype" level="required">
<FEEDBACK>
<ON_ERROR message="ctyperequired" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="zip" level="required">
<FEEDBACK>
<ON_ERROR message="ziprequired" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="zlib" level="required">
</PHP_EXTENSION>
<PHP_EXTENSION name="gd" level="required">
<FEEDBACK>
<ON_ERROR message="gdrequired" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="simplexml" level="required">
<FEEDBACK>
<ON_ERROR message="simplexmlrequired" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="spl" level="required">
<FEEDBACK>
<ON_ERROR message="splrequired" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="pcre" level="required">
</PHP_EXTENSION>
<PHP_EXTENSION name="dom" level="required">
</PHP_EXTENSION>
<PHP_EXTENSION name="xml" level="required">
</PHP_EXTENSION>
<PHP_EXTENSION name="intl" level="optional">
<FEEDBACK>
<ON_CHECK message="intlrecommended" />
</FEEDBACK>
</PHP_EXTENSION>
<PHP_EXTENSION name="json" level="required">
</PHP_EXTENSION>
<PHP_EXTENSION name="hash" level="required"/>
</PHP_EXTENSIONS>
<PHP_SETTINGS>
<PHP_SETTING name="memory_limit" value="96M" level="required">
<FEEDBACK>
<ON_ERROR message="settingmemorylimit" />
</FEEDBACK>
</PHP_SETTING>
<PHP_SETTING name="file_uploads" value="1" level="optional">
<FEEDBACK>
<ON_CHECK message="settingfileuploads" />
</FEEDBACK>
</PHP_SETTING>
<PHP_SETTING name="opcache.enable" value="1" level="optional">
<FEEDBACK>
<ON_CHECK message="opcacherecommended" />
</FEEDBACK>
</PHP_SETTING>
</PHP_SETTINGS>
<CUSTOM_CHECKS>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_database_storage_engine" level="required">
<FEEDBACK>
<ON_ERROR message="unsupporteddbstorageengine" />
</FEEDBACK>
</CUSTOM_CHECK>
<CUSTOM_CHECK file="question/engine/upgrade/upgradelib.php" function="quiz_attempts_upgraded" level="required">
<FEEDBACK>
<ON_ERROR message="quizattemptsupgradedmessage" />
</FEEDBACK>
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_slasharguments" level="optional">
<FEEDBACK>
<ON_CHECK message="slashargumentswarning" />
</FEEDBACK>
</CUSTOM_CHECK>
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_database_tables_row_format" level="optional">
<FEEDBACK>
<ON_CHECK message="unsupporteddbtablerowformat" />
</FEEDBACK>
</CUSTOM_CHECK>
</CUSTOM_CHECKS>
</MOODLE>
</COMPATIBILITY_MATRIX>
+2
View File
@@ -39,6 +39,8 @@ require_once($CFG->dirroot . '/' . $CFG->admin . '/registration/forms.php');
require_once($CFG->dirroot . '/webservice/lib.php');
require_once($CFG->dirroot . '/' . $CFG->admin . '/registration/lib.php');
require_sesskey();
$huburl = required_param('huburl', PARAM_URL);
$huburl = rtrim($huburl, "/");
+6 -2
View File
@@ -50,7 +50,8 @@ $table->set_attribute('class', 'admintable generaltable');
$table->setup();
$plugins = array();
foreach (core_component::get_plugin_list('report') as $plugin => $plugindir) {
$availableplugins = core_component::get_plugin_list('report');
foreach ($availableplugins as $plugin => $plugindir) {
if (get_string_manager()->string_exists('pluginname', 'report_' . $plugin)) {
$strpluginname = get_string('pluginname', 'report_' . $plugin);
} else {
@@ -80,7 +81,10 @@ foreach ($plugins as $plugin => $name) {
$uninstall = html_writer::link($uninstallurl, $struninstall);
}
$stores = $logmanager->get_supported_logstores('report_' . $plugin);
$stores = array();
if (isset($availableplugins[$plugin])) {
$stores = $logmanager->get_supported_logstores('report_' . $plugin);
}
if ($stores === false) {
$supportedstores = get_string('logstorenotrequired', 'admin');
} else if (!empty($stores)) {
+2 -1
View File
@@ -64,7 +64,8 @@ mybadges,badges|/badges/mybadges.php|award',
array('0' => new lang_string('default', 'calendar'),
'%I:%M %p' => new lang_string('timeformat_12', 'calendar'),
'%H:%M' => new lang_string('timeformat_24', 'calendar'))));
$temp->add(new admin_setting_configselect('calendar_startwday', new lang_string('configstartwday', 'admin'), new lang_string('helpstartofweek', 'admin'), 0,
$temp->add(new admin_setting_configselect('calendar_startwday', new lang_string('configstartwday', 'admin'),
new lang_string('helpstartofweek', 'admin'), get_string('firstdayofweek', 'langconfig'),
array(
0 => new lang_string('sunday', 'calendar'),
1 => new lang_string('monday', 'calendar'),
+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));
+1 -1
View File
@@ -11,7 +11,7 @@ $hassiteconfig = has_capability('moodle/site:config', $systemcontext);
$ADMIN->add('root', new admin_externalpage('adminnotifications', new lang_string('notifications'), "$CFG->wwwroot/$CFG->admin/index.php"));
$ADMIN->add('root', new admin_externalpage('registrationmoodleorg', new lang_string('registration', 'admin'),
"$CFG->wwwroot/$CFG->admin/registration/register.php?huburl=" . HUB_MOODLEORGHUBURL . "&hubname=Moodle.org"));
"$CFG->wwwroot/$CFG->admin/registration/register.php?huburl=" . HUB_MOODLEORGHUBURL . "&hubname=Moodle.org&sesskey=" . sesskey()));
$ADMIN->add('root', new admin_externalpage('registrationhub', new lang_string('registerwith', 'hub'),
"$CFG->wwwroot/$CFG->admin/registration/register.php", 'moodle/site:config', true));
$ADMIN->add('root', new admin_externalpage('registrationhubs', new lang_string('hubs', 'admin'),
@@ -137,6 +137,17 @@ Feature: Verify that all form fields values can be get and set
And I press "Save my choice"
And the field "one" matches value "1"
And the field "two" matches value ""
# Check if field xpath set/match works.
And I am on site homepage
And I follow "Course 1"
And I navigate to "Edit settings" node in "Course administration"
And I set the field with xpath "//input[@id='id_idnumber']" to "Course id number"
And the field with xpath "//input[@name='idnumber']" matches value "Course id number"
And the field with xpath "//input[@name='idnumber']" does not match value ""
And I press "Save changes"
And I navigate to "Edit settings" node in "Course administration"
And the field "Course ID number" matches value "Course id number"
Scenario: with JS disabled all form fields getters and setters works as expected
@@ -208,10 +208,13 @@ class tool_generator_course_backend extends tool_generator_backend {
*/
private function create_course() {
$this->log('createcourse', $this->shortname);
$courserecord = array('shortname' => $this->shortname,
'fullname' => get_string('fullname', 'tool_generator',
array('size' => get_string('shortsize_' . $this->size, 'tool_generator'))),
'numsections' => self::$paramsections[$this->size]);
$courserecord = array(
'shortname' => $this->shortname,
'fullname' => get_string('fullname', 'tool_generator',
array('size' => get_string('shortsize_' . $this->size, 'tool_generator'))),
'numsections' => self::$paramsections[$this->size],
'startdate' => usergetmidnight(time())
);
return $this->generator->create_course($courserecord, array('createsections' => true));
}
@@ -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) {
@@ -735,6 +739,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).
*
@@ -219,10 +219,12 @@ class rules extends \table_sql implements \renderable {
if (has_capability('tool/monitor:subscribe', \context_system::instance())) {
$options[0] = get_string('site');
}
if ($courses = get_user_capability_course('tool/monitor:subscribe', null, true, 'fullname', $orderby)) {
if ($courses = get_user_capability_course('tool/monitor:subscribe', null, true, 'fullname, visible', $orderby)) {
foreach ($courses as $course) {
$options[$course->id] = format_string($course->fullname, true,
array('context' => \context_course::instance($course->id)));
$coursecontext = \context_course::instance($course->id);
if ($course->visible || has_capability('moodle/course:viewhiddencourses', $coursecontext)) {
$options[$course->id] = format_string($course->fullname, true, array('context' => $coursecontext));
}
}
}
$url = new \moodle_url('/admin/tool/monitor/index.php');
+5 -1
View File
@@ -78,8 +78,12 @@
echo $OUTPUT->header();
$fullname = fullname($user, true);
echo $OUTPUT->heading(get_string('deleteuser', 'admin'));
$optionsyes = array('delete'=>$delete, 'confirm'=>md5($delete), 'sesskey'=>sesskey());
echo $OUTPUT->confirm(get_string('deletecheckfull', '', "'$fullname'"), new moodle_url($returnurl, $optionsyes), $returnurl);
$deleteurl = new moodle_url($returnurl, $optionsyes);
$deletebutton = new single_button($deleteurl, get_string('delete'), 'post');
echo $OUTPUT->confirm(get_string('deletecheckfull', '', "'$fullname'"), $deletebutton, $returnurl);
echo $OUTPUT->footer();
die;
} else if (data_submitted() and !$user->deleted) {
+28
View File
@@ -314,6 +314,7 @@ class auth_plugin_db extends auth_plugin_base {
$updateuser = new stdClass();
$updateuser->id = $user->id;
$updateuser->suspended = 1;
$updateuser = $this->clean_data($updateuser);
user_update_user($updateuser, false);
$trace->output(get_string('auth_dbsuspenduser', 'auth_db', array('name'=>$user->username, 'id'=>$user->id)), 1);
}
@@ -400,6 +401,7 @@ class auth_plugin_db extends auth_plugin_base {
$updateuser = new stdClass();
$updateuser->id = $olduser->id;
$updateuser->suspended = 0;
$updateuser = $this->clean_data($updateuser);
user_update_user($updateuser);
$trace->output(get_string('auth_dbreviveduser', 'auth_db', array('name' => $username,
'id' => $olduser->id)), 1);
@@ -422,6 +424,7 @@ class auth_plugin_db extends auth_plugin_base {
$trace->output(get_string('auth_dbinsertuserduplicate', 'auth_db', array('username'=>$user->username, 'auth'=>$collision->auth)), 1);
continue;
}
$user = $this->clean_data($user);
try {
$id = user_create_user($user, false); // It is truly a new user.
$trace->output(get_string('auth_dbinsertuser', 'auth_db', array('name'=>$user->username, 'id'=>$id)), 1);
@@ -563,6 +566,7 @@ class auth_plugin_db extends auth_plugin_base {
}
if ($needsupdate) {
require_once($CFG->dirroot . '/user/lib.php');
$updateuser = $this->clean_data($updateuser);
user_update_user($updateuser);
}
return $DB->get_record('user', array('id'=>$userid, 'deleted'=>0));
@@ -888,6 +892,30 @@ class auth_plugin_db extends auth_plugin_base {
error_reporting($CFG->debug);
ob_end_flush();
}
/**
* Clean the user data that comes from an external database.
*
* @param array $user the user data to be validated against properties definition.
* @return stdClass $user the cleaned user data.
*/
public function clean_data($user) {
if (empty($user)) {
return $user;
}
foreach ($user as $field => $value) {
// Get the property parameter type and do the cleaning.
try {
$property = core_user::get_property_definition($field);
$user->$field = clean_param($value, $property['type']);
} catch (coding_exception $e) {
debugging("The property '$field' could not be cleaned.", DEBUG_DEVELOPER);
}
}
return $user;
}
}
+73
View File
@@ -374,4 +374,77 @@ class auth_db_testcase extends advanced_testcase {
$this->cleanup_auth_database();
}
/**
* Testing the clean_data() method.
*/
public function test_clean_data() {
global $DB;
$this->resetAfterTest(false);
$this->preventResetByRollback();
$this->init_auth_database();
$auth = get_auth_plugin('db');
$auth->db_init();
// Create users on external table.
$extdbuser1 = (object)array('name'=>'u1', 'pass'=>'heslo', 'email'=>'u1@example.com');
$extdbuser1->id = $DB->insert_record('auth_db_users', $extdbuser1);
// User with malicious data on the name.
$extdbuser2 = (object)array('name'=>'user<script>alert(1);</script>xss', 'pass'=>'heslo', 'email'=>'xssuser@example.com');
$extdbuser2->id = $DB->insert_record('auth_db_users', $extdbuser2);
$trace = new null_progress_trace();
// Let's test user sync make sure still works as expected..
$auth->sync_users($trace, true);
// Get the user on moodle user table.
$user2 = $DB->get_record('user', array('email'=> $extdbuser2->email, 'auth'=>'db'));
// The malicious code should be sanitized.
$this->assertEquals($user2->username, 'userscriptalert1scriptxss');
$this->assertNotEquals($user2->username, $extdbuser2->name);
// User with correct data, should be equal to external db.
$user1 = $DB->get_record('user', array('email'=> $extdbuser1->email, 'auth'=>'db'));
$this->assertEquals($extdbuser1->name, $user1->username);
$this->assertEquals($extdbuser1->email, $user1->email);
// Now, let's update the name.
$extdbuser2->name = 'user no xss anymore';
$DB->update_record('auth_db_users', $extdbuser2);
// Run sync again to update the user data.
$auth->sync_users($trace, true);
// The user information should be updated.
$user2 = $DB->get_record('user', array('username' => 'usernoxssanymore', 'auth' => 'db'));
// The spaces should be removed, as it's the username.
$this->assertEquals($user2->username, 'usernoxssanymore');
// Now let's test just the clean_data() method isolated.
// Testing PARAM_USERNAME, PARAM_NOTAGS, PARAM_RAW_TRIMMED and others.
$user3 = new stdClass();
$user3->firstname = 'John <script>alert(1)</script> Doe';
$user3->username = 'john%#&~%*_doe';
$user3->email = ' john@testing.com ';
$user3->deleted = 'no';
$user3->description = '<b>A description <script>alert(123)</script>about myself.</b>';
$user3cleaned = $auth->clean_data($user3);
// Expected results.
$this->assertEquals($user3cleaned->firstname, 'John alert(1) Doe');
$this->assertEquals($user3cleaned->email, 'john@testing.com');
$this->assertEquals($user3cleaned->deleted, 0);
$this->assertEquals($user3->description, '<b>A description about myself.</b>');
$this->assertEquals($user3->username, 'john_doe');
// Try to clean an invalid property (fullname).
$user3->fullname = 'John Doe';
$auth->clean_data($user3);
$this->assertDebuggingCalled("The property 'fullname' could not be cleaned.");
$this->cleanup_auth_database();
}
}
+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';
+1 -1
View File
@@ -60,7 +60,7 @@ if (!isset($config->changepasswordurl)) {
</tr>
<tr valign="top" >
<td align="right"><?php echo html_writer::label(get_string('auth_radiustype_key', 'auth_radius'), 'menuradiustype'); ?>: </td>
<td align="right"><?php echo html_writer::label(get_string('auth_radiustype_key', 'auth_radius') . ':', 'menuradiustype'); ?> </td>
<td>
<?php
+2 -1
View File
@@ -210,7 +210,8 @@ class auth_plugin_shibboleth extends auth_plugin_base {
}
// Overwrite redirect in order to send user to Shibboleth logout page and let him return back
$redirect = $this->config->logout_handler.'?return='.urlencode($temp_redirect);
$redirecturl = new moodle_url($this->config->logout_handler, array('return' => $temp_redirect));
$redirect = $redirecturl->out();
}
}
+16 -2
View File
@@ -328,7 +328,14 @@ class tree extends tree_node {
// Loop through all valid children.
foreach ($this->children as $index => $child) {
if (!$child->is_applied_to_user_lists()) {
continue;
if ($andoperator) {
continue;
} else {
// OR condition with one option that doesn't restrict user
// lists = everyone is allowed.
$anyconditions = false;
break;
}
}
$childresult = $child->filter_user_list($users, $innernot, $info, $checker);
if ($andoperator) {
@@ -359,7 +366,14 @@ class tree extends tree_node {
$childresults = array();
foreach ($this->children as $index => $child) {
if (!$child->is_applied_to_user_lists()) {
continue;
if ($andoperator) {
continue;
} else {
// OR condition with one option that doesn't restrict user
// lists = everyone is allowed.
$childresults = array();
break;
}
}
$childresult = $child->get_user_list_sql($innernot, $info, $onlyactive);
if ($childresult[0]) {
@@ -234,4 +234,68 @@ class condition extends \core_availability\condition {
}
return false;
}
/**
* Changes all date restrictions on a course by the specified shift amount.
* Used by the course reset feature.
*
* @param int $courseid Course id
* @param int $timeshift Offset in seconds
*/
public static function update_all_dates($courseid, $timeshift) {
global $DB;
$modinfo = get_fast_modinfo($courseid);
$anychanged = false;
// Adjust dates from all course modules.
foreach ($modinfo->cms as $cm) {
if (!$cm->availability) {
continue;
}
$info = new \core_availability\info_module($cm);
$tree = $info->get_availability_tree();
$dates = $tree->get_all_children('availability_date\condition');
$changed = false;
foreach ($dates as $date) {
$date->time += $timeshift;
$changed = true;
}
// Save the updated course module.
if ($changed) {
$DB->set_field('course_modules', 'availability', json_encode($tree->save()),
array('id' => $cm->id));
$anychanged = true;
}
}
// Adjust dates from all course sections.
foreach ($modinfo->get_section_info_all() as $section) {
if (!$section->availability) {
continue;
}
$info = new \core_availability\info_section($section);
$tree = $info->get_availability_tree();
$dates = $tree->get_all_children('availability_date\condition');
$changed = false;
foreach ($dates as $date) {
$date->time += $timeshift;
$changed = true;
}
// Save the updated course module.
if ($changed) {
$DB->set_field('course_sections', 'availability', json_encode($tree->save()),
array('id' => $section->id));
$anychanged = true;
}
}
// Ensure course cache is cleared if required.
if ($anychanged) {
rebuild_course_cache($courseid, true);
}
}
}
@@ -25,6 +25,7 @@
defined('MOODLE_INTERNAL') || die();
use availability_date\condition;
use core_availability\tree;
/**
* Unit tests for the date condition.
@@ -233,4 +234,53 @@ class availability_date_condition_testcase extends advanced_testcase {
$information = $date->get_standalone_description(true, true, $info);
$this->assertRegExp('~from.*5 March 2014([^0-9]*)$~', $information);
}
/**
* Tests the update_all_dates function.
*/
public function test_update_all_dates() {
global $DB;
$this->resetAfterTest();
// Create a course with 3 pages.
$generator = $this->getDataGenerator();
$course = $generator->create_course();
$rec = array('course' => $course);
$page1 = $generator->get_plugin_generator('mod_page')->create_instance($rec);
$page2 = $generator->get_plugin_generator('mod_page')->create_instance($rec);
$page3 = $generator->get_plugin_generator('mod_page')->create_instance($rec);
// Set the availability page 2 to a simple date condition. You can access
// it from 1337 onwards.
$simplecondition = tree::get_root_json(array(
condition::get_json(condition::DIRECTION_FROM, 1337)));
$DB->set_field('course_modules', 'availability',
json_encode($simplecondition), array('id' => $page2->cmid));
// Set page 3 to a complex set of conditions including a nested date condition.
// You can access it until 1459, *or* after 2810 if you belong to a group.
$complexcondition = tree::get_root_json(array(
condition::get_json(condition::DIRECTION_UNTIL, 1459),
tree::get_nested_json(array(
condition::get_json(condition::DIRECTION_FROM, 2810),
\availability_group\condition::get_json()))),
tree::OP_OR);
$DB->set_field('course_modules', 'availability',
json_encode($complexcondition), array('id' => $page3->cmid));
// Now use the update_all_dates function to move date forward 100000.
condition::update_all_dates($course->id, 100000);
// Get the expected conditions after adjusting time, and compare to database.
$simplecondition->c[0]->t = 101337;
$complexcondition->c[0]->t = 101459;
$complexcondition->c[1]->c[0]->t = 102810;
$this->assertEquals($simplecondition, json_decode(
$DB->get_field('course_modules', 'availability', array('id' => $page2->cmid))));
$this->assertEquals($complexcondition, json_decode(
$DB->get_field('course_modules', 'availability', array('id' => $page3->cmid))));
// The one without availability conditions should still be null.
$this->assertNull($DB->get_field('course_modules', 'availability', array('id' => $page1->cmid)));
}
}
@@ -229,7 +229,12 @@ class condition extends \core_availability\condition {
* @return stdClass Object representing condition
*/
public static function get_json($groupid = 0) {
return (object)array('type' => 'group', 'id' => (int)$groupid);
$result = (object)array('type' => 'group');
// Id is only included if set.
if ($groupid) {
$result->id = (int)$groupid;
}
return $result;
}
public function get_user_list_sql($not, \core_availability\info $info, $onlyactive) {
+5 -5
View File
@@ -35,7 +35,7 @@ use core_availability\info_section;
* @copyright 2014 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class info_testcase extends \advanced_testcase {
class info_testcase extends advanced_testcase {
public function setUp() {
// Load the mock condition so that it can be used.
require_once(__DIR__ . '/fixtures/mock_condition.php');
@@ -87,8 +87,8 @@ class info_testcase extends \advanced_testcase {
// Check invalid one.
$info = new info_module($cm3);
$this->assertFalse($info->is_available($information));
$debugging = phpunit_util::get_debugging_messages();
phpunit_util::reset_debugging();
$debugging = $this->getDebuggingMessages();
$this->resetDebugging();
$this->assertEquals(1, count($debugging));
$this->assertContains('Invalid availability', $debugging[0]->message);
@@ -141,8 +141,8 @@ class info_testcase extends \advanced_testcase {
// Check invalid one.
$info = new info_section($sections[3]);
$this->assertFalse($info->is_available($information));
$debugging = phpunit_util::get_debugging_messages();
phpunit_util::reset_debugging();
$debugging = $this->getDebuggingMessages();
$this->resetDebugging();
$this->assertEquals(1, count($debugging));
$this->assertContains('Invalid availability', $debugging[0]->message);
+19
View File
@@ -600,6 +600,15 @@ class tree_testcase extends \advanced_testcase {
ksort($result);
$this->assertEquals(array(1, 3), array_keys($result));
// Tree with OR condition one of which doesn't filter.
$structure = tree::get_root_json(array(
self::mock(array('filter' => array(3))),
self::mock(array())), tree::OP_OR);
$tree = new tree($structure);
$result = $tree->filter_user_list($users, false, $info, $checker);
ksort($result);
$this->assertEquals(array(1, 2, 3), array_keys($result));
// Tree with two condition that both filter (&).
$structure = tree::get_root_json(array(
self::mock(array('filter' => array(2, 3))),
@@ -718,6 +727,16 @@ class tree_testcase extends \advanced_testcase {
$result = $DB->get_fieldset_sql($sql, $params);
sort($result);
$this->assertEquals(array($userinneither->id), $result);
// Tree with 'OR' of group conditions and a non-filtering condition.
// The non-filtering condition should mean that ALL users are included.
$tree = new tree(tree::get_root_json(array(
\availability_group\condition::get_json($group1->id),
\availability_date\condition::get_json(\availability_date\condition::DIRECTION_UNTIL, 3)
), tree::OP_OR));
list($sql, $params) = $tree->get_user_list_sql(false, $info, false);
$this->assertEquals('', $sql);
$this->assertEquals(array(), $params);
}
/**
+12 -2
View File
@@ -27,8 +27,18 @@ class cc_assesment_question_truefalse extends cc_assesment_question_proc_base {
public function __construct($quiz, $questions, $manifest, $section, $question_node, $rootpath, $contextid, $outdir) {
parent::__construct($quiz, $questions, $manifest, $section, $question_node, $rootpath, $contextid, $outdir);
$this->qtype = cc_qti_profiletype::true_false;
$this->correct_answer_node_id = $this->questions->nodeValue(
'plugin_qtype_truefalse_question/truefalse/trueanswer', $this->question_node);
// Determine the correct answer by finding out which answer has the non zero fraction...
// This is because a true / false question type can have 'false' as the correct answer.
$answers = $this->questions->nodeList('plugin_qtype_truefalse_question/answers/answer', $this->question_node);
foreach ($answers as $answer) {
$fraction = $this->questions->nodeValue('fraction', $answer);
if ($fraction != 0) {
$this->correct_answer_node_id = (int)$this->questions->nodeValue('@id', $answer);
}
}
$maximum_quiz_grade = (int)$this->quiz->nodeValue('/activity/quiz/grade');
$this->total_grade_value = ($maximum_quiz_grade + 1).'.0000000';
}
+17 -14
View File
@@ -885,27 +885,30 @@ class cc_quiz extends entities {
$sheet_question_categories_question = cc2moodle::loadsheet(SHEET_COURSE_QUESTION_CATEGORIES_QUESTION_CATEGORY_QUESTION_TRUE_FALSE);
$max_score = 0;
$true_answer_id = 0;
$false_answer_id = 0;
$trueanswer = null;
$falseanswer = null;
if (!empty($question['answers'])) {
// Identify the true and false answers.
foreach ($question['answers'] as $answer) {
if ($answer['score'] > $max_score) {
$max_score = $answer['score'];
$true_answer_id = $answer['id'];
if ($answer['identifier'] == 'true') {
$trueanswer = $answer;
} else if ($answer['identifier'] == 'false') {
$falseanswer = $answer;
} else {
// Should not happen, but just in case.
throw new coding_exception("Unknown answer identifier detected" .
" in true/false quiz question with id {$question['id']}.");
}
$node_course_question_categories_question_answer .= $this->create_node_course_question_categories_question_category_question_answer($answer);
}
foreach ($question['answers'] as $answer) {
if ($answer['id'] != $true_answer_id) {
$max_score = $answer['score'];
$false_answer_id = $answer['id'];
}
// Make sure the true and false answer was found.
if (is_null($trueanswer) || is_null($falseanswer)) {
throw new coding_exception("Unable to correctly identify the " .
"true and false answers in the question with id {$question['id']}.");
}
}
@@ -914,8 +917,8 @@ class cc_quiz extends entities {
'[#false_answer_id#]');
$replace_values = array($node_course_question_categories_question_answer,
$true_answer_id,
$false_answer_id);
$trueanswer['id'],
$falseanswer['id']);
$node_question_categories_question = str_replace($find_tags, $replace_values, $sheet_question_categories_question);
+17 -14
View File
@@ -908,27 +908,30 @@ class cc11_quiz extends entities11 {
$sheet_question_categories_question = cc112moodle::loadsheet(SHEET_COURSE_QUESTION_CATEGORIES_QUESTION_CATEGORY_QUESTION_TRUE_FALSE);
$max_score = 0;
$true_answer_id = 0;
$false_answer_id = 0;
$trueanswer = null;
$falseanswer = null;
if (!empty($question['answers'])) {
// Identify the true and false answers.
foreach ($question['answers'] as $answer) {
if ($answer['score'] > $max_score) {
$max_score = $answer['score'];
$true_answer_id = $answer['id'];
if ($answer['identifier'] == 'true') {
$trueanswer = $answer;
} else if ($answer['identifier'] == 'false') {
$falseanswer = $answer;
} else {
// Should not happen, but just in case.
throw new coding_exception("Unknown answer identifier detected " .
"in true/false quiz question with id {$question['id']}.");
}
$node_course_question_categories_question_answer .= $this->create_node_course_question_categories_question_category_question_answer($answer);
}
foreach ($question['answers'] as $answer) {
if ($answer['id'] != $true_answer_id) {
$max_score = $answer['score'];
$false_answer_id = $answer['id'];
}
// Make sure the true and false answer was found.
if (is_null($trueanswer) || is_null($falseanswer)) {
throw new coding_exception("Unable to correctly identify the " .
"true and false answers in the question with id {$question['id']}.");
}
}
@@ -937,8 +940,8 @@ class cc11_quiz extends entities11 {
'[#false_answer_id#]');
$replace_values = array($node_course_question_categories_question_answer,
$true_answer_id,
$false_answer_id);
$trueanswer['id'],
$falseanswer['id']);
$node_question_categories_question = str_replace($find_tags, $replace_values, $sheet_question_categories_question);
+33 -7
View File
@@ -169,13 +169,39 @@ abstract class restore_check {
}
}
// Ensure the user has the rolldates capability. If not we want to lock this
// settings so that they cannot change it.
$hasrolldatescap = has_capability('moodle/restore:rolldates', $coursectx, $userid);
if ($type == backup::TYPE_1COURSE && !$hasrolldatescap) {
$datesetting = $restore_controller->get_plan()->get_setting('course_startdate');
if ($datesetting) {
$datesetting->set_status(base_setting::LOCKED_BY_PERMISSION);
if ($type == backup::TYPE_1COURSE) {
// Ensure the user has the rolldates capability. If not we want to lock this
// settings so that they cannot change it.
$hasrolldatescap = has_capability('moodle/restore:rolldates', $coursectx, $userid);
if (!$hasrolldatescap) {
$datesetting = $restore_controller->get_plan()->get_setting('course_startdate');
if ($datesetting) {
$datesetting->set_status(base_setting::LOCKED_BY_PERMISSION);
}
}
// Ensure the user has the changefullname capability. If not we want to lock
// the setting so that they cannot change it.
$haschangefullnamecap = has_capability('moodle/course:changefullname', $coursectx, $userid);
if (!$haschangefullnamecap) {
$fullnamesetting = $restore_controller->get_plan()->get_setting('course_fullname');
$fullnamesetting->set_status(base_setting::LOCKED_BY_PERMISSION);
}
// Ensure the user has the changeshortname capability. If not we want to lock
// the setting so that they cannot change it.
$haschangeshortnamecap = has_capability('moodle/course:changeshortname', $coursectx, $userid);
if (!$haschangeshortnamecap) {
$shortnamesetting = $restore_controller->get_plan()->get_setting('course_shortname');
$shortnamesetting->set_status(base_setting::LOCKED_BY_PERMISSION);
}
// Ensure the user has the update capability. If not we want to lock
// the overwrite setting so that they cannot change it.
$hasupdatecap = has_capability('moodle/course:update', $coursectx, $userid);
if (!$hasupdatecap) {
$overwritesetting = $restore_controller->get_plan()->get_setting('overwrite_conf');
$overwritesetting->set_status(base_setting::LOCKED_BY_PERMISSION);
}
}
+3 -3
View File
@@ -45,7 +45,7 @@ class edit_backpack_form extends moodleform {
$mform->addElement('html', html_writer::tag('span', '', array('class' => 'notconnected', 'id' => 'connection-error')));
$mform->addElement('header', 'backpackheader', get_string('backpackconnection', 'badges'));
$mform->addHelpButton('backpackheader', 'backpackconnection', 'badges');
$mform->addElement('static', 'url', get_string('url'), 'http://' . BADGE_BACKPACKURL);
$mform->addElement('static', 'url', get_string('url'), BADGE_BACKPACKURL);
$status = html_writer::tag('span', get_string('notconnected', 'badges'),
array('class' => 'notconnected', 'id' => 'connection-status'));
$mform->addElement('static', 'status', get_string('status'), $status);
@@ -67,7 +67,7 @@ class edit_backpack_form extends moodleform {
$mform->addElement('hidden', 'userid', $USER->id);
$mform->setType('userid', PARAM_INT);
$mform->addElement('hidden', 'backpackurl', 'http://' . BADGE_BACKPACKURL);
$mform->addElement('hidden', 'backpackurl', BADGE_BACKPACKURL);
$mform->setType('backpackurl', PARAM_URL);
}
@@ -118,7 +118,7 @@ class edit_collections_form extends moodleform {
$mform->addElement('header', 'backpackheader', get_string('backpackconnection', 'badges'));
$mform->addHelpButton('backpackheader', 'backpackconnection', 'badges');
$mform->addElement('static', 'url', get_string('url'), 'http://' . BADGE_BACKPACKURL);
$mform->addElement('static', 'url', get_string('url'), BADGE_BACKPACKURL);
$status = html_writer::tag('span', get_string('connected', 'badges'), array('class' => 'connected'));
$mform->addElement('static', 'status', get_string('status'), $status);
+2 -2
View File
@@ -87,7 +87,7 @@ if (!isset($data->status) || $data->status != 'okay') {
// Make sure email matches a backpack.
$check = new stdClass();
$check->backpackurl = 'http://' . BADGE_BACKPACKURL;
$check->backpackurl = BADGE_BACKPACKURL;
$check->email = $data->email;
$bp = new OpenBadgesBackpackHandler($check);
@@ -106,7 +106,7 @@ if (isset($request->status) && $request->status == 'missing') {
$obj = new stdClass();
$obj->userid = $USER->id;
$obj->email = $data->email;
$obj->backpackurl = 'http://' . BADGE_BACKPACKURL;
$obj->backpackurl = BADGE_BACKPACKURL;
$obj->backpackuid = $backpackuid;
$obj->autosync = 0;
$obj->password = '';
+26 -31
View File
@@ -170,33 +170,31 @@ class award_criteria_profile extends award_criteria {
}
$join = '';
$where = '';
$whereparts = array();
$sqlparams = array();
$rule = ($this->method == BADGE_CRITERIA_AGGREGATION_ANY) ? ' OR ' : ' AND ';
foreach ($this->params as $param) {
if (is_numeric($param['field'])) {
$infodata[] = " uid.fieldid = :fieldid{$param['field']} ";
$sqlparams["fieldid{$param['field']}"] = $param['field'];
// This is a custom field.
$idx = count($whereparts) + 1;
$join .= " LEFT JOIN {user_info_data} uid{$idx} ON uid{$idx}.userid = u.id AND uid{$idx}.fieldid = :fieldid{$idx} ";
$sqlparams["fieldid{$idx}"] = $param['field'];
$whereparts[] = "uid{$idx}.id IS NOT NULL";
} else {
$userdata[] = $DB->sql_isnotempty('u', "u.{$param['field']}", false, true);
// This is a field from {user} table.
$whereparts[] = $DB->sql_isnotempty('u', "u.{$param['field']}", false, true);
}
}
// Add user custom field parameters if there are any.
if (!empty($infodata)) {
$extraon = implode($rule, $infodata);
$join = " LEFT JOIN {user_info_data} uid ON uid.userid = u.id AND ({$extraon})";
}
// Add user table field parameters if there are any.
if (!empty($userdata)) {
$extraon = implode($rule, $userdata);
$where = " AND ({$extraon})";
}
$sqlparams['userid'] = $userid;
$sql = "SELECT u.* FROM {user} u " . $join . " WHERE u.id = :userid " . $where;
if ($whereparts) {
$where = " AND (" . implode($rule, $whereparts) . ")";
} else {
$where = '';
}
$sql = "SELECT 1 FROM {user} u " . $join . " WHERE u.id = :userid $where";
$overall = $DB->record_exists_sql($sql, $sqlparams);
return $overall;
@@ -212,29 +210,26 @@ class award_criteria_profile extends award_criteria {
global $DB;
$join = '';
$where = '';
$whereparts = array();
$params = array();
$rule = ($this->method == BADGE_CRITERIA_AGGREGATION_ANY) ? ' OR ' : ' AND ';
foreach ($this->params as $param) {
if (is_numeric($param['field'])) {
$infodata[] = " uid.fieldid = :fieldid{$param['field']} ";
$params["fieldid{$param['field']}"] = $param['field'];
// This is a custom field.
$idx = count($whereparts);
$join .= " LEFT JOIN {user_info_data} uid{$idx} ON uid{$idx}.userid = u.id AND uid{$idx}.fieldid = :fieldid{$idx} ";
$params["fieldid{$idx}"] = $param['field'];
$whereparts[] = "uid{$idx}.id IS NOT NULL";
} else {
$userdata[] = $DB->sql_isnotempty('u', "u.{$param['field']}", false, true);
$whereparts[] = $DB->sql_isnotempty('u', "u.{$param['field']}", false, true);
}
}
// Add user custom fields if there are any.
if (!empty($infodata)) {
$extraon = implode($rule, $infodata);
$join = " LEFT JOIN {user_info_data} uid ON uid.userid = u.id AND ({$extraon})";
}
// Add user table fields if there are any.
if (!empty($userdata)) {
$extraon = implode($rule, $userdata);
$where = " AND ({$extraon})";
if ($whereparts) {
$where = " AND (" . implode($rule, $whereparts) . ")";
} else {
$where = '';
}
return array($join, $where, $params);
}
+3 -1
View File
@@ -61,6 +61,8 @@ $PAGE->set_heading($badge->name);
$PAGE->set_title($badge->name);
$PAGE->navbar->add($badge->name);
require_capability('moodle/badges:viewbadges', $context);
echo $OUTPUT->header();
echo $OUTPUT->heading(print_badge_image($badge, $context, 'small') . ' ' . $badge->name);
@@ -75,4 +77,4 @@ echo $output->print_badge_status_box($badge);
$output->print_badge_tabs($badgeid, $context, 'overview');
echo $output->print_badge_overview($badge, $context);
echo $OUTPUT->footer();
echo $OUTPUT->footer();
+27 -2
View File
@@ -324,6 +324,11 @@ class core_badges_badgeslib_testcase extends advanced_testcase {
$criteria_overall = award_criteria::build(array('criteriatype' => BADGE_CRITERIA_TYPE_ACTIVITY, 'badgeid' => $badge->id));
$criteria_overall->save(array('agg' => BADGE_CRITERIA_AGGREGATION_ANY, 'module_'.$this->module->cmid => $this->module->cmid));
// Assert the badge will not be issued to the user as is.
$badge = new badge($this->coursebadge);
$badge->review_all_criteria();
$this->assertFalse($badge->is_issued($this->user->id));
// Set completion for forum activity.
$c = new completion_info($this->course);
$activities = $c->get_activities();
@@ -359,6 +364,11 @@ class core_badges_badgeslib_testcase extends advanced_testcase {
$ccompletion = new completion_completion(array('course' => $this->course->id, 'userid' => $this->user->id));
// Assert the badge will not be issued to the user as is.
$badge = new badge($this->coursebadge);
$badge->review_all_criteria();
$this->assertFalse($badge->is_issued($this->user->id));
// Mark course as complete.
$sink = $this->redirectEmails();
$ccompletion->mark_complete();
@@ -374,18 +384,33 @@ class core_badges_badgeslib_testcase extends advanced_testcase {
* Test badges observer when user_updated event is fired.
*/
public function test_badges_observer_profile_criteria_review() {
global $CFG, $DB;
require_once($CFG->dirroot.'/user/profile/lib.php');
// Add a custom field of textarea type.
$customprofileid = $DB->insert_record('user_info_field', array(
'shortname' => 'newfield', 'name' => 'Description of new field', 'categoryid' => 1,
'datatype' => 'textarea'));
$this->preventResetByRollback(); // Messaging is not compatible with transactions.
$badge = new badge($this->coursebadge);
$this->assertFalse($badge->is_issued($this->user->id));
$criteria_overall = award_criteria::build(array('criteriatype' => BADGE_CRITERIA_TYPE_OVERALL, 'badgeid' => $badge->id));
$criteria_overall->save(array('agg' => BADGE_CRITERIA_AGGREGATION_ANY));
$criteria_overall1 = award_criteria::build(array('criteriatype' => BADGE_CRITERIA_TYPE_PROFILE, 'badgeid' => $badge->id));
$criteria_overall1->save(array('agg' => BADGE_CRITERIA_AGGREGATION_ALL, 'field_address' => 'address', 'field_aim' => 'aim'));
$criteria_overall1->save(array('agg' => BADGE_CRITERIA_AGGREGATION_ALL, 'field_address' => 'address', 'field_aim' => 'aim',
'field_' . $customprofileid => $customprofileid));
// Assert the badge will not be issued to the user as is.
$badge = new badge($this->coursebadge);
$badge->review_all_criteria();
$this->assertFalse($badge->is_issued($this->user->id));
// Set the required fields and make sure the badge got issued.
$this->user->address = 'Test address';
$this->user->aim = '999999999';
$sink = $this->redirectEmails();
profile_save_data((object)array('id' => $this->user->id, 'profile_field_newfield' => 'X'));
user_update_user($this->user, false);
$this->assertCount(1, $sink->get_messages());
$sink->close();
+3 -1
View File
@@ -75,6 +75,8 @@ if ($type == BADGE_TYPE_SITE) {
$PAGE->set_heading($title);
}
require_capability('moodle/badges:viewbadges', $PAGE->context);
$PAGE->set_title($title);
$output = $PAGE->get_renderer('core', 'badges');
@@ -103,4 +105,4 @@ if ($totalcount) {
echo $output->notification(get_string('nobadges', 'badges'));
}
echo $output->footer();
echo $output->footer();
+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;
}
function preferred_width() {
@@ -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"
@@ -16,6 +16,7 @@ Feature: Course summary block used on the frontpage
Scenario: Guest can view site summary
When I am on 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
@@ -23,6 +24,7 @@ Feature: Course summary block used on the frontpage
And I am on 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"
@@ -30,4 +32,5 @@ Feature: Course summary block used on the frontpage
When I log in as "admin"
And I am on 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"
+17
View File
@@ -104,6 +104,23 @@ class block_html extends block_base {
return true;
}
/**
* Copy any block-specific data when copying to a new block instance.
* @param int $fromid the id number of the block instance to copy from
* @return boolean
*/
public function instance_copy($fromid) {
$fromcontext = context_block::instance($fromid);
$fs = get_file_storage();
// This extra check if file area is empty adds one query if it is not empty but saves several if it is.
if (!$fs->is_area_empty($fromcontext->id, 'block_html', 'content', 0, false)) {
$draftitemid = 0;
file_prepare_draft_area($draftitemid, $fromcontext->id, 'block_html', 'content', 0, array('subdirs' => true));
file_save_draft_area_files($draftitemid, $this->context->id, 'block_html', 'content', 0, array('subdirs' => true));
}
return true;
}
function content_is_trusted() {
global $SCRIPT;
+1 -1
View File
@@ -133,7 +133,7 @@ class block_myprofile extends block_base {
if(!empty($this->config->display_phone2) && !empty($USER->phone2)) {
$this->content->text .= '<div class="myprofileitem phone2">';
$this->content->text .= get_string('phone').': ' . s($USER->phone2);
$this->content->text .= get_string('phone2').': ' . s($USER->phone2);
$this->content->text .= '</div>';
}
+2 -2
View File
@@ -34,8 +34,8 @@ $string['display_skype'] = 'Display Skype';
$string['display_yahoo'] = 'Display Yahoo';
$string['display_aim'] = 'Display AIM';
$string['display_msn'] = 'Display MSN';
$string['display_phone1'] = 'Display phone number 1';
$string['display_phone2'] = 'Display phone number 2';
$string['display_phone1'] = 'Display phone';
$string['display_phone2'] = 'Display mobile phone';
$string['display_institution'] = 'Display institution';
$string['display_address'] = 'Display address';
$string['display_firstaccess'] = 'Display first access';
@@ -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';
+4
View File
@@ -26,6 +26,7 @@
require_once(dirname(dirname(__FILE__)).'/config.php');
require_once('lib.php');
require_once('locallib.php');
require_once($CFG->dirroot .'/comment/lib.php');
$action = required_param('action', PARAM_ALPHA);
$id = optional_param('entryid', 0, PARAM_INT);
@@ -110,6 +111,9 @@ $output = $PAGE->get_renderer('blog');
$strblogs = get_string('blogs', 'blog');
if ($action === 'delete') {
// Init comment JS strings.
comment::init();
if (empty($entry->id)) {
print_error('wrongentryid', 'blog');
}
+5 -1
View File
@@ -189,10 +189,14 @@ foreach($events as $event) {
$ev->add_property('class', 'PUBLIC'); // PUBLIC / PRIVATE / CONFIDENTIAL
$ev->add_property('last-modified', Bennu::timestamp_to_datetime($event->timemodified));
$ev->add_property('dtstamp', Bennu::timestamp_to_datetime()); // now
$ev->add_property('dtstart', Bennu::timestamp_to_datetime($event->timestart)); // when event starts
if ($event->timeduration > 0) {
//dtend is better than duration, because it works in Microsoft Outlook and works better in Korganizer
$ev->add_property('dtstart', Bennu::timestamp_to_datetime($event->timestart)); // when event starts.
$ev->add_property('dtend', Bennu::timestamp_to_datetime($event->timestart + $event->timeduration));
} else {
// When no duration is present, ie an all day event, VALUE should be date instead of time and dtend = dtstart + 1 day.
$ev->add_property('dtstart', Bennu::timestamp_to_date($event->timestart), array('value' => 'DATE')); // All day event.
$ev->add_property('dtend', Bennu::timestamp_to_date($event->timestart + DAYSECS), array('value' => 'DATE')); // All day event.
}
if ($event->courseid != 0) {
$coursecontext = context_course::instance($event->courseid);
+10 -9
View File
@@ -173,6 +173,8 @@ class core_calendar_external extends external_api {
// Let us findout courses that we can return events from.
if (!$hassystemcap) {
$courses = enrol_get_my_courses('id');
$courses = array_keys($courses);
foreach ($params['events']['courseids'] as $id) {
try {
$context = context_course::instance($id);
@@ -185,7 +187,6 @@ class core_calendar_external extends external_api {
'warningcode' => 'nopermissions',
'message' => 'No access rights in course context '.$e->getMessage().$e->getTraceAsString()
);
continue;
}
}
} else {
@@ -221,21 +222,21 @@ class core_calendar_external extends external_api {
$funcparam['courses'][] = $SITE->id;
}
// Event list does not check visibility and permissions, we'll check that later.
$eventlist = calendar_get_events($params['options']['timestart'], $params['options']['timeend'], $funcparam['users'], $funcparam['groups'],
$funcparam['courses'], true, $params['options']['ignorehidden']);
// WS expects arrays.
$events = array();
foreach ($eventlist as $id => $event) {
$events[$id] = (array) $event;
}
// We need to get events asked for eventids.
$eventsbyid = calendar_get_events_by_id($params['events']['eventids']);
foreach ($eventsbyid as $eventid => $eventobj) {
if ($eventsbyid = calendar_get_events_by_id($params['events']['eventids'])) {
$eventlist += $eventsbyid;
}
foreach ($eventlist as $eventid => $eventobj) {
$event = (array) $eventobj;
if (isset($events[$eventid])) {
continue;
}
if ($hassystemcap) {
// User can see everything, no further check is needed.
$events[$eventid] = $event;
+31
View File
@@ -363,6 +363,37 @@ class core_calendar_externallib_testcase extends externallib_advanced_testcase {
$events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events);
$this->assertEquals(1, count($events['events'])); // site.
$this->assertEquals(0, count($events['warnings']));
// Try getting a course event by its id.
$paramevents = array ('eventids' => array($courseevent->id));
$events = core_calendar_external::get_calendar_events($paramevents, $options);
$events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events);
$this->assertEquals(1, count($events['events']));
$this->assertEquals(0, count($events['warnings']));
// Now, create an activity event.
$this->setAdminUser();
$nexttime = time() + DAYSECS;
$assign = $this->getDataGenerator()->create_module('assign', array('course' => $course->id, 'duedate' => $nexttime));
$this->setUser($user);
$paramevents = array ('courseids' => array($course->id));
$options = array ('siteevents' => true, 'userevents' => true, 'timeend' => time() + WEEKSECS);
$events = core_calendar_external::get_calendar_events($paramevents, $options);
$events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events);
$this->assertCount(5, $events['events']);
// Hide the assignment.
set_coursemodule_visible($assign->cmid, 0);
// Empty all the caches that may be affected by this change.
accesslib_clear_all_caches_for_unit_testing();
course_modinfo::clear_instance_cache();
$events = core_calendar_external::get_calendar_events($paramevents, $options);
$events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events);
// Expect one less.
$this->assertCount(4, $events['events']);
}
/**
+2 -7
View File
@@ -78,13 +78,8 @@ class core_calendar_lib_testcase extends advanced_testcase {
global $CFG;
require_once($CFG->dirroot . '/lib/cronlib.php');
// Moodle ICal URL (moodle.org events).
$presetwhat = 'all';
$presettime = 'recentupcoming';
$userid = 1;
$authtoken = 'a8bcfee2fb868a05357f650bd65dc0699b026524';
$subscriptionurl = 'https://moodle.org/calendar/export_execute.php'
. '?preset_what='.$presetwhat.'&preset_time='.$presettime.'&userid='.$userid.'&authtoken='.$authtoken;
// ICal URL from external test repo.
$subscriptionurl = $this->getExternalTestFileUrl('/ical.ics');
$subscription = new stdClass();
$subscription->eventtype = 'site';
+19 -10
View File
@@ -256,15 +256,17 @@ function cohort_get_available_cohorts($currentcontext, $withmembers = 0, $offset
$groupbysql = '';
$havingsql = '';
if ($withmembers) {
$groupbysql = " GROUP BY $fieldssql";
$fieldssql .= ', s.memberscnt';
$subfields = "c.id, COUNT(DISTINCT cm.userid) AS memberscnt";
$groupbysql = " GROUP BY c.id";
$fromsql = " LEFT JOIN {cohort_members} cm ON cm.cohortid = c.id ";
$fieldssql .= ', COUNT(DISTINCT cm.userid) AS memberscnt';
if (in_array($withmembers,
array(COHORT_COUNT_ENROLLED_MEMBERS, COHORT_WITH_ENROLLED_MEMBERS_ONLY, COHORT_WITH_NOTENROLLED_MEMBERS_ONLY))) {
list($esql, $params2) = get_enrolled_sql($currentcontext);
$fromsql .= " LEFT JOIN ($esql) u ON u.id = cm.userid ";
$params = array_merge($params2, $params);
$fieldssql .= ', COUNT(DISTINCT u.id) AS enrolledcnt';
$fieldssql .= ', s.enrolledcnt';
$subfields .= ', COUNT(DISTINCT u.id) AS enrolledcnt';
}
if ($withmembers == COHORT_WITH_MEMBERS_ONLY) {
$havingsql = " HAVING COUNT(DISTINCT cm.userid) > 0";
@@ -280,13 +282,20 @@ function cohort_get_available_cohorts($currentcontext, $withmembers = 0, $offset
$params = array_merge($params, $searchparams);
}
$sql = "SELECT $fieldssql
FROM {cohort} c
$fromsql
WHERE $wheresql
$groupbysql
$havingsql
ORDER BY c.name, c.idnumber";
if ($withmembers) {
$sql = "SELECT " . str_replace('c.', 'cohort.', $fieldssql) . "
FROM {cohort} cohort
JOIN (SELECT $subfields
FROM {cohort} c $fromsql
WHERE $wheresql $groupbysql $havingsql
) s ON cohort.id = s.id
ORDER BY cohort.name, cohort.idnumber";
} else {
$sql = "SELECT $fieldssql
FROM {cohort} c $fromsql
WHERE $wheresql
ORDER BY c.name, c.idnumber";
}
return $DB->get_records_sql($sql, $params, $offset, $limit);
}
@@ -232,6 +232,20 @@ abstract class completion_criteria extends data_object {
*/
abstract public function get_details($completion);
/**
* Return pix_icon for display in reports.
*
* @param string $alt The alt text to use for the icon
* @param array $attributes html attributes
* @return pix_icon
*/
public function get_icon($alt, array $attributes = null) {
global $COMPLETION_CRITERIA_TYPES;
$criteriatype = $COMPLETION_CRITERIA_TYPES[$this->criteriatype];
return new pix_icon('i/'.$criteriatype, $alt, 'moodle', $attributes);
}
/**
* Return criteria status text for display in reports
*
@@ -287,4 +287,15 @@ class completion_criteria_activity extends completion_criteria {
return $details;
}
/**
* Return pix_icon for display in reports.
*
* @param string $alt The alt text to use for the icon
* @param array $attributes html attributes
* @return pix_icon
*/
public function get_icon($alt, array $attributes = null) {
return new pix_icon('icon', $alt, 'mod_'.$this->module, $attributes);
}
}
@@ -202,4 +202,15 @@ class completion_criteria_date extends completion_criteria {
return $details;
}
/**
* Return pix_icon for display in reports.
*
* @param string $alt The alt text to use for the icon
* @param array $attributes html attributes
* @return pix_icon
*/
public function get_icon($alt, array $attributes = null) {
return new pix_icon('i/calendar', $alt, 'moodle', $attributes);
}
}
@@ -248,4 +248,15 @@ class completion_criteria_grade extends completion_criteria {
return $details;
}
/**
* Return pix_icon for display in reports.
*
* @param string $alt The alt text to use for the icon
* @param array $attributes html attributes
* @return pix_icon
*/
public function get_icon($alt, array $attributes = null) {
return new pix_icon('i/grades', $alt, 'moodle', $attributes);
}
}
@@ -149,4 +149,15 @@ class completion_criteria_self extends completion_criteria {
return $details;
}
/**
* Return pix_icon for display in reports.
*
* @param string $alt The alt text to use for the icon
* @param array $attributes html attributes
* @return pix_icon
*/
public function get_icon($alt, array $attributes = null) {
return new pix_icon('i/completion_self', $alt, 'moodle', $attributes);
}
}
@@ -131,4 +131,15 @@ class completion_criteria_unenrol extends completion_criteria {
$details['status'] = '';
return $details;
}
/**
* Return pix_icon for display in reports.
*
* @param string $alt The alt text to use for the icon
* @param array $attributes html attributes
* @return pix_icon
*/
public function get_icon($alt, array $attributes = null) {
return new pix_icon('i/user', $alt, 'moodle', $attributes);
}
}
@@ -122,4 +122,40 @@ class behat_completion extends behat_base {
new Given('I press "'.get_string('savechanges').'"')
);
}
/**
* Checks if the activity with specified name is maked as complete.
*
* @Given /^the "(?P<activityname_string>(?:[^"]|\\")*)" "(?P<activitytype_string>(?:[^"]|\\")*)" activity with "(manual|auto)" completion should be marked as complete$/
* @return array
*/
public function activity_marked_as_complete($activityname, $activitytype, $completiontype) {
if ($completiontype == "manual") {
$imgalttext = get_string("completion-alt-manual-y", 'core_completion', $activityname);
} else {
$imgalttext = get_string("completion-alt-auto-y", 'core_completion', $activityname);
}
$csselementforactivitytype = "li.modtype_".strtolower($activitytype);
return new Given('"//img[contains(@alt, \''.$imgalttext.'\')]" "xpath_element" ' .
'should exist in the "'.$csselementforactivitytype.'" "css_element"');
}
/**
* Checks if the activity with specified name is maked as complete.
*
* @Given /^the "(?P<activityname_string>(?:[^"]|\\")*)" "(?P<activitytype_string>(?:[^"]|\\")*)" activity with "(manual|auto)" completion should be marked as not complete$/
* @return array
*/
public function activity_marked_as_not_complete($activityname, $activitytype, $completiontype) {
if ($completiontype == "manual") {
$imgalttext = get_string("completion-alt-manual-n", 'core_completion', $activityname);
} else {
$imgalttext = get_string("completion-alt-auto-n", 'core_completion', $activityname);
}
$csselementforactivitytype = "li.modtype_".strtolower($activitytype);
return new Given('"//img[contains(@alt, \''.$imgalttext.'\')]" "xpath_element" ' .
'should exist in the "'.$csselementforactivitytype.'" "css_element"');
}
}
@@ -71,10 +71,10 @@ Feature: Restrict sections availability through completion or grade conditions
And I expand all fieldsets
And I click on "Add restriction..." "button"
And I click on "Grade" "button" in the "Add restriction..." "dialogue"
And I click on "min" "checkbox"
And I set the following fields to these values:
| id | Grade assignment |
| minval | 20 |
| id | Grade assignment |
| min | 1 |
| minval | 20 |
And I press "Save changes"
And I log out
When I log in as "student1"
+1 -1
View File
@@ -1130,7 +1130,7 @@ class core_course_management_renderer extends plugin_renderer_base {
$totalpages = ceil($totalcourses / $perpage);
if ($showtotals) {
if ($totalpages == 0) {
$str = get_string('nocoursesfound', 'moodle', $search);
$str = get_string('nocoursesfound', 'moodle', s($search));
} else if ($totalpages == 1) {
$str = get_string('showingacourses', 'moodle', $totalcourses);
} else {
+3 -2
View File
@@ -76,11 +76,12 @@ $strdeletecoursecheck = get_string("deletecoursecheck");
$message = "{$strdeletecoursecheck}<br /><br />{$coursefullname} ({$courseshortname})";
$continueurl = new moodle_url('/course/delete.php', array('id' => $course->id, 'delete' => md5($course->timemodified)));
$continuebutton = new single_button($continueurl, get_string('delete'), 'post');
$PAGE->navbar->add($strdeletecheck);
$PAGE->set_title("$SITE->shortname: $strdeletecheck");
$PAGE->set_heading($SITE->fullname);
echo $OUTPUT->header();
echo $OUTPUT->confirm($message, $continueurl, $categoryurl);
echo $OUTPUT->confirm($message, $continuebutton, $categoryurl);
echo $OUTPUT->footer();
exit;
exit;
+1 -1
View File
@@ -212,7 +212,7 @@ if (!empty($activities)) {
echo $OUTPUT->spacer(array('height'=>30, 'br'=>true)); // should be done with CSS instead
}
echo $OUTPUT->box_start();
if (!empty($activity->name)) {
if (strval($activity->name) !== '') {
echo html_writer::tag('h2', $activity->name);
}
$inbox = true;
+1 -1
View File
@@ -173,7 +173,7 @@ switch($requestmethod) {
$module->name = clean_param($title, PARAM_CLEANHTML);
}
if (!empty($module->name)) {
if (strval($module->name) !== '') {
$DB->update_record($cm->modname, $module);
$cm->name = $module->name;
\core\event\course_module_updated::create_from_cm($cm, $modcontext)->trigger();
@@ -55,7 +55,7 @@ Feature: Test we can both create and delete a course.
# Redirect
And I should see "Delete TCCAC"
And I should see "Test course: create a course (TCCAC)"
And I press "Continue"
And I press "Delete"
# Redirect
And I should see "Deleting TCCAC"
And I should see "TCCAC has been completely deleted"
@@ -94,7 +94,7 @@ Feature: Test we can both create and delete a course.
# Redirect
And I should see "Delete TCCAC"
And I should see "Test course: create a course (TCCAC)"
And I press "Continue"
And I press "Delete"
# Redirect
And I should see "Deleting TCCAC"
And I should see "TCCAC has been completely deleted"
@@ -689,6 +689,8 @@ Y.extend(RESOURCETOOLBOX, TOOLBOX, {
newtitlestr = M.util.get_string('clicktochangeinbrackets', 'moodle', M.util.get_string(newtitle, 'moodle'));
// Change the UI
var oldAction = button.getData('action');
button.replaceClass('editing_' + oldAction, 'editing_' + newtitle);
buttonimg.setAttrs({
'src': iconsrc
});
File diff suppressed because one or more lines are too long
@@ -689,6 +689,8 @@ Y.extend(RESOURCETOOLBOX, TOOLBOX, {
newtitlestr = M.util.get_string('clicktochangeinbrackets', 'moodle', M.util.get_string(newtitle, 'moodle'));
// Change the UI
var oldAction = button.getData('action');
button.replaceClass('editing_' + oldAction, 'editing_' + newtitle);
buttonimg.setAttrs({
'src': iconsrc
});
+2
View File
@@ -485,6 +485,8 @@ Y.extend(RESOURCETOOLBOX, TOOLBOX, {
newtitlestr = M.util.get_string('clicktochangeinbrackets', 'moodle', M.util.get_string(newtitle, 'moodle'));
// Change the UI
var oldAction = button.getData('action');
button.replaceClass('editing_' + oldAction, 'editing_' + newtitle);
buttonimg.setAttrs({
'src': iconsrc
});
+38 -9
View File
@@ -363,7 +363,10 @@ class core_enrol_external extends external_api {
)
), 'Option names:
* withcapability (string) return only users with this capability. This option requires \'moodle/role:review\' on the course context.
* groupid (integer) return only users in this group id. This option requires \'moodle/site:accessallgroups\' on the course context.
* groupid (integer) return only users in this group id. If the course has groups enabled and this param
isn\'t defined, returns all the viewable users.
This option requires \'moodle/site:accessallgroups\' on the course context if the
user doesn\'t belong to the group.
* onlyactive (integer) return only users with active enrolments and matching time restrictions. This option requires \'moodle/course:enrolreview\' on the course context.
* userfields (\'string, string, ...\') return only the values of these user fields.
* limitfrom (integer) sql limit from.
@@ -451,7 +454,7 @@ class core_enrol_external extends external_api {
require_capability('moodle/role:review', $coursecontext);
}
// need accessallgroups capability if you want to overwrite this option
if (!empty($groupid) && groups_is_member($groupid)) {
if (!empty($groupid) && !groups_is_member($groupid)) {
require_capability('moodle/site:accessallgroups', $coursecontext);
}
// to overwrite this option, you need course:enrolereview permission
@@ -463,10 +466,29 @@ class core_enrol_external extends external_api {
$ctxselect = ', ' . context_helper::get_preload_record_columns_sql('ctx');
$ctxjoin = "LEFT JOIN {context} ctx ON (ctx.instanceid = u.id AND ctx.contextlevel = :contextlevel)";
$enrolledparams['contextlevel'] = CONTEXT_USER;
$sql = "SELECT u.* $ctxselect
FROM {user} u $ctxjoin
WHERE u.id IN ($enrolledsql)
ORDER BY u.id ASC";
$groupjoin = '';
if (empty($groupid) && groups_get_course_groupmode($course) == SEPARATEGROUPS &&
!has_capability('moodle/site:accessallgroups', $coursecontext)) {
// Filter by groups the user can view.
$usergroups = groups_get_user_groups($course->id);
if (!empty($usergroups['0'])) {
list($groupsql, $groupparams) = $DB->get_in_or_equal($usergroups['0'], SQL_PARAMS_NAMED);
$groupjoin = "JOIN {groups_members} gm ON (u.id = gm.userid AND gm.groupid $groupsql)";
$enrolledparams = array_merge($enrolledparams, $groupparams);
} else {
// User doesn't belong to any group, so he can't see any user. Return an empty array.
return array();
}
}
$sql = "SELECT us.*
FROM {user} us
JOIN (
SELECT DISTINCT u.id $ctxselect
FROM {user} u $ctxjoin $groupjoin
WHERE u.id IN ($enrolledsql)
) q ON q.id = us.id
ORDER BY us.id ASC";
$enrolledusers = $DB->get_recordset_sql($sql, $enrolledparams, $limitfrom, $limitnumber);
$users = array();
foreach ($enrolledusers as $user) {
@@ -581,14 +603,21 @@ class core_enrol_external extends external_api {
*
* @param int $courseid
* @return array of course enrolment methods
* @throws moodle_exception
*/
public static function get_course_enrolment_methods($courseid) {
global $DB;
$params = self::validate_parameters(self::get_course_enrolment_methods_parameters(), array('courseid' => $courseid));
$coursecontext = context_course::instance($params['courseid']);
$categorycontext = $coursecontext->get_parent_context();
self::validate_context($categorycontext);
// Note that we can't use validate_context because the user is not enrolled in the course.
require_login(null, false, null, false, true);
$course = $DB->get_record('course', array('id' => $params['courseid']), '*', MUST_EXIST);
$context = context_course::instance($course->id);
if (!$course->visible and !has_capability('moodle/course:viewhiddencourses', $context)) {
throw new moodle_exception('coursehidden');
}
$result = array();
$enrolinstances = enrol_get_instances($params['courseid'], true);
+1 -1
View File
@@ -446,7 +446,7 @@ class enrol_flatfile_plugin extends enrol_plugin {
$notify = false;
if ($ue = $DB->get_record('user_enrolments', array('enrolid'=>$instance->id, 'userid'=>$user->id))) {
// Update only.
$this->update_user_enrol($instance, $user->id, ENROL_USER_ACTIVE, $roleid, $timestart, $timeend);
$this->update_user_enrol($instance, $user->id, ENROL_USER_ACTIVE, $timestart, $timeend);
if (!$DB->record_exists('role_assignments', array('contextid'=>$context->id, 'roleid'=>$roleid, 'userid'=>$user->id, 'component'=>'enrol_flatfile', 'itemid'=>$instance->id))) {
role_assign($roleid, $user->id, $context->id, 'enrol_flatfile', $instance->id);
}
+7 -6
View File
@@ -1091,13 +1091,14 @@ class course_enrolment_manager {
*/
private function prepare_user_for_display($user, $extrafields, $now) {
$details = array(
'userid' => $user->id,
'courseid' => $this->get_course()->id,
'picture' => new user_picture($user),
'firstname' => fullname($user, has_capability('moodle/site:viewfullnames', $this->get_context())),
'lastseen' => get_string('never'),
'lastcourseaccess' => get_string('never'),
'userid' => $user->id,
'courseid' => $this->get_course()->id,
'picture' => new user_picture($user),
'userfullnamedisplay' => fullname($user, has_capability('moodle/site:viewfullnames', $this->get_context())),
'lastseen' => get_string('never'),
'lastcourseaccess' => get_string('never'),
);
foreach ($extrafields as $field) {
$details[$field] = $user->{$field};
}
+1
View File
@@ -50,6 +50,7 @@ navigation_node::override_active_url(new moodle_url('/enrol/otherusers.php', arr
$userdetails = array (
'picture' => false,
'userfullnamedisplay' => false,
'firstname' => get_string('firstname'),
'lastname' => get_string('lastname'),
);
+41
View File
@@ -0,0 +1,41 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Empty enrol_self form.
*
* Useful to mimic valid enrol instances UI when the enrolment instance is not available.
*
* @package enrol_self
* @copyright 2015 David Monllaó
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
require_once($CFG->libdir.'/formslib.php');
class enrol_self_empty_form extends moodleform {
/**
* Form definition.
* @return void
*/
public function definition() {
$this->_form->addElement('header', 'selfheader', $this->_customdata->header);
$this->_form->addElement('static', 'info', '', $this->_customdata->info);
}
}
+9 -3
View File
@@ -52,6 +52,7 @@ class enrol_self_external extends external_api {
*
* @param int $instanceid instance id of self enrolment plugin.
* @return array instance information.
* @throws moodle_exception
*/
public static function get_instance_info($instanceid) {
global $DB, $CFG;
@@ -66,10 +67,15 @@ class enrol_self_external extends external_api {
throw new moodle_exception('invaliddata', 'error');
}
// Note that we can't use validate_context because the user is not enrolled in the course.
require_login(null, false, null, false, true);
$enrolinstance = $DB->get_record('enrol', array('id' => $params['instanceid']), '*', MUST_EXIST);
$coursecontext = context_course::instance($enrolinstance->courseid);
$categorycontext = $coursecontext->get_parent_context();
self::validate_context($categorycontext);
$course = $DB->get_record('course', array('id' => $enrolinstance->courseid), '*', MUST_EXIST);
$context = context_course::instance($course->id);
if (!$course->visible and !has_capability('moodle/course:viewhiddencourses', $context)) {
throw new moodle_exception('coursehidden');
}
$instanceinfo = (array) $enrolplugin->get_enrol_info($enrolinstance);
if (isset($instanceinfo['requiredparam']->enrolpassword)) {
+2 -2
View File
@@ -95,9 +95,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}"?';
+16 -7
View File
@@ -234,8 +234,8 @@ class enrol_self_plugin extends enrol_plugin {
$enrolstatus = $this->can_self_enrol($instance);
// Don't show enrolment instance form, if user can't enrol using it.
if (true === $enrolstatus) {
// This user can self enrol using this instance.
$form = new enrol_self_enrol_form(NULL, $instance);
$instanceid = optional_param('instance', 0, PARAM_INT);
if ($instance->id == $instanceid) {
@@ -243,14 +243,23 @@ class enrol_self_plugin extends enrol_plugin {
$this->enrol_self($instance, $data);
}
}
ob_start();
$form->display();
$output = ob_get_clean();
return $OUTPUT->box($output);
} else {
return $OUTPUT->box($enrolstatus);
// This user can not self enrol using this instance. Using an empty form to keep
// the UI consistent with other enrolment plugins that returns a form.
$data = new stdClass();
$data->header = $this->get_instance_name($instance);
$data->info = $enrolstatus;
// The can_self_enrol call returns a button to the login page if the user is a
// guest, setting the login url to the form if that is the case.
$url = isguestuser() ? get_login_url() : null;
$form = new enrol_self_empty_form($url, $data);
}
ob_start();
$form->display();
$output = ob_get_clean();
return $OUTPUT->box($output);
}
/**
@@ -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"
+13 -1
View File
@@ -47,7 +47,9 @@ class enrol_self_external_testcase extends externallib_advanced_testcase {
$studentrole = $DB->get_record('role', array('shortname'=>'student'));
$this->assertNotEmpty($studentrole);
$course = self::getDataGenerator()->create_course();
$coursedata = new stdClass();
$coursedata->visible = 0;
$course = self::getDataGenerator()->create_course($coursedata);
// Add enrolment methods for course.
$instanceid1 = $selfplugin->add_instance($course, array('status' => ENROL_INSTANCE_ENABLED,
@@ -68,6 +70,7 @@ class enrol_self_external_testcase extends externallib_advanced_testcase {
$enrolmentmethods = $DB->get_records('enrol', array('courseid' => $course->id, 'status' => ENROL_INSTANCE_ENABLED));
$this->assertCount(3, $enrolmentmethods);
$this->setAdminUser();
$instanceinfo1 = enrol_self_external::get_instance_info($instanceid1);
$instanceinfo1 = external_api::clean_returnvalue(enrol_self_external::get_instance_info_returns(), $instanceinfo1);
@@ -95,5 +98,14 @@ class enrol_self_external_testcase extends externallib_advanced_testcase {
$this->assertEquals('Test instance 3', $instanceinfo3['name']);
$this->assertTrue($instanceinfo3['status']);
$this->assertEquals(get_string('password', 'enrol_self'), $instanceinfo3['enrolpassword']);
// Try to retrieve information using a normal user for a hidden course.
$user = self::getDataGenerator()->create_user();
$this->setUser($user);
try {
enrol_self_external::get_instance_info($instanceid3);
} catch (moodle_exception $e) {
$this->assertEquals('coursehidden', $e->errorcode);
}
}
}
+504 -130
View File
@@ -33,59 +33,325 @@ require_once($CFG->dirroot . '/enrol/externallib.php');
class core_enrol_externallib_testcase extends externallib_advanced_testcase {
/**
* Test get_enrolled_users
* dataProvider for test_get_enrolled_users_visibility().
*/
public function test_get_enrolled_users() {
public function get_enrolled_users_visibility_provider() {
return array(
'Course without groups, default behavior (not filtering by cap, group, active)' =>
array(
'settings' => array(
'coursegroupmode' => NOGROUPS,
'withcapability' => null,
'groupid' => null,
'onlyactive' => false,
'allowedcaps' => array(),
),
'results' => array( // Everybody can view everybody.
'user0' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
'user1' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
'user2' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
'user31' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
'userall' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
),
),
'Course with visible groups, default behavior (not filtering by cap, group, active)' =>
array(
'settings' => array(
'coursegroupmode' => VISIBLEGROUPS,
'withcapability' => null,
'groupid' => null,
'onlyactive' => false,
'allowedcaps' => array(),
),
'results' => array( // Everybody can view everybody.
'user0' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
'user1' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
'user2' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
'user31' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
'userall' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
),
),
'Course with separate groups, default behavior (not filtering by cap, group, active)' =>
array(
'settings' => array(
'coursegroupmode' => SEPARATEGROUPS,
'withcapability' => null,
'groupid' => null,
'onlyactive' => false,
'allowedcaps' => array(),
),
'results' => array( // Only users from own groups are visible.
'user0' => array('canview' => array()), // Poor guy, cannot see anybody, himself included.
'user1' => array('canview' => array('user1', 'userall')),
'user2' => array('canview' => array('user2', 'user2su', 'userall')),
'user31' => array('canview' => array('user31', 'user32', 'userall')),
'userall' => array('canview' => array('user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
),
),
'Course with separate groups, default behavior (not filtering but having moodle/site:accessallgroups)' =>
array(
'settings' => array(
'coursegroupmode' => VISIBLEGROUPS,
'withcapability' => null,
'groupid' => null,
'onlyactive' => false,
'allowedcaps' => array('moodle/site:accessallgroups'),
),
'results' => array( // Everybody can view everybody.
'user0' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
'user1' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
'user2' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
'user31' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
'userall' => array('canview' => array('user0', 'user1', 'user2', 'user2su', 'user31', 'user32', 'userall')),
),
),
'Course with separate groups, filtering onlyactive (missing moodle/course:enrolreview)' =>
array(
'settings' => array(
'coursegroupmode' => SEPARATEGROUPS,
'withcapability' => null,
'groupid' => null,
'onlyactive' => true,
'allowedcaps' => array(),
),
'results' => array( // returns exception, cannot view anybody without the cap.
'user2' => array('exception' => array(
'type' => 'required_capability_exception',
'message' => 'Review course enrolments')),
'userall' => array('exception' => array(
'type' => 'required_capability_exception',
'message' => 'Review course enrolments')),
),
),
'Course with separate groups, filtering onlyactive (having moodle/course:enrolreview)' =>
array(
'settings' => array(
'coursegroupmode' => SEPARATEGROUPS,
'withcapability' => null,
'groupid' => null,
'onlyactive' => true,
'allowedcaps' => array('moodle/course:enrolreview'),
),
'results' => array( // Suspended are not returned.
'user2' => array('canview' => array('user2', 'userall')),
'user31' => array('canview' => array('user31', 'user32', 'userall')),
'userall' => array('canview' => array('user1', 'user2', 'user31', 'user32', 'userall')),
),
),
'Course with separate groups, filtering by groupid (not having moodle/site:accessallgroups)' =>
array(
'settings' => array(
'coursegroupmode' => SEPARATEGROUPS,
'withcapability' => null,
'groupid' => 'group2',
'onlyactive' => false,
'allowedcaps' => array(),
),
'results' => array( // Only group 2 members and only for members. Exception for non-members.
'user0' => array('exception' => array(
'type' => 'required_capability_exception',
'message' => 'Access all groups')),
'user1' => array('exception' => array(
'type' => 'required_capability_exception',
'message' => 'Access all groups')),
'user2' => array('canview' => array('user2', 'user2su', 'userall')),
'userall' => array('canview' => array('user2', 'user2su', 'userall')),
),
),
'Course with separate groups, filtering by groupid (having moodle/site:accessallgroups)' =>
array(
'settings' => array(
'coursegroupmode' => SEPARATEGROUPS,
'withcapability' => null,
'groupid' => 'group2',
'onlyactive' => false,
'allowedcaps' => array('moodle/site:accessallgroups'),
),
'results' => array( // All users with 'moodle/site:accessallgroups' can view group 2
'user0' => array('canview' => array('user2', 'user2su', 'userall')),
'user1' => array('canview' => array('user2', 'user2su', 'userall')),
'user2' => array('canview' => array('user2', 'user2su', 'userall')),
'userall' => array('canview' => array('user2', 'user2su', 'userall')),
),
),
'Course with separate groups, filtering by withcapability (not having moodle/role:review)' =>
array(
'settings' => array(
'coursegroupmode' => SEPARATEGROUPS,
'withcapability' => 'moodle/course:bulkmessaging',
'groupid' => null,
'onlyactive' => false,
'allowedcaps' => array(),
),
'results' => array( // No user has 'moodle/role:review' so exception.
'user0' => array('exception' => array(
'type' => 'required_capability_exception',
'message' => 'Review permissions for others')),
'user1' => array('exception' => array(
'type' => 'required_capability_exception',
'message' => 'Review permissions for others')),
'user2' => array('exception' => array(
'type' => 'required_capability_exception',
'message' => 'Review permissions for others')),
'userall' => array('exception' => array(
'type' => 'required_capability_exception',
'message' => 'Review permissions for others')),
),
),
'Course with separate groups, filtering by withcapability (having moodle/role:review)' =>
array(
'settings' => array(
'coursegroupmode' => SEPARATEGROUPS,
'withcapability' => 'moodle/course:bulkmessaging',
'groupid' => null,
'onlyactive' => false,
'allowedcaps' => array('moodle/role:review'),
),
'results' => array( // No user has withcapability, but all have 'moodle/role:review'. Empties.
'user0' => array('canview' => array()),
'user1' => array('canview' => array()),
'user2' => array('canview' => array()),
'userall' => array('canview' => array()),
),
),
'Course with separate groups, filtering by withcapability (having moodle/role:review)' =>
array(
'settings' => array(
'coursegroupmode' => SEPARATEGROUPS,
'withcapability' => 'moodle/course:bulkmessaging',
'groupid' => null,
'onlyactive' => false,
'allowedcaps' => array('moodle/role:review', 'moodle/course:bulkmessaging'),
),
'results' => array( // Users (previous) have withcapability, and all have 'moodle/role:review'.
'user0' => array('canview' => array()),
'user1' => array('canview' => array('user1')),
'user2' => array('canview' => array('user2')),
'userall' => array('canview' => array('user1', 'user2', 'userall')),
),
),
);
}
/**
* Verify get_enrolled_users() returned users are the expected in every situation.
*
* @dataProvider get_enrolled_users_visibility_provider
*/
public function test_get_enrolled_users_visibility($settings, $results) {
global $USER;
$this->resetAfterTest(true);
$this->resetAfterTest();
$course = self::getDataGenerator()->create_course();
$user1 = self::getDataGenerator()->create_user();
$user2 = self::getDataGenerator()->create_user();
$user3 = self::getDataGenerator()->create_user();
$this->setUser($user3);
// Create the course and the users.
$course = $this->getDataGenerator()->create_course(array('groupmode' => $settings['coursegroupmode']));
$coursecontext = context_course::instance($course->id);
$user0 = $this->getDataGenerator()->create_user(array('username' => 'user0')); // A user without group.
$user1 = $this->getDataGenerator()->create_user(array('username' => 'user1')); // User for group 1.
$user2 = $this->getDataGenerator()->create_user(array('username' => 'user2')); // Two users for group 2.
$user2su = $this->getDataGenerator()->create_user(array('username' => 'user2su')); // (one suspended).
$user31 = $this->getDataGenerator()->create_user(array('username' => 'user31')); // Two users for group 3.
$user32 = $this->getDataGenerator()->create_user(array('username' => 'user32')); // (both enabled).
$userall = $this->getDataGenerator()->create_user(array('username' => 'userall')); // A user in all groups.
// Set the required capabilities by the external function.
$context = context_course::instance($course->id);
$roleid = $this->assignUserCapability('moodle/course:viewparticipants', $context->id);
$this->assignUserCapability('moodle/user:viewdetails', $context->id, $roleid);
// Create utility array of created users, to produce better assertion messages.
$createdusers = array();
foreach (array($user0, $user1, $user2, $user2su, $user31, $user32, $userall) as $createduser) {
$createdusers[$createduser->id] = $createduser->username;
}
// Enrol the users in the course.
$this->getDataGenerator()->enrol_user($user1->id, $course->id, $roleid, 'manual');
$this->getDataGenerator()->enrol_user($user2->id, $course->id, $roleid, 'manual');
$this->getDataGenerator()->enrol_user($user3->id, $course->id, $roleid, 'manual');
$this->getDataGenerator()->enrol_user($user0->id, $course->id);
$this->getDataGenerator()->enrol_user($user1->id, $course->id);
$this->getDataGenerator()->enrol_user($user2->id, $course->id);
$this->getDataGenerator()->enrol_user($user2su->id, $course->id, null, 'manual', 0, 0, ENROL_USER_SUSPENDED);
$this->getDataGenerator()->enrol_user($user31->id, $course->id);
$this->getDataGenerator()->enrol_user($user32->id, $course->id);
$this->getDataGenerator()->enrol_user($userall->id, $course->id);
// Call the external function.
$enrolledusers = core_enrol_external::get_enrolled_users($course->id);
// Create 3 groups.
$group1 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
$group2 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
$group3 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
// We need to execute the return values cleaning process to simulate the web service server.
$enrolledusers = external_api::clean_returnvalue(core_enrol_external::get_enrolled_users_returns(), $enrolledusers);
// Add the users to the groups.
$this->getDataGenerator()->create_group_member(array('groupid' => $group1->id, 'userid' => $user1->id));
$this->getDataGenerator()->create_group_member(array('groupid' => $group2->id, 'userid' => $user2->id));
$this->getDataGenerator()->create_group_member(array('groupid' => $group2->id, 'userid' => $user2su->id));
$this->getDataGenerator()->create_group_member(array('groupid' => $group3->id, 'userid' => $user31->id));
$this->getDataGenerator()->create_group_member(array('groupid' => $group3->id, 'userid' => $user32->id));
$this->getDataGenerator()->create_group_member(array('groupid' => $group1->id, 'userid' => $userall->id));
$this->getDataGenerator()->create_group_member(array('groupid' => $group2->id, 'userid' => $userall->id));
$this->getDataGenerator()->create_group_member(array('groupid' => $group3->id, 'userid' => $userall->id));
// Check the result set.
$this->assertEquals(3, count($enrolledusers));
$this->assertArrayHasKey('email', $enrolledusers[0]);
// Create a role to add the allowedcaps. Users will have this role assigned.
$roleid = $this->getDataGenerator()->create_role();
// Allow the specified capabilities.
if (!empty($settings['allowedcaps'])) {
foreach ($settings['allowedcaps'] as $capability) {
assign_capability($capability, CAP_ALLOW, $roleid, $coursecontext);
}
}
// Call the function with some parameters set.
$enrolledusers = core_enrol_external::get_enrolled_users($course->id, array(
array('name' => 'limitfrom', 'value' => 2),
array('name' => 'limitnumber', 'value' => 1),
array('name' => 'userfields', 'value' => 'id')
));
// For each of the users, configure everything, perform the call, and assert results.
foreach ($results as $user => $expectations) {
// Convert canview expectations into a nice array of ids for easier handling.
$canview = array();
$exception = null;
// Analyse the expectations.
if (isset($expectations['canview'])) {
foreach ($expectations['canview'] as $canviewuser) {
$canview[] = $createdusers[${$canviewuser}->id];
}
} else if (isset($expectations['exception'])) {
$exception = $expectations['exception'];
$this->setExpectedException($exception['type'], $exception['message']);
} else {
// Failed, only canview and exception are supported.
$this->markTestIncomplete('Incomplete, only canview and exception are supported');
}
// Switch to the user and assign the role.
$this->setUser(${$user});
role_assign($roleid, $USER->id, $coursecontext);
// We need to execute the return values cleaning process to simulate the web service server.
$enrolledusers = external_api::clean_returnvalue(core_enrol_external::get_enrolled_users_returns(), $enrolledusers);
// Convert groupid to proper id.
$groupid = 0;
if (isset($settings['groupid'])) {
$groupid = ${$settings['groupid']}->id;
}
// Check the result set, we should only get the 3rd result, which is $user3.
$this->assertCount(1, $enrolledusers);
$this->assertEquals($user3->id, $enrolledusers[0]['id']);
$this->assertArrayHasKey('id', $enrolledusers[0]);
$this->assertArrayNotHasKey('email', $enrolledusers[0]);
// Call to the function.
$options = array(
array('name' => 'withcapability', 'value' => $settings['withcapability']),
array('name' => 'groupid', 'value' => $groupid),
array('name' => 'onlyactive', 'value' => $settings['onlyactive']),
array('name' => 'userfields', 'value' => 'id')
);
$enrolledusers = core_enrol_external::get_enrolled_users($course->id, $options);
// Call without required capability.
$this->unassignUserCapability('moodle/course:viewparticipants', $context->id, $roleid);
$this->setExpectedException('moodle_exception');
$categories = core_enrol_external::get_enrolled_users($course->id);
// We need to execute the return values cleaning process to simulate the web service server.
$enrolledusers = external_api::clean_returnvalue(core_enrol_external::get_enrolled_users_returns(), $enrolledusers);
// We are only interested in ids to check visibility.
$viewed = array();
// Verify the user canview the expected users.
foreach ($enrolledusers as $enrolleduser) {
$viewed[] = $createdusers[$enrolleduser['id']];
}
// Verify viewed matches canview expectation (using canonicalize to ignore ordering).
$this->assertEquals($canview, $viewed, "Problem checking visible users for '{$createdusers[$USER->id]}'", 0, 1, true);
}
}
/**
@@ -121,96 +387,6 @@ class core_enrol_externallib_testcase extends externallib_advanced_testcase {
$this->assertEquals(2, count($enrolledincourses));
}
/**
* Test get_enrolled_users_with_capability
*/
public function test_get_enrolled_users_with_capability () {
global $DB, $USER;
$this->resetAfterTest(true);
$user1 = $this->getDataGenerator()->create_user();
$coursedata['idnumber'] = 'idnumbercourse1';
$coursedata['fullname'] = 'Lightwork Course 1';
$coursedata['summary'] = 'Lightwork Course 1 description';
$coursedata['summaryformat'] = FORMAT_MOODLE;
$course1 = self::getDataGenerator()->create_course($coursedata);
// Create a manual enrolment record.
$manual_enrol_data['enrol'] = 'manual';
$manual_enrol_data['status'] = 0;
$manual_enrol_data['courseid'] = $course1->id;
$enrolid = $DB->insert_record('enrol', $manual_enrol_data);
// Create the users and give them capabilities in the course context.
$context = context_course::instance($course1->id);
$roleid = $this->assignUserCapability('moodle/course:viewparticipants', $context->id, 3);
// Create 2 students.
$student1 = self::getDataGenerator()->create_user();
$student2 = self::getDataGenerator()->create_user();
// Give the capability to student2.
assign_capability('moodle/course:viewparticipants', CAP_ALLOW, 3, $context->id);
role_assign(3, $student2->id, $context->id);
accesslib_clear_all_caches_for_unit_testing();
// Enrol both the user and the students in the course.
$user_enrolment_data['status'] = 0;
$user_enrolment_data['enrolid'] = $enrolid;
$user_enrolment_data['userid'] = $USER->id;
$DB->insert_record('user_enrolments', $user_enrolment_data);
$user_enrolment_data['status'] = 0;
$user_enrolment_data['enrolid'] = $enrolid;
$user_enrolment_data['userid'] = $student1->id;
$DB->insert_record('user_enrolments', $user_enrolment_data);
$user_enrolment_data['status'] = 0;
$user_enrolment_data['enrolid'] = $enrolid;
$user_enrolment_data['userid'] = $student2->id;
$DB->insert_record('user_enrolments', $user_enrolment_data);
$params = array("coursecapabilities" => array('courseid' => $course1->id,
'capabilities' => array('moodle/course:viewparticipants')));
$options = array();
$result = core_enrol_external::get_enrolled_users_with_capability($params, $options);
// We need to execute the return values cleaning process to simulate the web service server.
$result = external_api::clean_returnvalue(core_enrol_external::get_enrolled_users_with_capability_returns(), $result);
// Check an array containing the expected user for the course capability is returned.
$expecteduserlist = $result[0];
$this->assertEquals($course1->id, $expecteduserlist['courseid']);
$this->assertEquals('moodle/course:viewparticipants', $expecteduserlist['capability']);
$this->assertEquals(2, count($expecteduserlist['users']));
// Now doing the query again with options.
$params = array(
"coursecapabilities" => array(
'courseid' => $course1->id,
'capabilities' => array('moodle/course:viewparticipants')
)
);
$options = array(
array('name' => 'limitfrom', 'value' => 1),
array('name' => 'limitnumber', 'value' => 1),
array('name' => 'userfields', 'value' => 'id')
);
$result = core_enrol_external::get_enrolled_users_with_capability($params, $options);
// We need to execute the return values cleaning process to simulate the web service server.
$result = external_api::clean_returnvalue(core_enrol_external::get_enrolled_users_with_capability_returns(), $result);
// Check an array containing the expected user for the course capability is returned.
$expecteduserlist = $result[0]['users'];
$expecteduser = reset($expecteduserlist);
$this->assertEquals(1, count($expecteduserlist));
$this->assertEquals($student2->id, $expecteduser['id']);
}
/**
* Test get_course_enrolment_methods
*/
@@ -229,7 +405,9 @@ class core_enrol_externallib_testcase extends externallib_advanced_testcase {
$this->assertNotEmpty($studentrole);
$course1 = self::getDataGenerator()->create_course();
$course2 = self::getDataGenerator()->create_course();
$coursedata = new stdClass();
$coursedata->visible = 0;
$course2 = self::getDataGenerator()->create_course($coursedata);
// Add enrolment methods for course.
$instanceid1 = $selfplugin->add_instance($course1, array('status' => ENROL_INSTANCE_ENABLED,
@@ -246,6 +424,8 @@ class core_enrol_externallib_testcase extends externallib_advanced_testcase {
$enrolmentmethods = $DB->get_records('enrol', array('courseid' => $course1->id, 'status' => ENROL_INSTANCE_ENABLED));
$this->assertCount(2, $enrolmentmethods);
$this->setAdminUser();
// Check if information is returned.
$enrolmentmethods = core_enrol_external::get_course_enrolment_methods($course1->id);
$enrolmentmethods = external_api::clean_returnvalue(core_enrol_external::get_course_enrolment_methods_returns(),
@@ -275,5 +455,199 @@ class core_enrol_externallib_testcase extends externallib_advanced_testcase {
$this->assertEquals('self', $enrolmentmethod['type']);
$this->assertTrue($enrolmentmethod['status']);
$this->assertEquals('enrol_self_get_instance_info', $enrolmentmethod['wsfunction']);
// Try to retrieve information using a normal user for a hidden course.
$user = self::getDataGenerator()->create_user();
$this->setUser($user);
try {
core_enrol_external::get_course_enrolment_methods($course2->id);
} catch (moodle_exception $e) {
$this->assertEquals('coursehidden', $e->errorcode);
}
}
public function get_enrolled_users_setup($capability) {
global $USER;
$this->resetAfterTest(true);
$return = new stdClass();
$return->course = self::getDataGenerator()->create_course();
$return->user1 = self::getDataGenerator()->create_user();
$return->user2 = self::getDataGenerator()->create_user();
$return->user3 = self::getDataGenerator()->create_user();
$this->setUser($return->user3);
// Set the required capabilities by the external function.
$return->context = context_course::instance($return->course->id);
$return->roleid = $this->assignUserCapability($capability, $return->context->id);
$this->assignUserCapability('moodle/user:viewdetails', $return->context->id, $return->roleid);
// Enrol the users in the course.
$this->getDataGenerator()->enrol_user($return->user1->id, $return->course->id, $return->roleid, 'manual');
$this->getDataGenerator()->enrol_user($return->user2->id, $return->course->id, $return->roleid, 'manual');
$this->getDataGenerator()->enrol_user($return->user3->id, $return->course->id, $return->roleid, 'manual');
return $return;
}
/**
* Test get_enrolled_users from core_enrol_external without additional
* parameters.
*/
public function test_get_enrolled_users_without_parameters() {
$capability = 'moodle/course:viewparticipants';
$data = $this->get_enrolled_users_setup($capability);
// Call the external function.
$enrolledusers = core_enrol_external::get_enrolled_users($data->course->id);
// We need to execute the return values cleaning process to simulate the web service server.
$enrolledusers = external_api::clean_returnvalue(core_enrol_external::get_enrolled_users_returns(), $enrolledusers);
// Check the result set.
$this->assertEquals(3, count($enrolledusers));
$this->assertArrayHasKey('email', $enrolledusers[0]);
}
/**
* Test get_enrolled_users from core_enrol_external with some parameters set.
*/
public function test_get_enrolled_users_with_parameters() {
$capability = 'moodle/course:viewparticipants';
$data = $this->get_enrolled_users_setup($capability);
// Call the function with some parameters set.
$enrolledusers = core_enrol_external::get_enrolled_users($data->course->id, array(
array('name' => 'limitfrom', 'value' => 2),
array('name' => 'limitnumber', 'value' => 1),
array('name' => 'userfields', 'value' => 'id')
));
// We need to execute the return values cleaning process to simulate the web service server.
$enrolledusers = external_api::clean_returnvalue(core_enrol_external::get_enrolled_users_returns(), $enrolledusers);
// Check the result set, we should only get the 3rd result, which is $user3.
$this->assertCount(1, $enrolledusers);
$this->assertEquals($data->user3->id, $enrolledusers[0]['id']);
$this->assertArrayHasKey('id', $enrolledusers[0]);
$this->assertArrayNotHasKey('email', $enrolledusers[0]);
}
/**
* Test get_enrolled_users from core_enrol_external with capability to
* viewparticipants removed.
*/
public function test_get_enrolled_users_without_capability() {
$capability = 'moodle/course:viewparticipants';
$data = $this->get_enrolled_users_setup($capability);
// Call without required capability.
$this->unassignUserCapability($capability, $data->context->id, $data->roleid);
$this->setExpectedException('moodle_exception');
$categories = core_enrol_external::get_enrolled_users($data->course->id);
}
public function get_enrolled_users_with_capability_setup($capability) {
global $USER, $DB;
$this->resetAfterTest(true);
$return = new stdClass();
// Create the course and fetch its context.
$return->course = self::getDataGenerator()->create_course();
$context = context_course::instance($return->course->id);
// Create one teacher, and two students.
$return->teacher = self::getDataGenerator()->create_user();
$return->student1 = self::getDataGenerator()->create_user();
$return->student2 = self::getDataGenerator()->create_user();
// Create a new student role based on the student archetype but with the capability prohibitted.
$fakestudentroleid = create_role('Fake student role', 'fakestudent', 'Fake student role', 'student');
assign_capability($capability, CAP_PROHIBIT, $fakestudentroleid, $context->id);
// Enrol all of the users in the course.
// * 'teacher' is an editing teacher.
// * 'student1' is a standard student.
// * 'student2' is a student with the capability prohibitted.
$editingteacherroleid = $DB->get_field('role', 'id', array('shortname' => 'editingteacher'));
$studentroleid = $DB->get_field('role', 'id', array('shortname' => 'student'));
$this->getDataGenerator()->enrol_user($return->teacher->id, $return->course->id, $editingteacherroleid);
$this->getDataGenerator()->enrol_user($return->student1->id, $return->course->id, $studentroleid);
$this->getDataGenerator()->enrol_user($return->student2->id, $return->course->id, $fakestudentroleid);
// Log in as the teacher.
$this->setUser($return->teacher);
// Clear caches.
accesslib_clear_all_caches_for_unit_testing();
return $return;
}
/**
* Test get_enrolled_users_with_capability without additional paramaters.
*/
public function test_get_enrolled_users_with_capability_without_parameters() {
$capability = 'moodle/course:viewparticipants';
$data = $this->get_enrolled_users_with_capability_setup($capability);
$result = core_enrol_external::get_enrolled_users_with_capability(
array(
'coursecapabilities' => array(
'courseid' => $data->course->id,
'capabilities' => array(
$capability,
),
),
),
array()
);
// We need to execute the return values cleaning process to simulate the web service server.
$result = external_api::clean_returnvalue(core_enrol_external::get_enrolled_users_with_capability_returns(), $result);
// Check an array containing the expected user for the course capability is returned.
$expecteduserlist = $result[0];
$this->assertEquals($data->course->id, $expecteduserlist['courseid']);
$this->assertEquals($capability, $expecteduserlist['capability']);
$this->assertEquals(2, count($expecteduserlist['users']));
}
/**
* Test get_enrolled_users_with_capability
*/
public function test_get_enrolled_users_with_capability_with_parameters () {
$capability = 'moodle/course:viewparticipants';
$data = $this->get_enrolled_users_with_capability_setup($capability);
$result = core_enrol_external::get_enrolled_users_with_capability(
array(
'coursecapabilities' => array(
'courseid' => $data->course->id,
'capabilities' => array(
$capability,
),
),
),
array(
array('name' => 'limitfrom', 'value' => 1),
array('name' => 'limitnumber', 'value' => 1),
array('name' => 'userfields', 'value' => 'id')
)
);
// We need to execute the return values cleaning process to simulate the web service server.
$result = external_api::clean_returnvalue(core_enrol_external::get_enrolled_users_with_capability_returns(), $result);
// Check an array containing the expected user for the course capability is returned.
$expecteduserlist = $result[0]['users'];
$expecteduser = reset($expecteduserlist);
$this->assertEquals(1, count($expecteduserlist));
$this->assertEquals($data->student1->id, $expecteduser['id']);
}
}
+4
View File
@@ -1,6 +1,10 @@
This files describes API changes in /enrol/* - plugins,
information provided here is intended especially for developers.
=== 2.8.9 ===
* External function core_enrol_external::get_enrolled_users now returns only the viewable participants if the course has groups enabled and no groupid is passed.
=== 2.8 ===
* enrol_plugin::instance_deleteable() is deprecated and has been replaced by enrol_plugin::can_delete_instance()
+1 -1
View File
@@ -174,7 +174,7 @@ if ($action) {
$renderer = $PAGE->get_renderer('core_enrol');
$userdetails = array('picture' => false);
$userdetails = array('picture' => false, 'userfullnamedisplay' => false);
// Get all the user names in a reasonable default order.
$allusernames = get_all_user_name_fields(false, null, null, null, true);
// Initialise the variable for the user's names in the table header.
+4 -1
View File
@@ -1015,7 +1015,10 @@ class core_files_renderer extends plugin_renderer_base {
<label class="control-label"></label>
<div class="controls"><select></select></div>
</div>
</div>';
// HACK to prevent browsers from automatically inserting the user's password into the wrong fields.
$rv .= prevent_form_autofill_password();
$rv .= '
<div class="fp-login-input control-group clearfix">
<label class="control-label"></label>
<div class="controls"><input/></div>
+13 -2
View File
@@ -68,7 +68,6 @@ class grade_export_xml extends grade_export {
foreach ($userdata->grades as $itemid => $grade) {
$grade_item = $this->grade_items[$itemid];
$grade->grade_item =& $grade_item;
$gradestr = $this->format_grade($grade, $this->displaytype); // no formating for now
// MDL-11669, skip exported grades or bad grades (if setting says so)
if ($export_tracking) {
@@ -88,7 +87,19 @@ class grade_export_xml extends grade_export {
fwrite($handle, "\t\t<assignment>{$grade_item->idnumber}</assignment>\n");
// this column should be customizable to use either student id, idnumber, uesrname or email.
fwrite($handle, "\t\t<student>{$user->idnumber}</student>\n");
fwrite($handle, "\t\t<score>$gradestr</score>\n");
// Format and display the grade in the selected display type (real, letter, percentage).
if (is_array($this->displaytype)) {
// Grades display type came from the return of export_bulk_export_data() on grade publishing.
foreach ($this->displaytype as $gradedisplayconst) {
$gradestr = $this->format_grade($grade, $gradedisplayconst);
fwrite($handle, "\t\t<score>$gradestr</score>\n");
}
} else {
// Grade display type submitted directly from the grade export form.
$gradestr = $this->format_grade($grade, $this->displaytype);
fwrite($handle, "\t\t<score>$gradestr</score>\n");
}
if ($this->export_feedback) {
$feedbackstr = $this->format_feedback($userdata->feedbacks[$itemid]);
fwrite($handle, "\t\t<feedback>$feedbackstr</feedback>\n");
+1 -1
View File
@@ -185,7 +185,7 @@ class grading_manager {
} else if ($this->get_context()->contextlevel >= CONTEXT_COURSE) {
list($context, $course, $cm) = get_context_info_array($this->get_context()->id);
if (!empty($cm->name)) {
if (strval($cm->name) !== '') {
$title = $cm->name;
} else {
debugging('Gradable areas are currently supported at the course module level only', DEBUG_DEVELOPER);
+1 -1
View File
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
require_once(__DIR__ . "../../../../config.php");
require_once(__DIR__ . "/../../../config.php");
require_once($CFG->libdir.'/gradelib.php');
require_once($CFG->dirroot.'/grade/lib.php');
require_once($CFG->dirroot.'/grade/import/lib.php');
+1 -3
View File
@@ -308,6 +308,7 @@ class graded_users_iterator {
$grades[$grade_item->id] =
new grade_grade(array('userid'=>$user->id, 'itemid'=>$grade_item->id), false);
}
$grades[$grade_item->id]->grade_item = $grade_item;
}
}
@@ -3120,9 +3121,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
+27 -9
View File
@@ -741,7 +741,8 @@ class grade_report_grader extends grade_report {
'cfg' => array('ajaxenabled'=>false),
'items' => array(),
'users' => array(),
'feedback' => array()
'feedback' => array(),
'grades' => array()
);
$jsscales = array();
@@ -993,6 +994,14 @@ class grade_report_grader extends grade_report {
} else if ($USER->gradeediting[$this->courseid]) {
if ($item->scaleid && !empty($scalesarray[$item->scaleid])) {
$itemcell->attributes['class'] .= ' grade_type_scale';
} else if ($item->gradetype == GRADE_TYPE_VALUE) {
$itemcell->attributes['class'] .= ' grade_type_value';
} else if ($item->gradetype == GRADE_TYPE_TEXT) {
$itemcell->attributes['class'] .= ' grade_type_text';
}
if ($item->scaleid && !empty($scalesarray[$item->scaleid])) {
$scale = $scalesarray[$item->scaleid];
$gradeval = (int)$gradeval; // scales use only integers
@@ -1062,16 +1071,16 @@ class grade_report_grader extends grade_report {
if ($item->scaleid && !empty($scalesarray[$item->scaleid])) {
$itemcell->attributes['class'] .= ' grade_type_scale';
} else if ($item->gradetype != GRADE_TYPE_TEXT) {
} else if ($item->gradetype == GRADE_TYPE_VALUE) {
$itemcell->attributes['class'] .= ' grade_type_value';
} else if ($item->gradetype == GRADE_TYPE_TEXT) {
$itemcell->attributes['class'] .= ' grade_type_text';
}
if ($enableajax) {
$canoverride = true;
if ($item->is_category_item() || $item->is_course_item()) {
$canoverride = (bool) get_config('moodle', 'grade_overridecat');
}
if ($canoverride) {
// Only allow edting if the grade is editable (not locked, not in a unoverridable category, etc).
if ($enableajax && $grade->is_editable()) {
// If a grade item is type text, and we don't have show quick feedback on, it can't be edited.
if ($item->gradetype != GRADE_TYPE_TEXT || $showquickfeedback) {
$itemcell->attributes['class'] .= ' clickable';
}
}
@@ -1093,6 +1102,14 @@ class grade_report_grader extends grade_report {
}
}
// Enable keyboard navigation if the grade is editable (not locked, not in a unoverridable category, etc).
if ($enableajax && $grade->is_editable()) {
// If a grade item is type text, and we don't have show quick feedback on, it can't be edited.
if ($item->gradetype != GRADE_TYPE_TEXT || $showquickfeedback) {
$itemcell->attributes['class'] .= ' gbnavigable';
}
}
if (!empty($this->gradeserror[$item->id][$userid])) {
$itemcell->text .= $this->gradeserror[$item->id][$userid];
}
@@ -1106,7 +1123,8 @@ class grade_report_grader extends grade_report {
$jsarguments['cfg']['ajaxenabled'] = true;
$jsarguments['cfg']['scales'] = array();
foreach ($jsscales as $scale) {
$jsarguments['cfg']['scales'][$scale->id] = explode(',', $scale->scale);
// Trim the scale values, as they may have a space that is ommitted from values later.
$jsarguments['cfg']['scales'][$scale->id] = array_map('trim', explode(',', $scale->scale));
}
$jsarguments['cfg']['feedbacktrunclength'] = $this->feedback_trunc_length;
+234 -81
View File
@@ -209,9 +209,11 @@ M.gradereport_grader.classes.ajax.prototype.make_editable = function(e) {
}
// Sort out the field type
var fieldtype = 'text';
var fieldtype = 'value';
if (node.hasClass('grade_type_scale')) {
fieldtype = 'scale';
} else if (node.hasClass('grade_type_text')) {
fieldtype = 'text';
}
// Create the appropriate field widget
switch (fieldtype) {
@@ -219,6 +221,8 @@ M.gradereport_grader.classes.ajax.prototype.make_editable = function(e) {
this.current = new M.gradereport_grader.classes.scalefield(this.report, node);
break;
case 'text':
this.current = new M.gradereport_grader.classes.feedbackfield(this.report, node);
break;
default:
this.current = new M.gradereport_grader.classes.textfield(this.report, node);
break;
@@ -247,6 +251,7 @@ M.gradereport_grader.classes.ajax.prototype.keypress_enter = function(e) {
* @param {Bool} ignoreshift If true and shift is pressed then don't exec
*/
M.gradereport_grader.classes.ajax.prototype.keypress_tab = function(e, ignoreshift) {
e.preventDefault();
var next = null;
if (e.shiftKey) {
if (ignoreshift) {
@@ -322,7 +327,11 @@ M.gradereport_grader.classes.ajax.prototype.get_next_cell = function(cell) {
next = tr.all('.grade').item(0);
}
if (!next) {
next = this.current.node;
return this.current.node;
}
// Continue on until we find a navigable cell
if (!next.hasClass('gbnavigable')) {
return this.get_next_cell(next);
}
return next;
};
@@ -342,7 +351,11 @@ M.gradereport_grader.classes.ajax.prototype.get_prev_cell = function(cell) {
next = cells.item(cells.size()-1);
}
if (!next) {
next = this.current.node;
return this.current.node;
}
// Continue on until we find a navigable cell
if (!next.hasClass('gbnavigable')) {
return this.get_prev_cell(next);
}
return next;
};
@@ -366,7 +379,11 @@ M.gradereport_grader.classes.ajax.prototype.get_above_cell = function(cell) {
next = tr.all('td.cell').item(column);
}
if (!next) {
next = this.current.node;
return this.current.node;
}
// Continue on until we find a navigable cell
if (!next.hasClass('gbnavigable')) {
return this.get_above_cell(next);
}
return next;
};
@@ -389,7 +406,13 @@ M.gradereport_grader.classes.ajax.prototype.get_below_cell = function(cell) {
}
next = tr.all('td.cell').item(column);
}
// next will be null when we get to the bottom of a column
if (!next) {
return this.current.node;
}
// Continue on until we find a navigable cell
if (!next.hasClass('gbnavigable')) {
return this.get_below_cell(next);
}
return next;
};
/**
@@ -478,6 +501,7 @@ M.gradereport_grader.classes.ajax.prototype.submission_outcome = function(tid, o
}
// Calculate the final grade for the cell
var finalgrade = '';
var scalegrade = -1;
if (!r.finalgrade) {
if (this.report.isediting) {
// In edit mode don't put hyphens in the grade text boxes
@@ -488,35 +512,51 @@ M.gradereport_grader.classes.ajax.prototype.submission_outcome = function(tid, o
}
} else {
if (r.scale) {
finalgrade = this.scales[r.scale][parseFloat(r.finalgrade)-1];
scalegrade = parseFloat(r.finalgrade);
finalgrade = this.scales[r.scale][scalegrade-1];
} else {
finalgrade = parseFloat(r.finalgrade).toFixed(info.itemdp);
}
}
if (this.report.isediting) {
if (args.properties.itemtype == 'scale') {
info.cell.one('#grade_'+r.userid+'_'+r.itemid).all('options').each(function(option){
if (option.get('value') == finalgrade) {
option.setAttribute('selected', 'selected');
} else {
option.removeAttribute('selected');
}
});
} else {
info.cell.one('#grade_'+r.userid+'_'+r.itemid).set('value', finalgrade);
var grade = info.cell.one('#grade_'+r.userid+'_'+r.itemid);
if (grade) {
// This means the item has a input element to update.
var parent = grade.ancestor('td');
if (parent.hasClass('grade_type_scale')) {
grade.all('option').each(function(option) {
if (option.get('value') == scalegrade) {
option.setAttribute('selected', 'selected');
} else {
option.removeAttribute('selected');
}
});
} else {
grade.set('value', finalgrade);
}
} else if (info.cell.one('.gradevalue')) {
// This means we are updating a value for something without editing boxed (locked, etc).
info.cell.one('.gradevalue').set('innerHTML', finalgrade);
}
} else {
// If there is no currently editing field or if this cell is not being currently edited
if (!this.current || info.cell.get('id') != this.current.node.get('id')) {
// Update the value
info.cell.one('.gradevalue').set('innerHTML',finalgrade);
var node = info.cell.one('.gradevalue');
var td = node.ancestor('td');
// Only scale and value type grades should have their content updated in this way.
if (td.hasClass('grade_type_value') || td.hasClass('grade_type_scale')) {
node.set('innerHTML', finalgrade);
}
} else if (this.current && info.cell.get('id') == this.current.node.get('id')) {
// If we are here the grade value of the cell currently being edited has changed !!!!!!!!!
// If the user has not actually changed the old value yet we will automatically correct it
// otherwise we will prompt the user to choose to use their value or the new value!
if (!this.current.has_changed() || confirm(M.str.gradereport_grader.ajaxfieldchanged)) {
this.current.set_grade(finalgrade);
this.current.grade.set('value', finalgrade);
if (this.current.grade) {
this.current.grade.set('value', finalgrade);
}
}
}
}
@@ -594,57 +634,69 @@ M.gradereport_grader.classes.existingfield = function(ajax, userid, itemid) {
this.editfeedback = ajax.showquickfeedback;
this.grade = this.report.Y.one('#grade_'+userid+'_'+itemid);
if (this.report.grades) {
for (var i = 0; i < this.report.grades.length; i++) {
if (this.report.grades[i]['user']==this.userid && this.report.grades[i]['item']==this.itemid) {
var i = 0;
if (this.grade) {
for (i = 0; i < this.report.grades.length; i++) {
if (this.report.grades[i]['user'] == this.userid && this.report.grades[i]['item'] == this.itemid) {
this.oldgrade = this.report.grades[i]['grade'];
}
}
}
if (!this.oldgrade) {
// Assigning an empty string makes determining whether the grade has been changed easier
// This value is never sent to the server
this.oldgrade = '';
}
if (!this.oldgrade) {
// Assigning an empty string makes determining whether the grade has been changed easier
// This value is never sent to the server
this.oldgrade = '';
}
// On blur save any changes in the grade field
this.grade.on('blur', this.submit, this);
// On blur save any changes in the grade field
this.grade.on('blur', this.submit, this);
}
// Check if feedback is enabled
if (this.editfeedback) {
// Get the feedback fields
this.feedback = this.report.Y.one('#feedback_'+userid+'_'+itemid);
for(var i = 0; i < this.report.feedback.length; i++) {
if (this.report.feedback[i]['user']==this.userid && this.report.feedback[i]['item']==this.itemid) {
this.oldfeedback = this.report.feedback[i]['content'];
if (this.feedback) {
for(i = 0; i < this.report.feedback.length; i++) {
if (this.report.feedback[i]['user'] == this.userid && this.report.feedback[i]['item'] == this.itemid) {
this.oldfeedback = this.report.feedback[i]['content'];
}
}
if(!this.oldfeedback) {
// Assigning an empty string makes determining whether the feedback has been changed easier
// This value is never sent to the server
this.oldfeedback = '';
}
// On blur save any changes in the feedback field
this.feedback.on('blur', this.submit, this);
// Override the default tab movements when moving between cells
this.keyevents.push(this.report.Y.on('key', this.keypress_tab, this.feedback, 'press:9', this, true)); // Handle Tab
this.keyevents.push(this.report.Y.on('key', this.keypress_enter, this.feedback, 'press:13', this)); // Handle the Enter key being pressed
this.keyevents.push(this.report.Y.on('key', this.keypress_arrows, this.feedback, 'press:37,38,39,40+ctrl', this)); // Handle CTRL + arrow keys
if (this.grade) {
// Override the default tab movements when moving between cells
this.keyevents.push(this.report.Y.on('key', this.keypress_tab, this.grade, 'press:9+shift', this)); // Handle Shift+Tab
// Override the default tab movements for fields in the same cell
this.keyevents.push(this.report.Y.on('key', function(e){e.preventDefault();this.grade.focus();}, this.feedback, 'press:9+shift', this));
this.keyevents.push(this.report.Y.on('key', function(e){if (e.shiftKey) {return;}e.preventDefault();this.feedback.focus();}, this.grade, 'press:9', this));
}
}
if(!this.oldfeedback) {
// Assigning an empty string makes determining whether the feedback has been changed easier
// This value is never sent to the server
this.oldfeedback = '';
}
// On blur save any changes in the feedback field
this.feedback.on('blur', this.submit, this);
// Override the default tab movements when moving between cells
this.keyevents.push(this.report.Y.on('key', this.keypress_tab, this.grade, 'press:9+shift', this)); // Handle Shift+Tab
this.keyevents.push(this.report.Y.on('key', this.keypress_tab, this.feedback, 'press:9', this, true)); // Handle Tab
this.keyevents.push(this.report.Y.on('key', this.keypress_enter, this.feedback, 'press:13', this)); // Handle the Enter key being pressed
this.keyevents.push(this.report.Y.on('key', this.keypress_arrows, this.feedback, 'press:37,38,39,40+ctrl', this)); // Handle CTRL + arrow keys
// Override the default tab movements for fields in the same cell
this.keyevents.push(this.report.Y.on('key', function(e){e.preventDefault();this.grade.focus();}, this.feedback, 'press:9+shift', this));
this.keyevents.push(this.report.Y.on('key', function(e){if (e.shiftKey) {return;}e.preventDefault();this.feedback.focus();}, this.grade, 'press:9', this));
} else {
this.keyevents.push(this.report.Y.on('key', this.keypress_tab, this.grade, 'press:9', this)); // Handle Tab and Shift+Tab
} else if (this.grade) {
// Handle Tab and Shift+Tab.
this.keyevents.push(this.report.Y.on('key', this.keypress_tab, this.grade, 'down:9', this));
}
if (this.grade) {
// Handle the Enter key being pressed.
this.keyevents.push(this.report.Y.on('key', this.keypress_enter, this.grade, 'up:13', this));
// Handle CTRL + arrow keys.
this.keyevents.push(this.report.Y.on('key', this.keypress_arrows, this.grade, 'down:37,38,39,40+ctrl', this));
}
this.keyevents.push(this.report.Y.on('key', this.keypress_enter, this.grade, 'press:13', this)); // Handle the Enter key being pressed
this.keyevents.push(this.report.Y.on('key', this.keypress_arrows, this.grade, 'press:37,38,39,40+ctrl', this)); // Handle CTRL + arrow keys
};
/**
* Attach the required properties and methods to the existing field class
@@ -696,6 +748,7 @@ M.gradereport_grader.classes.existingfield.prototype.keypress_tab = function(e,
* @param {Event} e
*/
M.gradereport_grader.classes.existingfield.prototype.keypress_arrows = function(e) {
e.preventDefault();
var next = null;
switch (e.keyCode) {
case 37: // Left
@@ -722,6 +775,7 @@ M.gradereport_grader.classes.existingfield.prototype.keypress_arrows = function(
M.gradereport_grader.classes.existingfield.prototype.move_focus = function(node) {
if (node) {
var properties = this.report.get_cell_info(node);
this.report.ajax.current = node;
switch(properties.itemtype) {
case 'scale':
properties.cell.one('select.select').focus();
@@ -741,10 +795,17 @@ M.gradereport_grader.classes.existingfield.prototype.move_focus = function(node)
* @return {Bool}
*/
M.gradereport_grader.classes.existingfield.prototype.has_changed = function() {
if (this.editfeedback) {
return (this.grade.get('value') !== this.oldgrade || this.feedback.get('value') !== this.oldfeedback);
if (this.grade) {
if (this.grade.get('value') !== this.oldgrade) {
return true;
}
}
return (this.grade.get('value') !== this.oldgrade);
if (this.editfeedback && this.feedback) {
if (this.feedback.get('value') !== this.oldfeedback) {
return true;
}
}
return false;
};
/**
* Submits any changes and then updates the fields accordingly
@@ -759,15 +820,19 @@ M.gradereport_grader.classes.existingfield.prototype.submit = function() {
var properties = this.report.get_cell_info([this.userid,this.itemid]);
var values = (function(f){
var feedback, oldfeedback = null;
if (f.editfeedback) {
var feedback, oldfeedback, grade, oldgrade = null;
if (f.editfeedback && f.feedback) {
feedback = f.feedback.get('value');
oldfeedback = f.oldfeedback;
}
if (f.grade) {
grade = f.grade.get('value');
oldgrade = f.oldgrade;
}
return {
editablefeedback : f.editfeedback,
grade : f.grade.get('value'),
oldgrade : f.oldgrade,
grade : grade,
oldgrade : oldgrade,
feedback : feedback,
oldfeedback : oldfeedback
};
@@ -799,11 +864,11 @@ M.gradereport_grader.classes.textfield = function(report, node) {
this.gradespan = node.one('.gradevalue');
this.inputdiv = this.report.Y.Node.create('<div></div>');
this.editfeedback = this.report.ajax.showquickfeedback;
this.grade = this.report.Y.Node.create('<input type="text" class="text" value="" />');
this.grade = this.report.Y.Node.create('<input type="text" class="text" value="" name="ajaxgrade" />');
this.gradetype = 'value';
this.inputdiv.append(this.grade);
if (this.report.ajax.showquickfeedback) {
this.feedback = this.report.Y.Node.create('<input type="text" class="quickfeedback" value="" />');
this.feedback = this.report.Y.Node.create('<input type="text" class="quickfeedback" value="" name="ajaxfeedback" />');
this.inputdiv.append(this.feedback);
}
};
@@ -832,7 +897,11 @@ M.gradereport_grader.classes.textfield.prototype.replace = function() {
this.set_feedback(this.get_feedback());
}
this.node.replaceChild(this.inputdiv, this.gradespan);
this.grade.focus();
if (this.grade) {
this.grade.focus();
} else if (this.feedback) {
this.feedback.focus();
}
this.editable = true;
return this;
};
@@ -845,6 +914,7 @@ M.gradereport_grader.classes.textfield.prototype.replace = function() {
M.gradereport_grader.classes.textfield.prototype.commit = function() {
// Produce an anonymous result object contianing all values
var result = (function(field){
// Editable false lets us get the pre-update values.
field.editable = false;
var oldgrade = field.get_grade();
if (oldgrade == '-') {
@@ -855,6 +925,8 @@ M.gradereport_grader.classes.textfield.prototype.commit = function() {
if (field.editfeedback) {
oldfeedback = field.get_feedback();
}
// Now back to editable gives us the values in the edit areas.
field.editable = true;
if (field.editfeedback) {
feedback = field.get_feedback();
@@ -931,7 +1003,11 @@ M.gradereport_grader.classes.textfield.prototype.set_grade = function(value) {
*/
M.gradereport_grader.classes.textfield.prototype.get_feedback = function() {
if (this.editable) {
return this.feedback.get('value');
if (this.feedback) {
return this.feedback.get('value');
} else {
return null;
}
}
var properties = this.report.get_cell_info(this.node);
if (properties) {
@@ -947,7 +1023,9 @@ M.gradereport_grader.classes.textfield.prototype.get_feedback = function() {
*/
M.gradereport_grader.classes.textfield.prototype.set_feedback = function(value) {
if (!this.editable) {
this.feedback.set('value', value);
if (this.feedback) {
this.feedback.set('value', value);
}
} else {
var properties = this.report.get_cell_info(this.node);
this.report.update_feedback(properties.userid, properties.itemid, value);
@@ -971,7 +1049,12 @@ M.gradereport_grader.classes.textfield.prototype.has_changed = function() {
return true;
}
}
return (this.get_grade() != this.gradespan.get('innerHTML'));
if (this.grade) {
return (this.get_grade() != this.gradespan.get('innerHTML'));
} else {
return false;
}
};
/**
* Attaches the key listeners for the editable fields and stored the event references
@@ -984,22 +1067,92 @@ M.gradereport_grader.classes.textfield.prototype.attach_key_events = function()
var a = this.report.ajax;
// Setup the default key events for tab and enter
if (this.editfeedback) {
this.keyevents.push(this.report.Y.on('key', a.keypress_tab, this.grade, 'press:9+shift', a)); // Handle Shift+Tab
this.keyevents.push(this.report.Y.on('key', a.keypress_tab, this.feedback, 'press:9', a, true)); // Handle Tab
this.keyevents.push(this.report.Y.on('key', a.keypress_enter, this.feedback, 'press:13', a)); // Handle the Enter key being pressed
if (this.grade) {
// Handle Shift+Tab.
this.keyevents.push(this.report.Y.on('key', a.keypress_tab, this.grade, 'down:9+shift', a));
}
// Handle Tab.
this.keyevents.push(this.report.Y.on('key', a.keypress_tab, this.feedback, 'down:9', a, true));
// Handle the Enter key being pressed.
this.keyevents.push(this.report.Y.on('key', a.keypress_enter, this.feedback, 'up:13', a));
} else {
this.keyevents.push(this.report.Y.on('key', a.keypress_tab, this.grade, 'press:9', a)); // Handle Tab and Shift+Tab
if (this.grade) {
// Handle Tab and Shift+Tab.
this.keyevents.push(this.report.Y.on('key', a.keypress_tab, this.grade, 'down:9', a));
}
}
this.keyevents.push(this.report.Y.on('key', a.keypress_enter, this.grade, 'press:13', a)); // Handle the Enter key being pressed
// Setup the arrow key events
this.keyevents.push(this.report.Y.on('key', a.keypress_arrows, this.grade.ancestor('td'), 'down:37,38,39,40+ctrl', a)); // Handle CTRL + arrow keys
// Prevent the default key action on all fields for arrow keys on all key events!
// Note: this still does not work in FF!!!!!
this.keyevents.push(this.report.Y.on('key', function(e){e.preventDefault();}, this.grade, 'down:37,38,39,40+ctrl'));
this.keyevents.push(this.report.Y.on('key', function(e){e.preventDefault();}, this.grade, 'press:37,38,39,40+ctrl'));
this.keyevents.push(this.report.Y.on('key', function(e){e.preventDefault();}, this.grade, 'up:37,38,39,40+ctrl'));
this.keyevents.push(this.report.Y.on('key', a.keypress_arrows, this.inputdiv.ancestor('td'), 'down:37,38,39,40+ctrl', a)); // Handle CTRL + arrow keys
if (this.grade) {
// Handle the Enter key being pressed.
this.keyevents.push(this.report.Y.on('key', a.keypress_enter, this.grade, 'up:13', a));
// Prevent the default key action on all fields for arrow keys on all key events!
// Note: this still does not work in FF!!!!!
this.keyevents.push(this.report.Y.on('key', function(e){e.preventDefault();}, this.grade, 'down:37,38,39,40+ctrl'));
this.keyevents.push(this.report.Y.on('key', function(e){e.preventDefault();}, this.grade, 'press:37,38,39,40+ctrl'));
this.keyevents.push(this.report.Y.on('key', function(e){e.preventDefault();}, this.grade, 'up:37,38,39,40+ctrl'));
}
};
/**
* Feedback field class
* This classes gets used in conjunction with the report running with AJAX enabled
* and is used to manage a cell that no editable grade, only possibly feedback
*
* @class feedbackfield
* @constructor
* @this {M.gradereport_grader.classes.feedbackfield}
* @param {M.gradereport_grader.classes.report} report
* @param {Y.Node} node
*/
M.gradereport_grader.classes.feedbackfield = function(report, node) {
this.report = report;
this.node = node;
this.gradespan = node.one('.gradevalue');
this.inputdiv = this.report.Y.Node.create('<div></div>');
this.editfeedback = this.report.ajax.showquickfeedback;
this.gradetype = 'text';
if (this.report.ajax.showquickfeedback) {
this.feedback = this.report.Y.Node.create('<input type="text" class="quickfeedback" value="" name="ajaxfeedback" />');
this.inputdiv.append(this.feedback);
}
};
/**
* Gets the grade for current cell (which will always be null)
*
* @function
* @this {M.gradereport_grader.classes.feedbackfield}
* @return {Mixed}
*/
M.gradereport_grader.classes.feedbackfield.prototype.get_grade = function() {
return null;
};
/**
* Overrides the set_grade function of textfield so that it can ignore the set-grade
* for grade cells without grades
*
* @function
* @this {M.gradereport_grader.classes.feedbackfield}
* @param {String} value
*/
M.gradereport_grader.classes.feedbackfield.prototype.set_grade = function() {
return;
};
/**
* Manually extend the feedbackfield class with the properties and methods of the
* textfield class that have not been defined
*/
for (var i in M.gradereport_grader.classes.textfield.prototype) {
if (!M.gradereport_grader.classes.feedbackfield.prototype[i]) {
M.gradereport_grader.classes.feedbackfield.prototype[i] = M.gradereport_grader.classes.textfield.prototype[i];
}
}
/**
* An editable scale field
*
@@ -1017,11 +1170,11 @@ M.gradereport_grader.classes.scalefield = function(report, node) {
this.gradespan = node.one('.gradevalue');
this.inputdiv = this.report.Y.Node.create('<div></div>');
this.editfeedback = this.report.ajax.showquickfeedback;
this.grade = this.report.Y.Node.create('<select type="text" class="text" /><option value="-1">'+M.str.gradereport_grader.ajaxchoosescale+'</option></select>');
this.grade = this.report.Y.Node.create('<select type="text" class="text" name="ajaxgrade" /><option value="-1">'+M.str.gradereport_grader.ajaxchoosescale+'</option></select>');
this.gradetype = 'scale';
this.inputdiv.append(this.grade);
if (this.editfeedback) {
this.feedback = this.report.Y.Node.create('<input type="text" class="quickfeedback" value="" />');
this.feedback = this.report.Y.Node.create('<input type="text" class="quickfeedback" value="" name="ajaxfeedback"/>');
this.inputdiv.append(this.feedback);
}
var properties = this.report.get_cell_info(node);
@@ -0,0 +1,210 @@
@gradereport @gradereport_grader
Feature: Using the AJAX grading feature of Grader report to update grades and feedback
In order to use AJAX grading
As a teacher
I need to be able to update and verify grades
Background:
Given the following "courses" exist:
| fullname | shortname | category | groupmode |
| Course 1 | C1 | 0 | 1 |
And the following "users" exist:
| username | firstname | lastname | email | idnumber |
| teacher1 | Teacher | 1 | teacher1@example.com | t1 |
| student1 | Student | 1 | student1@example.com | s1 |
| student2 | Student | 2 | student2@example.com | s2 |
| student3 | Student | 3 | student3@example.com | s3 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
| student2 | C1 | student |
| student3 | C1 | student |
And the following "scales" exist:
| name | scale |
| Test Scale | Disappointing,Good,Very good,Excellent |
And the following "grade categories" exist:
| fullname | course |
| Grade Cat | C1 |
And the following "grade items" exist:
| itemname | course | locked | gradetype | gradecategory |
| Item 1 | C1 | 0 | value | Grade Cat |
| Item VU | C1 | 0 | value | Grade Cat |
| Item VL | C1 | 1 | value | Grade Cat |
| Item TU | C1 | 0 | text | Grade Cat |
| Item TL | C1 | 1 | text | Grade Cat |
And the following "grade items" exist:
| itemname | course | locked | gradetype | scale | gradecategory |
| Item SU | C1 | 0 | scale | Test Scale | Grade Cat |
| Item SL | C1 | 1 | scale | Test Scale | Grade Cat |
And the following "grade items" exist:
| itemname | course | locked | gradetype | gradecategory |
| Item 3 | C1 | 0 | value | Grade Cat |
And the following config values are set as admin:
| grade_report_showaverages | 0 |
| grade_report_enableajax | 1 |
@javascript
Scenario: Use the grader report without editing, with AJAX on and quick feedback off
When the following config values are set as admin:
| grade_overridecat | 1 |
| grade_report_showquickfeedback | 0 |
And I log in as "teacher1"
And I follow "Course 1"
And I navigate to "Grades" node in "Course administration"
And I click on student "Student 2" for grade item "Item VU"
Then I should see a grade field for "Student 2" and grade item "Item VU"
And I should not see a feedback field for "Student 2" and grade item "Item VU"
And I set the field "ajaxgrade" to "33"
And I click on student "Student 1" for grade item "Item VU"
And I should not see a grade field for "Student 2" and grade item "Item VU"
And I should not see a feedback field for "Student 2" and grade item "Item VU"
And I click on student "Student 3" for grade item "Item VU"
And I set the field "ajaxgrade" to "50"
And I click on student "Student 1" for grade item "Item VU"
And I click on student "Student 3" for grade item "Item 1"
And I set the field "ajaxgrade" to "80"
And I click on student "Student 1" for grade item "Item 1"
And I click on student "Student 3" for grade item "Item SU"
And I set the field "ajaxgrade" to "Very good"
And I click on student "Student 1" for grade item "Item SU"
And the following should exist in the "user-grades" table:
| -1- | -4- | -5- | -9- | -13- |
| Student 2 | - | 33.00 | - | 33.00 |
| Student 3 | 80.00 | 50.00 | Very good | 133.00 |
And I click on student "Student 3" for grade item "Item VL"
And I should not see a grade field for "Student 3" and grade item "Item VL"
And I should not see a feedback field for "Student 3" and grade item "Item VL"
And I click on student "Student 3" for grade item "Item SL"
And I should not see a grade field for "Student 3" and grade item "Item SL"
And I should not see a feedback field for "Student 3" and grade item "Item SL"
And I click on student "Student 3" for grade item "Item TU"
And I should not see a grade field for "Student 3" and grade item "Item TU"
And I should not see a feedback field for "Student 3" and grade item "Item TU"
And I click on student "Student 1" for grade item "Course total"
And I should see a grade field for "Student 1" and grade item "Course total"
And I should not see a feedback field for "Student 1" and grade item "Course total"
And I set the field "ajaxgrade" to "90"
And I click on student "Student 1" for grade item "Item 3"
And the following should exist in the "user-grades" table:
| -1- | -13- |
| Student 1 | 90.00 |
And I navigate to "Grader report" node in "Grade administration"
And the following should exist in the "user-grades" table:
| -1- | -4- | -5- | -9- | -13- |
| Student 1 | - | - | - | 90.00 |
| Student 2 | - | 33.00 | - | 33.00 |
| Student 3 | 80.00 | 50.00 | Very good | 133.00 |
@javascript
Scenario: Use the grader report without editing, with AJAX and quick feedback on
When the following config values are set as admin:
| grade_overridecat | 1 |
| grade_report_showquickfeedback | 1 |
And I log in as "teacher1"
And I follow "Course 1"
And I navigate to "Grades" node in "Course administration"
And I click on student "Student 2" for grade item "Item VU"
Then I should see a grade field for "Student 2" and grade item "Item VU"
And I should see a feedback field for "Student 2" and grade item "Item VU"
And I set the field "ajaxgrade" to "33"
And I set the field "ajaxfeedback" to "Student 2 VU feedback"
And I click on student "Student 3" for grade item "Item VL"
And I should not see a grade field for "Student 3" and grade item "Item VL"
And I should not see a feedback field for "Student 3" and grade item "Item VL"
And I click on student "Student 3" for grade item "Item TU"
And I should not see a grade field for "Student 3" and grade item "Item TU"
And I should see a feedback field for "Student 3" and grade item "Item TU"
And I set the field "ajaxfeedback" to "Student 3 TU feedback"
And I click on student "Student 2" for grade item "Item SU"
And I set the field "ajaxgrade" to "Very good"
And I set the field "ajaxfeedback" to "Student 2 SU feedback"
And I click on student "Student 1" for grade item "Item SU"
And I navigate to "Grader report" node in "Grade administration"
And the following should exist in the "user-grades" table:
| -1- | -5- | -9- | -13- |
| Student 2 | 33.00 | Very good | 36.00 |
And I click on student "Student 3" for grade item "Item TU"
And the field "ajaxfeedback" matches value "Student 3 TU feedback"
And I click on student "Student 2" for grade item "Item SU"
And the field "ajaxfeedback" matches value "Student 2 SU feedback"
@javascript
Scenario: Use the grader report without editing, with AJAX and quick feedback on, without category override
When the following config values are set as admin:
| grade_overridecat | 0 |
| grade_report_showquickfeedback | 1 |
And I log in as "teacher1"
And I follow "Course 1"
And I navigate to "Grades" node in "Course administration"
And I click on student "Student 2" for grade item "Item VU"
Then I should see a grade field for "Student 2" and grade item "Item VU"
And I should see a feedback field for "Student 2" and grade item "Item VU"
And I set the field "ajaxgrade" to "33"
And I click on student "Student 1" for grade item "Item VU"
And I click on student "Student 2" for grade item "Course total"
And I should not see a grade field for "Student 3" and grade item "Course total"
And I should not see a feedback field for "Student 3" and grade item "Course total"
And the following should exist in the "user-grades" table:
| -1- | -5- | -13- |
| Student 2 | 33.00 | 33.00 |
@javascript
Scenario: Use the grader report with editing, with AJAX and quick feedback on, with category override
When the following config values are set as admin:
| grade_overridecat | 1 |
| grade_report_showquickfeedback | 1 |
And I log in as "teacher1"
And I follow "Course 1"
And I navigate to "Grades" node in "Course administration"
And I turn editing mode on
Then I should not see a grade field for "Student 2" and grade item "Item VL"
And I should not see a feedback field for "Student 2" and grade item "Item VL"
And I should not see a grade field for "Student 2" and grade item "Item TU"
And I should see a feedback field for "Student 2" and grade item "Item TU"
And I should see a grade field for "Student 2" and grade item "Course total"
And I should see a feedback field for "Student 2" and grade item "Course total"
And I give the grade "20.00" to the user "Student 2" for the grade item "Item VU"
And I click away from student "Student 2" and grade item "Item VU" value
And I give the grade "30.00" to the user "Student 2" for the grade item "Item 1"
And I give the feedback "Some feedback" to the user "Student 2" for the grade item "Item 1"
And I click away from student "Student 2" and grade item "Item 1" feedback
And I give the grade "Very good" to the user "Student 2" for the grade item "Item SU"
And I click away from student "Student 2" and grade item "Item SU" value
And the grade for "Student 2" in grade item "Grade Cat" should match "53.00"
And the grade for "Student 2" in grade item "Course total" should match "53.00"
And I turn editing mode off
And the following should exist in the "user-grades" table:
| -1- | -4- | -5- | -9- | -12- | -13- |
| Student 2 | 30.00 | 20.00 | Very good | 53.00 | 53.00 |
And I click on student "Student 2" for grade item "Item 1"
And the field "ajaxfeedback" matches value "Some feedback"
@javascript
Scenario: Use the grader report with editing, with AJAX and quick feedback on, without category override
When the following config values are set as admin:
| grade_overridecat | 0 |
| grade_report_showquickfeedback | 1 |
And I log in as "teacher1"
And I follow "Course 1"
And I navigate to "Grades" node in "Course administration"
And I turn editing mode on
Then I should not see a grade field for "Student 2" and grade item "Course total"
And I should not see a feedback field for "Student 2" and grade item "Course total"
And I give the grade "20.00" to the user "Student 2" for the grade item "Item VU"
And I click away from student "Student 2" and grade item "Item VU" value
And I give the grade "30.00" to the user "Student 2" for the grade item "Item 1"
And I click away from student "Student 2" and grade item "Item 1" value
And I give the feedback "Some feedback" to the user "Student 2" for the grade item "Item 1"
And I click away from student "Student 2" and grade item "Item 1" feedback
And the following should exist in the "user-grades" table:
| -1- | -13- |
| Student 2 | 50.00 |
And I turn editing mode off
And the following should exist in the "user-grades" table:
| -1- | -4- | -5- | -13- |
| Student 2 | 30.00 | 20.00 | 50.00 |
And I click on student "Student 2" for grade item "Item 1"
And the field "ajaxfeedback" matches value "Some feedback"
@@ -0,0 +1,273 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Behat steps definitions for drag and drop onto image.
*
* @package gradereport_grader
* @category test
* @copyright 2015 Oakland University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
// NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php.
require_once(__DIR__ . '/../../../../../lib/behat/behat_base.php');
use Behat\Behat\Context\Step\Given,
Behat\Behat\Context\Step\Then,
Behat\Mink\Exception\ExpectationException as ExpectationException,
Behat\Mink\Exception\ElementNotFoundException as ElementNotFoundException;
/**
* Steps definitions related with the drag and drop onto image question type.
*
* @copyright 2015 Oakland University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class behat_gradereport_grader extends behat_base {
/**
* Click a given user grade cell.
*
* @Given /^I click on student "([^"]*)" for grade item "([^"]*)"$/
* @param string $student
* @param string $itemname
* @return Given
*/
public function i_click_on_student_and_grade_item($student, $itemname) {
$xpath = $this->get_student_and_grade_cell_selector($student, $itemname);
return new Given('I click on "' . $this->escape($xpath) . '" "xpath_element"');
}
/**
* Remove focus for a grade value cell.
*
* @Given /^I click away from student "([^"]*)" and grade item "([^"]*)" value$/
* @param string $student
* @param string $itemname
* @return Given
*/
public function i_click_away_from_student_and_grade_value($student, $itemname) {
$xpath = $this->get_student_and_grade_value_selector($student, $itemname);
return new Given('I take focus off "' . $this->escape($xpath) . '" "xpath_element"');
}
/**
* Remove focus for a grade value cell.
*
* @Given /^I click away from student "([^"]*)" and grade item "([^"]*)" feedback$/
* @param string $student
* @param string $itemname
* @return Given
*/
public function i_click_away_from_student_and_grade_feedback($student, $itemname) {
$xpath = $this->get_student_and_grade_feedback_selector($student, $itemname);
return new Given('I take focus off "' . $this->escape($xpath) . '" "xpath_element"');
}
/**
* Checks grade values with or without a edit box.
*
* @Then /^the grade for "([^"]*)" in grade item "([^"]*)" should match "([^"]*)"$/
* @throws Exception
* @throws ElementNotFoundException
* @param string $student
* @param string $itemname
* @param string $value
* @return Then
*/
public function the_grade_should_match($student, $itemname, $value) {
$xpath = $this->get_student_and_grade_value_selector($student, $itemname);
$gradefield = $this->getSession()->getPage()->find('xpath', $xpath);
if (!empty($gradefield)) {
// Get the field.
$fieldtype = behat_field_manager::guess_field_type($gradefield, $this->getSession());
if (!$fieldtype) {
throw new Exception('Could not get field type for grade field "' . $itemname . '"');
}
$field = behat_field_manager::get_field_instance($fieldtype, $gradefield, $this->getSession());
if (!$field->matches($value)) {
$fieldvalue = $field->get_value();
throw new ExpectationException(
'The "' . $student . '" and "' . $itemname . '" grade is "' . $fieldvalue . '", "' . $value . '" expected' ,
$this->getSession()
);
}
} else {
// If there isn't a form field, just search for contents.
$valueliteral = $this->getSession()->getSelectorsHandler()->xpathLiteral($value);
$xpath = $this->get_student_and_grade_cell_selector($student, $itemname);
$xpath .= "[contains(normalize-space(.)," . $valueliteral . ")]";
$node = $this->getSession()->getDriver()->find($xpath);
if (empty($node)) {
$locatorexceptionmsg = 'Cell for "' . $student . '" and "' . $itemname . '" with value "' . $value . '"';
throw new ElementNotFoundException($this->getSession(), $locatorexceptionmsg, null, $xpath);
}
}
}
/**
* Look for a grade editing field.
*
* @Then /^I should see a grade field for "([^"]*)" and grade item "([^"]*)"$/
* @param string $student
* @param string $itemname
* @return Then
*/
public function i_should_see_grade_field($student, $itemname) {
$xpath = $this->get_student_and_grade_value_selector($student, $itemname);
return new Then('"' . $this->escape($xpath) . '" "xpath_element" should be visible');
}
/**
* Look for a feedback editing field.
*
* @Then /^I should see a feedback field for "([^"]*)" and grade item "([^"]*)"$/
* @param string $student
* @param string $itemname
* @return Then
*/
public function i_should_see_feedback_field($student, $itemname) {
$xpath = $this->get_student_and_grade_feedback_selector($student, $itemname);
return new Then('"' . $this->escape($xpath) . '" "xpath_element" should be visible');
}
/**
* Look for a lack of the grade editing field.
*
* @Then /^I should not see a grade field for "([^"]*)" and grade item "([^"]*)"$/
* @param string $student
* @param string $itemname
* @return Then
*/
public function i_should_not_see_grade_field($student, $itemname) {
$xpath = $this->get_student_and_grade_value_selector($student, $itemname);
return new Then('"' . $this->escape($xpath) . '" "xpath_element" should not exist');
}
/**
* Look for a lack of the feedback editing field.
*
* @Then /^I should not see a feedback field for "([^"]*)" and grade item "([^"]*)"$/
* @param string $student
* @param string $itemname
* @return Then
*/
public function i_should_not_see_feedback_field($student, $itemname) {
$xpath = $this->get_student_and_grade_feedback_selector($student, $itemname);
return new Then('"' . $this->escape($xpath) . '" "xpath_element" should not exist');
}
/**
* Gets the user id from its name.
*
* @throws Exception
* @param string $name
* @return int
*/
protected function get_user_id($name) {
global $DB;
$names = explode(' ', $name);
if (!$id = $DB->get_field('user', 'id', array('firstname' => $names[0], 'lastname' => $names[1]))) {
throw new Exception('The specified user with username "' . $name . '" does not exist');
}
return $id;
}
/**
* Gets the grade item id from its name.
*
* @throws Exception
* @param string $itemname
* @return int
*/
protected function get_grade_item_id($itemname) {
global $DB;
if ($id = $DB->get_field('grade_items', 'id', array('itemname' => $itemname))) {
return $id;
}
// The course total is a special case.
if ($itemname === "Course total") {
if (!$id = $DB->get_field('grade_items', 'id', array('itemtype' => 'course'))) {
throw new Exception('The specified grade_item with name "' . $itemname . '" does not exist');
}
return $id;
}
// Find a category with the name.
if ($catid = $DB->get_field('grade_categories', 'id', array('fullname' => $itemname))) {
if ($id = $DB->get_field('grade_items', 'id', array('iteminstance' => $catid))) {
return $id;
}
}
throw new Exception('The specified grade_item with name "' . $itemname . '" does not exist');
}
/**
* Gets unique xpath selector for a student/grade item combo.
*
* @throws Exception
* @param string $student
* @param string $itemname
* @return string
*/
protected function get_student_and_grade_cell_selector($student, $itemname) {
$itemid = 'u' . $this->get_user_id($student) . 'i' . $this->get_grade_item_id($itemname);
return "//table[@id='user-grades']//td[@id='" . $itemid . "']";
}
/**
* Gets xpath for a particular student/grade item grade value cell.
*
* @throws Exception
* @param string $student
* @param string $itemname
* @return string
*/
protected function get_student_and_grade_value_selector($student, $itemname) {
$cell = $this->get_student_and_grade_cell_selector($student, $itemname);
return $cell . "//*[contains(@id, 'grade_') or @name='ajaxgrade']";
}
/**
* Gets xpath for a particular student/grade item feedback cell.
*
* @throws Exception
* @param string $student
* @param string $itemname
* @return string
*/
protected function get_student_and_grade_feedback_selector($student, $itemname) {
$cell = $this->get_student_and_grade_cell_selector($student, $itemname);
return $cell . "//input[contains(@id, 'feedback_') or @name='ajaxfeedback']";
}
}
+2 -2
View File
@@ -271,10 +271,10 @@ function grade_report_overview_settings_definition(&$mform) {
GRADE_REPORT_SHOW_TOTAL_IF_CONTAINS_HIDDEN => get_string('hidetotalshowexhiddenitems', 'grades'),
GRADE_REPORT_SHOW_REAL_TOTAL_IF_CONTAINS_HIDDEN => get_string('hidetotalshowinchiddenitems', 'grades') );
if (empty($CFG->grade_report_overview_showtotalsifcontainhidden)) {
if (!array_key_exists($CFG->grade_report_overview_showtotalsifcontainhidden, $options)) {
$options[-1] = get_string('defaultprev', 'grades', $options[0]);
} else {
$options[-1] = get_string('defaultprev', 'grades', $options[1]);
$options[-1] = get_string('defaultprev', 'grades', $options[$CFG->grade_report_overview_showtotalsifcontainhidden]);
}
$mform->addElement('select', 'report_overview_showtotalsifcontainhidden', get_string('hidetotalifhiddenitems', 'grades'), $options);
@@ -325,19 +325,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';
}
@@ -329,6 +329,13 @@ abstract class screen {
continue;
}
// If the user submits Exclude grade elements without the proper.
// permissions then we should refuse to update.
if ($matches[1] === 'exclude' && !has_capability('moodle/grade:manage', $this->context)){
$warnings[] = get_string('nopermissions', 'error', get_string('grade:manage', 'role'));
continue;
}
$msg = $element->set($posted);
// Optional type.
@@ -141,6 +141,12 @@ abstract class tablelike extends screen {
$html .= $this->structure->get_grade_analysis_icon($grade);
}
// Singleview users without proper permissions should be presented
// disabled checkboxes for the Exclude grade attribute.
if ($field == 'exclude' && !has_capability('moodle/grade:manage', $this->context)){
$html->disabled = true;
}
$line[] = $html;
}
return $line;
@@ -334,29 +334,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';
}
@@ -35,11 +35,14 @@ use grade_grade;
* @copyright 2014 Moodle Pty Ltd (http://moodle.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class exclude extends grade_attribute_format implements be_checked {
class exclude extends grade_attribute_format implements be_checked, be_disabled {
/** @var string $name The name of the input */
public $name = 'exclude';
/** @var bool $disabled Is the checkbox disabled? */
public $disabled = false;
/**
* Is it checked?
*
@@ -49,6 +52,15 @@ class exclude extends grade_attribute_format implements be_checked {
return $this->grade->is_excluded();
}
/**
* Is it disabled?
*
* @return bool
*/
public function is_disabled() {
return $this->disabled;
}
/**
* Generate the element used to render the UI
*
@@ -58,7 +70,8 @@ class exclude extends grade_attribute_format implements be_checked {
return new checkbox_attribute(
$this->get_name(),
$this->get_label(),
$this->is_checked()
$this->is_checked(),
$this->is_disabled()
);
}
@@ -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 {
@@ -11,6 +11,7 @@ Feature: We can use Single view
And the following "users" exist:
| username | firstname | lastname | email | idnumber | alternatename |
| teacher1 | Teacher | 1 | teacher1@example.com | t1 | fred |
| teacher2 | No edit | 1 | teacher2@example.com | t2 | nick |
| student1 | Student | 1 | student1@example.com | s1 | james |
| student2 | Student | 2 | student1@example.com | s2 | holly |
| student3 | Student | 3 | student1@example.com | s3 | anna |
@@ -27,6 +28,7 @@ Feature: We can use Single view
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| teacher2 | C1 | teacher |
| student1 | C1 | student |
| student2 | C1 | student |
| student3 | C1 | student |
@@ -44,6 +46,10 @@ Feature: We can use Single view
And the following "grade items" exist:
| itemname | course | gradetype |
| Test grade item | C1 | Scale |
And the following "permission overrides" exist:
| capability | permission | role | contextlevel | reference |
| moodle/grade:edit | Allow | teacher | Course | C1 |
| gradereport/singleview:view | Allow | teacher | Course | C1 |
And I log in as "teacher1"
And I follow "Course 1"
And I follow "Grades"
@@ -89,6 +95,14 @@ Feature: We can use Single view
And the following should exist in the "generaltable" table:
| First name (Alternate name) Surname | Grade |
| james (Student) 1 | Very good |
And I log out
And I log in as "teacher2"
And I follow "Course 1"
And I navigate to "Grades" node in "Course administration"
And I click on "Single view" "option"
And I click on "Student 4" "option"
And the "Exclude for Test assignment one" "checkbox" should be disabled
And the "Override for Test assignment one" "checkbox" should be enabled
Scenario: Single view links work on grade report.
Given I follow "Single view for Test assignment one"
@@ -105,7 +119,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 "Update"
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"
+14 -8
View File
@@ -164,13 +164,19 @@ if (has_capability('moodle/grade:viewall', $context)) { //Teachers will see all
}
}
$event = \gradereport_user\event\grade_report_viewed::create(
array(
'context' => $context,
'courseid' => $courseid,
'relateduserid' => $userid,
)
);
$event->trigger();
// Trigger report viewed event.
if (isset($report)) {
$event = \gradereport_user\event\grade_report_viewed::create(
array(
'context' => $context,
'courseid' => $courseid,
'relateduserid' => $userid,
)
);
$event->trigger();
} else {
echo html_writer::tag('div', '', array('class' => 'clearfix'));
echo $OUTPUT->notification(get_string('nostudentsyet'));
}
echo $OUTPUT->footer();
+1
View File
@@ -1,5 +1,6 @@
.path-grade-report-user #graded_users_selector {
float: right;
margin-bottom: 5px;
}
.path-grade-report-user #graded_users_selector .singleselect label {
display: inline-block;
@@ -0,0 +1,20 @@
@core @core_grades @gradereport_user
Feature: We can use the user report
As a user
I browse the User report
Background:
Given the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
Scenario: Verify we can view a user grade report with no users enrolled.
Given I log in as "admin"
And I am on site homepage
And I follow "Course 1"
And I navigate to "Grades" node in "Course administration"
And I select "User report" from the "Grade report" singleselect
And I press "Go"
And I select "All users (0)" from the "Select all or one user" singleselect
And I click on "Go" "button" in the "#choosegradeuser" "css_element"
Then I should see "No students enrolled in this course yet"
+17
View File
@@ -48,6 +48,23 @@ class behat_grade extends behat_base {
return new Given('I set the field "' . $this->escape($fieldstr) . '" to "' . $grade . '"');
}
/**
* Enters a quick feedback via the gradebook for a specific grade item and user when viewing
* the 'Grader report' with editing mode turned on.
*
* @Given /^I give the feedback "(?P<grade_number>(?:[^"]|\\")*)" to the user "(?P<username_string>(?:[^"]|\\")*)" for the grade item "(?P<grade_activity_string>(?:[^"]|\\")*)"$/
* @param string $feedback
* @param string $userfullname the user's fullname as returned by fullname()
* @param string $itemname
* @return Given
*/
public function i_give_the_feedback($feedback, $userfullname, $itemname) {
$gradelabel = $userfullname . ' ' . $itemname;
$fieldstr = get_string('useractivityfeedback', 'gradereport_grader', $gradelabel);
return new Given('I set the field "' . $this->escape($fieldstr) . '" to "' . $this->escape($feedback) . '"');
}
/**
* Changes the settings of a grade item or category or the course.
*
+1
View File
@@ -35,6 +35,7 @@ $string['cliansweryes'] = 'Да';
$string['cliincorrectvalueerror'] = 'Грешка, некоректна стойност "{$a->value}" за "{$a->option}"';
$string['cliincorrectvalueretry'] = 'Неправилна стойност. Моля опитайте отново';
$string['clitypevalue'] = 'Тип стойност';
$string['clitypevaluedefault'] = 'въведете стойност, натиснете Enter за да се използва стойността по подразбиране ({$a})';
$string['cliyesnoprompt'] = 'Въведете y (означава да) или n (означава не)';
$string['environmentrequireinstall'] = 'Трябва да бъде инсталиран и разрешен';
$string['environmentrequireversion'] = 'Необходима е версия {$a->needed} а Вие имате {$a->current}';
+2 -2
View File
@@ -31,6 +31,6 @@
defined('MOODLE_INTERNAL') || die();
$string['language'] = 'Език';
$string['next'] = 'Следваща';
$string['previous'] = 'Предишна';
$string['next'] = 'Още';
$string['previous'] = 'Обратно';
$string['reload'] = 'Презареждане';
+1 -1
View File
@@ -35,7 +35,7 @@ $string['cliansweryes'] = 'Sí';
$string['cliincorrectvalueerror'] = 'Error, valor incorrecte "{$a->value}" per a "{$a->option}"';
$string['cliincorrectvalueretry'] = 'Valor incorrecte, si us plau, torneu-ho a provar.';
$string['clitypevalue'] = 'Valor de tipus';
$string['clitypevaluedefault'] = 'valor de tipus, premeu Intro per fer servir un valor per defecte ({$a})';
$string['clitypevaluedefault'] = 'valor de tipus, premeu la tecla de retorn (<em>Enter</em>) per fer servir un valor per defecte ({$a})';
$string['cliunknowoption'] = 'Opcions invàlides:
{$a}
L\'opció --help us orientarà.';

Some files were not shown because too many files have changed in this diff Show More