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