272 lines
8.3 KiB
PHP
272 lines
8.3 KiB
PHP
<?php
|
|
|
|
namespace SilverStripe\ORM\FieldType;
|
|
|
|
use InvalidArgumentException;
|
|
use SilverStripe\Core\Config\Config;
|
|
use SilverStripe\Core\Convert;
|
|
use SilverStripe\Forms\NullableField;
|
|
use SilverStripe\Forms\TextareaField;
|
|
use SilverStripe\Forms\TextField;
|
|
use SilverStripe\ORM\Connect\MySQLDatabase;
|
|
use SilverStripe\ORM\DB;
|
|
|
|
/**
|
|
* Represents a variable-length string of up to 16 megabytes, designed to store raw text
|
|
*
|
|
* Example definition via {@link DataObject::$db}:
|
|
* <code>
|
|
* static $db = array(
|
|
* "MyDescription" => "Text",
|
|
* );
|
|
* </code>
|
|
*
|
|
* @see DBHTMLText
|
|
* @see DBHTMLVarchar
|
|
* @see Varchar
|
|
*/
|
|
class DBText extends DBString
|
|
{
|
|
|
|
private static $casting = [
|
|
'BigSummary' => 'Text',
|
|
'ContextSummary' => 'HTMLFragment', // Always returns HTML as it contains formatting and highlighting
|
|
'FirstParagraph' => 'Text',
|
|
'FirstSentence' => 'Text',
|
|
'LimitSentences' => 'Text',
|
|
'Summary' => 'Text',
|
|
];
|
|
|
|
/**
|
|
* (non-PHPdoc)
|
|
* @see DBField::requireField()
|
|
*/
|
|
public function requireField()
|
|
{
|
|
$charset = Config::inst()->get(MySQLDatabase::class, 'charset');
|
|
$collation = Config::inst()->get(MySQLDatabase::class, 'collation');
|
|
|
|
$parts = [
|
|
'datatype' => 'mediumtext',
|
|
'character set' => $charset,
|
|
'collate' => $collation,
|
|
'default' => $this->defaultVal,
|
|
'arrayValue' => $this->arrayValue
|
|
];
|
|
|
|
$values = [
|
|
'type' => 'text',
|
|
'parts' => $parts
|
|
];
|
|
|
|
DB::require_field($this->tableName, $this->name, $values);
|
|
}
|
|
|
|
/**
|
|
* Limit sentences, can be controlled by passing an integer.
|
|
*
|
|
* @param int $maxSentences The amount of sentences you want.
|
|
* @return string
|
|
*/
|
|
public function LimitSentences($maxSentences = 2)
|
|
{
|
|
if (!is_numeric($maxSentences)) {
|
|
throw new InvalidArgumentException("Text::LimitSentence() expects one numeric argument");
|
|
}
|
|
|
|
$value = $this->Plain();
|
|
if (!$value) {
|
|
return '';
|
|
}
|
|
|
|
// Do a word-search
|
|
$words = preg_split('/\s+/u', $value ?? '') ?: [];
|
|
$sentences = 0;
|
|
foreach ($words as $i => $word) {
|
|
if (preg_match('/(!|\?|\.)$/', $word ?? '') && !preg_match('/(Dr|Mr|Mrs|Ms|Miss|Sr|Jr|No)\.$/i', $word ?? '')) {
|
|
$sentences++;
|
|
if ($sentences >= $maxSentences) {
|
|
return implode(' ', array_slice($words ?? [], 0, $i + 1));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Failing to find the number of sentences requested, fallback to a logical default
|
|
if ($maxSentences > 1) {
|
|
return $value;
|
|
}
|
|
// If searching for a single sentence (and there are none) just do a text summary
|
|
return $this->Summary(20);
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the first string that finishes with a period (.) in this text.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function FirstSentence()
|
|
{
|
|
return $this->LimitSentences(1);
|
|
}
|
|
|
|
/**
|
|
* Builds a basic summary, up to a maximum number of words
|
|
*
|
|
* @param int $maxWords
|
|
* @param string|false $add
|
|
* @return string
|
|
*/
|
|
public function Summary($maxWords = 50, $add = false)
|
|
{
|
|
// Get plain-text version
|
|
$value = $this->Plain();
|
|
if (!$value) {
|
|
return '';
|
|
}
|
|
|
|
// If no $elipsis string is provided, use the default one.
|
|
if ($add === false) {
|
|
$add = $this->defaultEllipsis();
|
|
}
|
|
|
|
// Split on sentences (don't remove period)
|
|
$sentences = array_filter(array_map(function ($str) {
|
|
return trim($str ?? '');
|
|
}, preg_split('@(?<=\.)@', $value ?? '') ?: []));
|
|
$wordCount = count(preg_split('#\s+#u', $sentences[0] ?? '') ?: []);
|
|
|
|
// if the first sentence is too long, show only the first $maxWords words
|
|
if ($wordCount > $maxWords) {
|
|
return implode(' ', array_slice(explode(' ', $sentences[0] ?? ''), 0, $maxWords)) . $add;
|
|
}
|
|
|
|
// add each sentence while there are enough words to do so
|
|
$result = '';
|
|
do {
|
|
// Add next sentence
|
|
$result .= ' ' . array_shift($sentences);
|
|
|
|
// If more sentences to process, count number of words
|
|
if ($sentences) {
|
|
$wordCount += count(preg_split('#\s+#u', $sentences[0] ?? '') ?: []);
|
|
}
|
|
} while ($wordCount < $maxWords && $sentences && trim($sentences[0] ?? ''));
|
|
|
|
return trim($result ?? '');
|
|
}
|
|
|
|
/**
|
|
* Get first paragraph
|
|
*
|
|
* @return string
|
|
*/
|
|
public function FirstParagraph()
|
|
{
|
|
$value = $this->Plain();
|
|
if (empty($value)) {
|
|
return '';
|
|
}
|
|
|
|
// Split paragraphs and return first
|
|
$paragraphs = preg_split('#\n{2,}#', $value ?? '') ?: [];
|
|
return reset($paragraphs);
|
|
}
|
|
|
|
/**
|
|
* Perform context searching to give some context to searches, optionally
|
|
* highlighting the search term.
|
|
*
|
|
* @param int $characters Number of characters in the summary
|
|
* @param string $keywords Supplied string ("keywords"). Will fall back to 'Search' querystring arg.
|
|
* @param bool $highlight Add a highlight <mark> element around search query?
|
|
* @param string|false $prefix Prefix text
|
|
* @param string|false $suffix Suffix text
|
|
* @return string HTML string with context
|
|
*/
|
|
public function ContextSummary(
|
|
$characters = 500,
|
|
$keywords = null,
|
|
$highlight = true,
|
|
$prefix = false,
|
|
$suffix = false
|
|
) {
|
|
|
|
if (!$keywords) {
|
|
// Use the default "Search" request variable (from SearchForm)
|
|
$keywords = isset($_REQUEST['Search']) ? $_REQUEST['Search'] : '';
|
|
}
|
|
|
|
if ($prefix === false) {
|
|
$prefix = $this->defaultEllipsis() . ' ';
|
|
}
|
|
|
|
if ($suffix === false) {
|
|
$suffix = $this->defaultEllipsis();
|
|
}
|
|
|
|
// Get raw text value, but XML encode it (as we'll be merging with HTML tags soon)
|
|
$text = Convert::raw2xml($this->Plain());
|
|
$keywords = Convert::raw2xml($keywords);
|
|
|
|
// Find the search string
|
|
$position = empty($keywords) ? 0 : (int) mb_stripos($text ?? '', $keywords ?? '');
|
|
|
|
// We want to search string to be in the middle of our block to give it some context
|
|
$position = floor(max(0, $position - ($characters / 2)) ?? 0.0);
|
|
|
|
if ($position > 0) {
|
|
// We don't want to start mid-word
|
|
$position = max(
|
|
(int) mb_strrpos(mb_substr($text ?? '', 0, $position), ' '),
|
|
(int) mb_strrpos(mb_substr($text ?? '', 0, $position), "\n")
|
|
);
|
|
}
|
|
|
|
$summary = mb_substr($text ?? '', $position ?? 0, $characters);
|
|
$stringPieces = explode(' ', $keywords ?? '');
|
|
|
|
if ($highlight) {
|
|
// Add a span around all key words from the search term as well
|
|
if ($stringPieces) {
|
|
foreach ($stringPieces as $stringPiece) {
|
|
if (mb_strlen($stringPiece ?? '') > 2) {
|
|
// Maintain case of original string
|
|
$summary = preg_replace(
|
|
'/' . preg_quote($stringPiece ?? '', '/') . '/i',
|
|
'<mark>$0</mark>',
|
|
$summary ?? ''
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
$summary = trim($summary ?? '');
|
|
|
|
// Add leading / trailing '...' if trimmed on either end
|
|
if ($position > 0) {
|
|
$summary = $prefix . $summary;
|
|
}
|
|
if (strlen($text ?? '') > ($characters + $position)) {
|
|
$summary = $summary . $suffix;
|
|
}
|
|
|
|
return nl2br($summary ?? '');
|
|
}
|
|
|
|
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 NullableField::create(TextareaField::create($this->name, $title));
|
|
}
|
|
// Automatically determine null (empty string)
|
|
return TextareaField::create($this->name, $title);
|
|
}
|
|
|
|
public function scaffoldSearchField($title = null)
|
|
{
|
|
return new TextField($this->name, $title);
|
|
}
|
|
}
|