lib.php 21.6 KB
Newer Older
1
2
3
4
<?php
/**
 *
 * @package    mahara
5
 * @subpackage blocktype.blog/taggedposts
6
 * @author     Catalyst IT Ltd
7
8
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL version 3 or later
 * @copyright  For copyright information on Mahara, please see the README file distributed with this software.
9
10
11
12
13
 *
 */

defined('INTERNAL') || die();

14
class PluginBlocktypeTaggedposts extends MaharaCoreBlocktype {
15

16
17
18
    const TAGTYPE_INCLUDE = 1;
    const TAGTYPE_EXCLUDE = 0;

19
    public static function get_title() {
20
        return get_string('title', 'blocktype.blog/taggedposts');
21
22
23
    }

    public static function get_description() {
24
        return get_string('description', 'blocktype.blog/taggedposts');
25
26
27
    }

    public static function get_categories() {
28
        return array('blog' => 13000);
29
30
    }

31
32
33
34
35
36
37
38
39
40
    public static function get_instance_javascript(BlockInstance $bi) {
        $blockid = $bi->get('id');
        return array(
            array(
                'file'   => 'js/taggedposts.js',
                'initjs' => "addNewTaggedPostShortcut($blockid);",
            )
        );
    }

41
42
43
44
45
46
47
    /**
     * Given a list of tags, finds blocks by the current user that contain those tags
     * (Used to determine which ones to check for the view_artefact table)
     * @param array $tags
     * @return array
     */
    public static function find_matching_blocks(array $tags) {
48
49
        global $USER;

50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
        $taggedblockids = (array)get_column_sql(
                'SELECT bi.id as block
                FROM
                    {blocktype_taggedposts_tags} btt
                    INNER JOIN {block_instance} bi
                        ON btt.block_instance = bi.id
                    INNER JOIN {view} v
                        ON bi.view = v.id
                WHERE
                    v.owner = ?
                    AND btt.tagtype = ?
                    AND btt.tag IN (' . implode(',', db_array_to_ph($tags)) . ')
                ',
                array_merge(
                    array(
                        $USER->id,
                        PluginBlocktypeTaggedposts::TAGTYPE_INCLUDE
                    ),
                    $tags
                )
        );
        if ($taggedblockids) {
            return $taggedblockids;
        }
        else {
            return array();
        }
    }

    /**
     * Returns the blog posts that will be displayed by this block.
     *
     * @param BlockInstance $instance
     * @param array $tagsin Optional reference variable for finding out the "include" tags used by this block
     * @param array $tagsout Optional reference variable for finding out the "extclude" tags used by this block
     * @return array of blogpost records
     */
    public static function get_blog_posts_in_block(BlockInstance $instance, &$tagsinreturn = null, &$tagsoutreturn = null) {
88
        $configdata = $instance->get('configdata');
89
        $results = array();
90

91
92
93
94
95
96
97
98
        $tagsin = $tagsout = array();
        $tagrecords = get_records_array('blocktype_taggedposts_tags', 'block_instance', $instance->get('id'), 'tagtype desc, tag', 'tag, tagtype');
        if ($tagrecords) {

            $view = $instance->get('view');
            $limit = isset($configdata['count']) ? (int) $configdata['count'] : 10;

            foreach ($tagrecords as $tag) {
99
100
                //tag is encoded in the db if it has special characters
                $tag->tag = htmlspecialchars_decode($tag->tag);
101
102
                if ($tag->tagtype == PluginBlocktypeTaggedposts::TAGTYPE_INCLUDE) {
                    $tagsin[] = $tag->tag;
103
104
                }
                else {
105
                    $tagsout[] = $tag->tag;
106
107
108
109
                }
            }
            $tagsout = array_filter($tagsout);
            $sqlvalues = array($view);
110
            $sql =
111
112
                'SELECT a.title, p.title AS parenttitle, a.id, a.parent, a.owner, a.author, a.authorname,
                    a.description, a.allowcomments, at.tag, a.ctime, a.mtime
113
114
115
116
117
                FROM {artefact} a
                JOIN {artefact} p ON a.parent = p.id
                JOIN {artefact_blog_blogpost} ab ON (ab.blogpost = a.id AND ab.published = 1)
                JOIN {artefact_tag} at ON (at.artefact = a.id)
                WHERE a.artefacttype = \'blogpost\'
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
                AND a.owner = (SELECT "owner" from {view} WHERE id = ?)';
            if (!empty($tagsin)) {
                foreach ($tagsin as $tagin) {
                    $sql .= ' AND EXISTS (
                        SELECT * FROM {artefact_tag} AS at
                        WHERE a.id = at.artefact
                        AND at.tag = ?
                    )';
                }
                $sqlvalues = array_merge($sqlvalues, $tagsin);
            }
            if (!empty($tagsout)) {
                foreach ($tagsout as $tagout) {
                    $sql .= ' AND NOT EXISTS (
                        SELECT * FROM {artefact_tag} AS at
                        WHERE a.id = at.artefact
                        AND at.tag = ?
                    )';
                }
                $sqlvalues = array_merge($sqlvalues, $tagsout);
            }
            $sql .= ' ORDER BY a.ctime DESC, a.id DESC';
            $results = get_records_sql_array($sql, $sqlvalues);
            // We need to filter this down to unique results
            if (!empty($results)) {
                $used = array();
                foreach ($results as $key => $result) {
                    if (array_search($result->id, $used) === false) {
                        $used[] = $result->id;
                    }
                    else {
                        unset($results[$key]);
                    }
                }
                if (!empty($limit)) {
                    $results = array_slice($results, 0, $limit);
                }
            }
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
            else {
                $results = array();
            }
        }

        if ($tagsinreturn !== null) {
            $tagsinreturn = $tagsin;
        }
        if ($tagsoutreturn !== null) {
            $tagsoutreturn = $tagsout;
        }

        return $results;
    }

    public static function render_instance(BlockInstance $instance, $editing=false) {
        global $USER;

        $configdata = $instance->get('configdata');
        $view = $instance->get('view');
        $full = isset($configdata['full']) ? $configdata['full'] : false;
        $results = array();

        $smarty = smarty_core();
        $smarty->assign('view', $view);
        $viewownerdisplay = null;
        // Display all posts, from all blogs, owned by this user
        $tagsin = $tagsout = array();
        $results = self::get_blog_posts_in_block($instance, $tagsin, $tagsout);
        if ($tagsin || $tagsout) {

187
188
189
190
191
192
193
194
            $smarty->assign('blockid', $instance->get('id'));
            $smarty->assign('editing', $editing);
            if ($editing) {
                // Get list of blogs owned by this user to create the "Add new post" shortcut while editing
                $viewowner = $instance->get_view()->get('owner');
                if (!$viewowner || !$blogs = get_records_select_array('artefact', 'artefacttype = \'blog\' AND owner = ?', array($viewowner), 'title ASC', 'id, title')) {
                    $blogs = array();
                }
195
                $smarty->assign('tagselect', implode(', ', $tagsin));
196
197
                $smarty->assign('blogs', $blogs);
            }
198

199
200
            // if posts are not found with the selected tag, notify the user
            if (!$results) {
201
202
                $smarty->assign('badtag', implode(', ', $tagsin));
                $smarty->assign('badnotag', implode(', ', $tagsout));
203
204
205
206
207
208
209
210
211
                return $smarty->fetch('blocktype:taggedposts:taggedposts.tpl');
            }

            // update the view_artefact table so journal entries are accessible when this is the only block on the page
            // referencing this journal
            $dataobject = array(
                'view'      => $view,
                'block'     => $instance->get('id'),
            );
212
213
            require_once(get_config('docroot') . 'lib/view.php');
            $viewobj = new View($view);
214
215
216
            require_once(get_config('docroot') . 'artefact/lib.php');
            safe_require('artefact', 'blog');
            safe_require('artefact', 'comment');
217
218
            foreach ($results as $result) {
                $dataobject["artefact"] = $result->parent;
219
                $result->displaydate= format_date(strtotime($result->ctime));
220
221
                $by = $result->author ? display_default_name($result->author) : $result->authorname;
                $result->postedbyon = ArtefactTypeBlog::display_postedby($result->ctime, $by);
222

223
224
225
                if ($result->ctime != $result->mtime) {
                    $result->updateddate= format_date(strtotime($result->mtime));
                }
226

227
                $artefact = new ArtefactTypeBlogpost($result->id);
228
                // get comments for this post
229
                $result->commentcount = count_records_select('artefact_comment_comment', "onartefact = {$result->id} AND private = 0 AND deletedby IS NULL AND hidden=0");
230
231
232
233
234
235
236
                $allowcomments = $artefact->get('allowcomments');
                if (empty($result->commentcount) && empty($allowcomments)) {
                    $result->commentcount = null;
                }

                list($commentcount, $comments) = ArtefactTypeComment::get_artefact_comments_for_view($artefact, $viewobj, null, false);
                $result->comments = $comments;
237
238
239
240
241
242

                // get all tags for this post
                $taglist = get_records_array('artefact_tag', 'artefact', $result->id, "tag DESC");
                foreach ($taglist as $t) {
                    $result->taglist[] = $t->tag;
                }
243
                if ($full) {
244
                    $rendered = $artefact->render_self(array('viewid' => $view, 'details' => true, 'blockid' => $instance->get('id')));
245
246
                    $result->html = $rendered['html'];
                    if (!empty($rendered['javascript'])) {
247
                        $result->html .= '<script type="application/javascript">' . $rendered['javascript'] . '</script>';
248
                    }
249
250
251
252
253
254
255
256
257
                    $attachments = $rendered['attachments'];
                    if (!empty($attachments)) {
                        $smarty->assign('attachments', $attachments);
                        $smarty->assign('postid', $result->id);
                        $result->attachments = $smarty->fetch('artefact:blog:render/blogpost_renderattachments.tpl');
                    }

                    safe_require('artefact', 'file');
                    $result->description = ArtefactTypeFolder::append_view_url($result->description, $view);
258
                }
259
260
261
262
            }

            // check if the user viewing the page is the owner of the selected tag
            $owner = $results[0]->owner;
263
            if ($USER->is_logged_in() && $USER->id != $owner) {
264
                $viewownerdisplay = get_user_for_display($owner);
265
            }
266
267
268
            $smarty->assign('tagsin', $tagsin);
            $smarty->assign('tagsout', $tagsout);
        }
269
        else if (!self::get_chooseable_tags()) {
270
271
272
            // error if block configuration fails
            $smarty->assign('configerror', get_string('notagsavailableerror', 'blocktype.blog/taggedposts'));
            return $smarty->fetch('blocktype:taggedposts:taggedposts.tpl');
273
274
275
        }
        else {
            // error if block configuration fails
276
            $smarty->assign('configerror', get_string('configerror', 'blocktype.blog/taggedposts'));
277
278
279
            return $smarty->fetch('blocktype:taggedposts:taggedposts.tpl');
        }

280
281
282
283
284
285
        // add any needed links to the tags
        $tagstr = $tagomitstr = '';
        foreach ($tagsin as $key => $tag) {
            if ($key > 0) {
                $tagstr .= ', ';
            }
286
            $tagstr .= ($viewownerdisplay) ? '"' . $tag . '"' : '"<a href="' . get_config('wwwroot') . 'tags.php?tag=' . urlencode($tag) . '&sort=name&type=text">' . hsc($tag) . '</a>"';
287
288
289
290
291
292
        }
        if (!empty($tagsout)) {
            foreach ($tagsout as $key => $tag) {
                if ($key > 0) {
                    $tagomitstr .= ', ';
                }
293
                $tagomitstr .= ($viewownerdisplay) ? '"' . $tag . '"' : '"<a href="' . get_config('wwwroot') . 'tags.php?tag=' . urlencode($tag) . '&sort=name&type=text">' . hsc($tag) . '</a>"';
294
295
            }
        }
296
297
298
299
300
301
302
        if (empty($tagsin)) {
            $blockheading = get_string('blockheadingtagsomitonly', 'blocktype.blog/taggedposts', count($tagsout), $tagomitstr);
        }
        else {
            $blockheading = get_string('blockheadingtags', 'blocktype.blog/taggedposts', count($tagsin), $tagstr);
            $blockheading .= (!empty($tagomitstr)) ? get_string('blockheadingtagsomitboth', 'blocktype.blog/taggedposts', count($tagsout), $tagomitstr) : '';
        }
303
        $blockheading .= ($viewownerdisplay) ? ' ' . get_string('by', 'artefact.blog') . ' <a href="' . profile_url($viewownerdisplay) . '">' . display_name($viewownerdisplay) . '</a>' : '';
304
        $smarty->assign('full', $full);
305
        $smarty->assign('results', $results);
306
        $smarty->assign('blockheading', $blockheading);
307
308
309
        return $smarty->fetch('blocktype:taggedposts:taggedposts.tpl');
    }

310
311
312
313
314
315
316
317
318
319
320
    private static function get_selected_tags() {

    }


    /**
     * Get the tags the user can choose from
     * (i.e. tags they use on their blogpost artefacts)
     * @return array
     */
    private static function get_chooseable_tags() {
321
        global $USER;
322

323
        return get_records_sql_array("
324
            SELECT at.tag
325
326
327
328
329
330
331
            FROM
                {artefact_tag} at
                JOIN {artefact} a
                ON a.id = at.artefact
            WHERE
                a.owner = ?
                AND a.artefacttype = 'blogpost'
332
333
334
            GROUP BY at.tag
            ORDER BY at.tag ASC
            ", array($USER->id));
335
336
337
338
339
340
341
342
    }

    public static function has_instance_config() {
        return true;
    }

    public static function instance_config_form(BlockInstance $instance) {
        global $USER;
343
        safe_require('artefact', 'blog');
344
        $configdata = $instance->get('configdata');
345
        $tags = self::get_chooseable_tags();
346

347
        $elements = array();
348
        if (!empty($tags)) {
349
350
351
352
353
            $tagselect = array();
            $tagrecords = get_records_array('blocktype_taggedposts_tags', 'block_instance', $instance->get('id'), 'tagtype desc, tag', 'tag, tagtype');
            if ($tagrecords) {
                foreach ($tagrecords as $tag) {
                    if ($tag->tagtype == PluginBlocktypeTaggedposts::TAGTYPE_INCLUDE) {
354
                        $tagselect[] = $tag->tag;
355
356
                    }
                    else {
357
                        $tagselect[] = '-' . $tag->tag;
358
359
                    }
                }
360
            }
361
362
363
364
            // The javascript to alter the display for the excluded tags
            $excludetag = get_string('excludetag', 'blocktype.blog/taggedposts');
            $formatSelection = <<<EOF
function (item, container) {
365
    item.title = item.id;
366
    if (item.id[0] == "-") {
367
368
        container.addClass("tagexcluded");
        if (!item.text.match(/sr\-only/)) {
369
            return '<span class="sr-only">{$excludetag}</span>' + jQuery('<div>').text(item.text).html();
370
        }
371
372
373
374
    }
    return item.text;
}
EOF;
375
            $elements['tagselect'] = array(
376
                'type'          => 'autocomplete',
377
                'title'         => get_string('taglist','blocktype.blog/taggedposts'),
378
                'description'   => get_string('taglistdesc1', 'blocktype.blog/taggedposts'),
379
380
381
382
383
384
                'defaultvalue'  => $tagselect,
                'ajaxurl' => get_config('wwwroot') . 'artefact/blog/blocktype/taggedposts/taggedposts.json.php',
                'initfunction' => 'translate_ids_to_tags',
                'multiple' => true,
                'ajaxextraparams' => array(),
                'rules'         => array('required' => 'true'),
385
                'required'      => true,
386
387
388
                'blockconfig'   => true,
                'help'          => true,
                'mininputlength' => 0,
389
390
391
                'extraparams'   => array(
                        'templateSelection' => "$formatSelection",
                        // We'll escape the text on the PHP side, so select2 doesn't need to
392
393
394
395
396
397
398
399
                        'escapeMarkup' => 'function(textToEscape) {
                            if (textToEscape.match(/sr\-only/)) {
                                return textToEscape;
                            }
                            else {
                                return jQuery("<div>").text(textToEscape).html();
                            }
                        }',
400
                ),
401
            );
402
            $elements[] = PluginArtefactBlog::block_advanced_options_element($configdata, 'taggedposts');
403
404
405
406
407
408
409
            $elements['count']  = array(
                'type'          => 'text',
                'title'         => get_string('itemstoshow', 'blocktype.blog/taggedposts'),
                'description'   => get_string('betweenxandy', 'mahara', 1, 100),
                'defaultvalue'  => isset($configdata['count']) ? $configdata['count'] : 10,
                'size'          => 3,
                'rules'         => array('integer' => true, 'minvalue' => 1, 'maxvalue' => 999),
410
            );
411
            $elements['full']  = array(
412
                'type'         => 'switchbox',
413
                'title'        => get_string('showjournalitemsinfull', 'blocktype.blog/taggedposts'),
414
                'description'  => get_string('showjournalitemsinfulldesc1', 'blocktype.blog/taggedposts'),
415
416
417
418
                'defaultvalue' => isset($configdata['full']) ? $configdata['full'] : false,
            );

            return $elements;
419
420
421
422
423
        }
        else {
            return array(
                'notags'    => array(
                    'type'          => 'html',
424
425
                    'title'         => get_string('taglist', 'blocktype.blog/taggedposts'),
                    'value'         => get_string('notagsavailable', 'blocktype.blog/taggedposts'),
426
427
428
429
430
431
                ),
            );
        }

    }

432
433
434
435
    public static function delete_instance(BlockInstance $instance) {
        delete_records('blocktype_taggedposts_tags', 'block_instance', $instance->get('id'));
    }

436
    public static function instance_config_validate(Pieform $form, $values) {
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452

        if (empty($values['tagselect'])) {
            // We don't have a tagselect field due to no journal entries having a tag
            $form->set_error(null, get_string('notagsavailableerror', 'blocktype.blog/taggedposts'));
        }
        else {
            // Need to fully check that the returned array is empty
            $values['tagselect'] = array_filter($values['tagselect']);
            if (empty($values['tagselect'])) {
                $result['message'] = get_string('required', 'mahara');
                $form->set_error('tagselect', $form->i18n('rule', 'required', 'required'), false);
                $form->reply(PIEFORM_ERR, $result);
            }
        }
    }

453
    public static function instance_config_save($values, BlockInstance $instance) {
454
455
456
        $tagselect = $values['tagselect'];
        unset($values['tagselect']);
        if (!empty($tagselect)) {
457
            delete_records('blocktype_taggedposts_tags', 'block_instance', $instance->get('id'));
458
            foreach ($tagselect as $tag) {
459
                $value = PluginBlocktypeTaggedposts::TAGTYPE_INCLUDE;
460
                if (substr($tag, 0, 1) == '-') {
461
                    $value = PluginBlocktypeTaggedposts::TAGTYPE_EXCLUDE;
462
463
                    $tag = substr($tag, 1);
                }
464
465
                $todb = new stdClass();
                $todb->block_instance = $instance->get('id');
466
                $todb->tag = htmlspecialchars_decode($tag);
467
468
                $todb->tagtype = $value;
                insert_record('blocktype_taggedposts_tags', $todb);
469
470
471
472
473
            }
        }
        return $values;
    }

474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
    /**
     * Returns a list of artefact IDs that are "in" this blockinstance.
     *
     * {@internal{Because links to artefacts within blogposts don't count
     * as making those artefacts 'children' of the blog post, we have to add
     * them directly to the blog.}}
     *
     * @return array List of artefact IDs that are 'in' this blog - all
     *               blogposts in it plus all links to other artefacts that are
     *               part of the blogpost text. Note that proper artefact
     *               children, such as blog post attachments, aren't included -
     *               the artefact parent cache is used for them
     * @see PluginBlocktypeBlogPost::get_artefacts()
     */
    public static function get_artefacts(BlockInstance $instance) {
        $artefacts = array();
        $blogposts = self::get_blog_posts_in_block($instance);
        foreach ($blogposts as $blogpost) {
            $artefacts[] = $blogpost->id;

            $blogpostobj = $instance->get_artefact_instance($blogpost->id);
            $artefacts = array_merge($artefacts, $blogpostobj->get_referenced_artefacts_from_postbody());
        }
        $artefacts = array_unique($artefacts);
        return $artefacts;
    }

501
    public static function default_copy_type() {
502
        return 'nocopy';
503
504
505
506
507
508
509
510
511
512
513
    }

    /**
     * Taggedposts blocktype is only allowed in personal views, because currently
     * there's no such thing as group/site blogs
     */
    public static function allowed_in_view(View $view) {
        return $view->get('owner') != null;
    }

}
514
515
516
517
518
519
520
521
522
523
524
525

function translate_ids_to_tags(array $ids) {
    $ids = array_diff($ids, array(''));
    $results = array();
    if (!empty($ids)) {
        foreach ($ids as $id) {
            $text = (substr($id, 0, 1) == '-') ? substr($id, 1) : $id;
            $results[] = (object) array('id' => $id, 'text' => $text);
        }
    }
    return $results;
}