[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/mod/glossary/ -> lib.php (source)

   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 &lt;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&amp;eid='.$entry->id.'&amp;mode='.$mode.
1238                     '&amp;hook='.urlencode($hook).'&amp;sesskey='.sesskey().
1239                     '"><img src="'.$OUTPUT->pix_url('t/block').'" class="smallicon" alt="'.
1240                     get_string('disapprove','glossary').$altsuffix.'" /></a>';
1241      }
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.'&amp;prevmode='.$mode.'&amp;hook='.urlencode($hook).'"><img src="'.$OUTPUT->pix_url('export', 'glossary').'" class="smallicon" alt="'.get_string('exporttomainglossary','glossary').$altsuffix.'" /></a>';
1252              }
1253          }
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&amp;mode=delete&amp;entry=$entry->id&amp;prevmode=$mode&amp;hook=".urlencode($hook)."\"><img src=\"";
1268              $return .= $icon;
1269              $return .= "\" class=\"smallicon\" alt=\"" . get_string("delete") .$altsuffix."\" /></a>";
1270  
1271              $return .= "<a class='action-icon' title=\"" . get_string("edit") . "\" href=\"edit.php?cmid=$cm->id&amp;id=$entry->id&amp;mode=$mode&amp;hook=".urlencode($hook)."\"><img src=\"" . $OUTPUT->pix_url('t/edit') . "\" class=\"smallicon\" alt=\"" . get_string("edit") .$altsuffix. "\" /></a>";
1272          } elseif ( $importedentry ) {
1273              $return .= "<font size=\"-1\">" . get_string("exportedentry","glossary") . "</font>";
1274          }
1275      }
1276      if (!empty($CFG->enableportfolios) && (has_capability('mod/glossary:exportentry', $context) || ($iscurrentuser && has_capability('mod/glossary:exportownentry', $context)))) {
1277          require_once($CFG->libdir . '/portfoliolib.php');
1278          $button = new portfolio_add_button();
1279          $button->set_callback_options('glossary_entry_portfolio_caller',  array('id' => $cm->id, 'entryid' => $entry->id), 'mod_glossary');
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&amp;mode=$mode&amp;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&amp;mode=$mode&amp;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&amp;mode=$mode&amp;hook=".urlencode($alphabet[$i])."&amp;sortkey=$sortkey&amp;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 = '&amp;sortorder=desc';
2038               $newordertitle = get_string('changeto', 'glossary', $desc);
2039           } else {
2040               $currentorder = $desc;
2041               $neworder = '&amp;sortorder=asc';
2042               $newordertitle = get_string('changeto', 'glossary', $asc);
2043           }
2044           $icon = " <img src=\"".$OUTPUT->pix_url($sortorder, 'glossary')."\" class=\"icon\" alt=\"$newordertitle\" />";
2045       } else {
2046           if ( $sortkey != 'CREATION' and $sortkey != 'UPDATE' and
2047                 $sortkey != 'FIRSTNAME' and $sortkey != 'LASTNAME' ) {
2048               $icon = "";
2049               $newordertitle = $asc;
2050           } else {
2051               $newordertitle = $desc;
2052               $neworder = '&amp;sortorder=desc';
2053               $icon = ' <img src="'.$OUTPUT->pix_url('asc', 'glossary').'" class="icon" alt="'.$newordertitle.'" />';
2054           }
2055       }
2056       $ficon     = '';
2057       $fneworder = '';
2058       $fbtag     = '';
2059       $fendbtag  = '';
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&amp;sortkey=$sorder$sneworder&amp;mode=$mode\">$ssort$sicon</a>$sendbtag | ".
2115                            "$fbtag<a title=\"$fsort $fordertitle\" href=\"$CFG->wwwroot/mod/glossary/view.php?id=$cm->id&amp;sortkey=$forder$fneworder&amp;mode=$mode\">$fsort$ficon</a>$fendbtag<br />";
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="&nbsp;", $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 .= "&nbsp;(<a  href=\"{$baseurl}page=$pagenum\">".get_string("previous")."</a>)&nbsp;";
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  }


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1