PageRenderTime 46ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/modx/core/model/modx/filters/modoutputfilter.class.php

https://bitbucket.org/argnist/mohana
PHP | 580 lines | 451 code | 39 blank | 90 comment | 95 complexity | 2ae5c67a5c0358fd7291b68e53ca81e8 MD5 | raw file
Possible License(s): BSD-3-Clause, AGPL-1.0, LGPL-2.1, GPL-2.0, GPL-3.0, LGPL-2.0
  1. <?php
  2. /*
  3. * MODX Revolution
  4. *
  5. * Copyright 2006-2011 by MODX, LLC.
  6. * All rights reserved.
  7. *
  8. * This program is free software; you can redistribute it and/or modify it under
  9. * the terms of the GNU General Public License as published by the Free Software
  10. * Foundation; either version 2 of the License, or (at your option) any later
  11. * version.
  12. *
  13. * This program is distributed in the hope that it will be useful, but WITHOUT
  14. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  15. * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
  16. * details.
  17. *
  18. * You should have received a copy of the GNU General Public License along with
  19. * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
  20. * Place, Suite 330, Boston, MA 02111-1307 USA
  21. */
  22. /**
  23. * Provides the default output filter implementation for modElement processing.
  24. * @package modx
  25. * @subpackage filters
  26. */
  27. /**
  28. * Base output filter implementation for modElement processing, based on phX.
  29. *
  30. * @package modx
  31. * @subpackage filters
  32. */
  33. class modOutputFilter {
  34. /**
  35. * @var modX A reference to the modX instance
  36. */
  37. public $modx= null;
  38. /**
  39. * @param modX $modx A reference to the modX instance
  40. * @return modOutputFilter A new instance of the modOutputFilter class
  41. */
  42. function __construct(modX &$modx) {
  43. $this->modx= &$modx;
  44. }
  45. /**
  46. * Filters the output
  47. *
  48. * @param mixed $element The element to filter
  49. */
  50. public function filter(&$element) {
  51. $usemb = function_exists('mb_strlen') && (boolean)$this->modx->getOption('use_multibyte',null,false);
  52. $encoding = $this->modx->getOption('modx_charset',null,'UTF-8');
  53. $output= & $element->_output;
  54. $inputFilter = $element->getInputFilter();
  55. if ($inputFilter !== null && $inputFilter->hasCommands()) {
  56. $modifier_cmd = $inputFilter->getCommands();
  57. $modifier_value = $inputFilter->getModifiers();
  58. $count = count($modifier_cmd);
  59. $condition = array();
  60. for ($i= 0; $i < $count; $i++) {
  61. $m_cmd = $modifier_cmd[$i];
  62. $m_val = $modifier_value[$i];
  63. $this->log('Processing Modifier: ' . $m_cmd . ' (parameters: ' . $m_val . ')');
  64. $output = trim($output);
  65. try {
  66. switch ($m_cmd) {
  67. /* conditional operators */
  68. /* @todo these conditionals should be removed because there are cleaner ways to do this now */
  69. case 'input':
  70. case 'if':
  71. $output= $m_val;
  72. break;
  73. case 'eq':
  74. case 'is':
  75. case 'equals':
  76. case 'equalto':
  77. case 'isequal':
  78. case 'isequalto':
  79. $condition[]= intval(($output == $m_val));
  80. break;
  81. case 'ne':
  82. case 'neq':
  83. case 'isnot':
  84. case 'isnt':
  85. case 'notequals':
  86. case 'notequalto':
  87. $condition[]= intval(($output != $m_val));
  88. break;
  89. case 'gte':
  90. case 'isgte':
  91. case 'eg':
  92. case 'ge':
  93. case 'equalorgreaterthan':
  94. case 'greaterthanorequalto':
  95. $condition[]= intval(($output >= $m_val));
  96. break;
  97. case 'lte':
  98. case 'islte':
  99. case 'le':
  100. case 'el':
  101. case 'lessthanorequalto':
  102. case 'equaltoorlessthan':
  103. $condition[]= intval(($output <= $m_val));
  104. break;
  105. case 'gt':
  106. case 'isgt':
  107. case 'greaterthan':
  108. case 'isgreaterthan':
  109. $condition[]= intval(($output > $m_val));
  110. break;
  111. case 'lt':
  112. case 'islt':
  113. case 'lessthan':
  114. case 'lowerthan':
  115. case 'islessthan':
  116. case 'islowerthan':
  117. $condition[]= intval(($output < $m_val));
  118. break;
  119. case 'ismember':
  120. case 'memberof':
  121. case 'mo': /* Is Member Of (same as inrole but this one can be stringed as a conditional) */
  122. if (empty($output) || $output == "&_PHX_INTERNAL_&") {
  123. $output= $this->modx->user->get('id');
  124. }
  125. $grps= (strlen($m_val) > 0) ? explode(',', $m_val) : array ();
  126. $user = $this->modx->getObject('modUser',$output);
  127. $condition[]= $user->isMember($grps);
  128. break;
  129. case 'or':
  130. $condition[]= "||";
  131. break;
  132. case 'and':
  133. $condition[]= "&&";
  134. break;
  135. case 'hide':
  136. $m_con = intval(eval("return (" . join(' ', $condition) . ");"));
  137. if ($m_con) {
  138. $output= null;
  139. }
  140. break;
  141. case 'show':
  142. $m_con = intval(eval("return (" . join(' ', $condition) . ");"));
  143. if (!$m_con) {
  144. $output= null;
  145. }
  146. break;
  147. case 'then':
  148. $m_con = intval(eval("return (" . join(' ', $condition) . ");"));
  149. if ($m_con) {
  150. $output= $m_val;
  151. } else {
  152. $output= null;
  153. }
  154. break;
  155. case 'else':
  156. $m_con = intval(eval("return (" . join(' ', $condition) . ");"));
  157. if (!$m_con) {
  158. $output= $m_val;
  159. }
  160. break;
  161. case 'select':
  162. $raw= explode("&", $m_val);
  163. $map= array ();
  164. for ($m= 0; $m < (count($raw)); $m++) {
  165. $mi= explode("=", $raw[$m]);
  166. $map[$mi[0]]= $mi[1];
  167. }
  168. $output= $map[$output];
  169. break;
  170. /* ##### End of Conditional Modifiers */
  171. /* ##### String Modifiers */
  172. case 'cat': /* appends the options value (if not empty) to the input value */
  173. if (!empty($m_val))
  174. $output = $output . $m_val;
  175. break;
  176. case 'lcase':
  177. case 'lowercase':
  178. case 'strtolower':
  179. /* See PHP's strtolower - http://www.php.net/manual/en/function.strtolower.php */
  180. $output = $usemb ? mb_strtolower($output,$encoding) : strtolower($output);
  181. break;
  182. case 'ucase':
  183. case 'uppercase':
  184. case 'strtoupper':
  185. /* See PHP's strtoupper - http://www.php.net/manual/en/function.strtoupper.php */
  186. $output = $usemb ? mb_strtoupper($output,$encoding) : strtoupper($output);
  187. break;
  188. case 'ucwords':
  189. /* See PHP's ucwords - http://www.php.net/manual/en/function.ucwords.php */
  190. $output = $usemb ? mb_convert_case($output,MB_CASE_TITLE,$encoding) : ucwords($output);
  191. break;
  192. case 'ucfirst':
  193. /* See PHP's ucfirst - http://www.php.net/manual/en/function.ucfirst.php */
  194. if ($usemb) {
  195. $output = mb_strtoupper(mb_substr($output,0,1)) . mb_substr($output, 1);
  196. } else {
  197. $output = ucfirst($output);
  198. }
  199. break;
  200. case 'htmlent':
  201. case 'htmlentities':
  202. /* See PHP's htmlentities - http://www.php.net/manual/en/function.htmlentities.php */
  203. $output = htmlentities($output,ENT_QUOTES,$encoding);
  204. break;
  205. case 'esc':
  206. case 'escape':
  207. $output = preg_replace("/&amp;(#[0-9]+|[a-z]+);/i", "&$1;", htmlspecialchars($output));
  208. $output = str_replace(array ("[", "]", "`"), array ("&#91;", "&#93;", "&#96;"), $output);
  209. break;
  210. case 'strip':
  211. /* Replaces all linebreaks, tabs and multiple spaces with just one space */
  212. $output= preg_replace("/\s+/"," ",$output);
  213. break;
  214. case 'stripString':
  215. /* strips string of this value */
  216. $output= str_replace($m_val,'',$output);
  217. break;
  218. case 'replace':
  219. /* replaces one value with another */
  220. $opt = explode('==',$m_val);
  221. if (count($opt) >= 2) {
  222. $output = str_replace($opt[0],$opt[1],$output);
  223. }
  224. break;
  225. case 'notags':
  226. case 'striptags':
  227. case 'stripTags':
  228. case 'strip_tags':
  229. /* See PHP's strip_tags - http://www.php.net/manual/en/function.strip_tags.php */
  230. if (!empty($m_val)) {
  231. $output= strip_tags($output,$m_val);
  232. } else {
  233. $output= strip_tags($output);
  234. }
  235. break;
  236. case 'length':
  237. case 'len':
  238. case 'strlen':
  239. /* See PHP's strlen - http://www.php.net/manual/en/function.strlen.php */
  240. $output = $usemb ? mb_strlen($output,$encoding) : strlen($output);
  241. break;
  242. case 'reverse':
  243. case 'strrev':
  244. /* See PHP's strrev - http://www.php.net/manual/en/function.strrev.php */
  245. if ($usemb) {
  246. $ar = array();
  247. preg_match_all('/(\d+)?./us', $output, $ar);
  248. $output = join('',array_reverse($ar[0]));
  249. } else {
  250. $output = strrev($output);
  251. }
  252. break;
  253. case 'wordwrap':
  254. /* See PHP's wordwrap - http://www.php.net/manual/en/function.wordwrap.php */
  255. $wrapat= intval($m_val);
  256. if ($wrapat) {
  257. $output= wordwrap($output, $wrapat,"<br />\n ", 0);
  258. } else {
  259. $output= wordwrap($output, 70,"<br />\n", 0);
  260. }
  261. break;
  262. case 'wordwrapcut':
  263. /* See PHP's wordwrap - http://www.php.net/manual/en/function.wordwrap.php */
  264. $wrapat= intval($m_val);
  265. if ($wrapat) {
  266. $output= wordwrap($output, $wrapat,"<br />\n ", 1);
  267. } else {
  268. $output= wordwrap($output, 70,"<br />\n", 1);
  269. }
  270. break;
  271. case 'limit':
  272. /* default: 100 */
  273. $limit= intval($m_val) ? intval($m_val) : 100;
  274. /* ensure that filter correctly counts special chars */
  275. $str = html_entity_decode($output,ENT_COMPAT,$encoding);
  276. if ($usemb) {
  277. $output= mb_substr($str,0,$limit,$encoding);
  278. } else {
  279. $output= substr($str,0,$limit);
  280. }
  281. break;
  282. case 'ellipsis':
  283. $limit= intval($m_val) ? intval($m_val) : 100;
  284. /* ensure that filter correctly counts special chars */
  285. $str = html_entity_decode($output,ENT_COMPAT,$encoding);
  286. if ($usemb) {
  287. if (mb_strlen($str,$encoding) > $limit) {
  288. $output = mb_substr($str,0,$limit,$encoding).'&#8230;';
  289. }
  290. } else if (strlen($str) > $limit) {
  291. $output = substr($str,0,$limit).'&#8230;';
  292. }
  293. break;
  294. /* ##### Special functions */
  295. case 'tag':
  296. /* Displays the raw element tag without :tag */
  297. $tag = $element->_tag;
  298. $tag = htmlentities($tag,ENT_QUOTES,$encoding);
  299. $tag = str_replace(array ("[", "]", "`"), array ("&#91;", "&#93;", "&#96;"), $tag);
  300. $tag = str_replace(":tag","",$tag);
  301. $output = $tag;
  302. break;
  303. case 'math':
  304. /* Returns the result of an advanced calculation (expensive) */
  305. $filter= preg_replace("~([a-zA-Z\n\r\t\s])~", "", $m_val);
  306. $filter= str_replace('?', $output, $filter);
  307. $output= eval("return " . $filter . ";");
  308. break;
  309. case 'add':
  310. case 'increment':
  311. case 'incr':
  312. /* Returns input incremented by option (default: +1) */
  313. if (empty($m_val))
  314. $m_val = 1;
  315. $output = (float)$output + (float)$m_val;
  316. break;
  317. case 'subtract':
  318. case 'decrement':
  319. case 'decr':
  320. /* Returns input decremented by option (default: -1) */
  321. if (empty($m_val))
  322. $m_val = 1;
  323. $output = (float)$output - (float)$m_val;
  324. break;
  325. case 'multiply':
  326. case 'mpy':
  327. /* Returns input multiplied by option (default: *2) */
  328. if (empty($m_val))
  329. $m_val = 1;
  330. $output = (float)$output * (float)$m_val;
  331. break;
  332. case 'divide':
  333. case 'div':
  334. /* Returns input divided by option (default: /2) */
  335. if (empty($m_val))
  336. $m_val = 2;
  337. $output = (float)$output / (float)$m_val;
  338. break;
  339. case 'modulus':
  340. case 'mod':
  341. /* Returns the option modulus on input (default: %2, returns 0 or 1) */
  342. if (empty($m_val))
  343. $m_val = 2;
  344. $output = (float)$output % (float)$m_val;
  345. break;
  346. case 'default':
  347. case 'ifempty':
  348. case 'isempty':
  349. case 'empty':
  350. /* Returns the input value if empty */
  351. if (empty($output))
  352. $output= $m_val;
  353. break;
  354. case 'ifnotempty':
  355. case 'isnotempty':
  356. case 'notempty':
  357. case '!empty':
  358. /* returns input value if not empty */
  359. if (!empty($output))
  360. $output= $m_val;
  361. break;
  362. case 'nl2br':
  363. /* See PHP's nl2br - http://www.php.net/manual/en/function.nl2br.php */
  364. $output= nl2br($output);
  365. break;
  366. case 'date':
  367. /* See PHP's strftime - http://www.php.net/manual/en/function.strftime.php */
  368. if (empty($m_val))
  369. $m_val = "%A, %d %B %Y %H:%M:%S"; /* @todo this should be modx default date/time format? Lexicon? */
  370. $value = 0 + $output;
  371. if ($value != 0 && $value != -1) {
  372. $output= strftime($m_val, 0 + $output);
  373. } else {
  374. $output= '';
  375. }
  376. break;
  377. case 'strtotime':
  378. /* See PHP's strtotime() function - http://www.php.net/strtotime */
  379. if (!empty($output)) {
  380. $output = strtotime($output);
  381. } else {
  382. $output = '';
  383. }
  384. break;
  385. case 'fuzzydate':
  386. /* displays a "fuzzy" date reference */
  387. if (empty($this->modx->lexicon)) $this->modx->getService('lexicon','modLexicon');
  388. $this->modx->lexicon->load('filters');
  389. if (empty($m_val)) $m_val= '%b %e';
  390. if (!empty($output)) {
  391. $time = strtotime($output);
  392. if ($time >= strtotime('today')) {
  393. $output = $this->modx->lexicon('today_at',array('time' => strftime('%I:%M %p',$time)));
  394. } elseif ($time >= strtotime('yesterday')) {
  395. $output = $this->modx->lexicon('yesterday_at',array('time' => strftime('%I:%M %p',$time)));
  396. } else {
  397. $output = strftime($m_val, $time);
  398. }
  399. } else {
  400. $output = '&mdash;';
  401. }
  402. break;
  403. case 'ago':
  404. /* calculates relative time ago from a timestamp */
  405. if (empty($output)) break;
  406. if (empty($this->modx->lexicon)) $this->modx->getService('lexicon','modLexicon');
  407. $this->modx->lexicon->load('filters');
  408. $agoTS = array();
  409. $uts['start'] = strtotime($output);
  410. $uts['end'] = time();
  411. if( $uts['start']!==-1 && $uts['end']!==-1 ) {
  412. if( $uts['end'] >= $uts['start'] ) {
  413. $diff = $uts['end'] - $uts['start'];
  414. $years = intval((floor($diff/31536000)));
  415. if ($years) $diff = $diff % 31536000;
  416. $months = intval((floor($diff/2628000)));
  417. if ($months) $diff = $diff % 2628000;
  418. $weeks = intval((floor($diff/604800)));
  419. if ($weeks) $diff = $diff % 604800;
  420. $days = intval((floor($diff/86400)));
  421. if ($days) $diff = $diff % 86400;
  422. $hours = intval((floor($diff/3600)));
  423. if ($hours) $diff = $diff % 3600;
  424. $minutes = intval((floor($diff/60)));
  425. if ($minutes) $diff = $diff % 60;
  426. $diff = intval($diff);
  427. $agoTS = array(
  428. 'years' => $years,
  429. 'months' => $months,
  430. 'weeks' => $weeks,
  431. 'days' => $days,
  432. 'hours' => $hours,
  433. 'minutes' => $minutes,
  434. 'seconds' => $diff,
  435. );
  436. }
  437. }
  438. $ago = array();
  439. if (!empty($agoTS['years'])) {
  440. $ago[] = $this->modx->lexicon(($agoTS['years'] > 1 ? 'ago_years' : 'ago_year'),array('time' => $agoTS['years']));
  441. }
  442. if (!empty($agoTS['months'])) {
  443. $ago[] = $this->modx->lexicon(($agoTS['months'] > 1 ? 'ago_months' : 'ago_month'),array('time' => $agoTS['months']));
  444. }
  445. if (!empty($agoTS['weeks']) && empty($agoTS['years'])) {
  446. $ago[] = $this->modx->lexicon(($agoTS['weeks'] > 1 ? 'ago_weeks' : 'ago_week'),array('time' => $agoTS['weeks']));
  447. }
  448. if (!empty($agoTS['days']) && empty($agoTS['months']) && empty($agoTS['years'])) {
  449. $ago[] = $this->modx->lexicon(($agoTS['days'] > 1 ? 'ago_days' : 'ago_day'),array('time' => $agoTS['days']));
  450. }
  451. if (!empty($agoTS['hours']) && empty($agoTS['weeks']) && empty($agoTS['months']) && empty($agoTS['years'])) {
  452. $ago[] = $this->modx->lexicon(($agoTS['hours'] > 1 ? 'ago_hours' : 'ago_hour'),array('time' => $agoTS['hours']));
  453. }
  454. if (!empty($agoTS['minutes']) && empty($agoTS['days']) && empty($agoTS['weeks']) && empty($agoTS['months']) && empty($agoTS['years'])) {
  455. $ago[] = $this->modx->lexicon('ago_minutes',array('time' => $agoTS['minutes']));
  456. }
  457. if (empty($ago)) { /* handle <1 min */
  458. $ago[] = $this->modx->lexicon('ago_seconds',array('time' => $agoTS['seconds']));
  459. }
  460. $output = implode(', ',$ago);
  461. $output = $this->modx->lexicon('ago',array('time' => $output));
  462. break;
  463. case 'md5':
  464. /* See PHP's md5 - http://www.php.net/manual/en/function.md5.php */
  465. $output= md5($output);
  466. break;
  467. case 'cdata':
  468. if ($usemb) {
  469. $len = mb_strlen($output,$encoding);
  470. if (mb_strpos($output,'[',0,$encoding) === 0) { $output = ' '.$output; }
  471. if (mb_strpos($output,']',0,$encoding) === $len) { $output = $output.' '; }
  472. } else {
  473. $len = strlen($output);
  474. if (strpos($output,'[') === 0) { $output = ' '.$output; }
  475. if (strpos($output,']') === $len) { $output = $output.' '; }
  476. }
  477. $output= "<![CDATA[{$output}]]>";
  478. break;
  479. case 'userinfo':
  480. /* Returns the requested user data (input: userid) */
  481. if (!empty($output)) {
  482. $key = (!empty($m_val)) ? $m_val : 'username';
  483. $userInfo= false;
  484. if ($user= $this->modx->getObjectGraph('modUser', '{"Profile":{}}', $output)) {
  485. $userInfo= $user->get(array ('username', 'password'));
  486. if ($user->getOne('Profile')) {
  487. $userInfo= array_merge($userInfo, $user->Profile->toArray());
  488. }
  489. }
  490. $output = $userInfo && isset($userInfo[$key]) ? $userInfo[$key] : null;
  491. }
  492. break;
  493. case 'isloggedin':
  494. /* returns true if user is logged in */
  495. $output= $this->modx->user->isAuthenticated($this->modx->context->get('key'));
  496. $output= $output ? true : false;
  497. break;
  498. case 'isnotloggedin':
  499. /* returns true if user is not logged in */
  500. $output= $this->modx->user->isAuthenticated($this->modx->context->get('key'));
  501. $output= $output ? false : true;
  502. break;
  503. case 'urlencode':
  504. $output = urlencode($output);
  505. break;
  506. case 'urldecode':
  507. $output = urldecode($output);
  508. break;
  509. /* Default, custom modifier (run snippet with modifier name) */
  510. default:
  511. /*@todo Possibility to only look for snippet names prefixed with 'filter:' */
  512. /*@todo Maybe pass whole element by reference instead of token/tag/name? */
  513. $params = array (
  514. 'input' => $output,
  515. 'options' => $m_val,
  516. 'token' => $element->_token, /* type of parent element */
  517. 'name' => $element->get('name'), /* name of the parent element */
  518. 'tag' => $element->getTag() /* complete parent tag */
  519. );
  520. $this->log('This modifier is custom running as snippet.');
  521. $tmp = $this->modx->runSnippet($m_cmd, $params);
  522. if ($tmp!='') $output = $tmp;
  523. break;
  524. }
  525. } catch (Exception $e) {
  526. $this->modx->log(modX::LOG_LEVEL_ERROR,$e->getMessage());
  527. }
  528. }
  529. }
  530. }
  531. /**
  532. * Send a log message to the message logger
  533. * @param string $msg
  534. * @return void
  535. */
  536. public function log($msg) {
  537. if ($this->modx->getDebug() === true) {
  538. $this->modx->log(modX::LOG_LEVEL_DEBUG, $msg);
  539. }
  540. }
  541. }