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