MDL-20700 coding style cleanup - cvs keywords removed, closign php tag removed, trail...
[moodle.git] / tag / coursetagslib.php
1 <?php
2 /**
3  * coursetagslib.php
4  * @author j.beedell@open.ac.uk July07
5  */
7 require_once $CFG->dirroot.'/tag/lib.php';
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  */
21 function coursetag_get_tags($courseid, $userid=0, $tagtype='', $numtags=0, $sort='name') {
23     global $CFG, $DB;
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     }
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' ";
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     }
54     if ($userid > 0) {
55         $sql .= "    AND tiuserid = :userid ";
56         $params['userid'] = $userid;
57     }
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";
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     }
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     }
88     return $return;
90 }
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  */
101 function coursetag_get_all_tags($sort='name', $numtags=0) {
103     global $CFG, $DB;
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     }
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     }
134     return $return;
137 /**
138  * Callback function for coursetag_get_tags() and coursetag_get_all_tags() only
139  * @uses $CFG
140  */
141 function coursetag_sort($a, $b) {
142     // originally from block_blog_tags
143     global $CFG;
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     }
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     }
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  */
169 function coursetag_print_cloud($tagcloud, $return=false, $max_size=180, $min_size=80) {
171     global $CFG;
173     if (empty($tagcloud)) {
174         return;
175     }
177     ksort($tagcloud);
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     }
188     $max = max($count);
189     $min = min($count);
191     $spread = $max - $min;
192     if (0 == $spread) { // we don't want to divide by zero
193         $spread = 1;
194     }
196     $step = ($max_size - $min_size)/($spread);
198     $systemcontext   = get_context_instance(CONTEXT_SYSTEM);
199     $can_manage_tags = has_capability('moodle/tag:manage', $systemcontext);
201     //prints the tag cloud
202     $output = '<ul class="tag-cloud inline-list">';
203     foreach ($tagcloud as $key => $tag) {
205         $size = $min_size + ((log10($tag->count) - $min) * $step);
206         $size = ceil($size);
208         $style = 'style="font-size: '.$size.'%"';
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         }
216         $href = 'href="'.$CFG->wwwroot.'/tag/index.php?id='.$tag->id.'"';
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         }
224         $tag_link = '<li><a '.$href.' '.$title.' '. $style .'>'.$tagname.'</a></li> ';
226         $output .= $tag_link;
228     }
229     $output .= '</ul>'."\n";
231     if ($return) {
232         return $output;
233     } else {
234         echo $output;
235     }
238 /**
239  * Returns javascript for use in tags block and supporting pages
240  * @param string $coursetagdivs comma separated divs ids
241  * @uses $CFG
242  */
243 function coursetag_get_jscript($coursetagdivs = '') {
244     global $CFG, $DB, $PAGE;
246     $PAGE->requires->js('tag/tag.js');
247     $PAGE->requires->strings_for_js(array('jserror1', 'jserror2'), 'block_tags');
249     if ($coursetagdivs) {
250         $PAGE->requires->js_function_call('set_course_tag_divs', $coursetagdivs);
251     }
253     if ($coursetags = $DB->get_records('tag', null, 'name ASC', 'name, id')) {
254         foreach ($coursetags as $key => $value) {
255             $PAGE->requires->js_function_call('set_course_tag', array($key));
256         }
257     }
259     $PAGE->requires->js('blocks/tags/coursetags.js');
261     return '';
264 /**
265  * Returns javascript to create the links in the tag block footer.
266  */
267 function coursetag_get_jscript_links($coursetagslinks) {
268     global $PAGE;
270     if (!empty($coursetagslinks)) {
271         foreach ($coursetagslinks as $a) {
272             $PAGE->requires->js_function_call('add_tag_footer_link', array('coursetagslinks', $a['title'], $a['onclick'], $a['text']))->on_dom_ready();
273         }
274     }
276     return '';
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  */
286 function coursetag_get_records($courseid, $userid) {
288     global $CFG, $DB;
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";
297     return $DB->get_records_sql($sql, array('userid'=>$userid, 'courseid'=>$courseid));
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  */
310 function coursetag_store_keywords($tags, $courseid, $userid=0, $tagtype='official', $myurl='') {
312     global $CFG;
314     if (is_array($tags) and !empty($tags)) {
315         foreach($tags as $tag) {
316             $tag = trim($tag);
317             if (strlen($tag) > 0) {
318                 //tag_set_add('course', $courseid, $tag, $userid); //deletes official tags
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;
330                 }
331                 //set type
332                 tag_type_set($tagid, $tagtype);
334                 //tag_instance entry
335                 tag_assign('course', $courseid, $tagid, $ordering, $userid);
337                 //logging - note only for user added tags
338                 if ($tagtype == 'default' and $myurl != '') {
339                     $url = $myurl.'?query='.urlencode($tag);
340                     add_to_log($courseid, 'coursetags', 'add', $url, 'Course tagged');
341                 }
342             }
343         }
344     }
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   */
356 function coursetag_delete_keyword($tagid, $userid, $courseid) {
358     global $CFG, $DB;
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     }
385 /**
386  * Get courses tagged with a tag
387  *
388  * @param int $tagid
389  * @return array of course objects
390  */
391 function coursetag_get_tagged_courses($tagid) {
393     global $DB;
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;
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  */
419 function coursetag_delete_course_tags($courseid, $showfeedback=false) {
421     global $DB, $OUTPUT;
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     }
434     if ($showfeedback) {
435         echo $OUTPUT->notification(get_string('deletedcoursetags', 'tag'));
436     }
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 /*
449 function coursetag_rss_feeds() {
451     global $CFG, $DB;
452     require_once($CFG->dirroot.'/lib/dmllib.php');
453     require_once($CFG->dirroot.'/lib/rsslib.php');
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 {
461         //  Load all the categories for use later on
462         $categories = $DB->get_records('course_categories');
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)) {
477             $items = array(); //contains rss data items for each user
478             foreach ($users as $user) {
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
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                     }
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                     }
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);
527                     //  Set path to RSS file
528                     $full_path = "$save_path/$file_name";
530                     mtrace("    Preparing to update RSS feed for $file_name");
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                     }
551                     if ($changes) {
552                         //  Now we know something has changed, write the new file
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                         }
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                         }
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     }
613     return $status;
615  */
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 /*
631 function 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;
658 */