lib.php 21 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
                'SELECT a.title, p.title AS parenttitle, a.id, a.parent, a.owner, a.description, a.allowcomments, at.tag, a.ctime, a.mtime
112
113
114
115
116
                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\'
117
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
                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);
                }
            }
155
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
            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) {

186
187
188
189
190
191
192
193
            $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();
                }
194
                $smarty->assign('tagselect', implode(', ', $tagsin));
195
196
                $smarty->assign('blogs', $blogs);
            }
197

198
199
            // if posts are not found with the selected tag, notify the user
            if (!$results) {
200
201
                $smarty->assign('badtag', implode(', ', $tagsin));
                $smarty->assign('badnotag', implode(', ', $tagsout));
202
203
204
205
206
207
208
209
210
                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'),
            );
211
212
            require_once(get_config('docroot') . 'lib/view.php');
            $viewobj = new View($view);
213
214
215
            require_once(get_config('docroot') . 'artefact/lib.php');
            safe_require('artefact', 'blog');
            safe_require('artefact', 'comment');
216
217
            foreach ($results as $result) {
                $dataobject["artefact"] = $result->parent;
218
                $result->displaydate= format_date(strtotime($result->ctime));
219
220
                $result->postedbyon = ArtefactTypeBlog::display_postedby($result->ctime, display_default_name($result->owner));

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

225
                $artefact = new ArtefactTypeBlogpost($result->id);
226
                // get comments for this post
227
                $result->commentcount = count_records_select('artefact_comment_comment', "onartefact = {$result->id} AND private = 0 AND deletedby IS NULL AND hidden=0");
228
229
230
231
232
233
234
                $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;
235
236
237
238
239
240

                // 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;
                }
241
                if ($full) {
242
                    $rendered = $artefact->render_self(array('viewid' => $view, 'details' => true, 'blockid' => $instance->get('id')));
243
244
                    $result->html = $rendered['html'];
                    if (!empty($rendered['javascript'])) {
245
                        $result->html .= '<script type="application/javascript">' . $rendered['javascript'] . '</script>';
246
247
                    }
                }
248
249
250
251
            }

            // check if the user viewing the page is the owner of the selected tag
            $owner = $results[0]->owner;
252
            if ($USER->is_logged_in() && $USER->id != $owner) {
253
                $viewownerdisplay = get_user_for_display($owner);
254
            }
255
256
257
            $smarty->assign('tagsin', $tagsin);
            $smarty->assign('tagsout', $tagsout);
        }
258
        else if (!self::get_chooseable_tags()) {
259
260
261
            // error if block configuration fails
            $smarty->assign('configerror', get_string('notagsavailableerror', 'blocktype.blog/taggedposts'));
            return $smarty->fetch('blocktype:taggedposts:taggedposts.tpl');
262
263
264
        }
        else {
            // error if block configuration fails
265
            $smarty->assign('configerror', get_string('configerror', 'blocktype.blog/taggedposts'));
266
267
268
            return $smarty->fetch('blocktype:taggedposts:taggedposts.tpl');
        }

269
270
271
272
273
274
        // add any needed links to the tags
        $tagstr = $tagomitstr = '';
        foreach ($tagsin as $key => $tag) {
            if ($key > 0) {
                $tagstr .= ', ';
            }
275
            $tagstr .= ($viewownerdisplay) ? '"' . $tag . '"' : '"<a href="' . get_config('wwwroot') . 'tags.php?tag=' . urlencode($tag) . '&sort=name&type=text">' . hsc($tag) . '</a>"';
276
277
278
279
280
281
        }
        if (!empty($tagsout)) {
            foreach ($tagsout as $key => $tag) {
                if ($key > 0) {
                    $tagomitstr .= ', ';
                }
282
                $tagomitstr .= ($viewownerdisplay) ? '"' . $tag . '"' : '"<a href="' . get_config('wwwroot') . 'tags.php?tag=' . urlencode($tag) . '&sort=name&type=text">' . hsc($tag) . '</a>"';
283
284
            }
        }
285
286
287
288
289
290
291
        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) : '';
        }
292
        $blockheading .= ($viewownerdisplay) ? ' ' . get_string('by', 'artefact.blog') . ' <a href="' . profile_url($viewownerdisplay) . '">' . display_name($viewownerdisplay) . '</a>' : '';
293
        $smarty->assign('full', $full);
294
        $smarty->assign('results', $results);
295
        $smarty->assign('blockheading', $blockheading);
296
297
298
        return $smarty->fetch('blocktype:taggedposts:taggedposts.tpl');
    }

299
300
301
302
303
304
305
306
307
308
309
    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() {
310
        global $USER;
311

312
        return get_records_sql_array("
313
            SELECT at.tag
314
315
316
317
318
319
320
            FROM
                {artefact_tag} at
                JOIN {artefact} a
                ON a.id = at.artefact
            WHERE
                a.owner = ?
                AND a.artefacttype = 'blogpost'
321
322
323
            GROUP BY at.tag
            ORDER BY at.tag ASC
            ", array($USER->id));
324
325
326
327
328
329
330
331
    }

    public static function has_instance_config() {
        return true;
    }

    public static function instance_config_form(BlockInstance $instance) {
        global $USER;
332
        safe_require('artefact', 'blog');
333
        $configdata = $instance->get('configdata');
334
        $tags = self::get_chooseable_tags();
335

336
        $elements = array();
337
        if (!empty($tags)) {
338
339
340
341
342
            $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) {
343
                        $tagselect[] = $tag->tag;
344
345
                    }
                    else {
346
                        $tagselect[] = '-' . $tag->tag;
347
348
                    }
                }
349
            }
350
351
352
353
            // The javascript to alter the display for the excluded tags
            $excludetag = get_string('excludetag', 'blocktype.blog/taggedposts');
            $formatSelection = <<<EOF
function (item, container) {
354
    item.title = item.id;
355
    if (item.id[0] == "-") {
356
357
        container.addClass("tagexcluded");
        if (!item.text.match(/sr\-only/)) {
358
            return '<span class="sr-only">{$excludetag}</span>' + jQuery('<div>').text(item.text).html();
359
        }
360
361
362
363
    }
    return item.text;
}
EOF;
364
            $elements['tagselect'] = array(
365
                'type'          => 'autocomplete',
366
                'title'         => get_string('taglist','blocktype.blog/taggedposts'),
367
                'description'   => get_string('taglistdesc1', 'blocktype.blog/taggedposts'),
368
369
370
371
372
373
                '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'),
374
                'required'      => true,
375
376
377
                'blockconfig'   => true,
                'help'          => true,
                'mininputlength' => 0,
378
379
380
                'extraparams'   => array(
                        'templateSelection' => "$formatSelection",
                        // We'll escape the text on the PHP side, so select2 doesn't need to
381
382
383
384
385
386
387
388
                        'escapeMarkup' => 'function(textToEscape) {
                            if (textToEscape.match(/sr\-only/)) {
                                return textToEscape;
                            }
                            else {
                                return jQuery("<div>").text(textToEscape).html();
                            }
                        }',
389
                ),
390
            );
391
            $elements[] = PluginArtefactBlog::block_advanced_options_element($configdata, 'taggedposts');
392
393
394
395
396
397
398
            $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),
399
            );
400
            $elements['full']  = array(
401
                'type'         => 'switchbox',
402
                'title'        => get_string('showjournalitemsinfull', 'blocktype.blog/taggedposts'),
403
                'description'  => get_string('showjournalitemsinfulldesc1', 'blocktype.blog/taggedposts'),
404
405
406
407
                'defaultvalue' => isset($configdata['full']) ? $configdata['full'] : false,
            );

            return $elements;
408
409
410
411
412
        }
        else {
            return array(
                'notags'    => array(
                    'type'          => 'html',
413
414
                    'title'         => get_string('taglist', 'blocktype.blog/taggedposts'),
                    'value'         => get_string('notagsavailable', 'blocktype.blog/taggedposts'),
415
416
417
418
419
420
                ),
            );
        }

    }

421
422
423
424
    public static function delete_instance(BlockInstance $instance) {
        delete_records('blocktype_taggedposts_tags', 'block_instance', $instance->get('id'));
    }

425
    public static function instance_config_validate(Pieform $form, $values) {
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441

        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);
            }
        }
    }

442
    public static function instance_config_save($values, BlockInstance $instance) {
443
444
445
        $tagselect = $values['tagselect'];
        unset($values['tagselect']);
        if (!empty($tagselect)) {
446
            delete_records('blocktype_taggedposts_tags', 'block_instance', $instance->get('id'));
447
            foreach ($tagselect as $tag) {
448
                $value = PluginBlocktypeTaggedposts::TAGTYPE_INCLUDE;
449
                if (substr($tag, 0, 1) == '-') {
450
                    $value = PluginBlocktypeTaggedposts::TAGTYPE_EXCLUDE;
451
452
                    $tag = substr($tag, 1);
                }
453
454
                $todb = new stdClass();
                $todb->block_instance = $instance->get('id');
455
                $todb->tag = htmlspecialchars_decode($tag);
456
457
                $todb->tagtype = $value;
                insert_record('blocktype_taggedposts_tags', $todb);
458
459
460
461
462
            }
        }
        return $values;
    }

463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
    /**
     * 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;
    }

490
    public static function default_copy_type() {
491
        return 'nocopy';
492
493
494
495
496
497
498
499
500
501
502
    }

    /**
     * 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;
    }

}
503
504
505
506
507
508
509
510
511
512
513
514

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;
}