Version: current

      1 <?php
      2 /**
      3  * Represents a long text field.
      4  * @package sapphire
      5  * @subpackage model
      6  */
      7 class Text extends StringField {
      8     static $casting = array(
      9         "AbsoluteLinks" => "HTMLText",
     10     );
     11
     12     /**
     13      * (non-PHPdoc)
     14      * @see DBField::requireField()
     15      */
     16     function requireField() {
     17         $parts=Array('datatype'=>'mediumtext', 'character set'=>'utf8', 'collate'=>'utf8_unicode_ci', 'arrayValue'=>$this->arrayValue);
     18         $values=Array('type'=>'text', 'parts'=>$parts);
     19         DB::requireField($this->tableName, $this->name, $values, $this->default);
     20     }
     21
     22     /**
     23      * Limit this field's content by a number of words.
     24      * CAUTION: This is not XML safe. Please use
     25      * {@link LimitWordCountXML()} instead.
     26      *
     27      * @param int $numWords Number of words to limit by
     28      * @param string $add Ellipsis to add to the end of truncated string
     29      * @return string
     30      */
     31     function LimitWordCount($numWords = 26, $add = '...') {
     32         $this->value = trim(Convert::xml2raw($this->value));
     33         $ret = explode(' ', $this->value, $numWords + 1);
     34
     35         if(count($ret) <= $numWords - 1) {
     36             $ret = $this->value;
     37         } else {
     38             array_pop($ret);
     39             $ret = implode(' ', $ret) . $add;
     40         }
     41
     42         return $ret;
     43     }
     44
     45     /**
     46      * Return the value of the field stripped of html tags
     47      * @return string
     48      */
     49     function NoHTML() {
     50         return strip_tags($this->value);
     51     }
     52     /**
     53      * Return the value of the field with XML tags escaped.
     54      * @return string
     55      */
     56     function EscapeXML() {
     57         return str_replace(array('&','<','>','"'), array('&amp;','&lt;','&gt;','&quot;'), $this->value);
     58     }
     59
     60     /**
     61      * Return the value of the field with relative links converted to absolute urls.
     62      * @return string
     63      */
     64     function AbsoluteLinks() {
     65         return HTTP::absoluteURLs($this->value);
     66     }
     67
     68     /**
     69      * Limit this field's content by a number of characters.
     70      * CAUTION: Does not take into account HTML tags, so it
     71      * has the potential to return malformed HTML.
     72      *
     73      * @param int $limit Number of characters to limit by
     74      * @param string $add Ellipsis to add to the end of truncated string
     75      * @return string
     76      */
     77     function LimitCharacters($limit = 20, $add = "...") {
     78         $value = trim($this->value);
     79         return (mb_strlen($value) > $limit) ? mb_substr($value, 0, $limit) . $add : $value;
     80     }
     81
     82     /**
     83      * Limit the number of words of the current field's
     84      * content. This is XML safe, so characters like &
     85      * are converted to &amp;
     86      *
     87      * @param int $numWords Number of words to limit by
     88      * @param string $add Ellipsis to add to the end of truncated string
     89      * @return string
     90      */
     91     function LimitWordCountXML($numWords = 26, $add = '...') {
     92         $ret = $this->LimitWordCount($numWords, $add);
     93         return Convert::raw2xml($ret);
     94     }
     95
     96     /**
     97      * Limit sentences, can be controlled by passing an integer.
     98      * @param int $sentCount The amount of sentences you want.
     99      */
    100     function LimitSentences($sentCount = 2) {
    101         if(!is_numeric($sentCount)) user_error("Text::LimitSentence() expects one numeric argument", E_USER_NOTICE);
    102
    103         $output = array();
    104         $data = trim(Convert::xml2raw($this->value));
    105         $sentences = explode('.', $data);
    106
    107         if ($sentCount == 0) return '';
    108
    109         for($i = 0; $i < $sentCount; $i++) {
    110             if(isset($sentences[$i])) {
    111                 $sentence = trim($sentences[$i]);
    112                 if(!empty($sentence)) $output[] .= $sentence;
    113             }
    114         }
    115
    116         return count($output)==0 ? '' : implode($output, '. ') . '.';
    117     }
    118
    119
    120     /**
    121      * Caution: Not XML/HTML-safe - does not respect closing tags.
    122      */
    123     function FirstSentence() {
    124         $data = Convert::xml2raw( $this->value );
    125         if( !$data ) return "";
    126
    127
    128         $sentences = explode( '.', $data );
    129
    130         if( count( $sentences ) )
    131             return $sentences[0] . '.';
    132         else
    133             return $this->Summary(20);
    134     }
    135
    136     /**
    137      * Caution: Not XML/HTML-safe - does not respect closing tags.
    138      */
    139     function Summary($maxWords = 50) {
    140
    141         // get first sentence?
    142         // this needs to be more robust
    143         $data = Convert::xml2raw( $this->value /*, true*/ );
    144
    145
    146         if( !$data )
    147             return "";
    148
    149         // grab the first paragraph, or, failing that, the whole content
    150         if( strpos( $data, "\n\n" ) )
    151             $data = substr( $data, 0, strpos( $data, "\n\n" ) );
    152
    153         $sentences = explode( '.', $data );
    154
    155         $count = count( explode( ' ', $sentences[0] ) );
    156
    157         // if the first sentence is too long, show only the first $maxWords words
    158         if( $count > $maxWords ) {
    159             return implode( ' ', array_slice( explode( ' ', $sentences[0] ), 0, $maxWords ) ).'...';
    160         }
    161         // add each sentence while there are enough words to do so
    162         $result = '';
    163         do {
    164             $result .= trim(array_shift( $sentences )).'.';
    165             if(count($sentences) > 0) {
    166                 $count += count( explode( ' ', $sentences[0] ) );
    167             }
    168
    169             // Ensure that we don't trim half way through a tag or a link
    170             $brokenLink = (substr_count($result,'<') != substr_count($result,'>')) ||
    171                 (substr_count($result,'<a') != substr_count($result,'</a'));
    172
    173         } while( ($count < $maxWords || $brokenLink) && $sentences && trim( $sentences[0] ) );
    174
    175         if( preg_match( '/<a[^>]*>/', $result ) && !preg_match( '/<\/a>/', $result ) )
    176             $result .= '</a>';
    177
    178         $result = Convert::raw2xml( $result );
    179         return $result;
    180     }
    181
    182     /**
    183     * Performs the same function as the big summary, but doesnt trim new paragraphs off data.
    184     * Caution: Not XML/HTML-safe - does not respect closing tags.
    185     */
    186     function BigSummary($maxWords = 50, $plain = 1) {
    187         $result = "";
    188         // get first sentence?
    189         // this needs to be more robust
    190         if($plain) $data = Convert::xml2raw( $this->value, true );
    191
    192         if( !$data )
    193             return "";
    194
    195         $sentences = explode( '.', $data );
    196         $count = count( explode( ' ', $sentences[0] ) );
    197
    198         // if the first sentence is too long, show only the first $maxWords words
    199         if( $count > $maxWords ) {
    200             return implode( ' ', array_slice( explode( ' ', $sentences[0] ), 0, $maxWords ) ).'...';
    201         }
    202         // add each sentence while there are enough words to do so
    203         do {
    204             $result .= trim(array_shift($sentences));
    205             if($sentences) {
    206                 $result .= '. ';
    207                 $count += count(explode(' ', $sentences[0]));
    208             }
    209
    210             // Ensure that we don't trim half way through a tag or a link
    211             $brokenLink = (substr_count($result,'<') != substr_count($result,'>')) ||
    212                 (substr_count($result,'<a') != substr_count($result,'</a'));
    213         } while( ($count < $maxWords || $brokenLink) && $sentences && trim( $sentences[0] ) );
    214
    215         if( preg_match( '/<a[^>]*>/', $result ) && !preg_match( '/<\/a>/', $result ) )
    216             $result .= '</a>';
    217
    218         return $result;
    219     }
    220
    221     /**
    222      * Caution: Not XML/HTML-safe - does not respect closing tags.
    223      */
    224     function FirstParagraph($plain = 1) {
    225         // get first sentence?
    226         // this needs to be more robust
    227         if($plain && $plain != 'html') {
    228             $data = Convert::xml2raw( $this->value, true );
    229             if( !$data ) return "";
    230
    231             // grab the first paragraph, or, failing that, the whole content
    232             if( strpos( $data, "\n\n" ) )
    233                 $data = substr( $data, 0, strpos( $data, "\n\n" ) );
    234
    235             return $data;
    236
    237         } else {
    238             if(strpos( $this->value, "</p>" ) === false) return $this->value;
    239
    240             $data = substr( $this->value, 0, strpos( $this->value, "</p>" ) + 4 );
    241
    242
    243             if(strlen($data) < 20 && strpos( $this->value, "</p>", strlen($data) )) $data = substr( $this->value, 0, strpos( $this->value, "</p>", strlen($data) ) + 4 );
    244
    245             return $data;
    246         }
    247     }
    248
    249     /**
    250      * Perform context searching to give some context to searches, optionally
    251      * highlighting the search term.
    252      *
    253      * @param int $characters Number of characters in the summary
    254      * @param boolean $string Supplied string ("keywords")
    255      * @param boolean $striphtml Strip HTML?
    256      * @param boolean $highlight Add a highlight <span> element around search query?
    257      * @return string
    258      */
    259     function ContextSummary($characters = 500, $string = false, $striphtml = true, $highlight = true) {
    260         if(!$string) $string = $_REQUEST['Search']; // Use the default "Search" request variable (from SearchForm)
    261
    262         // Remove HTML tags so we don't have to deal with matching tags
    263         $text = $striphtml ? $this->NoHTML() : $this->value;
    264
    265         // Find the search string
    266         $position = (int) stripos($text, $string);
    267
    268         // We want to search string to be in the middle of our block to give it some context
    269         $position = max(0, $position - ($characters / 2));
    270
    271         if($position > 0) {
    272             // We don't want to start mid-word
    273             $position = max((int) strrpos(substr($text, 0, $position), ' '), (int) strrpos(substr($text, 0, $position), "\n"));
    274         }
    275
    276         $summary = substr($text, $position, $characters);
    277         $stringPieces = explode(' ', $string);
    278
    279         if($highlight) {
    280             // Add a span around all key words from the search term as well
    281             if($stringPieces) foreach($stringPieces as $stringPiece) {
    282                 $summary = str_ireplace($stringPiece, "<span class=\"highlight\">$stringPiece</span>", $summary);
    283             }
    284         }
    285
    286         return trim($summary);
    287     }
    288
    289     /**
    290      * Allows a sub-class of TextParser to be rendered.
    291      *
    292      * @see TextParser for implementation details.
    293      * @return string
    294      */
    295     function Parse($parser = "TextParser") {
    296         if($parser == "TextParser" || is_subclass_of($parser, "TextParser")) {
    297             $obj = new $parser($this->value);
    298             return $obj->parse();
    299         } else {
    300             // Fallback to using raw2xml and show a warning
    301             // TODO Don't kill script execution, we can continue without losing complete control of the app
    302             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);
    303             return Convert::raw2xml($this->value);
    304         }
    305     }
    306
    307     /**
    308      * (non-PHPdoc)
    309      * @see DBField::scaffoldFormField()
    310      */
    311     public function scaffoldFormField($title = null, $params = null) {
    312         if(!$this->nullifyEmpty) {
    313             // Allow the user to select if it's null instead of automatically assuming empty string is
    314             return new NullableField(new TextareaField($this->name, $title));
    315         } else {
    316             // Automatically determine null (empty string)
    317             return new TextareaField($this->name, $title);
    318         }
    319     }
    320
    321     /**
    322      * (non-PHPdoc)
    323      * @see DBField::scaffoldSearchField()
    324      */
    325     public function scaffoldSearchField($title = null, $params = null) {
    326         return new TextField($this->name, $title);
    327     }
    328 }
    329
    330 ?>
    331

[Raise a SilverStripe Framework issue/bug](https://github.com/silverstripe/silverstripe-framework/issues/new)
- [Raise a SilverStripe CMS issue/bug](https://github.com/silverstripe/silverstripe-cms/issues/new)
- Please use the Silverstripe Forums to ask development related questions. -