From d5de8747278ffbc9ef3636e793ee7fb92b3d2d56 Mon Sep 17 00:00:00 2001 From: Sam Hemelryk Date: Mon, 14 Oct 2013 14:07:26 +1300 Subject: [PATCH] MDL-42299 course: management interface remember categories expanded by AJAX Categories that have been expanded using AJAX are now remember and expanded again autoamtically when something triggers a page refresh. --- course/ajax/management.php | 12 +++ course/classes/management/helper.php | 90 +++++++++++++++++++ course/classes/management_renderer.php | 13 ++- ...course_category_management_listing.feature | 51 +++++++++++ .../moodle-course-management-debug.js | 7 ++ .../moodle-course-management-min.js | 6 +- .../moodle-course-management.js | 6 ++ course/yui/src/management/js/category.js | 2 + course/yui/src/management/js/console.js | 5 ++ 9 files changed, 185 insertions(+), 7 deletions(-) diff --git a/course/ajax/management.php b/course/ajax/management.php index 9a0e4e62273..8ba679b8cad 100644 --- a/course/ajax/management.php +++ b/course/ajax/management.php @@ -103,6 +103,18 @@ switch ($action) { ); } break; + case 'expandcategory': + $categoryid = required_param('categoryid', PARAM_INT); + $coursecat = coursecat::get($categoryid); + \core_course\management\helper::record_expanded_category($coursecat); + $outcome->outcome = true; + break; + case 'collapsecategory': + $categoryid = required_param('categoryid', PARAM_INT); + $coursecat = coursecat::get($categoryid); + \core_course\management\helper::record_expanded_category($coursecat, false); + $outcome->outcome = true; + break; case 'getsubcategorieshtml' : $categoryid = required_param('categoryid', PARAM_INT); /* @var core_course_management_renderer $renderer */ diff --git a/course/classes/management/helper.php b/course/classes/management/helper.php index f4848efb87e..1c4c862e71c 100644 --- a/course/classes/management/helper.php +++ b/course/classes/management/helper.php @@ -41,6 +41,13 @@ defined('MOODLE_INTERNAL') || die; * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class helper { + + /** + * The expanded category structure if its already being loaded from the cache. + * @var null|array + */ + protected static $expandedcategories = null; + /** * Returns course details in an array ready to be printed. * @@ -803,4 +810,87 @@ class helper { $params = array('path' => $path); return $DB->get_records_sql($sql, $params); } + + /** + * Records when a category is expanded or collapsed so that when the user + * + * @param \coursecat $coursecat The category we're working with. + * @param bool $expanded True if the category is expanded now. + */ + public static function record_expanded_category(\coursecat $coursecat, $expanded = true) { + // If this ever changes we are going to reset it and reload the categories as required. + self::$expandedcategories = null; + $categoryid = $coursecat->id; + $path = $coursecat->get_parents(); + /* @var \cache_session $cache */ + $cache = \cache::make('core', 'coursecat'); + $categories = $cache->get('managementexpanded'); + if (!is_array($categories)) { + if (!$expanded) { + // No categories recorded, nothing to remove. + return; + } + $categories = array(); + } + if ($expanded) { + $ref =& $categories; + foreach ($coursecat->get_parents() as $path) { + if (!isset($ref[$path]) || !is_array($ref[$path])) { + $ref[$path] = array(); + } + $ref =& $ref[$path]; + } + if (!isset($ref[$categoryid])) { + $ref[$categoryid] = true; + } + } else { + $found = true; + $ref =& $categories; + foreach ($coursecat->get_parents() as $path) { + if (!isset($ref[$path])) { + $found = false; + break; + } + $ref =& $ref[$path]; + } + if ($found) { + $ref[$categoryid] = null; + unset($ref[$categoryid]); + } + } + $cache->set('managementexpanded', $categories); + } + + /** + * Returns the categories that should be expanded when displaying the interface. + * + * @param int|null $withpath If specified a path to require as the parent. + * @return \coursecat[] An array of Category ID's to expand. + */ + public static function get_expanded_categories($withpath = null) { + if (self::$expandedcategories === null) { + /* @var \cache_session $cache */ + $cache = \cache::make('core', 'coursecat'); + self::$expandedcategories = $cache->get('managementexpanded'); + if (self::$expandedcategories === false) { + self::$expandedcategories = array(); + } + } + if (empty($withpath)) { + return array_keys(self::$expandedcategories); + } + $parents = explode('/', trim($withpath, '/')); + $ref =& self::$expandedcategories; + foreach ($parents as $parent) { + if (!isset($ref[$parent])) { + return array(); + } + $ref =& $ref[$parent]; + } + if (is_array($ref)) { + return array_keys($ref); + } else { + return array($parent); + } + } } diff --git a/course/classes/management_renderer.php b/course/classes/management_renderer.php index f6564909c30..aae1ced0376 100644 --- a/course/classes/management_renderer.php +++ b/course/classes/management_renderer.php @@ -118,7 +118,9 @@ class core_course_management_renderer extends plugin_renderer_base { $selectedparents[] = $category->id; $selectedcategory = $category->id; } - $catatlevel = array_shift($selectedparents); + $catatlevel = \core_course\management\helper::get_expanded_categories(''); + $catatlevel[] = array_shift($selectedparents); + $catatlevel = array_unique($catatlevel); $listing = coursecat::get(0)->get_children(); @@ -135,7 +137,7 @@ class core_course_management_renderer extends plugin_renderer_base { foreach ($listing as $listitem) { // Render each category in the listing. $subcategories = array(); - if ($listitem->id == $catatlevel) { + if (in_array($listitem->id, $catatlevel)) { $subcategories = $listitem->get_children(); } $html .= $this->category_listitem( @@ -166,6 +168,7 @@ class core_course_management_renderer extends plugin_renderer_base { */ public function category_listitem(coursecat $category, array $subcategories, $totalsubcategories, $selectedcategory = null, $selectedcategories = array()) { + $isexpandable = ($totalsubcategories > 0); $isexpanded = (!empty($subcategories)); $activecategory = ($selectedcategory === $category->id); @@ -224,9 +227,11 @@ class core_course_management_renderer extends plugin_renderer_base { $html .= html_writer::end_div(); if ($isexpanded) { $html .= html_writer::start_tag('ul', array('class' => 'ml', 'role' => 'group')); - $catatlevel = array_shift($selectedcategories); + $catatlevel = \core_course\management\helper::get_expanded_categories($category->path); + $catatlevel[] = array_shift($selectedcategories); + $catatlevel = array_unique($catatlevel); foreach ($subcategories as $listitem) { - $childcategories = ($listitem->id == $catatlevel) ? $listitem->get_children() : array(); + $childcategories = (in_array($listitem->id, $catatlevel)) ? $listitem->get_children() : array(); $html .= $this->category_listitem( $listitem, $childcategories, diff --git a/course/tests/behat/course_category_management_listing.feature b/course/tests/behat/course_category_management_listing.feature index e1873a8ae7e..09bea8f34cf 100644 --- a/course/tests/behat/course_category_management_listing.feature +++ b/course/tests/behat/course_category_management_listing.feature @@ -693,3 +693,54 @@ Feature: Course category management interface performs as expected # Redirect And I should see "Edit course settings" And I should see "Course 1" + + @javascript + Scenario: Test AJAX expanded categories stay open. + Given the following "categories" exists: + | name | category | idnumber | + | Cat 1 | 0 | CAT1 | + | Cat 2 | 0 | CAT2 | + | Cat 1-1 | CAT1 | CAT3 | + | Cat 1-2 | CAT1 | CAT4 | + | Cat 1-1-1 | CAT3 | CAT5 | + | Cat 1-1-2 | CAT3 | CAT6 | + | Cat 2-1 | CAT2 | CAT7 | + | Cat 2-1-1 | CAT7 | CAT8 | + | Cat 2-1-1-1 | CAT8 | CAT10 | + | Cat 2-1-2 | CAT7 | CAT9 | + | Cat 2-1-2-1 | CAT9 | CAT11 | + + And I log in as "admin" + And I go to the courses management page + And I should see the "Course categories" management page + And I should see "Cat 1" in the "#course-category-listings ul.ml" "css_element" + And I should see "Cat 2" in the "#course-category-listings ul.ml" "css_element" + And I should not see "Cat 1-1" in the "#course-category-listings ul.ml" "css_element" + And I should not see "Cat 1-2" in the "#course-category-listings ul.ml" "css_element" + And I should not see "Cat 2-1" in the "#course-category-listings ul.ml" "css_element" + And I click to expand category "CAT2" in the management interface + # AJAX action - no redirect. + And I click to expand category "CAT7" in the management interface + # AJAX action - no redirect. + And I click to expand category "CAT9" in the management interface + # AJAX action - no redirect. + And I should see "Cat 1" in the "#course-category-listings ul.ml" "css_element" + And I should see "Cat 2" in the "#course-category-listings ul.ml" "css_element" + And I should not see "Cat 1-1" in the "#course-category-listings ul.ml" "css_element" + And I should not see "Cat 1-2" in the "#course-category-listings ul.ml" "css_element" + And I should see "Cat 2-1" in the "#course-category-listings ul.ml" "css_element" + And I should see "Cat 2-1-1" in the "#course-category-listings ul.ml" "css_element" + And I should see "Cat 2-1-2" in the "#course-category-listings ul.ml" "css_element" + And I should not see "Cat 2-1-1-1" in the "#course-category-listings ul.ml" "css_element" + And I should see "Cat 2-1-2-1" in the "#course-category-listings ul.ml" "css_element" + And I click on "Cat 1" "link" + # Redirect. + And I should see "Cat 1" in the "#course-category-listings ul.ml" "css_element" + And I should see "Cat 2" in the "#course-category-listings ul.ml" "css_element" + And I should see "Cat 1-1" in the "#course-category-listings ul.ml" "css_element" + And I should see "Cat 1-2" in the "#course-category-listings ul.ml" "css_element" + And I should see "Cat 2-1" in the "#course-category-listings ul.ml" "css_element" + And I should see "Cat 2-1-1" in the "#course-category-listings ul.ml" "css_element" + And I should see "Cat 2-1-2" in the "#course-category-listings ul.ml" "css_element" + And I should not see "Cat 2-1-1-1" in the "#course-category-listings ul.ml" "css_element" + And I should see "Cat 2-1-2-1" in the "#course-category-listings ul.ml" "css_element" \ No newline at end of file diff --git a/course/yui/build/moodle-course-management/moodle-course-management-debug.js b/course/yui/build/moodle-course-management/moodle-course-management-debug.js index 3da10487455..af49f08f866 100644 --- a/course/yui/build/moodle-course-management/moodle-course-management-debug.js +++ b/course/yui/build/moodle-course-management/moodle-course-management-debug.js @@ -423,6 +423,11 @@ Console.prototype = { args.action = action; args.ajax = '1'; args.sesskey = M.cfg.sesskey; + if (callback === null) { + callback = function() { + Y.log("'Action '"+action+"' completed", 'debug', 'moodle-course-management'); + }; + } io.send(this.get('ajaxurl'), { method : 'POST', on : { @@ -1155,6 +1160,7 @@ Category.prototype = { title : M.util.get_string('collapse', 'moodle'), alt : M.util.get_string('collapse', 'moodle') }); + this.get('console').performAjaxAction('expandcategory', {categoryid : this.get('categoryid')}, null, this); }, /** @@ -1170,6 +1176,7 @@ Category.prototype = { title : M.util.get_string('expand', 'moodle'), alt : M.util.get_string('expand', 'moodle') }); + this.get('console').performAjaxAction('collapsecategory', {categoryid : this.get('categoryid')}, null, this); }, /** diff --git a/course/yui/build/moodle-course-management/moodle-course-management-min.js b/course/yui/build/moodle-course-management/moodle-course-management-min.js index 28a3ee93876..c8c707db224 100644 --- a/course/yui/build/moodle-course-management/moodle-course-management-min.js +++ b/course/yui/build/moodle-course-management/moodle-course-management-min.js @@ -1,3 +1,3 @@ -YUI.add("moodle-course-management",function(e,t){function n(){n.superclass.constructor.apply(this,arguments)}function r(e){n.superclass.constructor.apply(this,[e])}function i(){i.superclass.constructor.apply(this,arguments)}function s(){s.superclass.constructor.apply(this,arguments)}function o(){o.superclass.constructor.apply(this,arguments)}n.NAME="moodle-course-management",n.CSS_PREFIX="management",n.ATTRS={element:{setter:function(t){return typeof t=="string"&&(t=e.one("#"+t)),t}},categorylisting:{value:null},courselisting:{value:null},coursedetails:{value:null},activecategoryid:{value:null},activecourseid:{value:null},categories:{setter:function(t,n){if(e.Lang.isArray(t))return t;var r=this.get(n);return r.push(t),r},value:[]},courses:{validator:function(t){return e.Lang.isArray(t)},value:[]},page:{getter:function(e,t){return e===null&&(e=this.get("element").getData(t),this.set(t,e)),e},value:null},totalpages:{getter:function(e,t){return e===null&&(e=this.get("element").getData(t),this.set(t,e)),e},value:null},totalcourses:{getter:function(e,t){return e===null&&(e=this.get("element").getData(t),this.set(t,e)),e},value:null},ajaxurl:{getter:function(e){return e===null&&(e=M.cfg.wwwroot+"/course/ajax/management.php"),e},value:null},dragdrop:{value:null}},n.prototype={categoriesinit:!1,initializer:function(){this.set("element","coursecat-management");var e=this.get("element"),t=e.one("#category-listing"),n=e.one("#course-listing"),i=null,s=null;t&&(i=t.one('.listitem[data-selected="1"]')),n&&(s=n.one('.listitem[data-selected="1"]')),this.set("categorylisting",t),this.set("courselisting",n),this.set("coursedetails",e.one("#course-detail")),i&&this.set("activecategoryid",i.getData("id")),s&&this.set("activecourseid",s.getData("id")),this.initialiseCategories(t),this.initialiseCourses(),n&&this.set("dragdrop",new r({console:this}))},initialiseCategories:function(e){var t=0;if(!e)return!1;e.all(".listitem[data-id]").each(function(e){this.set("categories",new s({node:e,console:this})),t++},this),this.categoriesinit||(this.get("categorylisting").delegate("click",this.handleCategoryDelegation,"a[data-action]",this),this.categoriesinit=!0)},initialiseCourses:function(){var e=this.getCategoryById(this.get("activecategoryid")),t=this.get("courselisting"),n=0;if(!t)return!1;if(!e)return!1;t.all(".listitem[data-id]").each(function(t){this.registerCourse(new o({node:t,console:this,category:e})),n++},this),t.delegate("click",this.handleCourseDelegation,"a[data-action]",this)},registerCourse:function(e){var t=this.get("courses");t.push(e),this.set("courses",t)},handleCourseDelegation:function(e){var t=e.currentTarget,n=t.getData("action"),r=t.ancestor(".listitem").getData("id"),i=this.getCourseById(r);i&&i.handle(n,e)},handleCategoryDelegation:function(e){var t=e.currentTarget,n=t.getData("action"),r=t.ancestor(".listitem").getData("id"),i=this.getCategoryById(r);i&&i.handle(n,e)},getCategoryById:function(e){var t,n,r=this.get("categories"),i=r.length;for(t=0;t .course-listing"),s=r?r.one("ul.ml"):null,o=i?i.one("ul.ml"):null,u=i?i.getData("canmoveoutof"):!1,a=u?n:o;if(!o)return!1;o.all("> li").each(function(e){this.initCourseListing(e,a)},this),o.setData("dd",new e.DD.Drop({node:o})),u&&s&&s.all("li > div").each(function(e){this.initCategoryListitem(e)},this),e.DD.DDM.on("drag:start",this.dragStart,this),e.DD.DDM.on("drag:end",this.dragEnd,this),e.DD.DDM.on("drag:drag",this.dragDrag,this),e.DD.DDM.on("drop:over",this.dropOver,this),e.DD.DDM.on("drop:enter",this.dropEnter,this),e.DD.DDM.on("drop:exit",this.dropExit,this),e.DD.DDM.on("drop:hit",this.dropHit,this)},initCourseListing:function(t,n){t.setData("dd",(new e.DD.Drag({node:t,target:{padding:"0 0 0 20"}})).addHandle(".drag-handle").plug(e.Plugin.DDProxy,{moveOnEnd:!1,borderStyle:!1}).plug(e.Plugin.DDConstrained,{constrain2node:n}))},initCategoryListitem:function(t){t.setData("dd",new e.DD.Drop({node:t}))},dragStart:function(e){var t=e.target,n=t.get("node"),r=t.get("dragNode");n.addClass("course-being-dragged"),r.addClass("course-being-dragged-proxy").set("innerHTML",n.one("a.coursename").get("innerHTML")),this.previoussibling=n.get("previousSibling")},dragEnd:function(e){var t=e.target,n=t.get("node");n.removeClass("course-being-dragged"),this.get("console").get("element").all("#category-listing li.highlight").removeClass("highlight")},dragDrag:function(e){var t=e.target.lastXY[1];t div a.action-moveup"),o=i.one(" > div a.action-movedown");if(!a||!o)s=i.one(" > div a.action-moveup"),f=u.one(" > div a.action-movedown"),!a&&!o?(l=e.Node.create(' '),f.replace(l),s.replace(f),l.replace(s),l.destroy()):o||s.insert(f,"after");this.updated(!0)}else window.location.reload()},movedown:function(t,n,r){var i,s,o,u,a,f,l,c=this.checkAjaxResponse(t,n,r);if(c===!1)return!1;i=this.get("node"),s=i.next(".listitem");if(s){i.insert(s,"before"),f=s.one(" > div a.action-movedown"),o=i.one(" > div a.action-moveup");if(!f||!o)a=s.one(" > div a.action-moveup"),u=i.one(" > div a.action-movedown"),!f&&!o?(l=e.Node.create(' '),a.replace(l),u.replace(a),l.replace(u),l.destroy()):o||u.insert(a,"before");this.updated(!0)}else window.location.reload()},show:function(e,t,n){var r=this.checkAjaxResponse(e,t,n);if(r===!1)return!1;this.markVisible(),this.updated()},markVisible:function(){return this.get("node").setAttribute("data-visible","1"),!0},hide:function(e,t,n){var r=this.checkAjaxResponse(e,t,n);if(r===!1)return!1;this.markHidden(),this.updated()},markHidden:function(){return this.get("node").setAttribute("data-visible","0"),!0},updated:function(e){e&&this.highlight()},highlight:function(){var e=this.get("node");e.siblings(".highlight").removeClass("highlight"),e.addClass("highlight"),this.highlighttimeout&&window.clearTimeout(this.highlighttimeout),this.highlighttimeout=window.setTimeout(function(){e.removeClass("highlight")},2500)}},e.extend(i,e.Base,i.prototype),s.NAME="moodle-course-management-category",s.CSS_PREFIX="management-category",s.ATTRS={categoryid:{getter:function(e,t){return e===null&&(e=this.get("node").getData("id"),this.set(t,e)),e},value:null,writeOnce:!0},selected:{getter:function(e,t){return e===null&&(e=this.get("node").getData(t),e===null&&(e=!1),this.set(t,e)),e},value:null},courses:{validator:function(t){return e.Lang.isArray(t)},value:[]}},s.prototype={initializer:function(){this.set("itemname","category")},getName:function(){return this.get("node").one("a.categoryname").get("innerHTML")},registerCourse:function(e){var t=this.get("courses");t.push(e),this.set("courses",t)},handle:function(e,t){var n={categoryid:this.get("categoryid")},r=this.get("console").get("activecategoryid");r&&r!==n.categoryid&&(n.selectedcategory=r);switch(e){case"moveup":t.preventDefault(),this.get("console").performAjaxAction("movecategoryup",n,this.moveup,this);break;case"movedown":t.preventDefault(),this.get("console").performAjaxAction("movecategorydown",n,this.movedown,this);break;case"show":t.preventDefault(),this.get("console").performAjaxAction("showcategory",n,this.show,this);break;case"hide":t.preventDefault(),this.get("console").performAjaxAction("hidecategory",n,this.hide,this);break;case"expand":t.preventDefault(),this.get("node").getData("expanded")==="0"&&(this.get("node").setAttribute("data-expanded","1").setData("expanded","true"),this.get("console").performAjaxAction("getsubcategorieshtml",n,this.loadSubcategories,this)),this.expand();break;case"collapse":t.preventDefault(),this.collapse();break;default:return!1}},expand:function(){var e=this.get("node"),t=e.one("a[data-action=expand]");e.removeClass("collapsed").setAttribute("aria-expanded","true"),t.setAttribute("data-action","collapse").one("img").setAttrs({src:M.util.image_url("t/switch_minus","moodle"),title:M.util.get_string("collapse","moodle"),alt:M.util.get_string("collapse","moodle")})},collapse:function(){var e=this.get("node"),t=e.one("a[data-action=collapse]");e.addClass("collapsed").setAttribute("aria-expanded","false"),t.setAttribute("data-action","expand").one("img").setAttrs({src:M.util.image_url("t/switch_plus","moodle"),title:M.util.get_string("expand","moodle"),alt:M.util.get_string("expand","moodle")})},loadSubcategories:function(e,t,n){var r=this.checkAjaxResponse(e,t,n),i=this.get("node"),s=this.get("console");return r===!1?!1:(i.append(r.html),s.initialiseCategories(i),M.core&&M.core.actionmenu&&M.core.actionmenu.newDOMNode&&M.core.actionmenu.newDOMNode(i),!0)},moveCourseTo:function(t){var n=this;e.use("moodle-core-notification-confirm",function(){var e=new M.core.confirm({title:M.util.get_string("confirm","moodle"),question:M.util.get_string("confirmcoursemove","moodle",{course:t.getName(),category:n.getName()}),yesLabel:M.util.get_string("yes","moodle"),noLabel:M.util.get_string("no","moodle")});e.on("complete-yes",function(){e.hide(),e.destroy(),this.get("console").performAjaxAction("movecourseintocategory",{categoryid:this.get("categoryid"),courseid:t.get("courseid")},this.completeMoveCourse,this)},n),e.show()})},completeMoveCourse:function(e,t,n){var r=this.checkAjaxResponse -(e,t,n),i;return r===!1?!1:(i=this.get("console").getCourseById(n.courseid),i?(this.highlight(),i&&i.remove(),!0):!1)},show:function(e,t,n){var r=this.checkAjaxResponse(e,t,n);if(r===!1)return!1;this.markVisible(),r.categoryvisibility&&this.updateChildVisibility(r.categoryvisibility),r.coursevisibility&&this.updateCourseVisiblity(r.coursevisibility),this.updated()},hide:function(e,t,n){var r=this.checkAjaxResponse(e,t,n);if(r===!1)return!1;this.markHidden(),r.categoryvisibility&&this.updateChildVisibility(r.categoryvisibility),r.coursevisibility&&this.updateCourseVisiblity(r.coursevisibility),this.updated()},updateCourseVisiblity:function(e){var t=this.get("console"),n,r;try{for(n in e)typeof e[n]=="object"&&(r=t.getCourseById(e[n].id),r&&(e[n].visible==="1"?r.markVisible():r.markHidden()))}catch(i){}return this},updateChildVisibility:function(e){var t=this.get("console"),n,r;try{for(n in e)typeof e[n]=="object"&&(r=t.getCategoryById(e[n].id),r&&(e[n].visible==="1"?r.markVisible():r.markHidden()))}catch(i){}return this}},e.extend(s,i,s.prototype),o.NAME="moodle-course-management-course",o.CSS_PREFIX="management-course",o.ATTRS={courseid:{},selected:{getter:function(e,t){return e===null&&(e=this.get("node").getData(t),this.set(t,e)),e},value:null},node:{},console:{writeOnce:"initOnly"},category:{writeOnce:"initOnly"}},o.prototype={initializer:function(){var e=this.get("node"),t=this.get("category");this.set("courseid",e.getData("id")),t&&t.registerCourse&&t.registerCourse(this),this.set("itemname","course")},getName:function(){return this.get("node").one("a.coursename").get("innerHTML")},handle:function(e,t){var n=this.get("console"),r={courseid:this.get("courseid")};switch(e){case"moveup":t.halt(),n.performAjaxAction("movecourseup",r,this.moveup,this);break;case"movedown":t.halt(),n.performAjaxAction("movecoursedown",r,this.movedown,this);break;case"show":t.halt(),n.performAjaxAction("showcourse",r,this.show,this);break;case"hide":t.halt(),n.performAjaxAction("hidecourse",r,this.hide,this);break;default:return!1}},remove:function(){this.get("console").removeCourseById(this.get("courseid")),this.get("node").remove()},moveAfter:function(e,t){var n=this.get("console"),r={courseid:this.get("courseid"),moveafter:e,previous:t};n.performAjaxAction("movecourseafter",r,this.moveAfterResponse,this)},moveAfterResponse:function(e,t,n){var r=this.checkAjaxResponse(e,t,n),i=this.get("node"),s;if(r===!1)return s=i.ancestor("ul").one("li[data-id="+n.previous+"]"),s?s.insertAfter(i,"after"):i.ancestor("ul").one("li").insert(i,"before"),!1;this.highlight()}},e.extend(o,i,o.prototype)},"@VERSION@",{requires:["base","node","io-base","moodle-core-notification-exception","json-parse","dd-constrain","dd-proxy","dd-drop","dd-delegate","node-event-delegate"]}); +YUI.add("moodle-course-management",function(e,t){function n(){n.superclass.constructor.apply(this,arguments)}function r(e){n.superclass.constructor.apply(this,[e])}function i(){i.superclass.constructor.apply(this,arguments)}function s(){s.superclass.constructor.apply(this,arguments)}function o(){o.superclass.constructor.apply(this,arguments)}n.NAME="moodle-course-management",n.CSS_PREFIX="management",n.ATTRS={element:{setter:function(t){return typeof t=="string"&&(t=e.one("#"+t)),t}},categorylisting:{value:null},courselisting:{value:null},coursedetails:{value:null},activecategoryid:{value:null},activecourseid:{value:null},categories:{setter:function(t,n){if(e.Lang.isArray(t))return t;var r=this.get(n);return r.push(t),r},value:[]},courses:{validator:function(t){return e.Lang.isArray(t)},value:[]},page:{getter:function(e,t){return e===null&&(e=this.get("element").getData(t),this.set(t,e)),e},value:null},totalpages:{getter:function(e,t){return e===null&&(e=this.get("element").getData(t),this.set(t,e)),e},value:null},totalcourses:{getter:function(e,t){return e===null&&(e=this.get("element").getData(t),this.set(t,e)),e},value:null},ajaxurl:{getter:function(e){return e===null&&(e=M.cfg.wwwroot+"/course/ajax/management.php"),e},value:null},dragdrop:{value:null}},n.prototype={categoriesinit:!1,initializer:function(){this.set("element","coursecat-management");var e=this.get("element"),t=e.one("#category-listing"),n=e.one("#course-listing"),i=null,s=null;t&&(i=t.one('.listitem[data-selected="1"]')),n&&(s=n.one('.listitem[data-selected="1"]')),this.set("categorylisting",t),this.set("courselisting",n),this.set("coursedetails",e.one("#course-detail")),i&&this.set("activecategoryid",i.getData("id")),s&&this.set("activecourseid",s.getData("id")),this.initialiseCategories(t),this.initialiseCourses(),n&&this.set("dragdrop",new r({console:this}))},initialiseCategories:function(e){var t=0;if(!e)return!1;e.all(".listitem[data-id]").each(function(e){this.set("categories",new s({node:e,console:this})),t++},this),this.categoriesinit||(this.get("categorylisting").delegate("click",this.handleCategoryDelegation,"a[data-action]",this),this.categoriesinit=!0)},initialiseCourses:function(){var e=this.getCategoryById(this.get("activecategoryid")),t=this.get("courselisting"),n=0;if(!t)return!1;if(!e)return!1;t.all(".listitem[data-id]").each(function(t){this.registerCourse(new o({node:t,console:this,category:e})),n++},this),t.delegate("click",this.handleCourseDelegation,"a[data-action]",this)},registerCourse:function(e){var t=this.get("courses");t.push(e),this.set("courses",t)},handleCourseDelegation:function(e){var t=e.currentTarget,n=t.getData("action"),r=t.ancestor(".listitem").getData("id"),i=this.getCourseById(r);i&&i.handle(n,e)},handleCategoryDelegation:function(e){var t=e.currentTarget,n=t.getData("action"),r=t.ancestor(".listitem").getData("id"),i=this.getCategoryById(r);i&&i.handle(n,e)},getCategoryById:function(e){var t,n,r=this.get("categories"),i=r.length;for(t=0;t .course-listing"),s=r?r.one("ul.ml"):null,o=i?i.one("ul.ml"):null,u=i?i.getData("canmoveoutof"):!1,a=u?n:o;if(!o)return!1;o.all("> li").each(function(e){this.initCourseListing(e,a)},this),o.setData("dd",new e.DD.Drop({node:o})),u&&s&&s.all("li > div").each(function(e){this.initCategoryListitem(e)},this),e.DD.DDM.on("drag:start",this.dragStart,this),e.DD.DDM.on("drag:end",this.dragEnd,this),e.DD.DDM.on("drag:drag",this.dragDrag,this),e.DD.DDM.on("drop:over",this.dropOver,this),e.DD.DDM.on("drop:enter",this.dropEnter,this),e.DD.DDM.on("drop:exit",this.dropExit,this),e.DD.DDM.on("drop:hit",this.dropHit,this)},initCourseListing:function(t,n){t.setData("dd",(new e.DD.Drag({node:t,target:{padding:"0 0 0 20"}})).addHandle(".drag-handle").plug(e.Plugin.DDProxy,{moveOnEnd:!1,borderStyle:!1}).plug(e.Plugin.DDConstrained,{constrain2node:n}))},initCategoryListitem:function(t){t.setData("dd",new e.DD.Drop({node:t}))},dragStart:function(e){var t=e.target,n=t.get("node"),r=t.get("dragNode");n.addClass("course-being-dragged"),r.addClass("course-being-dragged-proxy").set("innerHTML",n.one("a.coursename").get("innerHTML")),this.previoussibling=n.get("previousSibling")},dragEnd:function(e){var t=e.target,n=t.get("node");n.removeClass("course-being-dragged"),this.get("console").get("element").all("#category-listing li.highlight").removeClass("highlight")},dragDrag:function(e){var t=e.target.lastXY[1];t div a.action-moveup"),o=i.one(" > div a.action-movedown");if(!a||!o)s=i.one(" > div a.action-moveup"),f=u.one(" > div a.action-movedown"),!a&&!o?(l=e.Node.create(' '),f.replace(l),s.replace(f),l.replace(s),l.destroy()):o||s.insert(f,"after");this.updated(!0)}else window.location.reload()},movedown:function(t,n,r){var i,s,o,u,a,f,l,c=this.checkAjaxResponse(t,n,r);if(c===!1)return!1;i=this.get("node"),s=i.next(".listitem");if(s){i.insert(s,"before"),f=s.one(" > div a.action-movedown"),o=i.one(" > div a.action-moveup");if(!f||!o)a=s.one(" > div a.action-moveup"),u=i.one(" > div a.action-movedown"),!f&&!o?(l=e.Node.create(' '),a.replace(l),u.replace(a),l.replace(u),l.destroy()):o||u.insert(a,"before");this.updated(!0)}else window.location.reload()},show:function(e,t,n){var r=this.checkAjaxResponse(e,t,n);if(r===!1)return!1;this.markVisible(),this.updated()},markVisible:function(){return this.get("node").setAttribute("data-visible","1"),!0},hide:function(e,t,n){var r=this.checkAjaxResponse(e,t,n);if(r===!1)return!1;this.markHidden(),this.updated()},markHidden:function(){return this.get("node").setAttribute("data-visible","0"),!0},updated:function(e){e&&this.highlight()},highlight:function(){var e=this.get("node");e.siblings(".highlight").removeClass("highlight"),e.addClass("highlight"),this.highlighttimeout&&window.clearTimeout(this.highlighttimeout),this.highlighttimeout=window.setTimeout(function(){e.removeClass("highlight")},2500)}},e.extend(i,e.Base,i.prototype),s.NAME="moodle-course-management-category",s.CSS_PREFIX="management-category",s.ATTRS={categoryid:{getter:function(e,t){return e===null&&(e=this.get("node").getData("id"),this.set(t,e)),e},value:null,writeOnce:!0},selected:{getter:function(e,t){return e===null&&(e=this.get("node").getData(t),e===null&&(e=!1),this.set(t,e)),e},value:null},courses:{validator:function(t){return e.Lang.isArray(t)},value:[]}},s.prototype={initializer:function(){this.set("itemname","category")},getName:function(){return this.get("node").one("a.categoryname").get("innerHTML")},registerCourse:function(e){var t=this.get("courses");t.push(e),this.set("courses",t)},handle:function(e,t){var n={categoryid:this.get("categoryid")},r=this.get("console").get("activecategoryid");r&&r!==n.categoryid&&(n.selectedcategory=r);switch(e){case"moveup":t.preventDefault(),this.get("console").performAjaxAction("movecategoryup",n,this.moveup,this);break;case"movedown":t.preventDefault(),this.get("console").performAjaxAction("movecategorydown",n,this.movedown,this);break;case"show":t.preventDefault(),this.get("console").performAjaxAction("showcategory",n,this.show,this);break;case"hide":t.preventDefault(),this.get("console").performAjaxAction("hidecategory",n,this.hide,this);break;case"expand":t.preventDefault(),this.get("node").getData("expanded")==="0"&&(this.get("node").setAttribute("data-expanded","1").setData("expanded","true"),this.get("console").performAjaxAction("getsubcategorieshtml",n,this.loadSubcategories,this)),this.expand();break;case"collapse":t.preventDefault(),this.collapse();break;default:return!1}},expand:function(){var e=this.get("node"),t=e.one("a[data-action=expand]");e.removeClass("collapsed").setAttribute("aria-expanded","true"),t.setAttribute("data-action","collapse").one("img").setAttrs({src:M.util.image_url("t/switch_minus","moodle"),title:M.util.get_string("collapse","moodle"),alt:M.util.get_string("collapse","moodle")}),this.get("console").performAjaxAction("expandcategory",{categoryid:this.get("categoryid")},null,this)},collapse:function(){var e=this.get("node"),t=e.one("a[data-action=collapse]");e.addClass("collapsed").setAttribute("aria-expanded","false"),t.setAttribute("data-action","expand").one("img").setAttrs({src:M.util.image_url("t/switch_plus","moodle"),title:M.util.get_string("expand","moodle"),alt:M.util.get_string("expand","moodle")}),this.get("console").performAjaxAction("collapsecategory",{categoryid:this.get("categoryid")},null,this)},loadSubcategories:function(e,t,n){var r=this.checkAjaxResponse(e,t,n),i=this.get("node"),s=this.get("console");return r===!1?!1:(i.append(r.html),s.initialiseCategories(i),M.core&&M.core.actionmenu&&M.core.actionmenu.newDOMNode&&M.core.actionmenu.newDOMNode(i),!0)},moveCourseTo:function(t){var n=this;e.use("moodle-core-notification-confirm",function(){var e=new M.core.confirm({title:M.util.get_string("confirm","moodle"),question:M.util.get_string("confirmcoursemove","moodle",{course:t.getName(),category:n.getName()}),yesLabel:M.util.get_string("yes","moodle"),noLabel:M.util.get_string("no","moodle")});e.on("complete-yes",function(){e.hide(),e.destroy +(),this.get("console").performAjaxAction("movecourseintocategory",{categoryid:this.get("categoryid"),courseid:t.get("courseid")},this.completeMoveCourse,this)},n),e.show()})},completeMoveCourse:function(e,t,n){var r=this.checkAjaxResponse(e,t,n),i;return r===!1?!1:(i=this.get("console").getCourseById(n.courseid),i?(this.highlight(),i&&i.remove(),!0):!1)},show:function(e,t,n){var r=this.checkAjaxResponse(e,t,n);if(r===!1)return!1;this.markVisible(),r.categoryvisibility&&this.updateChildVisibility(r.categoryvisibility),r.coursevisibility&&this.updateCourseVisiblity(r.coursevisibility),this.updated()},hide:function(e,t,n){var r=this.checkAjaxResponse(e,t,n);if(r===!1)return!1;this.markHidden(),r.categoryvisibility&&this.updateChildVisibility(r.categoryvisibility),r.coursevisibility&&this.updateCourseVisiblity(r.coursevisibility),this.updated()},updateCourseVisiblity:function(e){var t=this.get("console"),n,r;try{for(n in e)typeof e[n]=="object"&&(r=t.getCourseById(e[n].id),r&&(e[n].visible==="1"?r.markVisible():r.markHidden()))}catch(i){}return this},updateChildVisibility:function(e){var t=this.get("console"),n,r;try{for(n in e)typeof e[n]=="object"&&(r=t.getCategoryById(e[n].id),r&&(e[n].visible==="1"?r.markVisible():r.markHidden()))}catch(i){}return this}},e.extend(s,i,s.prototype),o.NAME="moodle-course-management-course",o.CSS_PREFIX="management-course",o.ATTRS={courseid:{},selected:{getter:function(e,t){return e===null&&(e=this.get("node").getData(t),this.set(t,e)),e},value:null},node:{},console:{writeOnce:"initOnly"},category:{writeOnce:"initOnly"}},o.prototype={initializer:function(){var e=this.get("node"),t=this.get("category");this.set("courseid",e.getData("id")),t&&t.registerCourse&&t.registerCourse(this),this.set("itemname","course")},getName:function(){return this.get("node").one("a.coursename").get("innerHTML")},handle:function(e,t){var n=this.get("console"),r={courseid:this.get("courseid")};switch(e){case"moveup":t.halt(),n.performAjaxAction("movecourseup",r,this.moveup,this);break;case"movedown":t.halt(),n.performAjaxAction("movecoursedown",r,this.movedown,this);break;case"show":t.halt(),n.performAjaxAction("showcourse",r,this.show,this);break;case"hide":t.halt(),n.performAjaxAction("hidecourse",r,this.hide,this);break;default:return!1}},remove:function(){this.get("console").removeCourseById(this.get("courseid")),this.get("node").remove()},moveAfter:function(e,t){var n=this.get("console"),r={courseid:this.get("courseid"),moveafter:e,previous:t};n.performAjaxAction("movecourseafter",r,this.moveAfterResponse,this)},moveAfterResponse:function(e,t,n){var r=this.checkAjaxResponse(e,t,n),i=this.get("node"),s;if(r===!1)return s=i.ancestor("ul").one("li[data-id="+n.previous+"]"),s?s.insertAfter(i,"after"):i.ancestor("ul").one("li").insert(i,"before"),!1;this.highlight()}},e.extend(o,i,o.prototype)},"@VERSION@",{requires:["base","node","io-base","moodle-core-notification-exception","json-parse","dd-constrain","dd-proxy","dd-drop","dd-delegate","node-event-delegate"]}); diff --git a/course/yui/build/moodle-course-management/moodle-course-management.js b/course/yui/build/moodle-course-management/moodle-course-management.js index ec6b8f4d8b0..02452ba553b 100644 --- a/course/yui/build/moodle-course-management/moodle-course-management.js +++ b/course/yui/build/moodle-course-management/moodle-course-management.js @@ -416,6 +416,10 @@ Console.prototype = { args.action = action; args.ajax = '1'; args.sesskey = M.cfg.sesskey; + if (callback === null) { + callback = function() { + }; + } io.send(this.get('ajaxurl'), { method : 'POST', on : { @@ -1130,6 +1134,7 @@ Category.prototype = { title : M.util.get_string('collapse', 'moodle'), alt : M.util.get_string('collapse', 'moodle') }); + this.get('console').performAjaxAction('expandcategory', {categoryid : this.get('categoryid')}, null, this); }, /** @@ -1145,6 +1150,7 @@ Category.prototype = { title : M.util.get_string('expand', 'moodle'), alt : M.util.get_string('expand', 'moodle') }); + this.get('console').performAjaxAction('collapsecategory', {categoryid : this.get('categoryid')}, null, this); }, /** diff --git a/course/yui/src/management/js/category.js b/course/yui/src/management/js/category.js index c8ca2fbe592..ea09c8c45db 100644 --- a/course/yui/src/management/js/category.js +++ b/course/yui/src/management/js/category.js @@ -155,6 +155,7 @@ Category.prototype = { title : M.util.get_string('collapse', 'moodle'), alt : M.util.get_string('collapse', 'moodle') }); + this.get('console').performAjaxAction('expandcategory', {categoryid : this.get('categoryid')}, null, this); }, /** @@ -170,6 +171,7 @@ Category.prototype = { title : M.util.get_string('expand', 'moodle'), alt : M.util.get_string('expand', 'moodle') }); + this.get('console').performAjaxAction('collapsecategory', {categoryid : this.get('categoryid')}, null, this); }, /** diff --git a/course/yui/src/management/js/console.js b/course/yui/src/management/js/console.js index 5303b3a67db..394d63cd603 100644 --- a/course/yui/src/management/js/console.js +++ b/course/yui/src/management/js/console.js @@ -421,6 +421,11 @@ Console.prototype = { args.action = action; args.ajax = '1'; args.sesskey = M.cfg.sesskey; + if (callback === null) { + callback = function() { + Y.log("'Action '"+action+"' completed", 'debug', 'moodle-course-management'); + }; + } io.send(this.get('ajaxurl'), { method : 'POST', on : {