* @license http://www.gnu.org/copyleft/gpl.html GNU GPL * @copyright (C) 2006,2007 Catalyst IT Ltd http://catalyst.net.nz * */ defined('INTERNAL') || die(); require_once('artefact.php'); /** * Base artefact plugin class * @abstract */ abstract class PluginArtefact extends Plugin { /** * This function returns a list of classnames * of artefact types this plugin provides. * @abstract * @return array */ public static abstract function get_artefact_types(); /** * 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(); /** * This function returns the name of the plugin. * @abstract * @return string */ public static abstract function get_plugin_name(); /** * 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(); } } /** * Base artefact type class * @abstract */ abstract class ArtefactType { protected $dirty; protected $parentdirty; protected $deleted = false; protected $id; protected $artefacttype; protected $owner; protected $container; protected $parent; protected $ctime; protected $mtime; protected $atime; protected $locked; protected $title; protected $description; protected $note; protected $tags = array(); protected $viewsinstances; protected $viewsmetadata; protected $childreninstances; protected $childrenmetadata; protected $parentinstance; protected $parentmetadata; /** * 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 */ public function __construct($id=0, $data=null) { if (!empty($id)) { if (empty($data)) { if (!$data = get_record('artefact','id',$id)) { throw new ArtefactNotFoundException(get_string('artefactnotfound', 'error', $id)); } } $this->id = $id; } else { $this->ctime = $this->mtime = time(); $this->dirty = true; } if (empty($data)) { $data = array(); } foreach ((array)$data as $field => $value) { if (property_exists($this, $field)) { if (in_array($field, array('atime', 'ctime', 'mtime'))) { $value = strtotime($value); } if ($field == 'tags' && !is_array($field)) { $value = preg_split("/\s*,\s*/", trim($value)); } $this->{$field} = $value; } } // load tags if ($this->id) { $tags = get_column('artefact_tag', 'tag', 'artefact', $this->id); if (is_array($tags)) { $this->tags = $tags; } } $this->atime = time(); $this->artefacttype = $this->get_artefact_type(); } public function get_views_instances() { // @todo } public function get_views_metadata() { // @todo } public function count_children() { return count_records('artefact', 'parent', $this->get('id')); } public function has_children() { if ($this->get_children_metadata()) { return true; } return false; } public function get_plugin_name() { return get_field('artefact_installed_type', 'plugin', 'name', $this->get('artefacttype')); } /** * 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. */ public function get_children_instances() { if (!isset($this->childreninstances)) { $this->childreninstances = false; if ($children = $this->get_children_metadata()) { $this->childreninstances = array(); foreach ($children as $child) { $classname = generate_artefact_class_name($child->artefacttype); $instance = new $classname($child->id, $child); $this->childreninstances[] = $instance; } } } return $this->childreninstances; } /** * 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 */ public function get_children_metadata() { if (!isset($this->childrenmetadata)) { $this->childrenmetadata = get_records_array('artefact', 'parent', $this->id); } return $this->childrenmetadata; } /** * 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 */ public function get_parent_instance() { if (!isset($this->parentinstance)) { $this->parentinstance = false; if ($parent = $this->get_parent_metadata()) { $classname = generate_artefact_class_name($parent->artefacttype); $this->parentinstance = new $classname($parent->id, $parent); } } return $this->parentinstance; } /** * 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. * * @return object - db row */ public function get_parent_metadata() { return get_record('artefact','id',$this->parent); } public function get($field) { if (!property_exists($this, $field)) { throw new InvalidArgumentException("Field $field wasn't found in class " . get_class($this)); } return $this->{$field}; } public function set($field, $value) { if (property_exists($this, $field)) { if ($this->{$field} != $value) { // only set it to dirty if it's changed $this->dirty = true; } $this->{$field} = $value; if ($field == 'parent') { $this->parentdirty = true; } $this->mtime = time(); return true; } throw new InvalidArgumentException("Field $field wasn't found in class " . get_class($this)); } /** * Artefact destructor. Calls commit and marks the * artefact cache as dirty if necessary. * * A special case is when the object has just been deleted. In this case, * we do nothing. */ public function __destruct() { if ($this->deleted) { return; } if (!empty($this->dirty)) { $this->commit(); } } public function is_container() { return false; } /** * 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. */ public function commit() { if (empty($this->dirty)) { return; } $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); if (!empty($this->parent)) { $this->parentdirty = true; } } else { update_record('artefact', $fordb, 'id'); } delete_records('artefact_tag', 'artefact', $this->id); if (is_array($this->tags)) { foreach (array_unique($this->tags) as $tag) { if (empty($tag)) { continue; } insert_record( 'artefact_tag', (object) array( 'artefact' => $this->id, 'tag' => $tag, ) ); } } handle_event('saveartefact', $this); 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)); } $this->dirty = false; $this->deleted = false; $this->parentdirty = false; } /** * 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. */ public function delete() { if (empty($this->id)) { $this->dirty = false; return; } // Call delete() on children (if there are any) if ($children = $this->get_children_instances()) { foreach ($children as $child) { $child->delete(); } } // 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); delete_records('artefact_feedback', 'artefact', $this->id); delete_records('artefact_tag', 'artefact', $this->id); // Delete the record itself. delete_records('artefact', 'id', $this->id); handle_event('deleteartefact', $this); // Set flags. $this->dirty = false; $this->parentdirty = true; $this->deleted = true; } /** * * 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. */ public function add_to_render_path(&$options) { if (empty($options['path'])) { $options['path'] = $this->get('id'); } else { $options['path'] .= ',' . $this->get('id'); } } /** * 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; } /** * 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; } /** * 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) */ 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 } /** * whether a user will have exactly 0 or 1 of this artefact type * @abstract */ public static abstract function is_singular(); /** * Whether the 'note' field is for the artefact's private use */ public static function is_note_private() { return false; } /** * 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); // ******************** 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; } public function to_stdclass() { return (object)get_object_vars($this); } public static function has_config() { return false; } public static function get_config_options() { return array(); } public static function collapse_config() { return false; } } ?>