[ Index ]

PHP Cross Reference of moodle-2.8

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


Generated: Fri Nov 28 20:29:05 2014 Cross-referenced by PHPXref 0.7.1