[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
1 <?php 2 3 // This file is part of Moodle - http://moodle.org/ 4 // 5 // Moodle is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // Moodle is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU General Public License for more details. 14 // 15 // You should have received a copy of the GNU General Public License 16 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 17 18 /** 19 * 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'); 27 28 define("GLOSSARY_SHOW_ALL_CATEGORIES", 0); 29 define("GLOSSARY_SHOW_NOT_CATEGORISED", -1); 30 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); 40 41 // Glossary tabs. 42 define('GLOSSARY_STANDARD', 'standard'); 43 define('GLOSSARY_AUTHOR', 'author'); 44 define('GLOSSARY_CATEGORY', 'category'); 45 define('GLOSSARY_DATE', 'date'); 46 47 // Glossary displayformats. 48 define('GLOSSARY_CONTINUOUS', 'continuous'); 49 define('GLOSSARY_DICTIONARY', 'dictionary'); 50 define('GLOSSARY_FULLWITHOUTAUTHOR', 'fullwithoutauthor'); 51 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. 64 65 if (empty($glossary->ratingtime) or empty($glossary->assessed)) { 66 $glossary->assesstimestart = 0; 67 $glossary->assesstimefinish = 0; 68 } 69 70 if (empty($glossary->globalglossary) ) { 71 $glossary->globalglossary = 0; 72 } 73 74 if (!has_capability('mod/glossary:manageentries', context_system::instance())) { 75 $glossary->globalglossary = 0; 76 } 77 78 $glossary->timecreated = time(); 79 $glossary->timemodified = $glossary->timecreated; 80 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 } 86 87 $returnid = $DB->insert_record("glossary", $glossary); 88 $glossary->id = $returnid; 89 glossary_grade_item_update($glossary); 90 91 return $returnid; 92 } 93 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; 106 107 if (empty($glossary->globalglossary)) { 108 $glossary->globalglossary = 0; 109 } 110 111 if (!has_capability('mod/glossary:manageentries', context_system::instance())) { 112 // keep previous 113 unset($glossary->globalglossary); 114 } 115 116 $glossary->timemodified = time(); 117 $glossary->id = $glossary->instance; 118 119 if (empty($glossary->ratingtime) or empty($glossary->assessed)) { 120 $glossary->assesstimestart = 0; 121 $glossary->assesstimefinish = 0; 122 } 123 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 } 129 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); 135 136 return true; 137 } 138 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; 150 151 if (!$glossary = $DB->get_record('glossary', array('id'=>$id))) { 152 return false; 153 } 154 155 if (!$cm = get_coursemodule_from_instance('glossary', $id)) { 156 return false; 157 } 158 159 if (!$context = context_module::instance($cm->id, IGNORE_MISSING)) { 160 return false; 161 } 162 163 $fs = get_file_storage(); 164 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"; 172 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 } 199 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)); 204 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)); 209 210 // delete all files 211 $fs->delete_area_files($context->id); 212 213 glossary_grade_item_delete($glossary); 214 215 $DB->delete_records('glossary', array('id'=>$id)); 216 217 // Reset caches. 218 \mod_glossary\local\concept_cache::reset_glossary($glossary); 219 220 return true; 221 } 222 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; 238 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 } 246 247 if ($entries = glossary_get_user_entries($glossary->id, $user->id)) { 248 $result = new stdClass(); 249 $result->info = count($entries) . ' ' . get_string("entries", "glossary"); 250 251 $lastentry = array_pop($entries); 252 $result->time = $lastentry->timemodified; 253 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; 261 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 } 270 271 return $result; 272 } 273 return NULL; 274 } 275 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; 285 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)); 293 } 294 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"); 308 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 } 317 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 } 327 } 328 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; 343 344 if ($COURSE->id == $courseid) { 345 $course = $COURSE; 346 } else { 347 $course = $DB->get_record('course', array('id' => $courseid)); 348 } 349 350 $modinfo = get_fast_modinfo($course); 351 $cm = $modinfo->cms[$cmid]; 352 $context = context_module::instance($cm->id); 353 354 if (!$cm->uservisible) { 355 return; 356 } 357 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 */ 364 365 $params['timestart'] = $timestart; 366 367 if ($userid) { 368 $userselect = "AND u.id = :userid"; 369 $params['userid'] = $userid; 370 } else { 371 $userselect = ''; 372 } 373 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 } 382 383 $approvedselect = ""; 384 if (!has_capability('mod/glossary:approve', $context)) { 385 $approvedselect = " AND ge.approved = 1 "; 386 } 387 388 $params['timestart'] = $timestart; 389 $params['glossaryid'] = $cm->instance; 390 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); 404 405 if (!$entries) { 406 return; 407 } 408 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 */ 429 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; 444 445 $activities[$index++] = $tmpactivity; 446 } 447 448 return true; 449 } 450 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; 463 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 } 469 470 echo html_writer::start_tag('div', array('class'=>'glossary-activity-content')); 471 echo html_writer::start_tag('div', array('class'=>'glossary-activity-entry')); 472 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'); 483 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); 487 488 echo html_writer::start_tag('div', array('class'=>'user')); 489 echo $link .' - '. userdate($activity->timestamp); 490 echo html_writer::end_tag('div'); 491 492 echo html_writer::end_tag('div'); 493 494 echo html_writer::end_tag('div'); 495 return; 496 } 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; 512 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(); 519 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 } 529 530 if (!$ids) { 531 return false; 532 } 533 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 } 548 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(*)'; 555 556 $joins = array(' FROM {glossary_entries} ge '); 557 $joins[] = 'JOIN {user} u ON u.id = ge.userid '; 558 $fromsql = implode($joins, "\n"); 559 560 $params['timestart'] = $timestart; 561 $clausesql = ' WHERE ge.timemodified > :timestart '; 562 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)); 570 571 if (empty($entries)) { 572 return false; 573 } 574 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 } 600 601 return true; 602 } 603 604 /** 605 * @global object 606 * @param object $log 607 */ 608 function glossary_log_info($log) { 609 global $DB; 610 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)); 614 } 615 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; 624 } 625 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; 635 636 require_once($CFG->dirroot.'/rating/lib.php'); 637 638 $ratingoptions = new stdClass; 639 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'; 645 646 $ratingoptions->userid = $userid; 647 $ratingoptions->aggregationmethod = $glossary->assessed; 648 $ratingoptions->scaleid = $glossary->scale; 649 $ratingoptions->itemtable = 'glossary_entries'; 650 $ratingoptions->itemtableusercolumn = 'userid'; 651 652 $rm = new rating_manager(); 653 return $rm->get_user_grades($ratingoptions); 654 } 655 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 ); 677 } 678 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; 694 695 // Check the component is mod_forum 696 if ($params['component'] != 'mod_glossary') { 697 throw new rating_exception('invalidcomponent'); 698 } 699 700 // Check the ratingarea is post (the only rating area in forum) 701 if ($params['ratingarea'] != 'entry') { 702 throw new rating_exception('invalidratingarea'); 703 } 704 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 } 709 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 } 720 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 } 725 726 //check that the submitted rating is valid for the scale 727 728 // lower limit 729 if ($params['rating'] < 0 && $params['rating'] != RATING_UNSET_RATING) { 730 throw new rating_exception('invalidnum'); 731 } 732 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 } 749 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 } 756 757 $cm = get_coursemodule_from_instance('glossary', $info->glossaryid, $info->course, false, MUST_EXIST); 758 $context = context_module::instance($cm->id, MUST_EXIST); 759 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 } 764 765 return true; 766 } 767 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'); 779 780 if (!$glossary->assessed) { 781 glossary_grade_item_update($glossary); 782 783 } else if ($grades = glossary_get_user_grades($glossary, $userid)) { 784 glossary_grade_item_update($glossary, $grades); 785 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); 791 792 } else { 793 glossary_grade_item_update($glossary); 794 } 795 } 796 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'); 808 809 $params = array('itemname'=>$glossary->name, 'idnumber'=>$glossary->cmidnumber); 810 811 if (!$glossary->assessed or $glossary->scale == 0) { 812 $params['gradetype'] = GRADE_TYPE_NONE; 813 814 } else if ($glossary->scale > 0) { 815 $params['gradetype'] = GRADE_TYPE_VALUE; 816 $params['grademax'] = $glossary->scale; 817 $params['grademin'] = 0; 818 819 } else if ($glossary->scale < 0) { 820 $params['gradetype'] = GRADE_TYPE_SCALE; 821 $params['scaleid'] = -$glossary->scale; 822 } 823 824 if ($grades === 'reset') { 825 $params['reset'] = true; 826 $grades = NULL; 827 } 828 829 return grade_update('mod/glossary', $glossary->course, 'mod', 'glossary', $glossary->id, 0, $grades, $params); 830 } 831 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'); 841 842 return grade_update('mod/glossary', $glossary->course, 'mod', 'glossary', $glossary->id, 0, NULL, array('deleted'=>1)); 843 } 844 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; 854 855 $return = false; 856 857 $rec = $DB->get_record("glossary", array("id"=>$glossaryid, "scale"=>-$scaleid)); 858 859 if (!empty($rec) && !empty($scaleid)) { 860 $return = true; 861 } 862 863 return $return; 864 } 865 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; 877 878 if ($scaleid and $DB->record_exists('glossary', array('scale'=>-$scaleid))) { 879 return true; 880 } else { 881 return false; 882 } 883 } 884 885 ////////////////////////////////////////////////////////////////////////////////////// 886 /// Any other glossary functions go here. Each of them must have a name that 887 /// starts with glossary_ 888 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; 901 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 } 923 924 if (empty($rec->showtabs)) { 925 glossary_set_default_visible_tabs($rec); 926 } 927 } 928 } 929 } 930 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 } 939 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 } 951 952 //Now everything is ready in glossary_formats table 953 $formats = $DB->get_records("glossary_formats"); 954 955 return $formats; 956 } 957 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 } 970 } 971 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 } 985 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)); 990 } 991 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; 1001 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 } 1007 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 } 1013 1014 $conceptlower = core_text::strtolower(trim($concept)); 1015 1016 $params = array('courseid1'=>$courseid, 'courseid2'=>$courseid, 'conceptlower'=>$conceptlower, 'concept'=>$concept); 1017 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); 1032 } 1033 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 } 1061 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; 1073 } 1074 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; 1085 1086 require_once($CFG->libdir . '/filelib.php'); 1087 1088 echo $OUTPUT->heading(strip_tags($entry->concept), 4); 1089 1090 $definition = $entry->definition; 1091 1092 $definition = '<span class="nolink">' . strip_tags($definition) . '</span>'; 1093 1094 $context = context_module::instance($cm->id); 1095 $definition = file_rewrite_pluginfile_urls($definition, 'pluginfile.php', $context->id, 'mod_glossary', 'entry', $entry->id); 1096 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 />'; 1105 } 1106 1107 /** 1108 * Print glossary concept/term as a heading <h4> 1109 * @param object $entry 1110 */ 1111 function glossary_print_entry_concept($entry, $return=false) { 1112 global $OUTPUT; 1113 1114 $text = $OUTPUT->heading(format_string($entry->concept), 4); 1115 if (!empty($entry->highlight)) { 1116 $text = highlight($entry->highlight, $text); 1117 } 1118 1119 if ($return) { 1120 return $text; 1121 } else { 1122 echo $text; 1123 } 1124 } 1125 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; 1135 1136 $definition = $entry->definition; 1137 1138 // Do not link self. 1139 $GLOSSARY_EXCLUDEENTRY = $entry->id; 1140 1141 $context = context_module::instance($cm->id); 1142 $definition = file_rewrite_pluginfile_urls($definition, 'pluginfile.php', $context->id, 'mod_glossary', 'entry', $entry->id); 1143 1144 $options = new stdClass(); 1145 $options->para = false; 1146 $options->trusted = $entry->definitiontrust; 1147 $options->context = $context; 1148 $options->overflowdiv = true; 1149 1150 $text = format_text($definition, $entry->definitionformat, $options); 1151 1152 // Stop excluding concepts from autolinking 1153 unset($GLOSSARY_EXCLUDEENTRY); 1154 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; 1162 } 1163 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; 1178 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 } 1198 } 1199 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; 1216 1217 $context = context_module::instance($cm->id); 1218 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; 1222 1223 1224 $return = '<span class="commands">'; 1225 // Differentiate links for each entry. 1226 $altsuffix = ': '.strip_tags(format_text($entry->concept)); 1227 1228 if (!$entry->approved) { 1229 $output = true; 1230 $return .= html_writer::tag('span', get_string('entryishidden','glossary'), 1231 array('class' => 'glossary-hidden-note')); 1232 } 1233 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&eid='.$entry->id.'&mode='.$mode. 1238 '&hook='.urlencode($hook).'&sesskey='.sesskey(). 1239 '"><img src="'.$OUTPUT->pix_url('t/block').'" class="smallicon" alt="'. 1240 get_string('disapprove','glossary').$altsuffix.'" /></a>'; 1241 } 1242 1243 $iscurrentuser = ($entry->userid == $USER->id); 1244 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.'&prevmode='.$mode.'&hook='.urlencode($hook).'"><img src="'.$OUTPUT->pix_url('export', 'glossary').'" class="smallicon" alt="'.get_string('exporttomainglossary','glossary').$altsuffix.'" /></a>'; 1252 } 1253 } 1254 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 } 1260 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&mode=delete&entry=$entry->id&prevmode=$mode&hook=".urlencode($hook)."\"><img src=\""; 1268 $return .= $icon; 1269 $return .= "\" class=\"smallicon\" alt=\"" . get_string("delete") .$altsuffix."\" /></a>"; 1270 1271 $return .= "<a class='action-icon' title=\"" . get_string("edit") . "\" href=\"edit.php?cmid=$cm->id&id=$entry->id&mode=$mode&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'); 1280 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)) { 1290 1291 $button->set_formats(PORTFOLIO_FORMAT_RICHHTML); 1292 } else { 1293 $button->set_formats(PORTFOLIO_FORMAT_PLAINHTML); 1294 } 1295 1296 $return .= $button->to_html(PORTFOLIO_ADD_ICON_LINK); 1297 } 1298 $return .= '</span>'; 1299 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 } 1314 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 } 1325 } 1326 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 } 1363 } 1364 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 } 1388 } 1389 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; 1400 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 } 1414 } 1415 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; 1431 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 } 1443 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 { 1449 1450 $onlyvisible = ""; 1451 $onlyvisibletable = ""; 1452 } 1453 1454 if ($DB->sql_regex_supported()) { 1455 $REGEXP = $DB->sql_regex(true); 1456 $NOTREGEXP = $DB->sql_regex(false); 1457 } 1458 1459 $searchcond = array(); 1460 $params = array(); 1461 $i = 0; 1462 1463 $concat = $DB->sql_concat('e.concept', "' '", 'e.definition'); 1464 1465 1466 foreach ($searchterms as $searchterm) { 1467 $i++; 1468 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 1471 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 } 1480 1481 // TODO: +- may not work for non latin languages 1482 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]|$)"; 1488 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]|$)"; 1494 1495 } else { 1496 $searchcond[] = $DB->sql_like($concat, ":ss$i", false, true, $NOT); 1497 $params['ss'.$i] = "%$searchterm%"; 1498 } 1499 } 1500 1501 if (empty($searchcond)) { 1502 $totalcount = 0; 1503 return array(); 1504 } 1505 1506 $searchcond = implode(" AND ", $searchcond); 1507 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"; 1513 1514 return $DB->get_records_sql($sql, $params); 1515 } 1516 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; 1526 1527 $course = $DB->get_record("course", array("id"=>$glossary->course)); 1528 return glossary_search($course,$searchterms,$extended,$glossary); 1529 } 1530 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; 1547 1548 if (!$context = context_module::instance($cm->id, IGNORE_MISSING)) { 1549 return ''; 1550 } 1551 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); 1557 1558 } else { 1559 $filecontext = $context; 1560 } 1561 1562 $strattachment = get_string('attachment', 'glossary'); 1563 1564 $fs = get_file_storage(); 1565 1566 $imagereturn = ''; 1567 $output = ''; 1568 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); 1575 1576 if ($type == 'html') { 1577 $output .= "<a href=\"$path\">$iconimage</a> "; 1578 $output .= "<a href=\"$path\">".s($filename)."</a>"; 1579 $output .= "<br />"; 1580 1581 } else if ($type == 'text') { 1582 $output .= "$strattachment ".s($filename).":\n$path\n"; 1583 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 } 1596 1597 if ($type) { 1598 return $output; 1599 } else { 1600 echo $output; 1601 return $imagereturn; 1602 } 1603 } 1604 1605 //////////////////////////////////////////////////////////////////////////////// 1606 // File API // 1607 //////////////////////////////////////////////////////////////////////////////// 1608 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 ); 1624 } 1625 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; 1642 1643 if ($context->contextlevel != CONTEXT_MODULE) { 1644 return null; 1645 } 1646 1647 if (!isset($areas[$filearea])) { 1648 return null; 1649 } 1650 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 } 1655 1656 if (!$entry = $DB->get_record('glossary_entries', array('id' => $itemid))) { 1657 return null; 1658 } 1659 1660 if (!$glossary = $DB->get_record('glossary', array('id' => $cm->instance))) { 1661 return null; 1662 } 1663 1664 if ($glossary->defaultapproval and !$entry->approved and !has_capability('mod/glossary:approve', $context)) { 1665 return null; 1666 } 1667 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 } 1679 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 } 1686 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 } 1692 1693 $urlbase = $CFG->wwwroot.'/pluginfile.php'; 1694 1695 return new file_info_stored($browser, $filecontext, $storedfile, $urlbase, s($entry->concept), true, true, false, false); 1696 } 1697 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; 1714 1715 if ($context->contextlevel != CONTEXT_MODULE) { 1716 return false; 1717 } 1718 1719 require_course_login($course, true, $cm); 1720 1721 if ($filearea === 'attachment' or $filearea === 'entry') { 1722 $entryid = (int)array_shift($args); 1723 1724 require_course_login($course, true, $cm); 1725 1726 if (!$entry = $DB->get_record('glossary_entries', array('id'=>$entryid))) { 1727 return false; 1728 } 1729 1730 if (!$glossary = $DB->get_record('glossary', array('id'=>$cm->instance))) { 1731 return false; 1732 } 1733 1734 if ($glossary->defaultapproval and !$entry->approved and !has_capability('mod/glossary:approve', $context)) { 1735 return false; 1736 } 1737 1738 // this trickery here is because we need to support source glossary access 1739 1740 if ($entry->glossaryid == $cm->instance) { 1741 $filecontext = $context; 1742 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); 1748 1749 } else { 1750 return false; 1751 } 1752 1753 $relativepath = implode('/', $args); 1754 $fullpath = "/$filecontext->id/mod_glossary/$filearea/$entryid/$relativepath"; 1755 1756 $fs = get_file_storage(); 1757 if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) { 1758 return false; 1759 } 1760 1761 // finally send the file 1762 send_stored_file($file, 0, 0, true, $options); // download MUST be forced - security! 1763 1764 } else if ($filearea === 'export') { 1765 require_login($course, false, $cm); 1766 require_capability('mod/glossary:export', $context); 1767 1768 if (!$glossary = $DB->get_record('glossary', array('id'=>$cm->instance))) { 1769 return false; 1770 } 1771 1772 $cat = array_shift($args); 1773 $cat = clean_param($cat, PARAM_ALPHANUM); 1774 1775 $filename = clean_filename(strip_tags(format_string($glossary->name)).'.xml'); 1776 $content = glossary_generate_export_file($glossary, NULL, $cat); 1777 1778 send_file($content, $filename, 0, 0, true, true); 1779 } 1780 1781 return false; 1782 } 1783 1784 /** 1785 * 1786 */ 1787 function glossary_print_tabbed_table_end() { 1788 echo "</div></div>"; 1789 } 1790 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); 1804 1805 glossary_print_alphabet_links($cm, $glossary, $mode, $hook,$sortkey, $sortorder); 1806 1807 glossary_print_all_links($cm, $glossary, $mode, $hook); 1808 1809 glossary_print_sorting_links($cm, $mode, 'CREATION', 'asc'); 1810 } 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>'; 1820 } 1821 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>'; 1831 } 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 } 1844 1845 glossary_print_special_links($cm, $glossary, $mode, $hook); 1846 1847 glossary_print_alphabet_links($cm, $glossary, $mode, $hook, $sortkey, $sortorder); 1848 1849 glossary_print_all_links($cm, $glossary, $mode, $hook); 1850 } else { 1851 glossary_print_sorting_links($cm, $mode, $sortkey,$sortorder); 1852 } 1853 } 1854 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 } 1866 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); 1870 } 1871 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; 1882 1883 $context = context_module::instance($cm->id); 1884 1885 // Prepare format_string/text options 1886 $fmtoptions = array( 1887 'context' => $context); 1888 1889 echo '<table border="0" width="100%">'; 1890 echo '<tr>'; 1891 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>'; 1900 1901 echo '<td align="center" style="width:60%">'; 1902 echo '<b>'; 1903 1904 $menu = array(); 1905 $menu[GLOSSARY_SHOW_ALL_CATEGORIES] = get_string("allcategories","glossary"); 1906 $menu[GLOSSARY_SHOW_NOT_CATEGORISED] = get_string("notcategorised","glossary"); 1907 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 } 1924 1925 if ( $category ) { 1926 echo format_string($category->name, true, $fmtoptions); 1927 } else { 1928 if ( $hook == GLOSSARY_SHOW_NOT_CATEGORISED ) { 1929 1930 echo get_string("entrieswithoutcategory","glossary"); 1931 $selected = GLOSSARY_SHOW_NOT_CATEGORISED; 1932 1933 } elseif ( $hook == GLOSSARY_SHOW_ALL_CATEGORIES ) { 1934 1935 echo get_string("allcategories","glossary"); 1936 $selected = GLOSSARY_SHOW_ALL_CATEGORIES; 1937 1938 } 1939 } 1940 echo '</b></td>'; 1941 echo '<td align="center" style="width:20%">'; 1942 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); 1946 1947 echo '</td>'; 1948 echo '</tr>'; 1949 1950 echo '</table>'; 1951 } 1952 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&mode=$mode&hook=ALL\">$strallentries</a>"; 1969 } 1970 } 1971 } 1972 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&mode=$mode&hook=SPECIAL\">$strspecial</a> | "; 1989 } 1990 } 1991 } 1992 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&mode=$mode&hook=".urlencode($alphabet[$i])."&sortkey=$sortkey&sortorder=$sortorder\">$alphabet[$i]</a>"; 2010 } 2011 echo ' | '; 2012 } 2013 } 2014 } 2015 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; 2025 2026 $asc = get_string("ascending","glossary"); 2027 $desc = get_string("descending","glossary"); 2028 $bopen = '<b>'; 2029 $bclose = '</b>'; 2030 2031 $neworder = ''; 2032 $currentorder = ''; 2033 $currentsort = ''; 2034 if ( $sortorder ) { 2035 if ( $sortorder == 'asc' ) { 2036 $currentorder = $asc; 2037 $neworder = '&sortorder=desc'; 2038 $newordertitle = get_string('changeto', 'glossary', $desc); 2039 } else { 2040 $currentorder = $desc; 2041 $neworder = '&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 = '&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 = ''; 2060 2061 $sicon = ''; 2062 $sneworder = ''; 2063 2064 $sbtag = ''; 2065 $fbtag = ''; 2066 $fendbtag = ''; 2067 $sendbtag = ''; 2068 2069 $sendbtag = ''; 2070 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 } 2089 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"); 2095 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"); 2106 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&sortkey=$sorder$sneworder&mode=$mode\">$ssort$sicon</a>$sendbtag | ". 2115 "$fbtag<a title=\"$fsort $fordertitle\" href=\"$CFG->wwwroot/mod/glossary/view.php?id=$cm->id&sortkey=$forder$fneworder&mode=$mode\">$fsort$ficon</a>$fendbtag<br />"; 2116 } 2117 2118 /** 2119 * 2120 * @param object $entry0 2121 * @param object $entry1 2122 * @return int [-1 | 0 | 1] 2123 */ 2124 function glossary_sort_entries ( $entry0, $entry1 ) { 2125 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 } 2133 } 2134 2135 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 } 2149 } 2150 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; 2162 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 } 2177 2178 //If displayformat is present, override glossary->displayformat 2179 if ($displayformat < 0) { 2180 $dp = $glossary->displayformat; 2181 } else { 2182 $dp = $displayformat; 2183 } 2184 2185 //Get popupformatname 2186 $format = $DB->get_record('glossary_formats', array('name'=>$dp)); 2187 $displayformat = $format->popupformatname; 2188 2189 //Check displayformat variable and set to default if necessary 2190 if (!$displayformat) { 2191 $displayformat = 'dictionary'; 2192 } 2193 2194 $formatfile = $CFG->dirroot.'/mod/glossary/formats/'.$displayformat.'/'.$displayformat.'_format.php'; 2195 $functionname = 'glossary_show_entry_'.$displayformat; 2196 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>'; 2207 } 2208 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; 2228 2229 foreach ($entries as $entry) { 2230 $thisaliasesentry = array(); 2231 $thiscategoriesentry = array(); 2232 $thiscsventry = array($entry->concept, nl2br($entry->definition)); 2233 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; 2255 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; 2269 } 2270 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; 2280 2281 // Large exports are likely to take their time and memory. 2282 core_php_time_limit::raise(); 2283 raise_memory_limit(MEMORY_EXTRA); 2284 2285 $cm = get_coursemodule_from_instance('glossary', $glossary->id, $glossary->course); 2286 $context = context_module::instance($cm->id); 2287 2288 $co = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; 2289 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); 2306 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); 2342 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)); 2356 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 } 2364 2365 // Export files embedded in entries. 2366 $co .= glossary_xml_export_files('ENTRYFILES', 4, $context->id, 'entry', $entry->id); 2367 2368 // Export attachments. 2369 $co .= glossary_xml_export_files('ATTACHMENTFILES', 4, $context->id, 'attachment', $entry->id); 2370 2371 $co .= glossary_end_tag("ENTRY",3,true); 2372 } 2373 } 2374 $co .= glossary_end_tag("ENTRIES",2,true); 2375 2376 } 2377 2378 2379 $co .= glossary_end_tag("INFO",1,true); 2380 $co .= glossary_end_tag("GLOSSARY",0,true); 2381 2382 return $co; 2383 } 2384 /// Functions designed by Eloy Lafuente 2385 /// Functions to create, open and write header of the xml file 2386 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; 2397 2398 return xmlize($file_content, 0); 2399 } 2400 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; 2416 } 2417 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; 2432 } 2433 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; 2446 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; 2451 } 2452 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; 2479 } 2480 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; 2511 } 2512 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; 2523 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); 2530 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)); 2542 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 } 2568 } 2569 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=" ", $specialtext="", $specialvalue=-1, $previousandnext = true) { 2592 2593 $code = ''; 2594 2595 $showspecial = false; 2596 $specialselected = false; 2597 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 } 2606 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").":"; 2611 2612 $maxpage = (int)(($totalcount-1)/$perpage); 2613 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 } 2624 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 } 2637 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 } 2644 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 } 2660 2661 //Previous 2662 if ($page > 0 && $previousandnext) { 2663 $pagenum = $page - 1; 2664 $code .= " (<a href=\"{$baseurl}page=$pagenum\">".get_string("previous")."</a>) "; 2665 } 2666 2667 //Add first 2668 $code .= $firstpagecode; 2669 2670 $pagenum = $pagefrom; 2671 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 } 2682 2683 //Add last 2684 $code .= $lastpagecode; 2685 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 } 2691 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 } 2701 2702 //End html 2703 $code .= "</p>"; 2704 $code .= "</div>"; 2705 } 2706 2707 return $code; 2708 } 2709 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'); 2722 } 2723 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'); 2736 } 2737 2738 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')); 2747 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'); 2752 2753 $mform->addElement('checkbox', 'reset_glossary_notenrolled', get_string('deletenotenrolled', 'glossary')); 2754 $mform->disabledIf('reset_glossary_notenrolled', 'reset_glossary_all', 'checked'); 2755 2756 $mform->addElement('checkbox', 'reset_glossary_ratings', get_string('deleteallratings')); 2757 $mform->disabledIf('reset_glossary_ratings', 'reset_glossary_all', 'checked'); 2758 2759 $mform->addElement('checkbox', 'reset_glossary_comments', get_string('deleteallcomments')); 2760 $mform->disabledIf('reset_glossary_comments', 'reset_glossary_all', 'checked'); 2761 } 2762 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); 2769 } 2770 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; 2779 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 } 2785 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"; 2789 2790 if ($glossarys = $DB->get_records_sql($sql, array($courseid))) { 2791 foreach ($glossarys as $glossary) { 2792 glossary_grade_item_update($glossary, 'reset'); 2793 } 2794 } 2795 } 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'); 2807 2808 $componentstr = get_string('modulenameplural', 'glossary'); 2809 $status = array(); 2810 2811 $allentriessql = "SELECT e.id 2812 FROM {glossary_entries} e 2813 JOIN {glossary} g ON e.glossaryid = g.id 2814 WHERE g.course = ?"; 2815 2816 $allglossariessql = "SELECT g.id 2817 FROM {glossary} g 2818 WHERE g.course = ?"; 2819 2820 $params = array($data->courseid); 2821 2822 $fs = get_file_storage(); 2823 2824 $rm = new rating_manager(); 2825 $ratingdeloptions = new stdClass; 2826 $ratingdeloptions->component = 'mod_glossary'; 2827 $ratingdeloptions->ratingarea = 'entry'; 2828 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))) { 2832 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); 2837 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'); 2846 2847 //delete ratings 2848 $ratingdeloptions->contextid = $context->id; 2849 $rm->delete_ratings($ratingdeloptions); 2850 } 2851 } 2852 2853 // remove all grades from gradebook 2854 if (empty($data->reset_gradebook_grades)) { 2855 glossary_reset_gradebook($data->courseid); 2856 } 2857 2858 $status[] = array('component'=>$componentstr, 'item'=>get_string('resetglossariesall', 'glossary'), 'error'=>false); 2859 2860 } else if (!empty($data->reset_glossary_types)) { 2861 $mainentriessql = "$allentriessql AND g.mainglossary=1"; 2862 $secondaryentriessql = "$allentriessql AND g.mainglossary=0"; 2863 2864 $mainglossariessql = "$allglossariessql AND g.mainglossary=1"; 2865 $secondaryglossariessql = "$allglossariessql AND g.mainglossary=0"; 2866 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); 2871 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'); 2879 2880 //delete ratings 2881 $ratingdeloptions->contextid = $context->id; 2882 $rm->delete_ratings($ratingdeloptions); 2883 } 2884 } 2885 2886 // remove all grades from gradebook 2887 if (empty($data->reset_gradebook_grades)) { 2888 glossary_reset_gradebook($data->courseid, 'main'); 2889 } 2890 2891 $status[] = array('component'=>$componentstr, 'item'=>get_string('resetglossaries', 'glossary').': '.get_string('mainglossary', 'glossary'), 'error'=>false); 2892 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); 2901 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'); 2909 2910 //delete ratings 2911 $ratingdeloptions->contextid = $context->id; 2912 $rm->delete_ratings($ratingdeloptions); 2913 } 2914 } 2915 2916 // remove all grades from gradebook 2917 if (empty($data->reset_gradebook_grades)) { 2918 glossary_reset_gradebook($data->courseid, 'secondary'); 2919 } 2920 2921 $status[] = array('component'=>$componentstr, 'item'=>get_string('resetglossaries', 'glossary').': '.get_string('secondaryglossary', 'glossary'), 'error'=>false); 2922 } 2923 } 2924 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"; 2932 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)); 2942 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); 2946 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 } 2957 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); 2967 2968 //delete ratings 2969 $ratingdeloptions->contextid = $context->id; 2970 $rm->delete_ratings($ratingdeloptions); 2971 } 2972 } 2973 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 } 2980 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 } 2987 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 } 2993 2994 return $status; 2995 } 2996 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'); 3003 } 3004 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; 3021 3022 default: return null; 3023 } 3024 } 3025 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; 3041 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 } 3046 3047 $result=$type; // Default return value 3048 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 } 3058 3059 return $result; 3060 } 3061 3062 function glossary_extend_navigation($navigation, $course, $module, $cm) { 3063 global $CFG, $DB; 3064 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); 3068 3069 foreach ($showtabs as $showtabkey => $showtabvalue) { 3070 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 } 3090 } 3091 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; 3100 3101 $mode = optional_param('mode', '', PARAM_ALPHA); 3102 $hook = optional_param('hook', 'ALL', PARAM_CLEAN); 3103 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 } 3107 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 } 3111 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 } 3115 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 } 3119 3120 $glossary = $DB->get_record('glossary', array("id" => $PAGE->cm->instance)); 3121 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"); 3124 3125 $string = get_string('rsstype','forum'); 3126 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 } 3130 } 3131 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); 3154 } 3155 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); 3195 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; 3220 } 3221 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; 3234 } 3235 3236 /** 3237 * Return list of all glossary tabs. 3238 * @throws coding_exception 3239 * @return array 3240 */ 3241 function glossary_get_all_tabs() { 3242 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 ); 3248 } 3249 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; 3256 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 } 3280 3281 $DB->set_field('glossary_formats', 'showtabs', $showtabs, array('id' => $glossaryformat->id)); 3282 $glossaryformat->showtabs = $showtabs; 3283 } 3284 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); 3295 3296 return $showtabs; 3297 } 3298 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) { 3312 3313 // Completion trigger. 3314 $completion = new completion_info($course); 3315 $completion->set_module_viewed($cm); 3316 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(); 3327 } 3328 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) { 3339 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(); 3347 3348 } 3349 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()) { 3365 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 } 3373 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 } 3379 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); 3386 3387 // Fetching the entries. 3388 $count = $qb->count_records(); 3389 $entries = $qb->get_records(); 3390 3391 return array($entries, $count); 3392 } 3393 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()) { 3410 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 } 3417 3418 $qb->add_field('*', 'entries'); 3419 $qb->join_user(); 3420 $qb->add_user_fields(); 3421 $qb->limit($from, $limit); 3422 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. 3429 3430 // Fetching the entries. 3431 $count = $qb->count_records(); 3432 $entries = $qb->get_records(); 3433 3434 return array($entries, $count); 3435 } 3436 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()) { 3452 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 } 3459 3460 $qb->join_category($categoryid); 3461 $qb->join_user(); 3462 3463 // The first field must be the relationship ID when viewing all categories. 3464 if ($categoryid === GLOSSARY_SHOW_ALL_CATEGORIES) { 3465 $qb->add_field('id', 'entries_categories', 'cid'); 3466 } 3467 3468 $qb->add_field('*', 'entries'); 3469 $qb->add_field('categoryid', 'entries_categories'); 3470 $qb->add_user_fields(); 3471 3472 if ($categoryid === GLOSSARY_SHOW_ALL_CATEGORIES) { 3473 $qb->add_field('name', 'categories', 'categoryname'); 3474 $qb->order_by('name', 'categories'); 3475 3476 } else if ($categoryid === GLOSSARY_SHOW_NOT_CATEGORISED) { 3477 $qb->where('categoryid', 'entries_categories', null); 3478 } 3479 3480 // Sort on additional fields to avoid random ordering when entries share an ordering value. 3481 $qb->order_by('concept', 'entries'); 3482 $qb->order_by('id', 'entries', 'ASC'); 3483 $qb->limit($from, $limit); 3484 3485 // Fetching the entries. 3486 $count = $qb->count_records(); 3487 $entries = $qb->get_records(); 3488 3489 return array($entries, $count); 3490 } 3491 3492 /** 3493 * Returns the entries of a glossary by author. 3494 * 3495 * @param object $glossary The glossary. 3496 * @param context $context The context of the glossary. 3497 * @param string $letter The letter 3498 * @param string $field The field to search: FIRSTNAME or LASTNAME. 3499 * @param string $sort The sorting: ASC or DESC. 3500 * @param int $from Fetch records from. 3501 * @param int $limit Number of records to fetch. 3502 * @param array $options Accepts: 3503 * - (bool) includenotapproved. When false, includes the non-approved entries created by 3504 * the current user. When true, also includes the ones that the user has the permission to approve. 3505 * @return array The first element being the recordset, the second the number of entries. 3506 * @since Moodle 3.1 3507 */ 3508 function glossary_get_entries_by_author($glossary, $context, $letter, $field, $sort, $from, $limit, $options = array()) { 3509 3510 $firstnamefirst = $field === 'FIRSTNAME'; 3511 $qb = new mod_glossary_entry_query_builder($glossary); 3512 if ($letter != 'ALL' && $letter != 'SPECIAL' && core_text::strlen($letter)) { 3513 $qb->filter_by_author_letter($letter, $firstnamefirst); 3514 } 3515 if ($letter == 'SPECIAL') { 3516 $qb->filter_by_author_non_letter($firstnamefirst); 3517 } 3518 3519 if (!empty($options['includenotapproved']) && has_capability('mod/glossary:approve', $context)) { 3520 $qb->filter_by_non_approved(mod_glossary_entry_query_builder::NON_APPROVED_ALL); 3521 } else { 3522 $qb->filter_by_non_approved(mod_glossary_entry_query_builder::NON_APPROVED_SELF); 3523 } 3524 3525 $qb->add_field('*', 'entries'); 3526 $qb->join_user(true); 3527 $qb->add_user_fields(); 3528 $qb->order_by_author($firstnamefirst, $sort); 3529 $qb->order_by('concept', 'entries'); 3530 $qb->order_by('id', 'entries', 'ASC'); // Sort on ID to avoid random ordering when entries share an ordering value. 3531 $qb->limit($from, $limit); 3532 3533 // Fetching the entries. 3534 $count = $qb->count_records(); 3535 $entries = $qb->get_records(); 3536 3537 return array($entries, $count); 3538 } 3539 3540 /** 3541 * Returns the entries of a glossary by category. 3542 * 3543 * @param object $glossary The glossary. 3544 * @param context $context The context of the glossary. 3545 * @param int $authorid The author ID. 3546 * @param string $order The mode of ordering: CONCEPT, CREATION or UPDATE. 3547 * @param string $sort The direction of the ordering: ASC or DESC. 3548 * @param int $from Fetch records from. 3549 * @param int $limit Number of records to fetch. 3550 * @param array $options Accepts: 3551 * - (bool) includenotapproved. When false, includes the non-approved entries created by 3552 * the current user. When true, also includes the ones that the user has the permission to approve. 3553 * @return array The first element being the recordset, the second the number of entries. 3554 * @since Moodle 3.1 3555 */ 3556 function glossary_get_entries_by_author_id($glossary, $context, $authorid, $order, $sort, $from, $limit, $options = array()) { 3557 3558 $qb = new mod_glossary_entry_query_builder($glossary); 3559 if (!empty($options['includenotapproved']) && has_capability('mod/glossary:approve', $context)) { 3560 $qb->filter_by_non_approved(mod_glossary_entry_query_builder::NON_APPROVED_ALL); 3561 } else { 3562 $qb->filter_by_non_approved(mod_glossary_entry_query_builder::NON_APPROVED_SELF); 3563 } 3564 3565 $qb->add_field('*', 'entries'); 3566 $qb->join_user(true); 3567 $qb->add_user_fields(); 3568 $qb->where('id', 'user', $authorid); 3569 3570 if ($order == 'CREATION') { 3571 $qb->order_by('timecreated', 'entries', $sort); 3572 } else if ($order == 'UPDATE') { 3573 $qb->order_by('timemodified', 'entries', $sort); 3574 } else { 3575 $qb->order_by('concept', 'entries', $sort); 3576 } 3577 $qb->order_by('id', 'entries', $sort); // Sort on ID to avoid random ordering when entries share an ordering value. 3578 3579 $qb->limit($from, $limit); 3580 3581 // Fetching the entries. 3582 $count = $qb->count_records(); 3583 $entries = $qb->get_records(); 3584 3585 return array($entries, $count); 3586 } 3587 3588 /** 3589 * Returns the authors in a glossary 3590 * 3591 * @param object $glossary The glossary. 3592 * @param context $context The context of the glossary. 3593 * @param int $limit Number of records to fetch. 3594 * @param int $from Fetch records from. 3595 * @param array $options Accepts: 3596 * - (bool) includenotapproved. When false, includes self even if all of their entries require approval. 3597 * When true, also includes authors only having entries pending approval. 3598 * @return array The first element being the recordset, the second the number of entries. 3599 * @since Moodle 3.1 3600 */ 3601 function glossary_get_authors($glossary, $context, $limit, $from, $options = array()) { 3602 global $DB, $USER; 3603 3604 $params = array(); 3605 $userfields = user_picture::fields('u', null); 3606 3607 $approvedsql = '(ge.approved <> 0 OR ge.userid = :myid)'; 3608 $params['myid'] = $USER->id; 3609 if (!empty($options['includenotapproved']) && has_capability('mod/glossary:approve', $context)) { 3610 $approvedsql = '1 = 1'; 3611 } 3612 3613 $sqlselectcount = "SELECT COUNT(DISTINCT(u.id))"; 3614 $sqlselect = "SELECT DISTINCT(u.id) AS userId, $userfields"; 3615 $sql = " FROM {user} u 3616 JOIN {glossary_entries} ge 3617 ON ge.userid = u.id 3618 AND (ge.glossaryid = :gid1 OR ge.sourceglossaryid = :gid2) 3619 AND $approvedsql"; 3620 $ordersql = " ORDER BY u.lastname, u.firstname"; 3621 3622 $params['gid1'] = $glossary->id; 3623 $params['gid2'] = $glossary->id; 3624 3625 $count = $DB->count_records_sql($sqlselectcount . $sql, $params); 3626 $users = $DB->get_recordset_sql($sqlselect . $sql . $ordersql, $params, $from, $limit); 3627 3628 return array($users, $count); 3629 } 3630 3631 /** 3632 * Returns the categories of a glossary. 3633 * 3634 * @param object $glossary The glossary. 3635 * @param int $from Fetch records from. 3636 * @param int $limit Number of records to fetch. 3637 * @return array The first element being the recordset, the second the number of entries. 3638 * @since Moodle 3.1 3639 */ 3640 function glossary_get_categories($glossary, $from, $limit) { 3641 global $DB; 3642 3643 $count = $DB->count_records('glossary_categories', array('glossaryid' => $glossary->id)); 3644 $categories = $DB->get_recordset('glossary_categories', array('glossaryid' => $glossary->id), 'name ASC', '*', $from, $limit); 3645 3646 return array($categories, $count); 3647 } 3648 3649 /** 3650 * Get the SQL where clause for searching terms. 3651 * 3652 * Note that this does not handle invalid or too short terms. 3653 * 3654 * @param array $terms Array of terms. 3655 * @param bool $fullsearch Whether or not full search should be enabled. 3656 * @param int $glossaryid The ID of a glossary to reduce the search results. 3657 * @return array The first element being the where clause, the second array of parameters. 3658 * @since Moodle 3.1 3659 */ 3660 function glossary_get_search_terms_sql(array $terms, $fullsearch = true, $glossaryid = null) { 3661 global $DB; 3662 static $i = 0; 3663 3664 if ($DB->sql_regex_supported()) { 3665 $regexp = $DB->sql_regex(true); 3666 $notregexp = $DB->sql_regex(false); 3667 } 3668 3669 $params = array(); 3670 $conditions = array(); 3671 3672 foreach ($terms as $searchterm) { 3673 $i++; 3674 3675 $not = false; // Initially we aren't going to perform NOT LIKE searches, only MSSQL and Oracle 3676 // will use it to simulate the "-" operator with LIKE clause. 3677 3678 if (empty($fullsearch)) { 3679 // With fullsearch disabled, look only within concepts and aliases. 3680 $concat = $DB->sql_concat('ge.concept', "' '", "COALESCE(al.alias, :emptychar{$i})"); 3681 } else { 3682 // With fullsearch enabled, look also within definitions. 3683 $concat = $DB->sql_concat('ge.concept', "' '", 'ge.definition', "' '", "COALESCE(al.alias, :emptychar{$i})"); 3684 } 3685 $params['emptychar' . $i] = ''; 3686 3687 // Under Oracle and MSSQL, trim the + and - operators and perform simpler LIKE (or NOT LIKE) queries. 3688 if (!$DB->sql_regex_supported()) { 3689 if (substr($searchterm, 0, 1) === '-') { 3690 $not = true; 3691 } 3692 $searchterm = trim($searchterm, '+-'); 3693 } 3694 3695 if (substr($searchterm, 0, 1) === '+') { 3696 $searchterm = trim($searchterm, '+-'); 3697 $conditions[] = "$concat $regexp :searchterm{$i}"; 3698 $params['searchterm' . $i] = '(^|[^a-zA-Z0-9])' . preg_quote($searchterm, '|') . '([^a-zA-Z0-9]|$)'; 3699 3700 } else if (substr($searchterm, 0, 1) === "-") { 3701 $searchterm = trim($searchterm, '+-'); 3702 $conditions[] = "$concat $notregexp :searchterm{$i}"; 3703 $params['searchterm' . $i] = '(^|[^a-zA-Z0-9])' . preg_quote($searchterm, '|') . '([^a-zA-Z0-9]|$)'; 3704 3705 } else { 3706 $conditions[] = $DB->sql_like($concat, ":searchterm{$i}", false, true, $not); 3707 $params['searchterm' . $i] = '%' . $DB->sql_like_escape($searchterm) . '%'; 3708 } 3709 } 3710 3711 // Reduce the search results by restricting it to one glossary. 3712 if (isset($glossaryid)) { 3713 $conditions[] = 'ge.glossaryid = :glossaryid'; 3714 $params['glossaryid'] = $glossaryid; 3715 } 3716 3717 // When there are no conditions we add a negative one to ensure that we don't return anything. 3718 if (empty($conditions)) { 3719 $conditions[] = '1 = 2'; 3720 } 3721 3722 $where = implode(' AND ', $conditions); 3723 return array($where, $params); 3724 } 3725 3726 3727 /** 3728 * Returns the entries of a glossary by search. 3729 * 3730 * @param object $glossary The glossary. 3731 * @param context $context The context of the glossary. 3732 * @param string $query The search query. 3733 * @param bool $fullsearch Whether or not full search is required. 3734 * @param string $order The mode of ordering: CONCEPT, CREATION or UPDATE. 3735 * @param string $sort The direction of the ordering: ASC or DESC. 3736 * @param int $from Fetch records from. 3737 * @param int $limit Number of records to fetch. 3738 * @param array $options Accepts: 3739 * - (bool) includenotapproved. When false, includes the non-approved entries created by 3740 * the current user. When true, also includes the ones that the user has the permission to approve. 3741 * @return array The first element being the recordset, the second the number of entries. 3742 * @since Moodle 3.1 3743 */ 3744 function glossary_get_entries_by_search($glossary, $context, $query, $fullsearch, $order, $sort, $from, $limit, 3745 $options = array()) { 3746 global $DB, $USER; 3747 3748 // Remove too little terms. 3749 $terms = explode(' ', $query); 3750 foreach ($terms as $key => $term) { 3751 if (strlen(trim($term, '+-')) < 2) { 3752 unset($terms[$key]); 3753 } 3754 } 3755 3756 list($searchcond, $params) = glossary_get_search_terms_sql($terms, $fullsearch, $glossary->id); 3757 3758 $userfields = user_picture::fields('u', null, 'userdataid', 'userdata'); 3759 3760 // Need one inner view here to avoid distinct + text. 3761 $sqlwrapheader = 'SELECT ge.*, ge.concept AS glossarypivot, ' . $userfields . ' 3762 FROM {glossary_entries} ge 3763 LEFT JOIN {user} u ON u.id = ge.userid 3764 JOIN ( '; 3765 $sqlwrapfooter = ' ) gei ON (ge.id = gei.id)'; 3766 $sqlselect = "SELECT DISTINCT ge.id"; 3767 $sqlfrom = "FROM {glossary_entries} ge 3768 LEFT JOIN {glossary_alias} al ON al.entryid = ge.id"; 3769 3770 if (!empty($options['includenotapproved']) && has_capability('mod/glossary:approve', $context)) { 3771 $approvedsql = ''; 3772 } else { 3773 $approvedsql = 'AND (ge.approved <> 0 OR ge.userid = :myid)'; 3774 $params['myid'] = $USER->id; 3775 } 3776 3777 if ($order == 'CREATION') { 3778 $sqlorderby = "ORDER BY ge.timecreated $sort"; 3779 } else if ($order == 'UPDATE') { 3780 $sqlorderby = "ORDER BY ge.timemodified $sort"; 3781 } else { 3782 $sqlorderby = "ORDER BY ge.concept $sort"; 3783 } 3784 $sqlorderby .= " , ge.id ASC"; // Sort on ID to avoid random ordering when entries share an ordering value. 3785 3786 $sqlwhere = "WHERE ($searchcond) $approvedsql"; 3787 3788 // Fetching the entries. 3789 $count = $DB->count_records_sql("SELECT COUNT(DISTINCT(ge.id)) $sqlfrom $sqlwhere", $params); 3790 3791 $query = "$sqlwrapheader $sqlselect $sqlfrom $sqlwhere $sqlwrapfooter $sqlorderby"; 3792 $entries = $DB->get_recordset_sql($query, $params, $from, $limit); 3793 3794 return array($entries, $count); 3795 } 3796 3797 /** 3798 * Returns the entries of a glossary by term. 3799 * 3800 * @param object $glossary The glossary. 3801 * @param context $context The context of the glossary. 3802 * @param string $term The term we are searching for, a concept or alias. 3803 * @param int $from Fetch records from. 3804 * @param int $limit Number of records to fetch. 3805 * @param array $options Accepts: 3806 * - (bool) includenotapproved. When false, includes the non-approved entries created by 3807 * the current user. When true, also includes the ones that the user has the permission to approve. 3808 * @return array The first element being the recordset, the second the number of entries. 3809 * @since Moodle 3.1 3810 */ 3811 function glossary_get_entries_by_term($glossary, $context, $term, $from, $limit, $options = array()) { 3812 3813 // Build the query. 3814 $qb = new mod_glossary_entry_query_builder($glossary); 3815 if (!empty($options['includenotapproved']) && has_capability('mod/glossary:approve', $context)) { 3816 $qb->filter_by_non_approved(mod_glossary_entry_query_builder::NON_APPROVED_ALL); 3817 } else { 3818 $qb->filter_by_non_approved(mod_glossary_entry_query_builder::NON_APPROVED_SELF); 3819 } 3820 3821 $qb->add_field('*', 'entries'); 3822 $qb->join_alias(); 3823 $qb->join_user(); 3824 $qb->add_user_fields(); 3825 $qb->filter_by_term($term); 3826 3827 $qb->order_by('concept', 'entries'); 3828 $qb->order_by('id', 'entries'); // Sort on ID to avoid random ordering when entries share an ordering value. 3829 $qb->limit($from, $limit); 3830 3831 // Fetching the entries. 3832 $count = $qb->count_records(); 3833 $entries = $qb->get_records(); 3834 3835 return array($entries, $count); 3836 } 3837 3838 /** 3839 * Returns the entries to be approved. 3840 * 3841 * @param object $glossary The glossary. 3842 * @param context $context The context of the glossary. 3843 * @param string $letter The letter, or ALL, or SPECIAL. 3844 * @param string $order The mode of ordering: CONCEPT, CREATION or UPDATE. 3845 * @param string $sort The direction of the ordering: ASC or DESC. 3846 * @param int $from Fetch records from. 3847 * @param int $limit Number of records to fetch. 3848 * @return array The first element being the recordset, the second the number of entries. 3849 * @since Moodle 3.1 3850 */ 3851 function glossary_get_entries_to_approve($glossary, $context, $letter, $order, $sort, $from, $limit) { 3852 3853 $qb = new mod_glossary_entry_query_builder($glossary); 3854 if ($letter != 'ALL' && $letter != 'SPECIAL' && core_text::strlen($letter)) { 3855 $qb->filter_by_concept_letter($letter); 3856 } 3857 if ($letter == 'SPECIAL') { 3858 $qb->filter_by_concept_non_letter(); 3859 } 3860 3861 $qb->add_field('*', 'entries'); 3862 $qb->join_user(); 3863 $qb->add_user_fields(); 3864 $qb->filter_by_non_approved(mod_glossary_entry_query_builder::NON_APPROVED_ONLY); 3865 if ($order == 'CREATION') { 3866 $qb->order_by('timecreated', 'entries', $sort); 3867 } else if ($order == 'UPDATE') { 3868 $qb->order_by('timemodified', 'entries', $sort); 3869 } else { 3870 $qb->order_by('concept', 'entries', $sort); 3871 } 3872 $qb->order_by('id', 'entries', $sort); // Sort on ID to avoid random ordering when entries share an ordering value. 3873 $qb->limit($from, $limit); 3874 3875 // Fetching the entries. 3876 $count = $qb->count_records(); 3877 $entries = $qb->get_records(); 3878 3879 return array($entries, $count); 3880 } 3881 3882 /** 3883 * Fetch an entry. 3884 * 3885 * @param int $id The entry ID. 3886 * @return object|false The entry, or false when not found. 3887 * @since Moodle 3.1 3888 */ 3889 function glossary_get_entry_by_id($id) { 3890 3891 // Build the query. 3892 $qb = new mod_glossary_entry_query_builder(); 3893 $qb->add_field('*', 'entries'); 3894 $qb->join_user(); 3895 $qb->add_user_fields(); 3896 $qb->where('id', 'entries', $id); 3897 3898 // Fetching the entries. 3899 $entries = $qb->get_records(); 3900 if (empty($entries)) { 3901 return false; 3902 } 3903 return array_pop($entries); 3904 } 3905 3906 /** 3907 * Checks if the current user can see the glossary entry. 3908 * 3909 * @since Moodle 3.1 3910 * @param stdClass $entry 3911 * @param cm_info $cminfo 3912 * @return bool 3913 */ 3914 function glossary_can_view_entry($entry, $cminfo) { 3915 global $USER; 3916 3917 $cm = $cminfo->get_course_module_record(); 3918 $context = \context_module::instance($cm->id); 3919 3920 // Recheck uservisible although it should have already been checked in core_search. 3921 if ($cminfo->uservisible === false) { 3922 return false; 3923 } 3924 3925 // Check approval. 3926 if (empty($entry->approved) && $entry->userid != $USER->id && !has_capability('mod/glossary:approve', $context)) { 3927 return false; 3928 } 3929 3930 return true; 3931 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Aug 11 10:00:09 2016 | Cross-referenced by PHPXref 0.7.1 |