modtemplatevar.class.php 51 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309
  1. <?php
  2. /*
  3. * This file is part of MODX Revolution.
  4. *
  5. * Copyright (c) MODX, LLC. All Rights Reserved.
  6. *
  7. * For complete copyright and license information, see the COPYRIGHT and LICENSE
  8. * files found in the top-level directory of this distribution.
  9. */
  10. /**
  11. * Represents a template variable element.
  12. *
  13. * @property string $type The input type of this TV
  14. * @property string $name The name of this TV, and key by which it will be referenced in tags
  15. * @property string $caption The caption that will be used to display the name of this TV when on the Resource page
  16. * @property string $description A user-provided description of this TV
  17. * @property int $editor_type Deprecated
  18. * @property int $category The Category for this TV, or 0 if not in one
  19. * @property boolean $locked Whether or not this TV can only be edited by an Administrator
  20. * @property string $elements Default values for this TV
  21. * @property int $rank The rank of the TV when sorted and displayed relative to other TVs in its Category
  22. * @property string $display The output render type of this TV
  23. * @property string $default_text The default value of this TV if no other value is set
  24. * @property string $properties An array of default properties for this TV
  25. * @property string $input_properties An array of input properties related to the rendering of the input of this TV
  26. * @property string $output_properties An array of output properties related to the rendering of the output of this TV
  27. *
  28. * @todo Refactor this to allow user-defined and configured input and output
  29. * widgets.
  30. * @see modTemplateVarResource
  31. * @see modTemplateVarResourceGroup
  32. * @see modTemplateVarResourceTemplate
  33. * @see modTemplate
  34. * @package modx
  35. */
  36. class modTemplateVar extends modElement {
  37. /**
  38. * Supported bindings for MODX
  39. * @var array $bindings
  40. */
  41. public $bindings= array (
  42. 'FILE',
  43. 'CHUNK',
  44. 'DOCUMENT',
  45. 'RESOURCE',
  46. 'SELECT',
  47. 'EVAL',
  48. 'INHERIT',
  49. 'DIRECTORY'
  50. );
  51. /** @var modX $xpdo */
  52. public $xpdo;
  53. /**
  54. * A cache for modTemplateVar::getRenderDirectories()
  55. * @see getRenderDirectories()
  56. * @var array $_renderPaths
  57. */
  58. private static $_renderPaths = array();
  59. /**
  60. * Creates a modTemplateVar instance, and sets the token of the class to *
  61. *
  62. * {@inheritdoc}
  63. */
  64. function __construct(& $xpdo) {
  65. parent :: __construct($xpdo);
  66. $this->setToken('*');
  67. }
  68. /**
  69. * Overrides modElement::save to add custom error logging and fire
  70. * modX-specific events.
  71. *
  72. * {@inheritdoc}
  73. */
  74. public function save($cacheFlag = null) {
  75. $isNew = $this->isNew();
  76. if ($this->xpdo instanceof modX) {
  77. $this->xpdo->invokeEvent('OnTemplateVarBeforeSave',array(
  78. 'mode' => $isNew ? modSystemEvent::MODE_NEW : modSystemEvent::MODE_UPD,
  79. 'templateVar' => &$this,
  80. 'cacheFlag' => $cacheFlag,
  81. ));
  82. }
  83. $saved = parent::save($cacheFlag);
  84. if ($saved && $this->xpdo instanceof modX) {
  85. $this->xpdo->invokeEvent('OnTemplateVarSave',array(
  86. 'mode' => $isNew ? modSystemEvent::MODE_NEW : modSystemEvent::MODE_UPD,
  87. 'templateVar' => &$this,
  88. 'cacheFlag' => $cacheFlag,
  89. ));
  90. } else if (!$saved && !empty($this->xpdo->lexicon)) {
  91. $msg = $isNew ? $this->xpdo->lexicon('tv_err_create') : $this->xpdo->lexicon('tv_err_save');
  92. $this->xpdo->log(xPDO::LOG_LEVEL_ERROR,$msg.$this->toArray());
  93. }
  94. return $saved;
  95. }
  96. /**
  97. * Overrides modElement::remove to add custom error logging and fire
  98. * modX-specific events.
  99. *
  100. * {@inheritdoc}
  101. */
  102. public function remove(array $ancestors= array ()) {
  103. if ($this->xpdo instanceof modX) {
  104. $this->xpdo->invokeEvent('OnTemplateVarBeforeRemove',array(
  105. 'templateVar' => &$this,
  106. 'cacheFlag' => true,
  107. ));
  108. }
  109. $removed = parent :: remove($ancestors);
  110. if ($removed && $this->xpdo instanceof modX) {
  111. $this->xpdo->invokeEvent('OnTemplateVarRemove',array(
  112. 'templateVar' => &$this,
  113. 'cacheFlag' => true,
  114. ));
  115. } else if (!$removed && !empty($this->xpdo->lexicon)) {
  116. $this->xpdo->log(xPDO::LOG_LEVEL_ERROR,$this->xpdo->lexicon('tv_err_remove').$this->toArray());
  117. }
  118. return $removed;
  119. }
  120. /**
  121. * Process the template variable and return the output.
  122. *
  123. * {@inheritdoc}
  124. */
  125. public function process($properties= null, $content= null) {
  126. parent :: process($properties, $content);
  127. if (!$this->_processed) {
  128. $this->_content= $this->renderOutput($this->xpdo->resourceIdentifier);
  129. /* copy the content source to the output buffer */
  130. $this->_output= $this->_content;
  131. if (is_string($this->_output) && !empty ($this->_output)) {
  132. /* turn the processed properties into placeholders */
  133. $scope = $this->xpdo->toPlaceholders($this->_properties, '', '.', true);
  134. /* collect element tags in the content and process them */
  135. $maxIterations= intval($this->xpdo->getOption('parser_max_iterations',null,10));
  136. $this->xpdo->parser->processElementTags(
  137. $this->_tag,
  138. $this->_output,
  139. $this->xpdo->parser->isProcessingUncacheable(),
  140. $this->xpdo->parser->isRemovingUnprocessed(),
  141. '[[',
  142. ']]',
  143. array(),
  144. $maxIterations
  145. );
  146. /* remove the placeholders set from the properties of this element and restore global values */
  147. if (isset($scope['keys'])) $this->xpdo->unsetPlaceholders($scope['keys']);
  148. if (isset($scope['restore'])) $this->xpdo->toPlaceholders($scope['restore']);
  149. }
  150. /* apply output filtering */
  151. $this->filterOutput();
  152. /* cache the content */
  153. $this->cache();
  154. $this->_processed= true;
  155. }
  156. $this->xpdo->parser->setProcessingElement(false);
  157. /* finally, return the processed element content */
  158. return $this->_output;
  159. }
  160. /**
  161. * Get the value of a template variable for a resource.
  162. *
  163. * @access public
  164. * @param integer $resourceId The id of the resource; 0 defaults to the default_text field for the tv.
  165. * @return mixed The raw value of the template variable in context of the
  166. * specified resource or the default_text for the tv.
  167. */
  168. public function getValue($resourceId= 0) {
  169. $value= null;
  170. $resourceId = intval($resourceId);
  171. if ($resourceId) {
  172. if (is_object($this->xpdo->resource) && $resourceId === (integer) $this->xpdo->resourceIdentifier && is_array($this->xpdo->resource->get($this->get('name')))) {
  173. $valueArray= $this->xpdo->resource->get($this->get('name'));
  174. $value= $valueArray[1];
  175. } elseif ($resourceId === (integer) $this->get('resourceId') && array_key_exists('value', $this->_fields)) {
  176. $value= $this->get('value');
  177. } else {
  178. $resource = $this->xpdo->getObject('modTemplateVarResource',array(
  179. 'tmplvarid' => $this->get('id'),
  180. 'contentid' => $resourceId,
  181. ),true);
  182. if ($resource && $resource instanceof modTemplateVarResource) {
  183. $value= $resource->get('value');
  184. }
  185. }
  186. }
  187. if ($value === null) {
  188. $value= $this->get('default_text');
  189. }
  190. return $value;
  191. }
  192. /**
  193. * Set the value of a template variable for a resource.
  194. *
  195. * @access public
  196. * @param integer $resourceId The id of the resource; 0 defaults to the
  197. * current resource.
  198. * @param mixed $value The value to give the template variable for the
  199. * specified document.
  200. */
  201. public function setValue($resourceId= 0, $value= null) {
  202. $oldValue= '';
  203. if (intval($resourceId)) {
  204. $templateVarResource = $this->xpdo->getObject('modTemplateVarResource',array(
  205. 'tmplvarid' => $this->get('id'),
  206. 'contentid' => $resourceId,
  207. ),true);
  208. if (!$templateVarResource) {
  209. $templateVarResource= $this->xpdo->newObject('modTemplateVarResource');
  210. }
  211. if ($value !== $this->get('default_text')) {
  212. if (!$templateVarResource->isNew()) {
  213. $templateVarResource->set('value', $value);
  214. } else {
  215. $templateVarResource->set('contentid', $resourceId);
  216. $templateVarResource->set('value', $value);
  217. }
  218. $this->addMany($templateVarResource);
  219. } elseif (!$templateVarResource->isNew()
  220. && ($value === null || $value === $this->get('default_text'))) {
  221. $templateVarResource->remove();
  222. }
  223. }
  224. }
  225. /**
  226. * Returns the processed output of a template variable.
  227. *
  228. * @access public
  229. * @param integer $resourceId The id of the resource; 0 defaults to the
  230. * current resource.
  231. * @return mixed The processed output of the template variable.
  232. */
  233. public function renderOutput($resourceId= 0) {
  234. $value= $this->getValue($resourceId);
  235. /* process any TV commands in value */
  236. $value= $this->processBindings($value, $resourceId);
  237. $params= array ();
  238. /**
  239. * Backwards support for display_params
  240. * @deprecated To be removed in 2.2
  241. */
  242. if ($paramstring= $this->get('display_params')) {
  243. $this->xpdo->deprecated('2.2.0', 'Use output_properties instead.', 'modTemplateVar renderOutput display_params');
  244. $cp= explode("&", $paramstring);
  245. foreach ($cp as $p => $v) {
  246. $ar= explode("=", $v);
  247. if (is_array($ar) && count($ar) == 2) {
  248. $params[$ar[0]]= $this->decodeParamValue($ar[1]);
  249. }
  250. }
  251. }
  252. /* get output_properties for rendering properties */
  253. $outputProperties = $this->get('output_properties');
  254. if (!empty($outputProperties) && is_array($outputProperties)) {
  255. $params = array_merge($params,$outputProperties);
  256. }
  257. /* run prepareOutput to allow for custom overriding */
  258. $value = $this->prepareOutput($value, $resourceId);
  259. /* find the render */
  260. $outputRenderPaths = $this->getRenderDirectories('OnTVOutputRenderList','output');
  261. return $this->getRender($params,$value,$outputRenderPaths,'output',$resourceId,$this->get('display'));
  262. }
  263. /**
  264. * Prepare the output in this method to allow processing of this without depending on the actual render of the output
  265. * @param string $value
  266. * @param integer $resourceId The id of the resource; 0 defaults to the
  267. * current resource.
  268. * @return string
  269. */
  270. public function prepareOutput($value, $resourceId= 0) {
  271. /* Allow custom source types to manipulate the output URL for image/file tvs */
  272. $mTypes = $this->xpdo->getOption('manipulatable_url_tv_output_types',null,'image,file');
  273. $mTypes = explode(',',$mTypes);
  274. if (!empty($value) && in_array($this->get('type'),$mTypes)) {
  275. $context = !empty($resourceId) ? $this->xpdo->getObject('modResource', $resourceId)->get('context_key') : $this->xpdo->context->get('key');
  276. $sourceCache = $this->getSourceCache($context);
  277. if (!empty($sourceCache) && !empty($sourceCache['class_key'])) {
  278. $coreSourceClasses = $this->xpdo->getOption('core_media_sources',null,'modFileMediaSource,modS3MediaSource');
  279. $coreSourceClasses = explode(',',$coreSourceClasses);
  280. $classKey = in_array($sourceCache['class_key'],$coreSourceClasses) ? 'sources.'.$sourceCache['class_key'] : $sourceCache['class_key'];
  281. if ($this->xpdo->loadClass($classKey)) {
  282. /** @var modMediaSource $source */
  283. $source = $this->xpdo->newObject($classKey);
  284. if ($source) {
  285. $source->fromArray($sourceCache,'',true,true);
  286. $source->initialize();
  287. $isAbsolute = strpos($value,'http://') === 0 || strpos($value,'https://') === 0 || strpos($value,'ftp://') === 0;
  288. if (!$isAbsolute) {
  289. $value = $source->prepareOutputUrl($value);
  290. }
  291. }
  292. }
  293. }
  294. }
  295. return $value;
  296. }
  297. /**
  298. * Renders input forms for the template variable.
  299. *
  300. * @access public
  301. * @param modResource|null $resource The resource; 0 defaults to the current resource.
  302. * @param mixed $options Array of options ('value', 'style') or deprecated $style string
  303. * @return mixed The rendered input for the template variable.
  304. */
  305. public function renderInput($resource= null, $options = array()) {
  306. if (is_int($resource)) {
  307. $resource = $this->xpdo->getObject('modResource',$resource);
  308. }
  309. if (empty($resource)) {
  310. $resource = $this->xpdo->resource;
  311. } else {
  312. $this->xpdo->resource = $resource;
  313. }
  314. $resourceId = $resource ? $resource->get('id') : 0;
  315. if (is_string($options) && !empty($options)) {
  316. // fall back to deprecated $style setting
  317. $style = $options;
  318. } else {
  319. $style = is_array($options) && isset($options['style']) ? strval($options['style']) : '';
  320. $value = is_array($options) && isset($options['value']) ? strval($options['value']) : '';
  321. }
  322. if (!isset($this->xpdo->smarty)) {
  323. $this->xpdo->getService('smarty', 'smarty.modSmarty', '', array(
  324. 'template_dir' => $this->xpdo->getOption('manager_path') . 'templates/' . $this->xpdo->getOption('manager_theme',null,'default') . '/',
  325. ));
  326. }
  327. $this->xpdo->smarty->assign('style',$style);
  328. if(!isset($value) || empty($value)) {
  329. $value = $this->getValue($resourceId);
  330. }
  331. /* process only @INHERIT bindings in value, since we're inputting */
  332. $bindingData = $this->getBindingDataFromValue($value);
  333. if ($bindingData['cmd'] == 'INHERIT') {
  334. $value = $this->processInheritBinding($bindingData['param'], $resourceId);
  335. }
  336. /* if any FC tvDefault rules, set here */
  337. $value = $this->checkForFormCustomizationRules($value,$resource);
  338. /* properly set value back if any FC rules, resource values, or bindings have adjusted it */
  339. $this->set('value',$value);
  340. $this->set('processedValue',$value);
  341. $this->set('default_text',$this->processBindings($this->get('default_text'),$resourceId));
  342. /* strip tags from description */
  343. $this->set('description',strip_tags($this->get('description')));
  344. $params= array ();
  345. if ($paramstring= $this->get('display_params')) {
  346. $cp= explode("&", $paramstring);
  347. foreach ($cp as $p => $v) {
  348. $v= trim($v);
  349. $ar= explode("=", $v);
  350. if (is_array($ar) && count($ar) == 2) {
  351. $params[$ar[0]]= $this->decodeParamValue($ar[1]);
  352. }
  353. }
  354. }
  355. $params = $this->get('input_properties');
  356. /* default required status to no */
  357. if (!isset($params['allowBlank'])) $params['allowBlank'] = 1;
  358. /* find the correct renderer for the TV, if not one, render a textbox */
  359. $inputRenderPaths = $this->getRenderDirectories('OnTVInputRenderList','input');
  360. return $this->getRender($params,$value,$inputRenderPaths,'input',$resourceId,$this->get('type'));
  361. }
  362. /**
  363. * Gets the correct render given paths and type of render
  364. *
  365. * @param array $params The parameters to pass to the render
  366. * @param mixed $value The value of the TV
  367. * @param array $paths An array of paths to search
  368. * @param string $method The type of Render (input/output/properties)
  369. * @param integer $resourceId The ID of the current Resource
  370. * @param string $type The type of render to display
  371. * @return string
  372. */
  373. public function getRender($params,$value,array $paths,$method,$resourceId = 0,$type = 'text') {
  374. if (empty($type)) $type = 'text';
  375. if (empty($this->xpdo->resource)) {
  376. if (!empty($resourceId)) {
  377. $this->xpdo->resource = $this->xpdo->getObject('modResource',$resourceId);
  378. }
  379. if (empty($this->xpdo->resource) || empty($resourceId)) {
  380. $this->xpdo->resource = $this->xpdo->newObject('modResource');
  381. $this->xpdo->resource->set('id',0);
  382. }
  383. }
  384. if ($className = $this->checkForRegisteredRenderMethod($type,$method)) {
  385. /** @var modTemplateVarOutputRender $render */
  386. $render = new $className($this);
  387. $output = $render->render($value,$params);
  388. } else {
  389. $deprecatedClassName = $method == 'input' ? 'modTemplateVarInputRenderDeprecated' : 'modTemplateVarOutputRenderDeprecated';
  390. $render = new $deprecatedClassName($this);
  391. foreach ($paths as $path) {
  392. $renderFile = $path.$type.'.class.php';
  393. if (file_exists($renderFile)) {
  394. $className = include $renderFile;
  395. $this->registerRenderMethod($type,$method,$className);
  396. if (class_exists($className)) {
  397. /** @var modTemplateVarOutputRender $render */
  398. $render = new $className($this);
  399. }
  400. break;
  401. }
  402. /* 2.1< backwards compat */
  403. $renderFile = $path.$type.'.php';
  404. if (file_exists($renderFile)) {
  405. $this->xpdo->deprecated('2.2.0', '', 'Old style template variable with flat render file ' . $renderFile . ', for TV ' . $this->get('name'));
  406. $render = new $deprecatedClassName($this);
  407. $params['modx.renderFile'] = $renderFile;
  408. break;
  409. }
  410. }
  411. $output = $render->render($value,$params);
  412. /* if no output, fallback to text */
  413. if (empty($output)) {
  414. $p = $this->xpdo->getOption('processors_path').'element/tv/renders/mgr/'.$method.'/';
  415. $className = $method == 'output' ? 'modTemplateVarOutputRenderText' : 'modTemplateVarInputRenderText';
  416. if (!class_exists($className) && file_exists($p.'text.class.php')) {
  417. $className = include $p.'text.class.php';
  418. }
  419. if (class_exists($className)) {
  420. $render = new $className($this);
  421. $output = $render->render($value,$params);
  422. }
  423. }
  424. }
  425. return $output;
  426. }
  427. /**
  428. * Check for a registered TV render
  429. * @param string $type
  430. * @param string $method
  431. * @return bool
  432. */
  433. public function checkForRegisteredRenderMethod($type,$method) {
  434. $v = false;
  435. if (!isset($this->xpdo->tvRenders)) $this->xpdo->tvRenders = array();
  436. if (!isset($this->xpdo->tvRenders[$method])) {
  437. $this->xpdo->tvRenders[$method] = array();
  438. }
  439. if (!empty($this->xpdo->tvRenders[$method][$type])) {
  440. $v = $this->xpdo->tvRenders[$method][$type];
  441. }
  442. return $v;
  443. }
  444. /**
  445. * Register a render method to the array cache to prevent double loading of the class
  446. * @param string $type
  447. * @param string $method
  448. * @param string $className
  449. * @return mixed
  450. */
  451. public function registerRenderMethod($type,$method,$className) {
  452. if (!isset($this->xpdo->tvRenders)) $this->xpdo->tvRenders = array();
  453. if (!isset($this->xpdo->tvRenders[$method])) {
  454. $this->xpdo->tvRenders[$method] = array();
  455. }
  456. $this->xpdo->tvRenders[$method][$type] = $className;
  457. return $className;
  458. }
  459. /**
  460. * Finds the correct directories for renders
  461. *
  462. * @param string $event The plugin event to fire
  463. * @param string $subdir The subdir to search
  464. * @return array The found render directories
  465. */
  466. public function getRenderDirectories($event,$subdir) {
  467. $context = $this->xpdo->context->get('key');
  468. $renderPath = $this->xpdo->getOption('processors_path').'element/tv/renders/'.$context.'/'.$subdir.'/';
  469. $renderDirectories = array(
  470. $renderPath,
  471. $this->xpdo->getOption('processors_path').'element/tv/renders/'.($subdir == 'input' ? 'mgr' : 'web').'/'.$subdir.'/',
  472. );
  473. $pluginResult = $this->xpdo->invokeEvent($event,array(
  474. 'context' => $context,
  475. ));
  476. $pathsKey = serialize($pluginResult).$context.$event.$subdir;
  477. /* return cached value if exists */
  478. if (isset(self::$_renderPaths[$pathsKey])) {
  479. return self::$_renderPaths[$pathsKey];
  480. }
  481. /* process if there is no cached value */
  482. if (!is_array($pluginResult) && !empty($pluginResult)) { $pluginResult = array($pluginResult); }
  483. if (!empty($pluginResult)) {
  484. foreach ($pluginResult as $result) {
  485. if (empty($result)) continue;
  486. $renderDirectories[] = $result;
  487. }
  488. }
  489. /* search directories */
  490. $renderPaths = array();
  491. foreach ($renderDirectories as $renderDirectory) {
  492. if (empty($renderDirectory) || !is_dir($renderDirectory)) continue;
  493. try {
  494. $dirIterator = new DirectoryIterator($renderDirectory);
  495. foreach ($dirIterator as $file) {
  496. if (!$file->isReadable() || !$file->isFile()) continue;
  497. $renderPaths[] = dirname($file->getPathname()).'/';
  498. }
  499. } catch (UnexpectedValueException $e) {}
  500. }
  501. self::$_renderPaths[$pathsKey] = array_unique($renderPaths);
  502. return self::$_renderPaths[$pathsKey];
  503. }
  504. /**
  505. * Check for any Form Customization rules for this TV
  506. * @param string $value
  507. * @param modResource $resource
  508. * @return mixed
  509. */
  510. public function checkForFormCustomizationRules($value,&$resource) {
  511. if ($this->xpdo->request && $this->xpdo->user instanceof modUser) {
  512. if (empty($resource)) {
  513. $resource =& $this->xpdo->resource;
  514. }
  515. if ($this->xpdo->getOption('form_customization_use_all_groups',null,false)) {
  516. $userGroups = $this->xpdo->user->getUserGroups();
  517. } else {
  518. $primaryGroup = $this->xpdo->user->getPrimaryGroup();
  519. if ($primaryGroup) {
  520. $userGroups = array($primaryGroup->get('id'));
  521. }
  522. }
  523. $c = $this->xpdo->newQuery('modActionDom');
  524. $c->innerJoin('modFormCustomizationSet','FCSet');
  525. $c->innerJoin('modFormCustomizationProfile','Profile','FCSet.profile = Profile.id');
  526. $c->leftJoin('modFormCustomizationProfileUserGroup','ProfileUserGroup','Profile.id = ProfileUserGroup.profile');
  527. $c->leftJoin('modFormCustomizationProfile','UGProfile','UGProfile.id = ProfileUserGroup.profile');
  528. $ruleFieldName = $this->xpdo->escape('rule');
  529. $c->where(array(
  530. array(
  531. "(modActionDom.{$ruleFieldName} = 'tvDefault'
  532. OR modActionDom.{$ruleFieldName} = 'tvVisible'
  533. OR modActionDom.{$ruleFieldName} = 'tvTitle')"
  534. ),
  535. "'tv{$this->get('id')}' IN ({$this->xpdo->escape('modActionDom')}.{$this->xpdo->escape('name')})",
  536. 'FCSet.active' => true,
  537. 'Profile.active' => true,
  538. ));
  539. if (!empty($userGroups)) {
  540. $c->where(array(
  541. array(
  542. 'ProfileUserGroup.usergroup:IN' => $userGroups,
  543. array(
  544. 'OR:ProfileUserGroup.usergroup:IS' => null,
  545. 'AND:UGProfile.active:=' => true,
  546. ),
  547. ),
  548. 'OR:ProfileUserGroup.usergroup:=' => null,
  549. ),xPDOQuery::SQL_AND,null,2);
  550. }
  551. if (!empty($this->xpdo->request) && !empty($this->xpdo->request->action)) {
  552. $wildAction = substr($this->xpdo->request->action, 0, strrpos($this->xpdo->request->action, '/')) . '/*';
  553. $c->where(array(
  554. 'modActionDom.action:IN' => array($this->xpdo->request->action, $wildAction),
  555. ));
  556. }
  557. $c->select($this->xpdo->getSelectColumns('modActionDom','modActionDom'));
  558. $c->select(array(
  559. 'FCSet.constraint_class',
  560. 'FCSet.constraint_field',
  561. 'FCSet.' . $this->xpdo->escape('constraint'),
  562. 'FCSet.template',
  563. ));
  564. $c->sortby('FCSet.template','ASC');
  565. $c->sortby('modActionDom.rank','ASC');
  566. $domRules = $this->xpdo->getCollection('modActionDom',$c);
  567. /** @var modActionDom $rule */
  568. foreach ($domRules as $rule) {
  569. if (!empty($resource)) {
  570. $template = $rule->get('template');
  571. if (!empty($template) && $template != $resource->get('template')) {
  572. continue;
  573. }
  574. $constraintClass = $rule->get('constraint_class');
  575. if (!empty($constraintClass)) {
  576. if (!($resource instanceof $constraintClass)) continue;
  577. $constraintField = $rule->get('constraint_field');
  578. $constraint = $rule->get('constraint');
  579. if ($resource->get($constraintField) != $constraint) {
  580. continue;
  581. }
  582. }
  583. }
  584. switch ($rule->get('rule')) {
  585. case 'tvVisible':
  586. if ($rule->get('value') == 0) {
  587. $this->set('type','hidden');
  588. }
  589. break;
  590. case 'tvDefault':
  591. $v = $rule->get('value');
  592. if (empty($resourceId)) {
  593. $value = $v;
  594. $this->set('value',$v);
  595. }
  596. $this->set('default_text',$v);
  597. break;
  598. case 'tvTitle':
  599. $v = $rule->get('value');
  600. $this->set('caption',$v);
  601. break;
  602. }
  603. }
  604. unset($domRules,$rule,$userGroups,$v,$c);
  605. }
  606. return $value;
  607. }
  608. /**
  609. * Decodes special function-based chars from a parameter value.
  610. *
  611. * @access public
  612. * @param string $s The string to decode.
  613. * @return string The decoded string.
  614. */
  615. public function decodeParamValue($s) {
  616. $s= str_replace(array("%3D",'&#61;'), '=', $s);
  617. $s= str_replace("%26", '&', $s);
  618. return $s;
  619. }
  620. /**
  621. * Returns an array of display params for this TV
  622. *
  623. * @return array The processed settings
  624. */
  625. public function getDisplayParams() {
  626. $settings = array();
  627. $params = $this->get('display_params');
  628. $ps = explode('&',$params);
  629. foreach ($ps as $p) {
  630. $param = explode('=',$p);
  631. if (!empty($p[0])) {
  632. $v = !empty($param[1]) ? $param[1] : 0;
  633. if ($v == 'true') $v = 1;
  634. if ($v == 'false') $v = 0;
  635. $settings[$param[0]] = $v;
  636. }
  637. }
  638. return $settings;
  639. }
  640. /**
  641. * Returns a string or array representation of input options from a source.
  642. *
  643. * @param mixed $src A PDOStatement, array or string source to parse.
  644. * @param string $delim A delimiter for string parsing.
  645. * @param string $type Type to return, either 'string' or 'array'.
  646. *
  647. * @return string|array If delimiter present, returns string, otherwise array.
  648. */
  649. public function parseInput($src, $delim= "||", $type= "string") {
  650. if (is_object($src)) {
  651. if ($src instanceof PDOStatement) {
  652. $rs= $src->fetchAll(PDO::FETCH_ASSOC);
  653. if ($type != "array") {
  654. $rows = array();
  655. foreach ($rs as $row) {
  656. $rows[]= implode(" ", $row);
  657. }
  658. } else {
  659. $rows= $rs;
  660. }
  661. return ($type == "array" ? $rows : implode($delim, $rows));
  662. }
  663. } elseif (is_array($src) && $type == "array") {
  664. return ($type == "array" ? $src : implode($delim, $src));
  665. } else {
  666. /* must be a text */
  667. if ($type == "array")
  668. return explode($delim, $src);
  669. else
  670. return $src;
  671. }
  672. }
  673. /**
  674. * Parses input options sent through post back.
  675. *
  676. * @param mixed $v A PDOStatement, array or string to parse.
  677. * @return mixed The parsed options.
  678. */
  679. public function parseInputOptions($v) {
  680. $a = array();
  681. if(is_array($v)) return $v;
  682. else if (is_object($v)) {
  683. $a = $v->fetchAll(PDO::FETCH_ASSOC);
  684. }
  685. else $a = explode("||", $v);
  686. return $a;
  687. }
  688. /**
  689. * Parses the binding data from a value
  690. *
  691. * @param mixed $value The value to parse
  692. * @return array An array of cmd and param for the binding
  693. */
  694. public function getBindingDataFromValue($value) {
  695. $nvalue = trim($value);
  696. $cmd = false;
  697. $param = '';
  698. if (substr($nvalue,0,1) == '@') {
  699. list($cmd,$param) = $this->parseBinding($nvalue);
  700. $cmd = trim($cmd);
  701. }
  702. return array('cmd' => $cmd,'param' => $param);
  703. }
  704. /**
  705. * Process bindings assigned to a template variable.
  706. *
  707. * @access public
  708. * @param string $value The value specified from the binding.
  709. * @param integer $resourceId The resource in which the TV is assigned.
  710. * @param boolean $preProcess Whether or not to process certain bindings.
  711. * @return string The processed value.
  712. */
  713. public function processBindings($value= '', $resourceId= 0, $preProcess = true) {
  714. $bdata = $this->getBindingDataFromValue($value);
  715. if (empty($bdata['cmd'])) return $value;
  716. $modx =& $this->xpdo;
  717. if (empty($modx->resource)) {
  718. if (!empty($resourceId)) {
  719. $modx->resource = $modx->getObject('modResource',$resourceId);
  720. }
  721. if (empty($modx->resource) || empty($resourceId)) {
  722. $modx->resource = $modx->newObject('modResource');
  723. $modx->resource->set('id',0);
  724. }
  725. }
  726. $cmd = $bdata['cmd'];
  727. $param = !empty($bdata['param']) ? $bdata['param'] : null;
  728. switch ($cmd) {
  729. case 'FILE':
  730. if ($preProcess) {
  731. $output = $this->processFileBinding($param);
  732. }
  733. break;
  734. case 'CHUNK': /* retrieve a chunk and process it's content */
  735. if ($preProcess) {
  736. $output = $this->xpdo->getChunk($param);
  737. }
  738. break;
  739. case 'RESOURCE':
  740. case 'DOCUMENT': /* retrieve a document and process it's content */
  741. if ($preProcess) {
  742. $query = $this->xpdo->newQuery('modResource', array(
  743. 'id' => (integer) $param,
  744. 'deleted' => false
  745. ));
  746. $query->select('content');
  747. if ($query->prepare() && $query->stmt->execute()) {
  748. $output = $query->stmt->fetch(PDO::FETCH_COLUMN);
  749. } else {
  750. $output = 'Unable to locate resource '.$param;
  751. }
  752. }
  753. break;
  754. case 'SELECT': /* selects a record from the cms database */
  755. if ($preProcess) {
  756. $dbtags = array();
  757. if ($modx->resource && $modx->resource instanceof modResource) {
  758. $dbtags = $modx->resource->toArray();
  759. }
  760. $dbtags['DBASE'] = $this->xpdo->getOption('dbname');
  761. $dbtags['PREFIX'] = $this->xpdo->getOption('table_prefix');
  762. foreach($dbtags as $key => $pValue) {
  763. $param = str_replace('[[+'.$key.']]', $pValue, $param);
  764. }
  765. $stmt = $this->xpdo->query('SELECT '.$param);
  766. if ($stmt && $stmt instanceof PDOStatement) {
  767. $data = '';
  768. while ($row = $stmt->fetch(PDO::FETCH_NUM)) {
  769. $col = '';
  770. if (isset($row[1])) {
  771. $col = $row[0].'=='.$row[1];
  772. } else {
  773. $col = $row[0];
  774. }
  775. $data .= (!empty($data) ? '||' : '').$col;
  776. }
  777. $stmt->closeCursor();
  778. }
  779. $output = $data;
  780. }
  781. break;
  782. case 'EVAL': /* evaluates text as php codes return the results */
  783. if ($preProcess) {
  784. $output = $param;
  785. if ($this->xpdo->getOption('allow_tv_eval', null, true)) {
  786. $output = eval($param);
  787. }
  788. }
  789. break;
  790. case 'INHERIT':
  791. if ($preProcess) {
  792. $output = $this->processInheritBinding($param,$resourceId);
  793. } else {
  794. $output = $value;
  795. }
  796. break;
  797. case 'DIRECTORY':
  798. $path = $this->xpdo->getOption('base_path').$param;
  799. if (substr($path,-1,1) != '/') { $path .= '/'; }
  800. if (!is_dir($path)) { break; }
  801. $files = array();
  802. $invalid = array('.','..','.svn','.git','.DS_Store');
  803. foreach (new DirectoryIterator($path) as $file) {
  804. if (!$file->isReadable()) continue;
  805. $basename = $file->getFilename();
  806. if(!in_array($basename,$invalid)) {
  807. $files[] = "{$basename}=={$param}/{$basename}";
  808. }
  809. }
  810. asort($files);
  811. $output = implode('||',$files);
  812. break;
  813. default:
  814. $output = $value;
  815. break;
  816. }
  817. /* support for nested bindings */
  818. return is_string($output) && ($output != $value) ? $this->processBindings($output) : $output;
  819. }
  820. /**
  821. * Parses bindings to an appropriate format.
  822. *
  823. * @access public
  824. * @param string $binding_string The binding to parse.
  825. * @return array The parsed binding, now in array format.
  826. */
  827. public function parseBinding($binding_string) {
  828. $match= array ();
  829. $binding_string= trim($binding_string);
  830. $regexp= '/@(' . implode('|', $this->bindings) . ')\s*(.*)/is'; /* Split binding on whitespace */
  831. if (preg_match($regexp, $binding_string, $match)) {
  832. /* We can't return the match array directly because the first element is the whole string */
  833. $binding_array= array (
  834. strtoupper($match[1]),
  835. trim($match[2])
  836. ); /* Make command uppercase */
  837. return $binding_array;
  838. }
  839. }
  840. /**
  841. * Parse inherit binding
  842. *
  843. * @param string $default The value to default if there is no inherited value
  844. * @param int $resourceId The current Resource, if any
  845. * @return string The inherited value
  846. */
  847. public function processInheritBinding($default = '',$resourceId = null) {
  848. $output = $default; /* Default to param value if no content from parents */
  849. $resource = null;
  850. $resourceColumns = $this->xpdo->getSelectColumns('modResource', '', '', array('id', 'parent'));
  851. $resourceQuery = new xPDOCriteria($this->xpdo, "SELECT {$resourceColumns} FROM {$this->xpdo->getTableName('modResource')} WHERE id = ?");
  852. if (!empty($resourceId) && (!($this->xpdo->resource instanceof modResource) || $this->xpdo->resource->get('id') != $resourceId)) {
  853. if ($resourceQuery->stmt && $resourceQuery->stmt->execute(array($resourceId))) {
  854. $result = $resourceQuery->stmt->fetchAll(PDO::FETCH_ASSOC);
  855. $resource = reset($result);
  856. }
  857. } else if ($this->xpdo->resource instanceof modResource) {
  858. $resource = $this->xpdo->resource->get(array('id', 'parent'));
  859. }
  860. if (!empty($resource)) {
  861. $currentResource = $resource;
  862. while ($currentResource['parent'] != 0) {
  863. if ($resourceQuery->stmt && $resourceQuery->stmt->execute(array($currentResource['parent']))) {
  864. $result = $resourceQuery->stmt->fetchAll(PDO::FETCH_ASSOC);
  865. $currentResource = reset($result);
  866. } else {
  867. break;
  868. }
  869. if (!empty($currentResource)) {
  870. $tv = $this->getValue($currentResource['id']);
  871. if (($tv === '0' || !empty($tv)) && substr($tv,0,1) != '@') {
  872. $output = $tv;
  873. break;
  874. }
  875. } else {
  876. break;
  877. }
  878. }
  879. }
  880. return $output;
  881. }
  882. /**
  883. * Special parsing for file bindings.
  884. *
  885. * @access public
  886. * @param string $file The absolute location of the file in the binding.
  887. * @return string The file buffer from the read file.
  888. */
  889. public function processFileBinding($file) {
  890. if (file_exists($file) && @ $handle= fopen($file,'r')) {
  891. $buffer= "";
  892. while (!feof($handle)) {
  893. $buffer .= fgets($handle, 4096);
  894. }
  895. fclose($handle);
  896. } else {
  897. $buffer= " Could not retrieve document '$file'.";
  898. }
  899. $displayParams = $this->getDisplayParams();
  900. if (!empty($displayParams['delimiter'])) {
  901. $buffer = str_replace("\n",'||',$buffer);
  902. }
  903. return $buffer;
  904. }
  905. /**
  906. * Loads the access control policies applicable to this template variable.
  907. *
  908. * {@inheritdoc}
  909. */
  910. public function findPolicy($context = '') {
  911. $policy = array();
  912. $context = !empty($context) ? $context : $this->xpdo->context->get('key');
  913. if ($context === $this->xpdo->context->get('key')) {
  914. $catEnabled = (boolean) $this->xpdo->getOption('access_category_enabled', null, true);
  915. $rgEnabled = (boolean) $this->xpdo->getOption('access_resource_group_enabled', null, true);
  916. } elseif ($this->xpdo->getContext($context)) {
  917. $catEnabled = (boolean) $this->xpdo->contexts[$context]->getOption('access_category_enabled', true);
  918. $rgEnabled = (boolean) $this->xpdo->contexts[$context]->getOption('access_resource_group_enabled', true);
  919. }
  920. $enabled = ($catEnabled || $rgEnabled);
  921. if ($enabled) {
  922. if (empty($this->_policies) || !isset($this->_policies[$context])) {
  923. if ($rgEnabled) {
  924. $accessTable = $this->xpdo->getTableName('modAccessResourceGroup');
  925. $policyTable = $this->xpdo->getTableName('modAccessPolicy');
  926. $resourceGroupTable = $this->xpdo->getTableName('modTemplateVarResourceGroup');
  927. $sql = "SELECT Acl.target, Acl.principal, Acl.authority, Acl.policy, Policy.data FROM {$accessTable} Acl " .
  928. "LEFT JOIN {$policyTable} Policy ON Policy.id = Acl.policy " .
  929. "JOIN {$resourceGroupTable} ResourceGroup ON Acl.principal_class = 'modUserGroup' " .
  930. "AND (Acl.context_key = :context OR Acl.context_key IS NULL OR Acl.context_key = '') " .
  931. "AND ResourceGroup.tmplvarid = :element " .
  932. "AND ResourceGroup.documentgroup = Acl.target " .
  933. "ORDER BY Acl.target, Acl.principal, Acl.authority";
  934. $bindings = array(
  935. ':element' => $this->get('id'),
  936. ':context' => $context
  937. );
  938. $query = new xPDOCriteria($this->xpdo, $sql, $bindings);
  939. if ($query->stmt && $query->stmt->execute()) {
  940. while ($row = $query->stmt->fetch(PDO::FETCH_ASSOC)) {
  941. $policy['modAccessResourceGroup'][$row['target']][] = array(
  942. 'principal' => $row['principal'],
  943. 'authority' => $row['authority'],
  944. 'policy' => $row['data'] ? $this->xpdo->fromJSON($row['data'], true) : array(),
  945. );
  946. }
  947. }
  948. }
  949. if ($catEnabled) {
  950. $accessTable = $this->xpdo->getTableName('modAccessCategory');
  951. $categoryClosureTable = $this->xpdo->getTableName('modCategoryClosure');
  952. $sql = "SELECT Acl.target, Acl.principal, Acl.authority, Acl.policy, Policy.data FROM {$accessTable} Acl " .
  953. "LEFT JOIN {$policyTable} Policy ON Policy.id = Acl.policy " .
  954. "JOIN {$categoryClosureTable} CategoryClosure ON CategoryClosure.descendant = :category " .
  955. "AND Acl.principal_class = 'modUserGroup' " .
  956. "AND CategoryClosure.ancestor = Acl.target " .
  957. "AND (Acl.context_key = :context OR Acl.context_key IS NULL OR Acl.context_key = '') " .
  958. "ORDER BY CategoryClosure.depth DESC, target, principal, authority ASC";
  959. $bindings = array(
  960. ':category' => $this->get('category'),
  961. ':context' => $context,
  962. );
  963. $query = new xPDOCriteria($this->xpdo, $sql, $bindings);
  964. if ($query->stmt && $query->stmt->execute()) {
  965. while ($row = $query->stmt->fetch(PDO::FETCH_ASSOC)) {
  966. $policy['modAccessCategory'][$row['target']][] = array(
  967. 'principal' => $row['principal'],
  968. 'authority' => $row['authority'],
  969. 'policy' => $row['data'] ? $this->xpdo->fromJSON($row['data'], true) : array(),
  970. );
  971. }
  972. }
  973. }
  974. $this->_policies[$context] = $policy;
  975. } else {
  976. $policy = $this->_policies[$context];
  977. }
  978. }
  979. return $policy;
  980. }
  981. /**
  982. * Check to see if the TV has access to a Template
  983. *
  984. * @param mixed $templatePk Either the ID, name or object of the Template
  985. * @return boolean Whether or not the TV has access to the specified Template
  986. */
  987. public function hasTemplate($templatePk) {
  988. if (!is_int($templatePk) && !is_object($templatePk)) {
  989. $template = $this->xpdo->getObject('modTemplate',array('templatename' => $templatePk));
  990. if (empty($template) || !is_object($template) || !($template instanceof modTemplate)) {
  991. $this->xpdo->log(modX::LOG_LEVEL_ERROR,'modTemplateVar::hasTemplate - No template: '.$templatePk);
  992. return false;
  993. }
  994. } else {
  995. $template =& $templatePk;
  996. }
  997. $templateVarTemplate = $this->xpdo->getObject('modTemplateVarTemplate',array(
  998. 'tmplvarid' => $this->get('id'),
  999. 'templateid' => is_object($template) ? $template->get('id') : $template,
  1000. ));
  1001. return !empty($templateVarTemplate) && is_object($templateVarTemplate);
  1002. }
  1003. /**
  1004. * Check to see if the
  1005. * @param modUser|null $user
  1006. * @param string $context
  1007. * @return bool
  1008. */
  1009. public function checkResourceGroupAccess($user = null,$context = '') {
  1010. $context = !empty($context) ? $context : '';
  1011. $c = $this->xpdo->newQuery('modResourceGroup');
  1012. $c->innerJoin('modTemplateVarResourceGroup','TemplateVarResourceGroups',array(
  1013. 'TemplateVarResourceGroups.documentgroup = modResourceGroup.id',
  1014. 'TemplateVarResourceGroups.tmplvarid' => $this->get('id'),
  1015. ));
  1016. $resourceGroups = $this->xpdo->getCollection('modResourceGroup',$c);
  1017. $hasAccess = true;
  1018. if (!empty($resourceGroups)) {
  1019. $hasAccess = false;
  1020. /** @var modResourceGroup $resourceGroup */
  1021. foreach ($resourceGroups as $resourceGroup) {
  1022. if ($resourceGroup->hasAccess($user,$context)) {
  1023. $hasAccess = true;
  1024. break;
  1025. }
  1026. }
  1027. }
  1028. return $hasAccess;
  1029. }
  1030. }
  1031. /**
  1032. * An abstract class meant to be used by TV renders. Do not extend this class directly; use its Input or Output
  1033. * derivatives instead.
  1034. *
  1035. * @package modx
  1036. */
  1037. abstract class modTemplateVarRender {
  1038. /** @var modTemplateVar $tv */
  1039. public $tv;
  1040. /** @var modX $modx */
  1041. public $modx;
  1042. /** @var array $config */
  1043. public $config = array();
  1044. function __construct(modTemplateVar $tv,array $config = array()) {
  1045. $this->tv =& $tv;
  1046. $this->modx =& $tv->xpdo;
  1047. $this->config = array_merge($this->config,$config);
  1048. }
  1049. /**
  1050. * Get any lexicon topics for your render. You may override this method in your render to provide an array of
  1051. * lexicon topics to load.
  1052. * @return array
  1053. */
  1054. public function getLexiconTopics() {
  1055. return array('tv_widget');
  1056. }
  1057. /**
  1058. * Render the TV render.
  1059. * @param string $value
  1060. * @param array $params
  1061. * @return mixed|void
  1062. */
  1063. public function render($value,array $params = array()) {
  1064. if (!empty($params)) {
  1065. foreach ($params as $k => $v) {
  1066. if ($v === 'true') {
  1067. $params[$k] = TRUE;
  1068. } elseif ($v === 'false') {
  1069. $params[$k] = FALSE;
  1070. } elseif (is_numeric($v) && ((int) $v == $v)) {
  1071. $params[$k] = intval($v);
  1072. } elseif (is_numeric($v)) {
  1073. $params[$k] = (float)($v);
  1074. }
  1075. }
  1076. }
  1077. $this->_loadLexiconTopics();
  1078. return $this->process($value,$params);
  1079. }
  1080. /**
  1081. * Load any specified lexicon topics for the render
  1082. */
  1083. protected function _loadLexiconTopics() {
  1084. $topics = $this->getLexiconTopics();
  1085. if (!empty($topics) && is_array($topics)) {
  1086. foreach ($topics as $topic) {
  1087. $this->modx->lexicon->load($topic);
  1088. }
  1089. }
  1090. }
  1091. /**
  1092. * @param string $value
  1093. * @param array $params
  1094. * @return void|mixed
  1095. */
  1096. public function process($value,array $params = array()) {
  1097. return $value;
  1098. }
  1099. }
  1100. /**
  1101. * An abstract class for extending Output Renders for TVs.
  1102. * @package modx
  1103. */
  1104. abstract class modTemplateVarOutputRender extends modTemplateVarRender {}
  1105. /**
  1106. * An abstract class for extending Input Renders for TVs.
  1107. * @package modx
  1108. */
  1109. abstract class modTemplateVarInputRender extends modTemplateVarRender {
  1110. public function render($value,array $params = array()) {
  1111. $this->setPlaceholder('tv',$this->tv);
  1112. $this->setPlaceholder('id',$this->tv->get('id'));
  1113. $this->setPlaceholder('ctx',isset($_REQUEST['ctx']) ? $_REQUEST['ctx'] : 'web');
  1114. $this->setPlaceholder('params',$params);
  1115. $output = parent::render($value,$params);
  1116. $tpl = $this->getTemplate();
  1117. return !empty($tpl) ? $this->modx->controller->fetchTemplate($tpl) : $output;
  1118. }
  1119. /**
  1120. * Set a placeholder to be used in the template
  1121. * @param string $k
  1122. * @param mixed $v
  1123. */
  1124. public function setPlaceholder($k,$v) {
  1125. $this->modx->controller->setPlaceholder($k,$v);
  1126. }
  1127. /**
  1128. * Return the template path to load
  1129. * @return string
  1130. */
  1131. public function getTemplate() {
  1132. return '';
  1133. }
  1134. /**
  1135. * Return the input options parsed for the TV
  1136. * @return mixed
  1137. */
  1138. public function getInputOptions() {
  1139. return $this->tv->parseInputOptions($this->tv->processBindings($this->tv->get('elements'),$this->modx->resource->get('id')));
  1140. }
  1141. }
  1142. /**
  1143. * Backwards support for <2.2-style output renders
  1144. * @package modx
  1145. */
  1146. class modTemplateVarOutputRenderDeprecated extends modTemplateVarOutputRender {
  1147. /** @var modX $xpdo */
  1148. public $xpdo;
  1149. public function process($value,array $params = array()) {
  1150. $output = '';
  1151. $modx =& $this->modx;
  1152. $this->xpdo =& $this->modx;
  1153. /* simulate hydration */
  1154. $tvArray = $this->tv->toArray();
  1155. foreach ($tvArray as $k => $v) {
  1156. $this->$k = $v;
  1157. }
  1158. $name= $this->tv->get('name');
  1159. $id= "tv$name";
  1160. $format= $this->tv->get('display');
  1161. $tvtype= $this->tv->get('type');
  1162. if (empty($type)) $type = 'default';
  1163. if (!empty($params['modx.renderFile']) && file_exists($params['modx.renderFile'])) {
  1164. $output = include $params['modx.renderFile'];
  1165. }
  1166. return $output;
  1167. }
  1168. public function get($k) {
  1169. return $this->tv->get($k);
  1170. }
  1171. public function set($k,$v) {
  1172. return $this->tv->set($k,$v);
  1173. }
  1174. }
  1175. /**
  1176. * Backwards support for <2.2-style input renders
  1177. * @package modx
  1178. */
  1179. class modTemplateVarInputRenderDeprecated extends modTemplateVarInputRender {
  1180. /** @var modX $xpdo */
  1181. public $xpdo;
  1182. public function process($value,array $params = array()) {
  1183. $this->setPlaceholder('tv',$this->tv);
  1184. $this->setPlaceholder('id',$this->tv->get('id'));
  1185. $this->setPlaceholder('ctx',isset($_REQUEST['ctx']) ? $_REQUEST['ctx'] : 'web');
  1186. $this->setPlaceholder('params',$params);
  1187. $modx =& $this->modx;
  1188. $this->xpdo =& $this->modx;
  1189. /* simulate hydration */
  1190. $tvArray = $this->tv->toArray();
  1191. foreach ($tvArray as $k => $v) {
  1192. $this->$k = $v;
  1193. }
  1194. $name= $this->tv->get('name');
  1195. $id= "tv$name";
  1196. $format= $this->tv->get('display');
  1197. $tvtype= $this->tv->get('type');
  1198. if (empty($type)) $type = 'default';
  1199. $output = '';
  1200. if (!empty($params['modx.renderFile']) && file_exists($params['modx.renderFile'])) {
  1201. $output = include $params['modx.renderFile'];
  1202. }
  1203. return $output;
  1204. }
  1205. public function get($k) {
  1206. return $this->tv->get($k);
  1207. }
  1208. public function set($k,$v) {
  1209. return $this->tv->set($k,$v);
  1210. }
  1211. }