f37e084f58a6a97f86d7d3299bdbce8aa62dd057
[moodle.git] / blog / lib.php
1 <?php //$Id$
3     /**
4      * Library of functions and constants for blog
5      */
6     require_once($CFG->libdir .'/blocklib.php');
7     require_once($CFG->libdir .'/pagelib.php');
8     require_once($CFG->dirroot .'/blog/rsslib.php');
9     require_once($CFG->dirroot .'/blog/blogpage.php');
10     require_once($CFG->dirroot.'/tag/lib.php');
12     /**
13      * Definition of blogcourse page type (blog page with course id present).
14      */
15     //not used at the moment, and may not need to be
16     define('PAGE_BLOG_COURSE_VIEW', 'blog_course-view');
19     /**
20      * Checks to see if user has visited blogpages before, if not, install 2
21      * default blocks (blog_menu and blog_tags).
22      */
23     function blog_check_and_install_blocks() {
24         global $USER, $DB;
26         if (isloggedin() && !isguest()) {
27             // if this user has not visited this page before
28             if (!get_user_preferences('blogpagesize')) {
29                 // find the correct ids for blog_menu and blog_from blocks
30                 $menublock = $DB->get_record('block', array('name'=>'blog_menu'));
31                 $tagsblock = $DB->get_record('block', array('name'=>'blog_tags'));
32                 // add those 2 into block_instance page
34                 // add blog_menu block
35                 $newblock = new object();
36                 $newblock->blockid  = $menublock->id;
37                 $newblock->pageid   = $USER->id;
38                 $newblock->pagetype = 'blog-view';
39                 $newblock->position = 'r';
40                 $newblock->weight   = 0;
41                 $newblock->visible  = 1;
42                 $DB->insert_record('block_instance', $newblock);
44                 // add blog_tags menu
45                 $newblock -> blockid = $tagsblock->id;
46                 $newblock -> weight  = 1;
47                 $DB->insert_record('block_instance', $newblock);
49                 // finally we set the page size pref
50                 set_user_preference('blogpagesize', 10);
51             }
52         }
53     }
56     /**
57      * Adaptation of isediting in moodlelib.php for blog module
58      * @return bool
59      */
60     function blog_isediting() {
61         global $SESSION;
63         return !empty($SESSION->blog_editing_enabled);
64     }
67     /**
68      *  This function is in lib and not in BlogInfo because entries being searched
69      *   might be found in any number of blogs rather than just one.
70      *
71      *   $@param ...
72      */
73     function blog_print_html_formatted_entries($postid, $filtertype, $filterselect, $tagid, $tag) {
75         global $CFG, $USER;
77         $blogpage  = optional_param('blogpage', 0, PARAM_INT);
78         $bloglimit = optional_param('limit', get_user_preferences('blogpagesize', 10), PARAM_INT);
79         $start     = $blogpage * $bloglimit;
81         $sitecontext = get_context_instance(CONTEXT_SYSTEM);
83         $morelink = '<br />&nbsp;&nbsp;';
85         $totalentries = get_viewable_entry_count($postid, $bloglimit, $start, $filtertype, $filterselect, $tagid, $tag, $sort='created DESC');
86         $blogEntries = blog_fetch_entries($postid, $bloglimit, $start, $filtertype, $filterselect, $tagid, $tag, $sort='created DESC', true);
88         print_paging_bar($totalentries, $blogpage, $bloglimit, get_baseurl($filtertype, $filterselect), 'blogpage');
90         if ($CFG->enablerssfeeds) {
91             blog_rss_print_link($filtertype, $filterselect, $tag);
92         }
94         if (has_capability('moodle/blog:create', $sitecontext)) {
95             //the user's blog is enabled and they are viewing their own blog
96             $addlink = '<div class="addbloglink">';
97             $addlink .= '<a href="'.$CFG->wwwroot .'/blog/edit.php?action=add'.'">'. get_string('addnewentry', 'blog').'</a>';
98             $addlink .= '</div>';
99             echo $addlink;
100         }
102         if ($blogEntries) {
104             $count = 0;
105             foreach ($blogEntries as $blogEntry) {
106                 blog_print_entry($blogEntry, 'list', $filtertype, $filterselect); //print this entry.
107                 $count++;
108             }
110             print_paging_bar($totalentries, $blogpage, $bloglimit, get_baseurl($filtertype, $filterselect), 'blogpage');
112             if (!$count) {
113                 print '<br /><div style="text-align:center">'. get_string('noentriesyet', 'blog') .'</div><br />';
115             }
117             print $morelink.'<br />'."\n";
118             return;
119         }
121         $output = '<br /><div style="text-align:center">'. get_string('noentriesyet', 'blog') .'</div><br />';
123         print $output;
125     }
128     /**
129      * This function is in lib and not in BlogInfo because entries being searched
130      * might be found in any number of blogs rather than just one.
131      *
132      * This function builds an array which can be used by the included
133      * template file, making predefined and nicely formatted variables available
134      * to the template. Template creators will not need to become intimate
135      * with the internal objects and vars of moodle blog nor will they need to worry
136      * about properly formatting their data
137      *
138      *   @param BlogEntry blogEntry - a hopefully fully populated BlogEntry object
139      *   @param string viewtype Default is 'full'. If 'full' then display this blog entry
140      *     in its complete form (eg. archive page). If anything other than 'full'
141      *     display the entry in its abbreviated format (eg. index page)
142      */
143     function blog_print_entry($blogEntry, $viewtype='full', $filtertype='', $filterselect='', $mode='loud') {
144         global $USER, $CFG, $COURSE, $ME, $DB;
146         $template['body'] = format_text($blogEntry->summary, $blogEntry->format);
147         $template['title'] = '<a id=b"'. s($blogEntry->id) .'" />';
148         //enclose the title in nolink tags so that moodle formatting doesn't autolink the text
149         $template['title'] .= '<span class="nolink">'. format_string($blogEntry->subject) .'</span>';
150         $template['userid'] = $blogEntry->userid;
151         $template['author'] = fullname($DB->get_record('user', array('id'=>$blogEntry->userid)));
152         $template['created'] = userdate($blogEntry->created);
154         if($blogEntry->created != $blogEntry->lastmodified){
155             $template['lastmod'] = userdate($blogEntry->lastmodified);
156         }
157         
158         $template['publishstate'] = $blogEntry->publishstate;
160         /// preventing user to browse blogs that they aren't supposed to see
161         /// This might not be too good since there are multiple calls per page
163         /*
164         if (!blog_user_can_view_user_post($template['userid'])) {
165             error ('you can not view this post');
166         }*/
168         $stredit = get_string('edit');
169         $strdelete = get_string('delete');
171         $user = $DB->get_record('user', array('id'=>$template['userid']));
173         /// Start printing of the blog
175         echo '<table cellspacing="0" class="forumpost blogpost blog'.$template['publishstate'].'" width="100%">';
177         echo '<tr class="header"><td class="picture left">';
178         print_user_picture($user, SITEID, $user->picture);
179         echo '</td>';
181         echo '<td class="topic starter"><div class="subject">'.$template['title'].'</div><div class="author">';
182         $fullname = fullname($user, $template['userid']);
183         $by = new object();
184         $by->name =  '<a href="'.$CFG->wwwroot.'/user/view.php?id='.
185                     $user->id.'&amp;course='.$COURSE->id.'">'.$fullname.'</a>';
186         $by->date = $template['created'];
187         print_string('bynameondate', 'forum', $by);
188         echo '</div></td></tr>';
190         echo '<tr><td class="left side">';
192     /// Actual content
194         echo '</td><td class="content">'."\n";
196         if ($blogEntry->attachment) {
197             echo '<div class="attachments">';
198             $attachedimages = blog_print_attachments($blogEntry);
199             echo '</div>';
200         } else {
201             $attachedimages = '';
202         }
204         switch ($template['publishstate']) {
205             case 'draft':
206                 $blogtype = get_string('publishtonoone', 'blog');
207             break;
208             case 'site':
209                 $blogtype = get_string('publishtosite', 'blog');
210             break;
211             case 'public':
212                 $blogtype = get_string('publishtoworld', 'blog');
213             break;
214             default:
215                 $blogtype = '';
216             break;
218         }
220         echo '<div class="audience">'.$blogtype.'</div>';
222         // Print whole message
223         echo format_text($template['body']);
225         /// Print attachments
226         echo $attachedimages;
227     /// Links to tags
229         if ( !empty($CFG->usetags) && ($blogtags = tag_get_tags_csv('post', $blogEntry->id)) ) {
230             echo '<div class="tags">';
231             if ($blogtags) {
232                 print(get_string('tags', 'tag') .': '. $blogtags);
233            }
234             echo '</div>';
235         } 
237     /// Commands
239         echo '<div class="commands">';
241         if (blog_user_can_edit_post($blogEntry)) {
242             echo '<a href="'.$CFG->wwwroot.'/blog/edit.php?action=edit&amp;id='.$blogEntry->id.'">'.$stredit.'</a>';
243             echo '| <a href="'.$CFG->wwwroot.'/blog/edit.php?action=delete&amp;id='.$blogEntry->id.'">'.$strdelete.'</a> | ';
244         }
246         echo '<a href="'.$CFG->wwwroot.'/blog/index.php?postid='.$blogEntry->id.'">'.get_string('permalink', 'blog').'</a>';
248         echo '</div>';
250         if( isset($template['lastmod']) ){
251             echo '<div style="font-size: 55%;">';
252             echo ' [ '.get_string('modified').': '.$template['lastmod'].' ]';
253             echo '</div>';
254         }
256         echo '</td></tr></table>'."\n\n";
258     }
260     /**
261      * Creates a directory file name, suitable for make_upload_directory()
262      * $CFG->dataroot/blog/attachments/xxxx/file.jpg
263      */
264     function blog_file_area_name($blogentry) {
265         return "blog/attachments/$blogentry->id";
266     }
268     function blog_file_area($blogentry) {
269         return make_upload_directory( blog_file_area_name($blogentry) );
270     }
272     /**
273      * Deletes all the user files in the attachments area for a post
274      * EXCEPT for any file named $exception
275      */
276     function blog_delete_old_attachments($post, $exception="") {
277         if ($basedir = blog_file_area($post)) {
278             if ($files = get_directory_list($basedir)) {
279                 foreach ($files as $file) {
280                     if ($file != $exception) {
281                         unlink("$basedir/$file");
282                         notify("Existing file '$file' has been deleted!");
283                     }
284                 }
285             }
286             if (!$exception) {  // Delete directory as well, if empty
287                 rmdir("$basedir");
288             }
289         }
290     }
292     /**
293      * if return=html, then return a html string.
294      * if return=text, then return a text-only string.
295      * otherwise, print HTML for non-images, and return image HTML
296      */
297     function blog_print_attachments($blogentry, $return=NULL) {
298         global $CFG;
300         $filearea = blog_file_area_name($blogentry);
302         $imagereturn = "";
303         $output = "";
305         if ($basedir = blog_file_area($blogentry)) {
306             if ($files = get_directory_list($basedir)) {
307                 $strattachment = get_string("attachment", "forum");
308                 foreach ($files as $file) {
309                     include_once($CFG->libdir.'/filelib.php');
310                     $icon = mimeinfo("icon", $file);
311                     $type = mimeinfo("type", $file);
312                     $ffurl = get_file_url("$filearea/$file");
313                     $image = "<img src=\"$CFG->pixpath/f/$icon\" class=\"icon\" alt=\"\" />";
315                     if ($return == "html") {
316                         $output .= "<a href=\"$ffurl\">$image</a> ";
317                         $output .= "<a href=\"$ffurl\">$file</a><br />";
319                     } else if ($return == "text") {
320                         $output .= "$strattachment $file:\n$ffurl\n";
322                     } else {
323                         if (in_array($type, array('image/gif', 'image/jpeg', 'image/png'))) {    // Image attachments don't get printed as links
324                             $imagereturn .= "<br /><img src=\"$ffurl\" alt=\"\" />";
325                         } else {
326                             echo "<a href=\"$ffurl\">$image</a> ";
327                             echo filter_text("<a href=\"$ffurl\">$file</a><br />");
328                         }
329                     }
330                 }
331             }
332         }
334         if ($return) {
335             return $output;
336         }
338         return $imagereturn;
339     }
342     /**
343      * Use this function to retrieve a list of publish states available for
344      * the currently logged in user.
345      *
346      * @return array This function returns an array ideal for sending to moodles'
347      *                choose_from_menu function.
348      */
349     function blog_applicable_publish_states($courseid='') {
350         global $CFG;
352         // everyone gets draft access
353         if ($CFG->bloglevel >= BLOG_USER_LEVEL) {
354             $options = array ( 'draft' => get_string('publishtonoone', 'blog') );
355         }
357         if ($CFG->bloglevel > BLOG_USER_LEVEL) {
358             $options['site'] = get_string('publishtosite', 'blog');
359         }
361         if ($CFG->bloglevel >= BLOG_GLOBAL_LEVEL) {
362             $options['public'] = get_string('publishtoworld', 'blog');
363         }
365         return $options;
366     }
369     /**
370      * User can edit a blog entry if this is their own blog post and they have
371      * the capability moodle/blog:create, or if they have the capability
372      * moodle/blog:manageentries.
373      *
374      * This also applies to deleting of posts.
375      */
376     function blog_user_can_edit_post($blogEntry) {
377         global $CFG, $USER;
379         $sitecontext = get_context_instance(CONTEXT_SYSTEM);
381         if (has_capability('moodle/blog:manageentries', $sitecontext)) {
382             return true; // can edit any blog post
383         }
385         if ($blogEntry->userid == $USER->id
386           and has_capability('moodle/blog:create', $sitecontext)) {
387             return true; // can edit own when having blog:create capability
388         }
390         return false;
391     }
394     /**
395      * Checks to see if a user can view the blogs of another user.
396      * Only blog level is checked here, the capabilities are enforced
397      * in blog/index.php
398      */
399     function blog_user_can_view_user_post($targetuserid, $blogEntry=null) {
400         global $CFG, $USER, $DB;
402         if (empty($CFG->bloglevel)) {
403             return false; // blog system disabled
404         }
406         if (!empty($USER->id) and $USER->id == $targetuserid) {
407             return true; // can view own posts in any case
408         }
410         $sitecontext = get_context_instance(CONTEXT_SYSTEM);
411         if (has_capability('moodle/blog:manageentries', $sitecontext)) {
412             return true; // can manage all posts
413         }
415         // coming for 1 post, make sure it's not a draft
416         if ($blogEntry and $blogEntry->publishstate == 'draft') {
417             return false;  // can not view draft of others
418         }
420         // coming for 1 post, make sure user is logged in, if not a public blog
421         if ($blogEntry && $blogEntry->publishstate != 'public' && !isloggedin()) {
422             return false;
423         }
425         switch ($CFG->bloglevel) {
426             case BLOG_GLOBAL_LEVEL:
427                 return true;
428             break;
430             case BLOG_SITE_LEVEL:
431                 if (!empty($USER->id)) { // not logged in viewers forbidden
432                     return true;
433                 }
434                 return false;
435             break;
437             case BLOG_COURSE_LEVEL:
438                 $mycourses = array_keys(get_my_courses($USER->id));
439                 $usercourses = array_keys(get_my_courses($targetuserid));
440                 $shared = array_intersect($mycourses, $usercourses);
441                 if (!empty($shared)) {
442                     return true;
443                 }
444                 return false;
445             break;
447             case BLOG_GROUP_LEVEL:
448                 $mycourses = array_keys(get_my_courses($USER->id));
449                 $usercourses = array_keys(get_my_courses($targetuserid));
450                 $shared = array_intersect($mycourses, $usercourses);
451                 foreach ($shared as $courseid) {
452                     $course = $DB->get_record('course', array('id'=>$courseid));
453                     $coursecontext = get_context_instance(CONTEXT_COURSE, $courseid);
454                     if (has_capability('moodle/site:accessallgroups', $coursecontext)
455                       or groups_get_course_groupmode($course) != SEPARATEGROUPS) {
456                         return true;
457                     } else {
458                         if ($usergroups = groups_get_all_groups($courseid, $targetuserid)) {
459                             foreach ($usergroups as $usergroup) {
460                                 if (groups_is_member($usergroup->id)) {
461                                     return true;
462                                 }
463                             }
464                         }
465                     }
466                 }
467                 return false;
468             break;
470             case BLOG_USER_LEVEL:
471             default:
472                 $personalcontext = get_context_instance(CONTEXT_USER, $targetuserid);
473                 return has_capability('moodle/user:readuserblogs', $personalcontext);
474             break;
476         }
477     }
480     /**
481      * Main filter function.
482      */
483     function blog_fetch_entries($postid='', $fetchlimit=10, $fetchstart='', $filtertype='', $filterselect='', $tagid='', $tag ='', $sort='lastmodified DESC', $limit=true) {
484         global $CFG, $USER, $DB;
486         /// the post table will be used for other things too
487         $typesql = "AND p.module = 'blog'";
489         /// set the tag id for searching
490         if ($tagid) {
491             $tag = $tagid;
492         } else if ($tag) {
493             if ($tagrec = $DB->get_record_sql("SELECT * FROM {tag} WHERE name LIKE ?", array($tag))) {
494                 $tag = $tagrec->id;
495             } else {
496                 $tag = -1;    //no records found
497             }
498         }
500         // If we have specified an ID
501         // Just return 1 entry
503         if ($postid) {
504             if ($post = $DB->get_record('post', array('id'=>$postid))) {
506                 if (blog_user_can_view_user_post($post->userid, $post)) {
508                     if ($user = $DB->get_record('user', array('id'=>$post->userid))) {
509                         $post->email = $user->email;
510                         $post->firstname = $user->firstname;
511                         $post->lastname = $user->lastname;
512                     }
513                     $retarray[] = $post;
514                     return $retarray;
515                 } else {
516                     return null;
517                 }
519             } else { // bad postid
520                 return null;
521             }
522         }
524         $params = array();
526         if ($tag) {
527             $tagtablesql = ", {tag_instance} ti";
528             $tagquerysql = "AND ti.itemid = p.id AND ti.tagid = :tag AND ti.itemtype = 'post'";
529             $params['tag'] = $tag;
530         } else {
531             $tagtablesql = '';
532             $tagquerysql = '';
533         }
535         if (isloggedin() && !has_capability('moodle/legacy:guest', get_context_instance(CONTEXT_SYSTEM), $USER->id, false)) {
536             $permissionsql = "AND (p.publishstate = 'site' OR p.publishstate = 'public' OR p.userid = :userid)";
537             $params['userid'] = $USER->id;
538         } else {
539             $permissionsql = "AND p.publishstate = 'public'";
540         }
542         // fix for MDL-9165, use with readuserblogs capability in a user context can read that user's private blogs
543         // admins can see all blogs regardless of publish states, as described on the help page
544         if (has_capability('moodle/user:readuserblogs', get_context_instance(CONTEXT_SYSTEM))) {
545             $permissionsql = '';
546         } else if ($filtertype=='user' && has_capability('moodle/user:readuserblogs', get_context_instance(CONTEXT_USER, $filterselect))) {
547             $permissionsql = '';
548         }
549         /****************************************
550          * depending on the type, there are 4   *
551          * different possible sqls              *
552          ****************************************/
554         $requiredfields = "p.*, u.firstname,u.lastname,u.email";
556         if ($filtertype == 'course' && $filterselect == SITEID) {  // Really a site
557             $filtertype = 'site';
558         }
560         switch ($filtertype) {
562             case 'site':
564                 $SQL = "SELECT $requiredfields
565                           FROM {post} p, {user} u $tagtablesql
566                          WHERE p.userid = u.id $tagquerysql
567                                AND u.deleted = 0
568                                $permissionsql $typesql";
570             break;
572             case 'course':
573                 // all users with a role assigned
574                 $context = get_context_instance(CONTEXT_COURSE, $filterselect);
576                 // MDL-10037, hidden users' blogs should not appear
577                 if (has_capability('moodle/role:viewhiddenassigns', $context)) {
578                     $hiddensql = '';
579                 } else {
580                     $hiddensql = 'AND ra.hidden = 0';
581                 }
583                 $SQL = "SELECT $requiredfields
584                           FROM {post} p, {user} u, {role_assignments} ra $tagtablesql
585                          WHERE p.userid = ra.userid $tagquerysql
586                                AND ra.contextid ".get_related_contexts_string($context)."
587                                AND u.id = p.userid
588                                AND u.deleted = 0
589                                $hiddensql $permissionsql $typesql";
591             break;
593             case 'group':
595                 $SQL = "SELECT $requiredfields
596                           FROM {post} p, {user} u, {groups_members} gm $tagtablesql
597                          WHERE p.userid = gm.userid AND u.id = p.userid $tagquerysql
598                                AND gm.groupid = :groupid
599                                AND u.deleted = 0
600                                $permissionsql $typesql";
601                 $params['groupid'] = $filterselect;
602             break;
604             case 'user':
606                 $SQL = "SELECT $requiredfields
607                           FROM {post} p, {user} u $tagtablesql
608                          WHERE p.userid = u.id $tagquerysql
609                                AND u.id = :uid
610                                AND u.deleted = 0
611                                $permissionsql $typesql";
612                $params['uid'] = $filterselect;
613             break;
614         }
616         $limitfrom = 0;
617         $limitnum = 0;
619         if ($fetchstart !== '' && $limit) {
620             $limitfrom = $fetchstart;
621             $limitnum = $fetchlimit;
622         }
624         $orderby = "ORDER BY $sort";
626         $records = $DB->get_records_sql("$SQL $orderby", $params, $limitfrom, $limitnum);
628         if (empty($records)) {
629             return array();
630         }
632         return $records;
633     }
636     /**
637      * get the count of viewable entries, easiest way is to count blog_fetch_entries
638      * this is used for print_paging_bar
639      * this is not ideal, but because of the UNION in the sql in blog_fetch_entries,
640      * it is hard to use $DB->count_records_sql
641      */
642     function get_viewable_entry_count($postid='', $fetchlimit=10,
643                 $fetchstart='', $filtertype='', $filterselect='', $tagid='',
644                 $tag ='', $sort='lastmodified DESC') {
646         $blogEntries = blog_fetch_entries($postid, $fetchlimit,
647                 $fetchstart, $filtertype, $filterselect, $tagid, $tag,
648                 $sort='lastmodified DESC', false);
650         return count($blogEntries);
651     }
654     /// Find the base url from $_GET variables, for print_paging_bar
655     function get_baseurl($filtertype, $filterselect) {
657         $getcopy  = $_GET;
659         unset($getcopy['blogpage']);
661         $strippedurl = strip_querystring(qualified_me());
662         if(!empty($getcopy)) {
663             $first = false;
664             $querystring = '';
665             foreach($getcopy as $var => $val) {
666                 if(!$first) {
667                     $first = true;
668                     if ($var != 'filterselect' && $var != 'filtertype') {
669                         $querystring .= '?'.$var.'='.$val;
670                         $hasparam = true;
671                     } else {
672                         $querystring .= '?';
673                     }
674                 } else {
675                     if ($var != 'filterselect' && $var != 'filtertype') {
676                     $querystring .= '&amp;'.$var.'='.$val;
677                     $hasparam = true;
678                     }
679                 }
680             }
681             if (isset($hasparam)) {
682                 $querystring .= '&amp;';
683             } else {
684                 $querystring = '?';
685             }
686         } else {
687             $querystring = '?';
688         }
690         return strip_querystring(qualified_me()) . $querystring. 'filtertype='.
691                 $filtertype.'&amp;filterselect='.$filterselect.'&amp;';
693     }
695     /**
696      * Returns a list of all user ids who have used blogs in the site
697      * Used in backup of site courses.
698      */
699     function blog_get_participants() {
700         global $CFG, $DB;
702         return $DB->get_records_sql("SELECT userid AS id 
703                                        FROM {post}
704                                       WHERE module = 'blog' AND courseid = 0");
705     }
706 ?>