Merge branch 'MDL-41599-master' of git://github.com/ankitagarwal/moodle
authorSam Hemelryk <sam@moodle.com>
Tue, 10 Sep 2013 20:54:26 +0000 (08:54 +1200)
committerSam Hemelryk <sam@moodle.com>
Tue, 10 Sep 2013 20:54:26 +0000 (08:54 +1200)
296 files changed:
admin/index.php
admin/modules.php
admin/settings/badges.php
admin/tool/behat/lang/en/tool_behat.php
auth/cas/auth.php
auth/db/lang/en/auth_db.php
auth/mnet/auth.php
backup/controller/restore_controller.class.php
backup/restore.php
backup/util/includes/restore_includes.php
backup/util/progress/core_backup_display_progress.class.php
backup/util/progress/core_backup_display_progress_if_slow.class.php [new file with mode: 0644]
backup/util/ui/restore_ui.class.php
backup/util/ui/restore_ui_stage.class.php
badges/action.php
badges/criteria.php
badges/edit.php
badges/newbadge.php
blocks/course_overview/locallib.php
blocks/rss_client/styles.css [new file with mode: 0644]
blocks/rss_client/viewfeed.php
blocks/site_main_menu/block_site_main_menu.php
blocks/social_activities/block_social_activities.php
blog/edit.php
blog/renderer.php
cache/classes/definition.php
cache/classes/factory.php
calendar/classes/type_base.php [new file with mode: 0644]
calendar/classes/type_factory.php [new file with mode: 0644]
calendar/tests/calendartype_test.php [new file with mode: 0644]
calendar/tests/calendartype_test_example.php [new file with mode: 0644]
calendar/type/gregorian/classes/structure.php [new file with mode: 0644]
calendar/type/gregorian/lang/en/calendartype_gregorian.php [new file with mode: 0644]
calendar/type/gregorian/version.php [new file with mode: 0644]
course/dnduploadlib.php
course/edit.php
course/edit_form.php
course/editcategory.php
course/externallib.php
course/format/renderer.php
course/lib.php
course/modedit.php
course/moodleform_mod.php
course/renderer.php
course/tests/courselib_test.php
enrol/database/lang/en/enrol_database.php
enrol/database/settings.php
enrol/meta/lib.php
enrol/tests/enrollib_test.php
enrol/yui/rolemanager/rolemanager.js
files/renderer.php
filter/activitynames/filter.php
grade/edit/outcome/edit.php
grade/edit/scale/edit.php
grade/report/grader/lib.php
grade/report/grader/module.js
grade/report/grader/styles.css
group/autogroup_form.php
group/group.php
group/group_form.php
group/grouping_form.php
group/import_form.php
index.php
install/lang/bs/admin.php [new file with mode: 0644]
install/lang/bs/error.php [new file with mode: 0644]
install/lang/bs/install.php
install/lang/bs/moodle.php [new file with mode: 0644]
install/lang/ckb/langconfig.php
install/lang/cy/error.php
install/lang/cy/install.php
install/lang/en/install.php
install/lang/es_mx/install.php
install/lang/hi/admin.php
install/lang/ja/install.php
install/lang/nl/error.php
install/lang/no/error.php
lang/en/admin.php
lang/en/cache.php
lang/en/calendar.php
lang/en/group.php
lang/en/plugin.php
lib/adminlib.php
lib/badgeslib.php
lib/behat/classes/util.php
lib/classes/component.php
lib/classes/event/manager.php
lib/completionlib.php
lib/conditionlib.php
lib/coursecatlib.php
lib/cronlib.php
lib/datalib.php
lib/db/caches.php
lib/db/install.xml
lib/db/upgrade.php
lib/editor/atto/plugins/title/lang/en/atto_title.php
lib/editor/atto/plugins/title/lib.php
lib/editor/atto/plugins/title/version.php
lib/editor/atto/plugins/title/yui/build/moodle-atto_title-button/moodle-atto_title-button-debug.js
lib/editor/atto/plugins/title/yui/build/moodle-atto_title-button/moodle-atto_title-button-min.js
lib/editor/atto/plugins/title/yui/build/moodle-atto_title-button/moodle-atto_title-button.js
lib/editor/atto/plugins/title/yui/src/button/js/button.js
lib/editor/atto/styles.css
lib/enrollib.php
lib/filelib.php
lib/filestorage/file_packer.php
lib/filestorage/file_progress.php [new file with mode: 0644]
lib/filestorage/stored_file.php
lib/filestorage/tests/zip_packer_test.php
lib/filestorage/zip_archive.php
lib/filestorage/zip_packer.php
lib/form/dateselector.php
lib/form/datetimeselector.php
lib/form/editor.php
lib/formslib.php
lib/grouplib.php
lib/modinfolib.php
lib/moodlelib.php
lib/outputrenderers.php
lib/pluginlib.php
lib/setuplib.php
lib/simplepie/moodle_simplepie.php
lib/tests/behat/behat_hooks.php
lib/tests/cronlib_test.php [new file with mode: 0644]
lib/tests/datalib_test.php
lib/tests/grouplib_test.php
lib/tests/modinfolib_test.php
lib/upgrade.txt
login/index.php
mod/assign/locallib.php
mod/assign/tests/locallib_test.php
mod/assignment/index.php
mod/book/delete.php
mod/book/edit.php
mod/book/edit_form.php
mod/book/styles.css
mod/book/tool/importhtml/import_form.php
mod/book/tool/importhtml/index.php
mod/book/tool/print/index.php
mod/book/tool/print/locallib.php
mod/book/tool/print/print.css
mod/book/view.php
mod/chat/gui_basic/index.php
mod/chat/index.php
mod/chat/report.php
mod/chat/styles.css
mod/chat/view.php
mod/data/edit.php
mod/data/export.php
mod/data/index.php
mod/data/lang/en/data.php
mod/data/lib.php
mod/data/locallib.php
mod/data/preset.php
mod/data/renderer.php
mod/data/styles.css
mod/data/templates.php
mod/data/view.php
mod/folder/edit.php
mod/folder/index.php
mod/forum/classes/post_form.php
mod/forum/discuss.php
mod/forum/index.php
mod/forum/lib.php
mod/forum/post.php
mod/forum/search.php
mod/forum/upgrade.txt
mod/forum/view.php
mod/glossary/approve.php
mod/glossary/backup/moodle2/restore_glossary_activity_task.class.php
mod/glossary/db/log.php
mod/glossary/edit.php
mod/glossary/editcategories.php
mod/glossary/index.php
mod/glossary/lang/en/glossary.php
mod/glossary/lib.php
mod/glossary/tabs.php
mod/glossary/view.php
mod/lesson/import.php
mod/lesson/index.php
mod/lesson/lesson.php
mod/lesson/report.php
mod/page/backup/moodle1/lib.php
mod/page/index.php
mod/page/lang/en/page.php
mod/page/lib.php
mod/page/mod_form.php
mod/page/settings.php
mod/page/tests/generator/lib.php
mod/page/view.php
mod/resource/backup/moodle1/lib.php
mod/resource/backup/moodle2/backup_resource_activity_task.class.php
mod/resource/index.php
mod/resource/lang/en/resource.php
mod/resource/lib.php
mod/resource/locallib.php
mod/resource/mod_form.php
mod/resource/settings.php
mod/resource/tests/generator/lib.php
mod/scorm/lang/en/scorm.php
mod/scorm/locallib.php
mod/scorm/renderer.php [new file with mode: 0644]
mod/scorm/report/basic/report.php
mod/scorm/report/default.php
mod/scorm/report/interactions/report.php
mod/scorm/report/objectives/report.php
mod/scorm/report/reportlib.php
mod/scorm/report/userreport.php [new file with mode: 0644]
mod/scorm/report/userreportinteractions.php [new file with mode: 0644]
mod/scorm/report/userreporttabs.php [new file with mode: 0644]
mod/scorm/report/userreporttracks.php [new file with mode: 0644]
mod/scorm/styles.css
mod/scorm/userreport.php [deleted file]
mod/survey/index.php
mod/survey/lang/en/survey.php
mod/survey/lib.php
mod/survey/report.php
mod/survey/save.php
mod/survey/view.php
mod/url/backup/moodle1/lib.php
mod/url/index.php
mod/url/lang/en/url.php
mod/url/lib.php
mod/url/locallib.php
mod/url/mod_form.php
mod/url/settings.php
mod/wiki/editors/html.php
mod/wiki/editors/wiki_editor.php
mod/wiki/files.php
mod/wiki/filesedit.php
mod/wiki/filesedit_form.php
mod/wiki/index.php
mod/wiki/pagelib.php
mod/wiki/renderer.php
mod/wiki/styles.css
mod/workshop/submission.php
question/format/blackboard_six/tests/blackboardformatpool_test.php
question/format/blackboard_six/tests/blackboardsixformatqti_test.php
report/participation/index.php
report/progress/index.php
rss/file.php
tag/edit.php
tag/index.php
theme/afterburner/style/afterburner_styles.css
theme/anomaly/style/base.css
theme/anomaly/style/general.css
theme/arialist/style/core.css
theme/base/style/core.css
theme/base/style/course.css
theme/base/style/grade.css
theme/base/style/user.css
theme/bootstrapbase/config.php
theme/bootstrapbase/less/moodle.less
theme/bootstrapbase/less/moodle/blocks.less
theme/bootstrapbase/less/moodle/core.less
theme/bootstrapbase/less/moodle/course.less
theme/bootstrapbase/less/moodle/forms.less
theme/bootstrapbase/less/moodle/grade.less [deleted file]
theme/bootstrapbase/less/moodle/modules.less
theme/bootstrapbase/less/moodle/undo.less
theme/bootstrapbase/less/moodle/user.less
theme/bootstrapbase/style/moodle.css
theme/bootstrapbase/upgrade.txt [new file with mode: 0644]
theme/boxxie/style/core.css
theme/brick/style/colors.css
theme/brick/style/core.css
theme/canvas/style/admin.css
theme/canvas/style/blocks.css
theme/canvas/style/core.css
theme/canvas/style/mods.css
theme/canvas/style/text.css
theme/clean/config.php
theme/formal_white/style/core.css
theme/formal_white/style/course.css
theme/formal_white/style/formal_white.css
theme/fusion/style/core.css
theme/leatherbound/style/core.css
theme/magazine/style/colors.css
theme/magazine/style/core.css
theme/mymobile/jquery/custom131.js
theme/mymobile/style/core.css
theme/nimble/style/core.css
theme/serenity/style/core.css
theme/sky_high/style/core.css
theme/splash/style/core.css
theme/splash/style/pagelayout.css
theme/standard/style/blocks.css
theme/standard/style/core.css
theme/standard/style/course.css
theme/standard/style/css3.css
theme/upgrade.txt
user/editlib.php
user/profile/field/datetime/define.class.php
user/profile/field/datetime/field.class.php
user/profile/field/datetime/lang/en/profilefield_datetime.php
user/profile/field/datetime/version.php
version.php

index 466832c..1f186b6 100644 (file)
@@ -66,6 +66,16 @@ if (empty($_GET['cache']) and empty($_POST['cache']) and empty($_GET['sesskey'])
 }
 
 require('../config.php');
+
+// Invalidate the cache of version.php in any circumstances to help core_component
+// detecting if the version has changed and component cache should be reset.
+if (function_exists('opcache_invalidate')) {
+    opcache_invalidate($CFG->dirroot . '/version.php', true);
+}
+// Make sure the component cache gets rebuilt if necessary, any method that
+// indirectly calls the protected init() method is good here.
+core_component::get_core_subsystems();
+
 require_once($CFG->libdir.'/adminlib.php');    // various admin-only functions
 require_once($CFG->libdir.'/upgradelib.php');  // general upgrade/install related functions
 require_once($CFG->libdir.'/pluginlib.php');   // available updates notifications
index 8eab4bc..c8f69d4 100644 (file)
                    SET visibleold=visible, visible=0
                  WHERE module=?";
         $DB->execute($sql, array($module->id));
-        // clear the course modinfo cache for courses
-        // where we just uninstalld something
-        $sql = "UPDATE {course}
-                   SET modinfo=''
-                 WHERE id IN (SELECT DISTINCT course
+        // Increment course.cacherev for courses where we just made something invisible.
+        // This will force cache rebuilding on the next request.
+        increment_revision_number('course', 'cacherev',
+                "id IN (SELECT DISTINCT course
                                 FROM {course_modules}
-                               WHERE visibleold=1 AND module=?)";
-        $DB->execute($sql, array($module->id));
+                               WHERE visibleold=1 AND module=?)",
+                array($module->id));
         admin_get_root(true, false);  // settings not required - only pages
     }
 
         }
         $DB->set_field("modules", "visible", "1", array("id"=>$module->id)); // Show main module
         $DB->set_field('course_modules', 'visible', '1', array('visibleold'=>1, 'module'=>$module->id)); // Get the previous saved visible state for the course module.
-        // clear the course modinfo cache for courses
-        // where we just made something visible
-        $sql = "UPDATE {course}
-                   SET modinfo = ''
-                 WHERE id IN (SELECT DISTINCT course
+        // Increment course.cacherev for courses where we just made something visible.
+        // This will force cache rebuilding on the next request.
+        increment_revision_number('course', 'cacherev',
+                "id IN (SELECT DISTINCT course
                                 FROM {course_modules}
-                               WHERE visible=1 AND module=?)";
-        $DB->execute($sql, array($module->id));
+                               WHERE visible=1 AND module=?)",
+                array($module->id));
         admin_get_root(true, false);  // settings not required - only pages
     }
 
index 10994fc..276582c 100644 (file)
@@ -32,6 +32,7 @@ if (($hassiteconfig || has_any_capability(array(
             'moodle/badges:createbadge',
             'moodle/badges:manageglobalsettings',
             'moodle/badges:awardbadge',
+            'moodle/badges:configurecriteria',
             'moodle/badges:configuremessages',
             'moodle/badges:configuredetails',
             'moodle/badges:deletebadge'), $systemcontext))) {
@@ -74,6 +75,7 @@ if (($hassiteconfig || has_any_capability(array(
                 'moodle/badges:viewawarded',
                 'moodle/badges:createbadge',
                 'moodle/badges:awardbadge',
+                'moodle/badges:configurecriteria',
                 'moodle/badges:configuremessages',
                 'moodle/badges:configuredetails',
                 'moodle/badges:deletebadge'
index 9f426cc..a67581d 100644 (file)
@@ -38,6 +38,7 @@ $string['stepsdefinitionscontains'] = 'Contains';
 $string['stepsdefinitionsfilters'] = 'Steps definitions';
 $string['stepsdefinitionstype'] = 'Type';
 $string['theninfo'] = 'Then. Checkings to ensure the outcomes are the expected ones';
+$string['unknownexceptioninfo'] = 'There was a problem with Selenium or the browser, try to upgrade Selenium to the latest version. Error: ';
 $string['viewsteps'] = 'Filter';
 $string['wheninfo'] = 'When. Actions that provokes an event';
 $string['wrongbehatsetup'] = 'Something is wrong with behat setup, ensure:<ul>
index 6d1d582..634e32b 100644 (file)
@@ -137,7 +137,7 @@ class auth_plugin_cas extends auth_plugin_ldap {
             // test pgtIou parameter for proxy mode (https connection
             // in background from CAS server to the php server)
             if ($authCAS != 'CAS' && !isset($_GET['pgtIou'])) {
-                $PAGE->set_url('/auth/cas/auth.php');
+                $PAGE->set_url('/login/index.php');
                 $PAGE->navbar->add($CASform);
                 $PAGE->set_title("$site->fullname: $CASform");
                 $PAGE->set_heading($site->fullname);
index e2e295c..678f11b 100644 (file)
@@ -35,13 +35,13 @@ $string['auth_dbfieldpass'] = 'Name of the field containing passwords';
 $string['auth_dbfieldpass_key'] = 'Password field';
 $string['auth_dbfielduser'] = 'Name of the field containing usernames';
 $string['auth_dbfielduser_key'] = 'Username field';
-$string['auth_dbhost'] = 'The computer hosting the database server.';
+$string['auth_dbhost'] = 'The computer hosting the database server. Use a system DSN entry if using ODBC.';
 $string['auth_dbhost_key'] = 'Host';
 $string['auth_dbchangepasswordurl_key'] = 'Password-change URL';
 $string['auth_dbinsertuser'] = 'Inserted user {$a->name} id {$a->id}';
 $string['auth_dbinsertuserduplicate'] = 'Error inserting user {$a->username} - user with this username was already created through \'{$a->auth}\' plugin.';
 $string['auth_dbinsertusererror'] = 'Error inserting user {$a}';
-$string['auth_dbname'] = 'Name of the database itself';
+$string['auth_dbname'] = 'Name of the database itself. Leave empty if using an ODBC DSN.';
 $string['auth_dbname_key'] = 'DB name';
 $string['auth_dbpass'] = 'Password matching the above username';
 $string['auth_dbpass_key'] = 'Password';
index 2f626bd..e21d77a 100644 (file)
@@ -766,26 +766,17 @@ class auth_plugin_mnet extends auth_plugin_base {
             }
             $mnethostlogssql = "
             SELECT
-                mhostlogs.remoteid, mhostlogs.time, mhostlogs.userid, mhostlogs.ip,
-                mhostlogs.course, mhostlogs.module, mhostlogs.cmid, mhostlogs.action,
-                mhostlogs.url, mhostlogs.info, mhostlogs.username, c.fullname as coursename,
-                c.modinfo
+                l.id as remoteid, l.time, l.userid, l.ip, l.course, l.module, l.cmid,
+                l.action, l.url, l.info, u.username
             FROM
-                (
-                    SELECT
-                        l.id as remoteid, l.time, l.userid, l.ip, l.course, l.module, l.cmid,
-                        l.action, l.url, l.info, u.username
-                    FROM
-                        {user} u
-                        INNER JOIN {log} l on l.userid = u.id
-                    WHERE
-                        u.mnethostid = ?
-                        AND l.id > ?
-                    ORDER BY remoteid ASC
-                    LIMIT 500
-                ) mhostlogs
-                INNER JOIN {course} c on c.id = mhostlogs.course
-            ORDER by mhostlogs.remoteid ASC";
+                {user} u
+                INNER JOIN {log} l on l.userid = u.id
+            WHERE
+                u.mnethostid = ?
+                AND l.id > ?
+                AND l.course IS NOT NULL
+            ORDER by l.id ASC
+            LIMIT 500";
 
             $mnethostlogs = $DB->get_records_sql($mnethostlogssql, array($mnethostid, $mnet_request->response['last log id']));
 
@@ -796,18 +787,18 @@ class auth_plugin_mnet extends auth_plugin_base {
             $processedlogs = array();
 
             foreach($mnethostlogs as $hostlog) {
-                // Extract the name of the relevant module instance from the
-                // course modinfo if possible.
-                if (!empty($hostlog->modinfo) && !empty($hostlog->cmid)) {
-                    $modinfo = unserialize($hostlog->modinfo);
-                    unset($hostlog->modinfo);
-                    $modulearray = array();
-                    foreach($modinfo as $module) {
-                        $modulearray[$module->cm] = $module->name;
+                try {
+                    // Get impersonalised course information. If it is cached there will be no DB queries.
+                    $modinfo = get_fast_modinfo($hostlog->course, -1);
+                    $hostlog->coursename = $modinfo->get_course()->fullname;
+                    if (!empty($hostlog->cmid) && isset($modinfo->cms[$hostlog->cmid])) {
+                        $hostlog->resource_name = $modinfo->cms[$hostlog->cmid]->name;
+                    } else {
+                        $hostlog->resource_name = '';
                     }
-                    $hostlog->resource_name = $modulearray[$hostlog->cmid];
-                } else {
-                    $hostlog->resource_name = '';
+                } catch (moodle_exception $e) {
+                    // Course not found
+                    continue;
                 }
 
                 $processedlogs[] = array (
index 9d98df6..00b36e0 100644 (file)
@@ -363,6 +363,9 @@ class restore_controller extends backup implements loggable {
         if ($this->status != backup::STATUS_NEED_PRECHECK) {
             throw new restore_controller_exception('cannot_precheck_wrong_status', $this->status);
         }
+        // Basic/initial prevention against time/memory limits
+        set_time_limit(1 * 60 * 60); // 1 hour for 1 course initially granted
+        raise_memory_limit(MEMORY_EXTRA);
         $this->precheck = restore_prechecks_helper::execute_prechecks($this, $droptemptablesafter);
         if (!array_key_exists('errors', $this->precheck)) { // No errors, can be executed
             $this->set_status(backup::STATUS_AWAITING);
index a31501c..6040d4b 100644 (file)
@@ -43,7 +43,6 @@ if ($stage & restore_ui::STAGE_CONFIRM + restore_ui::STAGE_DESTINATION) {
     }
 }
 
-$outcome = $restore->process();
 $heading = $course->fullname;
 
 $PAGE->set_title($heading.': '.$restore->get_stage_name());
@@ -52,6 +51,15 @@ $PAGE->navbar->add($restore->get_stage_name());
 
 $renderer = $PAGE->get_renderer('core','backup');
 echo $OUTPUT->header();
+
+// Prepare a progress bar which can display optionally during long-running
+// operations while setting up the UI.
+$slowprogress = new core_backup_display_progress_if_slow();
+// Depending on the code branch above, $restore may be a restore_ui or it may
+// be a restore_ui_independent_stage. Either way, this function exists.
+$restore->set_progress_reporter($slowprogress);
+$outcome = $restore->process();
+
 if (!$restore->is_independent() && $restore->enforce_changed_dependencies()) {
     debugging('Your settings have been altered due to unmet dependencies', DEBUG_DEVELOPER);
 }
index 78cd24b..c22faef 100644 (file)
@@ -63,6 +63,7 @@ require_once($CFG->dirroot . '/backup/util/loggers/output_indented_logger.class.
 require_once($CFG->dirroot . '/backup/util/progress/core_backup_progress.class.php');
 require_once($CFG->dirroot . '/backup/util/progress/core_backup_null_progress.class.php');
 require_once($CFG->dirroot . '/backup/util/progress/core_backup_display_progress.class.php');
+require_once($CFG->dirroot . '/backup/util/progress/core_backup_display_progress_if_slow.class.php');
 require_once($CFG->dirroot . '/backup/util/factories/backup_factory.class.php');
 require_once($CFG->dirroot . '/backup/util/factories/restore_factory.class.php');
 require_once($CFG->dirroot . '/backup/util/helper/backup_helper.class.php');
index 62696cb..f893b43 100644 (file)
@@ -95,6 +95,11 @@ class core_backup_display_progress extends core_backup_progress {
         echo html_writer::end_div();
     }
 
+    /**
+     * When progress is updated, updates the bar.
+     *
+     * @see core_backup_progress::update_progress()
+     */
     public function update_progress() {
         // If finished...
         if (!$this->is_in_progress_section()) {
diff --git a/backup/util/progress/core_backup_display_progress_if_slow.class.php b/backup/util/progress/core_backup_display_progress_if_slow.class.php
new file mode 100644 (file)
index 0000000..ee9511e
--- /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/>.
+
+/**
+ * Progress handler that uses a standard Moodle progress bar to display
+ * progress. Same as core_backup_display_progress, but the bar does not
+ * appear until a certain time has elapsed, and disappears automatically
+ * after it finishes.
+ *
+ * The bar can be re-used, i.e. if you end all sections it will disappear,
+ * but if you start all sections, a new bar will be output.
+ *
+ * @package core_backup
+ * @copyright 2013 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class core_backup_display_progress_if_slow extends core_backup_display_progress {
+    /**
+     * @var int Waits this many seconds before displaying progress bar
+     */
+    const DEFAULT_DISPLAY_DELAY = 5;
+
+    /**
+     * @var int Number in the next id to use
+     */
+    private static $nextid = 1;
+
+    /**
+     * @var string HTML id for containing div
+     */
+    protected $id;
+
+    /**
+     * @var int Time at which the progress bar should display (if it isn't yet)
+     */
+    protected $starttime;
+
+    /**
+     * Constructs the progress reporter. This will not output HTML just yet,
+     * until the required delay time expires.
+     *
+     * @param int $delay Delay time (default 5 seconds)
+     */
+    public function __construct($delay = self::DEFAULT_DISPLAY_DELAY) {
+        // Set start time based on delay.
+        $this->starttime = time() + $delay;
+        parent::__construct(false);
+    }
+
+    /**
+     * Adds a div around the parent display so it can be hidden.
+     *
+     * @see core_backup_display_progress::start_html()
+     */
+    public function start_html() {
+        $this->id = 'core_backup_display_progress_if_slow' . self::$nextid;
+        self::$nextid++;
+        echo html_writer::start_div('', array('id' => $this->id));
+        parent::start_html();
+    }
+
+    /**
+     * When progress is updated, after a certain time, starts actually displaying
+     * the progress bar.
+     *
+     * @see core_backup_progress::update_progress()
+     */
+    public function update_progress() {
+        // If we haven't started yet, consider starting.
+        if ($this->starttime) {
+            if (time() > $this->starttime) {
+                $this->starttime = 0;
+            } else {
+                // Do nothing until start time.
+                return;
+            }
+        }
+
+        // We have started, so handle as default.
+        parent::update_progress();
+    }
+
+    /**
+     * Finishes parent display then closes div and hides it.
+     *
+     * @see core_backup_display_progress::end_html()
+     */
+    public function end_html() {
+        parent::end_html();
+        echo html_writer::end_div();
+        echo html_writer::script('document.getElementById("' . $this->id .
+                '").style.display = "none"');
+    }
+}
index bf80a96..251cbc1 100644 (file)
@@ -50,6 +50,11 @@ class restore_ui extends base_ui {
      */
     protected $stage = null;
 
+    /**
+     * @var core_backup_progress Progress indicator (where there is no controller)
+     */
+    protected $progressreporter = null;
+
     /**
      * String mappings to the above stages
      * @var array
@@ -127,6 +132,38 @@ class restore_ui extends base_ui {
     public function get_restoreid() {
         return $this->controller->get_restoreid();
     }
+
+    /**
+     * Gets the progress reporter object in use for this restore UI.
+     *
+     * IMPORTANT: This progress reporter is used only for UI progress that is
+     * outside the restore controller. The restore controller has its own
+     * progress reporter which is used for progress during the main restore.
+     * Use the restore controller's progress reporter to report progress during
+     * a restore operation, not this one.
+     *
+     * This extra reporter is necessary because on some restore UI screens,
+     * there are long-running tasks even though there is no restore controller
+     * in use.
+     *
+     * @return core_backup_null_progress
+     */
+    public function get_progress_reporter() {
+        if (!$this->progressreporter) {
+            $this->progressreporter = new core_backup_null_progress();
+        }
+        return $this->progressreporter;
+    }
+
+    /**
+     * Sets the progress reporter that will be returned by get_progress_reporter.
+     *
+     * @param core_backup_progress $progressreporter Progress reporter
+     */
+    public function set_progress_reporter(core_backup_progress $progressreporter) {
+        $this->progressreporter = $progressreporter;
+    }
+
     /**
      * Executes the restore plan
      * @return bool
index a7464bf..327722e 100644 (file)
@@ -93,10 +93,48 @@ abstract class restore_ui_stage extends base_ui_stage {
  * no use for the restore controller.
  */
 abstract class restore_ui_independent_stage {
+    /**
+     * @var core_backup_progress Optional progress reporter
+     */
+    private $progressreporter;
+
     abstract public function __construct($contextid);
     abstract public function process();
     abstract public function display(core_backup_renderer $renderer);
     abstract public function get_stage();
+
+    /**
+     * Gets the progress reporter object in use for this restore UI stage.
+     *
+     * IMPORTANT: This progress reporter is used only for UI progress that is
+     * outside the restore controller. The restore controller has its own
+     * progress reporter which is used for progress during the main restore.
+     * Use the restore controller's progress reporter to report progress during
+     * a restore operation, not this one.
+     *
+     * This extra reporter is necessary because on some restore UI screens,
+     * there are long-running tasks even though there is no restore controller
+     * in use. There is a similar function in restore_ui. but that class is not
+     * used on some stages.
+     *
+     * @return core_backup_null_progress
+     */
+    public function get_progress_reporter() {
+        if (!$this->progressreporter) {
+            $this->progressreporter = new core_backup_null_progress();
+        }
+        return $this->progressreporter;
+    }
+
+    /**
+     * Sets the progress reporter that will be returned by get_progress_reporter.
+     *
+     * @param core_backup_progress $progressreporter Progress reporter
+     */
+    public function set_progress_reporter(core_backup_progress $progressreporter) {
+        $this->progressreporter = $progressreporter;
+    }
+
     /**
      * Gets an array of progress bar items that can be displayed through the restore renderer.
      * @return array Array of items for the progress bar
@@ -142,11 +180,18 @@ abstract class restore_ui_independent_stage {
  *
  * This is the first stage, it is independent.
  */
-class restore_ui_stage_confirm extends restore_ui_independent_stage {
+class restore_ui_stage_confirm extends restore_ui_independent_stage implements file_progress {
+
     protected $contextid;
     protected $filename = null;
     protected $filepath = null;
     protected $details;
+
+    /**
+     * @var bool True if we have started reporting progress
+     */
+    protected $startedprogress = false;
+
     public function __construct($contextid) {
         $this->contextid = $contextid;
         $this->filename = required_param('filename', PARAM_FILE);
@@ -168,7 +213,35 @@ class restore_ui_stage_confirm extends restore_ui_independent_stage {
         $this->filepath = restore_controller::get_tempdir_name($this->contextid, $USER->id);
 
         $fb = get_file_packer();
-        return ($fb->extract_to_pathname("$CFG->tempdir/backup/".$this->filename, "$CFG->tempdir/backup/$this->filepath/"));
+        $result = $fb->extract_to_pathname("$CFG->tempdir/backup/".$this->filename,
+                "$CFG->tempdir/backup/$this->filepath/", null, $this);
+
+        // If any progress happened, end it.
+        if ($this->startedprogress) {
+            $this->get_progress_reporter()->end_progress();
+        }
+        return $result;
+    }
+
+    /**
+     * Implementation for file_progress interface to display unzip progress.
+     *
+     * @param int $progress Current progress
+     * @param int $max Max value
+     */
+    public function progress($progress = file_progress::INDETERMINATE, $max = file_progress::INDETERMINATE) {
+        $reporter = $this->get_progress_reporter();
+
+        // Start tracking progress if necessary.
+        if (!$this->startedprogress) {
+            $reporter->start_progress('extract_file_to_dir',
+                    ($max == file_progress::INDETERMINATE) ? core_backup_progress::INDETERMINATE : $max);
+            $this->startedprogress = true;
+        }
+
+        // Pass progress through to whatever handles it.
+        $reporter->progress(
+                ($progress == file_progress::INDETERMINATE) ? core_backup_progress::INDETERMINATE : $progress);
     }
 
     /**
@@ -475,8 +548,9 @@ class restore_ui_stage_schema extends restore_ui_stage {
         if ($this->stageform === null) {
             $form = new restore_schema_form($this, $PAGE->url);
             $tasks = $this->ui->get_tasks();
-            $content = '';
             $courseheading = false;
+
+            $allsettings = array();
             foreach ($tasks as $task) {
                 if (!($task instanceof restore_root_task)) {
                     if (!$courseheading) {
@@ -484,13 +558,11 @@ class restore_ui_stage_schema extends restore_ui_stage {
                         $form->add_heading('coursesettings', get_string('coursesettings', 'backup'));
                         $courseheading = true;
                     }
-                    // First add each setting
-                    foreach ($task->get_settings() as $setting) {
-                        $form->add_setting($setting, $task);
-                    }
-                    // The add all the dependencies
+                    // Put each setting into an array of settings to add. Adding
+                    // a setting individually is a very slow operation, so we add
+                    // them all in a batch later on.
                     foreach ($task->get_settings() as $setting) {
-                        $form->add_dependencies($setting);
+                        $allsettings[] = array($setting, $task);
                     }
                 } else if ($this->ui->enforce_changed_dependencies()) {
                     // Only show these settings if dependencies changed them.
@@ -505,6 +577,15 @@ class restore_ui_stage_schema extends restore_ui_stage {
                     }
                 }
             }
+
+            // Actually add all the settings that we put in the array.
+            $form->add_settings($allsettings);
+
+            // Add the dependencies for all the settings.
+            foreach ($allsettings as $settingtask) {
+                $form->add_dependencies($settingtask[0]);
+            }
+
             $this->stageform = $form;
         }
         return $this->stageform;
@@ -523,7 +604,7 @@ class restore_ui_stage_schema extends restore_ui_stage {
 class restore_ui_stage_review extends restore_ui_stage {
     /**
      * Constructs the stage
-     * @param backup_ui $ui
+     * @param restore_ui $ui
      */
     public function __construct($ui, array $params=null) {
         $this->stage = restore_ui::STAGE_REVIEW;
@@ -562,7 +643,11 @@ class restore_ui_stage_review extends restore_ui_stage {
             $content = '';
             $courseheading = false;
 
-            foreach ($this->ui->get_tasks() as $task) {
+            $progress = $this->ui->get_progress_reporter();
+            $tasks = $this->ui->get_tasks();
+            $progress->start_progress('initialise_stage_form', count($tasks));
+            $done = 1;
+            foreach ($tasks as $task) {
                 if ($task instanceof restore_root_task) {
                     // If its a backup root add a root settings heading to group nicely
                     $form->add_heading('rootsettings', get_string('rootsettings', 'backup'));
@@ -575,7 +660,10 @@ class restore_ui_stage_review extends restore_ui_stage {
                 foreach ($task->get_settings() as $setting) {
                     $form->add_fixed_setting($setting, $task);
                 }
+                // Update progress.
+                $progress->progress($done++);
             }
+            $progress->end_progress();
             $this->stageform = $form;
         }
         return $this->stageform;
index ae38fa7..f6f9524 100644 (file)
@@ -95,7 +95,11 @@ if ($copy) {
     require_capability('moodle/badges:createbadge', $context);
 
     $cloneid = $badge->make_clone();
-    redirect(new moodle_url('/badges/edit.php', array('id' => $cloneid, 'action' => 'details')));
+    // If a user can edit badge details, they will be redirected to the edit page.
+    if (has_capability('moodle/badges:configuredetails', $context)) {
+        redirect(new moodle_url('/badges/edit.php', array('id' => $cloneid, 'action' => 'details')));
+    }
+    redirect(new moodle_url('/badges/overview.php', array('id' => $cloneid)));
 }
 
 if ($activate) {
index 9bfd389..7a1e0e4 100644 (file)
@@ -40,6 +40,8 @@ $badge = new badge($badgeid);
 $context = $badge->get_context();
 $navurl = new moodle_url('/badges/index.php', array('type' => $badge->type));
 
+require_capability('moodle/badges:configurecriteria', $context);
+
 if ($badge->type == BADGE_TYPE_COURSE) {
     if (empty($CFG->badges_allowcoursebadges)) {
         print_error('coursebadgesdisabled', 'badges');
@@ -66,7 +68,6 @@ $emsg = optional_param('emsg', '', PARAM_TEXT);
 
 if ((($update == BADGE_CRITERIA_AGGREGATION_ALL) || ($update == BADGE_CRITERIA_AGGREGATION_ANY))) {
     require_sesskey();
-    require_capability('moodle/badges:configurecriteria', $context);
     $obj = new stdClass();
     $obj->id = $badge->criteria[BADGE_CRITERIA_TYPE_OVERALL]->id;
     $obj->method = $update;
index 1c91562..b0a4783 100644 (file)
@@ -41,7 +41,11 @@ $badge = new badge($badgeid);
 $context = $badge->get_context();
 $navurl = new moodle_url('/badges/index.php', array('type' => $badge->type));
 
-require_capability('moodle/badges:configuredetails', $context);
+if ($action == 'message') {
+    require_capability('moodle/badges:configuremessages', $context);
+} else {
+    require_capability('moodle/badges:configuredetails', $context);
+}
 
 if ($badge->type == BADGE_TYPE_COURSE) {
     if (empty($CFG->badges_allowcoursebadges)) {
index 91c316c..756ef91 100644 (file)
@@ -98,7 +98,11 @@ if ($form->is_cancelled()) {
 
     $newbadge = new badge($newid);
     badges_process_badge_image($newbadge, $form->save_temp_file('image'));
-    redirect(new moodle_url('/badges/criteria.php', array('id' => $newid)));
+    // If a user can configure badge criteria, they will be redirected to the criteria page.
+    if (has_capability('moodle/badges:configurecriteria', $PAGE->context)) {
+        redirect(new moodle_url('/badges/criteria.php', array('id' => $newid)));
+    }
+    redirect(new moodle_url('/badges/overview.php', array('id' => $newid)));
 }
 
 echo $OUTPUT->header();
index 71267b6..439282e 100644 (file)
@@ -134,7 +134,7 @@ function block_course_overview_get_sorted_courses() {
 
     $limit = block_course_overview_get_max_user_courses();
 
-    $courses = enrol_get_my_courses('id, shortname, fullname, modinfo, sectioncache');
+    $courses = enrol_get_my_courses();
     $site = get_site();
 
     if (array_key_exists($site->id,$courses)) {
diff --git a/blocks/rss_client/styles.css b/blocks/rss_client/styles.css
new file mode 100644 (file)
index 0000000..1ec870d
--- /dev/null
@@ -0,0 +1,9 @@
+/* RSS Feeds
+-------------------------*/
+.block_rss_client .list li:first-child {
+    border-top-width: 0;
+}
+.block_rss_client .list li {
+    border-top: 1px solid;
+    padding: 5px;
+}
\ No newline at end of file
index 1c287d7..26451d6 100644 (file)
@@ -84,15 +84,16 @@ if (!empty($rssrecord->preferredtitle)) {
     $feedtitle =  $rss->get_title();
 }
 echo '<table align="center" width="50%" cellspacing="1">'."\n";
-echo '<tr><td colspan="2"><strong>'. $feedtitle .'</strong></td></tr>'."\n";
+echo '<tr><td colspan="2"><strong>'. s($feedtitle) .'</strong></td></tr>'."\n";
 foreach ($rss->get_items() as $item) {
     echo '<tr><td valign="middle">'."\n";
-    echo '<a href="'. $item->get_link() .'" target="_blank"><strong>'. $item->get_title();
+    echo '<a href="'.$item->get_link().'" target="_blank"><strong>';
+    echo s($item->get_title());
     echo '</strong></a>'."\n";
     echo '</td>'."\n";
     echo '</tr>'."\n";
     echo '<tr><td colspan="2"><small>';
-    echo $item->get_description() .'</small></td></tr>'."\n";
+    echo format_text($item->get_description(), FORMAT_HTML) .'</small></td></tr>'."\n";
 }
 echo '</table>'."\n";
 
index fc150c2..7f650c4 100644 (file)
@@ -73,7 +73,6 @@ class block_site_main_menu extends block_list {
             $strcancel= get_string('cancel');
             $stractivityclipboard = $USER->activitycopyname;
         }
-        // Casting $course->modinfo to string prevents one notice when the field is null.
         $editbuttons = '';
 
         if ($ismoving) {
index bcab936..b802221 100644 (file)
@@ -75,7 +75,6 @@ class block_social_activities extends block_list {
             $strcancel= get_string('cancel');
             $stractivityclipboard = $USER->activitycopyname;
         }
-        // Casting $course->modinfo to string prevents one notice when the field is null.
         $editbuttons = '';
 
         if ($ismoving) {
index 2c36763..5c06993 100644 (file)
@@ -162,7 +162,8 @@ if (!empty($entry->id)) {
 }
 
 require_once('edit_form.php');
-$summaryoptions = array('subdirs'=>false, 'maxfiles'=> 99, 'maxbytes'=>$CFG->maxbytes, 'trusttext'=>true, 'context'=>$sitecontext);
+$summaryoptions = array('maxfiles'=> 99, 'maxbytes'=>$CFG->maxbytes, 'trusttext'=>true, 'context'=>$sitecontext,
+    'subdirs'=>file_area_contains_subdirs($sitecontext, 'blog', 'post', $entry->id));
 $attachmentoptions = array('subdirs'=>false, 'maxfiles'=> 99, 'maxbytes'=>$CFG->maxbytes);
 
 $blogeditform = new blog_edit_form(null, compact('entry', 'summaryoptions', 'attachmentoptions', 'sitecontext', 'courseid', 'modid'));
index b605016..af1724d 100644 (file)
@@ -119,11 +119,14 @@ class core_blog_renderer extends plugin_renderer_base {
         // Body.
         $o .= format_text($entry->summary, $entry->summaryformat, array('overflowdiv' => true));
 
-        // Uniquehash is used as a link to an external blog.
         if (!empty($entry->uniquehash)) {
-            $o .= $this->output->container_start('externalblog');
-            $o .= html_writer::link($entry->uniquehash, get_string('linktooriginalentry', 'blog'));
-            $o .= $this->output->container_end();
+            // Uniquehash is used as a link to an external blog.
+            $url = clean_param($entry->uniquehash, PARAM_URL);
+            if (!empty($url)) {
+                $o .= $this->output->container_start('externalblog');
+                $o .= html_writer::link($url, get_string('linktooriginalentry', 'blog'));
+                $o .= $this->output->container_end();
+            }
         }
 
         // Links to tags.
index 1b19fb2..da5cd4c 100644 (file)
@@ -756,7 +756,7 @@ class cache_definition {
      */
     public function set_identifiers(array $identifiers = array()) {
         foreach ($this->requireidentifiers as $identifier) {
-            if (!array_key_exists($identifier, $identifiers)) {
+            if (!isset($identifiers[$identifier])) {
                 throw new coding_exception('Identifier required for cache has not been provided: '.$identifier);
             }
         }
index e57d6ef..476ec0b 100644 (file)
@@ -173,7 +173,7 @@ class cache_factory {
      */
     public function create_cache_from_definition($component, $area, array $identifiers = array(), $aggregate = null) {
         $definitionname = $component.'/'.$area;
-        if (array_key_exists($definitionname, $this->cachesfromdefinitions)) {
+        if (isset($this->cachesfromdefinitions[$definitionname])) {
             $cache = $this->cachesfromdefinitions[$definitionname];
             $cache->set_identifiers($identifiers);
             return $cache;
@@ -368,7 +368,7 @@ class cache_factory {
         if ($aggregate) {
             $id .= '::'.$aggregate;
         }
-        if (!array_key_exists($id, $this->definitions)) {
+        if (!isset($this->definitions[$id])) {
             // This is the first time this definition has been requested.
             if ($this->is_initialising()) {
                 // We're initialising the cache right now. Don't try to create another config instance.
diff --git a/calendar/classes/type_base.php b/calendar/classes/type_base.php
new file mode 100644 (file)
index 0000000..6458220
--- /dev/null
@@ -0,0 +1,116 @@
+<?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/>.
+
+namespace core_calendar;
+
+/**
+ * Defines functions used by calendar type plugins.
+ *
+ * This library provides a unified interface for calendar types.
+ *
+ * @package core_calendar
+ * @copyright 2008 onwards Foodle Group {@link http://foodle.org}
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+abstract class type_base {
+
+    /**
+     * Returns a list of all the possible days for all months.
+     *
+     * This is used to generate the select box for the days
+     * in the date selector elements. Some months contain more days
+     * than others so this function should return all possible days as
+     * we can not predict what month will be chosen (the user
+     * may have JS turned off and we need to support this situation in
+     * Moodle).
+     *
+     * @return array the days
+     */
+    public abstract function get_days();
+
+    /**
+     * Returns a list of all the names of the months.
+     *
+     * @return array the month names
+     */
+    public abstract function get_months();
+
+    /**
+     * Returns the minimum year of the calendar.
+     *
+     * @return int the minumum year
+     */
+    public abstract function get_min_year();
+
+    /**
+     * Returns the maximum year of the calendar.
+     *
+     * @return int the max year
+     */
+    public abstract function get_max_year();
+
+    /**
+     * Returns a formatted string that represents a date in user time.
+     *
+     * @param int $date the timestamp in UTC, as obtained from the database
+     * @param string $format strftime format
+     * @param int|float|string $timezone the timezone to use
+     *        {@link http://docs.moodle.org/dev/Time_API#Timezone}
+     * @param bool $fixday if true then the leading zero from %d is removed,
+     *        if false then the leading zero is maintained
+     * @param bool $fixhour if true then the leading zero from %I is removed,
+     *        if false then the leading zero is maintained
+     * @return string the formatted date/time
+     */
+    public abstract function timestamp_to_date_string($date, $format, $timezone, $fixday, $fixhour);
+
+    /**
+     * Given a $time timestamp in GMT (seconds since epoch), returns an array that represents
+     * the date in user time.
+     *
+     * @param int $time timestamp in GMT
+     * @param float|int|string $timezone the timezone to use to calculate the time
+     *        {@link http://docs.moodle.org/dev/Time_API#Timezone}
+     * @return array an array that represents the date in user time
+     */
+    public abstract function timestamp_to_date_array($time, $timezone);
+
+    /**
+     * Provided with a day, month, year, hour and minute in the specific
+     * calendar type convert it into the equivalent Gregorian date.
+     *
+     * @param int $year
+     * @param int $month
+     * @param int $day
+     * @param int $hour
+     * @param int $minute
+     * @return array the converted day, month and year.
+     */
+    public abstract function convert_to_gregorian($year, $month, $day, $hour = 0, $minute = 0);
+
+    /**
+     * Provided with a day, month, year, hour and minute in a Gregorian date
+     * convert it into the specific calendar type date.
+     *
+     * @param int $year
+     * @param int $month
+     * @param int $day
+     * @param int $hour
+     * @param int $minute
+     * @return array the converted day, month and year.
+     */
+    public abstract function convert_from_gregorian($year, $month, $day, $hour = 0, $minute = 0);
+}
diff --git a/calendar/classes/type_factory.php b/calendar/classes/type_factory.php
new file mode 100644 (file)
index 0000000..535dd2b
--- /dev/null
@@ -0,0 +1,91 @@
+<?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/>.
+
+namespace core_calendar;
+
+/**
+ * Class \core_calendar\type_factory.
+ *
+ * Factory class producing required subclasses of {@link \core_calendar\type_base}.
+ *
+ * @package core_calendar
+ * @copyright 2008 onwards Foodle Group {@link http://foodle.org}
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class type_factory {
+
+    /**
+     * Returns an instance of the currently used calendar type.
+     *
+     * @param string|null $type the calendar type to use, if none provided use logic to determine
+     * @return calendartype_* the created calendar_type class
+     * @throws coding_exception if the calendar type file could not be loaded
+     */
+    public static function get_calendar_instance($type = null) {
+        if (is_null($type)) {
+            $type = self::get_calendar_type();
+        }
+
+        $class = "\\calendartype_$type\\structure";
+
+        // Ensure the calendar type exists. It may occur that a user has selected a calendar type, which was then
+        // deleted. If this happens we want to fall back on the Gregorian calendar type.
+        if (!class_exists($class)) {
+            $class = "\\calendartype_gregorian\\structure";
+        }
+
+        return new $class();
+    }
+
+    /**
+     * Returns a list of calendar typess available for use.
+     *
+     * @return array the list of calendar types
+     */
+    public static function get_list_of_calendar_types() {
+        $calendars = array();
+        $calendardirs = \core_component::get_plugin_list('calendartype');
+
+        foreach ($calendardirs as $name => $location) {
+            $calendars[$name] = get_string('name', "calendartype_{$name}");
+        }
+
+        return $calendars;
+    }
+
+    /**
+     * Returns the current calendar type in use.
+     *
+     * @return string the current calendar type being used
+     */
+    public static function get_calendar_type() {
+        global $CFG, $USER, $SESSION, $COURSE;
+
+        if (!empty($COURSE->id) and $COURSE->id != SITEID and !empty($COURSE->calendartype)) { // Course calendartype can override all other settings for this page.
+            $return = $COURSE->calendartype;
+        } else if (!empty($SESSION->calendartype)) { // Session calendartype can override other settings.
+            $return = $SESSION->calendartype;
+        } else if (!empty($USER->calendartype)) {
+            $return = $USER->calendartype;
+        } else if (!empty($CFG->calendartype)) {
+            $return = $CFG->calendartype;
+        } else {
+            $return = 'gregorian';
+        }
+
+        return $return;
+    }
+}
diff --git a/calendar/tests/calendartype_test.php b/calendar/tests/calendartype_test.php
new file mode 100644 (file)
index 0000000..10e72ea
--- /dev/null
@@ -0,0 +1,282 @@
+<?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/>.
+
+/**
+ * This file contains the class that handles testing the calendar type system.
+ *
+ * @package core_calendar
+ * @copyright 2013 Mark Nelson <markn@moodle.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+global $CFG;
+
+// The test calendar type.
+require_once($CFG->dirroot . '/calendar/tests/calendartype_test_example.php');
+
+// Used to test the dateselector elements.
+require_once($CFG->libdir . '/form/dateselector.php');
+require_once($CFG->libdir . '/form/datetimeselector.php');
+
+// Used to test the user datetime profile field.
+require_once($CFG->dirroot . '/user/profile/lib.php');
+require_once($CFG->dirroot . '/user/profile/definelib.php');
+require_once($CFG->dirroot . '/user/profile/index_field_form.php');
+
+/**
+ * Unit tests for the calendar type system.
+ *
+ * @package core_calendar
+ * @copyright 2013 Mark Nelson <markn@moodle.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since Moodle 2.6
+ */
+class core_calendar_type_testcase extends advanced_testcase {
+
+    /**
+     * The test user.
+     */
+    private $user;
+
+    /**
+     * Test set up.
+     */
+    protected function setUp() {
+        // The user we are going to test this on.
+        $this->user = self::getDataGenerator()->create_user();
+        self::setUser($this->user);
+    }
+
+    /**
+     * Test that setting the calendar type works.
+     */
+    public function test_calendar_type_set() {
+        // We want to reset the test data after this run.
+        $this->resetAfterTest();
+
+        // Test setting it as the 'Test' calendar type.
+        $this->set_calendar_type('test');
+        $this->assertEquals('test', \core_calendar\type_factory::get_calendar_type());
+
+        // Test setting it as the 'Gregorian' calendar type.
+        $this->set_calendar_type('gregorian');
+        $this->assertEquals('gregorian', \core_calendar\type_factory::get_calendar_type());
+    }
+
+    /**
+     * Test that calling core Moodle functions responsible for displaying the date
+     * have the same results as directly calling the same function in the calendar type.
+     */
+    public function test_calendar_type_core_functions() {
+        // We want to reset the test data after this run.
+        $this->resetAfterTest();
+
+        // Test that the core functions reproduce the same results as the Gregorian calendar.
+        $this->core_functions_test('gregorian');
+
+        // Test that the core functions reproduce the same results as the test calendar.
+        $this->core_functions_test('test');
+    }
+
+    /**
+     * Test that dates selected using the date selector elements are being saved as unixtime, and that the
+     * unixtime is being converted back to a valid date to display in the date selector elements for
+     * different calendar types.
+     */
+    public function test_calendar_type_dateselector_elements() {
+        // We want to reset the test data after this run.
+        $this->resetAfterTest();
+
+        // Check converting dates to Gregorian when submitting a date selector element works. Note: the test
+        // calendar is 2 years, 2 months, 2 days, 2 hours and 2 minutes ahead of the Gregorian calendar.
+        $date1 = array();
+        $date1['day'] = 4;
+        $date1['month'] = 7;
+        $date1['year'] = 2013;
+        $date1['hour'] = 0;
+        $date1['minute'] = 0;
+        $date1['timestamp'] = 1372896000;
+        $this->convert_dateselector_to_unixtime_test('dateselector', 'gregorian', $date1);
+
+        $date2 = array();
+        $date2['day'] = 7;
+        $date2['month'] = 9;
+        $date2['year'] = 2015;
+        $date2['hour'] = 0; // The dateselector element does not have hours.
+        $date2['minute'] = 0; // The dateselector element does not have minutes.
+        $date2['timestamp'] = 1372896000;
+        $this->convert_dateselector_to_unixtime_test('dateselector', 'test', $date2);
+
+        $date3 = array();
+        $date3['day'] = 4;
+        $date3['month'] = 7;
+        $date3['year'] = 2013;
+        $date3['hour'] = 23;
+        $date3['minute'] = 15;
+        $date3['timestamp'] = 1372979700;
+        $this->convert_dateselector_to_unixtime_test('datetimeselector', 'gregorian', $date3);
+
+        $date4 = array();
+        $date4['day'] = 7;
+        $date4['month'] = 9;
+        $date4['year'] = 2015;
+        $date4['hour'] = 1;
+        $date4['minute'] = 17;
+        $date4['timestamp'] = 1372979700;
+        $this->convert_dateselector_to_unixtime_test('datetimeselector', 'test', $date4);
+
+        // The date selector element values are set by using the function usergetdate, here we want to check that
+        // the unixtime passed is being successfully converted to the correct values for the calendar type.
+        $this->convert_unixtime_to_dateselector_test('gregorian', $date3);
+        $this->convert_unixtime_to_dateselector_test('test', $date4);
+    }
+
+    /**
+     * Test that the user profile field datetime minimum and maximum year settings are saved as the
+     * equivalent Gregorian years.
+     */
+    public function test_calendar_type_datetime_field_submission() {
+        // We want to reset the test data after this run.
+        $this->resetAfterTest();
+
+        // Create an array with the input values and expected values once submitted.
+        $date = array();
+        $date['inputminyear'] = '1970';
+        $date['inputmaxyear'] = '2013';
+        $date['expectedminyear'] = '1970';
+        $date['expectedmaxyear'] = '2013';
+        $this->datetime_field_submission_test('gregorian', $date);
+
+        // The test calendar is 2 years, 2 months, 2 days in the future, so when the year 1970 is submitted,
+        // the year 1967 should be saved in the DB, as 1/1/1970 converts to 30/10/1967 in Gregorian.
+        $date['expectedminyear'] = '1967';
+        $date['expectedmaxyear'] = '2010';
+        $this->datetime_field_submission_test('test', $date);
+    }
+
+    /**
+     * Test all the core functions that use the calendar type system.
+     *
+     * @param string $type the calendar type we want to test
+     */
+    private function core_functions_test($type) {
+        $this->set_calendar_type($type);
+
+        // Get the calendar.
+        $calendar = \core_calendar\type_factory::get_calendar_instance();
+
+        // Test the userdate function.
+        $this->assertEquals($calendar->timestamp_to_date_string($this->user->timecreated, '', 99, true, true),
+            userdate($this->user->timecreated));
+    }
+
+    /**
+     * Simulates submitting a form with a date selector element and tests that the chosen dates
+     * are converted into unixtime before being saved in DB.
+     *
+     * @param string $element the form element we are testing
+     * @param string $type the calendar type we want to test
+     * @param array $date the date variables
+     */
+    private function convert_dateselector_to_unixtime_test($element, $type, $date) {
+        $this->set_calendar_type($type);
+
+        if ($element == 'dateselector') {
+            $el = new MoodleQuickForm_date_selector('dateselector', null, array('timezone' => 0.0, 'step' => 1));
+        } else {
+            $el = new MoodleQuickForm_date_time_selector('dateselector', null, array('timezone' => 0.0, 'step' => 1));
+        }
+        $el->_createElements();
+        $submitvalues = array('dateselector' => $date);
+
+        $this->assertSame($el->exportValue($submitvalues), array('dateselector' => $date['timestamp']));
+    }
+
+    /**
+     * Test converting dates from unixtime to a date for the calendar type specified.
+     *
+     * @param string $type the calendar type we want to test
+     * @param array $date the date variables
+     */
+    private function convert_unixtime_to_dateselector_test($type, $date) {
+        $this->set_calendar_type($type);
+
+        // Get the calendar.
+        $calendar = \core_calendar\type_factory::get_calendar_instance();
+
+        $usergetdate = $calendar->timestamp_to_date_array($date['timestamp'], 0.0);
+        $comparedate = array(
+            'minute' => $usergetdate['minutes'],
+            'hour' => $usergetdate['hours'],
+            'day' => $usergetdate['mday'],
+            'month' => $usergetdate['mon'],
+            'year' => $usergetdate['year'],
+            'timestamp' => $date['timestamp']
+        );
+
+        $this->assertEquals($comparedate, $date);
+    }
+
+    /**
+     * Test saving the minimum and max year settings for the user datetime field.
+     *
+     * @param string $type the calendar type we want to test
+     * @param array $date the date variables
+     */
+    private function datetime_field_submission_test($type, $date) {
+        $this->set_calendar_type($type);
+
+        // Get the data we are submitting for the form.
+        $formdata = array();
+        $formdata['id'] = 0;
+        $formdata['shortname'] = 'Shortname';
+        $formdata['name'] = 'Name';
+        $formdata['param1'] = $date['inputminyear'];
+        $formdata['param2'] = $date['inputmaxyear'];
+
+        // Mock submitting this.
+        field_form::mock_submit($formdata);
+
+        // Create the user datetime form.
+        $form = new field_form(null, 'datetime');
+
+        // Get the data from the submission.
+        $submissiondata = $form->get_data();
+        // On the user profile field page after get_data, the function define_save is called
+        // in the field base class, which then calls the field's function define_save_preprocess.
+        $field = new profile_define_datetime();
+        $submissiondata = $field->define_save_preprocess($submissiondata);
+
+        // Create an array we want to compare with the date passed.
+        $comparedate = $date;
+        $comparedate['expectedminyear'] = $submissiondata->param1;
+        $comparedate['expectedmaxyear'] = $submissiondata->param2;
+
+        $this->assertEquals($comparedate, $date);
+    }
+
+    /**
+     * Set the calendar type for this user.
+     *
+     * @param string $type the calendar type we want to set
+     */
+    private function set_calendar_type($type) {
+        $this->user->calendartype = $type;
+        session_set_user($this->user);
+    }
+}
diff --git a/calendar/tests/calendartype_test_example.php b/calendar/tests/calendartype_test_example.php
new file mode 100644 (file)
index 0000000..679422e
--- /dev/null
@@ -0,0 +1,176 @@
+<?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/>.
+
+namespace calendartype_test;
+use \core_calendar\type_base;
+
+/**
+ * Handles calendar functions for the test calendar.
+ *
+ * The test calendar is going to be 2 years, 2 days, 2 hours and 2 minutes
+ * in the future of the Gregorian calendar.
+ *
+ * @package core_calendar
+ * @copyright 2013 Mark Nelson <markn@moodle.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class structure extends type_base {
+
+    /**
+     * Returns a list of all the possible days for all months.
+     *
+     * This is used to generate the select box for the days
+     * in the date selector elements. Some months contain more days
+     * than others so this function should return all possible days as
+     * we can not predict what month will be chosen (the user
+     * may have JS turned off and we need to support this situation in
+     * Moodle).
+     *
+     * @return array the days
+     */
+    public function get_days() {
+        $days = array();
+
+        for ($i = 1; $i <= 31; $i++) {
+            $days[$i] = $i;
+        }
+
+        return $days;
+    }
+
+    /**
+     * Returns a list of all the names of the months.
+     *
+     * @return array the month names
+     */
+    public function get_months() {
+        $months = array();
+
+        for ($i = 1; $i <= 12; $i++) {
+            $months[$i] = $i;
+        }
+
+        return $months;
+    }
+
+    /**
+     * Returns the minimum year of the calendar.
+     *
+     * @return int the minumum year
+     */
+    public function get_min_year() {
+        return 1970;
+    }
+
+    /**
+     * Returns the maximum year of the calendar.
+     *
+     * @return int the max year
+     */
+    public function get_max_year() {
+        return 2050;
+    }
+
+    /**
+     * Returns a formatted string that represents a date in user time.
+     *
+     * @param int $date the timestamp in UTC, as obtained from the database
+     * @param string $format strftime format
+     * @param int|float|string $timezone the timezone to use
+     *        {@link http://docs.moodle.org/dev/Time_API#Timezone}
+     * @param bool $fixday if true then the leading zero from %d is removed,
+     *        if false then the leading zero is maintained
+     * @param bool $fixhour if true then the leading zero from %I is removed,
+     *        if false then the leading zero is maintained
+     * @return string the formatted date/time
+     */
+    public function timestamp_to_date_string($date, $format, $timezone, $fixday, $fixhour) {
+        return '';
+    }
+
+    /**
+     * Given a $time timestamp in GMT (seconds since epoch), returns an array that represents
+     * the date in user time.
+     *
+     * @param int $time timestamp in GMT
+     * @param float|int|string $timezone the timezone to use to calculate the time
+     *        {@link http://docs.moodle.org/dev/Time_API#Timezone}
+     * @return array an array that represents the date in user time
+     */
+    public function timestamp_to_date_array($time, $timezone) {
+        $gregoriancalendar = \core_calendar\type_factory::get_calendar_instance('gregorian');
+        $date = $gregoriancalendar->timestamp_to_date_array($time, $timezone);
+        $newdate = $this->convert_from_gregorian($date['year'], $date['mon'], $date['mday'],
+            $date['hours'], $date['minutes']);
+
+        $date['year'] = $newdate['year'];
+        $date['mon'] = $newdate['month'];
+        $date['mday'] = $newdate['day'];
+        $date['hours'] = $newdate['hour'];
+        $date['minutes']  = $newdate['minute'];
+
+        return $date;
+    }
+
+    /**
+     * Provided with a day, month, year, hour and minute
+     * convert it into the equivalent Gregorian date.
+     *
+     * @param int $year
+     * @param int $month
+     * @param int $day
+     * @param int $hour
+     * @param int $minute
+     * @return array the converted day, month, year, hour and minute.
+     */
+    public function convert_to_gregorian($year, $month, $day, $hour = 0, $minute = 0) {
+        $timestamp = make_timestamp($year, $month, $day, $hour, $minute);
+        $date = date('Y/n/j/H/i', strtotime('-2 year, -2 months, -2 days, -2 hours, -2 minutes', $timestamp));
+
+        list($year, $month, $day, $hour, $minute) = explode('/', $date);
+
+        return array('year' => (int) $year,
+                     'month' => (int) $month,
+                     'day' => (int) $day,
+                     'hour' => (int) $hour,
+                     'minute' => (int) $minute);
+
+    }
+
+    /**
+     * Provided with a day, month, year, hour and minute in a Gregorian date
+     * convert it into the specific calendar type date.
+     *
+     * @param int $year
+     * @param int $month
+     * @param int $day
+     * @param int $hour
+     * @param int $minute
+     * @return array the converted day, month, year, hour and minute.
+     */
+    public function convert_from_gregorian($year, $month, $day, $hour = 0, $minute = 0) {
+        $timestamp = make_timestamp($year, $month, $day, $hour, $minute);
+        $date = date('Y/n/j/H/i', strtotime('+2 year, +2 months, +2 days, +2 hours, +2 minutes', $timestamp));
+
+        list($year, $month, $day, $hour, $minute) = explode('/', $date);
+
+        return array('year' => (int) $year,
+                     'month' => (int) $month,
+                     'day' => (int) $day,
+                     'hour' => (int) $hour,
+                     'minute' => (int) $minute);
+    }
+}
diff --git a/calendar/type/gregorian/classes/structure.php b/calendar/type/gregorian/classes/structure.php
new file mode 100644 (file)
index 0000000..d209864
--- /dev/null
@@ -0,0 +1,232 @@
+<?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/>.
+
+namespace calendartype_gregorian;
+use core_calendar\type_base;
+
+/**
+ * Handles calendar functions for the gregorian calendar.
+ *
+ * @package calendartype_gregorian
+ * @copyright 2008 onwards Foodle Group {@link http://foodle.org}
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class structure extends type_base {
+
+    /**
+     * Returns a list of all the possible days for all months.
+     *
+     * This is used to generate the select box for the days
+     * in the date selector elements. Some months contain more days
+     * than others so this function should return all possible days as
+     * we can not predict what month will be chosen (the user
+     * may have JS turned off and we need to support this situation in
+     * Moodle).
+     *
+     * @return array the days
+     */
+    public function get_days() {
+        $days = array();
+
+        for ($i = 1; $i <= 31; $i++) {
+            $days[$i] = $i;
+        }
+
+        return $days;
+    }
+
+    /**
+     * Returns a list of all the names of the months.
+     *
+     * @return array the month names
+     */
+    public function get_months() {
+        $months = array();
+
+        for ($i = 1; $i <= 12; $i++) {
+            $months[$i] = userdate(gmmktime(12, 0, 0, $i, 15, 2000), '%B');
+        }
+
+        return $months;
+    }
+
+    /**
+     * Returns the minimum year of the calendar.
+     *
+     * @return int the minumum year
+     */
+    public function get_min_year() {
+        return 1900;
+    }
+
+    /**
+     * Returns the maximum year of the calendar.
+     *
+     * @return int the max year
+     */
+    public function get_max_year() {
+        return 2050;
+    }
+
+    /**
+     * Returns a formatted string that represents a date in user time.
+     *
+     * Returns a formatted string that represents a date in user time
+     * <b>WARNING: note that the format is for strftime(), not date().</b>
+     * Because of a bug in most Windows time libraries, we can't use
+     * the nicer %e, so we have to use %d which has leading zeroes.
+     * A lot of the fuss in the function is just getting rid of these leading
+     * zeroes as efficiently as possible.
+     *
+     * If parameter fixday = true (default), then take off leading
+     * zero from %d, else maintain it.
+     *
+     * @param int $date the timestamp in UTC, as obtained from the database
+     * @param string $format strftime format
+     * @param int|float|string $timezone the timezone to use
+     *        {@link http://docs.moodle.org/dev/Time_API#Timezone}
+     * @param bool $fixday if true then the leading zero from %d is removed,
+     *        if false then the leading zero is maintained
+     * @param bool $fixhour if true then the leading zero from %I is removed,
+     *        if false then the leading zero is maintained
+     * @return string the formatted date/time
+     */
+    public function timestamp_to_date_string($date, $format, $timezone, $fixday, $fixhour) {
+        global $CFG;
+
+        if (empty($format)) {
+            $format = get_string('strftimedaydatetime', 'langconfig');
+        }
+
+        if (!empty($CFG->nofixday)) { // Config.php can force %d not to be fixed.
+            $fixday = false;
+        } else if ($fixday) {
+            $formatnoday = str_replace('%d', 'DD', $format);
+            $fixday = ($formatnoday != $format);
+            $format = $formatnoday;
+        }
+
+        // Note: This logic about fixing 12-hour time to remove unnecessary leading
+        // zero is required because on Windows, PHP strftime function does not
+        // support the correct 'hour without leading zero' parameter (%l).
+        if (!empty($CFG->nofixhour)) {
+            // Config.php can force %I not to be fixed.
+            $fixhour = false;
+        } else if ($fixhour) {
+            $formatnohour = str_replace('%I', 'HH', $format);
+            $fixhour = ($formatnohour != $format);
+            $format = $formatnohour;
+        }
+
+        // Add daylight saving offset for string timezones only, as we can't get dst for
+        // float values. if timezone is 99 (user default timezone), then try update dst.
+        if ((99 == $timezone) || !is_numeric($timezone)) {
+            $date += dst_offset_on($date, $timezone);
+        }
+
+        $timezone = get_user_timezone_offset($timezone);
+
+        // If we are running under Windows convert to windows encoding and then back to UTF-8
+        // (because it's impossible to specify UTF-8 to fetch locale info in Win32).
+        if (abs($timezone) > 13) { // Server time.
+            $datestring = date_format_string($date, $format, $timezone);
+            if ($fixday) {
+                $daystring  = ltrim(str_replace(array(' 0', ' '), '', strftime(' %d', $date)));
+                $datestring = str_replace('DD', $daystring, $datestring);
+            }
+            if ($fixhour) {
+                $hourstring = ltrim(str_replace(array(' 0', ' '), '', strftime(' %I', $date)));
+                $datestring = str_replace('HH', $hourstring, $datestring);
+            }
+        } else {
+            $date += (int)($timezone * 3600);
+            $datestring = date_format_string($date, $format, $timezone);
+            if ($fixday) {
+                $daystring  = ltrim(str_replace(array(' 0', ' '), '', gmstrftime(' %d', $date)));
+                $datestring = str_replace('DD', $daystring, $datestring);
+            }
+            if ($fixhour) {
+                $hourstring = ltrim(str_replace(array(' 0', ' '), '', gmstrftime(' %I', $date)));
+                $datestring = str_replace('HH', $hourstring, $datestring);
+            }
+        }
+
+        return $datestring;
+    }
+
+    /**
+     * Given a $time timestamp in GMT (seconds since epoch), returns an array that
+     * represents the date in user time.
+     *
+     * @param int $time Timestamp in GMT
+     * @param float|int|string $timezone offset's time with timezone, if float and not 99, then no
+     *        dst offset is applied {@link http://docs.moodle.org/dev/Time_API#Timezone}
+     * @return array an array that represents the date in user time
+     */
+    public function timestamp_to_date_array($time, $timezone) {
+        return usergetdate($time, $timezone);
+    }
+
+    /**
+     * Provided with a day, month, year, hour and minute in a specific
+     * calendar type convert it into the equivalent Gregorian date.
+     *
+     * In this function we don't need to do anything except pass the data
+     * back as an array. This is because the date received is Gregorian.
+     *
+     * @param int $year
+     * @param int $month
+     * @param int $day
+     * @param int $hour
+     * @param int $minute
+     * @return array the converted day, month, year, hour and minute.
+     */
+    public function convert_from_gregorian($year, $month, $day, $hour = 0, $minute = 0) {
+        $date = array();
+        $date['year'] = $year;
+        $date['month'] = $month;
+        $date['day'] = $day;
+        $date['hour'] = $hour;
+        $date['minute'] = $minute;
+
+        return $date;
+    }
+
+    /**
+     * Provided with a day, month, year, hour and minute in a specific
+     * calendar type convert it into the equivalent Gregorian date.
+     *
+     * In this function we don't need to do anything except pass the data
+     * back as an array. This is because the date received is Gregorian.
+     *
+     * @param int $year
+     * @param int $month
+     * @param int $day
+     * @param int $hour
+     * @param int $minute
+     * @return array the converted day, month, year, hour and minute.
+     */
+    public function convert_to_gregorian($year, $month, $day, $hour = 0, $minute = 0) {
+        $date = array();
+        $date['year'] = $year;
+        $date['month'] = $month;
+        $date['day'] = $day;
+        $date['hour'] = $hour;
+        $date['minute'] = $minute;
+
+        return $date;
+    }
+}
diff --git a/calendar/type/gregorian/lang/en/calendartype_gregorian.php b/calendar/type/gregorian/lang/en/calendartype_gregorian.php
new file mode 100644 (file)
index 0000000..9d2c5ba
--- /dev/null
@@ -0,0 +1,26 @@
+<?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/>.
+
+/**
+ * Strings for component 'calendartype_gregorian', language 'en'.
+ *
+ * @package calendartype_gregorian
+ * @copyright 2008 onwards Foodle Group {@link http://foodle.org}
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+$string['name'] = 'Gregorian';
+$string['pluginname'] = 'Gregorian calendar type';
diff --git a/calendar/type/gregorian/version.php b/calendar/type/gregorian/version.php
new file mode 100644 (file)
index 0000000..deeac17
--- /dev/null
@@ -0,0 +1,29 @@
+<?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/>.
+
+/**
+ * Version details.
+ *
+ * @package calendartype_gregorian
+ * @copyright 2008 onwards Foodle Group {@link http://foodle.org}
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$plugin->version   = 2013082300; // The current plugin version (Date: YYYYMMDDXX).
+$plugin->requires  = 2012120300; // Requires this Moodle version.
+$plugin->component = 'calendartype_gregorian'; // Full name of the plugin (used for diagnostics).
index 917d670..38a323b 100644 (file)
@@ -684,7 +684,6 @@ class dndupload_ajax_processor {
         $DB->set_field('course_modules', 'instance', $instanceid, array('id' => $this->cm->id));
         // Rebuild the course cache after update action
         rebuild_course_cache($this->course->id, true);
-        $this->course->modinfo = null; // Otherwise we will just get the old version back again.
 
         $sectionid = course_add_cm_to_section($this->course, $this->cm->id, $this->section);
 
index c549cc1..1021c23 100644 (file)
@@ -70,6 +70,7 @@ $overviewfilesoptions = course_overviewfiles_options($course);
 if (!empty($course)) {
     //add context for editor
     $editoroptions['context'] = $coursecontext;
+    $editoroptions['subdirs'] = file_area_contains_subdirs($coursecontext, 'course', 'summary', 0);
     $course = file_prepare_standard_editor($course, 'summary', $editoroptions, $coursecontext, 'course', 'summary', 0);
     if ($overviewfilesoptions) {
         file_prepare_standard_filemanager($course, 'overviewfiles', $overviewfilesoptions, $coursecontext, 'course', 'overviewfiles', 0);
@@ -84,6 +85,7 @@ if (!empty($course)) {
 } else {
     //editor should respect category context if course context is not set.
     $editoroptions['context'] = $catcontext;
+    $editoroptions['subdirs'] = 0;
     $course = file_prepare_standard_editor($course, 'summary', $editoroptions, null, 'course', 'summary', null);
     if ($overviewfilesoptions) {
         file_prepare_standard_filemanager($course, 'overviewfiles', $overviewfilesoptions, null, 'course', 'overviewfiles', 0);
index 06f7435..2dcbce2 100644 (file)
@@ -6,12 +6,18 @@ require_once($CFG->libdir.'/formslib.php');
 require_once($CFG->libdir.'/completionlib.php');
 require_once($CFG->libdir. '/coursecatlib.php');
 
+/**
+ * The form for handling editing a course.
+ */
 class course_edit_form extends moodleform {
     protected $course;
     protected $context;
 
+    /**
+     * Form definition.
+     */
     function definition() {
-        global $USER, $CFG, $DB, $PAGE;
+        global $CFG, $PAGE;
 
         $mform    = $this->_form;
         $PAGE->requires->yui_module('moodle-course-formatchooser', 'M.course.init_formatchooser',
@@ -38,8 +44,7 @@ class course_edit_form extends moodleform {
         $this->course  = $course;
         $this->context = $context;
 
-/// form definition with new course defaults
-//--------------------------------------------------------------------------------
+        // Form definition with new course defaults.
         $mform->addElement('header','general', get_string('general', 'form'));
 
         $mform->addElement('hidden', 'returnto', null);
@@ -194,6 +199,16 @@ class course_edit_form extends moodleform {
         $mform->addElement('select', 'lang', get_string('forcelanguage'), $languages);
         $mform->setDefault('lang', $courseconfig->lang);
 
+        // Multi-Calendar Support - see MDL-18375.
+        $calendartypes = \core_calendar\type_factory::get_list_of_calendar_types();
+        // We do not want to show this option unless there is more than one calendar type to display.
+        if (count($calendartypes) > 1) {
+            $calendars = array();
+            $calendars[''] = get_string('forceno');
+            $calendars += $calendartypes;
+            $mform->addElement('select', 'calendartype', get_string('forcecalendartype', 'calendar'), $calendars);
+        }
+
         $options = range(0, 10);
         $mform->addElement('select', 'newsitems', get_string('newsitemsnumber'), $options);
         $mform->addHelpButton('newsitems', 'newsitemsnumber');
@@ -247,10 +262,8 @@ class course_edit_form extends moodleform {
             $mform->setDefault('enablecompletion', 0);
         }
 
-//--------------------------------------------------------------------------------
         enrol_course_edit_form($mform, $course, $context);
 
-//--------------------------------------------------------------------------------
         $mform->addElement('header','groups', get_string('groupsettingsheader', 'group'));
 
         $choices = array();
@@ -270,10 +283,7 @@ class course_edit_form extends moodleform {
         $options[0] = get_string('none');
         $mform->addElement('select', 'defaultgroupingid', get_string('defaultgrouping', 'group'), $options);
 
-//--------------------------------------------------------------------------------
-
-/// customizable role names in this course
-//--------------------------------------------------------------------------------
+        // Customizable role names in this course.
         $mform->addElement('header','rolerenaming', get_string('rolerenaming'));
         $mform->addHelpButton('rolerenaming', 'rolerenaming');
 
@@ -286,17 +296,18 @@ class course_edit_form extends moodleform {
             }
         }
 
-//--------------------------------------------------------------------------------
         $this->add_action_buttons();
-//--------------------------------------------------------------------------------
+
         $mform->addElement('hidden', 'id', null);
         $mform->setType('id', PARAM_INT);
 
-/// finally set the current form data
-//--------------------------------------------------------------------------------
+        // Finally set the current form data
         $this->set_data($course);
     }
 
+    /**
+     * Fill in the current page data for this course.
+     */
     function definition_after_data() {
         global $DB;
 
@@ -327,21 +338,31 @@ class course_edit_form extends moodleform {
         }
     }
 
-/// perform some extra moodle validation
+    /**
+     * Validation.
+     *
+     * @param array $data
+     * @param array $files
+     * @return array the errors that were found
+     */
     function validation($data, $files) {
-        global $DB, $CFG;
+        global $DB;
 
         $errors = parent::validation($data, $files);
-        if ($foundcourses = $DB->get_records('course', array('shortname'=>$data['shortname']))) {
-            if (!empty($data['id'])) {
-                unset($foundcourses[$data['id']]);
+
+        // Add field validation check for duplicate shortname.
+        if ($course = $DB->get_record('course', array('shortname' => $data['shortname']), '*', IGNORE_MULTIPLE)) {
+            if (empty($data['id']) || $course->id != $data['id']) {
+                $errors['shortname'] = get_string('shortnametaken', '', $course->fullname);
             }
-            if (!empty($foundcourses)) {
-                foreach ($foundcourses as $foundcourse) {
-                    $foundcoursenames[] = $foundcourse->fullname;
+        }
+
+        // Add field validation check for duplicate idnumber.
+        if (!empty($data['idnumber']) && (empty($data['id']) || $this->course->idnumber != $data['idnumber'])) {
+            if ($course = $DB->get_record('course', array('idnumber' => $data['idnumber']), '*', IGNORE_MULTIPLE)) {
+                if (empty($data['id']) || $course->id != $data['id']) {
+                    $errors['idnumber'] = get_string('courseidnumbertaken', 'error', $course->fullname);
                 }
-                $foundcoursenamestring = implode(',', $foundcoursenames);
-                $errors['shortname']= get_string('shortnametaken', '', $foundcoursenamestring);
             }
         }
 
index a2de1f1..05a5777 100644 (file)
@@ -77,7 +77,8 @@ $editoroptions = array(
     'maxfiles'  => EDITOR_UNLIMITED_FILES,
     'maxbytes'  => $CFG->maxbytes,
     'trusttext' => true,
-    'context'   => $editorcontext
+    'context'   => $editorcontext,
+    'subdirs'   => file_area_contains_subdirs($editorcontext, 'coursecat', 'description', $itemid),
 );
 $category = file_prepare_standard_editor($category, 'description', $editoroptions, $editorcontext, 'coursecat', 'description', $itemid);
 
index 26742db..2d61c28 100644 (file)
@@ -717,20 +717,14 @@ class core_course_external extends external_api {
                     require_capability('moodle/course:changefullname', $context);
                 }
 
-                // Check if the shortname already exist and user have capability.
+                // Check if the user can change shortname.
                 if (array_key_exists('shortname', $course) && ($oldcourse->shortname != $course['shortname'])) {
                     require_capability('moodle/course:changeshortname', $context);
-                    if ($DB->record_exists('course', array('shortname' => $course['shortname']))) {
-                        throw new moodle_exception('shortnametaken', '', '', $course['shortname']);
-                    }
                 }
 
-                // Check if the id number already exist and user have capability.
+                // Check if the user can change the idnumber.
                 if (array_key_exists('idnumber', $course) && ($oldcourse->idnumber != $course['idnumber'])) {
                     require_capability('moodle/course:changeidnumber', $context);
-                    if ($DB->record_exists('course', array('idnumber' => $course['idnumber']))) {
-                        throw new moodle_exception('courseidnumbertaken', '', '', $course['idnumber']);
-                    }
                 }
 
                 // Check if user can change summary.
index bfc5641..44c9352 100644 (file)
@@ -651,15 +651,16 @@ abstract class format_section_renderer_base extends plugin_renderer_base {
         // Title with section navigation links.
         $sectionnavlinks = $this->get_nav_links($course, $modinfo->get_section_info_all(), $displaysection);
         $sectiontitle = '';
-        $sectiontitle .= html_writer::start_tag('div', array('class' => 'section-navigation header headingblock'));
+        $sectiontitle .= html_writer::start_tag('div', array('class' => 'section-navigation'));
         $sectiontitle .= html_writer::tag('span', $sectionnavlinks['previous'], array('class' => 'mdl-left'));
         $sectiontitle .= html_writer::tag('span', $sectionnavlinks['next'], array('class' => 'mdl-right'));
         // Title attributes
-        $titleattr = 'mdl-align title';
+        $classes = 'sectionname';
         if (!$thissection->visible) {
-            $titleattr .= ' dimmed_text';
+            $classes .= ' dimmed_text';
         }
-        $sectiontitle .= html_writer::tag('div', get_section_name($course, $displaysection), array('class' => $titleattr));
+        $sectiontitle .= $this->output->heading(get_section_name($course, $displaysection), 3, $classes);
+
         $sectiontitle .= html_writer::end_tag('div');
         echo $sectiontitle;
 
index 5cfa240..9ceefb5 100644 (file)
@@ -1929,18 +1929,13 @@ function course_get_cm_edit_actions(cm_info $mod, $indent = -1, $sr = null) {
 
     // Groupmode.
     if ($hasmanageactivities and plugin_supports('mod', $mod->modname, FEATURE_GROUPS, 0)) {
-        if ($mod->coursegroupmodeforce) {
-            $modgroupmode = $mod->coursegroupmode;
-        } else {
-            $modgroupmode = $mod->groupmode;
-        }
-        if ($modgroupmode == SEPARATEGROUPS) {
+        if ($mod->effectivegroupmode == SEPARATEGROUPS) {
             $nextgroupmode = VISIBLEGROUPS;
             $grouptitle = $str->groupsseparate;
             $forcedgrouptitle = $str->forcedgroupsseparate;
             $actionname = 'groupsseparate';
             $groupimage = 't/groups';
-        } else if ($modgroupmode == VISIBLEGROUPS) {
+        } else if ($mod->effectivegroupmode == VISIBLEGROUPS) {
             $nextgroupmode = NOGROUPS;
             $grouptitle = $str->groupsvisible;
             $forcedgrouptitle = $str->forcedgroupsvisible;
@@ -2051,40 +2046,43 @@ function move_courses($courseids, $categoryid) {
     $newparent = context_coursecat::instance($category->id);
     $i = 1;
 
-    foreach ($courseids as $courseid) {
-        if ($dbcourse = $DB->get_record('course', array('id' => $courseid))) {
-            $course = new stdClass();
-            $course->id = $courseid;
-            $course->category  = $category->id;
-            $course->sortorder = $category->sortorder + MAX_COURSES_IN_CATEGORY - $i++;
-            if ($category->visible == 0) {
-                // Hide the course when moving into hidden category, do not update the visibleold flag - we want to get
-                // to previous state if somebody unhides the category.
-                $course->visible = 0;
-            }
-
-            $DB->update_record('course', $course);
-
-            // Store the context.
-            $context = context_course::instance($course->id);
-
-            // Update the course object we are passing to the event.
-            $dbcourse->category = $course->category;
-            $dbcourse->sortorder = $course->sortorder;
+    list($where, $params) = $DB->get_in_or_equal($courseids);
+    $dbcourses = $DB->get_records_select('course', 'id ' . $where, $params);
+    foreach ($dbcourses as $dbcourse) {
+        $course = new stdClass();
+        $course->id = $dbcourse->id;
+        $course->category  = $category->id;
+        $course->sortorder = $category->sortorder + MAX_COURSES_IN_CATEGORY - $i++;
+        if ($category->visible == 0) {
+            // Hide the course when moving into hidden category, do not update the visibleold flag - we want to get
+            // to previous state if somebody unhides the category.
+            $course->visible = 0;
+        }
+
+        $DB->update_record('course', $course);
+
+        // Store the context.
+        $context = context_course::instance($course->id);
+
+        // Update the course object we are passing to the event.
+        $dbcourse->category = $course->category;
+        $dbcourse->sortorder = $course->sortorder;
+        if (isset($course->visible)) {
+            $dbcourse->visible = $course->visible;
+        }
+
+        // Trigger a course updated event.
+        $event = \core\event\course_updated::create(array(
+            'objectid' => $course->id,
+            'context' => $context,
+            'other' => array('shortname' => $dbcourse->shortname,
+                             'fullname' => $dbcourse->fullname)
+        ));
+        $event->add_record_snapshot('course', $dbcourse);
+        $event->set_legacy_logdata(array($course->id, 'course', 'move', 'edit.php?id=' . $course->id, $course->id));
+        $event->trigger();
 
-            // Trigger a course updated event.
-            $event = \core\event\course_updated::create(array(
-                'objectid' => $course->id,
-                'context' => $context,
-                'other' => array('shortname' => $dbcourse->shortname,
-                                 'fullname' => $dbcourse->fullname)
-            ));
-            $event->add_record_snapshot('course', $dbcourse);
-            $event->set_legacy_logdata(array($course->id, 'course', 'move', 'edit.php?id=' . $course->id, $course->id));
-            $event->trigger();
-
-            $context->update_moved($newparent);
-        }
+        $context->update_moved($newparent);
     }
     fix_course_sortorder();
     cache_helper::purge_by_event('changesincourse');
@@ -2368,6 +2366,20 @@ function update_course($data, $editoroptions = NULL) {
         $data = file_postupdate_standard_filemanager($data, 'overviewfiles', $overviewfilesoptions, $context, 'course', 'overviewfiles', 0);
     }
 
+    // Check we don't have a duplicate shortname.
+    if (!empty($data->shortname) && $oldcourse->shortname != $data->shortname) {
+        if ($DB->record_exists('course', array('shortname' => $data->shortname))) {
+            throw new moodle_exception('shortnametaken', '', '', $data->shortname);
+        }
+    }
+
+    // Check we don't have a duplicate idnumber.
+    if (!empty($data->idnumber) && $oldcourse->idnumber != $data->idnumber) {
+        if ($DB->record_exists('course', array('idnumber' => $data->idnumber))) {
+            throw new moodle_exception('courseidnumbertaken', '', '', $data->idnumber);
+        }
+    }
+
     if (!isset($data->category) or empty($data->category)) {
         // prevent nulls and 0 in category field
         unset($data->category);
@@ -2408,7 +2420,10 @@ function update_course($data, $editoroptions = NULL) {
         $context->update_moved($newparent);
     }
 
-    fix_course_sortorder();
+    if ($movecat || (isset($data->sortorder) && $oldcourse->sortorder != $data->sortorder)) {
+        fix_course_sortorder();
+    }
+
     // purge appropriate caches in case fix_course_sortorder() did not change anything
     cache_helper::purge_by_event('changesincourse');
     if ($changesincoursecat) {
index 8cfe0a0..db70a9f 100644 (file)
@@ -78,7 +78,7 @@ if (!empty($add)) {
 
     if (plugin_supports('mod', $data->modulename, FEATURE_MOD_INTRO, true)) {
         $draftid_editor = file_get_submitted_draft_itemid('introeditor');
-        file_prepare_draft_area($draftid_editor, null, null, null, null);
+        file_prepare_draft_area($draftid_editor, null, null, null, null, array('subdirs'=>true));
         $data->introeditor = array('text'=>'', 'format'=>FORMAT_HTML, 'itemid'=>$draftid_editor); // TODO: add better default
     }
 
index 97724aa..8a7aa14 100644 (file)
@@ -821,7 +821,7 @@ abstract class moodleform_mod extends moodleform {
         $label = is_null($customlabel) ? get_string('moduleintro') : $customlabel;
 
         $mform->addElement('editor', 'introeditor', $label, array('rows' => 10), array('maxfiles' => EDITOR_UNLIMITED_FILES,
-            'noclean' => true, 'context' => $this->context));
+            'noclean' => true, 'context' => $this->context, 'subdirs' => true));
         $mform->setType('introeditor', PARAM_RAW); // no XSS prevention here, users must be trusted
         if ($required) {
             $mform->addRule('introeditor', get_string('required'), 'required', null, 'client');
index 8096fd6..59370e3 100644 (file)
@@ -1129,9 +1129,11 @@ class core_course_renderer extends plugin_renderer_base {
         $classes = trim('coursebox clearfix '. $additionalclasses);
         if ($chelper->get_show_courses() >= self::COURSECAT_SHOW_COURSES_EXPANDED) {
             $nametag = 'h3';
+            $nameattributes = array();
         } else {
             $classes .= ' collapsed';
             $nametag = 'div';
+            $nameattributes = array('class' => 'name');
         }
         $content .= html_writer::start_tag('div', array('class' => $classes)); // .coursebox
 
@@ -1141,7 +1143,7 @@ class core_course_renderer extends plugin_renderer_base {
         $coursename = $chelper->get_course_formatted_name($course);
         $coursenamelink = html_writer::link(new moodle_url('/course/view.php', array('id' => $course->id)),
                 $coursename, array('class' => $course->visible ? '' : 'dimmed'));
-        $content .= html_writer::tag($nametag, $coursenamelink, array('class' => 'name'));
+        $content .= html_writer::tag($nametag, $coursenamelink, $nameattributes);
 
         // If we display course in collapsed form but the course has summary or course contacts, display the link to the info page.
         $content .= html_writer::start_tag('div', array('class' => 'moreinfo'));
@@ -1513,7 +1515,7 @@ class core_course_renderer extends plugin_renderer_base {
                     array('title' => get_string('numberofcourses'), 'class' => 'numberofcourse'));
         }
         $content .= html_writer::start_tag('div', array('class' => 'info'));
-        $content .= html_writer::tag(($depth > 1) ? 'h4' : 'h3', $categoryname, array('class' => 'name'));
+        $content .= html_writer::tag(($depth > 1) ? 'h4' : 'h3', $categoryname);
         $content .= html_writer::end_tag('div'); // .info
 
         // add category content to the output
index 4c42b9f..dc68f34 100644 (file)
@@ -641,7 +641,7 @@ class core_course_courselib_testcase extends advanced_testcase {
                     'numsections' => 5),
                 array('createsections' => true));
 
-        // Ensure all 6 (0-5) sections were created and modinfo/sectioninfo cache works properly
+        // Ensure all 6 (0-5) sections were created and course content cache works properly
         $sectionscreated = array_keys(get_fast_modinfo($course)->get_section_info_all());
         $this->assertEquals(range(0, $course->numsections), $sectionscreated);
 
@@ -656,6 +656,53 @@ class core_course_courselib_testcase extends advanced_testcase {
         $this->assertEquals(range(0, $course->numsections + 1), $sectionscreated);
     }
 
+    public function test_update_course() {
+        global $DB;
+
+        $this->resetAfterTest();
+
+        $defaultcategory = $DB->get_field_select('course_categories', 'MIN(id)', 'parent = 0');
+
+        $course = new stdClass();
+        $course->fullname = 'Apu loves Unit Təsts';
+        $course->shortname = 'test1';
+        $course->idnumber = '1';
+        $course->summary = 'Awesome!';
+        $course->summaryformat = FORMAT_PLAIN;
+        $course->format = 'topics';
+        $course->newsitems = 0;
+        $course->numsections = 5;
+        $course->category = $defaultcategory;
+
+        $created = create_course($course);
+        // Ensure the checks only work on idnumber/shortname that are not already ours.
+        update_course($created);
+
+        $course->shortname = 'test2';
+        $course->idnumber = '2';
+
+        $created2 = create_course($course);
+
+        // Test duplicate idnumber.
+        $created2->idnumber = '1';
+        try {
+            update_course($created2);
+            $this->fail('Expected exception when trying to update a course with duplicate idnumber');
+        } catch (moodle_exception $e) {
+            $this->assertEquals(get_string('courseidnumbertaken', 'error', $created2->idnumber), $e->getMessage());
+        }
+
+        // Test duplicate shortname.
+        $created2->idnumber = '2';
+        $created2->shortname = 'test1';
+        try {
+            update_course($created2);
+            $this->fail('Expected exception when trying to update a course with a duplicate shortname');
+        } catch (moodle_exception $e) {
+            $this->assertEquals(get_string('shortnametaken', 'error', $created2->shortname), $e->getMessage());
+        }
+    }
+
     public function test_course_add_cm_to_section() {
         global $DB;
         $this->resetAfterTest(true);
@@ -684,13 +731,14 @@ class core_course_courselib_testcase extends advanced_testcase {
         $this->assertEquals($cmids[0], $sequence);
 
         // Add a second, this time using courseid variant of parameters.
+        $coursecacherev = $DB->get_field('course', 'cacherev', array('id' => $course->id));
         course_add_cm_to_section($course->id, $cmids[1], 1);
         $sequence = $DB->get_field('course_sections', 'sequence', array('course' => $course->id, 'section' => 1));
         $this->assertEquals($cmids[0] . ',' . $cmids[1], $sequence);
 
-        // Check modinfo was not rebuilt (important for performance if calling
-        // repeatedly).
-        $this->assertNull($DB->get_field('course', 'modinfo', array('id' => $course->id)));
+        // Check that modinfo cache was reset but not rebuilt (important for performance if calling repeatedly).
+        $this->assertGreaterThan($coursecacherev, $DB->get_field('course', 'cacherev', array('id' => $course->id)));
+        $this->assertEmpty(cache::make('core', 'coursemodinfo')->get($course->id));
 
         // Add one to section that doesn't exist (this might rebuild modinfo).
         course_add_cm_to_section($course, $cmids[2], 2);
@@ -1381,6 +1429,9 @@ class core_course_courselib_testcase extends advanced_testcase {
         // Create a category we are going to move this course to.
         $category = $this->getDataGenerator()->create_category();
 
+        // Create a hidden category we are going to move this course to.
+        $categoryhidden = $this->getDataGenerator()->create_category(array('visible' => 0));
+
         // Catch the update events.
         $sink = $this->redirectEvents();
 
@@ -1399,11 +1450,18 @@ class core_course_courselib_testcase extends advanced_testcase {
         // Return the moved course information from the DB.
         $movedcourse = $DB->get_record('course', array('id' => $course->id), '*', MUST_EXIST);
 
+        // Now move the course to the hidden category, this will also trigger an event.
+        move_courses(array($course->id), $categoryhidden->id);
+
+        // Return the moved course information from the DB.
+        $movedcoursehidden = $DB->get_record('course', array('id' => $course->id), '*', MUST_EXIST);
+
         // Now we want to set the sortorder back to what it was before fix_course_sortorder() was called. The reason for
         // this is because update_course() and move_courses() call fix_course_sortorder() which alters the sort order in
         // the DB, but it does not set the value of the sortorder for the course object passed to the event.
         $updatedcourse->sortorder = $sortorder;
         $movedcourse->sortorder = $category->sortorder + MAX_COURSES_IN_CATEGORY - 1;
+        $movedcoursehidden->sortorder = $categoryhidden->sortorder + MAX_COURSES_IN_CATEGORY - 1;
 
         // Capture the events.
         $events = $sink->get_events();
@@ -1431,6 +1489,17 @@ class core_course_courselib_testcase extends advanced_testcase {
         $this->assertEventLegacyData($movedcourse, $event);
         $expectedlog = array($movedcourse->id, 'course', 'move', 'edit.php?id=' . $movedcourse->id, $movedcourse->id);
         $this->assertEventLegacyLogData($expectedlog, $event);
+
+        $event = $events[2];
+        $this->assertInstanceOf('\core\event\course_updated', $event);
+        $this->assertEquals('course', $event->objecttable);
+        $this->assertEquals($movedcoursehidden->id, $event->objectid);
+        $this->assertEquals(context_course::instance($movedcoursehidden->id)->id, $event->contextid);
+        $this->assertEquals($movedcoursehidden, $event->get_record_snapshot('course', $movedcoursehidden->id));
+        $this->assertEquals('course_updated', $event->get_legacy_eventname());
+        $this->assertEventLegacyData($movedcoursehidden, $event);
+        $expectedlog = array($movedcoursehidden->id, 'course', 'move', 'edit.php?id=' . $movedcoursehidden->id, $movedcoursehidden->id);
+        $this->assertEventLegacyLogData($expectedlog, $event);
     }
 
     /**
index 98a967d..e7a47f6 100644 (file)
@@ -25,8 +25,9 @@
 $string['database:unenrol'] = 'Unenrol suspended users';
 $string['dbencoding'] = 'Database encoding';
 $string['dbhost'] = 'Database host';
-$string['dbhost_desc'] = 'Type database server IP address or host name';
+$string['dbhost_desc'] = 'Type database server IP address or host name. Use a system DSN name if using ODBC.';
 $string['dbname'] = 'Database name';
+$string['dbname_desc'] = 'Leave empty if using a DSN name in database host.';
 $string['dbpass'] = 'Database password';
 $string['dbsetupsql'] = 'Database setup command';
 $string['dbsetupsql_desc'] = 'SQL command for special database setup, often used to setup communication encoding - example for MySQL and PostgreSQL: <em>SET NAMES \'utf8\'</em>';
index 0a8d906..39d7881 100644 (file)
@@ -41,7 +41,7 @@ if ($ADMIN->fulltree) {
 
     $settings->add(new admin_setting_configpasswordunmask('enrol_database/dbpass', get_string('dbpass', 'enrol_database'), '', ''));
 
-    $settings->add(new admin_setting_configtext('enrol_database/dbname', get_string('dbname', 'enrol_database'), '', ''));
+    $settings->add(new admin_setting_configtext('enrol_database/dbname', get_string('dbname', 'enrol_database'), get_string('dbname_desc', 'enrol_database'), ''));
 
     $settings->add(new admin_setting_configtext('enrol_database/dbencoding', get_string('dbencoding', 'enrol_database'), '', 'utf-8'));
 
index 60dbb96..6742087 100644 (file)
@@ -46,7 +46,12 @@ class enrol_meta_plugin extends enrol_plugin {
         } else if (empty($instance->name)) {
             $enrol = $this->get_name();
             $course = $DB->get_record('course', array('id'=>$instance->customint1));
-            $coursename = format_string(get_course_display_name_for_list($course));
+            if ($course) {
+                $coursename = format_string(get_course_display_name_for_list($course));
+            } else {
+                // Use course id, if course is deleted.
+                $coursename = $instance->customint1;
+            }
             return get_string('pluginname', 'enrol_' . $enrol) . ' (' . $coursename . ')';
         } else {
             return format_string($instance->name);
index bbdc3bc..fcf9cad 100644 (file)
@@ -144,9 +144,9 @@ class core_enrollib_testcase extends advanced_testcase {
         $course = (array)$course;
         $this->assertEquals($basefields, array_keys($course), '', 0, 10, true);
 
-        $courses = enrol_get_all_users_courses($user2->id, false, 'modinfo');
+        $courses = enrol_get_all_users_courses($user2->id, false, 'timecreated');
         $course = reset($courses);
-        $this->assertTrue(property_exists($course, 'modinfo'));
+        $this->assertTrue(property_exists($course, 'timecreated'));
 
         $courses = enrol_get_all_users_courses($user2->id, false, null, 'id DESC');
         $this->assertEquals(array($course3->id, $course2->id, $course1->id), array_keys($courses));
index 6c82cc3..093a58a 100644 (file)
@@ -114,6 +114,8 @@ YUI.add('moodle-enrol-rolemanager', function(Y) {
                 event.detach();
                 var s = M.str.role, confirmation = {
                     lightbox :  true,
+                    visible  :  true,
+                    centered :  true,
                     title    :  s.confirmunassigntitle,
                     question :  s.confirmunassign,
                     yesLabel :  s.confirmunassignyes,
index 8ccb757..64353f0 100644 (file)
@@ -400,7 +400,7 @@ class core_files_renderer extends plugin_renderer_base {
             <button class="{!}fp-file-cancel">'.get_string('cancel').'</button>
         </div>
     </form>
-    <div class="fp-info">
+    <div class="fp-info clearfix">
         <div class="fp-hr"></div>
         <p class="{!}fp-thumbnail"></p>
         <div class="fp-fileinfo">
@@ -699,7 +699,7 @@ class core_files_renderer extends plugin_renderer_base {
             <button class="{!}fp-select-cancel">'.get_string('cancel').'</button>
         </div>
     </form>
-    <div class="fp-info">
+    <div class="fp-info clearfix">
         <div class="fp-hr"></div>
         <p class="{!}fp-thumbnail"></p>
         <div class="fp-fileinfo">
index 03f035e..07123c7 100644 (file)
@@ -55,27 +55,36 @@ class filter_activitynames extends moodle_text_filter {
 
             $modinfo = get_fast_modinfo($courseid);
             if (!empty($modinfo->cms)) {
-                self::$activitylist = array();      /// We will store all the activities here
+                self::$activitylist = array(); // We will store all the created filters here.
 
-                //Sort modinfo by name length
-                $sortedactivities = fullclone($modinfo->cms);
-                usort($sortedactivities, 'filter_activitynames_comparemodulenamesbylength');
+                // Create array of visible activities sorted by the name length (we are only interested in properties name and url).
+                $sortedactivities = array();
+                foreach ($modinfo->cms as $cm) {
+                    // Exclude labels, hidden activities and activities for group members only.
+                    if ($cm->visible and empty($cm->groupmembersonly) and $cm->has_view()) {
+                        $sortedactivities[] = (object)array(
+                            'name' => $cm->name,
+                            'url' => $cm->url,
+                            'id' => $cm->id,
+                            'namelen' => strlen($cm->name),
+                        );
+                    }
+                }
+                core_collator::asort_objects_by_property($sortedactivities, 'namelen', SORT_NUMERIC);
 
                 foreach ($sortedactivities as $cm) {
-                    //Exclude labels, hidden activities and activities for group members only
-                    if ($cm->visible and empty($cm->groupmembersonly) and $cm->has_view()) {
-                        $title = s(trim(strip_tags($cm->name)));
-                        $currentname = trim($cm->name);
-                        $entitisedname  = s($currentname);
-                        /// Avoid empty or unlinkable activity names
-                        if (!empty($title)) {
-                            $href_tag_begin = html_writer::start_tag('a',
-                                    array('class' => 'autolink', 'title' => $title,
-                                        'href' => $cm->get_url()));
-                            self::$activitylist[$cm->id] = new filterobject($currentname, $href_tag_begin, '</a>', false, true);
-                            if ($currentname != $entitisedname) { /// If name has some entity (&amp; &quot; &lt; &gt;) add that filter too. MDL-17545
-                                self::$activitylist[$cm->id.'-e'] = new filterobject($entitisedname, $href_tag_begin, '</a>', false, true);
-                            }
+                    $title = s(trim(strip_tags($cm->name)));
+                    $currentname = trim($cm->name);
+                    $entitisedname  = s($currentname);
+                    // Avoid empty or unlinkable activity names.
+                    if (!empty($title)) {
+                        $href_tag_begin = html_writer::start_tag('a',
+                                array('class' => 'autolink', 'title' => $title,
+                                    'href' => $cm->url));
+                        self::$activitylist[$cm->id] = new filterobject($currentname, $href_tag_begin, '</a>', false, true);
+                        if ($currentname != $entitisedname) {
+                            // If name has some entity (&amp; &quot; &lt; &gt;) add that filter too. MDL-17545.
+                            self::$activitylist[$cm->id.'-e'] = new filterobject($entitisedname, $href_tag_begin, '</a>', false, true);
                         }
                     }
                 }
@@ -100,14 +109,3 @@ class filter_activitynames extends moodle_text_filter {
         }
     }
 }
-
-
-
-//This function is used to order module names from longer to shorter
-function filter_activitynames_comparemodulenamesbylength($a, $b)  {
-    if (strlen($a->name) == strlen($b->name)) {
-        return 0;
-    }
-    return (strlen($a->name) < strlen($b->name)) ? 1 : -1;
-}
-
index 2d2c670..48164e2 100644 (file)
@@ -111,8 +111,10 @@ $editoroptions = array(
 );
 
 if (!empty($outcome_rec->id)) {
+    $editoroptions['subdirs'] = file_area_contains_subdirs($systemcontext, 'grade', 'outcome', $outcome_rec->id);
     $outcome_rec = file_prepare_standard_editor($outcome_rec, 'description', $editoroptions, $systemcontext, 'grade', 'outcome', $outcome_rec->id);
 } else {
+    $editoroptions['subdirs'] = false;
     $outcome_rec = file_prepare_standard_editor($outcome_rec, 'description', $editoroptions, $systemcontext, 'grade', 'outcome', null);
 }
 
index 9374a9f..04373b1 100644 (file)
@@ -104,8 +104,10 @@ $editoroptions = array(
 );
 
 if (!empty($scale_rec->id)) {
+    $editoroptions['subdirs'] = file_area_contains_subdirs($systemcontext, 'grade', 'scale', $scale_rec->id);
     $scale_rec = file_prepare_standard_editor($scale_rec, 'description', $editoroptions, $systemcontext, 'grade', 'scale', $scale_rec->id);
 } else {
+    $editoroptions['subdirs'] = false;
     $scale_rec = file_prepare_standard_editor($scale_rec, 'description', $editoroptions, $systemcontext, 'grade', 'scale', null);
 }
 $mform = new edit_scale_form(null, compact('gpr', 'editoroptions'));
index 2e18c8c..d4804fc 100644 (file)
@@ -783,7 +783,7 @@ class grade_report_grader extends grade_report {
                     $headerlink = $this->gtree->get_element_header($element, true, $this->get_pref('showactivityicons'), false);
 
                     $itemcell = new html_table_cell();
-                    $itemcell->attributes['class'] = $type . ' ' . $catlevel . ' highlightable';
+                    $itemcell->attributes['class'] = $type . ' ' . $catlevel . ' highlightable'. ' i'. $element['object']->id;
 
                     if ($element['object']->is_hidden()) {
                         $itemcell->attributes['class'] .= ' dimmed_text';
@@ -891,7 +891,7 @@ class grade_report_grader extends grade_report {
                 $eid = $this->gtree->get_grade_eid($grade);
                 $element = array('eid'=>$eid, 'object'=>$grade, 'type'=>'grade');
 
-                $itemcell->attributes['class'] .= ' grade';
+                $itemcell->attributes['class'] .= ' grade i'.$itemid;
                 if ($item->is_category_item()) {
                     $itemcell->attributes['class'] .= ' cat';
                 }
@@ -1226,7 +1226,7 @@ class grade_report_grader extends grade_report {
                 $eid = $this->gtree->get_item_eid($item);
                 $element = $this->gtree->locate_element($eid);
                 $itemcell = new html_table_cell();
-                $itemcell->attributes['class'] = 'controls icons';
+                $itemcell->attributes['class'] = 'controls icons i'.$itemid;
                 $itemcell->text = $this->get_icons($element);
                 $iconsrow->cells[] = $itemcell;
             }
@@ -1252,8 +1252,7 @@ class grade_report_grader extends grade_report {
             foreach ($this->gtree->items as $itemid=>$unused) {
                 $item =& $this->gtree->items[$itemid];
                 $itemcell = new html_table_cell();
-                $itemcell->header = true;
-                $itemcell->attributes['class'] .= ' header range';
+                $itemcell->attributes['class'] .= ' range i'. $itemid;
 
                 $hidden = '';
                 if ($item->is_hidden()) {
@@ -1371,6 +1370,7 @@ class grade_report_grader extends grade_report {
 
                 if ($item->needsupdate) {
                     $avgcell = new html_table_cell();
+                    $avgcell->attributes['class'] = 'i'. $itemid;
                     $avgcell->text = $OUTPUT->container(get_string('error'), 'gradingerror');
                     $avgrow->cells[] = $avgcell;
                     continue;
@@ -1414,6 +1414,7 @@ class grade_report_grader extends grade_report {
 
                 if (!isset($sumarray[$item->id]) || $meancount == 0) {
                     $avgcell = new html_table_cell();
+                    $avgcell->attributes['class'] = 'i'. $itemid;
                     $avgcell->text = '-';
                     $avgrow->cells[] = $avgcell;
 
@@ -1428,6 +1429,7 @@ class grade_report_grader extends grade_report {
                     }
 
                     $avgcell = new html_table_cell();
+                    $avgcell->attributes['class'] = 'i'. $itemid;
                     $avgcell->text = $gradehtml.$numberofgrades;
                     $avgrow->cells[] = $avgcell;
                 }
index 8d5f090..f0ff184 100644 (file)
@@ -183,18 +183,19 @@ M.gradereport_grader.classes.report.prototype.table_highlight_row = function (e,
     tr.all('.cell').toggleClass('hmarked');
 };
 /**
- * Highlights a cell in the table
+ * Highlights a column in the table
  *
  * @function
  * @param {Event} e
  * @param {Y.Node} cell
  */
 M.gradereport_grader.classes.report.prototype.table_highlight_column = function(e, cell) {
-    var column = 0;
-    while (cell = cell.previous('.cell')) {
-        column += parseFloat(cell.getAttribute('colspan')) || 1;
+    // Among cell classes find the one that matches pattern / i[\d]+ /
+    var itemclass = (' '+cell.getAttribute('class')+' ').match(/ (i[\d]+) /);
+    if (itemclass) {
+        // Toggle class .vmarked for all cells in the table with the same class
+        this.table.all('.cell.'+itemclass[1]).toggleClass('vmarked');
     }
-    this.table.all('.c'+column).toggleClass('vmarked');
 };
 /**
  * Builds an object containing information at the relevant cell given either
index 32d12f1..59906d7 100644 (file)
 .path-grade-report-grader .flexible th {
-white-space:normal;
+    white-space: normal;
 }
-
 .gradestable {
-    margin-bottom:0;
+    margin-bottom: 0;
 }
-
 .gradestable th.user img {
-width:20px;
-height:20px;
+    width: 20px;
+    height: 20px;
 }
-
 .gradestable th img {
     vertical-align: text-bottom;
     padding-bottom: 0;
 }
-.gradestable th .grade_icons { margin-top: .3em; }
-.gradestable th img.sorticon { margin-left: .3em; }
-.dir-rtl .gradestable th img.sorticon { margin-left: 0; margin-right: .3em; }
-
+.gradestable th .grade_icons {
+    margin-top: .3em;
+}
+.gradestable th img.sorticon {
+    margin-left: .3em;
+}
+.dir-rtl .gradestable th img.sorticon {
+    margin-left: 0;
+    margin-right: .3em;
+}
 table#user-grades .catlevel2 {
-background-color:#f9f9f9;
+    background-color: #f9f9f9;
+}
+table#user-grades tr.range td.cell {
+    font-weight: 700;
 }
-
 table#user-grades tr.avg td.cell {
-background-color:#efefff;
-font-weight:700;
-color:#00008B;
+    background-color: #efefff;
+    font-weight: 700;
+    color: #00008B;
 }
-
 table#user-grades tr.odd td.cell {
-background-color:#efefef;
-white-space:nowrap;
+    background-color: #efefef;
+    white-space: nowrap;
+}
+table#user-grades tr td.overridden {
+    background-color: #F3E4C0;
+}
+table#user-grades tr.odd td.overridden {
+    background-color: #EFD9A4;
+}
+table#user-grades tr td.ajaxoverridden {
+    background-color: #FFE3A0;
+}
+table#user-grades tr.odd td.ajaxoverridden {
+    background-color: #FFDA83;
 }
-
-table#user-grades tr td.overridden {background-color:#F3E4C0;}
-table#user-grades tr.odd td.overridden {background-color:#EFD9A4;}
-
-table#user-grades tr td.ajaxoverridden {background-color:#FFE3A0;}
-table#user-grades tr.odd td.ajaxoverridden {background-color:#FFDA83;}
-
 table#user-grades tr.even td.excluded {
-background-color:#EABFFF;
+    background-color: #EABFFF;
 }
-
 table#user-grades tr.odd td.excluded {
-background-color:#E5AFFF;
+    background-color: #E5AFFF;
 }
-
 table#user-grades tr.odd th.header {
-background-color:#efefef;
-background-image:none;
+    background-color: #efefef;
+    background-image: none;
+}
+table#user-grades tr.even th.header {
+    background-image: none;
 }
-
 table#user-grades tr.groupavg td.cell {
-background-color:#efffef;
-font-weight:700;
-color:#006400;
+    background-color: #efffef;
+    font-weight: 700;
+    color: #006400;
 }
-
 table#user-grades td.cat,
 table#user-grades td.course {
-font-weight:700;
+    font-weight: 700;
 }
-
 table#user-grades {
-font-size:10px;
-width:auto;
-background-color:transparent;
-border-style:solid;
-border-width:1px;
-margin:20px 0 0;
+    font-size: 10px;
+    width: auto;
+    background-color: transparent;
+    border-style: solid;
+    border-width: 1px;
+    margin: 20px 0 0;
 }
-
 .path-grade-report-grader #overDiv table {
-margin:0;
+    margin: 0;
 }
-
 .path-grade-report-grader #overDiv table td.feedback {
-border:0;
+    border: 0;
 }
-
 .path-grade-report-grader #overDiv .feedback {
-font-size:70%;
-background-color:#ABF;
-color:#000;
-font-family:Verdana;
-font-weight:400;
+    font-size: 70%;
+    background-color: #ABF;
+    color: #000;
+    font-family: Verdana;
+    font-weight: 400;
 }
-
 .path-grade-report-grader #overDiv .caption {
-font-size:70%;
-background-color:#56C;
-color:#CCF;
-font-family:Arial;
-font-weight:700;
+    font-size: 70%;
+    background-color: #56C;
+    color: #CCF;
+    font-family: Arial;
+    font-weight: 700;
 }
-
 .path-grade-report-grader #overDiv .intersection {
-font-size:70%;
-background-color:#ABF;
-color:#000;
-font-family:Verdana;
-font-weight:400;
+    font-size: 70%;
+    background-color: #ABF;
+    color: #000;
+    font-family: Verdana;
+    font-weight: 400;
 }
-
 .path-grade-report-grader #overDiv .intersectioncaption {
-background-color:#56C;
-color:#CCF;
-font-family:Arial;
-font-weight:700;
+    background-color: #56C;
+    color: #CCF;
+    font-family: Arial;
+    font-weight: 700;
 }
-
 .path-grade-report-grader div.submit {
-margin-top:20px;
-text-align:center;
+    margin-top: 20px;
+    text-align: center;
 }
-
 table#user-grades td {
-text-align:right;
-border-style:solid;
-border-width:0 1px 1px 0;
+    text-align: right;
+    border-style: solid;
+    border-width: 0 1px 1px 0;
 }
-
 table#user-grades th.category {
-vertical-align:top;
-border-style:solid;
-border-width:1px 1px 0;
+    vertical-align: top;
+    border-style: solid;
+    border-width: 1px 1px 0;
 }
-
 table#user-grades th.user {
-text-align:left;
-border-style:solid;
-border-width:0 0 1px;
+    text-align: left;
+    border-style: solid;
+    border-width: 1px 0;
 }
-
 table#user-grades th.userfield {
-border-style:solid;
-border-width:0 0 1px 1px;
+    border-style: solid;
+    border-width: 1px;
 }
-
 table#user-grades th.categoryitem,
 table#user-grades td.topleft {
-vertical-align: bottom;
-border-style:solid;
-border-width:0 1px;
+    vertical-align: bottom;
+    border-style: solid;
+    border-width: 0 1px;
 }
-
 .path-grade-report-grader td,.path-grade-report-grader th {
-border-color:#CECECE;
+    border-color: #CECECE;
 }
-
 .path-grade-report-grader table#participants th {
-vertical-align:top;
-width:auto;
+    vertical-align: top;
+    width: auto;
 }
-
 table#user-grades td.fillerfirst {
-border-style:solid;
-border-width:0 0 0 1px;
+    border-style: solid;
+    border-width: 0 0 0 1px;
 }
-
 table#user-grades td.fillerlast {
-border-style:solid;
-border-width:0 1px 0 0;
+    border-style: solid;
+    border-width: 0 1px 0 0;
 }
-
-table#user-grades th.item ,
+table#user-grades th.item,
 table#user-grades th.categoryitem,
 table#user-grades th.courseitem {
-border-bottom-color:#000;
-vertical-align:bottom;
-border-style:solid;
-border-width:1px;
+    border-bottom-color: #000;
+    vertical-align: bottom;
+    border-style: solid;
+    border-width: 1px;
 }
-
 div.gradertoggle {
-display:inline;
-margin-left:20px;
+    display: inline;
+    margin-left: 20px;
 }
-
 table#user-grades th.range {
-text-align:right;
-border-style:solid;
-border-width:1px;
+    text-align: right;
+    border-style: solid;
+    border-width: 1px;
 }
-
 table#user-grades .userpic {
-display:inline;
-margin-right:10px;
+    display: inline;
+    margin-right: 10px;
 }
-
 table#user-grades .quickfeedback {
-border:1px dashed #000;
-margin-left: 10px;
+    border: 1px dashed #000;
+    margin-left: 10px;
+}
+.dir-rtl table#user-grades .quickfeedback {
+    margin-left: 0;
+    margin-right: 10px;
 }
-.dir-rtl table#user-grades .quickfeedback { margin-left: 0; margin-right: 10px;}
-
 .path-grade-report-grader #siteconfiglink {
-text-align:right;
+    text-align: right;
 }
-
 table#user-grades .datesubmitted {
-font-size:.7em;
+    font-size: .7em;
 }
-
 table#user-grades td.cell {
-padding-left:5px;
-padding-right:5px;
-vertical-align:middle;
+    padding-left: 5px;
+    padding-right: 5px;
+    vertical-align: middle;
 }
-
 .path-grade-report-grader table {
-border-collapse:collapse;
-background-color:#fff;
-border-color:#cecece;
+    border-collapse: collapse;
+    background-color: #fff;
+    border-color: #cecece;
 }
-
 .path-grade-report-grader th {
-padding:1px 10px;
+    padding: 1px 10px;
 }
-
 .path-grade-report-grader span.inclusion-links {
-margin:0 5px 0 10px;
+    margin: 0 5px 0 10px;
 }
-
 table#user-grades .item {
-background-color:#e9e9e9;
+    background-color: #e9e9e9;
 }
-
 .path-grade-report-grader table tr.odd th.header {
-background-color:#efefef;
-background-image:none;
-border-width:0 0 1px;
+    background-color: #efefef;
+    background-image: none;
+    border-width: 0 0 1px;
 }
-
 .path-grade-report-grader table tr.heading th.header {
-border-top:1px solid #cecece;
+    border-top: 1px solid #cecece;
 }
-
 table#user-grades tr.heading th.categoryitem,
 table#user-grades tr.heading th.courseitem {
-border-width:0 0 0 1px;
+    border-width: 0 0 0 1px;
 }
-
 table#user-grades th.category.header.catlevel1 {
-vertical-align:top;
-border-style:solid;
-border-width:1px 1px 0 0;
+    vertical-align: top;
+    border-style: solid;
+    border-width: 1px 1px 0 0;
 }
-
 .path-grade-report-grader div.left_scroller th.user a {
-vertical-align:middle;
-margin:0;
-padding:0;
+    vertical-align: middle;
+    margin: 0;
+    padding: 0;
 }
-
 table#user-grades th.categoryitem,
 table#user-grades th.courseitem,
 .path-grade-report-grader table td.topleft {
-vertical-align:bottom;
-border-color:#cecece #cecece #000;
-border-style:solid;
-border-width:0 1px 1px;
+    vertical-align: bottom;
+    border-color: #cecece #cecece #000;
+    border-style: solid;
+    border-width: 0 1px 1px;
 }
-
 .path-grade-report-grader table td.topleft {
-border-bottom:0;
+    border-bottom: 0;
 }
-
 table#user-grades td.topleft {
-background-color:#fff;
+    background-color: #fff;
 }
-
 .path-grade-report-grader th.user img.userpicture {
-border:3px double #cecece;
-vertical-align:top;
-width:2.7em;
-height:2.7em;
-margin-right:10px;
+    border: 3px double #cecece;
+    vertical-align: top;
+    width: 2.7em;
+    height: 2.7em;
+    margin-right: 10px;
 }
-
 .path-grade-report-grader a.quickedit {
-line-height:1em;
-display:block;
-float:right;
-clear:none;
-font-size:9px;
-background-color:transparent;
-margin:.1em 0 0;
+    line-height: 1em;
+    display: block;
+    float: right;
+    clear: none;
+    font-size: 9px;
+    background-color: transparent;
+    margin: .1em 0 0;
 }
-
 .path-grade-report-grader a.quickedit2 {
-display:block;
-float:right;
-clear:none;
-background-color:transparent;
-margin:1.3em 0 0;
+    display: block;
+    float: right;
+    clear: none;
+    background-color: transparent;
+    margin: 1.3em 0 0;
 }
-
 .path-grade-report-grader table#quick_edit {
-border:1px solid #cecece;
-margin:0 auto;
+    border: 1px solid #cecece;
+    margin: 0 auto;
 }
-
 .path-grade-report-grader table#quick_edit td {
-vertical-align:middle;
-border:1px solid #cecece;
-text-align:left;
-margin:0;
-padding:5px;
+    vertical-align: middle;
+    border: 1px solid #cecece;
+    text-align: left;
+    margin: 0;
+    padding: 5px;
 }
-
 .path-grade-report-grader table#quick_edit td img {
-border:3px double #cecece;
-vertical-align:middle;
-padding:0;
+    border: 3px double #cecece;
+    vertical-align: middle;
+    padding: 0;
 }
-
 .path-grade-report-grader td input.text {
-border:1px solid #666;
+    border: 1px solid #666;
 }
-
 .path-grade-report-grader td input.submit {
-margin: 10px 10px 0px 10px;
+    margin: 10px 10px 0px 10px;
 }
-
 .path-grade-report-grader table#quick_edit td.fullname {
-border-left:0;
-padding-left:5px;
+    border-left: 0;
+    padding-left: 5px;
 }
-
 .path-grade-report-grader table#quick_edit td.picture {
-border-right:0;
+    border-right: 0;
 }
-
 .path-grade-report-grader table#quick_edit td.finalgrade input {
-width:5em;
+    width: 5em;
 }
-
 .path-grade-report-grader h1 {
-text-align:center;
-clear:both;
+    text-align: center;
+    clear: both;
 }
-
 .path-grade-report-grader input.center {
-margin:10px auto 0;
+    margin: 10px auto 0;
 }
-
 .path-grade-report-grader .lefttbody {
-width:auto;
-vertical-align:middle;
+    width: auto;
+    vertical-align: middle;
 }
-
 table#user-grades th.fixedcolumn {
-border:1px solid #cecece;
-vertical-align:middle;
+    border: 1px solid #cecece;
+    vertical-align: middle;
 }
-
 .path-grade-report-grader table#fixed_column th {
-border:1px solid #cecece;
-vertical-align:middle;
-border-right-color:#000;
+    border: 1px solid #cecece;
+    vertical-align: middle;
+    border-right-color: #000;
 }
-
 .path-grade-report-grader table#fixed_column th.user{
-border-right-color:#cecece;
+    border-right-color: #cecece;
 }
-
 .path-grade-report-grader table#fixed_column {
-padding-top:20px;
-border-top:1px solid #cecece;
-background-color:#fff;
+    padding-top: 20px;
+    border-top: 1px solid #cecece;
+    background-color: #fff;
 }
-
 .path-grade-report-grader .left_scroller {
-float:left;
-clear:none;
-padding-top:20px;
+    float: left;
+    clear: none;
+    padding-top: 20px;
+}
+.path-grade-report-grader.dir-rtl .left_scroller {
+    float: right;
 }
-.path-grade-report-grader.dir-rtl .left_scroller {float:right;}
-
-
 .path-grade-report-grader .right_scroller {
-width:auto;
-clear:none;
-overflow-x:scroll;
+    width: auto;
+    clear: none;
+    overflow-x: scroll;
 }
-
 .path-grade-report-grader table tr.avg,
 .path-grade-report-grader table tr.groupavg td,
 .path-grade-report-grader table tr.avg td,
@@ -398,48 +352,39 @@ overflow-x:scroll;
 .path-grade-report-grader table tr.range_row,
 .path-grade-report-grader table tr.range_row th,
 div.right_scroller tr {
-height:2em;
+    height: 2em;
 }
-
 table#user-grades tr.groupavg td.cell,
 tr.groupavg th.header {
-background-color:#efffef;
+    background-color: #efffef;
 }
-
 .path-grade-report-grader form td.excluded {
-color:red;
+    color: red;
 }
-
 .path-grade-report-grader .excludedfloater {
-font-weight:700;
-color:red;
-font-size:9px;
-float:left;
+    font-weight: 700;
+    color: red;
+    font-size: 9px;
+    float: left;
 }
-
 .path-grade-report-grader span.gradepass {
-color:#298721;
+    color: #298721;
 }
-
 .path-grade-report-grader span.gradefail {
-color:#890d0d;
+    color: #890d0d;
 }
-
 .path-grade-report-grader .gradeweight {
-color:#461d7c;
-font-weight:700;
+    color: #461d7c;
+    font-weight: 700;
 }
-
 .path-grade-report-grader td select {
-font-size:100%;
-padding:0;
+    font-size: 100%;
+    padding: 0;
 }
-
 .path-grade-report-grader .right_scroller td select {
-font-size:86%;
-padding:0;
+    font-size: 86%;
+    padding: 0;
 }
-
 .path-grade-report-grader tr.avg,
 .path-grade-report-grader tr.controls,
 .path-grade-report-grader td.controls,
@@ -449,24 +394,21 @@ padding:0;
 .path-grade-report-grader th.range,
 .path-grade-report-grader td.range,
 .path-grade-report-grader tr.heading th.range {
-height:2em!important;
-white-space:nowrap;
+    height: 2em!important;
+    white-space: nowrap;
 }
-
 .path-grade-report-grader .heading_name_row th {
-white-space:nowrap;
-width:2000px;
+    white-space: nowrap;
+    width: 2000px;
 }
 
 /*MDL-21088 - IE 7 ignores nowraps on tds or ths so we put a span within it with a nowrap on it*/
 .path-grade-report-grader heading_name_row th span {
-    white-space:nowrap;
+    white-space: nowrap;
 }
-
 .path-grade-report-grader .grade_icons img.ajax {
-float:right;
+    float: right;
 }
-
 .path-grade-report-grader .gradestable th.user,
 .path-grade-report-grader .gradestable th.range,
 .path-grade-report-grader .flexible th,
@@ -475,112 +417,97 @@ float:right;
 .path-grade-report-grader .flexible td a,
 .path-grade-report-grader .gradestable th.range,
 .path-grade-report-grader td {
-white-space:nowrap;
+    white-space: nowrap;
 }
-
 table#user-grades .catlevel1,
 table#user-grades .r1,
 .path-grade-report-grader table tr.even td.cell,
 .path-grade-report-grader table tr.even th {
-background-color:#fff;
+    background-color: #fff;
 }
-
 table#user-grades .catlevel3,
 .path-grade-report-grader table tr.odd td.cell {
-background-color:#efefef;
+    background-color: #efefef;
 }
-
 table#fixed_column tr.odd th ,
 table#user-grades tr.odd th {
-background-color:#efefef;
+    background-color: #efefef;
 }
-
-table#user-grades td.vmarked,
-table#user-grades tr.odd td.vmarked,
 table#user-grades td.vmarked,
 table#user-grades tr.odd td.vmarked,
-table#user-grades tr.even td.vmarked {
-background-color:#fc3;
+table#user-grades tr.avg td.vmarked,
+table#user-grades tr.controls td.vmarked,
+table#user-grades .catlevel1.vmarked,
+table#user-grades .catlevel2.vmarked,
+table#user-grades .catlevel3.vmarked,
+table#user-grades tr.range td.vmarked,
+table#user-grades tr.groupavg td.vmarked {
+    background-color: #fc3;
 }
-
-table#user-grades td.hmarked,
-table#user-grades tr.odd td.hmarked,
 table#user-grades td.hmarked,
 table#user-grades tr.odd td.hmarked,
-table#user-grades tr.even td.hmarked {
-background-color:#ff9;
+table#user-grades tr.even td.hmarked,
+table#user-grades tr.odd th.hmarked,
+table#user-grades tr.even th.hmarked {
+    background-color: #ff9;
 }
-
 table#user-grades td.hmarked.vmarked,
 table#user-grades tr.odd td.hmarked.vmarked,
-table#user-grades td.hmarked.vmarked,
-table#user-grades tr.even td.hmarked.vmarked,
-table#user-grades tr.odd td.hmarked.vmarked {
-background-color:#fc9;
+table#user-grades tr.even td.hmarked.vmarked {
+    background-color: #fc9;
 }
-
 table#user-grades tr.heading,
 table#user-grades .heading td {
-border-style:solid;
-border-width:0;
+    border-style: solid;
+    border-width: 0;
 }
-
 table#user-grades td.userfield,
 table#user-grades th,
 .path-grade-report-grader div.gradeparent,
 .path-grade-report-grader .ie6 form,
 table#user-grades td.ajax {
-text-align:left;
+    text-align: left;
 }
-
 .dir-rtl table#user-grades td.userfield,
 .dir-rtl table#user-grades th,
 .path-grade-report-grader.dir-rtl  div.gradeparent,
 .path-grade-report-grader.dir-rtl  .ie6 form,
 .dir-rtl table#user-grades td.ajax {
-text-align:right;
+    text-align: right;
 }
-
 .path-grade-report-grader .gradeparent {
-    overflow:auto;
+    overflow: auto;
 }
-
-.path-grade-report-grader table tr.avg td.cell,
 table#user-grades td.controls,
-.path-grade-report-grader table tr.avg,
-.path-grade-report-grader table tr.avg td,
-.path-grade-report-grader table tr.avg th {
-background-color:#f3ead8;
+.path-grade-report-grader table tr.avg .cell,
+.path-grade-report-grader table tr.range .cell {
+    background-color: #f3ead8;
 }
-
 .path-grade-report-grader div.left_scroller tr,
 .path-grade-report-grader div.right_scroller tr,
 .path-grade-report-grader div.left_scroller td,
 .path-grade-report-grader div.right_scroller td,
 .path-grade-report-grader div.left_scroller th,
 .path-grade-report-grader div.right_scroller th {
-    height:4.5em;
-    font-size:10px;
+    height: 4.5em;
+    font-size: 10px;
 }
-
 .path-grade-report-grader table th.user,
 .path-grade-report-grader table td.userfield {
-    text-align:left;
-    vertical-align:middle;
+    text-align: left;
+    vertical-align: middle;
 }
-
-.path-grade-report-grader .usersuspended a:link,
-.path-grade-report-grader .usersuspended a:visited {
+.path-grade-report-grader .usersuspended a: link,
+.path-grade-report-grader .usersuspended a: visited {
     color: #666;
 }
-
 .path-grade-report-grader table th.usersuspended img.usersuspendedicon {
     vertical-align: text-bottom;
     margin-left: .45em;
 }
-
-.path-grade-report-grader .grade_icons { margin-bottom: .3em;}
-
+.path-grade-report-grader .grade_icons {
+    margin-bottom: .3em;
+}
 .path-grade-report-grader .yui3-overlay {
     background-color: #FFEE69;
     border-color: #D4C237 #A6982B #A6982B;
@@ -590,7 +517,6 @@ background-color:#f3ead8;
     padding: 2px 5px;
     font-size: 0.7em;
 }
-
 .path-grade-report-grader .yui3-overlay .fullname {
     color: #5F3E00;
     font-weight: bold;
@@ -605,39 +531,49 @@ background-color:#f3ead8;
 /* table#user-grades td */
 /* .grader-report-grader table#user-grades td .yui-panel div.hd { */
 .path-grade-report-grader #tooltipPanel {
-  text-align: left;
+    text-align: left;
 }
-
 .path-grade-report-grader .yui3-overlay a.container-close {
-  margin-top: -3px;
+    margin-top: -3px;
 }
-
-.path-grade-report-grader #hiddentooltiproot, .tooltipDiv {
-  display: none;
+.path-grade-report-grader #hiddentooltiproot,
+.tooltipDiv {
+    display: none;
 }
-
 .path-grade-report-grader.ie .right_scroller {
-overflow-y:hidden;
+    overflow-y: hidden;
 }
-
 .path-grade-report-grader.ie table#fixed_column th {
-height:4.5em;
+    height: 4.5em;
 }
-
 .path-grade-report-grader.ie table#fixed_column tr.avg th {
-height:2.1em;
+    height: 2.1em;
 }
-
 .path-grade-report-grader.ie div.left_scroller td {
-height:4.5em;
+    height: 4.5em;
 }
-
 .path-grade-report-grader.ie6 div.right_scroller {
-margin-top:4em;
-width:auto;
-position:absolute;
+    margin-top: 4em;
+    width: auto;
+    position: absolute;
 }
-
 .path-grade-report-grader.ie6 .excludedfloater {
-font-size:7px;
+    font-size: 7px;
+}
+
+/** MDL-40071 **/
+.path-grade-report-grader.dir-rtl table th.user,
+.path-grade-report-grader.dir-rtl table td.userfield {
+    text-align: right;
 }
+
+/** MDL-40180 **/
+.dir-rtl table#user-grades th.category,
+.dir-rtl table#user-grades th#studentheader,
+.dir-rtl table#user-grades th.user {
+    text-align: right;
+}
+.path-grade-report-grader.dir-rtl th.user img.userpicture {
+    margin-left: 0.5em;
+}
+
index 8c67714..c31ade1 100644 (file)
@@ -46,7 +46,30 @@ class autogroup_form extends moodleform {
 
         $mform =& $this->_form;
 
-        $mform->addElement('header', 'autogroup', get_string('autocreategroups', 'group'));
+        $mform->addElement('header', 'autogroup', get_string('general'));
+
+        $mform->addElement('text', 'namingscheme', get_string('namingscheme', 'group'));
+        $mform->addHelpButton('namingscheme', 'namingscheme', 'group');
+        $mform->addRule('namingscheme', get_string('required'), 'required', null, 'client');
+        $mform->setType('namingscheme', PARAM_TEXT);
+        // There must not be duplicate group names in course.
+        $template = get_string('grouptemplate', 'group');
+        $gname = groups_parse_name($template, 0);
+        if (!groups_get_group_by_name($COURSE->id, $gname)) {
+            $mform->setDefault('namingscheme', $template);
+        }
+
+        $options = array('groups' => get_string('numgroups', 'group'),
+                         'members' => get_string('nummembers', 'group'));
+        $mform->addElement('select', 'groupby', get_string('groupby', 'group'), $options);
+
+        $mform->addElement('text', 'number', get_string('number', 'group'),'maxlength="4" size="4"');
+        $mform->setType('number', PARAM_INT);
+        $mform->addRule('number', null, 'numeric', null, 'client');
+        $mform->addRule('number', get_string('required'), 'required', null, 'client');
+
+        $mform->addElement('header', 'groupmembershdr', get_string('groupmembers', 'group'));
+        $mform->setExpanded('groupmembershdr', true);
 
         $options = array(0=>get_string('all'));
         $options += $this->_customdata['roles'];
@@ -76,20 +99,6 @@ class autogroup_form extends moodleform {
             $mform->setType('cohortid', PARAM_INT);
             $mform->setConstant('cohortid', '0');
         }
-
-        $options = array('groups' => get_string('numgroups', 'group'),
-                         'members' => get_string('nummembers', 'group'));
-        $mform->addElement('select', 'groupby', get_string('groupby', 'group'), $options);
-
-        $mform->addElement('text', 'number', get_string('number', 'group'),'maxlength="4" size="4"');
-        $mform->setType('number', PARAM_INT);
-        $mform->addRule('number', null, 'numeric', null, 'client');
-        $mform->addRule('number', get_string('required'), 'required', null, 'client');
-
-        $mform->addElement('checkbox', 'nosmallgroups', get_string('nosmallgroups', 'group'));
-        $mform->disabledIf('nosmallgroups', 'groupby', 'noteq', 'members');
-        $mform->setAdvanced('nosmallgroups');
-
         $options = array('no'        => get_string('noallocation', 'group'),
                          'random'    => get_string('random', 'group'),
                          'firstname' => get_string('byfirstname', 'group'),
@@ -97,20 +106,13 @@ class autogroup_form extends moodleform {
                          'idnumber'  => get_string('byidnumber', 'group'));
         $mform->addElement('select', 'allocateby', get_string('allocateby', 'group'), $options);
         $mform->setDefault('allocateby', 'random');
-        $mform->setAdvanced('allocateby');
 
-        $mform->addElement('text', 'namingscheme', get_string('namingscheme', 'group'));
-        $mform->addHelpButton('namingscheme', 'namingscheme', 'group');
-        $mform->addRule('namingscheme', get_string('required'), 'required', null, 'client');
-        $mform->setType('namingscheme', PARAM_TEXT);
-        // there must not be duplicate group names in course
-        $template = get_string('grouptemplate', 'group');
-        $gname = groups_parse_name($template, 0);
-        if (!groups_get_group_by_name($COURSE->id, $gname)) {
-            $mform->setDefault('namingscheme', $template);
-        }
+        $mform->addElement('checkbox', 'nosmallgroups', get_string('nosmallgroups', 'group'));
+        $mform->disabledIf('nosmallgroups', 'groupby', 'noteq', 'members');
+
+        $mform->addElement('header', 'groupinghdr', get_string('grouping', 'group'));
 
-        $options = array('0' => get_string('no'),
+        $options = array('0' => get_string('nogrouping', 'group'),
                          '-1'=> get_string('newgrouping', 'group'));
         if ($groupings = groups_get_all_groupings($COURSE->id)) {
             foreach ($groupings as $grouping) {
index 4e0d8b0..b6aa753 100644 (file)
@@ -79,8 +79,10 @@ $returnurl = $CFG->wwwroot.'/group/index.php?id='.$course->id.'&group='.$id;
 // Prepare the description editor: We do support files for group descriptions
 $editoroptions = array('maxfiles'=>EDITOR_UNLIMITED_FILES, 'maxbytes'=>$course->maxbytes, 'trust'=>false, 'context'=>$context, 'noclean'=>true);
 if (!empty($group->id)) {
+    $editoroptions['subdirs'] = file_area_contains_subdirs($context, 'group', 'description', $group->id);
     $group = file_prepare_standard_editor($group, 'description', $editoroptions, $context, 'group', 'description', $group->id);
 } else {
+    $editoroptions['subdirs'] = false;
     $group = file_prepare_standard_editor($group, 'description', $editoroptions, $context, 'group', 'description', null);
 }
 
index 6818ca2..72e6849 100644 (file)
@@ -55,7 +55,6 @@ class group_form extends moodleform {
         $mform->addElement('text','idnumber', get_string('idnumbergroup'), 'maxlength="100" size="10"');
         $mform->addHelpButton('idnumber', 'idnumbergroup');
         $mform->setType('idnumber', PARAM_RAW);
-        $mform->setAdvanced('idnumber');
         if (!has_capability('moodle/course:changeidnumber', $coursecontext)) {
             $mform->hardFreeze('idnumber');
         }
index d44444b..eb597bb 100644 (file)
@@ -57,7 +57,6 @@ class grouping_form extends moodleform {
         $mform->addElement('text','idnumber', get_string('idnumbergrouping'), 'maxlength="100" size="10"');
         $mform->addHelpButton('idnumber', 'idnumbergrouping');
         $mform->setType('idnumber', PARAM_RAW);
-        $mform->setAdvanced('idnumber');
         if (!has_capability('moodle/course:changeidnumber', $coursecontext)) {
             $mform->hardFreeze('idnumber');
         }
index abd8c72..e877b5f 100644 (file)
@@ -46,7 +46,7 @@ class groups_import_form extends moodleform {
 
         //fill in the data depending on page params
         //later using set_data
-        $mform->addElement('header', 'general');
+        $mform->addElement('header', 'general', get_string('general'));
 
         $filepickeroptions = array();
         $filepickeroptions['filetypes'] = '*';
index 54defb4..5c5f659 100644 (file)
--- a/index.php
+++ b/index.php
                         } else {
                             $subtext = get_string('subscribe', 'forum');
                         }
-                        echo $OUTPUT->heading($forumname, 2, 'headingblock header');
+                        echo $OUTPUT->heading($forumname);
                         $suburl = new moodle_url('/mod/forum/subscribe.php', array('id' => $newsforum->id, 'sesskey' => sesskey()));
                         echo html_writer::tag('div', html_writer::link($suburl, $subtext), array('class' => 'subscribelink'));
                     } else {
-                        echo $OUTPUT->heading($forumname, 2, 'headingblock header');
+                        echo $OUTPUT->heading($forumname);
                     }
 
                     forum_print_latest_discussions($SITE, $newsforum, $SITE->newsitems, 'plain', 'p.modified DESC');
                     //wrap frontpage course list in div container
                     echo html_writer::start_tag('div', array('id'=>'frontpage-course-list'));
 
-                    echo $OUTPUT->heading(get_string('mycourses'), 2, 'headingblock header');
+                    echo $OUTPUT->heading(get_string('mycourses'));
                     echo $mycourseshtml;
 
                     //end frontpage course list div container
                     //wrap frontpage course list in div container
                     echo html_writer::start_tag('div', array('id'=>'frontpage-course-list'));
 
-                    echo $OUTPUT->heading(get_string('availablecourses'), 2, 'headingblock header');
+                    echo $OUTPUT->heading(get_string('availablecourses'));
                     echo $availablecourseshtml;
 
                     //end frontpage course list div container
                 //wrap frontpage category names in div container
                 echo html_writer::start_tag('div', array('id'=>'frontpage-category-names'));
 
-                echo $OUTPUT->heading(get_string('categories'), 2, 'headingblock header');
+                echo $OUTPUT->heading(get_string('categories'));
                 echo $courserenderer->frontpage_categories_list();
 
                 //end frontpage category names div container
                 //wrap frontpage category combo in div container
                 echo html_writer::start_tag('div', array('id'=>'frontpage-category-combo'));
 
-                echo $OUTPUT->heading(get_string('courses'), 2, 'headingblock header');
+                echo $OUTPUT->heading(get_string('courses'));
                 echo $courserenderer->frontpage_combo_list();
 
                 //end frontpage category combo div container
diff --git a/install/lang/bs/admin.php b/install/lang/bs/admin.php
new file mode 100644 (file)
index 0000000..af7796d
--- /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'] = 'Greška, neispravna vrijednost "{$a->value}" za "{$a->option}"';
+$string['cliincorrectvalueretry'] = 'Neispravna vrijednost, molim pokušajte ponovo';
+$string['clitypevalue'] = 'otkucaj vrijednost';
+$string['clitypevaluedefault'] = 'otkucaj vrijednost, pritisni "Enter" da biste upotrebili zadanu vrijednost ({$a})';
+$string['cliunknowoption'] = 'Neprepoznate opcije:
+{$a}
+Molimo iskoristite opciju za pomoć.';
+$string['cliyesnoprompt'] = 'otkucaj y (znači DA) ili n (znači NE)';
+$string['environmentrequireinstall'] = 'mora biti instaliran i omogućen';
+$string['environmentrequireversion'] = 'neophodna verzija je {$a->needed} a Vi trenutno koristite verziju {$a->current}';
diff --git a/install/lang/bs/error.php b/install/lang/bs/error.php
new file mode 100644 (file)
index 0000000..b7db900
--- /dev/null
@@ -0,0 +1,48 @@
+<?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['cannotcreatelangdir'] = 'Nije moguće kreirati direktorij jezika';
+$string['cannotcreatetempdir'] = 'Nije moguće kreirati privremeni direktorij';
+$string['cannotdownloadcomponents'] = 'Nije moguće preuzeti komponente.';
+$string['cannotdownloadzipfile'] = 'Nije moguće preuzeti arhivu';
+$string['cannotfindcomponent'] = 'Nije moguće pronaći komponentu.';
+$string['cannotsavemd5file'] = 'Nije moguće sačuvati md5 datoteku.';
+$string['cannotsavezipfile'] = 'Nije moguće sačuvati ZIP arhivu.';
+$string['cannotunzipfile'] = 'Nije moguće raspakovati ZIP datoteku.';
+$string['componentisuptodate'] = 'Komponenta je dostupna u svojoj najnovijoj verziji';
+$string['downloadedfilecheckfailed'] = 'Nije uspjela provjera preuzete datoteke';
+$string['invalidmd5'] = 'Neispravna md5 datoteka';
+$string['missingrequiredfield'] = 'Nedostaje neko obavezno polje';
+$string['remotedownloaderror'] = 'Preuzimanje komponente na Vaš server nije uspjelo. Provjerite podešavanja proksi servera. PHP cURL ekstenzija se preporučuje.<br /><br />Morate da preuzmete <a href="{$a->url}">{$a->url}</a> datoteku ručno, kopirate je u direktorij "{$a->dest}" na svom sereveru i tamo je raspakovati.';
+$string['wrongdestpath'] = 'Pogrešna odredišna putanja';
+$string['wrongsourcebase'] = 'Pogrešna baza izvornog URL-a';
+$string['wrongzipfilename'] = 'Pogrešan naziv arhive';
index 9ea56b6..23fc129 100644 (file)
 defined('MOODLE_INTERNAL') || die();
 
 $string['admindirname'] = 'Administratorski direktorij';
+$string['availablelangs'] = 'Popis dostupnih jezika';
+$string['chooselanguagehead'] = 'Izaberite jezik';
+$string['chooselanguagesub'] = 'Molimo izaberite jezik koji će se koristiti tokom instalacije. Ovaj jezik će, također, biti korišten na nivou stranice kao zadani, mada to naknadno može biti promijenjeno.';
+$string['clialreadyconfigured'] = 'Datoteka config.php već postoji. Molimo, koristite admin/cli/install_database.php ako želite da instalirate ovu stranicu.';
+$string['clialreadyinstalled'] = 'Datoteka config.php već postoji. Upotrebite komandu admin/cli/upgrade.php ako želite da nadogradite ovu stranicu.';
+$string['cliinstallheader'] = 'Moodle {$a} program za instaliranje iz komandne linije';
+$string['databasehost'] = 'Server baze podataka';
+$string['databasename'] = 'Ime baze podataka';
+$string['databasetypehead'] = 'Izaberite driver baze podataka';
 $string['dataroot'] = 'Direktorij podataka';
+$string['datarootpermission'] = 'Ovlaštenja nad direktorijom podataka';
 $string['dbprefix'] = 'Lista prefiksa';
-$string['dirroot'] = 'Direktorij Moodla';
+$string['dirroot'] = 'Moodle direktorij';
+$string['environmenthead'] = 'Provjeravanje Vašeg okruženja...';
+$string['environmentsub2'] = 'Svaka verzija Moodlea ima minimum zahtjeva po pitanju odgovarajuće PHP verzije i nekoliko obaveznih PHP ekstenzija. Puna provjera okruženja se vrši prije svakog instaliranja ili ažuriranja postojeće verzije. Ukoliko ne znate kako da instalirate novu verziju ili omogućite PHP ekstenzije kontaktirajte Vašeg server administratora.';
+$string['errorsinenvironment'] = 'Provjera okruženja nije prošla!';
 $string['installation'] = 'Instalacija';
-$string['memorylimithelp'] = '<p>PHP ograničenje memorije za Vaš server je trenutno podešeno na {$a}.</p>
-
-<p>Ovo možda prouzrokuje Moodlu da kasnije ima problema sa memorijom, posebno ako imate mnogo dozvoljenih modula i /ili mnogo korisnika.</p>
-
-<p>Preporučujemo Vam da konfigurišete PHP sa  visokim ograničenjem ako je moguće, kao 40M. Čineći ovo tamo je nekoliko načina pa možete pokušati: </p><ol>
-<li>Ako ste, opet kompajlisati PHP sa <i>--dostupnim-memorijskim-ograničenjem</i>. Ovo će dozvoliti Moodle da postavi memorijsko ograničenje sam za sebe.</li>
-<li>Ako imate pristup na Vašu php.ini datoteku, možete promijeniti <b>memorijsko_ograničenje</b> podešavanje u nešto kao ovo 40M. Ako nemate pristup možete pitati svog administratora da to uradi za Vas.</li>
-<li>Na nekim PHP serverima možete kreirati  a.ht pristupnu datoteku u Moodle direktoriju koji se sadrži na ovoj liniji:<br /><blockquote>php_vrijednost memorijskog_ograničenja 40M</blockquote></li>
-<br />Kakogod, na istom serveru ovo izbjegavajte <b>sve</b> PHP stranice za rad (vidjet ćete grešku prilkom pregleda stranice) ćete na njima morati izbrisati .htpristupnu datoteku. </li></ol>';
+$string['langdownloaderror'] = 'Nažalost, jezik "{$a}" se ne može preuzeti. Proces instaliranja biće nastavljen na engleskom jeziku.';
+$string['memorylimithelp'] = '<p>PHP ograničenje memorije za Vaš server je trenutno podešeno na {$a}.</p> <p>Ovo podešavanje može kasnije da prouzrokuje da Moodle ima problema sa memorijom, posebno ako imate mnogo aktiviranih modula i/ili mnogo korisnika.</p> <p>Preporučujemo da konfigurišete PHP sa višim ograničenjem ako je moguće, recimo 40M. Postoji nekoliko načina na koje to može da se to uradi:</p><ol> <li>Ako možete, rekompajlirajte PHP sa <i>--enable-memory-limit</i>. Ovo će omogućiti Moodle sistemu da sam postavi memorijsko ograničenje.</li> <li>Ako imate pristup svojoj php.ini datoteci, možete promijeniti vrijednost za <b>memory_limit</b> na, recimo, 40M. Ako nemate pristup toj datoteci možete pitati svog administratora da to uradi umjesto Vas.</li> <li>Na nekim PHP serverima možete da kreirate .htaccess datoteku u Moodle direktoriju koja sadrži red: <blockquote><div>php_value memory_limit 40M</div></blockquote> <p>Međutim, na nekim serverima to će spriječiti prikazivanje <b>svih</b> PHP stranica (vidjećete poruku o grešci kada budete gledali stranice), pa ćete sa tih servera morati da uklonite .htaccess datoteku.</p></li> </ol>';
+$string['paths'] = 'Putanje';
+$string['pathserrcreatedataroot'] = 'Instalaciona procedura ne može da kreira direktorij baze podataka ({$a->dataroot}).';
+$string['pathshead'] = 'Potvrdi putanje';
+$string['pathsrodataroot'] = 'U direktorij za podatke nije moguć upis';
+$string['pathsroparentdataroot'] = 'Nije moguć upis u nadređeni direktorij ({$a->parent}). Instalacioni program ne može da kreira direktorij za podatke ({$a->dataroot}).';
+$string['pathssubadmindir'] = 'Vrlo mali broj web servera koristi /admin kao specijalni URL za pristup raznim podešavanjima (kontrolni panel i sl.). Nažalost, to dovodi do konflikta sa standardnom lokacijom za administratorske stranice u Moodleu. Ovaj problem možete riješiti tako što ćete promijeniti ime administratorskog direktoriju u Vašoj instalaciji, i ovdje upisati to novo ime. Na primjer <em>moodleadmin</em>. Ovo podešavanje će prepraviti administratorske linkove u Moodle sistemu.';
+$string['pathssubdataroot'] = 'Potreban Vam je prostor gdje će Moodle čuvati postavljene datoteke. Ovaj direktorij treba da bude podešen tako da se može čitati i u njega upisivati od strane korisnika web servera (obično \'nobody\' ili \'apache\'), ali istovremeno mora biti dostupan direktno preko weba. Ukoliko ovaj direktorij ne postoji Moodle će pokušati da ga kreira tokom instalacije.';
+$string['pathssubdirroot'] = 'Puna putanja do direktotija za instaliranje Moodlea.';
+$string['pathssubwwwroot'] = 'Puna web adresa putem koje će se pristupati Moodleu. Nije moguće pristupati Moodleu koristeći više adresa. Ako Vaša stranica ima više javnih adresa onda na svima morate da podesite permanentne redirekcije osim na ovoj. Ako je Vaša stranica dostupna sa interneta ali i iz intranet okruženja ovdje upotrijebite javnu adresu i podesite DNS tako da i intranet korisnici mogu da koriste javnu adresu. Ako je adresa netačna promijenite URL u svom web čitaču da biste ponovo pokrenuli instalaciju sa drugačijom vrijednošću.';
+$string['pathsunsecuredataroot'] = 'Dataroot lokacija nije bezbjedna';
+$string['pathswrongadmindir'] = 'Admin direktorij ne postoji';
+$string['phpextension'] = '{$a} PHP ekstenѕija';
 $string['phpversion'] = 'PHP verzija';
-$string['phpversionhelp'] = '<p>Moodle zahtijeva najmanju PHP verziju 4.1.0.</p>
-<p>Trenutno pokrećete verziju {$a}</p>
-<p>Možete nadograditi PHP ili premjestiti na glavnu sa novijom verzijom PHP!</p>';
+$string['phpversionhelp'] = '<p>Moodle zahtijeva najmanje PHP verziju 4.3.0 ili 5.1.0 (5.0.x ima brojne uočene probleme).</p> <p>Trenutno koristite verziju {$a}</p> <p>Morate nadograditi PHP ili premjestiti Moodle na web server sa novijom verzijom PHP-a!</br> (U slučaju verzije 5.0.x možete, također, da se vratite na 4.4.x verziju)</p>';
+$string['welcomep10'] = '{$a->installername} ({$a->installerversion})';
+$string['welcomep20'] = 'Ovu stranicu vidite zato što ste uspješno instalirali i pokrenuli <strong>{$a->packname} {$a->packversion}</strong> paket na svom serveru. Čestitamo!';
+$string['welcomep30'] = 'Ovo izdanje <strong>{$a->installername}</strong> uključuje aplikacije za kreiranje okruženja u kojem će <strong>Moodle</strong> uspješno funkcionisati, konkretno:';
+$string['welcomep40'] = 'Ovaj paket obuhvata i <strong>Moodle {$a->moodlerelease} ({$a->moodleversion})</strong>.';
+$string['welcomep50'] = 'Korištenje svih aplikacija ovog paketa je uređeno njihovim licencama. Kompletan<strong>{$a->installername}</strong> paket je <a href="http://www.opensource.org/docs/definition_plain.html">otvorenog koda</a> i distribuira se pod <a href="http://www.gnu.org/copyleft/gpl.html">GPL</a> licencom.';
+$string['welcomep60'] = 'Naredne stranice će vas provesti kroz nekoliko jednostavnih koraka tokom kojih ćete konfigurisati i podesiti <strong>Moodle</strong> na svom računaru. Možete prihvatiti zadano podešavanja ili ih, opciono, prilagoditi vlastitim potrebama.';
+$string['welcomep70'] = 'Kliknite na dugme za nastavak da biste dalje podešavali <strong>Moodle</strong>.';
 $string['wwwroot'] = 'Web adresa';
diff --git a/install/lang/bs/moodle.php b/install/lang/bs/moodle.php
new file mode 100644 (file)
index 0000000..d503e4a
--- /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'] = 'Jezik';
+$string['next'] = 'Sljedeći';
+$string['previous'] = 'Prethodni';
+$string['reload'] = 'Učitaj ponovo';
index 759ffd9..042b17c 100644 (file)
@@ -30,4 +30,5 @@
 
 defined('MOODLE_INTERNAL') || die();
 
+$string['thisdirection'] = 'rtl';
 $string['thislanguage'] = 'سۆرانی';
index b97501c..20afcce 100644 (file)
@@ -30,6 +30,7 @@
 
 defined('MOODLE_INTERNAL') || die();
 
+$string['cannotcreatedboninstall'] = '<p>Methu creu y gronfa ddata.</p> <p>Dydy’r gronfa ddata benodedig ddim yn bodoli a does gan y defnyddiwr dan sylw ddim hawl i greu cronfa ddata.</p> <p>Dylai gweinyddwr y safle wirio ffurfweddu cronfeydd data.</p>';
 $string['cannotcreatelangdir'] = 'Does dim modd creu cyfeiriadur iaith';
 $string['cannotcreatetempdir'] = 'Does dim modd creu cyfeiriadur dros dro';
 $string['cannotdownloadcomponents'] = 'Does dim modd llwytho cydrannau i lawr';
@@ -39,10 +40,11 @@ $string['cannotsavemd5file'] = 'Does dim modd cadw ffeil md5';
 $string['cannotsavezipfile'] = 'Does dim modd cadw ffeil ZIP';
 $string['cannotunzipfile'] = 'Does dim modd dadzipio\'r ffeil';
 $string['componentisuptodate'] = 'Mae\'r gydran wedi\'i diweddaru';
+$string['dmlexceptiononinstall'] = '<p>Roed gwall cronfa ddata [{$a->errorcode}].<br />{$a->debuginfo}</p>';
 $string['downloadedfilecheckfailed'] = 'Heb lwyddo i wirio\'r ffeil a lwythwyd i lawr';
-$string['invalidmd5'] = 'md5 annilys';
+$string['invalidmd5'] = 'Roedd y newidyn gwirio yn anghywir - rhowch gynnig arall arni';
 $string['missingrequiredfield'] = 'Mae maes gofynnol ar goll';
 $string['remotedownloaderror'] = 'Wedi methu llwytho cydran ar eich gweinydd, gwiriwch osodiadau\'r dirprwy, argymhellir yr estyniad PHP cURL yn gryf.<br /><br />Mae\'n rhaid i chi lwytho\'r ffeil <a href="{$a->url}">{$a->url}</a> eich hun, ei chopïo i "{$a->dest}" ar eich gweinydd, a\'i dad-zipio yno.';
-$string['wrongdestpath'] = 'Llwybr cyrchfan anghywir.';
-$string['wrongsourcebase'] = 'Bôn URL ffynhonnell anghywir.';
-$string['wrongzipfilename'] = 'Enw ffeil ZIP anghywir.';
+$string['wrongdestpath'] = 'Llwybr cyrchfan anghywir';
+$string['wrongsourcebase'] = 'Bôn URL ffynhonnell anghywir';
+$string['wrongzipfilename'] = 'Enw ffeil ZIP anghywir';
index c9bce3c..16b978d 100644 (file)
 
 defined('MOODLE_INTERNAL') || die();
 
-$string['admindirname'] = 'Cyfeiriadur y Gweinyddwr';
+$string['admindirname'] = 'Cyfeiriadur y gweinyddwr';
 $string['availablelangs'] = 'Y pecynnau iaith sydd ar gael';
 $string['chooselanguagehead'] = 'Dewis iaith';
-$string['chooselanguagesub'] = 'Dewiswch iaith ar gyfer y broses osod YN UNIG. Gallwch ddewis yr iaith ar gyfer y safle ac ar gyfer defnyddwyr yn nes ymlaen ar sgrin arall.';
+$string['chooselanguagesub'] = 'Dewiswch iaith ar gyfer y broses osod. Bydd yr iaith hon yn cael ei defnyddio fel yr iaith ddiofyn ar gyfer y safle, ond gellir ei newid yn nes ymlaen.';
+$string['clialreadyconfigured'] = 'Mae\'r ffeil config.php yn bodoli eisoes, defnyddiwch admin/cli/install_database.php os ydych chi am osod y safle hwn.';
+$string['clialreadyinstalled'] = 'Mae\'r ffeil config.php yn bodoli eisoes, defnyddiwch admin/cli/upgrade.php os ydych chi am uwchraddio eich safle.';
 $string['cliinstallheader'] = 'Rhaglen gosod llinell gorchymyn Moodle {$a}';
 $string['databasehost'] = 'Gwesteiwr y gronfa ddata';
 $string['databasename'] = 'Enw\'r gronfa ddata';
 $string['databasetypehead'] = 'Dewis gyrrwr ar gyfer y gronfa ddata';
-$string['dataroot'] = 'Cyfeiriadur Data';
+$string['dataroot'] = 'Cyfeiriadur data';
 $string['datarootpermission'] = 'Hawliau ar gyfer cyfeiriaduron data';
 $string['dbprefix'] = 'Llythrennau Blaen Tablau';
 $string['dirroot'] = 'Cyfeiriadur Moodle';
index f1d5f2a..bd664c1 100644 (file)
@@ -31,7 +31,7 @@
 defined('MOODLE_INTERNAL') || die();
 
 $string['admindirname'] = 'Admin directory';
-$string['availablelangs'] = 'List of available languages';
+$string['availablelangs'] = 'Available language packs';
 $string['chooselanguagehead'] = 'Choose a language';
 $string['chooselanguagesub'] = 'Please choose a language for the installation. This language will also be used as the default language for the site, though it may be changed later.';
 $string['clialreadyconfigured'] = 'File config.php already exists, please use admin/cli/install_database.php if you want to install this site.';
index f129f7e..6cec6af 100644 (file)
@@ -31,7 +31,7 @@
 defined('MOODLE_INTERNAL') || die();
 
 $string['admindirname'] = 'Directorio Admin';
-$string['availablelangs'] = 'Lista de idiomas disponibles';
+$string['availablelangs'] = 'Paquetes de idiomas disponibles';
 $string['chooselanguagehead'] = 'Seleccionar idioma';
 $string['chooselanguagesub'] = 'Por favor, seleccione un idioma para el proceso de instalación. Este idioma se usará también como idioma por defecto del sitio, si bien puede cambiarse más adelante.';
 $string['clialreadyconfigured'] = 'El archivo config.php ya existe, por favor use admin/cli/install_database.php si desea instalar este sitio';
index 7f314dd..3406062 100644 (file)
@@ -32,3 +32,4 @@ defined('MOODLE_INTERNAL') || die();
 
 $string['clianswerno'] = 'एन';
 $string['cliansweryes'] = 'वाई';
+$string['clitypevalue'] = 'टाइप मूल्य';
index dce0b84..70ecf47 100644 (file)
@@ -31,7 +31,7 @@
 defined('MOODLE_INTERNAL') || die();
 
 $string['admindirname'] = 'Adminディレクトリ';
-$string['availablelangs'] = '利用可能な言語一覧';
+$string['availablelangs'] = '利用可能な言語パック';
 $string['chooselanguagehead'] = '言語を選択してください。';
 $string['chooselanguagesub'] = 'インストールのみに使用する言語を選択してください。この言語は、サイトのデフォルト言語としても使用されます。サイト言語は、後で変更することが可能です。';
 $string['clialreadyconfigured'] = 'ファイルconfig.phpは、すでに登録されています。このサイトをインストールしたい場合、admin/cli/install_database.phpを使用してください。';
index 7ab2737..3688080 100644 (file)
@@ -30,6 +30,9 @@
 
 defined('MOODLE_INTERNAL') || die();
 
+$string['cannotcreatedboninstall'] = '<p>Kan databank niet maken.</p>
+<p>De opgegeven databank bestaat niet en de opgegeven gebruiker heeft onvoldoende rechten om de databank te maken.</p>
+<p>De site-beheerder moet de databankconfiguratie controleren.</p>';
 $string['cannotcreatelangdir'] = 'Kan lang map niet maken';
 $string['cannotcreatetempdir'] = 'Kan tijdelijke map niet maken.';
 $string['cannotdownloadcomponents'] = 'Kan componenten niet downloaden.';
@@ -39,6 +42,7 @@ $string['cannotsavemd5file'] = 'Kan md5-bestand niet bewaren.';
 $string['cannotsavezipfile'] = 'Kan ZIP-bestand niet bewaren.';
 $string['cannotunzipfile'] = 'Kon bestand niet unzippen';
 $string['componentisuptodate'] = 'Component is up-to-date';
+$string['dmlexceptiononinstall'] = '<p>Databankfout [{$a->errorcode}].<br />{$a->debuginfo}</p>';
 $string['downloadedfilecheckfailed'] = 'Controle van het gedownloade bestand mislukt';
 $string['invalidmd5'] = 'De controlevariable was fout - probeer nog eens';
 $string['missingrequiredfield'] = 'Vereist veld ontbreekt';
index 26b9927..11940b7 100644 (file)
@@ -30,6 +30,9 @@
 
 defined('MOODLE_INTERNAL') || die();
 
+$string['cannotcreatedboninstall'] = '<p>Kan ikke opprette databasen.</p>
+<p>Den angitte databasen eksisterer ikke og oppgitt bruker har ikke rettigheter til å opprette databasen.</p>
+<p>Portaladministratoren må derfor verifisere databaseoppsettet.</p>';
 $string['cannotcreatelangdir'] = 'Kan ikke opprette mappen \'lang\'.';
 $string['cannotcreatetempdir'] = 'Kan ikke opprette mappen \'temp';
 $string['cannotdownloadcomponents'] = 'Kan ikke laste ned komponentene.';
@@ -39,6 +42,7 @@ $string['cannotsavemd5file'] = 'Kan ikke lagre md5-fil.';
 $string['cannotsavezipfile'] = 'Kan ikke lagre ZIP-fil.';
 $string['cannotunzipfile'] = 'Kan ikke pakke opp filen.';
 $string['componentisuptodate'] = 'Komponenten er oppdatert';
+$string['dmlexceptiononinstall'] = '<p>Det oppstod en databasefeil [{$a->errorcode}].<br />{$a->debuginfo}</p>';
 $string['downloadedfilecheckfailed'] = 'Sjekk av nedlastet fil mislykkes.';
 $string['invalidmd5'] = 'Ugyldig md5, prøv igjen';
 $string['missingrequiredfield'] = 'Noen påkrevde felt mangler';
index d230f67..ef68ec9 100644 (file)
@@ -570,8 +570,9 @@ $string['gradeexport'] = 'Primary grade export methods';
 $string['guestroleid'] = 'Role for guest';
 $string['guestroleid_help'] = 'This role is automatically assigned to the guest user. It is also temporarily assigned to not enrolled users that enter the course via guest enrolment plugin.';
 $string['helpadminseesall'] = 'Do admins see all calendar events or just those that apply to themselves?';
-$string['helpcalendarsettings'] = 'Configure various calendar and date/time-related aspects of Moodle';
 $string['helpcalendarcustomexport'] = 'Enable custom date range export option in calendar exports. Calendar exports must be enabled before this is effective.';
+$string['helpcalendarsettings'] = 'Configure various calendar and date/time-related aspects of Moodle';
+$string['helpcalendartype'] = 'This is the calendar type that will be used throughout your site.';
 $string['helpexportlookahead'] = 'How many days in the future does the calendar look for events during export for the custom export option?';
 $string['helpexportlookback'] = 'How many days in the past does the calendar look for events during export for the custom export option?';
 $string['helpforcetimezone'] = 'You can allow users to individually select their timezone, or force a timezone for everyone.';
index 47403f2..2ec0c97 100644 (file)
@@ -42,6 +42,7 @@ $string['cachedef_coursecat'] = 'Course categories lists for particular user';
 $string['cachedef_coursecatrecords'] = 'Course categories records';
 $string['cachedef_coursecontacts'] = 'List of course contacts';
 $string['cachedef_coursecattree'] = 'Course categories tree';
+$string['cachedef_coursemodinfo'] = 'Accumulated information about modules and sections for each course';
 $string['cachedef_databasemeta'] = 'Database meta information';
 $string['cachedef_eventinvalidation'] = 'Event invalidation';
 $string['cachedef_externalbadges'] = 'External badges for particular user';
index 75d51b9..7a6a6af 100644 (file)
@@ -93,6 +93,7 @@ $string['export'] = 'Export';
 $string['exportbutton'] = 'Export';
 $string['exportcalendar'] = 'Export calendar';
 $string['for'] = 'for';
+$string['forcecalendartype'] = 'Force calendar';
 $string['fri'] = 'Fri';
 $string['friday'] = 'Friday';
 $string['generateurlbutton'] = 'Get calendar URL';
@@ -138,6 +139,7 @@ $string['pollinterval'] = 'Update interval';
 $string['pollinterval_help'] = 'How often you would like the calendar to update with new events.';
 $string['preferences'] = 'Preferences';
 $string['preferences_available'] = 'Your personal preferences';
+$string['preferredcalendar'] = 'Preferred calendar';
 $string['pref_lookahead'] = 'Upcoming events look-ahead';
 $string['pref_lookahead_help'] = 'This sets the (maximum) number of days in the future that an event has to start in in order to be displayed as an upcoming event. Events that start beyond this will never be displayed as upcoming. Please note that <strong>there is no guarantee</strong> that all events starting in this time frame will be displayed; if there are too many (more than the "Maximum upcoming events" preference) then the most distant events will not be shown.';
 $string['pref_maxevents'] = 'Maximum upcoming events';
index db4c617..beaab45 100644 (file)
@@ -42,7 +42,7 @@ $string['createautomaticgrouping'] = 'Create automatic grouping';
 $string['creategroup'] = 'Create group';
 $string['creategrouping'] = 'Create grouping';
 $string['creategroupinselectedgrouping'] = 'Create group in grouping';
-$string['createingrouping'] = 'Create in grouping';
+$string['createingrouping'] = 'Grouping of auto-created groups';
 $string['createorphangroup'] = 'Create orphan group';
 $string['databaseupgradegroups'] = 'Groups version is now {$a}';
 $string['defaultgrouping'] = 'Default grouping';
@@ -80,7 +80,7 @@ $string['filtergroups'] = 'Filter groups by:';
 $string['group'] = 'Group';
 $string['groupaddedsuccesfully'] = 'Group {$a} added successfully';
 $string['groupaddedtogroupingsuccesfully'] = 'Group {$a->groupname} added to grouping {$a->groupingname} successfully';
-$string['groupby'] = 'Specify';
+$string['groupby'] = 'Auto create based on';
 $string['groupdescription'] = 'Group description';
 $string['groupinfo'] = 'Info about selected group';
 $string['groupinfomembers'] = 'Info about selected members';
@@ -149,6 +149,7 @@ $string['newgrouping'] = 'New grouping';
 $string['newpicture'] = 'New picture';
 $string['newpicture_help'] = 'Select an image in JPG or PNG format. The image will be cropped to a square and resized to 100x100 pixels.';
 $string['noallocation'] = 'No allocation';
+$string['nogrouping'] = 'No grouping';
 $string['nogroups'] = 'There are no groups set up in this course yet';
 $string['nogroupsassigned'] = 'No groups assigned';
 $string['nopermissionforcreation'] = 'Can\'t create group "{$a}" as you don\'t have the required permissions';
@@ -169,7 +170,7 @@ $string['removefromgroupconfirm'] = 'Do you really want to remove user "{$a->use
 $string['removegroupingsmembers'] = 'Remove all groups from groupings';
 $string['removegroupsmembers'] = 'Remove all group members';
 $string['removeselectedusers'] = 'Remove selected users';
-$string['selectfromrole'] = 'Select members from role';
+$string['selectfromrole'] = 'Select members with role';
 $string['showgroupsingrouping'] = 'Show groups in grouping';
 $string['showmembersforgroup'] = 'Show members for group';
 $string['toomanygroups'] = 'Insufficient users to populate this number of groups - there are only {$a} users in the selected role.';
index e65e1ea..f1e7958 100644 (file)
@@ -91,6 +91,8 @@ $string['type_cachelock'] = 'Cache lock handler';
 $string['type_cachelock_plural'] = 'Cache lock handlers';
 $string['type_cachestore'] = 'Cache store';
 $string['type_cachestore_plural'] = 'Cache stores';
+$string['type_calendartype'] = 'Calendar type';
+$string['type_calendartype_plural'] = 'Calendar types';
 $string['type_coursereport'] = 'Course report';
 $string['type_coursereport_plural'] = 'Course reports';
 $string['type_editor'] = 'Editor';
index 9359ab8..efcf9a1 100644 (file)
@@ -194,13 +194,13 @@ function uninstall_plugin($type, $name) {
             }
         }
 
-        // clear course.modinfo for courses that used this module
-        $sql = "UPDATE {course}
-                   SET modinfo=''
-                 WHERE id IN (SELECT DISTINCT course
+        // Increment course.cacherev for courses that used this module.
+        // This will force cache rebuilding on the next request.
+        increment_revision_number('course', 'cacherev',
+                "id IN (SELECT DISTINCT course
                                 FROM {course_modules}
-                               WHERE module=?)";
-        $DB->execute($sql, array($module->id));
+                               WHERE module=?)",
+                array($module->id));
 
         // delete all the course module records
         $DB->delete_records('course_modules', array('module' => $module->id));
index 061ecd3..598a88e 100644 (file)
@@ -895,38 +895,29 @@ function badges_add_course_navigation(navigation_node $coursenode, stdClass $cou
 
     $coursecontext = context_course::instance($course->id);
     $isfrontpage = (!$coursecontext || $course->id == $SITE->id);
+    $canmanage = has_any_capability(array('moodle/badges:viewawarded',
+                                          'moodle/badges:createbadge',
+                                          'moodle/badges:awardbadge',
+                                          'moodle/badges:configurecriteria',
+                                          'moodle/badges:configuremessages',
+                                          'moodle/badges:configuredetails',
+                                          'moodle/badges:deletebadge'), $coursecontext);
 
-    if (!empty($CFG->enablebadges) && !empty($CFG->badges_allowcoursebadges) && !$isfrontpage) {
-        if (has_capability('moodle/badges:configuredetails', $coursecontext)) {
-            $coursenode->add(get_string('coursebadges', 'badges'), null,
-                    navigation_node::TYPE_CONTAINER, null, 'coursebadges',
-                    new pix_icon('i/badge', get_string('coursebadges', 'badges')));
+    if (!empty($CFG->enablebadges) && !empty($CFG->badges_allowcoursebadges) && !$isfrontpage && $canmanage) {
+        $coursenode->add(get_string('coursebadges', 'badges'), null,
+                navigation_node::TYPE_CONTAINER, null, 'coursebadges',
+                new pix_icon('i/badge', get_string('coursebadges', 'badges')));
 
-            if (has_capability('moodle/badges:viewawarded', $coursecontext)) {
-                $url = new moodle_url('/badges/index.php',
-                        array('type' => BADGE_TYPE_COURSE, 'id' => $course->id));
+        $url = new moodle_url('/badges/index.php', array('type' => BADGE_TYPE_COURSE, 'id' => $course->id));
 
-                $coursenode->get('coursebadges')->add(get_string('managebadges', 'badges'), $url,
-                    navigation_node::TYPE_SETTING, null, 'coursebadges');
-            }
-
-            if (has_capability('moodle/badges:createbadge', $coursecontext)) {
-                $url = new moodle_url('/badges/newbadge.php',
-                        array('type' => BADGE_TYPE_COURSE, 'id' => $course->id));
-
-                $coursenode->get('coursebadges')->add(get_string('newbadge', 'badges'), $url,
-                        navigation_node::TYPE_SETTING, null, 'newbadge');
-            }
-        } else if (has_capability('moodle/badges:awardbadge', $coursecontext)) {
-            $coursenode->add(get_string('coursebadges', 'badges'), null,
-                    navigation_node::TYPE_CONTAINER, null, 'coursebadges',
-                    new pix_icon('i/badge', get_string('coursebadges', 'badges')));
+        $coursenode->get('coursebadges')->add(get_string('managebadges', 'badges'), $url,
+            navigation_node::TYPE_SETTING, null, 'coursebadges');
 
-            $url = new moodle_url('/badges/index.php',
-                    array('type' => BADGE_TYPE_COURSE, 'id' => $course->id));
+        if (has_capability('moodle/badges:createbadge', $coursecontext)) {
+            $url = new moodle_url('/badges/newbadge.php', array('type' => BADGE_TYPE_COURSE, 'id' => $course->id));
 
-            $coursenode->get('coursebadges')->add(get_string('managebadges', 'badges'), $url,
-                    navigation_node::TYPE_SETTING, null, 'coursebadges');
+            $coursenode->get('coursebadges')->add(get_string('newbadge', 'badges'), $url,
+                    navigation_node::TYPE_SETTING, null, 'newbadge');
         }
     }
 }
index 64727eb..68592ae 100644 (file)
@@ -42,6 +42,11 @@ require_once(__DIR__ . '/../../filelib.php');
  */
 class behat_util extends testing_util {
 
+    /**
+     * The behat test site fullname and shortname.
+     */
+    const BEHATSITENAME = "Acceptance test site";
+
     /**
      * @var array Files to skip when resetting dataroot folder
      */
@@ -70,8 +75,8 @@ class behat_util extends testing_util {
         $options = array();
         $options['adminuser'] = 'admin';
         $options['adminpass'] = 'admin';
-        $options['fullname'] = 'Acceptance test site';
-        $options['shortname'] = 'Acceptance test site';
+        $options['fullname'] = self::BEHATSITENAME;
+        $options['shortname'] = self::BEHATSITENAME;
 
         install_cli_database($options, false);
 
index d5ec8ac..aa1a433 100644 (file)
@@ -43,6 +43,8 @@ class core_component {
     protected static $classmap = null;
     /** @var null list of some known files that can be included. */
     protected static $filemap = null;
+    /** @var int|float core version. */
+    protected static $version = null;
     /** @var array list of the files to map. */
     protected static $filestomap = array('lib.php', 'settings.php');
 
@@ -133,8 +135,11 @@ class core_component {
                 include($cachefile);
                 if (!is_array($cache)) {
                     // Something is very wrong.
-                } else if (!isset($cache['plugintypes']) or !isset($cache['plugins']) or !isset($cache['subsystems']) or !isset($cache['classmap'])) {
+                } else if (!isset($cache['version'])) {
                     // Something is very wrong.
+                } else if ((float) $cache['version'] !== (float) self::fetch_core_version()) {
+                    // Outdated cache. We trigger an error log to track an eventual repetitive failure of float comparison.
+                    error_log('Resetting core_component cache after core upgrade to version ' . self::fetch_core_version());
                 } else if ($cache['plugintypes']['mod'] !== "$CFG->dirroot/mod") {
                     // $CFG->dirroot was changed.
                 } else {
@@ -227,6 +232,7 @@ class core_component {
             'plugins'     => self::$plugins,
             'classmap'    => self::$classmap,
             'filemap'     => self::$filemap,
+            'version'     => self::$version,
         );
 
         return '<?php
@@ -249,6 +255,23 @@ $cache = '.var_export($cache, true).';
 
         self::fill_classmap_cache();
         self::fill_filemap_cache();
+        self::fetch_core_version();
+    }
+
+    /**
+     * Get the core version.
+     *
+     * In order for this to work properly, opcache should be reset beforehand.
+     *
+     * @return float core version.
+     */
+    protected static function fetch_core_version() {
+        global $CFG;
+        if (self::$version === null) {
+            require($CFG->dirroot . '/version.php');
+            self::$version = $version;
+        }
+        return self::$version;
     }
 
     /**
@@ -341,6 +364,7 @@ $cache = '.var_export($cache, true).';
             'qtype'         => $CFG->dirroot.'/question/type',
             'mod'           => $CFG->dirroot.'/mod',
             'auth'          => $CFG->dirroot.'/auth',
+            'calendartype'  => $CFG->dirroot.'/calendar/type',
             'enrol'         => $CFG->dirroot.'/enrol',
             'message'       => $CFG->dirroot.'/message/output',
             'block'         => $CFG->dirroot.'/blocks',
@@ -870,9 +894,7 @@ $cache = '.var_export($cache, true).';
         $versions = array();
 
         // Main version first.
-        $version = null;
-        include($CFG->dirroot.'/version.php');
-        $versions['core'] = $version;
+        $versions['core'] = self::fetch_core_version();
 
         // The problem here is tha the component cache might be stable,
         // we want this to work also on frontpage without resetting the component cache.
index 400723d..c2be7ab 100644 (file)
@@ -282,6 +282,7 @@ class manager {
                 if ($CFG->admin !== 'admin' and strpos($observer['includefile'], '/admin/') === 0) {
                     $observer['includefile'] = preg_replace('|^/admin/|', '/'.$CFG->admin.'/', $observer['includefile']);
                 }
+                $observer['includefile'] = $CFG->dirroot . '/' . ltrim($observer['includefile'], '/');
                 if (!file_exists($observer['includefile'])) {
                     debugging("Invalid 'includefile' detected in $file observer definition", DEBUG_DEVELOPER);
                     continue;
index 1b05837..e22d81b 100644 (file)
@@ -1077,7 +1077,7 @@ class completion_info {
      * Obtains a list of activities for which completion is enabled on the
      * course. The list is ordered by the section order of those activities.
      *
-     * @return array Array from $cmid => $cm of all activities with completion enabled,
+     * @return cm_info[] Array from $cmid => $cm of all activities with completion enabled,
      *   empty array if none
      */
     public function get_activities() {
index 3bf7a8f..f253c43 100644 (file)
@@ -763,16 +763,12 @@ abstract class condition_info_base {
      * Obtains a string describing all availability restrictions (even if
      * they do not apply any more).
      *
-     * @global stdClass $COURSE
-     * @global moodle_database $DB
-     * @param object $modinfo Usually leave as null for default. Specify when
-     *   calling recursively from inside get_fast_modinfo. The value supplied
-     *   here must include list of all CMs with 'id' and 'name'
+     * @param course_modinfo|null $modinfo Usually leave as null for default. Specify when
+     *   calling recursively from inside get_fast_modinfo()
      * @return string Information string (for admin) about all restrictions on
      *   this item
      */
     public function get_full_information($modinfo=null) {
-        global $COURSE, $DB;
         $this->require_data();
 
         $information = '';
@@ -780,16 +776,10 @@ abstract class condition_info_base {
 
         // Completion conditions
         if (count($this->item->conditionscompletion) > 0) {
-            if ($this->item->course == $COURSE->id) {
-                $course = $COURSE;
-            } else {
-                $course = $DB->get_record('course', array('id' => $this->item->course),
-                        'id, enablecompletion, modinfo, sectioncache', MUST_EXIST);
+            if (!$modinfo) {
+                $modinfo = get_fast_modinfo($this->item->course);
             }
             foreach ($this->item->conditionscompletion as $cmid => $expectedcompletion) {
-                if (!$modinfo) {
-                    $modinfo = get_fast_modinfo($course);
-                }
                 if (empty($modinfo->cms[$cmid])) {
                     continue;
                 }
@@ -948,8 +938,6 @@ abstract class condition_info_base {
      * - This does not take account of the viewhiddenactivities capability.
      *   That should apply later.
      *
-     * @global stdClass $COURSE
-     * @global moodle_database $DB
      * @uses COMPLETION_COMPLETE
      * @uses COMPLETION_COMPLETE_FAIL
      * @uses COMPLETION_COMPLETE_PASS
@@ -960,13 +948,11 @@ abstract class condition_info_base {
      *   required for all course-modules, to make the front page and similar
      *   pages work more quickly (works only for current user)
      * @param int $userid If set, specifies a different user ID to check availability for
-     * @param object $modinfo Usually leave as null for default. Specify when
-     *   calling recursively from inside get_fast_modinfo. The value supplied
-     *   here must include list of all CMs with 'id' and 'name'
+     * @param course_modinfo|null $modinfo Usually leave as null for default. Specify when
+     *   calling recursively from inside get_fast_modinfo()
      * @return bool True if this item is available to the user, false otherwise
      */
     public function is_available(&$information, $grabthelot=false, $userid=0, $modinfo=null) {
-        global $COURSE, $DB;
         $this->require_data();
 
         $available = true;
@@ -974,20 +960,13 @@ abstract class condition_info_base {
 
         // Check each completion condition
         if (count($this->item->conditionscompletion) > 0) {
-            if ($this->item->course == $COURSE->id) {
-                $course = $COURSE;
-            } else {
-                $course = $DB->get_record('course', array('id' => $this->item->course),
-                        'id, enablecompletion, modinfo, sectioncache', MUST_EXIST);
+            if (!$modinfo) {
+                $modinfo = get_fast_modinfo($this->item->course);
             }
-
-            $completion = new completion_info($course);
+            $completion = new completion_info($modinfo->get_course());
             foreach ($this->item->conditionscompletion as $cmid => $expectedcompletion) {
                 // If this depends on a deleted module, handle that situation
                 // gracefully.
-                if (!$modinfo) {
-                    $modinfo = get_fast_modinfo($course);
-                }
                 if (empty($modinfo->cms[$cmid])) {
                     global $PAGE;
                     if (isset($PAGE) && strpos($PAGE->pagetype, 'course-view-')===0) {
index 9cd0f6c..4699a90 100644 (file)
@@ -844,7 +844,7 @@ class coursecat implements renderable, cacheable_object, IteratorAggregate {
         $ctxselect = context_helper::get_preload_record_columns_sql('ctx');
         $fields = array('c.id', 'c.category', 'c.sortorder',
                         'c.shortname', 'c.fullname', 'c.idnumber',
-                        'c.startdate', 'c.visible');
+                        'c.startdate', 'c.visible', 'c.cacherev');
         if (!empty($options['summary'])) {
             $fields[] = 'c.summary';
             $fields[] = 'c.summaryformat';
@@ -2056,8 +2056,6 @@ class coursecat implements renderable, cacheable_object, IteratorAggregate {
  *     was called with option 'summary'. Otherwise will be retrieved from DB on first request
  * @property-read string $format Course format. Retrieved from DB on first request
  * @property-read int $showgrades Retrieved from DB on first request
- * @property-read string $sectioncache Retrieved from DB on first request
- * @property-read string $modinfo Retrieved from DB on first request
  * @property-read int $newsitems Retrieved from DB on first request
  * @property-read int $startdate
  * @property-read int $marker Retrieved from DB on first request
@@ -2076,6 +2074,7 @@ class coursecat implements renderable, cacheable_object, IteratorAggregate {
  * @property-read int $requested Retrieved from DB on first request
  * @property-read int $enablecompletion Retrieved from DB on first request
  * @property-read int $completionnotify Retrieved from DB on first request
+ * @property-read int $cacherev
  *
  * @package    core
  * @subpackage course
index 41c7bb2..2cce5c2 100644 (file)
@@ -170,6 +170,9 @@ function cron_run() {
             mtrace(' Cleaned up read notifications');
         }
 
+        mtrace(' Deleting temporary files...');
+        cron_delete_from_temp();
+
         mtrace("...finished clean-up tasks");
 
     } // End of occasional clean-up tasks
@@ -767,3 +770,48 @@ function notify_login_failures() {
 
     return true;
 }
+
+/**
+ * Delete files and directories older than one week from directory provided by $CFG->tempdir.
+ *
+ * @exception Exception Failed reading/accessing file or directory
+ * @return bool True on successful file and directory deletion; otherwise, false on failure
+ */
+function cron_delete_from_temp() {
+    global $CFG;
+
+    $tmpdir = $CFG->tempdir;
+    // Default to last weeks time.
+    $time = strtotime('-1 week');
+
+    try {
+        $dir = new RecursiveDirectoryIterator($tmpdir);
+        // Show all child nodes prior to their parent.
+        $iter = new RecursiveIteratorIterator($dir, RecursiveIteratorIterator::CHILD_FIRST);
+
+        for ($iter->rewind(); $iter->valid(); $iter->next()) {
+            $node = $iter->getPathname();
+            if (!is_readable($node)) {
+                continue;
+            }
+            // Check if file or directory is older than the given time.
+            if ($iter->getMTime() < $time) {
+                if ($iter->isDir() && !$iter->isDot()) {
+                    if (@rmdir($node) === false) {
+                        mtrace("Failed removing directory '$node'.");
+                    }
+                }
+                if ($iter->isFile()) {
+                    if (@unlink($node) === false) {
+                        mtrace("Failed removing file '$node'.");
+                    }
+                }
+            }
+        }
+    } catch (Exception $e) {
+        mtrace('Failed reading/accessing file or directory.');
+        return false;
+    }
+
+    return true;
+}
index c7ef03c..f2044c3 100644 (file)
@@ -810,8 +810,7 @@ function get_courses_search($searchterms, $sort, $page, $recordsperpage, &$total
     $ccjoin = "LEFT JOIN {context} ctx ON (ctx.instanceid = c.id AND ctx.contextlevel = :contextlevel)";
     $params['contextlevel'] = CONTEXT_COURSE;
 
-    $fields = array_diff(array_keys($DB->get_columns('course')), array('modinfo', 'sectioncache'));
-    $sql = "SELECT c.".join(',c.',$fields)." $ccselect
+    $sql = "SELECT c.* $ccselect
               FROM {course} c
            $ccjoin
              WHERE $searchcond AND c.id <> ".SITEID."
@@ -1232,6 +1231,36 @@ function update_timezone_records($timezones) {
     }
 }
 
+/**
+ * Increment standard revision field.
+ *
+ * The revision are based on current time and are incrementing.
+ * There is a protection for runaway revisions, it may not go further than
+ * one hour into future.
+ *
+ * The field has to be XMLDB_TYPE_INTEGER with size 10.
+ *
+ * @param string $table
+ * @param string $field name of the field containing revision
+ * @param string $select use empty string when updating all records
+ * @param array $params optional select parameters
+ */
+function increment_revision_number($table, $field, $select, array $params = null) {
+    global $DB;
+
+    $now = time();
+    $sql = "UPDATE {{$table}}
+                   SET $field = (CASE
+                       WHEN $field IS NULL THEN $now
+                       WHEN $field < $now THEN $now
+                       WHEN $field > $now + 3600 THEN $now
+                       ELSE $field + 1 END)";
+    if ($select) {
+        $sql = $sql . " WHERE $select";
+    }
+    $DB->execute($sql, $params);
+}
+
 
 /// MODULE FUNCTIONS /////////////////////////////////////////////////
 
index d03f1c3..305fdbc 100644 (file)
@@ -239,4 +239,10 @@ $definitions = array(
         'simplekeys' => true,
         'ttl' => 3600,
     ),
+    // Accumulated information about course modules and sections used to print course view page (user-independed).
+    // Used in function get_fast_modinfo(), reset in function rebuild_course_cache().
+    'coursemodinfo' => array(
+        'mode' => cache_store::MODE_APPLICATION,
+        'simplekeys' => true,
+    ),
 );
index cb6b597..8095fae 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8" ?>
-<XMLDB PATH="lib/db" VERSION="20130412" COMMENT="XMLDB file for core Moodle tables"
+<XMLDB PATH="lib/db" VERSION="20130905" COMMENT="XMLDB file for core Moodle tables"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:noNamespaceSchemaLocation="../../lib/xmldb/xmldb.xsd"
 >
@@ -79,8 +79,6 @@
         <FIELD NAME="summaryformat" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
         <FIELD NAME="format" TYPE="char" LENGTH="21" NOTNULL="true" DEFAULT="topics" SEQUENCE="false"/>
         <FIELD NAME="showgrades" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="1" SEQUENCE="false"/>
-        <FIELD NAME="sectioncache" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
-        <FIELD NAME="modinfo" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
         <FIELD NAME="newsitems" TYPE="int" LENGTH="5" NOTNULL="true" DEFAULT="1" SEQUENCE="false"/>
         <FIELD NAME="startdate" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
         <FIELD NAME="marker" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
         <FIELD NAME="groupmodeforce" TYPE="int" LENGTH="4" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
         <FIELD NAME="defaultgroupingid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="default grouping used in course modules, does not have key intentionally"/>
         <FIELD NAME="lang" TYPE="char" LENGTH="30" NOTNULL="true" SEQUENCE="false"/>
+        <FIELD NAME="calendartype" TYPE="char" LENGTH="30" NOTNULL="true" SEQUENCE="false"/>
         <FIELD NAME="theme" TYPE="char" LENGTH="50" NOTNULL="true" SEQUENCE="false"/>
         <FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
         <FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
         <FIELD NAME="requested" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
         <FIELD NAME="enablecompletion" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="1 = allow use of 'completion' progress-tracking on this course. 0 = disable completion tracking on this course."/>
         <FIELD NAME="completionnotify" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Notify users when they complete this course"/>
+        <FIELD NAME="cacherev" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Incrementing revision for validating the course content cache"/>
       </FIELDS>
       <KEYS>
         <KEY NAME="primary" TYPE="primary" FIELDS="id"/>
         <FIELD NAME="city" TYPE="char" LENGTH="120" NOTNULL="true" SEQUENCE="false"/>
         <FIELD NAME="country" TYPE="char" LENGTH="2" NOTNULL="true" SEQUENCE="false"/>
         <FIELD NAME="lang" TYPE="char" LENGTH="30" NOTNULL="true" DEFAULT="en" SEQUENCE="false"/>
+        <FIELD NAME="calendartype" TYPE="char" LENGTH="30" NOTNULL="true" DEFAULT="gregorian" SEQUENCE="false"/>
         <FIELD NAME="theme" TYPE="char" LENGTH="50" NOTNULL="true" SEQUENCE="false"/>
         <FIELD NAME="timezone" TYPE="char" LENGTH="100" NOTNULL="true" DEFAULT="99" SEQUENCE="false"/>
         <FIELD NAME="firstaccess" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
index 494ec78..1fad3d4 100644 (file)
@@ -2363,5 +2363,66 @@ function xmldb_main_upgrade($oldversion) {
         upgrade_main_savepoint(true, 2013082700.00);
     }
 
+    if ($oldversion < 2013090500.01) {
+        // Define field calendartype to be added to course.
+        $table = new xmldb_table('course');
+        $field = new xmldb_field('calendartype', XMLDB_TYPE_CHAR, '30', null, XMLDB_NOTNULL, null, null);
+
+        // Conditionally launch add field calendartype.
+        if (!$dbman->field_exists($table, $field)) {
+            $dbman->add_field($table, $field);
+        }
+
+        // Define field calendartype to be added to user.
+        $table = new xmldb_table('user');
+        $field = new xmldb_field('calendartype', XMLDB_TYPE_CHAR, '30', null, XMLDB_NOTNULL, null, 'gregorian');
+
+        // Conditionally launch add field calendartype.
+        if (!$dbman->field_exists($table, $field)) {
+            $dbman->add_field($table, $field);
+        }
+
+        // Main savepoint reached.
+        upgrade_main_savepoint(true, 2013090500.01);
+    }
+
+    if ($oldversion < 2013091000.02) {
+
+        // Define field cacherev to be added to course.
+        $table = new xmldb_table('course');
+        $field = new xmldb_field('cacherev', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0', 'completionnotify');
+
+        // Conditionally launch add field cacherev.
+        if (!$dbman->field_exists($table, $field)) {
+            $dbman->add_field($table, $field);
+        }
+
+        // Main savepoint reached.
+        upgrade_main_savepoint(true, 2013091000.02);
+    }
+
+    if ($oldversion < 2013091000.03) {
+
+        // Define field modinfo to be dropped from course.
+        $table = new xmldb_table('course');
+        $field = new xmldb_field('modinfo');
+
+        // Conditionally launch drop field modinfo.
+        if ($dbman->field_exists($table, $field)) {
+            $dbman->drop_field($table, $field);
+        }
+
+        // Define field sectioncache to be dropped from course.
+        $field = new xmldb_field('sectioncache');
+
+        // Conditionally launch drop field sectioncache.
+        if ($dbman->field_exists($table, $field)) {
+            $dbman->drop_field($table, $field);
+        }
+
+        // Main savepoint reached.
+        upgrade_main_savepoint(true, 2013091000.03);
+    }
+
     return true;
 }
index fd66aed..9f3d1cc 100644 (file)
@@ -24,7 +24,9 @@
 
 $string['pluginname'] = 'Paragraph styles';
 $string['title'] = 'Paragraph styles';
-$string['h1'] = 'Title';
-$string['h2'] = 'Heading';
+$string['h3'] = 'Heading 1';
+$string['h4'] = 'Heading 2';
+$string['h5'] = 'Heading 3';
 $string['blockquote'] = 'Quoted';
 $string['p'] = 'Plain';
+$string['pre'] = 'Pre-formatted';
index 5480467..6f40550 100644 (file)
@@ -36,9 +36,11 @@ function atto_title_init_editor($elementid) {
                               'atto_title',
                               array('class'=>'icon'));
 
-    $PAGE->requires->strings_for_js(array('h1',
-                                          'h2',
+    $PAGE->requires->strings_for_js(array('h3',
+                                          'h4',
+                                          'h5',
                                           'blockquote',
+                                          'pre',
                                           'p'), 'atto_title');
     $PAGE->requires->yui_module('moodle-atto_title-button',
                                 'M.atto_title.init',
index a6542af..1cae0c4 100644 (file)
@@ -24,6 +24,6 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2013080900;        // The current plugin version (Date: YYYYMMDDXX).
+$plugin->version   = 2013083000;        // The current plugin version (Date: YYYYMMDDXX).
 $plugin->requires  = 2013050100;        // Requires this Moodle version.
 $plugin->component = 'atto_title';  // Full name of the plugin (used for diagnostics).
index f99197f..641b0e5 100644 (file)
Binary files a/lib/editor/atto/plugins/title/yui/build/moodle-atto_title-button/moodle-atto_title-button-debug.js and b/lib/editor/atto/plugins/title/yui/build/moodle-atto_title-button/moodle-atto_title-button-debug.js differ
index 94806e6..2235d53 100644 (file)
Binary files a/lib/editor/atto/plugins/title/yui/build/moodle-atto_title-button/moodle-atto_title-button-min.js and b/lib/editor/atto/plugins/title/yui/build/moodle-atto_title-button/moodle-atto_title-button-min.js differ
index f99197f..641b0e5 100644 (file)
Binary files a/lib/editor/atto/plugins/title/yui/build/moodle-atto_title-button/moodle-atto_title-button.js and b/lib/editor/atto/plugins/title/yui/build/moodle-atto_title-button/moodle-atto_title-button.js differ
index aaa1c20..d6241e4 100644 (file)
  */
 M.atto_title = M.atto_title || {
     init : function(params) {
-        var click_h1 = function(e, elementid) {
-            e.preventDefault();
-            if (!M.editor_atto.is_active(elementid)) {
-                M.editor_atto.focus(elementid);
-            }
-            document.execCommand('formatBlock', false, '<h1>');
+        var click_h3 = function(e, elementid) {
+            M.atto_title.change_title(e, elementid, '<h3>');
         };
-        var click_h2 = function(e, elementid) {
-            e.preventDefault();
-            if (!M.editor_atto.is_active(elementid)) {
-                M.editor_atto.focus(elementid);
-            }
-            document.execCommand('formatBlock', false, '<h2>');
+        var click_h4 = function(e, elementid) {
+            M.atto_title.change_title(e, elementid, '<h4>');
+        };
+        var click_h5 = function(e, elementid) {
+            M.atto_title.change_title(e, elementid, '<h5>');
+        };
+        var click_pre = function(e, elementid) {
+            M.atto_title.change_title(e, elementid, '<pre>');
         };
         var click_blockquote = function(e, elementid) {
-            e.preventDefault();
-            if (!M.editor_atto.is_active(elementid)) {
-                M.editor_atto.focus(elementid);
-            }
-            document.execCommand('formatBlock', false, '<blockquote>');
+            M.atto_title.change_title(e, elementid, '<blockquote>');
         };
         var click_p = function(e, elementid) {
-            e.preventDefault();
-            if (!M.editor_atto.is_active(elementid)) {
-                M.editor_atto.focus(elementid);
-            }
-            document.execCommand('formatBlock', false, '<p>');
+            M.atto_title.change_title(e, elementid, '<p>');
         };
 
-        var h1 = '<h1>' +  M.util.get_string('h1', 'atto_title') + '</h1>';
-        var h2 = '<h2>' +  M.util.get_string('h2', 'atto_title') + '</h2>';
-        var blockquote = '<p>&nbsp;&nbsp;&nbsp;&nbsp;' +  M.util.get_string('blockquote', 'atto_title') + '</p>';
-        var p = '<p>' +  M.util.get_string('p', 'atto_title') + '</p>';
+        var h3 = '<h3>' + M.util.get_string('h3', 'atto_title') + '</h3>';
+        var h4 = '<h4>' + M.util.get_string('h4', 'atto_title') + '</h4>';
+        var h5 = '<h5>' + M.util.get_string('h5', 'atto_title') + '</h5>';
+        var pre = '<pre>' + M.util.get_string('pre', 'atto_title') + '</pre>';
+        var blockquote = '<p>&nbsp;&nbsp;&nbsp;&nbsp;' + M.util.get_string('blockquote', 'atto_title') + '</p>';
+        var p = '<p>' + M.util.get_string('p', 'atto_title') + '</p>';
 
         M.editor_atto.add_toolbar_menu(params.elementid,
                                                   'title',
                                                   params.icon,
                                                   [
-                                                      {'text' : h1, 'handler' : click_h1},
-                                                      {'text' : h2, 'handler' : click_h2},
+                                                      {'text' : h3, 'handler' : click_h3},
+                                                      {'text' : h4, 'handler' : click_h4},
+                                                      {'text' : h5, 'handler' : click_h5},
+                                                      {'text' : pre, 'handler' : click_pre},
                                                       {'text' : blockquote, 'handler' : click_blockquote},
                                                       {'text' : p, 'handler' : click_p}
                                                   ]);
+    },
+
+    /**
+     * Handle a choice from the menu (insert the node in the text editor matching elementid).
+     * @param event e - The event that triggered this.
+     * @param string elementid - The id of the editor
+     * @param string node - The html to insert
+     */
+    change_title : function(e, elementid, node) {
+        e.preventDefault();
+        if (!M.editor_atto.is_active(elementid)) {
+            M.editor_atto.focus(elementid);
+        }
+        document.execCommand('formatBlock', false, node);
     }
 };
index 437ae34..e794e85 100644 (file)
@@ -1,16 +1,34 @@
 div.editor_atto {
     background-color: white;
-    border: 1px solid #444;
+    border: 1px solid #BBB;
+}
+
+.editor_atto + textarea {
+    width: 100%;
+    padding: 0;
 }
 
 div.editor_atto_toolbar {
     display: block;
+    background: #F2F2F2;
+    min-height: 36px;
+    border-top: 1px solid #BBB;
+    border-left: 1px solid #BBB;
+    border-right: 1px solid #BBB;
 }
 
 div.editor_atto_toolbar button {
-    padding: 1px;
+    margin-top: 5px;
+    margin-bottom: 0px;
     padding: 3px;
+    background: none;
+    border: none;
+ }
+
+div.editor_atto_toolbar button[disabled] {
+    opacity: .45;
 }
+
 div.editor_atto_toolbar button img {
     padding: 1px;
 }
@@ -69,3 +87,4 @@ body.dir-rtl div.editor_atto_toolbar button.atto_indent_button {
     display: block;
     margin: 5px;
 }
+
index 1793749..3ee4606 100644 (file)
@@ -530,7 +530,7 @@ function enrol_get_my_courses($fields = NULL, $sort = 'visible DESC,sortorder AS
     $basefields = array('id', 'category', 'sortorder',
                         'shortname', 'fullname', 'idnumber',
                         'startdate', 'visible',
-                        'groupmode', 'groupmodeforce');
+                        'groupmode', 'groupmodeforce', 'cacherev');
 
     if (empty($fields)) {
         $fields = $basefields;
index efe335a..2260e4a 100644 (file)
@@ -80,6 +80,31 @@ function file_encode_url($urlbase, $path, $forcedownload=false, $https=false) {
     return $return;
 }
 
+/**
+ * Detects if area contains subdirs,
+ * this is intended for file areas that are attached to content
+ * migrated from 1.x where subdirs were allowed everywhere.
+ *
+ * @param context $context
+ * @param string $component
+ * @param string $filearea
+ * @param string $itemid
+ * @return bool
+ */
+function file_area_contains_subdirs(context $context, $component, $filearea, $itemid) {
+    global $DB;
+
+    if (!isset($itemid)) {
+        // Not initialised yet.
+        return false;
+    }
+
+    // Detect if any directories are already present, this is necessary for content upgraded from 1.x.
+    $select = "contextid = :contextid AND component = :component AND filearea = :filearea AND itemid = :itemid AND filepath <> '/' AND filename = '.'";
+    $params = array('contextid'=>$context->id, 'component'=>$component, 'filearea'=>$filearea, 'itemid'=>$itemid);
+    return $DB->record_exists_select('files', $select, $params);
+}
+
 /**
  * Prepares 'editor' formslib element from data in database
  *
index 3fbfb62..29cb8d8 100644 (file)
@@ -32,7 +32,6 @@ defined('MOODLE_INTERNAL') || die();
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 abstract class file_packer {
-
     /**
      * Archive files and store the result in file storage.
      *
@@ -45,9 +44,12 @@ abstract class file_packer {
      * @param string $filename file name
      * @param int $userid user ID
      * @param bool $ignoreinvalidfiles true means ignore missing or invalid files, false means abort on any error
+     * @param file_progress $progress Progress indicator callback or null if not required
      * @return stored_file|bool false if error stored_file instance if ok
      */
-    public abstract function archive_to_storage(array $files, $contextid, $component, $filearea, $itemid, $filepath, $filename, $userid = NULL, $ignoreinvalidfiles=true);
+    public abstract function archive_to_storage(array $files, $contextid,
+            $component, $filearea, $itemid, $filepath, $filename,
+            $userid = NULL, $ignoreinvalidfiles=true, file_progress $progress = null);
 
     /**
      * Archive files and store the result in os file.
@@ -55,9 +57,11 @@ abstract class file_packer {
      * @param array $files array with zip paths as keys (archivepath=>ospathname or archivepath=>stored_file)
      * @param string $archivefile path to target zip file
      * @param bool $ignoreinvalidfiles true means ignore missing or invalid files, false means abort on any error
+     * @param file_progress $progress Progress indicator callback or null if not required
      * @return bool true if file created, false if not
      */
-    public abstract function archive_to_pathname(array $files, $archivefile, $ignoreinvalidfiles=true);
+    public abstract function archive_to_pathname(array $files, $archivefile,
+            $ignoreinvalidfiles=true, file_progress $progress = null);
 
     /**
      * Extract file to given file path (real OS filesystem), existing files are overwritten.
@@ -65,9 +69,11 @@ abstract class file_packer {
      * @param stored_file|string $archivefile full pathname of zip file or stored_file instance
      * @param string $pathname target directory
      * @param array $onlyfiles only extract files present in the array
+     * @param file_progress $progress Progress indicator callback or null if not required
      * @return array|bool list of processed files; false if error
      */
-    public abstract function extract_to_pathname($archivefile, $pathname, array $onlyfiles = NULL);
+    public abstract function extract_to_pathname($archivefile, $pathname,
+            array $onlyfiles = NULL, file_progress $progress = null);
 
     /**
      * Extract file to given file path (real OS filesystem), existing files are overwritten.
@@ -79,9 +85,12 @@ abstract class file_packer {
      * @param int $itemid item ID
      * @param string $pathbase file path
      * @param int $userid user ID
+     * @param file_progress $progress Progress indicator callback or null if not required
      * @return array|bool list of processed files; false if error
      */
-    public abstract function extract_to_storage($archivefile, $contextid, $component, $filearea, $itemid, $pathbase, $userid = NULL);
+    public abstract function extract_to_storage($archivefile, $contextid,
+            $component, $filearea, $itemid, $pathbase, $userid = NULL,
+            file_progress $progress = null);
 
     /**
      * Returns array of info about all files in archive.
diff --git a/lib/filestorage/file_progress.php b/lib/filestorage/file_progress.php
new file mode 100644 (file)
index 0000000..23ae8c9
--- /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/>.
+
+/**
+ * Simple interface for receiving progress during long-running file
+ * operations.
+ *
+ * In some cases progress can be reported precisely. In other cases,
+ * progress is indeterminate which means that the progress function is called
+ * periodically but without information on completion.
+ *
+ * @package core_files
+ * @copyright 2013 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+interface file_progress {
+    /**
+     * @var int Constant used for indeterminate progress.
+     */
+    const INDETERMINATE = -1;
+
+    /**
+     * Called during a file processing operation that reports progress.
+     *
+     * This function will be called periodically during the operation, assuming
+     * it is successful.
+     *
+     * If numbers (rather than INDETERMINATE) are provided, then:
+     * - The $progress value will either be the same as last call, or increased
+     *   by some value (not necessarily 1).
+     * - The $progress value will be less than or equal to the $max value.
+     *
+     * There is no guarantee that this function will be called for every value
+     * in the range, or that it will be called with $progress == $max.
+     *
+     * @param int $progress Current progress, or INDETERMINATE if unknown
+     * @param int $max Max progress, or INDETERMINATE if unknown
+     */
+    public function progress($progress = self::INDETERMINATE, $max = self::INDETERMINATE);
+}
index fbce9af..d56aa52 100644 (file)
@@ -25,6 +25,8 @@
 
 defined('MOODLE_INTERNAL') || die();
 
+require_once($CFG->dirroot . '/lib/filestorage/file_progress.php');
+
 /**
  * Class representing local files stored in a sha1 file pool.
  *
@@ -480,11 +482,13 @@ class stored_file {
      *
      * @param file_packer $packer file packer instance
      * @param string $pathname target directory
+     * @param file_progress $progress Progress indicator callback or null if not required
      * @return array|bool list of processed files; false if error
      */
-    public function extract_to_pathname(file_packer $packer, $pathname) {
+    public function extract_to_pathname(file_packer $packer, $pathname,
+            file_progress $progress = null) {
         $archivefile = $this->get_content_file_location();
-        return $packer->extract_to_pathname($archivefile, $pathname);
+        return $packer->extract_to_pathname($archivefile, $pathname, null, $progress);
     }
 
     /**
@@ -497,11 +501,14 @@ class stored_file {
      * @param int $itemid item ID
      * @param string $pathbase path base
      * @param int $userid user ID
+     * @param file_progress $progress Progress indicator callback or null if not required
      * @return array|bool list of processed files; false if error
      */
-    public function extract_to_storage(file_packer $packer,&nb