Merge branch 'MDL-13331_m19' of git://github.com/nebgor/moodle into MOODLE_19_STABLE
[moodle.git] / blog / lib.php
blob401d1f0ddce04328cc0b24b05a469800fd56b948
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).
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).
23 function blog_check_and_install_blocks() {
24 global $USER;
25 if (isloggedin() && !isguest()) {
26 // if this user has not visited this page before
27 if (!get_user_preferences('blogpagesize')) {
28 // find the correct ids for blog_menu and blog_from blocks
29 $menublock = get_record('block','name','blog_menu');
30 $tagsblock = get_record('block','name','blog_tags');
31 // add those 2 into block_instance page
33 // add blog_menu block
34 $newblock = new object();
35 $newblock->blockid = $menublock->id;
36 $newblock->pageid = $USER->id;
37 $newblock->pagetype = 'blog-view';
38 $newblock->position = 'r';
39 $newblock->weight = 0;
40 $newblock->visible = 1;
41 insert_record('block_instance', $newblock);
43 // add blog_tags menu
44 $newblock -> blockid = $tagsblock->id;
45 $newblock -> weight = 1;
46 insert_record('block_instance', $newblock);
48 // finally we set the page size pref
49 set_user_preference('blogpagesize', 10);
55 /**
56 * Adaptation of isediting in moodlelib.php for blog module
57 * @return bool
59 function blog_isediting() {
60 global $SESSION;
62 return !empty($SESSION->blog_editing_enabled);
66 /**
67 * This function is in lib and not in BlogInfo because entries being searched
68 * might be found in any number of blogs rather than just one.
70 * $@param ...
72 function blog_print_html_formatted_entries($postid, $filtertype, $filterselect, $tagid, $tag) {
74 global $CFG, $USER;
76 $blogpage = optional_param('blogpage', 0, PARAM_INT);
77 $bloglimit = optional_param('limit', get_user_preferences('blogpagesize', 10), PARAM_INT);
78 $start = $blogpage * $bloglimit;
80 $sitecontext = get_context_instance(CONTEXT_SYSTEM);
82 $morelink = '<br />&nbsp;&nbsp;';
84 $totalentries = get_viewable_entry_count($postid, $bloglimit, $start, $filtertype, $filterselect, $tagid, $tag, $sort='created DESC');
85 $blogEntries = blog_fetch_entries($postid, $bloglimit, $start, $filtertype, $filterselect, $tagid, $tag, $sort='created DESC', true);
87 print_paging_bar($totalentries, $blogpage, $bloglimit, get_baseurl($filtertype, $filterselect), 'blogpage');
89 if ($CFG->enablerssfeeds) {
90 blog_rss_print_link($filtertype, $filterselect, $tag);
93 if (has_capability('moodle/blog:create', $sitecontext)) {
94 //the user's blog is enabled and they are viewing their own blog
95 $addlink = '<div class="addbloglink">';
96 $addlink .= '<a href="'.$CFG->wwwroot .'/blog/edit.php?action=add'.'">'. get_string('addnewentry', 'blog').'</a>';
97 $addlink .= '</div>';
98 echo $addlink;
101 if ($blogEntries) {
103 $count = 0;
104 foreach ($blogEntries as $blogEntry) {
105 blog_print_entry($blogEntry, 'list', $filtertype, $filterselect); //print this entry.
106 $count++;
109 print_paging_bar($totalentries, $blogpage, $bloglimit, get_baseurl($filtertype, $filterselect), 'blogpage');
111 if (!$count) {
112 print '<br /><div style="text-align:center">'. get_string('noentriesyet', 'blog') .'</div><br />';
116 print $morelink.'<br />'."\n";
117 return;
120 $output = '<br /><div style="text-align:center">'. get_string('noentriesyet', 'blog') .'</div><br />';
122 print $output;
128 * This function is in lib and not in BlogInfo because entries being searched
129 * might be found in any number of blogs rather than just one.
131 * This function builds an array which can be used by the included
132 * template file, making predefined and nicely formatted variables available
133 * to the template. Template creators will not need to become intimate
134 * with the internal objects and vars of moodle blog nor will they need to worry
135 * about properly formatting their data
137 * @param BlogEntry blogEntry - a hopefully fully populated BlogEntry object
138 * @param string viewtype Default is 'full'. If 'full' then display this blog entry
139 * in its complete form (eg. archive page). If anything other than 'full'
140 * display the entry in its abbreviated format (eg. index page)
142 function blog_print_entry($blogEntry, $viewtype='full', $filtertype='', $filterselect='', $mode='loud') {
144 global $USER, $CFG, $COURSE, $ME;
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(get_record('user','id',$blogEntry->userid));
152 $template['created'] = userdate($blogEntry->created);
154 if($blogEntry->created != $blogEntry->lastmodified){
155 $template['lastmod'] = userdate($blogEntry->lastmodified);
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
164 if (!blog_user_can_view_user_post($template['userid'])) {
165 error ('you can not view this post');
168 $stredit = get_string('edit');
169 $strdelete = get_string('delete');
171 $user = get_record('user','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, has_capability('moodle/site:viewfullnames', get_context_instance(CONTEXT_COURSE, $COURSE->id)));
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 = '';
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;
220 echo '<div class="audience">'.$blogtype.'</div>';
222 // Print whole message
223 echo $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);
234 echo '</div>';
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> | ';
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>';
256 echo '</td></tr></table>'."\n\n";
260 function blog_file_area_name($blogentry) {
261 // Creates a directory file name, suitable for make_upload_directory()
262 global $CFG;
263 // $CFG->dataroot/blog/attachments/xxxx/file.jpg
264 return "blog/attachments/$blogentry->id";
267 function blog_file_area($blogentry) {
268 return make_upload_directory( blog_file_area_name($blogentry) );
271 function blog_delete_old_attachments($post, $exception="") {
272 // Deletes all the user files in the attachments area for a post
273 // EXCEPT for any file named $exception
275 if ($basedir = blog_file_area($post)) {
276 if ($files = get_directory_list($basedir)) {
277 foreach ($files as $file) {
278 if ($file != $exception) {
279 unlink("$basedir/$file");
280 notify("Existing file '$file' has been deleted!");
284 if (!$exception) { // Delete directory as well, if empty
285 rmdir("$basedir");
290 function blog_print_attachments($blogentry, $return=NULL) {
291 // if return=html, then return a html string.
292 // if return=text, then return a text-only string.
293 // otherwise, print HTML for non-images, and return image HTML
295 global $CFG;
297 $filearea = blog_file_area_name($blogentry);
299 $imagereturn = "";
300 $output = "";
302 if ($basedir = blog_file_area($blogentry)) {
303 if ($files = get_directory_list($basedir)) {
304 $strattachment = get_string("attachment", "forum");
305 foreach ($files as $file) {
306 include_once($CFG->libdir.'/filelib.php');
307 $icon = mimeinfo("icon", $file);
308 $type = mimeinfo("type", $file);
309 $ffurl = get_file_url("$filearea/$file");
310 $image = "<img src=\"$CFG->pixpath/f/$icon\" class=\"icon\" alt=\"\" />";
312 if ($return == "html") {
313 $output .= "<a href=\"$ffurl\">$image</a> ";
314 $output .= "<a href=\"$ffurl\">$file</a><br />";
316 } else if ($return == "text") {
317 $output .= "$strattachment $file:\n$ffurl\n";
319 } else {
320 if (in_array($type, array('image/gif', 'image/jpeg', 'image/png'))) { // Image attachments don't get printed as links
321 $imagereturn .= "<br /><img src=\"$ffurl\" alt=\"\" />";
322 } else {
323 echo "<a href=\"$ffurl\">$image</a> ";
324 echo filter_text("<a href=\"$ffurl\">$file</a><br />");
331 if ($return) {
332 return $output;
335 return $imagereturn;
340 * Use this function to retrieve a list of publish states available for
341 * the currently logged in user.
343 * @return array This function returns an array ideal for sending to moodles'
344 * choose_from_menu function.
346 function blog_applicable_publish_states($courseid='') {
348 global $CFG;
350 // everyone gets draft access
351 if ($CFG->bloglevel >= BLOG_USER_LEVEL) {
352 $options = array ( 'draft' => get_string('publishtonoone', 'blog') );
355 if ($CFG->bloglevel > BLOG_USER_LEVEL) {
356 $options['site'] = get_string('publishtosite', 'blog');
359 if ($CFG->bloglevel >= BLOG_GLOBAL_LEVEL) {
360 $options['public'] = get_string('publishtoworld', 'blog');
363 return $options;
368 * User can edit a blog entry if this is their own blog post and they have
369 * the capability moodle/blog:create, or if they have the capability
370 * moodle/blog:manageentries.
372 * This also applies to deleting of posts.
374 function blog_user_can_edit_post($blogEntry) {
376 global $CFG, $USER;
378 $sitecontext = get_context_instance(CONTEXT_SYSTEM);
380 if (has_capability('moodle/blog:manageentries', $sitecontext)) {
381 return true; // can edit any blog post
384 if ($blogEntry->userid == $USER->id
385 and has_capability('moodle/blog:create', $sitecontext)) {
386 return true; // can edit own when having blog:create capability
389 return false;
394 * Checks to see if a user can view the blogs of another user.
395 * Only blog level is checked here, the capabilities are enforced
396 * in blog/index.php
398 function blog_user_can_view_user_post($targetuserid, $blogEntry=null) {
399 global $CFG, $USER;
401 if (empty($CFG->bloglevel)) {
402 return false; // blog system disabled
405 // a hack to publish some blogs openly. Uses $CFG->openblogs = array(44, 322); in config.php
406 if (isset($CFG->openblogs) && in_array($targetuserid,$CFG->openblogs)) {
407 return true;
410 if (!empty($USER->id) and $USER->id == $targetuserid) {
411 return true; // can view own posts in any case
414 $sitecontext = get_context_instance(CONTEXT_SYSTEM);
415 if (has_capability('moodle/blog:manageentries', $sitecontext)) {
416 return true; // can manage all posts
419 // coming for 1 post, make sure it's not a draft
420 if ($blogEntry and $blogEntry->publishstate == 'draft') {
421 return false; // can not view draft of others
424 // coming for 1 post, make sure user is logged in, if not a public blog
425 if ($blogEntry && $blogEntry->publishstate != 'public' && !isloggedin()) {
426 return false;
429 switch ($CFG->bloglevel) {
430 case BLOG_GLOBAL_LEVEL:
431 return true;
432 break;
434 case BLOG_SITE_LEVEL:
435 if (!empty($USER->id)) { // not logged in viewers forbidden
436 return true;
438 return false;
439 break;
441 case BLOG_COURSE_LEVEL:
442 $mycourses = array_keys(get_my_courses($USER->id));
443 $usercourses = array_keys(get_my_courses($targetuserid));
444 $shared = array_intersect($mycourses, $usercourses);
445 if (!empty($shared)) {
446 return true;
448 return false;
449 break;
451 case BLOG_GROUP_LEVEL:
452 $mycourses = array_keys(get_my_courses($USER->id));
453 $usercourses = array_keys(get_my_courses($targetuserid));
454 $shared = array_intersect($mycourses, $usercourses);
455 foreach ($shared as $courseid) {
456 $course = get_record('course', 'id', $courseid);
457 $coursecontext = get_context_instance(CONTEXT_COURSE, $courseid);
458 if (has_capability('moodle/site:accessallgroups', $coursecontext)
459 or groups_get_course_groupmode($course) != SEPARATEGROUPS) {
460 return true;
461 } else {
462 if ($usergroups = groups_get_all_groups($courseid, $targetuserid)) {
463 foreach ($usergroups as $usergroup) {
464 if (groups_is_member($usergroup->id)) {
465 return true;
471 return false;
472 break;
474 case BLOG_USER_LEVEL:
475 default:
476 $personalcontext = get_context_instance(CONTEXT_USER, $targetuserid);
477 return has_capability('moodle/user:readuserblogs', $personalcontext);
478 break;
485 * Main filter function.
487 function blog_fetch_entries($postid='', $fetchlimit=10, $fetchstart='', $filtertype='', $filterselect='', $tagid='', $tag ='', $sort='lastmodified DESC', $limit=true) {
489 global $CFG, $USER;
491 /// the post table will be used for other things too
492 $typesql = " AND p.module = 'blog' ";
494 /// set the tag id for searching
495 if ($tagid) {
496 $tag = $tagid;
497 } else if ($tag) {
498 if ($tagrec = get_record_sql('SELECT * FROM '.$CFG->prefix.'tag WHERE name LIKE "'.addslashes($tag).'"')) {
499 $tag = $tagrec->id;
500 } else {
501 $tag = -1; //no records found
505 // If we have specified an ID
506 // Just return 1 entry
508 if ($postid) {
510 if ($post = get_record('post', 'id', $postid)) {
512 if (blog_user_can_view_user_post($post->userid, $post)) {
514 if ($user = get_record('user', 'id', $post->userid)) {
515 $post->email = $user->email;
516 $post->firstname = $user->firstname;
517 $post->lastname = $user->lastname;
519 $retarray[] = $post;
520 return $retarray;
521 } else {
522 return null;
525 } else { // bad postid
526 return null;
530 if ($tag) {
531 $tagtablesql = $CFG->prefix.'tag_instance ti, ';
532 $tagquerysql = ' AND ti.itemid = p.id AND ti.tagid = '.$tag.' AND ti.itemtype = \'post\' ';
533 } else {
534 $tagtablesql = '';
535 $tagquerysql = '';
538 if (isloggedin() && !has_capability('moodle/legacy:guest', get_context_instance(CONTEXT_SYSTEM), $USER->id, false)) {
539 $permissionsql = 'AND (p.publishstate = \'site\' OR p.publishstate = \'public\' OR p.userid = '.$USER->id.')';
540 } else {
541 $permissionsql = 'AND p.publishstate = \'public\'';
544 // fix for MDL-9165, use with readuserblogs capability in a user context can read that user's private blogs
545 // admins can see all blogs regardless of publish states, as described on the help page
546 if (has_capability('moodle/user:readuserblogs', get_context_instance(CONTEXT_SYSTEM))) {
547 $permissionsql = '';
548 } else if ($filtertype=='user' && has_capability('moodle/user:readuserblogs', get_context_instance(CONTEXT_USER, $filterselect))) {
549 $permissionsql = '';
551 /****************************************
552 * depending on the type, there are 4 *
553 * different possible sqls *
554 ****************************************/
556 $requiredfields = 'p.*, u.firstname,u.lastname,u.email';
558 if ($filtertype == 'course' && $filterselect == SITEID) { // Really a site
559 $filtertype = 'site';
562 switch ($filtertype) {
564 case 'site':
566 $SQL = 'SELECT '.$requiredfields.' FROM '.$CFG->prefix.'post p, '.$tagtablesql
567 .$CFG->prefix.'user u
568 WHERE p.userid = u.id '.$tagquerysql.'
569 AND u.deleted = 0
570 '.$permissionsql.$typesql;
572 break;
574 case 'course':
575 // all users with a role assigned
576 $context = get_context_instance(CONTEXT_COURSE, $filterselect);
578 // MDL-10037, hidden users' blogs should not appear
579 if (has_capability('moodle/role:viewhiddenassigns', $context)) {
580 $hiddensql = '';
581 } else {
582 $hiddensql = ' AND ra.hidden = 0 ';
585 $SQL = 'SELECT '.$requiredfields.' FROM '.$CFG->prefix.'post p, '.$tagtablesql
586 .$CFG->prefix.'role_assignments ra, '.$CFG->prefix.'user u
587 WHERE p.userid = ra.userid '.$tagquerysql.'
588 AND ra.contextid '.get_related_contexts_string($context).'
589 AND u.id = p.userid
590 AND u.deleted = 0
591 '.$hiddensql.$permissionsql.$typesql;
593 break;
595 case 'group':
597 $SQL = 'SELECT '.$requiredfields.' FROM '.$CFG->prefix.'post p, '.$tagtablesql
598 .$CFG->prefix.'groups_members gm, '.$CFG->prefix.'user u
599 WHERE p.userid = gm.userid AND u.id = p.userid '.$tagquerysql.'
600 AND gm.groupid = '.$filterselect.'
601 AND u.deleted = 0
602 '.$permissionsql.$typesql;
603 break;
605 case 'user':
607 // a hack to publish some blogs openly. Uses $CFG->openblogs = array(44, 322); in config.php
608 if (isset($CFG->openblogs) && in_array($filterselect,$CFG->openblogs)) {
609 $permissionsql = ' AND (p.publishstate = \'site\' OR p.publishstate = \'public\') ';
612 $SQL = 'SELECT '.$requiredfields.' FROM '.$CFG->prefix.'post p, '.$tagtablesql
613 .$CFG->prefix.'user u
614 WHERE p.userid = u.id '.$tagquerysql.'
615 AND u.id = '.$filterselect.'
616 AND u.deleted = 0
617 '.$permissionsql.$typesql;
618 break;
621 $limitfrom = 0;
622 $limitnum = 0;
624 if ($fetchstart !== '' && $limit) {
625 $limitfrom = $fetchstart;
626 $limitnum = $fetchlimit;
629 $orderby = ' ORDER BY '. $sort .' ';
631 //global $db; $db->debug = true;
632 $records = get_records_sql($SQL . $orderby, $limitfrom, $limitnum);
633 //$db->debug = false;
635 if (empty($records)) {
636 return array();
639 return $records;
644 * get the count of viewable entries, easiest way is to count blog_fetch_entries
645 * this is used for print_paging_bar
646 * this is not ideal, but because of the UNION in the sql in blog_fetch_entries,
647 * it is hard to use count_records_sql
649 function get_viewable_entry_count($postid='', $fetchlimit=10,
650 $fetchstart='', $filtertype='', $filterselect='', $tagid='',
651 $tag ='', $sort='lastmodified DESC') {
653 $blogEntries = blog_fetch_entries($postid, $fetchlimit,
654 $fetchstart, $filtertype, $filterselect, $tagid, $tag,
655 $sort='lastmodified DESC', false);
657 return count($blogEntries);
661 /// Find the base url from $_GET variables, for print_paging_bar
662 /// WARNING: EVIL EVIL EVIL! This function directly acesses $_GET which is a big no no. MDL-22631
663 /// I added some clean_param() calls for now but $_GET should just not ever be used directly.
664 /// The function is totally gone in Moodle 2.0.
665 function get_baseurl($filtertype, $filterselect) {
667 unset($_GET['blogpage']);
669 $strippedurl = strip_querystring(qualified_me());
670 if(!empty($_GET)) {
671 $first = false;
672 $querystring = '';
673 foreach($_GET as $var => $val) {
674 $var = clean_param($var, PARAM_ALPHANUM); // See MDL-22631
675 $val = clean_param($val, PARAM_CLEAN);
676 if(!$first) {
677 $first = true;
678 if ($var != 'filterselect' && $var != 'filtertype') {
679 $querystring .= '?'.$var.'='.$val;
680 $hasparam = true;
681 } else {
682 $querystring .= '?';
684 } else {
685 if ($var != 'filterselect' && $var != 'filtertype') {
686 $querystring .= '&amp;'.$var.'='.$val;
687 $hasparam = true;
691 if (isset($hasparam)) {
692 $querystring .= '&amp;';
693 } else {
694 $querystring = '?';
696 } else {
697 $querystring = '?';
700 return strip_querystring(qualified_me()) . $querystring. 'filtertype='.
701 $filtertype.'&amp;filterselect='.$filterselect.'&amp;';
706 * Returns a list of all user ids who have used blogs in the site
707 * Used in backup of site courses.
709 function blog_get_participants() {
711 global $CFG;
713 return get_records_sql("SELECT userid as id
714 FROM {$CFG->prefix}post
715 WHERE module = 'blog'
716 AND courseid = 0");