2007-07-19 12:40:28 +02:00
|
|
|
<?php
|
2015-08-30 07:02:55 +02:00
|
|
|
|
2016-06-15 06:03:16 +02:00
|
|
|
namespace SilverStripe\ORM\FieldType;
|
2015-08-30 07:02:55 +02:00
|
|
|
|
|
|
|
use Injector;
|
|
|
|
use HTTP;
|
2016-05-06 01:43:47 +02:00
|
|
|
use HTMLEditorField;
|
2016-06-17 08:49:23 +02:00
|
|
|
use ShortcodeParser;
|
2015-08-30 07:02:55 +02:00
|
|
|
use TextField;
|
|
|
|
|
2008-02-25 03:10:37 +01:00
|
|
|
/**
|
|
|
|
* Represents a large text field that contains HTML content.
|
2010-10-15 01:58:09 +02:00
|
|
|
* This behaves similarly to {@link Text}, but the template processor won't escape any HTML content within it.
|
2014-08-15 08:53:05 +02:00
|
|
|
*
|
2016-06-03 10:51:02 +02:00
|
|
|
* Options can be specified in a $db config via one of the following:
|
2016-07-12 08:51:08 +02:00
|
|
|
* - "HTMLFragment(['shortcodes' => true, 'whitelist' => 'meta,link'])"
|
|
|
|
* - "HTMLFragment(['whitelist' => 'meta,link'])"
|
|
|
|
* - "HTMLFragment(['shortcodes' => true])". "HTMLText" is also a synonym for this.
|
|
|
|
* - "HTMLFragment(['shortcodes' => true])"
|
2016-06-03 10:51:02 +02:00
|
|
|
*
|
2010-10-15 01:58:09 +02:00
|
|
|
* @see HTMLVarchar
|
|
|
|
* @see Text
|
|
|
|
* @see Varchar
|
2014-08-15 08:53:05 +02:00
|
|
|
*
|
2012-04-12 08:02:46 +02:00
|
|
|
* @package framework
|
2016-06-15 06:03:16 +02:00
|
|
|
* @subpackage orm
|
2008-02-25 03:10:37 +01:00
|
|
|
*/
|
2015-08-30 07:02:55 +02:00
|
|
|
class DBHTMLText extends DBText {
|
2013-03-21 19:48:54 +01:00
|
|
|
private static $escape_type = 'xml';
|
2009-02-12 10:38:43 +01:00
|
|
|
|
2013-03-21 19:48:54 +01:00
|
|
|
private static $casting = array(
|
2016-06-03 10:51:02 +02:00
|
|
|
"AbsoluteLinks" => "HTMLFragment",
|
2016-06-17 08:49:23 +02:00
|
|
|
// DBString conversion / summary methods
|
|
|
|
// Not overridden, but returns HTML instead of plain text.
|
2016-06-03 10:51:02 +02:00
|
|
|
"LowerCase" => "HTMLFragment",
|
|
|
|
"UpperCase" => "HTMLFragment",
|
2012-01-31 16:22:22 +01:00
|
|
|
);
|
|
|
|
|
2016-06-03 10:51:02 +02:00
|
|
|
/**
|
|
|
|
* Enable shortcode parsing on this field
|
|
|
|
*
|
|
|
|
* @var bool
|
|
|
|
*/
|
|
|
|
protected $processShortcodes = false;
|
2013-03-11 21:52:30 +01:00
|
|
|
|
2016-06-03 10:51:02 +02:00
|
|
|
/**
|
|
|
|
* Check if shortcodes are enabled
|
|
|
|
*
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
public function getProcessShortcodes() {
|
|
|
|
return $this->processShortcodes;
|
|
|
|
}
|
2014-02-16 23:22:26 +01:00
|
|
|
|
2016-06-03 10:51:02 +02:00
|
|
|
/**
|
|
|
|
* Set shortcodes on or off by default
|
|
|
|
*
|
|
|
|
* @param bool $process
|
|
|
|
* @return $this
|
|
|
|
*/
|
|
|
|
public function setProcessShortcodes($process) {
|
|
|
|
$this->processShortcodes = (bool)$process;
|
|
|
|
return $this;
|
|
|
|
}
|
2014-02-16 23:22:26 +01:00
|
|
|
|
2016-06-03 10:51:02 +02:00
|
|
|
/**
|
|
|
|
* List of html properties to whitelist
|
|
|
|
*
|
|
|
|
* @var array
|
|
|
|
*/
|
|
|
|
protected $whitelist = [];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* List of html properties to whitelist
|
|
|
|
*
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
public function getWhitelist() {
|
|
|
|
return $this->whitelist;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set list of html properties to whitelist
|
|
|
|
*
|
|
|
|
* @param array $whitelist
|
|
|
|
* @return $this
|
|
|
|
*/
|
|
|
|
public function setWhitelist($whitelist) {
|
|
|
|
if(!is_array($whitelist)) {
|
|
|
|
$whitelist = preg_split('/\s*,\s*/', $whitelist);
|
|
|
|
}
|
|
|
|
$this->whitelist = $whitelist;
|
|
|
|
return $this;
|
2014-02-16 23:22:26 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param array $options
|
|
|
|
*
|
|
|
|
* Options accepted in addition to those provided by Text:
|
|
|
|
*
|
|
|
|
* - shortcodes: If true, shortcodes will be turned into the appropriate HTML.
|
|
|
|
* If false, shortcodes will not be processed.
|
|
|
|
*
|
|
|
|
* - whitelist: If provided, a comma-separated list of elements that will be allowed to be stored
|
|
|
|
* (be careful on relying on this for XSS protection - some seemingly-safe elements allow
|
|
|
|
* attributes that can be exploited, for instance <img onload="exploiting_code();" src="..." />)
|
2014-04-09 04:48:02 +02:00
|
|
|
* Text nodes outside of HTML tags are filtered out by default, but may be included by adding
|
|
|
|
* the text() directive. E.g. 'link,meta,text()' will allow only <link /> <meta /> and text at
|
|
|
|
* the root level.
|
2016-06-03 10:51:02 +02:00
|
|
|
*
|
|
|
|
* @return $this
|
2014-02-16 23:22:26 +01:00
|
|
|
*/
|
2013-03-11 21:52:30 +01:00
|
|
|
public function setOptions(array $options = array()) {
|
|
|
|
if(array_key_exists("shortcodes", $options)) {
|
2016-06-03 10:51:02 +02:00
|
|
|
$this->setProcessShortcodes(!!$options["shortcodes"]);
|
2013-03-11 21:52:30 +01:00
|
|
|
}
|
2014-02-16 23:22:26 +01:00
|
|
|
|
|
|
|
if(array_key_exists("whitelist", $options)) {
|
2016-06-03 10:51:02 +02:00
|
|
|
$this->setWhitelist($options['whitelist']);
|
2014-02-16 23:22:26 +01:00
|
|
|
}
|
2016-06-03 10:51:02 +02:00
|
|
|
|
|
|
|
return parent::setOptions($options);
|
|
|
|
}
|
|
|
|
|
2016-06-30 16:46:23 +02:00
|
|
|
public function RAW() {
|
|
|
|
if ($this->processShortcodes) {
|
|
|
|
return ShortcodeParser::get_active()->parse($this->value);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return $this->value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-10 10:04:03 +02:00
|
|
|
/**
|
|
|
|
* Return the value of the field with relative links converted to absolute urls (with placeholders parsed).
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
public function AbsoluteLinks() {
|
|
|
|
return HTTP::absoluteURLs($this->forTemplate());
|
|
|
|
}
|
|
|
|
|
2009-10-11 02:06:58 +02:00
|
|
|
public function forTemplate() {
|
2016-06-17 08:49:23 +02:00
|
|
|
// Suppress XML encoding for DBHtmlText
|
2016-06-30 16:46:23 +02:00
|
|
|
return $this->RAW();
|
2009-10-11 02:06:58 +02:00
|
|
|
}
|
2014-02-16 23:22:26 +01:00
|
|
|
|
2016-06-03 10:51:02 +02:00
|
|
|
/**
|
|
|
|
* Safely escape for XML string
|
|
|
|
*
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
public function CDATA() {
|
|
|
|
return sprintf(
|
|
|
|
'<![CDATA[%s]]>',
|
|
|
|
str_replace(']]>', ']]]]><![CDATA[>', $this->RAW())
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2014-02-16 23:22:26 +01:00
|
|
|
public function prepValueForDB($value) {
|
2014-04-09 04:48:02 +02:00
|
|
|
return parent::prepValueForDB($this->whitelistContent($value));
|
|
|
|
}
|
2014-08-15 08:53:05 +02:00
|
|
|
|
2014-04-09 04:48:02 +02:00
|
|
|
/**
|
|
|
|
* Filter the given $value string through the whitelist filter
|
2014-08-15 08:53:05 +02:00
|
|
|
*
|
2014-04-09 04:48:02 +02:00
|
|
|
* @param string $value Input html content
|
|
|
|
* @return string Value with all non-whitelisted content stripped (if applicable)
|
|
|
|
*/
|
|
|
|
public function whitelistContent($value) {
|
2014-02-16 23:22:26 +01:00
|
|
|
if($this->whitelist) {
|
|
|
|
$dom = Injector::inst()->create('HTMLValue', $value);
|
|
|
|
|
|
|
|
$query = array();
|
2014-04-09 04:48:02 +02:00
|
|
|
$textFilter = ' | //body/text()';
|
|
|
|
foreach ($this->whitelist as $tag) {
|
|
|
|
if($tag === 'text()') {
|
|
|
|
$textFilter = ''; // Disable text filter if allowed
|
|
|
|
} else {
|
|
|
|
$query[] = 'not(self::'.$tag.')';
|
|
|
|
}
|
|
|
|
}
|
2014-02-16 23:22:26 +01:00
|
|
|
|
2014-04-09 04:48:02 +02:00
|
|
|
foreach($dom->query('//body//*['.implode(' and ', $query).']'.$textFilter) as $el) {
|
2014-02-16 23:22:26 +01:00
|
|
|
if ($el->parentNode) $el->parentNode->removeChild($el);
|
|
|
|
}
|
|
|
|
|
|
|
|
$value = $dom->getContent();
|
|
|
|
}
|
2014-04-09 04:48:02 +02:00
|
|
|
return $value;
|
2014-02-16 23:22:26 +01:00
|
|
|
}
|
|
|
|
|
2016-06-17 08:49:23 +02:00
|
|
|
public function scaffoldFormField($title = null) {
|
2016-05-06 01:43:47 +02:00
|
|
|
return new HTMLEditorField($this->name, $title);
|
2008-08-06 04:43:46 +02:00
|
|
|
}
|
2014-08-15 08:53:05 +02:00
|
|
|
|
2016-06-17 08:49:23 +02:00
|
|
|
public function scaffoldSearchField($title = null) {
|
2008-08-06 04:43:46 +02:00
|
|
|
return new TextField($this->name, $title);
|
|
|
|
}
|
2007-07-19 12:40:28 +02:00
|
|
|
|
2016-06-03 10:51:02 +02:00
|
|
|
/**
|
2016-06-17 08:49:23 +02:00
|
|
|
* Get plain-text version
|
|
|
|
*
|
2016-06-03 10:51:02 +02:00
|
|
|
* @return string
|
|
|
|
*/
|
2016-06-17 08:49:23 +02:00
|
|
|
public function Plain() {
|
2016-06-03 10:51:02 +02:00
|
|
|
// Preserve line breaks
|
|
|
|
$text = preg_replace('/\<br(\s*)?\/?\>/i', "\n", $this->RAW());
|
2016-06-17 08:49:23 +02:00
|
|
|
|
|
|
|
// Convert paragraph breaks to multi-lines
|
|
|
|
$text = preg_replace('/\<\/p\>/i', "\n\n", $text);
|
|
|
|
|
|
|
|
// Strip out HTML tags
|
|
|
|
$text = strip_tags($text);
|
|
|
|
|
|
|
|
// Implode >3 consecutive linebreaks into 2
|
|
|
|
$text = preg_replace('~(\R){2,}~', "\n\n", $text);
|
|
|
|
|
|
|
|
// Decode HTML entities back to plain text
|
|
|
|
return trim(\Convert::xml2raw($text));
|
2016-06-03 10:51:02 +02:00
|
|
|
}
|
2012-02-12 21:22:11 +01:00
|
|
|
|
2016-06-03 10:51:02 +02:00
|
|
|
}
|