├── HacksawPlugin.php ├── LICENSE.txt ├── README.md ├── composer.json ├── releases.json ├── resources ├── icon-mask.svg └── icon.svg └── twigextensions └── HacksawTwigExtension.php /HacksawPlugin.php: -------------------------------------------------------------------------------- 1 | **-OR-**
Do a `git clone https://github.com/ryanshrum/hacksaw.git` directly into your `craft/plugins` folder. You can then update it with `git pull`
**-OR-**
Install with Composer via `composer require ryanshrum/hacksaw` 17 | 1. Install plugin in the Craft Control Panel under Settings > Plugins 18 | 1. The plugin folder should be named `hacksaw` for Craft to see it. GitHub recently started appending `-master` (the branch name) to the name of the folder for zip file downloads. 19 | 20 | ## Usage 21 | 22 | Hacksaw is a Twig filter that accepts the following parameters: 23 | 24 | | Parameter | Type | Default | Description | 25 | | ------------- | :-------: | :-------: | ----------------------------------------------------------------------------------------------------------------- | 26 | | hack | string | `'p'` | What you want to hack on (`'characters'`, `'words'` or `'paragraphs'` - can also use first letter as short hand `'c'`, `'w'` or `'p'`) | 27 | | limit | int | `1` | Starting point for chars limit (used with chars param) | 28 | | allow | string | `null` | Sometimes there are HTML tags in your content that you don' removed, pass them here | 29 | | append | string | `null` | String to append to the end of the excerpt | 30 | 31 | _**Note:** Cutoff has been deprecated from Hackasw._ 32 | 33 | ## Examples 34 | 35 | Hacking by paragraphs is default, so if you wanted to limit your text to 5 paragraphs, you would only need to set the limit parameter: 36 | 37 | ``` 38 | {{ entry.richTextField|hacksaw(limit='5') }} 39 | ``` 40 | 41 | If you wanted to limit to 50 characters or words, you'd use both the hack and limit parameter: 42 | 43 | ``` 44 | {{ entry.richTextField|hacksaw(hack='characters', limit='50') }} 45 | ``` 46 | 47 | ``` 48 | {{ entry.richTextField|hacksaw(hack='words', limit='50') }} 49 | ``` 50 | 51 | Hacksaw will strip all HTML from your excerpt by default. If you would like to keep some basic HTML you can use the `allow` parameter to keep specific HTML tags. For example, let's say you want to keep `` and `` tags: 52 | 53 | _**Note:** `

`'s are automatically allowed when hacking by paragraph._ 54 | 55 | ``` 56 | {{ entry.richTextField|hacksaw(limit='10', allow='') }} 57 | ``` 58 | 59 | **Note:** If you are including HTML in the append parameter, the elements must be present in the `allow` parameter. If you are including a Craft variable in any parameter, it must be added using the Twig concatenation operator, `~`. Example of both: 60 | 61 | ``` 62 | {{ entry.richTextField|hacksaw(hack='w', limit='100', allow='', append='Continue...') }} 63 | ``` 64 | 65 | Brought to you by [Ryan Shrum](http://ryanshrum.com) 66 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ryanshrum/hacksaw", 3 | "description": "A simple text truncation plugin for Craft CMS.", 4 | "type": "craft-plugin", 5 | "authors": [ 6 | { 7 | "name": "Ryan Shrum", 8 | "homepage": "http://ryanshrum.com" 9 | } 10 | ], 11 | "require": { 12 | "composer/installers": "~1.0" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /releases.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "version": "2.0.0", 4 | "downloadUrl": "https://github.com/ryanshrum/hacksaw/archive/master.zip", 5 | "date": "2016-09-13T01:04:38.366Z", 6 | "notes": [ 7 | "[Added] Initial release" 8 | ] 9 | } 10 | ] 11 | -------------------------------------------------------------------------------- /resources/icon-mask.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resources/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /twigextensions/HacksawTwigExtension.php: -------------------------------------------------------------------------------- 1 | new \Twig_Filter_Method($this, 'hacksaw', [ 33 | 'is_safe' => array('html') 34 | ]), 35 | ); 36 | } 37 | 38 | public function hacksaw($content, $hack = 'p', $limit = 1, $allow = null, $append = null) 39 | { 40 | if ($hack == 'c' || $hack == 'chars' || $hack == 'characters') 41 | { 42 | $clean_content = $this->cleanHtml($content, $allow); 43 | 44 | if (mb_strlen($clean_content) <= $limit) 45 | { 46 | $return = $clean_content; 47 | } 48 | else 49 | { 50 | $return = preg_replace('/\s+?(\S+)?$/u', '', mb_substr($clean_content, 0, $limit)) . $append; 51 | } 52 | 53 | return $this->closeTags($return); 54 | 55 | } 56 | else if ($hack == 'w' || $hack == 'words') 57 | { 58 | $clean_content = $this->cleanHtml($content, $allow); 59 | 60 | if (str_word_count($clean_content) <= $limit) 61 | { 62 | $return = $clean_content; 63 | } 64 | else 65 | { 66 | $word_count = str_word_count($clean_content, 0); 67 | 68 | if ($word_count > $limit) 69 | { 70 | $words = preg_split('/\s+/u', $clean_content); 71 | $clean_content = implode(' ', array_slice($words, 0, $limit)); 72 | $return = $clean_content; 73 | 74 | if (preg_match("/[0-9.!?,;:]$/u", $clean_content)) 75 | { 76 | $return = mb_substr($clean_content, 0, -1); 77 | } 78 | 79 | $return .= $append; 80 | } 81 | } 82 | 83 | return $this->closeTags($return); 84 | } 85 | else if ($hack = 'p' || $hack == 'paragraphs') 86 | { 87 | $clean_content = $this->cleanHtml($content, $allow . "

"); 88 | $paragraphs = array_filter(explode("

", str_replace("

", "", $content))); 89 | $paragraphs = array_slice($paragraphs, 0, $limit); 90 | $paragraphs_count = count($paragraphs)-1; 91 | 92 | $return = "

"; 93 | 94 | foreach ($paragraphs as $key => $paragraph) 95 | { 96 | $return .= "

" . $paragraph; 97 | 98 | if ($key < $paragraphs_count) 99 | { 100 | $return .= "

"; 101 | } 102 | } 103 | 104 | $return .= $append . "

"; 105 | 106 | return $return; 107 | } 108 | } 109 | 110 | private function cleanHtml($content, $allow) 111 | { 112 | return strip_tags($content, $allow); 113 | } 114 | 115 | private function closeTags($content) { 116 | preg_match_all('#<(?!meta|img|br|hr|input\b)\b([a-z]+)(?: .*)?(?#iU', $content, $result); 117 | 118 | $opened_tags = $result[1]; 119 | 120 | preg_match_all('##iU', $content, $result); 121 | 122 | $closed_tags = $result[1]; 123 | $open_tag_count = count($opened_tags); 124 | 125 | if (count($closed_tags) == $open_tag_count) { 126 | return $content; 127 | } 128 | 129 | $opened_tags = array_reverse($opened_tags); 130 | 131 | for ($i=0; $i < $open_tag_count; $i++) { 132 | if (!in_array($opened_tags[$i], $closed_tags)) { 133 | $content .= ''; 134 | } else { 135 | unset($closed_tags[array_search($opened_tags[$i], $closed_tags)]); 136 | } 137 | } 138 | 139 | return $content; 140 | } 141 | } 142 | --------------------------------------------------------------------------------