TextExtHelper.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. <?php
  2. App::uses('TextHelper', 'View/Helper');
  3. App::uses('HtmlHelper', 'View/Helper');
  4. App::uses('View', 'View');
  5. /**
  6. * the core text helper is unsecure and outdated in functionality
  7. * this aims to compensate the deficiencies
  8. *
  9. * autoLinkEmails
  10. * - obfuscate (defaults to FALSE right now)
  11. * (- maxLength?)
  12. * - escape (defaults to TRUE for security reasons regarding plain text)
  13. *
  14. * autoLinkUrls
  15. * - stripProtocol (defaults To FALSE right now)
  16. * - maxLength (to shorten links in order to not mess up the layout in some cases - appends ...)
  17. * - escape (defaults to TRUE for security reasons regarding plain text)
  18. *
  19. * 2011-03-30 ms
  20. */
  21. class TextExtHelper extends TextHelper {
  22. /**
  23. * Convert all links and email adresses to HTML links.
  24. *
  25. * @param string $text Text
  26. * @param array $options Array of HTML options.
  27. * @return string The text with links
  28. * @access public
  29. * @link http://book.cakephp.org/view/1469/Text#autoLink-1620
  30. */
  31. public function autoLink($text, $options = array(), $htmlOptions = array()) {
  32. if (!isset($options['escape']) || $options['escape'] !== false) {
  33. $text = h($text);
  34. $options['escape'] = false;
  35. }
  36. return $this->autoLinkEmails($this->autoLinkUrls($text, $options, $htmlOptions), $options, $htmlOptions);
  37. }
  38. /**
  39. * fix to allow obfuscation of email (js, img?)
  40. * @param string $text
  41. * @param htmlOptions (additionally - not yet supported by core):
  42. * - obfuscate: true/false (defaults to false)
  43. * @param array $options
  44. * - escape (defaults to true)
  45. * @return string $html
  46. * @override
  47. * 2010-11-20 ms
  48. */
  49. public function autoLinkEmails($text, $options = array(), $htmlOptions = array()) {
  50. if (!isset($options['escape']) || $options['escape'] !== false) {
  51. $text = h($text);
  52. }
  53. $linkOptions = 'array(';
  54. foreach ($htmlOptions as $option => $value) {
  55. $value = var_export($value, true);
  56. $linkOptions .= "'$option' => $value, ";
  57. }
  58. $linkOptions .= ')';
  59. $customOptions = 'array(';
  60. foreach ($options as $option => $value) {
  61. $value = var_export($value, true);
  62. $customOptions .= "'$option' => $value, ";
  63. }
  64. $customOptions .= ')';
  65. $atom = '[a-z0-9!#$%&\'*+\/=?^_`{|}~-]';
  66. return preg_replace_callback('/(' . $atom . '+(?:\.' . $atom . '+)*@[a-z0-9-]+(?:\.[a-z0-9-]+)+)/i',
  67. create_function('$matches', 'return TextExtHelper::prepareEmail($matches[0],' . $linkOptions . ',' . $customOptions . ');'), $text);
  68. }
  69. /**
  70. * @param string $email
  71. * @param options:
  72. * - obfuscate: true/false (defaults to false)
  73. * @return string $html
  74. * @static
  75. * 2010-11-20 ms
  76. */
  77. public static function prepareEmail($email, $options = array(), $customOptions = array()) {
  78. $obfuscate = false;
  79. if (isset($options['obfuscate'])) {
  80. $obfuscate = $options['obfuscate'];
  81. unset($options['obfuscate']);
  82. }
  83. if (!isset($customOptions['escape']) || $customOptions['escape'] !== false) {
  84. $email = hDec($email);
  85. }
  86. $Html = new HtmlHelper(new View(null));
  87. //$Html->tags = $Html->loadConfig();
  88. //debug($Html->tags);
  89. if (!$obfuscate) {
  90. return $Html->link($email, "mailto:" . $email, $options);
  91. }
  92. $class = __CLASS__;
  93. $Common = new $class;
  94. $Common->Html = $Html;
  95. return $Common->encodeEmailUrl($email, null, array(), $options);
  96. }
  97. /**
  98. * Helper Function to Obfuscate Email by inserting a span tag (not more! not very secure on its own...)
  99. * each part of this mail now does not make sense anymore on its own
  100. * (striptags will not work either)
  101. * @param string email: necessary (and valid - containing one @)
  102. * @return string $html
  103. * 2009-03-11 ms
  104. */
  105. public function encodeEmail($mail) {
  106. list($mail1, $mail2) = explode('@', $mail);
  107. $encMail = $this->encodeText($mail1).'<span>@</span>'.$this->encodeText($mail2);
  108. return $encMail;
  109. }
  110. /**
  111. * Obfuscates Email (works without JS!) to avoid lowlevel spam bots to get it
  112. * @param string mail: email to encode
  113. * @param string text: optional (if none is given, email will be text as well)
  114. * @param array attributes: html tag attributes
  115. * @param array params: ?subject=y&body=y to be attached to "mailto:xyz"
  116. * @return string $html with js generated link around email (and non js fallback)
  117. * 2009-04-20 ms
  118. */
  119. public function encodeEmailUrl($mail, $text=null, $params=array(), $attr = array()) {
  120. if (empty($class)) { $class='email'; }
  121. $defaults = array(
  122. 'title' => __('for use in an external mail client'),
  123. 'class' => 'email',
  124. 'escape' => false
  125. );
  126. if (empty($text)) {
  127. $text = $this->encodeEmail($mail);
  128. }
  129. $encMail = 'mailto:'.$mail;
  130. //$encMail = $this->encodeText($encMail); # not possible
  131. // additionally there could be a span tag in between: email<span syle="display:none"></span>@web.de
  132. $querystring = '';
  133. foreach ($params as $key => $val) {
  134. if ($querystring) {
  135. $querystring .= "&$key=".rawurlencode($val);
  136. } else {
  137. $querystring = "?$key=".rawurlencode($val);
  138. }
  139. }
  140. $attr = array_merge($defaults, $attr);
  141. $xmail = $this->Html->link('', $encMail.$querystring, $attr);
  142. $xmail1 = mb_substr($xmail, 0, count($xmail)-5);
  143. $xmail2 = mb_substr($xmail, -4, 4);
  144. $len = mb_strlen($xmail1);
  145. $i=0;
  146. while ($i<$len) {
  147. $c = mt_rand(2,6);
  148. $par[] = (mb_substr($xmail1, $i, $c));
  149. $i += $c;
  150. }
  151. $join = implode('\'+\'', $par);
  152. return '<script language=javascript><!--
  153. document.write(\''.$join.'\');
  154. //--></script>
  155. '.$text.'
  156. <script language=javascript><!--
  157. document.write(\''.$xmail2.'\');
  158. //--></script>';
  159. //return '<a class="'.$class.'" title="'.$title.'" href="'.$encmail.$querystring.'">'.$encText.'</a>';
  160. }
  161. /**
  162. * Encodes Piece of Text (without usage of JS!) to avoid lowlevel spam bots to get it
  163. * @param STRING text to encode
  164. * @return string $html (randomly encoded)
  165. * 2009-03-11 ms
  166. */
  167. public static function encodeText($text) {
  168. $encmail = '';
  169. for ($i=0; $i < mb_strlen($text); $i++) {
  170. $encMod = mt_rand(0,2);
  171. switch ($encMod) {
  172. case 0: // None
  173. $encmail .= mb_substr($text, $i, 1);
  174. break;
  175. case 1: // Decimal
  176. $encmail .= "&#".ord(mb_substr($text, $i, 1)).';';
  177. break;
  178. case 2: // Hexadecimal
  179. $encmail .= "&#x".dechex(ord(mb_substr($text, $i, 1))).';';
  180. break;
  181. }
  182. }
  183. return $encmail;
  184. }
  185. /**
  186. * fix to allow shortened urls that do not break layout etc
  187. * @param string $text
  188. * @param options (additionally - not yet supported by core):
  189. * - stripProtocol: bool (defaults to true)
  190. * - maxLength: int (defaults no none)
  191. * @param htmlOptions
  192. * - escape etc
  193. * @return string $html
  194. * @override
  195. * 2010-11-07 ms
  196. */
  197. public function autoLinkUrls($text, $options = array(), $htmlOptions = array()) {
  198. if (!isset($options['escape']) || $options['escape'] !== false) {
  199. $text = h($text);
  200. $matchString = 'hDec($matches[0])';
  201. } else {
  202. $matchString = '$matches[0]';
  203. }
  204. if (isset($htmlOptions['escape'])) {
  205. $options['escape'] = $htmlOptions['escape'];
  206. }
  207. //$htmlOptions['escape'] = false;
  208. $htmlOptions = var_export($htmlOptions, true);
  209. $customOptions = var_export($options, true);
  210. $text = preg_replace_callback('#(?<!href="|">)((?:https?|ftp|nntp)://[^\s<>()]+)#i', create_function('$matches',
  211. '$Html = new HtmlHelper(new View(null)); return $Html->link(TextExtHelper::prepareLinkName(hDec($matches[0]), '.$customOptions.'), hDec($matches[0]),' . $htmlOptions . ');'), $text);
  212. return preg_replace_callback('#(?<!href="|">)(?<!http://|https://|ftp://|nntp://)(www\.[^\n\%\ <]+[^<\n\%\,\.\ <])(?<!\))#i',
  213. create_function('$matches', '$Html = new HtmlHelper(new View(null)); return $Html->link(TextExtHelper::prepareLinkName(hDec($matches[0]), '.$customOptions.'), "http://" . hDec($matches[0]),' . $htmlOptions . ');'), $text);
  214. }
  215. /**
  216. * @param string $link
  217. * @param options:
  218. * - stripProtocol: bool (defaults to true)
  219. * - maxLength: int (defaults to 50)
  220. * - escape (defaults to false, true needed for hellip to work)
  221. * @return string $html/$plain
  222. * 2010-11-07 ms
  223. */
  224. public static function prepareLinkName($link, $options = array()) {
  225. # strip protocol if desired (default)
  226. if (!isset($options['stripProtocol']) || $options['stripProtocol'] !== false) {
  227. $link = self::stripProtocol($link);
  228. }
  229. if (!isset($options['maxLength'])) {
  230. $options['maxLength'] = 50; # should be long enough for most cases
  231. }
  232. # shorten display name if desired (default)
  233. if (!empty($options['maxLength']) && mb_strlen($link) > $options['maxLength']) {
  234. $link = mb_substr($link, 0, $options['maxLength']);
  235. # problematic with autoLink()
  236. if (!empty($options['html']) && isset($options['escape']) && $options['escape'] === false) {
  237. $link .= '&hellip;'; # only possible with escape => false!
  238. } else {
  239. $link .= '...';
  240. }
  241. }
  242. return $link;
  243. }
  244. /**
  245. * Remove http:// or other protocols from the link
  246. *
  247. * @param string $url
  248. * @return string $strippedUrl
  249. * 2010-11-07 ms
  250. */
  251. public static function stripProtocol($url) {
  252. $pieces = parse_url($url);
  253. if (empty($pieces['scheme'])) {
  254. return $url; # already stripped
  255. }
  256. return mb_substr($url, mb_strlen($pieces['scheme'])+3); # +3 <=> :// # can only be 4 with "file" (file:///)...
  257. }
  258. /**
  259. * minimizes the given url to a maximum length
  260. *
  261. * @param string $url the url
  262. * @param int $max the maximum length
  263. * @param array $options
  264. * - placeholder
  265. * @return string the manipulated url (+ eventuell ...)
  266. */
  267. public function minimizeUrl($url = null, $max = null, $options = array()) {
  268. // check if there is nothing to do
  269. if (empty($url) || mb_strlen($url) <= (int)$max) {
  270. return (string)$url;
  271. }
  272. // http:// has not to be displayed, so
  273. if (mb_substr($url,0,7) === 'http://') {
  274. $url = mb_substr($url, 7);
  275. }
  276. // cut the parameters
  277. if (mb_strpos($url, '/') !== false) {
  278. $url = strtok($url, '/');
  279. }
  280. // return if the url is short enough
  281. if (mb_strlen($url) <= (int)$max) {
  282. return $url;
  283. }
  284. // otherwise cut a part in the middle (but only if long enough!!!)
  285. # TODO: more dynamically
  286. $placeholder = CHAR_HELLIP;
  287. if (!empty($options['placeholder'])) {
  288. $placeholder = $options['placeholder'];
  289. }
  290. $end = mb_substr($url, -5, 5);
  291. $front = mb_substr($url, 0, (int)$max - 8);
  292. return $front . $placeholder . $end;
  293. }
  294. /**
  295. * Transforming int values into ordinal numbers (1st, 3rd, etc.)
  296. * @param $num (INT) - the number to be suffixed.
  297. * @param $sup (BOOL) - whether to wrap the suffix in a superscript (<sup>) tag on output.
  298. * @return string $ordinal
  299. */
  300. public static function ordinalNumber($num = 0, $sup = false) {
  301. $suff = '';
  302. if (!in_array(($num % 100), array(11, 12, 13))) {
  303. switch ($num % 10) {
  304. case 1:
  305. $suff = 'st';
  306. break;
  307. case 2:
  308. $suff = 'nd';
  309. break;
  310. case 3:
  311. $suff = 'rd';
  312. break;
  313. default:
  314. $suff = 'th';
  315. }
  316. }
  317. return ($sup) ? $num . '<sup>' . $suff . '</sup>' : $num . $suff;
  318. }
  319. /**
  320. * syntax highlighting using php internal highlighting
  321. * @param string $filename
  322. * @param bool $return (else echo directly)
  323. * 2009-07-26 ms
  324. */
  325. public static function highlightFile($file, $return = true) {
  326. return highlight_file($file, $return);
  327. }
  328. /**
  329. * syntax highlighting using php internal highlighting
  330. * @param string $contentstring
  331. * @param bool $return (else echo directly)
  332. * 2009-07-26 ms
  333. */
  334. public static function highlightString($string, $return = true) {
  335. return highlight_string($string, $return);
  336. }
  337. }