[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/course/ -> renderer.php (source)

   1  <?php
   2  
   3  // This file is part of Moodle - http://moodle.org/
   4  //
   5  // Moodle is free software: you can redistribute it and/or modify
   6  // it under the terms of the GNU General Public License as published by
   7  // the Free Software Foundation, either version 3 of the License, or
   8  // (at your option) any later version.
   9  //
  10  // Moodle is distributed in the hope that it will be useful,
  11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13  // GNU General Public License for more details.
  14  //
  15  // You should have received a copy of the GNU General Public License
  16  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  17  
  18  /**
  19   * Renderer for use with the course section and all the goodness that falls
  20   * within it.
  21   *
  22   * This renderer should contain methods useful to courses, and categories.
  23   *
  24   * @package   moodlecore
  25   * @copyright 2010 Sam Hemelryk
  26   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  27   */
  28  
  29  /**
  30   * The core course renderer
  31   *
  32   * Can be retrieved with the following:
  33   * $renderer = $PAGE->get_renderer('core','course');
  34   */
  35  class core_course_renderer extends plugin_renderer_base {
  36      const COURSECAT_SHOW_COURSES_NONE = 0; /* do not show courses at all */
  37      const COURSECAT_SHOW_COURSES_COUNT = 5; /* do not show courses but show number of courses next to category name */
  38      const COURSECAT_SHOW_COURSES_COLLAPSED = 10;
  39      const COURSECAT_SHOW_COURSES_AUTO = 15; /* will choose between collapsed and expanded automatically */
  40      const COURSECAT_SHOW_COURSES_EXPANDED = 20;
  41      const COURSECAT_SHOW_COURSES_EXPANDED_WITH_CAT = 30;
  42  
  43      const COURSECAT_TYPE_CATEGORY = 0;
  44      const COURSECAT_TYPE_COURSE = 1;
  45  
  46      /**
  47       * A cache of strings
  48       * @var stdClass
  49       */
  50      protected $strings;
  51  
  52      /**
  53       * Override the constructor so that we can initialise the string cache
  54       *
  55       * @param moodle_page $page
  56       * @param string $target
  57       */
  58      public function __construct(moodle_page $page, $target) {
  59          $this->strings = new stdClass;
  60          parent::__construct($page, $target);
  61      }
  62  
  63      /**
  64       * Adds the item in course settings navigation to toggle modchooser
  65       *
  66       * Theme can overwrite as an empty function to exclude it (for example if theme does not
  67       * use modchooser at all)
  68       *
  69       * @deprecated since 3.2
  70       */
  71      protected function add_modchoosertoggle() {
  72          debugging('core_course_renderer::add_modchoosertoggle() is deprecated.', DEBUG_DEVELOPER);
  73  
  74          global $CFG;
  75  
  76          // Only needs to be done once per page.
  77          if (!$this->page->requires->should_create_one_time_item_now('core_course_modchoosertoggle')) {
  78              return;
  79          }
  80  
  81          if ($this->page->state > moodle_page::STATE_PRINTING_HEADER ||
  82                  $this->page->course->id == SITEID ||
  83                  !$this->page->user_is_editing() ||
  84                  !($context = context_course::instance($this->page->course->id)) ||
  85                  !has_capability('moodle/course:manageactivities', $context) ||
  86                  !course_ajax_enabled($this->page->course) ||
  87                  !($coursenode = $this->page->settingsnav->find('courseadmin', navigation_node::TYPE_COURSE)) ||
  88                  !($turneditingnode = $coursenode->get('turneditingonoff'))) {
  89              // Too late, or we are on site page, or we could not find the
  90              // adjacent nodes in course settings menu, or we are not allowed to edit.
  91              return;
  92          }
  93  
  94          if ($this->page->url->compare(new moodle_url('/course/view.php'), URL_MATCH_BASE)) {
  95              // We are on the course page, retain the current page params e.g. section.
  96              $modchoosertoggleurl = clone($this->page->url);
  97          } else {
  98              // Edit on the main course page.
  99              $modchoosertoggleurl = new moodle_url('/course/view.php', array('id' => $this->page->course->id,
 100                  'return' => $this->page->url->out_as_local_url(false)));
 101          }
 102          $modchoosertoggleurl->param('sesskey', sesskey());
 103          if ($usemodchooser = get_user_preferences('usemodchooser', $CFG->modchooserdefault)) {
 104              $modchoosertogglestring = get_string('modchooserdisable', 'moodle');
 105              $modchoosertoggleurl->param('modchooser', 'off');
 106          } else {
 107              $modchoosertogglestring = get_string('modchooserenable', 'moodle');
 108              $modchoosertoggleurl->param('modchooser', 'on');
 109          }
 110          $modchoosertoggle = navigation_node::create($modchoosertogglestring, $modchoosertoggleurl, navigation_node::TYPE_SETTING, null, 'modchoosertoggle');
 111  
 112          // Insert the modchoosertoggle after the settings node 'turneditingonoff' (navigation_node only has function to insert before, so we insert before and then swap).
 113          $coursenode->add_node($modchoosertoggle, 'turneditingonoff');
 114          $turneditingnode->remove();
 115          $coursenode->add_node($turneditingnode, 'modchoosertoggle');
 116  
 117          $modchoosertoggle->add_class('modchoosertoggle');
 118          $modchoosertoggle->add_class('visibleifjs');
 119          user_preference_allow_ajax_update('usemodchooser', PARAM_BOOL);
 120      }
 121  
 122      /**
 123       * Renders course info box.
 124       *
 125       * @param stdClass|course_in_list $course
 126       * @return string
 127       */
 128      public function course_info_box(stdClass $course) {
 129          $content = '';
 130          $content .= $this->output->box_start('generalbox info');
 131          $chelper = new coursecat_helper();
 132          $chelper->set_show_courses(self::COURSECAT_SHOW_COURSES_EXPANDED);
 133          $content .= $this->coursecat_coursebox($chelper, $course);
 134          $content .= $this->output->box_end();
 135          return $content;
 136      }
 137  
 138      /**
 139       * Renderers a structured array of courses and categories into a nice XHTML tree structure.
 140       *
 141       * @deprecated since 2.5
 142       *
 143       * Please see http://docs.moodle.org/dev/Courses_lists_upgrade_to_2.5
 144       *
 145       * @param array $ignored argument ignored
 146       * @return string
 147       */
 148      public final function course_category_tree(array $ignored) {
 149          debugging('Function core_course_renderer::course_category_tree() is deprecated, please use frontpage_combo_list()', DEBUG_DEVELOPER);
 150          return $this->frontpage_combo_list();
 151      }
 152  
 153      /**
 154       * Renderers a category for use with course_category_tree
 155       *
 156       * @deprecated since 2.5
 157       *
 158       * Please see http://docs.moodle.org/dev/Courses_lists_upgrade_to_2.5
 159       *
 160       * @param array $category
 161       * @param int $depth
 162       * @return string
 163       */
 164      protected final function course_category_tree_category(stdClass $category, $depth=1) {
 165          debugging('Function core_course_renderer::course_category_tree_category() is deprecated', DEBUG_DEVELOPER);
 166          return '';
 167      }
 168  
 169      /**
 170       * Build the HTML for the module chooser javascript popup
 171       *
 172       * @param array $modules A set of modules as returned form @see
 173       * get_module_metadata
 174       * @param object $course The course that will be displayed
 175       * @return string The composed HTML for the module
 176       */
 177      public function course_modchooser($modules, $course) {
 178          if (!$this->page->requires->should_create_one_time_item_now('core_course_modchooser')) {
 179              return '';
 180          }
 181  
 182          // Add the module chooser
 183          $this->page->requires->yui_module('moodle-course-modchooser',
 184          'M.course.init_chooser',
 185          array(array('courseid' => $course->id, 'closeButtonTitle' => get_string('close', 'editor')))
 186          );
 187          $this->page->requires->strings_for_js(array(
 188                  'addresourceoractivity'
 189          ), 'moodle');
 190  
 191          // Add the header
 192          $header = html_writer::tag('div', get_string('addresourceoractivity', 'moodle'),
 193                  array('class' => 'hd choosertitle'));
 194  
 195          $formcontent = html_writer::start_tag('form', array('action' => new moodle_url('/course/jumpto.php'),
 196                  'id' => 'chooserform', 'method' => 'post'));
 197          $formcontent .= html_writer::start_tag('div', array('id' => 'typeformdiv'));
 198          $formcontent .= html_writer::tag('input', '', array('type' => 'hidden', 'id' => 'course',
 199                  'name' => 'course', 'value' => $course->id));
 200          $formcontent .= html_writer::tag('input', '', array('type' => 'hidden', 'name' => 'sesskey',
 201                  'value' => sesskey()));
 202          $formcontent .= html_writer::end_tag('div');
 203  
 204          // Put everything into one tag 'options'
 205          $formcontent .= html_writer::start_tag('div', array('class' => 'options'));
 206          $formcontent .= html_writer::tag('div', get_string('selectmoduletoviewhelp', 'moodle'),
 207                  array('class' => 'instruction'));
 208          // Put all options into one tag 'alloptions' to allow us to handle scrolling
 209          $formcontent .= html_writer::start_tag('div', array('class' => 'alloptions'));
 210  
 211           // Activities
 212          $activities = array_filter($modules, create_function('$mod', 'return ($mod->archetype !== MOD_ARCHETYPE_RESOURCE && $mod->archetype !== MOD_ARCHETYPE_SYSTEM);'));
 213          if (count($activities)) {
 214              $formcontent .= $this->course_modchooser_title('activities');
 215              $formcontent .= $this->course_modchooser_module_types($activities);
 216          }
 217  
 218          // Resources
 219          $resources = array_filter($modules, create_function('$mod', 'return ($mod->archetype === MOD_ARCHETYPE_RESOURCE);'));
 220          if (count($resources)) {
 221              $formcontent .= $this->course_modchooser_title('resources');
 222              $formcontent .= $this->course_modchooser_module_types($resources);
 223          }
 224  
 225          $formcontent .= html_writer::end_tag('div'); // modoptions
 226          $formcontent .= html_writer::end_tag('div'); // types
 227  
 228          $formcontent .= html_writer::start_tag('div', array('class' => 'submitbuttons'));
 229          $formcontent .= html_writer::tag('input', '',
 230                  array('type' => 'submit', 'name' => 'submitbutton', 'class' => 'submitbutton', 'value' => get_string('add')));
 231          $formcontent .= html_writer::tag('input', '',
 232                  array('type' => 'submit', 'name' => 'addcancel', 'class' => 'addcancel', 'value' => get_string('cancel')));
 233          $formcontent .= html_writer::end_tag('div');
 234          $formcontent .= html_writer::end_tag('form');
 235  
 236          // Wrap the whole form in a div
 237          $formcontent = html_writer::tag('div', $formcontent, array('id' => 'chooseform'));
 238  
 239          // Put all of the content together
 240          $content = $formcontent;
 241  
 242          $content = html_writer::tag('div', $content, array('class' => 'choosercontainer'));
 243          return $header . html_writer::tag('div', $content, array('class' => 'chooserdialoguebody'));
 244      }
 245  
 246      /**
 247       * Build the HTML for a specified set of modules
 248       *
 249       * @param array $modules A set of modules as used by the
 250       * course_modchooser_module function
 251       * @return string The composed HTML for the module
 252       */
 253      protected function course_modchooser_module_types($modules) {
 254          $return = '';
 255          foreach ($modules as $module) {
 256              $return .= $this->course_modchooser_module($module);
 257          }
 258          return $return;
 259      }
 260  
 261      /**
 262       * Return the HTML for the specified module adding any required classes
 263       *
 264       * @param object $module An object containing the title, and link. An
 265       * icon, and help text may optionally be specified. If the module
 266       * contains subtypes in the types option, then these will also be
 267       * displayed.
 268       * @param array $classes Additional classes to add to the encompassing
 269       * div element
 270       * @return string The composed HTML for the module
 271       */
 272      protected function course_modchooser_module($module, $classes = array('option')) {
 273          $output = '';
 274          $output .= html_writer::start_tag('div', array('class' => implode(' ', $classes)));
 275          $output .= html_writer::start_tag('label', array('for' => 'module_' . $module->name));
 276          if (!isset($module->types)) {
 277              $output .= html_writer::tag('input', '', array('type' => 'radio',
 278                      'name' => 'jumplink', 'id' => 'module_' . $module->name, 'value' => $module->link));
 279          }
 280  
 281          $output .= html_writer::start_tag('span', array('class' => 'modicon'));
 282          if (isset($module->icon)) {
 283              // Add an icon if we have one
 284              $output .= $module->icon;
 285          }
 286          $output .= html_writer::end_tag('span');
 287  
 288          $output .= html_writer::tag('span', $module->title, array('class' => 'typename'));
 289          if (!isset($module->help)) {
 290              // Add help if found
 291              $module->help = get_string('nohelpforactivityorresource', 'moodle');
 292          }
 293  
 294          // Format the help text using markdown with the following options
 295          $options = new stdClass();
 296          $options->trusted = false;
 297          $options->noclean = false;
 298          $options->smiley = false;
 299          $options->filter = false;
 300          $options->para = true;
 301          $options->newlines = false;
 302          $options->overflowdiv = false;
 303          $module->help = format_text($module->help, FORMAT_MARKDOWN, $options);
 304          $output .= html_writer::tag('span', $module->help, array('class' => 'typesummary'));
 305          $output .= html_writer::end_tag('label');
 306          $output .= html_writer::end_tag('div');
 307  
 308          return $output;
 309      }
 310  
 311      protected function course_modchooser_title($title, $identifier = null) {
 312          $module = new stdClass();
 313          $module->name = $title;
 314          $module->types = array();
 315          $module->title = get_string($title, $identifier);
 316          $module->help = '';
 317          return $this->course_modchooser_module($module, array('moduletypetitle'));
 318      }
 319  
 320      /**
 321       * Renders HTML for displaying the sequence of course module editing buttons
 322       *
 323       * @see course_get_cm_edit_actions()
 324       *
 325       * @param action_link[] $actions Array of action_link objects
 326       * @param cm_info $mod The module we are displaying actions for.
 327       * @param array $displayoptions additional display options:
 328       *     ownerselector => A JS/CSS selector that can be used to find an cm node.
 329       *         If specified the owning node will be given the class 'action-menu-shown' when the action
 330       *         menu is being displayed.
 331       *     constraintselector => A JS/CSS selector that can be used to find the parent node for which to constrain
 332       *         the action menu to when it is being displayed.
 333       *     donotenhance => If set to true the action menu that gets displayed won't be enhanced by JS.
 334       * @return string
 335       */
 336      public function course_section_cm_edit_actions($actions, cm_info $mod = null, $displayoptions = array()) {
 337          global $CFG;
 338  
 339          if (empty($actions)) {
 340              return '';
 341          }
 342  
 343          if (isset($displayoptions['ownerselector'])) {
 344              $ownerselector = $displayoptions['ownerselector'];
 345          } else if ($mod) {
 346              $ownerselector = '#module-'.$mod->id;
 347          } else {
 348              debugging('You should upgrade your call to '.__FUNCTION__.' and provide $mod', DEBUG_DEVELOPER);
 349              $ownerselector = 'li.activity';
 350          }
 351  
 352          if (isset($displayoptions['constraintselector'])) {
 353              $constraint = $displayoptions['constraintselector'];
 354          } else {
 355              $constraint = '.course-content';
 356          }
 357  
 358          $menu = new action_menu();
 359          $menu->set_owner_selector($ownerselector);
 360          $menu->set_constraint($constraint);
 361          $menu->set_alignment(action_menu::TR, action_menu::BR);
 362          $menu->set_menu_trigger(get_string('edit'));
 363          if (isset($CFG->modeditingmenu) && !$CFG->modeditingmenu || !empty($displayoptions['donotenhance'])) {
 364              $menu->do_not_enhance();
 365  
 366              // Swap the left/right icons.
 367              // Normally we have have right, then left but this does not
 368              // make sense when modactionmenu is disabled.
 369              $moveright = null;
 370              $_actions = array();
 371              foreach ($actions as $key => $value) {
 372                  if ($key === 'moveright') {
 373  
 374                      // Save moveright for later.
 375                      $moveright = $value;
 376                  } else if ($moveright) {
 377  
 378                      // This assumes that the order was moveright, moveleft.
 379                      // If we have a moveright, then we should place it immediately after the current value.
 380                      $_actions[$key] = $value;
 381                      $_actions['moveright'] = $moveright;
 382  
 383                      // Clear the value to prevent it being used multiple times.
 384                      $moveright = null;
 385                  } else {
 386  
 387                      $_actions[$key] = $value;
 388                  }
 389              }
 390              $actions = $_actions;
 391              unset($_actions);
 392          }
 393          foreach ($actions as $action) {
 394              if ($action instanceof action_menu_link) {
 395                  $action->add_class('cm-edit-action');
 396              }
 397              $menu->add($action);
 398          }
 399          $menu->attributes['class'] .= ' section-cm-edit-actions commands';
 400  
 401          // Prioritise the menu ahead of all other actions.
 402          $menu->prioritise = true;
 403  
 404          return $this->render($menu);
 405      }
 406  
 407      /**
 408       * Renders HTML for the menus to add activities and resources to the current course
 409       *
 410       * @param stdClass $course
 411       * @param int $section relative section number (field course_sections.section)
 412       * @param int $sectionreturn The section to link back to
 413       * @param array $displayoptions additional display options, for example blocks add
 414       *     option 'inblock' => true, suggesting to display controls vertically
 415       * @return string
 416       */
 417      function course_section_add_cm_control($course, $section, $sectionreturn = null, $displayoptions = array()) {
 418          global $CFG;
 419  
 420          $vertical = !empty($displayoptions['inblock']);
 421  
 422          // check to see if user can add menus and there are modules to add
 423          if (!has_capability('moodle/course:manageactivities', context_course::instance($course->id))
 424                  || !$this->page->user_is_editing()
 425                  || !($modnames = get_module_types_names()) || empty($modnames)) {
 426              return '';
 427          }
 428  
 429          // Retrieve all modules with associated metadata
 430          $modules = get_module_metadata($course, $modnames, $sectionreturn);
 431          $urlparams = array('section' => $section);
 432  
 433          // We'll sort resources and activities into two lists
 434          $activities = array(MOD_CLASS_ACTIVITY => array(), MOD_CLASS_RESOURCE => array());
 435  
 436          foreach ($modules as $module) {
 437              $activityclass = MOD_CLASS_ACTIVITY;
 438              if ($module->archetype == MOD_ARCHETYPE_RESOURCE) {
 439                  $activityclass = MOD_CLASS_RESOURCE;
 440              } else if ($module->archetype === MOD_ARCHETYPE_SYSTEM) {
 441                  // System modules cannot be added by user, do not add to dropdown.
 442                  continue;
 443              }
 444              $link = $module->link->out(true, $urlparams);
 445              $activities[$activityclass][$link] = $module->title;
 446          }
 447  
 448          $straddactivity = get_string('addactivity');
 449          $straddresource = get_string('addresource');
 450          $sectionname = get_section_name($course, $section);
 451          $strresourcelabel = get_string('addresourcetosection', null, $sectionname);
 452          $stractivitylabel = get_string('addactivitytosection', null, $sectionname);
 453  
 454          $output = html_writer::start_tag('div', array('class' => 'section_add_menus', 'id' => 'add_menus-section-' . $section));
 455  
 456          if (!$vertical) {
 457              $output .= html_writer::start_tag('div', array('class' => 'horizontal'));
 458          }
 459  
 460          if (!empty($activities[MOD_CLASS_RESOURCE])) {
 461              $select = new url_select($activities[MOD_CLASS_RESOURCE], '', array(''=>$straddresource), "ressection$section");
 462              $select->set_help_icon('resources');
 463              $select->set_label($strresourcelabel, array('class' => 'accesshide'));
 464              $output .= $this->output->render($select);
 465          }
 466  
 467          if (!empty($activities[MOD_CLASS_ACTIVITY])) {
 468              $select = new url_select($activities[MOD_CLASS_ACTIVITY], '', array(''=>$straddactivity), "section$section");
 469              $select->set_help_icon('activities');
 470              $select->set_label($stractivitylabel, array('class' => 'accesshide'));
 471              $output .= $this->output->render($select);
 472          }
 473  
 474          if (!$vertical) {
 475              $output .= html_writer::end_tag('div');
 476          }
 477  
 478          $output .= html_writer::end_tag('div');
 479  
 480          if (course_ajax_enabled($course) && $course->id == $this->page->course->id) {
 481              // modchooser can be added only for the current course set on the page!
 482              $straddeither = get_string('addresourceoractivity');
 483              // The module chooser link
 484              $modchooser = html_writer::start_tag('div', array('class' => 'mdl-right'));
 485              $modchooser.= html_writer::start_tag('div', array('class' => 'section-modchooser'));
 486              $icon = $this->output->pix_icon('t/add', '');
 487              $span = html_writer::tag('span', $straddeither, array('class' => 'section-modchooser-text'));
 488              $modchooser .= html_writer::tag('span', $icon . $span, array('class' => 'section-modchooser-link'));
 489              $modchooser.= html_writer::end_tag('div');
 490              $modchooser.= html_writer::end_tag('div');
 491  
 492              // Wrap the normal output in a noscript div
 493              $usemodchooser = get_user_preferences('usemodchooser', $CFG->modchooserdefault);
 494              if ($usemodchooser) {
 495                  $output = html_writer::tag('div', $output, array('class' => 'hiddenifjs addresourcedropdown'));
 496                  $modchooser = html_writer::tag('div', $modchooser, array('class' => 'visibleifjs addresourcemodchooser'));
 497              } else {
 498                  // If the module chooser is disabled, we need to ensure that the dropdowns are shown even if javascript is disabled
 499                  $output = html_writer::tag('div', $output, array('class' => 'show addresourcedropdown'));
 500                  $modchooser = html_writer::tag('div', $modchooser, array('class' => 'hide addresourcemodchooser'));
 501              }
 502              $output = $this->course_modchooser($modules, $course) . $modchooser . $output;
 503          }
 504  
 505          return $output;
 506      }
 507  
 508      /**
 509       * Renders html to display a course search form
 510       *
 511       * @param string $value default value to populate the search field
 512       * @param string $format display format - 'plain' (default), 'short' or 'navbar'
 513       * @return string
 514       */
 515      function course_search_form($value = '', $format = 'plain') {
 516          static $count = 0;
 517          $formid = 'coursesearch';
 518          if ((++$count) > 1) {
 519              $formid .= $count;
 520          }
 521  
 522          switch ($format) {
 523              case 'navbar' :
 524                  $formid = 'coursesearchnavbar';
 525                  $inputid = 'navsearchbox';
 526                  $inputsize = 20;
 527                  break;
 528              case 'short' :
 529                  $inputid = 'shortsearchbox';
 530                  $inputsize = 12;
 531                  break;
 532              default :
 533                  $inputid = 'coursesearchbox';
 534                  $inputsize = 30;
 535          }
 536  
 537          $strsearchcourses= get_string("searchcourses");
 538          $searchurl = new moodle_url('/course/search.php');
 539  
 540          $output = html_writer::start_tag('form', array('id' => $formid, 'action' => $searchurl, 'method' => 'get'));
 541          $output .= html_writer::start_tag('fieldset', array('class' => 'coursesearchbox invisiblefieldset'));
 542          $output .= html_writer::tag('label', $strsearchcourses.': ', array('for' => $inputid));
 543          $output .= html_writer::empty_tag('input', array('type' => 'text', 'id' => $inputid,
 544              'size' => $inputsize, 'name' => 'search', 'value' => s($value)));
 545          $output .= html_writer::empty_tag('input', array('type' => 'submit',
 546              'value' => get_string('go')));
 547          $output .= html_writer::end_tag('fieldset');
 548          $output .= html_writer::end_tag('form');
 549  
 550          return $output;
 551      }
 552  
 553      /**
 554       * Renders html for completion box on course page
 555       *
 556       * If completion is disabled, returns empty string
 557       * If completion is automatic, returns an icon of the current completion state
 558       * If completion is manual, returns a form (with an icon inside) that allows user to
 559       * toggle completion
 560       *
 561       * @param stdClass $course course object
 562       * @param completion_info $completioninfo completion info for the course, it is recommended
 563       *     to fetch once for all modules in course/section for performance
 564       * @param cm_info $mod module to show completion for
 565       * @param array $displayoptions display options, not used in core
 566       * @return string
 567       */
 568      public function course_section_cm_completion($course, &$completioninfo, cm_info $mod, $displayoptions = array()) {
 569          global $CFG;
 570          $output = '';
 571          if (!empty($displayoptions['hidecompletion']) || !isloggedin() || isguestuser() || !$mod->uservisible) {
 572              return $output;
 573          }
 574          if ($completioninfo === null) {
 575              $completioninfo = new completion_info($course);
 576          }
 577          $completion = $completioninfo->is_enabled($mod);
 578          if ($completion == COMPLETION_TRACKING_NONE) {
 579              if ($this->page->user_is_editing()) {
 580                  $output .= html_writer::span('&nbsp;', 'filler');
 581              }
 582              return $output;
 583          }
 584  
 585          $completiondata = $completioninfo->get_data($mod, true);
 586          $completionicon = '';
 587  
 588          if ($this->page->user_is_editing()) {
 589              switch ($completion) {
 590                  case COMPLETION_TRACKING_MANUAL :
 591                      $completionicon = 'manual-enabled'; break;
 592                  case COMPLETION_TRACKING_AUTOMATIC :
 593                      $completionicon = 'auto-enabled'; break;
 594              }
 595          } else if ($completion == COMPLETION_TRACKING_MANUAL) {
 596              switch($completiondata->completionstate) {
 597                  case COMPLETION_INCOMPLETE:
 598                      $completionicon = 'manual-n'; break;
 599                  case COMPLETION_COMPLETE:
 600                      $completionicon = 'manual-y'; break;
 601              }
 602          } else { // Automatic
 603              switch($completiondata->completionstate) {
 604                  case COMPLETION_INCOMPLETE:
 605                      $completionicon = 'auto-n'; break;
 606                  case COMPLETION_COMPLETE:
 607                      $completionicon = 'auto-y'; break;
 608                  case COMPLETION_COMPLETE_PASS:
 609                      $completionicon = 'auto-pass'; break;
 610                  case COMPLETION_COMPLETE_FAIL:
 611                      $completionicon = 'auto-fail'; break;
 612              }
 613          }
 614          if ($completionicon) {
 615              $formattedname = $mod->get_formatted_name();
 616              $imgalt = get_string('completion-alt-' . $completionicon, 'completion', $formattedname);
 617  
 618              if ($this->page->user_is_editing()) {
 619                  // When editing, the icon is just an image.
 620                  $completionpixicon = new pix_icon('i/completion-'.$completionicon, $imgalt, '',
 621                          array('title' => $imgalt, 'class' => 'iconsmall'));
 622                  $output .= html_writer::tag('span', $this->output->render($completionpixicon),
 623                          array('class' => 'autocompletion'));
 624              } else if ($completion == COMPLETION_TRACKING_MANUAL) {
 625                  $imgtitle = get_string('completion-title-' . $completionicon, 'completion', $formattedname);
 626                  $newstate =
 627                      $completiondata->completionstate == COMPLETION_COMPLETE
 628                      ? COMPLETION_INCOMPLETE
 629                      : COMPLETION_COMPLETE;
 630                  // In manual mode the icon is a toggle form...
 631  
 632                  // If this completion state is used by the
 633                  // conditional activities system, we need to turn
 634                  // off the JS.
 635                  $extraclass = '';
 636                  if (!empty($CFG->enableavailability) &&
 637                          core_availability\info::completion_value_used($course, $mod->id)) {
 638                      $extraclass = ' preventjs';
 639                  }
 640                  $output .= html_writer::start_tag('form', array('method' => 'post',
 641                      'action' => new moodle_url('/course/togglecompletion.php'),
 642                      'class' => 'togglecompletion'. $extraclass));
 643                  $output .= html_writer::start_tag('div');
 644                  $output .= html_writer::empty_tag('input', array(
 645                      'type' => 'hidden', 'name' => 'id', 'value' => $mod->id));
 646                  $output .= html_writer::empty_tag('input', array(
 647                      'type' => 'hidden', 'name' => 'sesskey', 'value' => sesskey()));
 648                  $output .= html_writer::empty_tag('input', array(
 649                      'type' => 'hidden', 'name' => 'modulename', 'value' => $mod->name));
 650                  $output .= html_writer::empty_tag('input', array(
 651                      'type' => 'hidden', 'name' => 'completionstate', 'value' => $newstate));
 652                  $output .= html_writer::empty_tag('input', array(
 653                      'type' => 'image',
 654                      'src' => $this->output->pix_url('i/completion-'.$completionicon),
 655                      'alt' => $imgalt, 'title' => $imgtitle,
 656                      'aria-live' => 'polite'));
 657                  $output .= html_writer::end_tag('div');
 658                  $output .= html_writer::end_tag('form');
 659              } else {
 660                  // In auto mode, the icon is just an image.
 661                  $completionpixicon = new pix_icon('i/completion-'.$completionicon, $imgalt, '',
 662                          array('title' => $imgalt));
 663                  $output .= html_writer::tag('span', $this->output->render($completionpixicon),
 664                          array('class' => 'autocompletion'));
 665              }
 666          }
 667          return $output;
 668      }
 669  
 670      /**
 671       * Checks if course module has any conditions that may make it unavailable for
 672       * all or some of the students
 673       *
 674       * This function is internal and is only used to create CSS classes for the module name/text
 675       *
 676       * @param cm_info $mod
 677       * @return bool
 678       */
 679      protected function is_cm_conditionally_hidden(cm_info $mod) {
 680          global $CFG;
 681          $conditionalhidden = false;
 682          if (!empty($CFG->enableavailability)) {
 683              $info = new \core_availability\info_module($mod);
 684              $conditionalhidden = !$info->is_available_for_all();
 685          }
 686          return $conditionalhidden;
 687      }
 688  
 689      /**
 690       * Renders html to display a name with the link to the course module on a course page
 691       *
 692       * If module is unavailable for user but still needs to be displayed
 693       * in the list, just the name is returned without a link
 694       *
 695       * Note, that for course modules that never have separate pages (i.e. labels)
 696       * this function return an empty string
 697       *
 698       * @param cm_info $mod
 699       * @param array $displayoptions
 700       * @return string
 701       */
 702      public function course_section_cm_name(cm_info $mod, $displayoptions = array()) {
 703          if ((!$mod->uservisible && empty($mod->availableinfo)) || !$mod->url) {
 704              // Nothing to be displayed to the user.
 705              return '';
 706          }
 707  
 708          // Render element that allows to edit activity name inline. It calls {@link course_section_cm_name_title()}
 709          // to get the display title of the activity.
 710          $tmpl = new \core_course\output\course_module_name($mod, $this->page->user_is_editing(), $displayoptions);
 711          return $this->output->render_from_template('core/inplace_editable', $tmpl->export_for_template($this->output));
 712      }
 713  
 714      /**
 715       * Renders html to display a name with the link to the course module on a course page
 716       *
 717       * If module is unavailable for user but still needs to be displayed
 718       * in the list, just the name is returned without a link
 719       *
 720       * Note, that for course modules that never have separate pages (i.e. labels)
 721       * this function return an empty string
 722       *
 723       * @param cm_info $mod
 724       * @param array $displayoptions
 725       * @return string
 726       */
 727      public function course_section_cm_name_title(cm_info $mod, $displayoptions = array()) {
 728          $output = '';
 729          if (!$mod->uservisible && empty($mod->availableinfo)) {
 730              // Nothing to be displayed to the user.
 731              return $output;
 732          }
 733          $url = $mod->url;
 734          if (!$url) {
 735              return $output;
 736          }
 737  
 738          //Accessibility: for files get description via icon, this is very ugly hack!
 739          $instancename = $mod->get_formatted_name();
 740          $altname = $mod->modfullname;
 741          // Avoid unnecessary duplication: if e.g. a forum name already
 742          // includes the word forum (or Forum, etc) then it is unhelpful
 743          // to include that in the accessible description that is added.
 744          if (false !== strpos(core_text::strtolower($instancename),
 745                  core_text::strtolower($altname))) {
 746              $altname = '';
 747          }
 748          // File type after name, for alphabetic lists (screen reader).
 749          if ($altname) {
 750              $altname = get_accesshide(' '.$altname);
 751          }
 752  
 753          // For items which are hidden but available to current user
 754          // ($mod->uservisible), we show those as dimmed only if the user has
 755          // viewhiddenactivities, so that teachers see 'items which might not
 756          // be available to some students' dimmed but students do not see 'item
 757          // which is actually available to current student' dimmed.
 758          $linkclasses = '';
 759          $accesstext = '';
 760          $textclasses = '';
 761          if ($mod->uservisible) {
 762              $conditionalhidden = $this->is_cm_conditionally_hidden($mod);
 763              $accessiblebutdim = (!$mod->visible || $conditionalhidden) &&
 764                  has_capability('moodle/course:viewhiddenactivities', $mod->context);
 765              if ($accessiblebutdim) {
 766                  $linkclasses .= ' dimmed';
 767                  $textclasses .= ' dimmed_text';
 768                  if ($conditionalhidden) {
 769                      $linkclasses .= ' conditionalhidden';
 770                      $textclasses .= ' conditionalhidden';
 771                  }
 772                  // Show accessibility note only if user can access the module himself.
 773                  $accesstext = get_accesshide(get_string('hiddenfromstudents').':'. $mod->modfullname);
 774              }
 775          } else {
 776              $linkclasses .= ' dimmed';
 777              $textclasses .= ' dimmed_text';
 778          }
 779  
 780          // Get on-click attribute value if specified and decode the onclick - it
 781          // has already been encoded for display (puke).
 782          $onclick = htmlspecialchars_decode($mod->onclick, ENT_QUOTES);
 783  
 784          $groupinglabel = $mod->get_grouping_label($textclasses);
 785  
 786          // Display link itself.
 787          $activitylink = html_writer::empty_tag('img', array('src' => $mod->get_icon_url(),
 788                  'class' => 'iconlarge activityicon', 'alt' => ' ', 'role' => 'presentation')) . $accesstext .
 789                  html_writer::tag('span', $instancename . $altname, array('class' => 'instancename'));
 790          if ($mod->uservisible) {
 791              $output .= html_writer::link($url, $activitylink, array('class' => $linkclasses, 'onclick' => $onclick)) .
 792                      $groupinglabel;
 793          } else {
 794              // We may be displaying this just in order to show information
 795              // about visibility, without the actual link ($mod->uservisible)
 796              $output .= html_writer::tag('div', $activitylink, array('class' => $textclasses)) .
 797                      $groupinglabel;
 798          }
 799          return $output;
 800      }
 801  
 802      /**
 803       * Renders html to display the module content on the course page (i.e. text of the labels)
 804       *
 805       * @param cm_info $mod
 806       * @param array $displayoptions
 807       * @return string
 808       */
 809      public function course_section_cm_text(cm_info $mod, $displayoptions = array()) {
 810          $output = '';
 811          if (!$mod->uservisible && empty($mod->availableinfo)) {
 812              // nothing to be displayed to the user
 813              return $output;
 814          }
 815          $content = $mod->get_formatted_content(array('overflowdiv' => true, 'noclean' => true));
 816          $accesstext = '';
 817          $textclasses = '';
 818          if ($mod->uservisible) {
 819              $conditionalhidden = $this->is_cm_conditionally_hidden($mod);
 820              $accessiblebutdim = (!$mod->visible || $conditionalhidden) &&
 821                  has_capability('moodle/course:viewhiddenactivities', $mod->context);
 822              if ($accessiblebutdim) {
 823                  $textclasses .= ' dimmed_text';
 824                  if ($conditionalhidden) {
 825                      $textclasses .= ' conditionalhidden';
 826                  }
 827                  // Show accessibility note only if user can access the module himself.
 828                  $accesstext = get_accesshide(get_string('hiddenfromstudents').':'. $mod->modfullname);
 829              }
 830          } else {
 831              $textclasses .= ' dimmed_text';
 832          }
 833          if ($mod->url) {
 834              if ($content) {
 835                  // If specified, display extra content after link.
 836                  $output = html_writer::tag('div', $content, array('class' =>
 837                          trim('contentafterlink ' . $textclasses)));
 838              }
 839          } else {
 840              $groupinglabel = $mod->get_grouping_label($textclasses);
 841  
 842              // No link, so display only content.
 843              $output = html_writer::tag('div', $accesstext . $content . $groupinglabel,
 844                      array('class' => 'contentwithoutlink ' . $textclasses));
 845          }
 846          return $output;
 847      }
 848  
 849      /**
 850       * Renders HTML to show course module availability information (for someone who isn't allowed
 851       * to see the activity itself, or for staff)
 852       *
 853       * @param cm_info $mod
 854       * @param array $displayoptions
 855       * @return string
 856       */
 857      public function course_section_cm_availability(cm_info $mod, $displayoptions = array()) {
 858          global $CFG;
 859          if (!$mod->uservisible) {
 860              // this is a student who is not allowed to see the module but might be allowed
 861              // to see availability info (i.e. "Available from ...")
 862              if (!empty($mod->availableinfo)) {
 863                  $formattedinfo = \core_availability\info::format_info(
 864                          $mod->availableinfo, $mod->get_course());
 865                  $output = html_writer::tag('div', $formattedinfo, array('class' => 'availabilityinfo'));
 866              }
 867              return $output;
 868          }
 869          // this is a teacher who is allowed to see module but still should see the
 870          // information that module is not available to all/some students
 871          $modcontext = context_module::instance($mod->id);
 872          $canviewhidden = has_capability('moodle/course:viewhiddenactivities', $modcontext);
 873          if ($canviewhidden && !empty($CFG->enableavailability)) {
 874              // Don't add availability information if user is not editing and activity is hidden.
 875              if ($mod->visible || $this->page->user_is_editing()) {
 876                  $hidinfoclass = '';
 877                  if (!$mod->visible) {
 878                      $hidinfoclass = 'hide';
 879                  }
 880                  $ci = new \core_availability\info_module($mod);
 881                  $fullinfo = $ci->get_full_information();
 882                  if ($fullinfo) {
 883                      $formattedinfo = \core_availability\info::format_info(
 884                              $fullinfo, $mod->get_course());
 885                      return html_writer::div($formattedinfo, 'availabilityinfo ' . $hidinfoclass);
 886                  }
 887              }
 888          }
 889          return '';
 890      }
 891  
 892      /**
 893       * Renders HTML to display one course module for display within a section.
 894       *
 895       * This function calls:
 896       * {@link core_course_renderer::course_section_cm()}
 897       *
 898       * @param stdClass $course
 899       * @param completion_info $completioninfo
 900       * @param cm_info $mod
 901       * @param int|null $sectionreturn
 902       * @param array $displayoptions
 903       * @return String
 904       */
 905      public function course_section_cm_list_item($course, &$completioninfo, cm_info $mod, $sectionreturn, $displayoptions = array()) {
 906          $output = '';
 907          if ($modulehtml = $this->course_section_cm($course, $completioninfo, $mod, $sectionreturn, $displayoptions)) {
 908              $modclasses = 'activity ' . $mod->modname . ' modtype_' . $mod->modname . ' ' . $mod->extraclasses;
 909              $output .= html_writer::tag('li', $modulehtml, array('class' => $modclasses, 'id' => 'module-' . $mod->id));
 910          }
 911          return $output;
 912      }
 913  
 914      /**
 915       * Renders HTML to display one course module in a course section
 916       *
 917       * This includes link, content, availability, completion info and additional information
 918       * that module type wants to display (i.e. number of unread forum posts)
 919       *
 920       * This function calls:
 921       * {@link core_course_renderer::course_section_cm_name()}
 922       * {@link core_course_renderer::course_section_cm_text()}
 923       * {@link core_course_renderer::course_section_cm_availability()}
 924       * {@link core_course_renderer::course_section_cm_completion()}
 925       * {@link course_get_cm_edit_actions()}
 926       * {@link core_course_renderer::course_section_cm_edit_actions()}
 927       *
 928       * @param stdClass $course
 929       * @param completion_info $completioninfo
 930       * @param cm_info $mod
 931       * @param int|null $sectionreturn
 932       * @param array $displayoptions
 933       * @return string
 934       */
 935      public function course_section_cm($course, &$completioninfo, cm_info $mod, $sectionreturn, $displayoptions = array()) {
 936          $output = '';
 937          // We return empty string (because course module will not be displayed at all)
 938          // if:
 939          // 1) The activity is not visible to users
 940          // and
 941          // 2) The 'availableinfo' is empty, i.e. the activity was
 942          //     hidden in a way that leaves no info, such as using the
 943          //     eye icon.
 944          if (!$mod->uservisible && empty($mod->availableinfo)) {
 945              return $output;
 946          }
 947  
 948          $indentclasses = 'mod-indent';
 949          if (!empty($mod->indent)) {
 950              $indentclasses .= ' mod-indent-'.$mod->indent;
 951              if ($mod->indent > 15) {
 952                  $indentclasses .= ' mod-indent-huge';
 953              }
 954          }
 955  
 956          $output .= html_writer::start_tag('div');
 957  
 958          if ($this->page->user_is_editing()) {
 959              $output .= course_get_cm_move($mod, $sectionreturn);
 960          }
 961  
 962          $output .= html_writer::start_tag('div', array('class' => 'mod-indent-outer'));
 963  
 964          // This div is used to indent the content.
 965          $output .= html_writer::div('', $indentclasses);
 966  
 967          // Start a wrapper for the actual content to keep the indentation consistent
 968          $output .= html_writer::start_tag('div');
 969  
 970          // Display the link to the module (or do nothing if module has no url)
 971          $cmname = $this->course_section_cm_name($mod, $displayoptions);
 972  
 973          if (!empty($cmname)) {
 974              // Start the div for the activity title, excluding the edit icons.
 975              $output .= html_writer::start_tag('div', array('class' => 'activityinstance'));
 976              $output .= $cmname;
 977  
 978  
 979              // Module can put text after the link (e.g. forum unread)
 980              $output .= $mod->afterlink;
 981  
 982              // Closing the tag which contains everything but edit icons. Content part of the module should not be part of this.
 983              $output .= html_writer::end_tag('div'); // .activityinstance
 984          }
 985  
 986          // If there is content but NO link (eg label), then display the
 987          // content here (BEFORE any icons). In this case cons must be
 988          // displayed after the content so that it makes more sense visually
 989          // and for accessibility reasons, e.g. if you have a one-line label
 990          // it should work similarly (at least in terms of ordering) to an
 991          // activity.
 992          $contentpart = $this->course_section_cm_text($mod, $displayoptions);
 993          $url = $mod->url;
 994          if (empty($url)) {
 995              $output .= $contentpart;
 996          }
 997  
 998          $modicons = '';
 999          if ($this->page->user_is_editing()) {
1000              $editactions = course_get_cm_edit_actions($mod, $mod->indent, $sectionreturn);
1001              $modicons .= ' '. $this->course_section_cm_edit_actions($editactions, $mod, $displayoptions);
1002              $modicons .= $mod->afterediticons;
1003          }
1004  
1005          $modicons .= $this->course_section_cm_completion($course, $completioninfo, $mod, $displayoptions);
1006  
1007          if (!empty($modicons)) {
1008              $output .= html_writer::span($modicons, 'actions');
1009          }
1010  
1011          // If there is content AND a link, then display the content here
1012          // (AFTER any icons). Otherwise it was displayed before
1013          if (!empty($url)) {
1014              $output .= $contentpart;
1015          }
1016  
1017          // show availability info (if module is not available)
1018          $output .= $this->course_section_cm_availability($mod, $displayoptions);
1019  
1020          $output .= html_writer::end_tag('div'); // $indentclasses
1021  
1022          // End of indentation div.
1023          $output .= html_writer::end_tag('div');
1024  
1025          $output .= html_writer::end_tag('div');
1026          return $output;
1027      }
1028  
1029      /**
1030       * Renders HTML to display a list of course modules in a course section
1031       * Also displays "move here" controls in Javascript-disabled mode
1032       *
1033       * This function calls {@link core_course_renderer::course_section_cm()}
1034       *
1035       * @param stdClass $course course object
1036       * @param int|stdClass|section_info $section relative section number or section object
1037       * @param int $sectionreturn section number to return to
1038       * @param int $displayoptions
1039       * @return void
1040       */
1041      public function course_section_cm_list($course, $section, $sectionreturn = null, $displayoptions = array()) {
1042          global $USER;
1043  
1044          $output = '';
1045          $modinfo = get_fast_modinfo($course);
1046          if (is_object($section)) {
1047              $section = $modinfo->get_section_info($section->section);
1048          } else {
1049              $section = $modinfo->get_section_info($section);
1050          }
1051          $completioninfo = new completion_info($course);
1052  
1053          // check if we are currently in the process of moving a module with JavaScript disabled
1054          $ismoving = $this->page->user_is_editing() && ismoving($course->id);
1055          if ($ismoving) {
1056              $movingpix = new pix_icon('movehere', get_string('movehere'), 'moodle', array('class' => 'movetarget'));
1057              $strmovefull = strip_tags(get_string("movefull", "", "'$USER->activitycopyname'"));
1058          }
1059  
1060          // Get the list of modules visible to user (excluding the module being moved if there is one)
1061          $moduleshtml = array();
1062          if (!empty($modinfo->sections[$section->section])) {
1063              foreach ($modinfo->sections[$section->section] as $modnumber) {
1064                  $mod = $modinfo->cms[$modnumber];
1065  
1066                  if ($ismoving and $mod->id == $USER->activitycopy) {
1067                      // do not display moving mod
1068                      continue;
1069                  }
1070  
1071                  if ($modulehtml = $this->course_section_cm_list_item($course,
1072                          $completioninfo, $mod, $sectionreturn, $displayoptions)) {
1073                      $moduleshtml[$modnumber] = $modulehtml;
1074                  }
1075              }
1076          }
1077  
1078          $sectionoutput = '';
1079          if (!empty($moduleshtml) || $ismoving) {
1080              foreach ($moduleshtml as $modnumber => $modulehtml) {
1081                  if ($ismoving) {
1082                      $movingurl = new moodle_url('/course/mod.php', array('moveto' => $modnumber, 'sesskey' => sesskey()));
1083                      $sectionoutput .= html_writer::tag('li',
1084                              html_writer::link($movingurl, $this->output->render($movingpix), array('title' => $strmovefull)),
1085                              array('class' => 'movehere'));
1086                  }
1087  
1088                  $sectionoutput .= $modulehtml;
1089              }
1090  
1091              if ($ismoving) {
1092                  $movingurl = new moodle_url('/course/mod.php', array('movetosection' => $section->id, 'sesskey' => sesskey()));
1093                  $sectionoutput .= html_writer::tag('li',
1094                          html_writer::link($movingurl, $this->output->render($movingpix), array('title' => $strmovefull)),
1095                          array('class' => 'movehere'));
1096              }
1097          }
1098  
1099          // Always output the section module list.
1100          $output .= html_writer::tag('ul', $sectionoutput, array('class' => 'section img-text'));
1101  
1102          return $output;
1103      }
1104  
1105      /**
1106       * Displays a custom list of courses with paging bar if necessary
1107       *
1108       * If $paginationurl is specified but $totalcount is not, the link 'View more'
1109       * appears under the list.
1110       *
1111       * If both $paginationurl and $totalcount are specified, and $totalcount is
1112       * bigger than count($courses), a paging bar is displayed above and under the
1113       * courses list.
1114       *
1115       * @param array $courses array of course records (or instances of course_in_list) to show on this page
1116       * @param bool $showcategoryname whether to add category name to the course description
1117       * @param string $additionalclasses additional CSS classes to add to the div.courses
1118       * @param moodle_url $paginationurl url to view more or url to form links to the other pages in paging bar
1119       * @param int $totalcount total number of courses on all pages, if omitted $paginationurl will be displayed as 'View more' link
1120       * @param int $page current page number (defaults to 0 referring to the first page)
1121       * @param int $perpage number of records per page (defaults to $CFG->coursesperpage)
1122       * @return string
1123       */
1124      public function courses_list($courses, $showcategoryname = false, $additionalclasses = null, $paginationurl = null, $totalcount = null, $page = 0, $perpage = null) {
1125          global $CFG;
1126          // create instance of coursecat_helper to pass display options to function rendering courses list
1127          $chelper = new coursecat_helper();
1128          if ($showcategoryname) {
1129              $chelper->set_show_courses(self::COURSECAT_SHOW_COURSES_EXPANDED_WITH_CAT);
1130          } else {
1131              $chelper->set_show_courses(self::COURSECAT_SHOW_COURSES_EXPANDED);
1132          }
1133          if ($totalcount !== null && $paginationurl !== null) {
1134              // add options to display pagination
1135              if ($perpage === null) {
1136                  $perpage = $CFG->coursesperpage;
1137              }
1138              $chelper->set_courses_display_options(array(
1139                  'limit' => $perpage,
1140                  'offset' => ((int)$page) * $perpage,
1141                  'paginationurl' => $paginationurl,
1142              ));
1143          } else if ($paginationurl !== null) {
1144              // add options to display 'View more' link
1145              $chelper->set_courses_display_options(array('viewmoreurl' => $paginationurl));
1146              $totalcount = count($courses) + 1; // has to be bigger than count($courses) otherwise link will not be displayed
1147          }
1148          $chelper->set_attributes(array('class' => $additionalclasses));
1149          $content = $this->coursecat_courses($chelper, $courses, $totalcount);
1150          return $content;
1151      }
1152  
1153      /**
1154       * Displays one course in the list of courses.
1155       *
1156       * This is an internal function, to display an information about just one course
1157       * please use {@link core_course_renderer::course_info_box()}
1158       *
1159       * @param coursecat_helper $chelper various display options
1160       * @param course_in_list|stdClass $course
1161       * @param string $additionalclasses additional classes to add to the main <div> tag (usually
1162       *    depend on the course position in list - first/last/even/odd)
1163       * @return string
1164       */
1165      protected function coursecat_coursebox(coursecat_helper $chelper, $course, $additionalclasses = '') {
1166          global $CFG;
1167          if (!isset($this->strings->summary)) {
1168              $this->strings->summary = get_string('summary');
1169          }
1170          if ($chelper->get_show_courses() <= self::COURSECAT_SHOW_COURSES_COUNT) {
1171              return '';
1172          }
1173          if ($course instanceof stdClass) {
1174              require_once($CFG->libdir. '/coursecatlib.php');
1175              $course = new course_in_list($course);
1176          }
1177          $content = '';
1178          $classes = trim('coursebox clearfix '. $additionalclasses);
1179          if ($chelper->get_show_courses() >= self::COURSECAT_SHOW_COURSES_EXPANDED) {
1180              $nametag = 'h3';
1181          } else {
1182              $classes .= ' collapsed';
1183              $nametag = 'div';
1184          }
1185  
1186          // .coursebox
1187          $content .= html_writer::start_tag('div', array(
1188              'class' => $classes,
1189              'data-courseid' => $course->id,
1190              'data-type' => self::COURSECAT_TYPE_COURSE,
1191          ));
1192  
1193          $content .= html_writer::start_tag('div', array('class' => 'info'));
1194  
1195          // course name
1196          $coursename = $chelper->get_course_formatted_name($course);
1197          $coursenamelink = html_writer::link(new moodle_url('/course/view.php', array('id' => $course->id)),
1198                                              $coursename, array('class' => $course->visible ? '' : 'dimmed'));
1199          $content .= html_writer::tag($nametag, $coursenamelink, array('class' => 'coursename'));
1200          // If we display course in collapsed form but the course has summary or course contacts, display the link to the info page.
1201          $content .= html_writer::start_tag('div', array('class' => 'moreinfo'));
1202          if ($chelper->get_show_courses() < self::COURSECAT_SHOW_COURSES_EXPANDED) {
1203              if ($course->has_summary() || $course->has_course_contacts() || $course->has_course_overviewfiles()) {
1204                  $url = new moodle_url('/course/info.php', array('id' => $course->id));
1205                  $image = html_writer::empty_tag('img', array('src' => $this->output->pix_url('i/info'),
1206                      'alt' => $this->strings->summary));
1207                  $content .= html_writer::link($url, $image, array('title' => $this->strings->summary));
1208                  // Make sure JS file to expand course content is included.
1209                  $this->coursecat_include_js();
1210              }
1211          }
1212          $content .= html_writer::end_tag('div'); // .moreinfo
1213  
1214          // print enrolmenticons
1215          if ($icons = enrol_get_course_info_icons($course)) {
1216              $content .= html_writer::start_tag('div', array('class' => 'enrolmenticons'));
1217              foreach ($icons as $pix_icon) {
1218                  $content .= $this->render($pix_icon);
1219              }
1220              $content .= html_writer::end_tag('div'); // .enrolmenticons
1221          }
1222  
1223          $content .= html_writer::end_tag('div'); // .info
1224  
1225          $content .= html_writer::start_tag('div', array('class' => 'content'));
1226          $content .= $this->coursecat_coursebox_content($chelper, $course);
1227          $content .= html_writer::end_tag('div'); // .content
1228  
1229          $content .= html_writer::end_tag('div'); // .coursebox
1230          return $content;
1231      }
1232  
1233      /**
1234       * Returns HTML to display course content (summary, course contacts and optionally category name)
1235       *
1236       * This method is called from coursecat_coursebox() and may be re-used in AJAX
1237       *
1238       * @param coursecat_helper $chelper various display options
1239       * @param stdClass|course_in_list $course
1240       * @return string
1241       */
1242      protected function coursecat_coursebox_content(coursecat_helper $chelper, $course) {
1243          global $CFG;
1244          if ($chelper->get_show_courses() < self::COURSECAT_SHOW_COURSES_EXPANDED) {
1245              return '';
1246          }
1247          if ($course instanceof stdClass) {
1248              require_once($CFG->libdir. '/coursecatlib.php');
1249              $course = new course_in_list($course);
1250          }
1251          $content = '';
1252  
1253          // display course summary
1254          if ($course->has_summary()) {
1255              $content .= html_writer::start_tag('div', array('class' => 'summary'));
1256              $content .= $chelper->get_course_formatted_summary($course,
1257                      array('overflowdiv' => true, 'noclean' => true, 'para' => false));
1258              $content .= html_writer::end_tag('div'); // .summary
1259          }
1260  
1261          // display course overview files
1262          $contentimages = $contentfiles = '';
1263          foreach ($course->get_course_overviewfiles() as $file) {
1264              $isimage = $file->is_valid_image();
1265              $url = file_encode_url("$CFG->wwwroot/pluginfile.php",
1266                      '/'. $file->get_contextid(). '/'. $file->get_component(). '/'.
1267                      $file->get_filearea(). $file->get_filepath(). $file->get_filename(), !$isimage);
1268              if ($isimage) {
1269                  $contentimages .= html_writer::tag('div',
1270                          html_writer::empty_tag('img', array('src' => $url)),
1271                          array('class' => 'courseimage'));
1272              } else {
1273                  $image = $this->output->pix_icon(file_file_icon($file, 24), $file->get_filename(), 'moodle');
1274                  $filename = html_writer::tag('span', $image, array('class' => 'fp-icon')).
1275                          html_writer::tag('span', $file->get_filename(), array('class' => 'fp-filename'));
1276                  $contentfiles .= html_writer::tag('span',
1277                          html_writer::link($url, $filename),
1278                          array('class' => 'coursefile fp-filename-icon'));
1279              }
1280          }
1281          $content .= $contentimages. $contentfiles;
1282  
1283          // display course contacts. See course_in_list::get_course_contacts()
1284          if ($course->has_course_contacts()) {
1285              $content .= html_writer::start_tag('ul', array('class' => 'teachers'));
1286              foreach ($course->get_course_contacts() as $userid => $coursecontact) {
1287                  $name = $coursecontact['rolename'].': '.
1288                          html_writer::link(new moodle_url('/user/view.php',
1289                                  array('id' => $userid, 'course' => SITEID)),
1290                              $coursecontact['username']);
1291                  $content .= html_writer::tag('li', $name);
1292              }
1293              $content .= html_writer::end_tag('ul'); // .teachers
1294          }
1295  
1296          // display course category if necessary (for example in search results)
1297          if ($chelper->get_show_courses() == self::COURSECAT_SHOW_COURSES_EXPANDED_WITH_CAT) {
1298              require_once($CFG->libdir. '/coursecatlib.php');
1299              if ($cat = coursecat::get($course->category, IGNORE_MISSING)) {
1300                  $content .= html_writer::start_tag('div', array('class' => 'coursecat'));
1301                  $content .= get_string('category').': '.
1302                          html_writer::link(new moodle_url('/course/index.php', array('categoryid' => $cat->id)),
1303                                  $cat->get_formatted_name(), array('class' => $cat->visible ? '' : 'dimmed'));
1304                  $content .= html_writer::end_tag('div'); // .coursecat
1305              }
1306          }
1307  
1308          return $content;
1309      }
1310  
1311      /**
1312       * Renders the list of courses
1313       *
1314       * This is internal function, please use {@link core_course_renderer::courses_list()} or another public
1315       * method from outside of the class
1316       *
1317       * If list of courses is specified in $courses; the argument $chelper is only used
1318       * to retrieve display options and attributes, only methods get_show_courses(),
1319       * get_courses_display_option() and get_and_erase_attributes() are called.
1320       *
1321       * @param coursecat_helper $chelper various display options
1322       * @param array $courses the list of courses to display
1323       * @param int|null $totalcount total number of courses (affects display mode if it is AUTO or pagination if applicable),
1324       *     defaulted to count($courses)
1325       * @return string
1326       */
1327      protected function coursecat_courses(coursecat_helper $chelper, $courses, $totalcount = null) {
1328          global $CFG;
1329          if ($totalcount === null) {
1330              $totalcount = count($courses);
1331          }
1332          if (!$totalcount) {
1333              // Courses count is cached during courses retrieval.
1334              return '';
1335          }
1336  
1337          if ($chelper->get_show_courses() == self::COURSECAT_SHOW_COURSES_AUTO) {
1338              // In 'auto' course display mode we analyse if number of courses is more or less than $CFG->courseswithsummarieslimit
1339              if ($totalcount <= $CFG->courseswithsummarieslimit) {
1340                  $chelper->set_show_courses(self::COURSECAT_SHOW_COURSES_EXPANDED);
1341              } else {
1342                  $chelper->set_show_courses(self::COURSECAT_SHOW_COURSES_COLLAPSED);
1343              }
1344          }
1345  
1346          // prepare content of paging bar if it is needed
1347          $paginationurl = $chelper->get_courses_display_option('paginationurl');
1348          $paginationallowall = $chelper->get_courses_display_option('paginationallowall');
1349          if ($totalcount > count($courses)) {
1350              // there are more results that can fit on one page
1351              if ($paginationurl) {
1352                  // the option paginationurl was specified, display pagingbar
1353                  $perpage = $chelper->get_courses_display_option('limit', $CFG->coursesperpage);
1354                  $page = $chelper->get_courses_display_option('offset') / $perpage;
1355                  $pagingbar = $this->paging_bar($totalcount, $page, $perpage,
1356                          $paginationurl->out(false, array('perpage' => $perpage)));
1357                  if ($paginationallowall) {
1358                      $pagingbar .= html_writer::tag('div', html_writer::link($paginationurl->out(false, array('perpage' => 'all')),
1359                              get_string('showall', '', $totalcount)), array('class' => 'paging paging-showall'));
1360                  }
1361              } else if ($viewmoreurl = $chelper->get_courses_display_option('viewmoreurl')) {
1362                  // the option for 'View more' link was specified, display more link
1363                  $viewmoretext = $chelper->get_courses_display_option('viewmoretext', new lang_string('viewmore'));
1364                  $morelink = html_writer::tag('div', html_writer::link($viewmoreurl, $viewmoretext),
1365                          array('class' => 'paging paging-morelink'));
1366              }
1367          } else if (($totalcount > $CFG->coursesperpage) && $paginationurl && $paginationallowall) {
1368              // there are more than one page of results and we are in 'view all' mode, suggest to go back to paginated view mode
1369              $pagingbar = html_writer::tag('div', html_writer::link($paginationurl->out(false, array('perpage' => $CFG->coursesperpage)),
1370                  get_string('showperpage', '', $CFG->coursesperpage)), array('class' => 'paging paging-showperpage'));
1371          }
1372  
1373          // display list of courses
1374          $attributes = $chelper->get_and_erase_attributes('courses');
1375          $content = html_writer::start_tag('div', $attributes);
1376  
1377          if (!empty($pagingbar)) {
1378              $content .= $pagingbar;
1379          }
1380  
1381          $coursecount = 0;
1382          foreach ($courses as $course) {
1383              $coursecount ++;
1384              $classes = ($coursecount%2) ? 'odd' : 'even';
1385              if ($coursecount == 1) {
1386                  $classes .= ' first';
1387              }
1388              if ($coursecount >= count($courses)) {
1389                  $classes .= ' last';
1390              }
1391              $content .= $this->coursecat_coursebox($chelper, $course, $classes);
1392          }
1393  
1394          if (!empty($pagingbar)) {
1395              $content .= $pagingbar;
1396          }
1397          if (!empty($morelink)) {
1398              $content .= $morelink;
1399          }
1400  
1401          $content .= html_writer::end_tag('div'); // .courses
1402          return $content;
1403      }
1404  
1405      /**
1406       * Renders the list of subcategories in a category
1407       *
1408       * @param coursecat_helper $chelper various display options
1409       * @param coursecat $coursecat
1410       * @param int $depth depth of the category in the current tree
1411       * @return string
1412       */
1413      protected function coursecat_subcategories(coursecat_helper $chelper, $coursecat, $depth) {
1414          global $CFG;
1415          $subcategories = array();
1416          if (!$chelper->get_categories_display_option('nodisplay')) {
1417              $subcategories = $coursecat->get_children($chelper->get_categories_display_options());
1418          }
1419          $totalcount = $coursecat->get_children_count();
1420          if (!$totalcount) {
1421              // Note that we call coursecat::get_children_count() AFTER coursecat::get_children() to avoid extra DB requests.
1422              // Categories count is cached during children categories retrieval.
1423              return '';
1424          }
1425  
1426          // prepare content of paging bar or more link if it is needed
1427          $paginationurl = $chelper->get_categories_display_option('paginationurl');
1428          $paginationallowall = $chelper->get_categories_display_option('paginationallowall');
1429          if ($totalcount > count($subcategories)) {
1430              if ($paginationurl) {
1431                  // the option 'paginationurl was specified, display pagingbar
1432                  $perpage = $chelper->get_categories_display_option('limit', $CFG->coursesperpage);
1433                  $page = $chelper->get_categories_display_option('offset') / $perpage;
1434                  $pagingbar = $this->paging_bar($totalcount, $page, $perpage,
1435                          $paginationurl->out(false, array('perpage' => $perpage)));
1436                  if ($paginationallowall) {
1437                      $pagingbar .= html_writer::tag('div', html_writer::link($paginationurl->out(false, array('perpage' => 'all')),
1438                              get_string('showall', '', $totalcount)), array('class' => 'paging paging-showall'));
1439                  }
1440              } else if ($viewmoreurl = $chelper->get_categories_display_option('viewmoreurl')) {
1441                  // the option 'viewmoreurl' was specified, display more link (if it is link to category view page, add category id)
1442                  if ($viewmoreurl->compare(new moodle_url('/course/index.php'), URL_MATCH_BASE)) {
1443                      $viewmoreurl->param('categoryid', $coursecat->id);
1444                  }
1445                  $viewmoretext = $chelper->get_categories_display_option('viewmoretext', new lang_string('viewmore'));
1446                  $morelink = html_writer::tag('div', html_writer::link($viewmoreurl, $viewmoretext),
1447                          array('class' => 'paging paging-morelink'));
1448              }
1449          } else if (($totalcount > $CFG->coursesperpage) && $paginationurl && $paginationallowall) {
1450              // there are more than one page of results and we are in 'view all' mode, suggest to go back to paginated view mode
1451              $pagingbar = html_writer::tag('div', html_writer::link($paginationurl->out(false, array('perpage' => $CFG->coursesperpage)),
1452                  get_string('showperpage', '', $CFG->coursesperpage)), array('class' => 'paging paging-showperpage'));
1453          }
1454  
1455          // display list of subcategories
1456          $content = html_writer::start_tag('div', array('class' => 'subcategories'));
1457  
1458          if (!empty($pagingbar)) {
1459              $content .= $pagingbar;
1460          }
1461  
1462          foreach ($subcategories as $subcategory) {
1463              $content .= $this->coursecat_category($chelper, $subcategory, $depth + 1);
1464          }
1465  
1466          if (!empty($pagingbar)) {
1467              $content .= $pagingbar;
1468          }
1469          if (!empty($morelink)) {
1470              $content .= $morelink;
1471          }
1472  
1473          $content .= html_writer::end_tag('div');
1474          return $content;
1475      }
1476  
1477      /**
1478       * Make sure that javascript file for AJAX expanding of courses and categories content is included
1479       */
1480      protected function coursecat_include_js() {
1481          if (!$this->page->requires->should_create_one_time_item_now('core_course_categoryexpanderjsinit')) {
1482              return;
1483          }
1484  
1485          // We must only load this module once.
1486          $this->page->requires->yui_module('moodle-course-categoryexpander',
1487                  'Y.Moodle.course.categoryexpander.init');
1488      }
1489  
1490      /**
1491       * Returns HTML to display the subcategories and courses in the given category
1492       *
1493       * This method is re-used by AJAX to expand content of not loaded category
1494       *
1495       * @param coursecat_helper $chelper various display options
1496       * @param coursecat $coursecat
1497       * @param int $depth depth of the category in the current tree
1498       * @return string
1499       */
1500      protected function coursecat_category_content(coursecat_helper $chelper, $coursecat, $depth) {
1501          $content = '';
1502          // Subcategories
1503          $content .= $this->coursecat_subcategories($chelper, $coursecat, $depth);
1504  
1505          // AUTO show courses: Courses will be shown expanded if this is not nested category,
1506          // and number of courses no bigger than $CFG->courseswithsummarieslimit.
1507          $showcoursesauto = $chelper->get_show_courses() == self::COURSECAT_SHOW_COURSES_AUTO;
1508          if ($showcoursesauto && $depth) {
1509              // this is definitely collapsed mode
1510              $chelper->set_show_courses(self::COURSECAT_SHOW_COURSES_COLLAPSED);
1511          }
1512  
1513          // Courses
1514          if ($chelper->get_show_courses() > core_course_renderer::COURSECAT_SHOW_COURSES_COUNT) {
1515              $courses = array();
1516              if (!$chelper->get_courses_display_option('nodisplay')) {
1517                  $courses = $coursecat->get_courses($chelper->get_courses_display_options());
1518              }
1519              if ($viewmoreurl = $chelper->get_courses_display_option('viewmoreurl')) {
1520                  // the option for 'View more' link was specified, display more link (if it is link to category view page, add category id)
1521                  if ($viewmoreurl->compare(new moodle_url('/course/index.php'), URL_MATCH_BASE)) {
1522                      $chelper->set_courses_display_option('viewmoreurl', new moodle_url($viewmoreurl, array('categoryid' => $coursecat->id)));
1523                  }
1524              }
1525              $content .= $this->coursecat_courses($chelper, $courses, $coursecat->get_courses_count());
1526          }
1527  
1528          if ($showcoursesauto) {
1529              // restore the show_courses back to AUTO
1530              $chelper->set_show_courses(self::COURSECAT_SHOW_COURSES_AUTO);
1531          }
1532  
1533          return $content;
1534      }
1535  
1536      /**
1537       * Returns HTML to display a course category as a part of a tree
1538       *
1539       * This is an internal function, to display a particular category and all its contents
1540       * use {@link core_course_renderer::course_category()}
1541       *
1542       * @param coursecat_helper $chelper various display options
1543       * @param coursecat $coursecat
1544       * @param int $depth depth of this category in the current tree
1545       * @return string
1546       */
1547      protected function coursecat_category(coursecat_helper $chelper, $coursecat, $depth) {
1548          // open category tag
1549          $classes = array('category');
1550          if (empty($coursecat->visible)) {
1551              $classes[] = 'dimmed_category';
1552          }
1553          if ($chelper->get_subcat_depth() > 0 && $depth >= $chelper->get_subcat_depth()) {
1554              // do not load content
1555              $categorycontent = '';
1556              $classes[] = 'notloaded';
1557              if ($coursecat->get_children_count() ||
1558                      ($chelper->get_show_courses() >= self::COURSECAT_SHOW_COURSES_COLLAPSED && $coursecat->get_courses_count())) {
1559                  $classes[] = 'with_children';
1560                  $classes[] = 'collapsed';
1561              }
1562          } else {
1563              // load category content
1564              $categorycontent = $this->coursecat_category_content($chelper, $coursecat, $depth);
1565              $classes[] = 'loaded';
1566              if (!empty($categorycontent)) {
1567                  $classes[] = 'with_children';
1568              }
1569          }
1570  
1571          // Make sure JS file to expand category content is included.
1572          $this->coursecat_include_js();
1573  
1574          $content = html_writer::start_tag('div', array(
1575              'class' => join(' ', $classes),
1576              'data-categoryid' => $coursecat->id,
1577              'data-depth' => $depth,
1578              'data-showcourses' => $chelper->get_show_courses(),
1579              'data-type' => self::COURSECAT_TYPE_CATEGORY,
1580          ));
1581  
1582          // category name
1583          $categoryname = $coursecat->get_formatted_name();
1584          $categoryname = html_writer::link(new moodle_url('/course/index.php',
1585                  array('categoryid' => $coursecat->id)),
1586                  $categoryname);
1587          if ($chelper->get_show_courses() == self::COURSECAT_SHOW_COURSES_COUNT
1588                  && ($coursescount = $coursecat->get_courses_count())) {
1589              $categoryname .= html_writer::tag('span', ' ('. $coursescount.')',
1590                      array('title' => get_string('numberofcourses'), 'class' => 'numberofcourse'));
1591          }
1592          $content .= html_writer::start_tag('div', array('class' => 'info'));
1593  
1594          $content .= html_writer::tag(($depth > 1) ? 'h4' : 'h3', $categoryname, array('class' => 'categoryname'));
1595          $content .= html_writer::end_tag('div'); // .info
1596  
1597          // add category content to the output
1598          $content .= html_writer::tag('div', $categorycontent, array('class' => 'content'));
1599  
1600          $content .= html_writer::end_tag('div'); // .category
1601  
1602          // Return the course category tree HTML
1603          return $content;
1604      }
1605  
1606      /**
1607       * Returns HTML to display a tree of subcategories and courses in the given category
1608       *
1609       * @param coursecat_helper $chelper various display options
1610       * @param coursecat $coursecat top category (this category's name and description will NOT be added to the tree)
1611       * @return string
1612       */
1613      protected function coursecat_tree(coursecat_helper $chelper, $coursecat) {
1614          $categorycontent = $this->coursecat_category_content($chelper, $coursecat, 0);
1615          if (empty($categorycontent)) {
1616              return '';
1617          }
1618  
1619          // Start content generation
1620          $content = '';
1621          $attributes = $chelper->get_and_erase_attributes('course_category_tree clearfix');
1622          $content .= html_writer::start_tag('div', $attributes);
1623  
1624          if ($coursecat->get_children_count()) {
1625              $classes = array(
1626                  'collapseexpand',
1627                  'collapse-all',
1628              );
1629              if ($chelper->get_subcat_depth() == 1) {
1630                  $classes[] = 'disabled';
1631              }
1632              // Only show the collapse/expand if there are children to expand.
1633              $content .= html_writer::start_tag('div', array('class' => 'collapsible-actions'));
1634              $content .= html_writer::link('#', get_string('collapseall'),
1635                      array('class' => implode(' ', $classes)));
1636              $content .= html_writer::end_tag('div');
1637              $this->page->requires->strings_for_js(array('collapseall', 'expandall'), 'moodle');
1638          }
1639  
1640          $content .= html_writer::tag('div', $categorycontent, array('class' => 'content'));
1641  
1642          $content .= html_writer::end_tag('div'); // .course_category_tree
1643  
1644          return $content;
1645      }
1646  
1647      /**
1648       * Renders HTML to display particular course category - list of it's subcategories and courses
1649       *
1650       * Invoked from /course/index.php
1651       *
1652       * @param int|stdClass|coursecat $category
1653       */
1654      public function course_category($category) {
1655          global $CFG;
1656          require_once($CFG->libdir. '/coursecatlib.php');
1657          $coursecat = coursecat::get(is_object($category) ? $category->id : $category);
1658          $site = get_site();
1659          $output = '';
1660  
1661          if (can_edit_in_category($coursecat->id)) {
1662              // Add 'Manage' button if user has permissions to edit this category.
1663              $managebutton = $this->single_button(new moodle_url('/course/management.php',
1664                  array('categoryid' => $coursecat->id)), get_string('managecourses'), 'get');
1665              $this->page->set_button($managebutton);
1666          }
1667          if (!$coursecat->id) {
1668              if (coursecat::count_all() == 1) {
1669                  // There exists only one category in the system, do not display link to it
1670                  $coursecat = coursecat::get_default();
1671                  $strfulllistofcourses = get_string('fulllistofcourses');
1672                  $this->page->set_title("$site->shortname: $strfulllistofcourses");
1673              } else {
1674                  $strcategories = get_string('categories');
1675                  $this->page->set_title("$site->shortname: $strcategories");
1676              }
1677          } else {
1678              $this->page->set_title("$site->shortname: ". $coursecat->get_formatted_name());
1679  
1680              // Print the category selector
1681              $output .= html_writer::start_tag('div', array('class' => 'categorypicker'));
1682              $select = new single_select(new moodle_url('/course/index.php'), 'categoryid',
1683                      coursecat::make_categories_list(), $coursecat->id, null, 'switchcategory');
1684              $select->set_label(get_string('categories').':');
1685              $output .= $this->render($select);
1686              $output .= html_writer::end_tag('div'); // .categorypicker
1687          }
1688  
1689          // Print current category description
1690          $chelper = new coursecat_helper();
1691          if ($description = $chelper->get_category_formatted_description($coursecat)) {
1692              $output .= $this->box($description, array('class' => 'generalbox info'));
1693          }
1694  
1695          // Prepare parameters for courses and categories lists in the tree
1696          $chelper->set_show_courses(self::COURSECAT_SHOW_COURSES_AUTO)
1697                  ->set_attributes(array('class' => 'category-browse category-browse-'.$coursecat->id));
1698  
1699          $coursedisplayoptions = array();
1700          $catdisplayoptions = array();
1701          $browse = optional_param('browse', null, PARAM_ALPHA);
1702          $perpage = optional_param('perpage', $CFG->coursesperpage, PARAM_INT);
1703          $page = optional_param('page', 0, PARAM_INT);
1704          $baseurl = new moodle_url('/course/index.php');
1705          if ($coursecat->id) {
1706              $baseurl->param('categoryid', $coursecat->id);
1707          }
1708          if ($perpage != $CFG->coursesperpage) {
1709              $baseurl->param('perpage', $perpage);
1710          }
1711          $coursedisplayoptions['limit'] = $perpage;
1712          $catdisplayoptions['limit'] = $perpage;
1713          if ($browse === 'courses' || !$coursecat->has_children()) {
1714              $coursedisplayoptions['offset'] = $page * $perpage;
1715              $coursedisplayoptions['paginationurl'] = new moodle_url($baseurl, array('browse' => 'courses'));
1716              $catdisplayoptions['nodisplay'] = true;
1717              $catdisplayoptions['viewmoreurl'] = new moodle_url($baseurl, array('browse' => 'categories'));
1718              $catdisplayoptions['viewmoretext'] = new lang_string('viewallsubcategories');
1719          } else if ($browse === 'categories' || !$coursecat->has_courses()) {
1720              $coursedisplayoptions['nodisplay'] = true;
1721              $catdisplayoptions['offset'] = $page * $perpage;
1722              $catdisplayoptions['paginationurl'] = new moodle_url($baseurl, array('browse' => 'categories'));
1723              $coursedisplayoptions['viewmoreurl'] = new moodle_url($baseurl, array('browse' => 'courses'));
1724              $coursedisplayoptions['viewmoretext'] = new lang_string('viewallcourses');
1725          } else {
1726              // we have a category that has both subcategories and courses, display pagination separately
1727              $coursedisplayoptions['viewmoreurl'] = new moodle_url($baseurl, array('browse' => 'courses', 'page' => 1));
1728              $catdisplayoptions['viewmoreurl'] = new moodle_url($baseurl, array('browse' => 'categories', 'page' => 1));
1729          }
1730          $chelper->set_courses_display_options($coursedisplayoptions)->set_categories_display_options($catdisplayoptions);
1731          // Add course search form.
1732          $output .= $this->course_search_form();
1733  
1734          // Display course category tree.
1735          $output .= $this->coursecat_tree($chelper, $coursecat);
1736  
1737          // Add action buttons
1738          $output .= $this->container_start('buttons');
1739          $context = get_category_or_system_context($coursecat->id);
1740          if (has_capability('moodle/course:create', $context)) {
1741              // Print link to create a new course, for the 1st available category.
1742              if ($coursecat->id) {
1743                  $url = new moodle_url('/course/edit.php', array('category' => $coursecat->id, 'returnto' => 'category'));
1744              } else {
1745                  $url = new moodle_url('/course/edit.php', array('category' => $CFG->defaultrequestcategory, 'returnto' => 'topcat'));
1746              }
1747              $output .= $this->single_button($url, get_string('addnewcourse'), 'get');
1748          }
1749          ob_start();
1750          if (coursecat::count_all() == 1) {
1751              print_course_request_buttons(context_system::instance());
1752          } else {
1753              print_course_request_buttons($context);
1754          }
1755          $output .= ob_get_contents();
1756          ob_end_clean();
1757          $output .= $this->container_end();
1758  
1759          return $output;
1760      }
1761  
1762      /**
1763       * Serves requests to /course/category.ajax.php
1764       *
1765       * In this renderer implementation it may expand the category content or
1766       * course content.
1767       *
1768       * @return string
1769       * @throws coding_exception
1770       */
1771      public function coursecat_ajax() {
1772          global $DB, $CFG;
1773          require_once($CFG->libdir. '/coursecatlib.php');
1774  
1775          $type = required_param('type', PARAM_INT);
1776  
1777          if ($type === self::COURSECAT_TYPE_CATEGORY) {
1778              // This is a request for a category list of some kind.
1779              $categoryid = required_param('categoryid', PARAM_INT);
1780              $showcourses = required_param('showcourses', PARAM_INT);
1781              $depth = required_param('depth', PARAM_INT);
1782  
1783              $category = coursecat::get($categoryid);
1784  
1785              $chelper = new coursecat_helper();
1786              $baseurl = new moodle_url('/course/index.php', array('categoryid' => $categoryid));
1787              $coursedisplayoptions = array(
1788                  'limit' => $CFG->coursesperpage,
1789                  'viewmoreurl' => new moodle_url($baseurl, array('browse' => 'courses', 'page' => 1))
1790              );
1791              $catdisplayoptions = array(
1792                  'limit' => $CFG->coursesperpage,
1793                  'viewmoreurl' => new moodle_url($baseurl, array('browse' => 'categories', 'page' => 1))
1794              );
1795              $chelper->set_show_courses($showcourses)->
1796                      set_courses_display_options($coursedisplayoptions)->
1797                      set_categories_display_options($catdisplayoptions);
1798  
1799              return $this->coursecat_category_content($chelper, $category, $depth);
1800          } else if ($type === self::COURSECAT_TYPE_COURSE) {
1801              // This is a request for the course information.
1802              $courseid = required_param('courseid', PARAM_INT);
1803  
1804              $course = $DB->get_record('course', array('id' => $courseid), '*', MUST_EXIST);
1805  
1806              $chelper = new coursecat_helper();
1807              $chelper->set_show_courses(self::COURSECAT_SHOW_COURSES_EXPANDED);
1808              return $this->coursecat_coursebox_content($chelper, $course);
1809          } else {
1810              throw new coding_exception('Invalid request type');
1811          }
1812      }
1813  
1814      /**
1815       * Renders html to display search result page
1816       *
1817       * @param array $searchcriteria may contain elements: search, blocklist, modulelist, tagid
1818       * @return string
1819       */
1820      public function search_courses($searchcriteria) {
1821          global $CFG;
1822          $content = '';
1823          if (!empty($searchcriteria)) {
1824              // print search results
1825              require_once($CFG->libdir. '/coursecatlib.php');
1826  
1827              $displayoptions = array('sort' => array('displayname' => 1));
1828              // take the current page and number of results per page from query
1829              $perpage = optional_param('perpage', 0, PARAM_RAW);
1830              if ($perpage !== 'all') {
1831                  $displayoptions['limit'] = ((int)$perpage <= 0) ? $CFG->coursesperpage : (int)$perpage;
1832                  $page = optional_param('page', 0, PARAM_INT);
1833                  $displayoptions['offset'] = $displayoptions['limit'] * $page;
1834              }
1835              // options 'paginationurl' and 'paginationallowall' are only used in method coursecat_courses()
1836              $displayoptions['paginationurl'] = new moodle_url('/course/search.php', $searchcriteria);
1837              $displayoptions['paginationallowall'] = true; // allow adding link 'View all'
1838  
1839              $class = 'course-search-result';
1840              foreach ($searchcriteria as $key => $value) {
1841                  if (!empty($value)) {
1842                      $class .= ' course-search-result-'. $key;
1843                  }
1844              }
1845              $chelper = new coursecat_helper();
1846              $chelper->set_show_courses(self::COURSECAT_SHOW_COURSES_EXPANDED_WITH_CAT)->
1847                      set_courses_display_options($displayoptions)->
1848                      set_search_criteria($searchcriteria)->
1849                      set_attributes(array('class' => $class));
1850  
1851              $courses = coursecat::search_courses($searchcriteria, $chelper->get_courses_display_options());
1852              $totalcount = coursecat::search_courses_count($searchcriteria);
1853              $courseslist = $this->coursecat_courses($chelper, $courses, $totalcount);
1854  
1855              if (!$totalcount) {
1856                  if (!empty($searchcriteria['search'])) {
1857                      $content .= $this->heading(get_string('nocoursesfound', '', $searchcriteria['search']));
1858                  } else {
1859                      $content .= $this->heading(get_string('novalidcourses'));
1860                  }
1861              } else {
1862                  $content .= $this->heading(get_string('searchresults'). ": $totalcount");
1863                  $content .= $courseslist;
1864              }
1865  
1866              if (!empty($searchcriteria['search'])) {
1867                  // print search form only if there was a search by search string, otherwise it is confusing
1868                  $content .= $this->box_start('generalbox mdl-align');
1869                  $content .= $this->course_search_form($searchcriteria['search']);
1870                  $content .= $this->box_end();
1871              }
1872          } else {
1873              // just print search form
1874              $content .= $this->box_start('generalbox mdl-align');
1875              $content .= $this->course_search_form();
1876              $content .= html_writer::tag('div', get_string("searchhelp"), array('class' => 'searchhelp'));
1877              $content .= $this->box_end();
1878          }
1879          return $content;
1880      }
1881  
1882      /**
1883       * Renders html to print list of courses tagged with particular tag
1884       *
1885       * @param int $tagid id of the tag
1886       * @param bool $exclusivemode if set to true it means that no other entities tagged with this tag
1887       *             are displayed on the page and the per-page limit may be bigger
1888       * @param int $fromctx context id where the link was displayed, may be used by callbacks
1889       *            to display items in the same context first
1890       * @param int $ctx context id where to search for records
1891       * @param bool $rec search in subcontexts as well
1892       * @param array $displayoptions
1893       * @return string empty string if no courses are marked with this tag or rendered list of courses
1894       */
1895      public function tagged_courses($tagid, $exclusivemode = true, $ctx = 0, $rec = true, $displayoptions = null) {
1896          global $CFG;
1897          require_once($CFG->libdir . '/coursecatlib.php');
1898          if (empty($displayoptions)) {
1899              $displayoptions = array();
1900          }
1901          $showcategories = coursecat::count_all() > 1;
1902          $displayoptions += array('limit' => $CFG->coursesperpage, 'offset' => 0);
1903          $chelper = new coursecat_helper();
1904          $searchcriteria = array('tagid' => $tagid, 'ctx' => $ctx, 'rec' => $rec);
1905          $chelper->set_show_courses($showcategories ? self::COURSECAT_SHOW_COURSES_EXPANDED_WITH_CAT :
1906                      self::COURSECAT_SHOW_COURSES_EXPANDED)->
1907                  set_search_criteria($searchcriteria)->
1908                  set_courses_display_options($displayoptions)->
1909                  set_attributes(array('class' => 'course-search-result course-search-result-tagid'));
1910                  // (we set the same css class as in search results by tagid)
1911          if ($totalcount = coursecat::search_courses_count($searchcriteria)) {
1912              $courses = coursecat::search_courses($searchcriteria, $chelper->get_courses_display_options());
1913              if ($exclusivemode) {
1914                  return $this->coursecat_courses($chelper, $courses, $totalcount);
1915              } else {
1916                  $tagfeed = new core_tag\output\tagfeed();
1917                  $img = $this->output->pix_icon('i/course', '');
1918                  foreach ($courses as $course) {
1919                      $url = course_get_url($course);
1920                      $imgwithlink = html_writer::link($url, $img);
1921                      $coursename = html_writer::link($url, $course->get_formatted_name());
1922                      $details = '';
1923                      if ($showcategories && ($cat = coursecat::get($course->category, IGNORE_MISSING))) {
1924                          $details = get_string('category').': '.
1925                                  html_writer::link(new moodle_url('/course/index.php', array('categoryid' => $cat->id)),
1926                                          $cat->get_formatted_name(), array('class' => $cat->visible ? '' : 'dimmed'));
1927                      }
1928                      $tagfeed->add($imgwithlink, $coursename, $details);
1929                  }
1930                  return $this->output->render_from_template('core_tag/tagfeed', $tagfeed->export_for_template($this->output));
1931              }
1932          }
1933          return '';
1934      }
1935  
1936      /**
1937       * Returns HTML to display one remote course
1938       *
1939       * @param stdClass $course remote course information, contains properties:
1940             id, remoteid, shortname, fullname, hostid, summary, summaryformat, cat_name, hostname
1941       * @return string
1942       */
1943      protected function frontpage_remote_course(stdClass $course) {
1944          $url = new moodle_url('/auth/mnet/jump.php', array(
1945              'hostid' => $course->hostid,
1946              'wantsurl' => '/course/view.php?id='. $course->remoteid
1947          ));
1948  
1949          $output = '';
1950          $output .= html_writer::start_tag('div', array('class' => 'coursebox remotecoursebox clearfix'));
1951          $output .= html_writer::start_tag('div', array('class' => 'info'));
1952          $output .= html_writer::start_tag('h3', array('class' => 'name'));
1953          $output .= html_writer::link($url, format_string($course->fullname), array('title' => get_string('entercourse')));
1954          $output .= html_writer::end_tag('h3'); // .name
1955          $output .= html_writer::tag('div', '', array('class' => 'moreinfo'));
1956          $output .= html_writer::end_tag('div'); // .info
1957          $output .= html_writer::start_tag('div', array('class' => 'content'));
1958          $output .= html_writer::start_tag('div', array('class' => 'summary'));
1959          $options = new stdClass();
1960          $options->noclean = true;
1961          $options->para = false;
1962          $options->overflowdiv = true;
1963          $output .= format_text($course->summary, $course->summaryformat, $options);
1964          $output .= html_writer::end_tag('div'); // .summary
1965          $addinfo = format_string($course->hostname) . ' : '
1966              . format_string($course->cat_name) . ' : '
1967              . format_string($course->shortname);
1968          $output .= html_writer::tag('div', $addinfo, array('class' => 'remotecourseinfo'));
1969          $output .= html_writer::end_tag('div'); // .content
1970          $output .= html_writer::end_tag('div'); // .coursebox
1971          return $output;
1972      }
1973  
1974      /**
1975       * Returns HTML to display one remote host
1976       *
1977       * @param array $host host information, contains properties: name, url, count
1978       * @return string
1979       */
1980      protected function frontpage_remote_host($host) {
1981          $output = '';
1982          $output .= html_writer::start_tag('div', array('class' => 'coursebox remotehost clearfix'));
1983          $output .= html_writer::start_tag('div', array('class' => 'info'));
1984          $output .= html_writer::start_tag('h3', array('class' => 'name'));
1985          $output .= html_writer::link($host['url'], s($host['name']), array('title' => s($host['name'])));
1986          $output .= html_writer::end_tag('h3'); // .name
1987          $output .= html_writer::tag('div', '', array('class' => 'moreinfo'));
1988          $output .= html_writer::end_tag('div'); // .info
1989          $output .= html_writer::start_tag('div', array('class' => 'content'));
1990          $output .= html_writer::start_tag('div', array('class' => 'summary'));
1991          $output .= $host['count'] . ' ' . get_string('courses');
1992          $output .= html_writer::end_tag('div'); // .content
1993          $output .= html_writer::end_tag('div'); // .coursebox
1994          return $output;
1995      }
1996  
1997      /**
1998       * Returns HTML to print list of courses user is enrolled to for the frontpage
1999       *
2000       * Also lists remote courses or remote hosts if MNET authorisation is used
2001       *
2002       * @return string
2003       */
2004      public function frontpage_my_courses() {
2005          global $USER, $CFG, $DB;
2006  
2007          if (!isloggedin() or isguestuser()) {
2008              return '';
2009          }
2010  
2011          $output = '';
2012          if (!empty($CFG->navsortmycoursessort)) {
2013              // sort courses the same as in navigation menu
2014              $sortorder = 'visible DESC,'. $CFG->navsortmycoursessort.' ASC';
2015          } else {
2016              $sortorder = 'visible DESC,sortorder ASC';
2017          }
2018          $courses  = enrol_get_my_courses('summary, summaryformat', $sortorder);
2019          $rhosts   = array();
2020          $rcourses = array();
2021          if (!empty($CFG->mnet_dispatcher_mode) && $CFG->mnet_dispatcher_mode==='strict') {
2022              $rcourses = get_my_remotecourses($USER->id);
2023              $rhosts   = get_my_remotehosts();
2024          }
2025  
2026          if (!empty($courses) || !empty($rcourses) || !empty($rhosts)) {
2027  
2028              $chelper = new coursecat_helper();
2029              if (count($courses) > $CFG->frontpagecourselimit) {
2030                  // There are more enrolled courses than we can display, display link to 'My courses'.
2031                  $totalcount = count($courses);
2032                  $courses = array_slice($courses, 0, $CFG->frontpagecourselimit, true);
2033                  $chelper->set_courses_display_options(array(
2034                          'viewmoreurl' => new moodle_url('/my/'),
2035                          'viewmoretext' => new lang_string('mycourses')
2036                      ));
2037              } else {
2038                  // All enrolled courses are displayed, display link to 'All courses' if there are more courses in system.
2039                  $chelper->set_courses_display_options(array(
2040                          'viewmoreurl' => new moodle_url('/course/index.php'),
2041                          'viewmoretext' => new lang_string('fulllistofcourses')
2042                      ));
2043                  $totalcount = $DB->count_records('course') - 1;
2044              }
2045              $chelper->set_show_courses(self::COURSECAT_SHOW_COURSES_EXPANDED)->
2046                      set_attributes(array('class' => 'frontpage-course-list-enrolled'));
2047              $output .= $this->coursecat_courses($chelper, $courses, $totalcount);
2048  
2049              // MNET
2050              if (!empty($rcourses)) {
2051                  // at the IDP, we know of all the remote courses
2052                  $output .= html_writer::start_tag('div', array('class' => 'courses'));
2053                  foreach ($rcourses as $course) {
2054                      $output .= $this->frontpage_remote_course($course);
2055                  }
2056                  $output .= html_writer::end_tag('div'); // .courses
2057              } elseif (!empty($rhosts)) {
2058                  // non-IDP, we know of all the remote servers, but not courses
2059                  $output .= html_writer::start_tag('div', array('class' => 'courses'));
2060                  foreach ($rhosts as $host) {
2061                      $output .= $this->frontpage_remote_host($host);
2062                  }
2063                  $output .= html_writer::end_tag('div'); // .courses
2064              }
2065          }
2066          return $output;
2067      }
2068  
2069      /**
2070       * Returns HTML to print list of available courses for the frontpage
2071       *
2072       * @return string
2073       */
2074      public function frontpage_available_courses() {
2075          global $CFG;
2076          require_once($CFG->libdir. '/coursecatlib.php');
2077  
2078          $chelper = new coursecat_helper();
2079          $chelper->set_show_courses(self::COURSECAT_SHOW_COURSES_EXPANDED)->
2080                  set_courses_display_options(array(
2081                      'recursive' => true,
2082                      'limit' => $CFG->frontpagecourselimit,
2083                      'viewmoreurl' => new moodle_url('/course/index.php'),
2084                      'viewmoretext' => new lang_string('fulllistofcourses')));
2085  
2086          $chelper->set_attributes(array('class' => 'frontpage-course-list-all'));
2087          $courses = coursecat::get(0)->get_courses($chelper->get_courses_display_options());
2088          $totalcount = coursecat::get(0)->get_courses_count($chelper->get_courses_display_options());
2089          if (!$totalcount && !$this->page->user_is_editing() && has_capability('moodle/course:create', context_system::instance())) {
2090              // Print link to create a new course, for the 1st available category.
2091              return $this->add_new_course_button();
2092          }
2093          return $this->coursecat_courses($chelper, $courses, $totalcount);
2094      }
2095  
2096      /**
2097       * Returns HTML to the "add new course" button for the page
2098       *
2099       * @return string
2100       */
2101      public function add_new_course_button() {
2102          global $CFG;
2103          // Print link to create a new course, for the 1st available category.
2104          $output = $this->container_start('buttons');
2105          $url = new moodle_url('/course/edit.php', array('category' => $CFG->defaultrequestcategory, 'returnto' => 'topcat'));
2106          $output .= $this->single_button($url, get_string('addnewcourse'), 'get');
2107          $output .= $this->container_end('buttons');
2108          return $output;
2109      }
2110  
2111      /**
2112       * Returns HTML to print tree with course categories and courses for the frontpage
2113       *
2114       * @return string
2115       */
2116      public function frontpage_combo_list() {
2117          global $CFG;
2118          require_once($CFG->libdir. '/coursecatlib.php');
2119          $chelper = new coursecat_helper();
2120          $chelper->set_subcat_depth($CFG->maxcategorydepth)->
2121              set_categories_display_options(array(
2122                  'limit' => $CFG->coursesperpage,
2123                  'viewmoreurl' => new moodle_url('/course/index.php',
2124                          array('browse' => 'categories', 'page' => 1))
2125              ))->
2126              set_courses_display_options(array(
2127                  'limit' => $CFG->coursesperpage,
2128                  'viewmoreurl' => new moodle_url('/course/index.php',
2129                          array('browse' => 'courses', 'page' => 1))
2130              ))->
2131              set_attributes(array('class' => 'frontpage-category-combo'));
2132          return $this->coursecat_tree($chelper, coursecat::get(0));
2133      }
2134  
2135      /**
2136       * Returns HTML to print tree of course categories (with number of courses) for the frontpage
2137       *
2138       * @return string
2139       */
2140      public function frontpage_categories_list() {
2141          global $CFG;
2142          require_once($CFG->libdir. '/coursecatlib.php');
2143          $chelper = new coursecat_helper();
2144          $chelper->set_subcat_depth($CFG->maxcategorydepth)->
2145                  set_show_courses(self::COURSECAT_SHOW_COURSES_COUNT)->
2146                  set_categories_display_options(array(
2147                      'limit' => $CFG->coursesperpage,
2148                      'viewmoreurl' => new moodle_url('/course/index.php',
2149                              array('browse' => 'categories', 'page' => 1))
2150                  ))->
2151                  set_attributes(array('class' => 'frontpage-category-names'));
2152          return $this->coursecat_tree($chelper, coursecat::get(0));
2153      }
2154  }
2155  
2156  /**
2157   * Class storing display options and functions to help display course category and/or courses lists
2158   *
2159   * This is a wrapper for coursecat objects that also stores display options
2160   * and functions to retrieve sorted and paginated lists of categories/courses.
2161   *
2162   * If theme overrides methods in core_course_renderers that access this class
2163   * it may as well not use this class at all or extend it.
2164   *
2165   * @package   core
2166   * @copyright 2013 Marina Glancy
2167   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2168   */
2169  class coursecat_helper {
2170      /** @var string [none, collapsed, expanded] how (if) display courses list */
2171      protected $showcourses = 10; /* core_course_renderer::COURSECAT_SHOW_COURSES_COLLAPSED */
2172      /** @var int depth to expand subcategories in the tree (deeper subcategories will be loaded by AJAX or proceed to category page by clicking on category name) */
2173      protected $subcatdepth = 1;
2174      /** @var array options to display courses list */
2175      protected $coursesdisplayoptions = array();
2176      /** @var array options to display subcategories list */
2177      protected $categoriesdisplayoptions = array();
2178      /** @var array additional HTML attributes */
2179      protected $attributes = array();
2180      /** @var array search criteria if the list is a search result */
2181      protected $searchcriteria = null;
2182  
2183      /**
2184       * Sets how (if) to show the courses - none, collapsed, expanded, etc.
2185       *
2186       * @param int $showcourses SHOW_COURSES_NONE, SHOW_COURSES_COLLAPSED, SHOW_COURSES_EXPANDED, etc.
2187       * @return coursecat_helper
2188       */
2189      public function set_show_courses($showcourses) {
2190          $this->showcourses = $showcourses;
2191          // Automatically set the options to preload summary and coursecontacts for coursecat::get_courses() and coursecat::search_courses()
2192          $this->coursesdisplayoptions['summary'] = $showcourses >= core_course_renderer::COURSECAT_SHOW_COURSES_AUTO;
2193          $this->coursesdisplayoptions['coursecontacts'] = $showcourses >= core_course_renderer::COURSECAT_SHOW_COURSES_EXPANDED;
2194          return $this;
2195      }
2196  
2197      /**
2198       * Returns how (if) to show the courses - none, collapsed, expanded, etc.
2199       *
2200       * @return int - COURSECAT_SHOW_COURSES_NONE, COURSECAT_SHOW_COURSES_COLLAPSED, COURSECAT_SHOW_COURSES_EXPANDED, etc.
2201       */
2202      public function get_show_courses() {
2203          return $this->showcourses;
2204      }
2205  
2206      /**
2207       * Sets the maximum depth to expand subcategories in the tree
2208       *
2209       * deeper subcategories may be loaded by AJAX or proceed to category page by clicking on category name
2210       *
2211       * @param int $subcatdepth
2212       * @return coursecat_helper
2213       */
2214      public function set_subcat_depth($subcatdepth) {
2215          $this->subcatdepth = $subcatdepth;
2216          return $this;
2217      }
2218  
2219      /**
2220       * Returns the maximum depth to expand subcategories in the tree
2221       *
2222       * deeper subcategories may be loaded by AJAX or proceed to category page by clicking on category name
2223       *
2224       * @return int
2225       */
2226      public function get_subcat_depth() {
2227          return $this->subcatdepth;
2228      }
2229  
2230      /**
2231       * Sets options to display list of courses
2232       *
2233       * Options are later submitted as argument to coursecat::get_courses() and/or coursecat::search_courses()
2234       *
2235       * Options that coursecat::get_courses() accept:
2236       *    - recursive - return courses from subcategories as well. Use with care,
2237       *      this may be a huge list!
2238       *    - summary - preloads fields 'summary' and 'summaryformat'
2239       *    - coursecontacts - preloads course contacts
2240       *    - isenrolled - preloads indication whether this user is enrolled in the course
2241       *    - sort - list of fields to sort. Example
2242       *             array('idnumber' => 1, 'shortname' => 1, 'id' => -1)
2243       *             will sort by idnumber asc, shortname asc and id desc.
2244       *             Default: array('sortorder' => 1)
2245       *             Only cached fields may be used for sorting!
2246       *    - offset
2247       *    - limit - maximum number of children to return, 0 or null for no limit
2248       *
2249       * Options summary and coursecontacts are filled automatically in the set_show_courses()
2250       *
2251       * Also renderer can set here any additional options it wants to pass between renderer functions.
2252       *
2253       * @param array $options
2254       * @return coursecat_helper
2255       */
2256      public function set_courses_display_options($options) {
2257          $this->coursesdisplayoptions = $options;
2258          $this->set_show_courses($this->showcourses); // this will calculate special display options
2259          return $this;
2260      }
2261  
2262      /**
2263       * Sets one option to display list of courses
2264       *
2265       * @see coursecat_helper::set_courses_display_options()
2266       *
2267       * @param string $key
2268       * @param mixed $value
2269       * @return coursecat_helper
2270       */
2271      public function set_courses_display_option($key, $value) {
2272          $this->coursesdisplayoptions[$key] = $value;
2273          return $this;
2274      }
2275  
2276      /**
2277       * Return the specified option to display list of courses
2278       *
2279       * @param string $optionname option name
2280       * @param mixed $defaultvalue default value for option if it is not specified
2281       * @return mixed
2282       */
2283      public function get_courses_display_option($optionname, $defaultvalue = null) {
2284          if (array_key_exists($optionname, $this->coursesdisplayoptions)) {
2285              return $this->coursesdisplayoptions[$optionname];
2286          } else {
2287              return $defaultvalue;
2288          }
2289      }
2290  
2291      /**
2292       * Returns all options to display the courses
2293       *
2294       * This array is usually passed to {@link coursecat::get_courses()} or
2295       * {@link coursecat::search_courses()}
2296       *
2297       * @return array
2298       */
2299      public function get_courses_display_options() {
2300          return $this->coursesdisplayoptions;
2301      }
2302  
2303      /**
2304       * Sets options to display list of subcategories
2305       *
2306       * Options 'sort', 'offset' and 'limit' are passed to coursecat::get_children().
2307       * Any other options may be used by renderer functions
2308       *
2309       * @param array $options
2310       * @return coursecat_helper
2311       */
2312      public function set_categories_display_options($options) {
2313          $this->categoriesdisplayoptions = $options;
2314          return $this;
2315      }
2316  
2317      /**
2318       * Return the specified option to display list of subcategories
2319       *
2320       * @param string $optionname option name
2321       * @param mixed $defaultvalue default value for option if it is not specified
2322       * @return mixed
2323       */
2324      public function get_categories_display_option($optionname, $defaultvalue = null) {
2325          if (array_key_exists($optionname, $this->categoriesdisplayoptions)) {
2326              return $this->categoriesdisplayoptions[$optionname];
2327          } else {
2328              return $defaultvalue;
2329          }
2330      }
2331  
2332      /**
2333       * Returns all options to display list of subcategories
2334       *
2335       * This array is usually passed to {@link coursecat::get_children()}
2336       *
2337       * @return array
2338       */
2339      public function get_categories_display_options() {
2340          return $this->categoriesdisplayoptions;
2341      }
2342  
2343      /**
2344       * Sets additional general options to pass between renderer functions, usually HTML attributes
2345       *
2346       * @param array $attributes
2347       * @return coursecat_helper
2348       */
2349      public function set_attributes($attributes) {
2350          $this->attributes = $attributes;
2351          return $this;
2352      }
2353  
2354      /**
2355       * Return all attributes and erases them so they are not applied again
2356       *
2357       * @param string $classname adds additional class name to the beginning of $attributes['class']
2358       * @return array
2359       */
2360      public function get_and_erase_attributes($classname) {
2361          $attributes = $this->attributes;
2362          $this->attributes = array();
2363          if (empty($attributes['class'])) {
2364              $attributes['class'] = '';
2365          }
2366          $attributes['class'] = $classname . ' '. $attributes['class'];
2367          return $attributes;
2368      }
2369  
2370      /**
2371       * Sets the search criteria if the course is a search result
2372       *
2373       * Search string will be used to highlight terms in course name and description
2374       *
2375       * @param array $searchcriteria
2376       * @return coursecat_helper
2377       */
2378      public function set_search_criteria($searchcriteria) {
2379          $this->searchcriteria = $searchcriteria;
2380          return $this;
2381      }
2382  
2383      /**
2384       * Returns formatted and filtered description of the given category
2385       *
2386       * @param coursecat $coursecat category
2387       * @param stdClass|array $options format options, by default [noclean,overflowdiv],
2388       *     if context is not specified it will be added automatically
2389       * @return string|null
2390       */
2391      public function get_category_formatted_description($coursecat, $options = null) {
2392          if ($coursecat->id && !empty($coursecat->description)) {
2393              if (!isset($coursecat->descriptionformat)) {
2394                  $descriptionformat = FORMAT_MOODLE;
2395              } else {
2396                  $descriptionformat = $coursecat->descriptionformat;
2397              }
2398              if ($options === null) {
2399                  $options = array('noclean' => true, 'overflowdiv' => true);
2400              } else {
2401                  $options = (array)$options;
2402              }
2403              $context = context_coursecat::instance($coursecat->id);
2404              if (!isset($options['context'])) {
2405                  $options['context'] = $context;
2406              }
2407              $text = file_rewrite_pluginfile_urls($coursecat->description,
2408                      'pluginfile.php', $context->id, 'coursecat', 'description', null);
2409              return format_text($text, $descriptionformat, $options);
2410          }
2411          return null;
2412      }
2413  
2414      /**
2415       * Returns given course's summary with proper embedded files urls and formatted
2416       *
2417       * @param course_in_list $course
2418       * @param array|stdClass $options additional formatting options
2419       * @return string
2420       */
2421      public function get_course_formatted_summary($course, $options = array()) {
2422          global $CFG;
2423          require_once($CFG->libdir. '/filelib.php');
2424          if (!$course->has_summary()) {
2425              return '';
2426          }
2427          $options = (array)$options;
2428          $context = context_course::instance($course->id);
2429          if (!isset($options['context'])) {
2430              // TODO see MDL-38521
2431              // option 1 (current), page context - no code required
2432              // option 2, system context
2433              // $options['context'] = context_system::instance();
2434              // option 3, course context:
2435              // $options['context'] = $context;
2436              // option 4, course category context:
2437              // $options['context'] = $context->get_parent_context();
2438          }
2439          $summary = file_rewrite_pluginfile_urls($course->summary, 'pluginfile.php', $context->id, 'course', 'summary', null);
2440          $summary = format_text($summary, $course->summaryformat, $options, $course->id);
2441          if (!empty($this->searchcriteria['search'])) {
2442              $summary = highlight($this->searchcriteria['search'], $summary);
2443          }
2444          return $summary;
2445      }
2446  
2447      /**
2448       * Returns course name as it is configured to appear in courses lists formatted to course context
2449       *
2450       * @param course_in_list $course
2451       * @param array|stdClass $options additional formatting options
2452       * @return string
2453       */
2454      public function get_course_formatted_name($course, $options = array()) {
2455          $options = (array)$options;
2456          if (!isset($options['context'])) {
2457              $options['context'] = context_course::instance($course->id);
2458          }
2459          $name = format_string(get_course_display_name_for_list($course), true, $options);
2460          if (!empty($this->searchcriteria['search'])) {
2461              $name = highlight($this->searchcriteria['search'], $name);
2462          }
2463          return $name;
2464      }
2465  }


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1