Permalink
Browse files

FIX Add SiteTree link tracking as an extension, and apply to SiteTree…

… itself
  • Loading branch information...
1 parent 8ad00a1 commit 71608f0d4af1f6000dd08eee00a381fb2397bc97 Hamish Friedlander committed with chillu Aug 6, 2013
Showing with 139 additions and 22 deletions.
  1. +1 −22 code/model/SiteTree.php
  2. +127 −0 code/model/SiteTreeLinkTracking.php
  3. +11 −0 tests/model/SiteTreeHTMLEditorFieldTest.php
View
@@ -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 @@ protected function onBeforeWrite() {
}
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');
}
@@ -0,0 +1,127 @@
+<?php
+
+/**
+ * Adds tracking of links in any HTMLText fields which reference SiteTree or File items
+ *
+ * Attaching this to any DataObject will add four fields which contain all links to SiteTree and File items
+ * referenced in any HTMLText fields, and two booleans to indicate if there are any broken links
+ *
+ * Call augmentSyncLinkTracking to update those fields with any changes to those fields
+ */
+class SiteTreeLinkTracking extends DataExtension {
+
+ private static $db = array(
+ "HasBrokenFile" => "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;
@kmayo-ss

kmayo-ss Jul 22, 2014

Contributor

Just wondered what is the purpose of the 2 lines above where the HasBroken properties are set to false.

If you have a SiteTree object and you set HasBrokenLink or HasBrokenFile to true and then try to write the object onBeforeWrite will be called in SiteTree via the syncLinkTracking method which will end up calling these lines meaning you will not be able to set these properties to true

+
+ // 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);
+ }
+}
@@ -13,12 +13,14 @@ public function testLinkTracking() {
$editor->setValue("<a href=\"[sitetree_link,id=$aboutID]\">Example Link</a>");
$editor->saveInto($sitetree);
+ $sitetree->write();
$this->assertEquals(array($aboutID => $aboutID), $sitetree->LinkTracking()->getIdList(), 'Basic link tracking works.');
$editor->setValue (
"<a href=\"[sitetree_link,id=$aboutID]\"></a><a href=\"[sitetree_link,id=$contactID]\"></a>"
);
$editor->saveInto($sitetree);
+ $sitetree->write();
$this->assertEquals (
array($aboutID => $aboutID, $contactID => $contactID),
$sitetree->LinkTracking()->getIdList(),
@@ -27,6 +29,7 @@ public function testLinkTracking() {
$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 @@ public function testFileLinkTracking() {
$editor->setValue('<a href="assets/example.pdf">Example File</a>');
$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 @@ public function testImageInsertion() {
$editor->setValue('<img src="assets/example.jpg" />');
$editor->saveInto($sitetree);
+ $sitetree->write();
$parser = new CSSContentParser($sitetree->Content);
$xml = $parser->getByXpath('//img');
@@ -60,6 +66,7 @@ public function testImageInsertion() {
$editor->setValue('<img src="assets/example.jpg" alt="foo" title="bar" />');
$editor->saveInto($sitetree);
+ $sitetree->write();
$parser = new CSSContentParser($sitetree->Content);
$xml = $parser->getByXpath('//img');
@@ -74,12 +81,14 @@ public function testImageTracking() {
$editor->setValue('<img src="assets/example.jpg" />');
$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 @@ public function testBrokenLinkTracking() {
$editor->setValue('<p><a href="[sitetree_link,id=0]">Broken Link</a></p>');
$editor->saveInto($sitetree);
+ $sitetree->write();
$this->assertTrue($sitetree->HasBrokenLink);
@@ -102,6 +112,7 @@ public function testBrokenLinkTracking() {
));
$sitetree->HasBrokenLink = false;
$editor->saveInto($sitetree);
+ $sitetree->write();
$this->assertFalse((bool) $sitetree->HasBrokenLink);
}

0 comments on commit 71608f0

Please sign in to comment.