MDL-54623 mod_assign: Unit test for list_participants()
[moodle.git] / mod / glossary / lib.php
blob0f6c7cf7f0e292eae1963062ed1b6fa7fe852e1c
1 <?php
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.
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/>.
18 /**
19 * Library of functions and constants for module glossary
20 * (replace glossary with the name of your module and delete this line)
22 * @package mod_glossary
23 * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
24 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
26 require_once($CFG->libdir . '/completionlib.php');
28 define("GLOSSARY_SHOW_ALL_CATEGORIES", 0);
29 define("GLOSSARY_SHOW_NOT_CATEGORISED", -1);
31 define("GLOSSARY_NO_VIEW", -1);
32 define("GLOSSARY_STANDARD_VIEW", 0);
33 define("GLOSSARY_CATEGORY_VIEW", 1);
34 define("GLOSSARY_DATE_VIEW", 2);
35 define("GLOSSARY_AUTHOR_VIEW", 3);
36 define("GLOSSARY_ADDENTRY_VIEW", 4);
37 define("GLOSSARY_IMPORT_VIEW", 5);
38 define("GLOSSARY_EXPORT_VIEW", 6);
39 define("GLOSSARY_APPROVAL_VIEW", 7);
41 // Glossary tabs.
42 define('GLOSSARY_STANDARD', 'standard');
43 define('GLOSSARY_AUTHOR', 'author');
44 define('GLOSSARY_CATEGORY', 'category');
45 define('GLOSSARY_DATE', 'date');
47 // Glossary displayformats.
48 define('GLOSSARY_CONTINUOUS', 'continuous');
49 define('GLOSSARY_DICTIONARY', 'dictionary');
50 define('GLOSSARY_FULLWITHOUTAUTHOR', 'fullwithoutauthor');
52 /// STANDARD FUNCTIONS ///////////////////////////////////////////////////////////
53 /**
54 * @global object
55 * @param object $glossary
56 * @return int
58 function glossary_add_instance($glossary) {
59 global $DB;
60 /// Given an object containing all the necessary data,
61 /// (defined by the form in mod_form.php) this function
62 /// will create a new instance and return the id number
63 /// of the new instance.
65 if (empty($glossary->ratingtime) or empty($glossary->assessed)) {
66 $glossary->assesstimestart = 0;
67 $glossary->assesstimefinish = 0;
70 if (empty($glossary->globalglossary) ) {
71 $glossary->globalglossary = 0;
74 if (!has_capability('mod/glossary:manageentries', context_system::instance())) {
75 $glossary->globalglossary = 0;
78 $glossary->timecreated = time();
79 $glossary->timemodified = $glossary->timecreated;
81 //Check displayformat is a valid one
82 $formats = get_list_of_plugins('mod/glossary/formats','TEMPLATE');
83 if (!in_array($glossary->displayformat, $formats)) {
84 print_error('unknowformat', '', '', $glossary->displayformat);
87 $returnid = $DB->insert_record("glossary", $glossary);
88 $glossary->id = $returnid;
89 glossary_grade_item_update($glossary);
91 return $returnid;
94 /**
95 * Given an object containing all the necessary data,
96 * (defined by the form in mod_form.php) this function
97 * will update an existing instance with new data.
99 * @global object
100 * @global object
101 * @param object $glossary
102 * @return bool
104 function glossary_update_instance($glossary) {
105 global $CFG, $DB;
107 if (empty($glossary->globalglossary)) {
108 $glossary->globalglossary = 0;
111 if (!has_capability('mod/glossary:manageentries', context_system::instance())) {
112 // keep previous
113 unset($glossary->globalglossary);
116 $glossary->timemodified = time();
117 $glossary->id = $glossary->instance;
119 if (empty($glossary->ratingtime) or empty($glossary->assessed)) {
120 $glossary->assesstimestart = 0;
121 $glossary->assesstimefinish = 0;
124 //Check displayformat is a valid one
125 $formats = get_list_of_plugins('mod/glossary/formats','TEMPLATE');
126 if (!in_array($glossary->displayformat, $formats)) {
127 print_error('unknowformat', '', '', $glossary->displayformat);
130 $DB->update_record("glossary", $glossary);
131 if ($glossary->defaultapproval) {
132 $DB->execute("UPDATE {glossary_entries} SET approved = 1 where approved <> 1 and glossaryid = ?", array($glossary->id));
134 glossary_grade_item_update($glossary);
136 return true;
140 * Given an ID of an instance of this module,
141 * this function will permanently delete the instance
142 * and any data that depends on it.
144 * @global object
145 * @param int $id glossary id
146 * @return bool success
148 function glossary_delete_instance($id) {
149 global $DB, $CFG;
151 if (!$glossary = $DB->get_record('glossary', array('id'=>$id))) {
152 return false;
155 if (!$cm = get_coursemodule_from_instance('glossary', $id)) {
156 return false;
159 if (!$context = context_module::instance($cm->id, IGNORE_MISSING)) {
160 return false;
163 $fs = get_file_storage();
165 if ($glossary->mainglossary) {
166 // unexport entries
167 $sql = "SELECT ge.id, ge.sourceglossaryid, cm.id AS sourcecmid
168 FROM {glossary_entries} ge
169 JOIN {modules} m ON m.name = 'glossary'
170 JOIN {course_modules} cm ON (cm.module = m.id AND cm.instance = ge.sourceglossaryid)
171 WHERE ge.glossaryid = ? AND ge.sourceglossaryid > 0";
173 if ($exported = $DB->get_records_sql($sql, array($id))) {
174 foreach ($exported as $entry) {
175 $entry->glossaryid = $entry->sourceglossaryid;
176 $entry->sourceglossaryid = 0;
177 $newcontext = context_module::instance($entry->sourcecmid);
178 if ($oldfiles = $fs->get_area_files($context->id, 'mod_glossary', 'attachment', $entry->id)) {
179 foreach ($oldfiles as $oldfile) {
180 $file_record = new stdClass();
181 $file_record->contextid = $newcontext->id;
182 $fs->create_file_from_storedfile($file_record, $oldfile);
184 $fs->delete_area_files($context->id, 'mod_glossary', 'attachment', $entry->id);
185 $entry->attachment = '1';
186 } else {
187 $entry->attachment = '0';
189 $DB->update_record('glossary_entries', $entry);
192 } else {
193 // move exported entries to main glossary
194 $sql = "UPDATE {glossary_entries}
195 SET sourceglossaryid = 0
196 WHERE sourceglossaryid = ?";
197 $DB->execute($sql, array($id));
200 // Delete any dependent records
201 $entry_select = "SELECT id FROM {glossary_entries} WHERE glossaryid = ?";
202 $DB->delete_records_select('comments', "contextid=? AND commentarea=? AND itemid IN ($entry_select)", array($id, 'glossary_entry', $context->id));
203 $DB->delete_records_select('glossary_alias', "entryid IN ($entry_select)", array($id));
205 $category_select = "SELECT id FROM {glossary_categories} WHERE glossaryid = ?";
206 $DB->delete_records_select('glossary_entries_categories', "categoryid IN ($category_select)", array($id));
207 $DB->delete_records('glossary_categories', array('glossaryid'=>$id));
208 $DB->delete_records('glossary_entries', array('glossaryid'=>$id));
210 // delete all files
211 $fs->delete_area_files($context->id);
213 glossary_grade_item_delete($glossary);
215 $DB->delete_records('glossary', array('id'=>$id));
217 // Reset caches.
218 \mod_glossary\local\concept_cache::reset_glossary($glossary);
220 return true;
224 * Return a small object with summary information about what a
225 * user has done with a given particular instance of this module
226 * Used for user activity reports.
227 * $return->time = the time they did it
228 * $return->info = a short text description
230 * @param object $course
231 * @param object $user
232 * @param object $mod
233 * @param object $glossary
234 * @return object|null
236 function glossary_user_outline($course, $user, $mod, $glossary) {
237 global $CFG;
239 require_once("$CFG->libdir/gradelib.php");
240 $grades = grade_get_grades($course->id, 'mod', 'glossary', $glossary->id, $user->id);
241 if (empty($grades->items[0]->grades)) {
242 $grade = false;
243 } else {
244 $grade = reset($grades->items[0]->grades);
247 if ($entries = glossary_get_user_entries($glossary->id, $user->id)) {
248 $result = new stdClass();
249 $result->info = count($entries) . ' ' . get_string("entries", "glossary");
251 $lastentry = array_pop($entries);
252 $result->time = $lastentry->timemodified;
254 if ($grade) {
255 $result->info .= ', ' . get_string('grade') . ': ' . $grade->str_long_grade;
257 return $result;
258 } else if ($grade) {
259 $result = new stdClass();
260 $result->info = get_string('grade') . ': ' . $grade->str_long_grade;
262 //datesubmitted == time created. dategraded == time modified or time overridden
263 //if grade was last modified by the user themselves use date graded. Otherwise use date submitted
264 //TODO: move this copied & pasted code somewhere in the grades API. See MDL-26704
265 if ($grade->usermodified == $user->id || empty($grade->datesubmitted)) {
266 $result->time = $grade->dategraded;
267 } else {
268 $result->time = $grade->datesubmitted;
271 return $result;
273 return NULL;
277 * @global object
278 * @param int $glossaryid
279 * @param int $userid
280 * @return array
282 function glossary_get_user_entries($glossaryid, $userid) {
283 /// Get all the entries for a user in a glossary
284 global $DB;
286 return $DB->get_records_sql("SELECT e.*, u.firstname, u.lastname, u.email, u.picture
287 FROM {glossary} g, {glossary_entries} e, {user} u
288 WHERE g.id = ?
289 AND e.glossaryid = g.id
290 AND e.userid = ?
291 AND e.userid = u.id
292 ORDER BY e.timemodified ASC", array($glossaryid, $userid));
296 * Print a detailed representation of what a user has done with
297 * a given particular instance of this module, for user activity reports.
299 * @global object
300 * @param object $course
301 * @param object $user
302 * @param object $mod
303 * @param object $glossary
305 function glossary_user_complete($course, $user, $mod, $glossary) {
306 global $CFG, $OUTPUT;
307 require_once("$CFG->libdir/gradelib.php");
309 $grades = grade_get_grades($course->id, 'mod', 'glossary', $glossary->id, $user->id);
310 if (!empty($grades->items[0]->grades)) {
311 $grade = reset($grades->items[0]->grades);
312 echo $OUTPUT->container(get_string('grade').': '.$grade->str_long_grade);
313 if ($grade->str_feedback) {
314 echo $OUTPUT->container(get_string('feedback').': '.$grade->str_feedback);
318 if ($entries = glossary_get_user_entries($glossary->id, $user->id)) {
319 echo '<table width="95%" border="0"><tr><td>';
320 foreach ($entries as $entry) {
321 $cm = get_coursemodule_from_instance("glossary", $glossary->id, $course->id);
322 glossary_print_entry($course, $cm, $glossary, $entry,"","",0);
323 echo '<p>';
325 echo '</td></tr></table>';
330 * Returns all glossary entries since a given time for specified glossary
332 * @param array $activities sequentially indexed array of objects
333 * @param int $index
334 * @param int $timestart
335 * @param int $courseid
336 * @param int $cmid
337 * @param int $userid defaults to 0
338 * @param int $groupid defaults to 0
339 * @return void adds items into $activities and increases $index
341 function glossary_get_recent_mod_activity(&$activities, &$index, $timestart, $courseid, $cmid, $userid = 0, $groupid = 0) {
342 global $COURSE, $USER, $DB;
344 if ($COURSE->id == $courseid) {
345 $course = $COURSE;
346 } else {
347 $course = $DB->get_record('course', array('id' => $courseid));
350 $modinfo = get_fast_modinfo($course);
351 $cm = $modinfo->cms[$cmid];
352 $context = context_module::instance($cm->id);
354 if (!$cm->uservisible) {
355 return;
358 $viewfullnames = has_capability('moodle/site:viewfullnames', $context);
359 // Groups are not yet supported for glossary. See MDL-10728 .
361 $accessallgroups = has_capability('moodle/site:accessallgroups', $context);
362 $groupmode = groups_get_activity_groupmode($cm, $course);
365 $params['timestart'] = $timestart;
367 if ($userid) {
368 $userselect = "AND u.id = :userid";
369 $params['userid'] = $userid;
370 } else {
371 $userselect = '';
374 if ($groupid) {
375 $groupselect = 'AND gm.groupid = :groupid';
376 $groupjoin = 'JOIN {groups_members} gm ON gm.userid=u.id';
377 $params['groupid'] = $groupid;
378 } else {
379 $groupselect = '';
380 $groupjoin = '';
383 $approvedselect = "";
384 if (!has_capability('mod/glossary:approve', $context)) {
385 $approvedselect = " AND ge.approved = 1 ";
388 $params['timestart'] = $timestart;
389 $params['glossaryid'] = $cm->instance;
391 $ufields = user_picture::fields('u', null, 'userid');
392 $entries = $DB->get_records_sql("
393 SELECT ge.id AS entryid, ge.glossaryid, ge.concept, ge.definition, ge.approved,
394 ge.timemodified, $ufields
395 FROM {glossary_entries} ge
396 JOIN {user} u ON u.id = ge.userid
397 $groupjoin
398 WHERE ge.timemodified > :timestart
399 AND ge.glossaryid = :glossaryid
400 $approvedselect
401 $userselect
402 $groupselect
403 ORDER BY ge.timemodified ASC", $params);
405 if (!$entries) {
406 return;
409 foreach ($entries as $entry) {
410 // Groups are not yet supported for glossary. See MDL-10728 .
412 $usersgroups = null;
413 if ($entry->userid != $USER->id) {
414 if ($groupmode == SEPARATEGROUPS and !$accessallgroups) {
415 if (is_null($usersgroups)) {
416 $usersgroups = groups_get_all_groups($course->id, $entry->userid, $cm->groupingid);
417 if (is_array($usersgroups)) {
418 $usersgroups = array_keys($usersgroups);
419 } else {
420 $usersgroups = array();
423 if (!array_intersect($usersgroups, $modinfo->get_groups($cm->groupingid))) {
424 continue;
430 $tmpactivity = new stdClass();
431 $tmpactivity->user = user_picture::unalias($entry, null, 'userid');
432 $tmpactivity->user->fullname = fullname($tmpactivity->user, $viewfullnames);
433 $tmpactivity->type = 'glossary';
434 $tmpactivity->cmid = $cm->id;
435 $tmpactivity->glossaryid = $entry->glossaryid;
436 $tmpactivity->name = format_string($cm->name, true);
437 $tmpactivity->sectionnum = $cm->sectionnum;
438 $tmpactivity->timestamp = $entry->timemodified;
439 $tmpactivity->content = new stdClass();
440 $tmpactivity->content->entryid = $entry->entryid;
441 $tmpactivity->content->concept = $entry->concept;
442 $tmpactivity->content->definition = $entry->definition;
443 $tmpactivity->content->approved = $entry->approved;
445 $activities[$index++] = $tmpactivity;
448 return true;
452 * Outputs the glossary entry indicated by $activity
454 * @param object $activity the activity object the glossary resides in
455 * @param int $courseid the id of the course the glossary resides in
456 * @param bool $detail not used, but required for compatibilty with other modules
457 * @param int $modnames not used, but required for compatibilty with other modules
458 * @param bool $viewfullnames not used, but required for compatibilty with other modules
459 * @return void
461 function glossary_print_recent_mod_activity($activity, $courseid, $detail, $modnames, $viewfullnames) {
462 global $OUTPUT;
464 echo html_writer::start_tag('div', array('class'=>'glossary-activity clearfix'));
465 if (!empty($activity->user)) {
466 echo html_writer::tag('div', $OUTPUT->user_picture($activity->user, array('courseid'=>$courseid)),
467 array('class' => 'glossary-activity-picture'));
470 echo html_writer::start_tag('div', array('class'=>'glossary-activity-content'));
471 echo html_writer::start_tag('div', array('class'=>'glossary-activity-entry'));
473 if (isset($activity->content->approved) && !$activity->content->approved) {
474 $urlparams = array('g' => $activity->glossaryid, 'mode' => 'approval', 'hook' => $activity->content->concept);
475 $class = array('class' => 'dimmed_text');
476 } else {
477 $urlparams = array('g' => $activity->glossaryid, 'mode' => 'entry', 'hook' => $activity->content->entryid);
478 $class = array();
480 echo html_writer::link(new moodle_url('/mod/glossary/view.php', $urlparams),
481 strip_tags($activity->content->concept), $class);
482 echo html_writer::end_tag('div');
484 $url = new moodle_url('/user/view.php', array('course'=>$courseid, 'id'=>$activity->user->id));
485 $name = $activity->user->fullname;
486 $link = html_writer::link($url, $name, $class);
488 echo html_writer::start_tag('div', array('class'=>'user'));
489 echo $link .' - '. userdate($activity->timestamp);
490 echo html_writer::end_tag('div');
492 echo html_writer::end_tag('div');
494 echo html_writer::end_tag('div');
495 return;
498 * Given a course and a time, this module should find recent activity
499 * that has occurred in glossary activities and print it out.
500 * Return true if there was output, or false is there was none.
502 * @global object
503 * @global object
504 * @global object
505 * @param object $course
506 * @param object $viewfullnames
507 * @param int $timestart
508 * @return bool
510 function glossary_print_recent_activity($course, $viewfullnames, $timestart) {
511 global $CFG, $USER, $DB, $OUTPUT, $PAGE;
513 //TODO: use timestamp in approved field instead of changing timemodified when approving in 2.0
514 if (!defined('GLOSSARY_RECENT_ACTIVITY_LIMIT')) {
515 define('GLOSSARY_RECENT_ACTIVITY_LIMIT', 50);
517 $modinfo = get_fast_modinfo($course);
518 $ids = array();
520 foreach ($modinfo->cms as $cm) {
521 if ($cm->modname != 'glossary') {
522 continue;
524 if (!$cm->uservisible) {
525 continue;
527 $ids[$cm->instance] = $cm->id;
530 if (!$ids) {
531 return false;
534 // generate list of approval capabilities for all glossaries in the course.
535 $approvals = array();
536 foreach ($ids as $glinstanceid => $glcmid) {
537 $context = context_module::instance($glcmid);
538 if (has_capability('mod/glossary:view', $context)) {
539 // get records glossary entries that are approved if user has no capability to approve entries.
540 if (has_capability('mod/glossary:approve', $context)) {
541 $approvals[] = ' ge.glossaryid = :glsid'.$glinstanceid.' ';
542 } else {
543 $approvals[] = ' (ge.approved = 1 AND ge.glossaryid = :glsid'.$glinstanceid.') ';
545 $params['glsid'.$glinstanceid] = $glinstanceid;
549 if (count($approvals) == 0) {
550 return false;
552 $selectsql = 'SELECT ge.id, ge.concept, ge.approved, ge.timemodified, ge.glossaryid,
553 '.user_picture::fields('u',null,'userid');
554 $countsql = 'SELECT COUNT(*)';
556 $joins = array(' FROM {glossary_entries} ge ');
557 $joins[] = 'JOIN {user} u ON u.id = ge.userid ';
558 $fromsql = implode($joins, "\n");
560 $params['timestart'] = $timestart;
561 $clausesql = ' WHERE ge.timemodified > :timestart ';
563 if (count($approvals) > 0) {
564 $approvalsql = 'AND ('. implode($approvals, ' OR ') .') ';
565 } else {
566 $approvalsql = '';
568 $ordersql = 'ORDER BY ge.timemodified ASC';
569 $entries = $DB->get_records_sql($selectsql.$fromsql.$clausesql.$approvalsql.$ordersql, $params, 0, (GLOSSARY_RECENT_ACTIVITY_LIMIT+1));
571 if (empty($entries)) {
572 return false;
575 echo $OUTPUT->heading(get_string('newentries', 'glossary').':', 3);
576 $strftimerecent = get_string('strftimerecent');
577 $entrycount = 0;
578 foreach ($entries as $entry) {
579 if ($entrycount < GLOSSARY_RECENT_ACTIVITY_LIMIT) {
580 if ($entry->approved) {
581 $dimmed = '';
582 $urlparams = array('g' => $entry->glossaryid, 'mode' => 'entry', 'hook' => $entry->id);
583 } else {
584 $dimmed = ' dimmed_text';
585 $urlparams = array('id' => $ids[$entry->glossaryid], 'mode' => 'approval', 'hook' => format_text($entry->concept, true));
587 $link = new moodle_url($CFG->wwwroot.'/mod/glossary/view.php' , $urlparams);
588 echo '<div class="head'.$dimmed.'">';
589 echo '<div class="date">'.userdate($entry->timemodified, $strftimerecent).'</div>';
590 echo '<div class="name">'.fullname($entry, $viewfullnames).'</div>';
591 echo '</div>';
592 echo '<div class="info"><a href="'.$link.'">'.format_string($entry->concept, true).'</a></div>';
593 $entrycount += 1;
594 } else {
595 $numnewentries = $DB->count_records_sql($countsql.$joins[0].$clausesql.$approvalsql, $params);
596 echo '<div class="head"><div class="activityhead">'.get_string('andmorenewentries', 'glossary', $numnewentries - GLOSSARY_RECENT_ACTIVITY_LIMIT).'</div></div>';
597 break;
601 return true;
605 * @global object
606 * @param object $log
608 function glossary_log_info($log) {
609 global $DB;
611 return $DB->get_record_sql("SELECT e.*, u.firstname, u.lastname
612 FROM {glossary_entries} e, {user} u
613 WHERE e.id = ? AND u.id = ?", array($log->info, $log->userid));
617 * Function to be run periodically according to the moodle cron
618 * This function searches for things that need to be done, such
619 * as sending out mail, toggling flags etc ...
620 * @return bool
622 function glossary_cron () {
623 return true;
627 * Return grade for given user or all users.
629 * @param stdClass $glossary A glossary instance
630 * @param int $userid Optional user id, 0 means all users
631 * @return array An array of grades, false if none
633 function glossary_get_user_grades($glossary, $userid=0) {
634 global $CFG;
636 require_once($CFG->dirroot.'/rating/lib.php');
638 $ratingoptions = new stdClass;
640 //need these to work backwards to get a context id. Is there a better way to get contextid from a module instance?
641 $ratingoptions->modulename = 'glossary';
642 $ratingoptions->moduleid = $glossary->id;
643 $ratingoptions->component = 'mod_glossary';
644 $ratingoptions->ratingarea = 'entry';
646 $ratingoptions->userid = $userid;
647 $ratingoptions->aggregationmethod = $glossary->assessed;
648 $ratingoptions->scaleid = $glossary->scale;
649 $ratingoptions->itemtable = 'glossary_entries';
650 $ratingoptions->itemtableusercolumn = 'userid';
652 $rm = new rating_manager();
653 return $rm->get_user_grades($ratingoptions);
657 * Return rating related permissions
659 * @param int $contextid the context id
660 * @param string $component The component we want to get permissions for
661 * @param string $ratingarea The ratingarea that we want to get permissions for
662 * @return array an associative array of the user's rating permissions
664 function glossary_rating_permissions($contextid, $component, $ratingarea) {
665 if ($component != 'mod_glossary' || $ratingarea != 'entry') {
666 // We don't know about this component/ratingarea so just return null to get the
667 // default restrictive permissions.
668 return null;
670 $context = context::instance_by_id($contextid);
671 return array(
672 'view' => has_capability('mod/glossary:viewrating', $context),
673 'viewany' => has_capability('mod/glossary:viewanyrating', $context),
674 'viewall' => has_capability('mod/glossary:viewallratings', $context),
675 'rate' => has_capability('mod/glossary:rate', $context)
680 * Validates a submitted rating
681 * @param array $params submitted data
682 * context => object the context in which the rated items exists [required]
683 * component => The component for this module - should always be mod_forum [required]
684 * ratingarea => object the context in which the rated items exists [required]
685 * itemid => int the ID of the object being rated [required]
686 * scaleid => int the scale from which the user can select a rating. Used for bounds checking. [required]
687 * rating => int the submitted rating
688 * rateduserid => int the id of the user whose items have been rated. NOT the user who submitted the ratings. 0 to update all. [required]
689 * aggregation => int the aggregation method to apply when calculating grades ie RATING_AGGREGATE_AVERAGE [optional]
690 * @return boolean true if the rating is valid. Will throw rating_exception if not
692 function glossary_rating_validate($params) {
693 global $DB, $USER;
695 // Check the component is mod_forum
696 if ($params['component'] != 'mod_glossary') {
697 throw new rating_exception('invalidcomponent');
700 // Check the ratingarea is post (the only rating area in forum)
701 if ($params['ratingarea'] != 'entry') {
702 throw new rating_exception('invalidratingarea');
705 // Check the rateduserid is not the current user .. you can't rate your own posts
706 if ($params['rateduserid'] == $USER->id) {
707 throw new rating_exception('nopermissiontorate');
710 $glossarysql = "SELECT g.id as glossaryid, g.scale, g.course, e.userid as userid, e.approved, e.timecreated, g.assesstimestart, g.assesstimefinish
711 FROM {glossary_entries} e
712 JOIN {glossary} g ON e.glossaryid = g.id
713 WHERE e.id = :itemid";
714 $glossaryparams = array('itemid' => $params['itemid']);
715 $info = $DB->get_record_sql($glossarysql, $glossaryparams);
716 if (!$info) {
717 //item doesn't exist
718 throw new rating_exception('invaliditemid');
721 if ($info->scale != $params['scaleid']) {
722 //the scale being submitted doesnt match the one in the database
723 throw new rating_exception('invalidscaleid');
726 //check that the submitted rating is valid for the scale
728 // lower limit
729 if ($params['rating'] < 0 && $params['rating'] != RATING_UNSET_RATING) {
730 throw new rating_exception('invalidnum');
733 // upper limit
734 if ($info->scale < 0) {
735 //its a custom scale
736 $scalerecord = $DB->get_record('scale', array('id' => -$info->scale));
737 if ($scalerecord) {
738 $scalearray = explode(',', $scalerecord->scale);
739 if ($params['rating'] > count($scalearray)) {
740 throw new rating_exception('invalidnum');
742 } else {
743 throw new rating_exception('invalidscaleid');
745 } else if ($params['rating'] > $info->scale) {
746 //if its numeric and submitted rating is above maximum
747 throw new rating_exception('invalidnum');
750 //check the item we're rating was created in the assessable time window
751 if (!empty($info->assesstimestart) && !empty($info->assesstimefinish)) {
752 if ($info->timecreated < $info->assesstimestart || $info->timecreated > $info->assesstimefinish) {
753 throw new rating_exception('notavailable');
757 $cm = get_coursemodule_from_instance('glossary', $info->glossaryid, $info->course, false, MUST_EXIST);
758 $context = context_module::instance($cm->id, MUST_EXIST);
760 // if the supplied context doesnt match the item's context
761 if ($context->id != $params['context']->id) {
762 throw new rating_exception('invalidcontext');
765 return true;
769 * Update activity grades
771 * @category grade
772 * @param stdClass $glossary Null means all glossaries (with extra cmidnumber property)
773 * @param int $userid specific user only, 0 means all
774 * @param bool $nullifnone If true and the user has no grade then a grade item with rawgrade == null will be inserted
776 function glossary_update_grades($glossary=null, $userid=0, $nullifnone=true) {
777 global $CFG, $DB;
778 require_once($CFG->libdir.'/gradelib.php');
780 if (!$glossary->assessed) {
781 glossary_grade_item_update($glossary);
783 } else if ($grades = glossary_get_user_grades($glossary, $userid)) {
784 glossary_grade_item_update($glossary, $grades);
786 } else if ($userid and $nullifnone) {
787 $grade = new stdClass();
788 $grade->userid = $userid;
789 $grade->rawgrade = NULL;
790 glossary_grade_item_update($glossary, $grade);
792 } else {
793 glossary_grade_item_update($glossary);
798 * Create/update grade item for given glossary
800 * @category grade
801 * @param stdClass $glossary object with extra cmidnumber
802 * @param mixed $grades Optional array/object of grade(s); 'reset' means reset grades in gradebook
803 * @return int, 0 if ok, error code otherwise
805 function glossary_grade_item_update($glossary, $grades=NULL) {
806 global $CFG;
807 require_once($CFG->libdir.'/gradelib.php');
809 $params = array('itemname'=>$glossary->name, 'idnumber'=>$glossary->cmidnumber);
811 if (!$glossary->assessed or $glossary->scale == 0) {
812 $params['gradetype'] = GRADE_TYPE_NONE;
814 } else if ($glossary->scale > 0) {
815 $params['gradetype'] = GRADE_TYPE_VALUE;
816 $params['grademax'] = $glossary->scale;
817 $params['grademin'] = 0;
819 } else if ($glossary->scale < 0) {
820 $params['gradetype'] = GRADE_TYPE_SCALE;
821 $params['scaleid'] = -$glossary->scale;
824 if ($grades === 'reset') {
825 $params['reset'] = true;
826 $grades = NULL;
829 return grade_update('mod/glossary', $glossary->course, 'mod', 'glossary', $glossary->id, 0, $grades, $params);
833 * Delete grade item for given glossary
835 * @category grade
836 * @param object $glossary object
838 function glossary_grade_item_delete($glossary) {
839 global $CFG;
840 require_once($CFG->libdir.'/gradelib.php');
842 return grade_update('mod/glossary', $glossary->course, 'mod', 'glossary', $glossary->id, 0, NULL, array('deleted'=>1));
846 * @global object
847 * @param int $gloassryid
848 * @param int $scaleid
849 * @return bool
851 function glossary_scale_used ($glossaryid,$scaleid) {
852 //This function returns if a scale is being used by one glossary
853 global $DB;
855 $return = false;
857 $rec = $DB->get_record("glossary", array("id"=>$glossaryid, "scale"=>-$scaleid));
859 if (!empty($rec) && !empty($scaleid)) {
860 $return = true;
863 return $return;
867 * Checks if scale is being used by any instance of glossary
869 * This is used to find out if scale used anywhere
871 * @global object
872 * @param int $scaleid
873 * @return boolean True if the scale is used by any glossary
875 function glossary_scale_used_anywhere($scaleid) {
876 global $DB;
878 if ($scaleid and $DB->record_exists('glossary', array('scale'=>-$scaleid))) {
879 return true;
880 } else {
881 return false;
885 //////////////////////////////////////////////////////////////////////////////////////
886 /// Any other glossary functions go here. Each of them must have a name that
887 /// starts with glossary_
890 * This function return an array of valid glossary_formats records
891 * Everytime it's called, every existing format is checked, new formats
892 * are included if detected and old formats are deleted and any glossary
893 * using an invalid format is updated to the default (dictionary).
895 * @global object
896 * @global object
897 * @return array
899 function glossary_get_available_formats() {
900 global $CFG, $DB;
902 //Get available formats (plugin) and insert (if necessary) them into glossary_formats
903 $formats = get_list_of_plugins('mod/glossary/formats', 'TEMPLATE');
904 $pluginformats = array();
905 foreach ($formats as $format) {
906 //If the format file exists
907 if (file_exists($CFG->dirroot.'/mod/glossary/formats/'.$format.'/'.$format.'_format.php')) {
908 include_once($CFG->dirroot.'/mod/glossary/formats/'.$format.'/'.$format.'_format.php');
909 //If the function exists
910 if (function_exists('glossary_show_entry_'.$format)) {
911 //Acummulate it as a valid format
912 $pluginformats[] = $format;
913 //If the format doesn't exist in the table
914 if (!$rec = $DB->get_record('glossary_formats', array('name'=>$format))) {
915 //Insert the record in glossary_formats
916 $gf = new stdClass();
917 $gf->name = $format;
918 $gf->popupformatname = $format;
919 $gf->visible = 1;
920 $id = $DB->insert_record('glossary_formats', $gf);
921 $rec = $DB->get_record('glossary_formats', array('id' => $id));
924 if (empty($rec->showtabs)) {
925 glossary_set_default_visible_tabs($rec);
931 //Delete non_existent formats from glossary_formats table
932 $formats = $DB->get_records("glossary_formats");
933 foreach ($formats as $format) {
934 $todelete = false;
935 //If the format in DB isn't a valid previously detected format then delete the record
936 if (!in_array($format->name,$pluginformats)) {
937 $todelete = true;
940 if ($todelete) {
941 //Delete the format
942 $DB->delete_records('glossary_formats', array('name'=>$format->name));
943 //Reasign existing glossaries to default (dictionary) format
944 if ($glossaries = $DB->get_records('glossary', array('displayformat'=>$format->name))) {
945 foreach($glossaries as $glossary) {
946 $DB->set_field('glossary','displayformat','dictionary', array('id'=>$glossary->id));
952 //Now everything is ready in glossary_formats table
953 $formats = $DB->get_records("glossary_formats");
955 return $formats;
959 * @param bool $debug
960 * @param string $text
961 * @param int $br
963 function glossary_debug($debug,$text,$br=1) {
964 if ( $debug ) {
965 echo '<font color="red">' . $text . '</font>';
966 if ( $br ) {
967 echo '<br />';
974 * @global object
975 * @param int $glossaryid
976 * @param string $entrylist
977 * @param string $pivot
978 * @return array
980 function glossary_get_entries($glossaryid, $entrylist, $pivot = "") {
981 global $DB;
982 if ($pivot) {
983 $pivot .= ",";
986 return $DB->get_records_sql("SELECT $pivot id,userid,concept,definition,format
987 FROM {glossary_entries}
988 WHERE glossaryid = ?
989 AND id IN ($entrylist)", array($glossaryid));
993 * @global object
994 * @global object
995 * @param object $concept
996 * @param string $courseid
997 * @return array
999 function glossary_get_entries_search($concept, $courseid) {
1000 global $CFG, $DB;
1002 //Check if the user is an admin
1003 $bypassadmin = 1; //This means NO (by default)
1004 if (has_capability('moodle/course:viewhiddenactivities', context_system::instance())) {
1005 $bypassadmin = 0; //This means YES
1008 //Check if the user is a teacher
1009 $bypassteacher = 1; //This means NO (by default)
1010 if (has_capability('mod/glossary:manageentries', context_course::instance($courseid))) {
1011 $bypassteacher = 0; //This means YES
1014 $conceptlower = core_text::strtolower(trim($concept));
1016 $params = array('courseid1'=>$courseid, 'courseid2'=>$courseid, 'conceptlower'=>$conceptlower, 'concept'=>$concept);
1018 return $DB->get_records_sql("SELECT e.*, g.name as glossaryname, cm.id as cmid, cm.course as courseid
1019 FROM {glossary_entries} e, {glossary} g,
1020 {course_modules} cm, {modules} m
1021 WHERE m.name = 'glossary' AND
1022 cm.module = m.id AND
1023 (cm.visible = 1 OR cm.visible = $bypassadmin OR
1024 (cm.course = :courseid1 AND cm.visible = $bypassteacher)) AND
1025 g.id = cm.instance AND
1026 e.glossaryid = g.id AND
1027 ( (e.casesensitive != 0 AND LOWER(concept) = :conceptlower) OR
1028 (e.casesensitive = 0 and concept = :concept)) AND
1029 (g.course = :courseid2 OR g.globalglossary = 1) AND
1030 e.usedynalink != 0 AND
1031 g.usedynalink != 0", $params);
1035 * @global object
1036 * @global object
1037 * @param object $course
1038 * @param object $course
1039 * @param object $glossary
1040 * @param object $entry
1041 * @param string $mode
1042 * @param string $hook
1043 * @param int $printicons
1044 * @param int $displayformat
1045 * @param bool $printview
1046 * @return mixed
1048 function glossary_print_entry($course, $cm, $glossary, $entry, $mode='',$hook='',$printicons = 1, $displayformat = -1, $printview = false) {
1049 global $USER, $CFG;
1050 $return = false;
1051 if ( $displayformat < 0 ) {
1052 $displayformat = $glossary->displayformat;
1054 if ($entry->approved or ($USER->id == $entry->userid) or ($mode == 'approval' and !$entry->approved) ) {
1055 $formatfile = $CFG->dirroot.'/mod/glossary/formats/'.$displayformat.'/'.$displayformat.'_format.php';
1056 if ($printview) {
1057 $functionname = 'glossary_print_entry_'.$displayformat;
1058 } else {
1059 $functionname = 'glossary_show_entry_'.$displayformat;
1062 if (file_exists($formatfile)) {
1063 include_once($formatfile);
1064 if (function_exists($functionname)) {
1065 $return = $functionname($course, $cm, $glossary, $entry,$mode,$hook,$printicons);
1066 } else if ($printview) {
1067 //If the glossary_print_entry_XXXX function doesn't exist, print default (old) print format
1068 $return = glossary_print_entry_default($entry, $glossary, $cm);
1072 return $return;
1076 * Default (old) print format used if custom function doesn't exist in format
1078 * @param object $entry
1079 * @param object $glossary
1080 * @param object $cm
1081 * @return void Output is echo'd
1083 function glossary_print_entry_default ($entry, $glossary, $cm) {
1084 global $CFG;
1086 require_once($CFG->libdir . '/filelib.php');
1088 echo $OUTPUT->heading(strip_tags($entry->concept), 4);
1090 $definition = $entry->definition;
1092 $definition = '<span class="nolink">' . strip_tags($definition) . '</span>';
1094 $context = context_module::instance($cm->id);
1095 $definition = file_rewrite_pluginfile_urls($definition, 'pluginfile.php', $context->id, 'mod_glossary', 'entry', $entry->id);
1097 $options = new stdClass();
1098 $options->para = false;
1099 $options->trusted = $entry->definitiontrust;
1100 $options->context = $context;
1101 $options->overflowdiv = true;
1102 $definition = format_text($definition, $entry->definitionformat, $options);
1103 echo ($definition);
1104 echo '<br /><br />';
1108 * Print glossary concept/term as a heading &lt;h4>
1109 * @param object $entry
1111 function glossary_print_entry_concept($entry, $return=false) {
1112 global $OUTPUT;
1114 $text = $OUTPUT->heading(format_string($entry->concept), 4);
1115 if (!empty($entry->highlight)) {
1116 $text = highlight($entry->highlight, $text);
1119 if ($return) {
1120 return $text;
1121 } else {
1122 echo $text;
1128 * @global moodle_database DB
1129 * @param object $entry
1130 * @param object $glossary
1131 * @param object $cm
1133 function glossary_print_entry_definition($entry, $glossary, $cm) {
1134 global $GLOSSARY_EXCLUDEENTRY;
1136 $definition = $entry->definition;
1138 // Do not link self.
1139 $GLOSSARY_EXCLUDEENTRY = $entry->id;
1141 $context = context_module::instance($cm->id);
1142 $definition = file_rewrite_pluginfile_urls($definition, 'pluginfile.php', $context->id, 'mod_glossary', 'entry', $entry->id);
1144 $options = new stdClass();
1145 $options->para = false;
1146 $options->trusted = $entry->definitiontrust;
1147 $options->context = $context;
1148 $options->overflowdiv = true;
1150 $text = format_text($definition, $entry->definitionformat, $options);
1152 // Stop excluding concepts from autolinking
1153 unset($GLOSSARY_EXCLUDEENTRY);
1155 if (!empty($entry->highlight)) {
1156 $text = highlight($entry->highlight, $text);
1158 if (isset($entry->footer)) { // Unparsed footer info
1159 $text .= $entry->footer;
1161 echo $text;
1166 * @global object
1167 * @param object $course
1168 * @param object $cm
1169 * @param object $glossary
1170 * @param object $entry
1171 * @param string $mode
1172 * @param string $hook
1173 * @param string $type
1174 * @return string|void
1176 function glossary_print_entry_aliases($course, $cm, $glossary, $entry,$mode='',$hook='', $type = 'print') {
1177 global $DB;
1179 $return = '';
1180 if ( $aliases = $DB->get_records('glossary_alias', array('entryid'=>$entry->id))) {
1181 foreach ($aliases as $alias) {
1182 if (trim($alias->alias)) {
1183 if ($return == '') {
1184 $return = '<select id="keyword" style="font-size:8pt">';
1186 $return .= "<option>$alias->alias</option>";
1189 if ($return != '') {
1190 $return .= '</select>';
1193 if ($type == 'print') {
1194 echo $return;
1195 } else {
1196 return $return;
1202 * @global object
1203 * @global object
1204 * @global object
1205 * @param object $course
1206 * @param object $cm
1207 * @param object $glossary
1208 * @param object $entry
1209 * @param string $mode
1210 * @param string $hook
1211 * @param string $type
1212 * @return string|void
1214 function glossary_print_entry_icons($course, $cm, $glossary, $entry, $mode='',$hook='', $type = 'print') {
1215 global $USER, $CFG, $DB, $OUTPUT;
1217 $context = context_module::instance($cm->id);
1219 $output = false; //To decide if we must really return text in "return". Activate when needed only!
1220 $importedentry = ($entry->sourceglossaryid == $glossary->id);
1221 $ismainglossary = $glossary->mainglossary;
1224 $return = '<span class="commands">';
1225 // Differentiate links for each entry.
1226 $altsuffix = ': '.strip_tags(format_text($entry->concept));
1228 if (!$entry->approved) {
1229 $output = true;
1230 $return .= html_writer::tag('span', get_string('entryishidden','glossary'),
1231 array('class' => 'glossary-hidden-note'));
1234 if (has_capability('mod/glossary:approve', $context) && !$glossary->defaultapproval && $entry->approved) {
1235 $output = true;
1236 $return .= '<a class="action-icon" title="' . get_string('disapprove', 'glossary').
1237 '" href="approve.php?newstate=0&amp;eid='.$entry->id.'&amp;mode='.$mode.
1238 '&amp;hook='.urlencode($hook).'&amp;sesskey='.sesskey().
1239 '"><img src="'.$OUTPUT->pix_url('t/block').'" class="smallicon" alt="'.
1240 get_string('disapprove','glossary').$altsuffix.'" /></a>';
1243 $iscurrentuser = ($entry->userid == $USER->id);
1245 if (has_capability('mod/glossary:manageentries', $context) or (isloggedin() and has_capability('mod/glossary:write', $context) and $iscurrentuser)) {
1246 // only teachers can export entries so check it out
1247 if (has_capability('mod/glossary:export', $context) and !$ismainglossary and !$importedentry) {
1248 $mainglossary = $DB->get_record('glossary', array('mainglossary'=>1,'course'=>$course->id));
1249 if ( $mainglossary ) { // if there is a main glossary defined, allow to export the current entry
1250 $output = true;
1251 $return .= '<a class="action-icon" title="'.get_string('exporttomainglossary','glossary') . '" href="exportentry.php?id='.$entry->id.'&amp;prevmode='.$mode.'&amp;hook='.urlencode($hook).'"><img src="'.$OUTPUT->pix_url('export', 'glossary').'" class="smallicon" alt="'.get_string('exporttomainglossary','glossary').$altsuffix.'" /></a>';
1255 if ( $entry->sourceglossaryid ) {
1256 $icon = $OUTPUT->pix_url('minus', 'glossary'); // graphical metaphor (minus) for deleting an imported entry
1257 } else {
1258 $icon = $OUTPUT->pix_url('t/delete');
1261 //Decide if an entry is editable:
1262 // -It isn't a imported entry (so nobody can edit a imported (from secondary to main) entry)) and
1263 // -The user is teacher or he is a student with time permissions (edit period or editalways defined).
1264 $ineditperiod = ((time() - $entry->timecreated < $CFG->maxeditingtime) || $glossary->editalways);
1265 if ( !$importedentry and (has_capability('mod/glossary:manageentries', $context) or ($entry->userid == $USER->id and ($ineditperiod and has_capability('mod/glossary:write', $context))))) {
1266 $output = true;
1267 $return .= "<a class='action-icon' title=\"" . get_string("delete") . "\" href=\"deleteentry.php?id=$cm->id&amp;mode=delete&amp;entry=$entry->id&amp;prevmode=$mode&amp;hook=".urlencode($hook)."\"><img src=\"";
1268 $return .= $icon;
1269 $return .= "\" class=\"smallicon\" alt=\"" . get_string("delete") .$altsuffix."\" /></a>";
1271 $return .= "<a class='action-icon' title=\"" . get_string("edit") . "\" href=\"edit.php?cmid=$cm->id&amp;id=$entry->id&amp;mode=$mode&amp;hook=".urlencode($hook)."\"><img src=\"" . $OUTPUT->pix_url('t/edit') . "\" class=\"smallicon\" alt=\"" . get_string("edit") .$altsuffix. "\" /></a>";
1272 } elseif ( $importedentry ) {
1273 $return .= "<font size=\"-1\">" . get_string("exportedentry","glossary") . "</font>";
1276 if (!empty($CFG->enableportfolios) && (has_capability('mod/glossary:exportentry', $context) || ($iscurrentuser && has_capability('mod/glossary:exportownentry', $context)))) {
1277 require_once($CFG->libdir . '/portfoliolib.php');
1278 $button = new portfolio_add_button();
1279 $button->set_callback_options('glossary_entry_portfolio_caller', array('id' => $cm->id, 'entryid' => $entry->id), 'mod_glossary');
1281 $filecontext = $context;
1282 if ($entry->sourceglossaryid == $cm->instance) {
1283 if ($maincm = get_coursemodule_from_instance('glossary', $entry->glossaryid)) {
1284 $filecontext = context_module::instance($maincm->id);
1287 $fs = get_file_storage();
1288 if ($files = $fs->get_area_files($filecontext->id, 'mod_glossary', 'attachment', $entry->id, "timemodified", false)
1289 || $files = $fs->get_area_files($filecontext->id, 'mod_glossary', 'entry', $entry->id, "timemodified", false)) {
1291 $button->set_formats(PORTFOLIO_FORMAT_RICHHTML);
1292 } else {
1293 $button->set_formats(PORTFOLIO_FORMAT_PLAINHTML);
1296 $return .= $button->to_html(PORTFOLIO_ADD_ICON_LINK);
1298 $return .= '</span>';
1300 if (!empty($CFG->usecomments) && has_capability('mod/glossary:comment', $context) and $glossary->allowcomments) {
1301 require_once($CFG->dirroot . '/comment/lib.php');
1302 $cmt = new stdClass();
1303 $cmt->component = 'mod_glossary';
1304 $cmt->context = $context;
1305 $cmt->course = $course;
1306 $cmt->cm = $cm;
1307 $cmt->area = 'glossary_entry';
1308 $cmt->itemid = $entry->id;
1309 $cmt->showcount = true;
1310 $comment = new comment($cmt);
1311 $return .= '<div>'.$comment->output(true).'</div>';
1312 $output = true;
1315 //If we haven't calculated any REAL thing, delete result ($return)
1316 if (!$output) {
1317 $return = '';
1319 //Print or get
1320 if ($type == 'print') {
1321 echo $return;
1322 } else {
1323 return $return;
1328 * @param object $course
1329 * @param object $cm
1330 * @param object $glossary
1331 * @param object $entry
1332 * @param string $mode
1333 * @param object $hook
1334 * @param bool $printicons
1335 * @param bool $aliases
1336 * @return void
1338 function glossary_print_entry_lower_section($course, $cm, $glossary, $entry, $mode, $hook, $printicons, $aliases=true) {
1339 if ($aliases) {
1340 $aliases = glossary_print_entry_aliases($course, $cm, $glossary, $entry, $mode, $hook,'html');
1342 $icons = '';
1343 if ($printicons) {
1344 $icons = glossary_print_entry_icons($course, $cm, $glossary, $entry, $mode, $hook,'html');
1346 if ($aliases || $icons || !empty($entry->rating)) {
1347 echo '<table>';
1348 if ( $aliases ) {
1349 echo '<tr valign="top"><td class="aliases">' .
1350 '<label for="keyword">' . get_string('aliases','glossary').': </label>' .
1351 $aliases . '</td></tr>';
1353 if ($icons) {
1354 echo '<tr valign="top"><td class="icons">'.$icons.'</td></tr>';
1356 if (!empty($entry->rating)) {
1357 echo '<tr valign="top"><td class="ratings">';
1358 glossary_print_entry_ratings($course, $entry);
1359 echo '</td></tr>';
1361 echo '</table>';
1366 * Print the list of attachments for this glossary entry
1368 * @param object $entry
1369 * @param object $cm The coursemodule
1370 * @param string $format The format for this view (html, or text)
1371 * @param string $unused1 This parameter is no longer used
1372 * @param string $unused2 This parameter is no longer used
1374 function glossary_print_entry_attachment($entry, $cm, $format = null, $unused1 = null, $unused2 = null) {
1375 // Valid format values: html: The HTML link for the attachment is an icon; and
1376 // text: The HTML link for the attachment is text.
1377 if ($entry->attachment) {
1378 echo '<div class="attachments">';
1379 echo glossary_print_attachments($entry, $cm, $format);
1380 echo '</div>';
1382 if ($unused1) {
1383 debugging('The align parameter is deprecated, please use appropriate CSS instead', DEBUG_DEVELOPER);
1385 if ($unused2 !== null) {
1386 debugging('The insidetable parameter is deprecated, please use appropriate CSS instead', DEBUG_DEVELOPER);
1391 * @global object
1392 * @param object $cm
1393 * @param object $entry
1394 * @param string $mode
1395 * @param string $align
1396 * @param bool $insidetable
1398 function glossary_print_entry_approval($cm, $entry, $mode, $align="right", $insidetable=true) {
1399 global $CFG, $OUTPUT;
1401 if ($mode == 'approval' and !$entry->approved) {
1402 if ($insidetable) {
1403 echo '<table class="glossaryapproval" align="'.$align.'"><tr><td align="'.$align.'">';
1405 echo $OUTPUT->action_icon(
1406 new moodle_url('approve.php', array('eid' => $entry->id, 'mode' => $mode, 'sesskey' => sesskey())),
1407 new pix_icon('t/approve', get_string('approve','glossary'), '',
1408 array('class' => 'iconsmall', 'align' => $align))
1410 if ($insidetable) {
1411 echo '</td></tr></table>';
1417 * It returns all entries from all glossaries that matches the specified criteria
1418 * within a given $course. It performs an $extended search if necessary.
1419 * It restrict the search to only one $glossary if the $glossary parameter is set.
1421 * @global object
1422 * @global object
1423 * @param object $course
1424 * @param array $searchterms
1425 * @param int $extended
1426 * @param object $glossary
1427 * @return array
1429 function glossary_search($course, $searchterms, $extended = 0, $glossary = NULL) {
1430 global $CFG, $DB;
1432 if ( !$glossary ) {
1433 if ( $glossaries = $DB->get_records("glossary", array("course"=>$course->id)) ) {
1434 $glos = "";
1435 foreach ( $glossaries as $glossary ) {
1436 $glos .= "$glossary->id,";
1438 $glos = substr($glos,0,-1);
1440 } else {
1441 $glos = $glossary->id;
1444 if (!has_capability('mod/glossary:manageentries', context_course::instance($glossary->course))) {
1445 $glossarymodule = $DB->get_record("modules", array("name"=>"glossary"));
1446 $onlyvisible = " AND g.id = cm.instance AND cm.visible = 1 AND cm.module = $glossarymodule->id";
1447 $onlyvisibletable = ", {course_modules} cm";
1448 } else {
1450 $onlyvisible = "";
1451 $onlyvisibletable = "";
1454 if ($DB->sql_regex_supported()) {
1455 $REGEXP = $DB->sql_regex(true);
1456 $NOTREGEXP = $DB->sql_regex(false);
1459 $searchcond = array();
1460 $params = array();
1461 $i = 0;
1463 $concat = $DB->sql_concat('e.concept', "' '", 'e.definition');
1466 foreach ($searchterms as $searchterm) {
1467 $i++;
1469 $NOT = false; /// Initially we aren't going to perform NOT LIKE searches, only MSSQL and Oracle
1470 /// will use it to simulate the "-" operator with LIKE clause
1472 /// Under Oracle and MSSQL, trim the + and - operators and perform
1473 /// simpler LIKE (or NOT LIKE) queries
1474 if (!$DB->sql_regex_supported()) {
1475 if (substr($searchterm, 0, 1) == '-') {
1476 $NOT = true;
1478 $searchterm = trim($searchterm, '+-');
1481 // TODO: +- may not work for non latin languages
1483 if (substr($searchterm,0,1) == '+') {
1484 $searchterm = trim($searchterm, '+-');
1485 $searchterm = preg_quote($searchterm, '|');
1486 $searchcond[] = "$concat $REGEXP :ss$i";
1487 $params['ss'.$i] = "(^|[^a-zA-Z0-9])$searchterm([^a-zA-Z0-9]|$)";
1489 } else if (substr($searchterm,0,1) == "-") {
1490 $searchterm = trim($searchterm, '+-');
1491 $searchterm = preg_quote($searchterm, '|');
1492 $searchcond[] = "$concat $NOTREGEXP :ss$i";
1493 $params['ss'.$i] = "(^|[^a-zA-Z0-9])$searchterm([^a-zA-Z0-9]|$)";
1495 } else {
1496 $searchcond[] = $DB->sql_like($concat, ":ss$i", false, true, $NOT);
1497 $params['ss'.$i] = "%$searchterm%";
1501 if (empty($searchcond)) {
1502 $totalcount = 0;
1503 return array();
1506 $searchcond = implode(" AND ", $searchcond);
1508 $sql = "SELECT e.*
1509 FROM {glossary_entries} e, {glossary} g $onlyvisibletable
1510 WHERE $searchcond
1511 AND (e.glossaryid = g.id or e.sourceglossaryid = g.id) $onlyvisible
1512 AND g.id IN ($glos) AND e.approved <> 0";
1514 return $DB->get_records_sql($sql, $params);
1518 * @global object
1519 * @param array $searchterms
1520 * @param object $glossary
1521 * @param bool $extended
1522 * @return array
1524 function glossary_search_entries($searchterms, $glossary, $extended) {
1525 global $DB;
1527 $course = $DB->get_record("course", array("id"=>$glossary->course));
1528 return glossary_search($course,$searchterms,$extended,$glossary);
1532 * if return=html, then return a html string.
1533 * if return=text, then return a text-only string.
1534 * otherwise, print HTML for non-images, and return image HTML
1535 * if attachment is an image, $align set its aligment.
1537 * @global object
1538 * @global object
1539 * @param object $entry
1540 * @param object $cm
1541 * @param string $type html, txt, empty
1542 * @param string $unused This parameter is no longer used
1543 * @return string image string or nothing depending on $type param
1545 function glossary_print_attachments($entry, $cm, $type=NULL, $unused = null) {
1546 global $CFG, $DB, $OUTPUT;
1548 if (!$context = context_module::instance($cm->id, IGNORE_MISSING)) {
1549 return '';
1552 if ($entry->sourceglossaryid == $cm->instance) {
1553 if (!$maincm = get_coursemodule_from_instance('glossary', $entry->glossaryid)) {
1554 return '';
1556 $filecontext = context_module::instance($maincm->id);
1558 } else {
1559 $filecontext = $context;
1562 $strattachment = get_string('attachment', 'glossary');
1564 $fs = get_file_storage();
1566 $imagereturn = '';
1567 $output = '';
1569 if ($files = $fs->get_area_files($filecontext->id, 'mod_glossary', 'attachment', $entry->id, "timemodified", false)) {
1570 foreach ($files as $file) {
1571 $filename = $file->get_filename();
1572 $mimetype = $file->get_mimetype();
1573 $iconimage = $OUTPUT->pix_icon(file_file_icon($file), get_mimetype_description($file), 'moodle', array('class' => 'icon'));
1574 $path = file_encode_url($CFG->wwwroot.'/pluginfile.php', '/'.$context->id.'/mod_glossary/attachment/'.$entry->id.'/'.$filename);
1576 if ($type == 'html') {
1577 $output .= "<a href=\"$path\">$iconimage</a> ";
1578 $output .= "<a href=\"$path\">".s($filename)."</a>";
1579 $output .= "<br />";
1581 } else if ($type == 'text') {
1582 $output .= "$strattachment ".s($filename).":\n$path\n";
1584 } else {
1585 if (in_array($mimetype, array('image/gif', 'image/jpeg', 'image/png'))) {
1586 // Image attachments don't get printed as links
1587 $imagereturn .= "<br /><img src=\"$path\" alt=\"\" />";
1588 } else {
1589 $output .= "<a href=\"$path\">$iconimage</a> ";
1590 $output .= format_text("<a href=\"$path\">".s($filename)."</a>", FORMAT_HTML, array('context'=>$context));
1591 $output .= '<br />';
1597 if ($type) {
1598 return $output;
1599 } else {
1600 echo $output;
1601 return $imagereturn;
1605 ////////////////////////////////////////////////////////////////////////////////
1606 // File API //
1607 ////////////////////////////////////////////////////////////////////////////////
1610 * Lists all browsable file areas
1612 * @package mod_glossary
1613 * @category files
1614 * @param stdClass $course course object
1615 * @param stdClass $cm course module object
1616 * @param stdClass $context context object
1617 * @return array
1619 function glossary_get_file_areas($course, $cm, $context) {
1620 return array(
1621 'attachment' => get_string('areaattachment', 'mod_glossary'),
1622 'entry' => get_string('areaentry', 'mod_glossary'),
1627 * File browsing support for glossary module.
1629 * @param file_browser $browser
1630 * @param array $areas
1631 * @param stdClass $course
1632 * @param cm_info $cm
1633 * @param context $context
1634 * @param string $filearea
1635 * @param int $itemid
1636 * @param string $filepath
1637 * @param string $filename
1638 * @return file_info_stored file_info_stored instance or null if not found
1640 function glossary_get_file_info($browser, $areas, $course, $cm, $context, $filearea, $itemid, $filepath, $filename) {
1641 global $CFG, $DB, $USER;
1643 if ($context->contextlevel != CONTEXT_MODULE) {
1644 return null;
1647 if (!isset($areas[$filearea])) {
1648 return null;
1651 if (is_null($itemid)) {
1652 require_once($CFG->dirroot.'/mod/glossary/locallib.php');
1653 return new glossary_file_info_container($browser, $course, $cm, $context, $areas, $filearea);
1656 if (!$entry = $DB->get_record('glossary_entries', array('id' => $itemid))) {
1657 return null;
1660 if (!$glossary = $DB->get_record('glossary', array('id' => $cm->instance))) {
1661 return null;
1664 if ($glossary->defaultapproval and !$entry->approved and !has_capability('mod/glossary:approve', $context)) {
1665 return null;
1668 // this trickery here is because we need to support source glossary access
1669 if ($entry->glossaryid == $cm->instance) {
1670 $filecontext = $context;
1671 } else if ($entry->sourceglossaryid == $cm->instance) {
1672 if (!$maincm = get_coursemodule_from_instance('glossary', $entry->glossaryid)) {
1673 return null;
1675 $filecontext = context_module::instance($maincm->id);
1676 } else {
1677 return null;
1680 $fs = get_file_storage();
1681 $filepath = is_null($filepath) ? '/' : $filepath;
1682 $filename = is_null($filename) ? '.' : $filename;
1683 if (!($storedfile = $fs->get_file($filecontext->id, 'mod_glossary', $filearea, $itemid, $filepath, $filename))) {
1684 return null;
1687 // Checks to see if the user can manage files or is the owner.
1688 // TODO MDL-33805 - Do not use userid here and move the capability check above.
1689 if (!has_capability('moodle/course:managefiles', $context) && $storedfile->get_userid() != $USER->id) {
1690 return null;
1693 $urlbase = $CFG->wwwroot.'/pluginfile.php';
1695 return new file_info_stored($browser, $filecontext, $storedfile, $urlbase, s($entry->concept), true, true, false, false);
1699 * Serves the glossary attachments. Implements needed access control ;-)
1701 * @package mod_glossary
1702 * @category files
1703 * @param stdClass $course course object
1704 * @param stdClass $cm course module object
1705 * @param stdClsss $context context object
1706 * @param string $filearea file area
1707 * @param array $args extra arguments
1708 * @param bool $forcedownload whether or not force download
1709 * @param array $options additional options affecting the file serving
1710 * @return bool false if file not found, does not return if found - justsend the file
1712 function glossary_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload, array $options=array()) {
1713 global $CFG, $DB;
1715 if ($context->contextlevel != CONTEXT_MODULE) {
1716 return false;
1719 require_course_login($course, true, $cm);
1721 if ($filearea === 'attachment' or $filearea === 'entry') {
1722 $entryid = (int)array_shift($args);
1724 require_course_login($course, true, $cm);
1726 if (!$entry = $DB->get_record('glossary_entries', array('id'=>$entryid))) {
1727 return false;
1730 if (!$glossary = $DB->get_record('glossary', array('id'=>$cm->instance))) {
1731 return false;
1734 if ($glossary->defaultapproval and !$entry->approved and !has_capability('mod/glossary:approve', $context)) {
1735 return false;
1738 // this trickery here is because we need to support source glossary access
1740 if ($entry->glossaryid == $cm->instance) {
1741 $filecontext = $context;
1743 } else if ($entry->sourceglossaryid == $cm->instance) {
1744 if (!$maincm = get_coursemodule_from_instance('glossary', $entry->glossaryid)) {
1745 return false;
1747 $filecontext = context_module::instance($maincm->id);
1749 } else {
1750 return false;
1753 $relativepath = implode('/', $args);
1754 $fullpath = "/$filecontext->id/mod_glossary/$filearea/$entryid/$relativepath";
1756 $fs = get_file_storage();
1757 if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
1758 return false;
1761 // finally send the file
1762 send_stored_file($file, 0, 0, true, $options); // download MUST be forced - security!
1764 } else if ($filearea === 'export') {
1765 require_login($course, false, $cm);
1766 require_capability('mod/glossary:export', $context);
1768 if (!$glossary = $DB->get_record('glossary', array('id'=>$cm->instance))) {
1769 return false;
1772 $cat = array_shift($args);
1773 $cat = clean_param($cat, PARAM_ALPHANUM);
1775 $filename = clean_filename(strip_tags(format_string($glossary->name)).'.xml');
1776 $content = glossary_generate_export_file($glossary, NULL, $cat);
1778 send_file($content, $filename, 0, 0, true, true);
1781 return false;
1787 function glossary_print_tabbed_table_end() {
1788 echo "</div></div>";
1792 * @param object $cm
1793 * @param object $glossary
1794 * @param string $mode
1795 * @param string $hook
1796 * @param string $sortkey
1797 * @param string $sortorder
1799 function glossary_print_approval_menu($cm, $glossary,$mode, $hook, $sortkey = '', $sortorder = '') {
1800 if ($glossary->showalphabet) {
1801 echo '<div class="glossaryexplain">' . get_string("explainalphabet","glossary") . '</div><br />';
1803 glossary_print_special_links($cm, $glossary, $mode, $hook);
1805 glossary_print_alphabet_links($cm, $glossary, $mode, $hook,$sortkey, $sortorder);
1807 glossary_print_all_links($cm, $glossary, $mode, $hook);
1809 glossary_print_sorting_links($cm, $mode, 'CREATION', 'asc');
1812 * @param object $cm
1813 * @param object $glossary
1814 * @param string $hook
1815 * @param string $sortkey
1816 * @param string $sortorder
1818 function glossary_print_import_menu($cm, $glossary, $mode, $hook, $sortkey='', $sortorder = '') {
1819 echo '<div class="glossaryexplain">' . get_string("explainimport","glossary") . '</div>';
1823 * @param object $cm
1824 * @param object $glossary
1825 * @param string $hook
1826 * @param string $sortkey
1827 * @param string $sortorder
1829 function glossary_print_export_menu($cm, $glossary, $mode, $hook, $sortkey='', $sortorder = '') {
1830 echo '<div class="glossaryexplain">' . get_string("explainexport","glossary") . '</div>';
1833 * @param object $cm
1834 * @param object $glossary
1835 * @param string $hook
1836 * @param string $sortkey
1837 * @param string $sortorder
1839 function glossary_print_alphabet_menu($cm, $glossary, $mode, $hook, $sortkey='', $sortorder = '') {
1840 if ( $mode != 'date' ) {
1841 if ($glossary->showalphabet) {
1842 echo '<div class="glossaryexplain">' . get_string("explainalphabet","glossary") . '</div><br />';
1845 glossary_print_special_links($cm, $glossary, $mode, $hook);
1847 glossary_print_alphabet_links($cm, $glossary, $mode, $hook, $sortkey, $sortorder);
1849 glossary_print_all_links($cm, $glossary, $mode, $hook);
1850 } else {
1851 glossary_print_sorting_links($cm, $mode, $sortkey,$sortorder);
1856 * @param object $cm
1857 * @param object $glossary
1858 * @param string $hook
1859 * @param string $sortkey
1860 * @param string $sortorder
1862 function glossary_print_author_menu($cm, $glossary,$mode, $hook, $sortkey = '', $sortorder = '') {
1863 if ($glossary->showalphabet) {
1864 echo '<div class="glossaryexplain">' . get_string("explainalphabet","glossary") . '</div><br />';
1867 glossary_print_alphabet_links($cm, $glossary, $mode, $hook, $sortkey, $sortorder);
1868 glossary_print_all_links($cm, $glossary, $mode, $hook);
1869 glossary_print_sorting_links($cm, $mode, $sortkey,$sortorder);
1873 * @global object
1874 * @global object
1875 * @param object $cm
1876 * @param object $glossary
1877 * @param string $hook
1878 * @param object $category
1880 function glossary_print_categories_menu($cm, $glossary, $hook, $category) {
1881 global $CFG, $DB, $OUTPUT;
1883 $context = context_module::instance($cm->id);
1885 // Prepare format_string/text options
1886 $fmtoptions = array(
1887 'context' => $context);
1889 echo '<table border="0" width="100%">';
1890 echo '<tr>';
1892 echo '<td align="center" style="width:20%">';
1893 if (has_capability('mod/glossary:managecategories', $context)) {
1894 $options['id'] = $cm->id;
1895 $options['mode'] = 'cat';
1896 $options['hook'] = $hook;
1897 echo $OUTPUT->single_button(new moodle_url("editcategories.php", $options), get_string("editcategories","glossary"), "get");
1899 echo '</td>';
1901 echo '<td align="center" style="width:60%">';
1902 echo '<b>';
1904 $menu = array();
1905 $menu[GLOSSARY_SHOW_ALL_CATEGORIES] = get_string("allcategories","glossary");
1906 $menu[GLOSSARY_SHOW_NOT_CATEGORISED] = get_string("notcategorised","glossary");
1908 $categories = $DB->get_records("glossary_categories", array("glossaryid"=>$glossary->id), "name ASC");
1909 $selected = '';
1910 if ( $categories ) {
1911 foreach ($categories as $currentcategory) {
1912 $url = $currentcategory->id;
1913 if ( $category ) {
1914 if ($currentcategory->id == $category->id) {
1915 $selected = $url;
1918 $menu[$url] = format_string($currentcategory->name, true, $fmtoptions);
1921 if ( !$selected ) {
1922 $selected = GLOSSARY_SHOW_NOT_CATEGORISED;
1925 if ( $category ) {
1926 echo format_string($category->name, true, $fmtoptions);
1927 } else {
1928 if ( $hook == GLOSSARY_SHOW_NOT_CATEGORISED ) {
1930 echo get_string("entrieswithoutcategory","glossary");
1931 $selected = GLOSSARY_SHOW_NOT_CATEGORISED;
1933 } elseif ( $hook == GLOSSARY_SHOW_ALL_CATEGORIES ) {
1935 echo get_string("allcategories","glossary");
1936 $selected = GLOSSARY_SHOW_ALL_CATEGORIES;
1940 echo '</b></td>';
1941 echo '<td align="center" style="width:20%">';
1943 $select = new single_select(new moodle_url("/mod/glossary/view.php", array('id'=>$cm->id, 'mode'=>'cat')), 'hook', $menu, $selected, null, "catmenu");
1944 $select->set_label(get_string('categories', 'glossary'), array('class' => 'accesshide'));
1945 echo $OUTPUT->render($select);
1947 echo '</td>';
1948 echo '</tr>';
1950 echo '</table>';
1954 * @global object
1955 * @param object $cm
1956 * @param object $glossary
1957 * @param string $mode
1958 * @param string $hook
1960 function glossary_print_all_links($cm, $glossary, $mode, $hook) {
1961 global $CFG;
1962 if ( $glossary->showall) {
1963 $strallentries = get_string("allentries", "glossary");
1964 if ( $hook == 'ALL' ) {
1965 echo "<b>$strallentries</b>";
1966 } else {
1967 $strexplainall = strip_tags(get_string("explainall","glossary"));
1968 echo "<a title=\"$strexplainall\" href=\"$CFG->wwwroot/mod/glossary/view.php?id=$cm->id&amp;mode=$mode&amp;hook=ALL\">$strallentries</a>";
1974 * @global object
1975 * @param object $cm
1976 * @param object $glossary
1977 * @param string $mode
1978 * @param string $hook
1980 function glossary_print_special_links($cm, $glossary, $mode, $hook) {
1981 global $CFG;
1982 if ( $glossary->showspecial) {
1983 $strspecial = get_string("special", "glossary");
1984 if ( $hook == 'SPECIAL' ) {
1985 echo "<b>$strspecial</b> | ";
1986 } else {
1987 $strexplainspecial = strip_tags(get_string("explainspecial","glossary"));
1988 echo "<a title=\"$strexplainspecial\" href=\"$CFG->wwwroot/mod/glossary/view.php?id=$cm->id&amp;mode=$mode&amp;hook=SPECIAL\">$strspecial</a> | ";
1994 * @global object
1995 * @param object $glossary
1996 * @param string $mode
1997 * @param string $hook
1998 * @param string $sortkey
1999 * @param string $sortorder
2001 function glossary_print_alphabet_links($cm, $glossary, $mode, $hook, $sortkey, $sortorder) {
2002 global $CFG;
2003 if ( $glossary->showalphabet) {
2004 $alphabet = explode(",", get_string('alphabet', 'langconfig'));
2005 for ($i = 0; $i < count($alphabet); $i++) {
2006 if ( $hook == $alphabet[$i] and $hook) {
2007 echo "<b>$alphabet[$i]</b>";
2008 } else {
2009 echo "<a href=\"$CFG->wwwroot/mod/glossary/view.php?id=$cm->id&amp;mode=$mode&amp;hook=".urlencode($alphabet[$i])."&amp;sortkey=$sortkey&amp;sortorder=$sortorder\">$alphabet[$i]</a>";
2011 echo ' | ';
2017 * @global object
2018 * @param object $cm
2019 * @param string $mode
2020 * @param string $sortkey
2021 * @param string $sortorder
2023 function glossary_print_sorting_links($cm, $mode, $sortkey = '',$sortorder = '') {
2024 global $CFG, $OUTPUT;
2026 $asc = get_string("ascending","glossary");
2027 $desc = get_string("descending","glossary");
2028 $bopen = '<b>';
2029 $bclose = '</b>';
2031 $neworder = '';
2032 $currentorder = '';
2033 $currentsort = '';
2034 if ( $sortorder ) {
2035 if ( $sortorder == 'asc' ) {
2036 $currentorder = $asc;
2037 $neworder = '&amp;sortorder=desc';
2038 $newordertitle = get_string('changeto', 'glossary', $desc);
2039 } else {
2040 $currentorder = $desc;
2041 $neworder = '&amp;sortorder=asc';
2042 $newordertitle = get_string('changeto', 'glossary', $asc);
2044 $icon = " <img src=\"".$OUTPUT->pix_url($sortorder, 'glossary')."\" class=\"icon\" alt=\"$newordertitle\" />";
2045 } else {
2046 if ( $sortkey != 'CREATION' and $sortkey != 'UPDATE' and
2047 $sortkey != 'FIRSTNAME' and $sortkey != 'LASTNAME' ) {
2048 $icon = "";
2049 $newordertitle = $asc;
2050 } else {
2051 $newordertitle = $desc;
2052 $neworder = '&amp;sortorder=desc';
2053 $icon = ' <img src="'.$OUTPUT->pix_url('asc', 'glossary').'" class="icon" alt="'.$newordertitle.'" />';
2056 $ficon = '';
2057 $fneworder = '';
2058 $fbtag = '';
2059 $fendbtag = '';
2061 $sicon = '';
2062 $sneworder = '';
2064 $sbtag = '';
2065 $fbtag = '';
2066 $fendbtag = '';
2067 $sendbtag = '';
2069 $sendbtag = '';
2071 if ( $sortkey == 'CREATION' or $sortkey == 'FIRSTNAME' ) {
2072 $ficon = $icon;
2073 $fneworder = $neworder;
2074 $fordertitle = $newordertitle;
2075 $sordertitle = $asc;
2076 $fbtag = $bopen;
2077 $fendbtag = $bclose;
2078 } elseif ($sortkey == 'UPDATE' or $sortkey == 'LASTNAME') {
2079 $sicon = $icon;
2080 $sneworder = $neworder;
2081 $fordertitle = $asc;
2082 $sordertitle = $newordertitle;
2083 $sbtag = $bopen;
2084 $sendbtag = $bclose;
2085 } else {
2086 $fordertitle = $asc;
2087 $sordertitle = $asc;
2090 if ( $sortkey == 'CREATION' or $sortkey == 'UPDATE' ) {
2091 $forder = 'CREATION';
2092 $sorder = 'UPDATE';
2093 $fsort = get_string("sortbycreation", "glossary");
2094 $ssort = get_string("sortbylastupdate", "glossary");
2096 $currentsort = $fsort;
2097 if ($sortkey == 'UPDATE') {
2098 $currentsort = $ssort;
2100 $sort = get_string("sortchronogically", "glossary");
2101 } elseif ( $sortkey == 'FIRSTNAME' or $sortkey == 'LASTNAME') {
2102 $forder = 'FIRSTNAME';
2103 $sorder = 'LASTNAME';
2104 $fsort = get_string("firstname");
2105 $ssort = get_string("lastname");
2107 $currentsort = $fsort;
2108 if ($sortkey == 'LASTNAME') {
2109 $currentsort = $ssort;
2111 $sort = get_string("sortby", "glossary");
2113 $current = '<span class="accesshide">'.get_string('current', 'glossary', "$currentsort $currentorder").'</span>';
2114 echo "<br />$current $sort: $sbtag<a title=\"$ssort $sordertitle\" href=\"$CFG->wwwroot/mod/glossary/view.php?id=$cm->id&amp;sortkey=$sorder$sneworder&amp;mode=$mode\">$ssort$sicon</a>$sendbtag | ".
2115 "$fbtag<a title=\"$fsort $fordertitle\" href=\"$CFG->wwwroot/mod/glossary/view.php?id=$cm->id&amp;sortkey=$forder$fneworder&amp;mode=$mode\">$fsort$ficon</a>$fendbtag<br />";
2120 * @param object $entry0
2121 * @param object $entry1
2122 * @return int [-1 | 0 | 1]
2124 function glossary_sort_entries ( $entry0, $entry1 ) {
2126 if ( core_text::strtolower(ltrim($entry0->concept)) < core_text::strtolower(ltrim($entry1->concept)) ) {
2127 return -1;
2128 } elseif ( core_text::strtolower(ltrim($entry0->concept)) > core_text::strtolower(ltrim($entry1->concept)) ) {
2129 return 1;
2130 } else {
2131 return 0;
2137 * @global object
2138 * @global object
2139 * @global object
2140 * @param object $course
2141 * @param object $entry
2142 * @return bool
2144 function glossary_print_entry_ratings($course, $entry) {
2145 global $OUTPUT;
2146 if( !empty($entry->rating) ){
2147 echo $OUTPUT->render($entry->rating);
2153 * @global object
2154 * @global object
2155 * @global object
2156 * @param int $courseid
2157 * @param array $entries
2158 * @param int $displayformat
2160 function glossary_print_dynaentry($courseid, $entries, $displayformat = -1) {
2161 global $USER,$CFG, $DB;
2163 echo '<div class="boxaligncenter">';
2164 echo '<table class="glossarypopup" cellspacing="0"><tr>';
2165 echo '<td>';
2166 if ( $entries ) {
2167 foreach ( $entries as $entry ) {
2168 if (! $glossary = $DB->get_record('glossary', array('id'=>$entry->glossaryid))) {
2169 print_error('invalidid', 'glossary');
2171 if (! $course = $DB->get_record('course', array('id'=>$glossary->course))) {
2172 print_error('coursemisconf');
2174 if (!$cm = get_coursemodule_from_instance('glossary', $entry->glossaryid, $glossary->course) ) {
2175 print_error('invalidid', 'glossary');
2178 //If displayformat is present, override glossary->displayformat
2179 if ($displayformat < 0) {
2180 $dp = $glossary->displayformat;
2181 } else {
2182 $dp = $displayformat;
2185 //Get popupformatname
2186 $format = $DB->get_record('glossary_formats', array('name'=>$dp));
2187 $displayformat = $format->popupformatname;
2189 //Check displayformat variable and set to default if necessary
2190 if (!$displayformat) {
2191 $displayformat = 'dictionary';
2194 $formatfile = $CFG->dirroot.'/mod/glossary/formats/'.$displayformat.'/'.$displayformat.'_format.php';
2195 $functionname = 'glossary_show_entry_'.$displayformat;
2197 if (file_exists($formatfile)) {
2198 include_once($formatfile);
2199 if (function_exists($functionname)) {
2200 $functionname($course, $cm, $glossary, $entry,'','','','');
2205 echo '</td>';
2206 echo '</tr></table></div>';
2211 * @global object
2212 * @param array $entries
2213 * @param array $aliases
2214 * @param array $categories
2215 * @return string
2217 function glossary_generate_export_csv($entries, $aliases, $categories) {
2218 global $CFG;
2219 $csv = '';
2220 $delimiter = '';
2221 require_once($CFG->libdir . '/csvlib.class.php');
2222 $delimiter = csv_import_reader::get_delimiter('comma');
2223 $csventries = array(0 => array(get_string('concept', 'glossary'), get_string('definition', 'glossary')));
2224 $csvaliases = array(0 => array());
2225 $csvcategories = array(0 => array());
2226 $aliascount = 0;
2227 $categorycount = 0;
2229 foreach ($entries as $entry) {
2230 $thisaliasesentry = array();
2231 $thiscategoriesentry = array();
2232 $thiscsventry = array($entry->concept, nl2br($entry->definition));
2234 if (array_key_exists($entry->id, $aliases) && is_array($aliases[$entry->id])) {
2235 $thiscount = count($aliases[$entry->id]);
2236 if ($thiscount > $aliascount) {
2237 $aliascount = $thiscount;
2239 foreach ($aliases[$entry->id] as $alias) {
2240 $thisaliasesentry[] = trim($alias);
2243 if (array_key_exists($entry->id, $categories) && is_array($categories[$entry->id])) {
2244 $thiscount = count($categories[$entry->id]);
2245 if ($thiscount > $categorycount) {
2246 $categorycount = $thiscount;
2248 foreach ($categories[$entry->id] as $catentry) {
2249 $thiscategoriesentry[] = trim($catentry);
2252 $csventries[$entry->id] = $thiscsventry;
2253 $csvaliases[$entry->id] = $thisaliasesentry;
2254 $csvcategories[$entry->id] = $thiscategoriesentry;
2257 $returnstr = '';
2258 foreach ($csventries as $id => $row) {
2259 $aliasstr = '';
2260 $categorystr = '';
2261 if ($id == 0) {
2262 $aliasstr = get_string('alias', 'glossary');
2263 $categorystr = get_string('category', 'glossary');
2265 $row = array_merge($row, array_pad($csvaliases[$id], $aliascount, $aliasstr), array_pad($csvcategories[$id], $categorycount, $categorystr));
2266 $returnstr .= '"' . implode('"' . $delimiter . '"', $row) . '"' . "\n";
2268 return $returnstr;
2273 * @param object $glossary
2274 * @param string $ignored invalid parameter
2275 * @param int|string $hook
2276 * @return string
2278 function glossary_generate_export_file($glossary, $ignored = "", $hook = 0) {
2279 global $CFG, $DB;
2281 // Large exports are likely to take their time and memory.
2282 core_php_time_limit::raise();
2283 raise_memory_limit(MEMORY_EXTRA);
2285 $cm = get_coursemodule_from_instance('glossary', $glossary->id, $glossary->course);
2286 $context = context_module::instance($cm->id);
2288 $co = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
2290 $co .= glossary_start_tag("GLOSSARY",0,true);
2291 $co .= glossary_start_tag("INFO",1,true);
2292 $co .= glossary_full_tag("NAME",2,false,$glossary->name);
2293 $co .= glossary_full_tag("INTRO",2,false,$glossary->intro);
2294 $co .= glossary_full_tag("INTROFORMAT",2,false,$glossary->introformat);
2295 $co .= glossary_full_tag("ALLOWDUPLICATEDENTRIES",2,false,$glossary->allowduplicatedentries);
2296 $co .= glossary_full_tag("DISPLAYFORMAT",2,false,$glossary->displayformat);
2297 $co .= glossary_full_tag("SHOWSPECIAL",2,false,$glossary->showspecial);
2298 $co .= glossary_full_tag("SHOWALPHABET",2,false,$glossary->showalphabet);
2299 $co .= glossary_full_tag("SHOWALL",2,false,$glossary->showall);
2300 $co .= glossary_full_tag("ALLOWCOMMENTS",2,false,$glossary->allowcomments);
2301 $co .= glossary_full_tag("USEDYNALINK",2,false,$glossary->usedynalink);
2302 $co .= glossary_full_tag("DEFAULTAPPROVAL",2,false,$glossary->defaultapproval);
2303 $co .= glossary_full_tag("GLOBALGLOSSARY",2,false,$glossary->globalglossary);
2304 $co .= glossary_full_tag("ENTBYPAGE",2,false,$glossary->entbypage);
2305 $co .= glossary_xml_export_files('INTROFILES', 2, $context->id, 'intro', 0);
2307 if ( $entries = $DB->get_records("glossary_entries", array("glossaryid"=>$glossary->id))) {
2308 $co .= glossary_start_tag("ENTRIES",2,true);
2309 foreach ($entries as $entry) {
2310 $permissiongranted = 1;
2311 if ( $hook ) {
2312 switch ( $hook ) {
2313 case "ALL":
2314 case "SPECIAL":
2315 break;
2316 default:
2317 $permissiongranted = ($entry->concept[ strlen($hook)-1 ] == $hook);
2318 break;
2321 if ( $hook ) {
2322 switch ( $hook ) {
2323 case GLOSSARY_SHOW_ALL_CATEGORIES:
2324 break;
2325 case GLOSSARY_SHOW_NOT_CATEGORISED:
2326 $permissiongranted = !$DB->record_exists("glossary_entries_categories", array("entryid"=>$entry->id));
2327 break;
2328 default:
2329 $permissiongranted = $DB->record_exists("glossary_entries_categories", array("entryid"=>$entry->id, "categoryid"=>$hook));
2330 break;
2333 if ( $entry->approved and $permissiongranted ) {
2334 $co .= glossary_start_tag("ENTRY",3,true);
2335 $co .= glossary_full_tag("CONCEPT",4,false,trim($entry->concept));
2336 $co .= glossary_full_tag("DEFINITION",4,false,$entry->definition);
2337 $co .= glossary_full_tag("FORMAT",4,false,$entry->definitionformat); // note: use old name for BC reasons
2338 $co .= glossary_full_tag("USEDYNALINK",4,false,$entry->usedynalink);
2339 $co .= glossary_full_tag("CASESENSITIVE",4,false,$entry->casesensitive);
2340 $co .= glossary_full_tag("FULLMATCH",4,false,$entry->fullmatch);
2341 $co .= glossary_full_tag("TEACHERENTRY",4,false,$entry->teacherentry);
2343 if ( $aliases = $DB->get_records("glossary_alias", array("entryid"=>$entry->id))) {
2344 $co .= glossary_start_tag("ALIASES",4,true);
2345 foreach ($aliases as $alias) {
2346 $co .= glossary_start_tag("ALIAS",5,true);
2347 $co .= glossary_full_tag("NAME",6,false,trim($alias->alias));
2348 $co .= glossary_end_tag("ALIAS",5,true);
2350 $co .= glossary_end_tag("ALIASES",4,true);
2352 if ( $catentries = $DB->get_records("glossary_entries_categories", array("entryid"=>$entry->id))) {
2353 $co .= glossary_start_tag("CATEGORIES",4,true);
2354 foreach ($catentries as $catentry) {
2355 $category = $DB->get_record("glossary_categories", array("id"=>$catentry->categoryid));
2357 $co .= glossary_start_tag("CATEGORY",5,true);
2358 $co .= glossary_full_tag("NAME",6,false,$category->name);
2359 $co .= glossary_full_tag("USEDYNALINK",6,false,$category->usedynalink);
2360 $co .= glossary_end_tag("CATEGORY",5,true);
2362 $co .= glossary_end_tag("CATEGORIES",4,true);
2365 // Export files embedded in entries.
2366 $co .= glossary_xml_export_files('ENTRYFILES', 4, $context->id, 'entry', $entry->id);
2368 // Export attachments.
2369 $co .= glossary_xml_export_files('ATTACHMENTFILES', 4, $context->id, 'attachment', $entry->id);
2371 $co .= glossary_end_tag("ENTRY",3,true);
2374 $co .= glossary_end_tag("ENTRIES",2,true);
2379 $co .= glossary_end_tag("INFO",1,true);
2380 $co .= glossary_end_tag("GLOSSARY",0,true);
2382 return $co;
2384 /// Functions designed by Eloy Lafuente
2385 /// Functions to create, open and write header of the xml file
2388 * Read import file and convert to current charset
2390 * @global object
2391 * @param string $file
2392 * @return string
2394 function glossary_read_imported_file($file_content) {
2395 require_once "../../lib/xmlize.php";
2396 global $CFG;
2398 return xmlize($file_content, 0);
2402 * Return the xml start tag
2404 * @param string $tag
2405 * @param int $level
2406 * @param bool $endline
2407 * @return string
2409 function glossary_start_tag($tag,$level=0,$endline=false) {
2410 if ($endline) {
2411 $endchar = "\n";
2412 } else {
2413 $endchar = "";
2415 return str_repeat(" ",$level*2)."<".strtoupper($tag).">".$endchar;
2419 * Return the xml end tag
2420 * @param string $tag
2421 * @param int $level
2422 * @param bool $endline
2423 * @return string
2425 function glossary_end_tag($tag,$level=0,$endline=true) {
2426 if ($endline) {
2427 $endchar = "\n";
2428 } else {
2429 $endchar = "";
2431 return str_repeat(" ",$level*2)."</".strtoupper($tag).">".$endchar;
2435 * Return the start tag, the contents and the end tag
2437 * @global object
2438 * @param string $tag
2439 * @param int $level
2440 * @param bool $endline
2441 * @param string $content
2442 * @return string
2444 function glossary_full_tag($tag,$level=0,$endline=true,$content) {
2445 global $CFG;
2447 $st = glossary_start_tag($tag,$level,$endline);
2448 $co = preg_replace("/\r\n|\r/", "\n", s($content));
2449 $et = glossary_end_tag($tag,0,true);
2450 return $st.$co.$et;
2454 * Prepares file area to export as part of XML export
2456 * @param string $tag XML tag to use for the group
2457 * @param int $taglevel
2458 * @param int $contextid
2459 * @param string $filearea
2460 * @param int $itemid
2461 * @return string
2463 function glossary_xml_export_files($tag, $taglevel, $contextid, $filearea, $itemid) {
2464 $co = '';
2465 $fs = get_file_storage();
2466 if ($files = $fs->get_area_files(
2467 $contextid, 'mod_glossary', $filearea, $itemid, 'itemid,filepath,filename', false)) {
2468 $co .= glossary_start_tag($tag, $taglevel, true);
2469 foreach ($files as $file) {
2470 $co .= glossary_start_tag('FILE', $taglevel + 1, true);
2471 $co .= glossary_full_tag('FILENAME', $taglevel + 2, false, $file->get_filename());
2472 $co .= glossary_full_tag('FILEPATH', $taglevel + 2, false, $file->get_filepath());
2473 $co .= glossary_full_tag('CONTENTS', $taglevel + 2, false, base64_encode($file->get_content()));
2474 $co .= glossary_end_tag('FILE', $taglevel + 1);
2476 $co .= glossary_end_tag($tag, $taglevel);
2478 return $co;
2482 * Parses files from XML import and inserts them into file system
2484 * @param array $xmlparent parent element in parsed XML tree
2485 * @param string $tag
2486 * @param int $contextid
2487 * @param string $filearea
2488 * @param int $itemid
2489 * @return int
2491 function glossary_xml_import_files($xmlparent, $tag, $contextid, $filearea, $itemid) {
2492 $count = 0;
2493 if (isset($xmlparent[$tag][0]['#']['FILE'])) {
2494 $fs = get_file_storage();
2495 $files = $xmlparent[$tag][0]['#']['FILE'];
2496 foreach ($files as $file) {
2497 $filerecord = array(
2498 'contextid' => $contextid,
2499 'component' => 'mod_glossary',
2500 'filearea' => $filearea,
2501 'itemid' => $itemid,
2502 'filepath' => $file['#']['FILEPATH'][0]['#'],
2503 'filename' => $file['#']['FILENAME'][0]['#'],
2505 $content = $file['#']['CONTENTS'][0]['#'];
2506 $fs->create_file_from_string($filerecord, base64_decode($content));
2507 $count++;
2510 return $count;
2514 * How many unrated entries are in the given glossary for a given user?
2516 * @global moodle_database $DB
2517 * @param int $glossaryid
2518 * @param int $userid
2519 * @return int
2521 function glossary_count_unrated_entries($glossaryid, $userid) {
2522 global $DB;
2524 $sql = "SELECT COUNT('x') as num
2525 FROM {glossary_entries}
2526 WHERE glossaryid = :glossaryid AND
2527 userid <> :userid";
2528 $params = array('glossaryid' => $glossaryid, 'userid' => $userid);
2529 $entries = $DB->count_records_sql($sql, $params);
2531 if ($entries) {
2532 // We need to get the contextid for the glossaryid we have been given.
2533 $sql = "SELECT ctx.id
2534 FROM {context} ctx
2535 JOIN {course_modules} cm ON cm.id = ctx.instanceid
2536 JOIN {modules} m ON m.id = cm.module
2537 JOIN {glossary} g ON g.id = cm.instance
2538 WHERE ctx.contextlevel = :contextlevel AND
2539 m.name = 'glossary' AND
2540 g.id = :glossaryid";
2541 $contextid = $DB->get_field_sql($sql, array('glossaryid' => $glossaryid, 'contextlevel' => CONTEXT_MODULE));
2543 // Now we need to count the ratings that this user has made
2544 $sql = "SELECT COUNT('x') AS num
2545 FROM {glossary_entries} e
2546 JOIN {rating} r ON r.itemid = e.id
2547 WHERE e.glossaryid = :glossaryid AND
2548 r.userid = :userid AND
2549 r.component = 'mod_glossary' AND
2550 r.ratingarea = 'entry' AND
2551 r.contextid = :contextid";
2552 $params = array('glossaryid' => $glossaryid, 'userid' => $userid, 'contextid' => $contextid);
2553 $rated = $DB->count_records_sql($sql, $params);
2554 if ($rated) {
2555 // The number or enties minus the number or rated entries equals the number of unrated
2556 // entries
2557 if ($entries > $rated) {
2558 return $entries - $rated;
2559 } else {
2560 return 0; // Just in case there was a counting error
2562 } else {
2563 return (int)$entries;
2565 } else {
2566 return 0;
2572 * Returns the html code to represent any pagging bar. Paramenters are:
2574 * The function dinamically show the first and last pages, and "scroll" over pages.
2575 * Fully compatible with Moodle's print_paging_bar() function. Perhaps some day this
2576 * could replace the general one. ;-)
2578 * @param int $totalcount total number of records to be displayed
2579 * @param int $page page currently selected (0 based)
2580 * @param int $perpage number of records per page
2581 * @param string $baseurl url to link in each page, the string 'page=XX' will be added automatically.
2583 * @param int $maxpageallowed Optional maximum number of page allowed.
2584 * @param int $maxdisplay Optional maximum number of page links to show in the bar
2585 * @param string $separator Optional string to be used between pages in the bar
2586 * @param string $specialtext Optional string to be showed as an special link
2587 * @param string $specialvalue Optional value (page) to be used in the special link
2588 * @param bool $previousandnext Optional to decide if we want the previous and next links
2589 * @return string
2591 function glossary_get_paging_bar($totalcount, $page, $perpage, $baseurl, $maxpageallowed=99999, $maxdisplay=20, $separator="&nbsp;", $specialtext="", $specialvalue=-1, $previousandnext = true) {
2593 $code = '';
2595 $showspecial = false;
2596 $specialselected = false;
2598 //Check if we have to show the special link
2599 if (!empty($specialtext)) {
2600 $showspecial = true;
2602 //Check if we are with the special link selected
2603 if ($showspecial && $page == $specialvalue) {
2604 $specialselected = true;
2607 //If there are results (more than 1 page)
2608 if ($totalcount > $perpage) {
2609 $code .= "<div style=\"text-align:center\">";
2610 $code .= "<p>".get_string("page").":";
2612 $maxpage = (int)(($totalcount-1)/$perpage);
2614 //Lower and upper limit of page
2615 if ($page < 0) {
2616 $page = 0;
2618 if ($page > $maxpageallowed) {
2619 $page = $maxpageallowed;
2621 if ($page > $maxpage) {
2622 $page = $maxpage;
2625 //Calculate the window of pages
2626 $pagefrom = $page - ((int)($maxdisplay / 2));
2627 if ($pagefrom < 0) {
2628 $pagefrom = 0;
2630 $pageto = $pagefrom + $maxdisplay - 1;
2631 if ($pageto > $maxpageallowed) {
2632 $pageto = $maxpageallowed;
2634 if ($pageto > $maxpage) {
2635 $pageto = $maxpage;
2638 //Some movements can be necessary if don't see enought pages
2639 if ($pageto - $pagefrom < $maxdisplay - 1) {
2640 if ($pageto - $maxdisplay + 1 > 0) {
2641 $pagefrom = $pageto - $maxdisplay + 1;
2645 //Calculate first and last if necessary
2646 $firstpagecode = '';
2647 $lastpagecode = '';
2648 if ($pagefrom > 0) {
2649 $firstpagecode = "$separator<a href=\"{$baseurl}page=0\">1</a>";
2650 if ($pagefrom > 1) {
2651 $firstpagecode .= "$separator...";
2654 if ($pageto < $maxpage) {
2655 if ($pageto < $maxpage -1) {
2656 $lastpagecode = "$separator...";
2658 $lastpagecode .= "$separator<a href=\"{$baseurl}page=$maxpage\">".($maxpage+1)."</a>";
2661 //Previous
2662 if ($page > 0 && $previousandnext) {
2663 $pagenum = $page - 1;
2664 $code .= "&nbsp;(<a href=\"{$baseurl}page=$pagenum\">".get_string("previous")."</a>)&nbsp;";
2667 //Add first
2668 $code .= $firstpagecode;
2670 $pagenum = $pagefrom;
2672 //List of maxdisplay pages
2673 while ($pagenum <= $pageto) {
2674 $pagetoshow = $pagenum +1;
2675 if ($pagenum == $page && !$specialselected) {
2676 $code .= "$separator<b>$pagetoshow</b>";
2677 } else {
2678 $code .= "$separator<a href=\"{$baseurl}page=$pagenum\">$pagetoshow</a>";
2680 $pagenum++;
2683 //Add last
2684 $code .= $lastpagecode;
2686 //Next
2687 if ($page < $maxpage && $page < $maxpageallowed && $previousandnext) {
2688 $pagenum = $page + 1;
2689 $code .= "$separator(<a href=\"{$baseurl}page=$pagenum\">".get_string("next")."</a>)";
2692 //Add special
2693 if ($showspecial) {
2694 $code .= '<br />';
2695 if ($specialselected) {
2696 $code .= "<b>$specialtext</b>";
2697 } else {
2698 $code .= "$separator<a href=\"{$baseurl}page=$specialvalue\">$specialtext</a>";
2702 //End html
2703 $code .= "</p>";
2704 $code .= "</div>";
2707 return $code;
2711 * List the actions that correspond to a view of this module.
2712 * This is used by the participation report.
2714 * Note: This is not used by new logging system. Event with
2715 * crud = 'r' and edulevel = LEVEL_PARTICIPATING will
2716 * be considered as view action.
2718 * @return array
2720 function glossary_get_view_actions() {
2721 return array('view','view all','view entry');
2725 * List the actions that correspond to a post of this module.
2726 * This is used by the participation report.
2728 * Note: This is not used by new logging system. Event with
2729 * crud = ('c' || 'u' || 'd') and edulevel = LEVEL_PARTICIPATING
2730 * will be considered as post action.
2732 * @return array
2734 function glossary_get_post_actions() {
2735 return array('add category','add entry','approve entry','delete category','delete entry','edit category','update entry');
2740 * Implementation of the function for printing the form elements that control
2741 * whether the course reset functionality affects the glossary.
2742 * @param object $mform form passed by reference
2744 function glossary_reset_course_form_definition(&$mform) {
2745 $mform->addElement('header', 'glossaryheader', get_string('modulenameplural', 'glossary'));
2746 $mform->addElement('checkbox', 'reset_glossary_all', get_string('resetglossariesall','glossary'));
2748 $mform->addElement('select', 'reset_glossary_types', get_string('resetglossaries', 'glossary'),
2749 array('main'=>get_string('mainglossary', 'glossary'), 'secondary'=>get_string('secondaryglossary', 'glossary')), array('multiple' => 'multiple'));
2750 $mform->setAdvanced('reset_glossary_types');
2751 $mform->disabledIf('reset_glossary_types', 'reset_glossary_all', 'checked');
2753 $mform->addElement('checkbox', 'reset_glossary_notenrolled', get_string('deletenotenrolled', 'glossary'));
2754 $mform->disabledIf('reset_glossary_notenrolled', 'reset_glossary_all', 'checked');
2756 $mform->addElement('checkbox', 'reset_glossary_ratings', get_string('deleteallratings'));
2757 $mform->disabledIf('reset_glossary_ratings', 'reset_glossary_all', 'checked');
2759 $mform->addElement('checkbox', 'reset_glossary_comments', get_string('deleteallcomments'));
2760 $mform->disabledIf('reset_glossary_comments', 'reset_glossary_all', 'checked');
2764 * Course reset form defaults.
2765 * @return array
2767 function glossary_reset_course_form_defaults($course) {
2768 return array('reset_glossary_all'=>0, 'reset_glossary_ratings'=>1, 'reset_glossary_comments'=>1, 'reset_glossary_notenrolled'=>0);
2772 * Removes all grades from gradebook
2774 * @param int $courseid The ID of the course to reset
2775 * @param string $type The optional type of glossary. 'main', 'secondary' or ''
2777 function glossary_reset_gradebook($courseid, $type='') {
2778 global $DB;
2780 switch ($type) {
2781 case 'main' : $type = "AND g.mainglossary=1"; break;
2782 case 'secondary' : $type = "AND g.mainglossary=0"; break;
2783 default : $type = ""; //all
2786 $sql = "SELECT g.*, cm.idnumber as cmidnumber, g.course as courseid
2787 FROM {glossary} g, {course_modules} cm, {modules} m
2788 WHERE m.name='glossary' AND m.id=cm.module AND cm.instance=g.id AND g.course=? $type";
2790 if ($glossarys = $DB->get_records_sql($sql, array($courseid))) {
2791 foreach ($glossarys as $glossary) {
2792 glossary_grade_item_update($glossary, 'reset');
2797 * Actual implementation of the reset course functionality, delete all the
2798 * glossary responses for course $data->courseid.
2800 * @global object
2801 * @param $data the data submitted from the reset course.
2802 * @return array status array
2804 function glossary_reset_userdata($data) {
2805 global $CFG, $DB;
2806 require_once($CFG->dirroot.'/rating/lib.php');
2808 $componentstr = get_string('modulenameplural', 'glossary');
2809 $status = array();
2811 $allentriessql = "SELECT e.id
2812 FROM {glossary_entries} e
2813 JOIN {glossary} g ON e.glossaryid = g.id
2814 WHERE g.course = ?";
2816 $allglossariessql = "SELECT g.id
2817 FROM {glossary} g
2818 WHERE g.course = ?";
2820 $params = array($data->courseid);
2822 $fs = get_file_storage();
2824 $rm = new rating_manager();
2825 $ratingdeloptions = new stdClass;
2826 $ratingdeloptions->component = 'mod_glossary';
2827 $ratingdeloptions->ratingarea = 'entry';
2829 // delete entries if requested
2830 if (!empty($data->reset_glossary_all)
2831 or (!empty($data->reset_glossary_types) and in_array('main', $data->reset_glossary_types) and in_array('secondary', $data->reset_glossary_types))) {
2833 $params[] = 'glossary_entry';
2834 $DB->delete_records_select('comments', "itemid IN ($allentriessql) AND commentarea=?", $params);
2835 $DB->delete_records_select('glossary_alias', "entryid IN ($allentriessql)", $params);
2836 $DB->delete_records_select('glossary_entries', "glossaryid IN ($allglossariessql)", $params);
2838 // now get rid of all attachments
2839 if ($glossaries = $DB->get_records_sql($allglossariessql, $params)) {
2840 foreach ($glossaries as $glossaryid=>$unused) {
2841 if (!$cm = get_coursemodule_from_instance('glossary', $glossaryid)) {
2842 continue;
2844 $context = context_module::instance($cm->id);
2845 $fs->delete_area_files($context->id, 'mod_glossary', 'attachment');
2847 //delete ratings
2848 $ratingdeloptions->contextid = $context->id;
2849 $rm->delete_ratings($ratingdeloptions);
2853 // remove all grades from gradebook
2854 if (empty($data->reset_gradebook_grades)) {
2855 glossary_reset_gradebook($data->courseid);
2858 $status[] = array('component'=>$componentstr, 'item'=>get_string('resetglossariesall', 'glossary'), 'error'=>false);
2860 } else if (!empty($data->reset_glossary_types)) {
2861 $mainentriessql = "$allentriessql AND g.mainglossary=1";
2862 $secondaryentriessql = "$allentriessql AND g.mainglossary=0";
2864 $mainglossariessql = "$allglossariessql AND g.mainglossary=1";
2865 $secondaryglossariessql = "$allglossariessql AND g.mainglossary=0";
2867 if (in_array('main', $data->reset_glossary_types)) {
2868 $params[] = 'glossary_entry';
2869 $DB->delete_records_select('comments', "itemid IN ($mainentriessql) AND commentarea=?", $params);
2870 $DB->delete_records_select('glossary_entries', "glossaryid IN ($mainglossariessql)", $params);
2872 if ($glossaries = $DB->get_records_sql($mainglossariessql, $params)) {
2873 foreach ($glossaries as $glossaryid=>$unused) {
2874 if (!$cm = get_coursemodule_from_instance('glossary', $glossaryid)) {
2875 continue;
2877 $context = context_module::instance($cm->id);
2878 $fs->delete_area_files($context->id, 'mod_glossary', 'attachment');
2880 //delete ratings
2881 $ratingdeloptions->contextid = $context->id;
2882 $rm->delete_ratings($ratingdeloptions);
2886 // remove all grades from gradebook
2887 if (empty($data->reset_gradebook_grades)) {
2888 glossary_reset_gradebook($data->courseid, 'main');
2891 $status[] = array('component'=>$componentstr, 'item'=>get_string('resetglossaries', 'glossary').': '.get_string('mainglossary', 'glossary'), 'error'=>false);
2893 } else if (in_array('secondary', $data->reset_glossary_types)) {
2894 $params[] = 'glossary_entry';
2895 $DB->delete_records_select('comments', "itemid IN ($secondaryentriessql) AND commentarea=?", $params);
2896 $DB->delete_records_select('glossary_entries', "glossaryid IN ($secondaryglossariessql)", $params);
2897 // remove exported source flag from entries in main glossary
2898 $DB->execute("UPDATE {glossary_entries}
2899 SET sourceglossaryid=0
2900 WHERE glossaryid IN ($mainglossariessql)", $params);
2902 if ($glossaries = $DB->get_records_sql($secondaryglossariessql, $params)) {
2903 foreach ($glossaries as $glossaryid=>$unused) {
2904 if (!$cm = get_coursemodule_from_instance('glossary', $glossaryid)) {
2905 continue;
2907 $context = context_module::instance($cm->id);
2908 $fs->delete_area_files($context->id, 'mod_glossary', 'attachment');
2910 //delete ratings
2911 $ratingdeloptions->contextid = $context->id;
2912 $rm->delete_ratings($ratingdeloptions);
2916 // remove all grades from gradebook
2917 if (empty($data->reset_gradebook_grades)) {
2918 glossary_reset_gradebook($data->courseid, 'secondary');
2921 $status[] = array('component'=>$componentstr, 'item'=>get_string('resetglossaries', 'glossary').': '.get_string('secondaryglossary', 'glossary'), 'error'=>false);
2925 // remove entries by users not enrolled into course
2926 if (!empty($data->reset_glossary_notenrolled)) {
2927 $entriessql = "SELECT e.id, e.userid, e.glossaryid, u.id AS userexists, u.deleted AS userdeleted
2928 FROM {glossary_entries} e
2929 JOIN {glossary} g ON e.glossaryid = g.id
2930 LEFT JOIN {user} u ON e.userid = u.id
2931 WHERE g.course = ? AND e.userid > 0";
2933 $course_context = context_course::instance($data->courseid);
2934 $notenrolled = array();
2935 $rs = $DB->get_recordset_sql($entriessql, $params);
2936 if ($rs->valid()) {
2937 foreach ($rs as $entry) {
2938 if (array_key_exists($entry->userid, $notenrolled) or !$entry->userexists or $entry->userdeleted
2939 or !is_enrolled($course_context , $entry->userid)) {
2940 $DB->delete_records('comments', array('commentarea'=>'glossary_entry', 'itemid'=>$entry->id));
2941 $DB->delete_records('glossary_entries', array('id'=>$entry->id));
2943 if ($cm = get_coursemodule_from_instance('glossary', $entry->glossaryid)) {
2944 $context = context_module::instance($cm->id);
2945 $fs->delete_area_files($context->id, 'mod_glossary', 'attachment', $entry->id);
2947 //delete ratings
2948 $ratingdeloptions->contextid = $context->id;
2949 $rm->delete_ratings($ratingdeloptions);
2953 $status[] = array('component'=>$componentstr, 'item'=>get_string('deletenotenrolled', 'glossary'), 'error'=>false);
2955 $rs->close();
2958 // remove all ratings
2959 if (!empty($data->reset_glossary_ratings)) {
2960 //remove ratings
2961 if ($glossaries = $DB->get_records_sql($allglossariessql, $params)) {
2962 foreach ($glossaries as $glossaryid=>$unused) {
2963 if (!$cm = get_coursemodule_from_instance('glossary', $glossaryid)) {
2964 continue;
2966 $context = context_module::instance($cm->id);
2968 //delete ratings
2969 $ratingdeloptions->contextid = $context->id;
2970 $rm->delete_ratings($ratingdeloptions);
2974 // remove all grades from gradebook
2975 if (empty($data->reset_gradebook_grades)) {
2976 glossary_reset_gradebook($data->courseid);
2978 $status[] = array('component'=>$componentstr, 'item'=>get_string('deleteallratings'), 'error'=>false);
2981 // remove comments
2982 if (!empty($data->reset_glossary_comments)) {
2983 $params[] = 'glossary_entry';
2984 $DB->delete_records_select('comments', "itemid IN ($allentriessql) AND commentarea= ? ", $params);
2985 $status[] = array('component'=>$componentstr, 'item'=>get_string('deleteallcomments'), 'error'=>false);
2988 /// updating dates - shift may be negative too
2989 if ($data->timeshift) {
2990 shift_course_mod_dates('glossary', array('assesstimestart', 'assesstimefinish'), $data->timeshift, $data->courseid);
2991 $status[] = array('component'=>$componentstr, 'item'=>get_string('datechanged'), 'error'=>false);
2994 return $status;
2998 * Returns all other caps used in module
2999 * @return array
3001 function glossary_get_extra_capabilities() {
3002 return array('moodle/site:accessallgroups', 'moodle/site:viewfullnames', 'moodle/site:trustcontent', 'moodle/rating:view', 'moodle/rating:viewany', 'moodle/rating:viewall', 'moodle/rating:rate', 'moodle/comment:view', 'moodle/comment:post', 'moodle/comment:delete');
3006 * @param string $feature FEATURE_xx constant for requested feature
3007 * @return mixed True if module supports feature, null if doesn't know
3009 function glossary_supports($feature) {
3010 switch($feature) {
3011 case FEATURE_GROUPS: return false;
3012 case FEATURE_GROUPINGS: return false;
3013 case FEATURE_MOD_INTRO: return true;
3014 case FEATURE_COMPLETION_TRACKS_VIEWS: return true;
3015 case FEATURE_COMPLETION_HAS_RULES: return true;
3016 case FEATURE_GRADE_HAS_GRADE: return true;
3017 case FEATURE_GRADE_OUTCOMES: return true;
3018 case FEATURE_RATE: return true;
3019 case FEATURE_BACKUP_MOODLE2: return true;
3020 case FEATURE_SHOW_DESCRIPTION: return true;
3022 default: return null;
3027 * Obtains the automatic completion state for this glossary based on any conditions
3028 * in glossary settings.
3030 * @global object
3031 * @global object
3032 * @param object $course Course
3033 * @param object $cm Course-module
3034 * @param int $userid User ID
3035 * @param bool $type Type of comparison (or/and; can be used as return value if no conditions)
3036 * @return bool True if completed, false if not. (If no conditions, then return
3037 * value depends on comparison type)
3039 function glossary_get_completion_state($course,$cm,$userid,$type) {
3040 global $CFG, $DB;
3042 // Get glossary details
3043 if (!($glossary=$DB->get_record('glossary',array('id'=>$cm->instance)))) {
3044 throw new Exception("Can't find glossary {$cm->instance}");
3047 $result=$type; // Default return value
3049 if ($glossary->completionentries) {
3050 $value = $glossary->completionentries <=
3051 $DB->count_records('glossary_entries',array('glossaryid'=>$glossary->id, 'userid'=>$userid, 'approved'=>1));
3052 if ($type == COMPLETION_AND) {
3053 $result = $result && $value;
3054 } else {
3055 $result = $result || $value;
3059 return $result;
3062 function glossary_extend_navigation($navigation, $course, $module, $cm) {
3063 global $CFG, $DB;
3065 $displayformat = $DB->get_record('glossary_formats', array('name' => $module->displayformat));
3066 // Get visible tabs for the format and check if the menu needs to be displayed.
3067 $showtabs = glossary_get_visible_tabs($displayformat);
3069 foreach ($showtabs as $showtabkey => $showtabvalue) {
3071 switch($showtabvalue) {
3072 case GLOSSARY_STANDARD :
3073 $navigation->add(get_string('standardview', 'glossary'), new moodle_url('/mod/glossary/view.php',
3074 array('id' => $cm->id, 'mode' => 'letter')));
3075 break;
3076 case GLOSSARY_CATEGORY :
3077 $navigation->add(get_string('categoryview', 'glossary'), new moodle_url('/mod/glossary/view.php',
3078 array('id' => $cm->id, 'mode' => 'cat')));
3079 break;
3080 case GLOSSARY_DATE :
3081 $navigation->add(get_string('dateview', 'glossary'), new moodle_url('/mod/glossary/view.php',
3082 array('id' => $cm->id, 'mode' => 'date')));
3083 break;
3084 case GLOSSARY_AUTHOR :
3085 $navigation->add(get_string('authorview', 'glossary'), new moodle_url('/mod/glossary/view.php',
3086 array('id' => $cm->id, 'mode' => 'author')));
3087 break;
3093 * Adds module specific settings to the settings block
3095 * @param settings_navigation $settings The settings navigation object
3096 * @param navigation_node $glossarynode The node to add module settings to
3098 function glossary_extend_settings_navigation(settings_navigation $settings, navigation_node $glossarynode) {
3099 global $PAGE, $DB, $CFG, $USER;
3101 $mode = optional_param('mode', '', PARAM_ALPHA);
3102 $hook = optional_param('hook', 'ALL', PARAM_CLEAN);
3104 if (has_capability('mod/glossary:import', $PAGE->cm->context)) {
3105 $glossarynode->add(get_string('importentries', 'glossary'), new moodle_url('/mod/glossary/import.php', array('id'=>$PAGE->cm->id)));
3108 if (has_capability('mod/glossary:export', $PAGE->cm->context)) {
3109 $glossarynode->add(get_string('exportentries', 'glossary'), new moodle_url('/mod/glossary/export.php', array('id'=>$PAGE->cm->id, 'mode'=>$mode, 'hook'=>$hook)));
3112 if (has_capability('mod/glossary:approve', $PAGE->cm->context) && ($hiddenentries = $DB->count_records('glossary_entries', array('glossaryid'=>$PAGE->cm->instance, 'approved'=>0)))) {
3113 $glossarynode->add(get_string('waitingapproval', 'glossary'), new moodle_url('/mod/glossary/view.php', array('id'=>$PAGE->cm->id, 'mode'=>'approval')));
3116 if (has_capability('mod/glossary:write', $PAGE->cm->context)) {
3117 $glossarynode->add(get_string('addentry', 'glossary'), new moodle_url('/mod/glossary/edit.php', array('cmid'=>$PAGE->cm->id)));
3120 $glossary = $DB->get_record('glossary', array("id" => $PAGE->cm->instance));
3122 if (!empty($CFG->enablerssfeeds) && !empty($CFG->glossary_enablerssfeeds) && $glossary->rsstype && $glossary->rssarticles && has_capability('mod/glossary:view', $PAGE->cm->context)) {
3123 require_once("$CFG->libdir/rsslib.php");
3125 $string = get_string('rsstype','forum');
3127 $url = new moodle_url(rss_get_url($PAGE->cm->context->id, $USER->id, 'mod_glossary', $glossary->id));
3128 $glossarynode->add($string, $url, settings_navigation::TYPE_SETTING, null, null, new pix_icon('i/rss', ''));
3133 * Running addtional permission check on plugin, for example, plugins
3134 * may have switch to turn on/off comments option, this callback will
3135 * affect UI display, not like pluginname_comment_validate only throw
3136 * exceptions.
3137 * Capability check has been done in comment->check_permissions(), we
3138 * don't need to do it again here.
3140 * @package mod_glossary
3141 * @category comment
3143 * @param stdClass $comment_param {
3144 * context => context the context object
3145 * courseid => int course id
3146 * cm => stdClass course module object
3147 * commentarea => string comment area
3148 * itemid => int itemid
3150 * @return array
3152 function glossary_comment_permissions($comment_param) {
3153 return array('post'=>true, 'view'=>true);
3157 * Validate comment parameter before perform other comments actions
3159 * @package mod_glossary
3160 * @category comment
3162 * @param stdClass $comment_param {
3163 * context => context the context object
3164 * courseid => int course id
3165 * cm => stdClass course module object
3166 * commentarea => string comment area
3167 * itemid => int itemid
3169 * @return boolean
3171 function glossary_comment_validate($comment_param) {
3172 global $DB;
3173 // validate comment area
3174 if ($comment_param->commentarea != 'glossary_entry') {
3175 throw new comment_exception('invalidcommentarea');
3177 if (!$record = $DB->get_record('glossary_entries', array('id'=>$comment_param->itemid))) {
3178 throw new comment_exception('invalidcommentitemid');
3180 if ($record->sourceglossaryid && $record->sourceglossaryid == $comment_param->cm->instance) {
3181 $glossary = $DB->get_record('glossary', array('id'=>$record->sourceglossaryid));
3182 } else {
3183 $glossary = $DB->get_record('glossary', array('id'=>$record->glossaryid));
3185 if (!$glossary) {
3186 throw new comment_exception('invalidid', 'data');
3188 if (!$course = $DB->get_record('course', array('id'=>$glossary->course))) {
3189 throw new comment_exception('coursemisconf');
3191 if (!$cm = get_coursemodule_from_instance('glossary', $glossary->id, $course->id)) {
3192 throw new comment_exception('invalidcoursemodule');
3194 $context = context_module::instance($cm->id);
3196 if ($glossary->defaultapproval and !$record->approved and !has_capability('mod/glossary:approve', $context)) {
3197 throw new comment_exception('notapproved', 'glossary');
3199 // validate context id
3200 if ($context->id != $comment_param->context->id) {
3201 throw new comment_exception('invalidcontext');
3203 // validation for comment deletion
3204 if (!empty($comment_param->commentid)) {
3205 if ($comment = $DB->get_record('comments', array('id'=>$comment_param->commentid))) {
3206 if ($comment->commentarea != 'glossary_entry') {
3207 throw new comment_exception('invalidcommentarea');
3209 if ($comment->contextid != $comment_param->context->id) {
3210 throw new comment_exception('invalidcontext');
3212 if ($comment->itemid != $comment_param->itemid) {
3213 throw new comment_exception('invalidcommentitemid');
3215 } else {
3216 throw new comment_exception('invalidcommentid');
3219 return true;
3223 * Return a list of page types
3224 * @param string $pagetype current page type
3225 * @param stdClass $parentcontext Block's parent context
3226 * @param stdClass $currentcontext Current context of block
3228 function glossary_page_type_list($pagetype, $parentcontext, $currentcontext) {
3229 $module_pagetype = array(
3230 'mod-glossary-*'=>get_string('page-mod-glossary-x', 'glossary'),
3231 'mod-glossary-view'=>get_string('page-mod-glossary-view', 'glossary'),
3232 'mod-glossary-edit'=>get_string('page-mod-glossary-edit', 'glossary'));
3233 return $module_pagetype;
3237 * Return list of all glossary tabs.
3238 * @throws coding_exception
3239 * @return array
3241 function glossary_get_all_tabs() {
3243 return array (
3244 GLOSSARY_AUTHOR => get_string('authorview', 'glossary'),
3245 GLOSSARY_CATEGORY => get_string('categoryview', 'glossary'),
3246 GLOSSARY_DATE => get_string('dateview', 'glossary')
3251 * Set 'showtabs' value for glossary formats
3252 * @param stdClass $glossaryformat record from 'glossary_formats' table
3254 function glossary_set_default_visible_tabs($glossaryformat) {
3255 global $DB;
3257 switch($glossaryformat->name) {
3258 case GLOSSARY_CONTINUOUS:
3259 $showtabs = 'standard,category,date';
3260 break;
3261 case GLOSSARY_DICTIONARY:
3262 $showtabs = 'standard';
3263 // Special code for upgraded instances that already had categories set up
3264 // in this format - enable "category" tab.
3265 // In new instances only 'standard' tab will be visible.
3266 if ($DB->record_exists_sql("SELECT 1
3267 FROM {glossary} g, {glossary_categories} gc
3268 WHERE g.id = gc.glossaryid and g.displayformat = ?",
3269 array(GLOSSARY_DICTIONARY))) {
3270 $showtabs .= ',category';
3272 break;
3273 case GLOSSARY_FULLWITHOUTAUTHOR:
3274 $showtabs = 'standard,category,date';
3275 break;
3276 default:
3277 $showtabs = 'standard,category,date,author';
3278 break;
3281 $DB->set_field('glossary_formats', 'showtabs', $showtabs, array('id' => $glossaryformat->id));
3282 $glossaryformat->showtabs = $showtabs;
3286 * Convert 'showtabs' string to array
3287 * @param stdClass $displayformat record from 'glossary_formats' table
3288 * @return array
3290 function glossary_get_visible_tabs($displayformat) {
3291 if (empty($displayformat->showtabs)) {
3292 glossary_set_default_visible_tabs($displayformat);
3294 $showtabs = preg_split('/,/', $displayformat->showtabs, -1, PREG_SPLIT_NO_EMPTY);
3296 return $showtabs;
3300 * Notify that the glossary was viewed.
3302 * This will trigger relevant events and activity completion.
3304 * @param stdClass $glossary The glossary object.
3305 * @param stdClass $course The course object.
3306 * @param stdClass $cm The course module object.
3307 * @param stdClass $context The context object.
3308 * @param string $mode The mode in which the glossary was viewed.
3309 * @since Moodle 3.1
3311 function glossary_view($glossary, $course, $cm, $context, $mode) {
3313 // Completion trigger.
3314 $completion = new completion_info($course);
3315 $completion->set_module_viewed($cm);
3317 // Trigger the course module viewed event.
3318 $event = \mod_glossary\event\course_module_viewed::create(array(
3319 'objectid' => $glossary->id,
3320 'context' => $context,
3321 'other' => array('mode' => $mode)
3323 $event->add_record_snapshot('course', $course);
3324 $event->add_record_snapshot('course_modules', $cm);
3325 $event->add_record_snapshot('glossary', $glossary);
3326 $event->trigger();
3330 * Notify that a glossary entry was viewed.
3332 * This will trigger relevant events.
3334 * @param stdClass $entry The entry object.
3335 * @param stdClass $context The context object.
3336 * @since Moodle 3.1
3338 function glossary_entry_view($entry, $context) {
3340 // Trigger the entry viewed event.
3341 $event = \mod_glossary\event\entry_viewed::create(array(
3342 'objectid' => $entry->id,
3343 'context' => $context
3345 $event->add_record_snapshot('glossary_entries', $entry);
3346 $event->trigger();
3351 * Returns the entries of a glossary by letter.
3353 * @param object $glossary The glossary.
3354 * @param context $context The context of the glossary.
3355 * @param string $letter The letter, or ALL, or SPECIAL.
3356 * @param int $from Fetch records from.
3357 * @param int $limit Number of records to fetch.
3358 * @param array $options Accepts:
3359 * - (bool) includenotapproved. When false, includes the non-approved entries created by
3360 * the current user. When true, also includes the ones that the user has the permission to approve.
3361 * @return array The first element being the recordset, the second the number of entries.
3362 * @since Moodle 3.1
3364 function glossary_get_entries_by_letter($glossary, $context, $letter, $from, $limit, $options = array()) {
3366 $qb = new mod_glossary_entry_query_builder($glossary);
3367 if ($letter != 'ALL' && $letter != 'SPECIAL' && core_text::strlen($letter)) {
3368 $qb->filter_by_concept_letter($letter);
3370 if ($letter == 'SPECIAL') {
3371 $qb->filter_by_concept_non_letter();
3374 if (!empty($options['includenotapproved']) && has_capability('mod/glossary:approve', $context)) {
3375 $qb->filter_by_non_approved(mod_glossary_entry_query_builder::NON_APPROVED_ALL);
3376 } else {
3377 $qb->filter_by_non_approved(mod_glossary_entry_query_builder::NON_APPROVED_SELF);
3380 $qb->add_field('*', 'entries');
3381 $qb->join_user();
3382 $qb->add_user_fields();
3383 $qb->order_by('concept', 'entries');
3384 $qb->order_by('id', 'entries', 'ASC'); // Sort on ID to avoid random ordering when entries share an ordering value.
3385 $qb->limit($from, $limit);
3387 // Fetching the entries.
3388 $count = $qb->count_records();
3389 $entries = $qb->get_records();
3391 return array($entries, $count);
3395 * Returns the entries of a glossary by date.
3397 * @param object $glossary The glossary.
3398 * @param context $context The context of the glossary.
3399 * @param string $order The mode of ordering: CREATION or UPDATE.
3400 * @param string $sort The direction of the ordering: ASC or DESC.
3401 * @param int $from Fetch records from.
3402 * @param int $limit Number of records to fetch.
3403 * @param array $options Accepts:
3404 * - (bool) includenotapproved. When false, includes the non-approved entries created by
3405 * the current user. When true, also includes the ones that the user has the permission to approve.
3406 * @return array The first element being the recordset, the second the number of entries.
3407 * @since Moodle 3.1
3409 function glossary_get_entries_by_date($glossary, $context, $order, $sort, $from, $limit, $options = array()) {
3411 $qb = new mod_glossary_entry_query_builder($glossary);
3412 if (!empty($options['includenotapproved']) && has_capability('mod/glossary:approve', $context)) {
3413 $qb->filter_by_non_approved(mod_glossary_entry_query_builder::NON_APPROVED_ALL);
3414 } else {
3415 $qb->filter_by_non_approved(mod_glossary_entry_query_builder::NON_APPROVED_SELF);
3418 $qb->add_field('*', 'entries');
3419 $qb->join_user();
3420 $qb->add_user_fields();
3421 $qb->limit($from, $limit);
3423 if ($order == 'CREATION') {
3424 $qb->order_by('timecreated', 'entries', $sort);
3425 } else {
3426 $qb->order_by('timemodified', 'entries', $sort);
3428 $qb->order_by('id', 'entries', $sort); // Sort on ID to avoid random ordering when entries share an ordering value.
3430 // Fetching the entries.
3431 $count = $qb->count_records();
3432 $entries = $qb->get_records();
3434 return array($entries, $count);
3438 * Returns the entries of a glossary by category.
3440 * @param object $glossary The glossary.
3441 * @param context $context The context of the glossary.
3442 * @param int $categoryid The category ID, or GLOSSARY_SHOW_* constant.
3443 * @param int $from Fetch records from.
3444 * @param int $limit Number of records to fetch.
3445 * @param array $options Accepts:
3446 * - (bool) includenotapproved. When false, includes the non-approved entries created by
3447 * the current user. When true, also includes the ones that the user has the permission to approve.
3448 * @return array The first element being the recordset, the second the number of entries.
3449 * @since Moodle 3.1
3451 function glossary_get_entries_by_category($glossary, $context, $categoryid, $from, $limit, $options = array()) {
3453 $qb = new mod_glossary_entry_query_builder($glossary);
3454 if (!empty($options['includenotapproved']) && has_capability('mod/glossary:approve', $context)) {
3455 $qb->filter_by_non_approved(mod_glossary_entry_query_builder::NON_APPROVED_ALL);
3456 } else {
3457 $qb->filter_by_non_approved(mod_glossary_entry_query_builder::NON_APPROVED_SELF);
3460 $qb->join_category($categoryid);
3461 $qb->join_user();
3463 // The first field must be the relationship ID when viewing all categories.
3464 if ($categoryid === GLOSSARY_SHOW_ALL_CATEGORIES) {
3465 $qb->add_field('id', 'entries_categories', 'cid');
3468 $qb->add_field('*', 'entries');
3469 $qb->add_field('categoryid', 'entries_categories');
3470 $qb->add_user_fields();
3472 if ($categoryid === GLOSSARY_SHOW_ALL_CATEGORIES) {
3473 $qb->add_field('name', 'categories', 'categoryname');
3474 $qb->order_by('name', 'categories');
3476 } else if ($categoryid === GLOSSARY_SHOW_NOT_CATEGORISED) {
3477 $qb->where('categoryid', 'entries_categories', null);
3480 // Sort on additional fields to avoid random ordering when entries share an ordering value.
3481 $qb->order_by('concept', 'entries');
3482 $qb->order_by('id', 'entries', 'ASC');
3483 $qb->limit($from, $limit);
3485 // Fetching the entries.
3486 $count = $qb->count_records();
3487 $entries = $qb->get_records();
3489 return array($entries, $count);
3493 * Returns the entries of a glossary by author.
3495 * @param object $glossary The glossary.
3496 * @param context $context The context of the glossary.
3497 * @param string $letter The letter
3498 * @param string $field The field to search: FIRSTNAME or LASTNAME.
3499 * @param string $sort The sorting: ASC or DESC.
3500 * @param int $from Fetch records from.
3501 * @param int $limit Number of records to fetch.
3502 * @param array $options Accepts:
3503 * - (bool) includenotapproved. When false, includes the non-approved entries created by
3504 * the current user. When true, also includes the ones that the user has the permission to approve.
3505 * @return array The first element being the recordset, the second the number of entries.
3506 * @since Moodle 3.1
3508 function glossary_get_entries_by_author($glossary, $context, $letter, $field, $sort, $from, $limit, $options = array()) {
3510 $firstnamefirst = $field === 'FIRSTNAME';
3511 $qb = new mod_glossary_entry_query_builder($glossary);
3512 if ($letter != 'ALL' && $letter != 'SPECIAL' && core_text::strlen($letter)) {
3513 $qb->filter_by_author_letter($letter, $firstnamefirst);
3515 if ($letter == 'SPECIAL') {
3516 $qb->filter_by_author_non_letter($firstnamefirst);
3519 if (!empty($options['includenotapproved']) && has_capability('mod/glossary:approve', $context)) {
3520 $qb->filter_by_non_approved(mod_glossary_entry_query_builder::NON_APPROVED_ALL);
3521 } else {
3522 $qb->filter_by_non_approved(mod_glossary_entry_query_builder::NON_APPROVED_SELF);
3525 $qb->add_field('*', 'entries');
3526 $qb->join_user(true);
3527 $qb->add_user_fields();
3528 $qb->order_by_author($firstnamefirst, $sort);
3529 $qb->order_by('concept', 'entries');
3530 $qb->order_by('id', 'entries', 'ASC'); // Sort on ID to avoid random ordering when entries share an ordering value.
3531 $qb->limit($from, $limit);
3533 // Fetching the entries.
3534 $count = $qb->count_records();
3535 $entries = $qb->get_records();
3537 return array($entries, $count);
3541 * Returns the entries of a glossary by category.
3543 * @param object $glossary The glossary.
3544 * @param context $context The context of the glossary.
3545 * @param int $authorid The author ID.
3546 * @param string $order The mode of ordering: CONCEPT, CREATION or UPDATE.
3547 * @param string $sort The direction of the ordering: ASC or DESC.
3548 * @param int $from Fetch records from.
3549 * @param int $limit Number of records to fetch.
3550 * @param array $options Accepts:
3551 * - (bool) includenotapproved. When false, includes the non-approved entries created by
3552 * the current user. When true, also includes the ones that the user has the permission to approve.
3553 * @return array The first element being the recordset, the second the number of entries.
3554 * @since Moodle 3.1
3556 function glossary_get_entries_by_author_id($glossary, $context, $authorid, $order, $sort, $from, $limit, $options = array()) {
3558 $qb = new mod_glossary_entry_query_builder($glossary);
3559 if (!empty($options['includenotapproved']) && has_capability('mod/glossary:approve', $context)) {
3560 $qb->filter_by_non_approved(mod_glossary_entry_query_builder::NON_APPROVED_ALL);
3561 } else {
3562 $qb->filter_by_non_approved(mod_glossary_entry_query_builder::NON_APPROVED_SELF);
3565 $qb->add_field('*', 'entries');
3566 $qb->join_user(true);
3567 $qb->add_user_fields();
3568 $qb->where('id', 'user', $authorid);
3570 if ($order == 'CREATION') {
3571 $qb->order_by('timecreated', 'entries', $sort);
3572 } else if ($order == 'UPDATE') {
3573 $qb->order_by('timemodified', 'entries', $sort);
3574 } else {
3575 $qb->order_by('concept', 'entries', $sort);
3577 $qb->order_by('id', 'entries', $sort); // Sort on ID to avoid random ordering when entries share an ordering value.
3579 $qb->limit($from, $limit);
3581 // Fetching the entries.
3582 $count = $qb->count_records();
3583 $entries = $qb->get_records();
3585 return array($entries, $count);
3589 * Returns the authors in a glossary
3591 * @param object $glossary The glossary.
3592 * @param context $context The context of the glossary.
3593 * @param int $limit Number of records to fetch.
3594 * @param int $from Fetch records from.
3595 * @param array $options Accepts:
3596 * - (bool) includenotapproved. When false, includes self even if all of their entries require approval.
3597 * When true, also includes authors only having entries pending approval.
3598 * @return array The first element being the recordset, the second the number of entries.
3599 * @since Moodle 3.1
3601 function glossary_get_authors($glossary, $context, $limit, $from, $options = array()) {
3602 global $DB, $USER;
3604 $params = array();
3605 $userfields = user_picture::fields('u', null);
3607 $approvedsql = '(ge.approved <> 0 OR ge.userid = :myid)';
3608 $params['myid'] = $USER->id;
3609 if (!empty($options['includenotapproved']) && has_capability('mod/glossary:approve', $context)) {
3610 $approvedsql = '1 = 1';
3613 $sqlselectcount = "SELECT COUNT(DISTINCT(u.id))";
3614 $sqlselect = "SELECT DISTINCT(u.id) AS userId, $userfields";
3615 $sql = " FROM {user} u
3616 JOIN {glossary_entries} ge
3617 ON ge.userid = u.id
3618 AND (ge.glossaryid = :gid1 OR ge.sourceglossaryid = :gid2)
3619 AND $approvedsql";
3620 $ordersql = " ORDER BY u.lastname, u.firstname";
3622 $params['gid1'] = $glossary->id;
3623 $params['gid2'] = $glossary->id;
3625 $count = $DB->count_records_sql($sqlselectcount . $sql, $params);
3626 $users = $DB->get_recordset_sql($sqlselect . $sql . $ordersql, $params, $from, $limit);
3628 return array($users, $count);
3632 * Returns the categories of a glossary.
3634 * @param object $glossary The glossary.
3635 * @param int $from Fetch records from.
3636 * @param int $limit Number of records to fetch.
3637 * @return array The first element being the recordset, the second the number of entries.
3638 * @since Moodle 3.1
3640 function glossary_get_categories($glossary, $from, $limit) {
3641 global $DB;
3643 $count = $DB->count_records('glossary_categories', array('glossaryid' => $glossary->id));
3644 $categories = $DB->get_recordset('glossary_categories', array('glossaryid' => $glossary->id), 'name ASC', '*', $from, $limit);
3646 return array($categories, $count);
3650 * Get the SQL where clause for searching terms.
3652 * Note that this does not handle invalid or too short terms.
3654 * @param array $terms Array of terms.
3655 * @param bool $fullsearch Whether or not full search should be enabled.
3656 * @return array The first element being the where clause, the second array of parameters.
3657 * @since Moodle 3.1
3659 function glossary_get_search_terms_sql(array $terms, $fullsearch = true) {
3660 global $DB;
3661 static $i = 0;
3663 if ($DB->sql_regex_supported()) {
3664 $regexp = $DB->sql_regex(true);
3665 $notregexp = $DB->sql_regex(false);
3668 $params = array();
3669 $conditions = array();
3671 foreach ($terms as $searchterm) {
3672 $i++;
3674 $not = false; // Initially we aren't going to perform NOT LIKE searches, only MSSQL and Oracle
3675 // will use it to simulate the "-" operator with LIKE clause.
3677 if (empty($fullsearch)) {
3678 // With fullsearch disabled, look only within concepts and aliases.
3679 $concat = $DB->sql_concat('ge.concept', "' '", "COALESCE(al.alias, :emptychar{$i})");
3680 } else {
3681 // With fullsearch enabled, look also within definitions.
3682 $concat = $DB->sql_concat('ge.concept', "' '", 'ge.definition', "' '", "COALESCE(al.alias, :emptychar{$i})");
3684 $params['emptychar' . $i] = '';
3686 // Under Oracle and MSSQL, trim the + and - operators and perform simpler LIKE (or NOT LIKE) queries.
3687 if (!$DB->sql_regex_supported()) {
3688 if (substr($searchterm, 0, 1) === '-') {
3689 $not = true;
3691 $searchterm = trim($searchterm, '+-');
3694 if (substr($searchterm, 0, 1) === '+') {
3695 $searchterm = trim($searchterm, '+-');
3696 $conditions[] = "$concat $regexp :searchterm{$i}";
3697 $params['searchterm' . $i] = '(^|[^a-zA-Z0-9])' . preg_quote($searchterm, '|') . '([^a-zA-Z0-9]|$)';
3699 } else if (substr($searchterm, 0, 1) === "-") {
3700 $searchterm = trim($searchterm, '+-');
3701 $conditions[] = "$concat $notregexp :searchterm{$i}";
3702 $params['searchterm' . $i] = '(^|[^a-zA-Z0-9])' . preg_quote($searchterm, '|') . '([^a-zA-Z0-9]|$)';
3704 } else {
3705 $conditions[] = $DB->sql_like($concat, ":searchterm{$i}", false, true, $not);
3706 $params['searchterm' . $i] = '%' . $DB->sql_like_escape($searchterm) . '%';
3710 // When there are no conditions we add a negative one to ensure that we don't return anything.
3711 if (empty($conditions)) {
3712 $conditions[] = '1 = 2';
3715 $where = implode(' AND ', $conditions);
3716 return array($where, $params);
3721 * Returns the entries of a glossary by search.
3723 * @param object $glossary The glossary.
3724 * @param context $context The context of the glossary.
3725 * @param string $query The search query.
3726 * @param bool $fullsearch Whether or not full search is required.
3727 * @param string $order The mode of ordering: CONCEPT, CREATION or UPDATE.
3728 * @param string $sort The direction of the ordering: ASC or DESC.
3729 * @param int $from Fetch records from.
3730 * @param int $limit Number of records to fetch.
3731 * @param array $options Accepts:
3732 * - (bool) includenotapproved. When false, includes the non-approved entries created by
3733 * the current user. When true, also includes the ones that the user has the permission to approve.
3734 * @return array The first element being the recordset, the second the number of entries.
3735 * @since Moodle 3.1
3737 function glossary_get_entries_by_search($glossary, $context, $query, $fullsearch, $order, $sort, $from, $limit,
3738 $options = array()) {
3739 global $DB, $USER;
3741 // Remove too little terms.
3742 $terms = explode(' ', $query);
3743 foreach ($terms as $key => $term) {
3744 if (strlen(trim($term, '+-')) < 2) {
3745 unset($terms[$key]);
3749 list($searchcond, $params) = glossary_get_search_terms_sql($terms, $fullsearch);
3751 $userfields = user_picture::fields('u', null, 'userdataid', 'userdata');
3753 // Need one inner view here to avoid distinct + text.
3754 $sqlwrapheader = 'SELECT ge.*, ge.concept AS glossarypivot, ' . $userfields . '
3755 FROM {glossary_entries} ge
3756 LEFT JOIN {user} u ON u.id = ge.userid
3757 JOIN ( ';
3758 $sqlwrapfooter = ' ) gei ON (ge.id = gei.id)';
3759 $sqlselect = "SELECT DISTINCT ge.id";
3760 $sqlfrom = "FROM {glossary_entries} ge
3761 LEFT JOIN {glossary_alias} al ON al.entryid = ge.id";
3763 if (!empty($options['includenotapproved']) && has_capability('mod/glossary:approve', $context)) {
3764 $approvedsql = '';
3765 } else {
3766 $approvedsql = 'AND (ge.approved <> 0 OR ge.userid = :myid)';
3767 $params['myid'] = $USER->id;
3770 if ($order == 'CREATION') {
3771 $sqlorderby = "ORDER BY ge.timecreated $sort";
3772 } else if ($order == 'UPDATE') {
3773 $sqlorderby = "ORDER BY ge.timemodified $sort";
3774 } else {
3775 $sqlorderby = "ORDER BY ge.concept $sort";
3777 $sqlorderby .= " , ge.id ASC"; // Sort on ID to avoid random ordering when entries share an ordering value.
3779 $sqlwhere = "WHERE ($searchcond) $approvedsql";
3781 // Fetching the entries.
3782 $count = $DB->count_records_sql("SELECT COUNT(DISTINCT(ge.id)) $sqlfrom $sqlwhere", $params);
3784 $query = "$sqlwrapheader $sqlselect $sqlfrom $sqlwhere $sqlwrapfooter $sqlorderby";
3785 $entries = $DB->get_recordset_sql($query, $params, $from, $limit);
3787 return array($entries, $count);
3791 * Returns the entries of a glossary by term.
3793 * @param object $glossary The glossary.
3794 * @param context $context The context of the glossary.
3795 * @param string $term The term we are searching for, a concept or alias.
3796 * @param int $from Fetch records from.
3797 * @param int $limit Number of records to fetch.
3798 * @param array $options Accepts:
3799 * - (bool) includenotapproved. When false, includes the non-approved entries created by
3800 * the current user. When true, also includes the ones that the user has the permission to approve.
3801 * @return array The first element being the recordset, the second the number of entries.
3802 * @since Moodle 3.1
3804 function glossary_get_entries_by_term($glossary, $context, $term, $from, $limit, $options = array()) {
3806 // Build the query.
3807 $qb = new mod_glossary_entry_query_builder($glossary);
3808 if (!empty($options['includenotapproved']) && has_capability('mod/glossary:approve', $context)) {
3809 $qb->filter_by_non_approved(mod_glossary_entry_query_builder::NON_APPROVED_ALL);
3810 } else {
3811 $qb->filter_by_non_approved(mod_glossary_entry_query_builder::NON_APPROVED_SELF);
3814 $qb->add_field('*', 'entries');
3815 $qb->join_alias();
3816 $qb->join_user();
3817 $qb->add_user_fields();
3818 $qb->filter_by_term($term);
3820 $qb->order_by('concept', 'entries');
3821 $qb->order_by('id', 'entries'); // Sort on ID to avoid random ordering when entries share an ordering value.
3822 $qb->limit($from, $limit);
3824 // Fetching the entries.
3825 $count = $qb->count_records();
3826 $entries = $qb->get_records();
3828 return array($entries, $count);
3832 * Returns the entries to be approved.
3834 * @param object $glossary The glossary.
3835 * @param context $context The context of the glossary.
3836 * @param string $letter The letter, or ALL, or SPECIAL.
3837 * @param string $order The mode of ordering: CONCEPT, CREATION or UPDATE.
3838 * @param string $sort The direction of the ordering: ASC or DESC.
3839 * @param int $from Fetch records from.
3840 * @param int $limit Number of records to fetch.
3841 * @return array The first element being the recordset, the second the number of entries.
3842 * @since Moodle 3.1
3844 function glossary_get_entries_to_approve($glossary, $context, $letter, $order, $sort, $from, $limit) {
3846 $qb = new mod_glossary_entry_query_builder($glossary);
3847 if ($letter != 'ALL' && $letter != 'SPECIAL' && core_text::strlen($letter)) {
3848 $qb->filter_by_concept_letter($letter);
3850 if ($letter == 'SPECIAL') {
3851 $qb->filter_by_concept_non_letter();
3854 $qb->add_field('*', 'entries');
3855 $qb->join_user();
3856 $qb->add_user_fields();
3857 $qb->filter_by_non_approved(mod_glossary_entry_query_builder::NON_APPROVED_ONLY);
3858 if ($order == 'CREATION') {
3859 $qb->order_by('timecreated', 'entries', $sort);
3860 } else if ($order == 'UPDATE') {
3861 $qb->order_by('timemodified', 'entries', $sort);
3862 } else {
3863 $qb->order_by('concept', 'entries', $sort);
3865 $qb->order_by('id', 'entries', $sort); // Sort on ID to avoid random ordering when entries share an ordering value.
3866 $qb->limit($from, $limit);
3868 // Fetching the entries.
3869 $count = $qb->count_records();
3870 $entries = $qb->get_records();
3872 return array($entries, $count);
3876 * Fetch an entry.
3878 * @param int $id The entry ID.
3879 * @return object|false The entry, or false when not found.
3880 * @since Moodle 3.1
3882 function glossary_get_entry_by_id($id) {
3884 // Build the query.
3885 $qb = new mod_glossary_entry_query_builder();
3886 $qb->add_field('*', 'entries');
3887 $qb->join_user();
3888 $qb->add_user_fields();
3889 $qb->where('id', 'entries', $id);
3891 // Fetching the entries.
3892 $entries = $qb->get_records();
3893 if (empty($entries)) {
3894 return false;
3896 return array_pop($entries);
3900 * Checks if the current user can see the glossary entry.
3902 * @since Moodle 3.1
3903 * @param stdClass $entry
3904 * @param cm_info $cminfo
3905 * @return bool
3907 function glossary_can_view_entry($entry, $cminfo) {
3908 global $USER;
3910 $cm = $cminfo->get_course_module_record();
3911 $context = \context_module::instance($cm->id);
3913 // Recheck uservisible although it should have already been checked in core_search.
3914 if ($cminfo->uservisible === false) {
3915 return false;
3918 // Check approval.
3919 if (empty($entry->approved) && $entry->userid != $USER->id && !has_capability('mod/glossary:approve', $context)) {
3920 return false;
3923 return true;