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('
');
$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('