Merge branch 'MDL-50642' of https://github.com/ecampbell/moodle
authorEloy Lafuente (stronk7) <stronk7@moodle.org>
Tue, 11 Aug 2015 20:32:11 +0000 (22:32 +0200)
committerEloy Lafuente (stronk7) <stronk7@moodle.org>
Tue, 11 Aug 2015 20:32:11 +0000 (22:32 +0200)
109 files changed:
admin/environment.xml
admin/search.php
admin/settings.php
admin/tool/behat/tests/behat/edit_permissions.feature
admin/upgradesettings.php
auth/db/auth.php
availability/condition/grade/classes/condition.php
availability/tests/component_test.php
blocks/rss_client/block_rss_client.php
blocks/rss_client/db/install.xml
blocks/rss_client/db/upgrade.php [new file with mode: 0644]
blocks/rss_client/tests/cron_test.php [new file with mode: 0644]
blocks/rss_client/version.php
blocks/site_main_menu/block_site_main_menu.php
blocks/tag_youtube/db/install.php [new file with mode: 0644]
blocks/tag_youtube/tests/block_tag_youtube_test.php [new file with mode: 0644]
blocks/tests/behat/restrict_available_blocks.feature
cache/classes/helper.php
cache/tests/cache_test.php
composer.json
composer.lock
course/tests/behat/navigate_course_list.feature
course/tests/behat/restrict_available_activities.feature
group/module.js
group/overview.php
group/tests/behat/create_groups.feature
install/lang/oc_gsc/admin.php [new file with mode: 0644]
install/lang/oc_gsc/error.php [new file with mode: 0644]
install/lang/oc_gsc/install.php [new file with mode: 0644]
install/lang/oc_gsc/moodle.php [new file with mode: 0644]
lang/en/admin.php
lang/en/auth.php
lang/en/deprecated.txt
lang/en/notes.php
lib/authlib.php
lib/behat/behat_files.php
lib/classes/task/manager.php
lib/db/access.php
lib/db/services.php
lib/dml/oci_native_moodle_database.php
lib/dml/tests/dml_test.php
lib/environmentlib.php
lib/formslib.php
lib/phpunit/classes/advanced_testcase.php
lib/phpunit/classes/base_testcase.php [new file with mode: 0644]
lib/phpunit/classes/basic_testcase.php
lib/phpunit/classes/hint_resultprinter.php
lib/phpunit/lib.php
lib/phpunit/phpunit.xsd
lib/tests/behat/behat_permissions.php
lib/tests/environment_test.php
lib/tests/scheduled_task_test.php
lib/upgrade.txt
lib/upgradelib.php
lib/weblib.php
message/lib.php
message/tests/externallib_test.php
message/tests/messagelib_test.php
mod/assign/feedback/editpdf/classes/page_editor.php
mod/folder/classes/external.php [new file with mode: 0644]
mod/folder/db/services.php [new file with mode: 0644]
mod/folder/lib.php
mod/folder/tests/externallib_test.php [new file with mode: 0644]
mod/folder/tests/lib_test.php [new file with mode: 0644]
mod/folder/version.php
mod/forum/externallib.php
mod/forum/lib.php
mod/forum/subscribers.php
mod/forum/tests/behat/forum_subscriptions_availability.feature
mod/forum/tests/behat/separate_group_discussions.feature
mod/forum/tests/behat/separate_group_single_group_discussions.feature
mod/forum/tests/externallib_test.php
mod/lesson/format.php
mod/lesson/locallib.php
mod/lesson/tests/behat/lesson_delete_answers.feature [new file with mode: 0644]
mod/lti/typessettings.php
mod/page/classes/external.php [new file with mode: 0644]
mod/page/db/services.php [new file with mode: 0644]
mod/page/lib.php
mod/page/tests/externallib_test.php [new file with mode: 0644]
mod/page/tests/lib_test.php [new file with mode: 0644]
mod/page/version.php
mod/page/view.php
mod/quiz/classes/plugininfo/quizaccess.php
mod/quiz/version.php
mod/resource/classes/external.php [new file with mode: 0644]
mod/resource/db/services.php [new file with mode: 0644]
mod/resource/lib.php
mod/resource/tests/externallib_test.php [new file with mode: 0644]
mod/resource/tests/lib_test.php [new file with mode: 0644]
mod/resource/version.php
mod/resource/view.php
mod/scorm/locallib.php
my/tests/behat/restrict_available_blocks.feature
notes/delete.php
notes/externallib.php
notes/lib.php
phpunit.xml.dist
question/type/questiontypebase.php
question/type/tests/questiontype_test.php
repository/tests/generator_test.php
repository/youtube/db/install.php
tag/manage.php
theme/base/style/blocks.css
theme/bootstrapbase/less/moodle/blocks.less
theme/bootstrapbase/less/moodle/message.less
theme/bootstrapbase/style/moodle.css
user/tests/behat/view_full_profile.feature
version.php

index e596403..32bb207 100644 (file)
       <VENDOR name="oracle" version="10.2" />
     </DATABASE>
     <PHP version="5.4.4" level="required">
+      <RESTRICT function="restrict_php_version_7" message="unsupportedphpversion7" />
     </PHP>
     <PCREUNICODE level="optional">
       <FEEDBACK>
           <ON_ERROR message="quizattemptsupgradedmessage" />
         </FEEDBACK>
       </CUSTOM_CHECK>
+      <CUSTOM_CHECK file="lib/upgradelib.php" function="check_slasharguments" level="optional">
+        <FEEDBACK>
+          <ON_CHECK message="slashargumentswarning" />
+        </FEEDBACK>
+      </CUSTOM_CHECK>
+      <CUSTOM_CHECK file="lib/upgradelib.php" function="check_database_tables_row_format" level="optional">
+        <FEEDBACK>
+          <ON_CHECK message="unsupporteddbtablerowformat" />
+        </FEEDBACK>
+      </CUSTOM_CHECK>
     </CUSTOM_CHECKS>
   </MOODLE>
   <MOODLE version="2.8" requires="2.2">
       <VENDOR name="oracle" version="10.2" />
     </DATABASE>
     <PHP version="5.4.4" level="required">
+      <RESTRICT function="restrict_php_version_7" message="unsupportedphpversion7" />
     </PHP>
     <PCREUNICODE level="optional">
       <FEEDBACK>
           <ON_ERROR message="quizattemptsupgradedmessage" />
         </FEEDBACK>
       </CUSTOM_CHECK>
+      <CUSTOM_CHECK file="lib/upgradelib.php" function="check_slasharguments" level="optional">
+        <FEEDBACK>
+          <ON_CHECK message="slashargumentswarning" />
+        </FEEDBACK>
+      </CUSTOM_CHECK>
+      <CUSTOM_CHECK file="lib/upgradelib.php" function="check_database_tables_row_format" level="optional">
+        <FEEDBACK>
+          <ON_CHECK message="unsupporteddbtablerowformat" />
+        </FEEDBACK>
+      </CUSTOM_CHECK>
     </CUSTOM_CHECKS>
   </MOODLE>
   <MOODLE version="2.9" requires="2.2">
       <VENDOR name="oracle" version="10.2" />
     </DATABASE>
     <PHP version="5.4.4" level="required">
+      <RESTRICT function="restrict_php_version_7" message="unsupportedphpversion7" />
     </PHP>
     <PCREUNICODE level="optional">
       <FEEDBACK>
index dc39e04..82c5969 100644 (file)
@@ -46,6 +46,8 @@ $resultshtml = admin_search_settings_html($query); // case insensitive search on
 echo '<form action="' . $PAGE->url->out(true) . '" method="post" id="adminsettings">';
 echo '<div>';
 echo '<input type="hidden" name="sesskey" value="'.sesskey().'" />';
+// HACK to prevent browsers from automatically inserting the user's password into the wrong fields.
+echo prevent_form_autofill_password();
 echo '</div>';
 echo '<fieldset>';
 echo '<div class="clearer"><!-- --></div>';
index 2acbc83..e8f5782 100644 (file)
@@ -77,6 +77,8 @@ if (empty($SITE->fullname)) {
     echo html_writer::input_hidden_params($PAGE->url);
     echo '<input type="hidden" name="sesskey" value="'.sesskey().'" />';
     echo '<input type="hidden" name="return" value="'.$return.'" />';
+    // HACK to prevent browsers from automatically inserting the user's password into the wrong fields.
+    echo prevent_form_autofill_password();
 
     echo $settingspage->output_html();
 
@@ -119,6 +121,8 @@ if (empty($SITE->fullname)) {
     echo html_writer::input_hidden_params($PAGE->url);
     echo '<input type="hidden" name="sesskey" value="'.sesskey().'" />';
     echo '<input type="hidden" name="return" value="'.$return.'" />';
+    // HACK to prevent browsers from automatically inserting the user's password into the wrong fields.
+    echo prevent_form_autofill_password();
     echo $OUTPUT->heading($settingspage->visiblename);
 
     echo $settingspage->output_html();
index 26d4f42..29f7981 100644 (file)
@@ -15,7 +15,6 @@ Feature: Edit capabilities
       | user | course | role |
       | teacher1 | C1 | editingteacher |
 
-  @javascript
   Scenario: Default system capabilities modification
     Given I log in as "admin"
     And I set the following system permissions of "Teacher" role:
@@ -30,7 +29,6 @@ Feature: Edit capabilities
     And "moodle/grade:managesharedforms" capability has "Prevent" permission
     And "moodle/course:request" capability has "Prohibit" permission
 
-  @javascript
   Scenario: Course capabilities overrides
     Given I log in as "teacher1"
     And I follow "Course 1"
@@ -41,11 +39,11 @@ Feature: Edit capabilities
       | mod/forum:editanypost | Prevent |
       | mod/forum:addquestion | Allow |
     When I set the field "Advanced role override" to "Student (3)"
+    And I press "Go"
     Then "mod/forum:deleteanypost" capability has "Prohibit" permission
     And "mod/forum:editanypost" capability has "Prevent" permission
     And "mod/forum:addquestion" capability has "Allow" permission
 
-  @javascript
   Scenario: Module capabilities overrides
     Given I log in as "teacher1"
     And I follow "Course 1"
@@ -60,6 +58,7 @@ Feature: Edit capabilities
       | mod/forum:editanypost | Prevent |
       | mod/forum:addquestion | Allow |
     When I set the field "Advanced role override" to "Student (3)"
+    And I press "Go"
     Then "mod/forum:deleteanypost" capability has "Prohibit" permission
     And "mod/forum:editanypost" capability has "Prevent" permission
     And "mod/forum:addquestion" capability has "Allow" permission
index 38a8f06..f4aa617 100644 (file)
@@ -63,6 +63,8 @@ echo '<form action="upgradesettings.php" method="post" id="adminsettings">';
 echo '<div>';
 echo '<input type="hidden" name="sesskey" value="'.sesskey().'" />';
 echo '<input type="hidden" name="return" value="'.$return.'" />';
+// HACK to prevent browsers from automatically inserting the user's password into the wrong fields.
+echo prevent_form_autofill_password();
 echo '<fieldset>';
 echo '<div class="clearer"><!-- --></div>';
 echo $newsettingshtml;
index 9121c6e..f03cbef 100644 (file)
@@ -58,6 +58,11 @@ class auth_plugin_db extends auth_plugin_base {
     function user_login($username, $password) {
         global $CFG, $DB;
 
+        if ($this->is_configured() === false) {
+            debugging(get_string('auth_notconfigured', 'auth', $this->authtype));
+            return false;
+        }
+
         $extusername = core_text::convert($username, 'utf-8', $this->config->extencoding);
         $extpassword = core_text::convert($password, 'utf-8', $this->config->extencoding);
 
@@ -144,8 +149,13 @@ class auth_plugin_db extends auth_plugin_base {
      * Connect to external database.
      *
      * @return ADOConnection
+     * @throws moodle_exception
      */
     function db_init() {
+        if ($this->is_configured() === false) {
+            throw new moodle_exception('auth_dbcantconnect', 'auth_db');
+        }
+
         // Connect to the external database (forcing new connection).
         $authdb = ADONewConnection($this->config->type);
         if (!empty($this->config->debugauthdb)) {
@@ -661,6 +671,18 @@ class auth_plugin_db extends auth_plugin_base {
         return ($this->config->passtype === 'internal');
     }
 
+    /**
+     * Returns false if this plugin is enabled but not configured.
+     *
+     * @return bool
+     */
+    public function is_configured() {
+        if (!empty($this->config->type)) {
+            return true;
+        }
+        return false;
+    }
+
     /**
      * Indicates if moodle should automatically update internal user
      * records with data from external sources using the information
index 8a4bd5b..440683c 100644 (file)
@@ -228,7 +228,9 @@ class condition extends \core_availability\condition {
                         WHERE
                             gi.courseid = ?', array($userid, $courseid));
                 foreach ($rs as $record) {
-                    if (is_null($record->finalgrade)) {
+                    // This function produces division by zero error warnings when rawgrademax and rawgrademin
+                    // are equal. Below change does not affect function behavior, just avoids the warning.
+                    if (is_null($record->finalgrade) || $record->rawgrademax == $record->rawgrademin) {
                         // No grade = false.
                         $cachedgrades[$record->id] = false;
                     } else {
@@ -249,7 +251,9 @@ class condition extends \core_availability\condition {
                 // Just get current grade.
                 $record = $DB->get_record('grade_grades', array(
                     'userid' => $userid, 'itemid' => $gradeitemid));
-                if ($record && !is_null($record->finalgrade)) {
+                // This function produces division by zero error warnings when rawgrademax and rawgrademin
+                // are equal. Below change does not affect function behavior, just avoids the warning.
+                if ($record && !is_null($record->finalgrade) && $record->rawgrademax != $record->rawgrademin) {
                     $score = (($record->finalgrade - $record->rawgrademin) * 100) /
                         ($record->rawgrademax - $record->rawgrademin);
                 } else {
index 74c3a63..de05104 100644 (file)
@@ -49,7 +49,11 @@ class core_availability_component_testcase extends advanced_testcase {
         // fail, but it's obvious when running test at least.
         $pluginmanager = core_plugin_manager::instance();
         $list = $pluginmanager->get_enabled_plugins('availability');
-        $this->assertEquals(array('completion', 'date', 'grade', 'group', 'grouping', 'profile'),
-                array_keys($list));
+        $this->assertArrayHasKey('completion', $list);
+        $this->assertArrayHasKey('date', $list);
+        $this->assertArrayHasKey('grade', $list);
+        $this->assertArrayHasKey('group', $list);
+        $this->assertArrayHasKey('grouping', $list);
+        $this->assertArrayHasKey('profile', $list);
     }
 }
index 2838651..68a13ac 100644 (file)
@@ -23,6 +23,8 @@
  */
 
  class block_rss_client extends block_base {
+    /** The maximum time in seconds that cron will wait between attempts to retry failing RSS feeds. */
+    const CLIENT_MAX_SKIPTIME = 43200; // 60 * 60 * 12 seconds.
 
     function init() {
         $this->title = get_string('pluginname', 'block_rss_client');
     }
 
     /**
-     * cron - goes through all feeds and retrieves them with the cache
-     * duration set to 0 in order to force the retrieval of the item and
-     * refresh the cache
+     * cron - goes through all the feeds. If the feed has a skipuntil value
+     * that is less than the current time cron will attempt to retrieve it
+     * with the cache duration set to 0 in order to force the retrieval of
+     * the item and refresh the cache.
      *
-     * @return boolean true if all feeds were retrieved succesfully
+     * If a feed fails then the skipuntil time of that feed is set to be
+     * later than the next expected cron time. The amount of time will
+     * increase each time the fetch fails until the maximum is reached.
+     *
+     * If a feed that has been failing is successfully retrieved it will
+     * go back to being handled as though it had never failed.
+     *
+     * CRON should therefor process requests for permanently broken RSS
+     * feeds infrequently, and temporarily unavailable feeds will be tried
+     * less often until they become available again.
+     *
+     * @return boolean Always returns true
      */
     function cron() {
         global $CFG, $DB;
         require_once($CFG->libdir.'/simplepie/moodle_simplepie.php');
 
+        // Get the legacy cron time, strangely the cron property of block_base
+        // does not seem to get set. This means we must retrive it here.
+        $this->cron = $DB->get_field('block', 'cron', array('name' => 'rss_client'));
+
         // We are going to measure execution times
         $starttime =  microtime();
-
-        // And we have one initial $status
-        $status = true;
+        $starttimesec = time();
 
         // Fetch all site feeds.
         $rs = $DB->get_recordset('block_rss_client');
         mtrace('');
         foreach ($rs as $rec) {
             mtrace('    ' . $rec->url . ' ', '');
+
+            // Skip feed if it failed recently.
+            if ($starttimesec < $rec->skipuntil) {
+                mtrace('skipping until ' . userdate($rec->skipuntil));
+                continue;
+            }
+
             // Fetch the rss feed, using standard simplepie caching
             // so feeds will be renewed only if cache has expired
             core_php_time_limit::raise(60);
             $feed->init();
 
             if ($feed->error()) {
-                mtrace('Error: could not load/find the RSS feed');
-                $status = false;
+                // Skip this feed (for an ever-increasing time if it keeps failing).
+                $rec->skiptime = $this->calculate_skiptime($rec->skiptime);
+                $rec->skipuntil = time() + $rec->skiptime;
+                $DB->update_record('block_rss_client', $rec);
+                mtrace("Error: could not load/find the RSS feed - skipping for {$rec->skiptime} seconds.");
             } else {
                 mtrace ('ok');
+                // It worked this time, so reset the skiptime.
+                if ($rec->skiptime > 0) {
+                    $rec->skiptime = 0;
+                    $rec->skipuntil = 0;
+                    $DB->update_record('block_rss_client', $rec);
+                }
+                // Only increase the counter when a feed is sucesfully refreshed.
+                $counter ++;
             }
-            $counter ++;
         }
         $rs->close();
 
         // Show times
         mtrace($counter . ' feeds refreshed (took ' . microtime_diff($starttime, microtime()) . ' seconds)');
 
-        // And return $status
-        return $status;
+        return true;
+    }
+
+    /**
+     * Calculates a new skip time for a record based on the current skip time.
+     *
+     * @param int $currentskip The curreent skip time of a record.
+     * @return int A new skip time that should be set.
+     */
+    protected function calculate_skiptime($currentskip) {
+        // The default time to skiptime.
+        $newskiptime = $this->cron * 1.1;
+        if ($currentskip > 0) {
+            // Double the last time.
+            $newskiptime = $currentskip * 2;
+        }
+        if ($newskiptime > self::CLIENT_MAX_SKIPTIME) {
+            // Do not allow the skip time to increase indefinatly.
+            $newskiptime = self::CLIENT_MAX_SKIPTIME;
+        }
+        return $newskiptime;
     }
 }
 
index f81d650..7a7e9cb 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8" ?>
-<XMLDB PATH="blocks/rss_client/db" VERSION="20120122" COMMENT="XMLDB file for Moodle rss_client block"
+<XMLDB PATH="blocks/rss_client/db" VERSION="20150717" COMMENT="XMLDB file for Moodle rss_client block"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:noNamespaceSchemaLocation="../../../lib/xmldb/xmldb.xsd"
 >
@@ -13,6 +13,8 @@
         <FIELD NAME="description" TYPE="text" NOTNULL="true" SEQUENCE="false"/>
         <FIELD NAME="shared" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
         <FIELD NAME="url" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false"/>
+        <FIELD NAME="skiptime" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="How many seconds skip this feed for (increases every time it fails, resets to 0 when it succeeds)"/>
+        <FIELD NAME="skipuntil" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Do not query this RSS feed again until this time"/>
       </FIELDS>
       <KEYS>
         <KEY NAME="primary" TYPE="primary" FIELDS="id" />
diff --git a/blocks/rss_client/db/upgrade.php b/blocks/rss_client/db/upgrade.php
new file mode 100644 (file)
index 0000000..afc2b80
--- /dev/null
@@ -0,0 +1,54 @@
+<?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/>.
+
+/**
+ * Database upgrades for the RSS block.
+ *
+ * @package   block_rss_client
+ * @copyright 2014 Davo Smith
+ * @author    Neill Magill <neill.magill@nottingham.ac.uk>
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL
+ */
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Upgrade the block_rss_client database.
+ *
+ * @param int $oldversion The version number of the plugin that was installed.
+ * @return boolean
+ */
+function xmldb_block_rss_client_upgrade($oldversion) {
+    global $DB;
+    $dbman = $DB->get_manager();
+
+    if ($oldversion < 2015071700) {
+        // Support for skipping RSS feeds for a while when they fail.
+        $table = new xmldb_table('block_rss_client');
+        // How many seconds we are currently ignoring this RSS feed for (due to an error).
+        $field = new xmldb_field('skiptime', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0', 'url');
+        if (!$dbman->field_exists($table, $field)) {
+            $dbman->add_field($table, $field);
+        }
+        // When to next update this RSS feed.
+        $field = new xmldb_field('skipuntil', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0', 'skiptime');
+        if (!$dbman->field_exists($table, $field)) {
+            $dbman->add_field($table, $field);
+        }
+        upgrade_block_savepoint(true, 2015071700, 'rss_client');
+    }
+
+    return true;
+}
diff --git a/blocks/rss_client/tests/cron_test.php b/blocks/rss_client/tests/cron_test.php
new file mode 100644 (file)
index 0000000..9069c28
--- /dev/null
@@ -0,0 +1,141 @@
+<?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/>.
+
+/**
+ * PHPunit tests for rss client cron.
+ *
+ * @package    block_rss_client
+ * @copyright  2015 University of Nottingham
+ * @author     Neill Magill <neill.magill@nottingham.ac.uk>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+defined('MOODLE_INTERNAL') || die();
+require_once(dirname(dirname(__DIR__)) . '/moodleblock.class.php');
+require_once(dirname(__DIR__) . '/block_rss_client.php');
+
+/**
+ * Class for the PHPunit tests for rss client cron.
+ *
+ * @package    block_rss_client
+ * @copyright  2015 Universit of Nottingham
+ * @author     Neill Magill <neill.magill@nottingham.ac.uk>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class block_rss_client_cron_testcase extends advanced_testcase {
+    /**
+     * Test that when a record has a skipuntil time that is greater
+     * than the current time the attempt is skipped.
+     */
+    public function test_skip() {
+        global $DB;
+        $this->resetAfterTest();
+        // Create a RSS feed record with a skip until time set to the future.
+        $record = (object) array(
+            'userid' => 1,
+            'title' => 'Skip test feed',
+            'preferredtitle' => '',
+            'description' => 'A feed to test the skip time.',
+            'shared' => 0,
+            'url' => 'http://example.com/rss',
+            'skiptime' => 330,
+            'skipuntil' => time() + 300,
+        );
+        $DB->insert_record('block_rss_client', $record);
+
+        $block = new block_rss_client();
+        ob_start();
+        // Silence SimplePie php notices.
+        @$block->cron();
+        $cronoutput = ob_get_clean();
+        $this->assertContains('skipping until ' . userdate($record->skipuntil), $cronoutput);
+        $this->assertContains('0 feeds refreshed (took ', $cronoutput);
+    }
+
+    /**
+     * Test that when a feed has an error the skip time is increaed correctly.
+     */
+    public function test_error() {
+        global $DB;
+        $this->resetAfterTest();
+        $time = time();
+        // A record that has failed before.
+        $record = (object) array(
+            'userid' => 1,
+            'title' => 'Skip test feed',
+            'preferredtitle' => '',
+            'description' => 'A feed to test the skip time.',
+            'shared' => 0,
+            'url' => 'http://example.com/rss',
+            'skiptime' => 330,
+            'skipuntil' => $time - 300,
+        );
+        $record->id = $DB->insert_record('block_rss_client', $record);
+
+        // A record that has not failed before.
+        $record2 = (object) array(
+            'userid' => 1,
+            'title' => 'Skip test feed',
+            'preferredtitle' => '',
+            'description' => 'A feed to test the skip time.',
+            'shared' => 0,
+            'url' => 'http://example.com/rss2',
+            'skiptime' => 0,
+            'skipuntil' => 0,
+        );
+        $record2->id = $DB->insert_record('block_rss_client', $record2);
+
+        // A record that is near the maximum wait time.
+        $record3 = (object) array(
+            'userid' => 1,
+            'title' => 'Skip test feed',
+            'preferredtitle' => '',
+            'description' => 'A feed to test the skip time.',
+            'shared' => 0,
+            'url' => 'http://example.com/rss3',
+            'skiptime' => block_rss_client::CLIENT_MAX_SKIPTIME - 5,
+            'skipuntil' => $time - 1,
+        );
+        $record3->id = $DB->insert_record('block_rss_client', $record3);
+
+        // Run the cron.
+        $block = new block_rss_client();
+        ob_start();
+        // Silence SimplePie php notices.
+        @$block->cron();
+        $cronoutput = ob_get_clean();
+        $skiptime1 = $record->skiptime * 2;
+        $message1 = 'http://example.com/rss Error: could not load/find the RSS feed - skipping for ' . $skiptime1 . ' seconds.';
+        $this->assertContains($message1, $cronoutput);
+        $skiptime2 = 330; // Assumes that the cron time in the version file is 300.
+        $message2 = 'http://example.com/rss2 Error: could not load/find the RSS feed - skipping for ' . $skiptime2 . ' seconds.';
+        $this->assertContains($message2, $cronoutput);
+        $skiptime3 = block_rss_client::CLIENT_MAX_SKIPTIME;
+        $message3 = 'http://example.com/rss3 Error: could not load/find the RSS feed - skipping for ' . $skiptime3 . ' seconds.';
+        $this->assertContains($message3, $cronoutput);
+        $this->assertContains('0 feeds refreshed (took ', $cronoutput);
+
+        // Test that the records have been correctly updated.
+        $newrecord = $DB->get_record('block_rss_client', array('id' => $record->id));
+        $this->assertAttributeEquals($skiptime1, 'skiptime', $newrecord);
+        $this->assertAttributeGreaterThanOrEqual($time + $skiptime1, 'skipuntil', $newrecord);
+        $newrecord2 = $DB->get_record('block_rss_client', array('id' => $record2->id));
+        $this->assertAttributeEquals($skiptime2, 'skiptime', $newrecord2);
+        $this->assertAttributeGreaterThanOrEqual($time + $skiptime2, 'skipuntil', $newrecord2);
+        $newrecord3 = $DB->get_record('block_rss_client', array('id' => $record3->id));
+        $this->assertAttributeEquals($skiptime3, 'skiptime', $newrecord3);
+        $this->assertAttributeGreaterThanOrEqual($time + $skiptime3, 'skipuntil', $newrecord3);
+    }
+}
index d1979d3..2e2cea9 100644 (file)
@@ -24,7 +24,7 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2015051100;        // The current plugin version (Date: YYYYMMDDXX)
+$plugin->version   = 2015071700;        // The current plugin version (Date: YYYYMMDDXX)
 $plugin->requires  = 2015050500;        // Requires this Moodle version
 $plugin->component = 'block_rss_client'; // Full name of the plugin (used for diagnostics)
 $plugin->cron      = 300;               // Set min time between cron executions to 300 secs (5 mins)
index e28c75b..d0d938d 100644 (file)
@@ -47,7 +47,7 @@ class block_site_main_menu extends block_list {
             return $this->content;
         }
 
-        $course = $this->page->course;
+        $course = get_site();
         require_once($CFG->dirroot.'/course/lib.php');
         $context = context_course::instance($course->id);
         $isediting = $this->page->user_is_editing() && has_capability('moodle/course:manageactivities', $context);
diff --git a/blocks/tag_youtube/db/install.php b/blocks/tag_youtube/db/install.php
new file mode 100644 (file)
index 0000000..0572762
--- /dev/null
@@ -0,0 +1,36 @@
+<?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/>.
+
+/**
+ * Tag Youtube block installation.
+ *
+ * @package    block_tag_youtube
+ * @copyright  2015 Jun Pataleta
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/**
+ * Sets the install values for the tag_youtube entry in the block table.
+ *
+ * @return void
+ */
+function xmldb_block_tag_youtube_install() {
+    global $DB;
+
+    // Disable this block by default.
+    $DB->set_field('block', 'visible', 0, array('name' => 'tag_youtube'));
+}
+
diff --git a/blocks/tag_youtube/tests/block_tag_youtube_test.php b/blocks/tag_youtube/tests/block_tag_youtube_test.php
new file mode 100644 (file)
index 0000000..6434c44
--- /dev/null
@@ -0,0 +1,51 @@
+<?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/>.
+
+/**
+ * Block Tag Youtube tests
+ *
+ * @package    block_tag_youtube
+ * @category   test
+ * @copyright  2015 Jun Pataleta
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Block Tag Youtube test class.
+ *
+ * @package   block_tag_youtube
+ * @category  test
+ * @copyright 2015 Jun Pataleta
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class core_block_tag_youtube_testcase extends advanced_testcase {
+
+    /**
+     * Testing the tag youtube block's initial state after a new installation.
+     *
+     * @return void
+     */
+    public function test_after_install() {
+        global $DB;
+
+        $this->resetAfterTest(true);
+
+        // Assert that tag_youtube entry exists and that its visible attribute is set to 0 (disabled).
+        $this->assertTrue($DB->record_exists('block', array('name' => 'tag_youtube', 'visible' => 0)));
+    }
+}
index 4b1fd5a..363714c 100644 (file)
@@ -24,7 +24,6 @@ Feature: Allowed blocks controls
     Then I should see "Activities" in the "Activities" "block"
     And I should see "Course completion status" in the "Course completion status" "block"
 
-  @javascript
   Scenario: Blocks can not be added when the admin restricts the permissions
     Given I log in as "admin"
     And I set the following system permissions of "Teacher" role:
index eca785f..c25369e 100644 (file)
@@ -354,7 +354,7 @@ class cache_helper {
     protected static function ensure_ready_for_stats($store, $definition, $mode = cache_store::MODE_APPLICATION) {
         // This function is performance-sensitive, so exit as quickly as possible
         // if we do not need to do anything.
-        if (isset(self::$stats[$definition][$store])) {
+        if (isset(self::$stats[$definition]['stores'][$store])) {
             return;
         }
         if (!array_key_exists($definition, self::$stats)) {
@@ -368,7 +368,7 @@ class cache_helper {
                     )
                 )
             );
-        } else if (!array_key_exists($store, self::$stats[$definition])) {
+        } else if (!array_key_exists($store, self::$stats[$definition]['stores'])) {
             self::$stats[$definition]['stores'][$store] = array(
                 'hits' => 0,
                 'misses' => 0,
index 8300854..0d6daa7 100644 (file)
@@ -1875,4 +1875,155 @@ class core_cache_testcase extends advanced_testcase {
         $returnedinstance1->name = 'b';
         $this->assertEquals('b', $returnedinstance2->name);
     }
+
+    public function test_performance_debug() {
+        global $CFG;
+        $this->resetAfterTest(true);
+        $CFG->perfdebug = 15;
+
+        $instance = cache_config_testing::instance();
+        $applicationid = 'phpunit/applicationperf';
+        $instance->phpunit_add_definition($applicationid, array(
+            'mode' => cache_store::MODE_APPLICATION,
+            'component' => 'phpunit',
+            'area' => 'applicationperf'
+        ));
+        $sessionid = 'phpunit/sessionperf';
+        $instance->phpunit_add_definition($sessionid, array(
+            'mode' => cache_store::MODE_SESSION,
+            'component' => 'phpunit',
+            'area' => 'sessionperf'
+        ));
+        $requestid = 'phpunit/requestperf';
+        $instance->phpunit_add_definition($requestid, array(
+            'mode' => cache_store::MODE_REQUEST,
+            'component' => 'phpunit',
+            'area' => 'requestperf'
+        ));
+
+        $application = cache::make('phpunit', 'applicationperf');
+        $session = cache::make('phpunit', 'sessionperf');
+        $request = cache::make('phpunit', 'requestperf');
+
+        // Check that no stats are recorded for these definitions yet.
+        $stats = cache_helper::get_stats();
+        $this->assertArrayNotHasKey($applicationid, $stats);
+        $this->assertArrayHasKey($sessionid, $stats);       // Session cache sets a key on construct.
+        $this->assertArrayNotHasKey($requestid, $stats);
+
+        // Check that stores register misses.
+        $this->assertFalse($application->get('missMe'));
+        $this->assertFalse($application->get('missMe'));
+        $this->assertFalse($session->get('missMe'));
+        $this->assertFalse($session->get('missMe'));
+        $this->assertFalse($session->get('missMe'));
+        $this->assertFalse($request->get('missMe'));
+        $this->assertFalse($request->get('missMe'));
+        $this->assertFalse($request->get('missMe'));
+        $this->assertFalse($request->get('missMe'));
+
+        $endstats = cache_helper::get_stats();
+        $this->assertEquals(2, $endstats[$applicationid]['stores']['cachestore_file']['misses']);
+        $this->assertEquals(0, $endstats[$applicationid]['stores']['cachestore_file']['hits']);
+        $this->assertEquals(0, $endstats[$applicationid]['stores']['cachestore_file']['sets']);
+        $this->assertEquals(3, $endstats[$sessionid]['stores']['cachestore_session']['misses']);
+        $this->assertEquals(0, $endstats[$sessionid]['stores']['cachestore_session']['hits']);
+        $this->assertEquals(1, $endstats[$sessionid]['stores']['cachestore_session']['sets']);
+        $this->assertEquals(4, $endstats[$requestid]['stores']['cachestore_static']['misses']);
+        $this->assertEquals(0, $endstats[$requestid]['stores']['cachestore_static']['hits']);
+        $this->assertEquals(0, $endstats[$requestid]['stores']['cachestore_static']['sets']);
+
+        $startstats = cache_helper::get_stats();
+
+        // Check that stores register sets.
+        $this->assertTrue($application->set('setMe1', 1));
+        $this->assertTrue($application->set('setMe2', 2));
+        $this->assertTrue($session->set('setMe1', 1));
+        $this->assertTrue($session->set('setMe2', 2));
+        $this->assertTrue($session->set('setMe3', 3));
+        $this->assertTrue($request->set('setMe1', 1));
+        $this->assertTrue($request->set('setMe2', 2));
+        $this->assertTrue($request->set('setMe3', 3));
+        $this->assertTrue($request->set('setMe4', 4));
+
+        $endstats = cache_helper::get_stats();
+        $this->assertEquals(0, $endstats[$applicationid]['stores']['cachestore_file']['misses'] -
+            $startstats[$applicationid]['stores']['cachestore_file']['misses']);
+        $this->assertEquals(0, $endstats[$applicationid]['stores']['cachestore_file']['hits'] -
+            $startstats[$applicationid]['stores']['cachestore_file']['hits']);
+        $this->assertEquals(2, $endstats[$applicationid]['stores']['cachestore_file']['sets'] -
+            $startstats[$applicationid]['stores']['cachestore_file']['sets']);
+        $this->assertEquals(0, $endstats[$sessionid]['stores']['cachestore_session']['misses'] -
+            $startstats[$sessionid]['stores']['cachestore_session']['misses']);
+        $this->assertEquals(0, $endstats[$sessionid]['stores']['cachestore_session']['hits'] -
+            $startstats[$sessionid]['stores']['cachestore_session']['hits']);
+        $this->assertEquals(3, $endstats[$sessionid]['stores']['cachestore_session']['sets'] -
+            $startstats[$sessionid]['stores']['cachestore_session']['sets']);
+        $this->assertEquals(0, $endstats[$requestid]['stores']['cachestore_static']['misses'] -
+            $startstats[$requestid]['stores']['cachestore_static']['misses']);
+        $this->assertEquals(0, $endstats[$requestid]['stores']['cachestore_static']['hits'] -
+            $startstats[$requestid]['stores']['cachestore_static']['hits']);
+        $this->assertEquals(4, $endstats[$requestid]['stores']['cachestore_static']['sets'] -
+            $startstats[$requestid]['stores']['cachestore_static']['sets']);
+
+        $startstats = cache_helper::get_stats();
+
+        // Check that stores register hits.
+        $this->assertEquals($application->get('setMe1'), 1);
+        $this->assertEquals($application->get('setMe2'), 2);
+        $this->assertEquals($session->get('setMe1'), 1);
+        $this->assertEquals($session->get('setMe2'), 2);
+        $this->assertEquals($session->get('setMe3'), 3);
+        $this->assertEquals($request->get('setMe1'), 1);
+        $this->assertEquals($request->get('setMe2'), 2);
+        $this->assertEquals($request->get('setMe3'), 3);
+        $this->assertEquals($request->get('setMe4'), 4);
+
+        $endstats = cache_helper::get_stats();
+        $this->assertEquals(0, $endstats[$applicationid]['stores']['cachestore_file']['misses'] -
+            $startstats[$applicationid]['stores']['cachestore_file']['misses']);
+        $this->assertEquals(2, $endstats[$applicationid]['stores']['cachestore_file']['hits'] -
+            $startstats[$applicationid]['stores']['cachestore_file']['hits']);
+        $this->assertEquals(0, $endstats[$applicationid]['stores']['cachestore_file']['sets'] -
+            $startstats[$applicationid]['stores']['cachestore_file']['sets']);
+        $this->assertEquals(0, $endstats[$sessionid]['stores']['cachestore_session']['misses'] -
+            $startstats[$sessionid]['stores']['cachestore_session']['misses']);
+        $this->assertEquals(3, $endstats[$sessionid]['stores']['cachestore_session']['hits'] -
+            $startstats[$sessionid]['stores']['cachestore_session']['hits']);
+        $this->assertEquals(0, $endstats[$sessionid]['stores']['cachestore_session']['sets'] -
+            $startstats[$sessionid]['stores']['cachestore_session']['sets']);
+        $this->assertEquals(0, $endstats[$requestid]['stores']['cachestore_static']['misses'] -
+            $startstats[$requestid]['stores']['cachestore_static']['misses']);
+        $this->assertEquals(4, $endstats[$requestid]['stores']['cachestore_static']['hits'] -
+            $startstats[$requestid]['stores']['cachestore_static']['hits']);
+        $this->assertEquals(0, $endstats[$requestid]['stores']['cachestore_static']['sets'] -
+            $startstats[$requestid]['stores']['cachestore_static']['sets']);
+
+        $startstats = cache_helper::get_stats();
+
+        // Check that stores register through get_many.
+        $application->get_many(array('setMe1', 'setMe2'));
+        $session->get_many(array('setMe1', 'setMe2', 'setMe3'));
+        $request->get_many(array('setMe1', 'setMe2', 'setMe3', 'setMe4'));
+
+        $endstats = cache_helper::get_stats();
+        $this->assertEquals(0, $endstats[$applicationid]['stores']['cachestore_file']['misses'] -
+            $startstats[$applicationid]['stores']['cachestore_file']['misses']);
+        $this->assertEquals(2, $endstats[$applicationid]['stores']['cachestore_file']['hits'] -
+            $startstats[$applicationid]['stores']['cachestore_file']['hits']);
+        $this->assertEquals(0, $endstats[$applicationid]['stores']['cachestore_file']['sets'] -
+            $startstats[$applicationid]['stores']['cachestore_file']['sets']);
+        $this->assertEquals(0, $endstats[$sessionid]['stores']['cachestore_session']['misses'] -
+            $startstats[$sessionid]['stores']['cachestore_session']['misses']);
+        $this->assertEquals(3, $endstats[$sessionid]['stores']['cachestore_session']['hits'] -
+            $startstats[$sessionid]['stores']['cachestore_session']['hits']);
+        $this->assertEquals(0, $endstats[$sessionid]['stores']['cachestore_session']['sets'] -
+            $startstats[$sessionid]['stores']['cachestore_session']['sets']);
+        $this->assertEquals(0, $endstats[$requestid]['stores']['cachestore_static']['misses'] -
+            $startstats[$requestid]['stores']['cachestore_static']['misses']);
+        $this->assertEquals(4, $endstats[$requestid]['stores']['cachestore_static']['hits'] -
+            $startstats[$requestid]['stores']['cachestore_static']['hits']);
+        $this->assertEquals(0, $endstats[$requestid]['stores']['cachestore_static']['sets'] -
+            $startstats[$requestid]['stores']['cachestore_static']['sets']);
+    }
 }
index de96736..26e49e4 100644 (file)
@@ -1,7 +1,7 @@
 {
     "require-dev": {
-        "phpunit/phpunit": "3.7.*",
-        "phpunit/dbUnit": "1.2.*",
+        "phpunit/phpunit": "4.7.*",
+        "phpunit/dbUnit": "1.4.*",
         "moodlehq/behat-extension": "1.30.0"
     }
 }
index cec8279..719ac08 100644 (file)
@@ -4,7 +4,7 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
         "This file is @generated automatically"
     ],
-    "hash": "3ddf5ab21f539f6f64c7e80174be48bb",
+    "hash": "a85d8c9e61ccba5e235093157021f7b5",
     "packages": [],
     "packages-dev": [
         {
             ],
             "time": "2014-12-20 21:24:13"
         },
+        {
+            "name": "doctrine/instantiator",
+            "version": "1.0.5",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/doctrine/instantiator.git",
+                "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d",
+                "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3,<8.0-DEV"
+            },
+            "require-dev": {
+                "athletic/athletic": "~0.1.8",
+                "ext-pdo": "*",
+                "ext-phar": "*",
+                "phpunit/phpunit": "~4.0",
+                "squizlabs/php_codesniffer": "~2.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Marco Pivetta",
+                    "email": "ocramius@gmail.com",
+                    "homepage": "http://ocramius.github.com/"
+                }
+            ],
+            "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors",
+            "homepage": "https://github.com/doctrine/instantiator",
+            "keywords": [
+                "constructor",
+                "instantiate"
+            ],
+            "time": "2015-06-14 21:17:01"
+        },
         {
             "name": "doctrine/lexer",
             "version": "v1.0.1",
             ],
             "time": "2015-05-15 02:00:06"
         },
+        {
+            "name": "phpdocumentor/reflection-docblock",
+            "version": "2.0.4",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
+                "reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/d68dbdc53dc358a816f00b300704702b2eaff7b8",
+                "reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "~4.0"
+            },
+            "suggest": {
+                "dflydev/markdown": "~1.0",
+                "erusev/parsedown": "~1.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-0": {
+                    "phpDocumentor": [
+                        "src/"
+                    ]
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Mike van Riel",
+                    "email": "mike.vanriel@naenius.com"
+                }
+            ],
+            "time": "2015-02-03 12:10:50"
+        },
+        {
+            "name": "phpspec/prophecy",
+            "version": "v1.4.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/phpspec/prophecy.git",
+                "reference": "3132b1f44c7bf2ec4c7eb2d3cb78fdeca760d373"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/phpspec/prophecy/zipball/3132b1f44c7bf2ec4c7eb2d3cb78fdeca760d373",
+                "reference": "3132b1f44c7bf2ec4c7eb2d3cb78fdeca760d373",
+                "shasum": ""
+            },
+            "require": {
+                "doctrine/instantiator": "^1.0.2",
+                "phpdocumentor/reflection-docblock": "~2.0",
+                "sebastian/comparator": "~1.1"
+            },
+            "require-dev": {
+                "phpspec/phpspec": "~2.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.4.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-0": {
+                    "Prophecy\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Konstantin Kudryashov",
+                    "email": "ever.zet@gmail.com",
+                    "homepage": "http://everzet.com"
+                },
+                {
+                    "name": "Marcello Duarte",
+                    "email": "marcello.duarte@gmail.com"
+                }
+            ],
+            "description": "Highly opinionated mocking framework for PHP 5.3+",
+            "homepage": "https://github.com/phpspec/prophecy",
+            "keywords": [
+                "Double",
+                "Dummy",
+                "fake",
+                "mock",
+                "spy",
+                "stub"
+            ],
+            "time": "2015-04-27 22:15:08"
+        },
         {
             "name": "phpunit/dbunit",
-            "version": "1.2.3",
+            "version": "1.4.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/dbunit.git",
-                "reference": "8386782a2d55153e44a06eb1a9d13d6ed35d9c2d"
+                "reference": "1afe25c90834ec499f007f48dd73767fdec3bf4f"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/dbunit/zipball/8386782a2d55153e44a06eb1a9d13d6ed35d9c2d",
-                "reference": "8386782a2d55153e44a06eb1a9d13d6ed35d9c2d",
+                "url": "https://api.github.com/repos/sebastianbergmann/dbunit/zipball/1afe25c90834ec499f007f48dd73767fdec3bf4f",
+                "reference": "1afe25c90834ec499f007f48dd73767fdec3bf4f",
                 "shasum": ""
             },
             "require": {
                 "ext-pdo": "*",
                 "ext-simplexml": "*",
                 "php": ">=5.3.3",
-                "phpunit/phpunit": ">=3.7.0@stable"
+                "phpunit/phpunit": "~4.0",
+                "symfony/yaml": "~2.1"
             },
             "bin": [
-                "dbunit.php"
+                "composer/bin/dbunit"
             ],
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.2.x-dev"
+                    "dev-master": "1.3.x-dev"
                 }
             },
             "autoload": {
                 "testing",
                 "xunit"
             ],
-            "time": "2013-03-01 11:50:46"
+            "time": "2015-05-21 21:11:02"
         },
         {
             "name": "phpunit/php-code-coverage",
-            "version": "1.2.18",
+            "version": "2.2.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
-                "reference": "fe2466802556d3fe4e4d1d58ffd3ccfd0a19be0b"
+                "reference": "2d7c03c0e4e080901b8f33b2897b0577be18a13c"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/fe2466802556d3fe4e4d1d58ffd3ccfd0a19be0b",
-                "reference": "fe2466802556d3fe4e4d1d58ffd3ccfd0a19be0b",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/2d7c03c0e4e080901b8f33b2897b0577be18a13c",
+                "reference": "2d7c03c0e4e080901b8f33b2897b0577be18a13c",
                 "shasum": ""
             },
             "require": {
                 "php": ">=5.3.3",
-                "phpunit/php-file-iterator": ">=1.3.0@stable",
-                "phpunit/php-text-template": ">=1.2.0@stable",
-                "phpunit/php-token-stream": ">=1.1.3,<1.3.0"
+                "phpunit/php-file-iterator": "~1.3",
+                "phpunit/php-text-template": "~1.2",
+                "phpunit/php-token-stream": "~1.3",
+                "sebastian/environment": "^1.3.2",
+                "sebastian/version": "~1.0"
             },
             "require-dev": {
-                "phpunit/phpunit": "3.7.*@dev"
+                "ext-xdebug": ">=2.1.4",
+                "phpunit/phpunit": "~4"
             },
             "suggest": {
                 "ext-dom": "*",
-                "ext-xdebug": ">=2.0.5"
+                "ext-xdebug": ">=2.2.1",
+                "ext-xmlwriter": "*"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.2.x-dev"
+                    "dev-master": "2.2.x-dev"
                 }
             },
             "autoload": {
                 "classmap": [
-                    "PHP/"
+                    "src/"
                 ]
             },
             "notification-url": "https://packagist.org/downloads/",
-            "include-path": [
-                ""
-            ],
             "license": [
                 "BSD-3-Clause"
             ],
                 "testing",
                 "xunit"
             ],
-            "time": "2014-09-02 10:13:14"
+            "time": "2015-08-04 03:42:39"
         },
         {
             "name": "phpunit/php-file-iterator",
-            "version": "1.4.0",
+            "version": "1.4.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/php-file-iterator.git",
-                "reference": "a923bb15680d0089e2316f7a4af8f437046e96bb"
+                "reference": "6150bf2c35d3fc379e50c7602b75caceaa39dbf0"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/a923bb15680d0089e2316f7a4af8f437046e96bb",
-                "reference": "a923bb15680d0089e2316f7a4af8f437046e96bb",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/6150bf2c35d3fc379e50c7602b75caceaa39dbf0",
+                "reference": "6150bf2c35d3fc379e50c7602b75caceaa39dbf0",
                 "shasum": ""
             },
             "require": {
                 "filesystem",
                 "iterator"
             ],
-            "time": "2015-04-02 05:19:05"
+            "time": "2015-06-21 13:08:43"
         },
         {
             "name": "phpunit/php-text-template",
         },
         {
             "name": "phpunit/php-timer",
-            "version": "1.0.6",
+            "version": "1.0.7",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/php-timer.git",
-                "reference": "83fe1bdc5d47658b727595c14da140da92b3d66d"
+                "reference": "3e82f4e9fc92665fafd9157568e4dcb01d014e5b"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/83fe1bdc5d47658b727595c14da140da92b3d66d",
-                "reference": "83fe1bdc5d47658b727595c14da140da92b3d66d",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3e82f4e9fc92665fafd9157568e4dcb01d014e5b",
+                "reference": "3e82f4e9fc92665fafd9157568e4dcb01d014e5b",
                 "shasum": ""
             },
             "require": {
             "keywords": [
                 "timer"
             ],
-            "time": "2015-06-13 07:35:30"
+            "time": "2015-06-21 08:01:12"
         },
         {
             "name": "phpunit/php-token-stream",
-            "version": "1.2.2",
+            "version": "1.4.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/php-token-stream.git",
-                "reference": "ad4e1e23ae01b483c16f600ff1bebec184588e32"
+                "reference": "7a9b0969488c3c54fd62b4d504b3ec758fd005d9"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/ad4e1e23ae01b483c16f600ff1bebec184588e32",
-                "reference": "ad4e1e23ae01b483c16f600ff1bebec184588e32",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/7a9b0969488c3c54fd62b4d504b3ec758fd005d9",
+                "reference": "7a9b0969488c3c54fd62b4d504b3ec758fd005d9",
                 "shasum": ""
             },
             "require": {
                 "ext-tokenizer": "*",
                 "php": ">=5.3.3"
             },
+            "require-dev": {
+                "phpunit/phpunit": "~4.2"
+            },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.2-dev"
+                    "dev-master": "1.4-dev"
                 }
             },
             "autoload": {
                 "classmap": [
-                    "PHP/"
+                    "src/"
                 ]
             },
             "notification-url": "https://packagist.org/downloads/",
-            "include-path": [
-                ""
-            ],
             "license": [
                 "BSD-3-Clause"
             ],
             "authors": [
                 {
                     "name": "Sebastian Bergmann",
-                    "email": "sb@sebastian-bergmann.de",
-                    "role": "lead"
+                    "email": "sebastian@phpunit.de"
                 }
             ],
             "description": "Wrapper around PHP's tokenizer extension.",
             "keywords": [
                 "tokenizer"
             ],
-            "time": "2014-03-03 05:10:30"
+            "time": "2015-06-19 03:43:16"
         },
         {
             "name": "phpunit/phpunit",
-            "version": "3.7.38",
+            "version": "4.7.7",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/phpunit.git",
-                "reference": "38709dc22d519a3d1be46849868aa2ddf822bcf6"
+                "reference": "9b97f9d807b862c2de2a36e86690000801c85724"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/38709dc22d519a3d1be46849868aa2ddf822bcf6",
-                "reference": "38709dc22d519a3d1be46849868aa2ddf822bcf6",
+                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/9b97f9d807b862c2de2a36e86690000801c85724",
+                "reference": "9b97f9d807b862c2de2a36e86690000801c85724",
                 "shasum": ""
             },
             "require": {
-                "ext-ctype": "*",
                 "ext-dom": "*",
                 "ext-json": "*",
                 "ext-pcre": "*",
                 "ext-reflection": "*",
                 "ext-spl": "*",
                 "php": ">=5.3.3",
-                "phpunit/php-code-coverage": "~1.2",
-                "phpunit/php-file-iterator": "~1.3",
-                "phpunit/php-text-template": "~1.1",
-                "phpunit/php-timer": "~1.0",
-                "phpunit/phpunit-mock-objects": "~1.2",
-                "symfony/yaml": "~2.0"
-            },
-            "require-dev": {
-                "pear-pear.php.net/pear": "1.9.4"
+                "phpspec/prophecy": "~1.3,>=1.3.1",
+                "phpunit/php-code-coverage": "~2.1",
+                "phpunit/php-file-iterator": "~1.4",
+                "phpunit/php-text-template": "~1.2",
+                "phpunit/php-timer": ">=1.0.6",
+                "phpunit/phpunit-mock-objects": "~2.3",
+                "sebastian/comparator": "~1.1",
+                "sebastian/diff": "~1.2",
+                "sebastian/environment": "~1.2",
+                "sebastian/exporter": "~1.2",
+                "sebastian/global-state": "~1.0",
+                "sebastian/version": "~1.0",
+                "symfony/yaml": "~2.1|~3.0"
             },
             "suggest": {
                 "phpunit/php-invoker": "~1.1"
             },
             "bin": [
-                "composer/bin/phpunit"
+                "phpunit"
             ],
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "3.7.x-dev"
+                    "dev-master": "4.7.x-dev"
                 }
             },
             "autoload": {
                 "classmap": [
-                    "PHPUnit/"
+                    "src/"
                 ]
             },
             "notification-url": "https://packagist.org/downloads/",
-            "include-path": [
-                "",
-                "../../symfony/yaml/"
-            ],
             "license": [
                 "BSD-3-Clause"
             ],
                 }
             ],
             "description": "The PHP Unit Testing framework.",
-            "homepage": "http://www.phpunit.de/",
+            "homepage": "https://phpunit.de/",
             "keywords": [
                 "phpunit",
                 "testing",
                 "xunit"
             ],
-            "time": "2014-10-17 09:04:17"
+            "time": "2015-07-13 11:28:34"
         },
         {
             "name": "phpunit/phpunit-mock-objects",
-            "version": "1.2.3",
+            "version": "2.3.6",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git",
-                "reference": "5794e3c5c5ba0fb037b11d8151add2a07fa82875"
+                "reference": "18dfbcb81d05e2296c0bcddd4db96cade75e6f42"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/5794e3c5c5ba0fb037b11d8151add2a07fa82875",
-                "reference": "5794e3c5c5ba0fb037b11d8151add2a07fa82875",
+                "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/18dfbcb81d05e2296c0bcddd4db96cade75e6f42",
+                "reference": "18dfbcb81d05e2296c0bcddd4db96cade75e6f42",
                 "shasum": ""
             },
             "require": {
+                "doctrine/instantiator": "~1.0,>=1.0.2",
                 "php": ">=5.3.3",
-                "phpunit/php-text-template": ">=1.1.1@stable"
+                "phpunit/php-text-template": "~1.2",
+                "sebastian/exporter": "~1.2"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "~4.4"
             },
             "suggest": {
                 "ext-soap": "*"
             },
             "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.3.x-dev"
+                }
+            },
             "autoload": {
                 "classmap": [
-                    "PHPUnit/"
+                    "src/"
                 ]
             },
             "notification-url": "https://packagist.org/downloads/",
-            "include-path": [
-                ""
-            ],
             "license": [
                 "BSD-3-Clause"
             ],
                 "mock",
                 "xunit"
             ],
-            "time": "2013-01-13 10:24:48"
+            "time": "2015-07-10 06:54:24"
         },
         {
             "name": "psr/log",
             ],
             "time": "2012-12-21 11:40:51"
         },
+        {
+            "name": "sebastian/comparator",
+            "version": "1.2.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/comparator.git",
+                "reference": "937efb279bd37a375bcadf584dec0726f84dbf22"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/937efb279bd37a375bcadf584dec0726f84dbf22",
+                "reference": "937efb279bd37a375bcadf584dec0726f84dbf22",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3",
+                "sebastian/diff": "~1.2",
+                "sebastian/exporter": "~1.2"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "~4.4"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.2.x-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Jeff Welch",
+                    "email": "whatthejeff@gmail.com"
+                },
+                {
+                    "name": "Volker Dusch",
+                    "email": "github@wallbash.com"
+                },
+                {
+                    "name": "Bernhard Schussek",
+                    "email": "bschussek@2bepublished.at"
+                },
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                }
+            ],
+            "description": "Provides the functionality to compare PHP values for equality",
+            "homepage": "http://www.github.com/sebastianbergmann/comparator",
+            "keywords": [
+                "comparator",
+                "compare",
+                "equality"
+            ],
+            "time": "2015-07-26 15:48:44"
+        },
+        {
+            "name": "sebastian/diff",
+            "version": "1.3.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/diff.git",
+                "reference": "863df9687835c62aa423a22412d26fa2ebde3fd3"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/863df9687835c62aa423a22412d26fa2ebde3fd3",
+                "reference": "863df9687835c62aa423a22412d26fa2ebde3fd3",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "~4.2"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.3-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Kore Nordmann",
+                    "email": "mail@kore-nordmann.de"
+                },
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                }
+            ],
+            "description": "Diff implementation",
+            "homepage": "http://www.github.com/sebastianbergmann/diff",
+            "keywords": [
+                "diff"
+            ],
+            "time": "2015-02-22 15:13:53"
+        },
+        {
+            "name": "sebastian/environment",
+            "version": "1.3.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/environment.git",
+                "reference": "6324c907ce7a52478eeeaede764f48733ef5ae44"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/6324c907ce7a52478eeeaede764f48733ef5ae44",
+                "reference": "6324c907ce7a52478eeeaede764f48733ef5ae44",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "~4.4"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.3.x-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                }
+            ],
+            "description": "Provides functionality to handle HHVM/PHP environments",
+            "homepage": "http://www.github.com/sebastianbergmann/environment",
+            "keywords": [
+                "Xdebug",
+                "environment",
+                "hhvm"
+            ],
+            "time": "2015-08-03 06:14:51"
+        },
+        {
+            "name": "sebastian/exporter",
+            "version": "1.2.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/exporter.git",
+                "reference": "7ae5513327cb536431847bcc0c10edba2701064e"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/7ae5513327cb536431847bcc0c10edba2701064e",
+                "reference": "7ae5513327cb536431847bcc0c10edba2701064e",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3",
+                "sebastian/recursion-context": "~1.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "~4.4"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.2.x-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Jeff Welch",
+                    "email": "whatthejeff@gmail.com"
+                },
+                {
+                    "name": "Volker Dusch",
+                    "email": "github@wallbash.com"
+                },
+                {
+                    "name": "Bernhard Schussek",
+                    "email": "bschussek@2bepublished.at"
+                },
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                },
+                {
+                    "name": "Adam Harvey",
+                    "email": "aharvey@php.net"
+                }
+            ],
+            "description": "Provides the functionality to export PHP variables for visualization",
+            "homepage": "http://www.github.com/sebastianbergmann/exporter",
+            "keywords": [
+                "export",
+                "exporter"
+            ],
+            "time": "2015-06-21 07:55:53"
+        },
+        {
+            "name": "sebastian/global-state",
+            "version": "1.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/global-state.git",
+                "reference": "c7428acdb62ece0a45e6306f1ae85e1c05b09c01"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/c7428acdb62ece0a45e6306f1ae85e1c05b09c01",
+                "reference": "c7428acdb62ece0a45e6306f1ae85e1c05b09c01",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "~4.2"
+            },
+            "suggest": {
+                "ext-uopz": "*"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                }
+            ],
+            "description": "Snapshotting of global state",
+            "homepage": "http://www.github.com/sebastianbergmann/global-state",
+            "keywords": [
+                "global state"
+            ],
+            "time": "2014-10-06 09:23:50"
+        },
+        {
+            "name": "sebastian/recursion-context",
+            "version": "1.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/recursion-context.git",
+                "reference": "994d4a811bafe801fb06dccbee797863ba2792ba"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/994d4a811bafe801fb06dccbee797863ba2792ba",
+                "reference": "994d4a811bafe801fb06dccbee797863ba2792ba",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "~4.4"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Jeff Welch",
+                    "email": "whatthejeff@gmail.com"
+                },
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                },
+                {
+                    "name": "Adam Harvey",
+                    "email": "aharvey@php.net"
+                }
+            ],
+            "description": "Provides functionality to recursively process PHP variables",
+            "homepage": "http://www.github.com/sebastianbergmann/recursion-context",
+            "time": "2015-06-21 08:04:50"
+        },
+        {
+            "name": "sebastian/version",
+            "version": "1.0.6",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/version.git",
+                "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/58b3a85e7999757d6ad81c787a1fbf5ff6c628c6",
+                "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6",
+                "shasum": ""
+            },
+            "type": "library",
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "Library that helps with managing the version number of Git-hosted PHP projects",
+            "homepage": "https://github.com/sebastianbergmann/version",
+            "time": "2015-06-21 13:59:46"
+        },
         {
             "name": "symfony/icu",
             "version": "v1.2.2",
         },
         {
             "name": "twig/twig",
-            "version": "v1.18.2",
+            "version": "v1.19.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/twigphp/Twig.git",
-                "reference": "e8e6575abf6102af53ec283f7f14b89e304fa602"
+                "reference": "edbeaf43b0a606cdaadc32a11d2673614a377b90"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/twigphp/Twig/zipball/e8e6575abf6102af53ec283f7f14b89e304fa602",
-                "reference": "e8e6575abf6102af53ec283f7f14b89e304fa602",
+                "url": "https://api.github.com/repos/twigphp/Twig/zipball/edbeaf43b0a606cdaadc32a11d2673614a377b90",
+                "reference": "edbeaf43b0a606cdaadc32a11d2673614a377b90",
                 "shasum": ""
             },
             "require": {
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.18-dev"
+                    "dev-master": "1.19-dev"
                 }
             },
             "autoload": {
             "keywords": [
                 "templating"
             ],
-            "time": "2015-06-06 23:31:24"
+            "time": "2015-07-31 13:45:26"
         }
     ],
     "aliases": [],
index 529ae6a..f1c2e88 100644 (file)
@@ -42,7 +42,6 @@ Feature: Browse course list and return back from enrolment page
     And I press "Continue"
     Then I should see "Edit profile" in the ".breadcrumb-nav" "css_element"
 
-  @javascript
   Scenario: User can return to the choice activity from enrolment page
     Given the following "roles" exist:
       | name                   | shortname | description      | archetype      |
index 2140e16..6fc0568 100644 (file)
@@ -29,7 +29,6 @@ Feature: Restrict activities availability
     Then I should see "Test glossary name"
     And I should see "Test chat name"
 
-  @javascript
   Scenario: Activities can not be added when the admin restricts the permissions
     Given I log in as "admin"
     And I set the following system permissions of "Teacher" role:
index 79ff974..b4c322c 100644 (file)
@@ -20,24 +20,21 @@ M.core_group.init_hover_events = function(Y, events) {
         return overlay;
     })();
 
-    // Iterate over the events and attach an event to display the description on
-    // hover
-    for (var id in events) {
-        var node = Y.one('#'+id);
-        if (node) {
-            node = node.ancestor();
-            node.on('mouseenter', function(e, content){
-                M.core_group.hoveroverlay.set('xy', [this.getX()+(this.get('offsetWidth')/2),this.getY()+this.get('offsetHeight')-5]);
-                M.core_group.hoveroverlay.set("bodyContent", content);
-                M.core_group.hoveroverlay.show();
-                M.core_group.hoveroverlay.get('boundingBox').setStyle('visibility', 'visible');
-            }, node, events[id]);
-            node.on('mouseleave', function(e){
-                M.core_group.hoveroverlay.hide();
-                M.core_group.hoveroverlay.get('boundingBox').setStyle('visibility', 'hidden');
-            }, node);
-        }
-    }
+    Y.all('.group_hoverdescription').each(function() {
+        var node = this.ancestor();
+        var id = this.getAttribute('data-groupid');
+
+        node.on('mouseenter', function(){
+            M.core_group.hoveroverlay.set('xy', [this.getX()+(this.get('offsetWidth')/2),this.getY()+this.get('offsetHeight')-5]);
+            M.core_group.hoveroverlay.set("bodyContent", events[id]);
+            M.core_group.hoveroverlay.show();
+            M.core_group.hoveroverlay.get('boundingBox').setStyle('visibility', 'visible');
+        });
+        node.on('mouseleave', function(){
+            M.core_group.hoveroverlay.hide();
+            M.core_group.hoveroverlay.get('boundingBox').setStyle('visibility', 'hidden');
+        });
+    });
 };
 
 M.core_group.init_index = function(Y, wwwroot, courseid) {
index dd8f847..6d114c5 100644 (file)
@@ -186,8 +186,8 @@ foreach ($members as $gpgid=>$groupdata) {
         if (empty($jsdescription)) {
             $line[] = $name;
         } else {
-            $line[] = html_writer::tag('span', $name, array('id'=>'group_'.$gpid));
-            $hoverevents['group_'.$gpid] = $jsdescription;
+            $line[] = html_writer::tag('span', $name, array('class' => 'group_hoverdescription', 'data-groupid' => $gpid));
+            $hoverevents[$gpid] = $jsdescription;
         }
         $fullnames = array();
         foreach ($users as $user) {
index 3e34a4c..4d07c44 100644 (file)
@@ -57,7 +57,6 @@ Feature: Organize students into groups
     And I should see "Student 3"
     And I should not see "Student 0"
 
-  @javascript
   Scenario: Create groups and groupings without the 'moodle/course:changeidnumber' capability
     Given the following "courses" exist:
       | fullname | shortname | category | groupmode |
diff --git a/install/lang/oc_gsc/admin.php b/install/lang/oc_gsc/admin.php
new file mode 100644 (file)
index 0000000..cfd8e3b
--- /dev/null
@@ -0,0 +1,44 @@
+<?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/>.
+
+/**
+ * Automatically generated strings for Moodle installer
+ *
+ * Do not edit this file manually! It contains just a subset of strings
+ * needed during the very first steps of installation. This file was
+ * generated automatically by export-installer.php (which is part of AMOS
+ * {@link http://docs.moodle.org/dev/Languages/AMOS}) using the
+ * list of strings defined in /install/stringnames.txt.
+ *
+ * @package   installer
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$string['clianswerno'] = 'n';
+$string['cliansweryes'] = 'y';
+$string['cliincorrectvalueerror'] = 'Error, valor incorrècta « {$a->value} » peu paramètre « {$a->option} »';
+$string['cliincorrectvalueretry'] = 'Valor incorrècta, tornatz ensajar';
+$string['clitypevalue'] = 'tipe valor';
+$string['clitypevaluedefault'] = 'tipe valor, sasitz Entrada per utilizar la valor per defaut ({$a})';
+$string['cliunknowoption'] = 'Opcions pas reconegudas :
+ {$a}.
+Utilizatz l\'opcion --help.';
+$string['cliyesnoprompt'] = 'Picatz y (per òc) o n (per non)';
+$string['environmentrequireinstall'] = 'deu èster installat e activat';
+$string['environmentrequireversion'] = 'la version {$a->needed} qu\'ei requerida ; utilizatz actuaument la version {$a->current}';
diff --git a/install/lang/oc_gsc/error.php b/install/lang/oc_gsc/error.php
new file mode 100644 (file)
index 0000000..0f73df3
--- /dev/null
@@ -0,0 +1,53 @@
+<?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/>.
+
+/**
+ * Automatically generated strings for Moodle installer
+ *
+ * Do not edit this file manually! It contains just a subset of strings
+ * needed during the very first steps of installation. This file was
+ * generated automatically by export-installer.php (which is part of AMOS
+ * {@link http://docs.moodle.org/dev/Languages/AMOS}) using the
+ * list of strings defined in /install/stringnames.txt.
+ *
+ * @package   installer
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$string['cannotcreatedboninstall'] = '<p>Impossible de crear la banca de dadas.</p>
+<p>La banca de dadas indicada n\'existeish pas e l\'utilizaire especificat a pas las autorizacions que permeton de crear ua banca de dadas.</p>.
+<p>L\'administrator deu site deu repassar la configuracion de la banca de dadas.</p>';
+$string['cannotcreatelangdir'] = 'Creacion deu dossièr lang impossible';
+$string['cannotcreatetempdir'] = 'Creacion deu dossièr temp impossibla';
+$string['cannotdownloadcomponents'] = 'Telecargament deus components impossible';
+$string['cannotdownloadzipfile'] = 'Telecargament deu fichèr ZIP impossible';
+$string['cannotfindcomponent'] = 'Component introbable';
+$string['cannotsavemd5file'] = 'Enregistrament deu fichèr md5 impossible';
+$string['cannotsavezipfile'] = 'Enregistrament deu fichèr ZIP impossible';
+$string['cannotunzipfile'] = 'Descompression deu fichèr ZIP impossibla';
+$string['componentisuptodate'] = 'Lo component qu\'ei a dia';
+$string['dmlexceptiononinstall'] = '<p>Ua error de banca de dadas que s\'ei produsida [{$a->errorcode}].<br />{$a->debuginfo}</p>';
+$string['downloadedfilecheckfailed'] = 'La verificacion deu fichèr telecargat s\'i ei mauescadut';
+$string['invalidmd5'] = 'Lo còde de contraròtle md5 n\'ei pas valid';
+$string['missingrequiredfield'] = 'Un camp obligatòri n\'ei pas completat';
+$string['remotedownloaderror'] = '<p>Lo telecargament deu component suu vòste servidor s\'i ei mauescadut. Verificatz los reglatges de proxy. L\'extension cURL de PHP qu\'ei bravament recomandada.</p>
+<p>Que\'vs cau telecargar manuaument lo fichèr <a href="{$a->url}">{$a->url}</a>, lo copiar suu vòste servidor a l\'emplaçament « {$a->dest} » e lo descompressar a aqueste endret.</p>';
+$string['wrongdestpath'] = 'Camin de destinacion incorrècte';
+$string['wrongsourcebase'] = 'Adreça URL de basa de la hont incorrècta';
+$string['wrongzipfilename'] = 'Nom de fichèr ZIP incorrècte';
diff --git a/install/lang/oc_gsc/install.php b/install/lang/oc_gsc/install.php
new file mode 100644 (file)
index 0000000..514ed24
--- /dev/null
@@ -0,0 +1,55 @@
+<?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/>.
+
+/**
+ * Automatically generated strings for Moodle installer
+ *
+ * Do not edit this file manually! It contains just a subset of strings
+ * needed during the very first steps of installation. This file was
+ * generated automatically by export-installer.php (which is part of AMOS
+ * {@link http://docs.moodle.org/dev/Languages/AMOS}) using the
+ * list of strings defined in /install/stringnames.txt.
+ *
+ * @package   installer
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$string['admindirname'] = 'Dossièr d\'administracion';
+$string['availablelangs'] = 'Paquetatges de lenga disponibles';
+$string['chooselanguagehead'] = 'Causitz ua lenga';
+$string['cliinstallheader'] = 'Programa d\'installacion de Moodle {$a} en linha de comanda';
+$string['databasehost'] = 'Servidor de banca de dadas';
+$string['databasename'] = 'Nom de la banca de dadas';
+$string['databasetypehead'] = 'Seleccionar un pilòt de banca de dadas';
+$string['dataroot'] = 'Dossièr de dadas';
+$string['dbprefix'] = 'Prefix de las taulas';
+$string['dirroot'] = 'Dossièr Moodle';
+$string['environmenthead'] = 'Verificacion de l\'environament...';
+$string['errorsinenvironment'] = 'Mauescaduda de la verificacion de l\'environament !';
+$string['installation'] = 'Installacion';
+$string['paths'] = 'Camins';
+$string['pathserrcreatedataroot'] = 'Lo dossièr de dadas ({$a->dataroot}) ne pòt pas èster creat per l\'installador.';
+$string['pathshead'] = 'Confirmar los camins d\'accès';
+$string['pathsrodataroot'] = 'Lo dossièr de dadas n\'ei pas accessible en escritura.';
+$string['pathsunsecuredataroot'] = 'L\'emplaçament deu dossièr de dadas n\'ei pas segur';
+$string['pathswrongadmindir'] = 'Lo dossièr d\'administracion n\'existeish pas';
+$string['phpextension'] = 'Extension PHP {$a}';
+$string['phpversion'] = 'Version de PHP';
+$string['welcomep10'] = '{$a->installername} ({$a->installerversion})';
+$string['wwwroot'] = 'Adreça web';
diff --git a/install/lang/oc_gsc/moodle.php b/install/lang/oc_gsc/moodle.php
new file mode 100644 (file)
index 0000000..40736d4
--- /dev/null
@@ -0,0 +1,36 @@
+<?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/>.
+
+/**
+ * Automatically generated strings for Moodle installer
+ *
+ * Do not edit this file manually! It contains just a subset of strings
+ * needed during the very first steps of installation. This file was
+ * generated automatically by export-installer.php (which is part of AMOS
+ * {@link http://docs.moodle.org/dev/Languages/AMOS}) using the
+ * list of strings defined in /install/stringnames.txt.
+ *
+ * @package   installer
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$string['language'] = 'Lenga';
+$string['next'] = 'Seguent';
+$string['previous'] = 'Precedent';
+$string['reload'] = 'Actualizar';
index 11ab138..7e7f4c7 100644 (file)
@@ -1072,6 +1072,7 @@ $string['unsettheme'] = 'Unset theme';
 $string['unsupported'] = 'Unsupported';
 $string['unsupporteddbstorageengine'] = 'The database storage engine being used is no longer supported.';
 $string['unsupporteddbtablerowformat'] = 'Your database has tables using Antelope as the file format. You are recommended to convert the tables to the Barracuda file format. See the documentation <a href="https://docs.moodle.org/en/cli">Administration via command line</a> for details of a tool for converting InnoDB tables to Barracuda.';
+$string['unsupportedphpversion7'] = 'PHP version 7 is not supported.';
 $string['unsuspenduser'] = 'Activate user account';
 $string['updateaccounts'] = 'Update existing accounts';
 $string['updatecomponent'] = 'Update component';
index 68799d5..61e1d68 100644 (file)
@@ -40,6 +40,7 @@ $string['auth_changingemailaddress'] = 'You have requested a change of email add
 $string['authinstructions'] = 'Leave this blank for the default login instructions to be displayed on the login page. If you want to provide custom login instructions, enter them here.';
 $string['auth_invalidnewemailkey'] = 'Error: if you are trying to confirm a change of email address, you may have made a mistake in copying the URL we sent you by email. Please copy the address and try again.';
 $string['auth_multiplehosts'] = 'Multiple hosts OR addresses can be specified (eg host1.com;host2.com;host3.com) or (eg xxx.xxx.xxx.xxx;xxx.xxx.xxx.xxx)';
+$string['auth_notconfigured'] = 'The authentication method {$a} is not configured.';
 $string['auth_outofnewemailupdateattempts'] = 'You have run out of allowed attempts to update your email address. Your update request has been cancelled.';
 $string['auth_passwordisexpired'] = 'Your password is expired. Do you want change your password now?';
 $string['auth_passwordwillexpire'] = 'Your password will expire in {$a} days. Do you want change your password now?';
index 83a6cb4..93f82df 100644 (file)
@@ -10,3 +10,4 @@ myfilesmanage,core
 mypreferences,core_grades
 myprofile,core
 viewallmyentries,core_blog
+cannotdeletepost,core_notes
\ No newline at end of file
index 16f7097..4491e67 100644 (file)
@@ -26,7 +26,6 @@
 $string['addnewnote'] = 'Add a new note';
 $string['addnewnoteselect'] = 'Select users to write notes about';
 $string['bynameondate'] = 'by {$a->name} - {$a->date}';
-$string['cannotdeletepost'] = 'Error occurred while deleting post';
 $string['configenablenotes'] = 'Enable storing of notes about individual users.';
 $string['content'] = 'Content';
 $string['course'] = 'course';
@@ -66,3 +65,6 @@ $string['selectnotestate'] = "Select note state";
 $string['site'] = 'site';
 $string['sitenotes'] = 'Site notes';
 $string['unknown'] = 'unknown';
+
+// Deprecated since Moodle 3.0.
+$string['cannotdeletepost'] = 'Error occurred while deleting post';
index cb9ef99..5f6ba12 100644 (file)
@@ -212,6 +212,15 @@ class auth_plugin_base {
         return true;
     }
 
+    /**
+     * Returns false if this plugin is enabled but not configured.
+     *
+     * @return bool
+     */
+    public function is_configured() {
+        return false;
+    }
+
     /**
      * Indicates if password hashes should be stored in local moodle database.
      * @return bool true means md5 password hash stored in user table, false means flag 'not_cached' stored there instead
index 34afbdb..8ad9f23 100644 (file)
@@ -193,6 +193,14 @@ class behat_files extends behat_base {
         $this->ensure_node_is_visible($add);
         $add->click();
 
+        // Wait for the default repository (if any) to load. This checks that
+        // the relevant div exists and that it does not include the loading image.
+        $this->ensure_element_exists(
+                "//div[contains(concat(' ', normalize-space(@class), ' '), ' file-picker ')]" .
+                "//div[contains(concat(' ', normalize-space(@class), ' '), ' fp-content ')]" .
+                "[not(descendant::div[contains(concat(' ', normalize-space(@class), ' '), ' fp-content-loading ')])]",
+                'xpath_element');
+
         // Getting the repository link and opening it.
         $repoexception = new ExpectationException('The "' . $repositoryname . '" repository has not been found', $this->getSession());
 
@@ -210,7 +218,11 @@ class behat_files extends behat_base {
 
         // Selecting the repo.
         $this->ensure_node_is_visible($repositorylink);
-        $repositorylink->click();
+        if (!$repositorylink->getParent()->getParent()->hasClass('active')) {
+            // If the repository link is active, then the repository is already loaded.
+            // Clicking it while it's active causes issues, so only click it when it isn't (see MDL-51014).
+            $repositorylink->click();
+        }
     }
 
     /**
index 15e4512..24852e1 100644 (file)
@@ -55,7 +55,7 @@ class manager {
         }
 
         $tasks = null;
-        require_once($file);
+        include($file);
 
         if (!isset($tasks)) {
             return array();
@@ -86,6 +86,7 @@ class manager {
     public static function reset_scheduled_tasks_for_component($componentname) {
         global $DB;
         $tasks = self::load_default_scheduled_tasks_for_component($componentname);
+        $validtasks = array();
 
         foreach ($tasks as $taskid => $task) {
             $classname = get_class($task);
@@ -93,6 +94,8 @@ class manager {
                 $classname = '\\' . $classname;
             }
 
+            $validtasks[] = $classname;
+
             if ($currenttask = self::get_scheduled_task($classname)) {
                 if ($currenttask->is_customised()) {
                     // If there is an existing task with a custom schedule, do not override it.
@@ -110,6 +113,16 @@ class manager {
                 $DB->insert_record('task_scheduled', $record);
             }
         }
+
+        // Delete any task that is not defined in the component any more.
+        $sql = "component = :component";
+        $params = array('component' => $componentname);
+        if (!empty($validtasks)) {
+            list($insql, $inparams) = $DB->get_in_or_equal($validtasks, SQL_PARAMS_NAMED, 'param', false);
+            $sql .= ' AND classname ' . $insql;
+            $params = array_merge($params, $inparams);
+        }
+        $DB->delete_records_select('task_scheduled', $sql, $params);
     }
 
     /**
index e8fac11..73636bd 100644 (file)
@@ -1700,8 +1700,7 @@ $capabilities = array(
         'captype' => 'write',
         'contextlevel' => CONTEXT_SYSTEM,
         'archetypes' => array(
-            'manager' => CAP_ALLOW,
-            'user' => CAP_ALLOW
+            'manager' => CAP_ALLOW
         )
     ),
 
index d270a75..6afdfdd 100644 (file)
@@ -1141,6 +1141,9 @@ $services = array(
             'core_user_add_user_private_files',
             'mod_assign_view_grading_table',
             'mod_scorm_view_scorm',
+            'mod_page_view_page',
+            'mod_resource_view_resource',
+            'mod_folder_view_folder',
             ),
         'enabled' => 0,
         'restrictedusers' => 0,
index e04ada7..d8f8d67 100644 (file)
@@ -683,7 +683,7 @@ class oci_native_moodle_database extends moodle_database {
         if (is_bool($value)) { // Always, convert boolean to int
             $value = (int)$value;
 
-        } else if ($column->meta_type == 'B') { // CLOB detected, we return 'blob' array instead of raw value to allow
+        } else if ($column->meta_type == 'B') { // BLOB detected, we return 'blob' array instead of raw value to allow
             if (!is_null($value)) {             // binding/executing code later to know about its nature
                 $value = array('blob' => $value);
             }
@@ -945,6 +945,18 @@ class oci_native_moodle_database extends moodle_database {
                         $descriptors[] = $lob;
                         continue; // Column binding finished, go to next one
                     }
+                } else {
+                    // If, at this point, the param value > 4000 (bytes), let's assume it's a clob
+                    // passed in an arbitrary sql (not processed by normalise_value() ever,
+                    // and let's handle it as such. This will provide proper binding of CLOBs in
+                    // conditions and other raw SQLs not covered by the above function.
+                    if (strlen($value) > 4000) {
+                        $lob = oci_new_descriptor($this->oci, OCI_DTYPE_LOB);
+                        oci_bind_by_name($stmt, $key, $lob, -1, SQLT_CLOB);
+                        $lob->writeTemporary($this->oracle_dirty_hack($tablename, $columnname, $params[$key]), OCI_TEMP_CLOB);
+                        $descriptors[] = $lob;
+                        continue; // Param binding finished, go to next one.
+                    }
                 }
                 // TODO: Put proper types and length is possible (enormous speedup)
                 // Arrived here, continue with standard processing, using metadata if possible
index 4459abe..60137f1 100644 (file)
@@ -858,6 +858,7 @@ class core_dml_testcase extends database_driver_testcase {
         $tablename2 = $table2->getName();
         $table2->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
         $table2->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
+        $table2->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
         $table2->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
         $dbman->create_table($table2);
 
@@ -904,6 +905,30 @@ class core_dml_testcase extends database_driver_testcase {
                   FROM {{$tablename1}}";
         $this->assertTrue($DB->execute($sql));
         $this->assertEquals(4, $DB->count_records($tablename2));
+
+        // Insert a TEXT with raw SQL, binding TEXT params.
+        $course = 9999;
+        $onetext = file_get_contents(__DIR__ . '/fixtures/clob.txt');
+        $sql = "INSERT INTO {{$tablename2}} (course, onetext)
+                VALUES (:course, :onetext)";
+        $DB->execute($sql, array('course' => $course, 'onetext' => $onetext));
+        $records = $DB->get_records($tablename2, array('course' => $course));
+        $this->assertCount(1, $records);
+        $record = reset($records);
+        $this->assertSame($onetext, $record->onetext);
+
+        // Update a TEXT with raw SQL, binding TEXT params.
+        $newcourse = 10000;
+        $newonetext = file_get_contents(__DIR__ . '/fixtures/clob.txt') . '- updated';
+        $sql = "UPDATE {{$tablename2}} SET course = :newcourse, onetext = :newonetext
+                WHERE course = :oldcourse";
+        $DB->execute($sql, array('oldcourse' => $course, 'newcourse' => $newcourse, 'newonetext' => $newonetext));
+        $records = $DB->get_records($tablename2, array('course' => $course));
+        $this->assertCount(0, $records);
+        $records = $DB->get_records($tablename2, array('course' => $newcourse));
+        $this->assertCount(1, $records);
+        $record = reset($records);
+        $this->assertSame($newonetext, $record->onetext);
     }
 
     public function test_get_recordset() {
@@ -3724,6 +3749,15 @@ class core_dml_testcase extends database_driver_testcase {
             $this->assertCount(1, $records);
         }
 
+        // Now test the function with really big content and params.
+        $clob = file_get_contents(__DIR__ . '/fixtures/clob.txt');
+        $DB->insert_record($tablename, array('name' => 'zzzz', 'description' => $clob));
+        $sql = "SELECT * FROM {{$tablename}}
+                 WHERE " . $DB->sql_compare_text('description') . " = " . $DB->sql_compare_text(':clob');
+        $records = $DB->get_records_sql($sql, array('clob' => $clob));
+        $this->assertCount(1, $records);
+        $record = reset($records);
+        $this->assertSame($clob, $record->description);
     }
 
     public function test_unique_index_collation_trouble() {
index 802ebef..cd25167 100644 (file)
@@ -1538,3 +1538,37 @@ function process_environment_result($element, &$result) {
 /// Process restrict, modifying $result if needed.
     process_environment_restrict($element, $result);
 }
+
+/**
+ * Check if the current PHP version is greater than or equal to
+ * PHP version 7.
+ *
+ * @param object $result an environment_results instance
+ * @return bool result of version check
+ */
+function restrict_php_version_7(&$result) {
+    return restrict_php_version($result, '7');
+}
+
+/**
+ * Check if the current PHP version is greater than or equal to an
+ * unsupported version.
+ *
+ * @param object $result an environment_results instance
+ * @param string $version the version of PHP that can't be used
+ * @return bool result of version check
+ */
+function restrict_php_version(&$result, $version) {
+
+    // Get the current PHP version.
+    $currentversion = normalize_version(phpversion());
+
+    // Confirm we're using a supported PHP version.
+    if (version_compare($currentversion, $version, '<')) {
+        // Everything is ok, the restriction doesn't apply.
+        return false;
+    } else {
+        // We're using an unsupported PHP version, apply restriction.
+        return true;
+    }
+}
index a12e20d..70bbbd1 100644 (file)
@@ -188,6 +188,10 @@ abstract class moodleform {
             $this->_form->hardFreeze();
         }
 
+        // HACK to prevent browsers from automatically inserting the user's password into the wrong fields.
+        $element = $this->_form->addElement('hidden');
+        $element->setType('password');
+
         $this->definition();
 
         $this->_form->addElement('hidden', 'sesskey', null); // automatic sesskey protection
index ac02a97..1acdba7 100644 (file)
@@ -32,7 +32,7 @@
  * @copyright  2012 Petr Skoda {@link http://skodak.org}
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-abstract class advanced_testcase extends PHPUnit_Framework_TestCase {
+abstract class advanced_testcase extends base_testcase {
     /** @var bool automatically reset everything? null means log changes */
     private $resetAfterTest;
 
diff --git a/lib/phpunit/classes/base_testcase.php b/lib/phpunit/classes/base_testcase.php
new file mode 100644 (file)
index 0000000..dd1dc8f
--- /dev/null
@@ -0,0 +1,75 @@
+<?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/>.
+
+/**
+ * Base test case class.
+ *
+ * @package    core
+ * @category   test
+ * @author     Tony Levi <tony.levi@blackboard.com>
+ * @copyright  2015 Blackboard (http://www.blackboard.com)
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+
+/**
+ * Base class for PHPUnit test cases customised for Moodle
+ *
+ * It is intended for functionality common to both basic and advanced_testcase.
+ *
+ * @package    core
+ * @category   test
+ * @author     Tony Levi <tony.levi@blackboard.com>
+ * @copyright  2015 Blackboard (http://www.blackboard.com)
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+abstract class base_testcase extends PHPUnit_Framework_TestCase {
+    /**
+     * Note: we are overriding this method to remove the deprecated error
+     * @see https://tracker.moodle.org/browse/MDL-47129
+     *
+     * @param  array   $matcher
+     * @param  string  $actual
+     * @param  string  $message
+     * @param  boolean $ishtml
+     *
+     * @deprecated 3.0
+     */
+    public static function assertTag($matcher, $actual, $message = '', $ishtml = true) {
+        $dom = PHPUnit_Util_XML::load($actual, $ishtml);
+        $tags = PHPUnit_Util_XML::findNodes($dom, $matcher, $ishtml);
+        $matched = count($tags) > 0 && $tags[0] instanceof DOMNode;
+        self::assertTrue($matched, $message);
+    }
+
+    /**
+     * Note: we are overriding this method to remove the deprecated error
+     * @see https://tracker.moodle.org/browse/MDL-47129
+     *
+     * @param  array   $matcher
+     * @param  string  $actual
+     * @param  string  $message
+     * @param  boolean $ishtml
+     *
+     * @deprecated 3.0
+     */
+    public static function assertNotTag($matcher, $actual, $message = '', $ishtml = true) {
+        $dom = PHPUnit_Util_XML::load($actual, $ishtml);
+        $tags = PHPUnit_Util_XML::findNodes($dom, $matcher, $ishtml);
+        $matched = count($tags) > 0 && $tags[0] instanceof DOMNode;
+        self::assertFalse($matched, $message);
+    }
+}
index 6ca3900..8e53d15 100644 (file)
@@ -34,7 +34,7 @@
  * @copyright  2012 Petr Skoda {@link http://skodak.org}
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-abstract class basic_testcase extends PHPUnit_Framework_TestCase {
+abstract class basic_testcase extends base_testcase {
 
     /**
      * Constructs a test case with the given name.
index 2d5dfe7..51d9416 100644 (file)
@@ -45,7 +45,7 @@ class Hint_ResultPrinter extends PHPUnit_TextUI_ResultPrinter {
             }
         }
         // Fallback if something goes wrong.
-        parent::__construct(null, false, false, false);
+        parent::__construct(null, false, self::COLOR_DEFAULT, false);
     }
 
     protected function printDefectTrace(PHPUnit_Framework_TestFailure $defect) {
@@ -135,7 +135,7 @@ class Hacky_TextUI_Command_reader extends PHPUnit_TextUI_Command {
         $verbose = isset($config['verbose']) ? $config['verbose'] : false;
         $verbose = isset($arguments['verbose']) ? $arguments['verbose'] : $verbose;
 
-        $colors = isset($config['colors']) ? $config['colors'] : false;
+        $colors = isset($config['colors']) ? $config['colors'] : Hint_ResultPrinter::COLOR_DEFAULT;
         $colors = isset($arguments['colors']) ? $arguments['colors'] : $colors;
 
         $debug = isset($config['debug']) ? $config['debug'] : false;
index e01c804..daccb17 100644 (file)
@@ -30,6 +30,7 @@ require_once(__DIR__.'/classes/event_mock.php');
 require_once(__DIR__.'/classes/event_sink.php');
 require_once(__DIR__.'/classes/message_sink.php');
 require_once(__DIR__.'/classes/phpmailer_sink.php');
+require_once(__DIR__.'/classes/base_testcase.php');
 require_once(__DIR__.'/classes/basic_testcase.php');
 require_once(__DIR__.'/classes/database_driver_testcase.php');
 require_once(__DIR__.'/classes/arraydataset.php');
index 6180253..700f41a 100644 (file)
@@ -1,10 +1,10 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!--moodle: this schema was generated using code from https://github.com/gooh/phpunit-schema-->
-<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
   <xs:annotation>
-    <xs:documentation source="http://www.phpunit.de/manual/current/en/appendixes.configuration.html">
-            This Schema file defines the rules by which the XML configuration file of PHPUnit may be structured.
-        </xs:documentation>
+    <xs:documentation source="https://phpunit.de/manual/4.7/en/appendixes.configuration.html">
+      This Schema file defines the rules by which the XML configuration file of PHPUnit 4.7 may be structured.
+    </xs:documentation>
+    <xs:appinfo source="http://www.phpunit.de/manual/current/en/appendixes.configuration.html"/>
   </xs:annotation>
   <xs:element name="phpunit" type="phpUnitType">
     <xs:annotation>
@@ -35,7 +35,8 @@
   <xs:complexType name="whiteListType">
     <xs:complexContent>
       <xs:extension base="filterType">
-        <xs:attribute name="addUncoveredFilesFromWhitelist" default="true" type="xs:boolean"/>
+        <xs:attribute name="addUncoveredFilesFromWhitelist" default="false" type="xs:boolean"/>
+        <xs:attribute name="processUncoveredFilesFromWhitelist" default="true" type="xs:boolean"/>
       </xs:extension>
     </xs:complexContent>
   </xs:complexType>
       <xs:simpleType>
         <xs:restriction base="xs:string">
           <xs:enumeration value="coverage-html"/>
+          <xs:enumeration value="coverage-text"/>
           <xs:enumeration value="coverage-clover"/>
+          <xs:enumeration value="coverage-crap4j"/>
           <xs:enumeration value="json"/>
           <xs:enumeration value="plain"/>
           <xs:enumeration value="tap"/>
       </xs:simpleType>
     </xs:attribute>
     <xs:attribute name="target" type="xs:anyURI"/>
-    <xs:attribute name="title" type="xs:string"/>
-    <xs:attribute name="charset" type="xs:string" default="UTF-8"/>
-    <xs:attribute name="yui" type="xs:boolean" default="true"/>
-    <xs:attribute name="highlight" type="xs:boolean" default="false"/>
     <xs:attribute name="lowUpperBound" type="xs:nonNegativeInteger" default="35"/>
     <xs:attribute name="highLowerBound" type="xs:nonNegativeInteger" default="70"/>
     <xs:attribute name="logIncompleteSkipped" type="xs:boolean" default="false"/>
+    <xs:attribute name="showUncoveredFiles" type="xs:boolean" default="false"/>
   </xs:complexType>
   <xs:group name="pathGroup">
     <xs:sequence>
     <xs:attribute name="bootstrap" type="xs:anyURI"/>
     <xs:attribute name="cacheTokens" type="xs:boolean"/>
     <xs:attribute name="colors" type="xs:boolean" default="false"/>
+    <xs:attribute name="columns" type="xs:integer" default="80"/>
     <xs:attribute name="convertErrorsToExceptions" type="xs:boolean" default="true"/>
     <xs:attribute name="convertNoticesToExceptions" type="xs:boolean" default="true"/>
     <xs:attribute name="convertWarningsToExceptions" type="xs:boolean" default="true"/>
     <xs:attribute name="stopOnError" type="xs:boolean" default="false"/>
     <xs:attribute name="stopOnFailure" type="xs:boolean" default="false"/>
     <xs:attribute name="stopOnIncomplete" type="xs:boolean" default="false"/>
+    <xs:attribute name="stopOnRisky" type="xs:boolean" default="false"/>
     <xs:attribute name="stopOnSkipped" type="xs:boolean" default="false"/>
+    <xs:attribute name="beStrictAboutTestsThatDoNotTestAnything" type="xs:boolean" default="false"/>
+    <xs:attribute name="beStrictAboutOutputDuringTests" type="xs:boolean" default="false"/>
+    <xs:attribute name="beStrictAboutTestSize" type="xs:boolean" default="false"/>
+    <xs:attribute name="beStrictAboutTodoAnnotatedTests" type="xs:boolean" default="false"/>
+    <xs:attribute name="beStrictAboutChangesToGlobalState" type="xs:boolean" default="false"/>
+    <xs:attribute name="checkForUnintentionallyCoveredCode" type="xs:boolean" default="false"/>
     <xs:attribute name="strict" type="xs:boolean" default="false"/>
     <xs:attribute name="testSuiteLoaderClass" type="xs:string" default="PHPUnit_Runner_StandardTestSuiteLoader"/>
     <xs:attribute name="testSuiteLoaderFile" type="xs:anyURI"/>
     <xs:attribute name="timeoutForMediumTests" type="xs:integer" default="10"/>
     <xs:attribute name="timeoutForLargeTests" type="xs:integer" default="60"/>
     <xs:attribute name="verbose" type="xs:boolean" default="false"/>
+    <xs:attribute name="stderr" type="xs:boolean" default="false"/>
   </xs:attributeGroup>
   <xs:group name="configGroup">
     <xs:all>
   <xs:element name="testsuites" type="testSuitesType" substitutionGroup="testSuiteFacet"/>
   <xs:complexType name="testSuitesType">
     <xs:sequence>
-      <xs:element name="testsuite" type="testSuiteType" maxOccurs="unbounded"/><!--moodle: added macOccures-->
+      <xs:element name="testsuite" type="testSuiteType" maxOccurs="unbounded"/>
     </xs:sequence>
   </xs:complexType>
   <xs:complexType name="testSuiteType">
-    <xs:group ref="pathGroup"/>
+    <xs:sequence>
+      <xs:group ref="pathGroup"/>
+      <xs:element name="exclude" type="xs:anyURI" minOccurs="0" maxOccurs="unbounded"/>
+    </xs:sequence>
     <xs:attribute name="name" type="xs:string" use="required"/>
   </xs:complexType>
 </xs:schema>
index cc1ef38..4b932b6 100644 (file)
@@ -74,12 +74,15 @@ class behat_permissions extends behat_base {
         // We don't know the number of overrides so we have to get it to match the option contents.
         $roleoption = $this->find('xpath', '//select[@name="roleid"]/option[contains(.,"' . $this->escape($rolename) . '")]');
 
-        return array(
+        $result = array(
             new Given('I set the field "' . get_string('advancedoverride', 'role') .
-                '" to "' . $this->escape($roleoption->getText()) . '"'),
-            new Given('I fill the capabilities form with the following permissions:', $table),
-            new Given('I press "' . get_string('savechanges') . '"')
-        );
+                '" to "' . $this->escape($roleoption->getText()) . '"'));
+        if (!$this->running_javascript()) {
+            $result[] = new Given('I press "' . get_string('go') . '"');
+        }
+        $result[] = new Given('I fill the capabilities form with the following permissions:', $table);
+        $result[] = new Given('I press "' . get_string('savechanges') . '"');
+        return $result;
     }
 
     /**
@@ -132,7 +135,8 @@ class behat_permissions extends behat_base {
 
             // Here we wait for the element to appear and exception if it does not exist.
             $radio = $this->find('xpath', '//input[@name="' . $capability . '" and @value="' . $permissionvalue . '"]');
-            $radio->click();
+            $field = behat_field_manager::get_field_instance('radio', $radio, $this->getSession());
+            $field->set_value(1);
         }
     }
 
index 2554b0d..e59fbdb 100644 (file)
@@ -128,4 +128,69 @@ END;
         $this->assertFalse(environment_verify_plugin('mod_someother', $plugin1['PLUGIN']));
         $this->assertFalse(environment_verify_plugin('mod_someother', $plugin2['PLUGIN']));
     }
+
+    /**
+     * Test the restrict_php_version() function returns true if the current
+     * PHP version is greater than the restricted version
+     */
+    public function test_restrict_php_version_greater_than_restricted_version() {
+        global $CFG;
+        require_once($CFG->libdir.'/environmentlib.php');
+
+        $result = new environment_results('php');
+        $delimiter = '.';
+        // Get the current PHP version.
+        $currentversion = explode($delimiter, normalize_version(phpversion()));
+        // Lets drop back one major version to ensure we trip the restriction.
+        $currentversion[0]--;
+        $restrictedversion = implode($delimiter, $currentversion);
+
+        // Make sure the status is true before the test to see it flip to false.
+        $result->setStatus(true);
+
+        $this->assertTrue(restrict_php_version($result, $restrictedversion),
+            'restrict_php_version returns true if the current version exceeds the restricted version');
+    }
+
+    /**
+     * Test the restrict_php_version() function returns true if the current
+     * PHP version is equal to the restricted version
+     */
+    public function test_restrict_php_version_equal_to_restricted_version() {
+        global $CFG;
+        require_once($CFG->libdir.'/environmentlib.php');
+
+        $result = new environment_results('php');
+        // Get the current PHP version.
+        $currentversion = normalize_version(phpversion());
+
+        // Make sure the status is true before the test to see it flip to false.
+        $result->setStatus(true);
+
+        $this->assertTrue(restrict_php_version($result, $currentversion),
+            'restrict_php_version returns true if the current version is equal to the restricted version');
+    }
+
+    /**
+     * Test the restrict_php_version() function returns false if the current
+     * PHP version is less than the restricted version
+     */
+    public function test_restrict_php_version_less_than_restricted_version() {
+        global $CFG;
+        require_once($CFG->libdir.'/environmentlib.php');
+
+        $result = new environment_results('php');
+        $delimiter = '.';
+        // Get the current PHP version.
+        $currentversion = explode($delimiter, normalize_version(phpversion()));
+        // Lets increase the major version to ensure don't trip the restriction.
+        $currentversion[0]++;
+        $restrictedversion = implode($delimiter, $currentversion);
+
+        // Make sure the status is true before the test to see it flip to false.
+        $result->setStatus(true);
+
+        $this->assertFalse(restrict_php_version($result, $restrictedversion),
+            'restrict_php_version returns false if the current version is less than the restricted version');
+    }
 }
index ebe284c..7f3b150 100644 (file)
@@ -217,6 +217,47 @@ class core_scheduled_task_testcase extends advanced_testcase {
         $this->assertEquals($initcount, $finalcount);
     }
 
+    /**
+     * Tests that the reset function deletes old tasks.
+     */
+    public function test_reset_scheduled_tasks_for_component_delete() {
+        global $DB;
+        $this->resetAfterTest(true);
+
+        $count = $DB->count_records('task_scheduled', array('component' => 'moodle'));
+        $allcount = $DB->count_records('task_scheduled');
+
+        $task = new \core\task\scheduled_test_task();
+        $task->set_component('moodle');
+        $record = \core\task\manager::record_from_scheduled_task($task);
+        $DB->insert_record('task_scheduled', $record);
+        $this->assertTrue($DB->record_exists('task_scheduled', array('classname' => '\core\task\scheduled_test_task',
+            'component' => 'moodle')));
+
+        $task = new \core\task\scheduled_test2_task();
+        $task->set_component('moodle');
+        $record = \core\task\manager::record_from_scheduled_task($task);
+        $DB->insert_record('task_scheduled', $record);
+        $this->assertTrue($DB->record_exists('task_scheduled', array('classname' => '\core\task\scheduled_test2_task',
+            'component' => 'moodle')));
+
+        $aftercount = $DB->count_records('task_scheduled', array('component' => 'moodle'));
+        $afterallcount = $DB->count_records('task_scheduled');
+
+        $this->assertEquals($count + 2, $aftercount);
+        $this->assertEquals($allcount + 2, $afterallcount);
+
+        // Now check that the right things were deleted.
+        \core\task\manager::reset_scheduled_tasks_for_component('moodle');
+
+        $this->assertEquals($count, $DB->count_records('task_scheduled', array('component' => 'moodle')));
+        $this->assertEquals($allcount, $DB->count_records('task_scheduled'));
+        $this->assertFalse($DB->record_exists('task_scheduled', array('classname' => '\core\task\scheduled_test2_task',
+            'component' => 'moodle')));
+        $this->assertFalse($DB->record_exists('task_scheduled', array('classname' => '\core\task\scheduled_test_task',
+            'component' => 'moodle')));
+    }
+
     public function test_get_next_scheduled_task() {
         global $DB;
 
index b787dd8..a842ea7 100644 (file)
@@ -3,6 +3,7 @@ information provided here is intended especially for developers.
 
 === 3.0 ===
 
+* PHPUnit is upgraded to 4.7. Some tests using deprecated assertions etc may need changes to work correctly.
 * get_referer() has been deprecated, please use the get_local_referer function instead.
 * \core\progress\null is renamed to \core\progress\none for improved PHP7 compatibility as null is a reserved word (see MDL-50453).
 * \webservice_xmlrpc_client now respects proxy server settings. If your XMLRPC server is available on your local network and not via your proxy server, you may need to add it to the list of proxy
index 0c2349b..8cc30c3 100644 (file)
@@ -2241,7 +2241,7 @@ function check_database_storage_engine(environment_results $result) {
 function check_slasharguments(environment_results $result){
     global $CFG;
 
-    if (empty($CFG->slasharguments)) {
+    if (!during_initial_install() && empty($CFG->slasharguments)) {
         $result->setInfo('slasharguments');
         $result->setStatus(false);
         return $result;
index bb671da..60b65d3 100644 (file)
@@ -3561,3 +3561,13 @@ function get_formatted_help_string($identifier, $component, $ajax = false, $a =
     }
     return $data;
 }
+
+/**
+ * Renders a hidden password field so that browsers won't incorrectly autofill password fields with the user's password.
+ *
+ * @since 3.0
+ * @return string HTML to prevent password autofill
+ */
+function prevent_form_autofill_password() {
+    return '<div class="hide"><input type="password" /></div>';
+}
index a8342fb..004c183 100644 (file)
@@ -179,6 +179,7 @@ function message_print_participants($context, $courseid, $contactselecturl=null,
     $participants = $DB->get_records_sql($sql, $params, $page * MESSAGE_CONTACTS_PER_PAGE, MESSAGE_CONTACTS_PER_PAGE);
 
     $pagingbar = new paging_bar($countparticipants, $page, MESSAGE_CONTACTS_PER_PAGE, $PAGE->url, 'page');
+    $pagingbar->maxdisplay = 4;
     echo $OUTPUT->render($pagingbar);
 
     echo html_writer::start_tag('div', array('id' => 'message_participants', 'class' => 'boxaligncenter'));
@@ -242,7 +243,7 @@ function message_get_blocked_users($user1=null, $user2=null) {
                           FROM {message_contacts} mc
                           JOIN {user} u ON u.id = mc.contactid
                           LEFT OUTER JOIN {message} m ON m.useridfrom = mc.contactid AND m.useridto = :user1id1
-                         WHERE mc.userid = :user1id2 AND mc.blocked = 1
+                         WHERE u.deleted = 0 AND mc.userid = :user1id2 AND mc.blocked = 1
                       GROUP BY $userfields
                       ORDER BY u.firstname ASC";
     $rs =  $DB->get_recordset_sql($blockeduserssql, array('user1id1' => $user1->id, 'user1id2' => $user1->id));
@@ -339,7 +340,7 @@ function message_get_contacts($user1=null, $user2=null) {
                      FROM {message_contacts} mc
                      JOIN {user} u ON u.id = mc.contactid
                      LEFT OUTER JOIN {message} m ON m.useridfrom = mc.contactid AND m.useridto = ?
-                    WHERE mc.userid = ? AND mc.blocked = 0
+                    WHERE u.deleted = 0 AND mc.userid = ? AND mc.blocked = 0
                  GROUP BY $userfields
                  ORDER BY u.firstname ASC";
 
@@ -365,7 +366,7 @@ function message_get_contacts($user1=null, $user2=null) {
                       FROM {message} m
                       JOIN {user} u  ON u.id = m.useridfrom
                       LEFT OUTER JOIN {message_contacts} mc ON mc.contactid = m.useridfrom AND mc.userid = m.useridto
-                     WHERE mc.id IS NULL AND m.useridto = ?
+                     WHERE u.deleted = 0 AND mc.id IS NULL AND m.useridto = ?
                   GROUP BY $userfields
                   ORDER BY u.firstname ASC";
 
index 0cfd90c..c46828c 100644 (file)
@@ -322,8 +322,19 @@ class core_message_externallib_testcase extends externallib_advanced_testcase {
 
         // Checking some of the fields returned.
         $stranger = array_pop($contacts['strangers']);
+
         $this->assertEquals(core_user::NOREPLY_USER, $stranger['id']);
         $this->assertEquals(1, $stranger['unread']);
+
+        // Check that deleted users are not returned.
+        delete_user($user_offline1);
+        delete_user($user_stranger);
+        delete_user($user_online);
+        $contacts = core_message_external::get_contacts();
+        $contacts = external_api::clean_returnvalue(core_message_external::get_contacts_returns(), $contacts);
+        $this->assertCount(2, $contacts['offline']);
+        $this->assertCount(0, $contacts['online']);
+        $this->assertCount(1, $contacts['strangers']);
     }
 
     /**
@@ -618,6 +629,12 @@ class core_message_externallib_testcase extends externallib_advanced_testcase {
         $blockedusers = external_api::clean_returnvalue(core_message_external::get_blocked_users_returns(), $blockedusers);
         $this->assertCount(1, $blockedusers['users']);
 
+        // Remove the $userblocked and check that the list now is empty.
+        delete_user($userblocked);
+        $blockedusers = core_message_external::get_blocked_users($user1->id);
+        $blockedusers = external_api::clean_returnvalue(core_message_external::get_blocked_users_returns(), $blockedusers);
+        $this->assertCount(0, $blockedusers['users']);
+
     }
 
     /**
index 38fc3fc..d461347 100644 (file)
@@ -98,6 +98,10 @@ class core_message_messagelib_testcase extends advanced_testcase {
         // Block other user.
         message_block_contact($user1->id);
         $this->assertCount(2, message_get_blocked_users());
+
+        // Test deleting users.
+        delete_user($user1);
+        $this->assertCount(1, message_get_blocked_users());
     }
 
     /**
@@ -154,6 +158,15 @@ class core_message_messagelib_testcase extends advanced_testcase {
         $this->assertCount(0, $onlinecontacts);
         $this->assertCount(1, $offlinecontacts);
         $this->assertCount(2, $strangers);
+
+        // Test deleting users.
+        delete_user($user1);
+        delete_user($user3);
+
+        list($onlinecontacts, $offlinecontacts, $strangers) = message_get_contacts();
+        $this->assertCount(0, $onlinecontacts);
+        $this->assertCount(0, $offlinecontacts);
+        $this->assertCount(1, $strangers);
     }
 
     /**
index ace0403..c20d709 100644 (file)
@@ -159,7 +159,7 @@ class page_editor {
     public static function set_annotations($gradeid, $pageno, $annotations) {
         global $DB;
 
-        $DB->delete_records('assignfeedback_editpdf_annot', array('gradeid'=>$gradeid, 'pageno'=>$pageno));
+        $DB->delete_records('assignfeedback_editpdf_annot', array('gradeid' => $gradeid, 'pageno' => $pageno, 'draft' => 1));
         $added = 0;
         foreach ($annotations as $record) {
             // Force these.
diff --git a/mod/folder/classes/external.php b/mod/folder/classes/external.php
new file mode 100644 (file)
index 0000000..c547800
--- /dev/null
@@ -0,0 +1,107 @@
+<?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/>.
+
+/**
+ * folder external API
+ *
+ * @package    mod_folder
+ * @category   external
+ * @copyright  2015 Juan Leyva <juan@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since      Moodle 3.0
+ */
+
+defined('MOODLE_INTERNAL') || die;
+
+require_once("$CFG->libdir/externallib.php");
+
+/**
+ * folder external functions
+ *
+ * @package    mod_folder
+ * @category   external
+ * @copyright  2015 Juan Leyva <juan@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since      Moodle 3.0
+ */
+class mod_folder_external extends external_api {
+
+    /**
+     * Returns description of method parameters
+     *
+     * @return external_function_parameters
+     * @since Moodle 3.0
+     */
+    public static function view_folder_parameters() {
+        return new external_function_parameters(
+            array(
+                'folderid' => new external_value(PARAM_INT, 'folder instance id')
+            )
+        );
+    }
+
+    /**
+     * Simulate the folder/view.php web interface page: trigger events, completion, etc...
+     *
+     * @param int $folderid the folder instance id
+     * @return array of warnings and status result
+     * @since Moodle 3.0
+     * @throws moodle_exception
+     */
+    public static function view_folder($folderid) {
+        global $DB, $CFG;
+        require_once($CFG->dirroot . "/mod/folder/lib.php");
+
+        $params = self::validate_parameters(self::view_folder_parameters(),
+                                            array(
+                                                'folderid' => $folderid
+                                            ));
+        $warnings = array();
+
+        // Request and permission validation.
+        $folder = $DB->get_record('folder', array('id' => $params['folderid']), '*', MUST_EXIST);
+        list($course, $cm) = get_course_and_cm_from_instance($folder, 'folder');
+
+        $context = context_module::instance($cm->id);
+        self::validate_context($context);
+
+        require_capability('mod/folder:view', $context);
+
+        // Call the page/lib API.
+        folder_view($folder, $course, $cm, $context);
+
+        $result = array();
+        $result['status'] = true;
+        $result['warnings'] = $warnings;
+        return $result;
+    }
+
+    /**
+     * Returns description of method result value
+     *
+     * @return external_description
+     * @since Moodle 3.0
+     */
+    public static function view_folder_returns() {
+        return new external_single_structure(
+            array(
+                'status' => new external_value(PARAM_BOOL, 'status: true if success'),
+                'warnings' => new external_warnings()
+            )
+        );
+    }
+
+}
diff --git a/mod/folder/db/services.php b/mod/folder/db/services.php
new file mode 100644 (file)
index 0000000..737ae35
--- /dev/null
@@ -0,0 +1,39 @@
+<?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/>.
+
+/**
+ * folder external functions and service definitions.
+ *
+ * @package    mod_folder
+ * @category   external
+ * @copyright  2015 Juan Leyva <juan@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since      Moodle 3.0
+ */
+
+defined('MOODLE_INTERNAL') || die;
+
+$functions = array(
+
+    'mod_folder_view_folder' => array(
+        'classname'     => 'mod_folder_external',
+        'methodname'    => 'view_folder',
+        'description'   => 'Simulate the view.php web interface folder: trigger events, completion, etc...',
+        'type'          => 'write',
+        'capabilities'  => 'mod/folder:view'
+    ),
+
+);
index f8578b8..aa96de4 100644 (file)
@@ -454,3 +454,31 @@ function folder_cm_info_view(cm_info $cm) {
         $cm->set_content($renderer->display_folder($folder));
     }
 }
+
+/**
+ * Mark the activity completed (if required) and trigger the course_module_viewed event.
+ *
+ * @param  stdClass $folder     folder object
+ * @param  stdClass $course     course object
+ * @param  stdClass $cm         course module object
+ * @param  stdClass $context    context object
+ * @since Moodle 3.0
+ */
+function folder_view($folder, $course, $cm, $context) {
+
+    // Trigger course_module_viewed event.
+    $params = array(
+        'context' => $context,
+        'objectid' => $folder->id
+    );
+
+    $event = \mod_folder\event\course_module_viewed::create($params);
+    $event->add_record_snapshot('course_modules', $cm);
+    $event->add_record_snapshot('course', $course);
+    $event->add_record_snapshot('folder', $folder);
+    $event->trigger();
+
+    // Completion.
+    $completion = new completion_info($course);
+    $completion->set_module_viewed($cm);
+}
diff --git a/mod/folder/tests/externallib_test.php b/mod/folder/tests/externallib_test.php
new file mode 100644 (file)
index 0000000..14c3cb5
--- /dev/null
@@ -0,0 +1,112 @@
+<?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/>.
+
+/**
+ * External mod_folder functions unit tests
+ *
+ * @package    mod_folder
+ * @category   external
+ * @copyright  2015 Juan Leyva <juan@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since      Moodle 3.0
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+global $CFG;
+
+require_once($CFG->dirroot . '/webservice/tests/helpers.php');
+
+/**
+ * External mod_folder functions unit tests
+ *
+ * @package    mod_folder
+ * @category   external
+ * @copyright  2015 Juan Leyva <juan@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since      Moodle 3.0
+ */
+class mod_folder_external_testcase extends externallib_advanced_testcase {
+
+    /**
+     * Test view_folder
+     */
+    public function test_view_folder() {
+        global $DB;
+
+        $this->resetAfterTest(true);
+
+        $this->setAdminUser();
+        // Setup test data.
+        $course = $this->getDataGenerator()->create_course();
+        $folder = $this->getDataGenerator()->create_module('folder', array('course' => $course->id));
+        $context = context_module::instance($folder->cmid);
+        $cm = get_coursemodule_from_instance('folder', $folder->id);
+
+        // Test invalid instance id.
+        try {
+            mod_folder_external::view_folder(0);
+            $this->fail('Exception expected due to invalid mod_folder instance id.');
+        } catch (moodle_exception $e) {
+            $this->assertEquals('invalidrecord', $e->errorcode);
+        }
+
+        // Test not-enrolled user.
+        $user = self::getDataGenerator()->create_user();
+        $this->setUser($user);
+        try {
+            mod_folder_external::view_folder($folder->id);
+            $this->fail('Exception expected due to not enrolled user.');
+        } catch (moodle_exception $e) {
+            $this->assertEquals('requireloginerror', $e->errorcode);
+        }
+
+        // Test user with full capabilities.
+        $studentrole = $DB->get_record('role', array('shortname' => 'student'));
+        $this->getDataGenerator()->enrol_user($user->id, $course->id, $studentrole->id);
+
+        // Trigger and capture the event.
+        $sink = $this->redirectEvents();
+
+        $result = mod_folder_external::view_folder($folder->id);
+        $result = external_api::clean_returnvalue(mod_folder_external::view_folder_returns(), $result);
+
+        $events = $sink->get_events();
+        $this->assertCount(1, $events);
+        $event = array_shift($events);
+
+        // Checking that the event contains the expected values.
+        $this->assertInstanceOf('\mod_folder\event\course_module_viewed', $event);
+        $this->assertEquals($context, $event->get_context());
+        $moodlefolder = new \moodle_url('/mod/folder/view.php', array('id' => $cm->id));
+        $this->assertEquals($moodlefolder, $event->get_url());
+        $this->assertEventContextNotUsed($event);
+        $this->assertNotEmpty($event->get_name());
+
+        // Test user with no capabilities.
+        // We need a explicit prohibit since this capability is only defined in authenticated user and guest roles.
+        assign_capability('mod/folder:view', CAP_PROHIBIT, $studentrole->id, $context->id);
+        accesslib_clear_all_caches_for_unit_testing();
+
+        try {
+            mod_folder_external::view_folder($folder->id);
+            $this->fail('Exception expected due to missing capability.');
+        } catch (moodle_exception $e) {
+            $this->assertEquals('nopermissions', $e->errorcode);
+        }
+
+    }
+}
diff --git a/mod/folder/tests/lib_test.php b/mod/folder/tests/lib_test.php
new file mode 100644 (file)
index 0000000..2094175
--- /dev/null
@@ -0,0 +1,92 @@
+<?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/>.
+
+/**
+ * Unit tests for mod_folder lib
+ *
+ * @package    mod_folder
+ * @category   external
+ * @copyright  2015 Juan Leyva <juan@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since      Moodle 3.0
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+
+/**
+ * Unit tests for mod_folder lib
+ *
+ * @package    mod_folder
+ * @category   external
+ * @copyright  2015 Juan Leyva <juan@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since      Moodle 3.0
+ */
+class mod_folder_lib_testcase extends advanced_testcase {
+
+    /**
+     * Prepares things before this test case is initialised
+     * @return void
+     */
+    public static function setUpBeforeClass() {
+        global $CFG;
+        require_once($CFG->dirroot . '/mod/folder/lib.php');
+    }
+
+    /**
+     * Test folder_view
+     * @return void
+     */
+    public function test_folder_view() {
+        global $CFG;
+
+        $CFG->enablecompletion = 1;
+        $this->resetAfterTest();
+
+        $this->setAdminUser();
+        // Setup test data.
+        $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1));
+        $folder = $this->getDataGenerator()->create_module('folder', array('course' => $course->id),
+                                                            array('completion' => 2, 'completionview' => 1));
+        $context = context_module::instance($folder->cmid);
+        $cm = get_coursemodule_from_instance('folder', $folder->id);
+
+        // Trigger and capture the event.
+        $sink = $this->redirectEvents();
+
+        folder_view($folder, $course, $cm, $context);
+
+        $events = $sink->get_events();
+        // 2 additional events thanks to completion.
+        $this->assertCount(3, $events);
+        $event = array_shift($events);
+
+        // Checking that the event contains the expected values.
+        $this->assertInstanceOf('\mod_folder\event\course_module_viewed', $event);
+        $this->assertEquals($context, $event->get_context());
+        $moodleurl = new \moodle_url('/mod/folder/view.php', array('id' => $cm->id));
+        $this->assertEquals($moodleurl, $event->get_url());
+        $this->assertEventContextNotUsed($event);
+        $this->assertNotEmpty($event->get_name());
+
+        // Check completion status.
+        $completion = new completion_info($course);
+        $completiondata = $completion->get_data($cm);
+        $this->assertEquals(1, $completiondata->completionstate);
+
+    }
+}
index f4699f2..ecfc208 100644 (file)
@@ -24,7 +24,7 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2015051100;       // The current module version (Date: YYYYMMDDXX)
+$plugin->version   = 2015051101;       // The current module version (Date: YYYYMMDDXX)
 $plugin->requires  = 2015050500;    // Requires this Moodle version
 $plugin->component = 'mod_folder';     // Full name of the plugin (used for diagnostics)
 $plugin->cron      = 0;
index 9ef681b..340c88c 100644 (file)
@@ -404,6 +404,7 @@ class mod_forum_external extends external_api {
     public static function get_forum_discussion_posts($discussionid, $sortby = "created", $sortdirection = "DESC") {
         global $CFG, $DB, $USER;
 
+        $posts = array();
         $warnings = array();
 
         // Validate the parameter.
@@ -463,9 +464,9 @@ class mod_forum_external extends external_api {
         $forumtracked = forum_tp_is_tracked($forum);
 
         $sort = 'p.' . $sortby . ' ' . $sortdirection;
-        $posts = forum_get_all_discussion_posts($discussion->id, $sort, $forumtracked);
+        $allposts = forum_get_all_discussion_posts($discussion->id, $sort, $forumtracked);
 
-        foreach ($posts as $pid => $post) {
+        foreach ($allposts as $post) {
 
             if (!forum_user_can_see_post($forum, $discussion, $post, null, $cm)) {
                 $warning = array();
@@ -480,16 +481,16 @@ class mod_forum_external extends external_api {
             // Function forum_get_all_discussion_posts adds postread field.
             // Note that the value returned can be a boolean or an integer. The WS expects a boolean.
             if (empty($post->postread)) {
-                $posts[$pid]->postread = false;
+                $post->postread = false;
             } else {
-                $posts[$pid]->postread = true;
+                $post->postread = true;
             }
 
-            $posts[$pid]->canreply = $canreply;
-            if (!empty($posts[$pid]->children)) {
-                $posts[$pid]->children = array_keys($posts[$pid]->children);
+            $post->canreply = $canreply;
+            if (!empty($post->children)) {
+                $post->children = array_keys($post->children);
             } else {
-                $posts[$pid]->children = array();
+                $post->children = array();
             }
 
             $user = new stdclass();
@@ -530,7 +531,7 @@ class mod_forum_external extends external_api {
                 }
             }
 
-            $posts[$pid] = (array) $post;
+            $posts[] = $post;
         }
 
         $result = array();
index 0742c85..05fc701 100644 (file)
@@ -6811,16 +6811,20 @@ function forum_reset_userdata($data) {
         $types       = array();
     } else if (!empty($data->reset_forum_types)){
         $removeposts = true;
-        $typesql     = "";
         $types       = array();
+        $sqltypes    = array();
         $forum_types_all = forum_get_forum_types_all();
         foreach ($data->reset_forum_types as $type) {
             if (!array_key_exists($type, $forum_types_all)) {
                 continue;
             }
-            $typesql .= " AND f.type=?";
             $types[] = $forum_types_all[$type];
-            $params[] = $type;
+            $sqltypes[] = $type;
+        }
+        if (!empty($sqltypes)) {
+            list($typesql, $typeparams) = $DB->get_in_or_equal($sqltypes);
+            $typesql = " AND f.type " . $typesql;
+            $params = array_merge($params, $typeparams);
         }
         $typesstr = get_string('resetforums', 'forum').': '.implode(', ', $types);
     }
index 3b121a7..953eeeb 100644 (file)
@@ -100,7 +100,7 @@ $strsubscribers = get_string("subscribers", "forum");
 $PAGE->navbar->add($strsubscribers);
 $PAGE->set_title($strsubscribers);
 $PAGE->set_heading($COURSE->fullname);
-if (has_capability('mod/forum:managesubscriptions', $context)) {
+if (has_capability('mod/forum:managesubscriptions', $context) && \mod_forum\subscriptions::is_forcesubscribed($forum) === false) {
     if ($edit != -1) {
         $USER->subscriptionsediting = $edit;
     }
@@ -112,11 +112,39 @@ echo $OUTPUT->header();
 echo $OUTPUT->heading(get_string('forum', 'forum').' '.$strsubscribers);
 if (empty($USER->subscriptionsediting)) {
     $subscribers = \mod_forum\subscriptions::fetch_subscribed_users($forum, $currentgroup, $context);
+    if (\mod_forum\subscriptions::is_forcesubscribed($forum)) {
+        $subscribers = mod_forum_filter_hidden_users($cm, $context, $subscribers);
+    }
     echo $forumoutput->subscriber_overview($subscribers, $forum, $course);
-} else if (\mod_forum\subscriptions::is_forcesubscribed($forum)) {
-    $subscriberselector->set_force_subscribed(true);
-    echo $forumoutput->subscribed_users($subscriberselector);
 } else {
     echo $forumoutput->subscriber_selection_form($existingselector, $subscriberselector);
 }
 echo $OUTPUT->footer();
+
+/**
+ * Filters a list of users for whether they can see a given activity.
+ * If the course module is hidden (closed-eye icon), then only users who have
+ * the permission to view hidden activities will appear in the output list.
+ *
+ * @todo MDL-48625 This filtering should be handled in core libraries instead.
+ *
+ * @param stdClass $cm the course module record of the activity.
+ * @param context_module $context the activity context, to save re-fetching it.
+ * @param array $users the list of users to filter.
+ * @return array the filtered list of users.
+ */
+function mod_forum_filter_hidden_users(stdClass $cm, context_module $context, array $users) {
+    if ($cm->visible) {
+        return $users;
+    } else {
+        // Filter for users that can view hidden activities.
+        $filteredusers = array();
+        $hiddenviewers = get_users_by_capability($context, 'moodle/course:viewhiddenactivities');
+        foreach ($hiddenviewers as $hiddenviewer) {
+            if (array_key_exists($hiddenviewer->id, $users)) {
+                $filteredusers[$hiddenviewer->id] = $users[$hiddenviewer->id];
+            }
+        }
+        return $filteredusers;
+    }
+}
index 32d4567..2b6a121 100644 (file)
@@ -65,6 +65,35 @@ Feature: As a teacher I need to see an accurate list of subscribed users
     And I should not see "Student 2"
     And I should not see "Student 3"
 
+  Scenario: A forced forum does not allow to edit the subscribers
+    When I add a "Forum" to section "1" and I fill the form with:
+      | Forum name        | Forced Forum 2 |
+      | Forum type        | Standard forum for general use |
+      | Description       | Test forum description |
+      | Subscription mode | Forced subscription |
+      | Visible           | Show |
+    And I follow "Forced Forum 2"
+    And I follow "Show/edit current subscribers"
+    Then I should see "Teacher Teacher"
+    And I should see "Student 1"
+    And I should see "Student 2"
+    And I should see "Student 3"
+    And I should not see "Turn editing on"
+
+  Scenario: A forced and hidden forum lists only teachers
+    When I add a "Forum" to section "1" and I fill the form with:
+      | Forum name        | Forced Forum 2 |
+      | Forum type        | Standard forum for general use |
+      | Description       | Test forum description |
+      | Subscription mode | Forced subscription |
+      | Visible           | Hide |
+    And I follow "Forced Forum 2"
+    And I follow "Show/edit current subscribers"
+    Then I should see "Teacher Teacher"
+    And I should not see "Student 1"
+    And I should not see "Student 2"
+    And I should not see "Student 3"
+
   @javascript
   Scenario: An automatic forum lists all subscribers
     When I add a "Forum" to section "1" and I fill the form with:
index 3810bdf..86d4e08 100644 (file)
@@ -56,7 +56,6 @@ Feature: Posting to all groups in a separate group discussion is restricted to u
     And the "Group" select box should contain "Group B"
     And I should see "Post a copy to all groups"
 
-  @javascript
   Scenario: Teacher in all groups but without accessallgroups can only post in their groups
     And I log in as "admin"
     And I set the following system permissions of "Non-editing teacher" role:
@@ -71,7 +70,6 @@ Feature: Posting to all groups in a separate group discussion is restricted to u
     And the "Group" select box should contain "Group B"
     And I should see "Post a copy to all groups"
 
-  @javascript
   Scenario: Teacher in some groups and without accessallgroups can only post in their groups
     And I log in as "admin"
     And I set the following system permissions of "Non-editing teacher" role:
index 9699a30..7fda1f2 100644 (file)
@@ -66,7 +66,6 @@ Feature: Posting to groups in a separate group discussion when restricted to gro
     And the "Group" select box should contain "G2G1"
     And I should see "Post a copy to all groups"
 
-  @javascript
   Scenario: Teacher in all groups but without accessallgroups can post in either group but not to All Participants
     And I log in as "admin"
     And I set the following system permissions of "Non-editing teacher" role:
index e2a57cd..2627ffa 100644 (file)
@@ -510,6 +510,72 @@ class mod_forum_external_testcase extends externallib_advanced_testcase {
 
     }
 
+    /**
+     * Test get forum posts (qanda forum)
+     */
+    public function test_mod_forum_get_forum_discussion_posts_qanda() {
+        global $CFG, $DB;
+
+        $this->resetAfterTest(true);
+
+        $record = new stdClass();
+        $user1 = self::getDataGenerator()->create_user($record);
+        $user2 = self::getDataGenerator()->create_user();
+
+        // Set the first created user to the test user.
+        self::setUser($user1);
+
+        // Create course to add the module.
+        $course1 = self::getDataGenerator()->create_course();
+        $this->getDataGenerator()->enrol_user($user1->id, $course1->id);
+        $this->getDataGenerator()->enrol_user($user2->id, $course1->id);
+
+        // Forum with tracking off.
+        $record = new stdClass();
+        $record->course = $course1->id;
+        $record->type = 'qanda';
+        $forum1 = self::getDataGenerator()->create_module('forum', $record);
+        $forum1context = context_module::instance($forum1->cmid);
+
+        // Add discussions to the forums.
+        $record = new stdClass();
+        $record->course = $course1->id;
+        $record->userid = $user2->id;
+        $record->forum = $forum1->id;
+        $discussion1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record);
+
+        // Add 1 reply (not the actual user).
+        $record = new stdClass();
+        $record->discussion = $discussion1->id;
+        $record->parent = $discussion1->firstpost;
+        $record->userid = $user2->id;
+        $discussion1reply1 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
+
+        // We still see only the original post.
+        $posts = mod_forum_external::get_forum_discussion_posts($discussion1->id, 'modified', 'DESC');
+        $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
+        $this->assertEquals(1, count($posts['posts']));
+
+        // Add a new reply, the user is going to be able to see only the original post and their new post.
+        $record = new stdClass();
+        $record->discussion = $discussion1->id;
+        $record->parent = $discussion1->firstpost;
+        $record->userid = $user1->id;
+        $discussion1reply2 = self::getDataGenerator()->get_plugin_generator('mod_forum')->create_post($record);
+
+        $posts = mod_forum_external::get_forum_discussion_posts($discussion1->id, 'modified', 'DESC');
+        $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
+        $this->assertEquals(2, count($posts['posts']));
+
+        // Now, we can fake the time of the user post, so he can se the rest of the discussion posts.
+        $discussion1reply2->created -= $CFG->maxeditingtime * 2;
+        $DB->update_record('forum_posts', $discussion1reply2);
+
+        $posts = mod_forum_external::get_forum_discussion_posts($discussion1->id, 'modified', 'DESC');
+        $posts = external_api::clean_returnvalue(mod_forum_external::get_forum_discussion_posts_returns(), $posts);
+        $this->assertEquals(3, count($posts['posts']));
+    }
+
     /**
      * Test get forum discussions paginated
      */
index ddd94b0..02b58d6 100644 (file)
@@ -168,35 +168,49 @@ function lesson_save_question_options($question, $lesson, $contextid) {
 
         case LESSON_PAGE_TRUEFALSE:
 
-            // the truth
+            // In lesson the correct answer always come first, as it was the case
+            // in question bank exports years ago.
             $answer = clone($defaultanswer);
-            $answer->answer = get_string("true", "lesson");
-            $answer->grade = $question->correctanswer * 100;
-            if ($answer->grade > 50 ) {
-                $answer->jumpto = LESSON_NEXTPAGE;
-                $answer->score = 1;
-            }
-            if (isset($question->feedbacktrue)) {
-                $answer->response = $question->feedbacktrue['text'];
-                $answer->responseformat = $question->feedbacktrue['format'];
+            $answer->grade = 100;
+            $answer->jumpto = LESSON_NEXTPAGE;
+            $answer->score = 1;
+            if ($question->correctanswer) {
+                $answer->answer = get_string("true", "lesson");
+                if (isset($question->feedbacktrue)) {
+                    $answer->response = $question->feedbacktrue['text'];
+                    $answer->responseformat = $question->feedbacktrue['format'];
+                    $answer->id = $DB->insert_record("lesson_answers", $answer);
+                    lesson_import_question_files('response', $question->feedbacktrue, $answer, $contextid);
+                }
+            } else {
+                $answer->answer = get_string("false", "lesson");
+                if (isset($question->feedbackfalse)) {
+                    $answer->response = $question->feedbackfalse['text'];
+                    $answer->responseformat = $question->feedbackfalse['format'];
+                    $answer->id = $DB->insert_record("lesson_answers", $answer);
+                    lesson_import_question_files('response', $question->feedbackfalse, $answer, $contextid);
+                }
             }
-            $answer->id = $DB->insert_record("lesson_answers", $answer);
-            lesson_import_question_files('response', $question->feedbacktrue, $answer, $contextid);
 
-            // the lie
+            // Now the wrong answer.
             $answer = clone($defaultanswer);
-            $answer->answer = get_string("false", "lesson");
-            $answer->grade = (1 - (int)$question->correctanswer) * 100;
-            if ($answer->grade > 50 ) {
-                $answer->jumpto = LESSON_NEXTPAGE;
-                $answer->score = 1;
-            }
-            if (isset($question->feedbackfalse)) {
-                $answer->response = $question->feedbackfalse['text'];
-                $answer->responseformat = $question->feedbackfalse['format'];
+            if ($question->correctanswer) {
+                $answer->answer = get_string("false", "lesson");
+                if (isset($question->feedbackfalse)) {
+                    $answer->response = $question->feedbackfalse['text'];
+                    $answer->responseformat = $question->feedbackfalse['format'];
+                    $answer->id = $DB->insert_record("lesson_answers", $answer);
+                    lesson_import_question_files('response', $question->feedbackfalse, $answer, $contextid);
+                }
+            } else {
+                $answer->answer = get_string("true", "lesson");
+                if (isset($question->feedbacktrue)) {
+                    $answer->response = $question->feedbacktrue['text'];
+                    $answer->responseformat = $question->feedbacktrue['format'];
+                    $answer->id = $DB->insert_record("lesson_answers", $answer);
+                    lesson_import_question_files('response', $question->feedbacktrue, $answer, $contextid);
+                }
             }
-            $answer->id = $DB->insert_record("lesson_answers", $answer);
-            lesson_import_question_files('response', $question->feedbackfalse, $answer, $contextid);
 
           break;
 
index 668d76c..75b97c8 100644 (file)
@@ -2740,7 +2740,7 @@ abstract class lesson_page extends lesson_base {
                     $this->answers[$i]->timecreated = $this->timecreated;
                 }
 
-                if (!empty($properties->answer_editor[$i])) {
+                if (isset($properties->answer_editor[$i])) {
                     if (is_array($properties->answer_editor[$i])) {
                         // Multichoice and true/false pages have an HTML editor.
                         $this->answers[$i]->answer = $properties->answer_editor[$i]['text'];
diff --git a/mod/lesson/tests/behat/lesson_delete_answers.feature b/mod/lesson/tests/behat/lesson_delete_answers.feature
new file mode 100644 (file)
index 0000000..0e50764
--- /dev/null
@@ -0,0 +1,96 @@
+@mod @mod_lesson
+Feature: In a lesson activity, teacher can delete question answers and
+branch table contents
+  In order to modify an existing lesson
+  As a teacher
+  I need to question answers and branch table contents in the lesson
+
+  Background:
+    Given the following "users" exist:
+      | username | firstname | lastname | email |
+      | teacher1 | Teacher | 1 | teacher1@example.com |
+      | student1 | Student | 1 | student1@example.com |
+    And the following "courses" exist:
+      | fullname | shortname | category |
+      | Course 1 | C1 | 0 |
+    And the following "course enrolments" exist:
+      | user | course | role |
+      | teacher1 | C1 | editingteacher |
+      | student1 | C1 | student |
+    And I log in as "teacher1"
+    And I follow "Course 1"
+    And I turn editing mode on
+    And I add a "Lesson" to section "1" and I fill the form with:
+      | Name | Test lesson name |
+      | Description | Test lesson description |
+    And I follow "Test lesson name"
+    And I follow "Add a content page"
+    And I set the following fields to these values:
+      | Page title | First page name |
+      | Page contents | First page contents |
+      | id_answer_editor_0 | Next page |
+      | id_jumpto_0 | Next page |
+      | id_answer_editor_1 | Previous page |
+      | id_jumpto_1 | Previous page |
+    And I press "Save page"
+    And I set the field "qtype" to "Question"
+    And I set the field "Select a question type" to "Numerical"
+    And I press "Add a question page"
+    And I set the following fields to these values:
+      | Page title | Hardest question ever |
+      | Page contents | 1 + 1? |
+      | id_answer_editor_0 | 2 |
+      | id_response_editor_0 | Correct answer |
+      | id_jumpto_0 | End of lesson |
+      | id_score_0 | 1 |
+      | id_answer_editor_1 | 1 |
+      | id_response_editor_1 | Incorrect answer |
+      | id_jumpto_1 | First page name |
+      | id_score_1 | 0 |
+    And I press "Save page"
+    And I follow "Expanded"
+
+  @javascript
+  Scenario: Edit lesson content page
+    Given I click on "//th[normalize-space(.)='First page name']/descendant::a[2]" "xpath_element"
+    When I set the following fields to these values:
+      | id_answer_editor_1 | |
+    And I press "Save page"
+    And I should not see "Previous page"
+    And I log out
+    And I log in as "student1"
+    And I follow "Course 1"
+    And I follow "Test lesson name"
+    And I should see "First page contents"
+    And I should not see "Previous page"
+    And I press "Next page"
+    And I should see "1 + 1?"
+    And I set the following fields to these values:
+      | Your answer | 2 |
+    And I press "Submit"
+    And I should see "Correct answer"
+    And I should not see "Incorrect answer"
+    And I press "Continue"
+    And I should see "Congratulations - end of lesson reached"
+    And I should see "Your score is 1 (out of 1)."
+
+  @javascript
+  Scenario: Edit lesson question page
+    Given I click on "//th[normalize-space(.)='Hardest question ever']/descendant::a[2]" "xpath_element"
+    When I set the following fields to these values:
+      | id_answer_editor_1 | |
+    And I press "Save page"
+    And I should not see "Incorrect answer"
+    And I log out
+    And I log in as "student1"
+    And I follow "Course 1"
+    And I follow "Test lesson name"
+    And I should see "First page contents"
+    And I press "Next page"
+    And I should see "1 + 1?"
+    And I set the following fields to these values:
+      | Your answer | 1 |
+    And I press "Submit"
+    And I should not see "Incorrect answer"
+    And I should see "Congratulations - end of lesson reached"
+    And I should see "Your score is 0 (out of 1)."
\ No newline at end of file
index 544959c..780086f 100644 (file)
@@ -73,6 +73,9 @@ if (!empty($id)) {
     }
 } else {
     $type = new stdClass();
+    // Assign a default empty value for the lti_icon.
+    $type->lti_icon = '';
+    $type->lti_secureicon = '';
 }
 
 $pageurl = new moodle_url('/mod/lti/typessettings.php');
diff --git a/mod/page/classes/external.php b/mod/page/classes/external.php
new file mode 100644 (file)
index 0000000..417d8d6
--- /dev/null
@@ -0,0 +1,107 @@
+<?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/>.
+
+/**
+ * Page external API
+ *
+ * @package    mod_page
+ * @category   external
+ * @copyright  2015 Juan Leyva <juan@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since      Moodle 3.0
+ */
+
+defined('MOODLE_INTERNAL') || die;
+
+require_once("$CFG->libdir/externallib.php");
+
+/**
+ * Page external functions
+ *
+ * @package    mod_page
+ * @category   external
+ * @copyright  2015 Juan Leyva <juan@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since      Moodle 3.0
+ */
+class mod_page_external extends external_api {
+
+    /**
+     * Returns description of method parameters
+     *
+     * @return external_function_parameters
+     * @since Moodle 3.0
+     */
+    public static function view_page_parameters() {
+        return new external_function_parameters(
+            array(
+                'pageid' => new external_value(PARAM_INT, 'page instance id')
+            )
+        );
+    }
+
+    /**
+     * Simulate the page/view.php web interface page: trigger events, completion, etc...
+     *
+     * @param int $pageid the page instance id
+     * @return array of warnings and status result
+     * @since Moodle 3.0
+     * @throws moodle_exception
+     */
+    public static function view_page($pageid) {
+        global $DB, $CFG;
+        require_once($CFG->dirroot . "/mod/page/lib.php");
+
+        $params = self::validate_parameters(self::view_page_parameters(),
+                                            array(
+                                                'pageid' => $pageid
+                                            ));
+        $warnings = array();
+
+        // Request and permission validation.
+        $page = $DB->get_record('page', array('id' => $params['pageid']), '*', MUST_EXIST);
+        list($course, $cm) = get_course_and_cm_from_instance($page, 'page');
+
+        $context = context_module::instance($cm->id);
+        self::validate_context($context);
+
+        require_capability('mod/page:view', $context);
+
+        // Call the page/lib API.
+        page_view($page, $course, $cm, $context);
+
+        $result = array();
+        $result['status'] = true;
+        $result['warnings'] = $warnings;
+        return $result;
+    }
+
+    /**
+     * Returns description of method result value
+     *
+     * @return external_description
+     * @since Moodle 3.0
+     */
+    public static function view_page_returns() {
+        return new external_single_structure(
+            array(
+                'status' => new external_value(PARAM_BOOL, 'status: true if success'),
+                'warnings' => new external_warnings()
+            )
+        );
+    }
+
+}
diff --git a/mod/page/db/services.php b/mod/page/db/services.php
new file mode 100644 (file)
index 0000000..be85f2e
--- /dev/null
@@ -0,0 +1,39 @@
+<?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/>.
+
+/**
+ * Page external functions and service definitions.
+ *
+ * @package    mod_page
+ * @category   external
+ * @copyright  2015 Juan Leyva <juan@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since      Moodle 3.0
+ */
+
+defined('MOODLE_INTERNAL') || die;
+
+$functions = array(
+
+    'mod_page_view_page' => array(
+        'classname'     => 'mod_page_external',
+        'methodname'    => 'view_page',
+        'description'   => 'Simulate the view.php web interface page: trigger events, completion, etc...',
+        'type'          => 'write',
+        'capabilities'  => 'mod/page:view'
+    ),
+
+);
index 4fcf7e4..4b10e34 100644 (file)
@@ -474,3 +474,31 @@ function page_dndupload_handle($uploadinfo) {
 
     return page_add_instance($data, null);
 }
+
+/**
+ * Mark the activity completed (if required) and trigger the course_module_viewed event.
+ *
+ * @param  stdClass $page       page object
+ * @param  stdClass $course     course object
+ * @param  stdClass $cm         course module object
+ * @param  stdClass $context    context object
+ * @since Moodle 3.0
+ */
+function page_view($page, $course, $cm, $context) {
+
+    // Trigger course_module_viewed event.
+    $params = array(
+        'context' => $context,
+        'objectid' => $page->id
+    );
+
+    $event = \mod_page\event\course_module_viewed::create($params);
+    $event->add_record_snapshot('course_modules', $cm);
+    $event->add_record_snapshot('course', $course);
+    $event->add_record_snapshot('page', $page);
+    $event->trigger();
+
+    // Completion.
+    $completion = new completion_info($course);
+    $completion->set_module_viewed($cm);
+}
diff --git a/mod/page/tests/externallib_test.php b/mod/page/tests/externallib_test.php
new file mode 100644 (file)
index 0000000..128e363
--- /dev/null
@@ -0,0 +1,111 @@
+<?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/>.
+
+/**
+ * External mod_page functions unit tests
+ *
+ * @package    mod_page
+ * @category   external
+ * @copyright  2015 Juan Leyva <juan@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since      Moodle 3.0
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+global $CFG;
+
+require_once($CFG->dirroot . '/webservice/tests/helpers.php');
+
+/**
+ * External mod_page functions unit tests
+ *
+ * @package    mod_page
+ * @category   external
+ * @copyright  2015 Juan Leyva <juan@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since      Moodle 3.0
+ */
+class mod_page_external_testcase extends externallib_advanced_testcase {
+
+    /**
+     * Test view_page
+     */
+    public function test_view_page() {
+        global $DB;
+
+        $this->resetAfterTest(true);
+
+        // Setup test data.
+        $course = $this->getDataGenerator()->create_course();
+        $page = $this->getDataGenerator()->create_module('page', array('course' => $course->id));
+        $context = context_module::instance($page->cmid);
+        $cm = get_coursemodule_from_instance('page', $page->id);
+
+        // Test invalid instance id.
+        try {
+            mod_page_external::view_page(0);
+            $this->fail('Exception expected due to invalid mod_page instance id.');
+        } catch (moodle_exception $e) {
+            $this->assertEquals('invalidrecord', $e->errorcode);
+        }
+
+        // Test not-enrolled user.
+        $user = self::getDataGenerator()->create_user();
+        $this->setUser($user);
+        try {
+            mod_page_external::view_page($page->id);
+            $this->fail('Exception expected due to not enrolled user.');
+        } catch (moodle_exception $e) {
+            $this->assertEquals('requireloginerror', $e->errorcode);
+        }
+
+        // Test user with full capabilities.
+        $studentrole = $DB->get_record('role', array('shortname' => 'student'));
+        $this->getDataGenerator()->enrol_user($user->id, $course->id, $studentrole->id);
+
+        // Trigger and capture the event.
+        $sink = $this->redirectEvents();
+
+        $result = mod_page_external::view_page($page->id);
+        $result = external_api::clean_returnvalue(mod_page_external::view_page_returns(), $result);
+
+        $events = $sink->get_events();
+        $this->assertCount(1, $events);
+        $event = array_shift($events);
+
+        // Checking that the event contains the expected values.
+        $this->assertInstanceOf('\mod_page\event\course_module_viewed', $event);
+        $this->assertEquals($context, $event->get_context());
+        $moodlepage = new \moodle_url('/mod/page/view.php', array('id' => $cm->id));
+        $this->assertEquals($moodlepage, $event->get_url());
+        $this->assertEventContextNotUsed($event);
+        $this->assertNotEmpty($event->get_name());
+
+        // Test user with no capabilities.
+        // We need a explicit prohibit since this capability is only defined in authenticated user and guest roles.
+        assign_capability('mod/page:view', CAP_PROHIBIT, $studentrole->id, $context->id);
+        accesslib_clear_all_caches_for_unit_testing();
+
+        try {
+            mod_page_external::view_page($page->id);
+            $this->fail('Exception expected due to missing capability.');
+        } catch (moodle_exception $e) {
+            $this->assertEquals('nopermissions', $e->errorcode);
+        }
+
+    }
+}
diff --git a/mod/page/tests/lib_test.php b/mod/page/tests/lib_test.php
new file mode 100644 (file)
index 0000000..7f8862b
--- /dev/null
@@ -0,0 +1,92 @@
+<?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/>.
+
+/**
+ * Unit tests for mod_page lib
+ *
+ * @package    mod_page
+ * @category   external
+ * @copyright  2015 Juan Leyva <juan@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since      Moodle 3.0
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+
+/**
+ * Unit tests for mod_page lib
+ *
+ * @package    mod_page
+ * @category   external
+ * @copyright  2015 Juan Leyva <juan@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since      Moodle 3.0
+ */
+class mod_page_lib_testcase extends advanced_testcase {
+
+    /**
+     * Prepares things before this test case is initialised
+     * @return void
+     */
+    public static function setUpBeforeClass() {
+        global $CFG;
+        require_once($CFG->dirroot . '/mod/page/lib.php');
+    }
+
+    /**
+     * Test page_view
+     * @return void
+     */
+    public function test_page_view() {
+        global $CFG;
+
+        $CFG->enablecompletion = 1;
+        $this->resetAfterTest();
+
+        // Setup test data.
+        $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1));
+        $page = $this->getDataGenerator()->create_module('page', array('course' => $course->id),
+                                                            array('completion' => 2, 'completionview' => 1));
+        $context = context_module::instance($page->cmid);
+        $cm = get_coursemodule_from_instance('page', $page->id);
+
+        // Trigger and capture the event.
+        $sink = $this->redirectEvents();
+
+        $this->setAdminUser();
+        page_view($page, $course, $cm, $context);
+
+        $events = $sink->get_events();
+        // 2 additional events thanks to completion.
+        $this->assertCount(3, $events);
+        $event = array_shift($events);
+
+        // Checking that the event contains the expected values.
+        $this->assertInstanceOf('\mod_page\event\course_module_viewed', $event);
+        $this->assertEquals($context, $event->get_context());
+        $moodleurl = new \moodle_url('/mod/page/view.php', array('id' => $cm->id));
+        $this->assertEquals($moodleurl, $event->get_url());
+        $this->assertEventContextNotUsed($event);
+        $this->assertNotEmpty($event->get_name());
+
+        // Check completion status.
+        $completion = new completion_info($course);
+        $completiondata = $completion->get_data($cm);
+        $this->assertEquals(1, $completiondata->completionstate);
+
+    }
+}
index d801c8a..d9a8d35 100644 (file)
@@ -24,7 +24,7 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2015051100;       // The current module version (Date: YYYYMMDDXX)
+$plugin->version   = 2015051101;       // The current module version (Date: YYYYMMDDXX)
 $plugin->requires  = 2015050500;    // Requires this Moodle version
 $plugin->component = 'mod_page';       // Full name of the plugin (used for diagnostics)
 $plugin->cron      = 0;
index 65d9ea6..149e0b1 100644 (file)
@@ -24,6 +24,7 @@
  */
 
 require('../../config.php');
+require_once($CFG->dirroot.'/mod/page/lib.php');
 require_once($CFG->dirroot.'/mod/page/locallib.php');
 require_once($CFG->libdir.'/completionlib.php');
 
@@ -50,20 +51,8 @@ require_course_login($course, true, $cm);
 $context = context_module::instance($cm->id);
 require_capability('mod/page:view', $context);
 
-// Trigger module viewed event.
-$event = \mod_page\event\course_module_viewed::create(array(
-   'objectid' => $page->id,
-   'context' => $context
-));
-$event->add_record_snapshot('course_modules', $cm);
-$event->add_record_snapshot('course', $course);
-$event->add_record_snapshot('page', $page);
-$event->trigger();
-
-// Update 'viewed' state if required by completion system
-require_once($CFG->libdir . '/completionlib.php');
-$completion = new completion_info($course);
-$completion->set_module_viewed($cm);
+// Completion and trigger events.
+page_view($page, $course, $cm, $context);
 
 $PAGE->set_url('/mod/page/view.php', array('id' => $cm->id));
 
index 3a7758c..1c89f35 100644 (file)
@@ -30,6 +30,7 @@ defined('MOODLE_INTERNAL') || die();
 
 class quizaccess extends base {
     public function is_uninstall_allowed() {
-        return false;
+        // Only allow uninstall of non-core access rules.
+        return !$this->is_standard();
     }
 }
index 2934e5a..652348f 100644 (file)
@@ -24,7 +24,7 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2015051100;
+$plugin->version   = 2015072900;
 $plugin->requires  = 2015050500;
 $plugin->component = 'mod_quiz';
 $plugin->cron      = 60;
diff --git a/mod/resource/classes/external.php b/mod/resource/classes/external.php
new file mode 100644 (file)
index 0000000..de55514
--- /dev/null
@@ -0,0 +1,107 @@
+<?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/>.
+
+/**
+ * Resource external API
+ *
+ * @package    mod_resource
+ * @category   external
+ * @copyright  2015 Juan Leyva <juan@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since      Moodle 3.0
+ */
+
+defined('MOODLE_INTERNAL') || die;
+
+require_once("$CFG->libdir/externallib.php");
+
+/**
+ * Resource external functions
+ *
+ * @package    mod_resource
+ * @category   external
+ * @copyright  2015 Juan Leyva <juan@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since      Moodle 3.0
+ */
+class mod_resource_external extends external_api {
+
+    /**
+     * Returns description of method parameters
+     *
+     * @return external_function_parameters
+     * @since Moodle 3.0
+     */
+    public static function view_resource_parameters() {
+        return new external_function_parameters(
+            array(
+                'resourceid' => new external_value(PARAM_INT, 'resource instance id')
+            )
+        );
+    }
+
+    /**
+     * Simulate the resource/view.php web interface page: trigger events, completion, etc...
+     *
+     * @param int $resourceid the resource instance id
+     * @return array of warnings and status result
+     * @since Moodle 3.0
+     * @throws moodle_exception
+     */
+    public static function view_resource($resourceid) {
+        global $DB, $CFG;
+        require_once($CFG->dirroot . "/mod/resource/lib.php");
+
+        $params = self::validate_parameters(self::view_resource_parameters(),
+                                            array(
+                                                'resourceid' => $resourceid
+                                            ));
+        $warnings = array();
+
+        // Request and permission validation.
+        $resource = $DB->get_record('resource', array('id' => $params['resourceid']), '*', MUST_EXIST);
+        list($course, $cm) = get_course_and_cm_from_instance($resource, 'resource');
+
+        $context = context_module::instance($cm->id);
+        self::validate_context($context);
+
+        require_capability('mod/resource:view', $context);
+
+        // Call the resource/lib API.
+        resource_view($resource, $course, $cm, $context);
+
+        $result = array();
+        $result['status'] = true;
+        $result['warnings'] = $warnings;
+        return $result;
+    }
+
+    /**
+     * Returns description of method result value
+     *
+     * @return external_description
+     * @since Moodle 3.0
+     */
+    public static function view_resource_returns() {
+        return new external_single_structure(
+            array(
+                'status' => new external_value(PARAM_BOOL, 'status: true if success'),
+                'warnings' => new external_warnings()
+            )
+        );
+    }
+
+}
diff --git a/mod/resource/db/services.php b/mod/resource/db/services.php
new file mode 100644 (file)
index 0000000..7699540
--- /dev/null
@@ -0,0 +1,39 @@
+<?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/>.
+
+/**
+ * Resource external functions and service definitions.
+ *
+ * @package    mod_resource
+ * @category   external
+ * @copyright  2015 Juan Leyva <juan@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since      Moodle 3.0
+ */
+
+defined('MOODLE_INTERNAL') || die;
+
+$functions = array(
+
+    'mod_resource_view_resource' => array(
+        'classname'     => 'mod_resource_external',
+        'methodname'    => 'view_resource',
+        'description'   => 'Simulate the view.php web interface resource: trigger events, completion, etc...',
+        'type'          => 'write',
+        'capabilities'  => 'mod/resource:view'
+    ),
+
+);
index 9439854..b91d1e1 100644 (file)
@@ -475,3 +475,31 @@ function resource_dndupload_handle($uploadinfo) {
 
     return resource_add_instance($data, null);
 }
+
+/**
+ * Mark the activity completed (if required) and trigger the course_module_viewed event.
+ *
+ * @param  stdClass $resource   resource object
+ * @param  stdClass $course     course object
+ * @param  stdClass $cm         course module object
+ * @param  stdClass $context    context object
+ * @since Moodle 3.0
+ */
+function resource_view($resource, $course, $cm, $context) {
+
+    // Trigger course_module_viewed event.
+    $params = array(
+        'context' => $context,
+        'objectid' => $resource->id
+    );
+
+    $event = \mod_resource\event\course_module_viewed::create($params);
+    $event->add_record_snapshot('course_modules', $cm);
+    $event->add_record_snapshot('course', $course);
+    $event->add_record_snapshot('resource', $resource);
+    $event->trigger();
+
+    // Completion.
+    $completion = new completion_info($course);
+    $completion->set_module_viewed($cm);
+}
diff --git a/mod/resource/tests/externallib_test.php b/mod/resource/tests/externallib_test.php
new file mode 100644 (file)
index 0000000..6b79da1
--- /dev/null
@@ -0,0 +1,112 @@
+<?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/>.
+
+/**
+ * External mod_resource functions unit tests
+ *
+ * @package    mod_resource
+ * @category   external
+ * @copyright  2015 Juan Leyva <juan@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since      Moodle 3.0
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+global $CFG;
+
+require_once($CFG->dirroot . '/webservice/tests/helpers.php');
+
+/**
+ * External mod_resource functions unit tests
+ *
+ * @package    mod_resource
+ * @category   external
+ * @copyright  2015 Juan Leyva <juan@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since      Moodle 3.0
+ */
+class mod_resource_external_testcase extends externallib_advanced_testcase {
+
+    /**
+     * Test view_resource
+     */
+    public function test_view_resource() {
+        global $DB;
+
+        $this->resetAfterTest(true);
+
+        $this->setAdminUser();
+        // Setup test data.
+        $course = $this->getDataGenerator()->create_course();
+        $resource = $this->getDataGenerator()->create_module('resource', array('course' => $course->id));
+        $context = context_module::instance($resource->cmid);
+        $cm = get_coursemodule_from_instance('resource', $resource->id);
+
+        // Test invalid instance id.
+        try {
+            mod_resource_external::view_resource(0);
+            $this->fail('Exception expected due to invalid mod_resource instance id.');
+        } catch (moodle_exception $e) {
+            $this->assertEquals('invalidrecord', $e->errorcode);
+        }
+
+        // Test not-enrolled user.
+        $user = self::getDataGenerator()->create_user();
+        $this->setUser($user);
+        try {
+            mod_resource_external::view_resource($resource->id);
+            $this->fail('Exception expected due to not enrolled user.');
+        } catch (moodle_exception $e) {
+            $this->assertEquals('requireloginerror', $e->errorcode);
+        }
+
+        // Test user with full capabilities.
+        $studentrole = $DB->get_record('role', array('shortname' => 'student'));
+        $this->getDataGenerator()->enrol_user($user->id, $course->id, $studentrole->id);
+
+        // Trigger and capture the event.
+        $sink = $this->redirectEvents();
+
+        $result = mod_resource_external::view_resource($resource->id);
+        $result = external_api::clean_returnvalue(mod_resource_external::view_resource_returns(), $result);
+
+        $events = $sink->get_events();
+        $this->assertCount(1, $events);
+        $event = array_shift($events);
+
+        // Checking that the event contains the expected values.
+        $this->assertInstanceOf('\mod_resource\event\course_module_viewed', $event);
+        $this->assertEquals($context, $event->get_context());
+        $moodleurl = new \moodle_url('/mod/resource/view.php', array('id' => $cm->id));
+        $this->assertEquals($moodleurl, $event->get_url());
+        $this->assertEventContextNotUsed($event);
+        $this->assertNotEmpty($event->get_name());
+
+        // Test user with no capabilities.
+        // We need a explicit prohibit since this capability is only defined in authenticated user and guest roles.
+        assign_capability('mod/resource:view', CAP_PROHIBIT, $studentrole->id, $context->id);
+        accesslib_clear_all_caches_for_unit_testing();
+
+        try {
+            mod_resource_external::view_resource($resource->id);
+            $this->fail('Exception expected due to missing capability.');
+        } catch (moodle_exception $e) {
+            $this->assertEquals('nopermissions', $e->errorcode);
+        }
+
+    }
+}
diff --git a/mod/resource/tests/lib_test.php b/mod/resource/tests/lib_test.php
new file mode 100644 (file)
index 0000000..13855c3
--- /dev/null
@@ -0,0 +1,92 @@
+<?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/>.
+
+/**
+ * Unit tests for mod_resource lib
+ *
+ * @package    mod_resource
+ * @category   external
+ * @copyright  2015 Juan Leyva <juan@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since      Moodle 3.0
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+
+/**
+ * Unit tests for mod_resource lib
+ *
+ * @package    mod_resource
+ * @category   external
+ * @copyright  2015 Juan Leyva <juan@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since      Moodle 3.0
+ */
+class mod_resource_lib_testcase extends advanced_testcase {
+
+    /**
+     * Prepares things before this test case is initialised
+     * @return void
+     */
+    public static function setUpBeforeClass() {
+        global $CFG;
+        require_once($CFG->dirroot . '/mod/resource/lib.php');
+    }
+
+    /**
+     * Test resource_view
+     * @return void
+     */
+    public function test_resource_view() {
+        global $CFG;
+
+        $CFG->enablecompletion = 1;
+        $this->resetAfterTest();
+
+        $this->setAdminUser();
+        // Setup test data.
+        $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1));
+        $resource = $this->getDataGenerator()->create_module('resource', array('course' => $course->id),
+                                                            array('completion' => 2, 'completionview' => 1));
+        $context = context_module::instance($resource->cmid);
+        $cm = get_coursemodule_from_instance('resource', $resource->id);
+
+        // Trigger and capture the event.
+        $sink = $this->redirectEvents();
+
+        resource_view($resource, $course, $cm, $context);
+
+        $events = $sink->get_events();
+        // 2 additional events thanks to completion.
+        $this->assertCount(3, $events);
+        $event = array_shift($events);
+
+        // Checking that the event contains the expected values.
+        $this->assertInstanceOf('\mod_resource\event\course_module_viewed', $event);
+        $this->assertEquals($context, $event->get_context());
+        $moodleurl = new \moodle_url('/mod/resource/view.php', array('id' => $cm->id));
+        $this->assertEquals($moodleurl, $event->get_url());
+        $this->assertEventContextNotUsed($event);
+        $this->assertNotEmpty($event->get_name());
+
+        // Check completion status.
+        $completion = new completion_info($course);
+        $completiondata = $completion->get_data($cm);
+        $this->assertEquals(1, $completiondata->completionstate);
+
+    }
+}
index cd1ba6a..5c44c78 100644 (file)
@@ -24,7 +24,7 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2015051100;       // The current module version (Date: YYYYMMDDXX)
+$plugin->version   = 2015051101;       // The current module version (Date: YYYYMMDDXX)
 $plugin->requires  = 2015050500;    // Requires this Moodle version
 $plugin->component = 'mod_resource'; // Full name of the plugin (used for diagnostics)
 $plugin->cron      = 0;
index bc55f03..74bf44d 100644 (file)
@@ -24,6 +24,7 @@
  */
 
 require('../../config.php');
+require_once($CFG->dirroot.'/mod/resource/lib.php');
 require_once($CFG->dirroot.'/mod/resource/locallib.php');
 require_once($CFG->libdir.'/completionlib.php');
 
@@ -52,19 +53,8 @@ require_course_login($course, true, $cm);
 $context = context_module::instance($cm->id);
 require_capability('mod/resource:view', $context);
 
-$params = array(
-    'context' => $context,
-    'objectid' => $resource->id
-);
-$event = \mod_resource\event\course_module_viewed::create($params);
-$event->add_record_snapshot('course_modules', $cm);
-$event->add_record_snapshot('course', $course);
-$event->add_record_snapshot('resource', $resource);
-$event->trigger();
-
-// Update 'viewed' state if required by completion system
-$completion = new completion_info($course);
-$completion->set_module_viewed($cm);
+// Completion and trigger events.
+resource_view($resource, $course, $cm, $context);
 
 $PAGE->set_url('/mod/resource/view.php', array('id' => $cm->id));
 
index 7472137..f3ba525 100644 (file)
@@ -1303,7 +1303,7 @@ function scorm_get_attempt_count($userid, $scorm, $returnobjects = false, $ignor
  * @return boolean - debugging true/false
  */
 function scorm_debugging($scorm) {
-    global $CFG, $USER;
+    global $USER;
     $cfgscorm = get_config('scorm');
 
     if (!$cfgscorm->allowapidebug) {
@@ -1315,9 +1315,11 @@ function scorm_debugging($scorm) {
     if (!preg_match('/^[\w\s\*\.\?\+\:\_\\\]+$/', $test)) {
         return false;
     }
-    $res = false;
-    eval('$res = preg_match(\'/^'.$test.'/\', $identifier) ? true : false;');
-    return $res;
+
+    if (preg_match('/^'.$test.'/', $identifier)) {
+        return true;
+    }
+    return false;
 }
 
 /**
index 618bf8e..72c0105 100644 (file)
@@ -25,7 +25,6 @@ Feature: Restrict which blocks can be added to Dashboard
     And the "Add a block" select box should contain "HTML"
     And the "Add a block" select box should contain "Tags"
 
-  @javascript
   Scenario: Remove the ability to add the comments block to Dashboard
     When I log in as "admin"
     And I set the following system permissions of "Authenticated user" role:
index ccd2b16..3e0efc4 100644 (file)
@@ -48,9 +48,7 @@ if (!has_capability('moodle/notes:manage', $context)) {
 if (data_submitted() && confirm_sesskey()) {
     // If data was submitted and is valid, then delete note.
     $returnurl = $CFG->wwwroot . '/notes/index.php?course=' . $course->id . '&amp;user=' . $note->userid;
-    if (!note_delete($note)) {
-        print_error('cannotdeletepost', 'notes', $returnurl);
-    }
+    note_delete($note);
     redirect($returnurl);
 
 } else {
index 22d8cbf..13ca62d 100644 (file)
@@ -239,12 +239,7 @@ class core_notes_external extends external_api {
                 $context = context_course::instance($note->courseid);
                 self::validate_context($context);
                 require_capability('moodle/notes:manage', $context);
-                if (!note_delete($note)) {
-                    $warnings[] = array(array('item' => 'note',
-                                              'itemid' => $noteid,
-                                              'warningcode' => 'savedfailed',
-                                              'message' => 'Note could not be modified'));
-                }
+                note_delete($note);
             } else {
                 $warnings[] = array('item'=>'note', 'itemid'=>$noteid, 'warningcode'=>'badid', 'message'=>'Note does not exist');
             }
index ea05994..a443a4b 100644 (file)
@@ -154,7 +154,7 @@ function note_save(&$note) {
  * Deletes a note object based on its id.
  *
  * @param int|object    $note id of the note to delete, or a note object which is to be deleted.
- * @return boolean true if the object was deleted; false otherwise
+ * @return boolean true always
  */
 function note_delete($note) {
     global $DB;
index f5fc51f..6e1b95f 100644 (file)
@@ -13,7 +13,6 @@
         stopOnFailure="false"
         stopOnIncomplete="false"
         stopOnSkipped="false"
-        strict="false"
         printerClass="Hint_ResultPrinter"
         testSuiteLoaderClass="phpunit_autoloader"
         >
index 7c70a6a..c812900 100644 (file)
@@ -335,9 +335,9 @@ class question_type {
         $question->generalfeedbackformat = !empty($form->generalfeedback['format']) ?
                 $form->generalfeedback['format'] : 0;
 
-        if (empty($question->name)) {
+        if ($question->name === '') {
             $question->name = shorten_text(strip_tags($form->questiontext['text']), 15);
-            if (empty($question->name)) {
+            if ($question->name === '') {
                 $question->name = '-';
             }
         }
index 6eda33f..6459629 100644 (file)
@@ -36,10 +36,32 @@ require_once($CFG->dirroot . '/question/type/questiontypebase.php');
  * @copyright  2008 The Open University
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-class question_type_test extends advanced_testcase {
+class question_type_testcase extends advanced_testcase {
     public static $includecoverage = array('question/type/questiontypebase.php');
 
-    public function test_not_done_yet() {
+    public function test_save_question_name() {
+        $this->resetAfterTest();
 
+        $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question');
+        $cat = $questiongenerator->create_question_category(array());
+
+        $saq = $questiongenerator->create_question('shortanswer', null,
+                array('category' => $cat->id, 'name' => 'Test question'));
+        $actual = question_bank::load_question_data($saq->id);
+
+        $this->assertSame('Test question', $actual->name);
+    }
+
+    public function test_save_question_zero_name() {
+        $this->resetAfterTest();
+
+        $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question');
+        $cat = $questiongenerator->create_question_category(array());
+
+        $saq = $questiongenerator->create_question('shortanswer', null,
+                array('category' => $cat->id, 'name' => '0'));
+        $actual = question_bank::load_question_data($saq->id);
+
+        $this->assertSame('0', $actual->name);
     }
 }
index d21fc35..9f24d63 100644 (file)
@@ -50,7 +50,7 @@ class core_repository_generator_testcase extends advanced_testcase {
             'user', 'webdav', 'wikimedia', 'youtube');
 
         // The ones enabled during installation.
-        $alreadyenabled = array('local', 'recent', 'upload', 'url', 'user', 'wikimedia', 'youtube');
+        $alreadyenabled = array('local', 'recent', 'upload', 'url', 'user', 'wikimedia');
 
         // Enable all the repositories which are not enabled yet.
         foreach ($all as $type) {
index 654f9e9..28a4f0b 100644 (file)
  */
 
 /**
- * Create a default instance of the youtube repository
+ * This was supposed to be the installer script for the Youtube repository.
  *
- * @return bool A status indicating success or failure
+ * However, since the Youtube repository is disabled in new Moodle installations from 3.0, and since we cannot
+ * just delete this file, the function's contents has been replaced to just return true.
+ * See https://tracker.moodle.org/browse/MDL-50572 for more details.
+ *
+ * @return bool Return true.
  */
 function xmldb_repository_youtube_install() {
-    global $CFG;
-    $result = true;
-    require_once($CFG->dirroot.'/repository/lib.php');
-    $youtubeplugin = new repository_type('youtube', array(), true);
-    if(!$id = $youtubeplugin->create(true)) {
-        $result = false;
-    }
-    return $result;
+    return true;
 }
index deccb47..fc2f253 100644 (file)
@@ -49,8 +49,6 @@ if ($perpage != DEFAULT_PAGE_SIZE) {
 admin_externalpage_setup('managetags', '', $params, '', array('pagelayout' => 'standard'));
 
 $PAGE->set_blocks_editing_capability('moodle/tag:editblocks');
-$PAGE->navbar->add(get_string('tags', 'tag'), new moodle_url('/tag/search.php'));
-$PAGE->navbar->add(get_string('managetags', 'tag'));
 
 echo $OUTPUT->header();
 
index 0694d3f..338f6d1 100644 (file)
@@ -3,10 +3,6 @@
 .block .header .block_action {float:right;margin:4px 0 3px 0;vertical-align:top;}
 .block .header .block_action img,
 .block .header .block_action input {margin:0 3px; width: 12px; height: 12px;}
-.block .header .commands { text-align: right; clear: both;}
-.block .header .commands > a { margin:0 3px; }
-.block .header .commands .icon img {width:12px;height:12px;}
-.block .header .commands img.actionmenu {width:auto;}
 .block .content {padding:4px;}
 .jsenabled .block.hidden .content {display: none;}
 .block .content .userpicture {width:16px;height:16px;margin-right:6px;}
     min-height: 400px;
     overflow: hidden;
 }
+/** Block commands when editing  **/
+
+.editing .block .header .commands {
+    text-align: right;
+    clear: both;
+}
+.editing .block .header .commands > a {
+    margin: 0 3px;
+}
+.editing .block .header .commands .icon img {
+    width: 12px;
+    height: 12px;
+}
+.editing .block .header .commands img.actionmenu {
+    width: auto;
+}
 
 /** Overide for RTL layout **/
 .dir-rtl .block .header,
 .dir-rtl .block h2.header {text-align:right;}
-.dir-rtl .block .header .commands { text-align: left;}
 .dir-rtl .block .header .block_action { float: left; margin-left: 4px; margin-left: 0;}
+
+.dir-rtl.editing .block .header .commands {
+    text-align: left;
+}
+
index 15a2041..413addc 100644 (file)
                 display: none;
             }
         }
-        .commands {
-            clear: both;
-            text-align: right;
-            display: block;
-            padding: 3px 15px;
-
-            > a {
-                margin: 0 3px;
-            }
-            .icon img {
-                width: 12px;
-                height: 12px;
-            }
-            img.actionmenu {
-                width: auto;
-            }
-        }
     }
     .content {
         padding: 4px 14px;
         .ie7-inline-block();
     }
 }
+.editing {
+    .block {
+        .header {
+            .commands {
+                clear: both;
+                text-align: right;
+                display: block;
+                padding: 3px 15px;
+
+                > a {
+                    margin: 0 3px;
+                }
+                .icon img {
+                    width: 12px;
+                    height: 12px;
+                }
+                img.actionmenu {
+                    width: auto;
+                }
+            }
+        }
+    }
+}
+.dir-rtl {
+    &.editing {
+        .block {
+            .header {
+                .commands {
+                    text-align: left;
+                }
+            }
+        }
+    }
+}
 
 // Hide the block content when the block has been minimised.
 .jsenabled .block.hidden .content {
                     margin-right: 3px;
                 }
             }
-            .commands {
-                text-align: left;
-            }
         }
     }
 
index 0852df1..0fcb247 100644 (file)
@@ -11,6 +11,7 @@
     font-size: 1em;
     font-weight: bold;
     clear: both;
+    word-wrap: break-word;
 }
 .message .author {
     font-weight: bold;
@@ -37,6 +38,9 @@
 #page-message-send td.fixeditor {
     text-align: center;
 }
+.message {
+    overflow: hidden;
+}
 .message .note {
     padding: 10px;
 }
@@ -44,75 +48,125 @@ table.message .searchresults td {
     padding: 5px;
 }
 .message .contactselector {
+    @maxwidth: 240px;
+
     @media screen and (min-width: 1000px) {
-        width: 24%;
+        float: left;
+        padding: 0 8px 0 0;
+    }
+    width: auto;
+    .singleselect {
+        width: @maxwidth;
     }
     float: left;
     .dir-rtl & {
         float:right;
     }
-}
-.message .contactselector .paging {
-    z-index: 1;
-    position: relative;
-}
+    .paging {
+        z-index: 1;
+        position: relative;
+    }
 
-.message .message-contacts {
-    list-style-type: none;
-    margin: 0;
+    #message_participants {
+        max-width: @maxwidth;
+    }
 
-    li {
-        clear: both;
-        position: relative;
-        min-height: 23px;
+    .message-contacts {
+        list-style-type: none;
+        margin: 0;
 
-        .pix {
-            left: 0;
-            position: absolute;
-        }
-        .contact {
-            margin: 0 24% 0 25px;
-            text-align: left;
-        }
-        .contact.nolinks {
-            margin-right: 5px;
-        }
-        .link {
-            float: right;
-            max-width: 30%;
+        li {
+            clear: both;
+            position: relative;
+            min-height: 23px;
+            padding: 2px;
+            &:nth-child(odd) {
+                background: rgba(120, 120, 255, 0.1);
+            }
+            > div {
+                display: block;
+                height: 100%;
+                > * {
+                    vertical-align: middle;
+                    display: inline-block;
+                }
+
+                &.pix {
+                    position: relative;
+                    float: left;
+                }
+                &.link {
+                    white-space: nowrap;
+                    width: 60px;
+                    position: relative;
+                    float: right;
+                    text-align: right;
+                }
+                &.contact {
+                    position: relative;
+                    word-break: break-all;
+                    a {
+                        line-height: 22px;
+                    }
+                }
+            }
         }
     }
 }
-
-.dir-rtl .message .message-contacts li {
-    .pix {
-        right: 0;
-        left: auto;
-    }
-    .contact {
-        margin: 0 25px 0 24%;
-    }
-    .contact.nolinks {
-        margin-right: 25px;
-        margin-left: 5px;
+.dir-ltr {
+    .message .message-contacts li {
+        text-align: left;
+        > div {
+            &.pix {
+                float: left;
+            }
+            &.link {
+                float: right;
+                text-align: right;
+            }
+            &.contact {
+                margin: 0 60px 0 24px;
+                &.nolinks {
+                    margin: 0 0 0 24px;
+                }
+            }
+        }
     }
-    .link {
-        float: left;
+}
+.dir-rtl {
+    .message .message-contacts li {
+        text-align: right;
+        > div {
+            &.pix {
+                float: right;
+            }
+            &.link {
+                float: left;
+                text-align: left;
+            }
+            &.contact {
+                margin: 0 24px 0 60px;
+                &.nolinks {
+                    margin: 0 24px 0 0;
+                }
+            }
+        }
     }
 }
-
 .message .messagearea  {
     @media screen and (min-width: 1000px) {
-        width: 74%;
         border-left: 1px solid @tableBorder;
-        padding-left: 1%;
+        padding: 0 8px;
     }
     @media screen and (max-width: 1000px) {
         width: 100%;
     }
-    float: right;
+    float: none;
+    overflow: hidden;
     min-height: 200px;
 }
+
+
 .message .messagearea .messagehistorytype {
     clear: both;
     padding-bottom: 20px;
@@ -211,6 +265,7 @@ table.message .searchresults td {
 }
 .message .messagearea .messagesend .messagesendbox {
     width: 100%;
+    box-sizing: border-box;
 }
 .message .messagearea .messagesend fieldset {
     padding: 0;
index 9908a8b..e165ac4 100644 (file)
@@ -1,4 +1,4 @@
-.layout-option-noheader #page-header,.layout-option-nonavbar #page-navbar,.layout-option-nofooter #page-footer,.layout-option-nocourseheader .course-content-header,.layout-option-nocoursefooter .course-content-footer{display:none}.empty-region-side-pre #block-region-side-pre,.empty-region-side-post #block-region-side-post,.jsenabled.docked-region-side-post #block-region-side-post,.jsenabled.docked-region-side-pre #block-region-side-pre{display:none}.content-only #region-main.span9,.empty-region-side-post #region-bs-main-and-pre.span9,.empty-region-side-pre #region-bs-main-and-post.span9,.empty-region-side-post #region-bs-main-and-post.span9 #region-main.span8,.jsenabled.docked-region-side-post #region-bs-main-and-pre.span9,.jsenabled.docked-region-side-post #region-bs-main-and-post.span9 #region-main.span8,.jsenabled.docked-region-side-pre #region-bs-main-and-post.span9{width:100%}.empty-region-side-pre #region-bs-main-and-pre.span9 #region-main,.jsenabled.docked-region-side-pre #region-bs-main-and-pre.span9 #region-main{float:none;width:100%}.empty-region-side-pre #region-bs-main-and-post.span9 #region-main.span8,.jsenabled.docked-region-side-pre #region-bs-main-and-post.span9 #region-main.span8{float:right}.content-only #region-main-box,.content-only #region-main{width:100%}.empty-region-side-pre.used-region-side-post #region-main{width:100%}.empty-region-side-post.used-region-side-pre #region-main-box{width:100%}.jsenabled.docked-region-side-pre.empty-region-side-pre.used-region-side-post #region-main{width:100%}.jsenabled.docked-region-side-post.empty-region-side-post.used-region-side-pre #region-main-box{width:100%}.empty-region-side-post.used-region-side-pre #region-main.span8,.jsenabled.docked-region-side-post.used-region-side-pre #region-main.span8{width:74.46808510638297%;*width:74.41489361702126%}.empty-region-side-post.used-region-side-pre #block-region-side-pre.span4,.jsenabled.docked-region-side-post.used-region-side-pre #block-region-side-pre.span4{width:23.404255319148934%;*width:23.351063829787233%}.dir-ltr,.mdl-left,.dir-rtl .mdl-right{text-align:left}.dir-rtl,.mdl-right,.dir-rtl .mdl-left{text-align:right}#add,#remove,.centerpara,.mdl-align{text-align:center}a.dimmed,a.dimmed:link,a.dimmed:visited,a.dimmed_text,a.dimmed_text:link,a.dimmed_text:visited,.dimmed_text,.dimmed_text a,.dimmed_text a:link,.dimmed_text a:visited,.usersuspended,.usersuspended a,.usersuspended a:link,.usersuspended a:visited,.dimmed_category,.dimmed_category a{color:#999}.activity.label .dimmed_text{opacity:.5;filter:alpha(opacity=50)}.unlist,.unlist li,.inline-list,.inline-list li,.block .list,.block .list li,.section li.activity,.section li.movehere,.tabtree li{padding:0;margin:0;list-style:none}.inline,.inline-list li{display:inline}.notifytiny{font-size:10.5px}.notifytiny li,.notifytiny td{font-size:100%}.red,.notifyproblem{color:#b94a48}.green,.notifysuccess{color:#468847}.highlight{background:#d9edf7}.reportlink{text-align:right}a.autolink.glossary:hover{cursor:help}.collapsibleregioncaption{white-space:nowrap}.pagelayout-mydashboard.jsenabled .collapsibleregioncaption{cursor:pointer}.collapsibleregioncaption img{vertical-align:middle}.jsenabled .hiddenifjs{display:none}.visibleifjs{display:none}.jsenabled .visibleifjs{display:inline}.jsenabled .collapsibleregion{overflow:hidden}.jsenabled .collapsed .collapsibleregioninner{visibility:hidden}.collapsible-actions{display:none;text-align:right}.dir-rtl .collapsible-actions{text-align:left}.jsenabled .collapsible-actions{display:block}.collapsible-actions .collapseexpand{padding-left:20px;background:url([[pix:t/collapsed]]) 2px center no-repeat}.dir-rtl .collapsible-actions .collapseexpand{padding-right:20px;padding-left:0;background:url([[pix:t/collapsed_rtl]]) right center no-repeat}.collapsible-actions .collapse-all,.dir-rtl .collapsible-actions .collapse-all{background-image:url([[pix:t/expanded]])}.yui-overlay .yui-widget-bd{position:relative;top:0;left:0;z-index:1;padding:2px 5px;color:#000;background-color:#ffee69;border:1px solid #a6982b;border-top-color:#d4c237}.clearer{display:block;height:1px;padding:0;margin:0;clear:both;background:transparent;border-width:0}.bold,.warning,.errorbox .title,.pagingbar .title,.pagingbar .thispage{font-weight:bold}img.resize{width:1em;height:1em}.block img.resize,.breadcrumb img.resize{width:.8em;height:.9em}img.icon{width:16px;height:16px;padding-right:6px;vertical-align:text-bottom}.dir-rtl img.icon{padding-right:0;padding-left:6px}img.iconsmall{width:12px;height:12px;margin-right:3px;vertical-align:middle}img.iconhelp,.helplink img{width:16px;height:16px;padding-left:3px;vertical-align:text-bottom}h1 img.iconhelp,h1 img.icon,h2 img.iconhelp,h2 img.icon,h3 img.iconhelp,h3 img.icon,h4 img.iconhelp,h4 img.icon,h5 img.iconhelp,h5 img.icon,h6 img.iconhelp,h6 img.icon{padding:4px;vertical-align:middle}.dir-rtl img.iconhelp,.dir-rtl .helplink img{padding-right:3px;padding-left:0}img.iconlarge{width:24px;height:24px;vertical-align:middle}img.iconsort{padding-left:.3em;margin-bottom:.15em;vertical-align:text-bottom}.dir-rtl img.iconsort{padding-right:.3em;padding-left:0}img.icontoggle{width:50px;height:17px;vertical-align:middle}img.iconkbhelp{width:49px;height:17px}img.icon-pre,.dir-rtl img.icon-post{padding-right:3px;padding-left:0}img.icon-post,.dir-rtl img.icon-pre{padding-right:0;padding-left:3px}.boxaligncenter{margin-right:auto;margin-left:auto}.boxalignright{margin-right:0;margin-left:auto}.boxalignleft{margin-right:auto;margin-left:0}.boxwidthnarrow{width:30%}.boxwidthnormal{width:50%}.boxwidthwide{width:80%}.headermain{font-weight:bold}#maincontent{display:block;height:1px;overflow:hidden}img.uihint{cursor:help}#addmembersform table{margin-right:auto;margin-left:auto}table.flexible .emptyrow{display:none}img.emoticon{width:15px;height:15px;vertical-align:middle}form.popupform,form.popupform div{display:inline}.arrow_button input{overflow:hidden}.action-icon img.smallicon{margin:0 .3em;vertical-align:text-bottom}.no-overflow{padding-bottom:1px;overflow:auto}.pagelayout-report .no-overflow{overflow:visible}.no-overflow>.generaltable{margin-bottom:0}.accesshide{position:absolute;left:-10000px;font-size:1em;font-weight:normal}.dir-rtl .accesshide{top:-30000px;left:auto}span.hide,div.hide{display:none}a.skip-block,a.skip{position:absolute;top:-1000em;font-size:.85em;text-decoration:none}a.skip-block:focus,a.skip-block:active,a.skip:focus,a.skip:active{position:static;display:block}.skip-block-to{display:block;height:1px;overflow:hidden}.addbloglink{text-align:center}.blog_entry .audience{padding-right:4px;text-align:right}.blog_entry .tags{margin-top:15px}.blog_entry .tags .action-icon img.smallicon{width:16px;height:16px}.blog_entry .content{margin-left:43px}#page-group-index #groupeditform{text-align:center}#doc-contents h1{margin:1em 0 0 0}#doc-contents ul{width:90%;padding:0;margin:0}#doc-contents ul li{list-style-type:none}.groupmanagementtable td{vertical-align:top}.groupmanagementtable #existingcell,.groupmanagementtable #potentialcell{width:42%}.groupmanagementtable #buttonscell{width:16%}.groupmanagementtable #buttonscell p.arrow_button input{width:auto;min-width:80%;margin:0 auto}.groupmanagementtable #removeselect_wrapper,.groupmanagementtable #addselect_wrapper{width:100%}.groupmanagementtable #removeselect_wrapper label,.groupmanagementtable #addselect_wrapper label{font-weight:normal}.dir-rtl .groupmanagementtable p{text-align:right}#group-usersummary{width:14em}.groupselector{display:inline-block;margin-top:3px;margin-bottom:3px}.groupselector label{display:inline-block}.loginbox{margin:15px;overflow:visible}.loginbox.twocolumns{margin:15px}.loginbox h2,.loginbox .subcontent{padding:10px;margin:5px;text-align:center}.loginbox .loginpanel .desc{padding:0;margin:0;margin-top:15px;margin-bottom:5px}.loginbox .signuppanel .subcontent{text-align:left}.dir-rtl .loginbox .signuppanel .subcontent{text-align:right}.loginbox .loginsub{margin-right:0;margin-left:0}.loginbox .guestsub,.loginbox .forgotsub,.loginbox .potentialidps{margin:5px 12%}.loginbox .potentialidps .potentialidplist{margin-left:40%}.loginbox .potentialidps .potentialidplist div{text-align:left}.loginbox .loginform{margin-top:1em;text-align:left}.loginbox .loginform .form-label{float:left;width:49%;text-align:right;white-space:nowrap}.loginbox .loginform .form-input{float:right;width:50%}.loginbox .loginform .form-input input{width:6em}.loginbox .signupform{margin-top:1em;text-align:center}.loginbox.twocolumns .loginpanel,.loginbox.twocolumns .signuppanel{display:block;float:left;width:48%;min-height:30px;padding:0;padding-bottom:2000px;margin:0;margin-bottom:-2000px;margin-left:2.76243%;border:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.dir-rtl .loginbox.twocolumns .loginpanel,.dir-rtl .loginbox.twocolumns .signuppanel{float:right}.loginbox .potentialidp .smallicon{margin:0 .3em;vertical-align:text-bottom}.notepost{margin-bottom:1em}.notepost .userpicture{float:left;margin-right:5px}.notepost .content,.notepost .footer{clear:both}.notesgroup{margin-left:20px}.path-my .coursebox .overview{margin:15px 30px 10px 30px}.path-my .coursebox .info{float:none;margin:0}.mod_introbox{padding:10px}table.mod_index{width:100%}.comment-ctrl{display:none;padding:0;margin:0;font-size:12px}.comment-ctrl h5{padding:5px;margin:0}.comment-area{max-width:400px;padding:5px}.comment-area textarea{width:100%;overflow:auto}.comment-area textarea.fullwidth{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.comment-area .fd{text-align:right}.comment-meta span{color:gray}.comment-link img{vertical-align:text-bottom}.comment-list{padding:0;margin:0;overflow:auto;font-size:11px;list-style:none}.comment-list li{position:relative;padding:.3em;margin:2px;margin-bottom:5px;clear:both;list-style:none}.comment-list li.first{display:none}.comment-paging{text-align:center}.comment-paging .pageno{padding:2px}.comment-paging .curpage{border:1px solid #CCC}.comment-message .picture{float:left;width:20px}.dir-rtl .comment-message .picture{float:right}.comment-message .text{padding:0;margin:0}.comment-message .text p{padding:0;margin:0 18px 0 0}.comment-delete{position:absolute;top:0;right:0;margin:.3em}.dir-rtl .comment-delete{position:absolute;right:auto;left:0;margin:.3em}.comment-report-selectall{display:none}.comment-link{display:none}.jsenabled .comment-link{display:block}.jsenabled .showcommentsnonjs{display:none}.jsenabled .comment-report-selectall{display:inline}.completion-expired{background:#f2dede}.completion-expected{font-size:10.5px}.completion-sortchoice,.completion-identifyfield{font-size:10.5px;vertical-align:bottom}.completion-progresscell{text-align:right}.completion-expired .completion-expected{font-weight:bold}#page-tag-coursetags_edit .coursetag_edit_centered{position:relative;width:600px;margin:20px auto}#page-tag-coursetags_edit .coursetag_edit_row{clear:both}#page-tag-coursetags_edit .coursetag_edit_row .coursetag_edit_left{float:left;width:50%;text-align:right}#page-tag-coursetags_edit .coursetag_edit_row .coursetag_edit_right{margin-left:50%}#page-tag-coursetags_edit .coursetag_edit_input3{display:none}#page-tag-coursetags_more .coursetag_more_large{font-size:120%}#page-tag-coursetags_more .coursetag_more_small{font-size:80%}#page-tag-coursetags_more .coursetag_more_link{font-size:80%}#tag-description,#tag-blogs{width:100%}#tag-management-box{margin-bottom:10px;line-height:20px}#tag-user-table{width:100%;padding:3px;clear:both}#tag-user-table{*zoom:1}#tag-user-table:before,#tag-user-table:after{display:table;line-height:0;content:""}#tag-user-table:after{clear:both}img.user-image{width:100px;height:100px}#small-tag-cloud-box{width:300px;margin:0 auto}#big-tag-cloud-box{float:none;width:600px;margin:0 auto}ul#tag-cloud-list{padding:5px;margin:0;list-style:none}ul#tag-cloud-list li{display:inline;margin:0;list-style-type:none}#tag-search-box{margin:10px auto;text-align:center}#tag-search-results-container{width:100%;padding:0}#tag-search-results{display:block;float:left;width:60%;padding:0;margin:15px 20% 0 20%}#tag-search-results li{float:left;width:30%;padding-right:1%;padding-left:1%;line-height:20px;text-align:left;list-style:none}span.flagged-tag,span.flagged-tag a{color:#b94a48}table#tag-management-list{width:100%;text-align:left}table#tag-management-list td,table#tag-management-list th{padding:4px;text-align:left;vertical-align:middle}.tag-management-form{text-align:center}#relatedtags-autocomplete-container{width:100%;min-height:4.6em;margin-right:auto;margin-left:auto}#relatedtags-autocomplete{position:relative;display:block;width:60%;margin-right:auto;margin-left:auto}#relatedtags-autocomplete .yui-ac-content{position:absolute;left:20%;z-index:9050;width:420px;overflow:hidden;background:#fff;border:1px solid rgba(0,0,0,0.2)}#relatedtags-autocomplete .ysearchquery{position:absolute;right:10px;z-index:10;color:#808080}#relatedtags-autocomplete .yui-ac-shadow{position:absolute;z-index:9049;width:100%;margin:.3em;background:#a0a0a0}#relatedtags-autocomplete ul{width:100%;padding:0;margin:0;list-style-type:none}#relatedtags-autocomplete li{padding:0 5px;white-space:nowrap;cursor:default}#relatedtags-autocomplete li.yui-ac-highlight{color:#fff;background:#0070a8}h2.tag-heading,div#tag-description,div#tag-blogs,body.tag .managelink{padding:5px}.tag_cloud .s20{font-size:1.5em;font-weight:bold}.tag_cloud .s19{font-size:1.5em}.tag_cloud .s18{font-size:1.4em;font-weight:bold}.tag_cloud .s17{font-size:1.4em}.tag_cloud .s16{font-size:1.3em;font-weight:bold}.tag_cloud .s15{font-size:1.3em}.tag_cloud .s14{font-size:1.2em;font-weight:bold}.tag_cloud .s13{font-size:1.2em}.tag_cloud .s12,.tag_cloud .s11{font-size:1.1em;font-weight:bold}.tag_cloud .s10,.tag_cloud .s9{font-size:1.1em}.tag_cloud .s8,.tag_cloud .s7{font-size:1em;font-weight:bold}.tag_cloud .s6,.tag_cloud .s5{font-size:1em}.tag_cloud .s4,.tag_cloud .s3{font-size:.9em;font-weight:bold}.tag_cloud .s2,.tag_cloud .s1{font-size:.9em}.tag_cloud .s0{font-size:.8em}#webservice-doc-generator td{text-align:left;border:0 solid black}.smartselect{position:absolute}.smartselect .smartselect_mask{background-color:#fff}.smartselect ul{padding:0;margin:0}.smartselect ul li{list-style:none}.smartselect .smartselect_menu{margin-right:5px}.safari .smartselect .smartselect_menu{margin-left:2px}.smartselect .smartselect_menu,.smartselect .smartselect_submenu{display:none;background-color:#FFF;border:1px solid #000}.smartselect .smartselect_menu.visible,.smartselect .smartselect_submenu.visible{display:block}.smartselect .smartselect_menu_content ul li{position:relative;padding:2px 5px}.smartselect .smartselect_menu_content ul li a{color:#333;text-decoration:none}.smartselect .smartselect_menu_content ul li a.selectable{color:inherit}.smartselect .smartselect_submenuitem{background-image:url([[pix:moodle|t/collapsed]]);background-position:100%;background-repeat:no-repeat}.smartselect.spanningmenu .smartselect_submenu{position:absolute;top:-1px;left:100%}.smartselect.spanningmenu .smartselect_submenu a{padding-right:16px;white-space:nowrap}.smartselect.spanningmenu .smartselect_menu_content ul li a.selectable:hover{text-decoration:underline}.smartselect.compactmenu .smartselect_submenu{position:relative;z-index:1010;display:none;margin:2px -3px;margin-left:10px;border-width:0}.smartselect.compactmenu .smartselect_submenu.visible{display:block}.smartselect.compactmenu .smartselect_menu{z-index:1000;overflow:hidden}.smartselect.compactmenu .smartselect_submenu .smartselect_submenu{z-index:1020}.smartselect.compactmenu .smartselect_submenuitem:hover>.smartselect_menuitem_label{font-weight:bold}#page-admin-registration-register .registration_textfield{width:300px}.userenrolment{width:100%;border-collapse:collapse}.userenrolment tr{vertical-align:top}.userenrolment td{height:41px;padding:0}.userenrolment .subfield{margin-right:5px}.userenrolment .col_userdetails .subfield_picture{float:left}.userenrolment .col_lastseen{width:150px}.userenrolment .col_role{width:262px}.userenrolment .col_role .roles,.userenrolment .col_group .groups{margin-right:30px}.userenrolment .col_role .role,.userenrolment .col_group .group{float:left;padding:3px;margin:3px;white-space:nowrap}.userenrolment .col_role .role a,.userenrolment .col_group .group a{margin-left:3px;cursor:pointer}.userenrolment .col_role .addrole,.userenrolment .col_group .addgroup{float:right;padding:3px;margin:3px}.userenrolment .col_role .addrole>*:hover,.userenrolment .col_group .addgroup>*:hover{border-bottom:1px solid #666}.userenrolment .col_role .addrole img,.userenrolment .col_group .addgroup img{vertical-align:baseline}.dir-rtl .userenrolment .col_role .role{float:right}.userenrolment .hasAllRoles .col_role .addrole{display:none}.userenrolment .col_enrol .enrolment{float:left;padding:3px;margin:3px}.userenrolment .col_enrol .enrolment a{float:right;margin-left:3px}#page-enrol-users .enrol_user_buttons{float:right}#page-enrol-users .enrol_user_buttons .enrolusersbutton{display:inline}#page-enrol-users .enrol_user_buttons .enrolusersbutton div,#page-enrol-users .enrol_user_buttons .enrolusersbutton form{display:inline;margin-right:0}#page-enrol-users #filterform{display:inline-block;min-height:20px;padding:19px;padding:9px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-color:#e3e3e3;-webkit-border-radius:4px;-webkit-border-radius:3px;-moz-border-radius:4px;-moz-border-radius:3px;border-radius:4px;border-radius:3px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}#page-enrol-users #filterform blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}#page-enrol-users #filterform .fitem{display:inline-block;margin-right:.3em;line-height:40px;white-space:nowrap}#page-enrol-users #filterform .fitem label{display:inline;padding-right:.3em;line-height:20px}#page-enrol-users #filterform .fitem :before,#page-enrol-users #filterform .fitem:after{display:inline}#page-enrol-users #filterform div,#page-enrol-users #filterform fieldset{display:inline;float:none;width:auto;margin:0;clear:none}#page-enrol-users #filterform select,#page-enrol-users #filterform .ftext input{width:7em}#page-enrol-users #filterform input,#page-enrol-users #filterform select{margin-bottom:0}#page-enrol-users .user-enroller-panel .uep-search-results .user .details{width:237px}#page-enrol-users .user-enroller-panel .uep-search-results .cohort .details{width:237px}.dir-rtl#page-enrol-users .col_userdetails .subfield_picture{float:right}.dir-rtl#page-enrol-users .enrol_user_buttons{float:left}.dir-rtl#page-enrol-users .enrol_user_buttons .enrolusersbutton{margin-right:1em;margin-left:0}.dir-rtl#page-enrol-users .enrol_user_buttons .enrolusersbutton div{margin-left:0}.dir-rtl#page-enrol-users #filterform .fitem{margin-right:0;margin-left:.3em}.dir-rtl#page-enrol-users #filterform .fitem label{padding-right:0;padding-left:.3em}.dir-rtl .headermain{float:right}.dir-rtl .headermenu{float:left}.dir-rtl .loginbox .loginform .form-label{float:right;text-align:left}.dir-rtl .loginbox .loginform .form-input{margin-right:1%;text-align:right}.dir-rtl .yui3-menu-hidden{left:0}#page-admin-roles-define.dir-rtl #rolesform .felement{margin-right:180px}#page-message-edit.dir-rtl table.generaltable th.c0{text-align:right}.corelightbox{position:absolute;top:0;left:0;width:100%;height:100%;text-align:center;background-color:#CCC}.corelightbox img{position:fixed;top:50%;left:50%}.mod-indent-outer{display:table}.mod-indent{display:table-cell}.label .mod-indent{float:left;padding-top:20px}.mod-indent-1{width:30px}.mod-indent-2{width:60px}.mod-indent-3{width:90px}.mod-indent-4{width:120px}.mod-indent-5{width:150px}.mod-indent-6{width:180px}.mod-indent-7{width:210px}.mod-indent-8{width:240px}.mod-indent-9{width:270px}.mod-indent-10{width:300px}.mod-indent-11{width:330px}.mod-indent-12{width:360px}.mod-indent-13{width:390px}.mod-indent-14{width:420px}.mod-indent-15{width:450px}.mod-indent-16{width:480px}.mod-indent-huge{width:480px}.resourcecontent .mediaplugin_mp3 object{width:600px;height:25px}.resourcecontent audio.mediaplugin_html5audio{width:600px}.resourceimage{max-width:100%}.mediaplugin_mp3 object{width:300px;height:15px}audio.mediaplugin_html5audio{width:300px}.core_media_preview.pagelayout-embedded #content{padding:0}.core_media_preview.pagelayout-embedded #maincontent{height:0}body#page-lib-editor-tinymce-plugins-moodlemedia-preview{min-width:0;padding:0;margin:0;background:0}.dir-rtl .ygtvtn,.dir-rtl .ygtvtm,.dir-rtl .ygtvtmh,.dir-rtl .ygtvtmhh,.dir-rtl .ygtvtp,.dir-rtl .ygtvtph,.dir-rtl .ygtvtphh,.dir-rtl .ygtvln,.dir-rtl .ygtvlm,.dir-rtl .ygtvlmh,.dir-rtl .ygtvlmhh,.dir-rtl .ygtvlp,.dir-rtl .ygtvlph,.dir-rtl .ygtvlphh,.dir-rtl .ygtvdepthcell,.dir-rtl .ygtvok,.dir-rtl .ygtvok:hover,.dir-rtl .ygtvcancel,.dir-rtl .ygtvcancel:hover{width:18px;height:22px;cursor:pointer;background-image:url([[pix:theme|yui2-treeview-sprite-rtl]]);background-repeat:no-repeat}.dir-rtl .ygtvtn{background-position:0 -5600px}.dir-rtl .ygtvtm{background-position:0 -4000px}.dir-rtl .ygtvtmh,.dir-rtl .ygtvtmhh{background-position:0 -4800px}.dir-rtl .ygtvtp{background-position:0 -6400px}.dir-rtl .ygtvtph,.dir-rtl .ygtvtphh{background-position:0 -7200px}.dir-rtl .ygtvln{background-position:0 -1600px}.dir-rtl .ygtvlm{background-position:0 0}.dir-rtl .ygtvlmh,.dir-rtl .ygtvlmhh{background-position:0 -800px}.dir-rtl .ygtvlp{background-position:0 -2400px}.dir-rtl .ygtvlph,.dir-rtl .ygtvlphh{background-position:0 -3200px}.dir-rtl .ygtvdepthcell{background-position:0 -8000px}.dir-rtl .ygtvok{background-position:0 -8800px}.dir-rtl .ygtvok:hover{background-position:0 -8844px}.dir-rtl .ygtvcancel{background-position:0 -8822px}.dir-rtl .ygtvcancel:hover{background-position:0 -8866px}.dir-rtl.yui-skin-sam .yui-panel .hd{text-align:right}.dir-rtl .yui-skin-sam .yui-layout .yui-layout-unit div.yui-layout-bd{text-align:right}.dir-rtl .clearlooks2.ie9 .mceAlert .mceMiddle span,.dir-rtl .clearlooks2 .mceConfirm .mceMiddle span{top:44px}.dir-rtl .o2k7Skin table,.dir-rtl .o2k7Skin tbody,.dir-rtl .o2k7Skin a,.dir-rtl .o2k7Skin img,.dir-rtl .o2k7Skin tr,.dir-rtl .o2k7Skin div,.dir-rtl .o2k7Skin td,.dir-rtl .o2k7Skin iframe,.dir-rtl .o2k7Skin span,.dir-rtl .o2k7Skin *,.dir-rtl .o2k7Skin .mceText,.dir-rtl .o2k7Skin .mceListBox .mceText{text-align:right}.path-rating .ratingtable{width:100%;margin-bottom:1em}.path-rating .ratingtable th.rating{width:100%}.path-rating .ratingtable td.rating,.path-rating .ratingtable td.time{text-align:center;white-space:nowrap}.initialbar a,.initialbar strong{padding-right:3px;padding-left:3px}.moodle-dialogue-base .moodle-dialogue-lightbox{background-color:#AAA}.moodle-dialogue-base .hidden,.moodle-dialogue-base .moodle-dialogue-hidden{display:none}.no-scrolling{overflow:hidden}.moodle-dialogue-base .moodle-dialogue-fullscreen{position:fixed;top:0;right:0;bottom:-50px;left:0}.moodle-dialogue-base .moodle-dialogue-fullscreen .moodle-dialogue-content{overflow:auto}.moodle-dialogue-base .moodle-dialogue-fullscreen .closebutton{width:28px;height:16px;background-size:100%}.moodle-dialogue-base .moodle-dialogue{z-index:600;padding:0;margin:0;background:0;border:0;outline:#000 dotted 0}.moodle-dialogue-base .moodle-dialogue-wrap{margin-top:-3px;margin-left:-3px;background-color:#fff;border:1px solid #ccc;-webkit-border-radius:10px;-moz-border-radius:10px;border-radius:10px;-webkit-box-shadow:5px 5px 20px 0 #666;-moz-box-shadow:5px 5px 20px 0 #666;box-shadow:5px 5px 20px 0 #666}.moodle-dialogue-base .moodle-dialogue-wrap .moodle-dialogue-hd,.moodle-dialogue-base .moodle-dialogue-wrap .moodle-dialogue-hd.yui3-widget-hd{padding:5px;margin:0;font-size:12px;font-weight:normal;letter-spacing:1px;color:#333;text-align:center;text-shadow:1px 1px 1px #fff;background:#ccc;background-color:#ebebeb;background-image:-moz-linear-gradient(top,#fff,#ccc);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#ccc));background-image:-webkit-linear-gradient(top,#fff,#ccc);background-image:-o-linear-gradient(top,#fff,#ccc);background-image:linear-gradient(to bottom,#fff,#ccc);background-repeat:repeat-x;border-bottom:1px solid #bbb;-webkit-border-radius:10px 10px 0 0;-moz-border-radius:10px 10px 0 0;border-radius:10px 10px 0 0;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#ffcccccc',GradientType=0);filter:0}.moodle-dialogue-base .moodle-dialogue-wrap .moodle-dialogue-hd h1{display:inline;padding:0;margin:0;font-size:100%;font-weight:bold}.moodle-dialogue-base .moodle-dialogue-wrap .moodle-dialogue-hd .yui3-widget-buttons{padding:5px}.moodle-dialogue-base .closebutton{display:inline-block;float:right;width:25px;height:15px;padding:0;vertical-align:middle;cursor:pointer;background-image:url([[pix:theme|sprite]]);background-repeat:no-repeat;border-style:none}.dir-rtl .moodle-dialogue-base .moodle-dialogue-wrap .moodle-dialogue-hd .yui3-widget-buttons{right:auto;left:0}.moodle-dialogue-base .moodle-dialogue .moodle-dialogue-bd{padding:1em;font-size:12px;line-height:2em;color:#555}.moodle-dialogue-base .moodle-dialogue-wrap .moodle-dialogue-content{padding:0;background:#FFF}.moodle-dialogue-base .moodle-dialogue-fullscreen .moodle-dialogue-hd{padding:10px;font-size:16px}.moodle-dialogue-base .moodle-dialogue-fullscreen .moodle-dialogue-content{position:absolute;top:0;right:0;bottom:50px;left:0;margin:0;overflow:auto;border:0}.moodle-dialogue-base .moodle-dialogue-fullscreen .moodle-dialogue-hd,.moodle-dialogue-base .moodle-dialogue-fullscreen .moodle-dialogue-wrap{border-radius:0}.moodle-dialogue-confirm .confirmation-dialogue{text-align:center}.moodle-dialogue-confirm .confirmation-dialogue input{text-align:center}.moodle-dialogue-exception .moodle-exception-message{text-align:center}.moodle-dialogue-exception .moodle-exception-param label{font-weight:bold}.moodle-dialogue-exception .param-stacktrace label{background-color:#EEE;border:1px solid #ccc;border-bottom-width:0}.moodle-dialogue-exception .param-stacktrace pre{background-color:#fff;border:1px solid #ccc}.moodle-dialogue-exception .param-stacktrace .stacktrace-file{font-size:11.9px;color:navy}.moodle-dialogue-exception .param-stacktrace .stacktrace-line{font-size:11.9px;color:#b94a48}.moodle-dialogue-exception .param-stacktrace .stacktrace-call{font-size:90%;color:#333;border-bottom:1px solid #eee}.moodle-dialogue-base .moodle-dialogue .moodle-dialogue-content .moodle-dialogue-ft{padding:0;margin:.7em 1em;font-size:12px;text-align:right;background-color:#FFF}.moodle-dialogue-confirm .confirmation-message{margin:.5em 1em}.moodle-dialogue-confirm .confirmation-dialogue input{min-width:80px}.moodle-dialogue-exception .moodle-exception-message{margin:1em}.moodle-dialogue-exception .moodle-exception-param{margin-bottom:.5em}.moodle-dialogue-exception .moodle-exception-param label{width:150px}.moodle-dialogue-exception .param-stacktrace label{display:block;padding:4px 1em;margin:0}.moodle-dialogue-exception .param-stacktrace pre{display:block;height:200px;overflow:auto}.moodle-dialogue-exception .param-stacktrace .stacktrace-file{display:inline-block;margin:4px 0}.moodle-dialogue-exception .param-stacktrace .stacktrace-line{display:inline-block;width:50px;margin:4px 1em}.moodle-dialogue-exception .param-stacktrace .stacktrace-call{padding-bottom:4px;padding-left:25px;margin-bottom:4px}.moodle-dialogue .moodle-dialogue-bd .content-lightbox{top:0;left:0;width:100%;height:100%;padding:10% 0;text-align:center;background-color:white;opacity:.75;filter:alpha(opacity=75)}.moodle-dialogue .tooltiptext{max-height:300px}.moodle-dialogue-base .moodle-dialogue.moodle-dialogue-tooltip{z-index:3001}.moodle-dialogue-base .moodle-dialogue.moodle-dialogue-tooltip .moodle-dialogue-bd{overflow:auto}#page-question-edit.dir-rtl a.container-close{right:auto;left:6px}.chooserdialoguebody,.choosertitle{display:none}.moodle-dialogue.chooserdialogue .moodle-dialogue-content .moodle-dialogue-ft{margin:0}.chooserdialogue .moodle-dialogue-wrap .moodle-dialogue-bd{padding:0;background:#f2f2f2;-webkit-border-bottom-right-radius:10px;border-bottom-right-radius:10px;-webkit-border-bottom-left-radius:10px;border-bottom-left-radius:10px;-moz-border-radius-bottomright:10px;-moz-border-radius-bottomleft:10px}.choosercontainer #chooseform .submitbuttons{padding:.7em 0;text-align:center}@media(max-height:639px){.ios.safari .choosercontainer #chooseform .submitbuttons{padding:45px 0}}.choosercontainer #chooseform .submitbuttons input{min-width:100px;margin:0 .5em}.choosercontainer #chooseform .options{position:relative;border-bottom:1px solid #bbb}.jschooser .choosercontainer #chooseform .alloptions{max-width:20.3em;overflow-x:hidden;overflow-y:auto;-webkit-box-shadow:inset 0 0 30px 0 #ccc;-moz-box-shadow:inset 0 0 30px 0 #ccc;box-shadow:inset 0 0 30px 0 #ccc}.dir-rtl.jschooser .choosercontainer #chooseform .alloptions{max-width:18.3em}.choosercontainer #chooseform .moduletypetitle,.choosercontainer #chooseform .option,.choosercontainer #chooseform .nonoption{padding:0 1.6em 0 1.6em;margin-bottom:0}.choosercontainer #chooseform .moduletypetitle{padding-top:1.2em;padding-bottom:.4em;text-transform:uppercase}.choosercontainer #chooseform .option .typename,.choosercontainer #chooseform .option span.modicon img.icon,.choosercontainer #chooseform .nonoption .typename,.choosercontainer #chooseform .nonoption span.modicon img.icon{padding:0 0 0 .5em}.dir-rtl .choosercontainer #chooseform .option .typename,.dir-rtl .choosercontainer #chooseform .option span.modicon img.icon,.dir-rtl .choosercontainer #chooseform .nonoption .typename,.dir-rtl .choosercontainer #chooseform .nonoption span.modicon img.icon{padding:0 .5em 0 0}.chooserdialogue-course-modchooser .choosercontainer #chooseform .option span.modicon img.icon,.chooserdialogue-course-modchooser .choosercontainer #chooseform .nonoption span.modicon img.icon{width:24px;height:24px}.choosercontainer #chooseform .option input[type=radio],.choosercontainer #chooseform .option span.typename,.choosercontainer #chooseform .option span.modicon{vertical-align:middle}.choosercontainer #chooseform .option label{display:block;padding:.3em 0 .1em 0;border-bottom:1px solid #fff}.choosercontainer #chooseform .nonoption{padding-top:.3em;padding-bottom:.1em;padding-left:2.7em}.dir-rtl .choosercontainer #chooseform .nonoption{padding-right:2.7em;padding-left:0}.choosercontainer #chooseform .subtype{padding:0 1.6em 0 3.2em;margin-bottom:0}.dir-rtl .choosercontainer #chooseform .subtype{padding:0 3.2em 0 1.6em}.choosercontainer #chooseform .subtype .typename{margin:0 0 0 .2em}.dir-rtl .choosercontainer #chooseform .subtype .typename{margin:0 .2em 0 0}.jschooser .choosercontainer #chooseform .instruction,.jschooser .choosercontainer #chooseform .typesummary{position:absolute;top:0;right:0;bottom:0;left:20.3em;display:none;padding:1.6em;margin:0;overflow-x:hidden;overflow-y:auto;line-height:2em;background-color:#fff}.dir-rtl.jschooser .choosercontainer #chooseform .instruction,.dir-rtl.jschooser .choosercontainer #chooseform .typesummary{right:18.5em;left:0;border-right:1px solid grey}.jschooser .choosercontainer #chooseform .instruction,.choosercontainer #chooseform .selected .typesummary{display:block}.choosercontainer #chooseform .selected{background-color:#fff;-webkit-box-shadow:0 0 10px 0 #ccc;-moz-box-shadow:0 0 10px 0 #ccc;box-shadow:0 0 10px 0 #ccc}.section-modchooser-link img.smallicon{padding:3px}.formlistingradio{padding-right:10px;padding-bottom:25px}.formlistinginputradio{float:left}.formlistingmain{min-height:225px}.formlisting{position:relative;padding:1px 19px 14px;margin:15px 0;background-color:white;border:1px solid #DDD;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.formlistingmore{position:absolute;right:-1px;bottom:-1px;padding:3px 7px;font-size:12px;font-weight:bold;color:#9da0a4;cursor:pointer;background-color:whiteSmoke;border:1px solid #ddd;-webkit-border-radius:4px 0 4px 0;-moz-border-radius:4px 0 4px 0;border-radius:4px 0 4px 0}.formlistingall{padding:0;margin:15px 0;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.formlistingrow{top:50%;left:50%;float:left;width:150px;min-height:34px;padding:6px;cursor:pointer;background-color:#f7f7f9;border-right:1px solid #e1e1e8;border-bottom:1px solid;border-left:1px solid #e1e1e8;border-color:#e1e1e8;-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px}body.jsenabled .formlistingradio{display:none}body.jsenabled .formlisting{display:block}table.collection{width:100%;margin-bottom:20px;border:1px solid #ddd;border-collapse:separate;*border-collapse:collapse;border-left:0;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}table.collection th,table.collection td{padding:8px;line-height:20px;text-align:left;vertical-align:top;border-top:1px solid #ddd}table.collection th{font-weight:bold}table.collection thead th{vertical-align:bottom}table.collection caption+thead tr:first-child th,table.collection caption+thead tr:first-child td,table.collection colgroup+thead tr:first-child th,table.collection colgroup+thead tr:first-child td,table.collection thead:first-child tr:first-child th,table.collection thead:first-child tr:first-child td{border-top:0}table.collection tbody+tbody{border-top:2px solid #ddd}table.collection .table{background-color:#fff}table.collection th,table.collection td{border-left:1px solid #ddd}table.collection caption+thead tr:first-child th,table.collection caption+tbody tr:first-child th,table.collection caption+tbody tr:first-child td,table.collection colgroup+thead tr:first-child th,table.collection colgroup+tbody tr:first-child th,table.collection colgroup+tbody tr:first-child td,table.collection thead:first-child tr:first-child th,table.collection tbody:first-child tr:first-child th,table.collection tbody:first-child tr:first-child td{border-top:0}table.collection thead:first-child tr:first-child>th:first-child,table.collection tbody:first-child tr:first-child>td:first-child,table.collection tbody:first-child tr:first-child>th:first-child{-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topleft:4px}table.collection thead:first-child tr:first-child>th:last-child,table.collection tbody:first-child tr:first-child>td:last-child,table.collection tbody:first-child tr:first-child>th:last-child{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-topright:4px}table.collection thead:last-child tr:last-child>th:first-child,table.collection tbody:last-child tr:last-child>td:first-child,table.collection tbody:last-child tr:last-child>th:first-child,table.collection tfoot:last-child tr:last-child>td:first-child,table.collection tfoot:last-child tr:last-child>th:first-child{-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px}table.collection thead:last-child tr:last-child>th:last-child,table.collection tbody:last-child tr:last-child>td:last-child,table.collection tbody:last-child tr:last-child>th:last-child,table.collection tfoot:last-child tr:last-child>td:last-child,table.collection tfoot:last-child tr:last-child>th:last-child{-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px}table.collection tfoot+tbody:last-child tr:last-child td:first-child{-webkit-border-bottom-left-radius:0;border-bottom-left-radius:0;-moz-border-radius-bottomleft:0}table.collection tfoot+tbody:last-child tr:last-child td:last-child{-webkit-border-bottom-right-radius:0;border-bottom-right-radius:0;-moz-border-radius-bottomright:0}table.collection caption+thead tr:first-child th:first-child,table.collection caption+tbody tr:first-child td:first-child,table.collection colgroup+thead tr:first-child th:first-child,table.collection colgroup+tbody tr:first-child td:first-child{-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topleft:4px}table.collection caption+thead tr:first-child th:last-child,table.collection caption+tbody tr:first-child td:last-child,table.collection colgroup+thead tr:first-child th:last-child,table.collection colgroup+tbody tr:first-child td:last-child{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-topright:4px}table.collection tbody>tr:nth-child(odd)>td,table.collection tbody>tr:nth-child(odd)>th{background-color:#f9f9f9}table.collection .name{text-align:left;vertical-align:middle}table.collection .awards{width:10%;text-align:center;vertical-align:middle}table.collection .criteria{width:40%;text-align:left;vertical-align:top}table.collection .badgeimage,table.collection .status{width:15%;text-align:center;vertical-align:middle}table.collection .description{width:25%;text-align:left}table.collection .actions{width:11em;text-align:center;vertical-align:middle}a.criteria-action{float:right;padding:0 3px}div.criteria-description{padding:10px 15px;margin:5px 0;background:none repeat scroll 0 0 #f9f9f9;border:1px solid #EEE}ul.badges{margin:0;list-style:none}.badges li{position:relative;display:inline-block;width:150px;padding-bottom:2em;text-align:center;vertical-align:top}.badges li .badge-name{display:block;padding:5px}.badges li>img{position:absolute}.badges li .badge-image{top:0;left:10px;z-index:1;width:100px;height:100px}.badges li .badge-actions{position:relative}.badges li .expireimage{position:absolute;top:0;left:25px;z-index:10;width:100px;height:100px;opacity:.85}#badge-image{position:relative;display:inline-block;width:20%;min-width:100px;padding:0;margin-top:17px;vertical-align:top;background-color:transparent}#badge-image .expireimage{position:absolute;top:0;left:0;z-index:10;width:100px;height:100px;opacity:.85;filter:alpha(opacity=85)}#badge-image .singlebutton{padding-top:5px}#badge-image .singlebutton input{margin-left:0}.dir-rtl #badge-image{float:right}.dir-rtl #badge-image .expireimage{left:41px}#badge-details{display:inline-block;width:79%}#badge-overview dl,#badge-details dl{margin:0}#badge-overview dl dt,#badge-details dl dt,#badge-overview dl dd,#badge-details dl dd{padding:3px 0;vertical-align:top}#badge-overview dl dt,#badge-details dl dt{display:inline-block;width:20%;min-width:100px;clear:both}#badge-overview dl dd,#badge-details dl dd{display:inline-block;width:79%;margin-left:1%}.badge-profile{vertical-align:top}.connected{color:#468847}.notconnected{color:#b94a48}.connecting{color:#c09853}#page-badges-award .recipienttable tr td{vertical-align:top}#page-badges-award .recipienttable tr td.actions .actionbutton{width:100%;padding:.5em 0;margin:.3em 0}#page-badges-award .recipienttable tr td.existing,#page-badges-award .recipienttable tr td.potential{width:42%}#issued-badge-table .activatebadge{display:inline-block}.statusbox.active{background-color:#dff0d8}.statusbox.inactive{background-color:#fcf8e3}.statusbox{padding:5px;margin-bottom:5px;text-align:center}.statusbox .activatebadge{display:inline-block}.statusbox .activatebadge input[type=submit]{margin:3px}.activatebadge{margin:0;text-align:left;vertical-align:middle}.dir-rtl .activatebadge{text-align:right}img#persona_signin{cursor:pointer}.addcourse{float:right}.invisiblefieldset{display:inline;padding:0;margin:0;border-width:0}.breadcrumb-nav{float:left;margin-bottom:10px}.dir-rtl .breadcrumb-nav{float:right}.breadcrumb-button .singlebutton div{margin-right:0}.breadcrumb-nav .breadcrumb{margin:0}.page-context-header{overflow:hidden}.page-context-header .page-header-image,.page-context-header .page-header-headings{position:relative;display:block}.page-context-header .page-header-image{margin-bottom:1em}.page-context-header .page-header-headings{margin-top:30px;margin-bottom:10px}.page-context-header .page-header-headings h1{display:block}.page-context-header .page-header-headings,.page-context-header .header-button-group{position:relative;line-height:24px;vertical-align:middle}.page-context-header .header-button-group{display:block}.page-context-header .header-button-group a{position:relative;top:-0.4em}.dir-ltr .page-context-header .page-header-image{float:left;margin-right:1em}.dir-ltr .page-context-header .header-button-group{float:right}.dir-rtl .page-context-header .page-header-image{float:right;margin-left:1em}.dir-rtl .page-context-header .header-button-group{right:-15px;float:left}.moodle-actionmenu,.moodle-actionmenu>ul,.moodle-actionmenu>ul>li{display:inline-block}.moodle-actionmenu ul{padding:0;margin:0;list-style-type:none}.moodle-actionmenu .toggle-display,.moodle-actionmenu .menu-action-text{display:none}.jsenabled .moodle-actionmenu[data-enhance]{display:block}.jsenabled .moodle-actionmenu[data-enhance] .menu{display:none}.jsenabled .moodle-actionmenu[data-enhance] .toggle-display{display:inline;opacity:.5;filter:alpha(opacity=50)}.jsenabled .moodle-actionmenu[data-enhance] .toggle-display.textmenu{display:block;padding-right:4px;padding-left:4px;margin-left:4px}.jsenabled .moodle-actionmenu[data-enhance] .toggle-display.textmenu .iconsmall,.jsenabled .moodle-actionmenu[data-enhance] .toggle-display.textmenu .smallicon{padding:8px 4px 0 2px;margin:4px 4px 4px 0;vertical-align:text-bottom}.jsenabled .moodle-actionmenu[data-enhance] .toggle-display.textmenu .caret{margin-top:8px;margin-left:2px;border-top-color:#777}.jsenabled .moodle-actionmenu[data-enhance] .toggle-display.textmenu .caret:hover,.jsenabled .moodle-actionmenu[data-enhance] .toggle-display.textmenu .caret:active{border-top-color:#555}.jsenabled .moodle-actionmenu[data-enhanced] .toggle-display{opacity:1;filter:alpha(opacity=100)}.jsenabled .moodle-actionmenu[data-enhanced] .menu-action-text{display:inline}.jsenabled.dir-rtl .moodle-actionmenu[data-enhance] .toggle-display.textmenu{margin-right:4px;margin-left:initial}.jsenabled.dir-rtl .moodle-actionmenu[data-enhance] .toggle-display.textmenu .caret{margin-right:2px;margin-left:initial}.moodle-actionmenu[data-enhanced].show{position:relative}.moodle-actionmenu[data-enhanced].show .menu{position:absolute;z-index:1000;display:block;text-align:left;background-color:#fff;border:1px solid rgba(0,0,0,0.2);-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;-webkit-box-shadow:5px 5px 20px 0 #666;-moz-box-shadow:5px 5px 20px 0 #666;box-shadow:5px 5px 20px 0 #666}.moodle-actionmenu[data-enhanced].show .menu a{display:block;padding:2px 1em 2px 28px;color:#333}.moodle-actionmenu[data-enhanced].show .menu a:hover{color:#fff;background-color:#0070a8}.moodle-actionmenu[data-enhanced].show .menu a:first-child{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-topleft:4px}.moodle-actionmenu[data-enhanced].show .menu a:last-child{-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-moz-border-radius-bottomright:4px;-moz-border-radius-bottomleft:4px}.moodle-actionmenu[data-enhanced].show .menu a.hidden{display:none}.moodle-actionmenu[data-enhanced].show .menu img{vertical-align:middle}.moodle-actionmenu[data-enhanced].show .menu .iconsmall,.moodle-actionmenu[data-enhanced].show .menu .smallicon{padding:4px;margin:4px 4px 4px -24px}.moodle-actionmenu[data-enhanced].show .menu>li{display:block}.moodle-actionmenu[data-enhanced].show .menu.align-tl-bl{top:100%;left:0;margin-top:4px}.moodle-actionmenu[data-enhanced].show .menu.align-tr-bl{top:100%;right:100%}.moodle-actionmenu[data-enhanced].show .menu.align-bl-bl{bottom:100%;left:0}.moodle-actionmenu[data-enhanced].show .menu.align-br-bl{right:100%;bottom:100%}.moodle-actionmenu[data-enhanced].show .menu.align-tl-br{top:100%;left:100%}.moodle-actionmenu[data-enhanced].show .menu.align-tr-br{top:100%;right:0;margin-top:4px}.moodle-actionmenu[data-enhanced].show .menu.align-bl-br{bottom:100%;left:100%}.moodle-actionmenu[data-enhanced].show .menu.align-br-br{right:0;bottom:100%}.moodle-actionmenu[data-enhanced].show .menu.align-tl-tl{top:0;left:0}.moodle-actionmenu[data-enhanced].show .menu.align-tr-tl{top:0;right:100%;margin-right:4px}.moodle-actionmenu[data-enhanced].show .menu.align-bl-tl{bottom:100%;left:0;margin-bottom:4px}.moodle-actionmenu[data-enhanced].show .menu.align-br-tl{right:100%;bottom:100%}.moodle-actionmenu[data-enhanced].show .menu.align-tl-tr{top:0;left:100%;margin-left:4px}.moodle-actionmenu[data-enhanced].show .menu.align-tr-tr{top:0;right:0}.moodle-actionmenu[data-enhanced].show .menu.align-bl-tr{bottom:100%;left:100%}.moodle-actionmenu[data-enhanced].show .menu.align-br-tr{right:0;bottom:100%;margin-bottom:4px}.moodle-actionmenu[data-enhanced].show.nowrap-items .menu>li{white-space:nowrap}.block .moodle-actionmenu{text-align:right}.dir-rtl .moodle-actionmenu[data-enhanced].show .menu{right:auto;left:0;text-align:right}.dir-rtl .moodle-actionmenu[data-enhanced].show .menu a{padding:2px 28px 2px 1em}.dir-rtl .moodle-actionmenu[data-enhanced].show .menu .iconsmall,.dir-rtl .moodle-actionmenu[data-enhanced].show .menu .smallicon{margin-right:-24px;margin-left:4px}.dir-rtl .moodle-actionmenu[data-enhanced].show .menu.align-tl-bl{right:0;left:auto}.dir-rtl .moodle-actionmenu[data-enhanced].show .menu.align-tr-bl{right:auto;left:100%}.dir-rtl .moodle-actionmenu[data-enhanced].show .menu.align-bl-bl{right:0;left:auto}.dir-rtl .moodle-actionmenu[data-enhanced].show .menu.align-br-bl{right:auto;left:100%}.dir-rtl .moodle-actionmenu[data-enhanced].show .menu.align-tl-br{right:100%;left:auto}.dir-rtl .moodle-actionmenu[data-enhanced].show .menu.align-tr-br{right:auto;left:0}.dir-rtl .moodle-actionmenu[data-enhanced].show .menu.align-bl-br{right:100%;left:auto}.dir-rtl .moodle-actionmenu[data-enhanced].show .menu.align-br-br{right:auto;left:0}.dir-rtl .moodle-actionmenu[data-enhanced].show .menu.align-tl-tl{right:0;left:auto}.dir-rtl .moodle-actionmenu[data-enhanced].show .menu.align-tr-tl{right:auto;left:100%}.dir-rtl .moodle-actionmenu[data-enhanced].show .menu.align-bl-tl{right:0;left:auto}.dir-rtl .moodle-actionmenu[data-enhanced].show .menu.align-br-tl{right:auto;left:100%}.dir-rtl .moodle-actionmenu[data-enhanced].show .menu.align-tl-tr{right:100%;left:auto}.dir-rtl .moodle-actionmenu[data-enhanced].show .menu.align-tr-tr{right:auto;left:0}.dir-rtl .moodle-actionmenu[data-enhanced].show .menu.align-bl-tr{right:100%;left:auto}.dir-rtl .moodle-actionmenu[data-enhanced].show .menu.align-br-tr{right:auto;left:0}.dir-rtl .block .moodle-actionmenu{text-align:right}ul.dragdrop-keyboard-drag li{list-style-type:none}.block-control-actions .moodle-core-dragdrop-draghandle img{width:12px;height:12px}a.disabled:hover,a.disabled{font-style:italic;color:#808080;text-decoration:none;cursor:default}body.lockscroll{height:100%;overflow:hidden}.dir-rtl ul{margin-right:25px;margin-left:0}.progressbar_container{max-width:500px;margin:0 auto}.ie10 .yui3-calendar-header-label{display:inline-block}dd:before,dd:after{display:block;content:" "}dd:after{clear:both}.formtable tbody th{font-weight:normal;text-align:right}.path-admin #assignrole{width:60%;margin-right:auto;margin-left:auto}.path-admin .admintable .leftalign{text-align:left}.dir-rtl.path-admin .admintable .leftalign{text-align:right}.environmenttable p.warn{color:#c09853;background-color:#fcf8e3}.environmenttable .error,.environmenttable span.warn,.environmenttable .ok{display:inline-block;padding:2px 4px;font-size:11.844px;font-weight:bold;line-height:14px;color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);white-space:nowrap;vertical-align:baseline;background-color:#999;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.environmenttable .error:empty,.environmenttable span.warn:empty,.environmenttable .ok:empty{display:none}.environmenttable .error-important,.environmenttable span.warn-important,.environmenttable .ok-important{background-color:#b94a48}.environmenttable .error-important[href],.environmenttable span.warn-important[href],.environmenttable .ok-important[href]{background-color:#953b39}.environmenttable .error-warning,.environmenttable span.warn-warning,.environmenttable .ok-warning{background-color:#f89406}.environmenttable .error-warning[href],.environmenttable span.warn-warning[href],.environmenttable .ok-warning[href]{background-color:#c67605}.environmenttable .error-success,.environmenttable span.warn-success,.environmenttable .ok-success{background-color:#468847}.environmenttable .error-success[href],.environmenttable span.warn-success[href],.environmenttable .ok-success[href]{background-color:#356635}.environmenttable .error-info,.environmenttable span.warn-info,.environmenttable .ok-info{background-color:#3a87ad}.environmenttable .error-info[href],.environmenttable span.warn-info[href],.environmenttable .ok-info[href]{background-color:#2d6987}.environmenttable .error-inverse,.environmenttable span.warn-inverse,.environmenttable .ok-inverse{background-color:#333}.environmenttable .error-inverse[href],.environmenttable span.warn-inverse[href],.environmenttable .ok-inverse[href]{background-color:#1a1a1a}.environmenttable .error{background-color:#b94a48}.environmenttable span.warn{background-color:#f89406}.environmenttable .ok{background-color:#468847}.path-admin .admintable.environmenttable .name,.path-admin .admintable.environmenttable .info,.path-admin #assignrole .admintable .role,.path-admin #assignrole .admintable .userrole,.path-admin #assignrole .admintable .roleholder{white-space:nowrap}.path-admin .incompatibleblockstable td.c0{font-weight:bold}#page-admin-course-category .addcategory{padding:10px}#page-admin-course-index .editcourse{margin:20px auto}#page-admin-course-index .editcourse th,#page-admin-course-index .editcourse td{padding-right:10px;padding-left:10px}.timewarninghidden{display:none}.statusok,.statuswarning,.statusserious,.statuscritical{display:inline-block;padding:2px 4px;font-size:11.844px;font-weight:bold;line-height:14px;color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);white-space:nowrap;vertical-align:baseline;background-color:#999;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.statusok:empty,.statuswarning:empty,.statusserious:empty,.statuscritical:empty{display:none}.statusok-important,.statuswarning-important,.statusserious-important,.statuscritical-important{background-color:#b94a48}.statusok-important[href],.statuswarning-important[href],.statusserious-important[href],.statuscritical-important[href]{background-color:#953b39}.statusok-warning,.statuswarning-warning,.statusserious-warning,.statuscritical-warning{background-color:#f89406}.statusok-warning[href],.statuswarning-warning[href],.statusserious-warning[href],.statuscritical-warning[href]{background-color:#c67605}.statusok-success,.statuswarning-success,.statusserious-success,.statuscritical-success{background-color:#468847}.statusok-success[href],.statuswarning-success[href],.statusserious-success[href],.statuscritical-success[href]{background-color:#356635}.statusok-info,.statuswarning-info,.statusserious-info,.statuscritical-info{background-color:#3a87ad}.statusok-info[href],.statuswarning-info[href],.statusserious-info[href],.statuscritical-info[href]{background-color:#2d6987}.statusok-inverse,.statuswarning-inverse,.statusserious-inverse,.statuscritical-inverse{background-color:#333}.statusok-inverse[href],.statuswarning-inverse[href],.statusserious-inverse[href],.statuscritical-inverse[href]{background-color:#1a1a1a}.statusok{background-color:#468847}.statuswarning{background-color:#c09853}.statusserious{background-color:#f89406}.statuscritical{background-color:#b94a48}#page-admin-report-capability-index #capabilitysearch{width:30em}#page-admin-report-backups-index .backup-error,#page-admin-report-backups-index .backup-unfinished{color:#b94a48}#page-admin-report-backups-index .backup-skipped,#page-admin-report-backups-index .backup-ok,#page-admin-report-backups-index .backup-notyetrun{color:#468847}#page-admin-report-backups-index .backup-warning{color:#c09853}#page-admin-qtypes .disabled,#page-admin-qbehaviours .disabled{color:#999}#page-admin-qtypes #qtypes div,#page-admin-qtypes #qtypes form,#page-admin-qbehaviours #qbehaviours div,#page-admin-qbehaviours #qbehaviours form{display:inline}#page-admin-qtypes #qtypes img.spacer,#page-admin-qbehaviours #qbehaviours img.spacer{width:16px}img.iconsmall{padding:.3em;margin:0}#page-admin-qbehaviours .cell.c3,#page-admin-qtypes .cell.c3{font-size:10.5px}#page-admin-lang .generalbox,#page-admin-course-index .singlebutton,#page-admin-course-index .addcategory,#page-course-index .buttons,#page-course-index-category .buttons,#page-admin-course-category .addcategory,#page-admin-stickyblocks .generalbox,#page-admin-maintenance .buttons,#page-admin-course-index .buttons,#page-admin-course-category .buttons,#page-admin-index .copyright,#page-admin-index .copyrightnotice,#page-admin-index .adminerror .singlebutton,#page-admin-index .adminwarning .singlebutton,#page-admin-index #layout-table .singlebutton{margin-bottom:1em;text-align:center}.path-admin-roles .capabilitysearchui{margin-right:auto;margin-left:auto;text-align:left}#page-admin-roles-define .topfields{margin:1em 0 2em}#page-admin-roles-define .capdefault{background-color:#f5f5f5;border:1px solid #ddd}#page-filter-manage .backlink,.path-admin-roles .backlink{margin-top:1em}#page-admin-roles-explain #chooseuser h3,#page-admin-roles-usersroles .contextname{margin-top:0}#page-admin-roles-explain #chooseusersubmit{margin-top:0;text-align:center}#page-admin-roles-usersroles p{margin:0}#page-admin-roles-override .cell.c1,#page-admin-roles-assign .cell.c3,#page-admin-roles-assign .cell.c1{padding-top:.75em}#page-admin-roles-override .overridenotice,#page-admin-roles-define .definenotice{margin:1em 10% 2em 10%;text-align:left}#notice{width:60%;min-width:220px;margin:auto}#page-admin-index .releasenoteslink,#page-admin-index .adminwarning,#page-admin-index .adminerror{width:60%;min-width:220px;padding:8px 35px 8px 14px;margin:auto;margin-bottom:20px;color:#c09853;text-shadow:0 1px 0 rgba(255,255,255,0.5);background-color:#fcf8e3;border:1px solid #fbeed5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}#page-admin-index .adminerror{color:#b94a48;background-color:#f2dede;border-color:#eed3d7}#page-admin-index .releasenoteslink{color:#3a87ad;background-color:#d9edf7;border-color:#bce8f1}#page-admin-index .adminwarning.availableupdatesinfo .moodleupdateinfo span{display:block}#page-admin-index .updateplugin div,#page-admin-plugins .updateplugin div{margin-bottom:.5em}#page-admin-index .updateplugin .updatepluginconfirmexternal,#page-admin-plugins .updateplugin .updatepluginconfirmexternal{padding:1em;background-color:#f2dede;border:1px solid #eed3d7}#page-admin-user-user_bulk #users .fgroup{white-space:nowrap}#page-admin-report-stats-index .graph{margin-bottom:1em;text-align:center}#page-admin-report-courseoverview-index .graph{margin-bottom:1em;text-align:center}#page-admin-lang .translator{border-style:solid;border-width:1px}.path-admin .roleassigntable{width:100%}.path-admin .roleassigntable td{padding:.2em .3em;vertical-align:top}.path-admin .roleassigntable p{margin:.2em 0;text-align:left}.path-admin .roleassigntable #existingcell,.path-admin .roleassigntable #potentialcell{width:42%}.path-admin .roleassigntable #existingcell p>label:first-child,.path-admin .roleassigntable #potentialcell p>label:first-child{font-weight:bold}.path-admin .roleassigntable #buttonscell{width:16%}.path-admin .roleassigntable #buttonscell #assignoptions{font-size:10.5px}.path-admin .roleassigntable #removeselect_wrapper,.path-admin .roleassigntable #addselect_wrapper{width:100%}.path-admin table.rolecap tr.rolecap th{font-weight:normal;text-align:left}.path-admin.dir-rtl table.rolecap tr.rolecap th{text-align:right}.path-admin .rolecap .hiddenrow{display:none}.path-admin #defineroletable .rolecap .inherit,.path-admin #defineroletable .rolecap .allow,.path-admin #defineroletable .rolecap .prevent,.path-admin #defineroletable .rolecap .prohibit{min-width:3.5em;padding:0;text-align:center}.path-admin .rolecap .cap-name,.path-admin .rolecap .note{display:block;font-size:10.5px;font-weight:normal;white-space:nowrap}.path-admin .rolecap label{display:block;padding:.5em;margin:0;text-align:center}.plugincheckwrapper{width:100%}.environmentbox{margin-top:1em}#mnetconfig table{margin-right:auto;margin-left:auto}.environmenttable .cell{padding:.15em .5em}.environmenttable img.iconhelp{padding-right:.3em}.dir-rtl .environmenttable img.iconhelp{padding-right:0;padding-left:.3em}#trustedhosts .generaltable{width:500px;margin-right:auto;margin-left:auto}#trustedhosts .standard{width:auto}#adminsettings legend{display:none}#adminsettings fieldset.error{margin:.2em 0 .5em 0}#adminsettings fieldset.error legend{display:block}.dir-rtl #admin-spelllanguagelist textarea,#page-admin-setting-editorsettingstinymce.dir-rtl .form-textarea textarea{text-align:left;direction:ltr}.adminsettingsflags{float:right}.dir-rtl .adminsettingsflags{float:left}.adminsettingsflags label{margin-right:7px}.dir-rtl .adminsettingsflags label{margin-left:7px}.form-description{clear:right}.dir-rtl .form-description{clear:left}.form-item .form-setting .form-htmlarea{display:inline;width:640px}.form-item .form-setting .form-htmlarea .htmlarea{display:block;width:640px}.form-item .form-setting .form-multicheckbox ul{padding:0;margin:7px 0 0 0;list-style:none}.form-item .form-setting .defaultsnext{display:inline;margin-right:.5em}.dir-rtl .form-item .form-setting .defaultsnext{margin-right:0;margin-left:.5em}.form-item .form-setting .locked-checkbox{display:inline;margin-right:.2em;margin-left:.5em}.dir-rtl .form-item .form-setting .locked-checkbox{display:inline;margin-right:.5em;margin-left:.2em}.form-item .form-setting .form-password .unmask,.form-item .form-setting .form-defaultinfo{display:inline-block}.form-item .pathok,.form-item .patherror{margin-left:.5em}#admin-emoticons td input{width:8em}#admin-emoticons td.c0 input{width:4em}#adminthemeselector .selectedtheme td.c0{border:1px solid #000;border-right-width:0}#adminthemeselector .selectedtheme td.c1{border:1px solid #000;border-left-width:0}.admin_colourpicker,.admin_colourpicker_preview{display:none}.jsenabled .admin_colourpicker_preview{display:inline}.jsenabled .admin_colourpicker{display:block;width:410px;height:102px;margin-bottom:10px}.admin_colourpicker .loadingicon{margin-left:auto;vertical-align:middle}.admin_colourpicker .colourdialogue{float:left;border:1px solid #000}.admin_colourpicker .previewcolour{margin-left:301px;border:1px solid #000}.admin_colourpicker .currentcolour{margin-left:301px;border:1px solid #000;border-top-width:0}.dir-rtl .form-item .form-setting,.dir-rtl .form-item .form-label,.dir-rtl .form-item .form-description,.dir-rtl.path-admin .roleassigntable p{text-align:right}#page-admin-index #notice .checkforupdates{text-align:center}#plugins-check-info{margin:1em;text-align:center}#plugins-check .displayname .pluginicon{width:16px}#plugins-check .status-new .status{background-color:#dff0d8}#page-admin-index .adminwarning.availableupdatesinfo .moodleupdateinfo.maturity200 .info.release,#plugins-check .status-upgrade .status,#plugins-check .status-delete .status{background-color:#d9edf7}#plugins-control-panel .extension .source,#page-admin-index .adminwarning.availableupdatesinfo .moodleupdateinfo.maturity100 .info.release,#page-admin-index .adminwarning.availableupdatesinfo .moodleupdateinfo.maturity150 .info.release,.pluginupdateinfo.maturity100,.pluginupdateinfo.maturity150,#plugins-check .extension .source{background-color:#fcf8e3}#page-admin-index .adminwarning.availableupdatesinfo .moodleupdateinfo.maturity50 .info.release,.pluginupdateinfo.maturity50,#plugins-check .requires-failed,#plugins-check .missingfromdisk .displayname,#plugins-check .status-missing .status,#plugins-check .status-downgrade .status{background-color:#f2dede}#plugins-control-panel .statusmsg{padding:3px;background-color:#eee;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}#plugins-control-panel .status-missing .pluginname{background-color:#f2dede}#plugins-control-panel .status-missing .statusmsg{color:#b94a48}#plugins-control-panel .status-new .pluginname{background-color:#dff0d8}#plugins-control-panel .status-new .statusmsg{color:#468847}#plugins-control-panel .disabled .availability{background-color:#eee}#plugins-check .standard .source,#plugins-check .status-nodb .status,#plugins-check .status-uptodate .status,#plugins-check .requires-ok{color:#999}#plugins-check .requires ul{margin:0;font-size:10.5px}#plugins-check .status .pluginupdateinfo{padding:5px 10px;margin:10px;background-color:#d9edf7;-webkit-border-radius:10px;-moz-border-radius:10px;border-radius:10px}#plugins-check .status .pluginupdateinfo span,#plugins-check .status .pluginupdateinfo a{padding-right:1em}#page-admin-index .upgradepluginsinfo{text-align:center}#page-admin-plugins .checkforupdates{margin:0 auto 1em;text-align:center}#plugins-control-panel .requiredby,#plugins-control-panel .pluginname .componentname{font-size:11.9px;color:#999}#plugins-control-panel .pluginname .componentname{margin-left:22px}#plugins-overview-filter .filter-item,#plugins-overview-panel .info{padding:0 10px}#page-admin-index .adminwarning.availableupdatesinfo .moodleupdateinfo .separator,#plugins-check .status .pluginupdateinfo .separator,#page-admin-plugins .separator{border-left:1px dotted #999}#plugins-control-panel .msg td{text-align:center}#plugins-overview-filter,#plugins-overview-panel{margin:1em auto;text-align:center}#plugins-overview-panel .info.updatable{margin-left:10px;font-weight:bold;background-color:#d9edf7;-webkit-border-radius:10px;-moz-border-radius:10px;border-radius:10px}#plugins-overview-filter .filter-item.active{font-weight:bold}#plugins-control-panel .displayname img.icon{padding-top:0;padding-bottom:0}#plugins-control-panel .uninstall a{color:#b94a48}#plugins-control-panel .notes .pluginupdateinfo{padding:5px 10px;margin:10px;background-color:#d9edf7;-webkit-border-radius:10px;-moz-border-radius:10px;border-radius:10px}#plugins-control-panel .notes .pluginupdateinfo span,#plugins-control-panel .notes .pluginupdateinfo a{padding-right:1em}.dir-rtl #plugins-check .pluginupdateinfo{text-align:center;direction:ltr}.dir-rtl #plugins-check .rootdir,.dir-rtl #plugins-check .requires-ok{text-align:left;direction:ltr}#page-admin-mnet-peers .box.deletedhosts{margin-bottom:1em;font-size:11.9px}#page-admin-mnet-peers .mform .deletedhostinfo{padding:4px;margin-bottom:5px;background-color:#f2dede;border:2px solid #eed3d7}#core-cache-plugin-summaries table,#core-cache-store-summaries table{width:100%}#core-cache-lock-summary table,#core-cache-definition-summaries table,#core-cache-mode-mappings table{margin:0 auto}#core-cache-store-summaries .default-store td{font-style:italic}#core-cache-rescan-definitions,#core-cache-mode-mappings .edit-link,#core-cache-lock-summary .new-instance{margin-top:.5em;text-align:center}.tinymcesubplugins img.icon{padding-top:0;padding-bottom:0}.maintenancewarning{position:fixed;right:0;bottom:0;z-index:1;padding:3px 1em;overflow:hidden;text-align:center}.maintenancewarning.error{font-weight:bold;color:#b94a48;background-color:#f2dede;border:2px solid #eed3d7}.maintenancewarning.warning{color:#c09853;background-color:#fcf8e3;border:2px solid #fbeed5}#adminsettings .form-overridden{color:#3a87ad;background-color:#d9edf7}.calendar_event_course{background-color:#ffd3bd}.calendar_event_global{background-color:#d6f8cd}.calendar_event_group{background-color:#fee7ae}.calendar_event_user{background-color:#dce7ec}.path-calendar .calendartable{width:100%}.path-calendar .calendartable th,.path-calendar .calendartable td{width:14%;text-align:center;vertical-align:top;border:0}.path-calendar .calendar-controls .previous,.path-calendar .calendar-controls .next,.path-calendar .calendar-controls .current{display:block;float:left;width:12%}.path-calendar .calendar-controls .previous{text-align:left}.path-calendar .calendar-controls .current{width:76%;text-align:center}.path-calendar .calendar-controls .next{text-align:right}.path-calendar .filters table{width:100%;border-collapse:separate;border-spacing:2px}.path-calendar .cal_courses_flt{float:left}.path-calendar .cal_courses_flt label{margin-right:.45em}.path-calendar .maincalendar{padding:0;vertical-align:top}.path-calendar .maincalendar .bottom{padding:5px 0 0 0;text-align:center}.path-calendar .maincalendar .heightcontainer{position:relative;height:100%}.path-calendar .maincalendar .calendarmonth{width:98%;margin:10px auto}.path-calendar .maincalendar .calendarmonth ul{margin:0}.path-calendar .maincalendar .calendarmonth ul li{margin-top:4px;list-style-type:none}.path-calendar .maincalendar .calendarmonth td{height:5em}.path-calendar .maincalendar .calendar-controls .previous,.path-calendar .maincalendar .calendar-controls .next{width:30%}.path-calendar .maincalendar .calendar-controls .current{width:39.95%}.path-calendar .maincalendar .controls{width:98%;margin:10px auto}.path-calendar .maincalendar .calendar_event_course,.path-calendar .maincalendar .calendar_event_global,.path-calendar .maincalendar .calendar_event_group,.path-calendar .maincalendar .calendar_event_user{border-style:solid;border-width:1px 1px 1px 12px}.path-calendar .maincalendar .calendar_event_course{border-color:#ffd3bd}.path-calendar .maincalendar .calendar_event_global{border-color:#d6f8cd}.path-calendar .maincalendar .calendar_event_group{border-color:#fee7ae}.path-calendar .maincalendar .calendar_event_user{border-color:#dce7ec}.path-calendar .maincalendar .calendar-event-panel{background-color:#eee;border:2px solid #eee}.path-calendar .maincalendar .calendar-event-panel .yui3-overlay-content{padding:19px;background-color:#fdfdfd;border:1px solid #e3e3e3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.path-calendar .maincalendar .calendar-controls .current{font-family:inherit;font-size:25px;font-weight:bold;line-height:1.2;color:inherit}.path-calendar .maincalendar .calendartable td,.path-calendar .maincalendar .calendartable li{padding:5px}.path-calendar .maincalendar .calendartable li{padding-left:10px;text-align:left}.path-calendar .maincalendar .header{overflow:hidden}.path-calendar .maincalendar .header .buttons{float:right}.path-calendar .maincalendar .eventlist{margin:0}.path-calendar .maincalendar .eventlist .event{position:relative;width:92%;padding:20px 4%;margin-bottom:20px;list-style-type:none;background-color:#fdfdfd;border:1px solid #e3e3e3;border-collapse:separate;border-spacing:0;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.path-calendar .maincalendar .eventlist .event>img{float:left;padding-top:3px}.path-calendar .maincalendar .eventlist .event .name{float:left;margin:0;font-size:17.5px;font-weight:200;line-height:24px}.path-calendar .maincalendar .eventlist .event .name,.path-calendar .maincalendar .eventlist .event .course{margin-bottom:5px}.path-calendar .maincalendar .eventlist .event .date{float:right}.path-calendar .maincalendar .eventlist .event .course,.path-calendar .maincalendar .eventlist .event .subscription{float:left;clear:left}.path-calendar .maincalendar .eventlist .event .side{width:22px}.path-calendar .maincalendar .eventlist .event .description{padding:5px;clear:both;background-color:#fff}.path-calendar .maincalendar .eventlist .event .description .commands{position:absolute;top:0;right:0;margin:3px}.path-calendar .maincalendar .eventlist .event .commands{position:absolute;top:2px;right:2px}.path-calendar .maincalendar .eventlist .event .commands a{margin:0 3px}.dir-rtl.path-calendar .cal_courses_flt{float:right}.dir-rtl.path-calendar .cal_courses_flt label{margin-right:0;margin-left:.45em}.dir-rtl.path-calendar .maincalendar .calendar_event_course,.dir-rtl.path-calendar .maincalendar .calendar_event_global,.dir-rtl.path-calendar .maincalendar .calendar_event_group,.dir-rtl.path-calendar .maincalendar .calendar_event_user{border-right-width:12px;border-left-width:1px}.dir-rtl.path-calendar .maincalendar .calendar-controls .next{text-align:left}.dir-rtl.path-calendar .maincalendar .calendar-controls .previous{text-align:right}.dir-rtl.path-calendar .maincalendar .calendartable td,.dir-rtl.path-calendar .maincalendar .calendartable li{text-align:right}.dir-rtl.path-calendar .maincalendar .calendartable li{padding-right:10px;padding-left:5px}.dir-rtl.path-calendar .maincalendar .header .buttons{float:left}.dir-rtl.path-calendar .maincalendar .eventlist .event>img{float:right}.dir-rtl.path-calendar .maincalendar .eventlist .event .name{float:right}.dir-rtl.path-calendar .maincalendar .eventlist .event .date{float:left}.dir-rtl.path-calendar .maincalendar .eventlist .event .description .commands{right:inherit;left:0}.dir-rtl.path-calendar .maincalendar .eventlist .event .course,.dir-rtl.path-calendar .maincalendar .eventlist .event .subscription{float:right;clear:right}.dir-rtl.path-calendar .maincalendar .eventlist .event .commands{right:inherit;left:2px}#page-calendar-export .indent{padding-left:20px}.block .minicalendar{width:100%;max-width:280px;margin:0 auto}.block .minicalendar th,.block .minicalendar td{padding:2px;font-size:.8em;text-align:center}.block .minicalendar td.weekend{color:#999}.block .minicalendar td a{display:block;width:100%;height:100%}.block .minicalendar caption{font-size:inherit;font-weight:inherit;line-height:inherit;text-align:center}.block .calendar-event-panel{background-color:#eee;border:1px solid #eee}.block .calendar-event-panel .yui3-overlay-content{padding:19px;background-color:#fdfdfd;border:1px solid #e3e3e3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.block .calendar-event-panel .yui3-overlay-content h2.eventtitle{font-size:18px;line-height:1.2}.block .calendar-event-panel .yui3-overlay-content .eventcontent img{padding-right:5px}.block .calendar-controls .previous,.block .calendar-controls .current,.block .calendar-controls .next{display:block;float:left}.block .calendar-controls .previous{width:12%;text-align:left}.block .calendar-controls .current{width:76%;text-align:center}.block .calendar-controls .next{width:12%;text-align:right}.block .calendar_filters ul{margin:0;list-style:none}.block .calendar_filters li{margin-bottom:.2em}.block .calendar_filters li span img{padding:0 .2em}.block .calendar_filters .eventname{padding-left:.2em}.block .content h3.eventskey{margin-top:.5em}.dir-rtl .block .calendar_filters .eventname{padding-right:.2em;padding-left:0}.dir-rtl .block .calendar-event-panel .yui3-overlay-content .eventcontent img{padding-right:0;padding-left:5px}.ical-link{padding:0 5px;font-size:10px;font-weight:bold;color:#fff;background-color:#f60;border-top:1px solid #f93;border-right:1px solid #013;border-bottom:1px solid #013;border-left:1px solid #f93}.ical-link:hover,.ical-link:active,.ical-link:focus,.ical-link:visited{color:#fff;text-decoration:none}@media(min-width:768px){#page-calender-view .container-fluid{min-width:1024px}}.section_add_menus{text-align:right}.dir-rtl .section_add_menus{text-align:left}.section_add_menus .horizontal div,.section_add_menus .horizontal form{display:inline}.section_add_menus optgroup{font-style:italic;font-weight:normal}.section_add_menus .urlselect{margin-left:.4em}.dir-rtl .section_add_menus .urlselect{margin-right:.4em;margin-left:0}.section_add_menus .urlselect select{margin-left:.2em}.dir-rtl .section_add_menus .urlselect select{margin-right:.2em;margin-left:0}.section_add_menus .urlselect img.iconhelp{padding:0;margin:0;vertical-align:text-bottom}.sitetopic ul.section{margin:0}.course-content ul.section{margin:1em}.section .spinner{width:16px;height:16px}.section .activity .spinner{position:absolute;left:100%;vertical-align:text-bottom}.section .activity .editing_move{position:absolute;top:0;left:0}.section .activity .mod-indent-outer{padding-left:32px}.section .activity .actions{position:absolute;top:0;right:0}.section .activity .contentwithoutlink,.section .activity .activityinstance{display:table-cell;min-width:40%;min-height:2em;padding-right:4px}.section .activity .contentwithoutlink .dimmed img.activityicon,.section .activity .activityinstance .dimmed img.activityicon{opacity:.5;filter:alpha(opacity=50)}.section .label .contentwithoutlink,.section .label .activityinstance{display:block;height:inherit;padding-right:32px}.section .label .mod-indent-outer{display:block;padding-left:24px}.section .filler{display:inline-block;width:16px;height:16px;padding:.3em}.section .activity.editor_displayed a.editing_title,.section .activity.editor_displayed .moodle-actionmenu{display:none}.section .activity.editor_displayed div.activityinstance{padding-right:initial}.section .activity.editor_displayed div.activityinstance input{padding-top:initial;padding-bottom:initial;margin-bottom:initial;vertical-align:text-bottom}.dir-rtl .section .activity .spinner{right:100%;left:auto}.dir-rtl .section .activity .mod-indent-outer{padding-right:32px;padding-left:initial}.dir-rtl .section .activity .actions{right:auto;left:0}.dir-rtl .section .activity .contentwithoutlink,.dir-rtl .section .activity .activityinstance{padding-right:initial;padding-left:4px}.dir-rtl .section .activity .editing_move{right:0;left:auto}.dir-rtl .section .activity.editor_displayed div.activityinstance{padding-left:initial}.activity img.activityicon{margin-right:6px;vertical-align:text-bottom}.dir-rtl .section .activity img.activityicon{margin-right:0;margin-left:6px}.section .activity .activityinstance,.section .activity .activityinstance div{display:inline-block}.editing .section .activity .contentwithoutlink,.editing .section .activity .activityinstance{padding-right:200px}.dir-rtl.editing .section .activity .contentwithoutlink,.dir-rtl.editing .section .activity .activityinstance{padding-right:0;padding-left:200px}.editing_show+.editing_assign,.editing_hide+.editing_assign{margin-left:20px}.section .activity .commands{display:inline;white-space:nowrap}.section .activity.modtype_label.label{padding:.2em;font-weight:normal}.section li.activity{padding:.2em;clear:both}.section .activity .activityinstance .groupinglabel{padding-left:30px}.dir-rtl .section .activity .activityinstance .groupinglabel{padding-right:30px}.section .activity .availabilityinfo,.section .activity .contentafterlink{margin-top:.5em;margin-left:30px}.dir-rtl .section .activity .availabilityinfo,.dir-rtl .section .activity .contentafterlink{margin-right:30px;margin-left:0}.section .activity .contentafterlink p{margin:.5em 0}.editing .section .activity:hover,.editing .section .activity.action-menu-shown{background-color:#eee}.course-content .current{background-color:#d9edf7}.course-content .section-summary{margin-top:5px;list-style:none;border:1px solid #ddd}.course-content .section-summary .section-title{margin:2px 5px 10px 5px}.course-content .section-summary .summarytext{margin:2px 5px 2px 5px}.course-content .section-summary .section-summary-activities .activity-count{display:inline-block;margin:3px;font-size:11.9px;color:#999;white-space:nowrap}.course-content .section-summary .summary{margin-top:5px}.course-content .single-section{margin-top:1em}.course-content .single-section .section-navigation{display:block;padding:.5em;margin-bottom:-0.5em}.course-content .single-section .section-navigation .title{clear:both;font-size:108%;font-weight:bold}.course-content .single-section .section-navigation .mdl-left{float:left;margin-right:1em;font-weight:normal}.dir-rtl .course-content .single-section .section-navigation .mdl-left{float:right}.course-content .single-section .section-navigation .mdl-left .larrow{margin-right:.1em}.course-content .single-section .section-navigation .mdl-right{float:right;margin-left:1em;font-weight:normal}.dir-rtl .course-content .single-section .section-navigation .mdl-right{float:left}.course-content .single-section .section-navigation .mdl-right .rarrow{margin-left:.1em}.course-content .single-section .section-navigation .mdl-bottom{margin-top:0}.course-content ul li.section.main{margin-top:0;border-bottom:2px solid #ddd}.course-content ul li.section.hidden{opacity:.5}.course-content ul.topics li.section .content,.course-content ul.weeks li.section .content{padding:0;margin-right:20px;margin-left:20px}.course-content{margin-top:0}.course-content ul.topics li.section{padding-bottom:20px}.course-content ul.topics li.section .summary{margin-left:25px}.path-course-view .completionprogress{margin-left:25px}.path-course-view .completionprogress{position:relative;display:block;float:right;height:20px}#page-site-index .subscribelink{text-align:right}#site-news-forum h2,#frontpage-course-list h2,#frontpage-category-names h2,#frontpage-category-combo h2{margin-bottom:9px}.path-course-view a.reduce-sections{padding-left:.2em}.path-course-view .subscribelink{text-align:right}.path-course-view .unread{margin-left:30px}.dir-rtl.path-course-view .unread{margin-right:30px}.path-course-view .block.drag .header{cursor:move}.path-course-view .completionprogress{text-align:right}.dir-rtl.path-course-view .completionprogress{text-align:left}.path-course-view .single-section .completionprogress{margin-right:5px}.path-course-view .section .summary{line-height:normal}.path-site li.activity>div,.path-course-view li.activity>div{position:relative;padding:0 16px 0 0}.dir-rtl.path-site li.activity>div,.dir-rtl.path-course-view li.activity>div{position:relative;padding:0 0 0 16px}.path-course-view li.activity span.autocompletion img{vertical-align:text-bottom}.path-course-view li.activity form.togglecompletion img{max-width:none}.path-course-view li.activity form.togglecompletion .ajaxworking{position:absolute;top:3px;right:22px;width:16px;height:16px;background:url([[pix:i/ajaxloader]]) no-repeat}.dir-rtl.path-course-view .completionprogress{float:none}.dir-rtl.path-course-view li.activity form.togglecompletion .ajaxworking{right:-22px}li.section.hidden span.commands a.editing_hide,li.section.hidden span.commands a.editing_show{cursor:default}ul.weeks h3.sectionname{white-space:nowrap}.editing ul.weeks h3.sectionname{white-space:normal}.single-section h3.sectionname{clear:both;text-align:center}.section img.movetarget{width:80px;height:16px}input.titleeditor{width:330px;vertical-align:text-bottom}span.editinstructions{position:absolute;top:0;z-index:9999;padding:.1em .4em;margin-top:-22px;margin-left:30px;font-size:11.9px;line-height:16px;color:#3a87ad;text-decoration:none;background-color:#d9edf7;border:1px solid #bce8f1;-webkit-box-shadow:2px 2px 5px 1px #ccc;-moz-box-shadow:2px 2px 5px 1px #ccc;box-shadow:2px 2px 5px 1px #ccc}#dndupload-status{position:fixed;left:0;z-index:1;width:40%;padding:6px;margin:0 30%;color:#3a87ad;text-align:center;background:#d9edf7;border:1px solid #bce8f1;-webkit-border-radius:8px;-moz-border-radius:8px;border-radius:8px;-webkit-box-shadow:2px 2px 5px 1px #ccc;-moz-box-shadow:2px 2px 5px 1px #ccc;box-shadow:2px 2px 5px 1px #ccc}.dndupload-preview{padding:.3em;margin-top:.2em;color:#909090;list-style:none;border:1px dashed #909090}.dndupload-preview img.icon{padding:0;vertical-align:text-bottom}.dndupload-progress-outer{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f7f7f7;background-image:-moz-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f5f5f5),to(#f9f9f9));background-image:-webkit-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:-o-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:linear-gradient(to bottom,#f5f5f5,#f9f9f9);background-repeat:repeat-x;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5',endColorstr='#fff9f9f9',GradientType=0);-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.dndupload-progress-inner{float:left;width:0;height:100%;font-size:12px;color:#fff;text-align:center;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#0e90d2;background-image:-moz-linear-gradient(top,#149bdf,#0480be);background-image:-webkit-gradient(linear,0 0,0 100%,from(#149bdf),to(#0480be));background-image:-webkit-linear-gradient(top,#149bdf,#0480be);background-image:-o-linear-gradient(top,#149bdf,#0480be);background-image:linear-gradient(to bottom,#149bdf,#0480be);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff149bdf',endColorstr='#ff0480be',GradientType=0);-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-moz-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-transition:width .6s ease;-moz-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.dndupload-hidden{display:none}#page-course-pending .singlebutton,#page-course-index .singlebutton,#page-course-index-category .singlebutton,#page-course-editsection .singlebutton{text-align:center}#page-admin-course-manage #movecourses td img{margin:0 .22em;vertical-align:text-bottom}#page-admin-course-manage #movecourses td img.icon{padding:0}#coursesearch{margin-top:1em;text-align:center}#page-course-pending .pendingcourserequests{margin-bottom:1em}#page-course-pending .pendingcourserequests .singlebutton{display:inline}#page-course-pending .pendingcourserequests .cell{padding:0 5px}#page-course-pending .pendingcourserequests .cell.c6{white-space:nowrap}.coursebox{padding:5px;margin-bottom:15px;border:1px dotted #ddd;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.coursebox>.info>.coursename a{display:block;padding-left:21px;background-image:url([[pix:moodle|i/course]]);background-position:left .2em;background-repeat:no-repeat}.dir-rtl .coursebox>.info>.coursename a{padding-right:21px;padding-left:0;background-position:right .2em}.coursebox>.info>.coursename,.coursebox .content .teachers,.coursebox .content .courseimage,.coursebox .content .coursefile{float:left;clear:left}.coursebox .content .teachers,.coursebox .content .courseimage,.coursebox .content .coursefile{width:40%}.dir-rtl .coursebox>.info>.coursename,.dir-rtl .coursebox .teachers,.dir-rtl .coursebox .content .courseimage,.dir-rtl .coursebox .content .coursefile{float:right;clear:right}.coursebox>.info>h3.coursename{margin:5px;line-height:1}.coursebox>.info>.coursename{padding:0;margin:5px}.coursebox .content .teachers li{padding:0;margin:0;list-style-type:none}.coursebox .enrolmenticons{float:right;padding:3px 0}.coursebox .moreinfo{float:right;padding:3px 0}.coursebox .enrolmenticons img,.coursebox .moreinfo img{margin:0 .2em}.coursebox .content{clear:both}.coursebox .content .summary,.coursebox .content .coursecat{float:right;width:55%}.coursebox .content .coursecat{clear:right;text-align:right}.coursebox.remotecoursebox .remotecourseinfo{float:left;width:40%}.coursebox .content .courseimage img{max-width:100px;max-height:100px}.coursebox .content .coursecat,.coursebox .content .summary,.coursebox .content .courseimage,.coursebox .content .coursefile,.coursebox .content .teachers,.coursebox.remotecoursebox .remotecourseinfo{padding:0;margin:3px 5px}.coursebox.remotehost>.info>.categoryname a{background-image:url([[pix:moodle|i/mnethost]])}.dir-rtl .coursebox>.info>.categoryname a{padding-right:21px;padding-left:0;background-position:center right}.dir-rtl .coursebox>.info>.categoryname,.dir-rtl .coursebox .teachers,.dir-rtl .coursebox .content .courseimage,.dir-rtl .coursebox .content .coursefile{float:right;clear:right}.dir-rtl .coursebox .enrolmenticons,.dir-rtl .coursebox .moreinfo{float:left}.dir-rtl .coursebox .summary,.dir-rtl .coursebox .coursecat{float:left}.dir-rtl .coursebox .coursecat{clear:left;text-align:left}.coursebox.collapsed{margin-bottom:0}.coursebox.collapsed>.content{display:none}.courses .coursebox.collapsed{padding:5px;border:1px solid #ddd}.courses .coursebox.even{background-color:#f9f9f9}.courses .coursebox:hover,.course_category_tree .courses>.paging.paging-morelink:hover{background-color:#f5f5f5}.course_category_tree .category .numberofcourse{font-size:11.9px}.course_category_tree .controls{visibility:hidden}.course_category_tree .controls div{display:inline;cursor:pointer}.jsenabled .course_category_tree .controls{visibility:visible}.course_category_tree .controls{float:right;margin-bottom:5px;text-align:right}.course_category_tree .controls div{padding-right:2em;font-size:75%}.course_category_tree .category>.info>.categoryname{padding:2px 18px;margin:3px;background-image:url([[pix:moodle|t/collapsed_empty]]);background-position:center left;background-repeat:no-repeat}.dir-rtl .course_category_tree .category>.info>.categoryname{background-image:url([[pix:moodle|t/collapsed_empty_rtl]]);background-position:center right}.course_category_tree .category.with_children>.info>.categoryname{cursor:pointer;background-image:url([[pix:moodle|t/expanded]])}.course_category_tree .category.with_children.collapsed>.info>.categoryname{background-image:url([[pix:moodle|t/collapsed]])}.dir-rtl .course_category_tree .category.with_children.collapsed>.info>.categoryname{background-image:url([[pix:moodle|t/collapsed_rtl]])}.course_category_tree .category.collapsed>.content{display:none}.course_category_tree .category>.info{min-height:20px;min-height:0;padding:19px;padding:0;margin:3px 0;margin-bottom:20px;margin-bottom:3px;clear:both;background-color:#f5f5f5;border:1px solid #e3e3e3;border-color:#e3e3e3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.course_category_tree .category>.info blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}.course_category_tree.frontpage-category-names .category>.info{margin:0;background:0;border:0}.course_category_tree .category>.content{padding-left:16px}.dir-rtl .course_category_tree .category>.content{padding-right:16px;padding-left:0}.course_category_tree .subcategories>.paging,.courses>.paging{padding:5px;margin:0;text-align:center}.courses>.paging.paging-morelink,.course_category_tree .subcategories>.paging.paging-morelink{text-align:left}.course_category_tree .paging.paging-morelink a{font-size:11.9px}.dir-rtl .courses>.paging.paging-morelink,.dir-rtl .course_category_tree .paging.paging-morelink{text-align:right}#page-course-index-category .generalbox.info{padding:5px;margin-bottom:15px;border:1px dotted #ddd;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}#page-course-index-category .categorypicker{margin:10px 0 20px;text-align:center}.section .summary .iconsmall,.section .activity .iconsmall{width:16px;height:16px}.section .editing_title .iconsmall{width:12px;height:12px;padding:4px 8px 0 0;margin:8px 8px 0 0;vertical-align:text-bottom}.section .moodle-actionmenu .iconsmall{width:16px;height:16px;max-width:none!important;padding:4px;vertical-align:text-bottom}.section .moodle-actionmenu[data-enhanced] .menu img{width:12px;height:12px}.dir-rtl .section .editing_title .iconsmall{padding:4px 0 0 8px;margin:8px 0 0 8px}#course-category-listings{margin-bottom:200px;background-color:transparent}#course-category-listings.columns-2>#course-listing>div{position:relative;left:-1px}#course-category-listings.columns-3>#course-listing>div{height:100%}#course-category-listings>div>div{min-height:300px}#course-category-listings>div>div>ul.ml>li:first-child>div{border-top:0}#course-category-listings h3{padding:.4rem .6rem .3rem;margin:0}#course-category-listings h4{padding:.6rem 1rem .5rem;margin:1rem 0 0}#course-category-listings .moodle-actionmenu{white-space:nowrap}#course-category-listings .moodle-actionmenu[data-enhance] .toggle-display img{width:auto}#course-category-listings .moodle-actionmenu[data-enhance] .toggle-display.textmenu{padding-right:4px}#course-category-listings .moodle-actionmenu[data-enhance] .toggle-display.textmenu .caret{margin-top:12px}#course-category-listings .listing-actions{padding:.4rem .3rem .3rem;line-height:2.2em;text-align:center}#course-category-listings .listing-actions>a,#course-category-listings .listing-actions>.moodle-actionmenu{display:inline-block}#course-category-listings .listing-actions>.moodle-actionmenu .menu a{padding-left:1rem}#course-category-listings .listing-actions .moodle-actionmenu:not([data-enhanced]) li{line-height:normal}#course-category-listings .listing-actions .moodle-actionmenu:not([data-enhanced])>.menubar a{display:inline-block;color:inherit}#course-category-listings .listing-actions .moodle-actionmenu:not([data-enhanced])>.menubar a>img{display:none}#course-category-listings .listing-actions .moodle-actionmenu:not([data-enhanced])>.menubar a .caret{display:none}#course-category-listings .listing-actions .moodle-actionmenu:not([data-enhanced])>.menu .menu-action-text{display:inline-block}#course-category-listings ul.ml{margin:1rem 0;list-style:none}#course-category-listings ul.ml ul.ml{margin:0}#course-category-listings li{line-height:2.2em}#course-category-listings li>div:hover{background-color:#f5f5f5}#course-category-listings li .tree-icon{width:12px;margin:2px 6px 0 0;vertical-align:inherit}#course-category-listings li[data-selected='1']>div{background-color:#f9f9f9}#course-category-listings li[data-selected='1']>div:hover{background-color:#f5f5f5}#course-category-listings li .tree-icon{margin-left:0}#course-category-listings li li .tree-icon{margin-left:1em}#course-category-listings li li li .tree-icon{margin-left:2em}#course-category-listings li li li li .tree-icon{margin-left:3em}#course-category-listings li li li li li .tree-icon{margin-left:4em}#course-category-listings li li li li li li .tree-icon{margin-left:4.5em}#course-category-listings li li li li li li li .tree-icon{margin-left:5em}#course-category-listings li li li li li li li li .tree-icon{margin-left:5.5em}#course-category-listings .item-actions{display:inline-block;display:initial;margin-right:1em}#course-category-listings .item-actions>a img,#course-category-listings .item-actions .menubar img{height:12px;padding:0;margin:0 4px;vertical-align:inherit}#course-category-listings .item-actions.show .menu li{line-height:20px}#course-category-listings .item-actions.show .menu img{width:12px;max-width:none}#course-category-listings .item-actions .menu-action-text{vertical-align:inherit}#course-category-listings .listitem>div>.float-left{float:left}#course-category-listings .listitem>div>.float-right{float:right;text-align:right}#course-category-listings .listitem>div .item-actions .action-show{display:none}#course-category-listings .listitem>div .item-actions .action-hide{display:inline}#course-category-listings .listitem>div .without-actions{color:#333}#course-category-listings .listitem>div .idnumber{margin-right:2em;color:#a1a1a8}#course-category-listings .listitem[data-visible="0"]{color:#999}#course-category-listings .listitem[data-visible="0"]>div>a{color:#999}#course-category-listings .listitem[data-visible="0"]>div .item-actions .action-show{display:inline}#course-category-listings .listitem[data-visible="0"]>div .item-actions .action-hide{display:none}#course-category-listings .listitem.highlight{background-color:transparent}#course-category-listings .listitem.highlight>div,#course-category-listings .listitem.highlight>div:hover,#course-category-listings .listitem.highlight[data-selected='1']>div{background-color:#f5f5f5}#course-category-listings #course-listing .listitem .categoryname{display:inline-block;margin-left:1em;color:#a1a1a8}#course-category-listings #course-listing .listitem .coursename{display:inline-block}#course-category-listings #course-listing .listitem>div{padding-left:1rem}#course-category-listings #course-listing>.firstpage .listitem:first-child>div .item-actions .action-moveup,#course-category-listings #course-listing>.lastpage .listitem:last-child>div .item-actions .action-movedown{display:none}#course-category-listings #course-listing .bulk-action-checkbox{margin:-2px 6px 0 0}#course-category-listings #category-listing .listitem.collapsed>ul.ml{display:none}#course-category-listings #category-listing .listitem>div>.ba-checkbox{width:2.2em;padding-top:2px;margin:-1px .5em 0 0;text-align:center}#course-category-listings #category-listing .listitem.highlight>div>.ba-checkbox{background-color:#f5f5f5}#course-category-listings #category-listing .listitem[data-selected='1']>div>.ba-checkbox{padding:0;margin:0 .5em 0 0;background-color:inherit}#course-category-listings #category-listing .listitem:first-child>div .item-actions .action-moveup,#course-category-listings #category-listing .listitem:last-child>div .item-actions .action-movedown{display:none}#course-category-listings #category-listing .course-count{display:inline-block;min-width:3.5em;margin-right:2rem;color:#a1a1a8}#course-category-listings #category-listing .course-count .smallicon{width:12px;margin-left:4px;vertical-align:inherit}#course-category-listings #category-listing .bulk-action-checkbox{margin-right:-3px}#course-category-listings #category-listing .category-listing>ul>.listitem:first-child{position:relative}#course-category-listings #category-listing .category-bulk-actions{position:relative;margin:0 .5em .5em}#course-category-listings .detail-pair{margin:0 1rem;border-bottom:1px solid #ddd}#course-category-listings .detail-pair>*{display:inline-block;line-height:2.2rem}#course-category-listings .detail-pair .pair-key{font-weight:bold;vertical-align:top}#course-category-listings .detail-pair .pair-key span{display:block;margin-right:1rem}#course-category-listings .detail-pair .pair-value select{max-width:100%}#course-category-listings .bulk-actions .detail-pair>*{display:block;width:100%}#course-category-listings .listing-pagination{text-align:center}#course-category-listings .listing-pagination .yui3-button{margin:.4rem .2rem .45rem;font-size:10.4px;background-color:#fff;border:0}#course-category-listings .listing-pagination .yui3-button.active-page{background-color:#e6e6e6}#course-category-listings .listing-pagination-totals{text-align:center}#course-category-listings .listing-pagination-totals.dimmed{margin:.4rem 1rem .45rem;color:#999}#course-category-listings .select-a-category .notifymessage,#course-category-listings .select-a-category .alert{margin:1em}#course-category-listings #course-listing .listitem .drag-handle{display:none}.jsenabled #course-category-listings #course-listing .listitem .drag-handle{display:inline-block;margin:0 6px 0 0;cursor:pointer}.dir-rtl #course-category-listings #category-listing,.dir-rtl #course-category-listings #course-listing{float:right;margin-left:0}.dir-rtl #course-category-listings .listitem>div>.float-left{float:right}.dir-rtl #course-category-listings .listitem>div>.float-right{float:left;text-align:left}.dir-rtl #course-category-listings li .tree-icon{margin:2px 0 0 6px}.dir-rtl #course-category-listings li .tree-icon{margin-right:0}.dir-rtl #course-category-listings li li .tree-icon{margin-right:1em}.dir-rtl #course-category-listings li li li .tree-icon{margin-right:2em}.dir-rtl #course-category-listings li li li li .tree-icon{margin-right:3em}.dir-rtl #course-category-listings li li li li li .tree-icon{margin-right:4em}.dir-rtl #course-category-listings li li li li li li .tree-icon{margin-right:4.5em}.dir-rtl #course-category-listings li li li li li li li .tree-icon{margin-right:5em}.dir-rtl #course-category-listings li li li li li li li li .tree-icon{margin-right:5.5em}.dir-rtl #course-category-listings #category-listing .listitem>div{margin-right:.5em;margin-left:0}.dir-rtl #course-category-listings #category-listing .listitem>div>.ba-checkbox{margin:-1px 0 0 .5em}.dir-rtl #course-category-listings #category-listing .listitem[data-selected='1']>div>.ba-checkbox{margin:0 0 0 .5em}.dir-rtl #course-category-listings #category-listing .course-count{margin-left:2rem}.dir-rtl #course-category-listings #category-listing .course-count .smallicon{margin-right:4px;margin-left:0}.dir-rtl #course-category-listings #category-listing .bulk-action-checkbox{margin-right:0;margin-left:-3px}.dir-rtl #course-category-listings #course-listing{padding-right:24px}.dir-rtl #course-category-listings #course-listing .listitem .idnumber{padding-right:2em;color:#a1a1a8}.dir-rtl #course-category-listings #course-listing .listitem .categoryname{display:inline-block;margin-right:1em;margin-left:0}.dir-rtl #course-category-listings #course-listing .listitem .drag-handle{margin:0 6px 0 6px}.dir-rtl #course-category-listings #course-listing .listitem>div{padding-left:1rem}.dir-rtl #course-category-listings #course-listing .bulk-action-checkbox{margin:-2px 0 0 6px;vertical-align:middle}.dir-rtl #course-category-listings .detail-pair>*{float:right;margin-right:0}.dir-rtl #course-category-listings .detail-pair .pair-key span{margin-right:0;margin-left:0}.dir-rtl #course-category-listings .detail-pair .pair-value{margin-right:.5em}.coursecat-management-header{vertical-align:middle}.coursecat-management-header h2{display:inline-block;text-align:left}.coursecat-management-header>div{display:inline-block;float:right;line-height:40px}.coursecat-management-header>div>div{display:inline-block;margin:10px 0;margin-left:1em}.coursecat-management-header select{max-width:300px;padding:.4em .5em .45em 1em;white-space:nowrap;vertical-align:baseline;cursor:pointer}.coursecat-management-header .view-mode-selector .moodle-actionmenu{display:inline-block;white-space:nowrap}.coursecat-management-header .view-mode-selector .moodle-actionmenu[data-enhanced].show .menu a{padding-left:1em}.dir-rtl .coursecat-management-header h2{text-align:right}.dir-rtl .coursecat-management-header>div{float:left;margin-right:1em;margin-left:0}.course-being-dragged-proxy{padding:0 0 0 4em;color:#0070a8;vertical-align:middle;border:0}.course-being-dragged{opacity:.5;filter:alpha(opacity=50)}@media(min-width:1200px) and (max-width:1600px){#course-category-listings.columns-3{background-color:transparent;border:0}#course-category-listings.columns-3 #category-listing,#course-category-listings.columns-3 #course-listing{width:50%}#course-category-listings.columns-3 #category-listing>div,#course-category-listings.columns-3 #course-listing>div,#course-category-listings.columns-3 #course-detail>div{background-color:transparent}#course-category-listings.columns-3 #course-detail{width:100%;margin-top:1em}}@media(max-width:1199px){#course-category-listings.columns-2,#course-category-listings.columns-3{background-color:transparent;border:0}#course-category-listings.columns-2 #category-listing,#course-category-listings.columns-3 #category-listing,#course-category-listings.columns-2 #course-listing,#course-category-listings.columns-3 #course-listing,#course-category-listings.columns-2 #course-detail,#course-category-listings.columns-3 #course-detail{width:100%;margin:0 0 1em}#course-category-listings.columns-2 #category-listing>div,#course-category-listings.columns-3 #category-listing>div,#course-category-listings.columns-2 #course-listing>div,#course-category-listings.columns-3 #course-listing>div,#course-category-listings.columns-2 #course-detail>div,#course-category-listings.columns-3 #course-detail>div{background-color:transparent}}.filemanager,.filepicker,.file-picker{font-size:11px}.filemanager a,.file-picker a,.filemanager a:hover,.file-picker a:hover{color:#555;text-decoration:none}.filemanager input[type="text"],.file-picker input[type="text"]{width:265px}.filemanager .fp-license td,.file-picker .fp-setlicense td{max-width:265px}.filemanager .fp-license select,.file-picker .fp-setlicense select{max-width:100%}.fp-content-center{display:table-cell;width:100%;height:100%;vertical-align:middle}.fp-content-hidden{visibility:hidden}.yui3-panel-focused{outline:0}#filesskin .yui3-panel-content{display:inline-block;*display:inline;padding-bottom:20px;background:#f2f2f2;border:1px solid #fff;-webkit-border-radius:8px;-moz-border-radius:8px;border-radius:8px;*zoom:1;-webkit-box-shadow:5px 5px 20px 0 #666;-moz-box-shadow:5px 5px 20px 0 #666;box-shadow:5px 5px 20px 0 #666}#filesskin .yui3-widget-hd{padding:5px;font-size:12px;letter-spacing:1px;color:#333;text-align:center;text-shadow:1px 1px 1px #fff;background-color:#ebebeb;background-image:-moz-linear-gradient(top,#fff,#ccc);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#ccc));background-image:-webkit-linear-gradient(top,#fff,#ccc);background-image:-o-linear-gradient(top,#fff,#ccc);background-image:linear-gradient(to bottom,#fff,#ccc);background-repeat:repeat-x;border-bottom:1px solid #bbb;-webkit-border-radius:10px 10px 0 0;-moz-border-radius:10px 10px 0 0;border-radius:10px 10px 0 0;filter:dropshadow(color=#ffffff,offx=1,offy=1);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#ffcccccc',GradientType=0)}.fp-panel-button{display:inline-block;*display:inline;padding:3px 20px 2px 20px;margin:10px;text-align:center;background:#fff;-webkit-border-radius:10px;-moz-border-radius:10px;border-radius:10px;*zoom:1;-webkit-box-shadow:2px 2px 3px .1px #999;-moz-box-shadow:2px 2px 3px .1px #999;box-shadow:2px 2px 3px .1px #999}.moodle-dialogue h3{margin:0;font-size:14px;line-height:20px}.moodle-dialogue-base .filepicker .moodle-dialogue-wrap .moodle-dialogue-bd{padding:0}#filesskin .file-picker.fp-generallayout{position:relative;width:859px;background:#fff;border:1px solid #ccc;-webkit-border-radius:10px;-moz-border-radius:10px;border-radius:10px}.file-picker .fp-repo-area{display:inline-block;*display:inline;float:left;width:180px;height:525px;overflow:auto;border-right:1px solid #bbb;*zoom:1}.dir-rtl .file-picker .fp-repo-area{float:right;border-right:0;border-left:1px solid #bbb}.file-picker .fp-repo-items{float:none;width:auto;margin-left:181px}.moodle-dialogue-fullscreen .file-picker .fp-repo-items{float:left;margin-right:0;margin-left:0}.dir-rtl .file-picker .fp-repo-items{margin-right:181px;margin-left:0}.dir-rtl .moodle-dialogue-fullscreen .file-picker .fp-repo-items{float:right;margin-right:0;margin-left:0}.file-picker .fp-navbar{min-height:40px;overflow:hidden;background:#f2f2f2;border-bottom:1px solid #bbb}.file-picker .fp-navbar .fp-viewbar{margin:4px}.file-picker .fp-content{height:452px;overflow:auto;clear:none;background:#fff}.filepicker.moodle-dialogue-fullscreen .file-picker .fp-content{width:100%}.file-picker .fp-content-loading{display:table;width:100%;height:100%;text-align:center}.file-picker .fp-content .fp-object-container{width:98%;height:98%}.dir-rtl .file-picker .fp-list{text-align:right}.dir-rtl .file-picker .fp-toolbar{padding:4px}.dir-rtl .file-picker .fp-list{text-align:right}.dir-rtl .file-picker .fp-repo-name{display:inline}.dir-rtl .file-picker .fp-pathbar{display:block;text-align:right;border-top:0}.dir-rtl .file-picker div.bd{text-align:right}.dir-rtl #filemenu .yuimenuitemlabel{text-align:right}.dir-rtl .filepicker .yui-layout-unit-left{left:500px}.dir-rtl .filepicker .yui-layout-unit-center{left:0}.dir-rtl .filemanager-toolbar a{padding:0}.file-picker .fp-list{float:left;width:100%;padding:0;margin:0;list-style-type:none}.dir-rtl .file-picker .fp-list{float:left;text-align:right}.file-picker .fp-list .fp-repo a{display:block;padding:.5em .7em}.file-picker .fp-list .fp-repo.active{background:#f2f2f2}.file-picker .fp-list .fp-repo-icon{width:16px;height:16px;padding:0 7px 0 5px}.fp-toolbar{float:left}.dir-rtl .fp-toolbar{float:right}.fp-toolbar.empty{display:none}.dir-rtl .fp-toolbar div.disabled,.fp-toolbar .disabled{display:none}.fp-toolbar div{display:block;float:left;margin-right:4px}.dir-rtl .fp-toolbar