lib.php 78.3 KB
Newer Older
1
2
3
4
5
<?php
/**
 *
 * @package    mahara
 * @subpackage blocktype
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
14
 *
 */

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


15
16
17
18
19
20
21
22
/**
 * Helper interface to hold IPluginBlocktype's abstract static methods
 */
interface IPluginBlocktype {
    public static function get_title();

    public static function get_description();

23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
    /**
     * Should be an array of blocktype categories that this block should be included in,
     * for determining how it shows up in the page editor's pallette of blocks.
     * See the function get_blocktype_categories() in lib/upgrade.php for the full list.
     *
     * A block can belong to multiple categories.
     *
     * The special category "shortcut" will make the blocktype show up on the top of the
     * block pallette instead of in a category.
     *
     * Blocktypes can have a sortorder in each category, that determines how they are
     * ordered in the category. To give a sortorder, put the category as the array key,
     * and the sortorder as the array value, like so:
     *
     * return array(
     *     'shortcut' => 1000,
     *     'general'  => 500,
     * );
     *
     * If no sortorder is provided, the blocktype's sortorder will default to 100,000.
     * Core blocktypes should have sortorders separated by 1,000 to give space for 3rd-party
     * blocks in between.
     *
     * Blocktypess with the same sortorder are sorted by blocktype name.
     *
     * @return array
     */
50
51
    public static function get_categories();

52
    public static function render_instance(BlockInstance $instance, $editing=false, $versioning=false);
53
54
}

55
56
57
58
/**
 * Base blocktype plugin class
 * @abstract
 */
59
abstract class PluginBlocktype extends Plugin implements IPluginBlocktype {
60

61
62
63
64
65
66
67
68
    /**
     * Default sortorder for a blocktype that has no sortorder defined for a
     * particular blocktype category that it's in. See IPluginBlocktype::get_categories()
     * for a full explanation of blocktype sortorder.
     * @var int
     */
    public static $DEFAULT_SORTORDER = 100000;

69
70
71
72
73
74
    /**
     * Used in the get_blocktype_list_icon() method
     */
    const BLOCKTYPE_LIST_ICON_PNG = 0;
    const BLOCKTYPE_LIST_ICON_FONTAWESOME = 1;

75
76
77
78
    public static function get_plugintype_name() {
        return 'blocktype';
    }

79
    /**
80
     * Optionally specify a place for a block to link to. This will be rendered in the block header
81
82
83
84
85
86
87
88
     * in templates
     * @var BlockInstance
     * @return String or false
     */
    public static function get_link(BlockInstance $instance) {
        return false;
    }

89
90
91
92
93
94
95
96
97
98
    public static function get_theme_path($pluginname) {
        if (($artefactname = blocktype_artefactplugin($pluginname))) {
            // Path for block plugins that sit under an artefact
            return 'artefact/' . $artefactname . '/blocktype/' . $pluginname;
        }
        else {
            return parent::get_theme_path($pluginname);
        }
    }

99
100
101
102
103
104
105
106
107
108
109
110
111
112

    /**
     * This function returns an array of menu items
     * to be displayed in the top right navigation menu
     *
     * See the function find_menu_children() in lib/web.php
     * for a description of the expected array structure.
     *
     * @return array
     */
    public static function right_nav_menu_items() {
        return array();
    }

113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
    /**
     * If the theme wants to display CSS icons for Mahara blocks, then it will
     * call this method to find out the name of the CSS icon to use. If this
     * method returns false, it will fall back to using the thumbnail.png
     *
     * In the core themes, these icons come from FontAwesome.
     * See htdocs/theme/raw/sass/lib/font-awesome/_icons.scss
     * for the full list of icons loaded by Mahara. (Note that this may change
     * from one Mahara version to another, as we upgrade FontAwesome.)
     * (Also note that the .scss files are stripped from the Mahara packaged
     * ZIP file. IF you don't have them, look in our git repository:
     * https://git.mahara.org/mahara/mahara/blob/master/htdocs/theme/raw/sass/lib/font-awesome/_icons.scss
     *
     * For the core blocktypes, we have "aliased" the name of the block
     * to the appropriate icon. See theme/raw/sass/lib/typography/_icons.scss.
     *
     * @param string $blocktypename The name of the blocktype
     * (since blocktype classes don't always know their own name as a string)
     * @return mixed Name of icon, or boolean false to fall back to thumbnail.png
     */
    public static function get_css_icon($blocktypename) {
        return false;
    }

137
138
    public static function extra_xmldb_substitution($xml) {
        return str_replace(
Aaron Wells's avatar
Aaron Wells committed
139
        '<!-- PLUGINTYPE_INSTALLED_EXTRAFIELDS -->',
140
        ' <FIELD NAME="artefactplugin" TYPE="char" LENGTH="255" NOTNULL="false" />',
141
        str_replace(
Aaron Wells's avatar
Aaron Wells committed
142
            '<!-- PLUGINTYPE_INSTALLED_EXTRAKEYS -->',
143
144
145
146
147
148
            '<KEY NAME="artefactpluginfk" TYPE="foreign" FIELDS="artefactplugin" REFTABLE="artefact_installed" REFFIELDS="name" />',
            $xml
            )
        );
    }

Aaron Wells's avatar
Aaron Wells committed
149
150
    /**
     * override this to return true if the blocktype
151
152
153
154
155
156
     * can only reasonably be placed once in a view
    */
    public static function single_only() {
        return false;
    }

Aaron Wells's avatar
Aaron Wells committed
157
158
159
160
161
    /**
     * Indicates whether this block can be loaded by Ajax after the page is done. This
     * improves page-load times by allowing blocks to be rendered in parallel instead
     * of in serial.
     *
162
     * You should avoid enabling this for:
Aaron Wells's avatar
Aaron Wells committed
163
164
165
     * - Blocks with particularly finicky Javascript contents
     * - Blocks that need to write to the session (the Ajax loader uses the session in read-only)
     * - Blocks that won't take long to render (static content, external content)
166
167
     * - Blocks that use hide_title_on_empty_content() (since you have to compute the content first
     * in order for that to work)
Aaron Wells's avatar
Aaron Wells committed
168
169
170
171
     *
     * @return boolean
     */
    public static function should_ajaxify() {
172
        return false;
Aaron Wells's avatar
Aaron Wells committed
173
174
    }

175
176
177
178
179
180
181
182
    /**
     * Allows block types to override the instance's title.
     *
     * For example: My Views, My Groups, My Friends, Wall
     */
    public static function override_instance_title(BlockInstance $instance) {
    }

183
184
185
186
187
188
189
190
191
192
193
194
    public static function get_viewtypes() {
        static $viewtypes = null;

        if (is_null($viewtypes)) {
            $viewtypes = get_column('view_type', 'type');
            if (!$viewtypes) {
                $viewtypes = array();
            }
        }

        return $viewtypes;
    }
195

196
197
198
199
200
201
202
203
204
    /**
    * This function must be implemented in the subclass if it requires
    * javascript. It returns an array of javascript files, either local
    * or remote.
    */
    public static function get_instance_javascript(BlockInstance $instance) {
        return array();
    }

205
206
207
208
209
210
211
212
213
   /**
    * This function must be implemented in the subclass if it requires
    * toolbar options for the view. It returns an array of button <a> tags and/or
    * html to display in the toobar area.
    */
    public static function get_instance_toolbars(BlockInstance $instance) {
        return array();
    }

214
215
216
217
218
219
220
221
222
    /**
    * This function must be implemented in the subclass if it requires
    * css file outside of sass compiled css. It returns an array of css files, either local
    * or remote.
    */
    public static function get_instance_css(BlockInstance $instance) {
        return array();
    }

223
224
225
226
227
228
    /**
     * Inline js to be executed when a block is rendered.
     */
    public static function get_instance_inline_javascript(BlockInstance $instance) {
    }

229
230
231
    /**
    * subclasses can override this if they need to do something a bit special
    * eg more than just what the BlockInstance->delete function does.
Aaron Wells's avatar
Aaron Wells committed
232
    *
233
234
235
236
    * @param BlockInstance $instance
    */
    public static function delete_instance(BlockInstance $instance) { }

237
238
239
    /**
    * This function must be implemented in the subclass if it has config
    */
240
241
    public static function instance_config_form(BlockInstance $instance) {
        throw new SystemException(get_string('blocktypemissingconfigform', 'error', $instance->get('blocktype')));
242
243
    }

244
245
246
247
248
249
250
251
252
    /**
    * Thus function must be implemented in the subclass is it has an
    * instance config form that requires javascript. It returns an
    * array of javascript files, either local or remote.
    */
    public static function get_instance_config_javascript(BlockInstance $instance) {
        return array();
    }

253
    /**
Aaron Wells's avatar
Aaron Wells committed
254
     * Blocktype plugins can implement this to perform custom pieform
255
256
257
     * validation, should they need it
     */
    public static function instance_config_validate(Pieform $form, $values) { }
258

259
    /**
260
    * Most blocktype plugins will attach to artefacts.
Aaron Wells's avatar
Aaron Wells committed
261
262
263
    * They should implement this function to keep a list of which ones. The
    * result of this method is used to populate the view_artefact table, and
    * thus decide whether an artefact is in a view for the purposes of access.
264
265
266
267
    * See {@link artefact_in_view} for more information about this.
    *
    * Note that it should just handle top level artefacts.
    * The cache rebuilder will figure out the children.
268
269
270
    *
    * @return array ids of artefacts in this block instance
    */
271
272
273
274
275
276
277
278
279
280
    public static function get_artefacts(BlockInstance $instance) {
        $configdata = $instance->get('configdata');
        if (isset($configdata['artefactids']) && is_array($configdata['artefactids'])) {
            return $configdata['artefactids'];
        }
        if (!empty($configdata['artefactid'])) {
            return array($configdata['artefactid']);
        }
        return false;
    }
281
282
283
284
285
286
287
288
289
290
291
    /**
    * Some blocktype plugins will have related artefacts based on artefactid.
    * They should implement this function to keep a list of which ones. The
    * result of this method is used to work out what artefacts where present at
    * the time of the version creation to save in view_versioning.
    *
    * Note that it should just handle child level artefacts.
    */
    public static function get_current_artefacts(BlockInstance $instance) {
        return array();
    }
292

Aaron Wells's avatar
Aaron Wells committed
293
    /**
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
     * If this blocktype contains artefacts, and uses the artefactchooser
     * Pieform element to choose them, this method must return the definition
     * for the element.
     *
     * This is used in view/artefactchooser.json.php to build pagination for
     * the element.
     *
     * The element returned MUST have the name key set to either 'artefactid'
     * or 'artefactids', depending on whether 'selectone' is true or false.
     *
     * The element must also have the 'blocktype' key set to the name of the
     * blocktype that the form is for.
     *
     * @param mixed $default The default value for the element
     */
    public static function artefactchooser_element($default=null) {
    }

    /**
     *
     * this is different to has_config - has_config is plugin wide config settings
     * this is specific to this TYPE of plugin and relates to whether individual instances
     * can be configured within a view
     */
318
    public static function has_instance_config() {
319
320
        return false;
    }
321

322
    public static function category_title_from_name($name) {
323
        $title = get_string('blocktypecategory.'. $name, 'view');
324
325
326
327
328
329
        if (strpos($title, '[[') !== 0) {
            return $title;
        }
        // else we're an artefact
        return get_string('pluginname', 'artefact.' . $name);
    }
330

331
332
333
334
335
    public static function category_description_from_name($name) {
        $description = get_string('blocktypecategorydesc.'. $name, 'view');
        return $description;
    }

336
    public static function get_blocktypes_for_category($category, View $view) {
337
        $sql = 'SELECT bti.name, bti.artefactplugin
Aaron Wells's avatar
Aaron Wells committed
338
            FROM {blocktype_installed} bti
339
            JOIN {blocktype_installed_category} btic ON btic.blocktype = bti.name
340
341
            JOIN {blocktype_installed_viewtype} btiv ON btiv.blocktype = bti.name
            WHERE btic.category = ? AND bti.active = 1 AND btiv.viewtype = ?
342
            ORDER BY btic.sortorder, bti.name';
343
        if (!$bts = get_records_sql_array($sql, array($category, $view->get('type')))) {
344
345
346
347
348
            return false;
        }

        $blocktypes = array();

349
350
351
352
        if (function_exists('local_get_allowed_blocktypes')) {
            $localallowed = local_get_allowed_blocktypes($category, $view);
        }

353
354
        foreach ($bts as $bt) {
            $namespaced = blocktype_single_to_namespaced($bt->name, $bt->artefactplugin);
355
            if (isset($localallowed) && is_array($localallowed) && !in_array($namespaced, $localallowed)) {
356
357
                continue;
            }
Aaron Wells's avatar
Aaron Wells committed
358
359
360
            safe_require('blocktype', $namespaced);
            // Note for later: this is Blocktype::allowed_in_view, which
            // returns true if the blocktype should be insertable into the
361
            // given view.
Aaron Wells's avatar
Aaron Wells committed
362
            // e.g. for blogs it returns false when view owner is not set,
363
            // because blogs can't be inserted into group views.
Aaron Wells's avatar
Aaron Wells committed
364
365
            // This could be different from whether a blockinstance is allowed
            // to be copied into a View (see the other place in this file where
366
367
            // allowed_in_view is called)
            //
Aaron Wells's avatar
Aaron Wells committed
368
369
370
            // Note also that if we want templates to be able to have all
            // blocktypes, we can add $view->get('template') here as part of
            // the condition, and also to View::addblocktype and
371
            // View::get_category_data
372
373
            $classname = generate_class_name('blocktype', $namespaced);
            if (call_static_method($classname, 'allowed_in_view', $view)) {
374
375
                $blocktypes[] = array(
                    'name'           => $bt->name,
376
377
378
                    'title'          => call_static_method($classname, 'get_title'),
                    'description'    => call_static_method($classname, 'get_description'),
                    'singleonly'     => call_static_method($classname, 'single_only'),
379
380
                    'artefactplugin' => $bt->artefactplugin,
                    'thumbnail_path' => get_config('wwwroot') . 'thumb.php?type=blocktype&bt=' . $bt->name . ((!empty($bt->artefactplugin)) ? '&ap=' . $bt->artefactplugin : ''),
381
                    'cssicon'        => call_static_method($classname, 'get_css_icon', $bt->name),
382
383
                );
            }
384
385
386
        }
        return $blocktypes;
    }
Richard Mansfield's avatar
Richard Mansfield committed
387

388
    /**
Aaron Wells's avatar
Aaron Wells committed
389
     * Takes config data for an existing blockinstance of this class and rewrites it so
390
391
     * it can be used to configure a block instance being put in a new view
     *
Aaron Wells's avatar
Aaron Wells committed
392
393
     * This is used at view copy time, to give blocktypes the chance to change
     * the configuration for a block based on aspects about the new view - for
394
395
     * example, who will own it.
     *
Aaron Wells's avatar
Aaron Wells committed
396
397
     * As an example - when the profile information blocktype is copied, we
     * want it so that all the fields that were configured previously are
398
399
400
401
     * pointing to the new owner's versions of those fields.
     *
     * The base method clears out any artefact IDs that are set.
     *
Aaron Wells's avatar
Aaron Wells committed
402
     * @param View $view The view that the blocktype will be placed into (e.g.
403
404
405
406
407
     *                   the View being created as a result of the copy)
     * @param array $configdata The configuration data for the old blocktype
     * @return array            The new configuration data.
     */
    public static function rewrite_blockinstance_config(View $view, $configdata) {
408
409
410
411
412
413
414
415
416
        if (isset($configdata['artefactid'])) {
            $configdata['artefactid'] = null;
        }
        if (isset($configdata['artefactids'])) {
            $configdata['artefactids'] = array();
        }
        return $configdata;
    }

417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
    /**
     * Takes extra config data for an existing blockinstance of this class
     * and rewrites it so it can be used to configure a new block instance being put
     * in a new view
     *
     * This is used at view copy time, to give blocktypes the chance to change
     * the extra configuration for a block based on aspects about the new view
     *
     * As an example - when the 'Text' blocktype is copied, we
     * want it so that all image urls in the $configdata['text'] are
     * pointing to the new images.
     *
     * @param View $view The view that the blocktype will be placed into (e.g.
     *                   the View being created as a result of the copy)
     * @param BlockInstance $block The new block
     * @param array $configdata The configuration data for the old blocktype
     * @param array $artefactcopies The mapping of old artefact ids to new ones
     * @return array            The new configuration data.
     */
    public static function rewrite_blockinstance_extra_config(View $view, BlockInstance $block, $configdata, $artefactcopies) {
        return $configdata;
    }

    /**
     * Rewrite extra config data for a blockinstance of this class when
     * importing its view from Leap
     *
     * As an example - when the 'text' blocktype is imported, we
     * want all image urls in the $configdata['text'] are
     * pointing to the new images.
     *
     * @param array $artefactids The mapping of leap entries to their artefact ID
     *      see more PluginImportLeap->artefactids
     * @param array $configdata The imported configuration data for the blocktype
     * @return array            The new configuration data.
     */
    public static function import_rewrite_blockinstance_extra_config_leap(array $artefactids, array $configdata) {
        return $configdata;
    }

457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473

    /**
     * Rewrite a block instance's relationships to views & collections at the end of the leap import process.
     *
     * (For instance the navigation block stores a collection ID, and needs to know the new ID the
     * collection wound up with.)
     *
     * This method is called at the end of the import process. You will probably want to access the
     * $importer->viewids, $importer->collectionids, and/or $importer->artefactids fields
     *
     * @param int $blockinstanceid ID of the block instance.
     * @param PluginImportLeap $importer The importer object.
     */
    public static function import_rewrite_blockinstance_relationships_leap($blockinstanceid, $importer) {
        // Do nothing, in the default case
    }

474
475
    /*
     * The copy_type of a block affects how it should be copied when its view gets copied.
476
477
478
479
480
481
482
483
484
     * nocopy:       The block doesn't appear in the new view at all.
     * shallow:      A new block of the same type is created in the new view with a configuration as specified by the
     *               rewrite_blockinstance_config method
     * reference:    Block configuration is copied as-is.  If the block contains artefacts, the original artefact ids are
     *               retained in the new block's configuration even though they may have a different owner from the view.
     * full:         All artefacts referenced by the block are copied to the new owner's portfolio, and ids in the new
     *               block are updated to point to the copied artefacts.
     * fullinclself: All artefacts referenced by the block are copied, whether we are copying to a new owner's portfolio
     *               or our own one, and ids in the new block are updated to point to the copied artefacts.
485
     *
486
     * If the old owner and the new owner are the same, reference is used unless 'fullinclself' is specified.
487
488
489
490
491
492
     * If a block contains no artefacts, reference and full are equivalent.
     */
    public static function default_copy_type() {
        return 'shallow';
    }

493
494
495
496
497
498
499
500
    /*
     * The ignore_copy_artefacttypes of a block affects which artefacttypes should be ignored when copying.
     * You can specify which artefacts to ignore by an array of artefacttypes.
     */
    public static function ignore_copy_artefacttypes() {
        return array();
    }

501
502
503
    /**
     * Whether this blocktype is allowed in the given View.
     *
Aaron Wells's avatar
Aaron Wells committed
504
505
     * Some blocktypes may wish to limit whether they're allowed in a View if,
     * for example, they make no sense when the view is owned by a certain type
506
507
     * of owner.
     *
Aaron Wells's avatar
Aaron Wells committed
508
     * For example, the 'profile information' blocktype makes no sense in a
509
510
     * group View.
     *
Aaron Wells's avatar
Aaron Wells committed
511
512
     * Of course, blocktypes could implement stranger rules - e.g. only allow
     * when the view has 'ponies' in its description (BTW: such blocktypes
513
514
515
     * would be totally awesome).
     *
     * @param View     The View to check
Aaron Wells's avatar
Aaron Wells committed
516
     * @return boolean Whether blocks of this blocktype are allowed in the
517
518
519
520
521
522
     *                 given view.
     */
    public static function allowed_in_view(View $view) {
        return true;
    }

523
    /**
Aaron Wells's avatar
Aaron Wells committed
524
     * Given a block instance, returns a hash with enough information so that
525
526
     * we could reconstruct it if given this information again.
     *
Aaron Wells's avatar
Aaron Wells committed
527
528
     * Import/Export routines can serialise this information appropriately, and
     * unserialise it on the way back in, where it is passed to {@link
529
530
531
532
533
534
     * import_create_blockinstance()} for creation of a new block instance.
     *
     * @param BlockInstance $bi The block instance to export config for
     * @return array The configuration required to import the block again later
     */
    public static function export_blockinstance_config(BlockInstance $bi) {
535
536
537
        $configdata = $bi->get('configdata');

        if (is_array($configdata)) {
Aaron Wells's avatar
Aaron Wells committed
538
539
            // Unset a bunch of stuff that we don't want to export. These fields
            // weren't being cleaned up before blockinstances were being saved
540
541
542
543
544
545
546
547
548
549
550
            // previously, so we make sure they're not going to be in the result
            unset($configdata['blockconfig']);
            unset($configdata['id']);
            unset($configdata['change']);
            unset($configdata['new']);
        }
        else {
            $configdata = array();
        }

        return $configdata;
551
    }
552
553

    /**
Francois Marier's avatar
Francois Marier committed
554
     * Exports configuration data the format required for Leap2A export.
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
     *
     * This format is XML, and as the exporter can't generate complicated XML
     * structures, we have to json_encode all the values.
     *
     * Furthermore, because of how json_encode and json_decode "work" in PHP,
     * we make double sure that our values are all inside arrays. See the
     * craziness that is PHP bugs 38680 and 46518 for more information.
     *
     * The array is assumed to be there when importing, so if you're overriding
     * this method and don't wrap any values in an array, you can expect import
     * to growl at you and not import your config.
     *
     * @param BlockInstance $bi The block instance to export config for
     * @return array The configuration required to import the block again later
     */
    public static function export_blockinstance_config_leap(BlockInstance $bi) {
        $configdata = call_static_method(generate_class_name('blocktype', $bi->get('blocktype')), 'export_blockinstance_config', $bi);
        foreach ($configdata as $key => &$value) {
            $value = json_encode(array($value));
        }
        return $configdata;
    }
577
578
579
580

    /**
     * Creates a block instance from a given configuration.
     *
581
582
     * The configuration is whatever was generated by {@link
     * export_blockinstance_config()}. This method doesn't have to worry about
583
584
     * setting the block title, or the position in the View.
     *
585
586
     * @param array $biconfig   The config to use to create the blockinstance
     * @param array $viewconfig The configuration for the view being imported
587
588
     * @return BlockInstance The new block instance
     */
589
    public static function import_create_blockinstance(array $biconfig, array $viewconfig) {
590
591
        $bi = new BlockInstance(0,
            array(
592
593
                'blocktype'  => $biconfig['type'],
                'configdata' => $biconfig['config'],
594
595
596
597
            )
        );

        return $bi;
598
599
    }

600
601
602
603
604
605
606
607
608
609
610
611
    /**
     * defines if the title should be shown if there is no content in the block
     *
     * If the title of the block should be hidden when there is no content,
     * override the the function in the blocktype class.
     *
     * @return boolean  whether the title of the block should be shown or not
     */
    public static function hide_title_on_empty_content() {
        return false;
    }

612
613
614
615
616
617
618
619
620
621
622
623
624
    /**
     * Defines if the title should be linked to an artefact view (if possible)
     * when viewing the block
     *
     * This method should be overridden in the child class, if a title link
     * is not desired.
     *
     * @return boolean whether to link the title or not
     */
    public static function has_title_link() {
        return true;
    }

625
626
627
628
629
630
631
632
633
634
635
636
637
    /**
     * Defines if the block is viewable by the logged in user
     *
     * This method should be overridden in the child class, if peer role
     * should be able to see the block
     *
     * @param array user access role for the view
     * @return boolean whether display the block content for the roles
     */
    public static function display_for_roles($roles) {
        return !(count($roles) == 1 && $roles[0] == 'peer');
    }

638
639
}

640

641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
/**
 * Mahara core blocks should extend this class. (Currently it only controls styling,
 * useful as a way of mapping the behavior of core blocks to theme items that are
 * not easily queried by the code.)
 */
abstract class MaharaCoreBlocktype extends PluginBlockType {

    /**
     * Use a css icon based on the name of the block
     * (These are defined in typography.scss)
     *
     * @param string $blocktypename
     * @return string
     */
    public static function get_css_icon($blocktypename) {
        return $blocktypename;
    }
}

/**
 * Old half-used "SystemBlockType" class. Deprecated, but still included because
 * some 3rd-party blocktypes use it.
 *
 * It was never clearly described what the purpose of this blocktype is; but most
 * likely its purpose was to indicate blocks that don't "contain" artefacts, such
 * as the "new views" block.
 *
 * But as long as your block isn't storing an item called "artefactid" or "artefactids"
 * in its blocktype.config field, then the default implementation of get_artefacts()
 * doesn't really matter.
 */
abstract class SystemBlockType extends PluginBlockType {
673
    public static function get_artefacts(BlockInstance $instance) {
674
675
676
        return array();
    }

677
    public final static function artefactchooser_element($default=null) {
678
    }
679
680
681
}


682
683
class BlockInstance {

684
685
686
687
    const RETRACTABLE_NO = 0;
    const RETRACTABLE_YES = 1;
    const RETRACTABLE_RETRACTED = 2;

688
689
    private $id;
    private $blocktype;
690
    private $artefactplugin;
691
    private $title;
692
    private $configdata = array();
693
    private $dirty;
Penny Leach's avatar
Penny Leach committed
694
695
    private $view;
    private $view_obj;
696
    private $row;
Penny Leach's avatar
Penny Leach committed
697
    private $column;
698
    private $order;
Penny Leach's avatar
Penny Leach committed
699
700
701
702
    private $canmoveleft;
    private $canmoveright;
    private $canmoveup;
    private $canmovedown;
703
    private $maxorderincolumn;
704
    private $artefacts = array();
705
    private $temp = array();
706
    private $tags = array();
707
    private $inedit = false;
Penny Leach's avatar
Penny Leach committed
708
709
710
711
712

    public function __construct($id=0, $data=null) {
         if (!empty($id)) {
            if (empty($data)) {
                if (!$data = get_record('block_instance','id',$id)) {
Aaron Wells's avatar
Aaron Wells committed
713
714
715
                    // TODO: 1) doesn't need get string here if this is the
                    // only place the exception is used - can be done in the
                    // class itself. 2) String needs to be defined, or taken
716
                    // from lang/*/view.php where there is already one for it
Penny Leach's avatar
Penny Leach committed
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
                    throw new BlockInstanceNotFoundException(get_string('blockinstancenotfound', 'error', $id));
                }
            }
            $this->id = $id;
        }
        else {
            $this->dirty = true;
        }
        if (empty($data)) {
            $data = array();
        }
        foreach ((array)$data as $field => $value) {
            if (property_exists($this, $field)) {
                $this->{$field} = $value;
            }
        }
733
        $this->artefactplugin = blocktype_artefactplugin($this->blocktype);
Penny Leach's avatar
Penny Leach committed
734
    }
735
736
737
738
739

    public function get($field) {
        if (!property_exists($this, $field)) {
            throw new InvalidArgumentException("Field $field wasn't found in class " . get_class($this));
        }
Penny Leach's avatar
Penny Leach committed
740
741
742
743
744
745
        if ($field == 'configdata') {
            // make sure we unserialise it
            if (!is_array($this->configdata)) {
                $this->configdata = unserialize($this->configdata);
            }
        }
746
747
748
749
750
751
752
753
754
755
756
757
758
759
        if ($field == 'tags') {
            $typecast = is_postgres() ? '::varchar' : '';
            $this->tags = get_column_sql("
            SELECT
                (CASE
                    WHEN t.tag LIKE 'tagid_%' THEN CONCAT(i.displayname, ': ', t2.tag)
                    ELSE t.tag
                END) AS tag, t.resourceid
            FROM {tag} t
            LEFT JOIN {tag} t2 ON t2.id" . $typecast . " = SUBSTRING(t.tag, 7)
            LEFT JOIN {institution} i ON i.name = t2.ownerid
            WHERE t.resourcetype = ? AND t.resourceid = ?
            ORDER BY tag", array('blocktype', $this->get('id')));
        }
Penny Leach's avatar
Penny Leach committed
760
761
762
763
764
765
766
        if (strpos($field, 'canmove') === 0) {
            return $this->can_move(substr($field, strlen('canmove'))); // needs to be calculated.
        }
        if ($field == 'maxorderincolumn') {
            // only fetch this when we're asked, it's a db query.
            if (empty($this->maxorderincolumn)) {
                $this->maxorderincolumn = get_field(
Aaron Wells's avatar
Aaron Wells committed
767
768
                    'block_instance',
                    'max("order")',
Penny Leach's avatar
Penny Leach committed
769
770
771
                    'view', $this->view, 'column', $this->column);
            }
        }
772
773
774
775
776
        return $this->{$field};
    }

    public function set($field, $value) {
        if (property_exists($this, $field)) {
777
778
779
            if ($field == 'tags') {
                $this->set_tags($value);
            }
Penny Leach's avatar
Penny Leach committed
780
            if ($field == 'configdata') {
781
                $value = serialize($value);
Penny Leach's avatar
Penny Leach committed
782
            }
783
            if ($this->{$field} !== $value) {
784
785
                // only set it to dirty if it's changed
                $this->dirty = true;
786
                $this->{$field} = $value;
787
788
789
            }
            return true;
        }
790
        throw new ParamOutOfRangeException("Field $field wasn't found in class " . get_class($this));
Penny Leach's avatar
Penny Leach committed
791
792
    }

793
794
795
    private function set_tags($tags) {
        global $USER;

796
797
798
799
        if (empty($this->view_obj)) {
            $this->get_view();
        }

800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
        if ($this->view_obj->get('group')) {
            $ownertype = 'group';
            $ownerid = $this->view_obj->get('group');
        }
        else if ($this->view_obj->get('institution')) {
            $ownertype = 'institution';
            $ownerid = $this->view_obj->get('institution');
        }
        else {
            $ownertype = 'user';
            $ownerid = $this->view_obj->get('owner');
        }
        $this->tags = check_case_sensitive($tags, 'tag');
        delete_records('tag', 'resourcetype', 'blocktype', 'resourceid', $this->get('id'));
        foreach ($this->tags as $tag) {
            // truncate the tag before insert it into the database
            $tag = substr($tag, 0, 128);
817
            $tag = check_if_institution_tag($tag);
818
819
820
821
822
823
824
825
826
827
828
829
830
831
            insert_record('tag',
                (object)array(
                    'resourcetype' => 'blocktype',
                    'resourceid' => $this->get('id'),
                    'ownertype' => $ownertype,
                    'ownerid' => $ownerid,
                    'tag' => $tag,
                    'ctime' => db_format_timestamp(time()),
                    'editedby' => $USER->get('id'),
                )
            );
        }
    }

832
833
834
835
836
837
838
839
    // returns false if it finds a bad attachment
    // returns true if all attachments are allowed
    private function verify_attachment_permissions($id) {
        global $USER;

        if (is_array($id)) {
            foreach ($id as $id) {
                $file = artefact_instance_from_id($id);
840
                if (!$USER->can_view_artefact($file)) {
841
842
843
844
845
846
847
                    // bail out now as at least one attachment is bad
                    return false;
                }
            }
        }
        else {
            $file = artefact_instance_from_id($id);
848
            if (!$USER->can_view_artefact($file)) {
849
850
851
852
853
854
                return false;
            }
        }
        return true;
    }

Nigel McNie's avatar
Nigel McNie committed
855
    public function instance_config_store(Pieform $form, $values) {
856
        global $SESSION, $USER;
857

Nigel McNie's avatar
Nigel McNie committed
858
859
860
861
        // Destroy form values we don't care about
        unset($values['sesskey']);
        unset($values['blockinstance']);
        unset($values['action_configureblockinstance_id_' . $this->get('id')]);
862
863
864
865
        unset($values['blockconfig']);
        unset($values['id']);
        unset($values['change']);
        unset($values['new']);
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
        if (isset($values['retractable'])) {
            switch ($values['retractable']) {
                case BlockInstance::RETRACTABLE_YES:
                    $values['retractable'] = 1;
                    $values['retractedonload'] = 0;
                    break;
                case BlockInstance::RETRACTABLE_RETRACTED:
                    $values['retractable'] = 1;
                    $values['retractedonload'] = 1;
                    break;
                case BlockInstance::RETRACTABLE_NO:
                default:
                    $values['retractable'] = 0;
                    $values['retractedonload'] = 0;
                    break;
            }
        }
Nigel McNie's avatar
Nigel McNie committed
883

884
885
886
        // make sure that user is allowed to publish artefact. This is to stop
        // hacking of form value to attach other users private data.
        $badattachment = false;
887
888
        if (isset($values['blocktemplate']) && !empty($values['blocktemplate'])) {
            // Ignore check on artefactids as they are not relating to actual artefacts
889
        }
890
891
892
893
894
895
896
        else {
            if (!empty($values['artefactid'])) {
                $badattachment = !$this->verify_attachment_permissions($values['artefactid']);
            }
            if (!empty($values['artefactids'])) {
                $badattachment = !$this->verify_attachment_permissions($values['artefactids']);
            }
897
        }
898

899
900
901
902
903
904
905
        if ($badattachment) {
            $result['message'] = get_string('unrecoverableerror', 'error');
            $form->set_error(null, $result['message']);
            $form->reply(PIEFORM_ERR, $result);
            exit();
        }

906
907
908
909
910
911
912
913
914
915
916
917
        $redirect = '/view/blocks.php?id=' . $this->get('view');
        if (param_boolean('new', false)) {
            $redirect .= '&new=1';
        }
        if ($category = param_alpha('c', '')) {
            $redirect .= '&c='. $category;
        }

        $result = array(
            'goto' => $redirect,
        );

Nigel McNie's avatar
Nigel McNie committed
918
        if (is_callable(array(generate_class_name('blocktype', $this->get('blocktype')), 'instance_config_save'))) {
919
920
921
922
923
924
925
926
            try {
                $values = call_static_method(generate_class_name('blocktype', $this->get('blocktype')), 'instance_config_save', $values, $this);
            }
            catch (MaharaException $e) {
                $result['message'] = $e instanceof UserException ? $e->getMessage() : get_string('unrecoverableerror', 'error');
                $form->set_error(null, $result['message']);
                $form->reply(PIEFORM_ERR, $result);
            }
Nigel McNie's avatar
Nigel McNie committed
927
928
        }

929
930
        $title = (isset($values['title'])) ? $values['title'] : '';
        unset($values['title']);
931

932
933
934
935
936
        if (isset($values['tags'])) {
            $this->set('tags', $values['tags']);
            unset($values['tags']);
        }

937
938
939
940
941
        // A block may return a list of other blocks that need to be
        // redrawn after configuration of this block.
        $torender = !empty($values['_redrawblocks']) && $form->submitted_by_js() ? $values['_redrawblocks'] : array();
        unset($values['_redrawblocks']);

942
        $this->set('configdata', $values);
943
        $this->set('title', $title);
944

945
946
        $this->commit();

947
948
949
950
951
952
953
954
        try {
            $rendered = $this->render_editing(false, false, $form->submitted_by_js());
        }
        catch (HTMLPurifier_Exception $e) {
            $message = get_string('blockconfigurationrenderingerror', 'view') . ' ' . $e->getMessage();
            $form->reply(PIEFORM_ERR, array('message' => $message));
        }

955
956
957
        $result = array(
            'error'   => false,
            'message' => get_string('blockinstanceconfiguredsuccessfully', 'view'),
958
            'data'    => $rendered,
959
            'blockid' => $this->get('id'),
960
            'viewid'  => $this->get('view'),
961
            'goto'    => $redirect,
962
963
        );

964
965
966
967
968
969
970
971
972
973
974
975
        // Render all the other blocks in the torender list
        $result['otherblocks'] = array();
        foreach ($torender as $blockid) {
            if ($blockid != $result['blockid']) {
                $otherblock = new BlockInstance($blockid);
                $result['otherblocks'][] = array(
                    'blockid' => $blockid,
                    'data'    => $otherblock->render_editing(false, false, true),
                );
            }
        }

976
        $form->reply(PIEFORM_OK, $result);
Nigel McNie's avatar
Nigel McNie committed
977
978
    }

979
980
981
982
983
984
985
986
987
988
989
990
991
992
    public function get_title() {
        $blocktypeclass = generate_class_name('blocktype', $this->get('blocktype'));
        if ($override = call_static_method($blocktypeclass, 'override_instance_title', $this)) {
            return $override;
        }
        if ($title = $this->get('title') and $title != '') {
            return $title;
        }
        if (method_exists($blocktypeclass, 'get_instance_title')) {
            return call_static_method($blocktypeclass, 'get_instance_title', $this);
        }
        return '';
    }

993
994
995
996
    public function to_stdclass() {
        return (object)get_object_vars($this);
    }

Penny Leach's avatar
Penny Leach committed
997
    /**
Aaron Wells's avatar
Aaron Wells committed
998
     * Builds the HTML for the block, inserting the blocktype content at the
999
     * appropriate place
1000
     *
Aaron Wells's avatar
Aaron Wells committed
1001
     * @param bool $configure Whether to render the block instance in configure
1002
     *                        mode
1003
     * @return array Array with two keys: 'html' for raw html, 'javascript' for
1004
     *               javascript to run
Penny Leach's avatar
Penny Leach committed
1005
     */
1006
    public function render_editing($configure=false, $new=false, $jsreply=false) {
1007
1008
        global $USER;

Penny Leach's avatar
Penny Leach committed
1009
        safe_require('blocktype', $this->get('blocktype'));
1010
        $movecontrols = array();
1011
        $this->inedit = true;
1012
        $blocktypeclass = generate_class_name('blocktype', $this->get('blocktype'));
1013
1014
1015
1016
1017
1018
1019
        try {
            $title = $this->get_title();
        }
        catch (NotFoundException $e) {
            log_debug('Cannot render block title. Original error follows: ' . $e->getMessage());
            $title = get_string('notitle', 'view');
        }
1020

1021
        if ($configure) {
1022
            list($content, $js, $css) = array_values($this->build_configure_form($new));
1023
1024
        }
        else {
1025
            try {
1026
1027
1028
1029
1030
1031
1032
              $user_roles = get_column('view_access', 'role', 'usr', $USER->get('id'), 'view', $this->view);
              if (!call_static_method($blocktypeclass, 'display_for_roles', $user_roles)) {
                  $content = '';
                  $css = '';
                  $js = '';
              }
              else   {
1033
                $content = call_static_method(generate_class_name('blocktype', $this->get('blocktype')), 'render_instance', $this, true);
1034
1035
1036
                $jsfiles = call_static_method($blocktypeclass, 'get_instance_javascript', $this);
                $inlinejs = call_static_method($blocktypeclass, 'get_instance_inline_javascript', $this);
                $js = $this->get_get_javascript_javascript($jsfiles) . $inlinejs;
1037
                $css = '';
1038
              }
1039
            }
1040
            catch (NotFoundException $e) {
Aaron Wells's avatar
Aaron Wells committed
1041
1042
1043
                // Whoops - where did the image go? There is possibly a bug
                // somewhere else that meant that this blockinstance wasn't
                // told that the image was previously deleted. But the block
1044
1045
1046
1047
1048
1049
                // instance is not allowed to treat this as a failure
                log_debug('Artefact not found when rendering a block instance. '
                    . 'There might be a bug with deleting artefacts of this type? '
                    . 'Original error follows:');
                log_debug($e->getMessage());
                $content = '';
1050
                $js = '';
1051
                $css = '';
1052
            }
1053

1054
1055
1056
1057
1058
            if (!defined('JSON') && !$jsreply) {
                if ($this->get('canmoveleft')) {
                    $movecontrols[] = array(
                        'column' => $this->get('column') - 1,
                        'order'  => $this->get('order'),
1059
                        'title'  => $title == '' ? get_string('movethisblockleft', 'view') : get_string('moveblockleft', 'view', "'$title'"),
1060
                        'arrow'  => "icon icon-long-arrow-left",
1061
1062
1063
1064
1065
1066
1067
                        'dir'    => 'left',
                    );
                }
                if ($this->get('canmovedown')) {
                    $movecontrols[] = array(
                        'column' => $this->get('column'),
                        'order'  => $this->get('order') + 1,
1068
                        'title'  => $title == '' ? get_string('movethisblockdown', 'view') : get_string('moveblockdown', 'view', "'$title'"),
1069
                        'arrow'  => 'icon icon-long-arrow-down',
1070
1071
1072
1073
1074
1075
1076
                        'dir'    => 'down',
                    );
                }
                if ($this->get('canmoveup')) {
                    $movecontrols[] = array(
                        'column' => $this->get('column'),
                        'order'  => $this->get('order') - 1,
1077
                        'title'  => $title == '' ? get_string('movethisblockup', 'view') : get_string('moveblockup', 'view', "'$title'"),
1078
                        'arrow'  => 'icon icon-long-arrow-up',
1079
1080
1081
1082
1083
1084
1085
                        'dir'    => 'up',
                    );
                }
                if ($this->get('canmoveright')) {
                    $movecontrols[] = array(
                        'column' => $this->get('column') + 1,
                        'order'  => $this->get('order'),
1086
                        'title'  => $title == '' ? get_string('movethisblockright', 'view') : get_string('moveblockright', 'view', "'$title'"),
1087
                        'arrow'  => 'icon icon-long-arrow-right',
1088
1089
1090
                        'dir'    => 'right',
                    );
                }
1091
1092
            }
        }
1093
1094
1095

        $configtitle = $title == '' ? call_static_method($blocktypeclass, 'get_title') : $title;

1096
        $smarty = smarty_core();
1097
1098
        $id = $this->get('id');
        $smarty->assign('id',     $id);
1099
        $smarty->assign('viewid', $this->get('view'));
1100
        $smarty->assign('title',  $title);
1101
        $smarty->assign('row',    $this->get('row'));
1102
1103
        $smarty->assign('column', $this->get('column'));
        $smarty->assign('order',  $this->get('order'));
1104
        $smarty->assign('blocktype', $this->get('blocktype'));
1105
        $smarty->assign('movecontrols', $movecontrols);
1106
        $smarty->assign('configurable', call_static_method($blocktypeclass, 'has_instance_config'));
1107
        $smarty->assign('configure', $configure); // Used by the javascript to rewrite the block, wider.
1108
        $smarty->assign('configtitle',  $configtitle);
1109
        $smarty->assign('content', $content);
1110
        $smarty->assign('javascript', defined('JSON'));
1111
        $smarty->assign('strnotitle', get_string('notitle', 'view'));
1112
        $smarty->assign('strmovetitletext', $title == '' ? get_string('movethisblock', 'view') : get_string('moveblock', 'view', "'$title'"));
1113
        $smarty->assign('strmovetitletexttooltip', get_string('moveblock2', 'view'));
1114
        $smarty->assign('strconfigtitletext', $title == '' ? get_string('configurethisblock1', 'view', $id) : get_string('configureblock1', 'view', "'$title'", $id));
1115
        $smarty->assign('strconfigtitletexttooltip', get_string('configureblock2', 'view'));
1116
        $smarty->assign('strremovetitletext', $title == '' ? get_string('removethisblock1', 'view', $id) : get_string('removeblock1', 'view', "'$title'", $id));
1117
        $smarty->assign('strremovetitletexttooltip', get_string('removeblock2', 'view'));
1118
        $smarty->assign('lockblocks', ($this->get_view()->get('lockblocks') && $this->get_view()->get('owner'))); // Only lock blocks for user's portfolio pages
1119

1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
        if (!$configure && $title) {
            $configdata = $this->get('configdata');
            if (isset($configdata['retractable']) && $configdata['retractable']) {
                $smarty->assign('retractable', true);
                if (defined('JSON') || $jsreply) {
                    $jssmarty = smarty_core();
                    $jssmarty->assign('id', $this->get('id'));
                    $js .= $jssmarty->fetch('view/retractablejs.tpl');
                }
            }
        }
1131
1132
1133
1134
        if (is_array($css)) {
            $css = array_unique($css);
        }
        return array('html' => $smarty->fetch('view/blocktypecontainerediting.tpl'), 'javascript' => $js, 'pieformcss' => $css);
Penny Leach's avatar
Penny Leach committed
1135
1136
    }

1137
1138
1139
1140
1141
1142
1143
1144


    public function order_artefacts_by_title($ids){
      $result = array();
      if ($ids) {
          $artefacts =  get_records_sql_array(
              'SELECT a.id, a.title FROM {artefact} a WHERE a.id in ( '. join(',', array_fill(0, count($ids), '?')) . ')', $ids
          );
1145
1146
1147
1148
1149
          if ($artefacts) {
              uasort($artefacts, array("BlockInstance", "my_files_cmp"));
              foreach ($artefacts as $artefact) {
                  $result[] = $artefact->id;
              }
1150
1151
1152
1153
1154
1155
1156
1157
1158
          }
      }
      return $result;
    }

    public static function my_files_cmp($a, $b) {
        return strnatcasecmp($a->title, $b->title);
    }

1159
1160
1161
1162
1163
    /**
     * To render the html of a block for viewing
     *
     * @param boolean $exporting  Indicate the rendering is for an export
     *                            If we are doing an export we can't render the block to be loaded via ajax
1164
1165
     * @param boolean $versioning Indicate the rendering is for an older view version
     *
1166
1167
     * @return the rendered block
     */
1168
    public function render_viewing($exporting=false, $versioning=false) {
1169
        global $USER;
1170

1171
1172
1173
        if (!safe_require_plugin('blocktype', $this->get('blocktype'))) {
            return;
        }
1174
1175
1176

        $smarty = smarty_core();

1177
1178
        $user_roles = get_column('view_access', 'role', 'usr', $USER->get('id'), 'view', $this->view);

1179
        $classname = generate_class_name('blocktype', $this->get('blocktype'));
1180
        $displayforrole = call_static_method($classname, 'display_for_roles', $user_roles);
1181
1182
1183
1184
        $checkview = $this->get_view();
        if ($USER->is_admin_for_user($checkview->get('owner')) && $checkview->is_objectionable()) {
            $displayforrole = true;
        }
1185
1186
1187
1188
        if (!$displayforrole) {
            $content = '';
            $smarty->assign('loadbyajax', false);
        }
1189
        else if (get_config('ajaxifyblocks') && call_static_method($classname, 'should_ajaxify') && $exporting === false && $versioning === false) {
1190
            $content = '';
1191
            $smarty->assign('loadbyajax', true);
1192
        }
Aaron Wells's avatar
Aaron Wells committed
1193
        else {
1194
            $smarty->assign('loadbyajax', false);
Aaron Wells's avatar
Aaron Wells committed
1195
            try {
1196
                $content = call_static_method($classname, 'render_instance', $this, false, $versioning);
Aaron Wells's avatar
Aaron Wells committed
1197
1198
            }
            catch (NotFoundException $e) {
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
                //Ignore not found error when fetching old verions of view
                if (!$versioning) {
                    // Whoops - where did the image go? There is possibly a bug
                    // somewhere else that meant that this blockinstance wasn't
                    // told that the image was previously deleted. But the block
                    // instance is not allowed to treat this as a failure
                    log_debug('Artefact not found when rendering a block instance. '
                        . 'There might be a bug with deleting artefacts of this type? '
                        . 'Original error follows:');
                    log_debug($e->getMessage());
                }
Aaron Wells's avatar
Aaron Wells committed
1210
1211
1212
                $content = '';
            }
        }
1213
1214

        $smarty->assign('id',     $this->get('id'));
1215
        $smarty->assign('blocktype', $this->get('blocktype'));
1216
        // hide the title if required and no content is present
1217
        if (call_static_method($classname, 'hide_title_on_empty_content')
1218
1219
1220
            && !trim($content)) {
            return;
        }
1221
1222
1223
1224
1225
1226
1227
1228
        try {
            $title = $this->get_title();
        }
        catch (NotFoundException $e) {
            log_debug('Cannot render block title. Original error follows: ' . $e->getMessage());
            $title = get_string('notitle', 'view');
        }
        $smarty->assign('title', $title);
1229

Nigel McNie's avatar
Nigel McNie committed
1230
        // If this block is for just one artefact, we set the title of the
1231
1232
        // block to be a link to view more information about that artefact
        $configdata = $this->get('configdata');
1233
        if (!empty($configdata['artefactid']) && $displayforrole) {
1234
            if (call_static_method($classname, 'has_title_link')) {
1235
                $smarty->assign('viewartefacturl', get_config('wwwroot') . 'artefact/artefact.php?artefact='
1236
                    . $configdata['artefactid'] . '&view=' . $this->get('view') . '&block=' . $this->get('id'));
1237
            }
1238
1239
        }

1240
1241
1242
1243
        if ($displayforrole) {
            if (method_exists($classname, 'feed_url')) {
                $smarty->assign('feedlink', call_static_method($classname, 'feed_url', $this));
            }
1244

1245
1246
            $smarty->assign('link', call_static_method($classname, 'get_link', $this));
        }
1247

1248
        $smarty->assign('content', $content);
1249
        if (isset($configdata['retractable']) && $title && !$exporting) {
1250
1251
1252
1253
1254
            $smarty->assign('retractable', $configdata['retractable']);
            if (isset($configdata['retractedonload'])) {
                $smarty->assign('retractedonload', $configdata['retractedonload']);
            }
        }