PageRenderTime 32ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 1ms

/Devblocks.class.php

https://github.com/rmiddle/devblocks
PHP | 5093 lines | 3748 code | 745 blank | 600 comment | 456 complexity | 4899a3fbd1abe25fcd0f0adcaa994fe3 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. include_once(DEVBLOCKS_PATH . "api/Model.php");
  3. include_once(DEVBLOCKS_PATH . "api/DAO.php");
  4. include_once(DEVBLOCKS_PATH . "api/Extension.php");
  5. define('PLATFORM_BUILD',2010120101);
  6. /**
  7. * A platform container for plugin/extension registries.
  8. *
  9. * @author Jeff Standen <jeff@webgroupmedia.com>
  10. */
  11. class DevblocksPlatform extends DevblocksEngine {
  12. const CACHE_ACL = 'devblocks_acl';
  13. const CACHE_EVENT_POINTS = 'devblocks_event_points';
  14. const CACHE_EVENTS = 'devblocks_events';
  15. const CACHE_EXTENSIONS = 'devblocks_extensions';
  16. const CACHE_PLUGINS = 'devblocks_plugins';
  17. const CACHE_POINTS = 'devblocks_points';
  18. const CACHE_SETTINGS = 'devblocks_settings';
  19. const CACHE_STORAGE_PROFILES = 'devblocks_storage_profiles';
  20. const CACHE_TABLES = 'devblocks_tables';
  21. const CACHE_TAG_TRANSLATIONS = 'devblocks_translations';
  22. static private $extensionDelegate = null;
  23. static private $start_time = 0;
  24. static private $start_memory = 0;
  25. static private $start_peak_memory = 0;
  26. static private $locale = 'en_US';
  27. static private $_tmp_files = array();
  28. private function __construct() { return false; }
  29. /**
  30. * @param mixed $value
  31. * @param string $type
  32. * @param mixed $default
  33. * @return mixed
  34. */
  35. static function importVar($value,$type=null,$default=null) {
  36. if(is_null($value) && !is_null($default))
  37. $value = $default;
  38. // Sanitize input
  39. switch($type) {
  40. case 'array':
  41. @settype($value,$type);
  42. break;
  43. case 'bit':
  44. $value = !empty($value) ? 1 : 0;
  45. break;
  46. case 'boolean':
  47. $value = !empty($value) ? true : false;
  48. break;
  49. case 'float':
  50. $value = floatval($value);
  51. break;
  52. case 'integer':
  53. $value = intval($value);
  54. break;
  55. case 'string':
  56. $value = (string) $value;
  57. break;
  58. case 'timestamp':
  59. if(!is_numeric($value)) {
  60. try {
  61. $value = strtotime($value);
  62. } catch(Exception $e) {}
  63. } else {
  64. $value = abs(intval($value));
  65. }
  66. break;
  67. default:
  68. @settype($value,$type);
  69. break;
  70. }
  71. return $value;
  72. }
  73. /**
  74. * @param mixed $var
  75. * @param string $cast
  76. * @param mixed $default
  77. * @return mixed
  78. */
  79. static function importGPC($var,$cast=null,$default=null) {
  80. if(!is_null($var)) {
  81. if(is_string($var)) {
  82. $var = get_magic_quotes_gpc() ? stripslashes($var) : $var;
  83. } elseif(is_array($var)) {
  84. foreach($var as $k => $v) {
  85. $var[$k] = get_magic_quotes_gpc() ? stripslashes($v) : $v;
  86. }
  87. }
  88. } elseif (is_null($var) && !is_null($default)) {
  89. $var = $default;
  90. }
  91. if(!is_null($cast))
  92. $var = self::importVar($var, $cast, $default);
  93. return $var;
  94. }
  95. /**
  96. * Returns a string as a regexp.
  97. * "*bob" returns "/(.*?)bob/".
  98. */
  99. static function parseStringAsRegExp($string) {
  100. $pattern = str_replace(array('*'),'__any__', $string);
  101. $pattern = sprintf("/%s/i",str_replace(array('__any__'),'(.*?)', preg_quote($pattern)));
  102. return $pattern;
  103. }
  104. /**
  105. * Returns a formatted string as a number of bytes (e.g. 200M = 209715200)
  106. *
  107. * @param string $string
  108. * @return integer|FALSE
  109. */
  110. static function parseBytesString($string) {
  111. if(is_numeric($string)) {
  112. return intval($string);
  113. } else {
  114. $value = intval($string);
  115. $unit = strtolower(substr($string, -1));
  116. switch($unit) {
  117. default:
  118. case 'm':
  119. return $value * 1048576; // 1024^2
  120. break;
  121. case 'g':
  122. return $value * 1073741824; // 1024^3
  123. break;
  124. case 'k':
  125. return $value * 1024; // 1024^1
  126. break;
  127. }
  128. }
  129. return FALSE;
  130. }
  131. static function parseCrlfString($string) {
  132. $parts = preg_split("/[\r\n]/", $string);
  133. // Remove any empty tokens
  134. foreach($parts as $idx => $part) {
  135. $parts[$idx] = trim($part);
  136. if(0 == strlen($parts[$idx]))
  137. unset($parts[$idx]);
  138. }
  139. return $parts;
  140. }
  141. /**
  142. * Return a string as a regular expression, parsing * into a non-greedy
  143. * wildcard, etc.
  144. *
  145. * @param string $arg
  146. * @return string
  147. */
  148. static function strToRegExp($arg, $is_partial=false) {
  149. $arg = str_replace(array('*'),array('__WILD__'),$arg);
  150. return sprintf("/%s%s%s/i",
  151. ($is_partial ? '' : '^'),
  152. str_replace(array('__WILD__','/'),array('.*?','\/'),preg_quote($arg)),
  153. ($is_partial ? '' : '$')
  154. );
  155. }
  156. /**
  157. * Return a string with only its alphanumeric characters
  158. *
  159. * @param string $arg
  160. * @return string
  161. */
  162. static function strAlphaNum($arg) {
  163. return preg_replace("/[^A-Z0-9\.]/i","", $arg);
  164. }
  165. /**
  166. * Return a string with only its alphanumeric characters or punctuation
  167. *
  168. * @param string $arg
  169. * @return string
  170. */
  171. static function strAlphaNumDash($arg) {
  172. return preg_replace("/[^A-Z0-9_\-\.]/i","", $arg);
  173. }
  174. /**
  175. * Return a string with only its alphanumeric characters or underscore
  176. *
  177. * @param string $arg
  178. * @return string
  179. */
  180. static function strAlphaNumUnder($arg) {
  181. return preg_replace("/[^A-Z0-9_]/i","", $arg);
  182. }
  183. static function stripHTML($str) {
  184. // Strip all CRLF and tabs, spacify </TD>
  185. $str = str_ireplace(
  186. array("\r","\n","\t","</td>"),
  187. array('','',' ',' '),
  188. trim($str)
  189. );
  190. // Handle XHTML variations
  191. $str = str_ireplace(
  192. array("<br />", "<br/>"),
  193. "<br>",
  194. $str
  195. );
  196. // Turn block tags into a linefeed
  197. $str = str_ireplace(
  198. array('<BR>','<P>','</P>','<HR>','</TR>','</H1>','</H2>','</H3>','</H4>','</H5>','</H6>','</DIV>'),
  199. "\n",
  200. $str
  201. );
  202. // Strip tags
  203. $search = array(
  204. '@<script[^>]*?>.*?</script>@si',
  205. '@<style[^>]*?>.*?</style>@siU',
  206. '@<[\/\!]*?[^<>]*?>@si',
  207. '@<![\s\S]*?--[ \t\n\r]*>@',
  208. );
  209. $str = preg_replace($search, '', $str);
  210. // Flatten multiple spaces into a single
  211. $str = preg_replace('# +#', ' ', $str);
  212. // Translate HTML entities into text
  213. $str = html_entity_decode($str, ENT_COMPAT, LANG_CHARSET_CODE);
  214. // Loop through each line, ltrim, and concat if not empty
  215. $lines = explode("\n", $str);
  216. if(is_array($lines)) {
  217. $str = '';
  218. $blanks = 0;
  219. foreach($lines as $idx => $line) {
  220. $lines[$idx] = ltrim($line);
  221. if(empty($lines[$idx])) {
  222. if(++$blanks >= 2)
  223. unset($lines[$idx]);
  224. //continue; // skip more than 2 blank lines in a row
  225. } else {
  226. $blanks = 0;
  227. }
  228. }
  229. $str = implode("\n", $lines);
  230. }
  231. unset($lines);
  232. // Clean up bytes (needed after HTML entities)
  233. $str = mb_convert_encoding($str, LANG_CHARSET_CODE, LANG_CHARSET_CODE);
  234. return $str;
  235. }
  236. static function parseMarkdown($text) {
  237. static $parser = null;
  238. if(is_null($parser))
  239. $parser = new Markdown_Parser();
  240. return $parser->transform($text);
  241. }
  242. static function parseRss($url) {
  243. // [TODO] curl | file_get_contents() support
  244. // [TODO] rss://
  245. if(null == (@$data = file_get_contents($url)))
  246. return false;
  247. if(null == (@$xml = simplexml_load_string($data)))
  248. return false;
  249. // [TODO] Better detection of RDF/RSS/Atom + versions
  250. $root_tag = strtolower(dom_import_simplexml($xml)->tagName);
  251. if('feed'==$root_tag && count($xml->entry)) { // Atom
  252. $feed = array(
  253. 'title' => (string) $xml->title,
  254. 'url' => $url,
  255. 'items' => array(),
  256. );
  257. if(!count($xml))
  258. return $feed;
  259. foreach($xml->entry as $entry) {
  260. $id = (string) $entry->id;
  261. $date = (string) $entry->published;
  262. $title = (string) $entry->title;
  263. $content = (string) $entry->summary;
  264. $link = '';
  265. // Link as the <id> element
  266. if(preg_match("/^(.*)\:\/\/(.*$)/i", $id, $matches)) {
  267. $link = $id;
  268. // Link as 'alternative' attrib
  269. } elseif(count($entry->link)) {
  270. foreach($entry->link as $link) {
  271. if(0==strcasecmp('alternate',(string)$link['rel'])) {
  272. $link = (string) $link['href'];
  273. break;
  274. }
  275. }
  276. }
  277. $feed['items'][] = array(
  278. 'date' => strtotime($date),
  279. 'link' => $link,
  280. 'title' => $title,
  281. 'content' => $content,
  282. );
  283. }
  284. } elseif('rdf:rdf'==$root_tag && count($xml->item)) { // RDF
  285. $feed = array(
  286. 'title' => (string) $xml->channel->title,
  287. 'url' => $url,
  288. 'items' => array(),
  289. );
  290. if(!count($xml))
  291. return $feed;
  292. foreach($xml->item as $item) {
  293. $date = (string) $item->pubDate;
  294. $link = (string) $item->link;
  295. $title = (string) $item->title;
  296. $content = (string) $item->description;
  297. $feed['items'][] = array(
  298. 'date' => strtotime($date),
  299. 'link' => $link,
  300. 'title' => $title,
  301. 'content' => $content,
  302. );
  303. }
  304. } elseif('rss'==$root_tag && count($xml->channel->item)) { // RSS
  305. $feed = array(
  306. 'title' => (string) $xml->channel->title,
  307. 'url' => $url,
  308. 'items' => array(),
  309. );
  310. if(!count($xml))
  311. return $feed;
  312. foreach($xml->channel->item as $item) {
  313. $date = (string) $item->pubDate;
  314. $link = (string) $item->link;
  315. $title = (string) $item->title;
  316. $content = (string) $item->description;
  317. $feed['items'][] = array(
  318. 'date' => strtotime($date),
  319. 'link' => $link,
  320. 'title' => $title,
  321. 'content' => $content,
  322. );
  323. }
  324. }
  325. if(empty($feed))
  326. return false;
  327. return $feed;
  328. }
  329. /**
  330. * Returns a string as alphanumerics delimited by underscores.
  331. * For example: "Devs: 1000 Ways to Improve Sales" becomes
  332. * "devs_1000_ways_to_improve_sales", which is suitable for
  333. * displaying in a URL of a blog, faq, etc.
  334. *
  335. * @param string $str
  336. * @return string
  337. */
  338. static function getStringAsURI($str) {
  339. $str = strtolower($str);
  340. // turn non [a-z, 0-9, _] into whitespace
  341. $str = preg_replace("/[^0-9a-z]/",' ',$str);
  342. // condense whitespace to a single underscore
  343. $str = preg_replace('/\s\s+/', ' ', $str);
  344. // replace spaces with underscore
  345. $str = str_replace(' ','_',$str);
  346. // remove a leading/trailing underscores
  347. $str = trim($str, '_');
  348. return $str;
  349. }
  350. /**
  351. * Takes a comma-separated value string and returns an array of tokens.
  352. * [TODO] Move to a FormHelper service?
  353. *
  354. * @param string $string
  355. * @return array
  356. */
  357. static function parseCsvString($string) {
  358. $tokens = explode(',', $string);
  359. if(!is_array($tokens))
  360. return array();
  361. foreach($tokens as $k => $v) {
  362. $tokens[$k] = trim($v);
  363. if(0==strlen($tokens[$k]))
  364. unset($tokens[$k]);
  365. }
  366. return $tokens;
  367. }
  368. /**
  369. * Clears any platform-level plugin caches.
  370. *
  371. */
  372. static function clearCache($one_cache=null) {
  373. $cache = self::getCacheService(); /* @var $cache _DevblocksCacheManager */
  374. if(!empty($one_cache)) {
  375. $cache->remove($one_cache);
  376. } else { // All
  377. $cache->remove(self::CACHE_ACL);
  378. $cache->remove(self::CACHE_PLUGINS);
  379. $cache->remove(self::CACHE_EVENT_POINTS);
  380. $cache->remove(self::CACHE_EVENTS);
  381. $cache->remove(self::CACHE_EXTENSIONS);
  382. $cache->remove(self::CACHE_POINTS);
  383. $cache->remove(self::CACHE_SETTINGS);
  384. $cache->remove(self::CACHE_TABLES);
  385. $cache->remove(_DevblocksClassLoadManager::CACHE_CLASS_MAP);
  386. // Clear all locale caches
  387. $langs = DAO_Translation::getDefinedLangCodes();
  388. if(is_array($langs) && !empty($langs))
  389. foreach($langs as $lang_code => $lang_name) {
  390. $cache->remove(self::CACHE_TAG_TRANSLATIONS . '_' . $lang_code);
  391. }
  392. }
  393. // Cache-specific 'after' actions
  394. switch($one_cache) {
  395. case self::CACHE_PLUGINS:
  396. case self::CACHE_EXTENSIONS:
  397. case NULL:
  398. self::getPluginRegistry();
  399. self::getExtensionRegistry();
  400. break;
  401. }
  402. }
  403. public static function loadClass($className) {
  404. $classloader = self::getClassLoaderService();
  405. return $classloader->loadClass($className);
  406. }
  407. public static function registerClasses($file,$classes=array()) {
  408. $classloader = self::getClassLoaderService();
  409. return $classloader->registerClasses($file,$classes);
  410. }
  411. public static function getStartTime() {
  412. return self::$start_time;
  413. }
  414. public static function getStartMemory() {
  415. return self::$start_memory;
  416. }
  417. public static function getStartPeakMemory() {
  418. return self::$start_peak_memory;
  419. }
  420. /**
  421. * @return resource $fp
  422. */
  423. public static function getTempFile() {
  424. // Generate a new temporary file
  425. $file_name = tempnam(APP_TEMP_PATH, 'tmp');
  426. // Open the file pointer
  427. $fp = fopen($file_name, "w+b");
  428. // Manually keep track of these temporary files
  429. self::$_tmp_files[intval($fp)] = $file_name;
  430. return $fp;
  431. }
  432. /**
  433. * @return resource $fp
  434. */
  435. public static function getTempFileInfo($fp) {
  436. // If we're asking about a specific temporary file
  437. if(!empty($fp)) {
  438. if(@isset(self::$_tmp_files[intval($fp)]))
  439. return self::$_tmp_files[intval($fp)];
  440. return false;
  441. }
  442. }
  443. /**
  444. * Checks whether the active database has any tables.
  445. *
  446. * @return boolean
  447. */
  448. static function isDatabaseEmpty() {
  449. $tables = self::getDatabaseTables();
  450. return empty($tables);
  451. }
  452. static function getDatabaseTables() {
  453. $cache = self::getCacheService();
  454. $tables = array();
  455. if(null === ($tables = $cache->load(self::CACHE_TABLES))) {
  456. $db = self::getDatabaseService();
  457. // Make sure the database connection is valid or error out.
  458. if(is_null($db) || !$db->isConnected())
  459. return array();
  460. $tables = $db->metaTables();
  461. $cache->save($tables, self::CACHE_TABLES);
  462. }
  463. return $tables;
  464. }
  465. /**
  466. * Checks to see if the application needs to patch
  467. *
  468. * @return boolean
  469. */
  470. static function versionConsistencyCheck() {
  471. $cache = DevblocksPlatform::getCacheService(); /* @var _DevblocksCacheManager $cache */
  472. if(null === ($build_cache = $cache->load("devblocks_app_build"))
  473. || $build_cache != APP_BUILD) {
  474. // If build changed, clear cache regardless of patch status
  475. // [TODO] We need to find a nicer way to not clear a shared memcached cluster when only one desk needs to
  476. $cache = DevblocksPlatform::getCacheService(); /* @var $cache _DevblocksCacheManager */
  477. $cache->clean();
  478. // Re-read manifests
  479. DevblocksPlatform::readPlugins();
  480. if(self::_needsToPatch()) {
  481. return false; // the update script will handle new caches
  482. } else {
  483. $cache->save(APP_BUILD, "devblocks_app_build");
  484. DAO_Translation::reloadPluginStrings(); // reload strings even without DB changes
  485. return true;
  486. }
  487. }
  488. return true;
  489. }
  490. /**
  491. * Enter description here...
  492. *
  493. * @return boolean
  494. */
  495. static private function _needsToPatch() {
  496. $plugins = DevblocksPlatform::getPluginRegistry();
  497. // First install or upgrade
  498. if(empty($plugins))
  499. return true;
  500. foreach($plugins as $plugin) { /* @var $plugin DevblocksPluginManifest */
  501. if($plugin->enabled) {
  502. foreach($plugin->getPatches() as $patch) { /* @var $patch DevblocksPatch */
  503. if(!$patch->hasRun())
  504. return true;
  505. }
  506. }
  507. }
  508. return false;
  509. }
  510. /**
  511. * Returns the list of extensions on a given extension point.
  512. *
  513. * @static
  514. * @param string $point
  515. * @return DevblocksExtensionManifest[]
  516. */
  517. static function getExtensions($point,$as_instances=false, $ignore_acl=false) {
  518. $results = array();
  519. $extensions = DevblocksPlatform::getExtensionRegistry($ignore_acl);
  520. if(is_array($extensions))
  521. foreach($extensions as $extension) { /* @var $extension DevblocksExtensionManifest */
  522. if($extension->point == $point) {
  523. $results[$extension->id] = ($as_instances) ? $extension->createInstance() : $extension;
  524. }
  525. }
  526. return $results;
  527. }
  528. /**
  529. * Returns the manifest of a given extension ID.
  530. *
  531. * @static
  532. * @param string $extension_id
  533. * @param boolean $as_instance
  534. * @return DevblocksExtensionManifest
  535. */
  536. static function getExtension($extension_id, $as_instance=false, $ignore_acl=false) {
  537. $result = null;
  538. $extensions = DevblocksPlatform::getExtensionRegistry($ignore_acl);
  539. if(is_array($extensions))
  540. foreach($extensions as $extension) { /* @var $extension DevblocksExtensionManifest */
  541. if($extension->id == $extension_id) {
  542. $result = $extension;
  543. break;
  544. }
  545. }
  546. if($as_instance && !is_null($result)) {
  547. return $result->createInstance();
  548. }
  549. return $result;
  550. }
  551. // static function getExtensionPoints() {
  552. // $cache = self::getCacheService();
  553. // if(null !== ($points = $cache->load(self::CACHE_POINTS)))
  554. // return $points;
  555. //
  556. // $extensions = DevblocksPlatform::getExtensionRegistry();
  557. // foreach($extensions as $extension) { /* @var $extension DevblocksExtensionManifest */
  558. // $point = $extension->point;
  559. // if(!isset($points[$point])) {
  560. // $p = new DevblocksExtensionPoint();
  561. // $p->id = $point;
  562. // $points[$point] = $p;
  563. // }
  564. //
  565. // $points[$point]->extensions[$extension->id] = $extension;
  566. // }
  567. //
  568. // $cache->save($points, self::CACHE_POINTS);
  569. // return $points;
  570. // }
  571. /**
  572. * Returns an array of all contributed extension manifests.
  573. *
  574. * @static
  575. * @return DevblocksExtensionManifest[]
  576. */
  577. static function getExtensionRegistry($ignore_acl=false) {
  578. $cache = self::getCacheService();
  579. static $acl_extensions = null;
  580. if(null === ($extensions = $cache->load(self::CACHE_EXTENSIONS))) {
  581. $db = DevblocksPlatform::getDatabaseService();
  582. if(is_null($db)) return;
  583. $prefix = (APP_DB_PREFIX != '') ? APP_DB_PREFIX.'_' : ''; // [TODO] Cleanup
  584. $sql = sprintf("SELECT e.id , e.plugin_id, e.point, e.pos, e.name , e.file , e.class, e.params ".
  585. "FROM %sextension e ".
  586. "INNER JOIN %splugin p ON (e.plugin_id=p.id) ".
  587. "WHERE p.enabled = 1 ".
  588. "ORDER BY e.id ASC, e.plugin_id ASC, e.pos ASC",
  589. $prefix,
  590. $prefix
  591. );
  592. $results = $db->GetArray($sql);
  593. foreach($results as $row) {
  594. $extension = new DevblocksExtensionManifest();
  595. $extension->id = $row['id'];
  596. $extension->plugin_id = $row['plugin_id'];
  597. $extension->point = $row['point'];
  598. $extension->name = $row['name'];
  599. $extension->file = $row['file'];
  600. $extension->class = $row['class'];
  601. $extension->params = @unserialize($row['params']);
  602. if(empty($extension->params))
  603. $extension->params = array();
  604. $extensions[$extension->id] = $extension;
  605. }
  606. $cache->save($extensions, self::CACHE_EXTENSIONS);
  607. $acl_extensions = null;
  608. }
  609. if(!$ignore_acl) {
  610. // If we don't have a cache in this request
  611. if(null == $acl_extensions) {
  612. // Check with an extension delegate if we have one
  613. if(class_exists(self::$extensionDelegate) && method_exists('DevblocksExtensionDelegate','shouldLoadExtension')) {
  614. if(is_array($extensions))
  615. foreach($extensions as $id => $extension) {
  616. // Ask the delegate if we should load the extension
  617. if(!call_user_func(array(self::$extensionDelegate,'shouldLoadExtension'),$extension))
  618. unset($extensions[$id]);
  619. }
  620. }
  621. // Cache for duration of request
  622. $acl_extensions = $extensions;
  623. } else {
  624. $extensions = $acl_extensions;
  625. }
  626. }
  627. return $extensions;
  628. }
  629. /**
  630. * @return DevblocksEventPoint[]
  631. */
  632. static function getEventPointRegistry() {
  633. $cache = self::getCacheService();
  634. if(null !== ($events = $cache->load(self::CACHE_EVENT_POINTS)))
  635. return $events;
  636. $events = array();
  637. $plugins = self::getPluginRegistry();
  638. // [JAS]: Event point hashing/caching
  639. if(is_array($plugins))
  640. foreach($plugins as $plugin) { /* @var $plugin DevblocksPluginManifest */
  641. $events = array_merge($events,$plugin->event_points);
  642. }
  643. $cache->save($events, self::CACHE_EVENT_POINTS);
  644. return $events;
  645. }
  646. /**
  647. * @return DevblocksAclPrivilege[]
  648. */
  649. static function getAclRegistry() {
  650. $cache = self::getCacheService();
  651. if(null !== ($acl = $cache->load(self::CACHE_ACL)))
  652. return $acl;
  653. $acl = array();
  654. $db = DevblocksPlatform::getDatabaseService();
  655. if(is_null($db)) return;
  656. //$plugins = self::getPluginRegistry();
  657. $prefix = (APP_DB_PREFIX != '') ? APP_DB_PREFIX.'_' : ''; // [TODO] Cleanup
  658. $sql = sprintf("SELECT a.id, a.plugin_id, a.label ".
  659. "FROM %sacl a ".
  660. "INNER JOIN %splugin p ON (a.plugin_id=p.id) ".
  661. "WHERE p.enabled = 1 ".
  662. "ORDER BY a.plugin_id, a.id ASC",
  663. $prefix,
  664. $prefix
  665. );
  666. $results = $db->GetArray($sql);
  667. foreach($results as $row) {
  668. $priv = new DevblocksAclPrivilege();
  669. $priv->id = $row['id'];
  670. $priv->plugin_id = $row['plugin_id'];
  671. $priv->label = $row['label'];
  672. $acl[$priv->id] = $priv;
  673. }
  674. $cache->save($acl, self::CACHE_ACL);
  675. return $acl;
  676. }
  677. static function getEventRegistry() {
  678. $cache = self::getCacheService();
  679. if(null !== ($events = $cache->load(self::CACHE_EVENTS)))
  680. return $events;
  681. $extensions = self::getExtensions('devblocks.listener.event',false,true);
  682. $events = array('*');
  683. // [JAS]: Event point hashing/caching
  684. if(is_array($extensions))
  685. foreach($extensions as $extension) { /* @var $extension DevblocksExtensionManifest */
  686. @$evts = $extension->params['events'][0];
  687. // Global listeners (every point)
  688. if(empty($evts) && !is_array($evts)) {
  689. $events['*'][] = $extension->id;
  690. continue;
  691. }
  692. if(is_array($evts))
  693. foreach(array_keys($evts) as $evt) {
  694. $events[$evt][] = $extension->id;
  695. }
  696. }
  697. $cache->save($events, self::CACHE_EVENTS);
  698. return $events;
  699. }
  700. /**
  701. * Returns an array of all contributed plugin manifests.
  702. *
  703. * @static
  704. * @return DevblocksPluginManifest[]
  705. */
  706. static function getPluginRegistry() {
  707. $cache = self::getCacheService();
  708. if(null !== ($plugins = $cache->load(self::CACHE_PLUGINS)))
  709. return $plugins;
  710. $db = DevblocksPlatform::getDatabaseService();
  711. if(is_null($db))
  712. return;
  713. $plugins = array();
  714. $prefix = (APP_DB_PREFIX != '') ? APP_DB_PREFIX.'_' : ''; // [TODO] Cleanup
  715. $sql = sprintf("SELECT p.* ".
  716. "FROM %splugin p ".
  717. "ORDER BY p.enabled DESC, p.name ASC ",
  718. $prefix
  719. );
  720. $results = $db->GetArray($sql);
  721. foreach($results as $row) {
  722. $plugin = new DevblocksPluginManifest();
  723. @$plugin->id = $row['id'];
  724. @$plugin->enabled = intval($row['enabled']);
  725. @$plugin->name = $row['name'];
  726. @$plugin->description = $row['description'];
  727. @$plugin->author = $row['author'];
  728. @$plugin->revision = intval($row['revision']);
  729. @$plugin->link = $row['link'];
  730. @$plugin->dir = $row['dir'];
  731. // JSON decode
  732. if(isset($row['manifest_cache_json'])
  733. && null != ($manifest_cache_json = $row['manifest_cache_json'])) {
  734. $plugin->manifest_cache = json_decode($manifest_cache_json, true);
  735. }
  736. if(file_exists(APP_PATH . DIRECTORY_SEPARATOR . $plugin->dir . DIRECTORY_SEPARATOR . 'plugin.xml'))
  737. $plugins[$plugin->id] = $plugin;
  738. }
  739. $sql = sprintf("SELECT p.id, p.name, p.params, p.plugin_id ".
  740. "FROM %sevent_point p ",
  741. $prefix
  742. );
  743. $results = $db->GetArray($sql);
  744. foreach($results as $row) {
  745. $point = new DevblocksEventPoint();
  746. $point->id = $row['id'];
  747. $point->name = $row['name'];
  748. $point->plugin_id = $row['plugin_id'];
  749. $params = $row['params'];
  750. $point->params = !empty($params) ? unserialize($params) : array();
  751. if(isset($plugins[$point->plugin_id])) {
  752. $plugins[$point->plugin_id]->event_points[$point->id] = $point;
  753. }
  754. }
  755. self::_sortPluginsByDependency($plugins);
  756. $cache->save($plugins, self::CACHE_PLUGINS);
  757. return $plugins;
  758. }
  759. static private function _sortPluginsByDependency(&$plugins) {
  760. $dependencies = array();
  761. $seen = array();
  762. $order = array();
  763. // Dependencies
  764. foreach($plugins as $plugin) {
  765. @$deps = $plugin->manifest_cache['dependencies'];
  766. $dependencies[$plugin->id] = is_array($deps) ? $deps : array();
  767. }
  768. foreach($plugins as $plugin)
  769. self::_recursiveDependency($plugin->id, $dependencies, $seen, $order);
  770. $original = $plugins;
  771. $plugins = array();
  772. foreach($order as $order_id) {
  773. $plugins[$order_id] = $original[$order_id];
  774. }
  775. }
  776. static private function _recursiveDependency($id, $deps, &$seen, &$order, $level=0) {
  777. if(isset($seen[$id]))
  778. return true;
  779. if(isset($deps[$id]) && !empty($deps[$id])) {
  780. foreach($deps[$id] as $dep) {
  781. if(!self::_recursiveDependency($dep, $deps, $seen, $order, ++$level))
  782. return false;
  783. }
  784. }
  785. if(!isset($seen[$id])) {
  786. $order[] = $id;
  787. $seen[$id] = true;
  788. }
  789. return true;
  790. }
  791. /**
  792. * Enter description here...
  793. *
  794. * @param string $id
  795. * @return DevblocksPluginManifest
  796. */
  797. static function getPlugin($id) {
  798. $plugins = DevblocksPlatform::getPluginRegistry();
  799. if(isset($plugins[$id]))
  800. return $plugins[$id];
  801. return null;
  802. }
  803. /**
  804. * Reads and caches manifests from the features + plugins directories.
  805. *
  806. * @static
  807. * @return DevblocksPluginManifest[]
  808. */
  809. static function readPlugins() {
  810. $scan_dirs = array(
  811. 'features',
  812. 'storage/plugins',
  813. );
  814. $plugins = array();
  815. // Devblocks
  816. if(null !== ($manifest = self::_readPluginManifest('libs/devblocks')))
  817. $plugin[] = $manifest;
  818. // Application
  819. if(is_array($scan_dirs))
  820. foreach($scan_dirs as $scan_dir) {
  821. $scan_path = APP_PATH . '/' . $scan_dir;
  822. if (is_dir($scan_path)) {
  823. if ($dh = opendir($scan_path)) {
  824. while (($file = readdir($dh)) !== false) {
  825. if($file=="." || $file == "..")
  826. continue;
  827. $plugin_path = $scan_path . '/' . $file;
  828. $rel_path = $scan_dir . '/' . $file;
  829. if(is_dir($plugin_path) && file_exists($plugin_path.'/plugin.xml')) {
  830. $manifest = self::_readPluginManifest($rel_path); /* @var $manifest DevblocksPluginManifest */
  831. if(null != $manifest) {
  832. $plugins[$manifest->id] = $manifest;
  833. }
  834. }
  835. }
  836. closedir($dh);
  837. }
  838. }
  839. }
  840. DevblocksPlatform::clearCache(DevblocksPlatform::CACHE_PLUGINS);
  841. return $plugins;
  842. }
  843. /**
  844. * @return _DevblocksPluginSettingsManager
  845. */
  846. static function getPluginSettingsService() {
  847. return _DevblocksPluginSettingsManager::getInstance();
  848. }
  849. static function getPluginSetting($plugin_id, $key, $default=null) {
  850. $settings = self::getPluginSettingsService();
  851. return $settings->get($plugin_id, $key, $default);
  852. }
  853. static function setPluginSetting($plugin_id, $key, $value) {
  854. $settings = self::getPluginSettingsService();
  855. return $settings->set($plugin_id, $key, $value);
  856. }
  857. /**
  858. * @return _DevblocksLogManager
  859. */
  860. static function getConsoleLog() {
  861. return _DevblocksLogManager::getConsoleLog();
  862. }
  863. /**
  864. * @return _DevblocksCacheManager
  865. */
  866. static function getCacheService() {
  867. return _DevblocksCacheManager::getInstance();
  868. }
  869. /**
  870. * Enter description here...
  871. *
  872. * @return _DevblocksDatabaseManager
  873. */
  874. static function getDatabaseService() {
  875. return _DevblocksDatabaseManager::getInstance();
  876. }
  877. /**
  878. * @return _DevblocksUrlManager
  879. */
  880. static function getUrlService() {
  881. return _DevblocksUrlManager::getInstance();
  882. }
  883. /**
  884. * @return _DevblocksEmailManager
  885. */
  886. static function getMailService() {
  887. return _DevblocksEmailManager::getInstance();
  888. }
  889. /**
  890. * @return _DevblocksEventManager
  891. */
  892. static function getEventService() {
  893. return _DevblocksEventManager::getInstance();
  894. }
  895. /**
  896. * @return DevblocksProxy
  897. */
  898. static function getProxyService() {
  899. return DevblocksProxy::getProxy();
  900. }
  901. /**
  902. * @return _DevblocksClassLoadManager
  903. */
  904. static function getClassLoaderService() {
  905. return _DevblocksClassLoadManager::getInstance();
  906. }
  907. /**
  908. * @return _DevblocksSessionManager
  909. */
  910. static function getSessionService() {
  911. return _DevblocksSessionManager::getInstance();
  912. }
  913. /**
  914. * @return _DevblocksOpenIDManager
  915. */
  916. static function getOpenIDService() {
  917. return _DevblocksOpenIDManager::getInstance();
  918. }
  919. /**
  920. * @return _DevblocksSearchEngineMysqlFulltext
  921. */
  922. static function getSearchService() {
  923. return _DevblocksSearchManager::getInstance();
  924. }
  925. /**
  926. * @param $profile_id | $extension_id, $options
  927. * @return Extension_DevblocksStorageEngine
  928. */
  929. static function getStorageService() {
  930. $args = func_get_args();
  931. if(empty($args))
  932. return false;
  933. $profile = $args[0];
  934. $params = array();
  935. // Handle $profile polymorphism
  936. if($profile instanceof Model_DevblocksStorageProfile) {
  937. $extension = $profile->extension_id;
  938. $params = $profile->params;
  939. } else if(is_numeric($profile)) {
  940. $storage_profile = DAO_DevblocksStorageProfile::get($profile);
  941. $extension = $storage_profile->extension_id;
  942. $params = $storage_profile->params;
  943. } else if(is_string($profile)) {
  944. $extension = $profile;
  945. if(isset($args[1]) && is_array($args[1]))
  946. $params = $args[1];
  947. }
  948. return _DevblocksStorageManager::getEngine($extension, $params);
  949. }
  950. /**
  951. * @return Smarty
  952. */
  953. static function getTemplateService() {
  954. return _DevblocksTemplateManager::getInstance();
  955. }
  956. /**
  957. *
  958. * @param string $set
  959. * @return DevblocksTemplate[]
  960. */
  961. static function getTemplates($set=null) {
  962. $templates = array();
  963. $plugins = self::getPluginRegistry();
  964. if(is_array($plugins))
  965. foreach($plugins as $plugin) {
  966. if(isset($plugin->manifest_cache['templates']) && is_array($plugin->manifest_cache['templates']))
  967. foreach($plugin->manifest_cache['templates'] as $tpl) {
  968. if(null === $set || 0 == strcasecmp($set, $tpl['set'])) {
  969. $template = new DevblocksTemplate();
  970. $template->plugin_id = $tpl['plugin_id'];
  971. $template->set = $tpl['set'];
  972. $template->path = $tpl['path'];
  973. $templates[] = $template;
  974. }
  975. }
  976. }
  977. return $templates;
  978. }
  979. /**
  980. * @return _DevblocksTemplateBuilder
  981. */
  982. static function getTemplateBuilder() {
  983. return _DevblocksTemplateBuilder::getInstance();
  984. }
  985. /**
  986. * @return _DevblocksDateManager
  987. */
  988. static function getDateService($datestamp=null) {
  989. return _DevblocksDateManager::getInstance();
  990. }
  991. static function setLocale($locale) {
  992. @setlocale(LC_ALL, $locale);
  993. self::$locale = $locale;
  994. }
  995. static function getLocale() {
  996. if(!empty(self::$locale))
  997. return self::$locale;
  998. return 'en_US';
  999. }
  1000. /**
  1001. * @return _DevblocksTranslationManager
  1002. */
  1003. static function getTranslationService() {
  1004. static $languages = array();
  1005. $locale = DevblocksPlatform::getLocale();
  1006. // Registry
  1007. if(isset($languages[$locale])) {
  1008. return $languages[$locale];
  1009. }
  1010. $cache = self::getCacheService();
  1011. if(null === ($map = $cache->load(self::CACHE_TAG_TRANSLATIONS.'_'.$locale))) { /* @var $cache _DevblocksCacheManager */
  1012. $map = array();
  1013. $map_en = DAO_Translation::getMapByLang('en_US');
  1014. if(0 != strcasecmp('en_US', $locale))
  1015. $map_loc = DAO_Translation::getMapByLang($locale);
  1016. // Loop through the English string objects
  1017. if(is_array($map_en))
  1018. foreach($map_en as $string_id => $obj_string_en) {
  1019. $string = '';
  1020. // If we have a locale to check
  1021. if(isset($map_loc) && is_array($map_loc)) {
  1022. @$obj_string_loc = $map_loc[$string_id];
  1023. @$string =
  1024. (!empty($obj_string_loc->string_override))
  1025. ? $obj_string_loc->string_override
  1026. : $obj_string_loc->string_default;
  1027. }
  1028. // If we didn't hit, load the English default
  1029. if(empty($string))
  1030. @$string =
  1031. (!empty($obj_string_en->string_override))
  1032. ? $obj_string_en->string_override
  1033. : $obj_string_en->string_default;
  1034. // If we found any match
  1035. if(!empty($string))
  1036. $map[$string_id] = $string;
  1037. }
  1038. unset($obj_string_en);
  1039. unset($obj_string_loc);
  1040. unset($map_en);
  1041. unset($map_loc);
  1042. // Cache with tag (tag allows easy clean for multiple langs at once)
  1043. $cache->save($map,self::CACHE_TAG_TRANSLATIONS.'_'.$locale);
  1044. }
  1045. $translate = _DevblocksTranslationManager::getInstance();
  1046. $translate->addLocale($locale, $map);
  1047. $translate->setLocale($locale);
  1048. $languages[$locale] = $translate;
  1049. return $translate;
  1050. }
  1051. /**
  1052. * Enter description here...
  1053. *
  1054. * @return DevblocksHttpRequest
  1055. */
  1056. static function getHttpRequest() {
  1057. return self::$request;
  1058. }
  1059. /**
  1060. * @param DevblocksHttpRequest $request
  1061. */
  1062. static function setHttpRequest(DevblocksHttpRequest $request) {
  1063. self::$request = $request;
  1064. }
  1065. /**
  1066. * Enter description here...
  1067. *
  1068. * @return DevblocksHttpRequest
  1069. */
  1070. static function getHttpResponse() {
  1071. return self::$response;
  1072. }
  1073. /**
  1074. * @param DevblocksHttpResponse $response
  1075. */
  1076. static function setHttpResponse(DevblocksHttpResponse $response) {
  1077. self::$response = $response;
  1078. }
  1079. /**
  1080. * Initializes the plugin platform (paths, etc).
  1081. *
  1082. * @static
  1083. * @return void
  1084. */
  1085. static function init() {
  1086. self::$start_time = microtime(true);
  1087. if(function_exists('memory_get_usage') && function_exists('memory_get_peak_usage')) {
  1088. self::$start_memory = memory_get_usage();
  1089. self::$start_peak_memory = memory_get_peak_usage();
  1090. }
  1091. // Encoding (mbstring)
  1092. mb_internal_encoding(LANG_CHARSET_CODE);
  1093. // [JAS] [MDF]: Automatically determine the relative webpath to Devblocks files
  1094. @$proxyhost = $_SERVER['HTTP_DEVBLOCKSPROXYHOST'];
  1095. @$proxybase = $_SERVER['HTTP_DEVBLOCKSPROXYBASE'];
  1096. // App path (always backend)
  1097. $app_self = $_SERVER["SCRIPT_NAME"];
  1098. if(DEVBLOCKS_REWRITE) {
  1099. $pos = strrpos($app_self,'/');
  1100. $app_self = substr($app_self,0,$pos) . '/';
  1101. } else {
  1102. $pos = strrpos($app_self,'index.php');
  1103. if(false === $pos) $pos = strrpos($app_self,'ajax.php');
  1104. $app_self = substr($app_self,0,$pos);
  1105. }
  1106. // Context path (abstracted: proxies or backend)
  1107. if(!empty($proxybase)) { // proxy
  1108. $context_self = $proxybase . '/';
  1109. } else { // non-proxy
  1110. $context_self = $app_self;
  1111. }
  1112. @define('DEVBLOCKS_WEBPATH',$context_self);
  1113. @define('DEVBLOCKS_APP_WEBPATH',$app_self);
  1114. // Register shutdown function
  1115. register_shutdown_function(array('DevblocksPlatform','shutdown'));
  1116. }
  1117. static function shutdown() {
  1118. // Clean up any temporary files
  1119. while(null != ($tmpfile = array_pop(self::$_tmp_files))) {
  1120. @unlink($tmpfile);
  1121. }
  1122. }
  1123. static function setExtensionDelegate($class) {
  1124. if(!empty($class) && class_exists($class, true))
  1125. self::$extensionDelegate = $class;
  1126. }
  1127. static function redirect(DevblocksHttpIO $httpIO) {
  1128. $url_service = self::getUrlService();
  1129. session_write_close();
  1130. $url = $url_service->writeDevblocksHttpIO($httpIO, true);
  1131. header('Location: '.$url);
  1132. exit;
  1133. }
  1134. };
  1135. abstract class DevblocksEngine {
  1136. protected static $request = null;
  1137. protected static $response = null;
  1138. /**
  1139. * Reads and caches a single manifest from a given plugin directory.
  1140. *
  1141. * @static
  1142. * @private
  1143. * @param string $dir
  1144. * @return DevblocksPluginManifest
  1145. */
  1146. static protected function _readPluginManifest($rel_dir, $persist=true) {
  1147. $manifest_file = APP_PATH . '/' . $rel_dir . '/plugin.xml';
  1148. if(!file_exists($manifest_file))
  1149. return NULL;
  1150. $plugin = simplexml_load_file($manifest_file);
  1151. $prefix = (APP_DB_PREFIX != '') ? APP_DB_PREFIX.'_' : ''; // [TODO] Cleanup
  1152. $manifest = new DevblocksPluginManifest();
  1153. $manifest->id = (string) $plugin->id;
  1154. $manifest->dir = $rel_dir;
  1155. $manifest->description = (string) $plugin->description;
  1156. $manifest->author = (string) $plugin->author;
  1157. $manifest->revision = (integer) $plugin->revision;
  1158. $manifest->link = (string) $plugin->link;
  1159. $manifest->name = (string) $plugin->name;
  1160. // Dependencies
  1161. if(isset($plugin->dependencies)) {
  1162. if(isset($plugin->dependencies->require))
  1163. foreach($plugin->dependencies->require as $eDependency) {
  1164. $depends_on = (string) $eDependency['plugin_id'];
  1165. $manifest->manifest_cache['dependencies'][] = $depends_on;
  1166. }
  1167. }
  1168. // Patches
  1169. if(isset($plugin->patches)) {
  1170. if(isset($plugin->patches->patch))
  1171. foreach($plugin->patches->patch as $ePatch) {
  1172. $patch_version = (string) $ePatch['version'];
  1173. $patch_revision = (string) $ePatch['revision'];
  1174. $patch_file = (string) $ePatch['file'];
  1175. $manifest->manifest_cache['patches'][] = array(
  1176. 'version' => $patch_version,
  1177. 'revision' => $patch_revision,
  1178. 'file' => $patch_file,
  1179. );
  1180. }
  1181. }
  1182. // Templates
  1183. if(isset($plugin->templates)) {
  1184. foreach($plugin->templates as $eTemplates) {
  1185. $template_set = (string) $eTemplates['set'];
  1186. if(isset($eTemplates->template))
  1187. foreach($eTemplates->template as $eTemplate) {
  1188. $manifest->manifest_cache['templates'][] = array(
  1189. 'plugin_id' => $manifest->id,
  1190. 'set' => $template_set,
  1191. 'path' => (string) $eTemplate['path'],
  1192. );
  1193. }
  1194. }
  1195. }
  1196. // Image
  1197. if(isset($plugin->image)) {
  1198. $manifest->manifest_cache['plugin_image'] = (string) $plugin->image;
  1199. }
  1200. if(!$persist)
  1201. return $manifest;
  1202. $db = DevblocksPlatform::getDatabaseService();
  1203. if(is_null($db))
  1204. return;
  1205. // Persist manifest
  1206. if($db->GetOne(sprintf("SELECT id FROM ${prefix}plugin WHERE id = %s", $db->qstr($manifest->id)))) { // update
  1207. $db->Execute(sprintf(
  1208. "UPDATE ${prefix}plugin ".
  1209. "SET name=%s,description=%s,author=%s,revision=%s,link=%s,dir=%s,manifest_cache_json=%s ".
  1210. "WHERE id=%s",
  1211. $db->qstr($manifest->name),
  1212. $db->qstr($manifest->description),
  1213. $db->qstr($manifest->author),
  1214. $db->qstr($manifest->revision),
  1215. $db->qstr($manifest->link),
  1216. $db->qstr($manifest->dir),
  1217. $db->qstr(json_encode($manifest->manifest_cache)),
  1218. $db->qstr($manifest->id)
  1219. ));
  1220. } else { // insert
  1221. $enabled = ('devblocks.core'==$manifest->id) ? 1 : 0;
  1222. $db->Execute(sprintf(
  1223. "INSERT INTO ${prefix}plugin (id,enabled,name,description,author,revision,link,dir,manifest_cache_json) ".
  1224. "VALUES (%s,%d,%s,%s,%s,%s,%s,%s,%s)",
  1225. $db->qstr($manifest->id),
  1226. $enabled,
  1227. $db->qstr($manifest->name),
  1228. $db->qstr($manifest->description),
  1229. $db->qstr($manifest->author),
  1230. $db->qstr($manifest->revision),
  1231. $db->qstr($manifest->link),
  1232. $db->qstr($manifest->dir),
  1233. $db->qstr(json_encode($manifest->manifest_cache))
  1234. ));
  1235. }
  1236. // Class Loader
  1237. if(isset($plugin->class_loader->file)) {
  1238. foreach($plugin->class_loader->file as $eFile) {
  1239. @$sFilePath = (string) $eFile['path'];
  1240. $manifest->class_loader[$sFilePath] = array();
  1241. if(isset($eFile->class))
  1242. foreach($eFile->class as $eClass) {
  1243. @$sClassName = (string) $eClass['name'];
  1244. $manifest->class_loader[$sFilePath][] = $sClassName;
  1245. }
  1246. }
  1247. }
  1248. // Routing
  1249. if(isset($plugin->uri_routing->uri)) {
  1250. foreach($plugin->uri_routing->uri as $eUri) {
  1251. @$sUriName = (string) $eUri['name'];
  1252. @$sController = (string) $eUri['controller'];
  1253. $manifest->uri_routing[$sUriName] = $sController;
  1254. }
  1255. }
  1256. // ACL
  1257. if(isset($plugin->acl->priv)) {
  1258. foreach($plugin->acl->priv as $ePriv) {
  1259. @$sId = (string) $ePriv['id'];
  1260. @$sLabel = (string) $ePriv['label'];
  1261. if(empty($sId) || empty($sLabel))
  1262. continue;
  1263. $priv = new DevblocksAclPrivilege();
  1264. $priv->id = $sId;
  1265. $priv->plugin_id = $manifest->id;
  1266. $priv->label = $sLabel;
  1267. $manifest->acl_privs[$priv->id] = $priv;
  1268. }
  1269. asort($manifest->acl_privs);
  1270. }
  1271. // Event points
  1272. if(isset($plugin->event_points->event)) {
  1273. foreach($plugin->event_points->event as $eEvent) {
  1274. $sId = (string) $eEvent['id'];
  1275. $sName = (string) $eEvent->name;
  1276. if(empty($sId) || empty($sName))
  1277. continue;
  1278. $point = new DevblocksEventPoint();
  1279. $point->id = $sId;
  1280. $point->plugin_id = $plugin->id;
  1281. $point->name = $sName;
  1282. $point->params = array();
  1283. if(isset($eEvent->param)) {
  1284. foreach($eEvent->param as $eParam) {
  1285. $key = (string) $eParam['key'];
  1286. $val = (string) $eParam['value'];
  1287. $point->param[$key] = $val;
  1288. }
  1289. }
  1290. $manifest->event_points[] = $point;
  1291. }
  1292. }
  1293. // Extensions
  1294. if(isset($plugin->extensions->extension)) {
  1295. foreach($plugin->extensions->extension as $eExtension) {
  1296. $sId = (string) $eExtension->id;
  1297. $sName = (string) $eExtension->name;
  1298. if(empty($sId) || empty($sName))
  1299. continue;
  1300. $extension = new DevblocksExtensionManifest();
  1301. $extension->id = $sId;
  1302. $extension->plugin_id = $manifest->id;
  1303. $extension->point = (string) $eExtension['point'];
  1304. $extension->name = $sName;
  1305. $extension->file = (string) $eExtension->class->file;
  1306. $extension->class = (string) $eExtension->class->name;
  1307. if(isset($eExtension->params->param)) {
  1308. foreach($eExtension->params->param as $eParam) {
  1309. $key = (string) $eParam['key'];
  1310. if(isset($eParam->value)) {
  1311. // [JSJ]: If there is a child of the param tag named value, then this
  1312. // param has multiple values and thus we need to grab them all.
  1313. foreach($eParam->value as $eValue) {
  1314. // [JSJ]: If there is a child named data, then this is a complex structure
  1315. if(isset($eValue->data)) {
  1316. $value = array();
  1317. foreach($eValue->data as $eData) {
  1318. $key2 = (string) $eData['key'];
  1319. if(isset($eData['value'])) {
  1320. $value[$key2] = (string) $eData['value'];
  1321. } else {
  1322. $value[$key2] = (string) $eData;
  1323. }
  1324. }
  1325. }
  1326. else {
  1327. // [JSJ]: Else, just grab the value and use it
  1328. $value = (string) $eValue;
  1329. }
  1330. $extension->params[$key][] = $value;
  1331. unset($value); // Just to be extra safe
  1332. }
  1333. }
  1334. else {
  1335. // [JSJ]: Otherwise, we grab the single value from the params value attribute.
  1336. $extension->params[$key] = (string) $eParam['value'];
  1337. }
  1338. }
  1339. }
  1340. $manifest->extensions[] = $extension;
  1341. }
  1342. }
  1343. // [JAS]: Extension caching
  1344. $new_extensions = array();
  1345. if(is_array($manifest->extensions))
  1346. foreach($manifest->extensions as $pos => $extension) { /* @var $extension DevblocksExtensionManifest */
  1347. $db->Execute(sprintf(
  1348. "REPLACE INTO ${prefix}extension (id,plugin_id,point,pos,name,file,class,params) ".
  1349. "VALUES (%s,%s,%s,%d,%s,%s,%s,%s)",
  1350. $db->qstr($extension->id),
  1351. $db->qstr($extension->plugin_id),
  1352. $db->qstr($extension->point),
  1353. $pos,
  1354. $db->qstr($extension->name),
  1355. $db->qstr($extension->file),
  1356. $db->qstr($extension->class),
  1357. $db->qstr(serialize($extension->params))
  1358. ));
  1359. $new_extensions[$extension->id] = true;
  1360. }
  1361. /*
  1362. * Compare our loaded XML manifest to the DB manifest cache and invalidate
  1363. * the cache for extensions that are no longer in the XML.
  1364. */
  1365. $sql = sprintf("SELECT id FROM %sextension WHERE plugin_id = %s",
  1366. $prefix,
  1367. $db->qstr($plugin->id)
  1368. );
  1369. $results = $db->GetArray($sql);
  1370. foreach($results as $row) {
  1371. $plugin_ext_id = $row['id'];
  1372. if(!isset($new_extensions[$plugin_ext_id]))
  1373. DAO_Platform::deleteExtension($plugin_ext_id);
  1374. }
  1375. // Class loader cache
  1376. $db->Execute(sprintf("DELETE FROM %sclass_loader WHERE plugin_id = %s",$prefix,$db->qstr($plugin->id)));
  1377. if(is_array($manifest->class_loader))
  1378. foreach($manifest->class_loader as $file_path => $classes) {
  1379. if(is_array($classes) && !empty($classes))
  1380. foreach($classes as $class)
  1381. $db->Execute(sprintf(
  1382. "REPLACE INTO ${prefix}class_loader (class,plugin_id,rel_path) ".
  1383. "VALUES (%s,%s,%s)",
  1384. $db->qstr($class),
  1385. $db->qstr($manifest->id),
  1386. $db->qstr($file_path)
  1387. ));
  1388. }
  1389. // URI routing cache
  1390. $db->Execute(sprintf("DELETE FROM %suri_routing WHERE plugin_id = %s",$prefix,$db->qstr($plugin->id)));
  1391. if(is_array($manifest->uri_routing))
  1392. foreach($manifest->uri_routing as $uri => $controller_id) {
  1393. $db->Execute(sprintf(
  1394. "REPLACE INTO ${prefix}uri_routing (uri,plugin_id,controller_id) ".
  1395. "VALUES (%s,%s,%s)",
  1396. $db->qstr($uri),
  1397. $db->qstr($manifest->id),
  1398. $db->qstr($controller_id)
  1399. ));
  1400. }
  1401. // ACL caching
  1402. $db->Execute(sprintf("DELETE FROM %sacl WHERE plugin_id = %s",$prefix,$db->qstr($plugin->id)));
  1403. if(is_array($manifest->acl_privs))
  1404. foreach($manifest->acl_privs as $priv) { /* @var $priv DevblocksAclPrivilege */
  1405. $db->Execute(sprintf(
  1406. "REPLACE INTO ${prefix}acl (id,plugin_id,label) ".
  1407. "VALUES (%s,%s,%s)",
  1408. $db->qstr($priv->id),
  1409. $db->qstr($priv->plugin_id),
  1410. $db->qstr($priv->label)
  1411. ));
  1412. }
  1413. // [JAS]: Event point caching
  1414. if(is_array($manifest->event_points))
  1415. foreach($manifest->event_points as $event) { /* @var $event DevblocksEventPoint */
  1416. $db->Execute(sprintf(
  1417. "REPLACE INTO ${prefix}event_point (id,plugin_id,name,params) ".
  1418. "VALUES (%s,%s,%s,%s)",
  1419. $db->qstr($event->id),
  1420. $db->qstr($event->plugin_id),
  1421. $db->qstr($event->name),
  1422. $db->qstr(serialize($event->params))
  1423. ));
  1424. }
  1425. return $manifest;
  1426. }
  1427. static function getWebPath() {
  1428. $location = "";
  1429. // Read the relative URL into an array
  1430. if(isset($_SERVER['HTTP_X_REWRITE_URL'])) { // IIS Rewrite
  1431. $location = $_SERVER['HTTP_X_REWRITE_URL'];
  1432. } elseif(isset($_SERVER['REQUEST_URI'])) { // Apache
  1433. $location = $_SERVER['REQUEST_URI'];
  1434. } elseif(isset($_SERVER['REDIRECT_URL'])) { // Apache mod_rewrite (breaks on CGI)
  1435. $location = $_SERVER['REDIRECT_URL'];
  1436. } elseif(isset($_SERVER['ORIG_PATH_INFO'])) { // IIS + CGI
  1437. $location = $_SERVER['ORIG_PATH_INFO'];
  1438. }
  1439. return $location;
  1440. }
  1441. /**
  1442. * Reads the HTTP Request object.
  1443. *
  1444. * @return DevblocksHttpRequest
  1445. */
  1446. static function readRequest() {
  1447. $url = DevblocksPlatform::getUrlService();
  1448. $location = self::getWebPath();
  1449. $parts = $url->parseURL($location);
  1450. // Add any query string arguments (?arg=value&arg=value)
  1451. @$query = $_SERVER['QUERY_STRING'];
  1452. $queryArgs = $url->parseQueryString($query);
  1453. if(empty($parts)) {
  1454. // Overrides (Form POST, etc.)
  1455. // Controller (GET has precedence over POST)
  1456. if(isset($_GET['c'])) {
  1457. @$uri = DevblocksPlatform::importGPC($_GET['c']); // extension
  1458. } elseif (isset($_POST['c'])) {
  1459. @$uri = DevblocksPlatform::importGPC($_POST['c']); // extension
  1460. }
  1461. if(!empty($uri)) $parts[] = DevblocksPlatform::strAlphaNum($uri);
  1462. // Action (GET has precedence over POST)
  1463. if(isset($_GET['a'])) {
  1464. @$listener = DevblocksPlatform::importGPC($_GET['a']); // listener
  1465. } elseif (isset($_POST['a'])) {
  1466. @$listener = DevblocksPlatform::importGPC($_POST['a']); // listener
  1467. }
  1468. if(!empty($listener)) $parts[] = DevblocksPlatform::strAlphaNum($listener);
  1469. }
  1470. // Controller XSS security (alphanum only)
  1471. if(isset($parts[0])) {
  1472. $parts[0] = DevblocksPlatform::strAlphaNum($parts[0]);
  1473. }
  1474. // Resource / Proxy
  1475. /*
  1476. * [TODO] Run this code through another audit. Is it worth a tiny hit per resource
  1477. * to verify the plugin matches exactly in the DB? If so, make sure we cache the
  1478. * resulting file.
  1479. *
  1480. * [TODO] Make this a controller
  1481. */
  1482. $path = $parts;
  1483. switch(array_shift($path)) {
  1484. case "resource":
  1485. $plugin_id = array_shift($path);
  1486. if(null == ($plugin = DevblocksPlatform::getPlugin($plugin_id)))
  1487. break;
  1488. $file = implode(DIRECTORY_SEPARATOR, $path); // combine path
  1489. $dir = APP_PATH . '/' . $plugin->dir . '/' . 'resources';
  1490. if(!is_dir($dir)) die(""); // basedir Security
  1491. $resource = $dir . '/' . $file;
  1492. if(0 != strstr($dir,$resource)) die("");
  1493. $ext = @array_pop(explode('.', $resource));
  1494. if(!is_file($resource) || 'php' == $ext) die(""); // extension security
  1495. // Caching
  1496. switch($ext) {
  1497. case 'css':
  1498. case 'gif':
  1499. case 'jpg':
  1500. case 'js':
  1501. case 'png':
  1502. header('Cache-control: max-age=604800', true); // 1 wk // , must-revalidate
  1503. header('Expires: ' . gmdate('D, d M Y H:i:s',time()+604800) . ' GMT'); // 1 wk
  1504. break;
  1505. }
  1506. switch($ext) {
  1507. case 'css':
  1508. header('Content-type: text/css;');
  1509. break;
  1510. case 'gif':
  1511. header('Content-type: image/gif;');
  1512. break;
  1513. case 'jpeg':
  1514. case 'jpg':
  1515. header('Content-type: image/jpeg;');
  1516. break;
  1517. case 'js':
  1518. header('Content-type: text/javascript;');
  1519. break;
  1520. case 'png':
  1521. header('Content-type: image/png;');
  1522. break;
  1523. case 'xml':
  1524. header('Content-type: text/xml;');
  1525. break;
  1526. }
  1527. $out = file_get_contents($resource, false);
  1528. // Pass through
  1529. if($out) {
  1530. header('Content-Length: '. strlen($out));
  1531. echo $out;
  1532. }
  1533. exit;
  1534. break;
  1535. default:
  1536. break;

Large files files are truncated, but you can click here to view the full file