From 8ed5dd63eeba4e9fc2e290bd3b9584aed440c13c Mon Sep 17 00:00:00 2001 From: tjhunt Date: Thu, 4 Dec 2008 08:53:10 +0000 Subject: [PATCH] course categories: Fix many bugs with category editing and permissions. Clean up code. Bugs: MDL-17479, MDL-16426, MDL-16063, MDL-16013, MDL-15658, MDL-15556, MDL-15161, MDL-14925, MDL-13742, MDL-11557. * Simplify category editing permissions to just moodle/category:manage and moodle/category:seehiddencategories. * Enforce those correctly. (Note MDL 17502 is still outstanding.) * Don't screw up category sort order when you just edit name or description. * Niceties like where redirects go when you cancel or submit forms. * Make sure a global course creator can see the site admin block. * Don't allow a category to be made the child of one of its children! * General code cleanup to bring key files more in line with best pracitice. Apologies for the fact it is one big patch, rather than a series of smaller patches. However, categoryedit.php, category.php and index.php where in pretty bad shape and needed significant cleaning up. categoryedit.php, in particular, was almost completely rewritten. Merged from MOODLE_19_STABLE. --- admin/settings/courses.php | 7 +- course/category.php | 300 ++++++++++++-------------- course/delete_category_form.php | 32 ++- course/edit_form.php | 9 +- course/editcategory.php | 348 ++++++------------------------- course/editcategory_form.php | 28 ++- course/index.php | 190 ++++++++--------- course/lib.php | 145 +++++++++++-- course/search.php | 4 +- lang/en_utf8/error.php | 2 + lang/en_utf8/moodle.php | 1 + lang/en_utf8/role.php | 2 + lib/accesslib.php | 31 +++ lib/datalib.php | 10 +- lib/db/access.php | 39 ++-- lib/weblib.php | 57 ----- theme/standard/styles_layout.css | 3 + version.php | 2 +- 18 files changed, 495 insertions(+), 715 deletions(-) diff --git a/admin/settings/courses.php b/admin/settings/courses.php index 9608e741bfd4d..d0d7013c46406 100644 --- a/admin/settings/courses.php +++ b/admin/settings/courses.php @@ -4,10 +4,11 @@ if ($hassiteconfig or has_capability('moodle/site:backup', $systemcontext) - or has_capability('moodle/category:update', $systemcontext)) { // speedup for non-admins, add all caps used on this page + or has_capability('moodle/category:manage', $systemcontext) + or has_capability('moodle/course:create', $systemcontext)) { // speedup for non-admins, add all caps used on this page - - $ADMIN->add('courses', new admin_externalpage('coursemgmt', get_string('coursemgmt', 'admin'), $CFG->wwwroot . '/course/index.php?categoryedit=on','moodle/category:update')); + $ADMIN->add('courses', new admin_externalpage('coursemgmt', get_string('coursemgmt', 'admin'), $CFG->wwwroot . '/course/index.php?categoryedit=on', + array('moodle/category:manage', 'moodle/course:create'))); $ADMIN->add('courses', new admin_enrolment_page()); diff --git a/course/category.php b/course/category.php index b30cb9fb73f2f..8bcbfd5acf5f9 100644 --- a/course/category.php +++ b/course/category.php @@ -6,25 +6,23 @@ require_once("../config.php"); require_once("lib.php"); - $id = required_param('id', PARAM_INT); // Category id - $page = optional_param('page', 0, PARAM_INT); // which page to show - $perpage = optional_param('perpage', $CFG->coursesperpage, PARAM_INT); // how many per page + $id = required_param('id', PARAM_INT); // Category id + $page = optional_param('page', 0, PARAM_INT); // which page to show + $perpage = optional_param('perpage', $CFG->coursesperpage, PARAM_INT); // how many per page $categoryedit = optional_param('categoryedit', -1, PARAM_BOOL); - $hide = optional_param('hide', 0, PARAM_INT); - $show = optional_param('show', 0, PARAM_INT); - $moveup = optional_param('moveup', 0, PARAM_INT); - $movedown = optional_param('movedown', 0, PARAM_INT); - $moveto = optional_param('moveto', 0, PARAM_INT); - $rename = optional_param('rename', '', PARAM_TEXT); - $resort = optional_param('resort', 0, PARAM_BOOL); - $categorytheme= optional_param('categorytheme', false, PARAM_SAFEDIR); + $hide = optional_param('hide', 0, PARAM_INT); + $show = optional_param('show', 0, PARAM_INT); + $moveup = optional_param('moveup', 0, PARAM_INT); + $movedown = optional_param('movedown', 0, PARAM_INT); + $moveto = optional_param('moveto', 0, PARAM_INT); + $resort = optional_param('resort', 0, PARAM_BOOL); if ($CFG->forcelogin) { require_login(); } if (!$site = get_site()) { - print_error("siteisnotdefined", 'debug'); + print_error('siteisnotdefined', 'debug'); } if (empty($id)) { @@ -38,45 +36,24 @@ if (!$category = $DB->get_record("course_categories", array("id"=>$id))) { print_error("unknowcategory"); } + if (!$category->visible) { + require_capability('moodle/category:viewhiddencategories', $context); + } - if (has_capability('moodle/course:create', $context)) { + if (update_category_button($category->id)) { if ($categoryedit !== -1) { $USER->categoryediting = $categoryedit; } - $navbaritem = update_category_button($category->id); - $creatorediting = !empty($USER->categoryediting); - $adminediting = (has_capability('moodle/site:config', get_context_instance(CONTEXT_SYSTEM)) and $creatorediting); - + $editingon = !empty($USER->categoryediting); + $navbaritem = update_category_button($category->id); // Must call this again after updating the state. } else { - if (!$category->visible) { - print_error('notavailable', 'error'); - } - $navbaritem = print_course_search("", true, "navbar"); - $adminediting = false; - $creatorediting = false; + $navbaritem = print_course_search('', true, 'navbar'); + $editingon = false; } - if (has_capability('moodle/category:update', $context)) { - /// Rename the category if requested - if (!empty($rename) and confirm_sesskey()) { - if (!$DB->set_field("course_categories", "name", $rename, array("id"=>$category->id))) { - notify("An error occurred while renaming the category"); - } - $category->name = $rename; - //trigger events - events_trigger('course_category_updated', $category); - } - - /// Set the category theme if requested - if (($categorytheme !== false) and confirm_sesskey()) { - $category->theme = $categorytheme; - if (!$DB->set_field('course_categories', 'theme', $category->theme, array('id'=>$category->id))) { - notify('An error occurred while setting the theme'); - } - } - + // Process any category actions. + if (has_capability('moodle/category:manage', $context)) { /// Resort the category if requested - if ($resort and confirm_sesskey()) { if ($courses = get_courses($category->id, "fullname ASC", 'c.id,c.fullname,c.sortorder')) { $i = 1; @@ -89,88 +66,75 @@ } } - if(! empty($CFG->allowcategorythemes) ){ - if(isset($category->theme)){ - // specifying theme here saves us some dbqs - theme_setup($category->theme); - } + if(!empty($CFG->allowcategorythemes) && isset($category->theme)) { + // specifying theme here saves us some dbqs + theme_setup($category->theme); } /// Print headings + $numcategories = $DB->count_records('course_categories'); - $numcategories = $DB->count_records("course_categories"); - - $stradministration = get_string("administration"); - $strcategories = get_string("categories"); - $strcategory = get_string("category"); - $strcourses = get_string("courses"); + $stradministration = get_string('administration'); + $strcategories = get_string('categories'); + $strcategory = get_string('category'); + $strcourses = get_string('courses'); $navlinks = array(); $navlinks[] = array('name' => $strcategories, 'link' => 'index.php', 'type' => 'misc'); $navlinks[] = array('name' => format_string($category->name), 'link' => null, 'type' => 'misc'); $navigation = build_navigation($navlinks); - if ($creatorediting) { - if ($adminediting) { - // modify this to treat this as an admin page - - require_once($CFG->libdir.'/adminlib.php'); - admin_externalpage_setup('coursemgmt'); - admin_externalpage_print_header(); - } else { - print_header("$site->shortname: $category->name", "$site->fullname: $strcourses", $navigation, "", "", true, $navbaritem); - } + if ($editingon && update_category_button()) { + // Integrate into the admin tree only if the user can edit categories at the top level, + // otherwise the admin block does not appear to this user, and you get an error. + require_once($CFG->libdir.'/adminlib.php'); + admin_externalpage_setup('coursemgmt'); + admin_externalpage_print_header(); } else { - print_header("$site->shortname: $category->name", "$site->fullname: $strcourses", $navigation, "", "", true, $navbaritem); + print_header("$site->shortname: $category->name", "$site->fullname: $strcourses", $navigation, '', '', true, $navbaritem); } /// Print button to turn editing off - if ($adminediting) { + if ($editingon) { echo '
'.update_category_button($category->id).'
'; } /// Print link to roles - if (has_capability('moodle/role:assign', $context)) { echo ''; } -/// Print the category selector +/// Print the category selector $displaylist = array(); - $parentlist = array(); - - make_categories_list($displaylist, $parentlist, ""); + $notused = array(); + make_categories_list($displaylist, $notused); echo '
'; popup_form('category.php?id=', $displaylist, 'switchcategory', $category->id, '', '', '', false, 'self', $strcategories.':'); echo '
'; /// Print current category description - if (!$creatorediting && $category->description) { + if (!$editingon && $category->description) { print_box_start(); echo format_text($category->description); // for multilang filter print_box_end(); } -/// Editing functions - - if ($creatorediting) { +/// Process any course actions. + if ($editingon) { /// Move a specified course to a new category - if (!empty($moveto) and $data = data_submitted() and confirm_sesskey()) { // Some courses are being moved - // user must have category update in both cats to perform this - require_capability('moodle/category:update', $context); - require_capability('moodle/category:update', get_context_instance(CONTEXT_COURSECAT, $moveto)); + require_capability('moodle/category:manage', $context); + require_capability('moodle/category:manage', get_context_instance(CONTEXT_COURSECAT, $moveto)); - if (! $destcategory = $DB->get_record("course_categories", array("id"=>$data->moveto))) { - print_error("cannotfindcategory", '', '', $data->moveto); + if (!$destcategory = $DB->get_record('course_categories', array('id' => $data->moveto))) { + print_error('cannotfindcategory', '', '', $data->moveto); } - $courses = array(); - foreach ( $data as $key => $value ) { + foreach ($data as $key => $value) { if (preg_match('/^c\d+$/', $key)) { array_push($courses, substr($key, 1)); } @@ -179,73 +143,67 @@ } /// Hide or show a course - if ((!empty($hide) or !empty($show)) and confirm_sesskey()) { require_capability('moodle/course:visibility', $context); if (!empty($hide)) { - $course = $DB->get_record("course", array("id"=>$hide)); + $course = $DB->get_record('course', array('id' => $hide)); $visible = 0; } else { - $course = $DB->get_record("course", array("id"=>$show)); + $course = $DB->get_record('course', array('id' => $show)); $visible = 1; } if ($course) { - if (!$DB->set_field("course", "visible", $visible, array("id"=>$course->id))) { - notify("Could not update that course!"); + if (!$DB->set_field('course', 'visible', $visible, array('id' => $course->id))) { + print_error('errorupdatingcoursevisibility'); } } } /// Move a course up or down - if ((!empty($moveup) or !empty($movedown)) and confirm_sesskey()) { - require_capability('moodle/category:update', $context); + require_capability('moodle/category:manage', $context); - // ensure the course order has continuous ordering + // Ensure the course order has continuous ordering fix_course_sortorder(); $swapcourse = NULL; if (!empty($moveup)) { - if ($movecourse = $DB->get_record('course', array('id'=>$moveup))) { - $swapcourse = $DB->get_record('course', array('sortorder'=>$movecourse->sortorder-1)); + if ($movecourse = $DB->get_record('course', array('id' => $moveup))) { + $swapcourse = $DB->get_record('course', array('sortorder' => $movecourse->sortorder - 1)); } } else { - if ($movecourse = $DB->get_record('course', array('id'=>$movedown))) { - $swapcourse = $DB->get_record('course', array('sortorder'=>$movecourse->sortorder+1)); + if ($movecourse = $DB->get_record('course', array('id' => $movedown))) { + $swapcourse = $DB->get_record('course', array('sortorder' => $movecourse->sortorder + 1)); } } if ($swapcourse and $movecourse) { - $DB->set_field('course', 'sortorder', $swapcourse->sortorder, array('id'=>$movecourse->id)); - $DB->set_field('course', 'sortorder', $movecourse->sortorder, array('id'=>$swapcourse->id)); + $DB->set_field('course', 'sortorder', $swapcourse->sortorder, array('id' => $movecourse->id)); + $DB->set_field('course', 'sortorder', $movecourse->sortorder, array('id' => $swapcourse->id)); } } } // End of editing stuff + if ($editingon && has_capability('moodle/category:manage', $context)) { + echo '
'; - if ($creatorediting) { - echo '
'; - if (has_capability('moodle/category:update', $context)) { // Print button to update this category - unset($options); - $options['id'] = $category->id; - print_single_button('editcategory.php', $options, get_string('editcategorythis'), 'get'); - } + // Print button to update this category + $options = array('id' => $category->id); + print_single_button($CFG->wwwroot.'/course/editcategory.php', $options, get_string('editcategorythis'), 'get'); + + // Print button for creating new categories + $options = array('parent' => $category->id); + print_single_button($CFG->wwwroot.'/course/editcategory.php', $options, get_string('addsubcategory'), 'get'); - if (has_capability('moodle/category:create', $context)) { // Print button for creating new categories - unset($options); - $options['categoryadd'] = 1; - $options['id'] = $id; - print_single_button('editcategory.php', $options, get_string('addsubcategory'), 'get'); - } echo '
'; } /// Print out all the sub-categories - if ($subcategories = $DB->get_records("course_categories", array("parent"=>$category->id), "sortorder ASC")) { + if ($subcategories = $DB->get_records('course_categories', array('parent' => $category->id), 'sortorder ASC')) { $firstentry = true; foreach ($subcategories as $subcategory) { - if ($subcategory->visible or has_capability('moodle/course:create', $context)) { + if ($subcategory->visible || has_capability('moodle/category:viewhiddencategories', $context)) { $subcategorieswereshown = true; if ($firstentry) { echo ''; @@ -253,24 +211,21 @@ echo '
'; $firstentry = false; } - $catlinkcss = $subcategory->visible ? "" : " class=\"dimmed\" "; + $catlinkcss = $subcategory->visible ? '' : ' class="dimmed" '; echo ''. format_string($subcategory->name).'
'; } } if (!$firstentry) { - echo "
"; - echo "
"; + echo ''; + echo '
'; } } - /// Print out all the courses - unset($course); // To avoid unwanted language effects later - $courses = get_courses_page($category->id, 'c.sortorder ASC', - 'c.id,c.sortorder,c.shortname,c.fullname,c.summary,c.visible,c.guest,c.password', - $totalcount, $page*$perpage, $perpage); + 'c.id,c.sortorder,c.shortname,c.fullname,c.summary,c.visible,c.guest,c.password', + $totalcount, $page*$perpage, $perpage); $numcourses = count($courses); if (!$courses) { @@ -278,7 +233,7 @@ print_heading(get_string("nocoursesyet")); } - } else if ($numcourses <= COURSE_MAX_SUMMARIES_PER_PAGE and !$page and !$creatorediting) { + } else if ($numcourses <= COURSE_MAX_SUMMARIES_PER_PAGE and !$page and !$editingon) { print_box_start('courseboxes'); print_courses($category); print_box_end(); @@ -286,33 +241,31 @@ } else { print_paging_bar($totalcount, $page, $perpage, "category.php?id=$category->id&perpage=$perpage&"); - $strcourses = get_string("courses"); - $strselect = get_string("select"); - $stredit = get_string("edit"); - $strdelete = get_string("delete"); - $strbackup = get_string("backup"); - $strrestore = get_string("restore"); - $strmoveup = get_string("moveup"); - $strmovedown = get_string("movedown"); - $strupdate = get_string("update"); - $strhide = get_string("hide"); - $strshow = get_string("show"); - $strsummary = get_string("summary"); - $strsettings = get_string("settings"); - $strassignteachers = get_string("assignteachers"); - $strallowguests = get_string("allowguests"); - $strrequireskey = get_string("requireskey"); + $strcourses = get_string('courses'); + $strselect = get_string('select'); + $stredit = get_string('edit'); + $strdelete = get_string('delete'); + $strbackup = get_string('backup'); + $strrestore = get_string('restore'); + $strmoveup = get_string('moveup'); + $strmovedown = get_string('movedown'); + $strupdate = get_string('update'); + $strhide = get_string('hide'); + $strshow = get_string('show'); + $strsummary = get_string('summary'); + $strsettings = get_string('settings'); + $strassignteachers = get_string('assignteachers'); + $strallowguests = get_string('allowguests'); + $strrequireskey = get_string('requireskey'); echo '
'; echo ''; echo ''; echo ''; - if ($creatorediting) { + if ($editingon) { echo ''; - if ($adminediting) { - echo ''; - } + echo ''; } else { echo ''; } @@ -336,6 +289,7 @@ $atlastpage = true; } + $spacer = ' '; foreach ($courses as $acourse) { if (isset($acourse->context)) { $coursecontext = $acourse->context; @@ -347,24 +301,31 @@ $up = ($count > 1 || !$atfirstpage); $down = ($count < $numcourses || !$atlastpage); - $linkcss = $acourse->visible ? "" : ' class="dimmed" '; + $linkcss = $acourse->visible ? '' : ' class="dimmed" '; echo ''; echo ''; - if ($creatorediting) { - echo "'; @@ -437,19 +406,12 @@ } if ($abletomovecourses) { + $movetocategories = array(); + $notused = array(); + make_categories_list($movetocategories, $notused, 'moodle/category:manage'); + $movetocategories[$category->id] = get_string('moveselectedcoursesto'); echo ''; } @@ -459,8 +421,9 @@ echo '
'; } - echo '
'; - if (has_capability('moodle/category:update', get_context_instance(CONTEXT_SYSTEM)) and $numcourses > 1) { /// Print button to re-sort courses by name + echo '
'; + if (has_capability('moodle/category:manage', $context) and $numcourses > 1) { + /// Print button to re-sort courses by name unset($options); $options['id'] = $category->id; $options['resort'] = 'name'; @@ -468,14 +431,13 @@ print_single_button('category.php', $options, get_string('resortcoursesbyname'), 'get'); } - if (has_capability('moodle/course:create', $context)) { /// Print button to create a new course + if (has_capability('moodle/course:create', $context)) { + /// Print button to create a new course unset($options); $options['category'] = $category->id; print_single_button('edit.php', $options, get_string('addnewcourse'), 'get'); } - echo '
'; - - + echo '
'; print_course_search(); diff --git a/course/delete_category_form.php b/course/delete_category_form.php index f25194fedfa70..82eb305c7c920 100644 --- a/course/delete_category_form.php +++ b/course/delete_category_form.php @@ -11,36 +11,30 @@ function definition() { $mform =& $this->_form; $category = $this->_customdata; + ensure_context_subobj_present($category, CONTEXT_COURSECAT); $this->_category = $category; $mform->addElement('header','general', get_string('categorycurrentcontents', '', format_string($category->name))); $displaylist = array(); - $parentlist = array(); - $children = array(); - make_categories_list($displaylist, $parentlist); - unset($displaylist[$category->id]); - foreach ($displaylist as $catid=>$unused) { - // remove all children of $category - if (isset($parentlist[$catid]) and in_array($category->id, $parentlist[$catid])) { - $children[] = $catid; - unset($displaylist[$catid]); - continue; - } - if (!has_capability('moodle/course:create', get_context_instance(CONTEXT_COURSECAT, $catid))) { - unset($displaylist[$catid]); - } - } + $notused = array(); + make_categories_list($displaylist, $notused, 'moodle/course:create', $category->id); + // Check permissions, to see if it OK to give the option to delete + // the contents, rather than move elsewhere. $candeletecontent = true; - foreach ($children as $catid) { - $context = get_context_instance(CONTEXT_COURSECAT, $catid); - if (!has_capability('moodle/category:delete', $context)) { + $tocheck = array($category); + while (!empty($tocheck)) { + $checkcat = array_pop($tocheck); + $tocheck = $tocheck + get_child_categories($checkcat->id); + if (!has_capability('moodle/category:manage', $checkcat->context)) { $candeletecontent = false; break; } } + // TODO check that the user is allowed to delete all the courses MDL-17502! + $options = array(); if ($displaylist) { @@ -48,7 +42,7 @@ function definition() { } if ($candeletecontent) { - $options[1] =get_string('delete'); + $options[1] = get_string('delete'); } if (empty($options)) { diff --git a/course/edit_form.php b/course/edit_form.php index 627b9a0190e20..66cda9f7b26f5 100644 --- a/course/edit_form.php +++ b/course/edit_form.php @@ -57,16 +57,11 @@ function definition() { //-------------------------------------------------------------------------------- $mform->addElement('header','general', get_string('general', 'form')); - //must have create course capability in both categories in order to move course + // Must have create course capability in both categories in order to move course if (has_capability('moodle/course:create', $categorycontext)) { $displaylist = array(); $parentlist = array(); - make_categories_list($displaylist, $parentlist); - foreach ($displaylist as $key=>$val) { - if (!has_capability('moodle/course:create', get_context_instance(CONTEXT_COURSECAT, $key))) { - unset($displaylist[$key]); - } - } + make_categories_list($displaylist, $parentlist, 'moodle/course:create'); $mform->addElement('select', 'category', get_string('category'), $displaylist); } else { $mform->addElement('hidden', 'category', null); diff --git a/course/editcategory.php b/course/editcategory.php index 70977ad8b4728..4759f54036742 100644 --- a/course/editcategory.php +++ b/course/editcategory.php @@ -1,323 +1,93 @@ coursesperpage, PARAM_INT); // how many per page -$hide = optional_param('hide', 0, PARAM_INT); -$show = optional_param('show', 0, PARAM_INT); -$moveup = optional_param('moveup', 0, PARAM_INT); -$movedown = optional_param('movedown', 0, PARAM_INT); -$moveto = optional_param('moveto', 0, PARAM_INT); -$categoryedit = optional_param('categoryedit', -1, PARAM_BOOL); // Enables Move/Delete/Hide icons near each category in the list -$categoryadd = optional_param('categoryadd', 0, PARAM_BOOL); // Enables the Add Category form -$categoryupdate = optional_param('categoryupdate', 0, PARAM_BOOL); // Enables the Edit Category form -$resort = optional_param('resort', 0, PARAM_BOOL); -$parent = optional_param('parent', 0, PARAM_INT ); +require_login(); -if (!$site = get_site()) { - print_error("siteisnotdefined"); -} - -if ($categoryadd and !$parent) { // Show Add category form: if $id is given, it is used as the parent category - $strtitle = get_string("addnewcategory"); - $context = get_context_instance(CONTEXT_SYSTEM); - $category = null; -} elseif ($categoryadd and $parent) { - $strtitle = get_string("addnewcategory"); - $context = get_context_instance(CONTEXT_COURSECAT,$parent); - $category = null; -} elseif (!is_null($id) && !$categoryadd) { // Show Edit category form: $id is given as the identifier of the category being edited - $strtitle = get_string("editcategorysettings"); - $context = get_context_instance(CONTEXT_COURSECAT, $id); - if (!$category = $DB->get_record("course_categories", array("id"=>$id))) { - print_error("unknowcategory"); +$id = optional_param('id', 0, PARAM_INT); +if ($id) { + if (!$category = $DB->get_record('course_categories', array('id' => $id))) { + print_error('unknowcategory'); } + require_capability('moodle/category:manage', get_context_instance(CONTEXT_COURSECAT, $id)); + $strtitle = get_string('editcategorysettings'); +} else { + $parent = required_param('parent', PARAM_INT); + if ($parent) { + if (!$DB->record_exists('course_categories', array('id' => $parent))) { + print_error('unknowcategory'); + } + $context = get_context_instance(CONTEXT_COURSECAT, $parent); + } else { + $context = get_system_context(); + } + $category = new stdClass(); + $category->id = 0; + $category->parent = $parent; + require_capability('moodle/category:manage', $context); + $strtitle = get_string("addnewcategory"); } -$mform = new editcategory_form('editcategory.php', compact(array('category', 'id'))); +$mform = new editcategory_form('editcategory.php', $category); +$mform->set_data($category); -if (!empty($category)) { - $mform->set_data($category); -} elseif (!is_null($id)) { - $data = new stdClass(); - $data->parent = $id; - $data->categoryadd = 1; - $mform->set_data($data); -} - -if ($mform->is_cancelled()){ - if (empty($category)) { - redirect($CFG->wwwroot .'/course/index.php?categoryedit=on'); +if ($mform->is_cancelled()) { + if ($id) { + redirect($CFG->wwwroot . '/course/category.php?id=' . $id . '&categoryedit=on'); + } else if ($parent) { + redirect($CFG->wwwroot .'/course/category.php?id=' . $parent . '&categoryedit=on'); } else { - redirect($CFG->wwwroot.'/course/category.php?categoryedit=on&id='.$category->id); - } -} else if (($data = $mform->get_data())) { + redirect($CFG->wwwroot .'/course/index.php?categoryedit=on'); + } +} else if ($data = $mform->get_data()) { $newcategory = new stdClass(); - $newcategory->name = $data->name; + $newcategory->name = $data->name; $newcategory->description = $data->description; - $newcategory->parent = $data->parent; // if $id = 0, the new category will be a top-level category + $newcategory->parent = $data->parent; // if $data->parent = 0, the new category will be a top-level category if (isset($data->theme) && !empty($CFG->allowcategorythemes)) { $newcategory->theme = $data->theme; - theme_setup(); /// TODO: Do we really want the theme to be changed here? Doesn't look ok IMO. Eloy - 20080828 } - if (empty($category) && has_capability('moodle/category:create', $context)) { // Create a new category - $newcategory->sortorder = MAX_COURSES_IN_CATEGORY*MAX_COURSE_CATEGORIES; // put as last category in any parent cat - if (!$newcategory->id = $DB->insert_record('course_categories', $newcategory)) { - notify( "Could not insert the new category '$newcategory->name' "); - } else { - $newcategory->context = get_context_instance(CONTEXT_COURSECAT, $newcategory->id); - mark_context_dirty($newcategory->context->path); - fix_course_sortorder(); - redirect('index.php?categoryedit=on'); - } - } elseif (has_capability('moodle/category:update', $context)) { + if ($id) { + // Update an existing category. $newcategory->id = $category->id; - if ($newcategory->parent != $category->parent) { - $parent_cat = $DB->get_record('course_categories', array('id'=>$newcategory->parent)); - move_category($newcategory, $parent_cat); // includes sortorder fix + $parent_cat = $DB->get_record('course_categories', array('id' => $newcategory->parent)); + move_category($newcategory, $parent_cat); } - if (!$DB->update_record('course_categories', $newcategory)) { print_error( "cannotupdatecategory", '', '', $newcategory->name); - } else { - if ($newcategory->parent == 0) { - $redirect_link = 'index.php?categoryedit=on'; - } else { - $redirect_link = 'category.php?id='.$newcategory->id.'&categoryedit=on'; - } - redirect($redirect_link); - } - } -} - - - -// If id is given, but not categoryadd or categoryupdate, we show the category with its list of subcategories -if ($id && !$categoryadd && !$categoryupdate && false) { - /* TODO implement - - if ($CFG->forcelogin) { - require_login(); - } - - // Determine whether to allow user to see this category - if (has_capability('moodle/course:create', $context)) { - if ($categoryedit !== -1) { - $USER->categoryediting = $categoryedit; } - $navbaritem = update_category_button($category->id); - $creatorediting = !empty($USER->categoryediting); - $adminediting = (has_capability('moodle/site:config', get_context_instance(CONTEXT_SYSTEM)) and $creatorediting); - - } else { - if (!$category->visible) { - print_error('notavailable', 'error'); - } - $navbaritem = print_course_search("", true, "navbar"); - $adminediting = false; - $creatorediting = false; - } - - // Resort the category if requested - if ($resort and confirm_sesskey()) { - if ($courses = get_courses($id, "fullname ASC", 'c.id,c.fullname,c.sortorder')) { - // move it off the range - $count = $DB->get_record_sql('SELECT MAX(sortorder) AS max, 1 - FROM {course} WHERE category=' . $category->id); - $count = $count->max + 100; - $DB->begin_sql(); - foreach ($courses as $course) { - $DB->set_field('course', 'sortorder', $count, array('id'=>$course->id)); - $count++; - } - $DB->commit_sql(); - fix_course_sortorder($category->id); - } - } - - // Print headings - $numcategories = $DB->count_records("course_categories"); - - $stradministration = get_string("administration"); - $strcategories = get_string("categories"); - $strcategory = get_string("category"); - $strcourses = get_string("courses"); - - $navlinks = array(); - $navlinks[] = array('name' => $strcategories, 'link' => 'index.php', 'type' => 'misc'); - $navlinks[] = array('name' => $category->name, 'link' => null, 'type' => 'misc'); - $navigation = build_navigation($navlinks); - - if ($creatorediting) { - if ($adminediting) { - // modify this to treat this as an admin page + fix_course_sortorder(); - require_once($CFG->libdir.'/adminlib.php'); - admin_externalpage_setup('categorymgmt'); - admin_externalpage_print_header(); - } else { - print_header("$site->shortname: $category->name", "$site->fullname: $strcategories", $navigation, "", "", true, $navbaritem); - } } else { - print_header("$site->shortname: $category->name", "$site->fullname: $strcategories", $navigation, "", "", true, $navbaritem); - } - - // Print button to turn editing off - if ($adminediting) { - echo '
'.update_category_button($category->id).'
'; - } - - // Print link to roles - - if (has_capability('moodle/role:assign', $context)) { - echo ''; - } - - // Print the category selector - - $displaylist = array(); - $parentlist = array(); - - make_categories_list($displaylist, $parentlist, ""); - - echo '
'; - popup_form('category.php?id=', $displaylist, 'switchcategory', $category->id, '', '', '', false, 'self', $strcategories.':'); - echo '
'; - - // Print current category description - if ($category->description) { - print_box_start(); - print_heading(get_string('description')); - echo $category->description; - print_box_end(); - } - - // Editing functions - if ($creatorediting) { - // Move a specified category to a new category - - if (!empty($moveto) and $data = data_submitted() and confirm_sesskey()) { // Some courses are being moved - - // user must have category update in both cats to perform this - require_capability('moodle/category:update', $context); - require_capability('moodle/category:update', get_context_instance(CONTEXT_COURSECAT, $moveto)); - - if (!$destcategory = $DB->get_record("course_categories", array("id"=>$data->moveto))) { - pritn_error("unknowcategory"); - } - // TODO function to move the category - } - - // Hide or show a category - if ((!empty($hide) or !empty($show)) and confirm_sesskey()) { - require_capability('moodle/category:visibility', $context); - if (!empty($hide)) { - $category = $DB->get_record("course_categories", array("id"=>$hide)); - $visible = 0; - } else { - $category = $DB->get_record("course_categories", array("id"=>$show)); - $visible = 1; - } - if ($category) { - if (! $DB->set_field("course_categories", "visible", $visible, array("id"=>$category->id))) { - notify("Could not update that category!"); - } - } - } - - - // Move a category up or down - if ((!empty($moveup) or !empty($movedown)) and confirm_sesskey()) { - require_capability('moodle/category:update', $context); - $movecategory = NULL; - $swapcategory = NULL; - - // TODO something like fix_course_sortorder() ? - - // we are going to need to know the range - $max = $DB->get_record_sql('SELECT MAX(sortorder) AS max, 1 FROM {course_categories} WHERE id=' . $category->id); - $max = $max->max + 100; - - if (!empty($moveup)) { - $movecategory = $DB->get_record('course_categories', array('id'=>$moveup)); - $swapcategory = $DB->get_record('course_categories', - array('category'=>$category->id, - 'sortorder'=>$movecategory->sortorder - 1)); - } else { - $movecategory = $DB->get_record('course_categories', array('id'=>$movedown)); - $swapcategory = $DB->get_record('course_categories', - array('category'=> $category->id, - 'sortorder'=>$movecategory->sortorder + 1)); - } - - if ($swapcourse and $movecourse) { // Renumber everything for robustness - $DB->begin_sql(); - if (!( $DB->set_field("course", "sortorder", $max, aray("id"=>$swapcourse->id)) - && $DB->set_field("course", "sortorder", $swapcourse->sortorder, array("id"=>$movecourse->id)) - && $DB->set_field("course", "sortorder", $movecourse->sortorder, array("id"=>$swapcourse->id)) - )) { - notify("Could not update that course!"); - } - $DB->commit_sql(); - } - - } - - } // End of editing stuff - - // Print out all the sub-categories - if ($subcategories = $DB->get_records("course_categories", array("parent"=>$category->id), "sortorder ASC")) { - $firstentry = true; - foreach ($subcategories as $subcategory) { - if ($subcategory->visible or has_capability('moodle/course:create', $context)) { - $subcategorieswereshown = true; - if ($firstentry) { - echo '
'.$strcourses.''.$stredit.''.$strselect.''.$strselect.' 
'. format_string($acourse->fullname) .'"; + if ($editingon) { + echo ''; if (has_capability('moodle/course:update', $coursecontext)) { - echo ''. - ''.$stredit.' '; } + echo ''. + ''.$stredit.' '; + } else { + echo $spacer; + } // role assignment link if (has_capability('moodle/role:assign', $coursecontext)) { - echo''.get_string('assignroles', 'role').''; + echo ''. + ''.get_string('assignroles', 'role').' '; + } else { + echo $spacer; } if (can_delete_course($acourse->id)) { echo ''. ''.$strdelete.' '; + } else { + echo $spacer; } // MDL-8885, users with no capability to view hidden courses, should not be able to lock themselves out @@ -378,26 +339,32 @@ '&perpage='.$perpage.'&show='.$acourse->id.'&sesskey='.$USER->sesskey.'">'. ''.$strshow.' '; } + } else { + echo $spacer; } if (has_capability('moodle/site:backup', $coursecontext)) { echo ''. ''.$strbackup.' '; + } else { + echo $spacer; } if (has_capability('moodle/site:restore', $coursecontext)) { echo ''. ''.$strrestore.' '; + } else { + echo $spacer; } - if (has_capability('moodle/category:update', $context)) { + if (has_capability('moodle/category:manage', $context)) { if ($up) { echo ''. ''.$strmoveup.' '; } else { - echo ' '; + echo $spacer; } if ($down) { @@ -405,9 +372,11 @@ '&perpage='.$perpage.'&movedown='.$acourse->id.'&sesskey='.$USER->sesskey.'">'. ''.$strmovedown.' '; } else { - echo ' '; + echo $spacer; } $abletomovecourses = true; + } else { + echo $spacer, $spacer; } echo '
'; - echo '
'; - unset($displaylist[$category->id]); - - // loop and unset categories the user can't move into - - foreach ($displaylist as $did=>$dlist) { - if (!has_capability('moodle/category:update', get_context_instance(CONTEXT_COURSECAT, $did))) { - unset($displaylist[$did]); - } - } - - choose_from_menu ($displaylist, "moveto", "", get_string("moveselectedcoursesto"), "javascript: submitFormById('movecourses')"); + choose_from_menu($movetocategories, 'moveto', $category->id, '', "javascript:submitFormById('movecourses')"); echo ''; echo '
'; - echo ''; - echo '
'.get_string('subcategories').'
'; - $firstentry = false; - } - $catlinkcss = $subcategory->visible ? "" : " class=\"dimmed\" "; - echo ''. - format_string($subcategory->name).'
'; - } - } - if (!$firstentry) { - echo "
"; - echo "
"; + // Create a new category. + $newcategory->sortorder = 999; + if (!$newcategory->id = $DB->insert_record('course_categories', $newcategory)) { + print_error('cannotcreatecategory', '', '', format_string($newcategory->name)); } + $newcategory->context = get_context_instance(CONTEXT_COURSECAT, $newcategory->id); + mark_context_dirty($newcategory->context->path); } + redirect('category.php?id='.$newcategory->id.'&categoryedit=on'); +} - // print option to add a subcategory - if (has_capability('moodle/category:create', $context) && $creatorediting) { - $cat->id = $id; - $mform->set_data($cat); - $mform->display(); - } - */ -} // Print the form - -$site = get_site(); - -$straddnewcategory = get_string("addnewcategory"); -$stradministration = get_string("administration"); -$strcategories = get_string("categories"); +$straddnewcategory = get_string('addnewcategory'); +$stradministration = get_string('administration'); +$strcategories = get_string('categories'); $navlinks = array(); -if (!empty($category->name)) { +if ($id) { $navlinks[] = array('name' => $strtitle, 'link' => null, 'type' => 'misc'); @@ -333,8 +103,8 @@ $navlinks[] = array('name' => $straddnewcategory, 'link' => null, 'type' => 'misc'); - $title = "$site->shortname: $straddnewcategory"; - $fullname = $site->fullname; + $title = "$SITE->shortname: $straddnewcategory"; + $fullname = $SITE->fullname; } $navigation = build_navigation($navlinks); diff --git a/course/editcategory_form.php b/course/editcategory_form.php index 73b54dbd7d971..2973e927738ae 100644 --- a/course/editcategory_form.php +++ b/course/editcategory_form.php @@ -6,15 +6,24 @@ class editcategory_form extends moodleform { function definition() { global $CFG; $mform =& $this->_form; - + $category = $this->_customdata; + // get list of categories to use as parents, with site as the first one $options = array(get_string('top')); $parents = array(); - make_categories_list($options, $parents); - + if ($category->id) { + // Editing an existing category. + make_categories_list($options, $parents, 'moodle/category:manage', $category->id); + $strsubmit = get_string('savechanges'); + } else { + // Making a new category + make_categories_list($options, $parents, 'moodle/category:manage'); + $strsubmit = get_string('createcategory'); + } + $mform->addElement('select', 'parent', get_string('parentcategory'), $options); - $mform->addElement('text', 'name', get_string('categoryname'), array('size'=>'30')); - $mform->addRule('name', get_string('required'), 'required', null); + $mform->addElement('text', 'name', get_string('categoryname'), array('size'=>'30')); + $mform->addRule('name', get_string('required'), 'required', null); $mform->addElement('htmleditor', 'description', get_string('description')); $mform->setType('description', PARAM_RAW); if (!empty($CFG->allowcategorythemes)) { @@ -24,11 +33,12 @@ function definition() { $mform->addElement('select', 'theme', get_string('forcetheme'), $themes); } $mform->setHelpButton('description', array('writing', 'richtext2'), false, 'editorhelpbutton'); - - $mform->addElement('hidden', 'id', null); - $mform->addElement('hidden', 'categoryadd', 0); + + $mform->addElement('hidden', 'id', 0); $mform->setType('id', PARAM_INT); - $this->add_action_buttons(true, get_string('savechanges')); + $mform->setDefault('id', $category->id); + + $this->add_action_buttons(true, $strsubmit); } } ?> diff --git a/course/index.php b/course/index.php index db389f752d934..8ca6f2594030f 100644 --- a/course/index.php +++ b/course/index.php @@ -14,18 +14,17 @@ $moveup = optional_param('moveup',0,PARAM_INT); $movedown = optional_param('movedown',0,PARAM_INT); - $sysctx = get_context_instance(CONTEXT_SYSTEM); - $context = $sysctx; + if ($CFG->forcelogin) { + require_login(); + } if (!$site = get_site()) { print_error('siteisnotdefined', 'debug'); } - if ($CFG->forcelogin) { - require_login(); - } + $systemcontext = get_context_instance(CONTEXT_SYSTEM); - if (has_capability('moodle/category:update', $sysctx)) { + if (update_category_button()) { if ($categoryedit !== -1) { $USER->categoryediting = $categoryedit; } @@ -44,11 +43,9 @@ /// Unless it's an editing admin, just print the regular listing of courses/categories - if (!$adminediting) { - /// Print form for creating new categories - + /// Print form for creating new categories $countcategories = $DB->count_records('course_categories'); if ($countcategories > 1 || ($countcategories == 1 && $DB->count_records('course') > 200)) { @@ -58,7 +55,7 @@ $navlinks = array(); $navlinks[] = array('name'=>$strcategories,'link'=>'','type'=>'misc'); $navigation = build_navigation($navlinks); - print_header("$site->shortname: $strcategories", $strcourses, $navigation, '', '', true, update_categories_button()); + print_header("$site->shortname: $strcategories", $strcourses, $navigation, '', '', true, update_category_button()); print_heading($strcategories); echo skip_main_destination(); print_box_start('categorybox'); @@ -69,45 +66,44 @@ $strfulllistofcourses = get_string('fulllistofcourses'); print_header("$site->shortname: $strfulllistofcourses", $strfulllistofcourses, build_navigation(array(array('name'=>$strfulllistofcourses, 'link'=>'','type'=>'misc'))), - '', '', true, update_categories_button()); + '', '', true, update_category_button()); echo skip_main_destination(); print_box_start('courseboxes'); print_courses(0); print_box_end(); } - /// I am not sure this context in the next has_capability call is correct. - if (isloggedin() and !isguest() and !has_capability('moodle/course:create', $sysctx) and $CFG->enablecourserequests) { // Print link to request a new course + echo '
'; + if ($CFG->enablecourserequests and isloggedin() and !isguest() and !has_capability('moodle/course:create', $systemcontext)) { + /// Print link to request a new course print_single_button('request.php', NULL, get_string('courserequest'), 'get'); } - if (has_capability('moodle/course:create', $sysctx)) { // Print link to create a new course + if (has_capability('moodle/course:create', $systemcontext)) { + /// Print link to create a new course /// Get the 1st available category - $options = array('category' => $DB->get_field('course_categories', 'id', array('parent'=>'0'))); + $options = array('category' => $CFG->defaultrequestcategory); print_single_button('edit.php', $options, get_string('addnewcourse'), 'get'); } - if (has_capability('moodle/site:approvecourse', $sysctx) and !empty($CFG->enablecourserequests)) { + if (has_capability('moodle/site:approvecourse', $systemcontext) and !empty($CFG->enablecourserequests)) { print_single_button('pending.php',NULL, get_string('coursespending'),'get'); } + echo '
'; print_footer(); exit; } +/// Everything else is editing on mode. -/// From now on is all the admin/course creator functions - -/// Delete a category if necessary - +/// Delete a category. if (!empty($delete) and confirm_sesskey()) { - require_once('delete_category_form.php'); - if (!$deletecat = $DB->get_record('course_categories', array('id'=>$delete))) { print_error('invalidcategoryid'); } - - $heading = get_string('deletecategory', '', format_string($deletecat->name)); - $context = get_context_instance(CONTEXT_COURSECAT, $delete); - require_capability('moodle/category:delete', $context); + require_capability('moodle/category:manage', $context); + require_capability('moodle/category:manage', get_category_or_system_context($deletecat->parent)); + $heading = get_string('deletecategory', '', format_string($deletecat->name)); + require_once('delete_category_form.php'); $mform = new delete_category_form(null, $deletecat); $mform->set_data(array('delete'=>$delete)); @@ -124,7 +120,7 @@ 'generalbox boxwidthnormal boxaligncenter'); } $mform->display(); - print_footer(); + admin_externalpage_print_footer(); exit(); } @@ -137,9 +133,14 @@ category_delete_move($deletecat, $data->newparent, true); } + // If we deleted $CFG->defaultrequestcategory, make it point somewhere else. + if ($delete == $CFG->defaultrequestcategory) { + set_config('defaultrequestcategory', get_field('course_categories', 'MIN(id)', 'parent', 0)); + } + print_continue('index.php'); - print_footer(); + admin_externalpage_print_footer(); die; } @@ -147,7 +148,6 @@ print_category_edit_header(); print_heading($strcategories); - /// Create a default category if necessary if (!$categories = get_categories()) { /// No category yet! // Try and make one @@ -160,21 +160,20 @@ mark_context_dirty('/'.SYSCONTEXTID); } - /// Move a category to a new parent if required - - if (!empty($move) and ($moveto>=0) and confirm_sesskey()) { - if ($tempcat = $DB->get_record('course_categories', array('id'=>$move))) { - if ($tempcat->parent != $moveto) { - $newp = $DB->get_record('course_categories', array('id'=>$moveto)); - if (! move_category($tempcat, $newp)) { - notify('Could not update that category!'); + if (!empty($move) and ($moveto >= 0) and confirm_sesskey()) { + if ($cattomove = $DB->get_record('course_categories', array('id'=>$move))) { + require_capability('moodle/category:manage', get_category_or_system_context($cattomove->parent)); + if ($cattomove->parent != $moveto) { + $newparent = $DB->get_record('course_categories', array('id'=>$moveto)); + require_capability('moodle/category:manage', get_category_or_system_context($moveto)); + if (!move_category($cattomove, $newparent)) { + print_error('cannotupdatecategory', '', '', format_string($cattomove->name)); } } } } - /// Hide or show a category if ((!empty($hide) or !empty($show)) and confirm_sesskey()) { if (!empty($hide)) { @@ -184,31 +183,31 @@ $tempcat = $DB->get_record('course_categories', array('id'=>$show)); $visible = 1; } + require_capability('moodle/category:manage', get_category_or_system_context($tempcat->parent)); if ($tempcat) { if (!$DB->set_field('course_categories', 'visible', $visible, array('id'=>$tempcat->id))) { - notify('Could not update that category!'); + print_error('cannotupdatecategory', '', '', format_string($tempcat->name)); } - if (!$DB->set_field('course', 'visible', $visible, array('category'=>$tempcat->id))) { - notify('Could not hide/show any courses in this category !'); + if (!$DB->set_field('course', 'visible', $visible, array('category' => $tempcat->id))) { + print_error('cannotshowhidecoursesincategory', '', '', format_string($tempcat->name)); } } } - /// Move a category up or down - if ((!empty($moveup) or !empty($movedown)) and confirm_sesskey()) { - fix_course_sortorder(); $swapcategory = NULL; if (!empty($moveup)) { + require_capability('moodle/category:manage', get_context_instance(CONTEXT_COURSECAT, $moveup)); if ($movecategory = $DB->get_record('course_categories', array('id'=>$moveup))) { if ($swapcategory = $DB->get_records_select('course_categories', "sortordersortorder, $movecategory->parent), 'sortorder ASC', '*', 0, 1)) { $swapcategory = reset($swapcategory); } } } else { + require_capability('moodle/category:manage', get_context_instance(CONTEXT_COURSECAT, $movedown)); if ($movecategory = $DB->get_record('course_categories', array('id'=>$movedown))) { if ($swapcategory = $DB->get_records_select('course_categories', "sortorder>? AND parent=?", array($movecategory->sortorder, $movecategory->parent), 'sortorder ASC', '*', 0, 1)) { $swapcategory = reset($swapcategory); @@ -224,11 +223,7 @@ fix_course_sortorder(); } -/// This should not be needed anymore - //fix_course_sortorder(); - /// Print out the categories with all the knobs - $strcategories = get_string('categories'); $strcourses = get_string('courses'); $strmovecategoryto = get_string('movecategoryto'); @@ -238,7 +233,7 @@ $parentlist = array(); $displaylist[0] = get_string('top'); - make_categories_list($displaylist, $parentlist, ''); + make_categories_list($displaylist, $parentlist); echo ''; echo ''; @@ -253,52 +248,39 @@ echo '
'; - if (!empty($category->id)) { - // Print link to create a new course in current category - if (has_capability('moodle/course:create', $context)) { - $options = array(); - $options['category'] = $category->id; - print_single_button('edit.php', $options, get_string('addnewcourse'), 'get'); - } - }else{ - if (has_capability('moodle/course:create', $sysctx)) { - // print create course link to first category - $options = array(); - $options = array('category' => $DB->get_field('course_categories', 'id', array('parent'=>'0'))); - print_single_button('edit.php', $options, get_string('addnewcourse'), 'get'); - } + if (has_capability('moodle/course:create', $systemcontext)) { + // print create course link to first category + $options = array(); + $options = array('category' => $CFG->defaultrequestcategory); + print_single_button('edit.php', $options, get_string('addnewcourse'), 'get'); } // Print button for creating new categories - if (has_capability('moodle/category:create', $context)) { - unset($options); - if (!empty($category->id)) { - $options['id'] = $category->id; - } else { - $options['id'] = 0; - } - $options['categoryadd'] = 1; + if (has_capability('moodle/category:manage', $systemcontext)) { + $options = array(); + $options['parent'] = 0; print_single_button('editcategory.php', $options, get_string('addnewcategory'), 'get'); } - if (has_capability('moodle/site:approvecourse', $sysctx) and !empty($CFG->enablecourserequests)) { + if (has_capability('moodle/site:approvecourse', $systemcontext) and !empty($CFG->enablecourserequests)) { print_single_button('pending.php',NULL, get_string('coursespending'), 'get'); } // admin page does not allow custom buttons in the navigation bar echo '
'; - echo update_categories_button(); + echo update_category_button(); echo '
'; - print_footer(); + admin_externalpage_print_footer(); function print_category_edit($category, $displaylist, $parentslist, $depth=-1, $up=false, $down=false) { /// Recursive function to print all the categories ready for editing global $CFG, $USER; - static $str = ''; + static $str = NULL; - if (empty($str)) { + if (is_null($str)) { + $str = new stdClass; $str->edit = get_string('edit'); $str->delete = get_string('delete'); $str->moveup = get_string('moveup'); @@ -306,9 +288,10 @@ function print_category_edit($category, $displaylist, $parentslist, $depth=-1, $ $str->edit = get_string('editthiscategory'); $str->hide = get_string('hide'); $str->show = get_string('show'); + $str->spacer = ' '; } - if ($category) { + if (!empty($category)) { if (!isset($category->context)) { $category->context = get_context_instance(CONTEXT_COURSECAT, $category->id); @@ -328,17 +311,13 @@ function print_category_edit($category, $displaylist, $parentslist, $depth=-1, $ echo ''; echo ''; echo ''; } else { @@ -396,13 +381,8 @@ function print_category_edit_header() { global $CFG; global $SITE; - if (has_capability('moodle/site:config', get_context_instance(CONTEXT_SYSTEM))) { - require_once($CFG->libdir.'/adminlib.php'); - admin_externalpage_setup('coursemgmt'); - admin_externalpage_print_header(); - } else { - print_header("$SITE->shortname:". get_string('categories'), get_string('courses'), - build_navigation(array(array('name'=>get_string('categories'),'link'=>'','type'=>'misc'))), '', '', true, update_categories_button()); - } + require_once($CFG->libdir.'/adminlib.php'); + admin_externalpage_setup('coursemgmt'); + admin_externalpage_print_header(); } ?> diff --git a/course/lib.php b/course/lib.php index a608c5e9c3af9..f4fb9792f591a 100644 --- a/course/lib.php +++ b/course/lib.php @@ -1625,6 +1625,21 @@ function print_section_add_menus($course, $section, $modnames, $vertical=false, } } +/** + * Return the course category context for the category with id $categoryid, except + * that if $categoryid is 0, return the system context. + * + * @param integer $categoryid a category id or 0. + * @return object the corresponding context + */ +function get_category_or_system_context($categoryid) { + if ($categoryid) { + return get_context_instance(CONTEXT_COURSECAT, $categoryid); + } else { + return get_context_instance(CONTEXT_SYSTEM); + } +} + /** * Rebuilds the cached list of course activities stored in the database * @param int $courseid - id of course to rebuil, empty means all @@ -1676,10 +1691,13 @@ function rebuild_course_cache($courseid=0, $clearonly=false) { } /** - * Returns an array of the children categories for the given category - * ID by caching all of the categories in a static hash + * Gets the child categories of a given coures category. Uses a static cache + * to make repeat calls efficient. + * + * @param unknown_type $parentid the id of a course category. + * @return array all the child course categories. */ -function get_child_categories($parent) { +function get_child_categories($parentid) { static $allcategories = null; // only fill in this variable the first time @@ -1695,19 +1713,51 @@ function get_child_categories($parent) { } } - if (empty($allcategories[$parent])) { + if (empty($allcategories[$parentid])) { return array(); } else { - return $allcategories[$parent]; + return $allcategories[$parentid]; } } /** - * Given an empty array, this function recursively travels the - * categories, building up a nice list for display. It also makes - * an array that list all the parents for each category. + * This function recursively travels the categories, building up a nice list + * for display. It also makes an array that list all the parents for each + * category. + * + * For example, if you have a tree of categories like: + * Miscellaneous (id = 1) + * Subcategory (id = 2) + * Sub-subcategory (id = 4) + * Other category (id = 3) + * Then after calling this function you will have + * $list = array(1 => 'Miscellaneous', 2 => 'Miscellaneous / Subcategory', + * 4 => 'Miscellaneous / Subcategory / Sub-subcategory', + * 3 => 'Other category'); + * $parents = array(2 => array(1), 4 => array(1, 2)); + * + * If you specify $requiredcapability, then only categories where the current + * user has that capability will be added to $list, although all categories + * will still be added to $parents, and if you only have $requiredcapability + * in a child category, not the parent, then the child catgegory will still be + * included. + * + * If you specify the option $excluded, then that category, and all its children, + * are omitted from the tree. This is useful when you are doing something like + * moving categories, where you do not want to allow people to move a category + * to be the child of itself. + * + * @param array $list For output, accumulates an array categoryid => full category path name + * @param array $parents For output, accumulates an array categoryid => list of parent category ids. + * @param string $requiredcapability if given, only categories where the current + * user has this capability will be added to $list. + * @param integer $excludeid Omit this category and its children from the lists built. + * @param object $category Build the tree starting at this category - otherwise starts at the top level. + * @param string $path For internal use, as part of recursive calls. */ -function make_categories_list(&$list, &$parents, $category=NULL, $path="") { +function make_categories_list(&$list, &$parents, $requiredcapability = '', + $excludeid = 0, $category = NULL, $path = "") { + // initialize the arrays if needed if (!is_array($list)) { $list = array(); @@ -1716,18 +1766,34 @@ function make_categories_list(&$list, &$parents, $category=NULL, $path="") { $parents = array(); } - if ($category) { + if (empty($category)) { + // Start at the top level. + $category = new stdClass; + $category->id = 0; + } else { + // This is the excluded category, don't include it. + if ($excludeid > 0 && $excludeid == $category->id) { + return; + } + + // Update $path. if ($path) { $path = $path.' / '.format_string($category->name); } else { $path = format_string($category->name); } - $list[$category->id] = $path; - } else { - $category->id = 0; + + // Add this category to $list, if the permissions check out. + if ($requiredcapability) { + ensure_context_subobj_present($category, CONTEXT_COURSECAT); + } + if (!$requiredcapability || has_capability($requiredcapability, $category->context)) { + $list[$category->id] = $path; + } } - if ($categories = get_child_categories($category->id)) { // Print all the children recursively + // Add all the children recursively, while updating the parents array. + if ($categories = get_child_categories($category->id)) { foreach ($categories as $cat) { if (!empty($category->id)) { if (isset($parents[$category->id])) { @@ -1735,7 +1801,7 @@ function make_categories_list(&$list, &$parents, $category=NULL, $path="") { } $parents[$cat->id][] = $category->id; } - make_categories_list($list, $parents, $cat, $path); + make_categories_list($list, $parents, $requiredcapability, $excludeid, $cat, $path); } } } @@ -1745,7 +1811,7 @@ function make_categories_list(&$list, &$parents, $category=NULL, $path="") { * Recursive function to print out all the categories in a nice format * with or without courses included */ -function print_whole_category_list($category=NULL, $displaylist=NULL, $parentslist=NULL, $depth=-1, $files = true) { +function print_whole_category_list($category=NULL, $displaylist=NULL, $parentslist=NULL, $depth=-1, $showcourses = true) { global $CFG; if (isset($CFG->max_category_depth) && ($depth >= $CFG->max_category_depth)) { @@ -1757,8 +1823,8 @@ function print_whole_category_list($category=NULL, $displaylist=NULL, $parentsli } if ($category) { - if ($category->visible or has_capability('moodle/course:update', get_context_instance(CONTEXT_SYSTEM))) { - print_category_info($category, $depth, $files); + if ($category->visible or has_capability('moodle/category:viewhiddencategories', get_context_instance(CONTEXT_SYSTEM))) { + print_category_info($category, $depth, $showcourses); } else { return; // Don't bother printing children of invisible categories } @@ -1781,7 +1847,7 @@ function print_whole_category_list($category=NULL, $displaylist=NULL, $parentsli $down = $last ? false : true; $first = false; - print_whole_category_list($cat, $displaylist, $parentslist, $depth + 1, $files); + print_whole_category_list($cat, $displaylist, $parentslist, $depth + 1, $showcourses); } } } @@ -1807,7 +1873,7 @@ function make_categories_options() { * Prints the category info in indented fashion * This function is only used by print_whole_category_list() above */ -function print_category_info($category, $depth, $files = false) { +function print_category_info($category, $depth, $showcourses = false) { global $CFG, $DB; static $strallowguests, $strrequireskey, $strsummary; @@ -1820,7 +1886,7 @@ function print_category_info($category, $depth, $files = false) { $catlinkcss = $category->visible ? '' : ' class="dimmed" '; $coursecount = $DB->count_records('course') <= FRONTPAGECOURSELIMIT; - if ($files and $coursecount) { + if ($showcourses and $coursecount) { $catimage = ''; } else { $catimage = " "; @@ -1829,7 +1895,7 @@ function print_category_info($category, $depth, $files = false) { echo "\n\n".'
'.$strcategories.''; /// Print little icons - if (has_capability('moodle/category:update', $category->context)) { - echo 'context)) { + echo ' '; - } - if (has_capability('moodle/category:delete', $category->context)) { echo ' '; - } - if (has_capability('moodle/category:visibility', $category->context)) { if (!empty($category->visible)) { echo ' '; @@ -346,27 +325,33 @@ function print_category_edit($category, $displaylist, $parentslist, $depth=-1, $ echo ' '; } - } - if ($up) { - echo ' '; - } - if ($down) { - echo ' '; + if ($up) { + echo ' '; + } else { + echo $str->spacer; + } + if ($down) { + echo ' '; + } else { + echo $str->spacer; + } } echo ''; - $tempdisplaylist = $displaylist; - unset($tempdisplaylist[$category->id]); - foreach ($parentslist as $key => $parents) { - if (in_array($category->id, $parents)) { - unset($tempdisplaylist[$key]); + if (has_capability('moodle/category:manage', $category->context)) { + $tempdisplaylist = $displaylist; + unset($tempdisplaylist[$category->id]); + foreach ($parentslist as $key => $parents) { + if (in_array($category->id, $parents)) { + unset($tempdisplaylist[$key]); + } } + popup_form ("index.php?move=$category->id&sesskey=$USER->sesskey&moveto=", $tempdisplaylist, "moveform$category->id", $category->parent, '', '', '', false); } - popup_form ("index.php?move=$category->id&sesskey=$USER->sesskey&moveto=", $tempdisplaylist, "moveform$category->id", $category->parent, '', '', '', false); echo '
'; $courses = get_courses($category->id, 'c.sortorder ASC', 'c.id,c.sortorder,c.visible,c.fullname,c.shortname,c.password,c.summary,c.guest,c.cost,c.currency'); - if ($files and $coursecount) { + if ($showcourses and $coursecount) { echo ''; @@ -1900,7 +1966,42 @@ function print_category_info($category, $depth, $files = false) { echo '
'; } +/** + * Prints the turn editing on/off button on course/index.php or course/category.php. + * + * @param integer $categoryid The id of the category we are showing, or 0 for system context. + * @return string HTML of the editing button, or empty string, if this user is not allowed + * to see it. + */ +function update_category_button($categoryid = 0) { + global $CFG, $USER; + + // Check permissions. + $context = get_category_or_system_context($categoryid); + if (!has_any_capability(array('moodle/category:manage', 'moodle/course:create'), $context)) { + return ''; + } + // Work out the appropriate action. + if (!empty($USER->categoryediting)) { + $label = get_string('turneditingoff'); + $edit = 'off'; + } else { + $label = get_string('turneditingon'); + $edit = 'on'; + } + + // Generate the button HTML. + $options = array('categoryedit' => $edit, 'sesskey' => sesskey()); + if ($categoryid) { + $options['id'] = $categoryid; + $page = 'category.php'; + } else { + $page = 'index.php'; + } + return print_single_button($CFG->wwwroot . '/course/' . $page, $options, + $label, 'get', '', true); +} /** * Category is 0 (for all courses) or an object diff --git a/course/search.php b/course/search.php index 3295bef3485cf..e8576481f571f 100644 --- a/course/search.php +++ b/course/search.php @@ -72,7 +72,7 @@ $displaylist = array(); $parentlist = array(); - make_categories_list($displaylist, $parentlist, ""); + make_categories_list($displaylist, $parentlist); $strcourses = get_string("courses"); $strsearch = get_string("search"); @@ -258,7 +258,7 @@ // this is ok since this will get inherited from course category context // if it is set - if (has_capability('moodle/category:update', $coursecontext)) { + if (has_capability('moodle/category:manage', $coursecontext)) { echo "id\" />\n"; } else { echo "id\" disabled=\"disabled\" />\n"; diff --git a/lang/en_utf8/error.php b/lang/en_utf8/error.php index f1c71f72e8032..42fa1fe70dc4c 100644 --- a/lang/en_utf8/error.php +++ b/lang/en_utf8/error.php @@ -127,6 +127,7 @@ $string['cannotsetupcapforplugin'] = 'Could not set up the capabilities for $a'; $string['cannotsetupcapformod'] = 'Could not set up the capabilities for $a'; $string['cannotsetupsite'] = 'Serious error! Could not set up the site!'; +$string['cannotshowhidecoursesincategory'] = 'Cannot show/hide the courses in category $a.'; $string['cannotunzipfile'] = 'Cannot unzip file'; $string['cannotupgradeblock'] = 'Upgrade of blocks system failed! (Could not update version in config table.)'; $string['cannotupgradecaps'] = 'Had difficulties upgrading the core capabilities for the Roles system'; @@ -211,6 +212,7 @@ $string['erroronline'] = 'Error on line $a'; $string['errorreadingfile'] = 'Error reading file \"$a\"'; $string['errorunzippingfiles'] = 'Error unzipping files'; +$string['errorupdatingcoursevisibility'] = 'Error updating the course visibility'; $string['errorsettinguserpref'] = 'Error setting user preference'; $string['expiredkey'] = 'Expired key'; $string['failtoloadblocks'] = 'One or more blocks are registered in the database, but they all failed to load!'; diff --git a/lang/en_utf8/moodle.php b/lang/en_utf8/moodle.php index ebb3a054d3746..a6ae1f7acb88a 100644 --- a/lang/en_utf8/moodle.php +++ b/lang/en_utf8/moodle.php @@ -329,6 +329,7 @@ $string['courseuploadlimit'] = 'Course upload limit'; $string['create'] = 'Create'; $string['createaccount'] = 'Create my new account'; +$string['createcategory'] = 'Create category'; $string['createfolder'] = 'Create a folder in $a'; $string['createuserandpass'] = 'Choose your username and password'; $string['createziparchive'] = 'Create zip archive'; diff --git a/lang/en_utf8/role.php b/lang/en_utf8/role.php index b176faab4ef5e..67122ac4d5708 100644 --- a/lang/en_utf8/role.php +++ b/lang/en_utf8/role.php @@ -35,7 +35,9 @@ $string['capability'] = 'Capability'; $string['category:create'] = 'Create categories'; $string['category:delete'] = 'Delete categories'; +$string['category:manage'] = 'Manage categories'; $string['category:update'] = 'Update categories'; +$string['category:viewhiddencategories'] = 'See hidden categories'; $string['category:visibility'] = 'See hidden categories'; $string['checkglobalpermissions'] = 'Check system permissions'; $string['checkpermissions'] = 'Check permissions'; diff --git a/lib/accesslib.php b/lib/accesslib.php index 8d8ead3694044..3b64ae596605b 100755 --- a/lib/accesslib.php +++ b/lib/accesslib.php @@ -5848,6 +5848,37 @@ function make_context_subobj($rec) { return $rec; } +/** + * Do some basic, quick checks to see whether $rec->context looks like a + * valid context object. + * + * @param object $rec a think that has a context, for example a course, + * course category, course modules, etc. + * @param integer $contextlevel the type of thing $rec is, one of the CONTEXT_... constants. + * @return boolean whether $rec->context looks like the correct context object + * for this thing. + */ +function is_context_subobj_valid($rec, $contextlevel) { + return isset($rec->context) && isset($rec->context->id) && + isset($rec->context->path) && isset($rec->context->depth) && + isset($rec->context->contextlevel) && isset($rec->context->instanceid) && + $rec->context->contextlevel == $contextlevel && $rec->context->instanceid == $rec->id; +} + +/** + * When you have a record (for example a $category, $course, $user or $cm that may, + * or may not, have come from a place that does make_context_subobj, you can use + * this method to ensure that $rec->context is present and correct before you continue. + * + * @param object $rec a thing that has an associated context. + * @param integer $contextlevel the type of thing $rec is, one of the CONTEXT_... constants. + */ +function ensure_context_subobj_present(&$rec, $contextlevel) { + if (!is_context_subobj_valid($rec, $contextlevel)) { + $rec->context = get_context_instance($contextlevel, $rec->id); + } +} + /** * Fetch recent dirty contexts to know cheaply whether our $USER->access * is stale and needs to be reloaded. diff --git a/lib/datalib.php b/lib/datalib.php index 5396ae137fc25..272aa5bcdc030 100644 --- a/lib/datalib.php +++ b/lib/datalib.php @@ -941,14 +941,14 @@ function get_my_courses($userid, $sort='visible DESC,sortorder ASC', $fields=NUL // // Perhaps it's actually visible to $USER - // check moodle/category:visibility + // check moodle/category:viewhiddencategories // // The name isn't obvious, but the description says // "See hidden categories" so the user shall see... // But also check if the allowvisiblecoursesinhiddencategories setting is true, and check for course visibility if ($viscat === false) { $catctx = $cats[$courses[$n]->category]->context; - if (has_capability('moodle/category:visibility', $catctx, $USER->id)) { + if (has_capability('moodle/category:viewhiddencategories', $catctx, $USER->id)) { $vcatpaths[$courses[$n]->categorypath] = true; $viscat = true; } elseif ($CFG->allowvisiblecoursesinhiddencategories && $courses[$n]->visible == true) { @@ -1160,7 +1160,7 @@ function get_categories($parent='none', $sort=NULL, $shallow=true) { if( $rs = $DB->get_recordset_sql($sql, $params) ){ foreach($rs as $cat) { $cat = make_context_subobj($cat); - if ($cat->visible || has_capability('moodle/category:visibility',$cat->context)) { + if ($cat->visible || has_capability('moodle/category:viewhiddencategories',$cat->context)) { $categories[$cat->id] = $cat; } } @@ -1332,7 +1332,7 @@ function fix_course_sortorder() { } // now make sure that sortorders in course table are withing the category sortorder ranges - $sql = "SELECT cc.id, cc.sortorder + $sql = "SELECT DISTINCT cc.id, cc.sortorder FROM {course_categories} cc JOIN {course} c ON c.category = cc.id WHERE c.sortorder < cc.sortorder OR c.sortorder > cc.sortorder + ".MAX_COURSES_IN_CATEGORY; @@ -2184,7 +2184,7 @@ function print_object($object) { * we'll save a dbquery * * - If we return false, you'll still need to check if - * the user can has the 'moodle/category:visibility' + * the user can has the 'moodle/category:viewhiddencategories' * capability... * * - Will generate 2 DB calls. diff --git a/lib/db/access.php b/lib/db/access.php index 8fc3555dca918..fafd9632e0016 100644 --- a/lib/db/access.php +++ b/lib/db/access.php @@ -452,7 +452,11 @@ ) ), - 'moodle/category:create' => array( + // Create, update and delete course categories. (Deleting a course category + // does not let you delete the courses it contains, unless you also have + // moodle/course: delete.) Creating and deleting requires this permission in + // the parent category. + 'moodle/category:manage' => array( 'riskbitmask' => RISK_XSS, @@ -460,38 +464,19 @@ 'contextlevel' => CONTEXT_COURSECAT, 'legacy' => array( 'admin' => CAP_ALLOW - ) - ), - - 'moodle/category:delete' => array( - - 'riskbitmask' => RISK_DATALOSS, - - 'captype' => 'write', - 'contextlevel' => CONTEXT_COURSECAT, - 'legacy' => array( - 'admin' => CAP_ALLOW - ) - ), - - 'moodle/category:update' => array( - - 'riskbitmask' => RISK_XSS, - - 'captype' => 'write', - 'contextlevel' => CONTEXT_COURSECAT, - 'legacy' => array( - 'admin' => CAP_ALLOW - ) + ), + 'clonepermissionsfrom' => 'moodle/category:update' ), - 'moodle/category:visibility' => array( + 'moodle/category:viewhiddencategories' => array( - 'captype' => 'write', + 'captype' => 'read', 'contextlevel' => CONTEXT_COURSECAT, 'legacy' => array( + 'coursecreator' => CAP_ALLOW, 'admin' => CAP_ALLOW - ) + ), + 'clonepermissionsfrom' => 'moodle/category:visibility' ), 'moodle/course:create' => array( diff --git a/lib/weblib.php b/lib/weblib.php index 7a5a0b7fe9fbc..5cd606f35ac34 100644 --- a/lib/weblib.php +++ b/lib/weblib.php @@ -5290,63 +5290,6 @@ function update_module_button($moduleid, $courseid, $string) { } } -/** - * Prints the editing button on a category page - * - * @uses $CFG - * @uses $USER - * @param int $categoryid ? - * @return string - * @todo Finish documenting this function - */ -function update_category_button($categoryid) { - global $CFG, $USER; - - if (has_capability('moodle/category:update', get_context_instance(CONTEXT_COURSECAT, $categoryid))) { - if (!empty($USER->categoryediting)) { - $string = get_string('turneditingoff'); - $edit = 'off'; - } else { - $string = get_string('turneditingon'); - $edit = 'on'; - } - - return "frametarget method=\"get\" action=\"$CFG->wwwroot/course/category.php\">". - '
'. - "". - "". - "sesskey\" />". - "
"; - } -} - -/** - * Prints the editing button on categories listing - * - * @uses $CFG - * @uses $USER - * @return string - */ -function update_categories_button() { - global $CFG, $USER; - - if (has_capability('moodle/category:update', get_context_instance(CONTEXT_SYSTEM))) { - if (!empty($USER->categoryediting)) { - $string = get_string('turneditingoff'); - $categoryedit = 'off'; - } else { - $string = get_string('turneditingon'); - $categoryedit = 'on'; - } - - return "
frametarget method=\"get\" action=\"$CFG->wwwroot/course/index.php\">". - '
'. - ''. - ''. - '
'; - } -} - /** * Prints the editing button on search results listing * For bulk move courses to another category diff --git a/theme/standard/styles_layout.css b/theme/standard/styles_layout.css index 186824e851f11..c3fe009c6ec1a 100644 --- a/theme/standard/styles_layout.css +++ b/theme/standard/styles_layout.css @@ -2229,10 +2229,12 @@ body#course-category .addcategory { padding: 10px; } +body#course-index .buttons .singlebutton, body#course-category .buttons .singlebutton { display: inline; } +body#course-index .buttons, body#course-category .buttons { text-align: center; margin-bottom: 15px; @@ -2329,6 +2331,7 @@ body#course-info .generalbox.info { #coursesearch, #coursesearch2 { + margin-top: 1em; text-align:center; } diff --git a/version.php b/version.php index e820c20bc6820..a30dc58d105f6 100644 --- a/version.php +++ b/version.php @@ -6,7 +6,7 @@ // This is compared against the values stored in the database to determine // whether upgrades should be performed (see lib/db/*.php) - $version = 2008120100; // YYYYMMDD = date of the last version bump + $version = 2008120400; // YYYYMMDD = date of the last version bump // XX = daily increments $release = '2.0 dev (Build: 20081204)'; // Human-friendly version name