PageRenderTime 51ms CodeModel.GetById 13ms RepoModel.GetById 1ms app.codeStats 0ms

/e107_handlers/shortcode_handler.php

https://github.com/CasperGemini/e107
PHP | 1455 lines | 851 code | 167 blank | 437 comment | 150 complexity | 75b87489c1771f8aeb08bae9384102ca MD5 | raw file
Possible License(s): GPL-2.0
  1. <?php
  2. /*
  3. * e107 website system
  4. *
  5. * Copyright (C) 2008-2010 e107 Inc (e107.org)
  6. * Released under the terms and conditions of the
  7. * GNU General Public License (http://www.gnu.org/licenses/gpl.txt)
  8. *
  9. * e107 Shortcode handler
  10. *
  11. * $URL$
  12. * $Id$
  13. */
  14. if (!defined('e107_INIT'))
  15. {
  16. exit;
  17. }
  18. /**
  19. *
  20. * @package e107
  21. * @subpackage e107_handlers
  22. * @version $Id$
  23. * @author e107inc
  24. *
  25. * e_parse_shortcode - shortcode parser/manager, former e_shortcode
  26. * e_shortcode - abstract batch class
  27. */
  28. /**
  29. * FIXME: to be removed
  30. */
  31. function register_shortcode($classFunc, $codes, $path = '', $force = false)
  32. {
  33. return e107::getScParser()->registerShortcode($classFunc, $codes, $path, $force);
  34. }
  35. /**
  36. * FIXME: to be removed
  37. */
  38. function setScVar($className, $scVarName, $value)
  39. {
  40. return e107::getScParser()->setScVar($className, $scVarName, $value);
  41. }
  42. /**
  43. * FIXME: to be removed (once event calendar changed)
  44. */
  45. function callScFunc($className, $scFuncName, $param = '')
  46. {
  47. return e107::getScParser()->callScFunc($className, $scFuncName, $param);
  48. }
  49. /**
  50. * FIXME: to be removed
  51. */
  52. function initShortcodeClass($class, $force = false, $eVars = null)
  53. {
  54. return e107::getScParser()->initShortcodeClass($class, $eVars, $force);
  55. }
  56. class e_parse_shortcode
  57. {
  58. protected $scList = array(); // The actual code - added by parsing files or when plugin codes encountered. Array key is the shortcode name.
  59. protected $parseSCFiles; // True if individual shortcode files are to be used
  60. protected $addedCodes = NULL; // Pointer to a class or array to be used on a single call
  61. protected $registered_codes = array(); // Shortcodes added by plugins TODO make it private
  62. protected $scClasses = array(); // Batch shortcode classes - TODO make it private
  63. protected $scOverride = array(); // Array of codes found in override/shortcodes dir
  64. protected $scBatchOverride = array(); // Array of codes found in override/shortcodes/batch dir
  65. /**
  66. * @var e_vars
  67. */
  68. protected $eVars = null;
  69. /**
  70. * Wrappers array for the current parsing cycle, see contact_template.php and $CONTACT_WRAPPER variable
  71. * @var array
  72. */
  73. protected $wrappers = array();
  74. /**
  75. * Former $sc_style global variable. Internally used - performance reasons
  76. * @var array
  77. */
  78. protected $sc_style = array();
  79. function __construct()
  80. {
  81. $this->parseSCFiles = true; // Default probably never used, but make sure its defined.
  82. $this->loadOverrideShortcodes();
  83. $this->loadThemeShortcodes();
  84. $this->loadPluginShortcodes();
  85. $this->loadPluginSCFiles();
  86. //$this->loadCoreShortcodes(); DEPRECATED
  87. }
  88. /**
  89. * Register shortcode
  90. * $classFunc could be function name, class name or object
  91. * $code could be 'true' when class name/object is passed to automate the
  92. * registration of shortcode methods
  93. *
  94. * @param mixed $classFunc
  95. * @param mixed $codes
  96. * @param string $path
  97. * @param boolean $force override
  98. * @return e_parse_shortcode
  99. */
  100. function registerShortcode($classFunc, $codes, $path = '', $force = false)
  101. {
  102. //If codes is set to true, let's go get a list of shortcode methods
  103. if ($codes === true)
  104. {
  105. $codes = array();
  106. $tmp = get_class_methods($classFunc);
  107. foreach ($tmp as $c)
  108. {
  109. if (strpos($c, 'sc_') === 0)
  110. {
  111. $codes[] = substr($c, 3);
  112. }
  113. }
  114. unset($tmp);
  115. }
  116. //Register object feature
  117. $classObj = null;
  118. if (is_object($classFunc))
  119. {
  120. $classObj = $classFunc;
  121. $classFunc = get_class($classObj);
  122. }
  123. //We only register these shortcodes if they have not already been registered in some manner
  124. //ie theme or other plugin .sc files
  125. if (is_array($codes))
  126. {
  127. foreach ($codes as $code)
  128. {
  129. $code = strtoupper($code);
  130. if ((!$this->isRegistered($code) || $force == true) && !$this->isOverride($code))
  131. {
  132. $this->registered_codes[$code] = array('type' => 'class', 'path' => $path, 'class' => $classFunc);
  133. }
  134. }
  135. //register object if required
  136. if (null !== $classObj && (!$this->isScClass($classFunc) || $force == true))
  137. {
  138. $this->scClasses[$classFunc] = $classObj;
  139. }
  140. }
  141. else
  142. {
  143. $codes = strtoupper($codes);
  144. if ((!$this->isRegistered($code) || $force == true) && !$this->isOverride($code))
  145. {
  146. $this->registered_codes[$codes] = array('type' => 'func', 'path' => $path, 'function' => $classFunc);
  147. }
  148. }
  149. return $this;
  150. }
  151. /**
  152. * Add value to already registered SC object
  153. *
  154. * @param string $className
  155. * @param string $scVarName
  156. * @param mixed $value
  157. * @return e_parse_shortcode
  158. */
  159. public function setScVar($className, $scVarName, $value)
  160. {
  161. if ($this->isScClass($className))
  162. {
  163. // new way - batch should extend e_shortcode class
  164. if (method_exists($this->scClasses[$className], 'setScVar'))
  165. {
  166. $this->scClasses[$className]->setScVar($scVarName, $value);
  167. }
  168. else // Old - DEPRECATED
  169. {
  170. $this->scClasses[$className]->$scVarName = $value;
  171. }
  172. }
  173. return $this;
  174. }
  175. /**
  176. * Call function on an already registered SC object
  177. *
  178. * @param string $className
  179. * @param string $scFuncName
  180. * @param mixed $param - passed to function
  181. *
  182. * @return mixed|boolean - NULL if class/method doesn't exist; otherwise whatever the function returns.
  183. */
  184. public function callScFunc($className, $scFuncName, $param = '')
  185. {
  186. if ($this->isScClass($className))
  187. {
  188. return method_exists($this->scClasses[$className], $scFuncName) ? call_user_func(array($this->scClasses[$className], $scFuncName), $param) : null;
  189. }
  190. return null;
  191. }
  192. /**
  193. * same as e_parse_shortcode::callScFunc(), but passes the last argument (array)
  194. * to the called method as multiple arguments
  195. *
  196. * @param string $className
  197. * @param string $scFuncName
  198. * @param array $param - arguments passed to function
  199. *
  200. * @return mixed|boolean - NULL if class/method doesn't exist; otherwise whatever the function returns.
  201. */
  202. protected function callScFuncA($className, $scFuncName, $args = array())
  203. {
  204. if ($this->isScClass($className))
  205. {
  206. // avoid warnings
  207. return method_exists($this->scClasses[$className], $scFuncName) ? call_user_func_array(array($this->scClasses[$className], $scFuncName), $args) : null;
  208. }
  209. return null;
  210. }
  211. /**
  212. * Create shortcode object - don't forget you still can use e_shortcode.php
  213. *
  214. * @param string $class
  215. * @param boolean $force
  216. * @return e_shortcode
  217. */
  218. public function initShortcodeClass($class, $force = false)
  219. {
  220. if (class_exists($class, false) && ($force || !$this->isScClass($class)))
  221. {
  222. $this->scClasses[$class] = new $class();
  223. if(method_exists($this->scClasses[$class], 'init'))
  224. {
  225. $this->scClasses[$class]->init();
  226. }
  227. return $this->scClasses[$class];
  228. }
  229. return null;
  230. }
  231. /**
  232. * Get registered SC object
  233. * Normally you would use the proxy of this method - e107::getScBatch()
  234. * Global File Override ClassName/Path examples:
  235. * 1. Core signup shortcodes
  236. * - Origin ClassName: signup_shortcodes
  237. * - Origin Location: core/shortcodes/batch/signup_shortcodes.php
  238. * - File Override ClassName: override_signup_shortcodes
  239. * - File Override Location: core/override/shortcodes/batch/signup_shortcodes.php
  240. *
  241. * 2. Plugin 'gallery' global shortcode batch (e_shortcode.php)
  242. * - Origin ClassName: gallery_shortcodes //FIXME Should be gallery_shortcode? (more below)
  243. * - Origin Location: plugins/gallery/e_shortcode.php
  244. * - File Override ClassName: override_gallery_shortcodes
  245. * - File Override Location: core/override/shortcodes/batch/gallery_shortcodes.php
  246. *
  247. * 3. Plugin 'forum' regular shortcode batch
  248. * - Origin ClassName: plugin_forum_view_shortcodes //FIXME Should be forum_shortcodes? (more below)
  249. * - Origin Location: plugins/forum/shortcodes/batch/view_shortcodes.php
  250. * - File Override ClassName: override_plugin_forum_view_shortcodes
  251. * - File Override Location: core/override/shortcodes/batch/forum_view_shortcodes.php
  252. *
  253. * <code><?php
  254. * // simple use
  255. * e107::getScParser()->getScObject('news_shortcodes'); // For Globally Registered shortcodes, including plugins using e_shortcode.php
  256. *
  257. * // plugin override - e107_plugins/myplug/shortcodes/batch/news_shortcodes.php -> class plugin_myplug_news_shortcodes
  258. * e107::getScParser()->getScObject('news_shortcodes', 'myplug', true);
  259. *
  260. * // more complex plugin override
  261. * // e107_plugins/myplug/shortcodes/batch/news2_shortcodes.php -> class plugin_myplug_news2_shortcodes
  262. * e107::getScParser()->getScObject('news_shortcodes', 'myplug', 'news2_shortcodes');
  263. * </code>
  264. * @param string $className
  265. * @param string $plugName if true className is used., if string, string value is used.
  266. * @param string $overrideClass if true, $className is used
  267. * @return e_shortcode
  268. */
  269. public function getScObject($className, $pluginName = null, $overrideClass = null)
  270. {
  271. /* FIXME Discuss Generic plugin Class naming. (excluding specific calls with $overrideClass.
  272. // Defaults should be:
  273. e_shortcode.php = {plugin}_shortcode
  274. {plugin}_shortcodes.php = {plugin}_shortcodes
  275. */
  276. if(trim($className)==""){ return; }
  277. $_class_fname = $className;
  278. if($pluginName === TRUE) //XXX When called manually by a plugin, not e_shortcode.php eg. $sc = e107::getScBatch('faqs',TRUE); for faqs_shortcode.php with class faqs_shortcode
  279. {
  280. $pluginName = str_replace("_shortcodes","",$className);
  281. $manualCall = true;
  282. }
  283. elseif(is_string($pluginName))
  284. {
  285. // FIXME "plugin_ " should NOT be used or be necessary.
  286. // FIXME Core classes should use special naming to avoid comflicts, not plugins.
  287. $className = 'plugin_'.$pluginName.'_'.str_replace('/', '_', $className);
  288. }
  289. $globalOverride = $this->isBatchOverride(str_replace('plugin_', '', $className));
  290. // forced override
  291. if($overrideClass)
  292. {
  293. if(true === $overrideClass)
  294. {
  295. $overrideClass = $className;
  296. }
  297. // e.g. class plugin_myplug_news_shortcodes
  298. if($pluginName != null)
  299. {
  300. $_class_fname = $overrideClass;
  301. $className = 'plugin_'.$pluginName.'_'.str_replace('/', '_', $overrideClass);
  302. }
  303. else
  304. {
  305. $className = $overrideClass;
  306. }
  307. }
  308. if($className == '_theme__shortcodes') // Check for theme shortcode batch. - @see header_default.php //XXX Discuss.
  309. {
  310. $className = 'theme_shortcodes';
  311. $path = THEME.'theme_shortcodes.php';
  312. }
  313. elseif(!$pluginName)
  314. {
  315. if(!$globalOverride)
  316. {
  317. $path = e_CORE.'shortcodes/batch/'.$_class_fname.'.php';
  318. }
  319. else
  320. {
  321. $path = e_CORE.'override/shortcodes/batch/'.$_class_fname.'.php';
  322. $className = 'override_'.$className;
  323. }
  324. }
  325. else
  326. {
  327. if(!$globalOverride)
  328. {
  329. // do nothing if it's e_shortcode batch global
  330. if($pluginName.'_shortcodes' !== $className || $manualCall == true) // manual call by plugin, not e_shortcode.php
  331. {
  332. // BC - required.
  333. $pathBC = e_PLUGIN.$pluginName.'/';
  334. $path = (is_readable($pathBC.$_class_fname.'.php') ? $pathBC : e_PLUGIN.$pluginName.'/shortcodes/batch/').$_class_fname.'.php';
  335. }
  336. }
  337. else
  338. {
  339. $path = e_CORE.'override/shortcodes/batch/'.$pluginName.'_'.$_class_fname.'.php';
  340. $className = 'override_'.$className;
  341. }
  342. }
  343. // Includes global Shortcode Classes (e_shortcode.php) or already loaded batch
  344. if ($this->isScClass($className))
  345. {
  346. return $this->scClasses[$className];
  347. }
  348. // If it already exists - don't include it again.
  349. if (class_exists($className, false)) // don't allow __autoload()
  350. {
  351. // $this->registerClassMethods($className, $path); // XXX Global registration should happen separately - here we want only the object.
  352. $this->scClasses[$className] = new $className();
  353. return $this->scClasses[$className];
  354. }
  355. if (is_readable($path))
  356. {
  357. require_once($path);
  358. if (class_exists($className, false)) // don't allow __autoload()
  359. {
  360. // register instance directly to allow override
  361. $this->scClasses[$className] = new $className();
  362. // $this->registerClassMethods($className, $path); // XXX Global registration should happen separately - here we want only the object.
  363. return $this->scClasses[$className];
  364. }
  365. elseif(E107_DBG_BBSC || E107_DBG_SC)
  366. {
  367. echo "<h3>Couldn't Find Class '".$className."' in <b>".$path."</b></h3>";
  368. }
  369. }
  370. elseif(E107_DBG_BBSC || E107_DBG_SC)
  371. {
  372. echo "<h3>Couldn't Load: <b>".$path." with class-name: {$className} and pluginName {$pluginName}</b></h3>";
  373. }
  374. // TODO - throw exception?
  375. return null;
  376. }
  377. /**
  378. * Register any shortcode from the override/shortcodes/ directory
  379. *
  380. * @return e_parse_shortcode
  381. */
  382. protected function loadOverrideShortcodes()
  383. {
  384. if (e107::getPref('sc_override'))
  385. {
  386. $tmp = explode(',', e107::getPref('sc_override'));
  387. foreach ($tmp as $code)
  388. {
  389. $code = strtoupper(trim($code));
  390. $this->registered_codes[$code]['type'] = 'override';
  391. $this->registered_codes[$code]['path'] = e_CORE.'override/shortcodes/single/';
  392. $this->registered_codes[$code]['function'] = 'override_'.strtolower($code).'_shortcode';
  393. $this->scOverride[] = $code;
  394. }
  395. }
  396. if (e107::getPref('sc_batch_override'))
  397. {
  398. $tmp = explode(',', e107::getPref('sc_batch_override'));
  399. foreach ($tmp as $code)
  400. {
  401. //$code = strtoupper(trim($code));
  402. //$this->registered_codes[$code]['type'] = 'override';
  403. $this->scBatchOverride[] = $code;
  404. }
  405. }
  406. return $this;
  407. }
  408. /**
  409. * Register any shortcodes that were registered by the theme
  410. * $register_sc[] = 'MY_THEME_CODE'
  411. *
  412. * @return e_parse_shortcode
  413. */
  414. protected function loadThemeShortcodes()
  415. {
  416. global $register_sc;
  417. // $this->registered_codes[$code]['type'] = 'plugin';
  418. // $this->registered_codes[$code]['function'] = strtolower($code).'_shortcode';
  419. // $this->registered_codes[$code]['path'] = e_PLUGIN.$path.'/shortcodes/single/';
  420. // $this->registered_codes[$code]['perms'] = $uclass;
  421. if (isset($register_sc) && is_array($register_sc))
  422. {
  423. foreach ($register_sc as $code)
  424. {
  425. if (!$this->isRegistered($code))
  426. {
  427. $code = strtoupper($code);
  428. $this->registered_codes[$code]['type'] = 'theme';
  429. }
  430. }
  431. }
  432. return $this;
  433. }
  434. /**
  435. * Register all .sc files found in plugin directories (via pref)
  436. *
  437. * @return e_parse_shortcode
  438. */
  439. protected function loadPluginSCFiles()
  440. {
  441. $pref = e107::getPref('shortcode_list');
  442. $prefl = e107::getPref('shortcode_legacy_list');
  443. // new shortcodes - functions, shortcode/single/*.php
  444. if ($pref)
  445. {
  446. foreach ($pref as $path => $namearray)
  447. {
  448. foreach ($namearray as $code => $uclass)
  449. {
  450. $code = strtoupper($code);
  451. if (!$this->isRegistered($code))
  452. {
  453. if($this->isOverride($code))
  454. {
  455. $this->registered_codes[$code]['type'] = 'override';
  456. $this->registered_codes[$code]['function'] = 'override_'.strtolower($code).'_shortcode';
  457. $this->registered_codes[$code]['path'] = e_CORE.'override/shortcodes/single/';
  458. $this->registered_codes[$code]['perms'] = $uclass;
  459. continue;
  460. }
  461. $this->registered_codes[$code]['type'] = 'plugin';
  462. $this->registered_codes[$code]['function'] = strtolower($code).'_shortcode';
  463. $this->registered_codes[$code]['path'] = e_PLUGIN.$path.'/shortcodes/single/';
  464. $this->registered_codes[$code]['perms'] = $uclass;
  465. }
  466. }
  467. }
  468. }
  469. // legacy .sc - plugin root
  470. if ($prefl)
  471. {
  472. foreach ($prefl as $path => $namearray)
  473. {
  474. foreach ($namearray as $code => $uclass)
  475. {
  476. // XXX old? investigate
  477. if ($code == 'shortcode_config')
  478. {
  479. include_once(e_PLUGIN.$path.'/shortcode_config.php');
  480. }
  481. else
  482. {
  483. $code = strtoupper($code);
  484. if (!$this->isRegistered($code))
  485. {
  486. $this->registered_codes[$code]['type'] = 'plugin_legacy';
  487. $this->registered_codes[$code]['path'] = $path;
  488. $this->registered_codes[$code]['perms'] = $uclass;
  489. }
  490. }
  491. }
  492. }
  493. }
  494. return $this;
  495. }
  496. /**
  497. * Register Plugin Shortcode Batch files (e_shortcode.php) for use site-wide.
  498. * Equivalent to multiple .sc files in the plugin's folder.
  499. *
  500. * @return e_parse_shortcode
  501. */
  502. protected function loadPluginShortcodes()
  503. {
  504. $pref = e107::getPref('e_shortcode_list');
  505. if (!$pref)
  506. {
  507. return $this;
  508. }
  509. foreach ($pref as $key => $val)
  510. {
  511. $globalOverride = $this->isBatchOverride($key.'_shortcodes');
  512. if($globalOverride)
  513. {
  514. $path = e_CORE.'override/shortcodes/batch/'.$key.'_shortcodes.php';
  515. $classFunc = 'override_'.$key.'_shortcodes';
  516. }
  517. else
  518. {
  519. $path = e_PLUGIN.$key.'/e_shortcode.php';
  520. $classFunc = $key.'_shortcodes';
  521. }
  522. if (!include_once($path))
  523. {
  524. // try to switch back to the batch origin in case it's an override
  525. if($globalOverride)
  526. {
  527. $path = e_PLUGIN.$key.'/e_shortcode.php';
  528. $classFunc = $key.'_shortcodes';
  529. if (!include_once($path))
  530. {
  531. continue;
  532. }
  533. }
  534. else continue;
  535. }
  536. $this->registerClassMethods($classFunc, $path, false);
  537. }
  538. return $this;
  539. }
  540. /**
  541. * Common Auto-Register function for class methods.
  542. * @return e_parse_shortcode
  543. */
  544. protected function registerClassMethods($class, $path, $force = false)
  545. {
  546. $tmp = get_class_methods($class);
  547. $className = is_object($class) ? get_class($class) : $class;
  548. foreach ($tmp as $c)
  549. {
  550. if (strpos($c, 'sc_') === 0)
  551. {
  552. $sc_func = substr($c, 3);
  553. $code = strtoupper($sc_func);
  554. if ($force || !$this->isRegistered($code))
  555. {
  556. $this->registered_codes[$code] = array('type' => 'class', 'path' => $path, 'class' => $className);
  557. $this->initShortcodeClass($className);
  558. // if (class_exists($className, false))
  559. // {
  560. // $this->scClasses[$className] = new $className(); // Required. Test with e107::getScBatch($className)
  561. // echo "CLASS=:".$className;
  562. // }
  563. }
  564. }
  565. }
  566. return $this;
  567. }
  568. /**
  569. * DEPRECATED admin_shortcodes now loaded inside admin parse function (see boot.php)
  570. * Register Core Shortcode Batches.
  571. * FIXME - make it smarter - currently loaded all the time (even on front-end)
  572. *
  573. * @return e_parse_shortcode
  574. */
  575. // function loadCoreShortcodes()
  576. // {
  577. // $coreBatchList = array('admin_shortcodes');
  578. //
  579. // foreach ($coreBatchList as $cb)
  580. // {
  581. // $path = e_CORE.'shortcodes/batch/'.$cb.".php";
  582. // if (include_once($path))
  583. // {
  584. // $this->registerClassMethods($cb, $path);
  585. // }
  586. // }
  587. // return $this;
  588. // }
  589. function isRegistered($code)
  590. {
  591. return array_key_exists($code, $this->registered_codes);
  592. }
  593. public function resetScClass($className, $object)
  594. {
  595. if(null === $object)
  596. {
  597. unset($this->scClasses[$className]);
  598. }
  599. elseif ($this->isScClass($className))
  600. {
  601. $this->scClasses[$className] = $object;
  602. }
  603. return $this;
  604. }
  605. function isScClass($className)
  606. {
  607. return isset($this->scClasses[$className]);
  608. }
  609. function isOverride($code)
  610. {
  611. return in_array($code, $this->scOverride);
  612. }
  613. function isBatchOverride($name)
  614. {
  615. return in_array($name, $this->scBatchOverride);
  616. }
  617. /**
  618. * Parse the shortcodes in some text
  619. *
  620. * @param string $text - the text containing the shortcodes
  621. * @param boolean $useSCFiles - if TRUE, all currently registered shortcodes can be used.
  622. * - if FALSE, only those passed are used.
  623. * @param array|object|null $extraCodes - if passed, defines additional shortcodes:
  624. * - if an object or an array, the shortcodes defined by the class of the object are available for this parsing only.
  625. * @param array|null $eVars - if defined, details values to be substituted for shortcodes. Array key (lower case) is shortcode name (upper case)
  626. *
  627. * @return string with shortcodes substituted
  628. */
  629. function parseCodes($text, $useSCFiles = true, $extraCodes = null, $eVars = null)
  630. {
  631. global $sc_style; //legacy, will be removed soon, use the non-global $SC_STYLE instead
  632. $saveParseSCFiles = $this->parseSCFiles; // In case of nested call
  633. $this->parseSCFiles = $useSCFiles;
  634. $saveVars = $this->eVars; // In case of nested call
  635. $saveCodes = $this->addedCodes;
  636. $this->eVars = $eVars;
  637. $this->addedCodes = NULL;
  638. // former $sc_style - do it once here and not on every doCode loop - performance
  639. $this->sc_style = e107::scStyle(); //FIXME - BC Problems and conflicts. - XXX Commenting this out will fix #3 below.
  640. /* --------- BUG TEST Scenario --------------
  641. * Front-end Theme: Bootstrap
  642. * MENU-1 contains '{NAVIGATION=side}' on top and chatbox_menu below
  643. * URL to use: /usersettings.php - 'Signature' input should be enabled.
  644. * Expected Result:
  645. * 1) {NAVIGATION=side} wrapped with $SC_WRAPPER ie. enclosed in box.
  646. * 2) Internal styling of chatbox_class not to be damaged by what happens globally ie. the text 'Display name' should not appear in the chatbox
  647. * 3) Usersettings Signature box to appear wrapped in BC $sc_style pre/post - ie. should appear at the bottom of the html table.(not at the top)
  648. * 4) Existing Chatbox Styling (v1.x) not broken (ie. test with v1 theme).
  649. * - All of the above to occur without changes to usersetting_template.php - since its logic is that of v1.x templates.
  650. *
  651. * Things that may help:
  652. * Modify e107::getScBatch() so that it never registers shortcodes globally; ie. no overriding of existing shortcodes with it, as it is a replacement for non-global shortcode declaration in v1
  653. * ONLY globally register shortcodes when they are declared in e_shortcode.php - this is consistent with the logic of e_xxxx which affect e107 Outside of the plugin/sript. (gallery plugin follows this logic)
  654. *
  655. */
  656. if(isset($sc_style) && is_array($sc_style))
  657. {
  658. $this->sc_style = array_merge($sc_style, $this->sc_style); // XXX Commenting this out will fix #2 above.
  659. }
  660. //object support
  661. if (is_object($extraCodes))
  662. {
  663. $this->addedCodes = &$extraCodes;
  664. // TEMPLATEID_WRAPPER support - see contact template
  665. // must be registered in e_shortcode object (batch) via wrapper() method before parsing
  666. // Do it only once per parsing cylcle and not on every doCode() loop - performance
  667. if(method_exists($this->addedCodes, 'wrapper'))
  668. {
  669. $this->wrappers = e107::templateWrapper($this->addedCodes->wrapper());
  670. }
  671. /*
  672. $classname = get_class($extraCodes);
  673. //register once
  674. if (!$this->isScClass($classname))
  675. {
  676. $this->registerShortcode($extraCodes, true); // Register class if not already registered
  677. }
  678. //always overwrite object
  679. $this->scClasses[$classname] = $extraCodes;
  680. */
  681. // auto-register eVars if possible - call it manually?
  682. // $this->callScFunc($classname, 'setParserVars', $this->eVars);
  683. }
  684. elseif (is_array($extraCodes))
  685. {
  686. $this->addedCodes = &$extraCodes;
  687. /*
  688. foreach ($extraCodes as $sc => $code)
  689. {
  690. $this->scList[$sc] = $code;
  691. }
  692. */
  693. }
  694. $ret = preg_replace_callback('#\{(\S[^\x02]*?\S)\}#', array(&$this, 'doCode'), $text);
  695. $this->parseSCFiles = $saveParseSCFiles; // Restore previous value
  696. $this->addedCodes = $saveCodes;
  697. $this->eVars = $saveVars; // restore eVars
  698. $this->debug_legacy = null;
  699. // $this->sc_style = array(); //XXX Adding this will also fix #2 above.
  700. return $ret;
  701. }
  702. /**
  703. * Callback looks up and substitutes a shortcode
  704. */
  705. function doCode($matches)
  706. {
  707. // XXX remove all globals, $sc_style removed
  708. global $pref, $e107cache, $menu_pref, $parm, $sql;
  709. $parmArray = false;
  710. if ($this->eVars)
  711. {
  712. if ($this->eVars->isVar($matches[1]))
  713. {
  714. return $this->eVars->$matches[1];
  715. }
  716. }
  717. if (strpos($matches[1], E_NL) !== false)
  718. {
  719. return $matches[0];
  720. }
  721. if(preg_match('/^([A-Z_]*):(.*)/', $matches[1], $newMatch))
  722. {
  723. $code = $newMatch[1];
  724. $parmStr = trim($newMatch[2]);
  725. $debugParm = $parmStr;
  726. parse_str($parmStr,$parm);
  727. $parmArray = true;
  728. }
  729. elseif (strpos($matches[1], '='))
  730. {
  731. list($code, $parm) = explode('=', $matches[1], 2);
  732. }
  733. else
  734. {
  735. $code = $matches[1];
  736. $parm = '';
  737. }
  738. //look for the $sc_mode
  739. if (strpos($code, '|'))
  740. {
  741. list($code, $sc_mode) = explode("|", $code, 2);
  742. $code = trim($code);
  743. $sc_mode = trim($sc_mode);
  744. }
  745. else
  746. {
  747. $sc_mode = '';
  748. }
  749. if($parmArray == false)
  750. {
  751. $parm = trim($parm);
  752. $parm = str_replace(array('[[', ']]'), array('{', '}'), $parm);
  753. }
  754. if (E107_DBG_BBSC || E107_DBG_SC || E107_DBG_TIMEDETAILS)
  755. {
  756. $sql->db_Mark_Time("SC $code");
  757. }
  758. if (E107_DBG_SC)
  759. {
  760. $dbg = "<strong>";
  761. $dbg .= '{';
  762. $dbg .= $code;
  763. $dbg .=($parm) ? '='.htmlentities($parm) : "";
  764. $dbg .= '}';
  765. $dbg .= "</strong>";
  766. // echo $dbg;
  767. return $dbg;
  768. // trigger_error('starting shortcode {'.$code.'}', E_USER_ERROR); // no longer useful - use ?[debug=bbsc]
  769. }
  770. $scCode = '';
  771. $scFile = '';
  772. $ret = '';
  773. $_method = 'sc_'.strtolower($code);
  774. if (is_object($this->addedCodes) && method_exists($this->addedCodes, $_method)) //It is class-based batch shortcode. Class already loaded; call the method
  775. {
  776. $ret = $this->addedCodes->$_method($parm, $sc_mode);
  777. if(E107_DBG_BBSC || E107_DBG_SC || E107_DBG_TIMEDETAILS)
  778. {
  779. $_class = get_class($this->addedCodes); // "(class loaded)"; // debug.
  780. $_function = $_method;
  781. $_path = "(already loaded)";
  782. }
  783. }
  784. elseif (is_array($this->addedCodes) && array_key_exists($code, $this->addedCodes)) // Its array-based shortcode. Load the code for evaluation later.
  785. {
  786. $scCode = $this->addedCodes[$code];
  787. // $_path = print_a($this->backTrace,true);
  788. //XXX $_path = print_a($this,true);
  789. }
  790. elseif (array_key_exists($code, $this->scList)) // Check to see if we've already loaded the .sc file contents
  791. {
  792. $scCode = $this->scList[$code];
  793. $_path = "(loaded earlier)"; // debug.
  794. }
  795. else
  796. {
  797. //.sc file not yet loaded, or shortcode is new function type
  798. if ($this->parseSCFiles == true)
  799. {
  800. if (array_key_exists($code, $this->registered_codes))
  801. {
  802. //shortcode is registered, let's proceed.
  803. if (isset($this->registered_codes[$code]['perms']))
  804. {
  805. if (!check_class($this->registered_codes[$code]['perms']))
  806. {
  807. return '';
  808. }
  809. }
  810. switch ($this->registered_codes[$code]['type'])
  811. {
  812. case 'class':
  813. //It is batch shortcode. Load the class and call the method
  814. $_class = $this->registered_codes[$code]['class'];
  815. $_method = 'sc_'.strtolower($code);
  816. if (!$this->isScClass($_class))
  817. {
  818. if (!class_exists($_class) && $this->registered_codes[$code]['path'])
  819. {
  820. include_once($this->registered_codes[$code]['path']);
  821. }
  822. $this->initShortcodeClass($_class, false);
  823. if(!$this->isScClass($_class))
  824. {
  825. return '';
  826. }
  827. // egister passed eVars object on init - call it manually?
  828. // $this->callScFunc($_class, 'setVars', $this->var);
  829. }
  830. // FIXME - register passed eVars object - BAD solution - called on EVERY sc method call
  831. // XXX - removal candidate - I really think it should be done manually (outside the parser)
  832. // via e107::getScBatch(name)->setParserVars($eVars);
  833. // $this->callScFunc($_class, 'setParserVars', $this->eVars);
  834. $wrapper = $this->callScFunc($_class, 'wrapper', null);
  835. $ret = $this->callScFuncA($_class, $_method, array($parm, $sc_mode));
  836. /*if (method_exists($this->scClasses[$_class], $_method))
  837. {
  838. $ret = $this->scClasses[$_class]->$_method($parm, $sc_mode);
  839. }
  840. else
  841. {
  842. echo $_class.'::'.$_method.' NOT FOUND!<br />';
  843. }*/
  844. break;
  845. case 'override':
  846. case 'func':
  847. case 'plugin':
  848. //It is a function, so include the file and call the function
  849. $_function = $this->registered_codes[$code]['function'];
  850. if (!function_exists($_function) && $this->registered_codes[$code]['path'])
  851. {
  852. include_once($this->registered_codes[$code]['path'].strtolower($code).'.php');
  853. }
  854. if (function_exists($_function))
  855. {
  856. $ret = call_user_func($_function, $parm, $sc_mode);
  857. }
  858. break;
  859. case 'plugin_legacy':
  860. $scFile = e_PLUGIN.strtolower($this->registered_codes[$code]['path']).'/'.strtolower($code).'.sc';
  861. break;
  862. // case 'override':
  863. // $scFile = e_CORE.'override/shortcodes/'.strtolower($code).'.sc';
  864. // break;
  865. case 'theme':
  866. $scFile = THEME.strtolower($code).'.sc';
  867. break;
  868. }
  869. }
  870. else
  871. {
  872. // Code is not registered, let's look for .sc or .php file
  873. // .php file takes precedence over .sc file
  874. if (is_readable(e_CORE.'shortcodes/single/'.strtolower($code).'.php'))
  875. {
  876. $_function = strtolower($code).'_shortcode';
  877. $_class = strtolower($code);
  878. $_path = e_CORE.'shortcodes/single/'.strtolower($code).'.php';
  879. include_once(e_CORE.'shortcodes/single/'.strtolower($code).'.php');
  880. if (class_exists($_class, false)) // prevent __autoload - performance
  881. {
  882. // SecretR - fix array(parm, sc_mode) causing parm to become an array, see issue 424
  883. $ret = call_user_func(array($_class, $_function), $parm, $sc_mode);
  884. }
  885. elseif (function_exists($_function))
  886. {
  887. $ret = call_user_func($_function, $parm, $sc_mode);
  888. }
  889. }
  890. else
  891. {
  892. $scFile = e_CORE.'shortcodes/single/'.strtolower($code).'.sc';
  893. $_path = $scFile;
  894. }
  895. }
  896. if ($scFile && file_exists($scFile))
  897. {
  898. $scCode = file_get_contents($scFile);
  899. $this->scList[$code] = $scCode;
  900. $_path = $scFile;
  901. }
  902. }
  903. if (!isset($scCode))
  904. {
  905. if (E107_DBG_BBSC)
  906. {
  907. trigger_error('shortcode not found:{'.$code.'}', E_USER_ERROR);
  908. }
  909. return $matches[0];
  910. }
  911. if (E107_DBG_SC && $scFile)
  912. {
  913. // echo (isset($scFile)) ? "<br />sc_file= ".str_replace(e_CORE.'shortcodes/single/', '', $scFile).'<br />' : '';
  914. // echo "<br />sc= <b>$code</b>";
  915. }
  916. }
  917. if ($scCode)
  918. {
  919. $ret = eval($scCode);
  920. }
  921. if (isset($ret) && ($ret != '' || is_numeric($ret)))
  922. {
  923. // Wrapper support - see contact_template.php
  924. if(isset($this->wrappers[$code]) && !empty($this->wrappers[$code]))
  925. {
  926. list($pre, $post) = explode("{---}", $this->wrappers[$code], 2);
  927. $ret = $pre.$ret.$post;
  928. }
  929. else
  930. {
  931. //if $sc_mode exists, we need it to parse $sc_style
  932. if ($sc_mode)
  933. {
  934. $code = $code.'|'.$sc_mode;
  935. }
  936. if (is_array($this->sc_style) && array_key_exists($code, $this->sc_style))
  937. {
  938. $pre = $post = '';
  939. // old way - pre/post keys
  940. if(is_array($this->sc_style[$code]))
  941. {
  942. if (isset($this->sc_style[$code]['pre']))
  943. {
  944. $pre = $this->sc_style[$code]['pre'];
  945. }
  946. if (isset($this->sc_style[$code]['post']))
  947. {
  948. $post = $this->sc_style[$code]['post'];
  949. }
  950. }
  951. // new way - same format as wrapper
  952. else
  953. {
  954. list($pre, $post) = explode("{---}", $this->sc_style[$code], 2);
  955. }
  956. $ret = $pre.$ret.$post;
  957. }
  958. }
  959. }
  960. if (E107_DBG_SC || E107_DBG_TIMEDETAILS)
  961. {
  962. $sql->db_Mark_Time("(After SC {$code})");
  963. }
  964. if (E107_DBG_BBSC || E107_DBG_SC || E107_DBG_TIMEDETAILS)
  965. {
  966. global $db_debug;
  967. $other = array();
  968. if($_class)
  969. {
  970. $other['class'] = $_class;
  971. }
  972. if(vartrue($_function))
  973. {
  974. $other['function'] = $_function;
  975. }
  976. if(vartrue($_path))
  977. {
  978. $other['path'] = str_replace('../','',$_path);
  979. }
  980. if($this->debug_legacy)
  981. {
  982. $other = $this->debug_legacy;
  983. }
  984. $info = (isset($this->registered_codes[$code])) ? print_a($this->registered_codes[$code],true) : print_a($other,true);
  985. $tmp = isset($debugParm) ? $debugParm : $parm;
  986. $db_debug->logCode(2, $code, $tmp, $info);
  987. }
  988. return isset($ret) ? $ret : '';
  989. }
  990. function parse_scbatch($fname, $type = 'file')
  991. {
  992. global $e107cache, $eArrayStorage;
  993. $cur_shortcodes = array();
  994. if ($type == 'file')
  995. {
  996. $batch_cachefile = 'nomd5_scbatch_'.md5($fname);
  997. // $cache_filename = $e107cache->cache_fname("nomd5_{$batchfile_md5}");
  998. $sc_cache = $e107cache->retrieve_sys($batch_cachefile);
  999. if (!$sc_cache)
  1000. {
  1001. $sc_batch = file($fname);
  1002. }
  1003. else
  1004. {
  1005. $cur_shortcodes = $eArrayStorage->ReadArray($sc_cache);
  1006. $sc_batch = "";
  1007. }
  1008. }
  1009. else
  1010. {
  1011. $sc_batch = $fname;
  1012. }
  1013. $this->debug_legacy = array('type'=>$type, 'path'=> str_replace(e_ROOT,"",$fname));
  1014. if ($sc_batch)
  1015. {
  1016. $cur_sc = '';
  1017. foreach ($sc_batch as $line)
  1018. {
  1019. if (trim($line) == 'SC_END')
  1020. {
  1021. $cur_sc = '';
  1022. }
  1023. if ($cur_sc)
  1024. {
  1025. $cur_shortcodes[$cur_sc] .= $line;
  1026. }
  1027. if (preg_match('#^SC_BEGIN (\w*).*#', $line, $matches))
  1028. {
  1029. $cur_sc = $matches[1];
  1030. $cur_shortcodes[$cur_sc] = varset($cur_shortcodes[$cur_sc], '');
  1031. }
  1032. }
  1033. if ($type == 'file')
  1034. {
  1035. $sc_cache = $eArrayStorage->WriteArray($cur_shortcodes, false);
  1036. $e107cache->set_sys($batch_cachefile, $sc_cache);
  1037. }
  1038. }
  1039. foreach (array_keys($cur_shortcodes) as $cur_sc)
  1040. {
  1041. if (array_key_exists($cur_sc, $this->registered_codes))
  1042. {
  1043. if ($this->registered_codes[$cur_sc]['type'] == 'plugin')
  1044. {
  1045. $scFile = e_PLUGIN.strtolower($this->registered_codes[$cur_sc]['path']).'/'.strtolower($cur_sc).'.sc';
  1046. }
  1047. else
  1048. {
  1049. $scFile = THEME.strtolower($cur_sc).'.sc';
  1050. }
  1051. if (is_readable($scFile))
  1052. {
  1053. $cur_shortcodes[$cur_sc] = file_get_contents($scFile);
  1054. }
  1055. }
  1056. }
  1057. return $cur_shortcodes;
  1058. }
  1059. }
  1060. class e_shortcode
  1061. {
  1062. /**
  1063. * Stores passed to shortcode handler array data
  1064. * Usage: $this->var['someKey']
  1065. * Assigned via setVars() and addVars() methods
  1066. * @var array
  1067. */
  1068. protected $var = array(); // value available to each shortcode.
  1069. protected $mode = 'view'; // or edit. Used within shortcodes for form elements vs values only.
  1070. protected $wrapper = null; // holds template/key value of the currently used wrapper (if any) - see contact_template.php for an example.
  1071. /**
  1072. * Storage for shortcode values
  1073. * @var e_vars
  1074. */
  1075. protected $scVars = null;
  1076. public function __construct()
  1077. {
  1078. $this->scVars = new e_vars();
  1079. }
  1080. /**
  1081. * Startup code for child class
  1082. */
  1083. public function init() {}
  1084. /**
  1085. * Sets wrapper id (to be retrieved from the registry while parsing)
  1086. * Example e107::getScBatch('contact')->wrapper('contact/form');
  1087. * which results in using the $CONTACT_WRAPPER['form'] wrapper in the parsing phase
  1088. */
  1089. public function wrapper($id = null)
  1090. {
  1091. if(null === $id) return $this->wrapper;
  1092. if(false === $id) $id = null;
  1093. $this->wrapper = $id;
  1094. return $this;
  1095. }
  1096. /**
  1097. * Set external array data to be used in the batch
  1098. * Use setVars() - preferred.
  1099. * //XXX will soon become private. Use setVars() instead.
  1100. * @param array $eVars
  1101. * @return e_shortcode
  1102. */
  1103. public function setParserVars($eVars)
  1104. {
  1105. $this->var = $eVars;
  1106. return $this;
  1107. }
  1108. /**
  1109. * Alias of setParserVars - Preferred use by Plugins.
  1110. */
  1111. public function setVars($eVars) // Alias of setParserVars();
  1112. {
  1113. return $this->setParserVars($eVars);
  1114. }
  1115. /**
  1116. * Add array to current parser array data
  1117. * @param array $array
  1118. * @return e_shortcode
  1119. */
  1120. public function addParserVars($array)
  1121. {
  1122. if(!is_array($array)) return $this;
  1123. $this->var = array_merge($this->var, $array);
  1124. return $this;
  1125. }
  1126. /**
  1127. * Alias of addParserVars()
  1128. * @param array $array
  1129. * @return e_shortcode
  1130. */
  1131. public function addVars($array)
  1132. {
  1133. return $this->addParserVars($array);
  1134. }
  1135. /**
  1136. * Get external simple parser object
  1137. *
  1138. * @return array
  1139. */
  1140. public function getParserVars()
  1141. {
  1142. return $this->var;
  1143. }
  1144. /**
  1145. * Alias of getParserVars()
  1146. *
  1147. * @return array
  1148. */
  1149. public function getVars()
  1150. {
  1151. return $this->getParserVars();
  1152. }
  1153. /**
  1154. * Batch mod
  1155. * @param string mod
  1156. * @return e_shortcode
  1157. */
  1158. public function setMode($mode)
  1159. {
  1160. $this->mode = ($mode == 'edit') ? 'edit' : 'view';
  1161. return $this;
  1162. }
  1163. /**
  1164. * Add shortcode value
  1165. * <code>e107::getScBatch('class_name')->setScVar('some_property', $some_value);</code>
  1166. *
  1167. * @param string $name
  1168. * @param mixed $value
  1169. * @return e_shortcode
  1170. */
  1171. public function setScVar($name, $value)
  1172. {
  1173. $this->scVars->$name = $value;
  1174. return $this;
  1175. }
  1176. /**
  1177. * Add shortcode values
  1178. * <code>e107::getScBatch('class_name')->addScVars(array('some_property', $some_value));</code>
  1179. *
  1180. * @param array $vars
  1181. * @return e_shortcode
  1182. */
  1183. public function addScVars($vars)
  1184. {
  1185. $this->scVars->addVars($vars);
  1186. return $this;
  1187. }
  1188. /**
  1189. * Retrieve shortcode value
  1190. * <code>$some_value = e107::getScBatch('class_name')->getScVar('some_property');</code>
  1191. *
  1192. * @param string $name
  1193. * @return mixed
  1194. */
  1195. public function getScVar($name)
  1196. {
  1197. return $this->scVars->$name;
  1198. }
  1199. /**
  1200. * Retrieve all shortcode values
  1201. * <code>$some_value = e107::getScBatch('class_name')->getScVars();</code>
  1202. *
  1203. * @return mixed
  1204. */
  1205. public function getScVars()
  1206. {
  1207. return $this->scVars->getVars();
  1208. }
  1209. /**
  1210. * Check if shortcode variable is set
  1211. * <code>if(e107::getScBatch('class_name')->issetScVar('some_property'))
  1212. * {
  1213. * //do something
  1214. * }</code>
  1215. *
  1216. * @param string $name
  1217. * @return boolean
  1218. */
  1219. public function issetScVar($name)
  1220. {
  1221. return isset($this->scVars->$name);
  1222. }
  1223. /**
  1224. * Unset shortcode value
  1225. * <code>e107::getScBatch('class_name')->unsetScVar('some_property');</code>
  1226. *
  1227. * @param string $name
  1228. * @return e_shortcode
  1229. */
  1230. public function unsetScVar($name)
  1231. {
  1232. $this->scVars->$name = null;
  1233. unset($this->scVars->$name);
  1234. return $this;
  1235. }
  1236. /**
  1237. * Empty scvar object data
  1238. * @return e_shortcode
  1239. */
  1240. public function emptyScVars()
  1241. {
  1242. $this->scVars->emptyVars();
  1243. return $this;
  1244. }
  1245. /**
  1246. * Magic setter - bind to eVars object
  1247. *
  1248. * @param string $name
  1249. * @param mixed $value
  1250. */
  1251. public function __set($name, $value)
  1252. {
  1253. $this->setScVar($name, $value);
  1254. }
  1255. /**
  1256. * Magic getter - bind to eVars object
  1257. *
  1258. * @param string $name
  1259. * @return mixed value or null if key not found
  1260. */
  1261. public function __get($name)
  1262. {
  1263. return $this->getScVar($name);
  1264. }
  1265. /**
  1266. * Magic method - bind to eVars object
  1267. * NOTE: works on PHP 5.1.0+
  1268. *
  1269. * @param string $name
  1270. * @return boolean
  1271. */
  1272. public function __isset($name)
  1273. {
  1274. return $this->issetScVar($name);
  1275. }
  1276. /**
  1277. * Magic method - bind to eVars object
  1278. * NOTE: works on PHP 5.1.0+
  1279. *
  1280. * @param string $name
  1281. */
  1282. public function __unset($name)
  1283. {
  1284. $this->unsetScVar($name);
  1285. }
  1286. }