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