Source of file FileLinkTracking.php
Size: 7,461 Bytes - Last Modified: 2021-12-23T10:27:40+00:00
/var/www/docs.ssmods.com/process/src/src/Shortcodes/FileLinkTracking.php
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239 | <?php namespace SilverStripe\Assets\Shortcodes; use DOMElement; use SilverStripe\Assets\File; use SilverStripe\Dev\Deprecation; use SilverStripe\Forms\FieldList; use SilverStripe\Forms\FormScaffolder; use SilverStripe\ORM\DataExtension; use SilverStripe\ORM\DataObject; use SilverStripe\ORM\FieldType\DBHTMLText; use SilverStripe\ORM\ManyManyList; use SilverStripe\Versioned\Versioned; use SilverStripe\View\Parsers\HTMLValue; /** * Adds tracking of links in any HTMLText fields which reference SiteTree or File items. * * Attaching this to any DataObject will a relation which links to File items * referenced in any HTMLText fields, and a boolean to indicate if there are any broken file links. Call * augmentSyncFileLinkTracking to update those fields with any changes to those fields. * * Note that since both SiteTree and File are versioned, LinkTracking and FileTracking will * only be enabled for the Stage record. * * @property DataObject|FileLinkTracking $owner * @method ManyManyList|File[] FileTracking() List of files linked on this dataobject */ class FileLinkTracking extends DataExtension { /** * @var FileLinkTrackingParser */ protected $fileParser; /** * Inject parser for each page * * @var array * @config */ private static $dependencies = [ 'FileParser' => '%$' . FileLinkTrackingParser::class, ]; private static $owns = [ 'FileTracking', ]; private static $many_many = [ 'FileTracking' => [ 'through' => FileLink::class, 'from' => 'Parent', 'to' => 'Linked', ], ]; /** * Controls visibility of the File Tracking tab * * @config * @see linktracking.yml * @var boolean */ private static $show_file_link_tracking = false; /** * @deprecated 4.2..5.0 Use FileTracking() instead * @return File[]|ManyManyList */ public function ImageTracking() { Deprecation::notice('5.0', 'Use FileTracking() instead'); return $this->FileTracking(); } /** * FileParser for link tracking * * @return FileLinkTrackingParser */ public function getFileParser() { return $this->fileParser; } /** * @param FileLinkTrackingParser $parser * @return $this */ public function setFileParser(FileLinkTrackingParser $parser = null) { $this->fileParser = $parser; return $this; } public function onBeforeWrite() { // Trigger link tracking // Note: SiteTreeLinkTracking::onBeforeWrite() has a check to // prevent this being triggered multiple times on a single write. $this->owner->syncLinkTracking(); } /** * Public method to call when triggering symlink extension. Can be called externally, * or overridden by class implementations. * * {@see FileLinkTracking::augmentSyncLinkTracking} */ public function syncLinkTracking() { $this->owner->extend('augmentSyncLinkTracking'); } /** * Find HTMLText fields on {@link owner} to scrape for links that need tracking */ public function augmentSyncLinkTracking() { // If owner is versioned, skip tracking on live if (class_exists(Versioned::class) && Versioned::get_stage() == Versioned::LIVE && $this->owner->hasExtension(Versioned::class) ) { return; } // Build a list of HTMLText fields, merging all linked pages together. $allFields = DataObject::getSchema()->fieldSpecs($this->owner); $linkedPages = []; $anyBroken = false; foreach ($allFields as $field => $fieldSpec) { $fieldObj = $this->owner->dbObject($field); if ($fieldObj instanceof DBHTMLText) { // Merge links in this field with global list. $linksInField = $this->trackLinksInField($field, $anyBroken); $linkedPages = array_merge($linkedPages, $linksInField); } } // Soft support for HasBrokenFile db field (e.g. SiteTree) if ($this->owner->hasField('HasBrokenFile')) { $this->owner->HasBrokenFile = $anyBroken; } // Update the "FileTracking" many_many. $this->owner->FileTracking()->setByIDList($linkedPages); } public function onAfterDelete() { // If owner is versioned, skip tracking on live if (class_exists(Versioned::class) && Versioned::get_stage() == Versioned::LIVE && $this->owner->hasExtension(Versioned::class) ) { return; } $this->owner->FileTracking()->removeAll(); } /** * Scrape the content of a field to detect anly links to local SiteTree pages or files * * @param string $fieldName The name of the field on {@link @owner} to scrape * @param bool &$anyBroken Will be flagged to true (by reference) if a link is broken. * @return int[] Array of page IDs found (associative array) */ public function trackLinksInField($fieldName, &$anyBroken = false) { // Pull down current field content $record = $this->owner; $htmlValue = HTMLValue::create($record->$fieldName); // Process all links $linkedFiles = []; $links = $this->fileParser->process($htmlValue); foreach ($links as $link) { // Toggle highlight class to element if ($link['DOMReference']) { $this->toggleElementClass($link['DOMReference'], 'ss-broken', $link['Broken']); } // Flag broken if ($link['Broken']) { $anyBroken = true; } // Collect page ids if ($link['Target'] && in_array($link['Type'], ['file', 'image'])) { $fileID = (int)$link['Target']; $linkedFiles[$fileID] = $fileID; } } // Update any changed content $record->$fieldName = $htmlValue->getContent(); return $linkedFiles; } /** * Add the given css class to the DOM element. * * @param DOMElement $domReference Element to modify. * @param string $class Class name to toggle. * @param bool $toggle On or off. */ protected function toggleElementClass(DOMElement $domReference, $class, $toggle) { // Get all existing classes. $classes = array_filter(explode(' ', trim($domReference->getAttribute('class')))); // Add or remove the broken class from the link, depending on the link status. if ($toggle) { $classes = array_unique(array_merge($classes, [$class])); } else { $classes = array_diff($classes, [$class]); } if (!empty($classes)) { $domReference->setAttribute('class', implode(' ', $classes)); } else { $domReference->removeAttribute('class'); } } public function updateCMSFields(FieldList $fields) { if (!$this->owner->config()->get('show_file_link_tracking')) { $fields->removeByName('FileTracking'); } elseif ($this->owner->ID && !$this->owner->getField('FileTracking')) { FormScaffolder::addManyManyRelationshipFields($fields, 'FileTracking', null, true, $this->owner); } } } |