Source of file MetadataExtension.php
Size: 12,510 Bytes - Last Modified: 2021-12-23T10:25:41+00:00
/var/www/docs.ssmods.com/process/src/code/extensions/MetadataExtension.php
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411 | <?php /** * An extension that must be applied to an object in order for it to have the * ability to have metadata attached to it. * * NOTE: You can use a "canApplySchemas" method in order to control whether a * user can apply manage the schemas attached to the object. * * @package silverstripe-metadata */ class MetadataExtension extends DataExtension { /** * @var DataObjectSet */ protected $schemas; private static $db = array( 'MetadataRaw' => 'Text' ); /** * Returns all the schema objects attached to this object, or any of its * parents. * * @return DataObjectSet */ public function getSchemas() { if (!$this->schemas) { $schemas = $this->getAttachedSchemas(); if ($this->owner->hasExtension('Hierarchy')) { $schemas->merge($this->getInheritedSchemas()); $schemas->removeDuplicates(); $schemas->sort('Title'); } $this->schemas = $schemas; } return $this->schemas; } /** * Returns metadata schemas directly attached to this object via a schema * link (not including inherited schemas). * * @return DataObjectSet */ public function getAttachedSchemas() { $filter = sprintf( '"MetadataSchema"."ID" = "MetadataSchemaLink"."SchemaID"' . ' AND "MetadataSchemaLink"."ParentClass" = \'%s\'' . ' AND "MetadataSchemaLink"."ParentID" = %d', ClassInfo::baseDataClass($this->owner->class), $this->owner->ID ); $schemas = MetadataSchema::get()->innerJoin('MetadataSchemaLink', $filter); return $schemas ? new ArrayList($schemas->toArray()) : new ArrayList(); } /** * Alternative ancestor lookup that doesn't use getParent() which fails for Image/File classes * to retrieve the true hierarchy. */ protected function altAncestors() { if ($this->owner instanceof File) { $ancestors = new ArrayList(); $object = $this->owner->Parent(); while ($object && $object->exists()) { $ancestors->push($object); $object = $object->Parent(); } return $ancestors; } return $this->owner->getAncestors(); } /** * If this is attached to an object with the hierarchy extension, it returns * a set of a schema objects attached to any ancestors (which should be * present on this object). * * @return ArrayList */ public function getInheritedSchemas() { $result = new ArrayList(); if (!$this->owner->hasExtension('Hierarchy')) { return new ArrayList(); } $ids = array(); $parents = $this->altAncestors(); foreach ($parents as $parent) { $ids[] = $parent->ID; } if (count($ids)) { $baseClass = ClassInfo::baseDataClass($this->owner->class); $filter = sprintf( '"MetadataSchema"."ID" = "MetadataSchemaLink"."SchemaID"' . ' AND "MetadataSchemaLink"."ParentClass" = \'%s\'' . ' AND "MetadataSchemaLink"."ParentID" IN (%s)', $baseClass, implode(', ', $ids) ); $result = MetadataSchema::get()->innerJoin('MetadataSchemaLink', $filter); if ($result) { $result = new ArrayList($result->toArray()); } else { $result = new ArrayList(); } } if ($this->owner instanceof SiteTree) { // Check SiteConfig too $config = SiteConfig::current_site_config(); if ($config->hasExtension('MetadataExtension')) { $schemas = $config->getAttachedSchemas(); if ($schemas && $schemas->count()) { $result->merge($schemas); } } } return $result; } /** * Links a metadata schema to this object, if it's not already linked. * * @param MetadataSchema|int $schema */ public function addSchema($schema) { $id = is_object($schema) ? $schema->ID : $schema; $attached = $this->getSchemas()->map(); if (!array_key_exists($id, $attached)) { $link = new MetadataSchemaLink(); $link->ParentClass = $this->owner->class; $link->ParentID = $this->owner->ID; $link->SchemaID = $id; $link->write(); } $this->schemas = null; } /** * @return array */ public function getAllMetadata() { if (!$raw = $this->owner->MetadataRaw) { return array(); } $metadata = @unserialize($raw); return is_array($metadata) ? $metadata : array(); } /** * Returns a raw metadata value (i.e. not run through a process method). * * @param MetadataSchema|string $schema * @param MetadataField|string $field * @return string */ public function getRawMetadataValue($schema, $field) { $metadata = $this->getAllMetadata(); if (!$schema instanceof MetadataSchema && !$schema = $this->getSchemas()->find('Name', $schema)) { return; } if (!$field instanceof MetadataField && !$field = $schema->Fields()->find('Name', $field)) { return; } if (isset($metadata[$schema->Name][$field->Name])) { return $metadata[$schema->Name][$field->Name]; } else { return $field->Default; } } /** * Returns a metadata value if it exists for a schema and field name, suitable * for injection into a template. * * NOTE: This can potentially be quite expensive with default and cascading * values, so results should be cached. * * @param MetadataSchema|string $schema * @param MetadataField|string $field * @return mixed */ public function Metadata($schema, $field) { if (!$schema instanceof MetadataSchema && !$schema = $this->getSchemas()->find('Name', $schema)) { return; } if (!$field instanceof MetadataField && !$field = $schema->Fields()->find('Name', $field)) { return; } $raw = $this->getRawMetadataValue($schema, $field); $hier = $this->owner->hasExtension('Hierarchy'); $parent = null; // if hierarchy is applicable, and we're a sitetree object, and at the root if ($hier && !$this->owner->ParentID && $this->owner instanceof SiteTree) { if (SiteConfig::current_site_config()->hasExtension('MetadataExtension')) { $parent = SiteConfig::current_site_config(); } } if (!$parent && $this->owner->ParentID) { $parent = $this->owner->Parent(); } if (!$raw && $hier && $field->Cascade && $parent) { return $parent->Metadata($schema, $field); } return $field->process($raw, $this->owner); } /** * Returns all the metadata fields for a schema name encased in standard * HTML <meta> tags. * * @param string $schema * @return string */ public function MetadataMetaTags($schemaName = null) { $result = ''; $cache = SS_Cache::factory('MetadataExtension'); $key = md5(implode('', array( $schemaName, 'MetadataMetaTags', $this->owner->class, $this->owner->ID, $this->owner->LastEdited ))); if ($cached = $cache->load($key)) { return $cached; } foreach ($this->getSchemas() as $schema) { if ($schemaName) { if (!$schema->Name == $schemaName) { continue; } } foreach ($schema->Fields() as $field) { $value = $this->Metadata($schema, $field); if (!$value || ($value instanceof DBField && !$value->hasValue())) { continue; } if (is_object($value)) { $value = $value instanceof DBField ? $value->Nice() : $value->getTitle(); } $extraAttributes = ''; $extras = $field->extend('getExtraTagAttributes'); $usedAttr = array(); if ($extras && is_array($extras)) { foreach ($extras as $extra) { if ($extra && is_array($extra)) { foreach ($extra as $k => $v) { if (!in_array($k, $usedAttr)) { $usedAttr[] = $k; $extraAttributes .= sprintf(' %s="%s"', $k, $v); } } } } } $result .= sprintf( "<meta name=\"%s\"%s content=\"%s\" />\n", Convert::raw2att($field->Name), $extraAttributes, Convert::raw2att($value) ); } } $cache->save($result, $key); return $result; } public function updateCMSFields(FieldList $fields) { // instance of SiteTree uses updateSettingsFields hook if ($this->owner instanceof SiteTree) { return; } $this->updateSettingsFields($fields); } public function updateSettingsFields(FieldList $fields) { if ($this->owner->ID <= 0) { return; } if (!$allSchemas = DataObject::get('MetadataSchema')) { return; } $tabName = 'Root.Metadata'; $rootTab = $fields->fieldByName('Root'); $newFields = array( new HeaderField('MetadataInfoHeader', 'Metadata Information'), new MetadataSetField($this->owner, 'MetadataRaw'), new HeaderField('MetadataSchemasHeader', 'Metadata Schemas'), $linkedSchemas = new CheckboxSetField('MetadataSchemas', '', $allSchemas) ); $inherited = $this->getInheritedSchemas()->map('ID', 'ID'); $linkedSchemas->setValue($this->getAttachedSchemas()->map('ID', 'ID')); $linkedSchemas->setDefaultItems($inherited); $linkedSchemas->setDisabledItems($inherited); $canApply = $this->owner->extendedCan('canApplySchemas', Member::currentUser()); if ($canApply === false) { $linkedSchemas->setDisabled(true); } if ($this->owner->hasExtension('Hierarchy')) { $newFields[] = new LiteralField( 'SchemaAppliedToChildrenNote', '<p>Any metadata schemas selected will also be applied to this' . " item's children.</p>" ); } if (!$rootTab) { foreach ($newFields as $f) { $fields->push($f); } } else { $fields->addFieldsToTab($tabName, $newFields); } } /** * Ensures that schemas that are linked to parent objects are not saved * into this object's relationships. * * @param string $values */ public function saveMetadataSchemas($values) { $attached = $this->getAttachedSchemas(); $inherited = $this->getInheritedSchemas()->map('ID', 'ID'); $ids = array_map('intval', explode(',', $values)); $ids = array_diff($ids, $inherited); $add = array_diff($ids, $attached->map('ID', 'ID')); $del = array_diff($attached->map('ID', 'ID'), $ids); if ($add) { foreach ($add as $id) { $this->addSchema($id); } } if ($del) { DB::query(sprintf( 'DELETE FROM "MetadataSchemaLink" WHERE "SchemaID" IN (%s)' . ' AND "ParentClass" = \'%s\' AND "ParentID" = %d', implode(', ', $del), Convert::raw2sql(ClassInfo::baseDataClass($this->owner->class)), $this->owner->ID )); } } public function flushCache() { $this->schemas = null; } } |