mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-08-26 20:45:47 +02:00
Conflicts: .travis.yml README.md admin/code/LeftAndMain.php admin/css/screen.css admin/scss/screen.scss api/RestfulService.php conf/ConfigureFromEnv.php control/injector/ServiceConfigurationLocator.php control/injector/SilverStripeServiceConfigurationLocator.php core/ClassInfo.php core/Object.php css/AssetUploadField.css css/ComplexTableField_popup.css dev/CSSContentParser.php dev/DevelopmentAdmin.php docs/en/changelogs/index.md docs/en/misc/contributing/code.md docs/en/reference/execution-pipeline.md filesystem/GD.php filesystem/ImagickBackend.php filesystem/Upload.php forms/Form.php forms/FormField.php forms/HtmlEditorConfig.php forms/gridfield/GridFieldDetailForm.php forms/gridfield/GridFieldSortableHeader.php lang/en.yml model/Aggregate.php model/DataList.php model/DataObject.php model/DataQuery.php model/Image.php model/MySQLDatabase.php model/SQLQuery.php model/fieldtypes/HTMLText.php model/fieldtypes/Text.php scss/AssetUploadField.scss search/filters/SearchFilter.php security/Authenticator.php security/LoginForm.php security/Member.php security/MemberAuthenticator.php security/MemberLoginForm.php security/Security.php tests/behat/features/bootstrap/SilverStripe/Framework/Test/Behaviour/CmsFormsContext.php tests/control/HTTPTest.php tests/control/RequestHandlingTest.php tests/filesystem/UploadTest.php tests/forms/FormTest.php tests/forms/NumericFieldTest.php tests/model/DataListTest.php tests/model/DataObjectTest.php tests/model/TextTest.php tests/security/MemberAuthenticatorTest.php tests/security/SecurityDefaultAdminTest.php tests/view/SSViewerCacheBlockTest.php tests/view/SSViewerTest.php
319 lines
9.1 KiB
PHP
319 lines
9.1 KiB
PHP
<?php
|
|
/**
|
|
* Represents a variable-length string of up to 2 megabytes, designed to store raw text
|
|
*
|
|
* Example definition via {@link DataObject::$db}:
|
|
* <code>
|
|
* static $db = array(
|
|
* "MyDescription" => "Text",
|
|
* );
|
|
* </code>
|
|
*
|
|
* @see HTMLText
|
|
* @see HTMLVarchar
|
|
* @see Varchar
|
|
*
|
|
* @package framework
|
|
* @subpackage model
|
|
*/
|
|
class Text extends StringField {
|
|
|
|
private static $casting = array(
|
|
"AbsoluteLinks" => "Text",
|
|
"BigSummary" => "Text",
|
|
"ContextSummary" => "Text",
|
|
"FirstParagraph" => "Text",
|
|
"FirstSentence" => "Text",
|
|
"LimitCharacters" => "Text",
|
|
"LimitSentences" => "Text",
|
|
"Summary" => "Text",
|
|
'EscapeXML' => 'Text',
|
|
'LimitWordCount' => 'Text',
|
|
'LimitWordCountXML' => 'HTMLText',
|
|
);
|
|
|
|
/**
|
|
* (non-PHPdoc)
|
|
* @see DBField::requireField()
|
|
*/
|
|
public function requireField() {
|
|
$parts = array(
|
|
'datatype' => 'mediumtext',
|
|
'character set' => 'utf8',
|
|
'collate' => 'utf8_general_ci',
|
|
'arrayValue' => $this->arrayValue
|
|
);
|
|
|
|
$values= array(
|
|
'type' => 'text',
|
|
'parts' => $parts
|
|
);
|
|
|
|
DB::require_field($this->tableName, $this->name, $values, $this->default);
|
|
}
|
|
|
|
/**
|
|
* Return the value of the field with relative links converted to absolute urls.
|
|
* @return string
|
|
*/
|
|
public function AbsoluteLinks() {
|
|
return HTTP::absoluteURLs($this->value);
|
|
}
|
|
|
|
/**
|
|
* Limit sentences, can be controlled by passing an integer.
|
|
*
|
|
* @param int $sentCount The amount of sentences you want.
|
|
*/
|
|
public function LimitSentences($sentCount = 2) {
|
|
if(!is_numeric($sentCount)) {
|
|
user_error("Text::LimitSentence() expects one numeric argument", E_USER_NOTICE);
|
|
}
|
|
|
|
$output = array();
|
|
$data = trim(Convert::xml2raw($this->value));
|
|
$sentences = explode('.', $data);
|
|
|
|
if ($sentCount == 0) return '';
|
|
|
|
for($i = 0; $i < $sentCount; $i++) {
|
|
if(isset($sentences[$i])) {
|
|
$sentence = trim($sentences[$i]);
|
|
if(!empty($sentence)) $output[] .= $sentence;
|
|
}
|
|
}
|
|
|
|
return count($output)==0 ? '' : implode($output, '. ') . '.';
|
|
}
|
|
|
|
|
|
/**
|
|
* Caution: Not XML/HTML-safe - does not respect closing tags.
|
|
*/
|
|
public function FirstSentence() {
|
|
$paragraph = Convert::xml2raw( $this->value );
|
|
if( !$paragraph ) return "";
|
|
|
|
$words = preg_split('/\s+/', $paragraph);
|
|
foreach ($words as $i => $word) {
|
|
if (preg_match('/(!|\?|\.)$/', $word) && !preg_match('/(Dr|Mr|Mrs|Ms|Miss|Sr|Jr|No)\.$/i', $word)) {
|
|
return implode(' ', array_slice($words, 0, $i+1));
|
|
}
|
|
}
|
|
|
|
/* If we didn't find a sentence ending, use the summary. We re-call rather than using paragraph so that
|
|
* Summary will limit the result this time */
|
|
return $this->Summary(20);
|
|
}
|
|
|
|
/**
|
|
* Caution: Not XML/HTML-safe - does not respect closing tags.
|
|
*/
|
|
public function Summary($maxWords = 50) {
|
|
// get first sentence?
|
|
// this needs to be more robust
|
|
$value = Convert::xml2raw( $this->value /*, true*/ );
|
|
if(!$value) return '';
|
|
|
|
// grab the first paragraph, or, failing that, the whole content
|
|
if(strpos($value, "\n\n")) $value = substr($value, 0, strpos($value, "\n\n"));
|
|
$sentences = explode('.', $value);
|
|
$count = count(explode(' ', $sentences[0]));
|
|
|
|
// if the first sentence is too long, show only the first $maxWords words
|
|
if($count > $maxWords) {
|
|
return implode( ' ', array_slice(explode( ' ', $sentences[0] ), 0, $maxWords)) . '...';
|
|
}
|
|
|
|
// add each sentence while there are enough words to do so
|
|
$result = '';
|
|
do {
|
|
$result .= trim(array_shift( $sentences )).'.';
|
|
if(count($sentences) > 0) {
|
|
$count += count(explode(' ', $sentences[0]));
|
|
}
|
|
|
|
// Ensure that we don't trim half way through a tag or a link
|
|
$brokenLink = (
|
|
substr_count($result,'<') != substr_count($result,'>')) ||
|
|
(substr_count($result,'<a') != substr_count($result,'</a')
|
|
);
|
|
} while(($count < $maxWords || $brokenLink) && $sentences && trim( $sentences[0]));
|
|
|
|
if(preg_match('/<a[^>]*>/', $result) && !preg_match( '/<\/a>/', $result)) $result .= '</a>';
|
|
|
|
return Convert::raw2xml($result);
|
|
}
|
|
|
|
/**
|
|
* Performs the same function as the big summary, but doesn't trim new paragraphs off data.
|
|
* Caution: Not XML/HTML-safe - does not respect closing tags.
|
|
*/
|
|
public function BigSummary($maxWords = 50, $plain = true) {
|
|
$result = '';
|
|
|
|
// get first sentence?
|
|
// this needs to be more robust
|
|
$data = $plain ? Convert::xml2raw($this->value, true) : $this->value;
|
|
|
|
if(!$data) return '';
|
|
|
|
$sentences = explode('.', $data);
|
|
$count = count(explode(' ', $sentences[0]));
|
|
|
|
// if the first sentence is too long, show only the first $maxWords words
|
|
if($count > $maxWords) {
|
|
return implode(' ', array_slice(explode( ' ', $sentences[0] ), 0, $maxWords)) . '...';
|
|
}
|
|
|
|
// add each sentence while there are enough words to do so
|
|
do {
|
|
$result .= trim(array_shift($sentences));
|
|
if($sentences) {
|
|
$result .= '. ';
|
|
$count += count(explode(' ', $sentences[0]));
|
|
}
|
|
|
|
// Ensure that we don't trim half way through a tag or a link
|
|
$brokenLink = (
|
|
substr_count($result,'<') != substr_count($result,'>')) ||
|
|
(substr_count($result,'<a') != substr_count($result,'</a')
|
|
);
|
|
} while(($count < $maxWords || $brokenLink) && $sentences && trim($sentences[0]));
|
|
|
|
if(preg_match( '/<a[^>]*>/', $result) && !preg_match( '/<\/a>/', $result)) {
|
|
$result .= '</a>';
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Caution: Not XML/HTML-safe - does not respect closing tags.
|
|
*/
|
|
public function FirstParagraph($plain = 1) {
|
|
// get first sentence?
|
|
// this needs to be more robust
|
|
if($plain && $plain != 'html') {
|
|
$data = Convert::xml2raw($this->value, true);
|
|
if(!$data) return "";
|
|
|
|
// grab the first paragraph, or, failing that, the whole content
|
|
$pos = strpos($data, "\n\n");
|
|
if($pos) $data = substr($data, 0, $pos);
|
|
|
|
return $data;
|
|
} else {
|
|
if(strpos($this->value, "</p>") === false) return $this->value;
|
|
|
|
$data = substr($this->value, 0, strpos($this->value, "</p>") + 4);
|
|
|
|
if(strlen($data) < 20 && strpos($this->value, "</p>", strlen($data))) {
|
|
$data = substr($this->value, 0, strpos( $this->value, "</p>", strlen($data)) + 4 );
|
|
}
|
|
|
|
return $data;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Perform context searching to give some context to searches, optionally
|
|
* highlighting the search term.
|
|
*
|
|
* @param int $characters Number of characters in the summary
|
|
* @param boolean $string Supplied string ("keywords")
|
|
* @param boolean $striphtml Strip HTML?
|
|
* @param boolean $highlight Add a highlight <span> element around search query?
|
|
* @param String prefix text
|
|
* @param String suffix
|
|
*
|
|
* @return string
|
|
*/
|
|
public function ContextSummary($characters = 500, $string = false, $striphtml = true, $highlight = true,
|
|
$prefix = "... ", $suffix = "...") {
|
|
|
|
if(!$string) {
|
|
// Use the default "Search" request variable (from SearchForm)
|
|
$string = isset($_REQUEST['Search']) ? $_REQUEST['Search'] : '';
|
|
}
|
|
|
|
// Remove HTML tags so we don't have to deal with matching tags
|
|
$text = $striphtml ? $this->NoHTML() : $this->value;
|
|
|
|
// Find the search string
|
|
$position = (int) stripos($text, $string);
|
|
|
|
// We want to search string to be in the middle of our block to give it some context
|
|
$position = max(0, $position - ($characters / 2));
|
|
|
|
if($position > 0) {
|
|
// We don't want to start mid-word
|
|
$position = max((int) strrpos(substr($text, 0, $position), ' '),
|
|
(int) strrpos(substr($text, 0, $position), "\n"));
|
|
}
|
|
|
|
$summary = substr($text, $position, $characters);
|
|
$stringPieces = explode(' ', $string);
|
|
|
|
if($highlight) {
|
|
// Add a span around all key words from the search term as well
|
|
if($stringPieces) {
|
|
|
|
foreach($stringPieces as $stringPiece) {
|
|
if(strlen($stringPiece) > 2) {
|
|
$summary = str_ireplace($stringPiece, "<span class=\"highlight\">$stringPiece</span>",
|
|
$summary);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
$summary = trim($summary);
|
|
|
|
if($position > 0) $summary = $prefix . $summary;
|
|
if(strlen($this->value) > ($characters + $position)) $summary = $summary . $suffix;
|
|
|
|
return $summary;
|
|
}
|
|
|
|
/**
|
|
* Allows a sub-class of TextParser to be rendered.
|
|
*
|
|
* @see TextParser for implementation details.
|
|
* @return string
|
|
*/
|
|
public function Parse($parser = "TextParser") {
|
|
if($parser == "TextParser" || is_subclass_of($parser, "TextParser")) {
|
|
$obj = new $parser($this->value);
|
|
return $obj->parse();
|
|
} else {
|
|
// Fallback to using raw2xml and show a warning
|
|
// TODO Don't kill script execution, we can continue without losing complete control of the app
|
|
user_error("Couldn't find an appropriate TextParser sub-class to create (Looked for '$parser')."
|
|
. "Make sure it sub-classes TextParser and that you've done ?flush=1.", E_USER_WARNING);
|
|
return Convert::raw2xml($this->value);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* (non-PHPdoc)
|
|
* @see DBField::scaffoldFormField()
|
|
*/
|
|
public function scaffoldFormField($title = null, $params = null) {
|
|
if(!$this->nullifyEmpty) {
|
|
// Allow the user to select if it's null instead of automatically assuming empty string is
|
|
return new NullableField(new TextareaField($this->name, $title));
|
|
} else {
|
|
// Automatically determine null (empty string)
|
|
return new TextareaField($this->name, $title);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* (non-PHPdoc)
|
|
* @see DBField::scaffoldSearchField()
|
|
*/
|
|
public function scaffoldSearchField($title = null, $params = null) {
|
|
return new TextField($this->name, $title);
|
|
}
|
|
}
|