lib.php 14.9 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
/**
 * This program is part of Mahara
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
 *
 * @package    mahara
Penny Leach's avatar
Penny Leach committed
20
 * @subpackage artefact
Penny Leach's avatar
Penny Leach committed
21
 * @author     Penny Leach <penny@catalyst.net.nz>
22
23
24
25
26
27
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL
 * @copyright  (C) 2006,2007 Catalyst IT Ltd http://catalyst.net.nz
 *
 */

defined('INTERNAL') || die();
28
require_once('artefact.php');
29
30
31
32
33

/**
 * Base artefact plugin class
 * @abstract
 */
34
abstract class PluginArtefact extends Plugin {
35
36
37
38

    /** 
     * This function returns a list of classnames 
     * of artefact types this plugin provides.
Penny Leach's avatar
Penny Leach committed
39
     * @abstract
40
41
42
43
     * @return array
     */
    public static abstract function get_artefact_types();

44
45
46
47
48
49
50
51
52
53
54
    
    /**
    * This function returns a list of classnames
    * of block types this plugin provides
    * they must match directories inside artefact/$name/blocktype
    * @abstract
    * @return array
    */
    public static abstract function get_block_types();


55
56
    /**
     * This function returns the name of the plugin.
Penny Leach's avatar
Penny Leach committed
57
     * @abstract
58
59
60
61
62
     * @return string
     */
    public static abstract function get_plugin_name();


63
64
65
66
67
68
69
70
71
72
73
    /**
     * This function returns an array of menu items
     * to be displayed
     * Each item should be a StdClass object containing -
     * - name language pack key
     * - url relative to wwwroot
     * @return array
     */
    public static function menu_items() {
        return array();
    }
74
75
76
77
78
79
80
81
}

/** 
 * Base artefact type class
 * @abstract
 */
abstract class ArtefactType {
    
82
83
    protected $dirty;
    protected $parentdirty;
84
    protected $deleted = false;
85
    protected $id;
86
87
    protected $artefacttype;
    protected $owner;
88
    protected $container;
89
    protected $parent;
90
91
    protected $ctime;
    protected $mtime;
92
    protected $atime;
93
94
95
96
    protected $locked;
    protected $title;
    protected $description;
    protected $note;
97
    protected $tags = array();
98

Penny Leach's avatar
Penny Leach committed
99
100
101
102
103
104
105
    protected $viewsinstances;
    protected $viewsmetadata;
    protected $childreninstances;
    protected $childrenmetadata;
    protected $parentinstance;
    protected $parentmetadata;

106
107
108
109
110
111
112
113
    /** 
     * Constructer. 
     * If an id is supplied, will query the database
     * to build up the basic information about the object.
     * If an id is not supplied, we just create an empty
     * artefact, ready to be filled up
     * @param int $id artefact.id
     */
Penny Leach's avatar
Penny Leach committed
114
    public function __construct($id=0, $data=null) {
115
        if (!empty($id)) {
Penny Leach's avatar
Penny Leach committed
116
117
            if (empty($data)) {
                if (!$data = get_record('artefact','id',$id)) {
118
                    throw new ArtefactNotFoundException(get_string('artefactnotfound', 'error', $id));
Penny Leach's avatar
Penny Leach committed
119
                }
120
            }
121
            $this->id = $id;
122
123
        }
        else {
124
125
            $this->ctime = $this->mtime = time();
            $this->dirty = true;
126
        }
127
128
129
130
131
        if (empty($data)) {
            $data = array();
        }
        foreach ((array)$data as $field => $value) {
            if (property_exists($this, $field)) {
132
133
134
                if (in_array($field, array('atime', 'ctime', 'mtime'))) {
                    $value = strtotime($value);
                } 
135
136
137
                if ($field == 'tags' && !is_array($field)) {
                    $value = preg_split("/\s*,\s*/", trim($value));
                }
138
139
140
141
                $this->{$field} = $value;
            }
        }

142
143
144
145
146
147
148
149
        // load tags
        if ($this->id) {
            $tags = get_column('artefact_tag', 'tag', 'artefact', $this->id);
            if (is_array($tags)) {
                $this->tags = $tags;
            }
        }

150
151
        $this->atime = time();
        $this->artefacttype = $this->get_artefact_type();
152
153
154
155
156
157
158
159
160
161
    }

    public function get_views_instances() {
        // @todo
    }
    
    public function get_views_metadata() {
        // @todo
    }

Richard Mansfield's avatar
Richard Mansfield committed
162
163
164
165
    public function count_children() {
        return count_records('artefact', 'parent', $this->get('id'));
    }

166
167
168
169
170
171
172
    public function has_children() {
        if ($this->get_children_metadata()) {
            return true;
        }
        return false;
    }

173
174
175
176
    public function get_plugin_name() {
        return get_field('artefact_installed_type', 'plugin', 'name', $this->get('artefacttype'));
    }

Penny Leach's avatar
Penny Leach committed
177
178
179
180
181
182
183
184
185
    /** 
     * This function returns the instances 
     * of all children of this artefact
     * If you just want the basic info, 
     * use {@link get_children_metadata} instead.
     * 
     * @return array of instances.
     */

186
    public function get_children_instances() {
Penny Leach's avatar
Penny Leach committed
187
188
189
190
191
        if (!isset($this->childreninstances)) {
            $this->childreninstances = false;
            if ($children = $this->get_children_metadata()) {
                $this->childreninstances = array();
                foreach ($children as $child) {
192
                    $classname = generate_artefact_class_name($child->artefacttype);
Penny Leach's avatar
Penny Leach committed
193
194
195
196
197
198
                    $instance = new $classname($child->id, $child);
                    $this->childreninstances[] = $instance;
                }
            }
        }
        return $this->childreninstances;
199
200
    }

201
202
203
204
205
206
207
208
209
    /**
     * This function returns the db rows 
     * from the artefact table that have this 
     * artefact as the parent.
     * If you want instances, use {@link get_children_instances}
     * but bear in mind this will have a performance impact.
     * 
     * @return array
     */
210
    public function get_children_metadata() {
Penny Leach's avatar
Penny Leach committed
211
        if (!isset($this->childrenmetadata)) {
212
            $this->childrenmetadata = get_records_array('artefact', 'parent', $this->id);
Penny Leach's avatar
Penny Leach committed
213
214
        }
        return $this->childrenmetadata;
215
    }
Penny Leach's avatar
Penny Leach committed
216
217
218
219
220
221
222
223
224

    /**
     * This function returns the instance relating to the parent
     * of this object, or false if there isn't one.
     * If you just want basic information about it,
     * use {@link get_parent_metadata} instead.
     *
     * @return ArtefactType
     */
225
    public function get_parent_instance() {
Penny Leach's avatar
Penny Leach committed
226
227
228
        if (!isset($this->parentinstance)) {
            $this->parentinstance = false;
            if ($parent = $this->get_parent_metadata()) {
229
                $classname = generate_artefact_class_name($parent->artefacttype);
Penny Leach's avatar
Penny Leach committed
230
231
232
233
                $this->parentinstance = new $classname($parent->id, $parent);
            }
        }
        return $this->parentinstance;
234
    }
235
236
237
238
239
240
241
242

    /** 
     * This function returns the db row 
     * (if there is one) of the parent
     * artefact for this instance.
     * If you want the instance, use 
     * {@link get_parent_instance} instead.
     * 
243
     * @return object - db row
244
     */
245
    public function get_parent_metadata() {
246
        return get_record('artefact','id',$this->parent);
247
248
249
    }

    public function get($field) {
250
        if (!property_exists($this, $field)) {
251
            throw new InvalidArgumentException("Field $field wasn't found in class " . get_class($this));
252
253
254
255
256
        }
        return $this->{$field};
    }

    public function set($field, $value) {
257
        if (property_exists($this, $field)) {
258
259
260
261
            if ($this->{$field} != $value) {
                // only set it to dirty if it's changed
                $this->dirty = true;
            }
262
            $this->{$field} = $value;
263
264
265
            if ($field == 'parent') {
                $this->parentdirty = true;
            }
266
            $this->mtime = time();
267
268
            return true;
        }
269
        throw new InvalidArgumentException("Field $field wasn't found in class " . get_class($this));
270
271
    }
    
272
273
274
    /**
     * Artefact destructor. Calls commit and marks the
     * artefact cache as dirty if necessary.
275
276
277
     *
     * A special case is when the object has just been deleted.  In this case,
     * we do nothing.
278
     */
279
    public function __destruct() {
280
281
282
283
        if ($this->deleted) {
            return;
        }
      
284
285
286
        if (!empty($this->dirty)) {
            $this->commit();
        }
287
288
289
290
291
292
    }
    
    public function is_container() {
        return false;
    }

293
    /** 
294
295
296
     * This method updates the contents of the artefact table only.  If your
     * artefact has extra information in other tables, you need to override
     * this method, and call parent::commit() in your own function.
297
     */
298
    public function commit() {
299
300
301
        if (empty($this->dirty)) {
            return;
        }
302
303
304
305
306
307
308
309
310
        $fordb = new StdClass;
        foreach (get_object_vars($this) as $k => $v) {
            $fordb->{$k} = $v;
            if (in_array($k, array('mtime', 'ctime', 'atime')) && !empty($v)) {
                $fordb->{$k} = db_format_timestamp($v);
            }
        }
        if (empty($this->id)) {
            $this->id = insert_record('artefact', $fordb, 'id', true);
311
312
313
            if (!empty($this->parent)) {
                $this->parentdirty = true;
            }
314
315
316
317
        }
        else {
            update_record('artefact', $fordb, 'id');
        }
318
319
320

        delete_records('artefact_tag', 'artefact', $this->id);
        if (is_array($this->tags)) {
Martyn Smith's avatar
Martyn Smith committed
321
            foreach (array_unique($this->tags) as $tag) {
322
323
324
                if (empty($tag)) {
                    continue;
                }
325
326
327
328
329
330
331
332
333
334
                insert_record(
                    'artefact_tag',
                    (object) array(
                        'artefact' => $this->id,
                        'tag'      => $tag,
                    )
                );
            }
        }

335
336
        handle_event('saveartefact', $this);

337
338
339
340
341
342
343
344
345
346
347
        if (!empty($this->parentdirty)) {
            if (!empty($this->parent) && !record_exists('artefact_parent_cache', 'artefact', $this->id)) {
                $apc = new StdClass;
                $apc->artefact = $this->id;
                $apc->parent = $this->parent;
                $apc->dirty  = 1; // set this so the cronjob will pick it up and go set all the other parents.
                insert_record('artefact_parent_cache', $apc);
            }
            set_field_select('artefact_parent_cache', 'dirty', 1,
                             'artefact = ? OR parent = ?', array($this->id, $this->id));
        }
348
        $this->dirty = false;
349
        $this->deleted = false;
350
        $this->parentdirty = false;
351
352
353
    }

    /** 
354
355
356
357
358
359
360
     * This function provides basic delete functionality.  It gets rid of the
     * artefact's row in the artefact table, and the tables that reference the
     * artefact table.  It also recursively deletes child artefacts.
     *
     * If your artefact has additional data in another table, you should
     * override this function, but you MUST call parent::delete() after you
     * have done your own thing.
361
     */
362
363
    public function delete() {
        if (empty($this->id)) {
364
            $this->dirty = false;
365
366
367
368
369
370
371
372
373
            return;
        }
      
        // Call delete() on children (if there are any)
        if ($children = $this->get_children_instances()) {
            foreach ($children as $child) {
                $child->delete();
            }
        }
374

375
376
377
        // Delete any references to this artefact from non-artefact places.
        delete_records_select('artefact_parent_cache', 'artefact = ? OR parent = ?', array($this->id, $this->id));
        delete_records('view_artefact', 'artefact', $this->id);
378
        delete_records('artefact_feedback', 'artefact', $this->id);
379
        delete_records('artefact_tag', 'artefact', $this->id);
380
381
      
        // Delete the record itself.
382
        delete_records('artefact', 'id', $this->id);
383
        
384
385
        handle_event('deleteartefact', $this);

386
        // Set flags.
387
388
        $this->dirty = false;
        $this->parentdirty = true;
389
        $this->deleted = true;
390
391
    }

Penny Leach's avatar
Penny Leach committed
392
    /**
393
394
395
396
397
    * 
    * this function provides the way to link to viewing very deeply nested artefacts
    * within a view, it makes urls like view/view.php?id=x&artefact=y
    * which is important for the access check.
    */
398
399
400
401
402
403
404
405
406
407
    public function add_to_render_path(&$options) {
        if (empty($options['path'])) {
            $options['path'] = $this->get('id');
        }
        else {
            $options['path'] .= ',' . $this->get('id');
        }
    }


408
409
410
411
412
413
414
415
416
417
    /**
     * By default public feedback can be placed on all artefacts.
     * Artefact types which don't want to allow public feedback should
     * redefine this function.
     */
    public function public_feedback_allowed() {
        return true;
    }


418
419
420
421
422
423
424
425
426
427
    /**
     * By default users are notified of all feedback on artefacts
     * which they own.  Artefact types which want to allow this
     * notification to be turned off should redefine this function.
     */
    public function feedback_notify_owner() {
        return true;
    }


Penny Leach's avatar
Penny Leach committed
428
429
430
431
432
433
434
    /**
     * returns path to icon
     * can be called statically but not defined so
     * so that can be either from instance or static.
     * @abstract 
     * @return string path to icon (relative to docroot)
     */
435
436
437
438
439
440
441
442
443
444
445
446
447
    public abstract function get_icon();
    

    // ******************** STATIC FUNCTIONS ******************** //

    public static function get_instances_by_userid($userid, $order, $offset, $limit) {
        // @todo
    }

    public static function get_metadata_by_userid($userid, $order, $offset, $limit) {
        // @todo
    }

Penny Leach's avatar
Penny Leach committed
448
449
450
451
    /**
     * whether a user will have exactly 0 or 1 of this artefact type
     * @abstract
     */
452
    public static abstract function is_singular();
Penny Leach's avatar
Penny Leach committed
453

454
455
456
457
458
459
460
    /**
     * Whether the 'note' field is for the artefact's private use
     */
    public static function is_note_private() {
        return false;
    }

Martyn Smith's avatar
Martyn Smith committed
461
462
463
464
465
466
467
468
469
    /**
     * Returns a list of key => value pairs where the key is either '_default'
     * or a langauge string, and value is a URL linking to that behaviour for
     * this artefact type
     * 
     * @param integer This is the ID of the artefact being linked to
     */
    public static abstract function get_links($id);

470
471
472
473
474
475
476
477
478
479
480
481
482
    // ******************** HELPER FUNCTIONS ******************** //

    protected function get_artefact_type() {
        $classname = get_class($this);
        
        $type = strtolower(substr($classname, strlen('ArtefactType')));

        if (!record_exists('artefact_installed_type', 'name', $type)) {
            throw new InvalidArgumentException("Classname $classname not a valid artefact type");
        }

        return $type;
    }
483

484
485
486
487
    public function to_stdclass() {
       return (object)get_object_vars($this); 
    }

488
489
490
491
492
493
494
495
496
497
498
    public static function has_config() {
        return false;
    }

    public static function get_config_options() {
        return array();
    }

    public static function collapse_config() {
        return false;
    }
499
500
501
}

?>