Merge branch 'MDL-43104-master' of git://github.com/andrewnicols/moodle
authorSam Hemelryk <sam@moodle.com>
Mon, 9 Dec 2013 21:27:45 +0000 (10:27 +1300)
committerSam Hemelryk <sam@moodle.com>
Mon, 9 Dec 2013 21:27:45 +0000 (10:27 +1300)
56 files changed:
.jshintrc
admin/index.php
admin/renderer.php
backup/util/ui/base_moodleform.class.php
backup/util/ui/yui/build/moodle-backup-backupselectall/moodle-backup-backupselectall-debug.js [new file with mode: 0644]
backup/util/ui/yui/build/moodle-backup-backupselectall/moodle-backup-backupselectall-min.js [new file with mode: 0644]
backup/util/ui/yui/build/moodle-backup-backupselectall/moodle-backup-backupselectall.js [new file with mode: 0644]
backup/util/ui/yui/build/moodle-backup-confirmcancel/moodle-backup-confirmcancel-debug.js [new file with mode: 0644]
backup/util/ui/yui/build/moodle-backup-confirmcancel/moodle-backup-confirmcancel-min.js [new file with mode: 0644]
backup/util/ui/yui/build/moodle-backup-confirmcancel/moodle-backup-confirmcancel.js [new file with mode: 0644]
backup/util/ui/yui/confirmcancel/confirmcancel.js [deleted file]
backup/util/ui/yui/src/backupselectall/build.json [new file with mode: 0644]
backup/util/ui/yui/src/backupselectall/js/backupselectall.js [moved from backup/util/ui/yui/backupselectall/backupselectall.js with 88% similarity]
backup/util/ui/yui/src/backupselectall/js/backupselectall.json [new file with mode: 0644]
backup/util/ui/yui/src/backupselectall/meta/backupselectall.json [new file with mode: 0644]
backup/util/ui/yui/src/confirmcancel/build.json [new file with mode: 0644]
backup/util/ui/yui/src/confirmcancel/js/confirmcancel.js [new file with mode: 0644]
backup/util/ui/yui/src/confirmcancel/meta/confirmcancel.json [new file with mode: 0644]
blocks/navigation/yui/build/moodle-block_navigation-navigation/moodle-block_navigation-navigation-debug.js
blocks/navigation/yui/build/moodle-block_navigation-navigation/moodle-block_navigation-navigation-min.js
blocks/navigation/yui/build/moodle-block_navigation-navigation/moodle-block_navigation-navigation.js
blocks/navigation/yui/src/navigation/js/navigation.js
config-dist.php
course/lib.php
enrol/yui/notification/notification.js [deleted file]
lib/behat/classes/behat_config_manager.php
lib/classes/component.php
lib/classes/event/base.php
lib/classes/update/checker.php
lib/classes/update/deployer.php
lib/classes/useragent.php
lib/navigationlib.php
lib/tests/event_test.php
lib/yui/build/moodle-core-blocks/moodle-core-blocks-debug.js
lib/yui/build/moodle-core-blocks/moodle-core-blocks-min.js
lib/yui/build/moodle-core-blocks/moodle-core-blocks.js
lib/yui/build/moodle-core-dragdrop/moodle-core-dragdrop-debug.js [new file with mode: 0644]
lib/yui/build/moodle-core-dragdrop/moodle-core-dragdrop-min.js [new file with mode: 0644]
lib/yui/build/moodle-core-dragdrop/moodle-core-dragdrop.js [new file with mode: 0644]
lib/yui/dragdrop/dragdrop.js [deleted file]
lib/yui/src/blocks/js/blocks.js
lib/yui/src/dragdrop/build.json [new file with mode: 0644]
lib/yui/src/dragdrop/js/dragdrop.js [new file with mode: 0644]
lib/yui/src/dragdrop/meta/dragdrop.json [new file with mode: 0644]
mdeploy.php
mod/assign/backup/moodle2/restore_assign_activity_task.class.php
repository/filesystem/lib.php
theme/afterburner/style/afterburner_styles.css
theme/bootstrapbase/readme_moodle.txt
theme/bootstrapbase/yui/build/moodle-theme_bootstrapbase-bootstrap/moodle-theme_bootstrapbase-bootstrap-debug.js
theme/bootstrapbase/yui/build/moodle-theme_bootstrapbase-bootstrap/moodle-theme_bootstrapbase-bootstrap-min.js
theme/bootstrapbase/yui/build/moodle-theme_bootstrapbase-bootstrap/moodle-theme_bootstrapbase-bootstrap.js
theme/bootstrapbase/yui/src/bootstrap/js/bootstrapcollapse.js
theme/bootstrapbase/yui/src/bootstrap/js/bootstrapdropdown.js
theme/bootstrapbase/yui/src/bootstrap/js/bootstrapengine.js
theme/clean/layout/secure.php

index 06de646..89f05f1 100644 (file)
--- a/.jshintrc
+++ b/.jshintrc
@@ -42,7 +42,7 @@
     "sub":          false,
     "supernew":     false,
     "maxerr":       500,
-    "maxlen":       150,
+    "maxlen":       180,
     "passfail":     false,
     "latedef":      true
 }
index ea9f37d..a07104c 100644 (file)
@@ -47,7 +47,9 @@ if (!function_exists('iconv')) {
 
 define('NO_OUTPUT_BUFFERING', true);
 
-if (empty($_GET['cache']) and empty($_POST['cache']) and empty($_GET['sesskey']) and empty($_POST['sesskey'])) {
+if ((isset($_GET['cache']) and $_GET['cache'] === '0')
+        or (isset($_POST['cache']) and $_POST['cache'] === '0')
+        or (!isset($_POST['cache']) and !isset($_GET['cache']) and empty($_GET['sesskey']) and empty($_POST['sesskey']))) {
     // Prevent caching at all cost when visiting this page directly,
     // we redirect to self once we known no upgrades are necessary.
     // Note: $_GET and $_POST are used here intentionally because our param cleaning is not loaded yet.
@@ -90,9 +92,7 @@ $newaddonreq    = optional_param('installaddonrequest', null, PARAM_RAW);
 
 // Set up PAGE.
 $url = new moodle_url('/admin/index.php');
-if ($cache) {
-    $url->param('cache', 1);
-}
+$url->param('cache', $cache);
 $PAGE->set_url($url);
 unset($url);
 
@@ -267,12 +267,13 @@ if (!$cache and $version > $CFG->version) {  // upgrade
     $PAGE->set_pagelayout('maintenance');
     $PAGE->set_popup_notification_allowed(false);
 
+    /** @var core_admin_renderer $output */
+    $output = $PAGE->get_renderer('core', 'admin');
+
     if (upgrade_stale_php_files_present()) {
         $PAGE->set_title($stradministration);
         $PAGE->set_cacheable(false);
 
-        /** @var core_admin_renderer $output */
-        $output = $PAGE->get_renderer('core', 'admin');
         echo $output->upgrade_stale_php_files_page();
         die();
     }
@@ -287,8 +288,6 @@ if (!$cache and $version > $CFG->version) {  // upgrade
         $PAGE->set_heading($strdatabasechecking);
         $PAGE->set_cacheable(false);
 
-        /** @var core_admin_renderer $output */
-        $output = $PAGE->get_renderer('core', 'admin');
         echo $output->upgrade_confirm_page($a->newversion, $maturity, $testsite);
         die();
 
@@ -302,8 +301,6 @@ if (!$cache and $version > $CFG->version) {  // upgrade
         $PAGE->set_heading($strcurrentrelease);
         $PAGE->set_cacheable(false);
 
-        /** @var core_admin_renderer $output */
-        $output = $PAGE->get_renderer('core', 'admin');
         echo $output->upgrade_environment_page($release, $envstatus, $environment_results);
         die();
 
@@ -315,23 +312,13 @@ if (!$cache and $version > $CFG->version) {  // upgrade
         $PAGE->set_heading($strplugincheck);
         $PAGE->set_cacheable(false);
 
-        $reloadurl = new moodle_url('/admin/index.php', array('confirmupgrade' => 1, 'confirmrelease' => 1));
-
-        /** @var core_admin_renderer $output */
-        $output = $PAGE->get_renderer('core', 'admin');
-
-        // check plugin dependencies first
-        $failed = array();
-        if (!core_plugin_manager::instance()->all_plugins_ok($version, $failed)) {
-            echo $output->unsatisfied_dependencies_page($version, $failed, $reloadurl);
-            die();
-        }
-        unset($failed);
+        $reloadurl = new moodle_url('/admin/index.php', array('confirmupgrade' => 1, 'confirmrelease' => 1, 'cache' => 0));
 
         if ($fetchupdates) {
-            // no sesskey support guaranteed here
-            if (empty($CFG->disableupdatenotifications)) {
-                \core\update\checker::instance()->fetch();
+            // No sesskey support guaranteed here, because sessions might not work yet.
+            $updateschecker = \core\update\checker::instance();
+            if ($updateschecker->enabled()) {
+                $updateschecker->fetch();
             }
             redirect($reloadurl);
         }
@@ -342,6 +329,7 @@ if (!$cache and $version > $CFG->version) {  // upgrade
 
             $deploydata = $deployer->submitted_data();
             if (!empty($deploydata)) {
+                // No sesskey support guaranteed here, because sessions might not work yet.
                 echo $output->upgrade_plugin_confirm_deploy_page($deployer, $deploydata);
                 die();
             }
@@ -349,11 +337,22 @@ if (!$cache and $version > $CFG->version) {  // upgrade
 
         echo $output->upgrade_plugin_check_page(core_plugin_manager::instance(), \core\update\checker::instance(),
                 $version, $showallplugins, $reloadurl,
-                new moodle_url('/admin/index.php', array('confirmupgrade'=>1, 'confirmrelease'=>1, 'confirmplugincheck'=>1)));
+                new moodle_url('/admin/index.php', array('confirmupgrade'=>1, 'confirmrelease'=>1, 'confirmplugincheck'=>1, 'cache'=>0)));
         die();
 
     } else {
-        // Launch main upgrade
+        // Always verify plugin dependencies!
+        $failed = array();
+        if (!core_plugin_manager::instance()->all_plugins_ok($version, $failed)) {
+            $PAGE->set_pagelayout('maintenance');
+            $PAGE->set_popup_notification_allowed(false);
+            $reloadurl = new moodle_url('/admin/index.php', array('confirmupgrade' => 1, 'confirmrelease' => 1, 'cache' => 0));
+            echo $output->unsatisfied_dependencies_page($version, $failed, $reloadurl);
+            die();
+        }
+        unset($failed);
+
+        // Launch main upgrade.
         upgrade_core($version, true);
     }
 } else if ($version < $CFG->version) {
@@ -373,6 +372,10 @@ if (!$cache and $branch <> $CFG->branch) {  // Update the branch
 if (!$cache and moodle_needs_upgrading()) {
     if (!$PAGE->headerprinted) {
         // means core upgrade or installation was not already done
+
+        /** @var core_admin_renderer $output */
+        $output = $PAGE->get_renderer('core', 'admin');
+
         if (!$confirmplugins) {
             $strplugincheck = get_string('plugincheck');
 
@@ -384,40 +387,46 @@ if (!$cache and moodle_needs_upgrading()) {
             $PAGE->set_cacheable(false);
 
             if ($fetchupdates) {
-                // no sesskey support guaranteed here
-                \core\update\checker::instance()->fetch();
+                require_sesskey();
+                $updateschecker = \core\update\checker::instance();
+                if ($updateschecker->enabled()) {
+                    $updateschecker->fetch();
+                }
                 redirect($PAGE->url);
             }
 
-            $output = $PAGE->get_renderer('core', 'admin');
-
             $deployer = \core\update\deployer::instance();
             if ($deployer->enabled()) {
                 $deployer->initialize($PAGE->url, $PAGE->url);
 
                 $deploydata = $deployer->submitted_data();
                 if (!empty($deploydata)) {
+                    require_sesskey();
                     echo $output->upgrade_plugin_confirm_deploy_page($deployer, $deploydata);
                     die();
                 }
             }
 
-            // check plugin dependencies first
-            $failed = array();
-            if (!core_plugin_manager::instance()->all_plugins_ok($version, $failed)) {
-                echo $output->unsatisfied_dependencies_page($version, $failed, $PAGE->url);
-                die();
-            }
-            unset($failed);
-
-            // dependencies check passed, let's rock!
+            // Show plugins info.
             echo $output->upgrade_plugin_check_page(core_plugin_manager::instance(), \core\update\checker::instance(),
                     $version, $showallplugins,
                     new moodle_url($PAGE->url),
-                    new moodle_url('/admin/index.php', array('confirmplugincheck'=>1)));
+                    new moodle_url('/admin/index.php', array('confirmplugincheck'=>1, 'cache'=>0)));
+            die();
+        }
+
+        // Make sure plugin dependencies are always checked.
+        $failed = array();
+        if (!core_plugin_manager::instance()->all_plugins_ok($version, $failed)) {
+            $PAGE->set_pagelayout('maintenance');
+            $PAGE->set_popup_notification_allowed(false);
+            $reloadurl = new moodle_url('/admin/index.php', array('cache' => 0));
+            echo $output->unsatisfied_dependencies_page($version, $failed, $reloadurl);
             die();
         }
+        unset($failed);
     }
+
     // install/upgrade all plugins and other parts
     upgrade_noncore(true);
 }
@@ -477,6 +486,17 @@ if (during_initial_install()) {
     upgrade_finished('upgradesettings.php');
 }
 
+if (has_capability('moodle/site:config', context_system::instance())) {
+    if ($fetchupdates) {
+        require_sesskey();
+        $updateschecker = \core\update\checker::instance();
+        if ($updateschecker->enabled()) {
+            $updateschecker->fetch();
+        }
+        redirect(new moodle_url('/admin/index.php', array('cache' => 0)));
+    }
+}
+
 // Now we can be sure everything was upgraded and caches work fine,
 // redirect if necessary to make sure caching is enabled.
 if (!$cache) {
@@ -564,12 +584,6 @@ $registered = $DB->count_records('registration_hubs', array('huburl' => HUB_MOOD
 
 admin_externalpage_setup('adminnotifications');
 
-if ($fetchupdates) {
-    require_sesskey();
-    $updateschecker->fetch();
-    redirect(new moodle_url('/admin/index.php'));
-}
-
 $output = $PAGE->get_renderer('core', 'admin');
 echo $output->admin_notifications_page($maturity, $insecuredataroot, $errorsdisplayed,
         $cronoverdue, $dbproblems, $maintenancemode, $availableupdates, $availableupdatesfetch, $buggyiconvnomb,
index f86097b..3ba44bb 100644 (file)
@@ -140,7 +140,7 @@ class core_admin_renderer extends plugin_renderer_base {
     public function upgrade_confirm_page($strnewversion, $maturity, $testsite) {
         $output = '';
 
-        $continueurl = new moodle_url('/admin/index.php', array('confirmupgrade' => 1));
+        $continueurl = new moodle_url('/admin/index.php', array('confirmupgrade' => 1, 'cache' => 0));
         $continue = new single_button($continueurl, get_string('continue'), 'get');
         $cancelurl = new moodle_url('/admin/index.php');
 
@@ -170,7 +170,7 @@ class core_admin_renderer extends plugin_renderer_base {
         $output .= $this->environment_check_table($envstatus, $environment_results);
 
         if (!$envstatus) {
-            $output .= $this->upgrade_reload(new moodle_url('/admin/index.php'), array('confirmupgrade' => 1));
+            $output .= $this->upgrade_reload(new moodle_url('/admin/index.php'), array('confirmupgrade' => 1, 'cache' => 0));
 
         } else {
             $output .= $this->notification(get_string('environmentok', 'admin'), 'notifysuccess');
@@ -179,7 +179,7 @@ class core_admin_renderer extends plugin_renderer_base {
                 $output .= $this->box(get_string('langpackwillbeupdated', 'admin'), 'generalbox', 'notice');
             }
 
-            $output .= $this->continue_button(new moodle_url('/admin/index.php', array('confirmupgrade' => 1, 'confirmrelease' => 1)));
+            $output .= $this->continue_button(new moodle_url('/admin/index.php', array('confirmupgrade' => 1, 'confirmrelease' => 1, 'cache' => 0)));
         }
 
         $output .= $this->footer();
@@ -711,7 +711,7 @@ class core_admin_renderer extends plugin_renderer_base {
         }
 
         $updateinfo .= $this->container_start('checkforupdates');
-        $fetchurl = new moodle_url('/admin/index.php', array('fetchupdates' => 1, 'sesskey' => sesskey(), 'cache' => 1));
+        $fetchurl = new moodle_url('/admin/index.php', array('fetchupdates' => 1, 'sesskey' => sesskey(), 'cache' => 0));
         $updateinfo .= $this->single_button($fetchurl, get_string('checkforupdates', 'core_plugin'));
         if ($fetch) {
             $updateinfo .= $this->container(get_string('checkforupdateslast', 'core_plugin',
@@ -962,7 +962,7 @@ class core_admin_renderer extends plugin_renderer_base {
             $out .= $this->output->heading(get_string('nonehighlighted', 'core_plugin'));
             if (empty($options['full'])) {
                 $out .= html_writer::link(new moodle_url('/admin/index.php',
-                    array('confirmupgrade' => 1, 'confirmrelease' => 1, 'showallplugins' => 1)),
+                    array('confirmupgrade' => 1, 'confirmrelease' => 1, 'showallplugins' => 1, 'cache' => 0)),
                     get_string('nonehighlightedinfo', 'core_plugin'));
             }
             $out .= $this->output->container_end();
@@ -972,11 +972,11 @@ class core_admin_renderer extends plugin_renderer_base {
             $out .= $this->output->heading(get_string('somehighlighted', 'core_plugin', $sumofhighlighted));
             if (empty($options['full'])) {
                 $out .= html_writer::link(new moodle_url('/admin/index.php',
-                    array('confirmupgrade' => 1, 'confirmrelease' => 1, 'showallplugins' => 1)),
+                    array('confirmupgrade' => 1, 'confirmrelease' => 1, 'showallplugins' => 1, 'cache' => 0)),
                     get_string('somehighlightedinfo', 'core_plugin'));
             } else {
                 $out .= html_writer::link(new moodle_url('/admin/index.php',
-                    array('confirmupgrade' => 1, 'confirmrelease' => 1, 'showallplugins' => 0)),
+                    array('confirmupgrade' => 1, 'confirmrelease' => 1, 'showallplugins' => 0, 'cache' => 0)),
                     get_string('somehighlightedonly', 'core_plugin'));
             }
             $out .= $this->output->container_end();
index 762ea70..262142e 100644 (file)
@@ -348,12 +348,12 @@ abstract class base_moodleform extends moodleform {
         $config->yesLabel = get_string('confirmcancelyes', 'backup');
         $config->noLabel = get_string('confirmcancelno', 'backup');
         $config->closeButtonTitle = get_string('close', 'editor');
-        $PAGE->requires->yui_module('moodle-backup-confirmcancel', 'M.core_backup.watch_cancel_buttons', array($config));
+        $PAGE->requires->yui_module('moodle-backup-confirmcancel', 'M.core_backup.confirmcancel.watch_cancel_buttons', array($config));
 
         // Get list of module types on course.
         $modinfo = get_fast_modinfo($COURSE);
         $modnames = $modinfo->get_used_module_names(true);
-        $PAGE->requires->yui_module('moodle-backup-backupselectall', 'M.core_backup.select_all_init',
+        $PAGE->requires->yui_module('moodle-backup-backupselectall', 'M.core_backup.backupselectall',
                 array($modnames));
         $PAGE->requires->strings_for_js(array('select', 'all', 'none'), 'moodle');
         $PAGE->requires->strings_for_js(array('showtypes', 'hidetypes'), 'backup');
diff --git a/backup/util/ui/yui/build/moodle-backup-backupselectall/moodle-backup-backupselectall-debug.js b/backup/util/ui/yui/build/moodle-backup-backupselectall/moodle-backup-backupselectall-debug.js
new file mode 100644 (file)
index 0000000..3de94d5
Binary files /dev/null and b/backup/util/ui/yui/build/moodle-backup-backupselectall/moodle-backup-backupselectall-debug.js differ
diff --git a/backup/util/ui/yui/build/moodle-backup-backupselectall/moodle-backup-backupselectall-min.js b/backup/util/ui/yui/build/moodle-backup-backupselectall/moodle-backup-backupselectall-min.js
new file mode 100644 (file)
index 0000000..b001213
Binary files /dev/null and b/backup/util/ui/yui/build/moodle-backup-backupselectall/moodle-backup-backupselectall-min.js differ
diff --git a/backup/util/ui/yui/build/moodle-backup-backupselectall/moodle-backup-backupselectall.js b/backup/util/ui/yui/build/moodle-backup-backupselectall/moodle-backup-backupselectall.js
new file mode 100644 (file)
index 0000000..3de94d5
Binary files /dev/null and b/backup/util/ui/yui/build/moodle-backup-backupselectall/moodle-backup-backupselectall.js differ
diff --git a/backup/util/ui/yui/build/moodle-backup-confirmcancel/moodle-backup-confirmcancel-debug.js b/backup/util/ui/yui/build/moodle-backup-confirmcancel/moodle-backup-confirmcancel-debug.js
new file mode 100644 (file)
index 0000000..173f290
Binary files /dev/null and b/backup/util/ui/yui/build/moodle-backup-confirmcancel/moodle-backup-confirmcancel-debug.js differ
diff --git a/backup/util/ui/yui/build/moodle-backup-confirmcancel/moodle-backup-confirmcancel-min.js b/backup/util/ui/yui/build/moodle-backup-confirmcancel/moodle-backup-confirmcancel-min.js
new file mode 100644 (file)
index 0000000..f19f1a9
Binary files /dev/null and b/backup/util/ui/yui/build/moodle-backup-confirmcancel/moodle-backup-confirmcancel-min.js differ
diff --git a/backup/util/ui/yui/build/moodle-backup-confirmcancel/moodle-backup-confirmcancel.js b/backup/util/ui/yui/build/moodle-backup-confirmcancel/moodle-backup-confirmcancel.js
new file mode 100644 (file)
index 0000000..173f290
Binary files /dev/null and b/backup/util/ui/yui/build/moodle-backup-confirmcancel/moodle-backup-confirmcancel.js differ
diff --git a/backup/util/ui/yui/confirmcancel/confirmcancel.js b/backup/util/ui/yui/confirmcancel/confirmcancel.js
deleted file mode 100644 (file)
index 77deaf1..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-YUI.add('moodle-backup-confirmcancel', function(Y) {
-
-// Namespace for the backup
-M.core_backup = M.core_backup || {};
-/**
- * Adds confirmation dialogues to the cancel buttons on the page.
- *
- * @param {object} config
- */
-M.core_backup.watch_cancel_buttons = function(config) {
-    Y.all('.confirmcancel').each(function(){
-        this._confirmationListener = this._confirmationListener || this.on('click', function(e){
-            // Prevent the default event (sumbit) from firing
-            e.preventDefault();
-            // Create the confirm box
-            var confirm = new M.core.confirm(config);
-            // If the user clicks yes
-            confirm.on('complete-yes', function(e){
-                // Detach the listener for the confirm box so it doesn't fire again.
-                this._confirmationListener.detach();
-                // Simulate the original cancel button click
-                this.simulate('click');
-            }, this);
-            // Show the confirm box
-            confirm.show();
-        }, this);
-    });
-}
-
-}, '@VERSION@', {'requires':['base','node','node-event-simulate','moodle-core-notification']});
diff --git a/backup/util/ui/yui/src/backupselectall/build.json b/backup/util/ui/yui/src/backupselectall/build.json
new file mode 100644 (file)
index 0000000..85b6072
--- /dev/null
@@ -0,0 +1,10 @@
+{
+  "name": "moodle-backup-backupselectall",
+  "builds": {
+    "moodle-backup-backupselectall": {
+      "jsfiles": [
+        "backupselectall.js"
+      ]
+    }
+  }
+}
@@ -1,12 +1,18 @@
-YUI.add('moodle-backup-backupselectall', function(Y) {
+/**
+ * Adds select all/none links to the top of the backup/restore/import schema page.
+ *
+ * @module moodle-backup-backupselectall
+ */
 
 // Namespace for the backup
 M.core_backup = M.core_backup || {};
 
 /**
  * Adds select all/none links to the top of the backup/restore/import schema page.
+ *
+ * @class M.core_backup.backupselectall
  */
-M.core_backup.select_all_init = function(modnames) {
+M.core_backup.backupselectall = function(modnames) {
     var formid = null;
 
     var helper = function(e, check, type, mod) {
@@ -23,7 +29,7 @@ M.core_backup.select_all_init = function(modnames) {
             if (prefix && name.substring(0, prefix.length) !== prefix) {
                 return;
             }
-            if (name.substring(name.length - len) == type) {
+            if (name.substring(name.length - len) === type) {
                 checkbox.set('checked', check);
             }
         });
@@ -67,9 +73,9 @@ M.core_backup.select_all_init = function(modnames) {
     var withuserdata = false;
     Y.all('input[type="checkbox"]').each(function(checkbox) {
         var name = checkbox.get('name');
-        if (name.substring(name.length - 9) == '_userdata') {
+        if (name.substring(name.length - 9) === '_userdata') {
             withuserdata = '_userdata';
-        } else if (name.substring(name.length - 9) == '_userinfo') {
+        } else if (name.substring(name.length - 9) === '_userinfo') {
             withuserdata = '_userinfo';
         }
     });
@@ -103,7 +109,7 @@ M.core_backup.select_all_init = function(modnames) {
         if (!modnames.hasOwnProperty(mod)) {
             continue;
         }
-        var html = html_generator('include_setting section_level', 'mod_' + mod, modnames[mod]);
+        html = html_generator('include_setting section_level', 'mod_' + mod, modnames[mod]);
         if (withuserdata) {
             html += html_generator('normal_setting', 'userdata-mod_' + mod, modnames[mod]);
         }
@@ -127,26 +133,27 @@ M.core_backup.select_all_init = function(modnames) {
         modlist.currentlyshown = !modlist.currentlyshown;
 
         // Either hide or show the links.
-        var animcfg = { node: modlist, duration: 0.2 };
+        var animcfg = { node: modlist, duration: 0.2 },
+            anim;
         if (modlist.currentlyshown) {
             // Animate reveal of the module links.
             modlist.show();
             animcfg.to = { maxHeight: modlist.get('clientHeight') + 'px' };
             modlist.setStyle('maxHeight', '0px');
-            var anim = new Y.Anim(animcfg);
+            anim = new Y.Anim(animcfg);
             anim.on('end', function() { modlist.setStyle('maxHeight', 'none'); });
             anim.run();
         } else {
             // Animate hide of the module links.
             animcfg.to = { maxHeight: '0px' };
             modlist.setStyle('maxHeight', modlist.get('clientHeight') + 'px');
-            var anim = new Y.Anim(animcfg);
+            anim = new Y.Anim(animcfg);
             anim.on('end', function() { modlist.hide(); modlist.setStyle('maxHeight', 'none'); });
             anim.run();
         }
 
     };
-    Y.one('#backup-bytype').on('click', function(e) { toggletypes(); });
+    Y.one('#backup-bytype').on('click', function() { toggletypes(); });
 
     Y.one('#backup-all-included').on('click',  function(e) { helper(e, true,  '_included'); });
     Y.one('#backup-none-included').on('click', function(e) { helper(e, false, '_included'); });
@@ -154,6 +161,4 @@ M.core_backup.select_all_init = function(modnames) {
         Y.one('#backup-all-userdata').on('click',  function(e) { helper(e, true,  withuserdata); });
         Y.one('#backup-none-userdata').on('click', function(e) { helper(e, false, withuserdata); });
     }
-}
-
-}, '@VERSION@', {'requires':['base', 'node', 'event', 'node-event-simulate', 'anim']});
+};
diff --git a/backup/util/ui/yui/src/backupselectall/js/backupselectall.json b/backup/util/ui/yui/src/backupselectall/js/backupselectall.json
new file mode 100644 (file)
index 0000000..81f6ce7
--- /dev/null
@@ -0,0 +1,10 @@
+{
+  "moodle-backup-backupselectall": {
+    "requires": [
+        "node",
+        "event",
+        "node-event-simulate",
+        "anim"
+    ]
+  }
+}
diff --git a/backup/util/ui/yui/src/backupselectall/meta/backupselectall.json b/backup/util/ui/yui/src/backupselectall/meta/backupselectall.json
new file mode 100644 (file)
index 0000000..81f6ce7
--- /dev/null
@@ -0,0 +1,10 @@
+{
+  "moodle-backup-backupselectall": {
+    "requires": [
+        "node",
+        "event",
+        "node-event-simulate",
+        "anim"
+    ]
+  }
+}
diff --git a/backup/util/ui/yui/src/confirmcancel/build.json b/backup/util/ui/yui/src/confirmcancel/build.json
new file mode 100644 (file)
index 0000000..603210c
--- /dev/null
@@ -0,0 +1,10 @@
+{
+    "name": "moodle-backup-confirmcancel",
+    "builds": {
+        "moodle-backup-confirmcancel": {
+            "jsfiles": [
+                "confirmcancel.js"
+            ]
+        }
+    }
+}
diff --git a/backup/util/ui/yui/src/confirmcancel/js/confirmcancel.js b/backup/util/ui/yui/src/confirmcancel/js/confirmcancel.js
new file mode 100644 (file)
index 0000000..ddaeb52
--- /dev/null
@@ -0,0 +1,77 @@
+/**
+ * Add a confirmation dialogue when cancelling a backup.
+ *
+ * @module moodle-backup-confirmcancel
+ */
+
+/**
+ * Add a confirmation dialogue when cancelling a backup.
+ *
+ * @class M.core_backup.confirmcancel
+ */
+
+
+// Namespace for the backup.
+M.core_backup = M.core_backup || {};
+
+M.core_backup.confirmcancel = {
+    /**
+     * An array of EventHandlers which call the confirm_cancel dialogue.
+     *
+     * @property listeners
+     * @protected
+     * @type Array
+     */
+    listeners: [],
+
+    /**
+     * The configuration supplied to this instance.
+     *
+     * @property config
+     * @protected
+     * @type Object
+     */
+    config: {},
+
+    /**
+     * Initializer to watch all cancel buttons.
+     *
+     * @method watch_cancel_buttons
+     * @param {Object} config The configuration for the confirmation dialogue.
+     */
+    watch_cancel_buttons: function(config) {
+        this.config = config;
+
+        this.listeners.push(
+            Y.one(Y.config.doc.body).delegate('click', this.confirm_cancel, '.confirmcancel', this)
+        );
+    },
+
+    /**
+     * Display the confirmation dialogue.
+     *
+     * @method confirm_cancel
+     * @protected
+     * @param {EventFacade} e
+     */
+    confirm_cancel: function(e) {
+        // Prevent the default event (submit) from firing.
+        e.preventDefault();
+
+        // Create the confirmation dialogue.
+        var confirm = new M.core.confirm(this.config);
+
+        // If the user clicks yes.
+        confirm.on('complete-yes', function(){
+            // Detach the listeners for the confirm box so they don't fire again.
+            new Y.EventHandle(M.core_backup.confirmcancel.listeners).detach();
+
+            // Simulate the original cancel button click.
+            c.currentTarget.simulate('click');
+        }, this);
+
+
+        // Show the confirm box.
+        confirm.show();
+    }
+};
diff --git a/backup/util/ui/yui/src/confirmcancel/meta/confirmcancel.json b/backup/util/ui/yui/src/confirmcancel/meta/confirmcancel.json
new file mode 100644 (file)
index 0000000..e352bdf
--- /dev/null
@@ -0,0 +1,9 @@
+{
+    "moodle-backup-confirmcancel": {
+        "requires": [
+            "node",
+            "node-event-simulate",
+            "moodle-core-notification-confirm"
+        ]
+    }
+}
index b6e6822..7a116ee 100644 (file)
Binary files a/blocks/navigation/yui/build/moodle-block_navigation-navigation/moodle-block_navigation-navigation-debug.js and b/blocks/navigation/yui/build/moodle-block_navigation-navigation/moodle-block_navigation-navigation-debug.js differ
index e474ba8..c48a7c3 100644 (file)
Binary files a/blocks/navigation/yui/build/moodle-block_navigation-navigation/moodle-block_navigation-navigation-min.js and b/blocks/navigation/yui/build/moodle-block_navigation-navigation/moodle-block_navigation-navigation-min.js differ
index 368a23d..0ecd3b9 100644 (file)
Binary files a/blocks/navigation/yui/build/moodle-block_navigation-navigation/moodle-block_navigation-navigation.js and b/blocks/navigation/yui/build/moodle-block_navigation-navigation/moodle-block_navigation-navigation.js differ
index 0748b98..7db6ca9 100644 (file)
@@ -409,7 +409,7 @@ Y.extend(TREE, Y.Base, TREE.prototype, {
         instance : {
             value : false,
             setter : function(val) {
-                return parseInt(val);
+                return parseInt(val, 10);
             }
         }
     }
index cafa530..e7f1631 100644 (file)
@@ -672,6 +672,11 @@ $CFG->admin = 'admin';
 // Example:
 //   $CFG->behat_usedeprecated = true;
 //
+// Including feature files from directories outside the dirroot is possible if required. The setting
+// requires that the running user has executable permissions on all parent directories in the paths.
+// Example:
+//   $CFG->behat_additionalfeatures = array('/home/developer/code/wipfeatures');
+//
 //=========================================================================
 // 12. DEVELOPER DATA GENERATOR
 //=========================================================================
index ce72a9c..2f3ef23 100644 (file)
@@ -2183,7 +2183,7 @@ function course_get_cm_move(cm_info $mod, $sr = null) {
     if ($hasmanageactivities) {
         $pixicon = 'i/dragdrop';
 
-        if ($mod->course == SITEID) {
+        if (!course_ajax_enabled($mod->get_course())) {
             // Override for course frontpage until we get drag/drop working there.
             $pixicon = 't/move';
         }
diff --git a/enrol/yui/notification/notification.js b/enrol/yui/notification/notification.js
deleted file mode 100644 (file)
index f2bf542..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-/**
- * This module provides backwards compatability and should be removed
- * entirely in Moodle 2.5
- */
-YUI.add('moodle-enrol-notification', function(Y) {
-    console.log("You are using a deprecated name. Please update your YUI module to use moodle-core-notification instead of moodle-enrol-notification");
-}, '@VERSION@', {requires:['base','node','overlay','event-key', 'moodle-core-notification']});
index c4f8a4b..d559ff4 100644 (file)
@@ -82,6 +82,11 @@ class behat_config_manager {
             $features = array_values($featurespaths);
         }
 
+        // Optionally include features from additional directories.
+        if (!empty($CFG->behat_additionalfeatures)) {
+            $features = array_merge($features, array_map("realpath", $CFG->behat_additionalfeatures));
+        }
+
         // Gets all the components with steps definitions.
         $stepsdefinitions = array();
         $steps = self::get_components_steps_definitions();
index 53c0241..e6ea562 100644 (file)
@@ -566,6 +566,9 @@ $cache = '.var_export($cache, true).';
         self::load_classes('core', "$CFG->dirroot/lib/classes");
 
         foreach (self::$subsystems as $subsystem => $fulldir) {
+            if (!$fulldir) {
+                continue;
+            }
             self::load_classes('core_'.$subsystem, "$fulldir/classes");
         }
 
index 6a303f2..6088406 100644 (file)
@@ -444,7 +444,9 @@ abstract class base implements \IteratorAggregate {
                 debugging('Number of event data fields must not be changed in event classes', DEBUG_DEVELOPER);
             }
             $encoded = json_encode($this->data['other']);
-            if ($encoded === false or $this->data['other'] !== json_decode($encoded, true)) {
+            // The comparison here is not set to strict as whole float numbers will be converted to integers through JSON encoding /
+            // decoding and send an unwanted debugging message.
+            if ($encoded === false or $this->data['other'] != json_decode($encoded, true)) {
                 debugging('other event data must be compatible with json encoding', DEBUG_DEVELOPER);
             }
             if ($this->data['userid'] and !is_number($this->data['userid'])) {
index f3d3582..2277627 100644 (file)
@@ -81,6 +81,18 @@ class checker {
         }
     }
 
+    /**
+     * Is automatic deployment enabled?
+     *
+     * @return bool
+     */
+    public function enabled() {
+        global $CFG;
+
+        // The feature can be prohibited via config.php.
+        return empty($CFG->disableupdateautodeploy);
+    }
+
     /**
      * Returns the timestamp of the last execution of {@link fetch()}
      *
index 89c47d6..0eccb8e 100644 (file)
@@ -32,9 +32,6 @@ defined('MOODLE_INTERNAL') || die();
  */
 class deployer {
 
-    const HTTP_PARAM_PREFIX     = 'updteautodpldata_';  // Hey, even Google has not heard of such a prefix! So it MUST be safe :-p.
-    const HTTP_PARAM_CHECKER    = 'datapackagesize';    // Name of the parameter that holds the number of items in the received data items.
-
     /** @var \core\update\deployer holds the singleton instance */
     protected static $singletoninstance;
     /** @var moodle_url URL of a page that includes the deployer UI */
@@ -207,9 +204,19 @@ class deployer {
             throw new coding_exception('Illegal method call - deployer not initialized.');
         }
 
-        $params = $this->data_to_params(array(
-            'updateinfo' => (array)$info,   // See http://www.php.net/manual/en/language.types.array.php#language.types.array.casting .
-        ));
+        $params = array(
+            'updateaddon' => $info->component,
+            'version' =>$info->version,
+            'sesskey' => sesskey(),
+        );
+
+        // Append some our own data.
+        if (!empty($this->callerurl)) {
+            $params['callerurl'] = $this->callerurl->out(false);
+        }
+        if (!empty($this->returnurl)) {
+            $params['returnurl'] = $this->returnurl->out(false);
+        }
 
         $widget = new \single_button(
             new moodle_url($this->callerurl, $params),
@@ -301,25 +308,46 @@ class deployer {
      * @return array
      */
     public function submitted_data() {
+        $component = optional_param('updateaddon', '', PARAM_COMPONENT);
+        $version = optional_param('version', '', PARAM_RAW);
+        if (!$component or !$version) {
+            return false;
+        }
+
+        $plugininfo = \core_plugin_manager::instance()->get_plugin_info($component);
+        if (!$plugininfo) {
+            return false;
+        }
 
-        $data = $this->params_to_data($_POST);
+        if ($plugininfo->is_standard()) {
+            return false;
+        }
 
-        if (empty($data) or empty($data[self::HTTP_PARAM_CHECKER])) {
+        if (!$updates = $plugininfo->available_updates()) {
             return false;
         }
 
-        if (!empty($data['updateinfo']) and is_object($data['updateinfo'])) {
-            $updateinfo = $data['updateinfo'];
-            if (!empty($updateinfo->component) and !empty($updateinfo->version)) {
-                $data['updateinfo'] = new info($updateinfo->component, (array)$updateinfo);
+        $info = null;
+        foreach ($updates as $update) {
+            if ($update->version == $version) {
+                $info = $update;
+                break;
             }
         }
+        if (!$info) {
+            return false;
+        }
 
-        if (!empty($data['callerurl'])) {
+        $data = array(
+            'updateaddon' => $component,
+            'updateinfo'  => $info,
+            'callerurl'   => optional_param('callerurl', null, PARAM_URL),
+            'returnurl'   => optional_param('returnurl', null, PARAM_URL),
+        );
+        if ($data['callerurl']) {
             $data['callerurl'] = new moodle_url($data['callerurl']);
         }
-
-        if (!empty($data['returnurl'])) {
+        if ($data['callerurl']) {
             $data['returnurl'] = new moodle_url($data['returnurl']);
         }
 
@@ -421,60 +449,6 @@ class deployer {
 
     /* === End of external API === */
 
-    /**
-     * Prepares an array of HTTP parameters that can be passed to another page.
-     *
-     * @param array|object $data associative array or an object holding the data, data JSON-able
-     * @return array suitable as a param for moodle_url
-     */
-    protected function data_to_params($data) {
-
-        // Append some our own data.
-        if (!empty($this->callerurl)) {
-            $data['callerurl'] = $this->callerurl->out(false);
-        }
-        if (!empty($this->returnurl)) {
-            $data['returnurl'] = $this->returnurl->out(false);
-        }
-
-        // Finally append the count of items in the package.
-        $data[self::HTTP_PARAM_CHECKER] = count($data);
-
-        // Generate params.
-        $params = array();
-        foreach ($data as $name => $value) {
-            $transname = self::HTTP_PARAM_PREFIX.$name;
-            $transvalue = json_encode($value);
-            $params[$transname] = $transvalue;
-        }
-
-        return $params;
-    }
-
-    /**
-     * Converts HTTP parameters passed to the script into native PHP data
-     *
-     * @param array $params such as $_REQUEST or $_POST
-     * @return array data passed for this class
-     */
-    protected function params_to_data(array $params) {
-
-        if (empty($params)) {
-            return array();
-        }
-
-        $data = array();
-        foreach ($params as $name => $value) {
-            if (strpos($name, self::HTTP_PARAM_PREFIX) === 0) {
-                $realname = substr($name, strlen(self::HTTP_PARAM_PREFIX));
-                $realvalue = json_decode($value);
-                $data[$realname] = $realvalue;
-            }
-        }
-
-        return $data;
-    }
-
     /**
      * Returns a random string to be used as a filename of the password storage.
      *
@@ -511,7 +485,13 @@ class deployer {
         $directory = core_component::get_plugin_directory($plugintype, $pluginname);
 
         if (is_null($directory)) {
-            throw new coding_exception('Unknown component location', $component);
+            // Plugin unknown, most probably deleted or missing during upgrade,
+            // look at the parent directory instead because they might want to install it.
+            $plugintypes = core_component::get_plugin_types();
+            if (!isset($plugintypes[$plugintype])) {
+                throw new coding_exception('Unknown component location', $component);
+            }
+            $directory = $plugintypes[$plugintype];
         }
 
         return $this->directory_writable($directory);
index 3782b5c..26ccba9 100644 (file)
@@ -500,17 +500,17 @@ class core_useragent {
         } else {
             return false;
         }
-        $compat_view = false;
+        $compatview = false;
         // IE8 and later versions may pretend to be IE7 for intranet sites, use Trident version instead,
         // the Trident should always describe the capabilities of IE in any emulation mode.
         if ($browser === '7.0' and preg_match("/Trident\/([0-9\.]+)/", $useragent, $match)) {
-            $compat_view = true;
+            $compatview = true;
             $browser = $match[1] + 4; // NOTE: Hopefully this will work also for future IE versions.
         }
         $browser = round($browser, 1);
         return array(
             'version'    => $browser,
-            'compatview' => $compat_view
+            'compatview' => $compatview
         );
     }
 
index 7e61808..748bddf 100644 (file)
@@ -4492,6 +4492,10 @@ class settings_navigation_ajax extends settings_navigation {
             return false;
         }
         $this->load_administration_settings();
+
+        // Check if local plugins is adding node to site admin.
+        $this->load_local_plugin_settings();
+
         $this->initialised = true;
     }
 }
index 6e1093c..84b2bcd 100644 (file)
@@ -666,6 +666,12 @@ class core_event_testcase extends advanced_testcase {
         $event6->trigger();
         $this->assertDebuggingCalled();
 
+        // Check that whole float numbers do not trigger debugging messages.
+        $event7 = \core_tests\event\unittest_executed::create(array('courseid'=>1, 'context'=>\context_system::instance(),
+            'other' => array('wholenumber' => 90.0000, 'numberwithdecimals' => 54.7656, 'sample' => 1)));
+        $event7->trigger();
+        $this->assertDebuggingNotCalled();
+
         $event = \core_tests\event\problematic_event2::create(array());
         $this->assertDebuggingNotCalled();
         $event = \core_tests\event\problematic_event2::create(array('context'=>\context_system::instance()));
index e2a1763..a765037 100644 (file)
Binary files a/lib/yui/build/moodle-core-blocks/moodle-core-blocks-debug.js and b/lib/yui/build/moodle-core-blocks/moodle-core-blocks-debug.js differ
index e4d3fd5..4da7627 100644 (file)
Binary files a/lib/yui/build/moodle-core-blocks/moodle-core-blocks-min.js and b/lib/yui/build/moodle-core-blocks/moodle-core-blocks-min.js differ
index 550debb..2b3315f 100644 (file)
Binary files a/lib/yui/build/moodle-core-blocks/moodle-core-blocks.js and b/lib/yui/build/moodle-core-blocks/moodle-core-blocks.js differ
diff --git a/lib/yui/build/moodle-core-dragdrop/moodle-core-dragdrop-debug.js b/lib/yui/build/moodle-core-dragdrop/moodle-core-dragdrop-debug.js
new file mode 100644 (file)
index 0000000..5014285
Binary files /dev/null and b/lib/yui/build/moodle-core-dragdrop/moodle-core-dragdrop-debug.js differ
diff --git a/lib/yui/build/moodle-core-dragdrop/moodle-core-dragdrop-min.js b/lib/yui/build/moodle-core-dragdrop/moodle-core-dragdrop-min.js
new file mode 100644 (file)
index 0000000..7cd70d5
Binary files /dev/null and b/lib/yui/build/moodle-core-dragdrop/moodle-core-dragdrop-min.js differ
diff --git a/lib/yui/build/moodle-core-dragdrop/moodle-core-dragdrop.js b/lib/yui/build/moodle-core-dragdrop/moodle-core-dragdrop.js
new file mode 100644 (file)
index 0000000..5014285
Binary files /dev/null and b/lib/yui/build/moodle-core-dragdrop/moodle-core-dragdrop.js differ
diff --git a/lib/yui/dragdrop/dragdrop.js b/lib/yui/dragdrop/dragdrop.js
deleted file mode 100644 (file)
index 773373c..0000000
+++ /dev/null
@@ -1,513 +0,0 @@
-YUI.add('moodle-core-dragdrop', function(Y) {
-    var MOVEICON = {
-        pix: "i/move_2d",
-        largepix: "i/dragdrop",
-        component: 'moodle',
-        cssclass: 'moodle-core-dragdrop-draghandle'
-    };
-
-   /*
-    * General DRAGDROP class, this should not be used directly,
-    * it is supposed to be extended by your class
-    */
-    var DRAGDROP = function() {
-        DRAGDROP.superclass.constructor.apply(this, arguments);
-    };
-
-    Y.extend(DRAGDROP, Y.Base, {
-        goingup : null,
-        absgoingup : null,
-        samenodeclass : null,
-        parentnodeclass : null,
-        groups : [],
-        lastdroptarget : null,
-        initializer : function() {
-            // Listen for all drag:start events
-            Y.DD.DDM.on('drag:start', this.global_drag_start, this);
-            // Listen for all drag:end events
-            Y.DD.DDM.on('drag:end', this.global_drag_end, this);
-            // Listen for all drag:drag events
-            Y.DD.DDM.on('drag:drag', this.global_drag_drag, this);
-            // Listen for all drop:over events
-            Y.DD.DDM.on('drop:over', this.global_drop_over, this);
-            // Listen for all drop:hit events
-            Y.DD.DDM.on('drop:hit', this.global_drop_hit, this);
-            // Listen for all drop:miss events
-            Y.DD.DDM.on('drag:dropmiss', this.global_drag_dropmiss, this);
-
-            Y.one(Y.config.doc.body).delegate('key', this.global_keydown, 'down:32, enter, esc', '.' + MOVEICON.cssclass, this);
-            Y.one(Y.config.doc.body).delegate('click', this.global_keydown, '.' + MOVEICON.cssclass , this);
-        },
-
-        get_drag_handle: function(title, classname, iconclass, large) {
-            var iconname = MOVEICON.pix;
-            if (large) {
-                iconname = MOVEICON.largepix;
-            }
-            var dragicon = Y.Node.create('<img />')
-                .setStyle('cursor', 'move')
-                .setAttrs({
-                    'src' : M.util.image_url(iconname, MOVEICON.component),
-                    'alt' : title
-                });
-            if (iconclass) {
-                dragicon.addClass(iconclass);
-            }
-
-            var dragelement = Y.Node.create('<span></span>')
-                .addClass(classname)
-                .setAttribute('title', title)
-                .setAttribute('tabIndex', 0)
-                .setAttribute('data-draggroups', this.groups)
-                .setAttribute('aria-grabbed', 'false')
-                .setAttribute('role', 'button');
-            dragelement.appendChild(dragicon);
-            dragelement.addClass(MOVEICON.cssclass);
-
-            return dragelement;
-        },
-
-        lock_drag_handle: function(drag, classname) {
-            drag.removeHandle('.'+classname);
-        },
-
-        unlock_drag_handle: function(drag, classname) {
-            drag.addHandle('.'+classname);
-        },
-
-        ajax_failure: function(response) {
-            var e = {
-                name : response.status+' '+response.statusText,
-                message : response.responseText
-            };
-            return new M.core.exception(e);
-        },
-
-        in_group: function(target) {
-            var ret = false;
-            Y.each(this.groups, function(v, k) {
-                if (target._groups[v]) {
-                    ret = true;
-                }
-            }, this);
-            return ret;
-        },
-        /*
-         * Drag-dropping related functions
-         */
-        global_drag_start : function(e) {
-            // Get our drag object
-            var drag = e.target;
-            // Check that drag object belongs to correct group
-            if (!this.in_group(drag)) {
-                return;
-            }
-            // Set some general styles here
-            drag.get('node').setStyle('opacity', '.25');
-            drag.get('dragNode').setStyles({
-                opacity: '.75',
-                borderColor: drag.get('node').getStyle('borderColor'),
-                backgroundColor: drag.get('node').getStyle('backgroundColor')
-            });
-            drag.get('dragNode').empty();
-            this.drag_start(e);
-        },
-
-        global_drag_end : function(e) {
-            var drag = e.target;
-            // Check that drag object belongs to correct group
-            if (!this.in_group(drag)) {
-                return;
-            }
-            //Put our general styles back
-            drag.get('node').setStyles({
-                visibility: '',
-                opacity: '1'
-            });
-            this.drag_end(e);
-        },
-
-        global_drag_drag : function(e) {
-            var drag = e.target,
-                info = e.info;
-
-            // Check that drag object belongs to correct group
-            if (!this.in_group(drag)) {
-                return;
-            }
-
-            // Note, we test both < and > situations here. We don't want to
-            // effect a change in direction if the user is only moving side
-            // to side with no Y position change.
-
-            // Detect changes in the position relative to the start point.
-            if (info.start[1] < info.xy[1]) {
-                // We are going up if our final position is higher than our start position.
-                this.absgoingup = true;
-
-            } else if (info.start[1] > info.xy[1]) {
-                // Otherwise we're going down.
-                this.absgoingup = false;
-            }
-
-            // Detect changes in the position relative to the last movement.
-            if (info.delta[1] < 0) {
-                // We are going up if our final position is higher than our start position.
-                this.goingup = true;
-
-            } else if (info.delta[1] > 0) {
-                // Otherwise we're going down.
-                this.goingup = false;
-            }
-
-            this.drag_drag(e);
-        },
-
-        global_drop_over : function(e) {
-            // Check that drop object belong to correct group.
-            if (!e.drop || !e.drop.inGroup(this.groups)) {
-                return;
-            }
-
-            // Get a reference to our drag and drop nodes.
-            var drag = e.drag.get('node'),
-                drop = e.drop.get('node');
-
-            // Save last drop target for the case of missed target processing.
-            this.lastdroptarget = e.drop;
-
-            // Are we dropping within the same parent node?
-            if (drop.hasClass(this.samenodeclass)) {
-                var where;
-
-                if (this.goingup) {
-                    where = "before";
-                } else {
-                    where = "after";
-                }
-
-                // Add the node contents so that it's moved, otherwise only the drag handle is moved.
-                drop.insert(drag, where);
-            } else if ((drop.hasClass(this.parentnodeclass) || drop.test('[data-droptarget="1"]')) && !drop.contains(drag)) {
-                // We are dropping on parent node and it is empty
-                if (this.goingup) {
-                    drop.append(drag);
-                } else {
-                    drop.prepend(drag);
-                }
-            }
-            this.drop_over(e);
-        },
-
-        global_drag_dropmiss : function(e) {
-            // drag:dropmiss does not have e.drag and e.drop properties
-            // we substitute them for the ease of use. For e.drop we use,
-            // this.lastdroptarget (ghost node we use for indicating where to drop)
-            e.drag = e.target;
-            e.drop = this.lastdroptarget;
-            // Check that drag object belongs to correct group
-            if (!this.in_group(e.drag)) {
-                return;
-            }
-            // Check that drop object belong to correct group
-            if (!e.drop || !e.drop.inGroup(this.groups)) {
-                return;
-            }
-            this.drag_dropmiss(e);
-        },
-
-        global_drop_hit : function(e) {
-            // Check that drop object belong to correct group
-            if (!e.drop || !e.drop.inGroup(this.groups)) {
-                return;
-            }
-            this.drop_hit(e);
-        },
-
-        /**
-         * This is used to build the text for the heading of the keyboard
-         * drag drop menu and the text for the nodes in the list.
-         * @method find_element_text
-         * @param {Node} n The node to start searching for a valid text node.
-         * @returns {string} The text of the first text-like child node of n.
-         */
-        find_element_text : function(n) {
-            // The valid node types to get text from.
-            var nodes = n.all('h2, h3, h4, h5, span, p, div.no-overflow, div.dimmed_text');
-            var text = '';
-
-            nodes.each(function () {
-                if (text == '') {
-                    if (Y.Lang.trim(this.get('text')) != '') {
-                        text = this.get('text');
-                    }
-                }
-            });
-
-            if (text != '') {
-                return text;
-            }
-            return M.util.get_string('emptydragdropregion', 'moodle');
-        },
-
-        /**
-         * This is used to initiate a keyboard version of a drag and drop.
-         * A dialog will open listing all the valid drop targets that can be selected
-         * using tab, tab, tab, enter.
-         * @method global_start_keyboard_drag
-         * @param {Event} e The keydown / click event on the grab handle.
-         * @param {Node} dragcontainer The resolved draggable node (an ancestor of the drag handle).
-         * @param {Node} draghandle The node that triggered this action.
-         */
-        global_start_keyboard_drag : function(e, draghandle, dragcontainer) {
-            M.core.dragdrop.keydragcontainer = dragcontainer;
-            M.core.dragdrop.keydraghandle = draghandle;
-
-            // Indicate to a screenreader the node that is selected for drag and drop.
-            dragcontainer.setAttribute('aria-grabbed', 'true');
-            // Get the name of the thing to move.
-            var nodetitle = this.find_element_text(dragcontainer);
-            var dialogtitle = M.util.get_string('movecontent', 'moodle', nodetitle);
-
-            // Build the list of drop targets.
-            var droplist = Y.Node.create('<ul></ul>');
-            droplist.addClass('dragdrop-keyboard-drag');
-            var listitem;
-            var listitemtext;
-
-            // Search for possible drop targets.
-            var droptargets = Y.all('.' + this.samenodeclass + ', .' + this.parentnodeclass);
-
-            droptargets.each(function (node) {
-                var validdrop = false, labelroot = node;
-                if (node.drop && node.drop.inGroup(this.groups) && node.drop.get('node') != dragcontainer) {
-                    // This is a drag and drop target with the same class as the grabbed node.
-                    validdrop = true;
-                } else {
-                    var elementgroups = node.getAttribute('data-draggroups').split(' ');
-                    var i, j;
-                    for (i = 0; i < elementgroups.length; i++) {
-                        for (j = 0; j < this.groups.length; j++) {
-                            if (elementgroups[i] == this.groups[j]) {
-                                // This is a parent node of the grabbed node (used for dropping in empty sections).
-                                validdrop = true;
-                                // This node will have no text - so we get the first valid text from the parent.
-                                labelroot = node.get('parentNode');
-                                break;
-                            }
-                        }
-                        if (validdrop) {
-                            break;
-                        }
-                    }
-                }
-
-                if (validdrop) {
-                    // It is a valid drop target - create a list item for it.
-                    listitem = Y.Node.create('<li></li>');
-                    listlink = Y.Node.create('<a></a>');
-                    nodetitle = this.find_element_text(labelroot);
-
-                    listitemtext = M.util.get_string('tocontent', 'moodle', nodetitle);
-                    listlink.setContent(listitemtext);
-
-                    // Add a data attribute so we can get the real drop target.
-                    listlink.setAttribute('data-drop-target', node.get('id'));
-                    // Notify the screen reader this is a valid drop target.
-                    listlink.setAttribute('aria-dropeffect', 'move');
-                    // Allow tabbing to the link.
-                    listlink.setAttribute('tabindex', '0');
-
-                    // Set the event listeners for enter, space or click.
-                    listlink.on('click', this.global_keyboard_drop, this);
-                    listlink.on('key', this.global_keyboard_drop, 'down:enter,32', this);
-
-                    // Add to the list or drop targets.
-                    listitem.append(listlink);
-                    droplist.append(listitem);
-                }
-            }, this);
-
-            // Create the dialog for the interaction.
-            M.core.dragdrop.dropui = new M.core.dialogue({
-                headerContent : dialogtitle,
-                bodyContent : droplist,
-                draggable : true,
-                visible : true,
-                centered : true
-            });
-
-            // Focus the first drop target.
-            if (droplist.one('a')) {
-                droplist.one('a').focus();
-            }
-        },
-
-        /**
-         * This is used as a simulated drag/drop event in order to prevent any
-         * subtle bugs from creating a real instance of a drag drop event. This means
-         * there are no state changes in the Y.DD.DDM and any undefined functions
-         * will trigger an obvious and fatal error.
-         * The end result is that we call all our drag/drop handlers but do not bubble the
-         * event to anyone else.
-         *
-         * The functions/properties implemented in the wrapper are:
-         * e.target
-         * e.drag
-         * e.drop
-         * e.drag.get('node')
-         * e.drop.get('node')
-         * e.drag.addHandle()
-         * e.drag.removeHandle()
-         *
-         * @class simulated_drag_drop_event
-         * @param {Node} dragnode The drag container node
-         * @param {Node} dropnode The node to initiate the drop on
-         */
-        simulated_drag_drop_event : function(dragnode, dropnode) {
-
-            // Subclass for wrapping both drag and drop.
-            var dragdropwrapper = function(node) {
-                this.node = node;
-            }
-
-            // Method e.drag.get() - get the node.
-            dragdropwrapper.prototype.get = function(param) {
-                if (param == 'node' || param == 'dragNode' || param == 'dropNode') {
-                    return this.node;
-                }
-                return null;
-            };
-
-            // Method e.drag.inGroup() - we have already run the group checks before triggering the event.
-            dragdropwrapper.prototype.inGroup = function() {
-                return true;
-            };
-
-            // Method e.drag.addHandle() - we don't want to run this.
-            dragdropwrapper.prototype.addHandle = function() {};
-            // Method e.drag.removeHandle() - we don't want to run this.
-            dragdropwrapper.prototype.removeHandle = function() {};
-
-            // Create instances of the dragdropwrapper.
-            this.drop = new dragdropwrapper(dropnode);
-            this.drag = new dragdropwrapper(dragnode);
-            this.target = this.drop;
-        },
-
-        /**
-         * This is used to complete a keyboard version of a drag and drop.
-         * A drop event will be simulated based on the drag and drop nodes.
-         * @method global_keyboard_drop
-         * @param {Event} e The keydown / click event on the proxy drop node.
-         */
-        global_keyboard_drop : function(e) {
-            // The drag node was saved.
-            var dragcontainer = M.core.dragdrop.keydragcontainer;
-            dragcontainer.setAttribute('aria-grabbed', 'false');
-            // The real drop node is stored in an attribute of the proxy.
-            var droptarget = Y.one('#' + e.target.getAttribute('data-drop-target'));
-
-            // Close the dialog.
-            M.core.dragdrop.dropui.hide();
-            // Cancel the event.
-            e.preventDefault();
-            // Convert to drag drop events.
-            var dragevent = new this.simulated_drag_drop_event(dragcontainer, dragcontainer);
-            var dropevent = new this.simulated_drag_drop_event(dragcontainer, droptarget);
-            // Simulate the full sequence.
-            this.drag_start(dragevent);
-            this.global_drop_over(dropevent);
-            this.global_drop_hit(dropevent);
-            M.core.dragdrop.keydraghandle.focus();
-        },
-
-        /**
-         * This is used to cancel a keyboard version of a drag and drop.
-         *
-         * @method global_cancel_keyboard_drag
-         */
-        global_cancel_keyboard_drag : function() {
-            if (M.core.dragdrop.keydragcontainer) {
-                M.core.dragdrop.keydragcontainer.setAttribute('aria-grabbed', 'false');
-                M.core.dragdrop.keydraghandle.focus();
-                M.core.dragdrop.keydragcontainer = null;
-            }
-        },
-
-        /**
-         * Process key events on the drag handles.
-         * @method global_keydown
-         * @param {Event} e The keydown / click event on the drag handle.
-         */
-        global_keydown : function(e) {
-            var draghandle = e.target.ancestor('.' + MOVEICON.cssclass, true),
-                dragcontainer,
-                draggroups;
-
-            if (draghandle === null) {
-                // The element clicked did not have a a draghandle in it's lineage.
-                return;
-            }
-
-            if (e.keyCode === 27 ) {
-                // Escape to cancel from anywhere.
-                this.global_cancel_keyboard_drag();
-                e.preventDefault();
-                return;
-            }
-
-            // Only process events on a drag handle.
-            if (!draghandle.hasClass(MOVEICON.cssclass)) {
-                return;
-            }
-
-            // Do nothing if not space or enter.
-            if (e.keyCode !== 13 && e.keyCode !== 32 && e.type !== 'click') {
-                return;
-            }
-
-            // Check the drag groups to see if we are the handler for this node.
-            draggroups = draghandle.getAttribute('data-draggroups').split(' ');
-            var i, j, validgroup = false;
-
-            for (i = 0; i < draggroups.length; i++) {
-                for (j = 0; j < this.groups.length; j++) {
-                    if (draggroups[i] === this.groups[j]) {
-                        validgroup = true;
-                        break;
-                    }
-                }
-                if (validgroup) {
-                    break;
-                }
-            }
-            if (!validgroup) {
-                return;
-            }
-
-            // Valid event - start the keyboard drag.
-            dragcontainer = draghandle.ancestor('.yui3-dd-drop');
-            this.global_start_keyboard_drag(e, draghandle, dragcontainer);
-
-            e.preventDefault();
-        },
-
-        /*
-         * Abstract functions definitions
-         */
-        drag_start : function(e) {},
-        drag_end : function(e) {},
-        drag_drag : function(e) {},
-        drag_dropmiss : function(e) {},
-        drop_over : function(e) {},
-        drop_hit : function(e) {}
-    }, {
-        NAME : 'dragdrop',
-        ATTRS : {}
-    });
-
-M.core = M.core || {};
-M.core.dragdrop = DRAGDROP;
-
-}, '@VERSION@', {requires:['base', 'node', 'io', 'dom', 'dd', 'event-key', 'event-focus', 'moodle-core-notification']});
index 6aa9f69..06a82ea 100644 (file)
@@ -61,7 +61,7 @@ Y.extend(DRAGBLOCK, M.core.dragdrop, {
 
         // See if we are missing either of block regions,
         // if yes we need to add an empty one to use as target
-        if (blockregionlist.size() != this.get('regions').length) {
+        if (blockregionlist.size() !== this.get('regions').length) {
             var blockregion = Y.Node.create('<div></div>')
                 .addClass(CSS.BLOCKREGION);
             var regioncontent = Y.Node.create('<div></div>')
@@ -88,7 +88,7 @@ Y.extend(DRAGBLOCK, M.core.dragdrop, {
             // Setting blockregion as droptarget (the case when it is empty)
             // The region-post (the right one)
             // is very narrow, so add extra padding on the left to drop block on it.
-            var tar = new Y.DD.Drop({
+            new Y.DD.Drop({
                 node: blockregionnode.one('div.'+CSS.REGIONCONTENT),
                 groups: this.groups,
                 padding: '40 240 40 240'
@@ -197,7 +197,7 @@ Y.extend(DRAGBLOCK, M.core.dragdrop, {
         // Moving from empty region-content towards the opposite one,
         // hide empty one (only for region-pre, region-post areas at the moment).
         regionname = this.get_region_id(drop.ancestor('div.'+CSS.BLOCKREGION));
-        if (this.dragsourceregion.all('.'+CSS.BLOCK).size() == 0 && this.dragsourceregion.get('id').match(/(region-pre|region-post)/i)) {
+        if (this.dragsourceregion.all('.'+CSS.BLOCK).size() === 0 && this.dragsourceregion.get('id').match(/(region-pre|region-post)/i)) {
             if (!documentbody.hasClass('side-'+regionname+'-only')) {
                 documentbody.addClass('side-'+regionname+'-only');
             }
diff --git a/lib/yui/src/dragdrop/build.json b/lib/yui/src/dragdrop/build.json
new file mode 100644 (file)
index 0000000..e21619a
--- /dev/null
@@ -0,0 +1,10 @@
+{
+    "name": "moodle-core-dragdrop",
+    "builds": {
+        "moodle-core-dragdrop": {
+            "jsfiles": [
+                "dragdrop.js"
+            ]
+        }
+    }
+}
diff --git a/lib/yui/src/dragdrop/js/dragdrop.js b/lib/yui/src/dragdrop/js/dragdrop.js
new file mode 100644 (file)
index 0000000..87bfd03
--- /dev/null
@@ -0,0 +1,640 @@
+/**
+ * The core drag and drop module for Moodle which extends the YUI drag and
+ * drop functionality with additional features.
+ *
+ * @module moodle-core-dragdrop
+ */
+var MOVEICON = {
+    pix: "i/move_2d",
+    largepix: "i/dragdrop",
+    component: 'moodle',
+    cssclass: 'moodle-core-dragdrop-draghandle'
+};
+
+/**
+ * General DRAGDROP class, this should not be used directly,
+ * it is supposed to be extended by your class
+ *
+ * @class M.core.dragdrop
+ * @constructor
+ * @extends Base
+ */
+var DRAGDROP = function() {
+    DRAGDROP.superclass.constructor.apply(this, arguments);
+};
+
+Y.extend(DRAGDROP, Y.Base, {
+    /**
+     * Whether the item is being moved upwards compared with the last
+     * location.
+     *
+     * @property goingup
+     * @type Boolean
+     * @default null
+     */
+    goingup: null,
+
+    /**
+     * Whether the item is being moved upwards compared with the start
+     * point.
+     *
+     * @property absgoingup
+     * @type Boolean
+     * @default null
+     */
+    absgoingup: null,
+
+    /**
+     * The class for the object.
+     *
+     * @property samenodeclass
+     * @type String
+     * @default null
+     */
+    samenodeclass: null,
+
+    /**
+     * The class on the parent of the item being moved.
+     *
+     * @property parentnodeclass
+     * @type String
+     * @default
+     */
+    parentnodeclass: null,
+
+    /**
+     * The groups for this instance.
+     *
+     * @property groups
+     * @type Array
+     * @default []
+     */
+    groups: [],
+
+    /**
+     * The previous drop location.
+     *
+     * @property lastdroptarget
+     * @type Node
+     * @default null
+     */
+    lastdroptarget: null,
+
+    /**
+     * The initializer which sets up the move action.
+     *
+     * @method initializer
+     * @protected
+     */
+    initializer: function() {
+        // Listen for all drag:start events.
+        Y.DD.DDM.on('drag:start', this.global_drag_start, this);
+
+        // Listen for all drag:end events.
+        Y.DD.DDM.on('drag:end', this.global_drag_end, this);
+
+        // Listen for all drag:drag events.
+        Y.DD.DDM.on('drag:drag', this.global_drag_drag, this);
+
+        // Listen for all drop:over events.
+        Y.DD.DDM.on('drop:over', this.global_drop_over, this);
+
+        // Listen for all drop:hit events.
+        Y.DD.DDM.on('drop:hit', this.global_drop_hit, this);
+
+        // Listen for all drop:miss events.
+        Y.DD.DDM.on('drag:dropmiss', this.global_drag_dropmiss, this);
+
+        // Add keybaord listeners for accessible drag/drop
+        Y.one(Y.config.doc.body).delegate('key', this.global_keydown,
+                'down:32, enter, esc', '.' + MOVEICON.cssclass, this);
+
+        // Make the accessible drag/drop respond to a single click.
+        Y.one(Y.config.doc.body).delegate('click', this.global_keydown,
+                '.' + MOVEICON.cssclass , this);
+    },
+
+    /**
+     * Build a new drag handle Node.
+     *
+     * @method get_drag_handle
+     * @param {String} title The title on the drag handle
+     * @param {String} classname The name of the class to add to the node
+     * wrapping the drag icon
+     * @param {String} [iconclass] The class to add to the icon
+     * @param {Boolean} [large=false] whether to use the larger version of
+     * the drag icon
+     * @return Node The built drag handle.
+     */
+    get_drag_handle: function(title, classname, iconclass, large) {
+        var iconname = MOVEICON.pix;
+        if (large) {
+            iconname = MOVEICON.largepix;
+        }
+        var dragicon = Y.Node.create('<img />')
+            .setStyle('cursor', 'move')
+            .setAttrs({
+                'src': M.util.image_url(iconname, MOVEICON.component),
+                'alt': title
+            });
+        if (iconclass) {
+            dragicon.addClass(iconclass);
+        }
+
+        var dragelement = Y.Node.create('<span></span>')
+            .addClass(classname)
+            .setAttribute('title', title)
+            .setAttribute('tabIndex', 0)
+            .setAttribute('data-draggroups', this.groups)
+            .setAttribute('role', 'button')
+            .setAttribute('aria-grabbed', 'false');
+        dragelement.appendChild(dragicon);
+        dragelement.addClass(MOVEICON.cssclass);
+
+        return dragelement;
+    },
+
+    lock_drag_handle: function(drag, classname) {
+        drag.removeHandle('.'+classname);
+    },
+
+    unlock_drag_handle: function(drag, classname) {
+        drag.addHandle('.'+classname);
+    },
+
+    ajax_failure: function(response) {
+        var e = {
+            name: response.status+' '+response.statusText,
+            message: response.responseText
+        };
+        return new M.core.exception(e);
+    },
+
+    in_group: function(target) {
+        var ret = false;
+        Y.each(this.groups, function(v) {
+            if (target._groups[v]) {
+                ret = true;
+            }
+        }, this);
+        return ret;
+    },
+    /*
+        * Drag-dropping related functions
+        */
+    global_drag_start: function(e) {
+        // Get our drag object
+        var drag = e.target;
+        // Check that drag object belongs to correct group
+        if (!this.in_group(drag)) {
+            return;
+        }
+        // Set some general styles here
+        drag.get('node').setStyle('opacity', '.25');
+        drag.get('dragNode').setStyles({
+            opacity: '.75',
+            borderColor: drag.get('node').getStyle('borderColor'),
+            backgroundColor: drag.get('node').getStyle('backgroundColor')
+        });
+        drag.get('dragNode').empty();
+        this.drag_start(e);
+    },
+
+    global_drag_end: function(e) {
+        var drag = e.target;
+        // Check that drag object belongs to correct group
+        if (!this.in_group(drag)) {
+            return;
+        }
+        //Put our general styles back
+        drag.get('node').setStyles({
+            visibility: '',
+            opacity: '1'
+        });
+        this.drag_end(e);
+    },
+
+    global_drag_drag: function(e) {
+        var drag = e.target,
+            info = e.info;
+
+        // Check that drag object belongs to correct group
+        if (!this.in_group(drag)) {
+            return;
+        }
+
+        // Note, we test both < and > situations here. We don't want to
+        // effect a change in direction if the user is only moving side
+        // to side with no Y position change.
+
+        // Detect changes in the position relative to the start point.
+        if (info.start[1] < info.xy[1]) {
+            // We are going up if our final position is higher than our start position.
+            this.absgoingup = true;
+
+        } else if (info.start[1] > info.xy[1]) {
+            // Otherwise we're going down.
+            this.absgoingup = false;
+        }
+
+        // Detect changes in the position relative to the last movement.
+        if (info.delta[1] < 0) {
+            // We are going up if our final position is higher than our start position.
+            this.goingup = true;
+
+        } else if (info.delta[1] > 0) {
+            // Otherwise we're going down.
+            this.goingup = false;
+        }
+
+        this.drag_drag(e);
+    },
+
+    global_drop_over: function(e) {
+        // Check that drop object belong to correct group.
+        if (!e.drop || !e.drop.inGroup(this.groups)) {
+            return;
+        }
+
+        // Get a reference to our drag and drop nodes.
+        var drag = e.drag.get('node'),
+            drop = e.drop.get('node');
+
+        // Save last drop target for the case of missed target processing.
+        this.lastdroptarget = e.drop;
+
+        // Are we dropping within the same parent node?
+        if (drop.hasClass(this.samenodeclass)) {
+            var where;
+
+            if (this.goingup) {
+                where = "before";
+            } else {
+                where = "after";
+            }
+
+            // Add the node contents so that it's moved, otherwise only the drag handle is moved.
+            drop.insert(drag, where);
+        } else if ((drop.hasClass(this.parentnodeclass) || drop.test('[data-droptarget="1"]')) && !drop.contains(drag)) {
+            // We are dropping on parent node and it is empty
+            if (this.goingup) {
+                drop.append(drag);
+            } else {
+                drop.prepend(drag);
+            }
+        }
+        this.drop_over(e);
+    },
+
+    global_drag_dropmiss: function(e) {
+        // drag:dropmiss does not have e.drag and e.drop properties
+        // we substitute them for the ease of use. For e.drop we use,
+        // this.lastdroptarget (ghost node we use for indicating where to drop)
+        e.drag = e.target;
+        e.drop = this.lastdroptarget;
+        // Check that drag object belongs to correct group
+        if (!this.in_group(e.drag)) {
+            return;
+        }
+        // Check that drop object belong to correct group
+        if (!e.drop || !e.drop.inGroup(this.groups)) {
+            return;
+        }
+        this.drag_dropmiss(e);
+    },
+
+    global_drop_hit: function(e) {
+        // Check that drop object belong to correct group
+        if (!e.drop || !e.drop.inGroup(this.groups)) {
+            return;
+        }
+        this.drop_hit(e);
+    },
+
+    /**
+     * This is used to build the text for the heading of the keyboard
+     * drag drop menu and the text for the nodes in the list.
+     * @method find_element_text
+     * @param {Node} n The node to start searching for a valid text node.
+     * @return {string} The text of the first text-like child node of n.
+     */
+    find_element_text: function(n) {
+        // The valid node types to get text from.
+        var nodes = n.all('h2, h3, h4, h5, span, p, div.no-overflow, div.dimmed_text');
+        var text = '';
+
+        nodes.each(function () {
+            if (text === '') {
+                if (Y.Lang.trim(this.get('text')) !== '') {
+                    text = this.get('text');
+                }
+            }
+        });
+
+        if (text !== '') {
+            return text;
+        }
+        return M.util.get_string('emptydragdropregion', 'moodle');
+    },
+
+    /**
+     * This is used to initiate a keyboard version of a drag and drop.
+     * A dialog will open listing all the valid drop targets that can be selected
+     * using tab, tab, tab, enter.
+     * @method global_start_keyboard_drag
+     * @param {Event} e The keydown / click event on the grab handle.
+     * @param {Node} dragcontainer The resolved draggable node (an ancestor of the drag handle).
+     * @param {Node} draghandle The node that triggered this action.
+     */
+    global_start_keyboard_drag: function(e, draghandle, dragcontainer) {
+        M.core.dragdrop.keydragcontainer = dragcontainer;
+        M.core.dragdrop.keydraghandle = draghandle;
+
+        // Indicate to a screenreader the node that is selected for drag and drop.
+        dragcontainer.setAttribute('aria-grabbed', 'true');
+        // Get the name of the thing to move.
+        var nodetitle = this.find_element_text(dragcontainer);
+        var dialogtitle = M.util.get_string('movecontent', 'moodle', nodetitle);
+
+        // Build the list of drop targets.
+        var droplist = Y.Node.create('<ul></ul>');
+        droplist.addClass('dragdrop-keyboard-drag');
+        var listitem;
+        var listitemtext;
+
+        // Search for possible drop targets.
+        var droptargets = Y.all('.' + this.samenodeclass + ', .' + this.parentnodeclass);
+
+        droptargets.each(function (node) {
+            var validdrop = false, labelroot = node;
+            if (node.drop && node.drop.inGroup(this.groups) && node.drop.get('node') !== dragcontainer) {
+                // This is a drag and drop target with the same class as the grabbed node.
+                validdrop = true;
+            } else {
+                var elementgroups = node.getAttribute('data-draggroups').split(' ');
+                var i, j;
+                for (i = 0; i < elementgroups.length; i++) {
+                    for (j = 0; j < this.groups.length; j++) {
+                        if (elementgroups[i] === this.groups[j]) {
+                            // This is a parent node of the grabbed node (used for dropping in empty sections).
+                            validdrop = true;
+                            // This node will have no text - so we get the first valid text from the parent.
+                            labelroot = node.get('parentNode');
+                            break;
+                        }
+                    }
+                    if (validdrop) {
+                        break;
+                    }
+                }
+            }
+
+            if (validdrop) {
+                // It is a valid drop target - create a list item for it.
+                listitem = Y.Node.create('<li></li>');
+                listlink = Y.Node.create('<a></a>');
+                nodetitle = this.find_element_text(labelroot);
+
+                listitemtext = M.util.get_string('tocontent', 'moodle', nodetitle);
+                listlink.setContent(listitemtext);
+
+                // Add a data attribute so we can get the real drop target.
+                listlink.setAttribute('data-drop-target', node.get('id'));
+                // Notify the screen reader this is a valid drop target.
+                listlink.setAttribute('aria-dropeffect', 'move');
+                // Allow tabbing to the link.
+                listlink.setAttribute('tabindex', '0');
+
+                // Set the event listeners for enter, space or click.
+                listlink.on('click', this.global_keyboard_drop, this);
+                listlink.on('key', this.global_keyboard_drop, 'down:enter,32', this);
+
+                // Add to the list or drop targets.
+                listitem.append(listlink);
+                droplist.append(listitem);
+            }
+        }, this);
+
+        // Create the dialog for the interaction.
+        M.core.dragdrop.dropui = new M.core.dialogue({
+            headerContent: dialogtitle,
+            bodyContent: droplist,
+            draggable: true,
+            visible: true,
+            centered: true
+        });
+
+        // Focus the first drop target.
+        if (droplist.one('a')) {
+            droplist.one('a').focus();
+        }
+    },
+
+    /**
+     * This is used as a simulated drag/drop event in order to prevent any
+     * subtle bugs from creating a real instance of a drag drop event. This means
+     * there are no state changes in the Y.DD.DDM and any undefined functions
+     * will trigger an obvious and fatal error.
+     * The end result is that we call all our drag/drop handlers but do not bubble the
+     * event to anyone else.
+     *
+     * The functions/properties implemented in the wrapper are:
+     * e.target
+     * e.drag
+     * e.drop
+     * e.drag.get('node')
+     * e.drop.get('node')
+     * e.drag.addHandle()
+     * e.drag.removeHandle()
+     *
+     * @method simulated_drag_drop_event
+     * @param {Node} dragnode The drag container node
+     * @param {Node} dropnode The node to initiate the drop on
+     */
+    simulated_drag_drop_event: function(dragnode, dropnode) {
+
+        // Subclass for wrapping both drag and drop.
+        var DragDropWrapper = function(node) {
+            this.node = node;
+        };
+
+        // Method e.drag.get() - get the node.
+        DragDropWrapper.prototype.get = function(param) {
+            if (param === 'node' || param === 'dragNode' || param === 'dropNode') {
+                return this.node;
+            }
+            return null;
+        };
+
+        // Method e.drag.inGroup() - we have already run the group checks before triggering the event.
+        DragDropWrapper.prototype.inGroup = function() {
+            return true;
+        };
+
+        // Method e.drag.addHandle() - we don't want to run this.
+        DragDropWrapper.prototype.addHandle = function() {};
+        // Method e.drag.removeHandle() - we don't want to run this.
+        DragDropWrapper.prototype.removeHandle = function() {};
+
+        // Create instances of the DragDropWrapper.
+        this.drop = new DragDropWrapper(dropnode);
+        this.drag = new DragDropWrapper(dragnode);
+        this.target = this.drop;
+    },
+
+    /**
+     * This is used to complete a keyboard version of a drag and drop.
+     * A drop event will be simulated based on the drag and drop nodes.
+     * @method global_keyboard_drop
+     * @param {Event} e The keydown / click event on the proxy drop node.
+     */
+    global_keyboard_drop: function(e) {
+        // The drag node was saved.
+        var dragcontainer = M.core.dragdrop.keydragcontainer;
+        dragcontainer.setAttribute('aria-grabbed', 'false');
+        // The real drop node is stored in an attribute of the proxy.
+        var droptarget = Y.one('#' + e.target.getAttribute('data-drop-target'));
+
+        // Close the dialog.
+        M.core.dragdrop.dropui.hide();
+        // Cancel the event.
+        e.preventDefault();
+        // Convert to drag drop events.
+        var dragevent = new this.simulated_drag_drop_event(dragcontainer, dragcontainer);
+        var dropevent = new this.simulated_drag_drop_event(dragcontainer, droptarget);
+        // Simulate the full sequence.
+        this.drag_start(dragevent);
+        this.global_drop_over(dropevent);
+        this.global_drop_hit(dropevent);
+        M.core.dragdrop.keydraghandle.focus();
+    },
+
+    /**
+     * This is used to cancel a keyboard version of a drag and drop.
+     *
+     * @method global_cancel_keyboard_drag
+     */
+    global_cancel_keyboard_drag: function() {
+        if (M.core.dragdrop.keydragcontainer) {
+            M.core.dragdrop.keydragcontainer.setAttribute('aria-grabbed', 'false');
+            M.core.dragdrop.keydraghandle.focus();
+            M.core.dragdrop.keydragcontainer = null;
+        }
+    },
+
+    /**
+     * Process key events on the drag handles.
+     *
+     * @method global_keydown
+     * @param {EventFacade} e The keydown / click event on the drag handle.
+     */
+    global_keydown: function(e) {
+        var draghandle = e.target.ancestor('.' + MOVEICON.cssclass, true),
+            dragcontainer,
+            draggroups;
+
+        if (draghandle === null) {
+            // The element clicked did not have a a draghandle in it's lineage.
+            return;
+        }
+
+        if (e.keyCode === 27 ) {
+            // Escape to cancel from anywhere.
+            this.global_cancel_keyboard_drag();
+            e.preventDefault();
+            return;
+        }
+
+        // Only process events on a drag handle.
+        if (!draghandle.hasClass(MOVEICON.cssclass)) {
+            return;
+        }
+
+        // Do nothing if not space or enter.
+        if (e.keyCode !== 13 && e.keyCode !== 32 && e.type !== 'click') {
+            return;
+        }
+
+        // Check the drag groups to see if we are the handler for this node.
+        draggroups = draghandle.getAttribute('data-draggroups').split(' ');
+        var i, j, validgroup = false;
+
+        for (i = 0; i < draggroups.length; i++) {
+            for (j = 0; j < this.groups.length; j++) {
+                if (draggroups[i] === this.groups[j]) {
+                    validgroup = true;
+                    break;
+                }
+            }
+            if (validgroup) {
+                break;
+            }
+        }
+        if (!validgroup) {
+            return;
+        }
+
+        // Valid event - start the keyboard drag.
+        dragcontainer = draghandle.ancestor('.yui3-dd-drop');
+        this.global_start_keyboard_drag(e, draghandle, dragcontainer);
+
+        e.preventDefault();
+    },
+
+
+    // Abstract functions definitions.
+
+    /**
+     * Callback to use when dragging starts.
+     *
+     * @method drag_start
+     * @param {EventFacade} e
+     */
+    drag_start: function() {},
+
+    /**
+     * Callback to use when dragging ends.
+     *
+     * @method drag_end
+     * @param {EventFacade} e
+     */
+    drag_end: function() {},
+
+    /**
+     * Callback to use during dragging.
+     *
+     * @method drag_drag
+     * @param {EventFacade} e
+     */
+    drag_drag: function() {},
+
+    /**
+     * Callback to use when dragging ends and is not over a drop target.
+     *
+     * @method drag_dropmiss
+     * @param {EventFacade} e
+     */
+    drag_dropmiss: function() {},
+
+    /**
+     * Callback to use when a drop over event occurs.
+     *
+     * @method drop_over
+     * @param {EventFacade} e
+     */
+    drop_over: function() {},
+
+    /**
+     * Callback to use on drop:hit.
+     *
+     * @method drop_hit
+     * @param {EventFacade} e
+     */
+    drop_hit: function() {}
+}, {
+    NAME: 'dragdrop',
+    ATTRS: {}
+});
+
+M.core = M.core || {};
+M.core.dragdrop = DRAGDROP;
diff --git a/lib/yui/src/dragdrop/meta/dragdrop.json b/lib/yui/src/dragdrop/meta/dragdrop.json
new file mode 100644 (file)
index 0000000..4e4f51c
--- /dev/null
@@ -0,0 +1,14 @@
+{
+    "moodle-core-dragdrop": {
+        "requires": [
+            "base",
+            "node",
+            "io",
+            "dom",
+            "dd",
+            "event-key",
+            "event-focus",
+            "moodle-core-notification"
+        ]
+    }
+}
index 620053e..ad42d99 100644 (file)
@@ -780,17 +780,25 @@ class worker extends singleton_pattern {
             $this->log('Current plugin code location: '.$sourcelocation);
             $this->log('Moving the current code into archive: '.$backuplocation);
 
-            // We don't want to touch files unless we are pretty sure it would be all ok.
-            if (!$this->move_directory_source_precheck($sourcelocation)) {
-                throw new backup_folder_exception('Unable to backup the current version of the plugin (source precheck failed)');
-            }
-            if (!$this->move_directory_target_precheck($backuplocation)) {
-                throw new backup_folder_exception('Unable to backup the current version of the plugin (backup precheck failed)');
-            }
+            if (file_exists($sourcelocation)) {
+                // We don't want to touch files unless we are pretty sure it would be all ok.
+                if (!$this->move_directory_source_precheck($sourcelocation)) {
+                    throw new backup_folder_exception('Unable to backup the current version of the plugin (source precheck failed)');
+                }
+                if (!$this->move_directory_target_precheck($backuplocation)) {
+                    throw new backup_folder_exception('Unable to backup the current version of the plugin (backup precheck failed)');
+                }
 
-            // Looking good, let's try it.
-            if (!$this->move_directory($sourcelocation, $backuplocation, true)) {
-                throw new backup_folder_exception('Unable to backup the current version of the plugin (moving failed)');
+                // Looking good, let's try it.
+                if (!$this->move_directory($sourcelocation, $backuplocation, true)) {
+                    throw new backup_folder_exception('Unable to backup the current version of the plugin (moving failed)');
+                }
+
+            } else {
+                // Upgrading missing plugin - this happens often during upgrades.
+                if (!$this->create_directory_precheck($sourcelocation)) {
+                    throw new filesystem_exception('Unable to prepare the plugin location (cannot create new directory)');
+                }
             }
 
             // Unzip the plugin package file into the target location.
index d1080aa..741dbc9 100644 (file)
@@ -120,4 +120,22 @@ class restore_assign_activity_task extends restore_activity_task {
         return $rules;
     }
 
+    /**
+     * Given a comment area, return the itemname that contains the itemid mappings.
+     *
+     * @param string $commentarea
+     * @return string
+     */
+    public function get_comment_mapping_itemname($commentarea) {
+        switch ($commentarea) {
+            case 'submission_comments':
+                $itemname = 'submission';
+                break;
+            default:
+                $itemname = parent::get_comment_mapping_itemname($commentarea);
+                break;
+        }
+
+        return $itemname;
+    }
 }
index 8d43b19..fd6f572 100644 (file)
@@ -572,10 +572,10 @@ class repository_filesystem extends repository {
             $filename = $mainfile->get_filename();
             $basepath = strstr($mainfilepath, $filename, true);
 
-            $fullrelativefilepath = realpath($this->root_path.$basepath.$relativepath);
+            $fullrelativefilepath = realpath($this->get_rootpath().$basepath.$relativepath);
 
             // Sanity check to make sure this path is inside this repository and the file exists.
-            if (strpos($fullrelativefilepath, $this->root_path) === 0 && file_exists($fullrelativefilepath)) {
+            if (strpos($fullrelativefilepath, $this->get_rootpath()) === 0 && file_exists($fullrelativefilepath)) {
                 send_file($fullrelativefilepath, basename($relativepath), null, 0);
             }
         }
index 9453b84..c730db3 100644 (file)
@@ -466,14 +466,6 @@ tab styles for ie6 & ie7
 .file-picker textarea {
     background-color: #EEE;
 }
-/* Important z-index fixes
--------------------------*/
-.yui-skin-sam .yui-panel-container {
-    z-index: 999999!important;
-}
-body#page-course-view-topics.path-course div.moodle-dialogue-base div.yui3-widget{
-    z-index: 600!important;
-}
 
 /* Moodle forms
 ----------------*/
index 049e694..9477ed6 100644 (file)
@@ -32,19 +32,17 @@ To update to the latest release of html5shiv:
 
 bootstrapcollapse.js, bootstrapdropdown.js, bootstrapengine.js
 --------------------------------------------------------------
-This theme uses YUI ports of the Twitter bootstrap jQuery based libs. These ported files are available on:
-
-https://github.com/jshirley/yui3-gallery/blob/master/src/gallery-bootstrap-collapse/js/bootstrap-collapse.js
-https://github.com/jshirley/yui3-gallery/blob/master/src/gallery-bootstrap-dropdown/js/bootstrap-dropdown.js
-https://github.com/jshirley/yui3-gallery/blob/master/src/gallery-bootstrap-engine/js/bootstrap-engine.js
-
-The content of these files are slightly modified to make sure all required YUI libraries are loaded. To achieve
-that the first and last line of each of these files has been modified.
+This theme uses YUI ports of the Twitter bootstrap jQuery based libs.
+
+Upgrade procedure:
+* git clone https://github.com/jshirley/yui-gallery.git
+* from that repository copy:
+** build/gallery-bootstrap-collapse/gallery-bootstrap-collapse-debug.js to js/gallery-bootstrapcollapse.js
+** build/gallery-bootstrap-dropdown/gallery-bootstrap-dropdown-debug.js to js/gallery-bootstrapdropdown.js
+** build/gallery-bootstrap-engine/gallery-bootstrap-engine-debug.js to js/gallery-bootstrapengine.js
+* apply patches from MDL-43152 to address linting issues
+* run shifter on this directory as required
+* update ../../../thirdpartylibs.xml
 
 The YUI port of the Twitter bootstrap libs are now longer maintained. If you need all of the Bootstrap JavaScript
 functionality consider switching to the original jQuery version of these file
-
-If you do want to update use these file locations:
-theme/bootstrapbase/yui/src/bootstrap/js/bootstrap-collapse.js
-theme/bootstrapbase/yui/src/bootstrap/js/bootstrap-dropdown.js
-theme/bootstrapbase/yui/src/bootstrap/js/bootstrap-engine.js
index 4284bd1..c98eef3 100644 (file)
Binary files a/theme/bootstrapbase/yui/build/moodle-theme_bootstrapbase-bootstrap/moodle-theme_bootstrapbase-bootstrap-debug.js and b/theme/bootstrapbase/yui/build/moodle-theme_bootstrapbase-bootstrap/moodle-theme_bootstrapbase-bootstrap-debug.js differ
index ebc87bf..c3a51d1 100644 (file)
Binary files a/theme/bootstrapbase/yui/build/moodle-theme_bootstrapbase-bootstrap/moodle-theme_bootstrapbase-bootstrap-min.js and b/theme/bootstrapbase/yui/build/moodle-theme_bootstrapbase-bootstrap/moodle-theme_bootstrapbase-bootstrap-min.js differ
index 9d753b3..e6691f0 100644 (file)
Binary files a/theme/bootstrapbase/yui/build/moodle-theme_bootstrapbase-bootstrap/moodle-theme_bootstrapbase-bootstrap.js and b/theme/bootstrapbase/yui/build/moodle-theme_bootstrapbase-bootstrap/moodle-theme_bootstrapbase-bootstrap.js differ
index 5392312..f62230a 100644 (file)
@@ -26,7 +26,7 @@ However, it can be manually plugged into any node or node list.
 @class Bootstrap.Collapse
 **/
 
-function CollapsePlugin(config) {
+function CollapsePlugin() {
     CollapsePlugin.superclass.constructor.apply(this, arguments);
 }
 
@@ -76,9 +76,7 @@ Y.extend(CollapsePlugin, Y.Plugin.Base, {
     * <code>data-target</code> or <code>href</code> attribute.
     */
     hide: function() {
-        var showClass = this.config.showClass,
-            hideClass = this.config.hideClass,
-            node      = this._getTarget();
+        var node      = this._getTarget();
 
         if ( this.transitioning ) {
             return;
@@ -95,9 +93,7 @@ Y.extend(CollapsePlugin, Y.Plugin.Base, {
     * <code>data-target</code> or <code>href</code> attribute.
     */
     show: function() {
-        var showClass = this.config.showClass,
-            hideClass = this.config.hideClass,
-            node      = this._getTarget(),
+        var node      = this._getTarget(),
             host      = this._node,
             self      = this,
             parent,
@@ -221,4 +217,3 @@ Y.namespace('Bootstrap').Collapse = CollapsePlugin;
 
 
 }, '@VERSION@' ,{requires:['plugin','transition','event','event-delegate']});
-;
\ No newline at end of file
index 77be2eb..7d1f3b1 100644 (file)
@@ -27,7 +27,7 @@ It can also be plugged into any node or node list.
 
 var NS = Y.namespace('Bootstrap');
 
-function DropdownPlugin(config) {
+function DropdownPlugin() {
   DropdownPlugin.superclass.constructor.apply(this, arguments);
 }
 
@@ -56,7 +56,7 @@ Y.extend( DropdownPlugin, Y.Plugin.Base, {
             className = this.config.className;
 
         target.toggleClass( className );
-        target.once('clickoutside', function(e) {
+        target.once('clickoutside', function() {
             target.toggleClass( className );
         });
     },
@@ -112,4 +112,3 @@ NS.dropdown_delegation = function() {
 
 
 }, '@VERSION@' ,{requires:['plugin','event','event-outside']});
-;
index 4a65af0..a401828 100644 (file)
@@ -14,6 +14,9 @@
 // You should have received a copy of the GNU General Public License
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
+// Get the HTML for the settings bits.
+$html = theme_clean_get_html_for_settings($OUTPUT, $PAGE);
+
 echo $OUTPUT->doctype() ?>
 <html <?php echo $OUTPUT->htmlattributes(); ?>>
 <head>