lib.php 20.9 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
99
100
        $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) {
                if ($tag->tagtype == PluginBlocktypeTaggedposts::TAGTYPE_INCLUDE) {
                    $tagsin[] = $tag->tag;
101
102
                }
                else {
103
                    $tagsout[] = $tag->tag;
104
105
106
107
                }
            }
            $tagsout = array_filter($tagsout);
            $sqlvalues = array($view);
108
            $sql =
109
                'SELECT a.title, p.title AS parenttitle, a.id, a.parent, a.owner, a.description, a.allowcomments, at.tag, a.ctime
110
111
112
113
114
                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\'
115
116
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
                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);
                }
            }
153
154
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
            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) {

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

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

219
                $artefact = new ArtefactTypeBlogpost($result->id);
220
                // get comments for this post
221
                $result->commentcount = count_records_select('artefact_comment_comment', "onartefact = {$result->id} AND private = 0 AND deletedby IS NULL");
222
223
224
225
226
227
228
                $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;
229
230
231
232
233
234

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

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

263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
        // add any needed links to the tags
        $tagstr = $tagomitstr = '';
        foreach ($tagsin as $key => $tag) {
            if ($key > 0) {
                $tagstr .= ', ';
            }
            $tagstr .= ($viewownerdisplay) ? '"' . $tag . '"' : '"<a href="' . get_config('wwwroot') . 'tags.php?tag=' . $tag . '&sort=name&type=text">' . $tag . '</a>"';
        }
        if (!empty($tagsout)) {
            foreach ($tagsout as $key => $tag) {
                if ($key > 0) {
                    $tagomitstr .= ', ';
                }
                $tagomitstr .= ($viewownerdisplay) ? '"' . $tag . '"' : '"<a href="' . get_config('wwwroot') . 'tags.php?tag=' . $tag . '&sort=name&type=text">' . $tag . '</a>"';
            }
        }
        $blockheading = get_string('blockheadingtags', 'blocktype.blog/taggedposts', count($tagsin), $tagstr);
        $blockheading .= (!empty($tagomitstr)) ? get_string('blockheadingtagsomit', 'blocktype.blog/taggedposts', count($tagsout), $tagomitstr) : '';
        $blockheading .= ($viewownerdisplay) ? ' ' . get_string('by', 'artefact.blog') . ' <a href="' . profile_url($viewownerdisplay) . '">' . display_name($viewownerdisplay) . '</a>' : '';
282
        $smarty->assign('full', $full);
283
        $smarty->assign('results', $results);
284
        $smarty->assign('blockheading', $blockheading);
285
286
287
        return $smarty->fetch('blocktype:taggedposts:taggedposts.tpl');
    }

288
289
290
291
292
293
294
295
296
297
298
    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() {
299
        global $USER;
300

301
        return get_records_sql_array("
302
            SELECT at.tag
303
304
305
306
307
308
309
            FROM
                {artefact_tag} at
                JOIN {artefact} a
                ON a.id = at.artefact
            WHERE
                a.owner = ?
                AND a.artefacttype = 'blogpost'
310
311
312
            GROUP BY at.tag
            ORDER BY at.tag ASC
            ", array($USER->id));
313
314
315
316
317
318
319
320
321
322
    }

    public static function has_instance_config() {
        return true;
    }

    public static function instance_config_form(BlockInstance $instance) {
        global $USER;

        $configdata = $instance->get('configdata');
323
        $tags = self::get_chooseable_tags();
324

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

            return $elements;
389
390
391
392
393
        }
        else {
            return array(
                'notags'    => array(
                    'type'          => 'html',
394
395
                    'title'         => get_string('taglist', 'blocktype.blog/taggedposts'),
                    'value'         => get_string('notagsavailable', 'blocktype.blog/taggedposts'),
396
397
398
399
400
401
                ),
            );
        }

    }

402
403
404
405
    public static function get_instance_config_javascript(BlockInstance $instance) {
        return <<<EOF
          jQuery(function($) {
              $('#instconf_tagselect').on('change', function() {
406
407
408
                  // Need a small delay so that MultipleSelection.prototype.update will
                  // fire before updatetagbuttons
                  setTimeout(updatetagbuttons, 1000);
409
410
411
412
413
414
415
416
417
              });

              updatetagbuttons();

              function updatetagbuttons() {
                  $('#instconf_tagselect_container ul button').on('click', function(e) {
                      e.stopPropagation();
                      e.preventDefault();
                      var li = $(this).parent();
418
419
420
421
422
423
                      var new_data = jQuery.grep($('#instconf_tagselect').select2('data'), function (value) {
                          if (value['id'] == li[0].title) {
                              $("#instconf_tagselect option[value='" + value['id'] + "']").remove();
                              $('#instconf_tagselect').trigger('change');
                          }
                      });
424
425
426
427
428
429
                  });
              }
          });
EOF;
    }

430
    public static function instance_config_validate(Pieform $form, $values) {
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446

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

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

468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
    /**
     * 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;
    }

495
496
497
498
499
500
501
502
503
504
505
506
507
    public static function default_copy_type() {
        return 'nocopy';
    }

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

}
508
509
510
511
512
513
514
515
516
517
518
519

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