diff --git a/code/model/SiteTree.php b/code/model/SiteTree.php index 54eb6f4b..898b6955 100644 --- a/code/model/SiteTree.php +++ b/code/model/SiteTree.php @@ -163,6 +163,7 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid private static $extensions = array( "Hierarchy", "Versioned('Stage', 'Live')", + "SiteTreeLinkTracking" ); private static $searchable_fields = array( @@ -1469,28 +1470,6 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid } public function syncLinkTracking() { - // Build a list of HTMLText fields - $allFields = $this->db(); - $htmlFields = array(); - foreach($allFields as $field => $fieldSpec) { - if(preg_match('/([^(]+)/', $fieldSpec, $matches)) { - $class = $matches[0]; - if(class_exists($class)){ - if($class == 'HTMLText' || is_subclass_of($class, 'HTMLText')) $htmlFields[] = $field; - } - } - } - - $linkedPages = array(); - $linkedFiles = array(); - $this->HasBrokenLink = false; - $this->HasBrokenFile = false; - - foreach($htmlFields as $field) { - $formField = new HTMLEditorField($field); - $formField->setValue($this->$field); - $formField->saveInto($this); - } $this->extend('augmentSyncLinkTracking'); } diff --git a/code/model/SiteTreeLinkTracking.php b/code/model/SiteTreeLinkTracking.php new file mode 100644 index 00000000..c5a5c11f --- /dev/null +++ b/code/model/SiteTreeLinkTracking.php @@ -0,0 +1,127 @@ + "Boolean", + "HasBrokenLink" => "Boolean" + ); + + private static $many_many = array( + "LinkTracking" => "SiteTree", + "ImageTracking" => "File" + ); + + private static $many_many_extraFields = array( + "LinkTracking" => array("FieldName" => "Varchar"), + "ImageTracking" => array("FieldName" => "Varchar") + ); + + function trackLinksInField($field) { + $record = $this->owner; + + $linkedPages = array(); + $linkedFiles = array(); + + $htmlValue = Injector::inst()->create('HTMLValue', $record->$field); + + // Populate link tracking for internal links & links to asset files. + if($links = $htmlValue->getElementsByTagName('a')) foreach($links as $link) { + $href = Director::makeRelative($link->getAttribute('href')); + + if($href) { + if(preg_match('/\[sitetree_link,id=([0-9]+)\]/i', $href, $matches)) { + $ID = $matches[1]; + + // clear out any broken link classes + if($class = $link->getAttribute('class')) { + $link->setAttribute('class', + preg_replace('/(^ss-broken|ss-broken$| ss-broken )/', null, $class)); + } + + $linkedPages[] = $ID; + if(!DataObject::get_by_id('SiteTree', $ID)) $record->HasBrokenLink = true; + + } else if(substr($href, 0, strlen(ASSETS_DIR) + 1) == ASSETS_DIR.'/') { + $candidateFile = File::find(Convert::raw2sql(urldecode($href))); + if($candidateFile) { + $linkedFiles[] = $candidateFile->ID; + } else { + $record->HasBrokenFile = true; + } + } else if($href == '' || $href[0] == '/') { + $record->HasBrokenLink = true; + } + } + } + + // Add file tracking for image references + if($images = $htmlValue->getElementsByTagName('img')) foreach($images as $img) { + if($image = File::find($path = urldecode(Director::makeRelative($img->getAttribute('src'))))) { + $linkedFiles[] = $image->ID; + } + else { + if(substr($path, 0, strlen(ASSETS_DIR) + 1) == ASSETS_DIR . '/') { + $record->HasBrokenFile = true; + } + } + } + + // Update the "LinkTracking" many_many + if($record->ID && $record->many_many('LinkTracking') && $tracker = $record->LinkTracking()) { + $tracker->removeByFilter(sprintf( + '"FieldName" = \'%s\' AND "%s" = %d', + $field, + $tracker->getForeignKey(), + $record->ID + )); + + if($linkedPages) foreach($linkedPages as $item) { + $tracker->add($item, array('FieldName' => $field)); + } + } + + // Update the "ImageTracking" many_many + if($record->ID && $record->many_many('ImageTracking') && $tracker = $record->ImageTracking()) { + $tracker->removeByFilter(sprintf( + '"FieldName" = \'%s\' AND "%s" = %d', + $field, + $tracker->getForeignKey(), + $record->ID + )); + + if($linkedFiles) foreach($linkedFiles as $item) { + $tracker->add($item, array('FieldName' => $field)); + } + } + } + + + function augmentSyncLinkTracking() { + // Reset boolean broken flags + $this->owner->HasBrokenLink = false; + $this->owner->HasBrokenFile = false; + + // Build a list of HTMLText fields + $allFields = $this->owner->db(); + $htmlFields = array(); + foreach($allFields as $field => $fieldSpec) { + if(preg_match('/([^(]+)/', $fieldSpec, $matches)) { + $class = $matches[0]; + if(class_exists($class)){ + if($class == 'HTMLText' || is_subclass_of($class, 'HTMLText')) $htmlFields[] = $field; + } + } + } + + foreach($htmlFields as $field) $this->trackLinksInField($field); + } +} \ No newline at end of file diff --git a/tests/model/SiteTreeHTMLEditorFieldTest.php b/tests/model/SiteTreeHTMLEditorFieldTest.php index cf2d4107..ee289164 100644 --- a/tests/model/SiteTreeHTMLEditorFieldTest.php +++ b/tests/model/SiteTreeHTMLEditorFieldTest.php @@ -13,12 +13,14 @@ class SiteTreeHtmlEditorFieldTest extends FunctionalTest { $editor->setValue("Example Link"); $editor->saveInto($sitetree); + $sitetree->write(); $this->assertEquals(array($aboutID => $aboutID), $sitetree->LinkTracking()->getIdList(), 'Basic link tracking works.'); $editor->setValue ( "" ); $editor->saveInto($sitetree); + $sitetree->write(); $this->assertEquals ( array($aboutID => $aboutID, $contactID => $contactID), $sitetree->LinkTracking()->getIdList(), @@ -27,6 +29,7 @@ class SiteTreeHtmlEditorFieldTest extends FunctionalTest { $editor->setValue(null); $editor->saveInto($sitetree); + $sitetree->write(); $this->assertEquals(array(), $sitetree->LinkTracking()->getIdList(), 'Link tracking is removed when links are.'); } @@ -37,12 +40,14 @@ class SiteTreeHtmlEditorFieldTest extends FunctionalTest { $editor->setValue('Example File'); $editor->saveInto($sitetree); + $sitetree->write(); $this->assertEquals ( array($fileID => $fileID), $sitetree->ImageTracking()->getIDList(), 'Links to assets are tracked.' ); $editor->setValue(null); $editor->saveInto($sitetree); + $sitetree->write(); $this->assertEquals(array(), $sitetree->ImageTracking()->getIdList(), 'Asset tracking is removed with links.'); } @@ -52,6 +57,7 @@ class SiteTreeHtmlEditorFieldTest extends FunctionalTest { $editor->setValue(''); $editor->saveInto($sitetree); + $sitetree->write(); $parser = new CSSContentParser($sitetree->Content); $xml = $parser->getByXpath('//img'); @@ -60,6 +66,7 @@ class SiteTreeHtmlEditorFieldTest extends FunctionalTest { $editor->setValue('foo'); $editor->saveInto($sitetree); + $sitetree->write(); $parser = new CSSContentParser($sitetree->Content); $xml = $parser->getByXpath('//img'); @@ -74,12 +81,14 @@ class SiteTreeHtmlEditorFieldTest extends FunctionalTest { $editor->setValue(''); $editor->saveInto($sitetree); + $sitetree->write(); $this->assertEquals ( array($fileID => $fileID), $sitetree->ImageTracking()->getIDList(), 'Inserted images are tracked.' ); $editor->setValue(null); $editor->saveInto($sitetree); + $sitetree->write(); $this->assertEquals ( array(), $sitetree->ImageTracking()->getIDList(), 'Tracked images are deleted when removed.' ); @@ -93,6 +102,7 @@ class SiteTreeHtmlEditorFieldTest extends FunctionalTest { $editor->setValue('

Broken Link

'); $editor->saveInto($sitetree); + $sitetree->write(); $this->assertTrue($sitetree->HasBrokenLink); @@ -102,6 +112,7 @@ class SiteTreeHtmlEditorFieldTest extends FunctionalTest { )); $sitetree->HasBrokenLink = false; $editor->saveInto($sitetree); + $sitetree->write(); $this->assertFalse((bool) $sitetree->HasBrokenLink); }