MDL-49231 mod_glossary: Ensure consistent order when pagination is used
[moodle.git] / mod / glossary / lib.php
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.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
18 /**
19  * Library of functions and constants for module glossary
20  * (replace glossary with the name of your module and delete this line)
21  *
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
25  */
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
57  */
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;
68     }
70     if (empty($glossary->globalglossary) ) {
71         $glossary->globalglossary = 0;
72     }
74     if (!has_capability('mod/glossary:manageentries', context_system::instance())) {
75         $glossary->globalglossary = 0;
76     }
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);
85     }
87     $returnid = $DB->insert_record("glossary", $glossary);
88     $glossary->id = $returnid;
89     glossary_grade_item_update($glossary);
91     return $returnid;
92 }
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.
98  *
99  * @global object
100  * @global object
101  * @param object $glossary
102  * @return bool
103  */
104 function glossary_update_instance($glossary) {
105     global $CFG, $DB;
107     if (empty($glossary->globalglossary)) {
108         $glossary->globalglossary = 0;
109     }
111     if (!has_capability('mod/glossary:manageentries', context_system::instance())) {
112         // keep previous
113         unset($glossary->globalglossary);
114     }
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;
122     }
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);
128     }
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));
133     }
134     glossary_grade_item_update($glossary);
136     return true;
139 /**
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.
143  *
144  * @global object
145  * @param int $id glossary id
146  * @return bool success
147  */
148 function glossary_delete_instance($id) {
149     global $DB, $CFG;
151     if (!$glossary = $DB->get_record('glossary', array('id'=>$id))) {
152         return false;
153     }
155     if (!$cm = get_coursemodule_from_instance('glossary', $id)) {
156         return false;
157     }
159     if (!$context = context_module::instance($cm->id, IGNORE_MISSING)) {
160         return false;
161     }
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);
183                     }
184                     $fs->delete_area_files($context->id, 'mod_glossary', 'attachment', $entry->id);
185                     $entry->attachment = '1';
186                 } else {
187                     $entry->attachment = '0';
188                 }
189                 $DB->update_record('glossary_entries', $entry);
190             }
191         }
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));
198     }
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;
223 /**
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
229  *
230  * @param object $course
231  * @param object $user
232  * @param object $mod
233  * @param object $glossary
234  * @return object|null
235  */
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);
245     }
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;
256         }
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;
269         }
271         return $result;
272     }
273     return NULL;
276 /**
277  * @global object
278  * @param int $glossaryid
279  * @param int $userid
280  * @return array
281  */
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));
295 /**
296  * Print a detailed representation of what a  user has done with
297  * a given particular instance of this module, for user activity reports.
298  *
299  * @global object
300  * @param object $course
301  * @param object $user
302  * @param object $mod
303  * @param object $glossary
304  */
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);
315         }
316     }
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>';
324         }
325         echo '</td></tr></table>';
326     }
329 /**
330  * Returns all glossary entries since a given time for specified glossary
331  *
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
340  */
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));
348     }
350     $modinfo = get_fast_modinfo($course);
351     $cm = $modinfo->cms[$cmid];
352     $context = context_module::instance($cm->id);
354     if (!$cm->uservisible) {
355         return;
356     }
358     $viewfullnames = has_capability('moodle/site:viewfullnames', $context);
359     // Groups are not yet supported for glossary. See MDL-10728 .
360     /*
361     $accessallgroups = has_capability('moodle/site:accessallgroups', $context);
362     $groupmode = groups_get_activity_groupmode($cm, $course);
363      */
365     $params['timestart'] = $timestart;
367     if ($userid) {
368         $userselect = "AND u.id = :userid";
369         $params['userid'] = $userid;
370     } else {
371         $userselect = '';
372     }
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   = '';
381     }
383     $approvedselect = "";
384     if (!has_capability('mod/glossary:approve', $context)) {
385         $approvedselect = " AND ge.approved = 1 ";
386     }
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;
407     }
409     foreach ($entries as $entry) {
410         // Groups are not yet supported for glossary. See MDL-10728 .
411         /*
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();
421                     }
422                 }
423                 if (!array_intersect($usersgroups, $modinfo->get_groups($cm->groupingid))) {
424                     continue;
425                 }
426             }
427         }
428          */
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;
446     }
448     return true;
451 /**
452  * Outputs the glossary entry indicated by $activity
453  *
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
460  */
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'));
468     }
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();
479     }
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;
497 /**
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.
501  *
502  * @global object
503  * @global object
504  * @global object
505  * @param object $course
506  * @param object $viewfullnames
507  * @param int $timestart
508  * @return bool
509  */
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);
516     }
517     $modinfo = get_fast_modinfo($course);
518     $ids = array();
520     foreach ($modinfo->cms as $cm) {
521         if ($cm->modname != 'glossary') {
522             continue;
523         }
524         if (!$cm->uservisible) {
525             continue;
526         }
527         $ids[$cm->instance] = $cm->id;
528     }
530     if (!$ids) {
531         return false;
532     }
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.') ';
544             }
545             $params['glsid'.$glinstanceid] = $glinstanceid;
546         }
547     }
549     if (count($approvals) == 0) {
550         return false;
551     }
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 = '';
567     }
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;
573     }
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));
586             }
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;
598         }
599     }
601     return true;
604 /**
605  * @global object
606  * @param object $log
607  */
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));
616 /**
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
621  */
622 function glossary_cron () {
623     return true;
626 /**
627  * Return grade for given user or all users.
628  *
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
632  */
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);
656 /**
657  * Return rating related permissions
658  *
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
663  */
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;
669     }
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)
676     );
679 /**
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
691  */
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');
698     }
700     // Check the ratingarea is post (the only rating area in forum)
701     if ($params['ratingarea'] != 'entry') {
702         throw new rating_exception('invalidratingarea');
703     }
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');
708     }
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');
719     }
721     if ($info->scale != $params['scaleid']) {
722         //the scale being submitted doesnt match the one in the database
723         throw new rating_exception('invalidscaleid');
724     }
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');
731     }
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');
741             }
742         } else {
743             throw new rating_exception('invalidscaleid');
744         }
745     } else if ($params['rating'] > $info->scale) {
746         //if its numeric and submitted rating is above maximum
747         throw new rating_exception('invalidnum');
748     }
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');
754         }
755     }
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');
763     }
765     return true;
768 /**
769  * Update activity grades
770  *
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
775  */
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);
794     }
797 /**
798  * Create/update grade item for given glossary
799  *
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
804  */
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;
822     }
824     if ($grades  === 'reset') {
825         $params['reset'] = true;
826         $grades = NULL;
827     }
829     return grade_update('mod/glossary', $glossary->course, 'mod', 'glossary', $glossary->id, 0, $grades, $params);
832 /**
833  * Delete grade item for given glossary
834  *
835  * @category grade
836  * @param object $glossary object
837  */
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));
845 /**
846  * @global object
847  * @param int $gloassryid
848  * @param int $scaleid
849  * @return bool
850  */
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;
861     }
863     return $return;
866 /**
867  * Checks if scale is being used by any instance of glossary
868  *
869  * This is used to find out if scale used anywhere
870  *
871  * @global object
872  * @param int $scaleid
873  * @return boolean True if the scale is used by any glossary
874  */
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;
882     }
885 //////////////////////////////////////////////////////////////////////////////////////
886 /// Any other glossary functions go here.  Each of them must have a name that
887 /// starts with glossary_
889 /**
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).
894  *
895  * @global object
896  * @global object
897  * @return array
898  */
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));
922                 }
924                 if (empty($rec->showtabs)) {
925                     glossary_set_default_visible_tabs($rec);
926                 }
927             }
928         }
929     }
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;
938         }
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));
947                 }
948             }
949         }
950     }
952     //Now everything is ready in glossary_formats table
953     $formats = $DB->get_records("glossary_formats");
955     return $formats;
958 /**
959  * @param bool $debug
960  * @param string $text
961  * @param int $br
962  */
963 function glossary_debug($debug,$text,$br=1) {
964     if ( $debug ) {
965         echo '<font color="red">' . $text . '</font>';
966         if ( $br ) {
967             echo '<br />';
968         }
969     }
972 /**
973  *
974  * @global object
975  * @param int $glossaryid
976  * @param string $entrylist
977  * @param string $pivot
978  * @return array
979  */
980 function glossary_get_entries($glossaryid, $entrylist, $pivot = "") {
981     global $DB;
982     if ($pivot) {
983        $pivot .= ",";
984     }
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));
992 /**
993  * @global object
994  * @global object
995  * @param object $concept
996  * @param string $courseid
997  * @return array
998  */
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
1006     }
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
1012     }
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);
1034 /**
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
1047  */
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;
1053     }
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;
1060         }
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);
1069             }
1070         }
1071     }
1072     return $return;
1075 /**
1076  * Default (old) print format used if custom function doesn't exist in format
1077  *
1078  * @param object $entry
1079  * @param object $glossary
1080  * @param object $cm
1081  * @return void Output is echo'd
1082  */
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 />';
1107 /**
1108  * Print glossary concept/term as a heading &lt;h4>
1109  * @param object $entry
1110  */
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);
1117     }
1119     if ($return) {
1120         return $text;
1121     } else {
1122         echo $text;
1123     }
1126 /**
1127  *
1128  * @global moodle_database DB
1129  * @param object $entry
1130  * @param object $glossary
1131  * @param object $cm
1132  */
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);
1157     }
1158     if (isset($entry->footer)) {   // Unparsed footer info
1159         $text .= $entry->footer;
1160     }
1161     echo $text;
1164 /**
1165  *
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
1175  */
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">';
1185                 }
1186                 $return .= "<option>$alias->alias</option>";
1187             }
1188         }
1189         if ($return != '') {
1190             $return .= '</select>';
1191         }
1192     }
1193     if ($type == 'print') {
1194         echo $return;
1195     } else {
1196         return $return;
1197     }
1200 /**
1201  *
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
1213  */
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'));
1232     }
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>';
1241     }
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>';
1252             }
1253         }
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');
1259         }
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>";
1274         }
1275     }
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);
1285             }
1286         }
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);
1294         }
1296         $return .= $button->to_html(PORTFOLIO_ADD_ICON_LINK);
1297     }
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;
1313     }
1315     //If we haven't calculated any REAL thing, delete result ($return)
1316     if (!$output) {
1317         $return = '';
1318     }
1319     //Print or get
1320     if ($type == 'print') {
1321         echo $return;
1322     } else {
1323         return $return;
1324     }
1327 /**
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
1337  */
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');
1341     }
1342     $icons   = '';
1343     if ($printicons) {
1344         $icons   = glossary_print_entry_icons($course, $cm, $glossary, $entry, $mode, $hook,'html');
1345     }
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>';
1352         }
1353         if ($icons) {
1354             echo '<tr valign="top"><td class="icons">'.$icons.'</td></tr>';
1355         }
1356         if (!empty($entry->rating)) {
1357             echo '<tr valign="top"><td class="ratings">';
1358             glossary_print_entry_ratings($course, $entry);
1359             echo '</td></tr>';
1360         }
1361         echo '</table>';
1362     }
1365 /**
1366  * Print the list of attachments for this glossary entry
1367  *
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
1373  */
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>';
1381     }
1382     if ($unused1) {
1383         debugging('The align parameter is deprecated, please use appropriate CSS instead', DEBUG_DEVELOPER);
1384     }
1385     if ($unused2 !== null) {
1386         debugging('The insidetable parameter is deprecated, please use appropriate CSS instead', DEBUG_DEVELOPER);
1387     }
1390 /**
1391  * @global object
1392  * @param object $cm
1393  * @param object $entry
1394  * @param string $mode
1395  * @param string $align
1396  * @param bool $insidetable
1397  */
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.'">';
1404         }
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))
1409         );
1410         if ($insidetable) {
1411             echo '</td></tr></table>';
1412         }
1413     }
1416 /**
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.
1420  *
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
1428  */
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,";
1437             }
1438             $glos = substr($glos,0,-1);
1439         }
1440     } else {
1441         $glos = $glossary->id;
1442     }
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 = "";
1452     }
1454     if ($DB->sql_regex_supported()) {
1455         $REGEXP    = $DB->sql_regex(true);
1456         $NOTREGEXP = $DB->sql_regex(false);
1457     }
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;
1477             }
1478             $searchterm = trim($searchterm, '+-');
1479         }
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%";
1498         }
1499     }
1501     if (empty($searchcond)) {
1502         $totalcount = 0;
1503         return array();
1504     }
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);
1517 /**
1518  * @global object
1519  * @param array $searchterms
1520  * @param object $glossary
1521  * @param bool $extended
1522  * @return array
1523  */
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);
1531 /**
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.
1536  *
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
1544  */
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 '';
1550     }
1552     if ($entry->sourceglossaryid == $cm->instance) {
1553         if (!$maincm = get_coursemodule_from_instance('glossary', $entry->glossaryid)) {
1554             return '';
1555         }
1556         $filecontext = context_module::instance($maincm->id);
1558     } else {
1559         $filecontext = $context;
1560     }
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 />';
1592                 }
1593             }
1594         }
1595     }
1597     if ($type) {
1598         return $output;
1599     } else {
1600         echo $output;
1601         return $imagereturn;
1602     }
1605 ////////////////////////////////////////////////////////////////////////////////
1606 // File API                                                                   //
1607 ////////////////////////////////////////////////////////////////////////////////
1609 /**
1610  * Lists all browsable file areas
1611  *
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
1618  */
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'),
1623     );
1626 /**
1627  * File browsing support for glossary module.
1628  *
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
1639  */
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;
1645     }
1647     if (!isset($areas[$filearea])) {
1648         return null;
1649     }
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);
1654     }
1656     if (!$entry = $DB->get_record('glossary_entries', array('id' => $itemid))) {
1657         return null;
1658     }
1660     if (!$glossary = $DB->get_record('glossary', array('id' => $cm->instance))) {
1661         return null;
1662     }
1664     if ($glossary->defaultapproval and !$entry->approved and !has_capability('mod/glossary:approve', $context)) {
1665         return null;
1666     }
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;
1674         }
1675         $filecontext = context_module::instance($maincm->id);
1676     } else {
1677         return null;
1678     }
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;
1685     }
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;
1691     }
1693     $urlbase = $CFG->wwwroot.'/pluginfile.php';
1695     return new file_info_stored($browser, $filecontext, $storedfile, $urlbase, s($entry->concept), true, true, false, false);
1698 /**
1699  * Serves the glossary attachments. Implements needed access control ;-)
1700  *
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
1711  */
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;
1717     }
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;
1728         }
1730         if (!$glossary = $DB->get_record('glossary', array('id'=>$cm->instance))) {
1731             return false;
1732         }
1734         if ($glossary->defaultapproval and !$entry->approved and !has_capability('mod/glossary:approve', $context)) {
1735             return false;
1736         }
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;
1746             }
1747             $filecontext = context_module::instance($maincm->id);
1749         } else {
1750             return false;
1751         }
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;
1759         }
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;
1770         }
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);
1779     }
1781     return false;
1784 /**
1785  *
1786  */
1787 function glossary_print_tabbed_table_end() {
1788      echo "</div></div>";
1791 /**
1792  * @param object $cm
1793  * @param object $glossary
1794  * @param string $mode
1795  * @param string $hook
1796  * @param string $sortkey
1797  * @param string $sortorder
1798  */
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 />';
1802     }
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');
1811 /**
1812  * @param object $cm
1813  * @param object $glossary
1814  * @param string $hook
1815  * @param string $sortkey
1816  * @param string $sortorder
1817  */
1818 function glossary_print_import_menu($cm, $glossary, $mode, $hook, $sortkey='', $sortorder = '') {
1819     echo '<div class="glossaryexplain">' . get_string("explainimport","glossary") . '</div>';
1822 /**
1823  * @param object $cm
1824  * @param object $glossary
1825  * @param string $hook
1826  * @param string $sortkey
1827  * @param string $sortorder
1828  */
1829 function glossary_print_export_menu($cm, $glossary, $mode, $hook, $sortkey='', $sortorder = '') {
1830     echo '<div class="glossaryexplain">' . get_string("explainexport","glossary") . '</div>';
1832 /**
1833  * @param object $cm
1834  * @param object $glossary
1835  * @param string $hook
1836  * @param string $sortkey
1837  * @param string $sortorder
1838  */
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 />';
1843         }
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);
1852     }
1855 /**
1856  * @param object $cm
1857  * @param object $glossary
1858  * @param string $hook
1859  * @param string $sortkey
1860  * @param string $sortorder
1861  */
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 />';
1865     }
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);
1872 /**
1873  * @global object
1874  * @global object
1875  * @param object $cm
1876  * @param object $glossary
1877  * @param string $hook
1878  * @param object $category
1879  */
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");
1898      }
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;
1916                      }
1917                  }
1918                  $menu[$url] = format_string($currentcategory->name, true, $fmtoptions);
1919           }
1920      }
1921      if ( !$selected ) {
1922          $selected = GLOSSARY_SHOW_NOT_CATEGORISED;
1923      }
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;
1938         }
1939      }
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>';
1953 /**
1954  * @global object
1955  * @param object $cm
1956  * @param object $glossary
1957  * @param string $mode
1958  * @param string $hook
1959  */
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>";
1969          }
1970      }
1973 /**
1974  * @global object
1975  * @param object $cm
1976  * @param object $glossary
1977  * @param string $mode
1978  * @param string $hook
1979  */
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> | ";
1989          }
1990      }
1993 /**
1994  * @global object
1995  * @param object $glossary
1996  * @param string $mode
1997  * @param string $hook
1998  * @param string $sortkey
1999  * @param string $sortorder
2000  */
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>";
2010               }
2011               echo ' | ';
2012           }
2013      }
2016 /**
2017  * @global object
2018  * @param object $cm
2019  * @param string $mode
2020  * @param string $sortkey
2021  * @param string $sortorder
2022  */
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);
2043          }
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.'" />';
2054          }
2055      }
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;
2088      }
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;
2099          }
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;
2110          }
2111          $sort        = get_string("sortby", "glossary");
2112      }
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 />";
2118 /**
2119  *
2120  * @param object $entry0
2121  * @param object $entry1
2122  * @return int [-1 | 0 | 1]
2123  */
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;
2132     }
2136 /**
2137  * @global object
2138  * @global object
2139  * @global object
2140  * @param object $course
2141  * @param object $entry
2142  * @return bool
2143  */
2144 function  glossary_print_entry_ratings($course, $entry) {
2145     global $OUTPUT;
2146     if( !empty($entry->rating) ){
2147         echo $OUTPUT->render($entry->rating);
2148     }
2151 /**
2152  *
2153  * @global object
2154  * @global object
2155  * @global object
2156  * @param int $courseid
2157  * @param array $entries
2158  * @param int $displayformat
2159  */
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');
2170             }
2171             if (! $course = $DB->get_record('course', array('id'=>$glossary->course))) {
2172                 print_error('coursemisconf');
2173             }
2174             if (!$cm = get_coursemodule_from_instance('glossary', $entry->glossaryid, $glossary->course) ) {
2175                 print_error('invalidid', 'glossary');
2176             }
2178             //If displayformat is present, override glossary->displayformat
2179             if ($displayformat < 0) {
2180                 $dp = $glossary->displayformat;
2181             } else {
2182                 $dp = $displayformat;
2183             }
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';
2192             }
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,'','','','');
2201                 }
2202             }
2203         }
2204     }
2205     echo '</td>';
2206     echo '</tr></table></div>';
2209 /**
2210  *
2211  * @global object
2212  * @param array $entries
2213  * @param array $aliases
2214  * @param array $categories
2215  * @return string
2216  */
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;
2238             }
2239             foreach ($aliases[$entry->id] as $alias) {
2240                 $thisaliasesentry[] = trim($alias);
2241             }
2242         }
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;
2247             }
2248             foreach ($categories[$entry->id] as $catentry) {
2249                 $thiscategoriesentry[] = trim($catentry);
2250             }
2251         }
2252         $csventries[$entry->id] = $thiscsventry;
2253         $csvaliases[$entry->id] = $thisaliasesentry;
2254         $csvcategories[$entry->id] = $thiscategoriesentry;
2256     }
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');
2264         }
2265         $row = array_merge($row, array_pad($csvaliases[$id], $aliascount, $aliasstr), array_pad($csvcategories[$id], $categorycount, $categorystr));
2266         $returnstr .= '"' . implode('"' . $delimiter . '"', $row) . '"' . "\n";
2267     }
2268     return $returnstr;
2271 /**
2272  *
2273  * @param object $glossary
2274  * @param string $ignored invalid parameter
2275  * @param int|string $hook
2276  * @return string
2277  */
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;
2319                     }
2320                 }
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;
2331                     }
2332                 }
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);
2349                         }
2350                         $co .= glossary_end_tag("ALIASES",4,true);
2351                     }
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);
2361                         }
2362                         $co .= glossary_end_tag("CATEGORIES",4,true);
2363                     }
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);
2372                 }
2373             }
2374             $co .= glossary_end_tag("ENTRIES",2,true);
2376         }
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
2387 /**
2388  * Read import file and convert to current charset
2389  *
2390  * @global object
2391  * @param string $file
2392  * @return string
2393  */
2394 function glossary_read_imported_file($file_content) {
2395     require_once "../../lib/xmlize.php";
2396     global $CFG;
2398     return xmlize($file_content, 0);
2401 /**
2402  * Return the xml start tag
2403  *
2404  * @param string $tag
2405  * @param int $level
2406  * @param bool $endline
2407  * @return string
2408  */
2409 function glossary_start_tag($tag,$level=0,$endline=false) {
2410         if ($endline) {
2411            $endchar = "\n";
2412         } else {
2413            $endchar = "";
2414         }
2415         return str_repeat(" ",$level*2)."<".strtoupper($tag).">".$endchar;
2418 /**
2419  * Return the xml end tag
2420  * @param string $tag
2421  * @param int $level
2422  * @param bool $endline
2423  * @return string
2424  */
2425 function glossary_end_tag($tag,$level=0,$endline=true) {
2426         if ($endline) {
2427            $endchar = "\n";
2428         } else {
2429            $endchar = "";
2430         }
2431         return str_repeat(" ",$level*2)."</".strtoupper($tag).">".$endchar;
2434 /**
2435  * Return the start tag, the contents and the end tag
2436  *
2437  * @global object
2438  * @param string $tag
2439  * @param int $level
2440  * @param bool $endline
2441  * @param string $content
2442  * @return string
2443  */
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;
2453 /**
2454  * Prepares file area to export as part of XML export
2455  *
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
2462  */
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);
2475         }
2476         $co .= glossary_end_tag($tag, $taglevel);
2477     }
2478     return $co;
2481 /**
2482  * Parses files from XML import and inserts them into file system
2483  *
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
2490  */
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]['#'],
2504             );
2505             $content =  $file['#']['CONTENTS'][0]['#'];
2506             $fs->create_file_from_string($filerecord, base64_decode($content));
2507             $count++;
2508         }
2509     }
2510     return $count;
2513 /**
2514  * How many unrated entries are in the given glossary for a given user?
2515  *
2516  * @global moodle_database $DB
2517  * @param int $glossaryid
2518  * @param int $userid
2519  * @return int
2520  */
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
2561             }
2562         } else {
2563             return (int)$entries;
2564         }
2565     } else {
2566         return 0;
2567     }
2570 /**
2571  *
2572  * Returns the html code to represent any pagging bar. Paramenters are:
2573  *
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. ;-)
2577  *
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.
2582  *
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
2590  */
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;
2601     }
2602     //Check if we are with the special link selected
2603     if ($showspecial && $page == $specialvalue) {
2604         $specialselected = true;
2605     }
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;
2617         }
2618         if ($page > $maxpageallowed) {
2619             $page = $maxpageallowed;
2620         }
2621         if ($page > $maxpage) {
2622             $page = $maxpage;
2623         }
2625         //Calculate the window of pages
2626         $pagefrom = $page - ((int)($maxdisplay / 2));
2627         if ($pagefrom < 0) {
2628             $pagefrom = 0;
2629         }
2630         $pageto = $pagefrom + $maxdisplay - 1;
2631         if ($pageto > $maxpageallowed) {
2632             $pageto = $maxpageallowed;
2633         }
2634         if ($pageto > $maxpage) {
2635             $pageto = $maxpage;
2636         }
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;
2642             }
2643         }
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...";
2652             }
2653         }
2654         if ($pageto < $maxpage) {
2655             if ($pageto < $maxpage -1) {
2656                 $lastpagecode = "$separator...";
2657             }
2658             $lastpagecode .= "$separator<a href=\"{$baseurl}page=$maxpage\">".($maxpage+1)."</a>";
2659         }
2661         //Previous
2662         if ($page > 0 && $previousandnext) {
2663             $pagenum = $page - 1;
2664             $code .= "&nbsp;(<a  href=\"{$baseurl}page=$pagenum\">".get_string("previous")."</a>)&nbsp;";
2665         }
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>";
2679             }
2680             $pagenum++;
2681         }
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>)";
2690         }
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>";
2699             }
2700         }
2702         //End html
2703         $code .= "</p>";
2704         $code .= "</div>";
2705     }
2707     return $code;
2710 /**
2711  * List the actions that correspond to a view of this module.
2712  * This is used by the participation report.
2713  *
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.
2717  *
2718  * @return array
2719  */
2720 function glossary_get_view_actions() {
2721     return array('view','view all','view entry');
2724 /**
2725  * List the actions that correspond to a post of this module.
2726  * This is used by the participation report.
2727  *
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.
2731  *
2732  * @return array
2733  */
2734 function glossary_get_post_actions() {
2735     return array('add category','add entry','approve entry','delete category','delete entry','edit category','update entry');
2739 /**
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
2743  */
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');
2763 /**
2764  * Course reset form defaults.
2765  * @return array
2766  */
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);
2771 /**
2772  * Removes all grades from gradebook
2773  *
2774  * @param int $courseid The ID of the course to reset
2775  * @param string $type The optional type of glossary. 'main', 'secondary' or ''
2776  */
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
2784     }
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');
2793         }
2794     }
2796 /**
2797  * Actual implementation of the reset course functionality, delete all the
2798  * glossary responses for course $data->courseid.
2799  *
2800  * @global object
2801  * @param $data the data submitted from the reset course.
2802  * @return array status array
2803  */
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;
2843                 }
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);
2850             }
2851         }
2853         // remove all grades from gradebook
2854         if (empty($data->reset_gradebook_grades)) {
2855             glossary_reset_gradebook($data->courseid);
2856         }
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;
2876                     }
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);
2883                 }
2884             }
2886             // remove all grades from gradebook
2887             if (empty($data->reset_gradebook_grades)) {
2888                 glossary_reset_gradebook($data->courseid, 'main');
2889             }
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;
2906                     }
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);
2913                 }
2914             }
2916             // remove all grades from gradebook
2917             if (empty($data->reset_gradebook_grades)) {
2918                 glossary_reset_gradebook($data->courseid, 'secondary');
2919             }
2921             $status[] = array('component'=>$componentstr, 'item'=>get_string('resetglossaries', 'glossary').': '.get_string('secondaryglossary', 'glossary'), 'error'=>false);
2922         }
2923     }
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);
2950                     }
2951                 }
2952             }
2953             $status[] = array('component'=>$componentstr, 'item'=>get_string('deletenotenrolled', 'glossary'), 'error'=>false);
2954         }
2955         $rs->close();
2956     }
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;
2965                 }
2966                 $context = context_module::instance($cm->id);
2968                 //delete ratings
2969                 $ratingdeloptions->contextid = $context->id;
2970                 $rm->delete_ratings($ratingdeloptions);
2971             }
2972         }
2974         // remove all grades from gradebook
2975         if (empty($data->reset_gradebook_grades)) {
2976             glossary_reset_gradebook($data->courseid);
2977         }
2978         $status[] = array('component'=>$componentstr, 'item'=>get_string('deleteallratings'), 'error'=>false);
2979     }
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);
2986     }
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);
2992     }
2994     return $status;
2997 /**
2998  * Returns all other caps used in module
2999  * @return array
3000  */
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');
3005 /**
3006  * @param string $feature FEATURE_xx constant for requested feature
3007  * @return mixed True if module supports feature, null if doesn't know
3008  */
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;
3023     }
3026 /**
3027  * Obtains the automatic completion state for this glossary based on any conditions
3028  * in glossary settings.
3029  *
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)
3038  */
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}");
3045     }
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;
3056         }
3057     }
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;
3088         }
3089     }
3092 /**
3093  * Adds module specific settings to the settings block
3094  *
3095  * @param settings_navigation $settings The settings navigation object
3096  * @param navigation_node $glossarynode The node to add module settings to
3097  */
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)));
3106     }
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)));
3110     }
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')));
3114     }
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)));
3118     }
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', ''));
3129     }
3132 /**
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.
3139  *
3140  * @package  mod_glossary
3141  * @category comment
3142  *
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
3149  * }
3150  * @return array
3151  */
3152 function glossary_comment_permissions($comment_param) {
3153     return array('post'=>true, 'view'=>true);
3156 /**
3157  * Validate comment parameter before perform other comments actions
3158  *
3159  * @package  mod_glossary
3160  * @category comment
3161  *
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
3168  * }
3169  * @return boolean
3170  */
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');
3176     }
3177     if (!$record = $DB->get_record('glossary_entries', array('id'=>$comment_param->itemid))) {
3178         throw new comment_exception('invalidcommentitemid');
3179     }
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));
3184     }
3185     if (!$glossary) {
3186         throw new comment_exception('invalidid', 'data');
3187     }
3188     if (!$course = $DB->get_record('course', array('id'=>$glossary->course))) {
3189         throw new comment_exception('coursemisconf');
3190     }
3191     if (!$cm = get_coursemodule_from_instance('glossary', $glossary->id, $course->id)) {
3192         throw new comment_exception('invalidcoursemodule');
3193     }
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');
3198     }
3199     // validate context id
3200     if ($context->id != $comment_param->context->id) {
3201         throw new comment_exception('invalidcontext');
3202     }
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');
3208             }
3209             if ($comment->contextid != $comment_param->context->id) {
3210                 throw new comment_exception('invalidcontext');
3211             }
3212             if ($comment->itemid != $comment_param->itemid) {
3213                 throw new comment_exception('invalidcommentitemid');
3214             }
3215         } else {
3216             throw new comment_exception('invalidcommentid');
3217         }
3218     }
3219     return true;
3222 /**
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
3227  */
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;
3236 /**
3237  * Return list of all glossary tabs.
3238  * @throws coding_exception
3239  * @return array
3240  */
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')
3247     );
3250 /**
3251  * Set 'showtabs' value for glossary formats
3252  * @param stdClass $glossaryformat record from 'glossary_formats' table
3253  */
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';
3271             }
3272             break;
3273         case GLOSSARY_FULLWITHOUTAUTHOR:
3274             $showtabs = 'standard,category,date';
3275             break;
3276         default:
3277             $showtabs = 'standard,category,date,author';
3278             break;
3279     }
3281     $DB->set_field('glossary_formats', 'showtabs', $showtabs, array('id' => $glossaryformat->id));
3282     $glossaryformat->showtabs = $showtabs;
3285 /**
3286  * Convert 'showtabs' string to array
3287  * @param stdClass $displayformat record from 'glossary_formats' table
3288  * @return array
3289  */
3290 function glossary_get_visible_tabs($displayformat) {
3291     if (empty($displayformat->showtabs)) {
3292         glossary_set_default_visible_tabs($displayformat);
3293     }
3294     $showtabs = preg_split('/,/', $displayformat->showtabs, -1, PREG_SPLIT_NO_EMPTY);
3296     return $showtabs;
3299 /**
3300  * Notify that the glossary was viewed.
3301  *
3302  * This will trigger relevant events and activity completion.
3303  *
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
3310  */
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)
3322     ));
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();
3329 /**
3330  * Notify that a glossary entry was viewed.
3331  *
3332  * This will trigger relevant events.
3333  *
3334  * @param stdClass $entry    The entry object.
3335  * @param stdClass $context  The context object.
3336  * @since Moodle 3.1
3337  */
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
3344     ));
3345     $event->add_record_snapshot('glossary_entries', $entry);
3346     $event->trigger();
3350 /**
3351  * Returns the entries of a glossary by letter.
3352  *
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
3363  */
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);
3369     }
3370     if ($letter == 'SPECIAL') {
3371         $qb->filter_by_concept_non_letter();
3372     }
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);
3378     }
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_recordset();
3391     return array($entries, $count);
3394 /**
3395  * Returns the entries of a glossary by date.
3396  *
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
3408  */
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);
3416     }
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);
3427     }
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_recordset();
3434     return array($entries, $count);
3437 /**
3438  * Returns the entries of a glossary by category.
3439  *
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
3450  */
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);
3458     }
3460     $qb->join_category($categoryid);
3461     $qb->join_user();
3462     $qb->add_field('*', 'entries');
3463     $qb->add_field('categoryid', 'entries_categories');
3464     $qb->add_user_fields();
3466     if ($categoryid === GLOSSARY_SHOW_ALL_CATEGORIES) {
3467         $qb->add_field('name', 'categories', 'categoryname');
3468         $qb->order_by('name', 'categories');
3470     } else if ($categoryid === GLOSSARY_SHOW_NOT_CATEGORISED) {
3471         $qb->where('categoryid', 'entries_categories', null);
3472     }
3474     // Sort on additional fields to avoid random ordering when entries share an ordering value.
3475     $qb->order_by('concept', 'entries');
3476     $qb->order_by('id', 'entries', 'ASC');
3477     $qb->limit($from, $limit);
3479     // Fetching the entries.
3480     $count = $qb->count_records();
3481     $entries = $qb->get_recordset();
3483     return array($entries, $count);
3486 /**
3487  * Returns the entries of a glossary by author.
3488  *
3489  * @param  object $glossary The glossary.
3490  * @param  context $context The context of the glossary.
3491  * @param  string $letter The letter
3492  * @param  string $field The field to search: FIRSTNAME or LASTNAME.
3493  * @param  string $sort The sorting: ASC or DESC.
3494  * @param  int $from Fetch records from.
3495  * @param  int $limit Number of records to fetch.
3496  * @param  array $options Accepts:
3497  *                        - (bool) includenotapproved. When false, includes the non-approved entries created by
3498  *                          the current user. When true, also includes the ones that the user has the permission to approve.
3499  * @return array The first element being the recordset, the second the number of entries.
3500  * @since Moodle 3.1
3501  */
3502 function glossary_get_entries_by_author($glossary, $context, $letter, $field, $sort, $from, $limit, $options = array()) {
3504     $firstnamefirst = $field === 'FIRSTNAME';
3505     $qb = new mod_glossary_entry_query_builder($glossary);
3506     if ($letter != 'ALL' && $letter != 'SPECIAL' && core_text::strlen($letter)) {
3507         $qb->filter_by_author_letter($letter, $firstnamefirst);
3508     }
3509     if ($letter == 'SPECIAL') {
3510         $qb->filter_by_author_non_letter($firstnamefirst);
3511     }
3513     if (!empty($options['includenotapproved']) && has_capability('mod/glossary:approve', $context)) {
3514         $qb->filter_by_non_approved(mod_glossary_entry_query_builder::NON_APPROVED_ALL);
3515     } else {
3516         $qb->filter_by_non_approved(mod_glossary_entry_query_builder::NON_APPROVED_SELF);
3517     }
3519     $qb->add_field('*', 'entries');
3520     $qb->join_user(true);
3521     $qb->add_user_fields();
3522     $qb->order_by_author($firstnamefirst, $sort);
3523     $qb->order_by('concept', 'entries');
3524     $qb->order_by('id', 'entries', 'ASC'); // Sort on ID to avoid random ordering when entries share an ordering value.
3525     $qb->limit($from, $limit);
3527     // Fetching the entries.
3528     $count = $qb->count_records();
3529     $entries = $qb->get_recordset();
3531     return array($entries, $count);
3534 /**
3535  * Returns the entries of a glossary by category.
3536  *
3537  * @param  object $glossary The glossary.
3538  * @param  context $context The context of the glossary.
3539  * @param  int $authorid The author ID.
3540  * @param  string $order The mode of ordering: CONCEPT, CREATION or UPDATE.
3541  * @param  string $sort The direction of the ordering: ASC or DESC.
3542  * @param  int $from Fetch records from.
3543  * @param  int $limit Number of records to fetch.
3544  * @param  array $options Accepts:
3545  *                        - (bool) includenotapproved. When false, includes the non-approved entries created by
3546  *                          the current user. When true, also includes the ones that the user has the permission to approve.
3547  * @return array The first element being the recordset, the second the number of entries.
3548  * @since Moodle 3.1
3549  */
3550 function glossary_get_entries_by_author_id($glossary, $context, $authorid, $order, $sort, $from, $limit, $options = array()) {
3552     $qb = new mod_glossary_entry_query_builder($glossary);
3553     if (!empty($options['includenotapproved']) && has_capability('mod/glossary:approve', $context)) {
3554         $qb->filter_by_non_approved(mod_glossary_entry_query_builder::NON_APPROVED_ALL);
3555     } else {
3556         $qb->filter_by_non_approved(mod_glossary_entry_query_builder::NON_APPROVED_SELF);
3557     }
3559     $qb->add_field('*', 'entries');
3560     $qb->join_user(true);
3561     $qb->add_user_fields();
3562     $qb->where('id', 'user', $authorid);
3564     if ($order == 'CREATION') {
3565         $qb->order_by('timecreated', 'entries', $sort);
3566     } else if ($order == 'UPDATE') {
3567         $qb->order_by('timemodified', 'entries', $sort);
3568     } else {
3569         $qb->order_by('concept', 'entries', $sort);
3570     }
3571     $qb->order_by('id', 'entries', $sort); // Sort on ID to avoid random ordering when entries share an ordering value.
3573     $qb->limit($from, $limit);
3575     // Fetching the entries.
3576     $count = $qb->count_records();
3577     $entries = $qb->get_recordset();