collection.php 63.8 KB
Newer Older
1
2
3
4
5
6
<?php
/**
 *
 * @package    mahara
 * @subpackage core
 * @author     Catalyst IT Ltd
7
8
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL version 3 or later
 * @copyright  For copyright information on Mahara, please see the README file distributed with this software.
9
10
11
12
13
 *
 */

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

14
15
16
17
18
19
class Collection {

    private $id;
    private $name;
    private $description;
    private $owner;
20
21
    private $group;
    private $institution;
22
23
    private $mtime;
    private $ctime;
24
    private $navigation;
25
26
27
    private $submittedgroup;
    private $submittedhost;
    private $submittedtime;
28
    private $submittedstatus;
29
    private $views;
30
    private $tags;
31
    private $framework;
32
    private $coverimage;
33
    private $progresscompletion;
34

35
36
37
38
    const UNSUBMITTED = 0;
    const SUBMITTED = 1;
    const PENDING_RELEASE = 2;

39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
    public function __construct($id=0, $data=null) {

        if (!empty($id)) {
            $tempdata = get_record('collection','id',$id);
            if (empty($tempdata)) {
                throw new CollectionNotFoundException("Collection with id $id not found");
            }
            if (!empty($data)) {
                $data = array_merge((array)$tempdata, $data);
            }
            else {
                $data = $tempdata; // use what the database has
            }
            $this->id = $id;
        }
        else {
            $this->ctime = time();
            $this->mtime = time();
        }
58

59
60
61
62
63
64
65
        if (empty($data)) {
            $data = array();
        }
        foreach ((array)$data as $field => $value) {
            if (property_exists($this, $field)) {
                $this->{$field} = $value;
            }
66
        }
67
68
69
70
        if (empty($this->group) && empty($this->institution) && empty($this->owner)) {
            global $USER;
            $this->owner = $USER->get('id');
        }
71
72
    }

73
74
75
76
    public function get($field) {
        if (!property_exists($this, $field)) {
            throw new InvalidArgumentException("Field $field wasn't found in class " . get_class($this));
        }
77
78
79
        if ($field == 'tags') {
            return $this->get_tags();
        }
80
81
82
        if ($field == 'views') {
            return $this->views();
        }
83
84
85
        if ($field == 'coverimage') {
            return $this->get_coverimage();
        }
86
87
        return $this->{$field};
    }
88

89
90
91
92
93
94
95
96
    public function set($field, $value) {
        if (property_exists($this, $field)) {
            $this->{$field} = $value;
            $this->mtime = time();
            return true;
        }
        throw new InvalidArgumentException("Field $field wasn't found in class " . get_class($this));
    }
97

98
    /**
99
     * Helper function to create or update a Collection from the supplied data.
100
101
     *
     * @param array $data
102
     * @return collection           The newly created/updated collection
103
104
     */
    public static function save($data) {
105
106
        if (array_key_exists('id', $data)) {
            $id = $data['id'];
107
            $state = 'updatecollection';
108
109
110
        }
        else {
            $id = 0;
111
            $state = 'createcollection';
112
113
        }
        $collection = new Collection($id, $data);
114
        $collection->set('mtime', time());
115
        $collection->commit();
116
117
        $views = $collection->get('views');
        $viewids = array();
118
119
120
121
        if (!empty($views)) {
            foreach ($views['views'] as $view) {
                $viewids[] = $view->view;
            }
122
123
124
        }
        $eventdata = array('id' => $collection->get('id'),
                           'name' => $collection->get('name'),
125
                           'eventfor' => 'collection',
126
127
                           'viewids' => $viewids);
        handle_event($state, $eventdata);
128
        return $collection; // return newly created Collections id
129
    }
130

131
132
133
134
135
    /**
     * Deletes a Collection
     *
     */
    public function delete() {
136
        $viewids = get_column('collection_view', 'view', 'collection', $this->id);
137
        db_begin();
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158

        // Delete navigation blocks within the collection's views which point at this collection.
        if ($viewids) {
            $values = $viewids;
            $values[] = 'navigation';
            $navigationblocks = get_records_select_assoc(
                'block_instance', 'view IN (' . join(',', array_fill(0, count($viewids), '?')) . ') AND blocktype = ?',
                $values
            );
            if ($navigationblocks) {
                safe_require('blocktype', 'navigation');
                foreach ($navigationblocks as $b) {
                    $bi = new BlockInstance($b->id, $b);
                    $configdata = $bi->get('configdata');
                    if (isset($configdata['collection']) && $configdata['collection'] == $this->id) {
                        $bi->delete();
                    }
                }
            }
        }

Stacey Walker's avatar
Stacey Walker committed
159
        delete_records('collection_view','collection',$this->id);
160
        delete_records('tag', 'resourcetype', 'collection', 'resourceid', $this->id);
161
162
163
        if (is_plugin_active('lti', 'module')) {
            delete_records('lti_assessment_submission', 'collectionid', $this->id);
        }
164
        delete_records('existingcopy', 'collection', $this->id);
165
        delete_records('collection', 'id', $this->id);
166
167
        // Delete any submission history
        delete_records('module_assessmentreport_history', 'event', 'collection', 'itemid', $this->id);
168
169
170
171
172
173

        // Secret url records belong to the collection, so remove them from the view.
        // @todo: add user message to whatever calls this.
        if ($viewids) {
            delete_records_select('view_access', 'view IN (' . join(',', $viewids) . ') AND token IS NOT NULL');
        }
174
175
        $data = array('id' => $this->id,
                      'name' => $this->name,
176
                      'eventfor' => 'collection',
177
178
                      'viewids' => $viewids);
        handle_event('deletecollection', $data);
179
180
181
        db_commit();
    }

182
183
184
185
    /**
     * This method updates the contents of the collection table only.
     */
    public function commit() {
186
        global $USER;
187

188
        $fordb = new stdClass();
189
190
        foreach (get_object_vars($this) as $k => $v) {
            $fordb->{$k} = $v;
191
            if (in_array($k, array('mtime', 'ctime', 'submittedtime')) && !empty($v)) {
192
193
194
                $fordb->{$k} = db_format_timestamp($v);
            }
        }
195

196
        db_begin();
197

198
199
200
201
202
        // if id is not empty we are editing an existing collection
        if (!empty($this->id)) {
            update_record('collection', $fordb, 'id');
        }
        else {
203
204
            $id = insert_record('collection', $fordb, 'id', true);
            if ($id) {
205
                $this->set('id', $id);
206
            }
207
        }
208

209
        if (isset($this->tags)) {
210
211
212
213
214
215
216
217
218
219
220
221
222
223
            if ($this->group) {
                $ownertype = 'group';
                $ownerid = $this->group;
            }
            else if ($this->institution) {
                $ownertype = 'institution';
                $ownerid = $this->institution;
            }
            else {
                $ownertype = 'user';
                $ownerid = $this->owner;
            }
            delete_records('tag', 'resourcetype', 'collection', 'resourceid', $this->get('id'));
            $tags = check_case_sensitive($this->get_tags(), 'tag');
224
            foreach (array_unique($tags) as $tag) {
225
226
                //truncate the tag before insert it into the database
                $tag = substr($tag, 0, 128);
227
                $tag = check_if_institution_tag($tag);
228
229
230
231
232
233
234
235
236
237
238
                insert_record('tag',
                    (object)array(
                        'resourcetype' => 'collection',
                        'resourceid' => $this->get('id'),
                        'ownertype' => $ownertype,
                        'ownerid' => $ownerid,
                        'tag' => $tag,
                        'ctime' => db_format_timestamp(time()),
                        'editedby' => $USER->get('id'),
                    )
                );
239
240
241
            }
        }

242
243
        db_commit();
    }
244

245
246
247
248
    /**
     * Generates a name for a newly created Collection
     */
    private static function new_name($name, $ownerdata) {
249
250
251
252
        $extText = get_string('version.', 'mahara');
        $tempname = preg_split('/ '. $extText . '[0-9]$/', $name);
        $name = $tempname[0];

253
254
255
256
257
        $taken = get_column_sql('
            SELECT name
            FROM {collection}
            WHERE ' . self::owner_sql($ownerdata) . "
                AND name LIKE ? || '%'", array($name));
258
259
260

        $ext = '';
        $i = 1;
261
262
        if ($taken) {
            while (in_array($name . $ext, $taken)) {
263
                $ext = ' ' . $extText . ++$i;
264
265
266
267
268
            }
        }
        return $name . $ext;
    }

269
270
271
272
273
274
275
276
277
278
279
280
281
    /**
     * Copy the cover image of the collection template
     *
     * @param Collection $template the collection template
     * @param array $collectiondata contains data about owner, group, institution of the copy
     * @return int new image artefact id
     */
    private static function copy_setting_coverimage(Collection $template, $collectiondata) {
        safe_require('artefact', 'file');
        $coverimageid = $template->get('coverimage');
        $owner = isset($collectiondata['owner']) ? $collectiondata['owner'] : null;
        $group = isset($collectiondata['group']) ? $collectiondata['group'] : null;
        $institution = isset($collectiondata['institution']) ? $collectiondata['institution'] : null;
282
283
284
285
286
287
288

        // if the owner of the template and the copy are the same, use the same file
        $sameowner = ($template->get('owner') && $template->get('owner') == $owner) ||
            ($template->get('group') && $template->get('group') == $group) ||
            ($template->get('institution') && $template->get('institution') == $institution);
        if ($sameowner) return $coverimageid;

289
290
291
292
293
294
295
296
297
298
        if ($coverimageid) {
            try {
                $a = artefact_instance_from_id($coverimageid);
                if ($a instanceof ArtefactTypeImage) {
                    $newid = $a->copy_for_new_owner(
                      $owner,
                      $group,
                      $institution
                    );
                }
299
300
301
302
303
304
305
306
307
                // move to cover image forlder
                $userobj = null;
                if ($owner) {
                    $userobj = new User();
                    $userobj->find_by_id($owner);
                }
                $newa = artefact_instance_from_id($newid);
                $folderid = ArtefactTypeImage::get_coverimage_folder($userobj, $group, $institution);
                $newa->move($folderid);
308
309
310
311
312
313
314
315
316
                return $newid;
            }
            catch (Exception $e) {
                return null;
            }
        }
        return null;
    }

317
318
319
320
    /**
     * Creates a Collection for the given user, based off a given template and other
     * Collection information supplied.
     *
321
322
     * Will set a default name of 'Copy of $collectiontitle' if name is not
     * specified in $collectiondata and $titlefromtemplate == false.
323
324
325
326
327
328
     *
     * @param array $collectiondata Contains information of the old collection, submitted in form
     * @param int $templateid The ID of the Collection to copy
     * @param int $userid     The user who has issued the command to create the
     *                        collection.
     * @param int $checkaccess Whether to check that the user can see the collection before copying it
329
330
     * @param boolean $titlefromtemplate  Title of new collection or view will be exactly copied from the template
     *
331
332
333
334
335
336
     * @return array A list consisting of the new collection, the template collection and
     *               information about the copy - i.e. how many blocks and
     *               artefacts were copied
     * @throws SystemException under various circumstances, see the source for
     *                         more information
     */
337
    public static function create_from_template($collectiondata, $templateid, $userid=null, $checkaccess=true, $titlefromtemplate=false) {
338
339
340
341
342
343
344
345
346
347
348
349
        require_once(get_config('libroot') . 'view.php');
        global $SESSION;

        if (is_null($userid)) {
            global $USER;
            $userid = $USER->get('id');
        }

        db_begin();

        $colltemplate = new Collection($templateid);

350
        $data = new stdClass();
351
352
353
354
355
356
357
358
359
360
361
362
363
        // Set a default name if one wasn't set in $collectiondata
        if ($titlefromtemplate) {
            $data->name = $colltemplate->get('name');
        }
        else if (!isset($collectiondata['name'])) {
            $desiredname = $colltemplate->get('name');
            if (get_config('renamecopies')) {
                $desiredname = get_string('Copyof', 'mahara', $desiredname);
            }
            $data->name = self::new_name($desiredname, (object)$collectiondata);
        }
        else {
            $data->name = $collectiondata['name'];
364
        }
365
        $data->description = $colltemplate->get('description');
366
        $data->coverimage = self::copy_setting_coverimage($colltemplate, $collectiondata);
367
        $data->tags = $colltemplate->get('tags');
368
        $data->navigation = $colltemplate->get('navigation');
369
370
371
372
373
374
375
376
377
378
379
380
        if (!empty($collectiondata['group'])) {
            $data->group = $collectiondata['group'];
        }
        else if (!empty($collectiondata['institution'])) {
            $data->institution = $collectiondata['institution'];
        }
        else if (!empty($collectiondata['owner'])) {
            $data->owner = $collectiondata['owner'];
        }
        else {
            $data->owner = $userid;
        }
Robert Lyon's avatar
Robert Lyon committed
381
        $data->framework = $colltemplate->get('framework');
382
        $data->submittedstatus = 0;
383

384
        $data->progresscompletion = $colltemplate->get('progresscompletion');
385
386
387
388
389
        $collection = self::save($data);

        $numcopied = array('pages' => 0, 'blocks' => 0, 'artefacts' => 0);

        $views = $colltemplate->get('views');
390
391
392
        if (empty($views)) {
            $views['views'] = array();
        }
393
        $copyviews = array();
394
        $evidenceviews = array();
395
        $artefactcopies = array();
396
        foreach ($views['views'] as $v) {
397
398
399
400
401
402
403
            $values = array(
                'new' => true,
                'owner' => isset($data->owner) ? $data->owner : null,
                'group' => isset($data->group) ? $data->group : null,
                'institution' => isset($data->institution) ? $data->institution : null,
                'usetemplate' => $v->view
            );
404
            list($view, $template, $copystatus) = View::create_from_template($values, $v->view, $userid, $checkaccess, $titlefromtemplate, $artefactcopies);
405
406
407
408
            // Check to see if we need to re-map any framework evidence
            if (!empty($data->framework) && $userid == $data->owner && count_records('framework_evidence', 'view', $v->view)) {
                $evidenceviews[$v->view] = $view->get('id');
            }
409
410
411
412
413
414
415
416
417
418
419
420
            if (isset($copystatus['quotaexceeded'])) {
                $SESSION->clear('messages');
                return array(null, $colltemplate, array('quotaexceeded' => true));
            }
            $copyviews['view_' . $view->get('id')] = true;
            $numcopied['blocks'] += $copystatus['blocks'];
            $numcopied['artefacts'] += $copystatus['artefacts'];
        }
        $numcopied['pages'] = count($views['views']);

        $collection->add_views($copyviews);

421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
        // Update all the navigation blocks referring to this collection
        if ($viewids = get_column('collection_view', 'view', 'collection', $collection->get('id'))) {
            $navblocks = get_records_select_array(
                'block_instance',
                'view IN (' . join(',', array_fill(0, count($viewids), '?')) . ") AND blocktype = 'navigation'",
                $viewids
            );

            if ($navblocks) {
                safe_require('blocktype', 'navigation');
                foreach ($navblocks as $b) {
                    $bi = new BlockInstance($b->id, $b);
                    $configdata = $bi->get('configdata');
                    if (isset($configdata['collection']) && $configdata['collection'] == $templateid) {
                        $bi->set('configdata', array('collection' => $collection->get('id')));
                        $bi->commit();
                    }
                }
            }
        }
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
        // If there are views with framework evidence to re-map
        if (!empty($evidenceviews)) {
            // We need to get how the old views/artefacts/blocks/evidence fit together
            $evidences = get_records_sql_array('
                SELECT va.view, va.artefact, va.block, fe.*
                FROM {view} v
                JOIN {view_artefact} va ON va.view = v.id
                JOIN {artefact} a ON a.id = va.artefact
                JOIN {framework_evidence} fe ON fe.view = v.id
                WHERE v.id IN (' . join(',', array_keys($evidenceviews)) . ')
                AND a.id IN (' . join(',', array_keys($artefactcopies)) . ')
                AND fe.annotation = va.block', array());
            $newartefactcopies = array();
            foreach ($artefactcopies as $ac) {
                $newartefactcopies[$ac->newid] = 1;
            }
            // And get how the new views/artefacts/blocks fit together
            $newblocks = get_records_sql_assoc('
                SELECT va.artefact, va.view, va.block
                FROM {view} v
                JOIN {view_artefact} va ON va.view = v.id
                JOIN {artefact} a ON a.id = va.artefact
                WHERE v.id IN (' . join(',', array_values($evidenceviews)) . ')
                AND a.id IN (' . join(',', array_keys($newartefactcopies)) . ')
                AND artefacttype = ?', array('annotation'));

            foreach ($evidences as $evidence) {
                if (key_exists($evidence->artefact, $artefactcopies) && key_exists($artefactcopies[$evidence->artefact]->newid, $newartefactcopies)) {
                    $newartefact = $artefactcopies[$evidence->artefact]->newid;
                    $newevidence = new stdClass();
                    $newevidence->view = $newblocks[$newartefact]->view;
                    $newevidence->artefact = $newartefact;
                    $newevidence->annotation = $newblocks[$newartefact]->block;
                    $newevidence->framework = $evidence->framework;
                    $newevidence->element = $evidence->element;
                    $newevidence->state = 0;
                    $newevidence->reviewer = null;
                    $newevidence->ctime = $evidence->ctime;
                    $newevidence->mtime = $evidence->mtime;
                    insert_record('framework_evidence', $newevidence);
                }
            }
        }
484

485
486
487
488
489
490
491
492
493
        db_commit();

        return array(
            $collection,
            $colltemplate,
            $numcopied,
        );
    }

494
    /**
495
     * Returns a list of the current user, group, or institution collections
496
497
498
     *
     * @param offset current page to display
     * @param limit how many collections to display per page
499
500
     * @param groupid current group ID
     * @param institutionname current institution name
501
502
     * @return array (count: integer, data: array, offset: integer, limit: integer)
     */
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
    public static function get_mycollections_data($offset=0, $limit=10, $owner=null, $groupid=null, $institutionname=null) {
        if (!empty($groupid)) {
            $wherestm = '"group" = ?';
            $values = array($groupid);
            $count  = count_records('collection', 'group', $groupid);
        }
        else if (!empty($institutionname)) {
            $wherestm = 'institution = ?';
            $values = array($institutionname);
            $count  = count_records('collection', 'institution', $institutionname);
        }
        else if (!empty($owner)) {
            $wherestm = 'owner = ?';
            $values = array($owner);
            $count  = count_records('collection', 'owner', $owner);
        }
        else {
            $count = 0;
        }
        $data = array();
        if ($count > 0) {
            $data = get_records_sql_assoc("
525
526
527
528
529
530
531
                SELECT
                    c.id,
                    c.description,
                    c.name,
                    c.submittedgroup,
                    c.submittedhost,
                    c.submittedtime,
532
                    c.framework,
533
                    (SELECT COUNT(*) FROM {collection_view} cv WHERE cv.collection = c.id) AS numviews
534
                FROM {collection} c
535
536
537
538
                WHERE " . $wherestm .
                " ORDER BY c.name, c.ctime, c.id ASC
                ", $values, $offset, $limit);
        }
539

540
        self::add_submission_info($data);
541
        self::add_framework_urls($data);
542

543
        $result = (object) array(
544
545
            'count'  => $count,
            'data'   => $data,
546
547
548
549
550
            'offset' => $offset,
            'limit'  => $limit,
        );
        return $result;
    }
551

552
    private static function add_submission_info(&$data) {
553
554
555
        global $CFG;
        require_once($CFG->docroot . 'lib/group.php');

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
        if (empty($data)) {
            return;
        }

        $records = get_records_sql_assoc('
            SELECT c.id, c.submittedgroup, c.submittedhost, ' . db_format_tsfield('submittedtime') . ',
                   sg.name AS groupname, sg.urlid, sh.name AS hostname
              FROM {collection} c
         LEFT JOIN {group} sg ON c.submittedgroup = sg.id
         LEFT JOIN {host} sh ON c.submittedhost = sh.wwwroot
             WHERE c.id IN (' . join(',', array_fill(0, count($data), '?')) . ')
               AND (c.submittedgroup IS NOT NULL OR c.submittedhost IS NOT NULL)',
            array_keys($data)
        );

        if (empty($records)) {
            return;
        }

        foreach ($records as $r) {
            if (!empty($r->submittedgroup)) {
                $groupdata = (object) array(
                    'id'    => $r->submittedgroup,
                    'name'  => $r->groupname,
                    'urlid' => $r->urlid,
                    'time'  => $r->submittedtime,
                );
                $groupdata->url = group_homepage_url($groupdata);
                $data[$r->id]->submitinfo = $groupdata;
            }
            else if (!empty($r->submittedhost)) {
                $data[$r->id]->submitinfo = (object) array(
                    'name' => $r->hostname,
                    'url'  => $r->submittedhost,
                    'time'  => $r->submittedtime,
                );
            }
        }
    }

596
597
598
599
    /**
    * Gets the fields for the new/edit collection form
    * - populates the fields with collection data if it is an edit
    *
600
    * @param array $collection
601
602
    * @return array $elements
    */
603
    public function get_collectionform_elements() {
604
605
606
607
608
        safe_require('artefact', 'file');
        global $USER;
        $folder = ArtefactTypeImage::get_coverimage_folder($USER, $this->group, $this->institution);

        $highlight = array(0);
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
        $elements = array(
            'name' => array(
                'type' => 'text',
                'defaultvalue' => null,
                'title' => get_string('name', 'collection'),
                'size' => 30,
                'rules' => array(
                    'required' => true,
                ),
            ),
            'description' => array(
                'type'  => 'textarea',
                'rows' => 10,
                'cols' => 50,
                'resizable' => false,
                'defaultvalue' => null,
                'title' => get_string('description', 'collection'),
            ),
627
628
629
630
631
632
            'tags'        => array(
                'type'         => 'tags',
                'title'        => get_string('tags'),
                'description'  => get_string('tagsdescprofile'),
                'defaultvalue' => null,
                'help'         => true,
633
                'institution'  =>  $this->institution,
634
            ),
635
            'navigation' => array(
636
                'type'  => 'switchbox',
637
638
639
640
                'title' => get_string('viewnavigation','collection'),
                'description' => get_string('viewnavigationdesc','collection'),
                'defaultvalue' => 1,
            ),
641
642
643
644
645
646
            'coverimage' => array(
                'type'         => 'filebrowser',
                'title'        => get_string('coverimage', 'view'),
                'description'  => get_string('coverimagedescription', 'view'),
                'folder'       => $folder,
                'highlight'    => $highlight,
647
                'accept'       => 'image/*',
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
                'institution'  => $this->institution,
                'group'        => $this->group,
                'page'         => '',
                'filters'      => array(
                     'artefacttype' => array('image'),
                ),
                'config'       => array(
                    'upload'          => true,
                    'uploadagreement' => get_config_plugin('artefact', 'file', 'uploadagreement'),
                    'resizeonuploaduseroption' => get_config_plugin('artefact', 'file', 'resizeonuploaduseroption'),
                    'resizeonuploaduserdefault' => $USER->get_account_preference('resizeonuploaduserdefault'),
                    'createfolder'    => false,
                    'edit'            => false,
                    'select'          => true,
                    'selectone'       => true,
                ),
                'selectlistcallback' => 'artefact_get_records_by_id',
                'selectcallback'     => 'add_view_coverimage',
                'unselectcallback'   => 'delete_view_coverimage',
            ),
668
        );
669
        if ($frameworks = $this->get_available_frameworks()) {
670
671
672
673
674
675
676
677
678
679
680
681
682
            $options = array('' => get_string('noframeworkselected', 'module.framework'));
            foreach ($frameworks as $framework) {
                $options[$framework->id] = $framework->name;
            }
            $elements['framework'] = array(
                'type' => 'select',
                'title' => get_string('Framework', 'module.framework'),
                'options' => $options,
                'defaultvalue' => $this->framework,
                'width' => '280px',
                'description' => get_string('frameworkdesc', 'module.framework'),
            );
        }
683

684
        if ($this->can_have_progresscompletion()) {
685
686
687
688
689
690
691
692
693
            $elements['progresscompletion'] = array(
                'type'  => 'switchbox',
                'title' => get_string('progresscompletion', 'admin'),
                'description' => get_string('progresscompletiondesc', 'collection'),
                'defaultvalue' => 0,
            );
        }


694
        // populate the fields with the existing values if any
695
        if (!empty($this->id)) {
696
            foreach ($elements as $k => $element) {
697
698
699
                if ($k === 'tags') {
                    $elements[$k]['defaultvalue'] = $this->get_tags();
                }
700
701
702
                else if ($k == 'coverimage') {
                    $elements[$k]['defaultvalue'] = ($this->get('coverimage') ? array($this->get('coverimage')) : null);
                }
703
704
705
                else {
                    $elements[$k]['defaultvalue'] = $this->$k;
                }
706
707
708
            }
            $elements['id'] = array(
                'type' => 'hidden',
709
                'value' => $this->id,
710
            );
711
712
713
714
715
716
717
718
719
720
721
722
723
724
        }
        if (!empty($this->group)) {
            $elements['group'] = array(
                'type' => 'hidden',
                'value' => $this->group,
            );
        }
        else if (!empty($this->institution)) {
            $elements['institution'] = array(
                'type' => 'hidden',
                'value' => $this->institution,
            );
        }
        else if (!empty($this->owner)) {
725
726
            $elements['owner'] = array(
                'type' => 'hidden',
727
                'value' => $this->owner,
728
            );
729
730
        }

731
732
        return $elements;
    }
733

734
    /**
735
     * Returns array of views in the current collection
736
     *
Aaron Wells's avatar
Aaron Wells committed
737
     * @return array views
738
     */
739
740
741
    public function views() {

        if (!isset($this->views)) {
742

743
            $sql = "SELECT v.id, cv.*, v.title, v.owner, v.group, v.institution, v.ownerformat, v.urlid
744
745
                FROM {collection_view} cv JOIN {view} v ON cv.view = v.id
                WHERE cv.collection = ?
746
                ORDER BY cv.displayorder, v.title, v.ctime ASC";
747

748
            $result = get_records_sql_assoc($sql, array($this->get('id')));
749

750
            if (!empty($result)) {
751
752
753
754
755
756
757
758
759
                require_once('view.php');
                View::get_extra_view_info($result, false, false);
                $result = array_values($result);
                $max = $min = $result[0]['displayorder'];
                foreach ($result as &$r) {
                    $max = max($max, $r['displayorder']);
                    $min = min($min, $r['displayorder']);
                    $r = (object) $r;
                }
760
                $this->views = array(
761
                    'views'     => array_values($result),
762
                    'count'     => count($result),
763
764
                    'max'       => $max,
                    'min'       => $min,
765
766
767
768
769
                );
            }
            else {
                $this->views = array();
            }
770

771
772
        }

773
        return $this->views;
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
    /**
     * Returns first view in the current collection
     *
     * @return View the first view of the collection, null if the collection is empty
     */
    public function first_view() {

        $viewid = get_field('collection_view', 'view', 'collection', $this->get('id'), 'displayorder', '0');
        $viewid = get_field_sql("SELECT cv.view
            FROM {collection_view} cv
            WHERE cv.collection = ?
            AND cv.displayorder = (
                SELECT MIN(displayorder)
                FROM {collection_view} cv2
                WHERE cv2.collection = ?)",
            array($this->get('id'), $this->get('id')));
        if ($viewid) {
            require_once('view.php');
            $view = new View($viewid);
            return $view;
        }
        return null;
    }

800
    /**
801
     * Check that a collection can have a framework
802
803
804
805
806
807
808
     *
     * @return bool
     */
    public function can_have_framework() {
        return ($this->get_framework_institution()) ? true : false;
    }

809
810
811
812
813
814
815
    /**
     * Check that a collection can have a portfolio progress completion
     *
     * @return bool
     */
    public function can_have_progresscompletion() {
        $allowspc = false;
816
        if (isset($this->group) && $this->group) {
817
818
819
820
821
822
823
824
825
            return $allowspc;
        }
        if (is_plugin_active('signoff', 'blocktype')) {
            require_once(get_config('docroot') . 'lib/institution.php');
            if ($this->institution) {
                $institution = $this->institution;
                $institution = new Institution($institution);
                $allowspc = ($institution->progresscompletion) ? $institution : false;
            }
826
827
828
829
830
            else if ($this->group) {
                $institution = get_field('group', 'institution', 'id', $this->group);
                $institution = new Institution($institution);
                $allowspc = ($institution->progresscompletion) ? $institution : false;
            }
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
            else {
                $user = new User();
                $user->find_by_id($this->owner);
                $institutionids = array_keys($user->get('institutions'));
                if (!empty($institutionids)) {
                    foreach ($institutionids as $institution) {
                        $institution = new Institution($institution);
                        if ($institution->progresscompletion == true) {
                            $allowspc = $institution;
                            break;
                        }
                    }
                }
                else {
                    $institution = new Institution('mahara');
                    $allowspc = ($institution->progresscompletion) ? $institution : false;
                }
            }
        }
        return $allowspc;
    }

853
854
855
856
857
    /**
     * Check if any allowed institutions lets a collection have a framework
     * and return first valid one.
     *
     * Checks:
858
     * - The collection is not owned by a group
859
860
     * - The framework plugin is active
     * - The institution has 'SmartEvidence' turned on
861
     * - There are frameworks available for the institutions
862
     *
863
     * @return object $institution or false
864
     */
865
     public function get_framework_institution() {
866
        require_once('institution.php');
867
868
869
870
        if (!empty($this->group)) {
            return false;
        }

871
        if (!is_plugin_active('framework', 'module')) {
872
873
            return false;
        }
874
        $allowsmartevidence = false;
875
876
        if ($this->institution) {
            $institution = $this->institution;
877
878
            $institution = new Institution($institution);
            $allowsmartevidence = ($institution->allowinstitutionsmartevidence) ? $institution : false;
879
880
881
882
        }
        else {
            $user = new User();
            $user->find_by_id($this->owner);
883
884
885
886
887
888
889
890
891
892
893
894
895
896
            $institutionids = array_keys($user->get('institutions'));
            if (!empty($institutionids)) {
                foreach ($institutionids as $institution) {
                    $institution = new Institution($institution);
                    if ($institution->allowinstitutionsmartevidence == true) {
                        $allowsmartevidence = $institution;
                        break;
                    }
                }
            }
            else {
                $institution = new Institution('mahara');
                $allowsmartevidence = ($institution->allowinstitutionsmartevidence) ? $institution : false;
            }
897
        }
898
        return $allowsmartevidence;
899
900
901
902
903
904
905
906
    }

    /**
     * Get available frameworks
     *
     * @return array Available frameworks
     */
    public function get_available_frameworks() {
907
908
        $institution = $this->get_framework_institution();
        if (!$institution) {
909
910
911
912
913
914
915
916
917
918
            return array();
        }

        if ($frameworks = Framework::get_frameworks($institution->name, true)) {
            // Inactive frameworks are only allowed if they were added to
            // collection when they were active.
            foreach ($frameworks as $key => $framework) {
                if (empty($framework->active) && $framework->id != $this->framework) {
                    unset ($frameworks[$key]);
                }
919
            }
920
            return $frameworks;
921
        }
922
        return array();
923
924
925
926
927
    }

    /**
     * Check that a collection has a framework
     * - The collection can have a framework
928
929
930
931
932
933
     * - It has a framework id
     * - It has views in the collection
     *
     * @return boolean
     */
    public function has_framework() {
934
        if (!$this->can_have_framework()) {
935
936
937
938
939
940
941
942
            return false;
        }
        if (empty($this->framework)) {
            return false;
        }
        if (!$this->views()) {
            return false;
        }
943
        if (!is_plugin_active('framework', 'module')) {
944
945
            return false;
        }
946
947
948
        return true;
    }

949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
    /**
     * Check that a collection has progress completion enable
     * - The collection can have progress completion enabled
     * - It has progress completion enabled
     * - It has views in the collection
     *
     * @return boolean
     */
    public function has_progresscompletion() {
        if (!$this->can_have_progresscompletion()) {
            return false;
        }
        if (!$this->progresscompletion) {
            return false;
        }
        if (!$this->views()) {
            return false;
        }
        return true;
    }

970
971
972
973
974
975
    /**
     * Get collection framework option for collection navigation
     *
     * @return object $option;
     */
    public function collection_nav_framework_option() {
976
        $option = new stdClass();
977
978
        $option->framework = $this->framework;
        $option->id = $this->id;
979
        $option->title = get_field('framework', 'name', 'id', $this->framework);
980
981
        $option->framework = true;

982
        $option->fullurl = self::get_framework_url($option);
983
984
985
986

        return $option;
    }

987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003

    /**
     * Get collection framework option for collection navigation
     *
     * @return object $option;
     */
    public function collection_nav_progresscompletion_option() {
        $option = new stdClass();
        $option->id = $this->id;
        $option->title = get_string('progresscompletion', 'admin');
        $option->progresscompletion = true;

        $option->fullurl = self::get_progresscompletion_url($option);

        return $option;
    }

1004
    /**
1005
     * Adding the framework frameworkurl / fullurl to collections
1006
     *
1007
     * @param array  $data    Array of objects
1008
     *
1009
     * @return $data
1010
     */
1011
    public static function add_framework_urls(&$data) {
1012
1013
        if (is_array($data)) {
            foreach ($data as $k => $r) {
1014
1015
                $r->frameworkurl = self::get_framework_url($r, false);
                $r->fullurl = self::get_framework_url($r, true);
1016
1017
            }
        }
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
    }

    /**
     * Making the framework url
     *
     * @param object $data    Either a collection or standard object
     * @param bool   $fullurl Return full url rather than relative one
     *
     * @return $url
     */
    public static function get_framework_url($data, $fullurl = true) {
        $url = 'module/framework/matrix.php?id=' . $data->id;
        if ($fullurl) {
            return get_config('wwwroot') . $url;
1032
        }
1033
        return $url;
1034
1035
    }

1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
    /**
     * Making the framework url
     *
     * @param object $data    Either a collection or standard object
     * @param bool   $fullurl Return full url rather than relative one
     *
     * @return $url
     */
    public static function get_progresscompletion_url($data, $fullurl = true) {
        $url = 'collection/progresscompletion.php?id=' . $data->id;
        if ($fullurl) {
            return get_config('wwwroot') . $url;
        }
        return $url;
    }

1052
    /**
1053
1054
     * Get the available views the current user can choose to add to their collections.
     * Restrictions on this list include:
1055
     * - currently dashboard, group and profile views are ignored to solve access issues
1056
     * - default pages (with template == 2) are ignored
1057
     * - each view can only belong to one collection
1058
     * - locked/submitted views can't be added to collections
1059
1060
1061
     *
     * @return array $views
     */
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
    public static function available_views($owner=null, $groupid=null, $institutionname=null) {
        if (!empty($groupid)) {
            $wherestm = '"group" = ?';
            $values = array($groupid);
        }
        else if (!empty($institutionname)) {
            $wherestm = 'institution = ?';
            $values = array($institutionname);
        }
        else if (!empty($owner)) {
            $wherestm = 'owner = ?';
            $values = array($owner);
        }
        else {
            return array();
        }
1078
        ($views = get_records_sql_array("SELECT v.id, v.title
1079
1080
1081
1082
            FROM {view} v
            LEFT JOIN {collection_view} cv ON cv.view = v.id
            WHERE " . $wherestm .
            "   AND cv.view IS NULL
1083
                AND v.type NOT IN ('dashboard','grouphomepage','profile')
1084
                AND v.template != 2
1085
1086
                AND v.submittedgroup IS NULL
                AND v.submittedhost IS NULL
1087
1088
1089
1090
            GROUP BY v.id, v.title
            ORDER BY v.title ASC
            ", $values))
            || ($views = array());
1091
        return $views;
1092
1093
    }

1094
    /**
1095
     * Submits the selected views to the collection
1096
1097
1098
1099
1100
     *
     * @param array values selected views
     * @return integer count so we know what SESSION message to display
     */
    public function add_views($values) {
1101
        require_once(get_config('libroot') . 'view.php');
1102

1103
        $count = 0; // how many views we are adding
1104
1105
        db_begin();

1106
        // each view was marked with a key of view_<id> in order to identify the correct items
1107
        // from the form values
1108
1109
1110
1111
1112
        foreach ($values as $key => $value) {
            if (substr($key,0,5) === 'view_' AND $value == true) {
                $cv = array();
                $cv['view'] = substr($key,5);
                $cv['collection'] = $this->get('id');
1113
1114
1115
1116
1117

                // set displayorder value
                $max = get_field('collection_view', 'MAX(displayorder)', 'collection', $this->get('id'));
                $cv['displayorder'] = is_numeric($max) ? $max + 1 : 0;

1118
1119
1120
1121
                insert_record('collection_view', (object)$cv);
                $count++;
            }
        }
1122

1123
        $viewids = get_column('collection_view', 'view', 'collection', $this->id);
1124
1125

        // Set the most permissive access records on all views
1126
        View::combine_access($viewids, true);
1127

1128
1129
1130
1131
1132
1133
1134
        // Copy the whole view config from the first view to all the others
        if (count($viewids)) {
            $firstview = new View($viewids[0]);
            $viewconfig = array(
                'startdate'       => $firstview->get('startdate'),
                'stopdate'        => $firstview->get('stopdate'),
                'template'        => $firstview->get('template'),
1135
                'retainview'      => $firstview->get('retainview'),
1136
1137
1138
                'allowcomments'   => $firstview->get('allowcomments'),
                'approvecomments' => (int) ($firstview->get('allowcomments') && $firstview->get('approvecomments')),
                'accesslist'      => $firstview->get_access(),
1139
                'lockblocks'      => $firstview->get('lockblocks'),
1140
1141
1142
1143
            );
            View::update_view_access($viewconfig, $viewids);
        }

1144
1145
1146
        // Now that we have added views to the collection we need to update the collection modified date
        $this->mtime = db_format_timestamp(time());
        $this->commit();
1147
1148
        db_commit();

1149
1150
        return $count;
    }
1151

1152
    /**
1153
     * Removes the selected views from the collection
1154
1155
1156
1157
1158
     *
     * @param integer $view the view to remove
     */
    public function remove_view($view) {
        db_begin();
1159
1160
1161
1162
1163
1164
1165
1166

        $position = get_field_sql('
            SELECT displayorder FROM {collection_view}
                WHERE collection = ?
                AND view = ?',
                array($this->get('id'),$view)
        );

1167
        delete_records('collection_view','view',$view,'collection',$this->get('id'));
1168

1169
        $this->update_display_order($position);
1170
1171
1172
1173
        // Secret url records belong to the collection, so remove them from the view.
        // @todo: add user message to whatever calls this.
        delete_records_select('view_access', 'view = ? AND token IS NOT NULL', array($view));

1174
1175
1176
1177
        // Now that we have removed views from the collection we need to update the collection modified date
        $this->mtime = db_format_timestamp(time());
        $this->commit();

1178
1179
1180
        db_commit();
    }

1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
    /**
     * Updates the position number of the views in a collection
     *
     * @param integer $start position from where to start updating
     */
    public function update_display_order($start = 0) {
        $start = intval($start);
        $ids = get_column_sql('
                SELECT view FROM {collection_view}
                WHERE collection = ?
                ORDER BY displayorder', array($this->get('id')));
        foreach ($ids as $k => $v) {
            if ($start <= $k) {
                set_field('collection_view', 'displayorder', $k, 'view', $v, 'collection',$this->get('id'));
            }
        }
    }

1199
1200
1201
    /**
     * Sets the displayorder for a view
     *
1202
1203
1204
1205
     * @param integer   $id   view id
     * @param mixed  direction - either string consisting 'up' or 'down' to
     *               indicate which way to move $id item, or an array containing
     *               the ids in order you want them saved
1206
1207
     */
    public function set_viewdisplayorder($id, $direction) {
1208
1209
1210
        if (is_array($direction)) {
            // we already have new sort order
            $neworder = $direction;
1211
        }
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
        else {
            $ids = get_column_sql('
                SELECT view FROM {collection_view}
                WHERE collection = ?
                ORDER BY displayorder', array($this->get('id')));

            foreach ($ids as $k => $v) {
                if ($v == $id) {
                    $oldorder = $k;
                    break;
                }
            }
1224

1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
            if ($direction == 'up' && $oldorder > 0) {
                $neworder = array_merge(array_slice($ids, 0, $oldorder - 1),
                                        array($id, $ids[$oldorder-1]),
                                        array_slice($ids, $oldorder+1));
            }
            else if ($direction == 'down' && ($oldorder + 1 < count($ids))) {
                $neworder = array_merge(array_slice($ids, 0, $oldorder),
                                        array($ids[$oldorder+1], $id),
                                        array_slice($ids, $oldorder+2));
            }
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
        }
        if (isset($neworder)) {
            foreach ($neworder as $k => $v) {
                set_field('collection_view', 'displayorder', $k, 'view', $v, 'collection',$this->get('id'));
            }
            $this->set('mtime', time());
            $this->commit();
        }
    }

1245
1246
1247
    /**
     * after editing the collection, redirect back to the appropriate place
     */
1248
    public function post_edit_redirect($new=false, $copy=false, $urlparams=null) {
1249
1250
1251
1252
1253
1254
1255
1256
        $redirecturl = post_edit_redirect_url($new, $copy, $urlparams);
        redirect($redirecturl);
    }

    /**
     * returns the url that we need to redirect to sfter editing a collection
     */
    public function post_edit_redirect_url($new=false, $copy=false, $urlparams=null) {
1257
        $redirecturl = get_config('wwwroot');
1258
1259
        if ($new || $copy) {
            $urlparams['id'] = $this->get('id');
1260
            $redirecturl .= 'collection/views.php';
1261
1262
        }
        else {
1263
1264
            if ($this->get('group')) {
                // Group owned collection
1265
                $redirecturl .= 'view/groupviews.php';
1266
1267
1268
1269
            }
            else if ($this->get('institution')) {
                if ($this->get('institution') == 'mahara') {
                    // Site owned collection
1270
                    <