示例#1
0
 /**
  * Renders the list of subcategories in a category
  *
  * @param coursecat_helper $chelper various display options
  * @param coursecat $coursecat
  * @param int $depth depth of the category in the current tree
  * @return string
  */
 protected function coursecat_subcategories(coursecat_helper $chelper, $coursecat, $depth)
 {
     global $CFG;
     $subcategories = array();
     if (!$chelper->get_categories_display_option('nodisplay')) {
         $subcategories = $coursecat->get_children($chelper->get_categories_display_options());
     }
     // IOMAD:  Filter out unwanted categories
     if (!is_siteadmin()) {
         $subcategories = iomad::iomad_filter_categories($subcategories);
     }
     $totalcount = $coursecat->get_children_count();
     if (!$totalcount) {
         // Note that we call get_child_categories_count() AFTER get_child_categories() to avoid extra DB requests.
         // Categories count is cached during children categories retrieval.
         return '';
     }
     // prepare content of paging bar or more link if it is needed
     $paginationurl = $chelper->get_categories_display_option('paginationurl');
     $paginationallowall = $chelper->get_categories_display_option('paginationallowall');
     if ($totalcount > count($subcategories)) {
         if ($paginationurl) {
             // the option 'paginationurl was specified, display pagingbar
             $perpage = $chelper->get_categories_display_option('limit', $CFG->coursesperpage);
             $page = $chelper->get_categories_display_option('offset') / $perpage;
             $pagingbar = $this->paging_bar($totalcount, $page, $perpage, $paginationurl->out(false, array('perpage' => $perpage)));
             if ($paginationallowall) {
                 $pagingbar .= html_writer::tag('div', html_writer::link($paginationurl->out(false, array('perpage' => 'all')), get_string('showall', '', $totalcount)), array('class' => 'paging paging-showall'));
             }
         } else {
             if ($viewmoreurl = $chelper->get_categories_display_option('viewmoreurl')) {
                 // the option 'viewmoreurl' was specified, display more link (if it is link to category view page, add category id)
                 if ($viewmoreurl->compare(new moodle_url('/course/index.php'), URL_MATCH_BASE)) {
                     $viewmoreurl->param('categoryid', $coursecat->id);
                 }
                 $viewmoretext = $chelper->get_categories_display_option('viewmoretext', new lang_string('viewmore'));
                 $morelink = html_writer::tag('div', html_writer::link($viewmoreurl, $viewmoretext), array('class' => 'paging paging-morelink'));
             }
         }
     } else {
         if ($totalcount > $CFG->coursesperpage && $paginationurl && $paginationallowall) {
             // there are more than one page of results and we are in 'view all' mode, suggest to go back to paginated view mode
             $pagingbar = html_writer::tag('div', html_writer::link($paginationurl->out(false, array('perpage' => $CFG->coursesperpage)), get_string('showperpage', '', $CFG->coursesperpage)), array('class' => 'paging paging-showperpage'));
         }
     }
     // display list of subcategories
     $content = html_writer::start_tag('div', array('class' => 'subcategories'));
     if (!empty($pagingbar)) {
         $content .= $pagingbar;
     }
     foreach ($subcategories as $subcategory) {
         $content .= $this->coursecat_category($chelper, $subcategory, $depth + 1);
     }
     if (!empty($pagingbar)) {
         $content .= $pagingbar;
     }
     if (!empty($morelink)) {
         $content .= $morelink;
     }
     $content .= html_writer::end_tag('div');
     return $content;
 }
 /**
  * Loads all categories (top level or if an id is specified for that category)
  *
  * @param int $categoryid The category id to load or null/0 to load all base level categories
  * @param bool $showbasecategories If set to true all base level categories will be loaded as well
  *        as the requested category and any parent categories.
  * @return navigation_node|void returns a navigation node if a category has been loaded.
  */
 protected function load_all_categories($categoryid = self::LOAD_ROOT_CATEGORIES, $showbasecategories = false)
 {
     global $CFG, $DB;
     // Check if this category has already been loaded
     if ($this->allcategoriesloaded || $categoryid < 1 && $this->is_category_fully_loaded($categoryid)) {
         return true;
     }
     $catcontextsql = context_helper::get_preload_record_columns_sql('ctx');
     $sqlselect = "SELECT cc.*, {$catcontextsql}\n                      FROM {course_categories} cc\n                      JOIN {context} ctx ON cc.id = ctx.instanceid";
     $sqlwhere = "WHERE ctx.contextlevel = " . CONTEXT_COURSECAT;
     $sqlorder = "ORDER BY cc.depth ASC, cc.sortorder ASC, cc.id ASC";
     $params = array();
     $categoriestoload = array();
     if ($categoryid == self::LOAD_ALL_CATEGORIES) {
         // We are going to load all categories regardless... prepare to fire
         // on the database server!
     } else {
         if ($categoryid == self::LOAD_ROOT_CATEGORIES) {
             // can be 0
             // We are going to load all of the first level categories (categories without parents)
             $sqlwhere .= " AND cc.parent = 0";
         } else {
             if (array_key_exists($categoryid, $this->addedcategories)) {
                 // The category itself has been loaded already so we just need to ensure its subcategories
                 // have been loaded
                 list($sql, $params) = $DB->get_in_or_equal(array_keys($this->addedcategories), SQL_PARAMS_NAMED, 'parent', false);
                 if ($showbasecategories) {
                     // We need to include categories with parent = 0 as well
                     $sqlwhere .= " AND (cc.parent = :categoryid OR cc.parent = 0) AND cc.parent {$sql}";
                 } else {
                     // All we need is categories that match the parent
                     $sqlwhere .= " AND cc.parent = :categoryid AND cc.parent {$sql}";
                 }
                 $params['categoryid'] = $categoryid;
             } else {
                 // This category hasn't been loaded yet so we need to fetch it, work out its category path
                 // and load this category plus all its parents and subcategories
                 $category = $DB->get_record('course_categories', array('id' => $categoryid), 'path', MUST_EXIST);
                 $categoriestoload = explode('/', trim($category->path, '/'));
                 list($select, $params) = $DB->get_in_or_equal($categoriestoload);
                 // We are going to use select twice so double the params
                 $params = array_merge($params, $params);
                 $basecategorysql = $showbasecategories ? ' OR cc.depth = 1' : '';
                 $sqlwhere .= " AND (cc.id {$select} OR cc.parent {$select}{$basecategorysql})";
             }
         }
     }
     $categoriesrs = $DB->get_recordset_sql("{$sqlselect} {$sqlwhere} {$sqlorder}", $params);
     // IOMAD - Filter out the unwanted categories
     if (!is_siteadmin()) {
         $categoriesiomad = iomad::iomad_filter_categories($categoriesrs);
     } else {
         $categoriesiomad = $categoriesrs;
     }
     $categories = array();
     foreach ($categoriesiomad as $category) {
         // Preload the context.. we'll need it when adding the category in order
         // to format the category name.
         context_helper::preload_from_record($category);
         if (array_key_exists($category->id, $this->addedcategories)) {
             // Do nothing, its already been added.
         } else {
             if ($category->parent == '0') {
                 // This is a root category lets add it immediately
                 $this->add_category($category, $this->rootnodes['courses']);
             } else {
                 if (array_key_exists($category->parent, $this->addedcategories)) {
                     // This categories parent has already been added we can add this immediately
                     $this->add_category($category, $this->addedcategories[$category->parent]);
                 } else {
                     $categories[] = $category;
                 }
             }
         }
     }
     $categoriesrs->close();
     // Now we have an array of categories we need to add them to the navigation.
     while (!empty($categories)) {
         $category = reset($categories);
         if (array_key_exists($category->id, $this->addedcategories)) {
             // Do nothing
         } else {
             if ($category->parent == '0') {
                 $this->add_category($category, $this->rootnodes['courses']);
             } else {
                 if (array_key_exists($category->parent, $this->addedcategories)) {
                     $this->add_category($category, $this->addedcategories[$category->parent]);
                 } else {
                     // This category isn't in the navigation and niether is it's parent (yet).
                     // We need to go through the category path and add all of its components in order.
                     $path = explode('/', trim($category->path, '/'));
                     foreach ($path as $catid) {
                         if (!array_key_exists($catid, $this->addedcategories)) {
                             // This category isn't in the navigation yet so add it.
                             $subcategory = $categories[$catid];
                             if ($subcategory->parent == '0') {
                                 // Yay we have a root category - this likely means we will now be able
                                 // to add categories without problems.
                                 $this->add_category($subcategory, $this->rootnodes['courses']);
                             } else {
                                 if (array_key_exists($subcategory->parent, $this->addedcategories)) {
                                     // The parent is in the category (as we'd expect) so add it now.
                                     $this->add_category($subcategory, $this->addedcategories[$subcategory->parent]);
                                     // Remove the category from the categories array.
                                     unset($categories[$catid]);
                                 } else {
                                     // We should never ever arrive here - if we have then there is a bigger
                                     // problem at hand.
                                     throw new coding_exception('Category path order is incorrect and/or there are missing categories');
                                 }
                             }
                         }
                     }
                 }
             }
         }
         // Remove the category from the categories array now that we know it has been added.
         unset($categories[$category->id]);
     }
     if ($categoryid === self::LOAD_ALL_CATEGORIES) {
         $this->allcategoriesloaded = true;
     }
     // Check if there are any categories to load.
     if (count($categoriestoload) > 0) {
         $readytoloadcourses = array();
         foreach ($categoriestoload as $category) {
             if ($this->can_add_more_courses_to_category($category)) {
                 $readytoloadcourses[] = $category;
             }
         }
         if (count($readytoloadcourses)) {
             $this->load_all_courses($readytoloadcourses);
         }
     }
     // Look for all categories which have been loaded
     if (!empty($this->addedcategories)) {
         $categoryids = array();
         foreach ($this->addedcategories as $category) {
             if ($this->can_add_more_courses_to_category($category)) {
                 $categoryids[] = $category->key;
             }
         }
         if ($categoryids) {
             list($categoriessql, $params) = $DB->get_in_or_equal($categoryids, SQL_PARAMS_NAMED);
             $params['limit'] = !empty($CFG->navcourselimit) ? $CFG->navcourselimit : 20;
             $sql = "SELECT cc.id, COUNT(c.id) AS coursecount\n                          FROM {course_categories} cc\n                          JOIN {course} c ON c.category = cc.id\n                         WHERE cc.id {$categoriessql}\n                      GROUP BY cc.id\n                        HAVING COUNT(c.id) > :limit";
             $excessivecategories = $DB->get_records_sql($sql, $params);
             foreach ($categories as &$category) {
                 if (array_key_exists($category->key, $excessivecategories) && !$this->can_add_more_courses_to_category($category)) {
                     $url = new moodle_url('/course/index.php', array('categoryid' => $category->key));
                     $category->add(get_string('viewallcourses'), $url, self::TYPE_SETTING);
                 }
             }
         }
     }
 }
 /**
  * This function returns a nice list representing category tree
  * for display or to use in a form <select> element
  *
  * List is cached for 10 minutes
  *
  * 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
  * array(1 => 'Miscellaneous',
  *       2 => 'Miscellaneous / Subcategory',
  *       4 => 'Miscellaneous / Subcategory / Sub-subcategory',
  *       3 => 'Other category');
  *
  * If you specify $requiredcapability, then only categories where the current
  * user has that capability will be added to $list.
  * 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 $excludeid, 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.
  *
  * See also {@link make_categories_options()}
  *
  * @param string/array $requiredcapability if given, only categories where the current
  *      user has this capability will be returned. Can also be an array of capabilities,
  *      in which case they are all required.
  * @param integer $excludeid Exclude this category and its children from the lists built.
  * @param string $separator string to use as a separator between parent and child category. Default ' / '
  * @return array of strings
  */
 public static function make_categories_list($requiredcapability = '', $excludeid = 0, $separator = ' / ')
 {
     global $DB;
     global $CFG;
     $coursecatcache = cache::make('core', 'coursecat');
     // Check if we cached the complete list of user-accessible category names ($baselist) or list of ids
     // with requried cap ($thislist).
     $basecachekey = 'catlist';
     $baselist = $coursecatcache->get($basecachekey);
     $thislist = false;
     $thiscachekey = null;
     if (!empty($requiredcapability)) {
         $requiredcapability = (array) $requiredcapability;
         $thiscachekey = 'catlist:' . serialize($requiredcapability);
         if ($baselist !== false && ($thislist = $coursecatcache->get($thiscachekey)) !== false) {
             $thislist = preg_split('|,|', $thislist, -1, PREG_SPLIT_NO_EMPTY);
         }
     } else {
         if ($baselist !== false) {
             $thislist = array_keys($baselist);
         }
     }
     if ($baselist === false) {
         // We don't have $baselist cached, retrieve it. Retrieve $thislist again in any case.
         $ctxselect = context_helper::get_preload_record_columns_sql('ctx');
         $sql = "SELECT cc.id, cc.sortorder, cc.name, cc.visible, cc.parent, cc.path, {$ctxselect}\n                    FROM {course_categories} cc\n                    JOIN {context} ctx ON cc.id = ctx.instanceid AND ctx.contextlevel = :contextcoursecat\n                    ORDER BY cc.sortorder";
         $rs = $DB->get_recordset_sql($sql, array('contextcoursecat' => CONTEXT_COURSECAT));
         $baselist = array();
         $thislist = array();
         foreach ($rs as $record) {
             // If the category's parent is not visible to the user, it is not visible as well.
             if (!$record->parent || isset($baselist[$record->parent])) {
                 context_helper::preload_from_record($record);
                 $context = context_coursecat::instance($record->id);
                 if (!$record->visible && !has_capability('moodle/category:viewhiddencategories', $context)) {
                     // No cap to view category, added to neither $baselist nor $thislist.
                     continue;
                 }
                 $baselist[$record->id] = array('name' => format_string($record->name, true, array('context' => $context)), 'path' => $record->path);
                 if (!empty($requiredcapability) && !has_all_capabilities($requiredcapability, $context)) {
                     // No required capability, added to $baselist but not to $thislist.
                     continue;
                 }
                 $thislist[] = $record->id;
             }
         }
         $rs->close();
         $coursecatcache->set($basecachekey, $baselist);
         if (!empty($requiredcapability)) {
             $coursecatcache->set($thiscachekey, join(',', $thislist));
         }
     } else {
         if ($thislist === false) {
             // We have $baselist cached but not $thislist. Simplier query is used to retrieve.
             $ctxselect = context_helper::get_preload_record_columns_sql('ctx');
             $sql = "SELECT ctx.instanceid AS id, {$ctxselect}\n                    FROM {context} ctx WHERE ctx.contextlevel = :contextcoursecat";
             $contexts = $DB->get_records_sql($sql, array('contextcoursecat' => CONTEXT_COURSECAT));
             $thislist = array();
             foreach (array_keys($baselist) as $id) {
                 context_helper::preload_from_record($contexts[$id]);
                 if (has_all_capabilities($requiredcapability, context_coursecat::instance($id))) {
                     $thislist[] = $id;
                 }
             }
             $coursecatcache->set($thiscachekey, join(',', $thislist));
         }
     }
     // Now build the array of strings to return, mind $separator and $excludeid.
     $names = array();
     foreach ($thislist as $id) {
         $path = preg_split('|/|', $baselist[$id]['path'], -1, PREG_SPLIT_NO_EMPTY);
         if (!$excludeid || !in_array($excludeid, $path)) {
             $namechunks = array();
             foreach ($path as $parentid) {
                 $namechunks[] = $baselist[$parentid]['name'];
             }
             $names[$id] = join($separator, $namechunks);
         }
     }
     // IOMAD :  Filter the list of categories.
     if (!is_siteadmin() and !during_initial_install()) {
         $names = iomad::iomad_filter_categories($names);
     }
     return $names;
 }