forked from MaharaProject/mahara
/
lib.php
1593 lines (1432 loc) · 63 KB
/
lib.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
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
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
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
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
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
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
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
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?php
/**
*
* @package mahara
* @subpackage blocktype
* @author Catalyst IT Ltd
* @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.
*
*/
defined('INTERNAL') || die();
/**
* Helper interface to hold IPluginBlocktype's abstract static methods
*/
interface IPluginBlocktype {
public static function get_title();
public static function get_description();
/**
* 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
*/
public static function get_categories();
public static function render_instance(BlockInstance $instance, $editing=false);
/**
* 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);
}
/**
* Base blocktype plugin class
* @abstract
*/
abstract class PluginBlocktype extends Plugin implements IPluginBlocktype {
/**
* 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;
public static function get_plugintype_name() {
return 'blocktype';
}
/**
* Optionally specify a place for a block to link to. This will be rendered in the block header
* in templates
* @var BlockInstance
* @return String or false
*/
public static function get_link(BlockInstance $instance) {
return false;
}
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);
}
}
public static function extra_xmldb_substitution($xml) {
return str_replace(
'<!-- PLUGINTYPE_INSTALLED_EXTRAFIELDS -->',
' <FIELD NAME="artefactplugin" TYPE="char" LENGTH="255" NOTNULL="false" />',
str_replace(
'<!-- PLUGINTYPE_INSTALLED_EXTRAKEYS -->',
'<KEY NAME="artefactpluginfk" TYPE="foreign" FIELDS="artefactplugin" REFTABLE="artefact_installed" REFFIELDS="name" />',
$xml
)
);
}
/**
* override this to return true if the blocktype
* can only reasonably be placed once in a view
*/
public static function single_only() {
return false;
}
/**
* 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.
*
* You should avoid enabling this for:
* - 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)
* - Blocks that use hide_title_on_empty_content() (since you have to compute the content first
* in order for that to work)
*
* @return boolean
*/
public static function should_ajaxify() {
return false;
}
/**
* 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) {
}
public static function get_viewtypes() {
static $viewtypes = null;
if (is_null($viewtypes)) {
$viewtypes = get_column('view_type', 'type');
if (!$viewtypes) {
$viewtypes = array();
}
}
return $viewtypes;
}
/**
* 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();
}
/**
* Inline js to be executed when a block is rendered.
*/
public static function get_instance_inline_javascript(BlockInstance $instance) {
}
/**
* subclasses can override this if they need to do something a bit special
* eg more than just what the BlockInstance->delete function does.
*
* @param BlockInstance $instance
*/
public static function delete_instance(BlockInstance $instance) { }
/**
* This function must be implemented in the subclass if it has config
*/
public static function instance_config_form(BlockInstance $instance) {
throw new SystemException(get_string('blocktypemissingconfigform', 'error', $instance->get('blocktype')));
}
/**
* 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();
}
/**
* Blocktype plugins can implement this to perform custom pieform
* validation, should they need it
*/
public static function instance_config_validate(Pieform $form, $values) { }
/**
* Most blocktype plugins will attach to artefacts.
* 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.
* 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.
*
* @return array ids of artefacts in this block instance
*/
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;
}
/**
* 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
*/
public static function has_instance_config() {
return false;
}
public static function category_title_from_name($name) {
$title = get_string('blocktypecategory.'. $name, 'view');
if (strpos($title, '[[') !== 0) {
return $title;
}
// else we're an artefact
return get_string('pluginname', 'artefact.' . $name);
}
public static function category_description_from_name($name) {
$description = get_string('blocktypecategorydesc.'. $name, 'view');
return $description;
}
public static function get_blocktypes_for_category($category, View $view) {
$sql = 'SELECT bti.name, bti.artefactplugin
FROM {blocktype_installed} bti
JOIN {blocktype_installed_category} btic ON btic.blocktype = bti.name
JOIN {blocktype_installed_viewtype} btiv ON btiv.blocktype = bti.name
WHERE btic.category = ? AND bti.active = 1 AND btiv.viewtype = ?
ORDER BY btic.sortorder, bti.name';
if (!$bts = get_records_sql_array($sql, array($category, $view->get('type')))) {
return false;
}
$blocktypes = array();
if (function_exists('local_get_allowed_blocktypes')) {
$localallowed = local_get_allowed_blocktypes($category, $view);
}
foreach ($bts as $bt) {
$namespaced = blocktype_single_to_namespaced($bt->name, $bt->artefactplugin);
if (isset($localallowed) && is_array($localallowed) && !in_array($namespaced, $localallowed)) {
continue;
}
safe_require('blocktype', $namespaced);
// Note for later: this is Blocktype::allowed_in_view, which
// returns true if the blocktype should be insertable into the
// given view.
// e.g. for blogs it returns false when view owner is not set,
// because blogs can't be inserted into group views.
// This could be different from whether a blockinstance is allowed
// to be copied into a View (see the other place in this file where
// allowed_in_view is called)
//
// 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
// View::get_category_data
if (call_static_method(generate_class_name('blocktype', $namespaced), 'allowed_in_view', $view)) {
$blocktypes[] = array(
'name' => $bt->name,
'title' => call_static_method(generate_class_name('blocktype', $namespaced), 'get_title'),
'description' => call_static_method(generate_class_name('blocktype', $namespaced), 'get_description'),
'singleonly' => call_static_method(generate_class_name('blocktype', $namespaced), 'single_only'),
'artefactplugin' => $bt->artefactplugin,
'thumbnail_path' => get_config('wwwroot') . 'thumb.php?type=blocktype&bt=' . $bt->name . ((!empty($bt->artefactplugin)) ? '&ap=' . $bt->artefactplugin : ''),
);
}
}
return $blocktypes;
}
/**
* Takes config data for an existing blockinstance of this class and rewrites it so
* it can be used to configure a block instance being put in a new view
*
* 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
* example, who will own it.
*
* As an example - when the profile information blocktype is copied, we
* want it so that all the fields that were configured previously are
* pointing to the new owner's versions of those fields.
*
* The base method clears out any artefact IDs that are set.
*
* @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 array $configdata The configuration data for the old blocktype
* @return array The new configuration data.
*/
public static function rewrite_blockinstance_config(View $view, $configdata) {
if (isset($configdata['artefactid'])) {
$configdata['artefactid'] = null;
}
if (isset($configdata['artefactids'])) {
$configdata['artefactids'] = array();
}
return $configdata;
}
/**
* 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;
}
/**
* 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
}
/*
* The copy_type of a block affects how it should be copied when its view gets copied.
* 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.
*
* If the old owner and the new owner are the same, reference is always used.
* If a block contains no artefacts, reference and full are equivalent.
*/
public static function default_copy_type() {
return 'shallow';
}
/**
* Whether this blocktype is allowed in the given View.
*
* 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
* of owner.
*
* For example, the 'profile information' blocktype makes no sense in a
* group View.
*
* Of course, blocktypes could implement stranger rules - e.g. only allow
* when the view has 'ponies' in its description (BTW: such blocktypes
* would be totally awesome).
*
* @param View The View to check
* @return boolean Whether blocks of this blocktype are allowed in the
* given view.
*/
public static function allowed_in_view(View $view) {
return true;
}
/**
* Given a block instance, returns a hash with enough information so that
* we could reconstruct it if given this information again.
*
* Import/Export routines can serialise this information appropriately, and
* unserialise it on the way back in, where it is passed to {@link
* 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) {
$configdata = $bi->get('configdata');
if (is_array($configdata)) {
// Unset a bunch of stuff that we don't want to export. These fields
// weren't being cleaned up before blockinstances were being saved
// 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;
}
/**
* Exports configuration data the format required for Leap2A export.
*
* 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;
}
/**
* Creates a block instance from a given configuration.
*
* The configuration is whatever was generated by {@link
* export_blockinstance_config()}. This method doesn't have to worry about
* setting the block title, or the position in the View.
*
* @param array $biconfig The config to use to create the blockinstance
* @param array $viewconfig The configuration for the view being imported
* @return BlockInstance The new block instance
*/
public static function import_create_blockinstance(array $biconfig, array $viewconfig) {
$bi = new BlockInstance(0,
array(
'blocktype' => $biconfig['type'],
'configdata' => $biconfig['config'],
)
);
return $bi;
}
/**
* 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;
}
/**
* 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;
}
}
abstract class SystemBlockType extends PluginBlockType {
public static function get_artefacts(BlockInstance $instance) {
return array();
}
public final static function artefactchooser_element($default=null) {
}
}
class BlockInstance {
const RETRACTABLE_NO = 0;
const RETRACTABLE_YES = 1;
const RETRACTABLE_RETRACTED = 2;
private $id;
private $blocktype;
private $artefactplugin;
private $title;
private $configdata = array();
private $dirty;
private $view;
private $view_obj;
private $row;
private $column;
private $order;
private $canmoveleft;
private $canmoveright;
private $canmoveup;
private $canmovedown;
private $maxorderincolumn;
private $artefacts = array();
private $temp = array();
public function __construct($id=0, $data=null) {
if (!empty($id)) {
if (empty($data)) {
if (!$data = get_record('block_instance','id',$id)) {
// 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
// from lang/*/view.php where there is already one for it
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;
}
}
$this->artefactplugin = blocktype_artefactplugin($this->blocktype);
}
public function get($field) {
if (!property_exists($this, $field)) {
throw new InvalidArgumentException("Field $field wasn't found in class " . get_class($this));
}
if ($field == 'configdata') {
// make sure we unserialise it
if (!is_array($this->configdata)) {
$this->configdata = unserialize($this->configdata);
}
}
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(
'block_instance',
'max("order")',
'view', $this->view, 'column', $this->column);
}
}
return $this->{$field};
}
public function set($field, $value) {
if (property_exists($this, $field)) {
if ($field == 'configdata') {
$value = serialize($value);
}
if ($this->{$field} !== $value) {
// only set it to dirty if it's changed
$this->dirty = true;
$this->{$field} = $value;
}
return true;
}
throw new ParamOutOfRangeException("Field $field wasn't found in class " . get_class($this));
}
// 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);
if (!$USER->can_view_artefact($file)) {
// bail out now as at least one attachment is bad
return false;
}
}
}
else {
$file = artefact_instance_from_id($id);
if (!$USER->can_view_artefact($file)) {
return false;
}
}
return true;
}
public function instance_config_store(Pieform $form, $values) {
global $SESSION, $USER;
// Destroy form values we don't care about
unset($values['sesskey']);
unset($values['blockinstance']);
unset($values['action_configureblockinstance_id_' . $this->get('id')]);
unset($values['blockconfig']);
unset($values['id']);
unset($values['change']);
unset($values['new']);
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;
}
}
// 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;
if (!empty($values['artefactid'])) {
$badattachment = !$this->verify_attachment_permissions($values['artefactid']);
}
if (!empty($values['artefactids'])) {
$badattachment = !$this->verify_attachment_permissions($values['artefactids']);
}
if ($badattachment) {
$result['message'] = get_string('unrecoverableerror', 'error');
$form->set_error(null, $result['message']);
$form->reply(PIEFORM_ERR, $result);
exit();
}
$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,
);
if (is_callable(array(generate_class_name('blocktype', $this->get('blocktype')), 'instance_config_save'))) {
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);
}
}
$title = (isset($values['title'])) ? $values['title'] : '';
unset($values['title']);
// 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']);
$this->set('configdata', $values);
$this->set('title', $title);
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));
}
$this->commit();
$result = array(
'error' => false,
'message' => get_string('blockinstanceconfiguredsuccessfully', 'view'),
'data' => $rendered,
'blockid' => $this->get('id'),
'viewid' => $this->get('view'),
'goto' => $redirect,
);
// 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),
);
}
}
$form->reply(PIEFORM_OK, $result);
}
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 '';
}
/**
* Builds the HTML for the block, inserting the blocktype content at the
* appropriate place
*
* @param bool $configure Whether to render the block instance in configure
* mode
* @return array Array with two keys: 'html' for raw html, 'javascript' for
* javascript to run
*/
public function render_editing($configure=false, $new=false, $jsreply=false) {
safe_require('blocktype', $this->get('blocktype'));
$movecontrols = array();
$blocktypeclass = generate_class_name('blocktype', $this->get('blocktype'));
try {
$title = $this->get_title();
}
catch (NotFoundException $e) {
log_debug('Cannot render block title. Original error follows: ' . $e->getMessage());
$title = get_string('notitle', 'view');
}
if ($configure) {
list($content, $js, $css) = array_values($this->build_configure_form($new));
}
else {
try {
$content = call_static_method(generate_class_name('blocktype', $this->get('blocktype')), 'render_instance', $this, true);
$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;
$css = '';
}
catch (NotFoundException $e) {
// 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());
$content = '';
$js = '';
}
if (!defined('JSON') && !$jsreply) {
if ($this->get('canmoveleft')) {
$movecontrols[] = array(
'column' => $this->get('column') - 1,
'order' => $this->get('order'),
'title' => $title == '' ? get_string('movethisblockleft', 'view') : get_string('moveblockleft', 'view', "'$title'"),
'arrow' => "icon icon-long-arrow-left",
'dir' => 'left',
);
}
if ($this->get('canmovedown')) {
$movecontrols[] = array(
'column' => $this->get('column'),
'order' => $this->get('order') + 1,
'title' => $title == '' ? get_string('movethisblockdown', 'view') : get_string('moveblockdown', 'view', "'$title'"),
'arrow' => 'icon icon-long-arrow-down',
'dir' => 'down',
);
}
if ($this->get('canmoveup')) {
$movecontrols[] = array(
'column' => $this->get('column'),
'order' => $this->get('order') - 1,
'title' => $title == '' ? get_string('movethisblockup', 'view') : get_string('moveblockup', 'view', "'$title'"),
'arrow' => 'icon icon-long-arrow-up',
'dir' => 'up',
);
}
if ($this->get('canmoveright')) {
$movecontrols[] = array(
'column' => $this->get('column') + 1,
'order' => $this->get('order'),
'title' => $title == '' ? get_string('movethisblockright', 'view') : get_string('moveblockright', 'view', "'$title'"),
'arrow' => 'icon icon-long-arrow-right',
'dir' => 'right',
);
}
}
}
$configtitle = $title == '' ? call_static_method($blocktypeclass, 'get_title') : $title;
$smarty = smarty_core();
$id = $this->get('id');
$smarty->assign('id', $id);
$smarty->assign('viewid', $this->get('view'));
$smarty->assign('title', $title);
$smarty->assign('row', $this->get('row'));
$smarty->assign('column', $this->get('column'));
$smarty->assign('order', $this->get('order'));
$smarty->assign('movecontrols', $movecontrols);
$smarty->assign('configurable', call_static_method($blocktypeclass, 'has_instance_config'));
$smarty->assign('configure', $configure); // Used by the javascript to rewrite the block, wider.
$smarty->assign('configtitle', $configtitle);
$smarty->assign('content', $content);
$smarty->assign('javascript', defined('JSON'));
$smarty->assign('strnotitle', get_string('notitle', 'view'));
$smarty->assign('strmovetitletext', $title == '' ? get_string('movethisblock', 'view') : get_string('moveblock', 'view', "'$title'"));
$smarty->assign('strconfigtitletext', $title == '' ? get_string('configurethisblock1', 'view', $id) : get_string('configureblock1', 'view', "'$title'", $id));
$smarty->assign('strremovetitletext', $title == '' ? get_string('removethisblock1', 'view', $id) : get_string('removeblock1', 'view', "'$title'", $id));
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');
}
}
}
if (is_array($css)) {
$css = array_unique($css);
}
return array('html' => $smarty->fetch('view/blocktypecontainerediting.tpl'), 'javascript' => $js, 'pieformcss' => $css);
}
/**
* 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
* @return the rendered block
*/
public function render_viewing($exporting=false) {
if (!safe_require_plugin('blocktype', $this->get('blocktype'))) {
return;
}
$smarty = smarty_core();
$classname = generate_class_name('blocktype', $this->get('blocktype'));
if (get_config('ajaxifyblocks') && call_static_method($classname, 'should_ajaxify') && $exporting === false) {
$content = '';
$smarty->assign('loadbyajax', true);
}
else {
$smarty->assign('loadbyajax', false);
try {
$content = call_static_method($classname, 'render_instance', $this);
}
catch (NotFoundException $e) {
// 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());
$content = '';
}
}
$smarty->assign('id', $this->get('id'));
$smarty->assign('blocktype', $this->get('blocktype'));
// hide the title if required and no content is present
if (call_static_method($classname, 'hide_title_on_empty_content')
&& !trim($content)) {
return;
}
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);
// If this block is for just one artefact, we set the title of the
// block to be a link to view more information about that artefact
$configdata = $this->get('configdata');
if (!empty($configdata['artefactid'])) {
if (call_static_method($classname, 'has_title_link')) {
$smarty->assign('viewartefacturl', get_config('wwwroot') . 'artefact/artefact.php?artefact='
. $configdata['artefactid'] . '&view=' . $this->get('view') . '&block=' . $this->get('id'));
}
}
if (method_exists($classname, 'feed_url')) {
$smarty->assign('feedlink', call_static_method($classname, 'feed_url', $this));
}
$smarty->assign('link', call_static_method($classname, 'get_link', $this));
$smarty->assign('content', $content);
if (isset($configdata['retractable']) && $title) {
$smarty->assign('retractable', $configdata['retractable']);
if (isset($configdata['retractedonload'])) {
$smarty->assign('retractedonload', $configdata['retractedonload']);
}
}
return $smarty->fetch('view/blocktypecontainerviewing.tpl');
}