FEATURE: Added SS_HTMLValue to provide a simple wrapper around DOMDocument to allow the manipulation of HTML snippets.

FEATURE: Re-instated broken link highlighting by manually checking all shortcodes in HtmlEditorField->Field(), and adding a class to broken ones.

From: Andrew Short <andrewjshort@gmail.com>

git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/trunk@88510 467b73ca-7a2a-4603-9d3b-597d59a354a9
This commit is contained in:
Andrew Short 2009-10-11 00:07:27 +00:00 committed by Sam Minnee
parent 8546a6d194
commit 9b4d9993a4
3 changed files with 110 additions and 19 deletions

View File

@ -32,18 +32,32 @@ class HtmlEditorField extends TextareaField {
* @return string
*/
function Field() {
// mark up broken links
$value = new SS_HTMLValue($this->value);
if($links = $value->getElementsByTagName('a')) foreach($links as $link) {
$matches = array();
if(preg_match('/\[sitetree_link id=([0-9]+)\]/i', $link->getAttribute('href'), $matches)) {
if(!DataObject::get_by_id('SiteTree', $matches[1])) {
$class = $link->getAttribute('class');
$link->setAttribute('class', ($class ? "$class ss-broken" : 'ss-broken'));
}
}
}
return $this->createTag (
'textarea',
array (
'class' => $this->extraClass(),
'rows' => $this->rows,
'cols' => $this->cols,
'style' => "width: 97%; height: " . ($this->rows * 16) . "px", // Prevents horizontal scrollbars
'style' => 'width: 97%; height: ' . ($this->rows * 16) . 'px', // prevents horizontal scrollbars
'tinymce' => 'true',
'id' => $this->id(),
'name' => $this->name
),
htmlentities($this->value, ENT_COMPAT, 'UTF-8')
htmlentities($value->getContent(), ENT_COMPAT, 'UTF-8')
);
}
@ -54,35 +68,38 @@ class HtmlEditorField extends TextareaField {
);
}
$value = $this->value ? $this->value : '<p></p>';
$value = preg_replace('/src="([^\?]*)\?r=[0-9]+"/i', 'src="$1"', $value);
$linkedPages = array();
$linkedFiles = array();
$document = new DOMDocument(null, 'UTF-8');
$document->strictErrorChecking = false;
$document->loadHTML($value);
$htmlValue = new SS_HTMLValue($this->value);
// Populate link tracking for internal links & links to asset files.
if($links = $document->getElementsByTagName('a')) foreach($links as $link) {
$link = Director::makeRelative($link->getAttribute('href'));
if($links = $htmlValue->getElementsByTagName('a')) foreach($links as $link) {
$href = Director::makeRelative($link->getAttribute('href'));
if(preg_match('/\[sitetree_link id=([0-9]+)\]/i', $link, $matches)) {
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));
}
if($page = DataObject::get_by_id('SiteTree', $ID)) {
$linkedPages[] = $page->ID;
} else {
$record->HasBrokenLink = true;
}
} elseif($link[0] != '/' && $file = File::find($link)) {
} elseif($href[0] != '/' && $file = File::find($href)) {
$linkedFiles[] = $file->ID;
}
}
// Resample images, add default attributes and add to assets tracking.
if($images = $document->getElementsByTagName('img')) foreach($images as $img) {
if($images = $htmlValue->getElementsByTagName('img')) foreach($images as $img) {
// strip any ?r=n data from the src attribute
$img->setAttribute('src', preg_replace('/([^\?]*)\?r=[0-9]+$/i', '$1', $img->getAttribute('src')));
if(!$image = File::find($path = Director::makeRelative($img->getAttribute('src')))) {
if(substr($path, 0, strlen(ASSETS_DIR) + 1) == ASSETS_DIR . '/') {
$record->HasBrokenFile = true;
@ -124,7 +141,7 @@ class HtmlEditorField extends TextareaField {
}
}
$record->{$this->name} = substr(simplexml_import_dom($document)->body->asXML(), 6, -7);
$record->{$this->name} = $htmlValue->getContent();
}
/**

78
integration/HTMLValue.php Executable file
View File

@ -0,0 +1,78 @@
<?php
/**
* This class acts as a wrapper around the built in DOMDocument class in order to use it to manage a HTML snippet,
* rather than a whole document, while still exposing the DOMDocument API.
*
* @package sapphire
* @subpackage integration
*/
class SS_HTMLValue extends ViewableData {
/**
* @var DOMDocument
*/
protected $document;
/**
* @param string $content
*/
public function __construct($content = null) {
$this->document = new DOMDocument('1.0', 'UTF-8');
$this->document->scrictErrorChecking = false;
$this->setContent($content);
parent::__construct();
}
/**
* @return string
*/
public function getContent() {
// strip the body tags from the output (which are automatically added by DOMDocument)
return preg_replace (
array (
'/^\s*<body[^>]*>/i',
'/<\/body[^>]*>\s*$/i'
),
null,
$this->getDocument()->saveXML($this->getDocument()->documentElement->lastChild)
);
}
/**
* @param string $content
* @return bool
*/
public function setContent($content) {
return $this->getDocument()->loadHTML(
'<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head>' .
"<body>$content</body></html>"
);
}
/**
* @return DOMDocument
*/
public function getDocument() {
return $this->document;
}
/**
* A simple convenience wrapper around DOMDocument::getElementsByTagName().
*
* @param string $name
* @return DOMNodeList
*/
public function getElementsByTagName($name) {
return $this->getDocument()->getElementsByTagName($name);
}
/**
* @see HTMLValue::getContent()
*/
public function forTemplate() {
return $this->getContent();
}
}

6
tests/forms/HtmlEditorFieldTest.php Normal file → Executable file
View File

@ -13,10 +13,6 @@ class HtmlEditorFieldTest extends FunctionalTest {
$sitetree = new SiteTree();
$editor = new HtmlEditorField('Content');
$editor->setValue('Un-enclosed Content');
$editor->saveInto($sitetree);
$this->assertEquals('<p>Un-enclosed Content</p>', $sitetree->Content, 'Un-enclosed content is put in p tags.');
$editor->setValue('<p class="foo">Simple Content</p>');
$editor->saveInto($sitetree);
$this->assertEquals('<p class="foo">Simple Content</p>', $sitetree->Content, 'Attributes are preserved.');
@ -32,7 +28,7 @@ class HtmlEditorFieldTest extends FunctionalTest {
$editor->setValue(null);
$editor->saveInto($sitetree);
$this->assertEquals('<p/>', $sitetree->Content, 'Doesn\'t choke on null values.');
$this->assertEquals('', $sitetree->Content, "Doesn't choke on empty/null values.");
}
public function testLinkTracking() {