MDL-21400 finalising JS api - removing ->on_dom_ready (now bool param in js() and...
[moodle.git] / tag / coursetagslib.php
CommitLineData
38fb8190 1<?php
2/**
3 * coursetagslib.php
4 * @author j.beedell@open.ac.uk July07
5 */
6
7require_once $CFG->dirroot.'/tag/lib.php';
8
9/**
10 * Returns an ordered array of tags associated with visible courses
11 * (boosted replacement of get_all_tags() allowing association with user and tagtype).
12 *
13 * @uses $CFG
14 * @param int $courseid, a 0 will return all distinct tags for visible courses
15 * @param int $userid optional the user id, a default of 0 will return all users tags for the course
16 * @param string $tagtype optional 'official' or 'default', empty returns both tag types
17 * @param int $numtags optional number of tags to display, default of 80 is set in the block, 0 returns all
18 * @param string $sort optional selected sorting, default is alpha sort (name) also timemodified or popularity
19 * @return array
20 */
21function coursetag_get_tags($courseid, $userid=0, $tagtype='', $numtags=0, $sort='name') {
22
23 global $CFG, $DB;
24
25 // get visible course ids
26 $courselist = array();
27 if ($courseid === 0) {
28 if ($courses = $DB->get_records_select('course', 'visible=1 AND category>0', null, '', 'id')) {
29 foreach ($courses as $key => $value) {
30 $courselist[] = $key;
31 }
32 }
33 }
34
35 // get tags from the db ordered by highest count first
36 $params = array();
37 $sql = "SELECT id as tkey, name, id, tagtype, rawname, f.timemodified, flag, count
38 FROM {tag} t,
39 (SELECT tagid, MAX(timemodified) as timemodified, COUNT(id) as count
40 FROM {tag_instance}
41 WHERE itemtype = 'course' ";
42
43 if ($courseid > 0) {
44 $sql .= " AND itemid = :courseid ";
45 $params['courseid'] = $courseid;
46 } else {
47 if (!empty($courselist)) {
48 list($usql, $uparams) = $DB->get_in_or_equal($courselist, SQL_PARAMS_NAMED);
49 $sql .= "AND itemid $usql ";
50 $params = $params + $uparams;
51 }
52 }
53
54 if ($userid > 0) {
55 $sql .= " AND tiuserid = :userid ";
56 $params['userid'] = $userid;
57 }
58
59 $sql .= " GROUP BY tagid) f
60 WHERE t.id = f.tagid ";
61 if ($tagtype != '') {
62 $sql .= "AND tagtype = :tagtype ";
63 $params['tagtype'] = $tagtype;
64 }
65 $sql .= "ORDER BY count DESC, name ASC";
66
67 // limit the number of tags for output
68 if ($numtags == 0) {
69 $tags = $DB->get_records_sql($sql, $params);
70 } else {
71 $tags = $DB->get_records_sql($sql, $params, 0, $numtags);
72 }
73
74 // prepare the return
75 $return = array();
76 if ($tags) {
77 // sort the tag display order
78 if ($sort != 'popularity') {
79 $CFG->tagsort = $sort;
80 usort($tags, "coursetag_sort");
81 }
82 // avoid print_tag_cloud()'s ksort upsetting ordering by setting the key here
83 foreach ($tags as $value) {
84 $return[] = $value;
85 }
86 }
87
88 return $return;
89
90}
91
92/**
93 * Returns an ordered array of tags
94 * (replaces popular_tags_count() allowing sorting).
95 *
96 * @uses $CFG
97 * @param string $sort optional selected sorting, default is alpha sort (name) also timemodified or popularity
98 * @param int $numtags optional number of tags to display, default of 20 is set in the block, 0 returns all
99 * @return array
100 */
101function coursetag_get_all_tags($sort='name', $numtags=0) {
102
103 global $CFG, $DB;
104
105 // note that this selects all tags except for courses that are not visible
106 $sql = "SELECT id, name, id, tagtype, rawname, f.timemodified, flag, count
107 FROM {tag} t,
108 (SELECT tagid, MAX(timemodified) as timemodified, COUNT(id) as count
109 FROM {tag_instance} WHERE tagid NOT IN
110 (SELECT tagid FROM {tag_instance} ti, {course} c
111 WHERE c.visible = 0
112 AND ti.itemtype = 'course'
113 AND ti.itemid = c.id)
114 GROUP BY tagid) f
115 WHERE t.id = f.tagid
116 ORDER BY count DESC, name ASC";
117 if ($numtags == 0) {
118 $tags = $DB->get_records_sql($sql);
119 } else {
120 $tags = $DB->get_records_sql($sql, null, 0, $numtags);
121 }
122
123 $return = array();
124 if ($tags) {
125 if ($sort != 'popularity') {
126 $CFG->tagsort = $sort;
127 usort($tags, "coursetag_sort");
128 }
129 foreach ($tags as $value) {
130 $return[] = $value;
131 }
132 }
133
134 return $return;
135}
136
137/**
138 * Callback function for coursetag_get_tags() and coursetag_get_all_tags() only
139 * @uses $CFG
140 */
141function coursetag_sort($a, $b) {
142 // originally from block_blog_tags
143 global $CFG;
144
145 // set up the variable $tagsort as either 'name' or 'timemodified' only, 'popularity' does not need sorting
146 if (empty($CFG->tagsort)) {
147 $tagsort = 'name';
148 } else {
149 $tagsort = $CFG->tagsort;
150 }
151
152 if (is_numeric($a->$tagsort)) {
153 return ($a->$tagsort == $b->$tagsort) ? 0 : ($a->$tagsort > $b->$tagsort) ? 1 : -1;
154 } elseif (is_string($a->$tagsort)) {
155 return strcmp($a->$tagsort, $b->$tagsort);
156 } else {
157 return 0;
158 }
159}
160
161/**
162 * Prints a tag cloud
163 *
164 * @param array $tagcloud array of tag objects (fields: id, name, rawname, count and flag)
165 * @param int $max_size maximum text size, in percentage
166 * @param int $min_size minimum text size, in percentage
167 * @param $return if true return html string
168 */
169function coursetag_print_cloud($tagcloud, $return=false, $max_size=180, $min_size=80) {
170
171 global $CFG;
172
173 if (empty($tagcloud)) {
174 return;
175 }
176
177 ksort($tagcloud);
178
179 $count = array();
180 foreach ($tagcloud as $key => $value) {
181 if(!empty($value->count)) {
182 $count[$key] = log10($value->count);
183 } else {
184 $count[$key] = 0;
185 }
186 }
187
188 $max = max($count);
189 $min = min($count);
190
191 $spread = $max - $min;
192 if (0 == $spread) { // we don't want to divide by zero
193 $spread = 1;
194 }
195
196 $step = ($max_size - $min_size)/($spread);
197
198 $systemcontext = get_context_instance(CONTEXT_SYSTEM);
199 $can_manage_tags = has_capability('moodle/tag:manage', $systemcontext);
200
201 //prints the tag cloud
202 $output = '<ul class="tag-cloud inline-list">';
203 foreach ($tagcloud as $key => $tag) {
204
205 $size = $min_size + ((log10($tag->count) - $min) * $step);
206 $size = ceil($size);
207
208 $style = 'style="font-size: '.$size.'%"';
209
210 if ($tag->count > 1) {
211 $title = 'title="'.s(get_string('thingstaggedwith','tag', $tag)).'"';
212 } else {
213 $title = 'title="'.s(get_string('thingtaggedwith','tag', $tag)).'"';
214 }
215
216 $href = 'href="'.$CFG->wwwroot.'/tag/index.php?id='.$tag->id.'"';
217
218 //highlight tags that have been flagged as inappropriate for those who can manage them
219 $tagname = tag_display_name($tag);
220 if ($tag->flag > 0 && $can_manage_tags) {
221 $tagname = '<span class="flagged-tag">' . tag_display_name($tag) . '</span>';
222 }
223
224 $tag_link = '<li><a '.$href.' '.$title.' '. $style .'>'.$tagname.'</a></li> ';
225
226 $output .= $tag_link;
227
228 }
229 $output .= '</ul>'."\n";
230
231 if ($return) {
232 return $output;
233 } else {
234 echo $output;
235 }
236}
237
238/**
239 * Returns javascript for use in tags block and supporting pages
240 * @param string $coursetagdivs comma separated divs ids
241 * @uses $CFG
242 */
243function coursetag_get_jscript($coursetagdivs = '') {
25ddb7ef 244 global $CFG, $DB, $PAGE;
38fb8190 245
9dec75db 246 $PAGE->requires->js('/tag/tag.js');
1cc02867 247 $PAGE->requires->strings_for_js(array('jserror1', 'jserror2'), 'block_tags');
248
38fb8190 249 if ($coursetagdivs) {
1cc02867 250 $PAGE->requires->js_function_call('set_course_tag_divs', $coursetagdivs);
38fb8190 251 }
252
1cc02867 253 if ($coursetags = $DB->get_records('tag', null, 'name ASC', 'name, id')) {
38fb8190 254 foreach ($coursetags as $key => $value) {
1cc02867 255 $PAGE->requires->js_function_call('set_course_tag', array($key));
38fb8190 256 }
257 }
7264519e 258
9dec75db 259 $PAGE->requires->js('/blocks/tags/coursetags.js');
38fb8190 260
1cc02867 261 return '';
38fb8190 262}
263
264/**
265 * Returns javascript to create the links in the tag block footer.
266 */
267function coursetag_get_jscript_links($coursetagslinks) {
1cc02867 268 global $PAGE;
6bfe7aac 269
38fb8190 270 if (!empty($coursetagslinks)) {
38fb8190 271 foreach ($coursetagslinks as $a) {
593f9b87 272 $PAGE->requires->js_function_call('add_tag_footer_link', array('coursetagslinks', $a['title'], $a['onclick'], $a['text']), true);
38fb8190 273 }
38fb8190 274 }
275
1cc02867 276 return '';
38fb8190 277}
278
279/**
280 * Returns all tags created by a user for a course
281 *
282 * @uses $CFG
283 * @param int $courseid
284 * @param int $userid
285 */
286function coursetag_get_records($courseid, $userid) {
287
288 global $CFG, $DB;
289
290 $sql = "SELECT t.id, name, rawname
291 FROM {tag} t, {tag_instance} ti
292 WHERE t.id = ti.tagid
293 AND ti.tiuserid = :userid
294 AND ti.itemid = :courseid
295 ORDER BY name ASC";
296
297 return $DB->get_records_sql($sql, array('userid'=>$userid, 'courseid'=>$courseid));
298}
299
300/**
301 * Stores a tag for a course for a user
302 *
303 * @uses $CFG
304 * @param array $tags simple array of keywords to be stored
305 * @param integer $courseid
306 * @param integer $userid
307 * @param string $tagtype official or default only
308 * @param string $myurl optional for logging creation of course tags
309 */
310function coursetag_store_keywords($tags, $courseid, $userid=0, $tagtype='official', $myurl='') {
311
312 global $CFG;
313
314 if (is_array($tags) and !empty($tags)) {
38fb8190 315 foreach($tags as $tag) {
316 $tag = trim($tag);
317 if (strlen($tag) > 0) {
b91de8a5 318 //tag_set_add('course', $courseid, $tag, $userid); //deletes official tags
319
320 //add tag if does not exist
321 if (!$tagid = tag_get_id($tag)) {
322 $tag_id_array = tag_add(array($tag), $tagtype);
323 $tagid = $tag_id_array[moodle_strtolower($tag)];
324 }
325 //ordering
326 $ordering = 0;
327 if ($current_ids = tag_get_tags_ids('course', $courseid)) {
328 end($current_ids);
329 $ordering = key($current_ids) + 1;
38fb8190 330 }
b91de8a5 331 //set type
332 tag_type_set($tagid, $tagtype);
333
334 //tag_instance entry
335 tag_assign('course', $courseid, $tagid, $ordering, $userid);
336
337 //logging - note only for user added tags
38fb8190 338 if ($tagtype == 'default' and $myurl != '') {
b91de8a5 339 $url = $myurl.'?query='.urlencode($tag);
38fb8190 340 add_to_log($courseid, 'coursetags', 'add', $url, 'Course tagged');
341 }
38fb8190 342 }
343 }
344 }
345
346}
347
348/**
349 * Deletes a personal tag for a user for a course.
350 *
351 * @uses $CFG
352 * @param int $tagid
353 * @param int $userid
354 * @param int $courseid
355 */
356function coursetag_delete_keyword($tagid, $userid, $courseid) {
357
358 global $CFG, $DB;
359
360 $sql = "SELECT COUNT(*)
361 FROM {tag_instance}
362 WHERE tagid = $tagid
363 AND tiuserid = $userid
364 AND itemtype = 'course'
365 AND itemid = $courseid";
366 if ($DB->count_records_sql($sql) == 1) {
367 $sql = "tagid = $tagid
368 AND tiuserid = $userid
369 AND itemtype = 'course'
370 AND itemid = $courseid";
371 $DB->delete_records_select('tag_instance', $sql);
372 // if there are no other instances of the tag then consider deleting the tag as well
373 if (!$DB->record_exists('tag_instance', array('tagid' => $tagid))) {
374 // if the tag is a personal tag then delete it - don't do official tags
375 if ($DB->record_exists('tag', array('id' => $tagid, 'tagtype' => 'default'))) {
376 $DB->delete_records('tag', array('id' => $tagid, 'tagtype' => 'default'));
377 }
378 }
379 } else {
380 print_error("errordeleting", 'tag', '', $tagid);
381 }
382
383}
384
385/**
386 * Get courses tagged with a tag
387 *
388 * @param int $tagid
389 * @return array of course objects
390 */
391function coursetag_get_tagged_courses($tagid) {
392
393 global $DB;
394
395 $courses = array();
396 if ($crs = $DB->get_records_select('tag_instance', "tagid=:tagid AND itemtype='course'", array('tagid'=>$tagid))) {
397 foreach ($crs as $c) {
398 //this capability check was introduced to stop display of courses that a student could not
399 //view, but arguably it is best that when clicking on a tag, the tagged course summary should
400 //be seen and then if the student clicks on that they will be given the opportunity to join
401 //note courses not visible should not have their tagid sent to this function
402 //if (has_capability('moodle/course:view', get_context_instance(CONTEXT_COURSE, $c->itemid))) {
403 $course = $DB->get_record('course', array('id'=>$c->itemid));
404 $courses[$c->itemid] = $course;
405 //}
406 }
407 }
408 return $courses;
409
410}
411
412/**
413 * Course tagging function used only during the deletion of a
414 * course (called by lib/moodlelib.php) to clean up associated tags
415 *
416 * @param $courseid
417 * @param $showfeedback
418 */
419function coursetag_delete_course_tags($courseid, $showfeedback=false) {
420
8f6e7e6c 421 global $DB, $OUTPUT;
38fb8190 422
423 if ($tags = $DB->get_records_select('tag_instance', "itemtype='course' AND itemid=:courseid", array('courseid'=>$courseid))) {
424 foreach ($tags as $tag) {
425 //delete the course tag instance record
426 $DB->delete_records('tag_instance', array('tagid'=>$tag->tagid, 'itemtype'=>'course', 'itemid'=> $courseid));
427 // delete tag if there are no other tag_instance entries now
428 if (!($DB->record_exists('tag_instance', array('tagid'=>$tag->tagid)))) {
429 $DB->delete_records('tag', array('id'=>$tag->tagid));
430 }
431 }
432 }
433
434 if ($showfeedback) {
8f6e7e6c 435 echo $OUTPUT->notification(get_string('deletedcoursetags', 'tag'));
38fb8190 436 }
437}
438
439/**
440 * Function called by cron to create/update users rss feeds
441 *
442 * @uses $CFG
443 * @return true
444 *
445 * Function removed.
446 * rsslib.php needs updating to accept Dublin Core tags (dc/cc) input before this can work.
447 */
448/*
449function coursetag_rss_feeds() {
450
451 global $CFG, $DB;
452 require_once($CFG->dirroot.'/lib/dmllib.php');
453 require_once($CFG->dirroot.'/lib/rsslib.php');
454
455 $status = true;
456 mtrace(' Preparing to update all user unit tags RSS feeds');
457 if (empty($CFG->enablerssfeeds)) {
458 mtrace(' RSS DISABLED (admin variables - enablerssfeeds)');
459 } else {
460
461 // Load all the categories for use later on
462 $categories = $DB->get_records('course_categories');
463
464 // get list of users who have tagged a unit
465 $sql = "
466 SELECT DISTINCT u.id as userid, u.username, u.firstname, u.lastname, u.email
467 FROM {user} u, {course} c, {tag_instance} cti, {tag} t
468 WHERE c.id = cti.itemid
469 AND u.id = cti.tiuserid
470 AND t.id = cti.tagid
471 AND t.tagtype = 'personal'
472 AND u.confirmed = 1
473 AND u.deleted = 0
474 ORDER BY userid";
475 if ($users = $DB->get_records_sql($sql)) {
476
477 $items = array(); //contains rss data items for each user
478 foreach ($users as $user) {
479
480 // loop through each user, getting the data (tags for courses)
481 $sql = "
482 SELECT cti.id, c.id as courseid, c.fullname, c.shortname, c.category, t.rawname, cti.timemodified
483 FROM {course} c, {tag_instance} cti, {tag} t
484 WHERE c.id = cti.itemid
485 AND cti.tiuserid = :userid{$user->userid}
486 AND cti.tagid = t.id
487 AND t.tagtype = 'personal'
488 ORDER BY courseid";
489 if ($usertags = $DB->get_records_sql($sql, array('userid' => $user->userid))) {
490 $latest_date = 0; //latest date any tag was created by a user
491 $c = 0; //course identifier
492
493 foreach ($usertags as $usertag) {
494 if ($usertag->courseid != $c) {
495 $c = $usertag->courseid;
496 $items[$c] = new stdClass();
497 $items[$c]->title = $usertag->fullname . '(' . $usertag->shortname . ')';
498 $items[$c]->link = $CFG->wwwroot . '/course/view.php?name=' . $usertag->shortname;
499 $items[$c]->description = ''; //needs to be blank
500 $items[$c]->category = $categories[$usertag->category]->name;
501 $items[$c]->subject[] = $usertag->rawname;
502 $items[$c]->pubdate = $usertag->timemodified;
503 $items[$c]->tag = true;
504 } else {
505 $items[$c]->subject[] .= $usertag->rawname;
506 }
507 // Check and set the latest modified date.
508 $latest_date = $usertag->timemodified > $latest_date ? $usertag->timemodified : $latest_date;
509 }
510
511 // Setup some vars for use while creating the file
512 $path = $CFG->dataroot.'/1/usertagsrss/'.$user->userid;
513 $file_name = 'user_unit_tags_rss.xml';
514 $title = get_string('rsstitle', 'tag', ucwords(strtolower($user->firstname.' '.$user->lastname)));
515 $desc = get_string('rssdesc', 'tag');
516 // check that the path exists
517 if (!file_exists($path)) {
518 mtrace(' Creating folder '.$path);
519 check_dir_exists($path, TRUE, TRUE);
520 }
521
522 // create or update the feed for the user
523 // this functionality can be copied into seperate lib as in next two lines
524 //require_once($CFG->dirroot.'/local/ocilib.php');
525 //oci_create_rss_feed( $path, $file_name, $latest_date, $items, $title, $desc, $dc=true, $cc=false);
526
527 // Set path to RSS file
528 $full_path = "$save_path/$file_name";
529
530 mtrace(" Preparing to update RSS feed for $file_name");
531
532 // First let's make sure there is work to do by checking the time the file was last modified,
533 // if a course was update after the file was mofified
534 if (file_exists($full_path)) {
535 if ($lastmodified = filemtime($full_path)) {
536 mtrace(" XML File $file_name Created on ".date( "D, j M Y G:i:s T", $lastmodified ));
537 mtrace(' Lastest course modification on '.date( "D, j M Y G:i:s T", $latest_date ));
538 if ($latest_date > $lastmodified) {
539 mtrace(" XML File $file_name needs updating");
540 $changes = true;
541 } else {
542 mtrace(" XML File $file_name doesn't need updating");
543 $changes = false;
544 }
545 }
546 } else {
547 mtrace(" XML File $file_name needs updating");
548 $changes = true;
549 }
550
551 if ($changes) {
552 // Now we know something has changed, write the new file
553
554 if (!empty($items)) {
555 // First set rss feeds common headers
556 $header = rss_standard_header(strip_tags(format_string($title,true)),
557 $CFG->wwwroot,
558 $desc,
559 true, true);
560 // Now all the rss items
561 if (!empty($header)) {
562 $articles = rss_add_items($items,$dc,$cc);
563 }
564 // Now all rss feeds common footers
565 if (!empty($header) && !empty($articles)) {
566 $footer = rss_standard_footer();
567 }
568 // Now, if everything is ok, concatenate it
569 if (!empty($header) && !empty($articles) && !empty($footer)) {
570 $result = $header.$articles.$footer;
571 } else {
572 $result = false;
573 }
574 } else {
575 $result = false;
576 }
577
578 // Save the XML contents to file
579 if (!empty($result)) {
580 $rss_file = fopen($full_path, "w");
581 if ($rss_file) {
582 $status = fwrite ($rss_file, $result);
583 fclose($rss_file);
584 } else {
585 $status = false;
586 }
587 }
588
589 // Output result
590 if (empty($result)) {
591 // There was nothing to put into the XML file. Delete it!
592 if( is_file($full_path) ) {
593 mtrace(" There were no items for XML File $file_name. Deleting XML File");
594 unlink($full_path);
595 mtrace(" $full_path -> (deleted)");
596 } else {
597 mtrace(" There were no items for the XML File $file_name and no file to delete. Ignore.");
598 }
599 } else {
600 if (!empty($status)) {
601 mtrace(" $full_path -> OK");
602 } else {
603 mtrace(" $full_path -> FAILED");
604 }
605 }
606 }
607 //end of oci_create_rss_feed()
608 }
609 }
610 }
611 }
612
613 return $status;
614}
615 */
616
617/**
618 * Get official keywords for the <meta name="keywords"> in header.html
619 * use: echo '<meta name="keywords" content="'.coursetag_get_official_keywords($COURSE->id).'"/>';
620 * @uses $CFG
621 * @param int $courseid
622 * @return string
623 *
624 * Function removed but fully working
625 * This function is potentially useful to anyone wanting to improve search results for course pages.
626 * The idea is to add official tags (not personal tags to prevent their deletion) to all
627 * courses (facility not added yet) which will be automatically added to the page header to boost
628 * search engine specificity/ratings.
629 */
630/*
631function coursetag_get_official_keywords($courseid, $asarray=false) {
632 global $CFG;
633 $returnstr = '';
634 $sql = "SELECT t.id, name, rawname
635 FROM {tag} t, {tag_instance} ti
636 WHERE ti.itemid = :courseid
637 AND ti.itemtype = 'course'
638 AND t.tagtype = 'official'
639 AND ti.tagid = t.id
640 ORDER BY name ASC";
641 if ($tags = $DB->get_records_sql($sql, array('courseid' => $courseid))) {
642 if ($asarray) {
643 return $tags;
644 }
645 foreach ($tags as $tag) {
646 if( empty($CFG->keeptagnamecase) ) {
647 $textlib = textlib_get_instance();
648 $name = $textlib->strtotitle($tag->name);
649 } else {
650 $name = $tag->rawname;
651 }
652 $returnstr .= $name.', ';
653 }
654 $returnstr = rtrim($returnstr, ', ');
655 }
656 return $returnstr;
657}
658*/