navigation MDL-20297 Fixed bug whereby ajax events wern't be detached leading to...
[moodle.git] / blog / locallib.php
CommitLineData
cae83708 1<?php
2
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/>.
17
18
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 */
27
28
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 posts 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 */
40class 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;
49
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;
62
63 // Other class variables
64 public $form;
65 public $tags = array();
66
67 // Methods
68 /**
69 * Constructor. If given an id, will fetch the corresponding record from the DB.
70 *
b73d1ca4 71 * @param mixed $idorparams A blog entry id if INT, or data for a new entry if array
cae83708 72 */
b73d1ca4 73 public function __construct($idorparams=null, $form=null) {
74 global $DB, $PAGE;
cae83708 75
b73d1ca4 76 if (!empty($idorparams) && !is_array($idorparams) && !is_object($idorparams)) {
77 $object = $DB->get_record('post', array('id' => $idorparams));
cae83708 78 foreach ($object as $var => $val) {
79 $this->$var = $val;
80 }
b73d1ca4 81 } else if (!empty($idorparams) && (is_array($idorparams) || is_object($idorparams))) {
82 foreach ($idorparams as $var => $val) {
cae83708 83 $this->$var = $val;
84 }
85 }
86
87 $this->form = $form;
88 }
89
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) {
97
b73d1ca4 98 global $USER, $CFG, $COURSE, $DB, $OUTPUT, $PAGE;
99
cae83708 100
cae83708 101 $user = $DB->get_record('user', array('id'=>$this->userid));
b73d1ca4 102 // Comments
103 $cmt = new stdClass();
104 $cmt->contextid = get_context_instance(CONTEXT_USER, $user->id)->id;
105 $cmt->area = 'format_blog';
106 $cmt->itemid = $this->id;
107 $options->comments = $cmt;
cae83708 108
b73d1ca4 109 $template['body'] = format_text($this->summary, $this->format, $options);
cae83708 110 $template['title'] = '<a id="b'. s($this->id) .'" />';
111 //enclose the title in nolink tags so that moodle formatting doesn't autolink the text
112 $template['title'] .= '<span class="nolink">'. format_string($this->subject) .'</span>';
113 $template['userid'] = $user->id;
114 $template['author'] = fullname($user);
115 $template['created'] = userdate($this->created);
116
117 if($this->created != $this->lastmodified){
118 $template['lastmod'] = userdate($this->lastmodified);
119 }
120
121 $template['publishstate'] = $this->publishstate;
122
123 $stredit = get_string('edit');
124 $strdelete = get_string('delete');
125
126 //check to see if the entry is unassociated with group/course level access
127 $unassociatedentry = false;
128 if (!empty($CFG->useblogassociations) && ($this->publishstate == 'group' || $this->publishstate == 'course')) {
129 if (!$DB->record_exists('blog_association', array('blogid' => $this->id))) {
130 $unassociatedentry = true;
131 }
132 }
133
134 /// Start printing of the blog
135 $table = new html_table();
136 $table->cellspacing = 0;
137 $table->add_classes('forumpost blog_entry blog'. ($unassociatedentry ? 'draft' : $template['publishstate']));
138 $table->width = '100%';
139
140 $picturecell = new html_table_cell();
141 $picturecell->add_classes('picture left');
142 $picturecell->text = $OUTPUT->user_picture(moodle_user_picture::make($user, SITEID));
143
144 $table->head[] = $picturecell;
145
146 $topiccell = new html_table_cell();
147 $topiccell->add_classes('topic starter');
148 $topiccell->text = $OUTPUT->container($template['title'], 'subject');
149 $topiccell->text .= $OUTPUT->container_start('author');
150
151 $fullname = fullname($user, has_capability('moodle/site:viewfullnames', get_context_instance(CONTEXT_COURSE, $COURSE->id)));
152 $by = new object();
153 $by->name = $OUTPUT->link(html_link::make(new moodle_url($CFG->wwwroot.'/user/view.php', array('id' => $user->id, 'course' => $COURSE->id)), $fullname));
154 $by->date = $template['created'];
155
156 $topiccell->text .= get_string('bynameondate', 'forum', $by);
157 $topiccell->text .= $OUTPUT->container_end();
158 $topiccell->header = false;
159 $table->head[] = $topiccell;
160
161 /// Actual content
162 $mainrow = new html_table_row();
163
164 $leftsidecell = new html_table_cell();
165 $leftsidecell->add_classes('left side');
166 $mainrow->cells[] = $leftsidecell;
167
168 $contentcell = new html_table_cell();
169 $contentcell->add_class('content');
170
171 if ($this->attachment) {
172 $attachedimages = $OUTPUT->container($this->print_attachments(), 'attachments');
173 } else {
174 $attachedimages = '';
175 }
176
177 //retrieve associations in case they're needed early
5f4d4d80 178 $blogassociations = $DB->get_records('blog_association', array('blogid' => $this->id));
cae83708 179 //determine text for publish state
180 switch ($template['publishstate']) {
181 case 'draft':
182 $blogtype = get_string('publishtonoone', 'blog');
183 break;
184 case 'site':
185 $blogtype = get_string('publishtosite', 'blog');
186 break;
187 case 'public':
188 $blogtype = get_string('publishtoworld', 'blog');
189 break;
190 default:
191 $blogtype = '';
192 break;
193
194 }
195
196 $contentcell->text .= $OUTPUT->container($blogtype, 'audience');
197
198 $contentcell->text .= $template['body'];
199 $contentcell->text .= $attachedimages;
200
201 // Uniquehash is used as a link to an external blog
202 if (!empty($this->uniquehash) && blog_is_valid_url($this->uniquehash)) {
203 $contentcell->text .= $OUTPUT->container_start('externalblog');
204 $contentcell->text .= $OUTPUT->link(html_link::make($this->uniquehash, get_string('linktooriginalentry', 'blog')));
205 $contentcell->text .= $OUTPUT->container_end();
206 }
207
208 // Links to tags
209
210 if (!empty($CFG->usetags) && ($blogtags = tag_get_tags_csv('post', $this->id)) ) {
211 $contentcell->text .= $OUTPUT->container_start('tags');
212
213 if ($blogtags) {
214 $contentcell->text .= get_string('tags', 'tag') .': '. $blogtags;
215 }
216 $contentcell->text .= $OUTPUT->container_end();
217 }
218
219 //add associations
5f4d4d80 220 if (!empty($CFG->useblogassociations) && $blogassociations) {
cae83708 221 $contentcell->text .= $OUTPUT->container_start('tags');
5f4d4d80 222 $assocstr = '';
223 $hascourseassocs = false;
cae83708 224
5f4d4d80 225 foreach ($blogassociations as $assocrec) { //first find and show the associated course
226 $context_rec = $DB->get_record('context', array('id' => $assocrec->contextid));
cae83708 227 if ($context_rec->contextlevel == CONTEXT_COURSE) {
228 $associcon = new moodle_action_icon();
229 $associcon->link->url = new moodle_url($CFG->wwwroot.'/course/view.php', array('id' => $context_rec->instanceid));
230 $associcon->image->src = $OUTPUT->old_icon_url('i/course');
231 $associcon->linktext = $DB->get_field('course', 'shortname', array('id' => $context_rec->instanceid));
5f4d4d80 232 $assocstr .= $OUTPUT->action_icon($associcon);
233 $hascourseassocs = true;
cae83708 234 }
235 }
236
5f4d4d80 237 foreach ($blogassociations as $assocrec) { //now show each mod association
238 $context_rec = $DB->get_record('context', array('id' => $assocrec->contextid));
cae83708 239
240 if ($context_rec->contextlevel == CONTEXT_MODULE) {
5f4d4d80 241 if ($hascourseassocs) {
242 $assocstr .= ', ';
243 $hascourseassocs = false;
244 }
245
cae83708 246 $modinfo = $DB->get_record('course_modules', array('id' => $context_rec->instanceid));
247 $modname = $DB->get_field('modules', 'name', array('id' => $modinfo->module));
248
249 $associcon = new moodle_action_icon();
250 $associcon->link->url = new moodle_url($CFG->wwwroot.'/mod/'.$modname.'/view.php', array('id' => $modinfo->id));
251 $associcon->image->src = $OUTPUT->mod_icon_url('icon', $modname);
252 $associcon->linktext = $DB->get_field($modname, 'name', array('id' => $modinfo->instance));
5f4d4d80 253 $assocstr .= $OUTPUT->action_icon($associcon);
254 $assocstr .= ', ';
cae83708 255
cae83708 256 }
257 }
5f4d4d80 258 $assocstr = substr($assocstr, 0, -2);
259 $contentcell->text .= get_string('associations', 'blog') . ': '. $assocstr;
cae83708 260
261 $contentcell->text .= $OUTPUT->container_end();
262 }
263
264 if ($unassociatedentry) {
265 $contentcell->text .= $OUTPUT->container(get_string('associationunviewable', 'blog'), 'noticebox');
266 }
267
268 /// Commands
269
270 $contentcell->text .= $OUTPUT->container_start('commands');
271
272 if (blog_user_can_edit_entry($this)) {
9366362a 273 $contentcell->text .= $OUTPUT->link(html_link::make(new moodle_url($CFG->wwwroot.'/blog/edit.php', array('action' => 'edit', 'entryid' => $this->id)), $stredit)) . ' | ';
5f4d4d80 274 $contentcell->text .= $OUTPUT->link(html_link::make(new moodle_url($CFG->wwwroot.'/blog/edit.php', array('action' => 'delete', 'entryid' => $this->id)), $strdelete)) . ' | ';
cae83708 275 }
276
277 $contentcell->text .= $OUTPUT->link(html_link::make(new moodle_url($CFG->wwwroot.'/blog/index.php', array('entryid' => $this->id)), get_string('permalink', 'blog')));
278
279 $contentcell->text .= $OUTPUT->container_end();
280
281 if (isset($template['lastmod']) ){
282 $contentcell->text .= '<div style="font-size: 55%;">';
283 $contentcell->text .= ' [ '.get_string('modified').': '.$template['lastmod'].' ]';
284 $contentcell->text .= '</div>';
285 }
286
287 $mainrow->cells[] = $contentcell;
288 $table->data = array($mainrow);
289
290 if ($return) {
291 return $OUTPUT->table($table);
292 } else {
293 echo $OUTPUT->table($table);
294 }
295 }
296
297 /**
298 * Inserts this entry in the database. Access control checks must be done by calling code.
299 *
300 * @param mform $form Used for attachments
301 * @return void
302 */
303 public function process_attachment($form) {
304 $this->form = $form;
305 }
306
307 /**
308 * Inserts this entry in the database. Access control checks must be done by calling code.
309 * TODO Set the publishstate correctly
310 * @param mform $form Used for attachments
311 * @return void
312 */
313 public function add() {
314 global $CFG, $USER, $DB;
315
316 unset($this->id);
317 $this->module = 'blog';
318 $this->userid = (empty($this->userid)) ? $USER->id : $this->userid;
319 $this->lastmodified = time();
320 $this->created = time();
321
322 // Insert the new blog entry.
323 $this->id = $DB->insert_record('post', $this);
324
325 // Add blog attachment
326 if (!empty($this->form) && $this->form->get_new_filename('attachment')) {
327 if ($this->form->save_stored_file('attachment', SYSCONTEXTID, 'blog', $this->id, '/', $this->form->get_new_filename('attachment'), $USER->id)) {
328 $DB->set_field("post", "attachment", 1, array("id"=>$this->id));
329 }
330 }
331
332 // Update tags.
333 $this->add_tags_info();
334
335 if (!empty($CFG->useblogassociations)) {
336 $this->add_associations();
337 add_to_log(SITEID, 'blog', 'add', 'index.php?userid='.$this->userid.'&entryid='.$this->id, $this->subject);
338
339 }
340
341 tag_set('post', $this->id, $this->tags);
342 }
343
344 /**
345 * Updates this entry in the database. Access control checks must be done by calling code.
346 *
347 * @param mform $form Used for attachments
348 * @return void
349 */
350 public function edit($params=array(), $form=null) {
b73d1ca4 351 global $CFG, $USER, $DB, $PAGE;
cae83708 352
353 $this->form = $form;
354 foreach ($params as $var => $val) {
355 $this->$var = $val;
356 }
357
b73d1ca4 358 $this->summary = file_save_draft_area_files($params->summary['itemid'], $PAGE->context->id, 'blog_post', $this->id, array('subdirs'=>true), $params->summary['text']);
359
5f4d4d80 360 if (!empty($CFG->useblogassociations)) {
361 $this->add_associations();
cae83708 362 }
363
364 $this->lastmodified = time();
365
366 if (!empty($this->form) && $this->form->get_new_filename('attachment')) {
367 $this->delete_attachments();
368 if ($this->form->save_stored_file('attachment', SYSCONTEXTID, 'blog', $this->id, '/', false, $USER->id)) {
369 $this->attachment = 1;
370 } else {
371 $this->attachment = 1;
372 }
373 }
374
375 // Update record
376 $DB->update_record('post', $this);
377 tag_set('post', $this->id, $this->tags);
378
379 add_to_log(SITEID, 'blog', 'update', 'index.php?userid='.$USER->id.'&entryid='.$this->id, $this->subject);
380 }
381
382 /**
383 * Deletes this entry from the database. Access control checks must be done by calling code.
384 *
385 * @return void
386 */
387 public function delete() {
388 global $DB, $USER;
389
390 $returnurl = '';
391
cae83708 392 $this->delete_attachments();
393
394 $DB->delete_records('post', array('id' => $this->id));
395 tag_set('post', $this->id, array());
396
397 add_to_log(SITEID, 'blog', 'delete', 'index.php?userid='. $this->userid, 'deleted blog entry with entry id# '. $this->id);
398 }
399
400 /**
401 * function to add all context associations to an entry
402 * @param int entry - data object processed to include all 'entry' fields and extra data from the edit_form object
403 */
404 public function add_associations() {
405 global $DB, $USER;
406
407 $allowaddcourseassoc = true;
408
409 $this->remove_associations();
410
411 if (!empty($this->courseassoc)) {
412 $this->add_association($this->courseassoc);
413 $allowaddcourseassoc = false;
414 }
415
416 if (!empty($this->modassoc)) {
417 foreach ($this->modassoc as $modid) {
418 $this->add_association($modid, $allowaddcourseassoc);
419 $allowaddcourseassoc = false; //let the course be added the first time
420 }
421 }
422 }
423
424 /**
425 * add a single association for a blog entry
426 * @param int contextid - id of context to associate with the blog entry
427 */
428 public function add_association($contextid) {
429 global $DB;
430
431 $assoc_object = new StdClass;
432 $assoc_object->contextid = $contextid;
433 $assoc_object->blogid = $this->id;
434 $DB->insert_record('blog_association', $assoc_object);
435 }
436
437 /**
438 * remove all associations for a blog entry
439 * @return voic
440 */
441 public function remove_associations() {
442 global $DB;
443 $DB->delete_records('blog_association', array('blogid' => $this->id));
444 }
445
446 /**
447 * Deletes all the user files in the attachments area for an entry
448 *
449 * @return void
450 */
451 public function delete_attachments() {
452 $fs = get_file_storage();
453 $fs->delete_area_files(SYSCONTEXTID, 'blog', $this->id);
454 }
455
456 /**
457 * if return=html, then return a html string.
458 * if return=text, then return a text-only string.
459 * otherwise, print HTML for non-images, and return image HTML
460 *
461 * @param bool $return Whether to return or print the generated code
462 * @return void
463 */
464 public function print_attachments($return=false) {
465 global $CFG;
466
467 require_once($CFG->libdir.'/filelib.php');
468
469 $fs = get_file_storage();
470 $browser = get_file_browser();
471
472 $files = $fs->get_area_files(SYSCONTEXTID, 'blog', $this->id);
473
474 $imagereturn = "";
475 $output = "";
476
477 $strattachment = get_string("attachment", "forum");
478
479 foreach ($files as $file) {
480 if ($file->is_directory()) {
481 continue;
482 }
483
484 $filename = $file->get_filename();
485 $ffurl = file_encode_url($CFG->wwwroot.'/pluginfile.php', '/'.SYSCONTEXTID.'/blog/'.$this->id.'/'.$filename);
486 $type = $file->get_mimetype();
487 $icon = mimeinfo_from_type("icon", $type);
488 $type = mimeinfo_from_type("type", $type);
489
490 $image = "<img src=\"$CFG->pixpath/f/$icon\" class=\"icon\" alt=\"\" />";
491
492 if ($return == "html") {
493 $output .= "<a href=\"$ffurl\">$image</a> ";
494 $output .= "<a href=\"$ffurl\">$filename</a><br />";
495
496 } else if ($return == "text") {
497 $output .= "$strattachment $filename:\n$ffurl\n";
498
499 } else {
500 if (in_array($type, array('image/gif', 'image/jpeg', 'image/png'))) { // Image attachments don't get printed as links
501 $imagereturn .= "<br /><img src=\"$ffurl\" alt=\"\" />";
502 } else {
503 $imagereturn .= "<a href=\"$ffurl\">$image</a> ";
504 $imagereturn .= filter_text("<a href=\"$ffurl\">$filename</a><br />");
505 }
506 }
507 }
508
509 if ($return) {
510 return $output;
511 }
512
513 return $imagereturn;
514
515 }
516
517 /**
518 * function to attach tags into an entry
519 * @return void
520 */
521 public function add_tags_info() {
522
523 $tags = array();
524
525 if ($otags = optional_param('otags', '', PARAM_INT)) {
526 foreach ($otags as $tagid) {
527 // TODO : make this use the tag name in the form
528 if ($tag = tag_get('id', $tagid)) {
529 $tags[] = $tag->name;
530 }
531 }
532 }
533
534 tag_set('post', $this->id, $tags);
535 }
536
537 /**
538 * User can edit a blog entry if this is their own blog entry and they have
539 * the capability moodle/blog:create, or if they have the capability
540 * moodle/blog:manageentries.
541 * This also applies to deleting of entries.
542 *
543 * @param int $userid Optional. If not given, $USER is used
544 * @return boolean
545 */
546 public function can_user_edit($userid=null) {
547 global $CFG, $USER;
548
549 if (empty($userid)) {
550 $userid = $USER->id;
551 }
552
553 $sitecontext = get_context_instance(CONTEXT_SYSTEM);
554
555 if (has_capability('moodle/blog:manageentries', $sitecontext)) {
556 return true; // can edit any blog entry
557 }
558
559 if ($this->userid == $userid && has_capability('moodle/blog:create', $sitecontext)) {
560 return true; // can edit own when having blog:create capability
561 }
562
563 return false;
564 }
565
566 /**
567 * Checks to see if a user can view the blogs of another user.
568 * Only blog level is checked here, the capabilities are enforced
569 * in blog/index.php
570 *
571 * @param int $targetuserid ID of the user we are checking
572 *
573 * @return bool
574 */
575 public function can_user_view($targetuserid) {
576 global $CFG, $USER, $DB;
577
578 if (empty($CFG->bloglevel)) {
579 return false; // blog system disabled
580 }
581
582 if (!empty($USER->id) and $USER->id == $targetuserid) {
583 return true; // can view own entries in any case
584 }
585
586 $sitecontext = get_context_instance(CONTEXT_SYSTEM);
587 if (has_capability('moodle/blog:manageentries', $sitecontext)) {
588 return true; // can manage all entries
589 }
590
591 // coming for 1 entry, make sure it's not a draft
592 if ($this->publishstate == 'draft') {
593 return false; // can not view draft of others
594 }
595
596 // coming for 1 entry, make sure user is logged in, if not a public blog
597 if ($this->publishstate != 'public' && !isloggedin()) {
598 return false;
599 }
600
601 switch ($CFG->bloglevel) {
602 case BLOG_GLOBAL_LEVEL:
603 return true;
604 break;
605
606 case BLOG_SITE_LEVEL:
607 if (!empty($USER->id)) { // not logged in viewers forbidden
608 return true;
609 }
610 return false;
611 break;
612
613 case BLOG_USER_LEVEL:
614 default:
615 $personalcontext = get_context_instance(CONTEXT_USER, $targetuserid);
616 return has_capability('moodle/user:readuserblogs', $personalcontext);
617 break;
618 }
619 }
620
621 /**
622 * Use this function to retrieve a list of publish states available for
623 * the currently logged in user.
624 *
625 * @return array This function returns an array ideal for sending to moodles'
626 * choose_from_menu function.
627 */
628
629 public static function get_applicable_publish_states() {
630 global $CFG;
631 $options = array();
632
633 // everyone gets draft access
634 if ($CFG->bloglevel >= BLOG_USER_LEVEL) {
635 $options['draft'] = get_string('publishtonoone', 'blog');
636 }
637
638 if ($CFG->bloglevel > BLOG_USER_LEVEL) {
639 $options['site'] = get_string('publishtosite', 'blog');
640 }
641
642 if ($CFG->bloglevel >= BLOG_GLOBAL_LEVEL) {
643 $options['public'] = get_string('publishtoworld', 'blog');
644 }
645
646 return $options;
647 }
648}
649
650/**
651 * Abstract Blog_Listing class: used to gather blog entries and output them as listings. One of the subclasses must be used.
652 *
653 * @package moodlecore
654 * @subpackage blog
655 * @copyright 2009 Nicolas Connault
656 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
657 */
658class blog_listing {
659 /**
660 * Array of blog_entry objects.
661 * @var array $entries
662 */
663 public $entries = array();
664
665 /**
666 * An array of blog_filter_* objects
667 * @var array $filters
668 */
669 public $filters = array();
670
671 /**
672 * Constructor
673 *
674 * @param array $filters An associative array of filtername => filterid
675 */
676 public function __construct($filters=array()) {
677 // Unset filters overridden by more specific filters
678 foreach ($filters as $type => $id) {
679 if (!empty($type) && !empty($id)) {
680 $this->filters[$type] = blog_filter::get_instance($id, $type);
681 }
682 }
683
684 foreach ($this->filters as $type => $filter) {
685 foreach ($filter->overrides as $override) {
686 if (array_key_exists($override, $this->filters)) {
687 unset($this->filters[$override]);
688 }
689 }
690 }
691 }
692
693 /**
694 * Fetches the array of blog entries.
695 *
696 * @return array
697 */
698 public function get_entries($start=0, $limit=10) {
699 global $DB;
700
701 if (empty($this->entries)) {
702 if ($sql_array = $this->get_entry_fetch_sql()) {
703 $this->entries = $DB->get_records_sql($sql_array['sql'] . " LIMIT $start, $limit", $sql_array['params']);
704 } else {
705 return false;
706 }
707 }
708
709 return $this->entries;
710 }
711
712 public function get_entry_fetch_sql($count=false, $sort='lastmodified DESC', $userid = false) {
713 global $DB, $USER, $CFG;
714
715 if(!$userid) {
716 $userid = $USER->id;
717 }
718
719 // The query used to locate blog entries is complicated. It will be built from the following components:
720 $requiredfields = "p.*, u.firstname, u.lastname, u.email"; // the SELECT clause
721 $tables = array('p' => 'post', 'u' => 'user'); // components of the FROM clause (table_id => table_name)
722 $conditions = array('u.deleted = 0', 'p.userid = u.id', 'p.module = \'blog\''); // components of the WHERE clause (conjunction)
723
724 // build up a clause for permission constraints
725
726 $params = array();
727
728 // fix for MDL-9165, use with readuserblogs capability in a user context can read that user's private blogs
729 // admins can see all blogs regardless of publish states, as described on the help page
730 if (has_capability('moodle/user:readuserblogs', get_context_instance(CONTEXT_SYSTEM))) {
731 // don't add permission constraints
732
733 } else if(!empty($this->filters['user']) && has_capability('moodle/user:readuserblogs',
734 get_context_instance(CONTEXT_USER, (empty($this->filters['user']->id) ? 0 : $this->filters['user']->id)))) {
735 // don't add permission constraints
736
737 } else {
738 if (isloggedin() && !has_capability('moodle/legacy:guest', get_context_instance(CONTEXT_SYSTEM, SITEID), $userid, false)) {
739 $assocexists = $DB->record_exists('blog_association', array()); //dont check association records if there aren't any
740
741 //begin permission sql clause
742 $permissionsql = '(p.userid = ? ';
743 $params[] = $userid;
744
745 if ($CFG->bloglevel >= BLOG_SITE_LEVEL) { // add permission to view site-level entries
746 $permissionsql .= " OR p.publishstate = 'site' ";
747 }
748
749 if ($CFG->bloglevel >= BLOG_GLOBAL_LEVEL) { // add permission to view global entries
750 $permissionsql .= " OR p.publishstate = 'public' ";
751 }
752
753 $permissionsql .= ') '; //close permissions sql clause
754 } else { // default is access to public entries
755 $permissionsql = "p.publishstate = 'public'";
756 }
757 $conditions[] = $permissionsql; //add permission constraints
758 }
759
760 foreach ($this->filters as $type => $blog_filter) {
761 $conditions = array_merge($conditions, $blog_filter->conditions);
762 $params = array_merge($params, $blog_filter->params);
763 $tables = array_merge($tables, $blog_filter->tables);
764 }
765
766 $tablessql = ''; // build up the FROM clause
767 foreach ($tables as $tablename => $table) {
768 $tablessql .= ($tablessql ? ', ' : '').'{'.$table.'} '.$tablename;
769 }
770
771 $sql = ($count) ? 'SELECT COUNT(*)' : 'SELECT ' . $requiredfields;
772 $sql .= " FROM $tablessql WHERE " . implode(' AND ', $conditions);
527761e0 773 $sql .= ($count) ? '' : " ORDER BY $sort";
cae83708 774
775 return array('sql' => $sql, 'params' => $params);
776 }
777
778 /**
779 * Outputs all the blog entries aggregated by this blog listing.
780 *
781 * @return void
782 */
783 public function print_entries() {
784 global $CFG, $USER, $DB, $OUTPUT;
785 $sitecontext = get_context_instance(CONTEXT_SYSTEM);
786
787 $page = optional_param('blogpage', 0, PARAM_INT);
788 $limit = optional_param('limit', get_user_preferences('blogpagesize', 10), PARAM_INT);
789 $start = $page * $limit;
790
791 $morelink = '<br />&nbsp;&nbsp;';
792
793 if ($sql_array = $this->get_entry_fetch_sql(true)) {
794 $totalentries = $DB->count_records_sql($sql_array['sql'], $sql_array['params']);
795 } else {
796 $totalentries = 0;
797 }
798
799 $entries = $this->get_entries($start, $limit);
800 $pagingbar = moodle_paging_bar::make($totalentries, $page, $limit, $this->get_baseurl());
801 $pagingbar->pagevar = 'blogpage';
802
803 echo $OUTPUT->paging_bar($pagingbar);
804
805 /* TODO RSS link
806 if ($CFG->enablerssfeeds) {
807 $this->blog_rss_print_link($filtertype, $filterselect, $tag);
808 }
809 */
810
811 if (has_capability('moodle/blog:create', $sitecontext)) {
812 //the user's blog is enabled and they are viewing their own blog
813 $userid = optional_param('userid', null, PARAM_INT);
814
815 if (empty($userid) || (!empty($userid) && $userid == $USER->id)) {
816 $add_url = new moodle_url("$CFG->wwwroot/blog/edit.php");
817 $url_params = array('action' => 'add',
818 'userid' => $userid,
819 'courseid' => optional_param('courseid', null, PARAM_INT),
820 'groupid' => optional_param('groupid', null, PARAM_INT),
821 'modid' => optional_param('modid', null, PARAM_INT),
822 'tagid' => optional_param('tagid', null, PARAM_INT),
823 'tag' => optional_param('tag', null, PARAM_INT),
824 'search' => optional_param('search', null, PARAM_INT));
825
826 foreach ($url_params as $var => $val) {
827 if (empty($val)) {
828 unset($url_params[$var]);
829 }
830 }
831 $add_url->params($url_params);
832
833 $addlink = '<div class="addbloglink">';
834 $addlink .= '<a href="'.$add_url->out().'">'. get_string('addnewentry', 'blog').'</a>';
835 $addlink .= '</div>';
836 echo $addlink;
837 }
838 }
839
840 if ($entries) {
841 $count = 0;
842
843 foreach ($entries as $entry) {
844 $blog_entry = new blog_entry($entry);
845 $blog_entry->print_html();
846 $count++;
847 }
848
849 echo $OUTPUT->paging_bar($pagingbar);
850
851 if (!$count) {
852 print '<br /><div style="text-align:center">'. get_string('noentriesyet', 'blog') .'</div><br />';
853 }
854
855 print $morelink.'<br />'."\n";
856 return;
857 }
858 }
859
860 /// Find the base url from $_GET variables, for print_paging_bar
861 public function get_baseurl() {
862 $getcopy = $_GET;
863
864 unset($getcopy['blogpage']);
865
866 if (!empty($getcopy)) {
867 $first = false;
868 $querystring = '';
869
870 foreach ($getcopy as $var => $val) {
871 if (!$first) {
872 $first = true;
873 $querystring .= "?$var=$val";
874 } else {
875 $querystring .= '&amp;'.$var.'='.$val;
876 $hasparam = true;
877 }
878 }
879 } else {
880 $querystring = '?';
881 }
882
883 return strip_querystring(qualified_me()) . $querystring;
884
885 }
886}
887
888/**
889 * Abstract class for blog_filter objects.
890 * A set of core filters are implemented here. To write new filters, you need to subclass
891 * blog_filter and give it the name of the type you want (for example, blog_filter_entry).
892 * The blog_filter abstract class will automatically use it when the filter is added to the
893 * URL. The first parameter of the constructor is the ID of your filter, but it can be a string
894 * or have any other meaning you wish it to have. The second parameter is called $type and is
895 * used as a sub-type for filters that have a very similar implementation (see blog_filter_context for an example)
896 */
897abstract class blog_filter {
898 /**
899 * An array of strings representing the available filter types for each blog_filter.
900 * @var array $available_types
901 */
902 public $available_types = array();
903
904 /**
905 * The type of filter (for example, types of blog_filter_context are site, course and module)
906 * @var string $type
907 */
908 public $type;
909
910 /**
911 * The unique ID for a filter's associated record
912 * @var int $id
913 */
914 public $id;
915
916 /**
917 * An array of table aliases that are used in the WHERE conditions
918 * @var array $tables
919 */
920 public $tables = array();
921
922 /**
923 * An array of WHERE conditions
924 * @var array $conditions
925 */
926 public $conditions = array();
927
928 /**
929 * An array of SQL params
930 * @var array $params
931 */
932 public $params = array();
933
934 /**
935 * An array of filter types which this particular filter type overrides: their conditions will not be evaluated
936 */
937 public $overrides = array();
938
939 public function __construct($id, $type=null) {
940 $this->id = $id;
941 $this->type = $type;
942 }
943
944 /**
945 * TODO This is poor design. A parent class should not know anything about its children.
946 * The default case helps to resolve this design issue
947 */
948 public static function get_instance($id, $type) {
949
950 switch ($type) {
951 case 'site':
952 case 'course':
953 case 'module':
954 return new blog_filter_context($id, $type);
955 break;
956
957 case 'group':
958 case 'user':
959 return new blog_filter_user($id, $type);
960 break;
961
962 case 'tag':
963 return new blog_filter_tag($id);
964 break;
965
966 default:
967 $class_name = "blog_filter_$type";
968 if (class_exists($class_name)) {
969 return new $class_name($id, $type);
970 }
971 }
972 }
973}
974
975/**
976 * This filter defines the context level of the blog entries being searched: site, course, module
977 */
978class blog_filter_context extends blog_filter {
979 /**
980 * Constructor
981 *
982 * @param string $type
983 * @param int $id
984 */
985 public function __construct($id=null, $type='site') {
986 global $SITE, $CFG, $DB;
987
988 if (empty($id)) {
989 $this->type = 'site';
990 } else {
991 $this->id = $id;
992 $this->type = $type;
993 }
994
995 $this->available_types = array('site' => get_string('site'), 'course' => get_string('course'), 'module' => get_string('module'));
996
997 switch ($this->type) {
998 case 'course': // Careful of site course!
999 // Ignore course filter if blog associations are not enabled
1000 if ($this->id != $SITE->id && !empty($CFG->useblogassociations)) {
1001 $this->overrides = array('site');
1002 $context = get_context_instance(CONTEXT_COURSE, $this->id);
1003 $this->tables['ba'] = 'blog_association';
1004 $this->conditions[] = 'p.id = ba.blogid';
1005 $this->conditions[] = 'ba.contextid = '.$context->id;
1006 break;
1007 } else {
1008 // We are dealing with the site course, do not break from the current case
1009 }
1010
1011 case 'site':
1012 // No special constraints
1013 break;
1014 case 'module':
1015 if (!empty($CFG->useblogassociations)) {
1016 $this->overrides = array('course', 'site');
1017
1018 $context = get_context_instance(CONTEXT_MODULE, $this->id);
1019 $this->tables['ba'] = 'blog_association';
1020 $this->tables['p'] = 'post';
1021 $this->conditions = array('p.id = ba.blogid', 'ba.contextid = ?');
1022 $this->params = array($context->id);
1023 }
1024 break;
1025 }
1026 }
1027}
1028
1029/**
1030 * This filter defines the user level of the blog entries being searched: a userid or a groupid.
1031 * It can be combined with a context filter in order to refine the search.
1032 */
1033class blog_filter_user extends blog_filter {
1034 public $tables = array('u' => 'user');
1035
1036 /**
1037 * Constructor
1038 *
1039 * @param string $type
1040 * @param int $id
1041 */
1042 public function __construct($id=null, $type='user') {
b73d1ca4 1043 global $CFG, $DB;
cae83708 1044 $this->available_types = array('user' => get_string('user'), 'group' => get_string('group'));
1045
1046 if (empty($id)) {
1047 $this->id = $USER->id;
1048 $this->type = 'user';
1049 } else {
1050 $this->id = $id;
1051 $this->type = $type;
1052 }
1053
1054 if ($this->type == 'user') {
1055 $this->conditions = array('u.id = ?');
1056 $this->params = array($this->id);
1057 $this->overrides = array('group');
1058
1059 } elseif ($this->type == 'group') {
1060 $this->overrides = array('course', 'site');
1061
1062 $this->tables['gm'] = 'groups_members';
1063 $this->conditions[] = 'p.userid = gm.userid';
1064 $this->conditions[] = 'gm.groupid = ?';
1065 $this->params[] = $this->id;
1066
1067 if (!empty($CFG->useblogassociations)) { // only show blog entries associated with this course
1068 $course_context = get_context_instance(CONTEXT_COURSE, $DB->get_field('groups', 'courseid', array('id' => $this->id)));
1069 $this->tables['ba'] = 'blog_association';
1070 $this->conditions[] = 'gm.groupid = ?';
1071 $this->conditions[] = 'ba.contextid = ?';
1072 $this->conditions[] = 'ba.blogid = p.id';
1073 $this->params[] = $this->id;
1074 $this->params[] = $course_context->id;
1075 }
1076 }
b73d1ca4 1077
cae83708 1078 }
1079}
1080
1081/**
1082 * This filter defines a tag by which blog entries should be searched.
1083 */
1084class blog_filter_tag extends blog_filter {
1085 public $tables = array('t' => 'tag', 'ti' => 'tag_instance', 'p' => 'post');
1086
1087 /**
1088 * Constructor
1089 *
1090 * @return void
1091 */
1092 public function __construct($id) {
1093 global $DB;
1094 $this->id = $id;
1095
1096 $this->conditions = array('ti.tagid = t.id',
1097 "ti.itemtype = 'post'",
1098 'ti.itemid = p.id',
1099 't.id = ?');
1100 $this->params = array($this->id);
1101 }
1102}
1103
1104/**
1105 * This filter defines a specific blog entry id.
1106 */
1107class blog_filter_entry extends blog_filter {
1108 public $conditions = array('p.id = ?');
1109 public $overrides = array('site', 'course', 'module', 'group', 'user', 'tag');
1110
1111 public function __construct($id) {
1112 $this->id = $id;
1113 $this->params[] = $this->id;
1114 }
1115}
1116
1117/**
1118 * Filter used to perform full-text search on an entry's subject, summary and content
1119 */
1120class blog_filter_search extends blog_filter {
1121
1122 public function __construct($search_term) {
1123 global $DB;
1124 $ilike = $DB->sql_ilike();
1125 $this->conditions = array("(p.summary $ilike '%$search_term%' OR
1126 p.content $ilike '%$search_term%' OR
1127 p.subject $ilike '%$search_term%')");
1128 }
1129}