Merge branch 'wip-mdl-52357' of https://github.com/rajeshtaneja/moodle
authorAndrew Nicols <andrew@nicols.co.uk>
Wed, 2 Dec 2015 03:48:05 +0000 (11:48 +0800)
committerEloy Lafuente (stronk7) <stronk7@moodle.org>
Thu, 3 Dec 2015 17:50:03 +0000 (18:50 +0100)
125 files changed:
admin/settings/plugins.php
admin/settings/subsystems.php
admin/tool/availabilityconditions/tests/behat/manage_conditions.feature
admin/tool/behat/tests/behat/data_generators.feature
auth/db/tests/db_test.php
availability/condition/completion/tests/behat/availability_completion.feature
availability/condition/completion/tests/behat/conditional_bug.feature
availability/condition/date/tests/behat/availability_date.feature
availability/condition/grade/tests/behat/availability_grade.feature
availability/condition/group/tests/behat/availability_group.feature
availability/condition/grouping/tests/behat/availability_grouping.feature
availability/condition/profile/tests/behat/availability_profile.feature
availability/tests/behat/display_availability.feature
availability/tests/behat/edit_availability.feature
availability/tests/info_test.php
backup/util/helper/restore_log_rule.class.php
badges/tests/behat/award_badge.feature
blocks/glossary_random/block_glossary_random.php
blocks/online_users/tests/generator/lib.php
blocks/recent_activity/tests/behat/structural_changes.feature
blog/edit.php
blog/edit_form.php
blog/index.php
blog/tests/behat/delete.feature [new file with mode: 0644]
completion/tests/behat/enable_manual_complete_mark.feature
completion/tests/behat/restrict_activity_by_date.feature
completion/tests/behat/restrict_activity_by_grade.feature
completion/tests/behat/restrict_section_availability.feature
completion/tests/behat/teacher_manual_completion.feature
course/edit_form.php
course/lib.php
course/tests/behat/activities_edit_completion.feature
course/tests/behat/role_renaming.feature [new file with mode: 0644]
course/tests/courselib_test.php
course/tests/externallib_test.php
enrol/externallib.php
enrol/upgrade.txt
grade/report/singleview/classes/local/ui/dropdown_attribute.php
grade/report/singleview/classes/local/ui/text_attribute.php
grade/report/singleview/js/singleview.js
lang/en/admin.php
lang/en/blog.php
lang/en/cache.php
lang/en/edufields.php
lang/en/grades.php
lang/en/hub.php
lang/en/moodle.php
lang/en/plagiarism.php
lang/en/repository.php
lang/en/role.php
lib/accesslib.php
lib/amd/build/form-autocomplete.min.js
lib/amd/src/form-autocomplete.js
lib/classes/event/course_section_deleted.php [new file with mode: 0644]
lib/classes/plugininfo/calendartype.php
lib/db/access.php
lib/db/services.php
lib/editor/atto/plugins/rtl/yui/build/moodle-atto_rtl-button/moodle-atto_rtl-button-debug.js
lib/editor/atto/plugins/rtl/yui/build/moodle-atto_rtl-button/moodle-atto_rtl-button-min.js
lib/editor/atto/plugins/rtl/yui/build/moodle-atto_rtl-button/moodle-atto_rtl-button.js
lib/editor/atto/plugins/rtl/yui/src/button/js/button.js
lib/externallib.php
lib/filestorage/file_storage.php
lib/filestorage/stored_file.php
lib/filestorage/tests/file_storage_test.php
lib/filestorage/tests/fixtures/testimage.png [new file with mode: 0644]
lib/filterlib.php
lib/navigationlib.php
lib/password_compat/readme_moodle.txt
lib/password_compat/tests/PasswordHashTest.php
lib/testing/generator/block_generator.php
lib/testing/generator/data_generator.php
lib/tests/behat/behat_data_generators.php
lib/tests/externallib_test.php
lib/tests/weblib_test.php
lib/upgrade.txt
lib/weblib.php
message/externallib.php
message/tests/externallib_test.php
mod/assign/extensionform.php
mod/assign/locallib.php
mod/assign/submission/onlinetext/locallib.php
mod/assign/tests/behat/grant_extension.feature
mod/book/classes/external.php
mod/chat/classes/external.php
mod/choice/classes/external.php
mod/feedback/tests/behat/show_nonrespondents.feature
mod/forum/externallib.php
mod/forum/tests/behat/completion_condition_number_discussions.feature
mod/forum/tests/behat/forum_subscriptions_availability.feature
mod/imscp/classes/external.php
mod/lesson/tests/behat/completion_condition_end_reached.feature
mod/lesson/tests/behat/completion_condition_time_spent.feature
mod/quiz/tests/behat/completion_condition_attempts_used.feature
mod/quiz/tests/behat/completion_condition_passing_grade.feature
mod/scorm/classes/external.php
mod/scorm/datamodels/scorm_13lib.php
mod/scorm/datamodels/sequencinglib.php
question/type/ddimageortext/yui/build/moodle-qtype_ddimageortext-form/moodle-qtype_ddimageortext-form-debug.js
question/type/ddimageortext/yui/build/moodle-qtype_ddimageortext-form/moodle-qtype_ddimageortext-form-min.js
question/type/ddimageortext/yui/build/moodle-qtype_ddimageortext-form/moodle-qtype_ddimageortext-form.js
question/type/ddimageortext/yui/src/form/js/form.js
report/outline/index.php
report/outline/lang/en/report_outline.php
report/outline/tests/behat/outline.feature
repository/filesystem/lib.php
repository/lib.php
theme/clean/classes/core_renderer.php
theme/clean/lang/en/theme_clean.php
theme/clean/layout/columns1.php
theme/clean/layout/columns2.php
theme/clean/layout/columns3.php
theme/clean/layout/secure.php
theme/clean/lib.php
theme/clean/settings.php
theme/clean/style/custom.css
theme/clean/version.php
theme/more/lang/en/theme_more.php
theme/more/lib.php
theme/more/settings.php
theme/more/style/custom.css
theme/more/version.php
theme/upgrade.txt
user/edit_form.php
version.php

index f5ea9e3..9ed5af8 100644 (file)
@@ -489,10 +489,9 @@ if ($hassiteconfig) {
 // Add Calendar type settings.
 if ($hassiteconfig) {
     $ADMIN->add('modules', new admin_category('calendartype', new lang_string('calendartypes', 'calendar')));
-    foreach (core_component::get_plugin_list_with_file('calendartype', 'settings.php') as $plugin => $settingspath) {
-        $settings = new admin_settingpage('calendartype_' . $plugin . '_settings', new lang_string('pluginname', 'calendartype_' . $plugin), 'moodle/site:config');
-        include($settingspath);
-        $ADMIN->add('calendartype', $settings);
+    foreach (core_plugin_manager::instance()->get_plugins_of_type('calendartype') as $plugin) {
+        /** @var \core\plugininfo\calendartype $plugin */
+        $plugin->load_settings($ADMIN, 'calendartype', $hassiteconfig);
     }
 }
 
index 1eae0d0..9cae26c 100644 (file)
@@ -34,7 +34,7 @@ if ($hassiteconfig) { // speedup for non-admins, add all caps used on this page
     // Conditional activities: completion and availability
     $optionalsubsystems->add(new admin_setting_configcheckbox('enablecompletion',
         new lang_string('enablecompletion','completion'),
-        new lang_string('configenablecompletion','completion'), 0));
+        new lang_string('configenablecompletion', 'completion'), 1));
 
     $options = array(
         1 => get_string('completionactivitydefault', 'completion'),
@@ -45,7 +45,7 @@ if ($hassiteconfig) { // speedup for non-admins, add all caps used on this page
 
     $optionalsubsystems->add($checkbox = new admin_setting_configcheckbox('enableavailability',
             new lang_string('enableavailability', 'availability'),
-            new lang_string('enableavailability_desc', 'availability'), 0));
+            new lang_string('enableavailability_desc', 'availability'), 1));
     $checkbox->set_affects_modinfo(true);
 
     $optionalsubsystems->add(new admin_setting_configcheckbox('enableplagiarism', new lang_string('enableplagiarism','plagiarism'), new lang_string('configenableplagiarism','plagiarism'), 0));
index 9234fee..65b0b4a 100644 (file)
@@ -8,6 +8,8 @@ Feature: Manage availability conditions
   Scenario: Display list of availability conditions
     # Check the report doesn't show when not enabled.
     Given I log in as "admin"
+    And the following config values are set as admin:
+      | enableavailability | 0 |
     And I expand "Site administration" node
     When I expand "Plugins" node
     Then I should not see "Availability restrictions"
@@ -28,8 +30,6 @@ Feature: Manage availability conditions
     Given the following "courses" exist:
       | fullname | shortname | format |
       | Course 1 | C1        | topics |
-    And the following config values are set as admin:
-      | enableavailability | 1 |
     And I log in as "admin"
     And I am on site homepage
     When I navigate to "Manage restrictions" node in "Site administration > Plugins > Availability restrictions"
index 9853ea5..0f3e3af 100644 (file)
@@ -424,9 +424,9 @@ Feature: Set up contextual data for tests
     And the following "grade categories" exist:
       | fullname         | course |
       | Grade category 1 | C1     |
-     And the following "grade items" exist:
-       | itemname                  | course | outcome | gradecategory    |
-       | Test Outcome Grade Item 1 | C1     | OT1     | Grade category 1 |
+    And the following "grade items" exist:
+      | itemname                  | course | outcome | gradecategory    |
+      | Test Outcome Grade Item 1 | C1     | OT1     | Grade category 1 |
     And the following config values are set as admin:
       | enableoutcomes | 1 |
     When I log in as "admin"
@@ -441,3 +441,15 @@ Feature: Set up contextual data for tests
     And I expand all fieldsets
     And "//div[contains(@class, 'fitem')]/div[contains(@class, 'fitemtitle')]/div[contains(@class, fstaticlabel) and contains(., 'Grade category')]/../../div[contains(@class, 'felement') and contains(., 'Grade category 1')]" "xpath_element" should exist
     And I press "Cancel"
+
+  Scenario: Add a block
+    Given the following "courses" exist:
+      | fullname | shortname |
+      | Course 1 | C1        |
+    And the following "blocks" exist:
+      | blockname    | contextlevel | reference | pagetypepattern | defaultregion |
+      | online_users | Course       | C1        | course-view-*   | site-pre      |
+    When I log in as "admin"
+    And I am on site homepage
+    And I follow "Course 1"
+    Then I should see "Online users"
index e619df6..7979bd8 100644 (file)
@@ -309,7 +309,7 @@ class auth_db_testcase extends advanced_testcase {
         require_once($CFG->libdir.'/password_compat/lib/password.php');
         set_config('passtype', 'saltedcrypt', 'auth/db');
         $auth->config->passtype = 'saltedcrypt';
-        $user3->pass = password_hash('heslo', PASSWORD_BCRYPT, array('salt' => 'best_salt_ever_moodle_rocks_dont_tell'));
+        $user3->pass = password_hash('heslo', PASSWORD_BCRYPT);
         $DB->update_record('auth_db_users', $user3);
         $this->assertTrue($auth->user_login('u3', 'heslo'));
 
index 3d95470..507ba1b 100644 (file)
@@ -16,9 +16,6 @@ Feature: availability_completion
       | user     | course | role           |
       | teacher1 | C1     | editingteacher |
       | student1 | C1     | student        |
-    And the following config values are set as admin:
-      | enableavailability  | 1 |
-      | enablecompletion | 1 |
 
   @javascript
   Scenario: Test condition
index b7f6fec..402afdd 100644 (file)
@@ -14,9 +14,6 @@ Feature: Confirm that conditions on completion no longer cause a bug
     And the following "course enrolments" exist:
       | user     | course | role           |
       | teacher1 | C1     | editingteacher |
-    And the following config values are set as admin:
-      | enableavailability | 1 |
-      | enablecompletion   | 1 |
 
   @javascript
   Scenario: Multiple completion conditions on glossary
index 925781d..161080f 100644 (file)
@@ -16,8 +16,6 @@ Feature: availability_date
       | user     | course | role           |
       | teacher1 | C1     | editingteacher |
       | student1 | C1     | student        |
-    And the following config values are set as admin:
-      | enableavailability  | 1 |
 
   @javascript
   Scenario: Test condition
index 967e7c8..9c250e1 100644 (file)
@@ -16,8 +16,6 @@ Feature: availability_grade
       | user     | course | role           |
       | teacher1 | C1     | editingteacher |
       | student1 | C1     | student        |
-    And the following config values are set as admin:
-      | enableavailability  | 1 |
 
   @javascript
   Scenario: Test condition
index d3c2b06..31d65d2 100644 (file)
@@ -16,8 +16,6 @@ Feature: availability_group
       | user     | course | role           |
       | teacher1 | C1     | editingteacher |
       | student1 | C1     | student        |
-    And the following config values are set as admin:
-      | enableavailability  | 1 |
 
   @javascript
   Scenario: Test condition
index e33b588..3c1ccb1 100644 (file)
@@ -22,8 +22,6 @@ Feature: availability_grouping
     And the following "group members" exist:
       | user     | group |
       | student1 | GI1   |
-    And the following config values are set as admin:
-      | enableavailability  | 1 |
 
   @javascript
   Scenario: Test condition
index ba207a1..2222fb7 100644 (file)
@@ -16,8 +16,6 @@ Feature: availability_profile
       | user     | course | role           |
       | teacher1 | C1     | editingteacher |
       | student1 | C1     | student        |
-    And the following config values are set as admin:
-      | enableavailability  | 1 |
 
   @javascript
   Scenario: Test condition
index ea70a05..0daeee2 100644 (file)
@@ -34,8 +34,6 @@ Feature: display_availability
       | user     | course | role           |
       | teacher1 | C1     | editingteacher |
       | student1 | C1     | student        |
-    And the following config values are set as admin:
-      | enableavailability | 1 |
 
   @javascript
   Scenario: Activity availability display
index 0af20b5..08a6f70 100644 (file)
@@ -29,6 +29,8 @@ Feature: edit_availability
       | student1 | C1     | student        |
 
   Scenario: Confirm the 'enable availability' option is working
+    Given the following config values are set as admin:
+      | enableavailability | 0 |
     When I log in as "teacher1"
     And I am on site homepage
     And I follow "Course 1"
@@ -55,9 +57,7 @@ Feature: edit_availability
   @javascript
   Scenario: Edit availability using settings in activity form
     # Set up.
-    Given the following config values are set as admin:
-      | enableavailability | 1 |
-    And I log in as "teacher1"
+    Given I log in as "teacher1"
     And I follow "Course 1"
 
     # Add a Page and check it has None in so far.
@@ -148,9 +148,7 @@ Feature: edit_availability
   @javascript
   Scenario: Edit availability using settings in section form
     # Set up.
-    Given the following config values are set as admin:
-      | enableavailability | 1 |
-    And I log in as "teacher1"
+    Given I log in as "teacher1"
     And I am on site homepage
     And I follow "Course 1"
     And I turn editing mode on
@@ -170,7 +168,9 @@ Feature: edit_availability
   @javascript
   Scenario: 'Add group/grouping access restriction' button unavailable
     # Button does not exist when conditional access restrictions are turned off.
-    Given I log in as "admin"
+    Given the following config values are set as admin:
+      | enableavailability | 0 |
+    And I log in as "admin"
     And I am on site homepage
     And I follow "Course 1"
     And I turn editing mode on
@@ -181,9 +181,7 @@ Feature: edit_availability
   @javascript
   Scenario: Use the 'Add group/grouping access restriction' button
     # Button should initially be disabled.
-    Given the following config values are set as admin:
-      | enableavailability | 1 |
-    And the following "groupings" exist:
+    Given the following "groupings" exist:
       | name | course | idnumber |
       | GX1  | C1     | GXI1     |
     And I log in as "admin"
index 1477978..d349b9c 100644 (file)
@@ -45,9 +45,10 @@ class info_testcase extends advanced_testcase {
      * Tests the info_module class (is_available, get_full_information).
      */
     public function test_info_module() {
-        global $DB;
+        global $DB, $CFG;
 
         // Create a course and pages.
+        $CFG->enableavailability = 0;
         $this->setAdminUser();
         $this->resetAfterTest();
         $generator = $this->getDataGenerator();
@@ -160,6 +161,7 @@ class info_testcase extends advanced_testcase {
         global $CFG, $DB;
         require_once($CFG->dirroot . '/course/lib.php');
         $this->resetAfterTest();
+        $CFG->enableavailability = 0;
 
         // Create a course and some pages:
         // 0. Invisible due to visible=0.
index bb7d4e3..6e182c0 100644 (file)
@@ -97,7 +97,10 @@ class restore_log_rule implements processable {
         return $this->module . '-' . $this->action;
     }
 
-    public function process($log) {
+    public function process($inputlog) {
+
+        // There might be multiple rules that process this log, we can't alter it in the process of checking it.
+        $log = clone($inputlog);
 
         // Reset the allpairs array
         $this->allpairs = array();
index 4bb0a66..0b68bd7 100644 (file)
@@ -129,8 +129,6 @@ Feature: Award badges
       | user | course | role |
       | teacher1 | C1 | editingteacher |
       | student1 | C1 | student |
-    And the following config values are set as admin:
-      | enablecompletion | 1 |
     And I log in as "teacher1"
     And I follow "Course 1"
     And I follow "Edit settings"
@@ -180,8 +178,6 @@ Feature: Award badges
       | user | course | role |
       | teacher1 | C1 | editingteacher |
       | student1 | C1 | student |
-    And the following config values are set as admin:
-      | enablecompletion | 1 |
     And I log in as "teacher1"
     And I follow "Course 1"
     And I follow "Edit settings"
index a16d200..6ca3bb5 100644 (file)
@@ -84,14 +84,13 @@ class block_glossary_random extends block_base {
             $limitfrom = 0;
             $limitnum = 1;
 
-            $BROWSE = 'timemodified';
+            $orderby = 'timemodified ASC';
 
             switch ($this->config->type) {
 
                 case BGR_RANDOMLY:
                     $i = rand(1,$numberofentries);
                     $limitfrom = $i-1;
-                    $SORT = 'ASC';
                     break;
 
                 case BGR_NEXTONE:
@@ -104,11 +103,10 @@ class block_glossary_random extends block_base {
                         $i = 1;
                     }
                     $limitfrom = $i-1;
-                    $SORT = 'ASC';
                     break;
 
                 case BGR_NEXTALPHA:
-                    $BROWSE = 'concept';
+                    $orderby = 'concept ASC';
                     if (isset($this->config->previous)) {
                         $i = $this->config->previous + 1;
                     } else {
@@ -118,20 +116,19 @@ class block_glossary_random extends block_base {
                         $i = 1;
                     }
                     $limitfrom = $i-1;
-                    $SORT = 'ASC';
                     break;
 
                 default:  // BGR_LASTMODIFIED
                     $i = $numberofentries;
                     $limitfrom = 0;
-                    $SORT = 'DESC';
+                    $orderby = 'timemodified DESC, id DESC';
                     break;
             }
 
             if ($entry = $DB->get_records_sql("SELECT id, concept, definition, definitionformat, definitiontrust
                                                  FROM {glossary_entries}
                                                 WHERE glossaryid = ? AND approved = 1
-                                             ORDER BY $BROWSE $SORT", array($this->config->glossary), $limitfrom, $limitnum)) {
+                                             ORDER BY $orderby", array($this->config->glossary), $limitfrom, $limitnum)) {
 
                 $entry = reset($entry);
 
index a560fd5..6b721a9 100644 (file)
@@ -36,31 +36,6 @@ defined('MOODLE_INTERNAL') || die();
  */
 class block_online_users_generator extends testing_block_generator {
 
-    /**
-     * Create new block instance
-     * @param array|stdClass $record
-     * @param array $options
-     * @return stdClass activity record with extra cmid field
-     */
-    public function create_instance($record = null, array $options = null) {
-        global $DB, $CFG;
-        require_once("$CFG->dirroot/mod/page/locallib.php");
-
-        $this->instancecount++;
-
-        $record = (object)(array)$record;
-        $options = (array)$options;
-
-        $record = $this->prepare_record($record);
-
-        $id = $DB->insert_record('block_instances', $record);
-        context_block::instance($id);
-
-        $instance = $DB->get_record('block_instances', array('id'=>$id), '*', MUST_EXIST);
-
-        return $instance;
-    }
-
     /**
      * Create (simulated) logged in users and add some of them to groups in a course
      */
index 4340c60..a43dbb7 100644 (file)
@@ -46,9 +46,7 @@ Feature: View structural changes in recent activity block
       | GG3      | G2    |
 
   Scenario: Check that Added module information is displayed respecting view capability
-    Given the following config values are set as admin:
-      | enableavailability | 1 |
-    And I log in as "teacher1"
+    Given I log in as "teacher1"
     And I follow "Course 1"
     And I turn editing mode on
     When I add a "Forum" to section "1" and I fill the form with:
index 7ff8333..14d4c87 100644 (file)
@@ -141,14 +141,15 @@ if ($action === 'delete') {
         // Output edit mode title.
         echo $OUTPUT->heading($strblogs . ': ' . get_string('deleteentry', 'blog'), 2);
 
+        echo $OUTPUT->confirm(get_string('blogdeleteconfirm', 'blog', format_string($entry->subject)),
+                              new moodle_url('edit.php', $optionsyes),
+                              new moodle_url('index.php', $optionsno));
+
+        echo '<br />';
         // Output the entry.
         $entry->prepare_render();
         echo $output->render($entry);
 
-        echo '<br />';
-        echo $OUTPUT->confirm(get_string('blogdeleteconfirm', 'blog'),
-                              new moodle_url('edit.php', $optionsyes),
-                              new moodle_url('index.php', $optionsno));
         echo $OUTPUT->footer();
         die;
     }
index 805bf79..773f153 100644 (file)
@@ -151,7 +151,7 @@ class blog_edit_form extends moodleform {
     public function validation($data, $files) {
         global $CFG, $DB, $USER;
 
-        $errors = array();
+        $errors = parent::validation($data, $files);
 
         // Validate course association.
         if (!empty($data['courseassoc'])) {
index ddbdf45..56ab7eb 100644 (file)
@@ -63,9 +63,9 @@ if ($entryid and !isset($userid)) {
     $userid = $entry->userid;
 }
 
-if (isset($userid) && !isset($courseid)) {
+if (isset($userid) && empty($courseid)) {
     $context = context_user::instance($userid);
-} else if (isset($courseid) && $courseid != SITEID) {
+} else if (!empty($courseid) && $courseid != SITEID) {
     $context = context_course::instance($courseid);
 } else {
     $context = context_system::instance();
diff --git a/blog/tests/behat/delete.feature b/blog/tests/behat/delete.feature
new file mode 100644 (file)
index 0000000..cd1dd5b
--- /dev/null
@@ -0,0 +1,43 @@
+@core @core_blog
+Feature: Delete a blog entry
+  In order to manage my blog entries
+  As a user
+  I need to be able to delete entries I no longer wish to appear
+
+  Background:
+    Given the following "users" exist:
+      | username | firstname | lastname | email |
+      | testuser | Test | User | moodle@example.com |
+    And I log in as "testuser"
+    And I expand "Site pages" node
+    And I follow "Site blogs"
+    And I follow "Add a new entry"
+    And I set the following fields to these values:
+      | Entry title | Blog post one |
+      | Blog entry body | User 1 blog post content |
+    And I press "Save changes"
+    And I follow "Add a new entry"
+    And I set the following fields to these values:
+      | Entry title | Blog post two |
+      | Blog entry body | User 1 blog post content |
+    And I press "Save changes"
+    And I am on site homepage
+    And I expand "Site pages" node
+    And I follow "Site blogs"
+
+  Scenario: Delete blog post results in post deleted
+    Given I follow "Blog post one"
+    And I follow "Delete"
+    And I should see "Delete the blog entry 'Blog post one'?"
+    When I press "Continue"
+    Then I should not see "Blog post one"
+    And I should see "Blog post two"
+
+  Scenario: Delete confirmation screen works and allows cancel
+    Given I follow "Blog post one"
+    When I follow "Delete"
+    Then I should see "Delete the blog entry 'Blog post one'?"
+    And I press "Cancel"
+    And I should see "Blog post one"
+    And I should see "Blog post two"
+
index 93ba976..75a11a2 100644 (file)
@@ -17,9 +17,6 @@ Feature: Allow students to manually mark an activity as complete
       | user | course | role |
       | teacher1 | C1 | editingteacher |
       | student1 | C1 | student |
-    And the following config values are set as admin:
-      | enablecompletion | 1 |
-      | enableavailability | 1 |
     And I log in as "teacher1"
     And I am on site homepage
     And I follow "Course 1"
index fc3033d..5d19955 100644 (file)
@@ -16,8 +16,6 @@ Feature: Restrict activity availability through date conditions
       | user | course | role |
       | teacher1 | C1 | editingteacher |
       | student1 | C1 | student |
-    And the following config values are set as admin:
-      | enableavailability | 1 |
     And I log in as "teacher1"
     And I am on site homepage
     And I follow "Course 1"
index 3b0a60f..27fa423 100644 (file)
@@ -17,8 +17,6 @@ Feature: Restrict activity availability through grade conditions
       | user | course | role |
       | teacher1 | C1 | editingteacher |
       | student1 | C1 | student |
-    And the following config values are set as admin:
-      | enableavailability | 1 |
     And I log in as "teacher1"
     #And I am on site homepage
     And I follow "Course 1"
index 436c63d..3a74723 100644 (file)
@@ -16,9 +16,6 @@ Feature: Restrict sections availability through completion or grade conditions
       | user | course | role |
       | teacher1 | C1 | editingteacher |
       | student1 | C1 | student |
-    And the following config values are set as admin:
-      | enablecompletion   | 1 |
-      | enableavailability | 1 |
 
   @javascript
   Scenario: Show section greyed-out to student when completion condition is not satisfied
index 3b8c4d4..c1ce408 100644 (file)
@@ -16,11 +16,7 @@ Feature: Allow teachers to manually mark users as complete when configured
       | user     | course | role           |
       | student1 | CC1    | student        |
       | teacher1 | CC1    | editingteacher |
-    And the following config values are set as admin:
-      | enablecompletion | 1 |
     And I log in as "admin"
-    And I set the following administration settings values:
-      | Enable completion tracking | 1 |
     And I am on site homepage
     And I follow "Completion course"
     And completion tracking is "Enabled" in current course
index 6e0ed50..94d260c 100644 (file)
@@ -288,16 +288,19 @@ class course_edit_form extends moodleform {
         $options[0] = get_string('none');
         $mform->addElement('select', 'defaultgroupingid', get_string('defaultgrouping', 'group'), $options);
 
-        // Customizable role names in this course.
-        $mform->addElement('header','rolerenaming', get_string('rolerenaming'));
-        $mform->addHelpButton('rolerenaming', 'rolerenaming');
-
-        if ($roles = get_all_roles()) {
-            $roles = role_fix_names($roles, null, ROLENAME_ORIGINAL);
-            $assignableroles = get_roles_for_contextlevels(CONTEXT_COURSE);
-            foreach ($roles as $role) {
-                $mform->addElement('text', 'role_'.$role->id, get_string('yourwordforx', '', $role->localname));
-                $mform->setType('role_'.$role->id, PARAM_TEXT);
+        if ((empty($course->id) && guess_if_creator_will_have_course_capability('moodle/course:renameroles', $categorycontext))
+                || (!empty($course->id) && has_capability('moodle/course:renameroles', $coursecontext))) {
+            // Customizable role names in this course.
+            $mform->addElement('header', 'rolerenaming', get_string('rolerenaming'));
+            $mform->addHelpButton('rolerenaming', 'rolerenaming');
+
+            if ($roles = get_all_roles()) {
+                $roles = role_fix_names($roles, null, ROLENAME_ORIGINAL);
+                $assignableroles = get_roles_for_contextlevels(CONTEXT_COURSE);
+                foreach ($roles as $role) {
+                    $mform->addElement('text', 'role_' . $role->id, get_string('yourwordforx', '', $role->localname));
+                    $mform->setType('role_' . $role->id, PARAM_TEXT);
+                }
             }
         }
 
index ba830ca..bee6132 100644 (file)
@@ -1842,7 +1842,40 @@ function move_section_to($course, $section, $destination, $ignorenumsections = f
  * @return bool whether section was deleted
  */
 function course_delete_section($course, $section, $forcedeleteifnotempty = true) {
-    return course_get_format($course)->delete_section($section, $forcedeleteifnotempty);
+    global $DB;
+
+    // Prepare variables.
+    $courseid = (is_object($course)) ? $course->id : (int)$course;
+    $sectionnum = (is_object($section)) ? $section->section : (int)$section;
+    $section = $DB->get_record('course_sections', array('course' => $courseid, 'section' => $sectionnum));
+    if (!$section) {
+        // No section exists, can't proceed.
+        return false;
+    }
+    $format = course_get_format($course);
+    $sectionname = $format->get_section_name($section);
+
+    // Delete section.
+    $result = $format->delete_section($section, $forcedeleteifnotempty);
+
+    // Trigger an event for course section deletion.
+    if ($result) {
+        $context = context_course::instance($courseid);
+        $event = \core\event\course_section_deleted::create(
+                array(
+                    'objectid' => $section->id,
+                    'courseid' => $courseid,
+                    'context' => $context,
+                    'other' => array(
+                        'sectionnum' => $section->section,
+                        'sectionname' => $sectionname,
+                    )
+                )
+            );
+        $event->add_record_snapshot('course_sections', $section);
+        $event->trigger();
+    }
+    return $result;
 }
 
 /**
index b264ea4..50878cc 100644 (file)
@@ -8,8 +8,6 @@ Feature: Edit completion settings of an activity
     Given the following "courses" exist:
       | fullname | shortname | enablecompletion |
       | Course 1 | C1        | 1                |
-    And the following config values are set as admin:
-      | enablecompletion | 1 |
     And I log in as "admin"
     And I am on site homepage
     And I follow "Course 1"
diff --git a/course/tests/behat/role_renaming.feature b/course/tests/behat/role_renaming.feature
new file mode 100644 (file)
index 0000000..7384c4c
--- /dev/null
@@ -0,0 +1,43 @@
+@core @core_course
+Feature: Rename roles in a course
+  In order to account for course-level differences
+  As a teacher
+  I need to be able to rename roles
+
+  Background:
+    Given the following "users" exist:
+      | username | firstname | lastname | email                |
+      | student1 | Student   | 1        | student1@example.com |
+      | teacher1 | Teacher   | 1        | teacher1@example.com |
+    And the following "courses" exist:
+      | fullname | shortname |
+      | Course 1 | C1        |
+    And the following "course enrolments" exist:
+      | user     | course | role           |
+      | student1 | C1     | student        |
+      | teacher1 | C1     | editingteacher |
+
+  Scenario: Teacher can rename roles
+    Given I log in as "teacher1"
+    And I follow "Course 1"
+    And I click on "Edit settings" "link" in the "Administration" "block"
+    And I should see "Role renaming"
+    When I set the following fields to these values:
+      | Your word for 'Teacher' | Lecturer |
+      | Your word for 'Student' | Learner  |
+    And I press "Save and display"
+    And I navigate to "Enrolled users" node in "Course administration > Users"
+    Then I should see "Lecturer" in the "Teacher 1" "table_row"
+    And I should see "Learner" in the "Student 1" "table_row"
+
+  Scenario: Ability to rename roles can be prevented
+    Given I log in as "admin"
+    And I set the following system permissions of "Teacher" role:
+      | capability         | permission |
+      | moodle/course:renameroles | Inherit |
+    And I follow "Log out"
+    When I log in as "teacher1"
+    And I follow "Course 1"
+    And I click on "Edit settings" "link" in the "Administration" "block"
+    Then I should not see "Role renaming"
+    And I should not see "Your word for 'Teacher'"
index 110b0c4..421b1e4 100644 (file)
@@ -2009,6 +2009,44 @@ class core_course_courselib_testcase extends advanced_testcase {
         $this->assertEventContextNotUsed($event);
     }
 
+    /**
+     * Test that triggering a course_section_deleted event works as expected.
+     */
+    public function test_course_section_deleted_event() {
+        global $USER, $DB;
+        $this->resetAfterTest();
+        $sink = $this->redirectEvents();
+
+        // Create the course with sections.
+        $course = $this->getDataGenerator()->create_course(array('numsections' => 10), array('createsections' => true));
+        $sections = $DB->get_records('course_sections', array('course' => $course->id));
+        $coursecontext = context_course::instance($course->id);
+        $section = array_pop($sections);
+        course_delete_section($course, $section);
+        $events = $sink->get_events();
+        $event = array_pop($events); // Delete section event.
+        $sink->close();
+
+        // Validate event data.
+        $this->assertInstanceOf('\core\event\course_section_deleted', $event);
+        $this->assertEquals('course_sections', $event->objecttable);
+        $this->assertEquals($section->id, $event->objectid);
+        $this->assertEquals($course->id, $event->courseid);
+        $this->assertEquals($coursecontext->id, $event->contextid);
+        $this->assertEquals($section->section, $event->other['sectionnum']);
+        $expecteddesc = "The user with id '{$event->userid}' deleted section number '{$event->other['sectionnum']}' " .
+                "(section name '{$event->other['sectionname']}') for the course with id '{$event->courseid}'";
+        $this->assertEquals($expecteddesc, $event->get_description());
+        $this->assertEquals($section, $event->get_record_snapshot('course_sections', $event->objectid));
+        $this->assertNull($event->get_url());
+
+        // Test legacy data.
+        $sectionnum = $section->section;
+        $expectedlegacydata = array($course->id, "course", "delete section", 'view.php?id=' . $course->id, $sectionnum);
+        $this->assertEventLegacyLogData($expectedlegacydata, $event);
+        $this->assertEventContextNotUsed($event);
+    }
+
     public function test_course_integrity_check() {
         global $DB;
 
index e443b33..c5a6464 100644 (file)
@@ -1041,9 +1041,7 @@ class core_course_externallib_testcase extends externallib_advanced_testcase {
                     $this->assertEquals($course2['forcetheme'], $courseinfo->theme);
                 }
 
-                if (completion_info::is_enabled_for_site()) {
-                    $this->assertEquals($course2['enabledcompletion'], $courseinfo->enablecompletion);
-                }
+                $this->assertEquals($course2['enablecompletion'], $courseinfo->enablecompletion);
             } else if ($course['id'] == $course1['id']) {
                 $this->assertEquals($course1['fullname'], $courseinfo->fullname);
                 $this->assertEquals($course1['shortname'], $courseinfo->shortname);
index 7e64f21..331ee3d 100644 (file)
@@ -385,7 +385,10 @@ class core_enrol_external extends external_api {
                             * 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.
-                            * limitnumber (integer) maximum number of returned users.', VALUE_DEFAULT, array()),
+                            * limitnumber (integer) maximum number of returned users.
+                            * sortby (string) sort by id, firstname or lastname. For ordering like the site does, use siteorder.
+                            * sortdirection (string) ASC or DESC',
+                            VALUE_DEFAULT, array()),
             )
         );
     }
@@ -417,6 +420,9 @@ class core_enrol_external extends external_api {
         $userfields     = array();
         $limitfrom = 0;
         $limitnumber = 0;
+        $sortby = 'us.id';
+        $sortparams = array();
+        $sortdirection = 'ASC';
         foreach ($options as $option) {
             switch ($option['name']) {
             case 'withcapability':
@@ -440,6 +446,26 @@ class core_enrol_external extends external_api {
             case 'limitnumber' :
                 $limitnumber = clean_param($option['value'], PARAM_INT);
                 break;
+            case 'sortby':
+                $sortallowedvalues = array('id', 'firstname', 'lastname', 'siteorder');
+                if (!in_array($option['value'], $sortallowedvalues)) {
+                    throw new invalid_parameter_exception('Invalid value for sortby parameter (value: ' . $option['value'] . '),' .
+                        'allowed values are: ' . implode(',', $sortallowedvalues));
+                }
+                if ($option['value'] == 'siteorder') {
+                    list($sortby, $sortparams) = users_order_by_sql('us');
+                } else {
+                    $sortby = 'us.' . $option['value'];
+                }
+                break;
+            case 'sortdirection':
+                $sortdirection = strtoupper($option['value']);
+                $directionallowedvalues = array('ASC', 'DESC');
+                if (!in_array($sortdirection, $directionallowedvalues)) {
+                    throw new invalid_parameter_exception('Invalid value for sortdirection parameter
+                        (value: ' . $sortdirection . '),' . 'allowed values are: ' . implode(',', $directionallowedvalues));
+                }
+                break;
             }
         }
 
@@ -503,7 +529,8 @@ class core_enrol_external extends external_api {
                         FROM {user} u $ctxjoin $groupjoin
                        WHERE u.id IN ($enrolledsql)
                   ) q ON q.id = us.id
-                ORDER BY us.id ASC";
+                ORDER BY $sortby $sortdirection";
+        $enrolledparams = array_merge($enrolledparams, $sortparams);
         $enrolledusers = $DB->get_recordset_sql($sql, $enrolledparams, $limitfrom, $limitnumber);
         $users = array();
         foreach ($enrolledusers as $user) {
index f98bcbb..38a41e1 100644 (file)
@@ -1,6 +1,9 @@
 This files describes API changes in /enrol/* - plugins,
 information provided here is intended especially for developers.
 
+=== 3.1 ===
+* core_enrol_external::get_enrolled_users now supports two additional parameters for ordering: sortby and sortdirection.
+
 === 3.0 ===
 
 * Added new events enrol_instance_created, enrol_instance_updated and
index aaaed88..19bc80c 100644 (file)
@@ -83,7 +83,7 @@ class dropdown_attribute extends element {
             'value' => $this->selected
         );
 
-        $attributes = array();
+        $attributes = array('tabindex' => '1');
 
         if (!empty($this->isdisabled)) {
             $attributes['disabled'] = 'DISABLED';
index 2766a9d..0102199 100644 (file)
@@ -85,9 +85,11 @@ class text_attribute extends element {
         $label = '';
         if (preg_match("/^feedback/", $this->name)) {
             $labeltitle = get_string('feedbackfor', 'gradereport_singleview', $this->label);
+            $attributes['tabindex'] = '2';
             $label = html_writer::tag('label', $labeltitle,  array('for' => $this->name, 'class' => 'accesshide'));
         } else if (preg_match("/^finalgrade/", $this->name)) {
             $labeltitle = get_string('gradefor', 'gradereport_singleview', $this->label);
+            $attributes['tabindex'] = '1';
             $label = html_writer::tag('label', $labeltitle,  array('for' => $this->name, 'class' => 'accesshide'));
         }
 
index 0c377b8..7ee45eb 100644 (file)
@@ -1,6 +1,96 @@
 M.gradereport_singleview = {};
 
 M.gradereport_singleview.init = function(Y) {
+    var getColumnIndex = function(cell) {
+        var rowNode = cell.ancestor('tr');
+        if (!rowNode || !cell) {
+            return;
+        }
+        var cells = rowNode.all('td, th');
+        return cells.indexOf(cell);
+    },
+    getNextCell = function(cell) {
+        var n = cell || document.activeElement,
+            next = n.next('td.cell, th.cell');
+        if (!next) {
+            return null;
+        }
+        // Continue on until we find a navigable cell
+        if (!next || !Y.one(next).one('input:not([type="hidden"]):not([disabled="DISABLED"]), select, a')) {
+            return getNextCell(next);
+        }
+        return next;
+    },
+    getPrevCell = function(cell) {
+        var n = cell || document.activeElement,
+            next = n.previous('td.cell, th.cell');
+        if (!next) {
+            return null;
+        }
+        // Continue on until we find a navigable cell
+        if (!Y.one(next).one('input:not([type="hidden"]):not([disabled="DISABLED"]), select, a')) {
+            return getPrevCell(next);
+        }
+        return next;
+    },
+    getAboveCell = function(cell) {
+        var n = cell || document.activeElement,
+            tr = n.ancestor('tr').previous('tr'),
+            columnIndex = getColumnIndex(n),
+            next = null;
+        if (tr) {
+            next = tr.all('td, th').item(columnIndex);
+        } else {
+            return null;
+        }
+        // Continue on until we find a navigable cell
+        if (!Y.one(next).one('input:not([type="hidden"]):not([disabled="DISABLED"]), select, a')) {
+            return getAboveCell(next);
+        }
+        return next;
+    },
+    getBelowCell = function(cell) {
+        var n = cell || document.activeElement,
+            tr = n.ancestor('tr').next('tr'),
+            columnIndex = getColumnIndex(n),
+            next = null;
+        if (tr) {
+            next = tr.all('td, th').item(columnIndex);
+        } else {
+            return null;
+        }
+        // Continue on until we find a navigable cell
+        if (!Y.one(next).one('input:not([type="hidden"]):not([disabled="DISABLED"]), select, a')) {
+            return getBelowCell(next);
+        }
+        return next;
+    };
+
+    // Add ctrl+arrow controls for navigation
+    Y.one(Y.config.doc.body).delegate('key', function(e) {
+        e.preventDefault();
+        e.stopPropagation();
+        var next = null;
+        switch (e.keyCode) {
+            case 37:
+                next = getPrevCell(this.ancestor('td, th'));
+                break;
+            case 38:
+                next = getAboveCell(this.ancestor('td, th'));
+                break;
+            case 39:
+                next = getNextCell(this.ancestor('td, th'));
+                break;
+            case 40:
+                next = getBelowCell(this.ancestor('td, th'));
+                break;
+        }
+        if (next) {
+            Y.one(next).one('input:not([type="hidden"]):not([disabled="DISABLED"]), select, a').focus();
+        }
+        return;
+    }, 'down:37,38,39,40+ctrl', 'table input, table select, table a');
+
     // Make toggle links
     Y.all('.include').each(function(link) {
         var type = link.getAttribute('class').split(" ")[2];
index 390320b..1ecd467 100644 (file)
@@ -911,9 +911,9 @@ $string['requiredentrieschanged'] = '<strong>IMPORTANT - PLEASE READ<br/>(This w
 $string['requiremodintro'] = 'Require activity description';
 $string['requiremodintro_desc'] = 'If enabled, users will be forced to enter a description for each activity.';
 $string['requires'] = 'Requires';
-$string['purgecaches']= 'Purge all caches';
-$string['purgecachesconfirm']= 'Moodle can cache themes, javascript, language strings, filtered text, rss feeds and many other pieces of calculated data.  Purging these caches will delete that data from the server and force browsers to refetch data, so that you can be sure you are seeing the most up-to-date values produced by the current code.  There is no danger in purging caches, but your site may appear slower for a while until the server and clients calculate new information and cache it.';
-$string['purgecachesfinished']= 'All caches were purged.';
+$string['purgecaches'] = 'Purge all caches';
+$string['purgecachesconfirm'] = 'Moodle can cache themes, javascript, language strings, filtered text, rss feeds and many other pieces of calculated data.  Purging these caches will delete that data from the server and force browsers to refetch data, so that you can be sure you are seeing the most up-to-date values produced by the current code.  There is no danger in purging caches, but your site may appear slower for a while until the server and clients calculate new information and cache it.';
+$string['purgecachesfinished'] = 'All caches were purged.';
 $string['requestcategoryselection'] = 'Enable category selection';
 $string['restorecourse'] = 'Restore course';
 $string['restorernewroleid'] = 'Restorers\' role in courses';
index a37b84a..c71832b 100644 (file)
@@ -41,7 +41,7 @@ $string['blogaboutthis'] = 'Blog about this {$a->type}';
 $string['blogaboutthiscourse'] = 'Add an entry about this course';
 $string['blogaboutthismodule'] = 'Add an entry about this {$a}';
 $string['blogadministration'] = 'Blog administration';
-$string['blogdeleteconfirm'] = 'Delete this blog entry?';
+$string['blogdeleteconfirm'] = 'Delete the blog entry \'{$a}\'?';
 $string['blogdisable'] = 'Blogging is disabled!';
 $string['blogentries'] = 'Blog entries';
 $string['blogentriesabout'] = 'Blog entries about {$a}';
index 257bb4d..a44742e 100644 (file)
@@ -89,7 +89,7 @@ $string['editsharing'] = 'Edit sharing';
 $string['editstore'] = 'Edit store';
 $string['editstoresuccess'] = 'Succesfully edited the cache store.';
 $string['editdefinitionmappings'] = '{$a} definition store mappings';
-$string['editdefinitionsharing'] =  'Edit definition sharing for {$a}';
+$string['editdefinitionsharing'] = 'Edit definition sharing for {$a}';
 $string['ex_configcannotsave'] = 'Unable to save the cache config to file.';
 $string['ex_nodefaultlock'] = 'Unable to find a default lock instance.';
 $string['ex_unabletolock'] = 'Unable to acquire a lock for caching.';
index d35a18d..08037a1 100644 (file)
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
-$string['field101']='Natural and Physical Science';
-$string['field10101']='Mathematical Sciences';
-$string['field1010101']='Mathematics';
-$string['field1010103']='Statistics';
-$string['field1010199']='Mathematical Sciences (Other)';
-$string['field10103']='Physics and Astronomy';
-$string['field1010301']='Physics';
-$string['field1010303']='Astronomy';
-$string['field10105']='Chemical Sciences';
-$string['field1010501']='Organic Chemistry';
-$string['field1010503']='Inorganic Chemistry';
-$string['field1010599']='Chemical Sciences (Other)';
-$string['field10107']='Earth Sciences';
-$string['field1010701']='Atmospheric Sciences';
-$string['field1010703']='Geology';
-$string['field1010705']='Geophysics';
-$string['field1010707']='Geochemistry';
-$string['field1010709']='Soil Science';
-$string['field1010711']='Hydrology';
-$string['field1010713']='Oceanography';
-$string['field1010799']='Earth Sciences (Other)';
-$string['field10109']='Biological Sciences';
-$string['field1010901']='Biochemistry and Cell Biology';
-$string['field1010903']='Botany';
-$string['field1010905']='Ecology and Evolution';
-$string['field1010907']='Marine Science';
-$string['field1010909']='Genetics';
-$string['field1010911']='Microbiology';
-$string['field1010913']='Human Biology';
-$string['field1010915']='Zoology';
-$string['field1010999']='Biological Sciences (Other)';
-$string['field10199']='Other Natural and Physical Sciences';
-$string['field1019901']='Medical Science';
-$string['field1019903']='Forensic Science';
-$string['field1019905']='Food Science and Biotechnology';
-$string['field1019907']='Pharmacology';
-$string['field1019909']='Laboratory Technology';
-$string['field1019999']='Natural and Physical Sciences (Other)';
-$string['field102']='Information Technology';
-$string['field10201']='Computer Science';
-$string['field1020101']='Formal Language Theory';
-$string['field1020103']='Programming';
-$string['field1020105']='Computational Theory';
-$string['field1020107']='Compiler Construction';
-$string['field1020109']='Algorithms';
-$string['field1020111']='Data Structures';
-$string['field1020113']='Networks and Communications';
-$string['field1020115']='Computer Graphics';
-$string['field1020117']='Operating Systems';
-$string['field1020119']='Artificial Intelligence';
-$string['field1020199']='Computer Science (Other)';
-$string['field10203']='Information Systems';
-$string['field1020301']='Conceptual Modelling';
-$string['field1020303']='Database Management';
-$string['field1020305']='Systems Analysis and Design';
-$string['field1020307']='Decision Support Systems';
-$string['field1020399']='Information Systems (Other)';
-$string['field10299']='Other Information Technology';
-$string['field1029901']='Security Science';
-$string['field1029999']='Information Technology (Other)';
-$string['field103']='Engineering and Related Technologies';
-$string['field10301']='Manufacturing Engineering and Technology';
-$string['field1030101']='Manufacturing Engineering';
-$string['field1030103']='Printing';
-$string['field1030105']='Textile Making';
-$string['field1030107']='Garment Making';
-$string['field1030109']='Footwear Making';
-$string['field1030111']='Wood Machining and Turning';
-$string['field1030113']='Cabinet Making';
-$string['field1030115']='Furniture Upholstery and Renovation';
-$string['field1030117']='Furniture Polishing';
-$string['field1030199']='Manufacturing Engineering and Technology (Other)';
-$string['field10303']='Process and Resources Engineering';
-$string['field1030301']='Chemical Engineering';
-$string['field1030303']='Mining Engineering';
-$string['field1030305']='Materials Engineering';
-$string['field1030307']='Food Processing Technology';
-$string['field1030399']='Process and Resources Engineering (Other)';
-$string['field10305']='Automotive Engineering and Technology';
-$string['field1030501']='Automotive Engineering';
-$string['field1030503']='Vehicle Mechanics';
-$string['field1030505']='Automotive Electrics and Electronics';
-$string['field1030507']='Automotive Vehicle Refinishing';
-$string['field1030509']='Automotive Body Construction';
-$string['field1030511']='Panel Beating';
-$string['field1030513']='Upholstery and Vehicle Trimming';
-$string['field1030515']='Automotive Vehicle Operations';
-$string['field1030599']='Automotive Engineering and Technology (Other)';
-$string['field10307']='Mechanical and Industrial Engineering and Technology';
-$string['field1030701']='Mechanical Engineering';
-$string['field1030703']='Industrial Engineering';
-$string['field1030705']='Toolmaking';
-$string['field1030707']='Metal Fitting, Turning and Machining';
-$string['field1030709']='Sheetmetal Working';
-$string['field1030711']='Boilermaking and Welding';
-$string['field1030713']='Metal Casting and Patternmaking';
-$string['field1030715']='Precision Metalworking';
-$string['field1030717']='Plant and Machine Operations';
-$string['field1030799']='Mechanical and Industrial Engineering and Technology (Other)';
-$string['field10309']='Civil Engineering';
-$string['field1030901']='Construction Engineering';
-$string['field1030903']='Structural Engineering';
-$string['field1030905']='Building Services Engineering';
-$string['field1030907']='Water and Sanitary Engineering';
-$string['field1030909']='Transport Engineering';
-$string['field1030911']='Geotechnical Engineering';
-$string['field1030913']='Ocean Engineering';
-$string['field1030999']='Civil Engineering (Other)';
-$string['field10311']='Geomatic Engineering';
-$string['field1031101']='Surveying';
-$string['field1031103']='Mapping Science';
-$string['field1031199']='Geomatic Engineering (Other)';
-$string['field10313']='Electrical and Electronic Engineering and Technology';
-$string['field1031301']='Electrical Engineering';
-$string['field1031303']='Electronic Engineering';
-$string['field1031305']='Computer Engineering';
-$string['field1031307']='Communications Technologies';
-$string['field1031309']='Communications Equipment Installation and Maintenance';
-$string['field1031311']='Powerline Installation and Maintenance';
-$string['field1031313']='Electrical Fitting, Electrical Mechanics';
-$string['field1031315']='Refrigeration and Air Conditioning Mechanics';
-$string['field1031317']='Electronic Equipment Servicing';
-$string['field1031399']='Electrical and Electronic Engineering and Technology (Other)';
-$string['field10315']='Aerospace Engineering and Technology';
-$string['field1031501']='Aerospace Engineering';
-$string['field1031503']='Aircraft Maintenance Engineering';
-$string['field1031505']='Aircraft Operation';
-$string['field1031507']='Air Traffic Control';
-$string['field1031599']='Aerospace Engineering and Technology (Other)';
-$string['field10317']='Maritime Engineering and Technology';
-$string['field1031701']='Maritime Engineering';
-$string['field1031703']='Marine Construction';
-$string['field1031705']='Marine Craft Operation';
-$string['field1031799']='Maritime Engineering and Technology (Other)';
-$string['field10399']='Other Engineering and Related Technologies';
-$string['field1039901']='Environmental Engineering';
-$string['field1039903']='Biomedical Engineering';
-$string['field1039905']='Fire Technology';
-$string['field1039907']='Rail Operations';
-$string['field1039909']='Cleaning';
-$string['field1039999']='Engineering and Related Technologies (Other)';
-$string['field104']='Architecture and Building';
-$string['field10401']='Architecture and Urban Environment';
-$string['field1040101']='Architecture';
-$string['field1040103']='Urban Design and Regional Planning';
-$string['field1040105']='Landscape Architecture';
-$string['field1040107']='Interior and Environmental Design';
-$string['field1040199']='Architecture and Urban Environment (Other)';
-$string['field10403']='Building';
-$string['field1040301']='Building Science and Technology';
-$string['field1040303']='Building Construction Management';
-$string['field1040305']='Building Surveying';
-$string['field1040307']='Building Construction Economics';
-$string['field1040309']='Bricklaying and Stonemasonry';
-$string['field1040311']='Carpentry and Joinery';
-$string['field1040313']='Ceiling, Wall and Floor Fixing';
-$string['field1040315']='Roof Fixing';
-$string['field1040317']='Plastering';
-$string['field1040319']='Furnishing Installation';
-$string['field1040321']='Floor Coverings';
-$string['field1040323']='Glazing';
-$string['field1040325']='Painting, Decorating and Sign Writing';
-$string['field1040327']='Plumbing';
-$string['field1040329']='Scaffolding and Rigging';
-$string['field1040399']='Building (Other)';
-$string['field105']='Agriculture, Environmental and Related Studies';
-$string['field10501']='Agriculture';
-$string['field1050101']='Agricultural Science';
-$string['field1050103']='Wool Science';
-$string['field1050105']='Animal Husbandry';
-$string['field1050199']='Agriculture (Other)';
-$string['field10503']='Horticulture and Viticulture';
-$string['field1050301']='Horticulture';
-$string['field1050303']='Viticulture';
-$string['field10505']='Forestry Studies';
-$string['field1050501']='Forestry Studies';
-$string['field10507']='Fisheries Studies';
-$string['field1050701']='Aquaculture';
-$string['field1050799']='Fisheries Studies (Other)';
-$string['field10509']='Environmental Studies';
-$string['field1050901']='Land, Parks and Wildlife Management';
-$string['field1050999']='Environmental Studies (Other)';
-$string['field10599']='Other Agriculture, Environmental and Related Studies';
-$string['field1059901']='Pest and Weed Control';
-$string['field1059999']='Agriculture, Environmental and Related Studies (Other)';
-$string['field106']='Health';
-$string['field10601']='Medical Studies';
-$string['field1060101']='General Medicine';
-$string['field1060103']='Surgery';
-$string['field1060105']='Psychiatry';
-$string['field1060107']='Obstetrics and Gynaecology';
-$string['field1060109']='Paediatrics';
-$string['field1060111']='Anaesthesiology';
-$string['field1060113']='Pathology';
-$string['field1060115']='Radiology';
-$string['field1060117']='Internal Medicine';
-$string['field1060119']='General Practice';
-$string['field1060199']='Medical Studies (Other)';
-$string['field10603']='Nursing';
-$string['field1060301']='General Nursing';
-$string['field1060303']='Midwifery';
-$string['field1060305']='Mental Health Nursing';
-$string['field1060307']='Community Nursing';
-$string['field1060309']='Critical Care Nursing';
-$string['field1060311']='Aged Care Nursing';
-$string['field1060313']='Palliative Care Nursing';
-$string['field1060315']='Mothercraft Nursing and Family and Child Health Nursing';
-$string['field1060399']='Nursing (Other)';
-$string['field10605']='Pharmacy';
-$string['field1060501']='Pharmacy';
-$string['field10607']='Dental Studies';
-$string['field1060701']='Dentistry';
-$string['field1060703']='Dental Assisting';
-$string['field1060705']='Dental Technology';
-$string['field1060799']='Dental Studies (Other)';
-$string['field10609']='Optical Science';
-$string['field1060901']='Optometry';
-$string['field1060903']='Optical Technology';
-$string['field1060999']='Optical Science (Other)';
-$string['field10611']='Veterinary Studies';
-$string['field1061101']='Veterinary Science';
-$string['field1061103']='Veterinary Assisting';
-$string['field1061199']='Veterinary Studies (Other)';
-$string['field10613']='Public Health';
-$string['field1061301']='Occupational Health and Safety';
-$string['field1061303']='Environmental Health';
-$string['field1061305']='Indigenous Health';
-$string['field1061307']='Health Promotion';
-$string['field1061309']='Community Health';
-$string['field1061311']='Epidemiology';
-$string['field1061399']='Public Health (Other)';
-$string['field10615']='Radiography';
-$string['field1061501']='Radiography';
-$string['field10617']='Rehabilitation Therapies';
-$string['field1061701']='Physiotherapy';
-$string['field1061703']='Occupational Therapy';
-$string['field1061705']='Chiropractic and Osteopathy';
-$string['field1061707']='Speech Pathology';
-$string['field1061709']='Audiology';
-$string['field1061711']='Massage Therapy';
-$string['field1061713']='Podiatry';
-$string['field1061799']='Rehabilitation Therapies (Other)';
-$string['field10619']='Complementary Therapies';
-$string['field1061901']='Naturopathy';
-$string['field1061903']='Acupuncture';
-$string['field1061905']='Traditional Chinese Medicine';
-$string['field1061999']='Complementary Therapies (Other)';
-$string['field10699']='Other Health';
-$string['field1069901']='Nutrition and Dietetics';
-$string['field1069903']='Human Movement';
-$string['field1069905']='Paramedical Studies';
-$string['field1069907']='First Aid';
-$string['field1069999']='Health (Other)';
-$string['field107']='Education';
-$string['field10701']='Teacher Education';
-$string['field1070101']='Teacher Education: Early Childhood';
-$string['field1070103']='Teacher Education: Primary';
-$string['field1070105']='Teacher Education: Secondary';
-$string['field1070107']='Teacher-Librarianship';
-$string['field1070109']='Teacher Education: Vocational Education and Training';
-$string['field1070111']='Teacher Education: Higher Education';
-$string['field1070113']='Teacher Education: Special Education';
-$string['field1070115']='English As A Second Language Teaching';
-$string['field1070117']='Nursing Education Teacher Training';
-$string['field1070199']='Teacher Education (Other)';
-$string['field10703']='Curriculum and Education Studies';
-$string['field1070301']='Curriculum Studies';
-$string['field1070303']='Education Studies';
-$string['field10799']='Other Education';
-$string['field1079999']='Education (Other)';
-$string['field108']='Management and Commerce';
-$string['field10801']='Accounting';
-$string['field1080101']='Accounting';
-$string['field10803']='Business and Management';
-$string['field1080301']='Business Management';
-$string['field1080303']='Human Resource Management';
-$string['field1080305']='Personal Management Training';
-$string['field1080307']='Organisation Management';
-$string['field1080309']='Industrial Relations';
-$string['field1080311']='International Business';
-$string['field1080313']='Public and Health Care Administration';
-$string['field1080315']='Project Management';
-$string['field1080317']='Quality Management';
-$string['field1080319']='Hospitality Management';
-$string['field1080321']='Farm Management and Agribusiness';
-$string['field1080323']='Tourism Management';
-$string['field1080399']='Business and Management (Other)';
-$string['field10805']='Sales and Marketing';
-$string['field1080501']='Sales';
-$string['field1080503']='Real Estate';
-$string['field1080505']='Marketing';
-$string['field1080507']='Advertising';
-$string['field1080509']='Public Relations';
-$string['field1080599']='Sales and Marketing (Other)';
-$string['field10807']='Tourism';
-$string['field1080701']='Tourism';
-$string['field10809']='Office Studies';
-$string['field1080901']='Secretarial and Clerical Studies';
-$string['field1080903']='Keyboard Skills';
-$string['field1080905']='Practical Computing Skills';
-$string['field1080999']='Office Studies (Other)';
-$string['field10811']='Banking, Finance and Related Fields';
-$string['field1081101']='Banking and Finance';
-$string['field1081103']='Insurance and Actuarial Studies';
-$string['field1081105']='Investment and Securities';
-$string['field1081199']='Banking, Finance and Related Fields (Other)';
-$string['field10899']='Other Management and Commerce';
-$string['field1089901']='Purchasing, Warehousing and Distribution';
-$string['field1089903']='Valuation';
-$string['field1089999']='Management and Commerce (Other)';
-$string['field109']='Society and Culture';
-$string['field10901']='Political Science and Policy Studies';
-$string['field1090101']='Political Science';
-$string['field1090103']='Policy Studies';
-$string['field10903']='Studies In Human Society';
-$string['field1090301']='Sociology';
-$string['field1090303']='Anthropology';
-$string['field1090305']='History';
-$string['field1090307']='Archaeology';
-$string['field1090309']='Human Geography';
-$string['field1090311']='Indigenous Studies';
-$string['field1090313']='Gender Specific Studies';
-$string['field1090399']='Studies In Human Society (Other)';
-$string['field10905']='Human Welfare Studies and Services';
-$string['field1090501']='Social Work';
-$string['field1090503']='Children\'S Services';
-$string['field1090505']='Youth Work';
-$string['field1090507']='Care For The Aged';
-$string['field1090509']='Care For The Disabled';
-$string['field1090511']='Residential Client Care';
-$string['field1090513']='Counselling';
-$string['field1090515']='Welfare Studies';
-$string['field1090599']='Human Welfare Studies and Services (Other)';
-$string['field10907']='Behavioural Science';
-$string['field1090701']='Psychology';
-$string['field1090799']='Behavioural Science (Other)';
-$string['field10909']='Law';
-$string['field1090901']='Business and Commercial Law';
-$string['field1090903']='Constitutional Law';
-$string['field1090905']='Criminal Law';
-$string['field1090907']='Family Law';
-$string['field1090909']='International Law';
-$string['field1090911']='Taxation Law';
-$string['field1090913']='Legal Practice';
-$string['field1090999']='Law (Other)';
-$string['field10911']='Justice and Law Enforcement';
-$string['field1091101']='Justice Administration';
-$string['field1091103']='Legal Studies';
-$string['field1091105']='Police Studies';
-$string['field1091199']='Justice and Law Enforcement (Other)';
-$string['field10913']='Librarianship, Information Management and Curatorial Studies';
-$string['field1091301']='Librarianship and Information Management';
-$string['field1091303']='Curatorial Studies';
-$string['field10915']='Language and Literature';
-$string['field1091501']='English Language';
-$string['field1091503']='Northern European Languages';
-$string['field1091505']='Southern European Languages';
-$string['field1091507']='Eastern European Languages';
-$string['field1091509']='Southwest Asian and North African Languages';
-$string['field1091511']='Southern Asian Languages';
-$string['field1091513']='Southeast Asian Languages';
-$string['field1091515']='Eastern Asian Languages';
-$string['field1091517']='Australian Indigenous Languages';
-$string['field1091519']='Translating and Interpreting';
-$string['field1091521']='Linguistics';
-$string['field1091523']='Literature';
-$string['field1091599']='Language and Literature (Other)';
-$string['field10917']='Philosophy and Religious Studies';
-$string['field1091701']='Philosophy';
-$string['field1091703']='Religious Studies';
-$string['field10919']='Economics and Econometrics';
-$string['field1091901']='Economics';
-$string['field1091903']='Econometrics';
-$string['field10921']='Sport and Recreation';
-$string['field1092101']='Sport and Recreation Activities';
-$string['field1092103']='Sports Coaching, Officiating and Instruction';
-$string['field1092199']='Sport and Recreation (Other)';
-$string['field10999']='Other Society and Culture';
-$string['field1099901']='Family and Consumer Studies';
-$string['field1099903']='Criminology';
-$string['field1099905']='Security Services';
-$string['field1099999']='Society and Culture (Other)';
-$string['field110']='Creative Arts';
-$string['field11001']='Performing Arts';
-$string['field1100101']='Music';
-$string['field1100103']='Drama and Theatre Studies';
-$string['field1100105']='Dance';
-$string['field1100199']='Performing Arts (Other)';
-$string['field11003']='Visual Arts and Crafts';
-$string['field1100301']='Fine Arts';
-$string['field1100303']='Photography';
-$string['field1100305']='Crafts';
-$string['field1100307']='Jewellery Making';
-$string['field1100309']='Floristry';
-$string['field1100399']='Visual Arts and Crafts (Other)';
-$string['field11005']='Graphic and Design Studies';
-$string['field1100501']='Graphic Arts and Design Studies';
-$string['field1100503']='Textile Design';
-$string['field1100505']='Fashion Design';
-$string['field1100599']='Graphic and Design Studies (Other)';
-$string['field11007']='Communication and Media Studies';
-$string['field1100701']='Audio Visual Studies';
-$string['field1100703']='Journalism';
-$string['field1100705']='Written Communication';
-$string['field1100707']='Verbal Communication';
-$string['field1100799']='Communication and Media Studies (Other)';
-$string['field11099']='Other Creative Arts';
-$string['field1109999']='Creative Arts (Other)';
-$string['field111']='Food, Hospitality and Personal Services';
-$string['field11101']='Food and Hospitality';
-$string['field1110101']='Hospitality';
-$string['field1110103']='Food and Beverage Service';
-$string['field1110105']='Butchery';
-$string['field1110107']='Baking and Pastrymaking';
-$string['field1110109']='Cookery';
-$string['field1110111']='Food Hygiene';
-$string['field1110199']='Food and Hospitality (Other)';
-$string['field11103']='Personal Services';
-$string['field1110301']='Beauty Therapy';
-$string['field1110303']='Hairdressing';
-$string['field1110399']='Personal Services (Other)';
-$string['field112']='Mixed Field Programmes';
-$string['field11201']='General Education Programmes';
-$string['field1120101']='General Primary and Secondary Education Programmes';
-$string['field1120103']='Literacy and Numeracy Programmes';
-$string['field1120105']='Learning Skills Programmes';
-$string['field1120199']='General Education Programmes (Other)';
-$string['field11203']='Social Skills Programmes';
-$string['field1120301']='Social and Interpersonal Skills Programmes';
-$string['field1120303']='Survival Skills Programmes';
-$string['field1120305']='Parental Education Programmes';
-$string['field1120399']='Social Skills Programmes (Other)';
-$string['field11205']='Employment Skills Programmes';
-$string['field1120501']='Career Development Programmes';
-$string['field1120503']='Job Search Skills Programmes';
-$string['field1120505']='Work Practices Programmes';
-$string['field1120599']='Employment Skills Programmes (Other)';
-$string['field11299']='Other Mixed Field Programmes';
-$string['field1129999']='Mixed Field Programmes (Other)';
+$string['field101'] = 'Natural and Physical Science';
+$string['field10101'] = 'Mathematical Sciences';
+$string['field1010101'] = 'Mathematics';
+$string['field1010103'] = 'Statistics';
+$string['field1010199'] = 'Mathematical Sciences (Other)';
+$string['field10103'] = 'Physics and Astronomy';
+$string['field1010301'] = 'Physics';
+$string['field1010303'] = 'Astronomy';
+$string['field10105'] = 'Chemical Sciences';
+$string['field1010501'] = 'Organic Chemistry';
+$string['field1010503'] = 'Inorganic Chemistry';
+$string['field1010599'] = 'Chemical Sciences (Other)';
+$string['field10107'] = 'Earth Sciences';
+$string['field1010701'] = 'Atmospheric Sciences';
+$string['field1010703'] = 'Geology';
+$string['field1010705'] = 'Geophysics';
+$string['field1010707'] = 'Geochemistry';
+$string['field1010709'] = 'Soil Science';
+$string['field1010711'] = 'Hydrology';
+$string['field1010713'] = 'Oceanography';
+$string['field1010799'] = 'Earth Sciences (Other)';
+$string['field10109'] = 'Biological Sciences';
+$string['field1010901'] = 'Biochemistry and Cell Biology';
+$string['field1010903'] = 'Botany';
+$string['field1010905'] = 'Ecology and Evolution';
+$string['field1010907'] = 'Marine Science';
+$string['field1010909'] = 'Genetics';
+$string['field1010911'] = 'Microbiology';
+$string['field1010913'] = 'Human Biology';
+$string['field1010915'] = 'Zoology';
+$string['field1010999'] = 'Biological Sciences (Other)';
+$string['field10199'] = 'Other Natural and Physical Sciences';
+$string['field1019901'] = 'Medical Science';
+$string['field1019903'] = 'Forensic Science';
+$string['field1019905'] = 'Food Science and Biotechnology';
+$string['field1019907'] = 'Pharmacology';
+$string['field1019909'] = 'Laboratory Technology';
+$string['field1019999'] = 'Natural and Physical Sciences (Other)';
+$string['field102'] = 'Information Technology';
+$string['field10201'] = 'Computer Science';
+$string['field1020101'] = 'Formal Language Theory';
+$string['field1020103'] = 'Programming';
+$string['field1020105'] = 'Computational Theory';
+$string['field1020107'] = 'Compiler Construction';
+$string['field1020109'] = 'Algorithms';
+$string['field1020111'] = 'Data Structures';
+$string['field1020113'] = 'Networks and Communications';
+$string['field1020115'] = 'Computer Graphics';
+$string['field1020117'] = 'Operating Systems';
+$string['field1020119'] = 'Artificial Intelligence';
+$string['field1020199'] = 'Computer Science (Other)';
+$string['field10203'] = 'Information Systems';
+$string['field1020301'] = 'Conceptual Modelling';
+$string['field1020303'] = 'Database Management';
+$string['field1020305'] = 'Systems Analysis and Design';
+$string['field1020307'] = 'Decision Support Systems';
+$string['field1020399'] = 'Information Systems (Other)';
+$string['field10299'] = 'Other Information Technology';
+$string['field1029901'] = 'Security Science';
+$string['field1029999'] = 'Information Technology (Other)';
+$string['field103'] = 'Engineering and Related Technologies';
+$string['field10301'] = 'Manufacturing Engineering and Technology';
+$string['field1030101'] = 'Manufacturing Engineering';
+$string['field1030103'] = 'Printing';
+$string['field1030105'] = 'Textile Making';
+$string['field1030107'] = 'Garment Making';
+$string['field1030109'] = 'Footwear Making';
+$string['field1030111'] = 'Wood Machining and Turning';
+$string['field1030113'] = 'Cabinet Making';
+$string['field1030115'] = 'Furniture Upholstery and Renovation';
+$string['field1030117'] = 'Furniture Polishing';
+$string['field1030199'] = 'Manufacturing Engineering and Technology (Other)';
+$string['field10303'] = 'Process and Resources Engineering';
+$string['field1030301'] = 'Chemical Engineering';
+$string['field1030303'] = 'Mining Engineering';
+$string['field1030305'] = 'Materials Engineering';
+$string['field1030307'] = 'Food Processing Technology';
+$string['field1030399'] = 'Process and Resources Engineering (Other)';
+$string['field10305'] = 'Automotive Engineering and Technology';
+$string['field1030501'] = 'Automotive Engineering';
+$string['field1030503'] = 'Vehicle Mechanics';
+$string['field1030505'] = 'Automotive Electrics and Electronics';
+$string['field1030507'] = 'Automotive Vehicle Refinishing';
+$string['field1030509'] = 'Automotive Body Construction';
+$string['field1030511'] = 'Panel Beating';
+$string['field1030513'] = 'Upholstery and Vehicle Trimming';
+$string['field1030515'] = 'Automotive Vehicle Operations';
+$string['field1030599'] = 'Automotive Engineering and Technology (Other)';
+$string['field10307'] = 'Mechanical and Industrial Engineering and Technology';
+$string['field1030701'] = 'Mechanical Engineering';
+$string['field1030703'] = 'Industrial Engineering';
+$string['field1030705'] = 'Toolmaking';
+$string['field1030707'] = 'Metal Fitting, Turning and Machining';
+$string['field1030709'] = 'Sheetmetal Working';
+$string['field1030711'] = 'Boilermaking and Welding';
+$string['field1030713'] = 'Metal Casting and Patternmaking';
+$string['field1030715'] = 'Precision Metalworking';
+$string['field1030717'] = 'Plant and Machine Operations';
+$string['field1030799'] = 'Mechanical and Industrial Engineering and Technology (Other)';
+$string['field10309'] = 'Civil Engineering';
+$string['field1030901'] = 'Construction Engineering';
+$string['field1030903'] = 'Structural Engineering';
+$string['field1030905'] = 'Building Services Engineering';
+$string['field1030907'] = 'Water and Sanitary Engineering';
+$string['field1030909'] = 'Transport Engineering';
+$string['field1030911'] = 'Geotechnical Engineering';
+$string['field1030913'] = 'Ocean Engineering';
+$string['field1030999'] = 'Civil Engineering (Other)';
+$string['field10311'] = 'Geomatic Engineering';
+$string['field1031101'] = 'Surveying';
+$string['field1031103'] = 'Mapping Science';
+$string['field1031199'] = 'Geomatic Engineering (Other)';
+$string['field10313'] = 'Electrical and Electronic Engineering and Technology';
+$string['field1031301'] = 'Electrical Engineering';
+$string['field1031303'] = 'Electronic Engineering';
+$string['field1031305'] = 'Computer Engineering';
+$string['field1031307'] = 'Communications Technologies';
+$string['field1031309'] = 'Communications Equipment Installation and Maintenance';
+$string['field1031311'] = 'Powerline Installation and Maintenance';
+$string['field1031313'] = 'Electrical Fitting, Electrical Mechanics';
+$string['field1031315'] = 'Refrigeration and Air Conditioning Mechanics';
+$string['field1031317'] = 'Electronic Equipment Servicing';
+$string['field1031399'] = 'Electrical and Electronic Engineering and Technology (Other)';
+$string['field10315'] = 'Aerospace Engineering and Technology';
+$string['field1031501'] = 'Aerospace Engineering';
+$string['field1031503'] = 'Aircraft Maintenance Engineering';
+$string['field1031505'] = 'Aircraft Operation';
+$string['field1031507'] = 'Air Traffic Control';
+$string['field1031599'] = 'Aerospace Engineering and Technology (Other)';
+$string['field10317'] = 'Maritime Engineering and Technology';
+$string['field1031701'] = 'Maritime Engineering';
+$string['field1031703'] = 'Marine Construction';
+$string['field1031705'] = 'Marine Craft Operation';
+$string['field1031799'] = 'Maritime Engineering and Technology (Other)';
+$string['field10399'] = 'Other Engineering and Related Technologies';
+$string['field1039901'] = 'Environmental Engineering';
+$string['field1039903'] = 'Biomedical Engineering';
+$string['field1039905'] = 'Fire Technology';
+$string['field1039907'] = 'Rail Operations';
+$string['field1039909'] = 'Cleaning';
+$string['field1039999'] = 'Engineering and Related Technologies (Other)';
+$string['field104'] = 'Architecture and Building';
+$string['field10401'] = 'Architecture and Urban Environment';
+$string['field1040101'] = 'Architecture';
+$string['field1040103'] = 'Urban Design and Regional Planning';
+$string['field1040105'] = 'Landscape Architecture';
+$string['field1040107'] = 'Interior and Environmental Design';
+$string['field1040199'] = 'Architecture and Urban Environment (Other)';
+$string['field10403'] = 'Building';
+$string['field1040301'] = 'Building Science and Technology';
+$string['field1040303'] = 'Building Construction Management';
+$string['field1040305'] = 'Building Surveying';
+$string['field1040307'] = 'Building Construction Economics';
+$string['field1040309'] = 'Bricklaying and Stonemasonry';
+$string['field1040311'] = 'Carpentry and Joinery';
+$string['field1040313'] = 'Ceiling, Wall and Floor Fixing';
+$string['field1040315'] = 'Roof Fixing';
+$string['field1040317'] = 'Plastering';
+$string['field1040319'] = 'Furnishing Installation';
+$string['field1040321'] = 'Floor Coverings';
+$string['field1040323'] = 'Glazing';
+$string['field1040325'] = 'Painting, Decorating and Sign Writing';
+$string['field1040327'] = 'Plumbing';
+$string['field1040329'] = 'Scaffolding and Rigging';
+$string['field1040399'] = 'Building (Other)';
+$string['field105'] = 'Agriculture, Environmental and Related Studies';
+$string['field10501'] = 'Agriculture';
+$string['field1050101'] = 'Agricultural Science';
+$string['field1050103'] = 'Wool Science';
+$string['field1050105'] = 'Animal Husbandry';
+$string['field1050199'] = 'Agriculture (Other)';
+$string['field10503'] = 'Horticulture and Viticulture';
+$string['field1050301'] = 'Horticulture';
+$string['field1050303'] = 'Viticulture';
+$string['field10505'] = 'Forestry Studies';
+$string['field1050501'] = 'Forestry Studies';
+$string['field10507'] = 'Fisheries Studies';
+$string['field1050701'] = 'Aquaculture';
+$string['field1050799'] = 'Fisheries Studies (Other)';
+$string['field10509'] = 'Environmental Studies';
+$string['field1050901'] = 'Land, Parks and Wildlife Management';
+$string['field1050999'] = 'Environmental Studies (Other)';
+$string['field10599'] = 'Other Agriculture, Environmental and Related Studies';
+$string['field1059901'] = 'Pest and Weed Control';
+$string['field1059999'] = 'Agriculture, Environmental and Related Studies (Other)';
+$string['field106'] = 'Health';
+$string['field10601'] = 'Medical Studies';
+$string['field1060101'] = 'General Medicine';
+$string['field1060103'] = 'Surgery';
+$string['field1060105'] = 'Psychiatry';
+$string['field1060107'] = 'Obstetrics and Gynaecology';
+$string['field1060109'] = 'Paediatrics';
+$string['field1060111'] = 'Anaesthesiology';
+$string['field1060113'] = 'Pathology';
+$string['field1060115'] = 'Radiology';
+$string['field1060117'] = 'Internal Medicine';
+$string['field1060119'] = 'General Practice';
+$string['field1060199'] = 'Medical Studies (Other)';
+$string['field10603'] = 'Nursing';
+$string['field1060301'] = 'General Nursing';
+$string['field1060303'] = 'Midwifery';
+$string['field1060305'] = 'Mental Health Nursing';
+$string['field1060307'] = 'Community Nursing';
+$string['field1060309'] = 'Critical Care Nursing';
+$string['field1060311'] = 'Aged Care Nursing';
+$string['field1060313'] = 'Palliative Care Nursing';
+$string['field1060315'] = 'Mothercraft Nursing and Family and Child Health Nursing';
+$string['field1060399'] = 'Nursing (Other)';
+$string['field10605'] = 'Pharmacy';
+$string['field1060501'] = 'Pharmacy';
+$string['field10607'] = 'Dental Studies';
+$string['field1060701'] = 'Dentistry';
+$string['field1060703'] = 'Dental Assisting';
+$string['field1060705'] = 'Dental Technology';
+$string['field1060799'] = 'Dental Studies (Other)';
+$string['field10609'] = 'Optical Science';
+$string['field1060901'] = 'Optometry';
+$string['field1060903'] = 'Optical Technology';
+$string['field1060999'] = 'Optical Science (Other)';
+$string['field10611'] = 'Veterinary Studies';
+$string['field1061101'] = 'Veterinary Science';
+$string['field1061103'] = 'Veterinary Assisting';
+$string['field1061199'] = 'Veterinary Studies (Other)';
+$string['field10613'] = 'Public Health';
+$string['field1061301'] = 'Occupational Health and Safety';
+$string['field1061303'] = 'Environmental Health';
+$string['field1061305'] = 'Indigenous Health';
+$string['field1061307'] = 'Health Promotion';
+$string['field1061309'] = 'Community Health';
+$string['field1061311'] = 'Epidemiology';
+$string['field1061399'] = 'Public Health (Other)';
+$string['field10615'] = 'Radiography';
+$string['field1061501'] = 'Radiography';
+$string['field10617'] = 'Rehabilitation Therapies';
+$string['field1061701'] = 'Physiotherapy';
+$string['field1061703'] = 'Occupational Therapy';
+$string['field1061705'] = 'Chiropractic and Osteopathy';
+$string['field1061707'] = 'Speech Pathology';
+$string['field1061709'] = 'Audiology';
+$string['field1061711'] = 'Massage Therapy';
+$string['field1061713'] = 'Podiatry';
+$string['field1061799'] = 'Rehabilitation Therapies (Other)';
+$string['field10619'] = 'Complementary Therapies';
+$string['field1061901'] = 'Naturopathy';
+$string['field1061903'] = 'Acupuncture';
+$string['field1061905'] = 'Traditional Chinese Medicine';
+$string['field1061999'] = 'Complementary Therapies (Other)';
+$string['field10699'] = 'Other Health';
+$string['field1069901'] = 'Nutrition and Dietetics';
+$string['field1069903'] = 'Human Movement';
+$string['field1069905'] = 'Paramedical Studies';
+$string['field1069907'] = 'First Aid';
+$string['field1069999'] = 'Health (Other)';
+$string['field107'] = 'Education';
+$string['field10701'] = 'Teacher Education';
+$string['field1070101'] = 'Teacher Education: Early Childhood';
+$string['field1070103'] = 'Teacher Education: Primary';
+$string['field1070105'] = 'Teacher Education: Secondary';
+$string['field1070107'] = 'Teacher-Librarianship';
+$string['field1070109'] = 'Teacher Education: Vocational Education and Training';
+$string['field1070111'] = 'Teacher Education: Higher Education';
+$string['field1070113'] = 'Teacher Education: Special Education';
+$string['field1070115'] = 'English As A Second Language Teaching';
+$string['field1070117'] = 'Nursing Education Teacher Training';
+$string['field1070199'] = 'Teacher Education (Other)';
+$string['field10703'] = 'Curriculum and Education Studies';
+$string['field1070301'] = 'Curriculum Studies';
+$string['field1070303'] = 'Education Studies';
+$string['field10799'] = 'Other Education';
+$string['field1079999'] = 'Education (Other)';
+$string['field108'] = 'Management and Commerce';
+$string['field10801'] = 'Accounting';
+$string['field1080101'] = 'Accounting';
+$string['field10803'] = 'Business and Management';
+$string['field1080301'] = 'Business Management';
+$string['field1080303'] = 'Human Resource Management';
+$string['field1080305'] = 'Personal Management Training';
+$string['field1080307'] = 'Organisation Management';
+$string['field1080309'] = 'Industrial Relations';
+$string['field1080311'] = 'International Business';
+$string['field1080313'] = 'Public and Health Care Administration';
+$string['field1080315'] = 'Project Management';
+$string['field1080317'] = 'Quality Management';
+$string['field1080319'] = 'Hospitality Management';
+$string['field1080321'] = 'Farm Management and Agribusiness';
+$string['field1080323'] = 'Tourism Management';
+$string['field1080399'] = 'Business and Management (Other)';
+$string['field10805'] = 'Sales and Marketing';
+$string['field1080501'] = 'Sales';
+$string['field1080503'] = 'Real Estate';
+$string['field1080505'] = 'Marketing';
+$string['field1080507'] = 'Advertising';
+$string['field1080509'] = 'Public Relations';
+$string['field1080599'] = 'Sales and Marketing (Other)';
+$string['field10807'] = 'Tourism';
+$string['field1080701'] = 'Tourism';
+$string['field10809'] = 'Office Studies';
+$string['field1080901'] = 'Secretarial and Clerical Studies';
+$string['field1080903'] = 'Keyboard Skills';
+$string['field1080905'] = 'Practical Computing Skills';
+$string['field1080999'] = 'Office Studies (Other)';
+$string['field10811'] = 'Banking, Finance and Related Fields';
+$string['field1081101'] = 'Banking and Finance';
+$string['field1081103'] = 'Insurance and Actuarial Studies';
+$string['field1081105'] = 'Investment and Securities';
+$string['field1081199'] = 'Banking, Finance and Related Fields (Other)';
+$string['field10899'] = 'Other Management and Commerce';
+$string['field1089901'] = 'Purchasing, Warehousing and Distribution';
+$string['field1089903'] = 'Valuation';
+$string['field1089999'] = 'Management and Commerce (Other)';
+$string['field109'] = 'Society and Culture';
+$string['field10901'] = 'Political Science and Policy Studies';
+$string['field1090101'] = 'Political Science';
+$string['field1090103'] = 'Policy Studies';
+$string['field10903'] = 'Studies In Human Society';
+$string['field1090301'] = 'Sociology';
+$string['field1090303'] = 'Anthropology';
+$string['field1090305'] = 'History';
+$string['field1090307'] = 'Archaeology';
+$string['field1090309'] = 'Human Geography';
+$string['field1090311'] = 'Indigenous Studies';
+$string['field1090313'] = 'Gender Specific Studies';
+$string['field1090399'] = 'Studies In Human Society (Other)';
+$string['field10905'] = 'Human Welfare Studies and Services';
+$string['field1090501'] = 'Social Work';
+$string['field1090503'] = 'Children\'S Services';
+$string['field1090505'] = 'Youth Work';
+$string['field1090507'] = 'Care For The Aged';
+$string['field1090509'] = 'Care For The Disabled';
+$string['field1090511'] = 'Residential Client Care';
+$string['field1090513'] = 'Counselling';
+$string['field1090515'] = 'Welfare Studies';
+$string['field1090599'] = 'Human Welfare Studies and Services (Other)';
+$string['field10907'] = 'Behavioural Science';
+$string['field1090701'] = 'Psychology';
+$string['field1090799'] = 'Behavioural Science (Other)';
+$string['field10909'] = 'Law';
+$string['field1090901'] = 'Business and Commercial Law';
+$string['field1090903'] = 'Constitutional Law';
+$string['field1090905'] = 'Criminal Law';
+$string['field1090907'] = 'Family Law';
+$string['field1090909'] = 'International Law';
+$string['field1090911'] = 'Taxation Law';
+$string['field1090913'] = 'Legal Practice';
+$string['field1090999'] = 'Law (Other)';
+$string['field10911'] = 'Justice and Law Enforcement';
+$string['field1091101'] = 'Justice Administration';
+$string['field1091103'] = 'Legal Studies';
+$string['field1091105'] = 'Police Studies';
+$string['field1091199'] = 'Justice and Law Enforcement (Other)';
+$string['field10913'] = 'Librarianship, Information Management and Curatorial Studies';
+$string['field1091301'] = 'Librarianship and Information Management';
+$string['field1091303'] = 'Curatorial Studies';
+$string['field10915'] = 'Language and Literature';
+$string['field1091501'] = 'English Language';
+$string['field1091503'] = 'Northern European Languages';
+$string['field1091505'] = 'Southern European Languages';
+$string['field1091507'] = 'Eastern European Languages';
+$string['field1091509'] = 'Southwest Asian and North African Languages';
+$string['field1091511'] = 'Southern Asian Languages';
+$string['field1091513'] = 'Southeast Asian Languages';
+$string['field1091515'] = 'Eastern Asian Languages';
+$string['field1091517'] = 'Australian Indigenous Languages';
+$string['field1091519'] = 'Translating and Interpreting';
+$string['field1091521'] = 'Linguistics';
+$string['field1091523'] = 'Literature';
+$string['field1091599'] = 'Language and Literature (Other)';
+$string['field10917'] = 'Philosophy and Religious Studies';
+$string['field1091701'] = 'Philosophy';
+$string['field1091703'] = 'Religious Studies';
+$string['field10919'] = 'Economics and Econometrics';
+$string['field1091901'] = 'Economics';
+$string['field1091903'] = 'Econometrics';
+$string['field10921'] = 'Sport and Recreation';
+$string['field1092101'] = 'Sport and Recreation Activities';
+$string['field1092103'] = 'Sports Coaching, Officiating and Instruction';
+$string['field1092199'] = 'Sport and Recreation (Other)';
+$string['field10999'] = 'Other Society and Culture';
+$string['field1099901'] = 'Family and Consumer Studies';
+$string['field1099903'] = 'Criminology';
+$string['field1099905'] = 'Security Services';
+$string['field1099999'] = 'Society and Culture (Other)';
+$string['field110'] = 'Creative Arts';
+$string['field11001'] = 'Performing Arts';
+$string['field1100101'] = 'Music';
+$string['field1100103'] = 'Drama and Theatre Studies';
+$string['field1100105'] = 'Dance';
+$string['field1100199'] = 'Performing Arts (Other)';
+$string['field11003'] = 'Visual Arts and Crafts';
+$string['field1100301'] = 'Fine Arts';
+$string['field1100303'] = 'Photography';
+$string['field1100305'] = 'Crafts';
+$string['field1100307'] = 'Jewellery Making';
+$string['field1100309'] = 'Floristry';
+$string['field1100399'] = 'Visual Arts and Crafts (Other)';
+$string['field11005'] = 'Graphic and Design Studies';
+$string['field1100501'] = 'Graphic Arts and Design Studies';
+$string['field1100503'] = 'Textile Design';
+$string['field1100505'] = 'Fashion Design';
+$string['field1100599'] = 'Graphic and Design Studies (Other)';
+$string['field11007'] = 'Communication and Media Studies';
+$string['field1100701'] = 'Audio Visual Studies';
+$string['field1100703'] = 'Journalism';
+$string['field1100705'] = 'Written Communication';
+$string['field1100707'] = 'Verbal Communication';
+$string['field1100799'] = 'Communication and Media Studies (Other)';
+$string['field11099'] = 'Other Creative Arts';
+$string['field1109999'] = 'Creative Arts (Other)';
+$string['field111'] = 'Food, Hospitality and Personal Services';
+$string['field11101'] = 'Food and Hospitality';
+$string['field1110101'] = 'Hospitality';
+$string['field1110103'] = 'Food and Beverage Service';
+$string['field1110105'] = 'Butchery';
+$string['field1110107'] = 'Baking and Pastrymaking';
+$string['field1110109'] = 'Cookery';
+$string['field1110111'] = 'Food Hygiene';
+$string['field1110199'] = 'Food and Hospitality (Other)';
+$string['field11103'] = 'Personal Services';
+$string['field1110301'] = 'Beauty Therapy';
+$string['field1110303'] = 'Hairdressing';
+$string['field1110399'] = 'Personal Services (Other)';
+$string['field112'] = 'Mixed Field Programmes';
+$string['field11201'] = 'General Education Programmes';
+$string['field1120101'] = 'General Primary and Secondary Education Programmes';
+$string['field1120103'] = 'Literacy and Numeracy Programmes';
+$string['field1120105'] = 'Learning Skills Programmes';
+$string['field1120199'] = 'General Education Programmes (Other)';
+$string['field11203'] = 'Social Skills Programmes';
+$string['field1120301'] = 'Social and Interpersonal Skills Programmes';
+$string['field1120303'] = 'Survival Skills Programmes';
+$string['field1120305'] = 'Parental Education Programmes';
+$string['field1120399'] = 'Social Skills Programmes (Other)';
+$string['field11205'] = 'Employment Skills Programmes';
+$string['field1120501'] = 'Career Development Programmes';
+$string['field1120503'] = 'Job Search Skills Programmes';
+$string['field1120505'] = 'Work Practices Programmes';
+$string['field1120599'] = 'Employment Skills Programmes (Other)';
+$string['field11299'] = 'Other Mixed Field Programmes';
+$string['field1129999'] = 'Mixed Field Programmes (Other)';
index cfa4f89..6bbb927 100644 (file)
@@ -71,11 +71,11 @@ $string['aggregationcoefextra_help'] = 'If the aggregation is \'Natural\' or \'S
 
 If the aggregation is \'Mean of grades (with extra credits)\' and the extra credit is set to a value greater than zero, the extra credit is the factor by which the grade is multiplied before adding it to the total after the computation of the mean.';
 $string['aggregationcoefextra_link'] = 'grade/aggregation';
-$string['aggregationcoefextrasum'] = 'Extra credit'; // for the form with checkboxes: Natural or Simple weighted mean
+$string['aggregationcoefextrasum'] = 'Extra credit'; // For the form with checkboxes: Natural or Simple weighted mean.
 $string['aggregationcoefextrasumabbr'] = '+';
 $string['aggregationcoefextrasum_help'] = 'If the extra credit checkbox is ticked, the grade item\'s maximum grade is not added to the category\'s maximum grade, resulting in the possibility of achieving the maximum grade (or grades over the maximum if enabled by the site administrator) in the category without having the maximum grade in all the grade items.';
 $string['aggregationcoefextrasum_link'] = 'grade/aggregation';
-$string['aggregationcoefextraweight'] = 'Extra credit weight'; // for the form with input: Mean of grades (with extra credits) only
+$string['aggregationcoefextraweight'] = 'Extra credit weight'; // For the form with input: Mean of grades (with extra credits) only.
 $string['aggregationcoefextraweight_help'] = 'If the extra credit weight is set to a value greater than zero, the grade acts as extra credit during aggregation. The number is the factor by which the grade is multiplied before adding it to the total for the computation of the mean.';
 $string['aggregationcoefextraweight_link'] = 'grade/aggregation';
 $string['aggregationcoefweight'] = 'Item weight';
@@ -251,7 +251,7 @@ $string['gradeboundary_help'] = 'This setting determines the minimum percentage
 $string['gradecategories'] = 'Grade categories';
 $string['gradecategory'] = 'Grade category';
 $string['gradecategoryonmodform'] = 'Grade category';
-$string['gradecategoryonmodform_help'] =  'This setting controls the category in which this activity\'s grades are placed in the gradebook.';
+$string['gradecategoryonmodform_help'] = 'This setting controls the category in which this activity\'s grades are placed in the gradebook.';
 $string['gradecategorysettings'] = 'Grade category settings';
 $string['gradedisplay'] = 'Grade display';
 $string['gradedisplaytype'] = 'Grade display type';
index 3b5d5c8..76c1b81 100644 (file)
@@ -98,7 +98,7 @@ $string['errorcronnoxmlrpc'] = 'XML-RPC must be enabled in order to update the r
 $string['errorhublisting'] = 'An error occurred when retrieving the hub listing from Moodle. Please try again later. ({$a})';
 $string['errorlangnotrecognized'] = 'The provided language code is unknown by Moodle. Please contact {$a}';
 $string['errorregistration'] = 'An error occurred during registration, please try again later. ({$a})';
-$string['errorunpublishcourses']= 'Due to an unexpected error, the courses could not be deleted on the hub. Try again later (recommended) or contact the hub administrator.';
+$string['errorunpublishcourses'] = 'Due to an unexpected error, the courses could not be deleted on the hub. Try again later (recommended) or contact the hub administrator.';
 $string['existingscreenshotnumber'] = '{$a} existing screenshots. You will be able to see these screenshots on this page, only once the hub administrator enables your course.';
 $string['existingscreenshots'] = 'Existing screenshots';
 $string['forceunregister'] = 'Yes, clean registration data';
index 9b92f32..59d6080 100644 (file)
@@ -738,6 +738,7 @@ $string['eventcourseresetended'] = 'Course reset ended';
 $string['eventcourseresetstarted'] = 'Course reset started';
 $string['eventcourserestored'] = 'Course restored';
 $string['eventcourseupdated'] = 'Course updated';
+$string['eventcoursesectiondeleted'] = 'Course section deleted';
 $string['eventcoursesectionupdated'] = 'Course section updated';
 $string['eventcoursemoduleinstancelistviewed'] = 'Course module instance list viewed';
 $string['eventcourseuserreportviewed'] = 'Course user report viewed';
@@ -834,7 +835,7 @@ $string['frontpageformatloggedin'] = 'Front page format when logged in';
 $string['frontpagenews'] = 'News items';
 $string['frontpagesettings'] = 'Front page settings';
 $string['fulllistofcourses'] = 'All courses';
-$string['fullname'] = 'Full name'; // @deprecated - use fullnamecourse or fullnameuser or some own context specific string
+$string['fullname'] = 'Full name'; /* @deprecated - Use fullnamecourse or fullnameuser or some own context specific string. */
 $string['fullnamecourse'] = 'Course full name';
 $string['fullnamecourse_help'] = 'The full name of the course is displayed at the top of each page in the course and in the list of courses.';
 $string['fullnamedisplay'] = '{$a->firstname} {$a->lastname}';
@@ -1606,7 +1607,7 @@ $string['scalestandard_link'] = 'grade/scale';
 $string['scalestip'] = 'To create custom scales, use the \'Scales...\' link in your course administration menu.';
 $string['scalestip2'] = 'To create custom scales, click the Grades link in the course administration menu, then choose Edit, Scales.';
 $string['screenshot'] = 'Screenshot';
-$string['search'] = 'Search'; // TODO MDL-34652 rename to searchforums and move to mod_forum
+$string['search'] = 'Search'; // TODO MDL-34652 rename to searchforums and move to mod_forum.
 $string['search_help'] = 'For basic searching of one or more words anywhere in the text, just type them separated by spaces. All words longer than two characters are used.
 
 For advanced searching, press the search button without typing anything in the search box to access the advanced search form.';
@@ -1674,7 +1675,7 @@ $string['setcategorytheme'] = 'Set category theme';
 $string['setpassword'] = 'Set password';
 $string['setpasswordinstructions'] = 'Please enter and repeat your new password below, then click "Set password". <br />Your new password will be saved, and you will be logged in.';
 $string['settings'] = 'Settings';
-$string['shortname'] = 'Short name'; // @deprecated MDL-34652 - use shortnamecourse or shortnameuser or some own context specific string
+$string['shortname'] = 'Short name'; /* @deprecated MDL-34652 - Use shortnamecourse or shortnameuser or some own context specific string. */
 $string['shortnamecollisionwarning'] = '[*] = This shortname is already in use by a course and will need to be changed upon approval';
 $string['shortnamecourse'] = 'Course short name';
 $string['shortnamecourse_help'] = 'The short name of the course is displayed in the navigation and is used in the subject line of course email messages.';
index 213eaea..723b28d 100644 (file)
@@ -24,7 +24,7 @@
 
 $string['availableplugins'] = 'Available plugins';
 $string['configplagiarismplugins'] = 'Please choose the plagiarism plugin you would like to configure';
-$string['enableplagiarism'] ='Enable plagiarism plugins';
+$string['enableplagiarism'] = 'Enable plagiarism plugins';
 $string['configenableplagiarism'] = 'This will allow administrators to configure plagiarism plugins (if installed)';
 $string['manageplagiarism'] = 'Manage plagiarism plugins';
 $string['nopluginsinstalled'] = 'No plagiarism plugins are installed.';
index 845f099..60e4628 100644 (file)
@@ -32,7 +32,7 @@ $string['addplugin'] = 'Add a repository plugin';
 $string['allowexternallinks'] = 'Allow external links';
 $string['areamainfile'] = 'Main file';
 $string['coursebackup'] = 'Course backups';
-$string['pluginname'] = 'Repository plugin name'; // todo fix this, this string identifier is reserved
+$string['pluginname'] = 'Repository plugin name'; // Todo fix this, this string identifier is reserved.
 $string['pluginnamehelp'] = 'If you leave this empty the default name will be used.';
 $string['sectionbackup'] = 'Section backups';
 $string['activitybackup'] = 'Activity backup';
index 4b2ee09..f429e4c 100644 (file)
@@ -141,6 +141,7 @@ $string['course:managescales'] = 'Manage scales';
 $string['course:markcomplete'] = 'Mark users as complete in course completion';
 $string['course:movesections'] = 'Move sections';
 $string['course:publish'] = 'Publish a course into hub';
+$string['course:renameroles'] = 'Rename roles';
 $string['course:request'] = 'Request new courses';
 $string['course:reset'] = 'Reset course';
 $string['course:reviewotherusers'] = 'Review other users';
index 4475357..f50c23f 100644 (file)
@@ -6222,7 +6222,7 @@ class context_system extends context {
      * Returns system context instance.
      *
      * @static
-     * @param int $instanceid
+     * @param int $instanceid should be 0
      * @param int $strictness
      * @param bool $cache
      * @return context_system context instance
@@ -6474,19 +6474,19 @@ class context_user extends context {
      * Returns user context instance.
      *
      * @static
-     * @param int $instanceid
+     * @param int $userid id from {user} table
      * @param int $strictness
      * @return context_user context instance
      */
-    public static function instance($instanceid, $strictness = MUST_EXIST) {
+    public static function instance($userid, $strictness = MUST_EXIST) {
         global $DB;
 
-        if ($context = context::cache_get(CONTEXT_USER, $instanceid)) {
+        if ($context = context::cache_get(CONTEXT_USER, $userid)) {
             return $context;
         }
 
-        if (!$record = $DB->get_record('context', array('contextlevel'=>CONTEXT_USER, 'instanceid'=>$instanceid))) {
-            if ($user = $DB->get_record('user', array('id'=>$instanceid, 'deleted'=>0), 'id', $strictness)) {
+        if (!$record = $DB->get_record('context', array('contextlevel' => CONTEXT_USER, 'instanceid' => $userid))) {
+            if ($user = $DB->get_record('user', array('id' => $userid, 'deleted' => 0), 'id', $strictness)) {
                 $record = context::insert_context_record(CONTEXT_USER, $user->id, '/'.SYSCONTEXTID, 0);
             }
         }
@@ -6652,19 +6652,19 @@ class context_coursecat extends context {
      * Returns course category context instance.
      *
      * @static
-     * @param int $instanceid
+     * @param int $categoryid id from {course_categories} table
      * @param int $strictness
      * @return context_coursecat context instance
      */
-    public static function instance($instanceid, $strictness = MUST_EXIST) {
+    public static function instance($categoryid, $strictness = MUST_EXIST) {
         global $DB;
 
-        if ($context = context::cache_get(CONTEXT_COURSECAT, $instanceid)) {
+        if ($context = context::cache_get(CONTEXT_COURSECAT, $categoryid)) {
             return $context;
         }
 
-        if (!$record = $DB->get_record('context', array('contextlevel'=>CONTEXT_COURSECAT, 'instanceid'=>$instanceid))) {
-            if ($category = $DB->get_record('course_categories', array('id'=>$instanceid), 'id,parent', $strictness)) {
+        if (!$record = $DB->get_record('context', array('contextlevel' => CONTEXT_COURSECAT, 'instanceid' => $categoryid))) {
+            if ($category = $DB->get_record('course_categories', array('id' => $categoryid), 'id,parent', $strictness)) {
                 if ($category->parent) {
                     $parentcontext = context_coursecat::instance($category->parent);
                     $record = context::insert_context_record(CONTEXT_COURSECAT, $category->id, $parentcontext->path);
@@ -6906,19 +6906,19 @@ class context_course extends context {
      * Returns course context instance.
      *
      * @static
-     * @param int $instanceid
+     * @param int $courseid id from {course} table
      * @param int $strictness
      * @return context_course context instance
      */
-    public static function instance($instanceid, $strictness = MUST_EXIST) {
+    public static function instance($courseid, $strictness = MUST_EXIST) {
         global $DB;
 
-        if ($context = context::cache_get(CONTEXT_COURSE, $instanceid)) {
+        if ($context = context::cache_get(CONTEXT_COURSE, $courseid)) {
             return $context;
         }
 
-        if (!$record = $DB->get_record('context', array('contextlevel'=>CONTEXT_COURSE, 'instanceid'=>$instanceid))) {
-            if ($course = $DB->get_record('course', array('id'=>$instanceid), 'id,category', $strictness)) {
+        if (!$record = $DB->get_record('context', array('contextlevel' => CONTEXT_COURSE, 'instanceid' => $courseid))) {
+            if ($course = $DB->get_record('course', array('id' => $courseid), 'id,category', $strictness)) {
                 if ($course->category) {
                     $parentcontext = context_coursecat::instance($course->category);
                     $record = context::insert_context_record(CONTEXT_COURSE, $course->id, $parentcontext->path);
@@ -7167,19 +7167,19 @@ class context_module extends context {
      * Returns module context instance.
      *
      * @static
-     * @param int $instanceid
+     * @param int $cmid id of the record from {course_modules} table; pass cmid there, NOT id in the instance column
      * @param int $strictness
      * @return context_module context instance
      */
-    public static function instance($instanceid, $strictness = MUST_EXIST) {
+    public static function instance($cmid, $strictness = MUST_EXIST) {
         global $DB;
 
-        if ($context = context::cache_get(CONTEXT_MODULE, $instanceid)) {
+        if ($context = context::cache_get(CONTEXT_MODULE, $cmid)) {
             return $context;
         }
 
-        if (!$record = $DB->get_record('context', array('contextlevel'=>CONTEXT_MODULE, 'instanceid'=>$instanceid))) {
-            if ($cm = $DB->get_record('course_modules', array('id'=>$instanceid), 'id,course', $strictness)) {
+        if (!$record = $DB->get_record('context', array('contextlevel' => CONTEXT_MODULE, 'instanceid' => $cmid))) {
+            if ($cm = $DB->get_record('course_modules', array('id' => $cmid), 'id,course', $strictness)) {
                 $parentcontext = context_course::instance($cm->course);
                 $record = context::insert_context_record(CONTEXT_MODULE, $cm->id, $parentcontext->path);
             }
@@ -7379,19 +7379,19 @@ class context_block extends context {
      * Returns block context instance.
      *
      * @static
-     * @param int $instanceid
+     * @param int $blockinstanceid id from {block_instances} table.
      * @param int $strictness
      * @return context_block context instance
      */
-    public static function instance($instanceid, $strictness = MUST_EXIST) {
+    public static function instance($blockinstanceid, $strictness = MUST_EXIST) {
         global $DB;
 
-        if ($context = context::cache_get(CONTEXT_BLOCK, $instanceid)) {
+        if ($context = context::cache_get(CONTEXT_BLOCK, $blockinstanceid)) {
             return $context;
         }
 
-        if (!$record = $DB->get_record('context', array('contextlevel'=>CONTEXT_BLOCK, 'instanceid'=>$instanceid))) {
-            if ($bi = $DB->get_record('block_instances', array('id'=>$instanceid), 'id,parentcontextid', $strictness)) {
+        if (!$record = $DB->get_record('context', array('contextlevel' => CONTEXT_BLOCK, 'instanceid' => $blockinstanceid))) {
+            if ($bi = $DB->get_record('block_instances', array('id' => $blockinstanceid), 'id,parentcontextid', $strictness)) {
                 $parentcontext = context::instance_by_id($bi->parentcontextid);
                 $record = context::insert_context_record(CONTEXT_BLOCK, $bi->id, $parentcontext->path);
             }
index 5b663e8..f8bd0d8 100644 (file)
Binary files a/lib/amd/build/form-autocomplete.min.js and b/lib/amd/build/form-autocomplete.min.js differ
index ed6ed9c..ec3fb43 100644 (file)
@@ -37,9 +37,6 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
         UP: 38
     };
 
-    /** @var {Number} closeSuggestionsTimer - integer used to cancel window.setTimeout. */
-    var closeSuggestionsTimer = null;
-
     /**
      * Make an item in the selection list "active".
      *
@@ -568,15 +565,12 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
                 createItem(options, state, originalSelect);
             }
         });
-        inputElement.on('blur focus', function(e) {
-            // We may be blurring because we have clicked on the suggestion list. We
-            // dont want to close the selection list before the click event fires, so
-            // we have to delay.
-            if (closeSuggestionsTimer) {
-                window.clearTimeout(closeSuggestionsTimer);
-            }
-            closeSuggestionsTimer = window.setTimeout(function() {
-                if (e.type == 'blur') {
+        inputElement.on('blur', function() {
+            window.setTimeout(function() {
+                // Get the current element with focus.
+                var focusElement = $(document.activeElement);
+                // Only close the menu if the input hasn't regained focus.
+                if (focusElement.attr('id') != inputElement.attr('id')) {
                     if (options.tags) {
                         createItem(options, state, originalSelect);
                     }
@@ -589,9 +583,6 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
             arrowElement.on('click', function() {
                 // Prevent the close timer, or we will open, then close the suggestions.
                 inputElement.focus();
-                if (closeSuggestionsTimer) {
-                    window.clearTimeout(closeSuggestionsTimer);
-                }
                 // Show the suggestions list.
                 updateSuggestions(options, state, inputElement.val(), originalSelect);
             });
diff --git a/lib/classes/event/course_section_deleted.php b/lib/classes/event/course_section_deleted.php
new file mode 100644 (file)
index 0000000..e73f144
--- /dev/null
@@ -0,0 +1,101 @@
+<?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/>.
+
+/**
+ * Course section deleted event.
+ *
+ * @package    core
+ * @copyright  2015 Ruslan Kabalin, Lancaster University.
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace core\event;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Course section deleted event class.
+ *
+ * @property-read array $other {
+ *      Extra information about event.
+ *
+ *      - int sectionnum: section number.
+ *      - string sectionname: section name.
+ * }
+ *
+ * @package    core
+ * @since      Moodle 3.1
+ * @copyright  2015 Ruslan Kabalin, Lancaster University.
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class course_section_deleted extends base {
+
+    /**
+     * Init method.
+     *
+     * @return void
+     */
+    protected function init() {
+        $this->data['objecttable'] = 'course_sections';
+        $this->data['crud'] = 'd';
+        $this->data['edulevel'] = self::LEVEL_TEACHING;
+    }
+
+    /**
+     * Return localised event name.
+     *
+     * @return string
+     */
+    public static function get_name() {
+        return get_string('eventcoursesectiondeleted');
+    }
+
+    /**
+     * Returns non-localised event description with id's for admin use only.
+     *
+     * @return string
+     */
+    public function get_description() {
+        return "The user with id '$this->userid' deleted section number '{$this->other['sectionnum']}' " .
+                "(section name '{$this->other['sectionname']}') for the course with id '$this->courseid'";
+    }
+
+    /**
+     * Return legacy data for add_to_log().
+     *
+     * @return array
+     */
+    protected function get_legacy_logdata() {
+        return array($this->courseid, 'course', 'delete section', 'view.php?id=' . $this->courseid, $this->other['sectionnum']);
+    }
+
+    /**
+     * Custom validation.
+     *
+     * @throws \coding_exception
+     * @return void
+     */
+    protected function validate_data() {
+        parent::validate_data();
+
+        if (!isset($this->other['sectionnum'])) {
+            throw new \coding_exception('The \'sectionnum\' value must be set in other.');
+        }
+        if (!isset($this->other['sectionname'])) {
+            throw new \coding_exception('The \'sectionname\' value must be set in other.');
+        }
+    }
+}
index 5e81a57..9e999c2 100644 (file)
@@ -23,6 +23,8 @@
  */
 namespace core\plugininfo;
 
+use part_of_admin_tree, admin_settingpage;
+
 defined('MOODLE_INTERNAL') || die();
 
 /**
index 7dac929..ccc30c9 100644 (file)
@@ -982,6 +982,16 @@ $capabilities = array(
         'clonepermissionsfrom' => 'moodle/course:update'
     ),
 
+    'moodle/course:renameroles' => array(
+        'captype' => 'write',
+        'contextlevel' => CONTEXT_COURSE,
+        'archetypes' => array(
+            'editingteacher' => CAP_ALLOW,
+            'manager' => CAP_ALLOW
+        ),
+        'clonepermissionsfrom' => 'moodle/course:update'
+    ),
+
     'moodle/course:changeidnumber' => array(
 
         'riskbitmask' => RISK_XSS,
index 8060d7f..3bf4a07 100644 (file)
@@ -906,6 +906,15 @@ $functions = array(
         'capabilities'  => '',
     ),
 
+    'core_message_delete_message' => array(
+        'classname'     => 'core_message_external',
+        'methodname'    => 'delete_message',
+        'classpath'     => 'message/externallib.php',
+        'description'   => 'Deletes a message.',
+        'type'          => 'write',
+        'capabilities'  => 'moodle/site:deleteownmessage',
+    ),
+
     // === notes related functions ===
 
     'moodle_notes_create_notes' => array(
@@ -1160,6 +1169,7 @@ $services = array(
             'core_enrol_get_enrolled_users',
             'core_enrol_get_course_enrolment_methods',
             'enrol_self_enrol_user',
+            'enrol_self_get_instance_info',
             'core_user_get_users_by_id',
             'core_webservice_get_site_info',
             'core_notes_create_notes',
@@ -1216,6 +1226,7 @@ $services = array(
             'core_completion_get_course_completion_status',
             'core_user_view_user_list',
             'core_message_mark_message_read',
+            'core_message_delete_message',
             'core_notes_view_notes',
             'mod_forum_view_forum_discussion',
             'core_user_view_user_profile',
index ba14c13..21c5cda 100644 (file)
Binary files a/lib/editor/atto/plugins/rtl/yui/build/moodle-atto_rtl-button/moodle-atto_rtl-button-debug.js and b/lib/editor/atto/plugins/rtl/yui/build/moodle-atto_rtl-button/moodle-atto_rtl-button-debug.js differ
index 51d0be4..21c5088 100644 (file)
Binary files a/lib/editor/atto/plugins/rtl/yui/build/moodle-atto_rtl-button/moodle-atto_rtl-button-min.js and b/lib/editor/atto/plugins/rtl/yui/build/moodle-atto_rtl-button/moodle-atto_rtl-button-min.js differ
index ba14c13..21c5cda 100644 (file)
Binary files a/lib/editor/atto/plugins/rtl/yui/build/moodle-atto_rtl-button/moodle-atto_rtl-button.js and b/lib/editor/atto/plugins/rtl/yui/build/moodle-atto_rtl-button/moodle-atto_rtl-button.js differ
index 2fb1543..88bf7c0 100644 (file)
@@ -63,7 +63,7 @@ Y.namespace('M.atto_rtl').Button = Y.Base.create('button', Y.M.editor_atto.Edito
      */
     _toggleRTL: function(e, direction) {
         var host = this.get('host'),
-            sourceSelection = rangy.saveSelection(),
+            sourceSelection = window.rangy.saveSelection(),
             selection = host.getSelection(),
             newDirection = {
                 rtl: 'ltr',
@@ -82,7 +82,7 @@ Y.namespace('M.atto_rtl').Button = Y.Base.create('button', Y.M.editor_atto.Edito
             }
 
             // Change selection from the containing paragraph to the original one.
-            rangy.restoreSelection(sourceSelection);
+            window.rangy.restoreSelection(sourceSelection);
             // Mark the text as having been updated.
             this.markUpdated();
         }
index 7fd7e52..002d572 100644 (file)
@@ -964,21 +964,28 @@ class external_util {
      * Validate a list of courses, returning the complete course objects for valid courses.
      *
      * @param  array $courseids A list of course ids
+     * @param  array $courses   An array of courses already pre-fetched, indexed by course id.
      * @return array            An array of courses and the validation warnings
      */
-    public static function validate_courses($courseids) {
+    public static function validate_courses($courseids, $courses = array()) {
         // Delete duplicates.
         $courseids = array_unique($courseids);
-        $courses = array();
         $warnings = array();
 
+        // Remove courses which are not even requested.
+        $courses =  array_intersect_key($courses, array_flip($courseids));
+
         foreach ($courseids as $cid) {
             // Check the user can function in this context.
             try {
                 $context = context_course::instance($cid);
                 external_api::validate_context($context);
-                $courses[$cid] = get_course($cid);
+
+                if (!isset($courses[$cid])) {
+                    $courses[$cid] = get_course($cid);
+                }
             } catch (Exception $e) {
+                unset($courses[$cid]);
                 $warnings[] = array(
                     'item' => 'course',
                     'itemid' => $cid,
index c832f61..9290814 100644 (file)
@@ -1521,7 +1521,6 @@ class file_storage {
 
         $width    = $imageinfo['width'];
         $height   = $imageinfo['height'];
-        $mimetype = $imageinfo['mimetype'];
 
         if ($keepaspectratio) {
             if (0 >= $newwidth and 0 >= $newheight) {
@@ -1553,15 +1552,53 @@ class file_storage {
             }
         }
 
+        // The original image.
         $img = imagecreatefromstring($file->get_content());
+
+        // A new true color image where we will copy our original image.
+        $newimg = imagecreatetruecolor($newwidth, $newheight);
+
+        // Determine if the file supports transparency.
+        $hasalpha = $filerecord['mimetype'] == 'image/png' || $filerecord['mimetype'] == 'image/gif';
+
+        // Maintain transparency.
+        if ($hasalpha) {
+            imagealphablending($newimg, true);
+
+            // Get the current transparent index for the original image.
+            $colour = imagecolortransparent($img);
+            if ($colour == -1) {
+                // Set a transparent colour index if there's none.
+                $colour = imagecolorallocatealpha($newimg, 255, 255, 255, 127);
+                // Save full alpha channel.
+                imagesavealpha($newimg, true);
+            }
+            imagecolortransparent($newimg, $colour);
+            imagefill($newimg, 0, 0, $colour);
+        }
+
+        // Process the image to be output.
         if ($height != $newheight or $width != $newwidth) {
-            $newimg = imagecreatetruecolor($newwidth, $newheight);
-            if (!imagecopyresized($newimg, $img, 0, 0, 0, 0, $newwidth, $newheight, $width, $height)) {
+            // Resample if the dimensions differ from the original.
+            if (!imagecopyresampled($newimg, $img, 0, 0, 0, 0, $newwidth, $newheight, $width, $height)) {
                 // weird
                 throw new file_exception('storedfileproblem', 'Can not resize image');
             }
             imagedestroy($img);
             $img = $newimg;
+
+        } else if ($hasalpha) {
+            // Just copy to the new image with the alpha channel.
+            if (!imagecopy($newimg, $img, 0, 0, 0, 0, $width, $height)) {
+                // Weird.
+                throw new file_exception('storedfileproblem', 'Can not copy image');
+            }
+            imagedestroy($img);
+            $img = $newimg;
+
+        } else {
+            // No particular processing needed for the original image.
+            imagedestroy($newimg);
         }
 
         ob_start();
@@ -1580,6 +1617,11 @@ class file_storage {
 
             case 'image/png':
                 $quality = (int)$quality;
+
+                // Woah nelly! Because PNG quality is in the range 0 - 9 compared to JPEG quality,
+                // the latter of which can go to 100, we need to make sure that quality here is
+                // in a safe range or PHP WILL CRASH AND DIE. You have been warned.
+                $quality = $quality > 9 ? (int)(max(1.0, (float)$quality / 100.0) * 9.0) : $quality;
                 imagepng($img, NULL, $quality, NULL);
                 break;
 
@@ -2154,7 +2196,7 @@ class file_storage {
         $now = time();
         foreach ($rs as $record) {
             $this->update_references($record->id, $now, null,
-                    $storedfile->get_contenthash(), $storedfile->get_filesize(), 0);
+                    $storedfile->get_contenthash(), $storedfile->get_filesize(), 0, $storedfile->get_timemodified());
         }
         $rs->close();
     }
@@ -2372,8 +2414,9 @@ class file_storage {
      * @param string $contenthash
      * @param int $filesize
      * @param int $status 0 if ok or 666 if source is missing
+     * @param int $timemodified last time modified of the source, if known
      */
-    public function update_references($referencefileid, $lastsync, $lifetime, $contenthash, $filesize, $status) {
+    public function update_references($referencefileid, $lastsync, $lifetime, $contenthash, $filesize, $status, $timemodified = null) {
         global $DB;
         $referencefileid = clean_param($referencefileid, PARAM_INT);
         $lastsync = clean_param($lastsync, PARAM_INT);
@@ -2383,9 +2426,10 @@ class file_storage {
         $params = array('contenthash' => $contenthash,
                     'filesize' => $filesize,
                     'status' => $status,
-                    'referencefileid' => $referencefileid);
+                    'referencefileid' => $referencefileid,
+                    'timemodified' => $timemodified);
         $DB->execute('UPDATE {files} SET contenthash = :contenthash, filesize = :filesize,
-            status = :status
+            status = :status ' . ($timemodified ? ', timemodified = :timemodified' : '') . '
             WHERE referencefileid = :referencefileid', $params);
         $data = array('id' => $referencefileid, 'lastsync' => $lastsync);
         $DB->update_record('files_reference', (object)$data);
index a959baf..b115f96 100644 (file)
@@ -961,8 +961,9 @@ class stored_file {
      * @param null|string $contenthash if set to null contenthash is not changed
      * @param int $filesize new size of the file
      * @param int $status new status of the file (0 means OK, 666 - source missing)
+     * @param int $timemodified last time modified of the source, if known
      */
-    public function set_synchronized($contenthash, $filesize, $status = 0) {
+    public function set_synchronized($contenthash, $filesize, $status = 0, $timemodified = null) {
         if (!$this->is_external_file()) {
             return;
         }
@@ -974,12 +975,15 @@ class stored_file {
             $oldcontenthash = $this->file_record->contenthash;
         }
         // this will update all entries in {files} that have the same filereference id
-        $this->fs->update_references($this->file_record->referencefileid, $now, null, $contenthash, $filesize, $status);
+        $this->fs->update_references($this->file_record->referencefileid, $now, null, $contenthash, $filesize, $status, $timemodified);
         // we don't need to call update() for this object, just set the values of changed fields
         $this->file_record->contenthash = $contenthash;
         $this->file_record->filesize = $filesize;
         $this->file_record->status = $status;
         $this->file_record->referencelastsync = $now;
+        if ($timemodified) {
+            $this->file_record->timemodified = $timemodified;
+        }
         if (isset($oldcontenthash)) {
             $this->fs->deleted_file_cleanup($oldcontenthash);
         }
index 9c7ff1d..95d6065 100644 (file)
@@ -986,6 +986,69 @@ class core_files_file_storage_testcase extends advanced_testcase {
         $this->assertInstanceOf('stored_file', $converted);
     }
 
+    public function test_convert_image_png() {
+        global $CFG;
+
+        $this->resetAfterTest(false);
+
+        $filepath = $CFG->dirroot.'/lib/filestorage/tests/fixtures/testimage.png';
+        $syscontext = context_system::instance();
+        $filerecord = array(
+            'contextid' => $syscontext->id,
+            'component' => 'core',
+            'filearea'  => 'unittest',
+            'itemid'    => 0,
+            'filepath'  => '/images/',
+            'filename'  => 'testimage.png',
+        );
+
+        $fs = get_file_storage();
+        $original = $fs->create_file_from_pathname($filerecord, $filepath);
+
+        // Vanilla test.
+        $filerecord['filename'] = 'testimage-converted-nosize.png';
+        $vanilla = $fs->convert_image($filerecord, $original);
+        $this->assertInstanceOf('stored_file', $vanilla);
+        // Assert that byte 25 has the ascii value 6 for PNG-24.
+        $this->assertTrue(ord(substr($vanilla->get_content(), 25, 1)) == 6);
+
+        // 10x10 resize test; also testing for a ridiculous quality setting, which
+        // we should if necessary scale to the 0 - 9 range.
+        $filerecord['filename'] = 'testimage-converted-10x10.png';
+        $converted = $fs->convert_image($filerecord, $original, 10, 10, true, 100);
+        $this->assertInstanceOf('stored_file', $converted);
+        // Assert that byte 25 has the ascii value 6 for PNG-24.
+        $this->assertTrue(ord(substr($converted->get_content(), 25, 1)) == 6);
+
+        // Transparency test.
+        $filerecord['filename'] = 'testimage-converted-102x31.png';
+        $converted = $fs->convert_image($filerecord, $original, 102, 31, true, 9);
+        $this->assertInstanceOf('stored_file', $converted);
+        // Assert that byte 25 has the ascii value 6 for PNG-24.
+        $this->assertTrue(ord(substr($converted->get_content(), 25, 1)) == 6);
+
+        $originalfile = imagecreatefromstring($original->get_content());
+        $convertedfile = imagecreatefromstring($converted->get_content());
+        $vanillafile = imagecreatefromstring($vanilla->get_content());
+
+        $originalcolors = imagecolorsforindex($originalfile, imagecolorat($originalfile, 0, 0));
+        $convertedcolors = imagecolorsforindex($convertedfile, imagecolorat($convertedfile, 0, 0));
+        $vanillacolors = imagecolorsforindex($vanillafile, imagecolorat($vanillafile, 0, 0));
+        $this->assertEquals(count($originalcolors), 4);
+        $this->assertEquals(count($convertedcolors), 4);
+        $this->assertEquals(count($vanillacolors), 4);
+        $this->assertEquals($originalcolors['red'], $convertedcolors['red']);
+        $this->assertEquals($originalcolors['green'], $convertedcolors['green']);
+        $this->assertEquals($originalcolors['blue'], $convertedcolors['blue']);
+        $this->assertEquals($originalcolors['alpha'], $convertedcolors['alpha']);
+        $this->assertEquals($originalcolors['red'], $vanillacolors['red']);
+        $this->assertEquals($originalcolors['green'], $vanillacolors['green']);
+        $this->assertEquals($originalcolors['blue'], $vanillacolors['blue']);
+        $this->assertEquals($originalcolors['alpha'], $vanillacolors['alpha']);
+        $this->assertEquals($originalcolors['alpha'], 127);
+
+    }
+
     private function generate_file_record() {
         $syscontext = context_system::instance();
         $filerecord = new stdClass();
diff --git a/lib/filestorage/tests/fixtures/testimage.png b/lib/filestorage/tests/fixtures/testimage.png
new file mode 100644 (file)
index 0000000..791ad6e
Binary files /dev/null and b/lib/filestorage/tests/fixtures/testimage.png differ
index ba89fd2..0c23431 100644 (file)
@@ -463,7 +463,7 @@ class filterobject {
      * @param bool $fullmatch
      * @param mixed $replacementphrase
      */
-    function filterobject($phrase, $hreftagbegin = '<span class="highlight">',
+    public function __construct($phrase, $hreftagbegin = '<span class="highlight">',
                                    $hreftagend = '</span>',
                                    $casesensitive = false,
                                    $fullmatch = false,
index 8bc4a91..9385451 100644 (file)
@@ -2342,6 +2342,15 @@ class global_navigation extends navigation_node {
                 }
             }
 
+            // Let plugins hook into user navigation.
+            $pluginsfunction = get_plugins_with_function('extend_navigation_user', 'lib.php');
+            foreach ($pluginsfunction as $plugintype => $plugins) {
+                if ($plugintype != 'report') {
+                    foreach ($plugins as $pluginfunction) {
+                        $pluginfunction($usernode, $user, $usercontext, $course, $coursecontext);
+                    }
+                }
+            }
         }
         return true;
     }
@@ -4306,6 +4315,17 @@ class settings_navigation extends navigation_node {
                 }
                 $dashboard->add(get_string('grades', 'grades'), $url, self::TYPE_SETTING, null, 'mygrades');
             }
+
+            // Let plugins hook into user navigation.
+            $pluginsfunction = get_plugins_with_function('extend_navigation_user', 'lib.php');
+            foreach ($pluginsfunction as $plugintype => $plugins) {
+                if ($plugintype != 'report') {
+                    foreach ($plugins as $pluginfunction) {
+                        $pluginfunction($profilenode, $user, $usercontext, $course, $coursecontext);
+                    }
+                }
+            }
+
             $usersetting = navigation_node::create(get_string('preferences', 'moodle'), $prefurl, self::TYPE_CONTAINER, null, $key);
             $dashboard->add_node($usersetting);
         } else {
index a0e38e3..4e07072 100644 (file)
@@ -16,6 +16,8 @@ Our changes:
 * Added the following require_once() to the test files:
     global $CFG;
     require_once($CFG->dirroot . '/lib/password_compat/lib/password.php');
+* tests/PasswordHashTest.php supresses debugging from using salt in password_hash()
+  see MDL-52283
 
 Library description:
 ====================
index 31435bc..261d6df 100644 (file)
@@ -19,12 +19,12 @@ class PasswordHashTest extends PHPUnit_Framework_TestCase {
     }
 
     public function testKnownSalt() {
-        $hash = password_hash("rasmuslerdorf", PASSWORD_BCRYPT, array("cost" => 7, "salt" => "usesomesillystringforsalt"));
+        $hash = @password_hash("rasmuslerdorf", PASSWORD_BCRYPT, array("cost" => 7, "salt" => "usesomesillystringforsalt"));
         $this->assertEquals('$2y$07$usesomesillystringfore2uDLvp1Ii2e./U9C8sBjqp8I90dH6hi', $hash);
     }
 
     public function testRawSalt() {
-        $hash = password_hash("test", PASSWORD_BCRYPT, array("salt" => "123456789012345678901" . chr(0)));
+        $hash = @password_hash("test", PASSWORD_BCRYPT, array("salt" => "123456789012345678901" . chr(0)));
         if (version_compare(PHP_VERSION, '5.5.0', '<')) {
             $this->assertEquals('$2y$10$KRGxLBS0Lxe3KBCwKxOzLexLDeu0ZfqJAKTubOfy7O/yL2hjimw3u', $hash);
         } else {
@@ -33,12 +33,12 @@ class PasswordHashTest extends PHPUnit_Framework_TestCase {
     }
 
     public function testNullBehavior() {
-        $hash = password_hash(null, PASSWORD_BCRYPT, array("salt" => "1234567890123456789012345678901234567890"));
+        $hash = @password_hash(null, PASSWORD_BCRYPT, array("salt" => "1234567890123456789012345678901234567890"));
         $this->assertEquals('$2y$10$123456789012345678901uhihPb9QpE2n03zMu9TDdvO34jDn6mO.', $hash);
     }
 
     public function testIntegerBehavior() {
-        $hash = password_hash(12345, PASSWORD_BCRYPT, array("salt" => "1234567890123456789012345678901234567890"));
+        $hash = @password_hash(12345, PASSWORD_BCRYPT, array("salt" => "1234567890123456789012345678901234567890"));
         $this->assertEquals('$2y$10$123456789012345678901ujczD5TiARVFtc68bZCAlbEg1fCIexfO', $hash);
     }    
 
index c5ed29a..1305ee1 100644 (file)
@@ -66,7 +66,8 @@ abstract class testing_block_generator extends component_generator_base {
     }
 
     /**
-     * Fill in record defaults
+     * Fill in record defaults.
+     *
      * @param stdClass $record
      * @return stdClass
      */
@@ -76,19 +77,19 @@ abstract class testing_block_generator extends component_generator_base {
             $record->parentcontextid = context_system::instance()->id;
         }
         if (!isset($record->showinsubcontexts)) {
-            $record->showinsubcontexts = 1;
+            $record->showinsubcontexts = 0;
         }
         if (!isset($record->pagetypepattern)) {
-            $record->pagetypepattern = '';
+            $record->pagetypepattern = '*';
         }
         if (!isset($record->subpagepattern)) {
             $record->subpagepattern = null;
         }
         if (!isset($record->defaultregion)) {
-            $record->defaultregion = '';
+            $record->defaultregion = 'side-pre';
         }
         if (!isset($record->defaultweight)) {
-            $record->defaultweight = '';
+            $record->defaultweight = 5;
         }
         if (!isset($record->configdata)) {
             $record->configdata = null;
@@ -97,10 +98,44 @@ abstract class testing_block_generator extends component_generator_base {
     }
 
     /**
-     * Create a test block
-     * @param array|stdClass $record
-     * @param array $options
-     * @return stdClass activity record
+     * Create a test block instance.
+     *
+     * The $record passed in becomes the basis for the new row added to the
+     * block_instances table. You only need to supply the values of interest.
+     * Any missing values have sensible defaults filled in.
+     *
+     * The $options array provides additional data, not directly related to what
+     * will be inserted in the block_instance table, which may affect the block
+     * that is created. The meanings of any data passed here depends on the particular
+     * type of block being created.
+     *
+     * @param array|stdClass $record forms the basis for the entry to be inserted in the block_instances table.
+     * @param array $options further, block-specific options to control how the block is created.
+     * @return stdClass the block_instance record that has just been created.
      */
-    abstract public function create_instance($record = null, array $options = null);
+    public function create_instance($record = null, $options = array()) {
+        global $DB;
+
+        $this->instancecount++;
+
+        $record = (object)(array)$record;
+        $this->preprocess_record($record, $options);
+        $record = $this->prepare_record($record);
+
+        $id = $DB->insert_record('block_instances', $record);
+        context_block::instance($id);
+
+        $instance = $DB->get_record('block_instances', array('id' => $id), '*', MUST_EXIST);
+        return $instance;
+    }
+
+    /**
+     * Can be overridden to do block-specific processing. $record can be modified
+     * in-place.
+     *
+     * @param stdClass $record the data, before defaults are filled in.
+     * @param array $options further, block-specific options, as passed to {@link create_instance()}.
+     */
+    protected function preprocess_record(stdClass $record, array $options) {
+    }
 }
index c15bfa9..7af3e2e 100644 (file)
@@ -457,23 +457,45 @@ EOD;
     }
 
     /**
-     * Create a test block
-     * @param string $blockname
-     * @param array|stdClass $record
-     * @param array $options
-     * @return stdClass block instance record
+     * Create a test block.
+     *
+     * The $record passed in becomes the basis for the new row added to the
+     * block_instances table. You only need to supply the values of interest.
+     * Any missing values have sensible defaults filled in, and ->blockname will be set based on $blockname.
+     *
+     * The $options array provides additional data, not directly related to what
+     * will be inserted in the block_instance table, which may affect the block
+     * that is created. The meanings of any data passed here depends on the particular
+     * type of block being created.
+     *
+     * @param string $blockname the type of block to create. E.g. 'html'.
+     * @param array|stdClass $record forms the basis for the entry to be inserted in the block_instances table.
+     * @param array $options further, block-specific options to control how the block is created.
+     * @return stdClass new block_instance record.
      */
-    public function create_block($blockname, $record=null, array $options=null) {
+    public function create_block($blockname, $record=null, array $options=array()) {
         $generator = $this->get_plugin_generator('block_'.$blockname);
         return $generator->create_instance($record, $options);
     }
 
     /**
-     * Create a test module
-     * @param string $modulename
-     * @param array|stdClass $record
-     * @param array $options
-     * @return stdClass activity record
+     * Create a test activity module.
+     *
+     * The $record should contain the same data that you would call from
+     * ->get_data() when the mod_[type]_mod_form is submitted, except that you
+     * only need to supply values of interest. The only required value is
+     * 'course'. Any missing values will have a sensible default supplied.
+     *
+     * The $options array provides additional data, not directly related to what
+     * would come back from the module edit settings form, which may affect the activity
+     * that is created. The meanings of any data passed here depends on the particular
+     * type of activity being created.
+     *
+     * @param string $modulename the type of activity to create. E.g. 'forum' or 'quiz'.
+     * @param array|stdClass $record data, as if from the module edit settings form.
+     * @param array $options additional data that may affect how the module is created.
+     * @return stdClass activity record new new record that was just inserted in the table
+     *      like 'forum' or 'quiz', with a ->cmid field added.
      */
     public function create_module($modulename, $record=null, array $options=null) {
         $generator = $this->get_plugin_generator('mod_'.$modulename);
index d7f2ec0..657b351 100644 (file)
@@ -112,6 +112,10 @@ class behat_data_generators extends behat_base {
             'required' => array('activity', 'idnumber', 'course'),
             'switchids' => array('course' => 'course', 'gradecategory' => 'gradecat')
         ),
+        'blocks' => array(
+            'datagenerator' => 'block_instance',
+            'required' => array('blockname', 'contextlevel', 'reference'),
+        ),
         'group members' => array(
             'datagenerator' => 'group_member',
             'required' => array('user', 'group'),
@@ -341,6 +345,39 @@ class behat_data_generators extends behat_base {
         }
     }
 
+    /**
+     * Add a block to a page.
+     *
+     * @param array $data should mostly match the fields of the block_instances table.
+     *     The block type is specified by blockname.
+     *     The parentcontextid is set from contextlevel and reference.
+     *     Missing values are filled in by testing_block_generator::prepare_record.
+     *     $data is passed to create_block as both $record and $options. Normally
+     *     the keys are different, so this is a way to let people set values in either place.
+     */
+    protected function process_block_instance($data) {
+
+        if (empty($data['blockname'])) {
+            throw new Exception('\'blocks\' requires the field \'block\' type to be specified');
+        }
+
+        if (empty($data['contextlevel'])) {
+            throw new Exception('\'blocks\' requires the field \'contextlevel\' to be specified');
+        }
+
+        if (!isset($data['reference'])) {
+            throw new Exception('\'blocks\' requires the field \'reference\' to be specified');
+        }
+
+        $context = $this->get_context($data['contextlevel'], $data['reference']);
+        $data['parentcontextid'] = $context->id;
+
+        // Pass $data as both $record and $options. I think that is unlikely to
+        // cause problems since the relevant key names are different.
+        // $options is not used in most blocks I have seen, but where it is, it is necessary.
+        $this->datagenerator->create_block($data['blockname'], $data, $data);
+    }
+
     /**
      * Adapter to enrol_user() data generator.
      * @throws Exception
index f67a120..b3b0198 100644 (file)
@@ -293,6 +293,67 @@ class core_externallib_testcase extends advanced_testcase {
             $this->assertInstanceOf('external_description', $desc->returns_desc);
         }
     }
+
+    public function test_validate_courses() {
+        $this->resetAfterTest(true);
+
+        $c1 = $this->getDataGenerator()->create_course();
+        $c2 = $this->getDataGenerator()->create_course();
+        $c3 = $this->getDataGenerator()->create_course();
+        $u1 = $this->getDataGenerator()->create_user();
+        $this->getDataGenerator()->enrol_user($u1->id, $c1->id);
+        $courseids = array($c1->id, $c2->id, $c3->id);
+
+        $this->setAdminUser();
+        list($courses, $warnings) = external_util::validate_courses($courseids);
+        $this->assertEmpty($warnings);
+        $this->assertCount(3, $courses);
+        $this->assertArrayHasKey($c1->id, $courses);
+        $this->assertArrayHasKey($c2->id, $courses);
+        $this->assertArrayHasKey($c3->id, $courses);
+        $this->assertEquals($c1->id, $courses[$c1->id]->id);
+        $this->assertEquals($c2->id, $courses[$c2->id]->id);
+        $this->assertEquals($c3->id, $courses[$c3->id]->id);
+
+        $this->setUser($u1);
+        list($courses, $warnings) = external_util::validate_courses($courseids);
+        $this->assertCount(2, $warnings);
+        $this->assertEquals($c2->id, $warnings[0]['itemid']);
+        $this->assertEquals($c3->id, $warnings[1]['itemid']);
+        $this->assertCount(1, $courses);
+        $this->assertArrayHasKey($c1->id, $courses);
+        $this->assertArrayNotHasKey($c2->id, $courses);
+        $this->assertArrayNotHasKey($c3->id, $courses);
+        $this->assertEquals($c1->id, $courses[$c1->id]->id);
+    }
+
+    /**
+     * Validate courses can re-use an array of prefetched courses.
+     */
+    public function test_validate_courses_prefetch() {
+        $this->resetAfterTest(true);
+
+        $c1 = $this->getDataGenerator()->create_course();
+        $c2 = $this->getDataGenerator()->create_course();
+        $c3 = $this->getDataGenerator()->create_course();
+        $c4 = $this->getDataGenerator()->create_course();
+        $u1 = $this->getDataGenerator()->create_user();
+        $this->getDataGenerator()->enrol_user($u1->id, $c1->id);
+        $this->getDataGenerator()->enrol_user($u1->id, $c2->id);
+
+        $courseids = array($c1->id, $c2->id, $c3->id);
+        $courses = array($c2->id => $c2, $c3->id => $c3, $c4->id => $c4);
+
+        $this->setUser($u1);
+        list($courses, $warnings) = external_util::validate_courses($courseids, $courses);
+        $this->assertCount(2, $courses);
+        $this->assertCount(1, $warnings);
+        $this->assertArrayHasKey($c1->id, $courses);
+        $this->assertSame($c2, $courses[$c2->id]);
+        $this->assertArrayNotHasKey($c3->id, $courses);
+        // The extra course passed is not returned.
+        $this->assertArrayNotHasKey($c4->id, $courses);
+    }
 }
 
 /*
index f2a60e3..899b088 100644 (file)
@@ -246,6 +246,24 @@ class core_weblib_testcase extends advanced_testcase {
         $this->assertSame($strurl, $url->out(false));
     }
 
+    /**
+     * Test set good scheme on Moodle URL objects.
+     */
+    public function test_moodle_url_set_good_scheme() {
+        $url = new moodle_url('http://moodle.org/foo/bar');
+        $url->set_scheme('myscheme');
+        $this->assertSame('myscheme://moodle.org/foo/bar', $url->out());
+    }
+
+    /**
+     * Test set bad scheme on Moodle URL objects.
+     */
+    public function test_moodle_url_set_bad_scheme() {
+        $url = new moodle_url('http://moodle.org/foo/bar');
+        $this->setExpectedException('coding_exception');
+        $url->set_scheme('not a valid $ scheme');
+    }
+
     public function test_moodle_url_round_trip_array_params() {
         $strurl = 'http://example.com/?a%5B1%5D=1&a%5B2%5D=2';
         $url = new moodle_url($strurl);
index c38064a..5a732f1 100644 (file)
@@ -1,6 +1,13 @@
 This files describes API changes in core libraries and APIs,
 information provided here is intended especially for developers.
 
+=== 3.1 ===
+
+* Plugins can extend the navigation for user by declaring the following callback:
+  <frankenstyle>_extend_navigation_user(navigation_node $parentnode, stdClass $user,
+                                        context_user $context, stdClass $course,
+                                        context_course $coursecontext)
+
 === 3.0 ===
 
 * Minify updated to 2.2.1
index b7e67e5..aed1cb4 100644 (file)
@@ -672,6 +672,20 @@ class moodle_url {
         }
     }
 
+    /**
+     * Sets the scheme for the URI (the bit before ://)
+     *
+     * @param string $scheme
+     */
+    public function set_scheme($scheme) {
+        // See http://www.ietf.org/rfc/rfc3986.txt part 3.1.
+        if (preg_match('/^[a-zA-Z][a-zA-Z0-9+.-]*$/', $scheme)) {
+            $this->scheme = $scheme;
+        } else {
+            throw new coding_exception('Bad URL scheme.');
+        }
+    }
+
     /**
      * Sets the url slashargument value.
      *
index 962216f..35aef7e 100644 (file)
@@ -25,6 +25,7 @@
  */
 
 require_once("$CFG->libdir/externallib.php");
+require_once($CFG->dirroot . "/message/lib.php");
 
 /**
  * Message external functions
@@ -69,7 +70,6 @@ class core_message_external extends external_api {
      */
     public static function send_instant_messages($messages = array()) {
         global $CFG, $USER, $DB;
-        require_once($CFG->dirroot . "/message/lib.php");
 
         // Check if messaging is enabled.
         if (!$CFG->messaging) {
@@ -652,7 +652,6 @@ class core_message_external extends external_api {
     public static function get_messages($useridto, $useridfrom = 0, $type = 'both', $read = true,
                                         $newestfirst = true, $limitfrom = 0, $limitnum = 0) {
         global $CFG, $USER;
-        require_once($CFG->dirroot . "/message/lib.php");
 
         $warnings = array();
 
@@ -853,7 +852,6 @@ class core_message_external extends external_api {
      */
     public static function get_blocked_users($userid) {
         global $CFG, $USER, $PAGE;
-        require_once($CFG->dirroot . "/message/lib.php");
 
         // Warnings array, it can be empty at the end but is mandatory.
         $warnings = array();
@@ -957,7 +955,6 @@ class core_message_external extends external_api {
      */
     public static function mark_message_read($messageid, $timeread) {
         global $CFG, $DB, $USER;
-        require_once($CFG->dirroot . "/message/lib.php");
 
         // Check if private messaging between users is allowed.
         if (empty($CFG->messaging)) {
@@ -1008,6 +1005,90 @@ class core_message_external extends external_api {
         );
     }
 
+    /**
+     * Returns description of method parameters
+     *
+     * @return external_function_parameters
+     * @since 3.1
+     */
+    public static function delete_message_parameters() {
+        return new external_function_parameters(
+            array(
+                'messageid' => new external_value(PARAM_INT, 'The message id'),
+                'userid' => new external_value(PARAM_INT, 'The user id of who we want to delete the message for'),
+                'read' => new external_value(PARAM_BOOL, 'If is a message read', VALUE_DEFAULT, true)
+            )
+        );
+    }
+
+    /**
+     * Deletes a message
+     *
+     * @param  int $messageid the message id
+     * @param  int $userid the user id of who we want to delete the message for
+     * @param  bool $read if is a message read (default to true)
+     * @return external_description
+     * @throws moodle_exception
+     * @since 3.1
+     */
+    public static function delete_message($messageid, $userid, $read = true) {
+        global $CFG, $DB;
+
+        // Check if private messaging between users is allowed.
+        if (empty($CFG->messaging)) {
+            throw new moodle_exception('disabled', 'message');
+        }
+
+        // Warnings array, it can be empty at the end but is mandatory.
+        $warnings = array();
+
+        // Validate params.
+        $params = array(
+            'messageid' => $messageid,
+            'userid' => $userid,
+            'read' => $read
+        );
+        $params = self::validate_parameters(self::delete_message_parameters(), $params);
+
+        // Validate context.
+        $context = context_system::instance();
+        self::validate_context($context);
+
+        $messagestable = $params['read'] ? 'message_read' : 'message';
+        $message = $DB->get_record($messagestable, array('id' => $params['messageid']), '*', MUST_EXIST);
+
+        $user = core_user::get_user($params['userid'], '*', MUST_EXIST);
+        core_user::require_active_user($user);
+
+        $status = false;
+        if (message_can_delete_message($message, $user->id)) {
+            $status = message_delete_message($message, $user->id);;
+        } else {
+            throw new moodle_exception('You do not have permission to delete this message');
+        }
+
+        $results = array(
+            'status' => $status,
+            'warnings' => $warnings
+        );
+        return $results;
+    }
+
+    /**
+     * Returns description of method result value
+     *
+     * @return external_description
+     * @since 3.1
+     */
+    public static function delete_message_returns() {
+        return new external_single_structure(
+            array(
+                'status' => new external_value(PARAM_BOOL, 'True if the message was deleted, false otherwise'),
+                'warnings' => new external_warnings()
+            )
+        );
+    }
+
 }
 
 /**
index c46828c..dff81e4 100644 (file)
@@ -697,4 +697,105 @@ class core_message_externallib_testcase extends externallib_advanced_testcase {
 
     }
 
+    /**
+     * Test delete_message.
+     */
+    public function test_delete_message() {
+        global $DB;
+        $this->resetAfterTest(true);
+
+        $user1 = self::getDataGenerator()->create_user();
+        $user2 = self::getDataGenerator()->create_user();
+        $user3 = self::getDataGenerator()->create_user();
+        $user4 = self::getDataGenerator()->create_user();
+
+        // Login as user1.
+        $this->setUser($user1);
+        $this->assertEquals(array(), core_message_external::create_contacts(array($user2->id, $user3->id)));
+
+        // User user1 does not interchange messages with user3.
+        $m1to2 = message_post_message($user1, $user2, 'some random text 1', FORMAT_MOODLE);
+        $m2to3 = message_post_message($user2, $user3, 'some random text 3', FORMAT_MOODLE);
+        $m3to2 = message_post_message($user3, $user2, 'some random text 4', FORMAT_MOODLE);
+        $m3to4 = message_post_message($user3, $user4, 'some random text 4', FORMAT_MOODLE);
+
+        // Retrieve all messages sent by user2 (they are currently unread).
+        $lastmessages = message_get_messages($user1->id, $user2->id, 0, false);
+
+        // Delete a message not read, as a user from.
+        $result = core_message_external::delete_message($m1to2, $user1->id, false);
+        $result = external_api::clean_returnvalue(core_message_external::delete_message_returns(), $result);
+        $this->assertTrue($result['status']);
+        $this->assertCount(0, $result['warnings']);
+        $deletedmessage = $DB->get_record('message', array('id' => $m1to2));
+        $this->assertNotEquals(0, $deletedmessage->timeuserfromdeleted);
+
+        // Try to delete the same message again.
+        $result = core_message_external::delete_message($m1to2, $user1->id, false);
+        $result = external_api::clean_returnvalue(core_message_external::delete_message_returns(), $result);
+        $this->assertFalse($result['status']);
+
+        // Try to delete a message that does not belong to me.
+        try {
+            $messageid = core_message_external::delete_message($m2to3, $user3->id, false);
+            $this->fail('Exception expected due invalid messageid.');
+        } catch (moodle_exception $e) {
+            $this->assertEquals('You do not have permission to delete this message', $e->errorcode);
+        }
+
+        $this->setUser($user3);
+        // Delete a message not read, as a user to.
+        $result = core_message_external::delete_message($m2to3, $user3->id, false);
+        $result = external_api::clean_returnvalue(core_message_external::delete_message_returns(), $result);
+        $this->assertTrue($result['status']);
+        $this->assertCount(0, $result['warnings']);
+        $deletedmessage = $DB->get_record('message', array('id' => $m2to3));
+        $this->assertNotEquals(0, $deletedmessage->timeusertodeleted);
+
+        // Delete a message read.
+        $message = $DB->get_record('message', array('id' => $m3to2));
+        $messageid = message_mark_message_read($message, time());
+        $result = core_message_external::delete_message($messageid, $user3->id);
+        $result = external_api::clean_returnvalue(core_message_external::delete_message_returns(), $result);
+        $this->assertTrue($result['status']);
+        $this->assertCount(0, $result['warnings']);
+        $deletedmessage = $DB->get_record('message_read', array('id' => $messageid));
+        $this->assertNotEquals(0, $deletedmessage->timeuserfromdeleted);
+
+        // Invalid message ids.
+        try {
+            $result = core_message_external::delete_message(-1, $user1->id);
+            $this->fail('Exception expected due invalid messageid.');
+        } catch (dml_missing_record_exception $e) {
+            $this->assertEquals('invalidrecord', $e->errorcode);
+        }
+
+        // Invalid user.
+        try {
+            $result = core_message_external::delete_message($m1to2, -1, false);
+            $this->fail('Exception expected due invalid user.');
+        } catch (moodle_exception $e) {
+            $this->assertEquals('invaliduser', $e->errorcode);
+        }
+
+        // Not active user.
+        delete_user($user2);
+        try {
+            $result = core_message_external::delete_message($m1to2, $user2->id, false);
+            $this->fail('Exception expected due invalid user.');
+        } catch (moodle_exception $e) {
+            $this->assertEquals('userdeleted', $e->errorcode);
+        }
+
+        // Now, as an admin, try to delete any message.
+        $this->setAdminUser();
+        $result = core_message_external::delete_message($m3to4, $user4->id, false);
+        $result = external_api::clean_returnvalue(core_message_external::delete_message_returns(), $result);
+        $this->assertTrue($result['status']);
+        $this->assertCount(0, $result['warnings']);
+        $deletedmessage = $DB->get_record('message', array('id' => $m3to4));
+        $this->assertNotEquals(0, $deletedmessage->timeusertodeleted);
+
+    }
+
 }
index 0b39697..30d0376 100644 (file)
@@ -44,19 +44,25 @@ class mod_assign_extension_form extends moodleform {
      */
     public function definition() {
         $mform = $this->_form;
+        $params = $this->_customdata;
 
-        list($coursemoduleid, $userid, $batchusers, $instance, $data) = $this->_customdata;
         // Instance variable is used by the form validation function.
+        $instance = $params['instance'];
         $this->instance = $instance;
 
-        if ($batchusers) {
-            $listusersmessage = get_string('grantextensionforusers', 'assign', count(explode(',', $batchusers)));
-            $mform->addElement('static', 'applytoselectedusers', '', $listusersmessage);
+        if (!empty($params['userscount'])) {
+            $listusersmessage = get_string('grantextensionforusers', 'assign', $params['userscount']);
+            $mform->addElement('header', 'general', $listusersmessage);
+            $mform->addElement('static', 'userslist', get_string('selectedusers', 'assign'), $params['usershtml']);
+        } else {
+            $mform->addElement('static', 'userslist', '', $params['usershtml']);
         }
         if ($instance->allowsubmissionsfromdate) {
             $mform->addElement('static', 'allowsubmissionsfromdate', get_string('allowsubmissionsfromdate', 'assign'),
                                userdate($instance->allowsubmissionsfromdate));
         }
+
+        $finaldate = 0;
         if ($instance->duedate) {
             $mform->addElement('static', 'duedate', get_string('duedate', 'assign'), userdate($instance->duedate));
             $finaldate = $instance->duedate;
@@ -68,19 +74,17 @@ class mod_assign_extension_form extends moodleform {
         $mform->addElement('date_time_selector', 'extensionduedate',
                            get_string('extensionduedate', 'assign'), array('optional'=>true));
         $mform->setDefault('extensionduedate', $finaldate);
-        $mform->addElement('hidden', 'id', $coursemoduleid);
+
+        $mform->addElement('hidden', 'id');
         $mform->setType('id', PARAM_INT);
-        $mform->addElement('hidden', 'userid', $userid);
+        $mform->addElement('hidden', 'userid');
         $mform->setType('userid', PARAM_INT);
-        $mform->addElement('hidden', 'selectedusers', $batchusers);
+        $mform->addElement('hidden', 'selectedusers');
         $mform->setType('selectedusers', PARAM_SEQUENCE);
         $mform->addElement('hidden', 'action', 'saveextension');
         $mform->setType('action', PARAM_ALPHA);
-        $this->add_action_buttons(true, get_string('savechanges', 'assign'));
 
-        if ($data) {
-            $this->set_data($data);
-        }
+        $this->add_action_buttons(true, get_string('savechanges', 'assign'));
     }
 
     /**
index dd61e45..392098a 100644 (file)
@@ -889,10 +889,12 @@ class assign {
         // Special case for add_instance as the coursemodule has not been set yet.
         $instance = $this->get_instance();
 
+        $eventtype = 'due';
+
         if ($instance->duedate) {
             $event = new stdClass();
 
-            $params = array('modulename'=>'assign', 'instance'=>$instance->id);
+            $params = array('modulename' => 'assign', 'instance' => $instance->id, 'eventtype' => $eventtype);
             $event->id = $DB->get_field('event', 'id', $params);
             $event->name = $instance->name;
             $event->timestart = $instance->duedate;
@@ -929,12 +931,12 @@ class assign {
                 $event->userid      = 0;
                 $event->modulename  = 'assign';
                 $event->instance    = $instance->id;
-                $event->eventtype   = 'due';
+                $event->eventtype   = $eventtype;
                 $event->timeduration = 0;
                 calendar_event::create($event);
             }
         } else {
-            $DB->delete_records('event', array('modulename'=>'assign', 'instance'=>$instance->id));
+            $DB->delete_records('event', array('modulename' => 'assign', 'instance' => $instance->id, 'eventtype' => $eventtype));
         }
     }
 
@@ -2010,39 +2012,76 @@ class assign {
         require_once($CFG->dirroot . '/mod/assign/extensionform.php');
 
         $o = '';
-        $batchusers = optional_param('selectedusers', '', PARAM_SEQUENCE);
+
         $data = new stdClass();
-        $data->extensionduedate = null;
-        $userid = 0;
-        if (!$batchusers) {
-            $userid = required_param('userid', PARAM_INT);
+        $data->id = $this->get_course_module()->id;
 
-            $flags = $this->get_user_flags($userid, false);
+        $formparams = array(
+            'instance' => $this->get_instance()
+        );
+
+        $extrauserfields = get_extra_user_fields($this->get_context());
+
+        if ($mform) {
+            $submitteddata = $mform->get_data();
+            $users = $submitteddata->selectedusers;
+            $userlist = explode(',', $users);
+
+            $data->selectedusers = $users;
+            $data->userid = 0;
 
+            $usershtml = '';
+            $usercount = 0;
+            foreach ($userlist as $userid) {
+                if ($usercount >= 5) {
+                    $usershtml .= get_string('moreusers', 'assign', count($userlist) - 5);
+                    break;
+                }
+                $user = $DB->get_record('user', array('id' => $userid), '*', MUST_EXIST);
+
+                $usershtml .= $this->get_renderer()->render(new assign_user_summary($user,
+                                                                    $this->get_course()->id,
+                                                                    has_capability('moodle/site:viewfullnames',
+                                                                    $this->get_course_context()),
+                                                                    $this->is_blind_marking(),
+                                                                    $this->get_uniqueid_for_user($user->id),
+                                                                    $extrauserfields,
+                                                                    !$this->is_active_user($userid)));
+                $usercount += 1;
+            }
+
+            $formparams['userscount'] = count($userlist);
+            $formparams['usershtml'] = $usershtml;
+
+        } else {
+            $userid = required_param('userid', PARAM_INT);
             $user = $DB->get_record('user', array('id'=>$userid), '*', MUST_EXIST);
+            $flags = $this->get_user_flags($userid, false);
 
+            $data->userid = $user->id;
             if ($flags) {
                 $data->extensionduedate = $flags->extensionduedate;
             }
-            $data->userid = $userid;
-        } else {
-            $data->batchusers = $batchusers;
+
+            $usershtml = $this->get_renderer()->render(new assign_user_summary($user,
+                                                                $this->get_course()->id,
+                                                                has_capability('moodle/site:viewfullnames',
+                                                                $this->get_course_context()),
+                                                                $this->is_blind_marking(),
+                                                                $this->get_uniqueid_for_user($user->id),
+                                                                $extrauserfields,
+                                                                !$this->is_active_user($userid)));
+            $formparams['usershtml'] = $usershtml;
         }
+
+        $mform = new mod_assign_extension_form(null, $formparams);
+        $mform->set_data($data);
         $header = new assign_header($this->get_instance(),
                                     $this->get_context(),
                                     $this->show_intro(),
                                     $this->get_course_module()->id,
                                     get_string('grantextension', 'assign'));
         $o .= $this->get_renderer()->render($header);
-
-        if (!$mform) {
-            $formparams = array($this->get_course_module()->id,
-                                $userid,
-                                $batchusers,
-                                $this->get_instance(),
-                                $data);
-            $mform = new mod_assign_extension_form(null, $formparams);
-        }
         $o .= $this->get_renderer()->render(new assign_form('extensionform', $mform));
         $o .= $this->view_footer();
         return $o;
@@ -3705,7 +3744,6 @@ class assign {
 
             if ($data->operation == 'grantextension') {
                 // Reset the form so the grant extension page will create the extension form.
-                $mform = null;
                 return 'grantextension';
             } else if ($data->operation == 'setmarkingworkflowstate') {
                 return 'viewbatchsetmarkingworkflowstate';
@@ -5294,34 +5332,34 @@ class assign {
         require_once($CFG->dirroot . '/mod/assign/extensionform.php');
         require_sesskey();
 
-        $batchusers = optional_param('selectedusers', '', PARAM_SEQUENCE);
-        $userid = 0;
-        if (!$batchusers) {
-            $userid = required_param('userid', PARAM_INT);
-            $user = $DB->get_record('user', array('id'=>$userid), '*', MUST_EXIST);
-        }
-        $mform = new mod_assign_extension_form(null, array($this->get_course_module()->id,
-                                                           $userid,
-                                                           $batchusers,
-                                                           $this->get_instance(),
-                                                           null));
+        $formparams = array(
+            'instance' => $this->get_instance(),
+            'userscount' => 0,
+            'usershtml' => '',
+        );
+
+        $mform = new mod_assign_extension_form(null, $formparams);
 
         if ($mform->is_cancelled()) {
             return true;
         }
 
         if ($formdata = $mform->get_data()) {
-            if ($batchusers) {
-                $users = explode(',', $batchusers);
+            if (!empty($formdata->selectedusers)) {
+                $users = explode(',', $formdata->selectedusers);
                 $result = true;
                 foreach ($users as $userid) {
-                    $result = $this->save_user_extension($userid, $formdata->extensionduedate) && $result;
+                    $user = $DB->get_record('user', array('id' => $userid), '*', MUST_EXIST);
+                    $result = $this->save_user_extension($user->id, $formdata->extensionduedate) && $result;
                 }
                 return $result;
-            } else {
-                return $this->save_user_extension($userid, $formdata->extensionduedate);
+            }
+            if (!empty($formdata->userid)) {
+                $user = $DB->get_record('user', array('id' => $formdata->userid), '*', MUST_EXIST);
+                return $this->save_user_extension($user->id, $formdata->extensionduedate);
             }
         }
+
         return false;
     }
 
index e83ccaf..ce31a25 100644 (file)
@@ -367,9 +367,9 @@ class assign_submission_onlinetext extends assign_submission_plugin {
             if ($text != $shorttext) {
                 $wordcount = get_string('numwords', 'assignsubmission_onlinetext', count_words($text));
 
-                return $shorttext . $plagiarismlinks . $wordcount;
+                return $plagiarismlinks . $wordcount . $shorttext;
             } else {
-                return $shorttext . $plagiarismlinks;
+                return $plagiarismlinks . $shorttext;
             }
         }
         return '';
@@ -423,6 +423,7 @@ class assign_submission_onlinetext extends assign_submission_plugin {
      * @return string
      */
     public function view(stdClass $submission) {
+        global $CFG;
         $result = '';
 
         $onlinetextsubmission = $this->get_onlinetext_submission($submission->id);
@@ -436,9 +437,20 @@ class assign_submission_onlinetext extends assign_submission_plugin {
                                                                 'onlinetext',
                                                                 'assignsubmission_onlinetext');
 
+            $plagiarismlinks = '';
+
+            if (!empty($CFG->enableplagiarism)) {
+                require_once($CFG->libdir . '/plagiarismlib.php');
+
+                $plagiarismlinks .= plagiarism_get_links(array('userid' => $submission->userid,
+                    'content' => trim($result),
+                    'cmid' => $this->assignment->get_course_module()->id,
+                    'course' => $this->assignment->get_course()->id,
+                    'assignment' => $submission->assignment));
+            }
         }
 
-        return $result;
+        return $plagiarismlinks . $result;
     }
 
     /**
index fffc415..3617275 100644 (file)
@@ -13,11 +13,19 @@ Feature: Grant an extension to an offline student
       | teacher1 | Teacher | 1 | teacher1@example.com |
       | student1 | Student | 1 | student1@example.com |
       | student2 | Student | 2 | student2@example.com |
+      | student3 | Student | 3 | student3@example.com |
+      | student4 | Student | 4 | student4@example.com |
+      | student5 | Student | 5 | student5@example.com |
+      | student6 | Student | 6 | student6@example.com |
     And the following "course enrolments" exist:
       | user | course | role |
       | teacher1 | C1 | editingteacher |
       | student1 | C1 | student |
       | student2 | C1 | student |
+      | student3 | C1 | student |
+      | student4 | C1 | student |
+      | student5 | C1 | student |
+      | student6 | C1 | student |
 
   @javascript
   Scenario: Granting an extension to an offline assignment
@@ -30,6 +38,7 @@ Feature: Grant an extension to an offline student
     When I follow "View/grade all submissions"
     And I click on "Edit" "link" in the "Student 1" "table_row"
     And I follow "Grant extension"
+    And I should see "Student 1 (student1@example.com)"
     And I set the field "Enable" to "1"
     And I press "Save changes"
     Then I should see "Extension granted until:" in the "Student 1" "table_row"
@@ -51,10 +60,20 @@ Feature: Grant an extension to an offline student
     And I set the field "selectall" to "1"
     And I set the field "operation" to "Grant extension"
     And I click on "Go" "button" confirming the dialogue
+    And I should see "Student 1 (student1@example.com)"
+    And I should see "Student 2 (student2@example.com)"
+    And I should see "Student 3 (student3@example.com)"
+    And I should see "Student 4 (student4@example.com)"
+    And I should see "Student 5 (student5@example.com)"
+    And I should see "1 more..."
     And I set the field "Enable" to "1"
     And I press "Save changes"
     Then I should see "Extension granted until:" in the "Student 1" "table_row"
     And I should see "Extension granted until:" in the "Student 2" "table_row"
+    And I should see "Extension granted until:" in the "Student 3" "table_row"
+    And I should see "Extension granted until:" in the "Student 4" "table_row"
+    And I should see "Extension granted until:" in the "Student 5" "table_row"
+    And I should see "Extension granted until:" in the "Student 6" "table_row"
     And I log out
     And I log in as "student1"
     And I follow "Course 1"
index 6ae937d..34d1493 100644 (file)
@@ -181,14 +181,16 @@ class mod_book_external extends external_api {
 
         $params = self::validate_parameters(self::get_books_by_courses_parameters(), array('courseids' => $courseids));
 
+        $courses = array();
         if (empty($params['courseids'])) {
-            $params['courseids'] = array_keys(enrol_get_my_courses());
+            $courses = enrol_get_my_courses();
+            $params['courseids'] = array_keys($courses);
         }
 
         // Ensure there are courseids to loop through.
         if (!empty($params['courseids'])) {
 
-            list($courses, $warnings) = external_util::validate_courses($params['courseids']);
+            list($courses, $warnings) = external_util::validate_courses($params['courseids'], $courses);
 
             // Get the books in this course, this function checks users visibility permissions.
             // We can avoid then additional validate_context calls.
index 45000f3..8405efb 100644 (file)
@@ -523,14 +523,16 @@ class mod_chat_external extends external_api {
 
         $params = self::validate_parameters(self::get_chats_by_courses_parameters(), array('courseids' => $courseids));
 
+        $courses = array();
         if (empty($params['courseids'])) {
-            $params['courseids'] = array_keys(enrol_get_my_courses());
+            $courses = enrol_get_my_courses();
+            $params['courseids'] = array_keys($courses);
         }
 
         // Ensure there are courseids to loop through.
         if (!empty($params['courseids'])) {
 
-            list($courses, $warnings) = external_util::validate_courses($params['courseids']);
+            list($courses, $warnings) = external_util::validate_courses($params['courseids'], $courses);
 
             // Get the chats in this course, this function checks users visibility permissions.
             // We can avoid then additional validate_context calls.
index 8182013..7eb5a16 100644 (file)
@@ -480,14 +480,16 @@ class mod_choice_external extends external_api {
 
         $params = self::validate_parameters(self::get_choices_by_courses_parameters(), array('courseids' => $courseids));
 
+        $courses = array();
         if (empty($params['courseids'])) {
-            $params['courseids'] = array_keys(enrol_get_my_courses());
+            $courses = enrol_get_my_courses();
+            $params['courseids'] = array_keys($courses);
         }
 
         // Ensure there are courseids to loop through.
         if (!empty($params['courseids'])) {
 
-            list($courses, $warnings) = external_util::validate_courses($params['courseids']);
+            list($courses, $warnings) = external_util::validate_courses($params['courseids'], $courses);
 
             // Get the choices in this course, this function checks users visibility permissions.
             // We can avoid then additional validate_context calls.
index 30bfc6e..6de0a23 100644 (file)
@@ -33,8 +33,6 @@ Feature: Show users who have not responded to the feedback survey
     And the following "grouping groups" exist:
       | grouping | group  |
       | GXI1     | GI1    |
-    And the following config values are set as admin:
-      | enableavailability | 1 |
     And I log in as "admin"
     And I navigate to "Manage activities" node in "Site administration > Plugins > Activity modules"
     And I click on "Show" "link" in the "Feedback" "table_row"
index 20c7890..69965c0 100644 (file)
@@ -60,8 +60,10 @@ class mod_forum_external extends external_api {
 
         $params = self::validate_parameters(self::get_forums_by_courses_parameters(), array('courseids' => $courseids));
 
+        $courses = array();
         if (empty($params['courseids'])) {
-            $params['courseids'] = array_keys(enrol_get_my_courses());
+            $courses = enrol_get_my_courses();
+            $params['courseids'] = array_keys($courses);
         }
 
         // Array to store the forums to return.
@@ -71,7 +73,7 @@ class mod_forum_external extends external_api {
         // Ensure there are courseids to loop through.
         if (!empty($params['courseids'])) {
 
-            list($courses, $warnings) = external_util::validate_courses($params['courseids']);
+            list($courses, $warnings) = external_util::validate_courses($params['courseids'], $courses);
 
             // Get the forums in this course. This function checks users visibility permissions.
             $forums = get_all_instances_in_courses("forum", $courses);
index ce20f61..1089efe 100644 (file)
@@ -16,9 +16,6 @@ Feature: Set a certain number of discussions as a completion condition for a for
       | user | course | role |
       | teacher1 | C1 | editingteacher |
       | student1 | C1 | student |
-    And the following config values are set as admin:
-      | enablecompletion   | 1 |
-      | enableavailability | 1 |
     And I log in as "teacher1"
     And I follow "Course 1"
     And I turn editing mode on
index 2b6a121..4d8a5df 100644 (file)
@@ -34,8 +34,6 @@ Feature: As a teacher I need to see an accurate list of subscribed users
     And the following "grouping groups" exist:
       | grouping | group |
       | GG1      | G1    |
-    And the following config values are set as admin:
-      | enableavailability | 1 |
     And I log in as "teacher"
     And I follow "Course 1"
     And I turn editing mode on
index 5a092de..4ae2fdd 100644 (file)
@@ -136,14 +136,16 @@ class mod_imscp_external extends external_api {
 
         $params = self::validate_parameters(self::get_imscps_by_courses_parameters(), array('courseids' => $courseids));
 
+        $courses = array();
         if (empty($params['courseids'])) {
-            $params['courseids'] = array_keys(enrol_get_my_courses());
+            $courses = enrol_get_my_courses();
+            $params['courseids'] = array_keys($courses);
         }
 
         // Ensure there are courseids to loop through.
         if (!empty($params['courseids'])) {
 
-            list($courses, $warnings) = external_util::validate_courses($params['courseids']);
+            list($courses, $warnings) = external_util::validate_courses($params['courseids'], $courses);
 
             // Get the imscps in this course, this function checks users visibility permissions.
             // We can avoid then additional validate_context calls.
index ac44d5b..e926a9a 100644 (file)
@@ -16,8 +16,6 @@ Feature: Set end of lesson reached as a completion condition for a lesson
       | user | course | role |
       | teacher1 | C1 | editingteacher |
       | student1 | C1 | student |
-    And the following config values are set as admin:
-      | enablecompletion | 1 |
     And I log in as "teacher1"
     And I follow "Course 1"
     And I turn editing mode on
index 821b152..920709a 100644 (file)
@@ -17,8 +17,6 @@ Feature: Set time spent as a completion condition for a lesson
       | user | course | role |
       | teacher1 | C1 | editingteacher |
       | student1 | C1 | student |
-    And the following config values are set as admin:
-      | enablecompletion | 1 |
     And I log in as "teacher1"
     And I follow "Course 1"
     And I turn editing mode on
index 2e9ff0a..330606d 100644 (file)
@@ -17,7 +17,6 @@ Feature: Set a quiz to be marked complete when the student uses all attempts all
       | teacher1 | C1     | editingteacher |
       | student1 | C1     | student        |
     And the following config values are set as admin:
-      | enablecompletion    | 1           |
       | grade_item_advanced | hiddenuntil |
     And the following "question categories" exist:
       | contextlevel | reference | name           |
index 1d781e6..3312cca 100644 (file)
@@ -17,7 +17,6 @@ Feature: Set a quiz to be marked complete when the student passes
       | teacher1 | C1     | editingteacher |
       | student1 | C1     | student        |
     And the following config values are set as admin:
-      | enablecompletion    | 1           |
       | grade_item_advanced | hiddenuntil |
     And the following "question categories" exist:
       | contextlevel | reference | name           |
index 87f2617..a67cb74 100644 (file)
@@ -640,14 +640,16 @@ class mod_scorm_external extends external_api {
 
         $params = self::validate_parameters(self::get_scorms_by_courses_parameters(), array('courseids' => $courseids));
 
+        $courses = array();
         if (empty($params['courseids'])) {
-            $params['courseids'] = array_keys(enrol_get_my_courses());
+            $courses = enrol_get_my_courses();
+            $params['courseids'] = array_keys($courses);
         }
 
         // Ensure there are courseids to loop through.
         if (!empty($params['courseids'])) {
 
-            list($courses, $warnings) = external_util::validate_courses($params['courseids']);
+            list($courses, $warnings) = external_util::validate_courses($params['courseids'], $courses);
 
             // Get the scorms in this course, this function checks users visibility permissions.
             // We can avoid then additional validate_context calls.
index b883223..0f79ace 100644 (file)
@@ -1108,7 +1108,7 @@ function scorm_seq_flow_tree_traversal($activity, $direction, $childrenflag, $pr
 }
 
 // Returns the next activity on the tree, traversal direction, control returned to the LTS, (may) exception.
-function scorm_seq_flow_activity_traversal ($activity, $userid, $direction, $childrenflag, $prevdirection, $seq, $userid) {
+function scorm_seq_flow_activity_traversal ($activity, $userid, $direction, $childrenflag, $prevdirection, $seq) {
     $parent = scorm_get_parent ($activity);
     if (!isset($parent->flow) || ($parent->flow == false)) {
         $seq->deliverable = false;
@@ -1123,7 +1123,7 @@ function scorm_seq_flow_activity_traversal ($activity, $userid, $direction, $chi
         if ($skip) {
             $seq = scorm_seq_flow_tree_traversal($activity, $direction, false, $prevdirection, $seq, $userid, $skip);
             $seq = scorm_seq_flow_activity_traversal($seq->nextactivity, $userid, $direction,
-                                                     $childrenflag, $prevdirection, $seq, $userid);
+                                                     $childrenflag, $prevdirection, $seq);
         } else if (!empty($seq->identifiedactivity)) {
             $seq->nextactivity = $activity;
         }
@@ -1147,10 +1147,10 @@ function scorm_seq_flow_activity_traversal ($activity, $userid, $direction, $chi
         } else {
             if ($direction == 'backward' && $seq->traversaldir == 'forward') {
                 $seq = scorm_seq_flow_activity_traversal($seq->identifiedactivity, $userid,
-                                                         'forward', $childrenflag, 'backward', $seq, $userid);
+                                                         'forward', $childrenflag, 'backward', $seq);
             } else {
                 $seq = scorm_seq_flow_activity_traversal($seq->identifiedactivity, $userid,
-                                                         $direction, $childrenflag, null, $seq, $userid);
+                                                         $direction, $childrenflag, null, $seq);
             }
             return $seq;
         }
@@ -1174,7 +1174,7 @@ function scorm_seq_flow ($activity, $direction, $seq, $childrenflag, $userid) {
         return $seq;
     } else {
         $activity = $seq->nextactivity;
-        $seq = scorm_seq_flow_activity_traversal($activity, $userid, $direction, $childrenflag, null, $seq, $userid);
+        $seq = scorm_seq_flow_activity_traversal($activity, $userid, $direction, $childrenflag, null, $seq);
         return $seq;
     }
 }
index 497206e..709efe0 100644 (file)
@@ -421,7 +421,8 @@ function scorm_seq_choice_sequencing($sco, $userid, $seq) {
     }
 
     if ($seq->currentactivity === $sco) {
-        break;
+        // MDL-51757 - this part of the SCORM 2004 sequencing and navigation was not completed.
+        throw new \coding_exception('Unexpected state encountered');
     }
 
     $sib = scorm_get_siblings($seq->currentactivity);
@@ -451,7 +452,8 @@ function scorm_seq_choice_sequencing($sco, $userid, $seq) {
                 return $seq;
             }
         }
-        break;
+        // MDL-51757 - this part of the SCORM 2004 sequencing and navigation was not completed.
+        throw new \coding_exception('Unexpected state encountered');
     }
 
     if ($seq->currentactivity == null || $seq->currentactivity == $comancestor) {
@@ -478,7 +480,8 @@ function scorm_seq_choice_sequencing($sco, $userid, $seq) {
                 return $seq;
             }
         }
-        break;
+        // MDL-51757 - this part of the SCORM 2004 sequencing and navigation was not completed.
+        throw new \coding_exception('Unexpected state encountered');
     }
 
     if ($comancestor->id == $sco->id) {
@@ -504,7 +507,8 @@ function scorm_seq_choice_sequencing($sco, $userid, $seq) {
                 }
             }
         }
-        break;
+        // MDL-51757 - this part of the SCORM 2004 sequencing and navigation was not completed.
+        throw new \coding_exception('Unexpected state encountered');
     }
 
     if (array_search($ancestors, $comancestor) !== false) {
@@ -589,7 +593,8 @@ function scorm_seq_choice_sequencing($sco, $userid, $seq) {
                 }
             }
         }
-        break;
+        // MDL-51757 - this part of the SCORM 2004 sequencing and navigation was not completed.
+        throw new \coding_exception('Unexpected state encountered');
     }
 
     if (scorm_is_leaf ($sco)) {
index 5dfd562..65c79cd 100644 (file)
Binary files a/question/type/ddimageortext/yui/build/moodle-qtype_ddimageortext-form/moodle-qtype_ddimageortext-form-debug.js and b/question/type/ddimageortext/yui/build/moodle-qtype_ddimageortext-form/moodle-qtype_ddimageortext-form-debug.js differ
index 198e509..eed668e 100644 (file)
Binary files a/question/type/ddimageortext/yui/build/moodle-qtype_ddimageortext-form/moodle-qtype_ddimageortext-form-min.js and b/question/type/ddimageortext/yui/build/moodle-qtype_ddimageortext-form/moodle-qtype_ddimageortext-form-min.js differ
index 5dfd562..65c79cd 100644 (file)
Binary files a/question/type/ddimageortext/yui/build/moodle-qtype_ddimageortext-form/moodle-qtype_ddimageortext-form.js and b/question/type/ddimageortext/yui/build/moodle-qtype_ddimageortext-form/moodle-qtype_ddimageortext-form.js differ
index 5cf5d6a..1cb2aea 100644 (file)
@@ -143,13 +143,9 @@ Y.extend(DDIMAGEORTEXT_FORM, M.qtype_ddimageortext.dd_base_class, {
                     if (value !== 0) { // No item option is always selectable.
                         var cbel = Y.one('#id_drags_' + (value - 1) + '_infinite');
                         if (cbel && !cbel.get('checked')) {
-                            Y.all('fieldset#id_dropzoneheader select').some(function (selector) {
-                                if (Number(selector.get('value')) === value) {
-                                    optionnode.set('disabled', true);
-                                    return true; // Stop looping.
-                                }
-                                return false;
-                            }, this);
+                            if (this.item_is_allocated_to_dropzone(value)) {
+                                optionnode.set('disabled', true);
+                            }
                         }
                     }
                 }
@@ -161,6 +157,19 @@ Y.extend(DDIMAGEORTEXT_FORM, M.qtype_ddimageortext.dd_base_class, {
         Y.all('fieldset#id_dropzoneheader select').detachAll();
     },
 
+    /**
+     * Checks if the specified drag item is allocated to a dropzone.
+     *
+     * @method item_is_allocated_to_dropzone
+     * @param {Number} value of the drag item to check
+     * @return {Boolean} true if item is allocated to dropzone
+     */
+    item_is_allocated_to_dropzone: function(itemvalue) {
+        return Y.all('fieldset#id_dropzoneheader select').some(function(selectNode) {
+            return Number(selectNode.get('value')) === itemvalue;
+        });
+    },
+
     setup_form_events : function () {
         // Events triggered by changes to form data.
 
@@ -188,16 +197,8 @@ Y.extend(DDIMAGEORTEXT_FORM, M.qtype_ddimageortext.dd_base_class, {
 
         for (var i = 0; i < this.form.get_form_value('noitems', []); i++) {
             // Change to group selector.
-            Y.all('#fgroup_id_drags_' + i + ' select.draggroup').on(
-                    'change', function () {
-                this.doc.drag_items().remove(true);
-                this.draw_dd_area();
-            }, this);
-            Y.all('#fgroup_id_drags_' + i + ' select.dragitemtype').on(
-                    'change', function () {
-                this.doc.drag_items().remove(true);
-                this.draw_dd_area();
-            }, this);
+            Y.all('#fgroup_id_drags_' + i + ' select.draggroup').on('change', this.redraw_dd_area, this);
+            Y.all('#fgroup_id_drags_' + i + ' select.dragitemtype').on('change', this.redraw_dd_area, this);
             Y.all('fieldset#draggableitemheader_' + i + ' input[type="text"]')
                                 .on('blur', this.set_options_for_drag_item_selectors, this);
             // Change to infinite checkbox.
@@ -214,6 +215,16 @@ Y.extend(DDIMAGEORTEXT_FORM, M.qtype_ddimageortext.dd_base_class, {
         }, M.form_filepicker, 'callback', this);
     },
 
+    /**
+     * Redraws drag and drop preview area.
+     *
+     * @method redraw_dd_area
+     */
+    redraw_dd_area: function() {
+        this.doc.drag_items().remove(true);
+        this.draw_dd_area();
+    },
+
     update_visibility_of_file_pickers : function() {
         for (var i = 0; i < this.form.get_form_value('noitems', []); i++) {
             if ('image' === this.form.get_form_value('drags', [i, 'dragitemtype'])) {
index 3ce928c..cd13403 100644 (file)
@@ -117,7 +117,7 @@ if ($uselegacyreader) {
         $sqllasttime = ", MAX(time) AS lasttime";
     }
     $logactionlike = $DB->sql_like('l.action', ':action');
-    $sql = "SELECT cm.id, COUNT('x') AS numviews $sqllasttime
+    $sql = "SELECT cm.id, COUNT('x') AS numviews, COUNT(DISTINCT userid) AS distinctusers $sqllasttime
               FROM {course_modules} cm
               JOIN {modules} m
                 ON m.id = cm.module
@@ -141,7 +141,7 @@ if ($useinternalreader) {
     if ($showlastaccess) {
         $sqllasttime = ", MAX(timecreated) AS lasttime";
     }
-    $sql = "SELECT contextinstanceid as cmid, COUNT('x') AS numviews $sqllasttime
+    $sql = "SELECT contextinstanceid as cmid, COUNT('x') AS numviews, COUNT(DISTINCT userid) AS distinctusers $sqllasttime
               FROM {" . $logtable . "} l
              WHERE courseid = :courseid
                AND anonymous = 0
@@ -215,7 +215,7 @@ foreach ($modinfo->sections as $sectionnum=>$section) {
         $numviewscell->attributes['class'] = 'numviews';
 
         if (!empty($views[$cm->id]->numviews)) {
-            $numviewscell->text = $views[$cm->id]->numviews;
+            $numviewscell->text = get_string('numviews', 'report_outline', $views[$cm->id]);
         } else {
             $numviewscell->text = '-';
         }
index 828e2d9..36e44fd 100644 (file)
@@ -27,6 +27,7 @@ $string['eventactivityreportviewed'] = 'Activity report viewed';
 $string['eventoutlinereportviewed'] = 'Outline report viewed';
 $string['neverseen'] = 'Never seen';
 $string['nologreaderenabled'] = 'No log reader enabled';
+$string['numviews'] = '{$a->numviews} by {$a->distinctusers} users';
 $string['outline:view'] = 'View activity report';
 $string['page-report-outline-x'] = 'Any outline report';
 $string['page-report-outline-index'] = 'Course outline report';
index be912d4..1362d1f 100644 (file)
@@ -29,7 +29,6 @@ Feature: View an outline report
       | Name | Book name |
       | Description | Book description |
 
-  @javascript
   Scenario: View the outline report when only the legacy log reader is enabled
     Given I navigate to "Manage log stores" node in "Site administration > Plugins > Logging"
     And I click on "Enable" "link" in the "Legacy log" "table_row"
@@ -50,10 +49,9 @@ Feature: View an outline report
     And I log in as "teacher1"
     And I follow "Course 1"
     When I navigate to "Activity report" node in "Course administration > Reports"
-    Then I should see "2" in the "//tbody/tr[(position() mod 2)=1]/child::td[contains(concat(' ', normalize-space(@class),' '),' numviews ')]" "xpath_element"
-    And I should see "1" in the "//tbody/tr[(position() mod 2)=0]/child::td[contains(concat(' ', normalize-space(@class),' '),' numviews ')]" "xpath_element"
+    Then I should see "2 by 2 users" in the "Book name" "table_row"
+    And I should see "1 by 1 users" in the "Forum name" "table_row"
 
-  @javascript
   Scenario: View the outline report when only the standard log reader is enabled
     Given I navigate to "Manage log stores" node in "Site administration > Plugins > Logging"
     And "Enable" "link" should exist in the "Legacy log" "table_row"
@@ -73,10 +71,9 @@ Feature: View an outline report
     And I am on site homepage
     And I follow "Course 1"
     When I navigate to "Activity report" node in "Course administration > Reports"
-    Then I should see "2" in the "//tbody/tr[(position() mod 2)=1]/child::td[contains(concat(' ', normalize-space(@class),' '),' numviews ')]" "xpath_element"
-    And I should see "1" in the "//tbody/tr[(position() mod 2)=0]/child::td[contains(concat(' ', normalize-space(@class),' '),' numviews ')]" "xpath_element"
+    Then I should see "2 by 2 users" in the "Book name" "table_row"
+    And I should see "1 by 1 users" in the "Forum name" "table_row"
 
-  @javascript
   Scenario: View the outline report when both the standard and legacy log readers are enabled
     Given I navigate to "Manage log stores" node in "Site administration > Plugins > Logging"
     And I click on "Enable" "link" in the "Legacy log" "table_row"
@@ -97,10 +94,9 @@ Feature: View an outline report
     And I log in as "teacher1"
     And I follow "Course 1"
     When I navigate to "Activity report" node in "Course administration > Reports"
-    Then I should see "2" in the "//tbody/tr[(position() mod 2)=1]/child::td[contains(concat(' ', normalize-space(@class),' '),' numviews ')]" "xpath_element"
-    And I should see "1" in the "//tbody/tr[(position() mod 2)=0]/child::td[contains(concat(' ', normalize-space(@class),' '),' numviews ')]" "xpath_element"
+    Then I should see "2 by 2 users" in the "Book name" "table_row"
+    And I should see "1 by 1 users" in the "Forum name" "table_row"
 
-  @javascript
   Scenario: View the outline report when no log reader is enabled
     Given I navigate to "Manage log stores" node in "Site administration > Plugins > Logging"
     And "Enable" "link" should exist in the "Legacy log" "table_row"
@@ -109,3 +105,20 @@ Feature: View an outline report
     And I follow "Course 1"
     When I navigate to "Activity report" node in "Course administration > Reports"
     Then I should see "No log reader enabled"
+
+  Scenario: Multiple views from a single user are identified as not distinct
+    Given I log out
+    And I log in as "student1"
+    And I follow "Course 1"
+    And I follow "Forum name"
+    And I follow "Course 1"
+    And I follow "Forum name"
+    And I follow "Course 1"
+    And I follow "Forum name"
+    And I am on site homepage
+    And I log out
+    When I log in as "teacher1"
+    And I follow "Course 1"
+    And I navigate to "Activity report" node in "Course administration > Reports"
+    Then I should see "3 by 1 users" in the "Forum name" "table_row"
+    And I should see "-" in the "Book name" "table_row"
index 1585d88..5ce1461 100644 (file)
@@ -375,7 +375,8 @@ class repository_filesystem extends repository {
                 $filesize = filesize($filepath);
             }
             $issyncing = false;
-            $file->set_synchronized($contenthash, $filesize);
+            $modified = filemtime($filepath);
+            $file->set_synchronized($contenthash, $filesize, 0, $modified);
         } else {
             $file->set_missingsource();
         }
index fdf9c3d..b8c3d20 100644 (file)
@@ -2752,7 +2752,7 @@ abstract class repository implements cacheable_object {
                     $params['filename']))) {
                 $file->set_missingsource();
             } else {
-                $file->set_synchronized($storedfile->get_contenthash(), $storedfile->get_filesize());
+                $file->set_synchronized($storedfile->get_contenthash(), $storedfile->get_filesize(), 0, $storedfile->get_timemodified());
             }
             return true;
         }
index 4b27144..74e7b07 100644 (file)
@@ -64,4 +64,58 @@ class theme_clean_core_renderer extends theme_bootstrapbase_core_renderer {
 
         return false;
     }
+
+    /**
+     * Returns the navigation bar home reference.
+     *
+     * The small logo is only rendered on pages where the logo is not displayed.
+     *
+     * @param bool $returnlink Whether to wrap the icon and the site name in links or not
+     * @return string The site name, the small logo or both depending on the theme settings.
+     */
+    public function navbar_home($returnlink = true) {
+        global $CFG;
+
+        if ($this->should_render_logo() || empty($this->page->theme->settings->smalllogo)) {
+            // If there is no small logo we always show the site name.
+            return $this->get_home_ref($returnlink);
+        }
+        $imageurl = $this->page->theme->setting_file_url('smalllogo', 'smalllogo');
+        $image = html_writer::img($imageurl, get_string('sitelogo', 'theme_' . $this->page->theme->name),
+            array('class' => 'small-logo'));
+
+        if ($returnlink) {
+            $logocontainer = html_writer::link($CFG->wwwroot, $image,
+                array('class' => 'small-logo-container', 'title' => get_string('home')));
+        } else {
+            $logocontainer = html_writer::tag('span', $image, array('class' => 'small-logo-container'));
+        }
+
+        // Sitename setting defaults to true.
+        if (!isset($this->page->theme->settings->sitename) || !empty($this->page->theme->settings->sitename)) {
+            return $logocontainer . $this->get_home_ref($returnlink);
+        }
+
+        return $logocontainer;
+    }
+
+    /**
+     * Returns a reference to the site home.
+     *
+     * It can be either a link or a span.
+     *
+     * @param bool $returnlink
+     * @return string
+     */
+    protected function get_home_ref($returnlink = true) {
+        global $CFG, $SITE;
+
+        $sitename = format_string($SITE->shortname, true, array('context' => context_course::instance(SITEID)));
+
+        if ($returnlink) {
+            return html_writer::link($CFG->wwwroot, $sitename, array('class' => 'brand', 'title' => get_string('home')));
+        }
+
+        return html_writer::tag('span', $sitename, array('class' => 'brand'));
+    }
 }
index 8904259..f98117d 100644 (file)
@@ -64,3 +64,8 @@ $string['pluginname'] = 'Clean';
 $string['region-side-post'] = 'Right';
 $string['region-side-pre'] = 'Left';
 
+$string['sitelogo'] = 'Site logo';
+$string['sitename'] = 'Display site name along with small logo';
+$string['sitenamedesc'] = 'This setting is only applied if a "Small logo" is set. If no small logo is set the site name is always displayed in the navigation bar, but if it is you can use this setting to display it or not.';
+$string['smalllogo'] = 'Small logo';
+$string['smalllogodesc'] = 'This logo is displayed in the navbar next to the site name, if a "Logo" is set, then it is only displayed on the pages where the logo is not displayed.';
index 1258e04..ab4e25a 100644 (file)
@@ -41,9 +41,7 @@ echo $OUTPUT->doctype() ?>
 <header role="banner" class="navbar navbar-fixed-top<?php echo $html->navbarclass ?> moodle-has-zindex">
     <nav role="navigation" class="navbar-inner">
         <div class="container-fluid">
-            <a class="brand" href="<?php echo $CFG->wwwroot;?>"><?php echo
-                format_string($SITE->shortname, true, array('context' => context_course::instance(SITEID)));
-                ?></a>
+            <?php echo $OUTPUT->navbar_home(); ?>
             <?php echo $OUTPUT->navbar_button(); ?>
             <?php echo $OUTPUT->user_menu(); ?>
             <div class="nav-collapse collapse">
index 7f57fcb..8ccbe05 100644 (file)
@@ -50,9 +50,7 @@ echo $OUTPUT->doctype() ?>
 <header role="banner" class="navbar navbar-fixed-top<?php echo $html->navbarclass ?> moodle-has-zindex">
     <nav role="navigation" class="navbar-inner">
         <div class="container-fluid">
-            <a class="brand" href="<?php echo $CFG->wwwroot;?>"><?php echo
-                format_string($SITE->shortname, true, array('context' => context_course::instance(SITEID)));
-                ?></a>
+            <?php echo $OUTPUT->navbar_home(); ?>
             <?php echo $OUTPUT->navbar_button(); ?>
             <?php echo $OUTPUT->user_menu(); ?>
             <div class="nav-collapse collapse">
index 0b03794..03200e8 100644 (file)
@@ -60,9 +60,7 @@ echo $OUTPUT->doctype() ?>
 <header role="banner" class="navbar navbar-fixed-top<?php echo $html->navbarclass ?> moodle-has-zindex">
     <nav role="navigation" class="navbar-inner">
         <div class="container-fluid">
-            <a class="brand" href="<?php echo $CFG->wwwroot;?>"><?php echo
-                format_string($SITE->shortname, true, array('context' => context_course::instance(SITEID)));
-                ?></a>
+            <?php echo $OUTPUT->navbar_home(); ?>
             <?php echo $OUTPUT->navbar_button(); ?>
             <?php echo $OUTPUT->user_menu(); ?>
             <div class="nav-collapse collapse">
index 5113ae7..857c002 100644 (file)
@@ -54,9 +54,7 @@ echo $OUTPUT->doctype() ?>
 <header role="banner" class="navbar navbar-fixed-top moodle-has-zindex">
     <nav role="navigation" class="navbar-inner">
         <div class="container-fluid">
-            <span class="brand"><?php echo
-                format_string($SITE->shortname, true, array('context' => context_course::instance(SITEID)));
-                ?></span>
+            <?php echo $OUTPUT->navbar_home(false); ?>
             <?php echo $OUTPUT->navbar_button(); ?>
             <div class="nav-collapse collapse">
                 <ul class="nav pull-right">
@@ -90,4 +88,4 @@ echo $OUTPUT->doctype() ?>
 
 </div>
 </body>
-</html>
\ No newline at end of file
+</html>
index 4a46bba..889c9ae 100644 (file)
@@ -86,13 +86,13 @@ function theme_clean_set_logo($css, $logo) {
  * @return bool
  */
 function theme_clean_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload, array $options = array()) {
-    if ($context->contextlevel == CONTEXT_SYSTEM and $filearea === 'logo') {
+    if ($context->contextlevel == CONTEXT_SYSTEM and ($filearea === 'logo' || $filearea === 'smalllogo')) {
         $theme = theme_config::load('clean');
         // By default, theme files must be cache-able by both browsers and proxies.
         if (!array_key_exists('cacheability', $options)) {
             $options['cacheability'] = 'public';
         }
-        return $theme->setting_file_serve('logo', $args, $forcedownload, $options);
+        return $theme->setting_file_serve($filearea, $args, $forcedownload, $options);
     } else {
         send_file_not_found();
     }
index cf0df83..0f265db 100644 (file)
@@ -48,6 +48,22 @@ if ($ADMIN->fulltree) {
     $setting->set_updatedcallback('theme_reset_all_caches');
     $settings->add($setting);
 
+    // Small logo file setting.
+    $name = 'theme_clean/smalllogo';
+    $title = get_string('smalllogo', 'theme_clean');
+    $description = get_string('smalllogodesc', 'theme_clean');
+    $setting = new admin_setting_configstoredfile($name, $title, $description, 'smalllogo');
+    $setting->set_updatedcallback('theme_reset_all_caches');
+    $settings->add($setting);
+
+    // Show site name along with small logo.
+    $name = 'theme_clean/sitename';
+    $title = get_string('sitename', 'theme_clean');
+    $description = get_string('sitenamedesc', 'theme_clean');
+    $setting = new admin_setting_configcheckbox($name, $title, $description, 1);
+    $setting->set_updatedcallback('theme_reset_all_caches');
+    $settings->add($setting);
+
     // Custom CSS file.
     $name = 'theme_clean/customcss';
     $title = get_string('customcss', 'theme_clean');
index bb4cc68..b8d37a0 100644 (file)
@@ -16,6 +16,37 @@ div.logo {
     float: right;
 }
 
+img.small-logo {
+    float: left;
+    height: 35px;
+    margin: 3px 10px 3px 0;
+}
+
+.dir-rtl img.small-logo {
+    float: right;
+    margin: 3px 0 3px 10px;
+}
+
+@media (max-width: 767px) {
+    .dir-rtl img.small-logo,
+    img.small-logo {
+        margin: 3px;
+    }
+}
+
+@media (max-width: 480px) {
+    .navbar img.small-logo {
+        max-width: 150px;
+    }
+    /* Applying accesshide styles */
+    .navbar .small-logo-container + .brand {
+        position: absolute;
+        left: -10000px;
+        font-size: 1em;
+        font-weight: normal;
+    }
+}
+
 /* Custom CSS Settings
 -------------------------*/
 [[setting:customcss]]
index 01427e4..4782380 100644 (file)
@@ -30,7 +30,7 @@
 
 defined('MOODLE_INTERNAL') || die;
 
-$plugin->version   = 2015111600;
+$plugin->version   = 2015111601;
 $plugin->requires  = 2015111000;
 $plugin->component = 'theme_clean';
 $plugin->dependencies = array(
index 69a4583..2653cf7 100644 (file)
@@ -72,6 +72,11 @@ $string['region-side-post'] = 'Right';
 $string['region-side-pre'] = 'Left';
 $string['secondarybackground'] = 'Secondary background colour';
 $string['secondarybackground_desc'] = 'The background colour of any secondary content, such as blocks.';
+$string['sitelogo'] = 'Site logo';
+$string['sitename'] = 'Display site name along with small logo';
+$string['sitenamedesc'] = 'This setting is only applied if a "Small logo" is set. If no small logo is set the site name is always displayed in the navigation bar, but if it is you can use this setting to display it or not.';
+$string['smalllogo'] = 'Small logo';
+$string['smalllogodesc'] = 'This logo is displayed in the navbar next to the site name, if a "Logo" is set, then it is only displayed on the pages where the logo is not displayed.';
 $string['textcolor'] = 'Text colour';
 $string['textcolor_desc'] = 'The colour of the text.';
 
index 0e8017f..b1f6f45 100644 (file)
@@ -147,7 +147,8 @@ function theme_more_set_logo($css, $logo) {
  * @return bool
  */
 function theme_more_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload, array $options = array()) {
-    if ($context->contextlevel == CONTEXT_SYSTEM && ($filearea === 'logo' || $filearea === 'backgroundimage')) {
+    if ($context->contextlevel == CONTEXT_SYSTEM &&
+            ($filearea === 'logo' || $filearea === 'smalllogo' || $filearea === 'backgroundimage')) {
         $theme = theme_config::load('more');
         // By default, theme files must be cache-able by both browsers and proxies.
         if (!array_key_exists('cacheability', $options)) {
index 8b41c30..69c800f 100644 (file)
@@ -149,6 +149,22 @@ if ($ADMIN->fulltree) {
     $setting->set_updatedcallback('theme_reset_all_caches');
     $settings->add($setting);
 
+    // Small logo file setting.
+    $name = 'theme_more/smalllogo';
+    $title = get_string('smalllogo', 'theme_more');
+    $description = get_string('smalllogodesc', 'theme_more');
+    $setting = new admin_setting_configstoredfile($name, $title, $description, 'smalllogo');
+    $setting->set_updatedcallback('theme_reset_all_caches');
+    $settings->add($setting);
+
+    // Show site name along with small logo.
+    $name = 'theme_more/sitename';
+    $title = get_string('sitename', 'theme_more');
+    $description = get_string('sitenamedesc', 'theme_more');
+    $setting = new admin_setting_configcheckbox($name, $title, $description, 1);
+    $setting->set_updatedcallback('theme_reset_all_caches');
+    $settings->add($setting);
+
     // Custom CSS file.
     $name = 'theme_more/customcss';
     $title = get_string('customcss', 'theme_more');
index bb4cc68..b8d37a0 100644 (file)
@@ -16,6 +16,37 @@ div.logo {
     float: right;
 }
 
+img.small-logo {
+    float: left;
+    height: 35px;
+    margin: 3px 10px 3px 0;
+}
+
+.dir-rtl img.small-logo {
+    float: right;
+    margin: 3px 0 3px 10px;
+}
+
+@media (max-width: 767px) {
+    .dir-rtl img.small-logo,
+    img.small-logo {
+        margin: 3px;
+    }
+}
+
+@media (max-width: 480px) {
+    .navbar img.small-logo {
+        max-width: 150px;
+    }
+    /* Applying accesshide styles */
+    .navbar .small-logo-container + .brand {
+        position: absolute;
+        left: -10000px;
+        font-size: 1em;
+        font-weight: normal;
+    }
+}
+
 /* Custom CSS Settings
 -------------------------*/
 [[setting:customcss]]
index 13ea56f..22f3624 100644 (file)
 
 defined('MOODLE_INTERNAL') || die;
 
-$plugin->version   = 2015111600;
+$plugin->version   = 2015111601;
 $plugin->requires  = 2015111000;
 $plugin->component = 'theme_more';
 $plugin->dependencies = array(
     'theme_bootstrapbase'  => 2015111000,
-    'theme_clean'  => 2015111000,
+    'theme_clean'  => 2015111601,
 );
index bb052c5..1264298 100644 (file)
@@ -14,6 +14,9 @@ information provided here is intended especially for theme designer.
   themes extending bootstrapbase and overriding its layouts can call replace their "a.btn-navbar"
   node for a call to this function.
 * Themes Clean and More page header logo only displays on front page and login page.
+* A new function navbar_home has been added to theme_clean and theme_more to display the navigation bar link
+  to the site home. Two new settings have been added to control if the link should be a small logo image, text
+  or both. It defaults to the current behaviour, only a text link.
 
 === 2.9 ===
 
index f519938..721b538 100644 (file)
@@ -137,7 +137,7 @@ class user_edit_form extends moodleform {
                 if (!$mform->elementExists($formfield)) {
                     continue;
                 }
-                $value = $mform->getElementValue($formfield);
+                $value = $mform->getElement($formfield)->exportValue($mform->getElementValue($formfield)) ?: '';
                 $configvariable = 'field_lock_' . $field;
                 if (isset($authplugin->config->{$configvariable})) {
                     if ($authplugin->config->{$configvariable} === 'locked') {
index 3e00bee..4306c88 100644 (file)
@@ -29,7 +29,7 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$version  = 2015111600.02;              // YYYYMMDD      = weekly release date of this DEV branch.
+$version  = 2015092400.01;              // YYYYMMDD      = weekly release date of this DEV branch.
                                         //         RR    = release increments - 00 in DEV branches.
                                         //           .XX = incremental changes.