lib.php 39.4 KB
Newer Older
1
2
3
4
5
<?php
/**
 *
 * @package    mahara
 * @subpackage artefact-blog
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();

Aaron Wells's avatar
Aaron Wells committed
14
/**
15
16
17
18
19
20
21
22
23
24
 * Users can create blogs and blog posts using this plugin.
 */
class PluginArtefactBlog extends PluginArtefact {

    public static function get_artefact_types() {
        return array(
            'blog',
            'blogpost',
        );
    }
Aaron Wells's avatar
Aaron Wells committed
25

26
27
28
    public static function get_block_types() {
        return array();
    }
29
30
31
32
33

    public static function get_plugin_name() {
        return 'blog';
    }

34
35
36
37
    public static function is_active() {
        return get_field('artefact_installed', 'active', 'name', 'blog');
    }

38
    public static function menu_items() {
39
        global $USER;
40
        $tab = array(
41
42
            'path'   => 'content/blogs',
            'weight' => 40,
43
        );
44
        if ($USER->get_account_preference('multipleblogs')) {
45
            $tab['url']   = 'artefact/blog/index.php';
46
            $tab['title'] = get_string('blogs', 'artefact.blog');
47
48
        }
        else {
49
            $tab['url']   = 'artefact/blog/view/index.php';
50
            $tab['title'] = get_string('blog', 'artefact.blog');
51
        }
52
        return array('content/blogs' => $tab);
53
    }
54

55
    public static function get_cron() {
56
        return array();
57
58
    }

59

60
61
62
63
64
65
66
67
68
69
    public static function get_event_subscriptions() {
        return array(
            (object)array(
                'plugin'       => 'blog',
                'event'        => 'createuser',
                'callfunction' => 'create_default_blog',
            ),
        );
    }

70
    public static function block_advanced_options_element($configdata, $artefacttype) {
71
        $strartefacttype = strtolower(get_string($artefacttype, 'artefact.blog'));
72
73
74
        return array(
            'type' => 'fieldset',
            'name' => 'advanced',
75
            'class' => 'first last',
76
77
            'collapsible' => true,
            'collapsed' => false,
78
            'legend' => get_string('moreoptions', 'artefact.blog'),
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
            'elements' => array(
                'copytype' => array(
                    'type' => 'select',
                    'title' => get_string('blockcopypermission', 'view'),
                    'description' => get_string('blockcopypermissiondesc', 'view'),
                    'defaultvalue' => isset($configdata['copytype']) ? $configdata['copytype'] : 'nocopy',
                    'options' => array(
                        'nocopy' => get_string('copynocopy', 'artefact.blog'),
                        'reference' => get_string('copyreference', 'artefact.blog', $strartefacttype),
                        'full' => get_string('copyfull', 'artefact.blog', $strartefacttype),
                    ),
                ),
            ),
        );
    }

95
96
97
98
99
100
101
102
    public static function create_default_blog($event, $user) {
        $name = display_name($user, null, true);
        $blog = new ArtefactTypeBlog(0, (object) array(
            'title'       => get_string('defaultblogtitle', 'artefact.blog', $name),
            'owner'       => $user['id'],
        ));
        $blog->commit();
    }
103
104
105
106
107
108

    public static function get_artefact_type_content_types() {
        return array(
            'blogpost' => array('text'),
        );
    }
109

110
    public static function progressbar_link($artefacttype) {
111
112
        return 'artefact/blog/view/index.php';
    }
113
114
115
116
117
118
119
}

/**
 * A Blog artefact is a collection of BlogPost artefacts.
 */
class ArtefactTypeBlog extends ArtefactType {

Alastair Pharo's avatar
Alastair Pharo committed
120
121
122
123
    /**
     * This constant gives the per-page pagination for listing blogs.
     */
    const pagination = 10;
124
125


126
127
128
129
130
131
132
133
    /**
     * We override the constructor to fetch the extra data.
     *
     * @param integer
     * @param object
     */
    public function __construct($id = 0, $data = null) {
        parent::__construct($id, $data);
134
135
136
137
138
139

        if (empty($this->id)) {
            $this->container = 1;
        }
    }

140
141
142
143
    public static function is_allowed_in_progressbar() {
        return false;
    }

Alastair Pharo's avatar
Alastair Pharo committed
144
    /**
145
146
147
     * This function updates or inserts the artefact.  This involves putting
     * some data in the artefact table (handled by parent::commit()), and then
     * some data in the artefact_blog_blog table.
Alastair Pharo's avatar
Alastair Pharo committed
148
     */
149
    public function commit() {
150
151
152
153
        // Just forget the whole thing when we're clean.
        if (empty($this->dirty)) {
            return;
        }
Aaron Wells's avatar
Aaron Wells committed
154

155
156
        // We need to keep track of newness before and after.
        $new = empty($this->id);
Aaron Wells's avatar
Aaron Wells committed
157

158
159
160
161
        // Commit to the artefact table.
        parent::commit();

        $this->dirty = false;
162
163
    }

Alastair Pharo's avatar
Alastair Pharo committed
164
    /**
165
166
     * This function extends ArtefactType::delete() by deleting blog-specific
     * data.
Alastair Pharo's avatar
Alastair Pharo committed
167
     */
168
    public function delete() {
169
170
171
172
173
174
        if (empty($this->id)) {
            return;
        }

        // Delete the artefact and all children.
        parent::delete();
175
176
    }

177
    /**
Aaron Wells's avatar
Aaron Wells committed
178
179
180
     * Checks that the person viewing this blog is the owner. If not, throws an
     * AccessDeniedException. Used in the blog section to ensure only the
     * owners of the blogs can view or change them there. Other people see
181
182
183
184
185
186
187
188
189
     * blogs when they are placed in views.
     */
    public function check_permission() {
        global $USER;
        if ($USER->get('id') != $this->owner) {
            throw new AccessDeniedException(get_string('youarenottheownerofthisblog', 'artefact.blog'));
        }
    }

190

191
192
    public function describe_size() {
        return $this->count_children() . ' ' . get_string('posts', 'artefact.blog');
193
194
    }

195
    /**
196
     * Renders a blog.
197
198
199
200
201
     *
     * @param  array  Options for rendering
     * @return array  A two key array, 'html' and 'javascript'.
     */
    public function render_self($options) {
202
203
204
205
206
207
208
209
210
211
212
        if (!isset($options['limit'])) {
            $limit = self::pagination;
        }
        else if ($options['limit'] === false) {
            $limit = null;
        }
        else {
            $limit = (int) $options['limit'];
        }
        $offset = isset($options['offset']) ? intval($options['offset']) : 0;

213
214
215
216
217
        if (!isset($options['countcomments'])) {
            // Count comments if this is a view
            $options['countcomments'] = (!empty($options['viewid']));
        }

218
219
220
221
        $posts = ArtefactTypeBlogpost::get_posts($this->id, $limit, $offset, $options);

        $template = 'artefact:blog:viewposts.tpl';

222
        $baseurl = get_config('wwwroot') . 'artefact/artefact.php?artefact=' . $this->id;
223
224
225
        if (!empty($options['viewid'])) {
            $baseurl .= '&view=' . $options['viewid'];
        }
226
227
228
229
230
231
232
233
234
        $pagination = array(
            'baseurl' => $baseurl,
            'id' => 'blogpost_pagination',
            'datatable' => 'postlist',
            'jsonscript' => 'artefact/blog/posts.json.php',
        );

        ArtefactTypeBlogpost::render_posts($posts, $template, $options, $pagination);

235
236
        $smarty = smarty_core();
        if (isset($options['viewid'])) {
237
            $smarty->assign('artefacttitle', '<a href="' . get_config('wwwroot') . 'artefact/artefact.php?artefact='
238
                                             . $this->get('id') . '&view=' . $options['viewid']
Richard Mansfield's avatar
Richard Mansfield committed
239
                                             . '">' . hsc($this->get('title')) . '</a>');
240
241
        }
        else {
Richard Mansfield's avatar
Richard Mansfield committed
242
            $smarty->assign('artefacttitle', hsc($this->get('title')));
243
244
        }

245
246
247
248
249
250
251
        if (!empty($options['details']) and get_config('licensemetadata')) {
            $smarty->assign('license', render_license($this));
        }
        else {
            $smarty->assign('license', false);
        }

252
        $options['hidetitle'] = true;
253
        $smarty->assign('options', $options);
254
        $smarty->assign('description', $this->get('description'));
255
256
        $smarty->assign('owner', $this->get('owner'));
        $smarty->assign('tags', $this->get('tags'));
257

258
        $smarty->assign_by_ref('posts', $posts);
259

260
        return array('html' => $smarty->fetch('artefact:blog:blog.tpl'), 'javascript' => '');
261
262
    }

Aaron Wells's avatar
Aaron Wells committed
263

264
    public static function get_icon($options=null) {
265
        global $THEME;
266
        return false;
267
268
    }

Nigel McNie's avatar
Nigel McNie committed
269
    public static function is_singular() {
Penny Leach's avatar
Penny Leach committed
270
271
272
        return false;
    }

Alastair Pharo's avatar
Alastair Pharo committed
273
    public static function collapse_config() {
274
275
    }

Alastair Pharo's avatar
Alastair Pharo committed
276
277
278
279
280
281
    /**
     * This function returns a list of the given user's blogs.
     *
     * @param User
     * @return array (count: integer, data: array)
     */
282
283
    public static function get_blog_list($limit, $offset) {
        global $USER;
Alastair Pharo's avatar
Alastair Pharo committed
284
        ($result = get_records_sql_array("
285
         SELECT b.id, b.title, b.description, b.locked, COUNT(p.id) AS postcount
286
287
         FROM {artefact} b LEFT JOIN {artefact} p ON (p.parent = b.id AND p.artefacttype = 'blogpost')
         WHERE b.owner = ? AND b.artefacttype = 'blog'
288
         GROUP BY b.id, b.title, b.description, b.locked
289
         ORDER BY b.title", array($USER->get('id')), $offset, $limit))
Alastair Pharo's avatar
Alastair Pharo committed
290
291
            || ($result = array());

292
293
        foreach ($result as &$r) {
            if (!$r->locked) {
294
                $r->deleteform = ArtefactTypeBlog::delete_form($r->id, $r->title);
295
296
297
            }
        }

298
        $count = (int)get_field('artefact', 'COUNT(*)', 'owner', $USER->get('id'), 'artefacttype', 'blog');
Alastair Pharo's avatar
Alastair Pharo committed
299
300
301
302

        return array($count, $result);
    }

303
304
305
    public static function build_blog_list_html(&$blogs) {
        $smarty = smarty_core();
        $smarty->assign_by_ref('blogs', $blogs);
306
307
        $blogs->tablerows = $smarty->fetch('artefact:blog:bloglist.tpl');
        $pagination = build_pagination(array(
308
309
310
            'id' => 'bloglist_pagination',
            'class' => 'center',
            'url' => get_config('wwwroot') . 'artefact/blog/index.php',
311
312
            'jsonscript' => 'artefact/blog/index.json.php',
            'datatable' => 'bloglist',
313
314
315
316
317
318
319
320
321
322
323
            'count' => $blogs->count,
            'limit' => $blogs->limit,
            'offset' => $blogs->offset,
            'firsttext' => '',
            'previoustext' => '',
            'nexttext' => '',
            'lasttext' => '',
            'numbersincludefirstlast' => false,
            'resultcounttextsingular' => get_string('blog', 'artefact.blog'),
            'resultcounttextplural' => get_string('blogs', 'artefact.blog'),
        ));
324
325
        $blogs->pagination = $pagination['html'];
        $blogs->pagination_js = $pagination['javascript'];
326
327
    }

Alastair Pharo's avatar
Alastair Pharo committed
328
329
330
331
332
333
    /**
     * This function creates a new blog.
     *
     * @param User
     * @param array
     */
Alastair Pharo's avatar
Alastair Pharo committed
334
    public static function new_blog(User $user, array $values) {
335
336
        require_once('embeddedimage.php');
        db_begin();
Alastair Pharo's avatar
Alastair Pharo committed
337
338
339
340
        $artefact = new ArtefactTypeBlog();
        $artefact->set('title', $values['title']);
        $artefact->set('description', $values['description']);
        $artefact->set('owner', $user->get('id'));
341
        $artefact->set('tags', $values['tags']);
342
343
344
345
346
        if (get_config('licensemetadata')) {
            $artefact->set('license', $values['license']);
            $artefact->set('licensor', $values['licensor']);
            $artefact->set('licensorurl', $values['licensorurl']);
        }
Alastair Pharo's avatar
Alastair Pharo committed
347
        $artefact->commit();
348
349
350
351
        $blogid = $artefact->get('id');
        $newdescription = EmbeddedImage::prepare_embedded_images($artefact->get('description'), 'blog', $blogid);
        $artefact->set('description', $newdescription);
        db_commit();
352
    }
Alastair Pharo's avatar
Alastair Pharo committed
353
354
355
356
357
358
359
360

    /**
     * This function updates an existing blog.
     *
     * @param User
     * @param array
     */
    public static function edit_blog(User $user, array $values) {
361
        require_once('embeddedimage.php');
Alastair Pharo's avatar
Alastair Pharo committed
362
363
364
365
366
        if (empty($values['id']) || !is_numeric($values['id'])) {
            return;
        }

        $artefact = new ArtefactTypeBlog($values['id']);
367
368
369
        if ($user->get('id') != $artefact->get('owner')) {
            return;
        }
370

Alastair Pharo's avatar
Alastair Pharo committed
371
        $artefact->set('title', $values['title']);
372
373
        $newdescription = EmbeddedImage::prepare_embedded_images($values['description'], 'blog', $values['id']);
        $artefact->set('description', $newdescription);
374
        $artefact->set('tags', $values['tags']);
375
376
377
378
379
        if (get_config('licensemetadata')) {
            $artefact->set('license', $values['license']);
            $artefact->set('licensor', $values['licensor']);
            $artefact->set('licensorurl', $values['licensorurl']);
        }
Alastair Pharo's avatar
Alastair Pharo committed
380
381
        $artefact->commit();
    }
382

Martyn Smith's avatar
Martyn Smith committed
383
384
    public static function get_links($id) {
        $wwwroot = get_config('wwwroot');
385

Martyn Smith's avatar
Martyn Smith committed
386
        return array(
387
388
            '_default'                                  => $wwwroot . 'artefact/blog/view/index.php?id=' . $id,
            get_string('blogsettings', 'artefact.blog') => $wwwroot . 'artefact/blog/settings/index.php?id=' . $id,
Martyn Smith's avatar
Martyn Smith committed
389
390
        );
    }
391
392
393
394
395

    public function copy_extra($new) {
        $new->set('title', get_string('Copyof', 'mahara', $this->get('title')));
    }

396
397
398
    /**
     * Returns the number of posts in this blog that have been published.
     *
Aaron Wells's avatar
Aaron Wells committed
399
     * The result of this function looked up from the database each time, so
400
401
402
403
404
405
406
407
408
409
410
411
412
     * cache it if you know it's safe to do so.
     *
     * @return int
     */
    public function count_published_posts() {
        return (int)get_field_sql("
            SELECT COUNT(*)
            FROM {artefact} a
            LEFT JOIN {artefact_blog_blogpost} bp ON a.id = bp.blogpost
            WHERE a.parent = ?
            AND bp.published = 1", array($this->get('id')));
    }

413
    public static function delete_form($id, $title = '') {
414
        global $THEME;
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438

        $confirm = get_string('deleteblog?', 'artefact.blog');

        // Check if this blog has posts.
        $postcnt = count_records_sql("
            SELECT COUNT(*)
            FROM {artefact} a
            INNER JOIN {artefact_blog_blogpost} bp ON a.id = bp.blogpost
            WHERE a.parent = ?
            ", array($id));
        if ($postcnt > 0) {
            $confirm = get_string('deletebloghaspost?', 'artefact.blog', $postcnt);

            // Check if this blog posts used in views.
            $viewscnt = count_records_sql("
                SELECT COUNT(DISTINCT(va.view))
                FROM {artefact} a
                INNER JOIN {view_artefact} va ON a.id = va.artefact
                WHERE a.parent = ? OR a.id = ?
                ", array($id, $id));
            if ($viewscnt > 0) {
                $confirm = get_string('deletebloghasview?', 'artefact.blog', $viewscnt);
            }
        }
439
440
441
442
443
444
445
446
447
448
        return pieform(array(
            'name' => 'delete_' . $id,
            'successcallback' => 'delete_blog_submit',
            'renderer' => 'oneline',
            'elements' => array(
                'delete' => array(
                    'type' => 'hidden',
                    'value' => $id,
                ),
                'submit' => array(
449
450
451
                    'type' => 'button',
                    'usebuttontag' => true,
                    'class' => 'btn btn-default btn-sm',
452
453
                    'alt' => get_string('deletespecific', 'mahara', $title),
                    'elementtitle' => get_string('delete'),
454
                    'confirm' => $confirm,
455
                    'value' => '<span class="fa fa-trash text-danger prs"></span> ' .get_string('delete'),
456
457
458
459
                ),
            ),
        ));
    }
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476

    /**
     * During the copying of a view, we might be allowed to copy
     * blogs. Users need to have multipleblogs enabled for these
     * to be visible.
     */
    public function default_parent_for_copy(&$view, &$template, $artefactstoignore) {
        global $USER, $SESSION;

        $viewid = $view->get('id');

        try {
            $user = get_user($view->get('owner'));
            set_account_preference($user->id, 'multipleblogs', 1);
            $SESSION->add_ok_msg(get_string('copiedblogpoststonewjournal', 'collection'));
        }
        catch (Exception $e) {
477
            $SESSION->add_error_msg(get_string('unabletosetmultipleblogs', 'error', $user->username, $viewid, get_config('wwwroot') . 'account/index.php'), false);
478
479
480
481
482
483
484
485
486
487
488
        }

        try {
            $USER->accountprefs = load_account_preferences($user->id);
        }
        catch (Exception $e) {
            $SESSION->add_error_msg(get_string('pleaseloginforjournals', 'error'));
        }

        return null;
    }
489
490
491
492
493
494
495
}

/**
 * BlogPost artefacts occur within Blog artefacts
 */
class ArtefactTypeBlogPost extends ArtefactType {

496
    /**
Alastair Pharo's avatar
Alastair Pharo committed
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
     * This defines whether the blogpost is published or not.
     *
     * @var boolean
     */
    protected $published = false;

    /**
     * We override the constructor to fetch the extra data.
     *
     * @param integer
     * @param object
     */
    public function __construct($id = 0, $data = null) {
        parent::__construct($id, $data);

512
513
514
515
516
        if ($this->id) {
            if ($bpdata = get_record('artefact_blog_blogpost', 'blogpost', $this->id)) {
                foreach($bpdata as $name => $value) {
                    if (property_exists($this, $name)) {
                        $this->$name = $value;
517
                    }
Alastair Pharo's avatar
Alastair Pharo committed
518
                }
519
520
521
522
            }
            else {
                // This should never happen unless the user is playing around with blog post IDs in the location bar or similar
                throw new ArtefactNotFoundException(get_string('blogpostdoesnotexist', 'artefact.blog'));
523
524
            }
        }
525
526
527
        else {
            $this->allowcomments = 1; // Turn comments on for new posts
        }
Alastair Pharo's avatar
Alastair Pharo committed
528
529
530
    }

    /**
531
     * This method extends ArtefactType::commit() by adding additional data
Alastair Pharo's avatar
Alastair Pharo committed
532
     * into the artefact_blog_blogpost table.
533
     *
Aaron Wells's avatar
Aaron Wells committed
534
     * This method also works out what blockinstances this blogpost is in, and
535
     * informs them that they should re-check what artefacts they have in them.
Aaron Wells's avatar
Aaron Wells committed
536
     * The post content may now link to different artefacts. See {@link
537
     * PluginBlocktypeBlogPost::get_artefacts for more information}
538
     */
539
    protected function postcommit_hook($new) {
540
541
        require_once(get_config('docroot') . 'blocktype/lib.php');
        require_once(get_config('docroot') . 'artefact/blog/blocktype/taggedposts/lib.php');
Alastair Pharo's avatar
Alastair Pharo committed
542
543
544
545
546
547
548
549
550
551
552
553
        $data = (object)array(
            'blogpost'  => $this->get('id'),
            'published' => ($this->get('published') ? 1 : 0)
        );

        if ($new) {
            insert_record('artefact_blog_blogpost', $data);
        }
        else {
            update_record('artefact_blog_blogpost', $data, 'blogpost');
        }

554
        // We want to get all blockinstances that may contain this blog post. That is currently:
555
556
        // 1) All blogpost blocktypes with this post in it
        // 2) All blog blocktypes with this posts's blog in it
557
558
        // 3) All recentposts blocktypes with this post's blog in it
        // 4) All taggedposts blocktypes with this post's tags
559
        $blocks = (array)get_column_sql('SELECT block
560
561
562
            FROM {view_artefact}
            WHERE artefact = ?
            OR artefact = ?', array($this->get('id'), $this->get('parent')));
563
564
565
566
567
568
569
570
571
572
        if (!$blocks) {
            $blocks = array();
        }

        // Get all "tagged blog entries" blocks that may contain this block
        // (we'll just check for a single matching tag here, and let each block
        // instance further down decide whether or not it matches
        $tags = $this->get('tags');
        if ($tags) {
            $blocks = array_merge($blocks, PluginBlocktypeTaggedposts::find_matching_blocks($tags));
573
574
575
576
        }

        // Now rebuild the list of which artefacts these blocks contain
        // in the view_artefacts table. (This is used for watchlist notifications)
577
578
        if ($blocks) {
            foreach ($blocks as $id) {
579
580
581
582
                $instance = new BlockInstance($id);
                $instance->rebuild_artefact_list();
            }
        }
583
584
    }

585
    /**
Alastair Pharo's avatar
Alastair Pharo committed
586
587
     * This function extends ArtefactType::delete() by also deleting anything
     * that's in blogpost.
588
     */
589
    public function delete() {
Alastair Pharo's avatar
Alastair Pharo committed
590
591
592
        if (empty($this->id)) {
            return;
        }
593

594
        require_once('embeddedimage.php');
595
        db_begin();
596
        $this->detach(); // Detach all file attachments
Alastair Pharo's avatar
Alastair Pharo committed
597
        delete_records('artefact_blog_blogpost', 'blogpost', $this->id);
598
        EmbeddedImage::delete_embedded_images('blogpost', $this->id);
Alastair Pharo's avatar
Alastair Pharo committed
599
        parent::delete();
600
        db_commit();
Alastair Pharo's avatar
Alastair Pharo committed
601
    }
602

603
604
605
606
607
608
609
610
611
612
613
614
615
616
    public static function bulk_delete($artefactids) {
        if (empty($artefactids)) {
            return;
        }

        $idstr = join(',', array_map('intval', $artefactids));

        db_begin();
        delete_records_select('artefact_blog_blogpost', 'blogpost IN (' . $idstr . ')');
        parent::bulk_delete($artefactids);
        db_commit();
    }


617
    /**
Aaron Wells's avatar
Aaron Wells committed
618
619
620
     * Checks that the person viewing this blog is the owner. If not, throws an
     * AccessDeniedException. Used in the blog section to ensure only the
     * owners of the blogs can view or change them there. Other people see
621
622
623
624
625
626
627
628
     * blogs when they are placed in views.
     */
    public function check_permission() {
        global $USER;
        if ($USER->get('id') != $this->owner) {
            throw new AccessDeniedException(get_string('youarenottheownerofthisblogpost', 'artefact.blog'));
        }
    }
Aaron Wells's avatar
Aaron Wells committed
629

630
631
    public function describe_size() {
        return $this->count_attachments() . ' ' . get_string('attachments', 'artefact.blog');
632
633
    }

634
    public function render_self($options) {
635
636
        global $USER;

637
        $smarty = smarty_core();
638
639
640
641
642
643
644
645
        $smarty->assign('published', $this->get('published'));
        if (!$this->get('published')) {
            $notpublishedblogpoststr = get_string('notpublishedblogpost', 'artefact.blog');
            if ($this->get('owner') == $USER->get('id')) {
                $notpublishedblogpoststr .= ' <a href="' . get_config('wwwroot') . 'artefact/blog/post.php?id=' . $this->get('id') . '">' . get_string('publishit', 'artefact.blog') . '</a>';
            }
            $smarty->assign('notpublishedblogpost', $notpublishedblogpoststr);
        }
646
        $artefacturl = get_config('wwwroot') . 'artefact/artefact.php?artefact=' . $this->get('id');
647
648
649
650
        if (isset($options['viewid'])) {
            $artefacturl .= '&view=' . $options['viewid'];
        }
        $smarty->assign('artefacturl', $artefacturl);
651
652
        if (empty($options['hidetitle'])) {
            if (isset($options['viewid'])) {
653
                $smarty->assign('artefacttitle', '<a href="' . $artefacturl . '">' . hsc($this->get('title')) . '</a>');
654
655
            }
            else {
Richard Mansfield's avatar
Richard Mansfield committed
656
                $smarty->assign('artefacttitle', hsc($this->get('title')));
657
658
659
660
            }
        }

        // We need to make sure that the images in the post have the right viewid associated with them
661
        $postcontent = $this->get('description');
662
        if (isset($options['viewid'])) {
663
664
            safe_require('artefact', 'file');
            $postcontent = ArtefactTypeFolder::append_view_url($postcontent, $options['viewid']);
665
666
        }
        $smarty->assign('artefactdescription', $postcontent);
667
668
        $smarty->assign('artefacttags', $this->get('tags'));
        $smarty->assign('artefactowner', $this->get('owner'));
669
670
671
672
673
674
675
        if (!empty($options['details']) and get_config('licensemetadata')) {
            $smarty->assign('license', render_license($this));
        }
        else {
            $smarty->assign('license', false);
        }

676
        $attachments = $this->get_attachments();
677
        if ($attachments) {
678
            require_once(get_config('docroot') . 'artefact/lib.php');
679
680
681
            foreach ($attachments as &$attachment) {
                $f = artefact_instance_from_id($attachment->id);
                $attachment->size = $f->describe_size();
682
                $attachment->iconpath = $f->get_icon(array('id' => $attachment->id, 'viewid' => isset($options['viewid']) ? $options['viewid'] : 0));
683
                $attachment->viewpath = get_config('wwwroot') . 'artefact/artefact.php?artefact=' . $attachment->id . '&view=' . (isset($options['viewid']) ? $options['viewid'] : 0);
684
685
                $attachment->downloadpath = get_config('wwwroot') . 'artefact/file/download.php?file=' . $attachment->id;
                if (isset($options['viewid'])) {
686
                    $attachment->downloadpath .= '&view=' . $options['viewid'];
687
688
689
                }
            }
            $smarty->assign('attachments', $attachments);
690
691
692
693
            if (isset($options['blockid'])) {
                $smarty->assign('blockid', $options['blockid']);
            }
            $smarty->assign('postid', $this->get('id'));
694
695
696
697
698
699
700
701
702
        }
        $smarty->assign('postedbyon', get_string('postedbyon', 'artefact.blog',
                                                 display_name($this->owner),
                                                 format_date($this->ctime)));
        return array('html' => $smarty->fetch('artefact:blog:render/blogpost_renderfull.tpl'),
                     'javascript' => '');
    }


703
704
    public function can_have_attachments() {
        return true;
Richard Mansfield's avatar
Richard Mansfield committed
705
706
707
    }


708
    public static function get_icon($options=null) {
709
        global $THEME;
710
        return false;
711
712
    }

Nigel McNie's avatar
Nigel McNie committed
713
    public static function is_singular() {
Penny Leach's avatar
Penny Leach committed
714
715
716
        return false;
    }

Alastair Pharo's avatar
Alastair Pharo committed
717
    public static function collapse_config() {
718
719
    }

720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
    /**
     * This function returns the blog id and offset for a given post.
     *
     * @param integer $postid The id of the required blog post
     * @return object An object containing the required data
     */
    public static function get_post_data($postid) {
        $post = new stdClass();

        $post->blogid = get_field('artefact', 'parent', 'id', $postid, 'artefacttype', 'blogpost');

        if (is_postgres()) {
            $rownum = get_field_sql("SELECT rownum
                                    FROM (SELECT id, ROW_NUMBER() OVER (ORDER BY id DESC) AS rownum
                                        FROM {artefact}
                                        WHERE parent = ?
                                        ORDER BY id DESC) AS posts
                                    WHERE id = ?",
                    array($post->blogid, $postid));
        }
        else if (is_mysql()) {
            $initvar = execute_sql("SET @row_num = 0");
            if ($initvar) {
                $rownum = get_field_sql("SELECT rownum
                                        FROM (SELECT id, @row_num := @row_num + 1 AS rownum
                                            FROM {artefact}
                                            WHERE parent = ?
                                            ORDER BY id DESC) AS posts
                                        WHERE id = ?",
                        array($post->blogid, $postid));
            }
        }
        $post->offset = $rownum - 1;

        return $post;
    }

Alastair Pharo's avatar
Alastair Pharo committed
757
    /**
758
     * This function returns a list of posts in a given blog.
Alastair Pharo's avatar
Alastair Pharo committed
759
760
761
     *
     * @param integer
     * @param integer
762
     * @param integer
763
     * @param array
Alastair Pharo's avatar
Alastair Pharo committed
764
     */
765
    public static function get_posts($id, $limit, $offset, $viewoptions=null) {
Alastair Pharo's avatar
Alastair Pharo committed
766

767
768
769
770
        $results = array(
            'limit'  => $limit,
            'offset' => $offset,
        );
771

772
773
774
775
776
777
778
779
780
781
782
        // If viewoptions is null, we're getting posts for the my blogs area,
        // and we should get all posts & show drafts first.  Otherwise it's a
        // blog in a view, and we should only get published posts.

        $from = "
            FROM {artefact} a LEFT JOIN {artefact_blog_blogpost} bp ON a.id = bp.blogpost
            WHERE a.artefacttype = 'blogpost' AND a.parent = ?";

        if (!is_null($viewoptions)) {
            if (isset($viewoptions['before'])) {
                $from .= " AND a.ctime < '{$viewoptions['before']}'";
783
            }
784
785
            $from .= ' AND bp.published = 1';
        }
786

787
788
789
790
791
        $results['count'] = count_records_sql('SELECT COUNT(*) ' . $from, array($id));

        $data = get_records_sql_assoc('
            SELECT
                a.id, a.title, a.description, a.author, a.authorname, ' .
792
793
                db_format_tsfield('a.ctime', 'ctime') . ', ' . db_format_tsfield('a.mtime', 'mtime') . ',
                a.locked, bp.published, a.allowcomments ' . $from . '
794
            ORDER BY bp.published ASC, a.ctime DESC, a.id DESC',
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
            array($id),
            $offset, $limit
        );

        if (!$data) {
            $results['data'] = array();
            return $results;
        }

        // Get the attached files.
        $postids = array_map(create_function('$a', 'return $a->id;'), $data);
        $files = ArtefactType::attachments_from_id_list($postids);
        if ($files) {
            safe_require('artefact', 'file');
            foreach ($files as &$file) {
810
811
812
813
814
                $params = array('id' => $file->attachment);
                if (!empty($viewoptions['viewid'])) {
                    $params['viewid'] = $viewoptions['viewid'];
                }
                $file->icon = call_static_method(generate_artefact_class_name($file->artefacttype), 'get_icon', $params);
815
816
817
818
819
820
821
822
823
824
825
                $data[$file->artefact]->files[] = $file;
            }
        }

        if ($tags = ArtefactType::tags_from_id_list($postids)) {
            foreach($tags as &$at) {
                $data[$at->artefact]->tags[] = $at->tag;
            }
        }

        foreach ($data as &$post) {
826
            // Format dates properly
827
            if (is_null($viewoptions)) {
828
829
                // My Blogs area: create forms for changing post status & deleting posts.
                $post->changepoststatus = ArtefactTypeBlogpost::changepoststatus_form($post->id, $post->published);
830
                $post->delete = ArtefactTypeBlogpost::delete_form($post->id, $post->title);
831
            }
832
833
834
            else {
                $by = $post->author ? display_default_name($post->author) : $post->authorname;
                $post->postedby = get_string('postedbyon', 'artefact.blog', $by, format_date($post->ctime));
835
836
837
838
839
840
                // Get comment counts
                if (!empty($viewoptions['countcomments'])) {
                    safe_require('artefact', 'comment');
                    require_once(get_config('docroot') . 'lib/view.php');
                    $view = new View($viewoptions['viewid']);
                    $artefact = artefact_instance_from_id($post->id);
841
842
843
                    list($commentcount, $comments) = ArtefactTypeComment::get_artefact_comments_for_view($artefact, $view, null, false);
                    $post->commentcount = $commentcount;
                    $post->comments = $comments;
844
                }
845
846
847
            }
            $post->ctime = format_date($post->ctime, 'strftimedaydatetime');
            $post->mtime = format_date($post->mtime);
848
849
850
851
852
853

            // Ensure images in the post have the right viewid associated with them
            if (!empty($viewoptions['viewid'])) {
                safe_require('artefact', 'file');
                $post->description = ArtefactTypeFolder::append_view_url($post->description, $viewoptions['viewid']);
            }
854
        }
Alastair Pharo's avatar
Alastair Pharo committed
855

856
857
858
        $results['data'] = array_values($data);

        return $results;
859
860
    }

861
862
863
864
865
866
867
868
869
    /**
     * This function renders a list of posts as html
     *
     * @param array posts
     * @param string template
     * @param array options
     * @param array pagination
     */
    public function render_posts(&$posts, $template, $options, $pagination) {
870
        $smarty = smarty_core();
871
        $smarty->assign('options', $options);
872
873
        $smarty->assign('posts', $posts['data']);

874
875
        $posts['tablerows'] = $smarty->fetch($template);

876
877
        $setlimit = isset($pagination['setlimit']) ? $pagination['setlimit'] : false;

878
        if ($posts['limit'] && $pagination) {
879
            $pagination = build_pagination(array(
880
881
882
883
884
885
886
                'id' => $pagination['id'],
                'class' => 'center',
                'datatable' => $pagination['datatable'],
                'url' => $pagination['baseurl'],
                'jsonscript' => $pagination['jsonscript'],
                'count' => $posts['count'],
                'limit' => $posts['limit'],
887
                'setlimit' => $setlimit,
888
889
890
891
892
893
894
                'offset' => $posts['offset'],
                'numbersincludefirstlast' => false,
                'resultcounttextsingular' => get_string('post', 'artefact.blog'),
                'resultcounttextplural' => get_string('posts', 'artefact.blog'),
            ));
            $posts['pagination'] = $pagination['html'];
            $posts['pagination_js'] = $pagination['javascript'];
895
        }
Alastair Pharo's avatar
Alastair Pharo committed
896
897
898
899
900
901
902
903
904
905
906
907
    }

    /**
     * This function creates a new blog post.
     *
     * @param User
     * @param array
     */
    public static function new_post(User $user, array $values) {
        $artefact = new ArtefactTypeBlogPost();
        $artefact->set('title', $values['title']);
        $artefact->set('description', $values['description']);
Alastair Pharo's avatar
Alastair Pharo committed
908
        $artefact->set('published', $values['published']);
Alastair Pharo's avatar
Alastair Pharo committed
909
        $artefact->set('owner', $user->get('id'));
910
        $artefact->set('parent', $values['parent']);
Alastair Pharo's avatar
Alastair Pharo committed
911
        $artefact->commit();
912
        return true;
913
    }
Alastair Pharo's avatar
Alastair Pharo committed
914

Aaron Wells's avatar
Aaron Wells committed
915
    /**
Alastair Pharo's avatar
Alastair Pharo committed
916
917
918
919
920
921
922
923
924
925
926
927
928
929
     * This function updates an existing blog post.
     *
     * @param User
     * @param array
     */
    public static function edit_post(User $user, array $values) {
        $artefact = new ArtefactTypeBlogPost($values['id']);
        if ($user->get('id') != $artefact->get('owner')) {
            return false;
        }

        $artefact->set('title', $values['title']);
        $artefact->set('description', $values['description']);
        $artefact->set('published', $values['published']);
930
        $artefact->set('tags', $values['tags']);
931
932
933
934
935
        if (get_config('licensemetadata')) {
            $artefact->set('license', $values['license']);
            $artefact->set('licensor', $values['licensor']);
            $artefact->set('licensorurl', $values['licensorurl']);
        }
Alastair Pharo's avatar
Alastair Pharo committed
936
937
938
939
        $artefact->commit();
        return true;
    }

940
941
942
943
944
945
946
    public static function changepoststatus_form($id, $published = null) {
        //Get current post status from database
        if ($published === null) {
            $post = new ArtefactTypeBlogPost($id);
            $published = $post->published;
        }
        if ($published) {
947
            $strchangepoststatus = '<span class="prs fa fa-times text-danger"></span> ' .get_string('unpublish', 'artefact.blog');
948
949
        }
        else {
950
            $strchangepoststatus = '<span class="prs fa fa-check text-success"></span> ' . get_string('publish', 'artefact.blog');
951
        }
952
        return pieform(array(
953
954
955
            'name' => 'changepoststatus_' . $id,
            'jssuccesscallback' => 'changepoststatus_success',
            'successcallback' => 'changepoststatus_submit',
956
            'jsform' => true,
957
            'renderer' => 'div',
958
            'elements' => array(
959
                'changepoststatus' => array(
960
961
962
                    'type' => 'hidden',
                    'value' => $id,
                ),
963
964
965
966
                'currentpoststatus' => array(
                    'type' => 'hidden',
                    'value' => $published,
                ),'submit' => array(
967
968
969
                    'type' => 'button',
                    'usebuttontag' => true,
                    'class' => 'btn btn-default btn-sm publish',
970
                    'value' => $strchangepoststatus,
971
972
973
974
975
                ),
            ),
        ));
    }

976
    public static function delete_form($id, $title = '') {
977
        global $THEME;
978
979
980
981
982
        return pieform(array(
            'name' => 'delete_' . $id,
            'successcallback' => 'delete_submit',
            'jsform' => true,
            'jssuccesscallback' => 'delete_success',
983
984
            'renderer' => 'div',
            'class' => 'form-as-button pull-left',
985
986
987
988
989
990
991
            'elements' => array(
                'delete' => array(
                    'type' => 'hidden',
                    'value' => $id,
                    'help' => true,
                ),
                'submit' => array(
992
993
994
                    'type' => 'button',
                    'usebuttontag' => true,
                    'class' => 'btn btn-default btn-sm',
995
                    'elementtitle' => get_string('delete'),
996
                    'confirm' => get_string('deleteblogpost?', 'artefact.blog'),
997
                    'value' => '<span class="fa fa-trash text-danger"></span><span class="btn-title pls">' .get_string('delete') . '</span>',
998
999
1000
1001
1002
                ),
            ),
        ));
    }

Alastair Pharo's avatar
Alastair Pharo committed
1003
    /**
1004
     * This function changes the blog post status.
Alastair Pharo's avatar
Alastair Pharo committed
1005
     *
1006
     * @param $newpoststatus: boolean 1=published, 0=draft
Alastair Pharo's avatar
Alastair Pharo committed
1007
1008
     * @return boolean
     */
1009
    public function changepoststatus($newpoststatus) {
Alastair Pharo's avatar
Alastair Pharo committed
1010
1011
1012
        if (!$this->id) {
            return false;
        }
1013

1014
1015
        $this->set('published', (int) $newpoststatus);
        $this->commit();
Alastair Pharo's avatar
Alastair Pharo committed
1016
1017
1018

        return true;
    }
1019

Martyn Smith's avatar
Martyn Smith committed
1020
1021
1022
1023
    public static function get_links($id) {
        $wwwroot = get_config('wwwroot');

        return array(
1024
            '_default' => $wwwroot . 'artefact/blog/view/index.php?blogpost=' . $id,
Martyn Smith's avatar
Martyn Smith committed
1025
1026
        );
    }
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036

    public function update_artefact_references(&$view, &$template, &$artefactcopies, $oldid) {
        parent::update_artefact_references($view, $template, $artefactcopies, $oldid);
        // Attach copies of the files that were attached to the old post.
        // Update <img> tags in the post body to refer to the new image artefacts.
        $regexp = array();
        $replacetext = array();
        if (isset($artefactcopies[$oldid]->oldattachments)) {
            foreach ($artefactcopies[$oldid]->oldattachments as $a) {
                if (isset($artefactcopies[$a])) {
1037
                    $this->attach($artefactcopies[$a]->newid);
1038
1039
1040
1041
1042
1043
1044
1045
                }
                $regexp[] = '#<img([^>]+)src="' . get_config('wwwroot') . 'artefact/file/download.php\?file=' . $a . '"#';
                $replacetext[] = '<img$1src="' . get_config('wwwroot') . 'artefact/file/download.php?file=' . $artefactcopies[$a]->newid . '"';
            }
            $this->set('description', preg_replace($regexp, $replacetext, $this->get('description')));
        }
    }

1046
1047
1048
1049
1050
    /**
     * During the copying of a view, we might be allowed to copy
     * blogposts but not the containing blog.  We need to create a new
     * blog to hold the copied posts.
     */
1051
    public function default_parent_for_copy(&$view, &$template, $artefactstoignore) {
1052
        static $blogids;
1053
        global $USER, $SESSION;
1054

1055
1056
1057
1058
        $viewid = $view->get('id');

        if (isset($blogids[$viewid])) {
            return $blogids[$viewid];
1059
1060
        }

1061
        $blogname = get_string('viewposts', 'artefact.blog', $viewid);
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
        $data = (object) array(
            'title'       => $blogname,
            'description' => get_string('postscopiedfromview', 'artefact.blog', $template->get('title')),
            'owner'       => $view->get('owner'),
            'group'       => $view->get('group'),
            'institution' => $view->get('institution'),
        );
        $blog = new ArtefactTypeBlog(0, $data);
        $blog->commit();

1072
        $blogids[$viewid] = $blog->get('id');
1073

1074
1075
1076
1077
1078
1079
        try {
            $user = get_user($view->get('owner'));
            set_account_preference($user->id, 'multipleblogs', 1);
            $SESSION->add_ok_msg(get_string('copiedblogpoststonewjournal', 'collection'));
        }
        catch (Exception $e) {
1080
            $SESSION->add_error_msg(get_string('unabletosetmultipleblogs', 'error', $user->username, $viewid, get_config('wwwroot') . 'account/index.php'), false);
1081
1082
1083
1084
1085
1086
1087
1088
1089
        }

        try {
            $USER->accountprefs = load_account_preferences($user->id);
        }
        catch (Exception $e) {
            $SESSION->add_error_msg(get_string('pleaseloginforjournals', 'error'));
        }

1090
        return $blogids[$viewid];
1091
    }
1092
1093

    /**
Aaron Wells's avatar
Aaron Wells committed
1094
     * Looks through the blog post text for links to download artefacts, and
1095
1096
1097
1098
1099
     * returns the IDs of those artefacts.
     */
    public function get_referenced_artefacts_from_postbody() {
        return artefact_get_references_in_html($this->get('description'));
    }
1100
1101
1102
1103

    public static function is_countable_progressbar() {
        return true;
    }
1104
}