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