Skip to content

Commit

Permalink
FIX Add SiteTree link tracking as an extension, and apply to SiteTree…
Browse files Browse the repository at this point in the history
… itself
  • Loading branch information
Hamish Friedlander authored and chillu committed Aug 9, 2013
1 parent 8ad00a1 commit 71608f0
Show file tree
Hide file tree
Showing 3 changed files with 139 additions and 22 deletions.
23 changes: 1 addition & 22 deletions code/model/SiteTree.php
Expand Up @@ -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(
Expand Down Expand Up @@ -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');
}

Expand Down
127 changes: 127 additions & 0 deletions code/model/SiteTreeLinkTracking.php
@@ -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;

This comment has been minimized.

Copy link
@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);
}
}
11 changes: 11 additions & 0 deletions tests/model/SiteTreeHTMLEditorFieldTest.php
Expand Up @@ -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(),
Expand All @@ -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.');
}

Expand All @@ -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.');
}

Expand All @@ -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');
Expand All @@ -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');
Expand All @@ -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.'
);
Expand All @@ -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);

Expand All @@ -102,6 +112,7 @@ public function testBrokenLinkTracking() {
));
$sitetree->HasBrokenLink = false;
$editor->saveInto($sitetree);
$sitetree->write();

$this->assertFalse((bool) $sitetree->HasBrokenLink);
}
Expand Down

0 comments on commit 71608f0

Please sign in to comment.