MDL-21891 fix for pg incompatibility - credit goes to Penny Leach
[moodle.git] / blog / locallib.php
1 <?php
3 // This file is part of Moodle - http://moodle.org/
4 //
5 // Moodle is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // Moodle is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
19 /**
20  * Classes for Blogs.
21  *
22  * @package    moodlecore
23  * @subpackage blog
24  * @copyright  2009 Nicolas Connault
25  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
26  */
29 /**
30  * Blog_entry class. Represents an entry in a user's blog. Contains all methods for managing this entry.
31  * This class does not contain any HTML-generating code. See blog_listing sub-classes for such code.
32  * This class follows the Object Relational Mapping technique, its member variables being mapped to
33  * the fields of the post table.
34  *
35  * @package    moodlecore
36  * @subpackage blog
37  * @copyright  2009 Nicolas Connault
38  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
39  */
40 class blog_entry {
41     // Public Database fields
42     public $id;
43     public $userid;
44     public $subject;
45     public $summary;
46     public $rating = 0;
47     public $attachment;
48     public $publishstate;
50     // Locked Database fields (Don't touch these)
51     public $courseid = 0;
52     public $groupid = 0;
53     public $module = 'blog';
54     public $moduleid = 0;
55     public $coursemoduleid = 0;
56     public $content;
57     public $format = 1;
58     public $uniquehash = '';
59     public $lastmodified;
60     public $created;
61     public $usermodified;
63     // Other class variables
64     public $form;
65     public $tags = array();
67     // Methods
68     /**
69      * Constructor. If given an id, will fetch the corresponding record from the DB.
70      *
71      * @param mixed $idorparams A blog entry id if INT, or data for a new entry if array
72      */
73     public function __construct($id=null, $params=null, $form=null) {
74         global $DB, $PAGE;
76         if (!empty($id)) {
77             $object = $DB->get_record('post', array('id' => $id));
78             foreach ($object as $var => $val) {
79                 $this->$var = $val;
80             }
81         } else if (!empty($params) && (is_array($params) || is_object($params))) {
82             foreach ($params as $var => $val) {
83                 $this->$var = $val;
84             }
85         }
87         $this->form = $form;
88     }
90     /**
91      * Prints or returns the HTML for this blog entry.
92      *
93      * @param bool $return
94      * @return string
95      */
96     public function print_html($return=false) {
98         global $USER, $CFG, $COURSE, $DB, $OUTPUT, $PAGE;
100         $user = $DB->get_record('user', array('id'=>$this->userid));
101         // Comments
102         $cmt = new stdClass();
103         $cmt->context = get_context_instance(CONTEXT_USER, $user->id);
104         $cmt->courseid = $PAGE->course->id;
105         $cmt->area = 'format_blog';
106         $cmt->env = 'blog';
107         $cmt->itemid = $this->id;
108         $options->comments = $cmt;
109         $this->summary = file_rewrite_pluginfile_urls($this->summary, 'pluginfile.php', SYSCONTEXTID, 'blog_post', $this->id);
111         $template['body'] = format_text($this->summary, $this->summaryformat, $options);
112         $template['title'] = '<a id="b'. s($this->id) .'" />';
113         $template['title'] .= '<span class="nolink">'. format_string($this->subject) .'</span>';
114         $template['userid'] = $user->id;
115         $template['author'] = fullname($user);
116         $template['created'] = userdate($this->created);
118         if($this->created != $this->lastmodified){
119             $template['lastmod'] = userdate($this->lastmodified);
120         }
122         $template['publishstate'] = $this->publishstate;
124         $stredit = get_string('edit');
125         $strdelete = get_string('delete');
127         // Check to see if the entry is unassociated with group/course level access
128         $unassociatedentry = false;
129         if (!empty($CFG->useblogassociations) && ($this->publishstate == 'group' || $this->publishstate == 'course')) {
130             if (!$DB->record_exists('blog_association', array('blogid' => $this->id))) {
131                 $unassociatedentry = true;
132             }
133         }
135         // Start printing of the blog
136         $table = new html_table();
137         $table->cellspacing = 0;
138         $table->attributes['class'] = 'forumpost blog_entry blog'. ($unassociatedentry ? 'draft' : $template['publishstate']);
139         $table->width = '100%';
141         $picturecell = new html_table_cell();
142         $picturecell->attributes['class'] = 'picture left';
143         $picturecell->text = $OUTPUT->user_picture($user);
145         $table->head[] = $picturecell;
147         $topiccell = new html_table_cell();
148         $topiccell->attributes['class'] = 'topic starter';
149         $topiccell->text = $OUTPUT->container($template['title'], 'subject');
150         $topiccell->text .= $OUTPUT->container_start('author');
152         $fullname = fullname($user, has_capability('moodle/site:viewfullnames', get_context_instance(CONTEXT_COURSE, $PAGE->course->id)));
153         $by = new object();
154         $by->name =  html_writer::link(new moodle_url('/user/view.php', array('id' => $user->id, 'course' => $PAGE->course->id)), $fullname);
155         $by->date = $template['created'];
157         $topiccell->text .= get_string('bynameondate', 'forum', $by);
158         $topiccell->text .= $OUTPUT->container_end();
160         if ($this->uniquehash && $this->content) {
161             if ($externalblog = $DB->get_record('blog_external', array('id' => $this->content))) {
162                 $urlparts = parse_url($externalblog->url);
163                 $topiccell->text .= $OUTPUT->container(get_string('retrievedfrom', 'blog') . html_writer::link($urlparts['scheme'].'://'.$urlparts['host'], $externalblog->name), 'externalblog');
164             }
165         }
167         $topiccell->header = false;
168         $table->head[] = $topiccell;
170         // Actual content
171         $mainrow = new html_table_row();
173         $leftsidecell = new html_table_cell();
174         $leftsidecell->attributes['class'] = 'left side';
175         $mainrow->cells[] = $leftsidecell;
177         $contentcell = new html_table_cell();
178         $contentcell->attributes['class'] = 'content';
180         $attachedimages = $OUTPUT->container($this->print_attachments(), 'attachments');
182         // retrieve associations in case they're needed early
183         $blogassociations = $DB->get_records('blog_association', array('blogid' => $this->id));
185         // determine text for publish state
186         switch ($template['publishstate']) {
187             case 'draft':
188                 $blogtype = get_string('publishtonoone', 'blog');
189             break;
190             case 'site':
191                 $blogtype = get_string('publishtosite', 'blog');
192             break;
193             case 'public':
194                 $blogtype = get_string('publishtoworld', 'blog');
195             break;
196             default:
197                 $blogtype = '';
198             break;
200         }
202         $contentcell->text .= $OUTPUT->container($blogtype, 'audience');
204         $contentcell->text .= $template['body'];
205         $contentcell->text .= $attachedimages;
207         // Uniquehash is used as a link to an external blog
208         if (!empty($this->uniquehash)) {
209             $contentcell->text .= $OUTPUT->container_start('externalblog');
210             $contentcell->text .= html_writer::link($this->uniquehash, get_string('linktooriginalentry', 'blog'));
211             $contentcell->text .= $OUTPUT->container_end();
212         }
214         // Links to tags
215         $officialtags = tag_get_tags_csv('post', $this->id, TAG_RETURN_HTML, 'official');
216         $defaulttags = tag_get_tags_csv('post', $this->id, TAG_RETURN_HTML, 'default');
218         if (!empty($CFG->usetags) && ($officialtags || $defaulttags) ) {
219             $contentcell->text .= $OUTPUT->container_start('tags');
221             if ($officialtags) {
222                 $contentcell->text .= get_string('tags', 'tag') .': '. $OUTPUT->container($officialtags, 'officialblogtags');
223                 if ($defaulttags) {
224                     $contentcell->text .=  ', ';
225                 }
226             }
227             $contentcell->text .=  $defaulttags;
228             $contentcell->text .= $OUTPUT->container_end();
229         }
231         // Add associations
232         if (!empty($CFG->useblogassociations) && $blogassociations) {
233             $contentcell->text .= $OUTPUT->container_start('tags');
234             $assocstr = '';
235             $hascourseassocs = false;
236             $assoctype = '';
238             // First find and show the associated course
239             foreach ($blogassociations as $assocrec) {
240                 $contextrec = $DB->get_record('context', array('id' => $assocrec->contextid));
241                 if ($contextrec->contextlevel ==  CONTEXT_COURSE) {
242                     $url = new moodle_url('/course/view.php', array('id' => $contextrec->instanceid));
243                     $text = $DB->get_field('course', 'shortname', array('id' => $contextrec->instanceid)); //TODO: performance!!!!
244                     $assocstr .= $OUTPUT->action_icon($associconurl, new pix_icon('i/course', $text));
245                     $hascourseassocs = true;
246                     $assoctype = get_string('course');
247                 }
248             }
250             // Now show mod association
251             foreach ($blogassociations as $assocrec) {
252                 $contextrec = $DB->get_record('context', array('id' => $assocrec->contextid));
254                 if ($contextrec->contextlevel ==  CONTEXT_MODULE) {
255                     if ($hascourseassocs) {
256                         $assocstr .= ', ';
257                         $hascourseassocs = false;
258                     }
260                     $modinfo = $DB->get_record('course_modules', array('id' => $contextrec->instanceid));
261                     $modname = $DB->get_field('modules', 'name', array('id' => $modinfo->module));
263                     $url = new moodle_url('/mod/'.$modname.'/view.php', array('id' => $modinfo->id));
264                     $text = $DB->get_field($modname, 'name', array('id' => $modinfo->instance)); //TODO: performance!!!!
265                     $assocstr .= $OUTPUT->action_icon($associconurl, new pix_icon('icon', $text, $modname));
266                     $assocstr .= ', ';
267                     $assoctype = get_string('modulename', $modname);
269                 }
270             }
271             $assocstr = substr($assocstr, 0, -2);
272             $contentcell->text .= get_string('associated', 'blog', $assoctype) . ': '. $assocstr;
274             $contentcell->text .= $OUTPUT->container_end();
275         }
277         if ($unassociatedentry) {
278             $contentcell->text .= $OUTPUT->container(get_string('associationunviewable', 'blog'), 'noticebox');
279         }
281     /// Commands
283         $contentcell->text .= $OUTPUT->container_start('commands');
285         if (blog_user_can_edit_entry($this) && empty($this->uniquehash)) {
286             $contentcell->text .= html_writer::link(new moodle_url('/blog/edit.php', array('action' => 'edit', 'entryid' => $this->id)), $stredit) . ' | ';
287             $contentcell->text .= html_writer::link(new moodle_url('/blog/edit.php', array('action' => 'delete', 'entryid' => $this->id)), $strdelete) . ' | ';
288         }
290         $contentcell->text .= html_writer::link(new moodle_url('/blog/index.php', array('entryid' => $this->id)), get_string('permalink', 'blog'));
292         $contentcell->text .= $OUTPUT->container_end();
294         if (isset($template['lastmod']) ){
295             $contentcell->text .= '<div style="font-size: 55%;">';
296             $contentcell->text .= ' [ '.get_string('modified').': '.$template['lastmod'].' ]';
297             $contentcell->text .= '</div>';
298         }
300         $mainrow->cells[] = $contentcell;
301         $table->data = array($mainrow);
303         if ($return) {
304             return html_writer::table($table);
305         } else {
306             echo html_writer::table($table);
307         }
308     }
310     /**
311      * Inserts this entry in the database. Access control checks must be done by calling code.
312      *
313      * @param mform $form Used for attachments
314      * @return void
315      */
316     public function process_attachment($form) {
317         $this->form = $form;
318     }
320     /**
321      * Inserts this entry in the database. Access control checks must be done by calling code.
322      * TODO Set the publishstate correctly
323      * @param mform $form Used for attachments
324      * @return void
325      */
326     public function add() {
327         global $CFG, $USER, $DB;
329         unset($this->id);
330         $this->module       = 'blog';
331         $this->userid       = (empty($this->userid)) ? $USER->id : $this->userid;
332         $this->lastmodified = time();
333         $this->created      = time();
335         // Insert the new blog entry.
336         if ($this->id = $DB->insert_record('post', $this)) {
338             // Update tags.
339             $this->add_tags_info();
341             if (!empty($CFG->useblogassociations)) {
342                 $this->add_associations();
343                 add_to_log(SITEID, 'blog', 'add', 'index.php?userid='.$this->userid.'&entryid='.$this->id, $this->subject);
344             }
346             tag_set('post', $this->id, $this->tags);
347         }
348     }
350     /**
351      * Updates this entry in the database. Access control checks must be done by calling code.
352      *
353      * @param mform $form Used for attachments
354      * @return void
355      */
356     public function edit($params=array(), $form=null, $summaryoptions=array(), $attachmentoptions=array()) {
357         global $CFG, $USER, $DB, $PAGE;
359         $sitecontext = get_context_instance(CONTEXT_SYSTEM);
360         $entry = $this;
362         $this->form = $form;
363         foreach ($params as $var => $val) {
364             $entry->$var = $val;
365         }
367         $entry = file_postupdate_standard_editor($entry, 'summary', $summaryoptions, $sitecontext, 'blog_post', $entry->id);
368         $entry = file_postupdate_standard_filemanager($entry, 'attachment', $attachmentoptions, $sitecontext, 'blog_attachment', $entry->id);
370         if (!empty($CFG->useblogassociations)) {
371             $entry->add_associations();
372         }
374         $entry->lastmodified = time();
376         // Update record
377         $DB->update_record('post', $entry);
378         tag_set('post', $entry->id, $entry->tags);
380         add_to_log(SITEID, 'blog', 'update', 'index.php?userid='.$USER->id.'&entryid='.$entry->id, $entry->subject);
381     }
383     /**
384      * Deletes this entry from the database. Access control checks must be done by calling code.
385      *
386      * @return void
387      */
388     public function delete() {
389         global $DB, $USER;
391         $returnurl = '';
393         $this->delete_attachments();
395         $DB->delete_records('post', array('id' => $this->id));
396         tag_set('post', $this->id, array());
398         add_to_log(SITEID, 'blog', 'delete', 'index.php?userid='. $this->userid, 'deleted blog entry with entry id# '. $this->id);
399     }
401     /**
402      * function to add all context associations to an entry
403      * @param int entry - data object processed to include all 'entry' fields and extra data from the edit_form object
404      */
405     public function add_associations($action='add') {
406         global $DB, $USER;
408         $this->remove_associations();
410         if (!empty($this->courseassoc)) {
411             $this->add_association($this->courseassoc, $action);
412         }
414         if (!empty($this->modassoc)) {
415             $this->add_association($this->modassoc, $action);
416         }
417     }
419     /**
420      * add a single association for a blog entry
421      * @param int contextid - id of context to associate with the blog entry
422      */
423     public function add_association($contextid, $action='add') {
424         global $DB, $USER;
426         $assocobject = new StdClass;
427         $assocobject->contextid = $contextid;
428         $assocobject->blogid = $this->id;
429         $DB->insert_record('blog_association', $assocobject);
431         $context = get_context_instance_by_id($contextid);
432         $courseid = null;
434         if ($context->contextlevel == CONTEXT_COURSE) {
435             $courseid = $context->instanceid;
436             add_to_log($courseid, 'blog', $action, 'index.php?userid='.$this->userid.'&entryid='.$this->id, $this->subject);
437         } else if ($context->contextlevel == CONTEXT_MODULE) {
438             $cm = $DB->get_record('course_modules', array('id' => $context->instanceid));
439             $modulename = $DB->get_field('modules', 'name', array('id' => $cm->module));
440             add_to_log($cm->course, 'blog', $action, 'index.php?userid='.$this->userid.'&entryid='.$this->id, $this->subject, $cm->id, $this->userid);
441         }
442     }
444     /**
445      * remove all associations for a blog entry
446      * @return voic
447      */
448     public function remove_associations() {
449         global $DB;
450         $DB->delete_records('blog_association', array('blogid' => $this->id));
451     }
453     /**
454      * Deletes all the user files in the attachments area for an entry
455      *
456      * @return void
457      */
458     public function delete_attachments() {
459         $fs = get_file_storage();
460         $fs->delete_area_files(SYSCONTEXTID, 'blog_attachment', $this->id);
461         $fs->delete_area_files(SYSCONTEXTID, 'blog_post', $this->id);
462     }
464     /**
465      * if return=html, then return a html string.
466      * if return=text, then return a text-only string.
467      * otherwise, print HTML for non-images, and return image HTML
468      *
469      * @param bool $return Whether to return or print the generated code
470      * @return void
471      */
472     public function print_attachments($return=false) {
473         global $CFG, $OUTPUT;
475         require_once($CFG->libdir.'/filelib.php');
477         $fs = get_file_storage();
478         $browser = get_file_browser();
480         $files = $fs->get_area_files(SYSCONTEXTID, 'blog_attachment', $this->id);
482         $imagereturn = "";
483         $output = "";
485         $strattachment = get_string("attachment", "forum");
487         foreach ($files as $file) {
488             if ($file->is_directory()) {
489                 continue;
490             }
492             $filename = $file->get_filename();
493             $ffurl    = file_encode_url($CFG->wwwroot.'/pluginfile.php', '/'.SYSCONTEXTID.'/blog_attachment/'.$this->id.'/'.$filename);
494             $mimetype = $file->get_mimetype();
496             $icon     = substr(mimeinfo_from_type("icon", $mimetype), 0, -4);
497             $type     = mimeinfo_from_type("type", $mimetype);
499             $image = $OUTPUT->pix_icon("/f/$icon", $filename, 'moodle', array('class'=>'icon'));
501             if ($return == "html") {
502                 $output .= html_writer::link($ffurl, $image);
503                 $output .= html_writer::link($ffurl, $filename);
505             } else if ($return == "text") {
506                 $output .= "$strattachment $filename:\n$ffurl\n";
508             } else {
509                 if (in_array($type, array('image/gif', 'image/jpeg', 'image/png'))) {    // Image attachments don't get printed as links
510                     $imagereturn .= "<br />" . $OUTPUT->pix_icon($ffurl, $filename);
511                 } else {
512                     $imagereturn .= html_writer::link($ffurl, $image);
513                     $imagereturn .= filter_text(html_writer::link($ffurl, $filename));
514                 }
515             }
516         }
518         if ($return) {
519             return $output;
520         }
522         return $imagereturn;
524     }
526     /**
527      * function to attach tags into an entry
528      * @return void
529      */
530     public function add_tags_info() {
532         $tags = array();
534         if ($otags = optional_param('otags', '', PARAM_INT)) {
535             foreach ($otags as $tagid) {
536                 // TODO : make this use the tag name in the form
537                 if ($tag = tag_get('id', $tagid)) {
538                     $tags[] = $tag->name;
539                 }
540             }
541         }
543         tag_set('post', $this->id, $tags);
544     }
546     /**
547      * User can edit a blog entry if this is their own blog entry and they have
548      * the capability moodle/blog:create, or if they have the capability
549      * moodle/blog:manageentries.
550      * This also applies to deleting of entries.
551      *
552      * @param int $userid Optional. If not given, $USER is used
553      * @return boolean
554      */
555     public function can_user_edit($userid=null) {
556         global $CFG, $USER;
558         if (empty($userid)) {
559             $userid = $USER->id;
560         }
562         $sitecontext = get_context_instance(CONTEXT_SYSTEM);
564         if (has_capability('moodle/blog:manageentries', $sitecontext)) {
565             return true; // can edit any blog entry
566         }
568         if ($this->userid == $userid && has_capability('moodle/blog:create', $sitecontext)) {
569             return true; // can edit own when having blog:create capability
570         }
572         return false;
573     }
575     /**
576      * Checks to see if a user can view the blogs of another user.
577      * Only blog level is checked here, the capabilities are enforced
578      * in blog/index.php
579      *
580      * @param int $targetuserid ID of the user we are checking
581      *
582      * @return bool
583      */
584     public function can_user_view($targetuserid) {
585         global $CFG, $USER, $DB;
586         $sitecontext = get_context_instance(CONTEXT_SYSTEM);
588         if (empty($CFG->bloglevel) || !has_capability('moodle/blog:view', $sitecontext)) {
589             return false; // blog system disabled or user has no blog view capability
590         }
592         if (isloggedin() && $USER->id == $targetuserid) {
593             return true; // can view own entries in any case
594         }
596         if (has_capability('moodle/blog:manageentries', $sitecontext)) {
597             return true; // can manage all entries
598         }
600         // coming for 1 entry, make sure it's not a draft
601         if ($this->publishstate == 'draft' && !has_capability('moodle/blog:viewdrafts', $sitecontext)) {
602             return false;  // can not view draft of others
603         }
605         // coming for 1 entry, make sure user is logged in, if not a public blog
606         if ($this->publishstate != 'public' && !isloggedin()) {
607             return false;
608         }
610         switch ($CFG->bloglevel) {
611             case BLOG_GLOBAL_LEVEL:
612                 return true;
613                 break;
615             case BLOG_SITE_LEVEL:
616                 if (isloggedin()) { // not logged in viewers forbidden
617                     return true;
618                 }
619                 return false;
620                 break;
622             case BLOG_USER_LEVEL:
623             default:
624                 $personalcontext = get_context_instance(CONTEXT_USER, $targetuserid);
625                 return has_capability('moodle/user:readuserblogs', $personalcontext);
626                 break;
627         }
628     }
630     /**
631      * Use this function to retrieve a list of publish states available for
632      * the currently logged in user.
633      *
634      * @return array This function returns an array ideal for sending to moodles'
635      *                choose_from_menu function.
636      */
638     public static function get_applicable_publish_states() {
639         global $CFG;
640         $options = array();
642         // everyone gets draft access
643         if ($CFG->bloglevel >= BLOG_USER_LEVEL) {
644             $options['draft'] = get_string('publishtonoone', 'blog');
645         }
647         if ($CFG->bloglevel > BLOG_USER_LEVEL) {
648             $options['site'] = get_string('publishtosite', 'blog');
649         }
651         if ($CFG->bloglevel >= BLOG_GLOBAL_LEVEL) {
652             $options['public'] = get_string('publishtoworld', 'blog');
653         }
655         return $options;
656     }
659 /**
660  * Abstract Blog_Listing class: used to gather blog entries and output them as listings. One of the subclasses must be used.
661  *
662  * @package    moodlecore
663  * @subpackage blog
664  * @copyright  2009 Nicolas Connault
665  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
666  */
667 class blog_listing {
668     /**
669      * Array of blog_entry objects.
670      * @var array $entries
671      */
672     public $entries = array();
674     /**
675      * An array of blog_filter_* objects
676      * @var array $filters
677      */
678     public $filters = array();
680     /**
681      * Constructor
682      *
683      * @param array $filters An associative array of filtername => filterid
684      */
685     public function __construct($filters=array()) {
686         // Unset filters overridden by more specific filters
687         foreach ($filters as $type => $id) {
688             if (!empty($type) && !empty($id)) {
689                 $this->filters[$type] = blog_filter::get_instance($id, $type);
690             }
691         }
693         foreach ($this->filters as $type => $filter) {
694             foreach ($filter->overrides as $override) {
695                 if (array_key_exists($override, $this->filters)) {
696                     unset($this->filters[$override]);
697                 }
698             }
699         }
700     }
702     /**
703      * Fetches the array of blog entries.
704      *
705      * @return array
706      */
707     public function get_entries($start=0, $limit=10) {
708         global $DB;
710         if (empty($this->entries)) {
711             if ($sqlarray = $this->get_entry_fetch_sql()) {
712                 $this->entries = $DB->get_records_sql($sqlarray['sql'], $sqlarray['params'], $start, $limit);
713             } else {
714                 return false;
715             }
716         }
718         return $this->entries;
719     }
721     public function get_entry_fetch_sql($count=false, $sort='lastmodified DESC', $userid = false) {
722         global $DB, $USER, $CFG;
724         if(!$userid) {
725             $userid = $USER->id;
726         }
728         // The query used to locate blog entries is complicated.  It will be built from the following components:
729         $requiredfields = "p.*, u.firstname, u.lastname, u.email";  // the SELECT clause
730         $tables = array('p' => 'post', 'u' => 'user');   // components of the FROM clause (table_id => table_name)
731         $conditions = array('u.deleted = 0', 'p.userid = u.id', '(p.module = \'blog\' OR p.module = \'blog_external\')');  // components of the WHERE clause (conjunction)
733         // build up a clause for permission constraints
735         $params = array();
737         // fix for MDL-9165, use with readuserblogs capability in a user context can read that user's private blogs
738         // admins can see all blogs regardless of publish states, as described on the help page
739         if (has_capability('moodle/user:readuserblogs', get_context_instance(CONTEXT_SYSTEM))) {
740             // don't add permission constraints
742         } else if(!empty($this->filters['user']) && has_capability('moodle/user:readuserblogs',
743                 get_context_instance(CONTEXT_USER, (empty($this->filters['user']->id) ? 0 : $this->filters['user']->id)))) {
744             // don't add permission constraints
746         } else {
747             if (isloggedin() and !isguestuser()) {
748                 $assocexists = $DB->record_exists('blog_association', array());  //dont check association records if there aren't any
750                 //begin permission sql clause
751                 $permissionsql =  '(p.userid = ? ';
752                 $params[] = $userid;
754                 if ($CFG->bloglevel >= BLOG_SITE_LEVEL) { // add permission to view site-level entries
755                     $permissionsql .= " OR p.publishstate = 'site' ";
756                 }
758                 if ($CFG->bloglevel >= BLOG_GLOBAL_LEVEL) { // add permission to view global entries
759                     $permissionsql .= " OR p.publishstate = 'public' ";
760                 }
762                 $permissionsql .= ') ';   //close permissions sql clause
763             } else {  // default is access to public entries
764                 $permissionsql = "p.publishstate = 'public'";
765             }
766             $conditions[] = $permissionsql;  //add permission constraints
767         }
769         foreach ($this->filters as $type => $blogfilter) {
770             $conditions = array_merge($conditions, $blogfilter->conditions);
771             $params = array_merge($params, $blogfilter->params);
772             $tables = array_merge($tables, $blogfilter->tables);
773         }
775         $tablessql = '';  // build up the FROM clause
776         foreach ($tables as $tablename => $table) {
777             $tablessql .= ($tablessql ? ', ' : '').'{'.$table.'} '.$tablename;
778         }
780         $sql = ($count) ? 'SELECT COUNT(*)' : 'SELECT ' . $requiredfields;
781         $sql .= " FROM $tablessql WHERE " . implode(' AND ', $conditions);
782         $sql .= ($count) ? '' : " ORDER BY $sort";
784         return array('sql' => $sql, 'params' => $params);
785     }
787     /**
788      * Outputs all the blog entries aggregated by this blog listing.
789      *
790      * @return void
791      */
792     public function print_entries() {
793         global $CFG, $USER, $DB, $OUTPUT;
794         $sitecontext = get_context_instance(CONTEXT_SYSTEM);
796         $page  = optional_param('blogpage', 0, PARAM_INT);
797         $limit = optional_param('limit', get_user_preferences('blogpagesize', 10), PARAM_INT);
798         $start = $page * $limit;
800         $morelink = '<br />&nbsp;&nbsp;';
802         if ($sqlarray = $this->get_entry_fetch_sql(true)) {
803             $totalentries = $DB->count_records_sql($sqlarray['sql'], $sqlarray['params']);
804         } else {
805             $totalentries = 0;
806         }
808         $entries = $this->get_entries($start, $limit);
809         $pagingbar = new paging_bar($totalentries, $page, $limit, $this->get_baseurl());
810         $pagingbar->pagevar = 'blogpage';
811         $blogheaders = blog_get_headers();
813         echo $OUTPUT->render($pagingbar);
815         /* TODO RSS link
816         if ($CFG->enablerssfeeds) {
817             $this->blog_rss_print_link($filtertype, $filterselect, $tag);
818         }
819         */
821         if (has_capability('moodle/blog:create', $sitecontext)) {
822             //the user's blog is enabled and they are viewing their own blog
823             $userid = optional_param('userid', null, PARAM_INT);
825             if (empty($userid) || (!empty($userid) && $userid == $USER->id)) {
826                 $addurl = new moodle_url("$CFG->wwwroot/blog/edit.php");
827                 $urlparams = array('action' => 'add',
828                                    'userid' => $userid,
829                                    'courseid' => optional_param('courseid', null, PARAM_INT),
830                                    'groupid' => optional_param('groupid', null, PARAM_INT),
831                                    'modid' => optional_param('modid', null, PARAM_INT),
832                                    'tagid' => optional_param('tagid', null, PARAM_INT),
833                                    'tag' => optional_param('tag', null, PARAM_INT),
834                                    'search' => optional_param('search', null, PARAM_INT));
836                 foreach ($urlparams as $var => $val) {
837                     if (empty($val)) {
838                         unset($urlparams[$var]);
839                     }
840                 }
841                 $addurl->params($urlparams);
843                 $addlink = '<div class="addbloglink">';
844                 $addlink .= '<a href="'.$addurl->out().'">'. $blogheaders['stradd'].'</a>';
845                 $addlink .= '</div>';
846                 echo $addlink;
847             }
848         }
850         if ($entries) {
851             $count = 0;
853             foreach ($entries as $entry) {
854                 $blogentry = new blog_entry(null, $entry);
855                 $blogentry->print_html();
856                 $count++;
857             }
859             echo $OUTPUT->render($pagingbar);
861             if (!$count) {
862                 print '<br /><div style="text-align:center">'. get_string('noentriesyet', 'blog') .'</div><br />';
863             }
865             print $morelink.'<br />'."\n";
866             return;
867         }
868     }
870     /// Find the base url from $_GET variables, for print_paging_bar
871     public function get_baseurl() {
872         $getcopy  = $_GET;
874         unset($getcopy['blogpage']);
876         if (!empty($getcopy)) {
877             $first = false;
878             $querystring = '';
880             foreach ($getcopy as $var => $val) {
881                 if (!$first) {
882                     $first = true;
883                     $querystring .= "?$var=$val";
884                 } else {
885                     $querystring .= '&amp;'.$var.'='.$val;
886                     $hasparam = true;
887                 }
888             }
889         } else {
890             $querystring = '?';
891         }
893         return strip_querystring(qualified_me()) . $querystring;
895     }
898 /**
899  * Abstract class for blog_filter objects.
900  * A set of core filters are implemented here. To write new filters, you need to subclass
901  * blog_filter and give it the name of the type you want (for example, blog_filter_entry).
902  * The blog_filter abstract class will automatically use it when the filter is added to the
903  * URL. The first parameter of the constructor is the ID of your filter, but it can be a string
904  * or have any other meaning you wish it to have. The second parameter is called $type and is
905  * used as a sub-type for filters that have a very similar implementation (see blog_filter_context for an example)
906  */
907 abstract class blog_filter {
908     /**
909      * An array of strings representing the available filter types for each blog_filter.
910      * @var array $availabletypes
911      */
912     public $availabletypes = array();
914     /**
915      * The type of filter (for example, types of blog_filter_context are site, course and module)
916      * @var string $type
917      */
918     public $type;
920     /**
921      * The unique ID for a filter's associated record
922      * @var int $id
923      */
924     public $id;
926     /**
927      * An array of table aliases that are used in the WHERE conditions
928      * @var array $tables
929      */
930     public $tables = array();
932     /**
933      * An array of WHERE conditions
934      * @var array $conditions
935      */
936     public $conditions = array();
938     /**
939      * An array of SQL params
940      * @var array $params
941      */
942     public $params = array();
944     /**
945      * An array of filter types which this particular filter type overrides: their conditions will not be evaluated
946      */
947     public $overrides = array();
949     public function __construct($id, $type=null) {
950         $this->id = $id;
951         $this->type = $type;
952     }
954     /**
955      * TODO This is poor design. A parent class should not know anything about its children.
956      * The default case helps to resolve this design issue
957      */
958     public static function get_instance($id, $type) {
960         switch ($type) {
961             case 'site':
962             case 'course':
963             case 'module':
964                 return new blog_filter_context($id, $type);
965                 break;
967             case 'group':
968             case 'user':
969                 return new blog_filter_user($id, $type);
970                 break;
972             case 'tag':
973                 return new blog_filter_tag($id);
974                 break;
976             default:
977                 $classname = "blog_filter_$type";
978                 if (class_exists($classname)) {
979                     return new $classname($id, $type);
980                 }
981         }
982     }
985 /**
986  * This filter defines the context level of the blog entries being searched: site, course, module
987  */
988 class blog_filter_context extends blog_filter {
989     /**
990      * Constructor
991      *
992      * @param string $type
993      * @param int    $id
994      */
995     public function __construct($id=null, $type='site') {
996         global $SITE, $CFG, $DB;
998         if (empty($id)) {
999             $this->type = 'site';
1000         } else {
1001             $this->id = $id;
1002             $this->type = $type;
1003         }
1005         $this->availabletypes = array('site' => get_string('site'), 'course' => get_string('course'), 'module' => get_string('module'));
1007         switch ($this->type) {
1008             case 'course': // Careful of site course!
1009                 // Ignore course filter if blog associations are not enabled
1010                 if ($this->id != $SITE->id && !empty($CFG->useblogassociations)) {
1011                     $this->overrides = array('site');
1012                     $context = get_context_instance(CONTEXT_COURSE, $this->id);
1013                     $this->tables['ba'] = 'blog_association';
1014                     $this->conditions[] = 'p.id = ba.blogid';
1015                     $this->conditions[] = 'ba.contextid = '.$context->id;
1016                     break;
1017                 } else {
1018                     // We are dealing with the site course, do not break from the current case
1019                 }
1021             case 'site':
1022                 // No special constraints
1023                 break;
1024             case 'module':
1025                 if (!empty($CFG->useblogassociations)) {
1026                     $this->overrides = array('course', 'site');
1028                     $context = get_context_instance(CONTEXT_MODULE, $this->id);
1029                     $this->tables['ba'] = 'blog_association';
1030                     $this->tables['p']  = 'post';
1031                     $this->conditions = array('p.id = ba.blogid', 'ba.contextid = ?');
1032                     $this->params = array($context->id);
1033                 }
1034                 break;
1035         }
1036     }
1039 /**
1040  * This filter defines the user level of the blog entries being searched: a userid or a groupid.
1041  * It can be combined with a context filter in order to refine the search.
1042  */
1043 class blog_filter_user extends blog_filter {
1044     public $tables = array('u' => 'user');
1046     /**
1047      * Constructor
1048      *
1049      * @param string $type
1050      * @param int    $id
1051      */
1052     public function __construct($id=null, $type='user') {
1053         global $CFG, $DB;
1054         $this->availabletypes = array('user' => get_string('user'), 'group' => get_string('group'));
1056         if (empty($id)) {
1057             $this->id = $USER->id;
1058             $this->type = 'user';
1059         } else {
1060             $this->id = $id;
1061             $this->type = $type;
1062         }
1064         if ($this->type == 'user') {
1065             $this->conditions = array('u.id = ?');
1066             $this->params = array($this->id);
1067             $this->overrides = array('group');
1069         } elseif ($this->type == 'group') {
1070             $this->overrides = array('course', 'site');
1072             $this->tables['gm'] = 'groups_members';
1073             $this->conditions[] = 'p.userid = gm.userid';
1074             $this->conditions[] = 'gm.groupid = ?';
1075             $this->params[]     = $this->id;
1077             if (!empty($CFG->useblogassociations)) {  // only show blog entries associated with this course
1078                 $coursecontext     = get_context_instance(CONTEXT_COURSE, $DB->get_field('groups', 'courseid', array('id' => $this->id)));
1079                 $this->tables['ba'] = 'blog_association';
1080                 $this->conditions[] = 'gm.groupid = ?';
1081                 $this->conditions[] = 'ba.contextid = ?';
1082                 $this->conditions[] = 'ba.blogid = p.id';
1083                 $this->params[]     = $this->id;
1084                 $this->params[]     = $coursecontext->id;
1085             }
1086         }
1088     }
1091 /**
1092  * This filter defines a tag by which blog entries should be searched.
1093  */
1094 class blog_filter_tag extends blog_filter {
1095     public $tables = array('t' => 'tag', 'ti' => 'tag_instance', 'p' => 'post');
1097     /**
1098      * Constructor
1099      *
1100      * @return void
1101      */
1102     public function __construct($id) {
1103         global $DB;
1104         $this->id = $id;
1106         $this->conditions = array('ti.tagid = t.id',
1107                                   "ti.itemtype = 'post'",
1108                                   'ti.itemid = p.id',
1109                                   't.id = ?');
1110         $this->params = array($this->id);
1111     }
1114 /**
1115  * This filter defines a specific blog entry id.
1116  */
1117 class blog_filter_entry extends blog_filter {
1118     public $conditions = array('p.id = ?');
1119     public $overrides  = array('site', 'course', 'module', 'group', 'user', 'tag');
1121     public function __construct($id) {
1122         $this->id = $id;
1123         $this->params[] = $this->id;
1124     }
1127 /**
1128  * This filter restricts the results to a time interval in seconds up to mktime()
1129  */
1130 class blog_filter_since extends blog_filter {
1131     public function __construct($interval) {
1132         $this->conditions[] = 'p.lastmodified >= ? AND p.lastmodified <= ?';
1133         $this->params[] = mktime() - $interval;
1134         $this->params[] = mktime();
1135     }
1138 /**
1139  * Filter used to perform full-text search on an entry's subject, summary and content
1140  */
1141 class blog_filter_search extends blog_filter {
1143     public function __construct($searchterm) {
1144         global $DB;
1145         $ilike = $DB->sql_ilike();
1146         $this->conditions = array("(p.summary $ilike ? OR
1147                                     p.content $ilike ? OR
1148                                     p.subject $ilike ?)");
1149         $this->params[] = "%$searchterm%";
1150         $this->params[] = "%$searchterm%";
1151         $this->params[] = "%$searchterm%";
1152     }