PageRenderTime 71ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/Devblocks.class.php

https://github.com/rmiddle/devblocks
PHP | 5093 lines | 3748 code | 745 blank | 600 comment | 456 complexity | 4899a3fbd1abe25fcd0f0adcaa994fe3 MD5 | raw 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;
  1537. }
  1538. $request = new DevblocksHttpRequest($parts,$queryArgs);
  1539. DevblocksPlatform::setHttpRequest($request);
  1540. return $request;
  1541. }
  1542. /**
  1543. * Processes the HTTP request.
  1544. *
  1545. * @param DevblocksHttpRequest $request
  1546. * @param boolean $is_ajax
  1547. */
  1548. static function processRequest(DevblocksHttpRequest $request, $is_ajax=false) {
  1549. $path = $request->path;
  1550. $controller_uri = array_shift($path);
  1551. // [JAS]: Offer the platform a chance to intercept.
  1552. switch($controller_uri) {
  1553. // [JAS]: Plugin-supplied URIs
  1554. default:
  1555. $routing = array();
  1556. $controllers = DevblocksPlatform::getExtensions('devblocks.controller', false);
  1557. // Add any controllers which have definitive routing
  1558. if(is_array($controllers))
  1559. foreach($controllers as $controller_mft) {
  1560. if(isset($controller_mft->params['uri']))
  1561. $routing[$controller_mft->params['uri']] = $controller_mft->id;
  1562. }
  1563. // [TODO] Ask the platform to look at any routing maps (extension manifest) or
  1564. // controller objects
  1565. // print_r($routing);
  1566. // [TODO] Pages like 'tickets' currently work because APP_DEFAULT_CONTROLLER
  1567. // is the ChPageController which looks up those URIs in manifests
  1568. if(empty($controllers))
  1569. die("No controllers are available!");
  1570. // Set our controller based on the results
  1571. $controller_mft = (isset($routing[$controller_uri]))
  1572. ? $controllers[$routing[$controller_uri]]
  1573. : $controllers[APP_DEFAULT_CONTROLLER];
  1574. // Instance our manifest
  1575. if(!empty($controller_mft)) {
  1576. $controller = $controller_mft->createInstance();
  1577. }
  1578. if($controller instanceof DevblocksHttpRequestHandler) {
  1579. $controller->handleRequest($request);
  1580. // [JAS]: If we didn't write a new response, repeat the request
  1581. if(null == ($response = DevblocksPlatform::getHttpResponse())) {
  1582. $response = new DevblocksHttpResponse($request->path);
  1583. DevblocksPlatform::setHttpResponse($response);
  1584. }
  1585. // [JAS]: An Ajax request doesn't need the full Http cycle
  1586. if(!$is_ajax) {
  1587. $controller->writeResponse($response);
  1588. }
  1589. } else {
  1590. header("Status: 404");
  1591. die(); // [TODO] Improve
  1592. }
  1593. break;
  1594. }
  1595. return;
  1596. }
  1597. static function update() {
  1598. if(null == ($manifest = self::_readPluginManifest('libs/devblocks', false)))
  1599. return FALSE;
  1600. if(!isset($manifest->manifest_cache['patches']))
  1601. return TRUE;
  1602. foreach($manifest->manifest_cache['patches'] as $mft_patch) {
  1603. $path = APP_PATH . '/' . $manifest->dir . '/' . $mft_patch['file'];
  1604. if(!file_exists($path))
  1605. return FALSE;
  1606. $patch = new DevblocksPatch($manifest->id, $mft_patch['version'], $mft_patch['revision'], $path);
  1607. if(!$patch->run())
  1608. return FALSE;
  1609. }
  1610. return TRUE;
  1611. }
  1612. };
  1613. class _DevblocksPluginSettingsManager {
  1614. private static $_instance = null;
  1615. private $_settings = array();
  1616. /**
  1617. * @return _DevblocksPluginSettingsManager
  1618. */
  1619. private function __construct() {
  1620. // Defaults (dynamic)
  1621. $plugin_settings = DAO_DevblocksSetting::getSettings();
  1622. foreach($plugin_settings as $plugin_id => $kv) {
  1623. if(!isset($this->_settings[$plugin_id]))
  1624. $this->_settings[$plugin_id] = array();
  1625. if(is_array($kv))
  1626. foreach($kv as $k => $v)
  1627. $this->_settings[$plugin_id][$k] = $v;
  1628. }
  1629. }
  1630. /**
  1631. * @return _DevblocksPluginSettingsManager
  1632. */
  1633. public static function getInstance() {
  1634. if(self::$_instance==null) {
  1635. self::$_instance = new _DevblocksPluginSettingsManager();
  1636. }
  1637. return self::$_instance;
  1638. }
  1639. public function set($plugin_id,$key,$value) {
  1640. DAO_DevblocksSetting::set($plugin_id,$key,$value);
  1641. if(!isset($this->_settings[$plugin_id]))
  1642. $this->_settings[$plugin_id] = array();
  1643. $this->_settings[$plugin_id][$key] = $value;
  1644. $cache = DevblocksPlatform::getCacheService();
  1645. $cache->remove(DevblocksPlatform::CACHE_SETTINGS);
  1646. return TRUE;
  1647. }
  1648. /**
  1649. * @param string $key
  1650. * @param string $default
  1651. * @return mixed
  1652. */
  1653. public function get($plugin_id,$key,$default=null) {
  1654. if(isset($this->_settings[$plugin_id][$key]))
  1655. return $this->_settings[$plugin_id][$key];
  1656. else
  1657. return $default;
  1658. }
  1659. };
  1660. /**
  1661. * Session Management Singleton
  1662. *
  1663. * @static
  1664. * @ingroup services
  1665. */
  1666. class _DevblocksSessionManager {
  1667. var $visit = null;
  1668. /**
  1669. * @private
  1670. */
  1671. private function _DevblocksSessionManager() {}
  1672. /**
  1673. * Returns an instance of the session manager
  1674. *
  1675. * @static
  1676. * @return _DevblocksSessionManager
  1677. */
  1678. static function getInstance() {
  1679. static $instance = null;
  1680. if(null == $instance) {
  1681. $db = DevblocksPlatform::getDatabaseService();
  1682. $url_writer = DevblocksPlatform::getUrlService();
  1683. if(is_null($db) || !$db->isConnected()) {
  1684. return null;
  1685. }
  1686. $prefix = (APP_DB_PREFIX != '') ? APP_DB_PREFIX.'_' : ''; // [TODO] Cleanup
  1687. @session_destroy();
  1688. $handler = '_DevblocksSessionDatabaseDriver';
  1689. session_set_save_handler(
  1690. array($handler, 'open'),
  1691. array($handler, 'close'),
  1692. array($handler, 'read'),
  1693. array($handler, 'write'),
  1694. array($handler, 'destroy'),
  1695. array($handler, 'gc')
  1696. );
  1697. session_name(APP_SESSION_NAME);
  1698. session_set_cookie_params(0, '/', NULL, $url_writer->isSSL(), true);
  1699. session_start();
  1700. $instance = new _DevblocksSessionManager();
  1701. $instance->visit = isset($_SESSION['db_visit']) ? $_SESSION['db_visit'] : NULL; /* @var $visit DevblocksVisit */
  1702. }
  1703. return $instance;
  1704. }
  1705. function decodeSession($data) {
  1706. $vars=preg_split(
  1707. '/([a-zA-Z_\.\x7f-\xff][a-zA-Z0-9_\.\x7f-\xff^|]*)\|/',
  1708. $data,
  1709. -1,
  1710. PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE
  1711. );
  1712. $scope = array();
  1713. while(!empty($vars)) {
  1714. @$key = array_shift($vars);
  1715. @$value = unserialize(array_shift($vars));
  1716. $scope[$key] = $value;
  1717. }
  1718. return $scope;
  1719. }
  1720. /**
  1721. * Returns the current session or NULL if no session exists.
  1722. *
  1723. * @return DevblocksVisit
  1724. */
  1725. function getVisit() {
  1726. return $this->visit;
  1727. }
  1728. /**
  1729. * @param DevblocksVisit $visit
  1730. */
  1731. function setVisit(DevblocksVisit $visit = null) {
  1732. $this->visit = $visit;
  1733. $_SESSION['db_visit'] = $this->visit;
  1734. }
  1735. function getAll() {
  1736. return _DevblocksSessionDatabaseDriver::getAll();
  1737. }
  1738. /**
  1739. * Kills the specified or current session.
  1740. *
  1741. */
  1742. function clear($key=null) {
  1743. if(is_null($key)) {
  1744. $this->visit = null;
  1745. unset($_SESSION['db_visit']);
  1746. session_destroy();
  1747. } else {
  1748. _DevblocksSessionDatabaseDriver::destroy($key);
  1749. }
  1750. }
  1751. function clearAll() {
  1752. self::clear();
  1753. // [TODO] Allow subclasses to be cleared here too
  1754. _DevblocksSessionDatabaseDriver::destroyAll();
  1755. }
  1756. };
  1757. class _DevblocksSessionDatabaseDriver {
  1758. static $_data = null;
  1759. static function open($save_path, $session_name) {
  1760. return true;
  1761. }
  1762. static function close() {
  1763. return true;
  1764. }
  1765. static function read($id) {
  1766. $db = DevblocksPlatform::getDatabaseService();
  1767. if(null != (self::$_data = $db->GetOne(sprintf("SELECT session_data FROM devblocks_session WHERE session_key = %s", $db->qstr($id)))))
  1768. return self::$_data;
  1769. return false;
  1770. }
  1771. static function write($id, $session_data) {
  1772. // Nothing changed!
  1773. if(self::$_data==$session_data) {
  1774. return true;
  1775. }
  1776. $db = DevblocksPlatform::getDatabaseService();
  1777. // Update
  1778. $result = $db->Execute(sprintf("UPDATE devblocks_session SET updated=%d, session_data=%s WHERE session_key=%s",
  1779. time(),
  1780. $db->qstr($session_data),
  1781. $db->qstr($id)
  1782. ));
  1783. if(0==$db->Affected_Rows()) {
  1784. // Insert
  1785. $db->Execute(sprintf("INSERT INTO devblocks_session (session_key, created, updated, session_data) ".
  1786. "VALUES (%s, %d, %d, %s)",
  1787. $db->qstr($id),
  1788. time(),
  1789. time(),
  1790. $db->qstr($session_data)
  1791. ));
  1792. }
  1793. return true;
  1794. }
  1795. static function destroy($id) {
  1796. $db = DevblocksPlatform::getDatabaseService();
  1797. $db->Execute(sprintf("DELETE FROM devblocks_session WHERE session_key = %s", $db->qstr($id)));
  1798. return true;
  1799. }
  1800. static function gc($maxlifetime) {
  1801. $db = DevblocksPlatform::getDatabaseService();
  1802. $db->Execute(sprintf("DELETE FROM devblocks_session WHERE updated + %d < %d", $maxlifetime, time()));
  1803. return true;
  1804. }
  1805. static function getAll() {
  1806. $db = DevblocksPlatform::getDatabaseService();
  1807. return $db->GetArray("SELECT session_key, created, updated, session_data FROM devblocks_session");
  1808. }
  1809. static function destroyAll() {
  1810. $db = DevblocksPlatform::getDatabaseService();
  1811. $db->Execute("DELETE FROM devblocks_session");
  1812. }
  1813. };
  1814. class _DevblocksOpenIDManager {
  1815. private static $instance = null;
  1816. public static function getInstance() {
  1817. if(null == self::$instance) {
  1818. self::$instance = new _DevblocksOpenIDManager();
  1819. }
  1820. return self::$instance;
  1821. }
  1822. public function discover($url) {
  1823. $num_redirects = 0;
  1824. $is_safemode = !(ini_get('open_basedir') == '' && ini_get('safe_mode' == 'Off'));
  1825. do {
  1826. $repeat = false;
  1827. $ch = curl_init();
  1828. curl_setopt($ch, CURLOPT_URL, $url);
  1829. curl_setopt($ch, CURLOPT_HEADER, 1);
  1830. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  1831. // We can't use option this w/ safemode enabled
  1832. if(!$is_safemode)
  1833. curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
  1834. $content = curl_exec($ch);
  1835. $info = curl_getinfo($ch);
  1836. curl_close($ch);
  1837. $lines = explode("\n", $content);
  1838. $headers = array();
  1839. $content = '';
  1840. $is_headers = true;
  1841. while($line = array_shift($lines)) {
  1842. if($is_headers && $line == "\r") {
  1843. // Is the next line another headers block?
  1844. $line = array_shift($lines);
  1845. if(preg_match("/^HTTP\/\S+ \d+/i", $line)) {
  1846. $headers = array(); // flush
  1847. } else {
  1848. $is_headers = false;
  1849. array_unshift($lines, $line);
  1850. continue;
  1851. }
  1852. }
  1853. if($is_headers) {
  1854. $headers[] = $line;
  1855. } else {
  1856. // Everything else
  1857. $content = $line . "\n" . implode("\n", $lines);
  1858. $lines = array();
  1859. }
  1860. }
  1861. unset($lines);
  1862. // Scan headers
  1863. foreach($headers as $header) {
  1864. // Safemode specific behavior
  1865. if($is_safemode) {
  1866. if(preg_match("/^Location:.*?/i", $header)) {
  1867. $out = explode(':', $header, 2);
  1868. $url = isset($out[1]) ? trim($out[1]) : null;
  1869. $repeat = true;
  1870. break;
  1871. }
  1872. }
  1873. // Check the headers for an 'X-XRDS-Location'
  1874. if(preg_match("/^X-XRDS-Location:.*?/i", $header)) {
  1875. $out = explode(':', $header, 2);
  1876. $xrds_url = isset($out[1]) ? trim($out[1]) : null;
  1877. // We have a redirect header on an XRDS document
  1878. if(0 == strcasecmp($xrds_url, $url)) {
  1879. $repeat = false;
  1880. // We're being redirected
  1881. } else {
  1882. $repeat = true;
  1883. $headers = array();
  1884. $url = $xrds_url;
  1885. }
  1886. break;
  1887. }
  1888. }
  1889. } while($repeat || ++$num_redirects > 10);
  1890. if(isset($info['content_type'])) {
  1891. $result = explode(';', $info['content_type']);
  1892. $type = isset($result[0]) ? trim($result[0]) : null;
  1893. $server = null;
  1894. switch($type) {
  1895. case 'application/xrds+xml':
  1896. $xml = simplexml_load_string($content);
  1897. foreach($xml->XRD->Service as $service) {
  1898. $types = array();
  1899. foreach($service->Type as $type) {
  1900. $types[] = $type;
  1901. }
  1902. // [TODO] OpenID 1.0
  1903. if(false !== ($pos = array_search('http://specs.openid.net/auth/2.0/server', $types))) {
  1904. $server = $service->URI;
  1905. } elseif(false !== ($pos = array_search('http://specs.openid.net/auth/2.0/signon', $types))) {
  1906. $server = $service->URI;
  1907. }
  1908. }
  1909. break;
  1910. case 'text/html':
  1911. // [TODO] This really needs to parse syntax better (can be single or double quotes, and attribs in any order)
  1912. preg_match("/<link rel=\"openid.server\" href=\"(.*?)\"/", $content, $found);
  1913. if($found && isset($found[1]))
  1914. $server = $found[1];
  1915. preg_match("/<link rel=\"openid.delegate\" href=\"(.*?)\"/", $content, $found);
  1916. if($found && isset($found[1]))
  1917. $delegate = $found[1];
  1918. break;
  1919. default:
  1920. break;
  1921. }
  1922. return $server;
  1923. }
  1924. }
  1925. public function getAuthUrl($openid_identifier, $return_to) {
  1926. $url_writer = DevblocksPlatform::getUrlService();
  1927. // Normalize the URL
  1928. $parts = parse_url($openid_identifier);
  1929. if(!isset($parts['scheme'])) {
  1930. $openid_identifier = 'http://' . $openid_identifier;
  1931. }
  1932. $server = $this->discover($openid_identifier);
  1933. if(empty($server))
  1934. return FALSE;
  1935. $parts = explode('?', $server, 2);
  1936. $url = isset($parts[0]) ? $parts[0] : '';
  1937. $query = isset($parts[1]) ? ('?'.$parts[1]) : '';
  1938. $query .= (!empty($query)) ? '&' : '?';
  1939. $query .= "openid.mode=checkid_setup";
  1940. $query .= "&openid.claimed_id=".urlencode("http://specs.openid.net/auth/2.0/identifier_select");
  1941. $query .= "&openid.identity=".urlencode("http://specs.openid.net/auth/2.0/identifier_select");
  1942. $query .= "&openid.realm=".urlencode($url_writer->write('',true));
  1943. $query .= "&openid.ns=".urlencode("http://specs.openid.net/auth/2.0");
  1944. $query .= "&openid.return_to=".urlencode($return_to);
  1945. // AX 1.0 (axschema)
  1946. $query .= "&openid.ns.ax=".urlencode("http://openid.net/srv/ax/1.0");
  1947. $query .= "&openid.ax.mode=".urlencode("fetch_request");
  1948. $query .= "&openid.ax.type.nickname=".urlencode('http://axschema.org/namePerson/friendly');
  1949. $query .= "&openid.ax.type.fullname=".urlencode('http://axschema.org/namePerson');
  1950. $query .= "&openid.ax.type.email=".urlencode('http://axschema.org/contact/email');
  1951. $query .= "&openid.ax.required=".urlencode('email,nickname,fullname');
  1952. // SREG 1.1
  1953. $query .= "&openid.ns.sreg=".urlencode('http://openid.net/extensions/sreg/1.1');
  1954. $query .= "&openid.sreg.required=".urlencode("nickname,fullname,email");
  1955. $query .= "&openid.sreg.optional=".urlencode("dob,gender,postcode,country,language,timezone");
  1956. return $url.$query;
  1957. }
  1958. public function validate($scope) {
  1959. $url_writer = DevblocksPlatform::getUrlService();
  1960. $openid_identifier = $scope['openid_identity'];
  1961. $server = $this->discover($openid_identifier);
  1962. $parts = explode('?', $server, 2);
  1963. $url = isset($parts[0]) ? $parts[0] : '';
  1964. $query = isset($parts[1]) ? ('?'.$parts[1]) : '';
  1965. $query .= (!empty($query)) ? '&' : '?';
  1966. $query .= "openid.ns=".urlencode("http://specs.openid.net/auth/2.0");
  1967. $query .= "&openid.mode=check_authentication";
  1968. $query .= "&openid.sig=".urlencode($_GET['openid_sig']);
  1969. $query .= "&openid.signed=".urlencode($_GET['openid_signed']);
  1970. // Append all the tokens used in the signed
  1971. $tokens = explode(',', $scope['openid_signed']);
  1972. foreach($tokens as $token) {
  1973. switch($token) {
  1974. case 'mode':
  1975. case 'ns':
  1976. case 'sig':
  1977. case 'signed':
  1978. break;
  1979. default:
  1980. $key = str_replace('.', '_', $token);
  1981. if(isset($scope['openid_'.$key])) {
  1982. $query .= sprintf("&openid.%s=%s",
  1983. $token,
  1984. urlencode($scope['openid_'.$key])
  1985. );
  1986. }
  1987. break;
  1988. }
  1989. }
  1990. $ch = curl_init();
  1991. curl_setopt($ch, CURLOPT_URL, $url.$query);
  1992. curl_setopt($ch, CURLOPT_HEADER, 0);
  1993. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  1994. $response = curl_exec($ch);
  1995. curl_close($ch);
  1996. if(preg_match('/is_valid:true/', $response))
  1997. return true;
  1998. else
  1999. return false;
  2000. }
  2001. public function getAttributes($scope) {
  2002. $ns = array();
  2003. $attribs = array();
  2004. foreach($scope as $ns => $spec) {
  2005. // Namespaces
  2006. if(preg_match("/^openid_ns_(.*)$/",$ns,$ns_found)) {
  2007. switch(strtolower($spec)) {
  2008. case 'http://openid.net/srv/ax/1.0';
  2009. foreach($scope as $k => $v) {
  2010. if(preg_match("/^openid_".$ns_found[1]."_value_(.*)$/i",$k,$attrib_found)) {
  2011. $attribs[strtolower($attrib_found[1])] = $v;
  2012. }
  2013. }
  2014. break;
  2015. case 'http://openid.net/srv/sreg/1.0';
  2016. case 'http://openid.net/extensions/sreg/1.1';
  2017. foreach($scope as $k => $v) {
  2018. if(preg_match("/^openid_".$ns_found[1]."_(.*)$/i",$k,$attrib_found)) {
  2019. $attribs[strtolower($attrib_found[1])] = $v;
  2020. }
  2021. }
  2022. break;
  2023. }
  2024. }
  2025. }
  2026. return $attribs;
  2027. }
  2028. };
  2029. class _DevblocksCacheManager {
  2030. private static $instance = null;
  2031. private static $_cacher = null;
  2032. private $_registry = array();
  2033. private $_statistics = array();
  2034. private $_io_reads_long = 0;
  2035. private $_io_reads_short = 0;
  2036. private $_io_writes = 0;
  2037. private function __construct() {}
  2038. /**
  2039. * @return _DevblocksCacheManager
  2040. */
  2041. public static function getInstance() {
  2042. if(null == self::$instance) {
  2043. self::$instance = new _DevblocksCacheManager();
  2044. $options = array(
  2045. 'key_prefix' => ((defined('DEVBLOCKS_CACHE_PREFIX') && DEVBLOCKS_CACHE_PREFIX) ? DEVBLOCKS_CACHE_PREFIX : null),
  2046. );
  2047. // Shared-memory cache
  2048. if((extension_loaded('memcache') || extension_loaded('memcached'))
  2049. && defined('DEVBLOCKS_MEMCACHED_SERVERS') && DEVBLOCKS_MEMCACHED_SERVERS) {
  2050. $pairs = DevblocksPlatform::parseCsvString(DEVBLOCKS_MEMCACHED_SERVERS);
  2051. $servers = array();
  2052. if(is_array($pairs) && !empty($pairs))
  2053. foreach($pairs as $server) {
  2054. list($host,$port) = explode(':',$server);
  2055. if(empty($host) || empty($port))
  2056. continue;
  2057. $servers[] = array(
  2058. 'host'=>$host,
  2059. 'port'=>$port,
  2060. // 'persistent'=>true
  2061. );
  2062. }
  2063. $options['servers'] = $servers;
  2064. self::$_cacher = new _DevblocksCacheManagerMemcached($options);
  2065. }
  2066. // Disk-based cache (default)
  2067. if(null == self::$_cacher) {
  2068. $options['cache_dir'] = APP_TEMP_PATH;
  2069. self::$_cacher = new _DevblocksCacheManagerDisk($options);
  2070. }
  2071. }
  2072. return self::$instance;
  2073. }
  2074. public function save($data, $key, $tags=array(), $lifetime=0) {
  2075. // Monitor short-term cache memory usage
  2076. @$this->_statistics[$key] = intval($this->_statistics[$key]);
  2077. $this->_io_writes++;
  2078. self::$_cacher->save($data, $key, $tags, $lifetime);
  2079. $this->_registry[$key] = $data;
  2080. }
  2081. public function load($key, $nocache=false) {
  2082. // Retrieving the long-term cache
  2083. if($nocache || !isset($this->_registry[$key])) {
  2084. if(false === ($this->_registry[$key] = self::$_cacher->load($key)))
  2085. return NULL;
  2086. @$this->_statistics[$key] = intval($this->_statistics[$key]) + 1;
  2087. $this->_io_reads_long++;
  2088. return $this->_registry[$key];
  2089. }
  2090. // Retrieving the short-term cache
  2091. if(isset($this->_registry[$key])) {
  2092. @$this->_statistics[$key] = intval($this->_statistics[$key]) + 1;
  2093. $this->_io_reads_short++;
  2094. return $this->_registry[$key];
  2095. }
  2096. return NULL;
  2097. }
  2098. public function remove($key) {
  2099. unset($this->_registry[$key]);
  2100. unset($this->_statistics[$key]);
  2101. self::$_cacher->remove($key);
  2102. }
  2103. public function clean() { // $mode=null
  2104. $this->_registry = array();
  2105. $this->_statistics = array();
  2106. self::$_cacher->clean();
  2107. }
  2108. public function printStatistics() {
  2109. arsort($this->_statistics);
  2110. print_r($this->_statistics);
  2111. echo "<BR>";
  2112. echo "Reads (short): ",$this->_io_reads_short,"<BR>";
  2113. echo "Reads (long): ",$this->_io_reads_long,"<BR>";
  2114. echo "Writes: ",$this->_io_writes,"<BR>";
  2115. }
  2116. };
  2117. abstract class _DevblocksCacheManagerAbstract {
  2118. protected $_options;
  2119. protected $_prefix = 'devblocks_cache---';
  2120. function __construct($options) {
  2121. if(is_array($options))
  2122. $this->_options = $options;
  2123. // Key prefix
  2124. if(!isset($this->_options['key_prefix']))
  2125. $this->_options['key_prefix'] = '';
  2126. }
  2127. function save($data, $key, $tags=array(), $lifetime=0) {}
  2128. function load($key) {}
  2129. function remove($key) {}
  2130. function clean() {} // $mode=null
  2131. };
  2132. class _DevblocksCacheManagerMemcached extends _DevblocksCacheManagerAbstract {
  2133. private $_driver;
  2134. function __construct($options) {
  2135. parent::__construct($options);
  2136. if(extension_loaded('memcached'))
  2137. $this->_driver = new Memcached();
  2138. elseif(extension_loaded('memcache'))
  2139. $this->_driver = new Memcache();
  2140. else
  2141. die("PECL/Memcache or PECL/Memcached is not loaded.");
  2142. // Check servers option
  2143. if(!isset($this->_options['servers']) || !is_array($this->_options['servers']))
  2144. die("_DevblocksCacheManagerMemcached requires the 'servers' option.");
  2145. if(is_array($this->_options['servers']))
  2146. foreach($this->_options['servers'] as $params) {
  2147. $this->_driver->addServer($params['host'], $params['port']);
  2148. }
  2149. }
  2150. function save($data, $key, $tags=array(), $lifetime=0) {
  2151. $key = $this->_options['key_prefix'] . $key;
  2152. if($this->_driver instanceof Memcached)
  2153. return $this->_driver->set($key, $data, $lifetime);
  2154. else
  2155. return $this->_driver->set($key, $data, 0, $lifetime);
  2156. }
  2157. function load($key) {
  2158. $key = $this->_options['key_prefix'] . $key;
  2159. return $this->_driver->get($key);
  2160. }
  2161. function remove($key) {
  2162. $key = $this->_options['key_prefix'] . $key;
  2163. $this->_driver->delete($key);
  2164. }
  2165. function clean() {
  2166. $this->_driver->flush();
  2167. }
  2168. };
  2169. class _DevblocksCacheManagerDisk extends _DevblocksCacheManagerAbstract {
  2170. function __construct($options) {
  2171. parent::__construct($options);
  2172. $path = $this->_getPath();
  2173. if(null == $path)
  2174. die("_DevblocksCacheManagerDisk requires the 'cache_dir' option.");
  2175. // Ensure we have a trailing slash
  2176. $this->_options['cache_dir'] = rtrim($path,"\\/") . DIRECTORY_SEPARATOR;
  2177. if(!is_writeable($path))
  2178. die("_DevblocksCacheManagerDisk requires write access to the 'path' directory ($path)");
  2179. }
  2180. private function _getPath() {
  2181. return $this->_options['cache_dir'];
  2182. }
  2183. private function _getFilename($key) {
  2184. $safe_key = preg_replace("/[^A-Za-z0-9_\-]/",'_', $key);
  2185. return $this->_prefix . $safe_key;
  2186. }
  2187. function load($key) {
  2188. $key = $this->_options['key_prefix'] . $key;
  2189. return @unserialize(file_get_contents($this->_getPath() . $this->_getFilename($key)));
  2190. }
  2191. function save($data, $key, $tags=array(), $lifetime=0) {
  2192. $key = $this->_options['key_prefix'] . $key;
  2193. return file_put_contents($this->_getPath() . $this->_getFilename($key), serialize($data));
  2194. }
  2195. function remove($key) {
  2196. $key = $this->_options['key_prefix'] . $key;
  2197. $file = $this->_getPath() . $this->_getFilename($key);
  2198. if(file_exists($file))
  2199. unlink($file);
  2200. }
  2201. function clean() {
  2202. $path = $this->_getPath();
  2203. $files = scandir($path);
  2204. unset($files['.']);
  2205. unset($files['..']);
  2206. if(is_array($files))
  2207. foreach($files as $file) {
  2208. if(0==strcmp('devblocks_cache',substr($file,0,15))) {
  2209. unlink($path . $file);
  2210. }
  2211. }
  2212. }
  2213. };
  2214. class _DevblocksStorageManager {
  2215. static $_connections = array();
  2216. /**
  2217. *
  2218. * @param string $extension_id
  2219. * @param array $options
  2220. * @return Extension_DevblocksStorageEngine
  2221. */
  2222. static public function getEngine($extension_id, $options=array()) {
  2223. $hash = sha1($extension_id.json_encode($options));
  2224. if(isset(self::$_connections[$hash])) {
  2225. return self::$_connections[$hash];
  2226. }
  2227. if(null !== ($engine = DevblocksPlatform::getExtension($extension_id, true, true))) {
  2228. /* @var $engine Extension_DevblocksStorageEngine */
  2229. $engine->setOptions($options);
  2230. self::$_connections[$hash] = $engine;
  2231. return self::$_connections[$hash];
  2232. }
  2233. return false;
  2234. }
  2235. };
  2236. class _DevblocksSearchManager {
  2237. static $_instance = null;
  2238. /**
  2239. * @return _DevblocksSearchEngineMysqlFulltext
  2240. */
  2241. static public function getInstance() {
  2242. if(null == self::$_instance) {
  2243. self::$_instance = new _DevblocksSearchEngineMysqlFulltext();
  2244. return self::$_instance;
  2245. }
  2246. return self::$_instance;
  2247. }
  2248. };
  2249. class _DevblocksSearchEngineMysqlFulltext {
  2250. private $_db = null;
  2251. public function __construct() {
  2252. $db = DevblocksPlatform::getDatabaseService();
  2253. $this->_db = $db->getConnection();
  2254. }
  2255. protected function escapeNamespace($namespace) {
  2256. return strtolower(DevblocksPlatform::strAlphaNumUnder($namespace));
  2257. }
  2258. public function query($ns, $query, $limit=25, $boolean_mode=true) {
  2259. $escaped_query = mysql_real_escape_string($query);
  2260. // [TODO] Process the query
  2261. if(!$boolean_mode) {
  2262. $result = mysql_query(sprintf("SELECT id ".
  2263. "FROM fulltext_%s ".
  2264. "WHERE MATCH content AGAINST ('%s') ".
  2265. "LIMIT 0,%d ",
  2266. $this->escapeNamespace($ns),
  2267. $escaped_query,
  2268. $limit
  2269. ), $this->_db);
  2270. } else {
  2271. $result = mysql_query(sprintf("SELECT id, MATCH content AGAINST ('%s' IN BOOLEAN MODE) AS score ".
  2272. "FROM fulltext_%s ".
  2273. "WHERE MATCH content AGAINST ('%s' IN BOOLEAN MODE) ".
  2274. "ORDER BY score DESC ".
  2275. "LIMIT 0,%d ",
  2276. $escaped_query,
  2277. $this->escapeNamespace($ns),
  2278. $escaped_query,
  2279. $limit
  2280. ), $this->_db);
  2281. }
  2282. if(false == $result)
  2283. return false;
  2284. $ids = array();
  2285. while($row = mysql_fetch_row($result)) {
  2286. $ids[] = $row[0];
  2287. }
  2288. return $ids;
  2289. }
  2290. private function _getStopWords() {
  2291. // English
  2292. $words = array(
  2293. '' => true,
  2294. 'a' => true,
  2295. 'about' => true,
  2296. 'all' => true,
  2297. 'am' => true,
  2298. 'an' => true,
  2299. 'and' => true,
  2300. 'any' => true,
  2301. 'as' => true,
  2302. 'at' => true,
  2303. 'are' => true,
  2304. 'be' => true,
  2305. 'been' => true,
  2306. 'but' => true,
  2307. 'by' => true,
  2308. 'can' => true,
  2309. 'could' => true,
  2310. 'did' => true,
  2311. 'do' => true,
  2312. 'doesn\'t' => true,
  2313. 'don\'t' => true,
  2314. 'e.g.' => true,
  2315. 'eg' => true,
  2316. 'for' => true,
  2317. 'from' => true,
  2318. 'get' => true,
  2319. 'had' => true,
  2320. 'has' => true,
  2321. 'have' => true,
  2322. 'hello' => true,
  2323. 'hi' => true,
  2324. 'how' => true,
  2325. 'i' => true,
  2326. 'i.e.' => true,
  2327. 'ie' => true,
  2328. 'i\'m' => true,
  2329. 'if' => true,
  2330. 'in' => true,
  2331. 'into' => true,
  2332. 'is' => true,
  2333. 'it' => true,
  2334. 'it\'s' => true,
  2335. 'its' => true,
  2336. 'may' => true,
  2337. 'me' => true,
  2338. 'my' => true,
  2339. 'not' => true,
  2340. 'of' => true,
  2341. 'on' => true,
  2342. 'or' => true,
  2343. 'our' => true,
  2344. 'out' => true,
  2345. 'please' => true,
  2346. 'p.s.' => true,
  2347. 'ps' => true,
  2348. 'so' => true,
  2349. 'than' => true,
  2350. 'thank' => true,
  2351. 'thanks' => true,
  2352. 'that' => true,
  2353. 'the' => true,
  2354. 'their' => true,
  2355. 'them' => true,
  2356. 'then' => true,
  2357. 'there' => true,
  2358. 'these' => true,
  2359. 'they' => true,
  2360. 'this' => true,
  2361. 'those' => true,
  2362. 'to' => true,
  2363. 'us' => true,
  2364. 'want' => true,
  2365. 'was' => true,
  2366. 'we' => true,
  2367. 'were' => true,
  2368. 'what' => true,
  2369. 'when' => true,
  2370. 'which' => true,
  2371. 'while' => true,
  2372. 'why' => true,
  2373. 'will' => true,
  2374. 'with' => true,
  2375. 'would' => true,
  2376. 'you' => true,
  2377. 'your' => true,
  2378. 'you\'re' => true,
  2379. );
  2380. return $words;
  2381. }
  2382. public function prepareText($text) {
  2383. // Encode apostrophes/etc
  2384. $tokens = array(
  2385. '__apos__' => '\''
  2386. );
  2387. $text = str_replace(array_values($tokens), array_keys($tokens), $text);
  2388. // Force lowercase and strip non-word punctuation (a-z, 0-9, _)
  2389. if(function_exists('mb_ereg_replace'))
  2390. $text = mb_ereg_replace('[^a-z0-9_]+', ' ', mb_convert_case($text, MB_CASE_LOWER));
  2391. else
  2392. $text = preg_replace('/[^a-z0-9_]+/', ' ', mb_convert_case($text, MB_CASE_LOWER));
  2393. // Decode apostrophes/etc
  2394. $text = str_replace(array_keys($tokens), array_values($tokens), $text);
  2395. $words = explode(' ', $text);
  2396. // Remove common words
  2397. $stop_words = $this->_getStopWords();
  2398. // Toss anything over/under the word length bounds
  2399. // [TODO] Make these configurable
  2400. foreach($words as $k => $v) {
  2401. //$len = mb_strlen($v);
  2402. // if($len < 3 || $len > 255) { // || is_numeric($k)
  2403. // unset($words[$k]); // toss
  2404. // } elseif(isset($stop_words[$v])) {
  2405. if(isset($stop_words[$v])) {
  2406. unset($words[$k]); // toss
  2407. }
  2408. }
  2409. $text = implode(' ', $words);
  2410. unset($words);
  2411. // Flatten multiple spaces into a single
  2412. $text = preg_replace('# +#', ' ', $text);
  2413. return $text;
  2414. }
  2415. private function _index($ns, $id, $content) {
  2416. $content = $this->prepareText($content);
  2417. $result = mysql_query(sprintf("REPLACE INTO fulltext_%s VALUES (%d, '%s') ",
  2418. $this->escapeNamespace($ns),
  2419. $id,
  2420. mysql_real_escape_string($content)
  2421. ), $this->_db);
  2422. return (false !== $result) ? true : false;
  2423. }
  2424. public function index($ns, $id, $content) {
  2425. if(false === ($ids = $this->_index($ns, $id, $content))) {
  2426. // Create the table dynamically
  2427. if($this->_createTable($ns)) {
  2428. return $this->_index($ns, $id, $content);
  2429. }
  2430. return false;
  2431. }
  2432. return true;
  2433. }
  2434. private function _createTable($namespace) {
  2435. $rs = mysql_query("SHOW TABLES", $this->_db);
  2436. $tables = array();
  2437. while($row = mysql_fetch_row($rs)) {
  2438. $tables[$row[0]] = true;
  2439. }
  2440. $namespace = $this->escapeNamespace($namespace);
  2441. if(isset($tables['fulltext_'.$namespace]))
  2442. return true;
  2443. $result = mysql_query(sprintf(
  2444. "CREATE TABLE IF NOT EXISTS fulltext_%s (
  2445. id INT UNSIGNED NOT NULL DEFAULT 0,
  2446. content LONGTEXT,
  2447. PRIMARY KEY (id),
  2448. FULLTEXT content (content)
  2449. ) ENGINE=MyISAM CHARACTER SET=utf8;", // MUST stay ENGINE=MyISAM
  2450. $this->escapeNamespace($namespace)
  2451. ), $this->_db);
  2452. DevblocksPlatform::clearCache(DevblocksPlatform::CACHE_TABLES);
  2453. return (false !== $result) ? true : false;
  2454. }
  2455. };
  2456. class _DevblocksEventManager {
  2457. private static $instance = null;
  2458. private function __construct() {}
  2459. /**
  2460. * @return _DevblocksEventManager
  2461. */
  2462. public static function getInstance() {
  2463. if(null == self::$instance) {
  2464. self::$instance = new _DevblocksEventManager();
  2465. }
  2466. return self::$instance;
  2467. }
  2468. function trigger(Model_DevblocksEvent $event) {
  2469. /*
  2470. * [TODO] Look at the hash and spawn our listeners for this particular point
  2471. */
  2472. $events = DevblocksPlatform::getEventRegistry();
  2473. if(null == ($listeners = @$events[$event->id])) {
  2474. $listeners = array();
  2475. }
  2476. // [TODO] Make sure we can't get a double listener
  2477. if(isset($events['*']) && is_array($events['*']))
  2478. foreach($events['*'] as $evt) {
  2479. $listeners[] = $evt;
  2480. }
  2481. if(is_array($listeners) && !empty($listeners))
  2482. foreach($listeners as $listener) { /* @var $listener DevblocksExtensionManifest */
  2483. // Extensions can be invoked on these plugins even by workers who cannot see them
  2484. if(null != ($manifest = DevblocksPlatform::getExtension($listener,false,true))) {
  2485. if(method_exists($manifest, 'createInstance')) {
  2486. $inst = $manifest->createInstance(); /* @var $inst DevblocksEventListenerExtension */
  2487. if($inst instanceof DevblocksEventListenerExtension)
  2488. $inst->handleEvent($event);
  2489. }
  2490. }
  2491. }
  2492. }
  2493. };
  2494. /**
  2495. * Email Management Singleton
  2496. *
  2497. * @static
  2498. * @ingroup services
  2499. */
  2500. class _DevblocksEmailManager {
  2501. private static $instance = null;
  2502. private $mailers = array();
  2503. /**
  2504. * @private
  2505. */
  2506. private function __construct() {
  2507. }
  2508. /**
  2509. * Enter description here...
  2510. *
  2511. * @return _DevblocksEmailManager
  2512. */
  2513. public static function getInstance() {
  2514. if(null == self::$instance) {
  2515. self::$instance = new _DevblocksEmailManager();
  2516. }
  2517. return self::$instance;
  2518. }
  2519. /**
  2520. * Enter description here...
  2521. *
  2522. * @return Swift_Message
  2523. */
  2524. function createMessage() {
  2525. return Swift_Message::newInstance();
  2526. }
  2527. /**
  2528. * @return Swift
  2529. */
  2530. function getMailer($options) {
  2531. // Options
  2532. $smtp_host = isset($options['host']) ? $options['host'] : '127.0.0.1';
  2533. $smtp_port = isset($options['port']) ? $options['port'] : '25';
  2534. $smtp_user = isset($options['auth_user']) ? $options['auth_user'] : null;
  2535. $smtp_pass = isset($options['auth_pass']) ? $options['auth_pass'] : null;
  2536. $smtp_enc = isset($options['enc']) ? $options['enc'] : 'None';
  2537. $smtp_max_sends = isset($options['max_sends']) ? intval($options['max_sends']) : 20;
  2538. $smtp_timeout = isset($options['timeout']) ? intval($options['timeout']) : 30;
  2539. /*
  2540. * [JAS]: We'll cache connection info hashed by params and hold a persistent
  2541. * connection for the request cycle. If we ask for the same params again
  2542. * we'll get the existing connection if it exists.
  2543. */
  2544. $hash = md5(sprintf("%s %s %s %s %s %d %d",
  2545. $smtp_host,
  2546. $smtp_user,
  2547. $smtp_pass,
  2548. $smtp_port,
  2549. $smtp_enc,
  2550. $smtp_max_sends,
  2551. $smtp_timeout
  2552. ));
  2553. if(!isset($this->mailers[$hash])) {
  2554. // Encryption
  2555. switch($smtp_enc) {
  2556. case 'TLS':
  2557. $smtp_enc = 'tls';
  2558. break;
  2559. case 'SSL':
  2560. $smtp_enc = 'ssl';
  2561. break;
  2562. default:
  2563. $smtp_enc = null;
  2564. break;
  2565. }
  2566. $smtp = Swift_SmtpTransport::newInstance($smtp_host, $smtp_port, $smtp_enc);
  2567. $smtp->setTimeout($smtp_timeout);
  2568. if(!empty($smtp_user) && !empty($smtp_pass)) {
  2569. $smtp->setUsername($smtp_user);
  2570. $smtp->setPassword($smtp_pass);
  2571. }
  2572. $mailer = Swift_Mailer::newInstance($smtp);
  2573. $mailer->registerPlugin(new Swift_Plugins_AntiFloodPlugin($smtp_max_sends,1));
  2574. $this->mailers[$hash] =& $mailer;
  2575. }
  2576. return $this->mailers[$hash];
  2577. }
  2578. function testImap($server, $port, $service, $username, $password) {
  2579. if (!extension_loaded("imap")) die("IMAP Extension not loaded!");
  2580. switch($service) {
  2581. default:
  2582. case 'pop3': // 110
  2583. $connect = sprintf("{%s:%d/pop3/notls}INBOX",
  2584. $server,
  2585. $port
  2586. );
  2587. break;
  2588. case 'pop3-ssl': // 995
  2589. $connect = sprintf("{%s:%d/pop3/ssl/novalidate-cert}INBOX",
  2590. $server,
  2591. $port
  2592. );
  2593. break;
  2594. case 'imap': // 143
  2595. $connect = sprintf("{%s:%d/notls}INBOX",
  2596. $server,
  2597. $port
  2598. );
  2599. break;
  2600. case 'imap-ssl': // 993
  2601. $connect = sprintf("{%s:%d/imap/ssl/novalidate-cert}INBOX",
  2602. $server,
  2603. $port
  2604. );
  2605. break;
  2606. }
  2607. @$mailbox = imap_open(
  2608. $connect,
  2609. !empty($username)?$username:"superuser",
  2610. !empty($password)?$password:"superuser"
  2611. );
  2612. if($mailbox === FALSE)
  2613. return FALSE;
  2614. @imap_close($mailbox);
  2615. return TRUE;
  2616. }
  2617. /**
  2618. * @return array
  2619. */
  2620. function getErrors() {
  2621. return imap_errors();
  2622. }
  2623. }
  2624. class _DevblocksDateManager {
  2625. private function __construct() {}
  2626. /**
  2627. *
  2628. * @return _DevblocksDateManager
  2629. */
  2630. static function getInstance() {
  2631. static $instance = null;
  2632. if(null == $instance) {
  2633. $instance = new _DevblocksDateManager();
  2634. }
  2635. return $instance;
  2636. }
  2637. public function formatTime($format, $timestamp, $gmt=false) {
  2638. try {
  2639. $datetime = new DateTime();
  2640. $datetime->setTimezone(new DateTimeZone('GMT'));
  2641. $date = explode(' ',gmdate("Y m d", $timestamp));
  2642. $time = explode(':',gmdate("H:i:s", $timestamp));
  2643. $datetime->setDate($date[0],$date[1],$date[2]);
  2644. $datetime->setTime($time[0],$time[1],$time[2]);
  2645. } catch (Exception $e) {
  2646. $datetime = new DateTime();
  2647. }
  2648. if(empty($format))
  2649. $format = DateTime::RFC822;
  2650. if(!$gmt)
  2651. $datetime->setTimezone(new DateTimeZone(date_default_timezone_get()));
  2652. return $datetime->format($format);
  2653. }
  2654. public function getTimezones() {
  2655. return array(
  2656. 'Africa/Abidjan',
  2657. 'Africa/Accra',
  2658. 'Africa/Addis_Ababa',
  2659. 'Africa/Algiers',
  2660. 'Africa/Asmera',
  2661. 'Africa/Bamako',
  2662. 'Africa/Bangui',
  2663. 'Africa/Banjul',
  2664. 'Africa/Bissau',
  2665. 'Africa/Blantyre',
  2666. 'Africa/Brazzaville',
  2667. 'Africa/Bujumbura',
  2668. 'Africa/Cairo',
  2669. 'Africa/Casablanca',
  2670. 'Africa/Ceuta',
  2671. 'Africa/Conakry',
  2672. 'Africa/Dakar',
  2673. 'Africa/Dar_es_Salaam',
  2674. 'Africa/Djibouti',
  2675. 'Africa/Douala',
  2676. 'Africa/El_Aaiun',
  2677. 'Africa/Freetown',
  2678. 'Africa/Gaborone',
  2679. 'Africa/Harare',
  2680. 'Africa/Johannesburg',
  2681. 'Africa/Kampala',
  2682. 'Africa/Khartoum',
  2683. 'Africa/Kigali',
  2684. 'Africa/Kinshasa',
  2685. 'Africa/Lagos',
  2686. 'Africa/Libreville',
  2687. 'Africa/Lome',
  2688. 'Africa/Luanda',
  2689. 'Africa/Lubumbashi',
  2690. 'Africa/Lusaka',
  2691. 'Africa/Malabo',
  2692. 'Africa/Maputo',
  2693. 'Africa/Maseru',
  2694. 'Africa/Mbabane',
  2695. 'Africa/Mogadishu',
  2696. 'Africa/Monrovia',
  2697. 'Africa/Nairobi',
  2698. 'Africa/Ndjamena',
  2699. 'Africa/Niamey',
  2700. 'Africa/Nouakchott',
  2701. 'Africa/Ouagadougou',
  2702. 'Africa/Porto-Novo',
  2703. 'Africa/Sao_Tome',
  2704. 'Africa/Timbuktu',
  2705. 'Africa/Tripoli',
  2706. 'Africa/Tunis',
  2707. 'Africa/Windhoek',
  2708. 'America/Adak',
  2709. 'America/Anchorage',
  2710. 'America/Anguilla',
  2711. 'America/Antigua',
  2712. 'America/Araguaina',
  2713. 'America/Aruba',
  2714. 'America/Asuncion',
  2715. 'America/Barbados',
  2716. 'America/Belem',
  2717. 'America/Belize',
  2718. 'America/Bogota',
  2719. 'America/Boise',
  2720. 'America/Buenos_Aires',
  2721. 'America/Cancun',
  2722. 'America/Caracas',
  2723. 'America/Catamarca',
  2724. 'America/Cayenne',
  2725. 'America/Cayman',
  2726. 'America/Chicago',
  2727. 'America/Chihuahua',
  2728. 'America/Cordoba',
  2729. 'America/Costa_Rica',
  2730. 'America/Cuiaba',
  2731. 'America/Curacao',
  2732. 'America/Dawson',
  2733. 'America/Dawson_Creek',
  2734. 'America/Denver',
  2735. 'America/Detroit',
  2736. 'America/Dominica',
  2737. 'America/Edmonton',
  2738. 'America/El_Salvador',
  2739. 'America/Ensenada',
  2740. 'America/Fortaleza',
  2741. 'America/Glace_Bay',
  2742. 'America/Godthab',
  2743. 'America/Goose_Bay',
  2744. 'America/Grand_Turk',
  2745. 'America/Grenada',
  2746. 'America/Guadeloupe',
  2747. 'America/Guatemala',
  2748. 'America/Guayaquil',
  2749. 'America/Guyana',
  2750. 'America/Halifax',
  2751. 'America/Havana',
  2752. 'America/Indiana/Knox',
  2753. 'America/Indiana/Marengo',
  2754. 'America/Indiana/Vevay',
  2755. 'America/Indianapolis',
  2756. 'America/Inuvik',
  2757. 'America/Iqaluit',
  2758. 'America/Jamaica',
  2759. 'America/Jujuy',
  2760. 'America/Juneau',
  2761. 'America/La_Paz',
  2762. 'America/Lima',
  2763. 'America/Los_Angeles',
  2764. 'America/Louisville',
  2765. 'America/Maceio',
  2766. 'America/Managua',
  2767. 'America/Manaus',
  2768. 'America/Martinique',
  2769. 'America/Mazatlan',
  2770. 'America/Mendoza',
  2771. 'America/Menominee',
  2772. 'America/Mexico_City',
  2773. 'America/Miquelon',
  2774. 'America/Montevideo',
  2775. 'America/Montreal',
  2776. 'America/Montserrat',
  2777. 'America/Nassau',
  2778. 'America/New_York',
  2779. 'America/Nipigon',
  2780. 'America/Nome',
  2781. 'America/Noronha',
  2782. 'America/Panama',
  2783. 'America/Pangnirtung',
  2784. 'America/Paramaribo',
  2785. 'America/Phoenix',
  2786. 'America/Port-au-Prince',
  2787. 'America/Port_of_Spain',
  2788. 'America/Porto_Acre',
  2789. 'America/Porto_Velho',
  2790. 'America/Puerto_Rico',
  2791. 'America/Rainy_River',
  2792. 'America/Rankin_Inlet',
  2793. 'America/Regina',
  2794. 'America/Rosario',
  2795. 'America/Santiago',
  2796. 'America/Santo_Domingo',
  2797. 'America/Sao_Paulo',
  2798. 'America/Scoresbysund',
  2799. 'America/Shiprock',
  2800. 'America/St_Johns',
  2801. 'America/St_Kitts',
  2802. 'America/St_Lucia',
  2803. 'America/St_Thomas',
  2804. 'America/St_Vincent',
  2805. 'America/Swift_Current',
  2806. 'America/Tegucigalpa',
  2807. 'America/Thule',
  2808. 'America/Thunder_Bay',
  2809. 'America/Tijuana',
  2810. 'America/Tortola',
  2811. 'America/Vancouver',
  2812. 'America/Whitehorse',
  2813. 'America/Winnipeg',
  2814. 'America/Yakutat',
  2815. 'America/Yellowknife',
  2816. 'Antarctica/Casey',
  2817. 'Antarctica/Davis',
  2818. 'Antarctica/DumontDUrville',
  2819. 'Antarctica/Mawson',
  2820. 'Antarctica/McMurdo',
  2821. 'Antarctica/Palmer',
  2822. 'Antarctica/South_Pole',
  2823. 'Arctic/Longyearbyen',
  2824. 'Asia/Aden',
  2825. 'Asia/Almaty',
  2826. 'Asia/Amman',
  2827. 'Asia/Anadyr',
  2828. 'Asia/Aqtau',
  2829. 'Asia/Aqtobe',
  2830. 'Asia/Ashkhabad',
  2831. 'Asia/Baghdad',
  2832. 'Asia/Bahrain',
  2833. 'Asia/Baku',
  2834. 'Asia/Bangkok',
  2835. 'Asia/Beirut',
  2836. 'Asia/Bishkek',
  2837. 'Asia/Brunei',
  2838. 'Asia/Calcutta',
  2839. 'Asia/Chungking',
  2840. 'Asia/Colombo',
  2841. 'Asia/Dacca',
  2842. 'Asia/Damascus',
  2843. 'Asia/Dubai',
  2844. 'Asia/Dushanbe',
  2845. 'Asia/Gaza',
  2846. 'Asia/Harbin',
  2847. 'Asia/Hong_Kong',
  2848. 'Asia/Irkutsk',
  2849. 'Asia/Jakarta',
  2850. 'Asia/Jayapura',
  2851. 'Asia/Jerusalem',
  2852. 'Asia/Kabul',
  2853. 'Asia/Kamchatka',
  2854. 'Asia/Karachi',
  2855. 'Asia/Kashgar',
  2856. 'Asia/Katmandu',
  2857. 'Asia/Krasnoyarsk',
  2858. 'Asia/Kuala_Lumpur',
  2859. 'Asia/Kuching',
  2860. 'Asia/Kuwait',
  2861. 'Asia/Macao',
  2862. 'Asia/Magadan',
  2863. 'Asia/Manila',
  2864. 'Asia/Muscat',
  2865. 'Asia/Nicosia',
  2866. 'Asia/Novosibirsk',
  2867. 'Asia/Omsk',
  2868. 'Asia/Phnom_Penh',
  2869. 'Asia/Pyongyang',
  2870. 'Asia/Qatar',
  2871. 'Asia/Rangoon',
  2872. 'Asia/Riyadh',
  2873. 'Asia/Saigon',
  2874. 'Asia/Samarkand',
  2875. 'Asia/Seoul',
  2876. 'Asia/Shanghai',
  2877. 'Asia/Singapore',
  2878. 'Asia/Taipei',
  2879. 'Asia/Tashkent',
  2880. 'Asia/Tbilisi',
  2881. 'Asia/Tehran',
  2882. 'Asia/Thimbu',
  2883. 'Asia/Tokyo',
  2884. 'Asia/Ujung_Pandang',
  2885. 'Asia/Ulan_Bator',
  2886. 'Asia/Urumqi',
  2887. 'Asia/Vientiane',
  2888. 'Asia/Vladivostok',
  2889. 'Asia/Yakutsk',
  2890. 'Asia/Yekaterinburg',
  2891. 'Asia/Yerevan',
  2892. 'Atlantic/Azores',
  2893. 'Atlantic/Bermuda',
  2894. 'Atlantic/Canary',
  2895. 'Atlantic/Cape_Verde',
  2896. 'Atlantic/Faeroe',
  2897. 'Atlantic/Jan_Mayen',
  2898. 'Atlantic/Madeira',
  2899. 'Atlantic/Reykjavik',
  2900. 'Atlantic/South_Georgia',
  2901. 'Atlantic/St_Helena',
  2902. 'Atlantic/Stanley',
  2903. 'Australia/Adelaide',
  2904. 'Australia/Brisbane',
  2905. 'Australia/Broken_Hill',
  2906. 'Australia/Darwin',
  2907. 'Australia/Hobart',
  2908. 'Australia/Lindeman',
  2909. 'Australia/Lord_Howe',
  2910. 'Australia/Melbourne',
  2911. 'Australia/Perth',
  2912. 'Australia/Sydney',
  2913. 'Europe/Amsterdam',
  2914. 'Europe/Andorra',
  2915. 'Europe/Athens',
  2916. 'Europe/Belfast',
  2917. 'Europe/Belgrade',
  2918. 'Europe/Berlin',
  2919. 'Europe/Bratislava',
  2920. 'Europe/Brussels',
  2921. 'Europe/Bucharest',
  2922. 'Europe/Budapest',
  2923. 'Europe/Chisinau',
  2924. 'Europe/Copenhagen',
  2925. 'Europe/Dublin',
  2926. 'Europe/Gibraltar',
  2927. 'Europe/Helsinki',
  2928. 'Europe/Istanbul',
  2929. 'Europe/Kaliningrad',
  2930. 'Europe/Kiev',
  2931. 'Europe/Lisbon',
  2932. 'Europe/Ljubljana',
  2933. 'Europe/London',
  2934. 'Europe/Luxembourg',
  2935. 'Europe/Madrid',
  2936. 'Europe/Malta',
  2937. 'Europe/Minsk',
  2938. 'Europe/Monaco',
  2939. 'Europe/Moscow',
  2940. 'Europe/Oslo',
  2941. 'Europe/Paris',
  2942. 'Europe/Prague',
  2943. 'Europe/Riga',
  2944. 'Europe/Rome',
  2945. 'Europe/Samara',
  2946. 'Europe/San_Marino',
  2947. 'Europe/Sarajevo',
  2948. 'Europe/Simferopol',
  2949. 'Europe/Skopje',
  2950. 'Europe/Sofia',
  2951. 'Europe/Stockholm',
  2952. 'Europe/Tallinn',
  2953. 'Europe/Tirane',
  2954. 'Europe/Vaduz',
  2955. 'Europe/Vatican',
  2956. 'Europe/Vienna',
  2957. 'Europe/Vilnius',
  2958. 'Europe/Warsaw',
  2959. 'Europe/Zagreb',
  2960. 'Europe/Zurich',
  2961. 'Indian/Antananarivo',
  2962. 'Indian/Chagos',
  2963. 'Indian/Christmas',
  2964. 'Indian/Cocos',
  2965. 'Indian/Comoro',
  2966. 'Indian/Kerguelen',
  2967. 'Indian/Mahe',
  2968. 'Indian/Maldives',
  2969. 'Indian/Mauritius',
  2970. 'Indian/Mayotte',
  2971. 'Indian/Reunion',
  2972. 'Pacific/Apia',
  2973. 'Pacific/Auckland',
  2974. 'Pacific/Chatham',
  2975. 'Pacific/Easter',
  2976. 'Pacific/Efate',
  2977. 'Pacific/Enderbury',
  2978. 'Pacific/Fakaofo',
  2979. 'Pacific/Fiji',
  2980. 'Pacific/Funafuti',
  2981. 'Pacific/Galapagos',
  2982. 'Pacific/Gambier',
  2983. 'Pacific/Guadalcanal',
  2984. 'Pacific/Guam',
  2985. 'Pacific/Honolulu',
  2986. 'Pacific/Johnston',
  2987. 'Pacific/Kiritimati',
  2988. 'Pacific/Kosrae',
  2989. 'Pacific/Kwajalein',
  2990. 'Pacific/Majuro',
  2991. 'Pacific/Marquesas',
  2992. 'Pacific/Midway',
  2993. 'Pacific/Nauru',
  2994. 'Pacific/Niue',
  2995. 'Pacific/Norfolk',
  2996. 'Pacific/Noumea',
  2997. 'Pacific/Pago_Pago',
  2998. 'Pacific/Palau',
  2999. 'Pacific/Pitcairn',
  3000. 'Pacific/Ponape',
  3001. 'Pacific/Port_Moresby',
  3002. 'Pacific/Rarotonga',
  3003. 'Pacific/Saipan',
  3004. 'Pacific/Tahiti',
  3005. 'Pacific/Tarawa',
  3006. 'Pacific/Tongatapu',
  3007. 'Pacific/Truk',
  3008. 'Pacific/Wake',
  3009. 'Pacific/Wallis',
  3010. 'Pacific/Yap',
  3011. );
  3012. }
  3013. }
  3014. class _DevblocksTranslationManager {
  3015. private $_locales = array();
  3016. private $_locale = 'en_US';
  3017. private function __construct() {}
  3018. static function getInstance() {
  3019. static $instance = null;
  3020. if(null == $instance) {
  3021. $instance = new _DevblocksTranslationManager();
  3022. }
  3023. return $instance;
  3024. }
  3025. public function addLocale($locale, $strings) {
  3026. $this->_locales[$locale] = $strings;
  3027. }
  3028. public function setLocale($locale) {
  3029. if(isset($this->_locales[$locale]))
  3030. $this->_locale = $locale;
  3031. }
  3032. public function _($token) {
  3033. if(isset($this->_locales[$this->_locale][$token]))
  3034. return $this->_locales[$this->_locale][$token];
  3035. // [JAS] Make it easy to find things that don't translate
  3036. //return '$'.$token.'('.$this->_locale.')';
  3037. return $token;
  3038. }
  3039. public function getLocaleCodes() {
  3040. return array(
  3041. 'af_ZA',
  3042. 'am_ET',
  3043. 'be_BY',
  3044. 'bg_BG',
  3045. 'ca_ES',
  3046. 'cs_CZ',
  3047. 'da_DK',
  3048. 'de_AT',
  3049. 'de_CH',
  3050. 'de_DE',
  3051. 'el_GR',
  3052. 'en_AU',
  3053. 'en_CA',
  3054. 'en_GB',
  3055. 'en_IE',
  3056. 'en_NZ',
  3057. 'en_US',
  3058. 'es_ES',
  3059. 'es_MX',
  3060. 'et_EE',
  3061. 'eu_ES',
  3062. 'fi_FI',
  3063. 'fr_BE',
  3064. 'fr_CA',
  3065. 'fr_CH',
  3066. 'fr_FR',
  3067. 'he_IL',
  3068. 'hr_HR',
  3069. 'hu_HU',
  3070. 'hy_AM',
  3071. 'is_IS',
  3072. 'it_CH',
  3073. 'it_IT',
  3074. 'ja_JP',
  3075. 'kk_KZ',
  3076. 'ko_KR',
  3077. 'lt_LT',
  3078. 'nl_BE',
  3079. 'nl_NL',
  3080. 'no_NO',
  3081. 'pl_PL',
  3082. 'pt_BR',
  3083. 'pt_PT',
  3084. 'ro_RO',
  3085. 'ru_RU',
  3086. 'sk_SK',
  3087. 'sl_SI',
  3088. 'sr_RS',
  3089. 'sv_SE',
  3090. 'tr_TR',
  3091. 'uk_UA',
  3092. 'zh_CN',
  3093. 'zh_HK',
  3094. 'zh_TW',
  3095. );
  3096. }
  3097. function getLocaleStrings() {
  3098. $codes = $this->getLocaleCodes();
  3099. $langs = $this->getLanguageCodes();
  3100. $countries = $this->getCountryCodes();
  3101. $lang_codes = array();
  3102. if(is_array($codes))
  3103. foreach($codes as $code) {
  3104. $data = explode('_', $code);
  3105. @$lang = $langs[strtolower($data[0])];
  3106. @$terr = $countries[strtoupper($data[1])];
  3107. $lang_codes[$code] = (!empty($lang) && !empty($terr))
  3108. ? ($lang . ' (' . $terr . ')')
  3109. : $code;
  3110. }
  3111. asort($lang_codes);
  3112. unset($codes);
  3113. unset($langs);
  3114. unset($countries);
  3115. return $lang_codes;
  3116. }
  3117. function getLanguageCodes() {
  3118. return array(
  3119. 'aa' => "Afar",
  3120. 'ab' => "Abkhazian",
  3121. 'ae' => "Avestan",
  3122. 'af' => "Afrikaans",
  3123. 'am' => "Amharic",
  3124. 'an' => "Aragonese",
  3125. 'ar' => "Arabic",
  3126. 'as' => "Assamese",
  3127. 'ay' => "Aymara",
  3128. 'az' => "Azerbaijani",
  3129. 'ba' => "Bashkir",
  3130. 'be' => "Belarusian",
  3131. 'bg' => "Bulgarian",
  3132. 'bh' => "Bihari",
  3133. 'bi' => "Bislama",
  3134. 'bn' => "Bengali",
  3135. 'bo' => "Tibetan",
  3136. 'br' => "Breton",
  3137. 'bs' => "Bosnian",
  3138. 'ca' => "Catalan",
  3139. 'ce' => "Chechen",
  3140. 'ch' => "Chamorro",
  3141. 'co' => "Corsican",
  3142. 'cs' => "Czech",
  3143. 'cu' => "Church Slavic; Slavonic; Old Bulgarian",
  3144. 'cv' => "Chuvash",
  3145. 'cy' => "Welsh",
  3146. 'da' => "Danish",
  3147. 'de' => "German",
  3148. 'dv' => "Divehi; Dhivehi; Maldivian",
  3149. 'dz' => "Dzongkha",
  3150. 'el' => "Greek, Modern",
  3151. 'en' => "English",
  3152. 'eo' => "Esperanto",
  3153. 'es' => "Spanish; Castilian",
  3154. 'et' => "Estonian",
  3155. 'eu' => "Basque",
  3156. 'fa' => "Persian",
  3157. 'fi' => "Finnish",
  3158. 'fj' => "Fijian",
  3159. 'fo' => "Faroese",
  3160. 'fr' => "French",
  3161. 'fy' => "Western Frisian",
  3162. 'ga' => "Irish",
  3163. 'gd' => "Gaelic; Scottish Gaelic",
  3164. 'gl' => "Galician",
  3165. 'gn' => "Guarani",
  3166. 'gu' => "Gujarati",
  3167. 'gv' => "Manx",
  3168. 'ha' => "Hausa",
  3169. 'he' => "Hebrew",
  3170. 'hi' => "Hindi",
  3171. 'ho' => "Hiri Motu",
  3172. 'hr' => "Croatian",
  3173. 'ht' => "Haitian; Haitian Creole ",
  3174. 'hu' => "Hungarian",
  3175. 'hy' => "Armenian",
  3176. 'hz' => "Herero",
  3177. 'ia' => "Interlingua",
  3178. 'id' => "Indonesian",
  3179. 'ie' => "Interlingue",
  3180. 'ii' => "Sichuan Yi",
  3181. 'ik' => "Inupiaq",
  3182. 'io' => "Ido",
  3183. 'is' => "Icelandic",
  3184. 'it' => "Italian",
  3185. 'iu' => "Inuktitut",
  3186. 'ja' => "Japanese",
  3187. 'jv' => "Javanese",
  3188. 'ka' => "Georgian",
  3189. 'ki' => "Kikuyu; Gikuyu",
  3190. 'kj' => "Kuanyama; Kwanyama",
  3191. 'kk' => "Kazakh",
  3192. 'kl' => "Kalaallisut",
  3193. 'km' => "Khmer",
  3194. 'kn' => "Kannada",
  3195. 'ko' => "Korean",
  3196. 'ks' => "Kashmiri",
  3197. 'ku' => "Kurdish",
  3198. 'kv' => "Komi",
  3199. 'kw' => "Cornish",
  3200. 'ky' => "Kirghiz",
  3201. 'la' => "Latin",
  3202. 'lb' => "Luxembourgish; Letzeburgesch",
  3203. 'li' => "Limburgan; Limburger; Limburgish",
  3204. 'ln' => "Lingala",
  3205. 'lo' => "Lao",
  3206. 'lt' => "Lithuanian",
  3207. 'lv' => "Latvian",
  3208. 'mg' => "Malagasy",
  3209. 'mh' => "Marshallese",
  3210. 'mi' => "Maori",
  3211. 'mk' => "Macedonian",
  3212. 'ml' => "Malayalam",
  3213. 'mn' => "Mongolian",
  3214. 'mo' => "Moldavian",
  3215. 'mr' => "Marathi",
  3216. 'ms' => "Malay",
  3217. 'mt' => "Maltese",
  3218. 'my' => "Burmese",
  3219. 'na' => "Nauru",
  3220. 'nb' => "Norwegian Bokmal",
  3221. 'nd' => "Ndebele, North",
  3222. 'ne' => "Nepali",
  3223. 'ng' => "Ndonga",
  3224. 'nl' => "Dutch",
  3225. 'nn' => "Norwegian Nynorsk",
  3226. 'no' => "Norwegian",
  3227. 'nr' => "Ndebele, South",
  3228. 'nv' => "Navaho, Navajo",
  3229. 'ny' => "Nyanja; Chichewa; Chewa",
  3230. 'oc' => "Occitan; Provencal",
  3231. 'om' => "Oromo",
  3232. 'or' => "Oriya",
  3233. 'os' => "Ossetian; Ossetic",
  3234. 'pa' => "Panjabi",
  3235. 'pi' => "Pali",
  3236. 'pl' => "Polish",
  3237. 'ps' => "Pushto",
  3238. 'pt' => "Portuguese",
  3239. 'qu' => "Quechua",
  3240. 'rm' => "Raeto-Romance",
  3241. 'rn' => "Rundi",
  3242. 'ro' => "Romanian",
  3243. 'ru' => "Russian",
  3244. 'rw' => "Kinyarwanda",
  3245. 'sa' => "Sanskrit",
  3246. 'sc' => "Sardinian",
  3247. 'sd' => "Sindhi",
  3248. 'se' => "Northern Sami",
  3249. 'sg' => "Sango",
  3250. 'si' => "Sinhala; Sinhalese",
  3251. 'sk' => "Slovak",
  3252. 'sl' => "Slovenian",
  3253. 'sm' => "Samoan",
  3254. 'sn' => "Shona",
  3255. 'so' => "Somali",
  3256. 'sq' => "Albanian",
  3257. 'sr' => "Serbian",
  3258. 'ss' => "Swati",
  3259. 'st' => "Sotho, Southern",
  3260. 'su' => "Sundanese",
  3261. 'sv' => "Swedish",
  3262. 'sw' => "Swahili",
  3263. 'ta' => "Tamil",
  3264. 'te' => "Telugu",
  3265. 'tg' => "Tajik",
  3266. 'th' => "Thai",
  3267. 'ti' => "Tigrinya",
  3268. 'tk' => "Turkmen",
  3269. 'tl' => "Tagalog",
  3270. 'tn' => "Tswana",
  3271. 'to' => "Tonga",
  3272. 'tr' => "Turkish",
  3273. 'ts' => "Tsonga",
  3274. 'tt' => "Tatar",
  3275. 'tw' => "Twi",
  3276. 'ty' => "Tahitian",
  3277. 'ug' => "Uighur",
  3278. 'uk' => "Ukrainian",
  3279. 'ur' => "Urdu",
  3280. 'uz' => "Uzbek",
  3281. 'vi' => "Vietnamese",
  3282. 'vo' => "Volapuk",
  3283. 'wa' => "Walloon",
  3284. 'wo' => "Wolof",
  3285. 'xh' => "Xhosa",
  3286. 'yi' => "Yiddish",
  3287. 'yo' => "Yoruba",
  3288. 'za' => "Zhuang; Chuang",
  3289. 'zh' => "Chinese",
  3290. 'zu' => "Zulu",
  3291. );
  3292. }
  3293. function getCountryCodes() {
  3294. return array(
  3295. 'AD' => "Andorra",
  3296. 'AE' => "United Arab Emirates",
  3297. 'AF' => "Afghanistan",
  3298. 'AG' => "Antigua and Barbuda",
  3299. 'AI' => "Anguilla",
  3300. 'AL' => "Albania",
  3301. 'AM' => "Armenia",
  3302. 'AN' => "Netherlands Antilles",
  3303. 'AO' => "Angola",
  3304. 'AQ' => "Antarctica",
  3305. 'AR' => "Argentina",
  3306. 'AS' => "American Samoa",
  3307. 'AT' => "Austria",
  3308. 'AU' => "Australia",
  3309. 'AW' => "Aruba",
  3310. 'AX' => "Aland Islands",
  3311. 'AZ' => "Azerbaijan",
  3312. 'BA' => "Bosnia and Herzegovina",
  3313. 'BB' => "Barbados",
  3314. 'BD' => "Bangladesh",
  3315. 'BE' => "Belgium",
  3316. 'BF' => "Burkina Faso",
  3317. 'BG' => "Bulgaria",
  3318. 'BH' => "Bahrain",
  3319. 'BI' => "Burundi",
  3320. 'BJ' => "Benin",
  3321. 'BL' => "Saint Barthélemy",
  3322. 'BM' => "Bermuda",
  3323. 'BN' => "Brunei Darussalam",
  3324. 'BO' => "Bolivia",
  3325. 'BR' => "Brazil",
  3326. 'BS' => "Bahamas",
  3327. 'BT' => "Bhutan",
  3328. 'BV' => "Bouvet Island",
  3329. 'BW' => "Botswana",
  3330. 'BY' => "Belarus",
  3331. 'BZ' => "Belize",
  3332. 'CA' => "Canada",
  3333. 'CC' => "Cocos (Keeling) Islands",
  3334. 'CD' => "Congo, the Democratic Republic of the",
  3335. 'CF' => "Central African Republic",
  3336. 'CG' => "Congo",
  3337. 'CH' => "Switzerland",
  3338. 'CI' => "Cote d'Ivoire Côte d'Ivoire",
  3339. 'CK' => "Cook Islands",
  3340. 'CL' => "Chile",
  3341. 'CM' => "Cameroon",
  3342. 'CN' => "China",
  3343. 'CO' => "Colombia",
  3344. 'CR' => "Costa Rica",
  3345. 'CU' => "Cuba",
  3346. 'CV' => "Cape Verde",
  3347. 'CX' => "Christmas Island",
  3348. 'CY' => "Cyprus",
  3349. 'CZ' => "Czech Republic",
  3350. 'DE' => "Germany",
  3351. 'DJ' => "Djibouti",
  3352. 'DK' => "Denmark",
  3353. 'DM' => "Dominica",
  3354. 'DO' => "Dominican Republic",
  3355. 'DZ' => "Algeria",
  3356. 'EC' => "Ecuador",
  3357. 'EE' => "Estonia",
  3358. 'EG' => "Egypt",
  3359. 'EH' => "Western Sahara",
  3360. 'ER' => "Eritrea",
  3361. 'ES' => "Spain",
  3362. 'ET' => "Ethiopia",
  3363. 'FI' => "Finland",
  3364. 'FJ' => "Fiji",
  3365. 'FK' => "Falkland Islands (Malvinas)",
  3366. 'FM' => "Micronesia, Federated States of",
  3367. 'FO' => "Faroe Islands",
  3368. 'FR' => "France",
  3369. 'GA' => "Gabon",
  3370. 'GB' => "United Kingdom",
  3371. 'GD' => "Grenada",
  3372. 'GE' => "Georgia",
  3373. 'GF' => "French Guiana",
  3374. 'GG' => "Guernsey",
  3375. 'GH' => "Ghana",
  3376. 'GI' => "Gibraltar",
  3377. 'GL' => "Greenland",
  3378. 'GM' => "Gambia",
  3379. 'GN' => "Guinea",
  3380. 'GP' => "Guadeloupe",
  3381. 'GQ' => "Equatorial Guinea",
  3382. 'GR' => "Greece",
  3383. 'GS' => "South Georgia and the South Sandwich Islands",
  3384. 'GT' => "Guatemala",
  3385. 'GU' => "Guam",
  3386. 'GW' => "Guinea-Bissau",
  3387. 'GY' => "Guyana",
  3388. 'HK' => "Hong Kong",
  3389. 'HM' => "Heard Island and McDonald Islands",
  3390. 'HN' => "Honduras",
  3391. 'HR' => "Croatia",
  3392. 'HT' => "Haiti",
  3393. 'HU' => "Hungary",
  3394. 'ID' => "Indonesia",
  3395. 'IE' => "Ireland",
  3396. 'IL' => "Israel",
  3397. 'IM' => "Isle of Man",
  3398. 'IN' => "India",
  3399. 'IO' => "British Indian Ocean Territory",
  3400. 'IQ' => "Iraq",
  3401. 'IR' => "Iran, Islamic Republic of",
  3402. 'IS' => "Iceland",
  3403. 'IT' => "Italy",
  3404. 'JE' => "Jersey",
  3405. 'JM' => "Jamaica",
  3406. 'JO' => "Jordan",
  3407. 'JP' => "Japan",
  3408. 'KE' => "Kenya",
  3409. 'KG' => "Kyrgyzstan",
  3410. 'KH' => "Cambodia",
  3411. 'KI' => "Kiribati",
  3412. 'KM' => "Comoros",
  3413. 'KN' => "Saint Kitts and Nevis",
  3414. 'KP' => "Korea, Democratic People's Republic of",
  3415. 'KR' => "Korea, Republic of",
  3416. 'KW' => "Kuwait",
  3417. 'KY' => "Cayman Islands",
  3418. 'KZ' => "Kazakhstan",
  3419. 'LA' => "Lao People's Democratic Republic",
  3420. 'LB' => "Lebanon",
  3421. 'LC' => "Saint Lucia",
  3422. 'LI' => "Liechtenstein",
  3423. 'LK' => "Sri Lanka",
  3424. 'LR' => "Liberia",
  3425. 'LS' => "Lesotho",
  3426. 'LT' => "Lithuania",
  3427. 'LU' => "Luxembourg",
  3428. 'LV' => "Latvia",
  3429. 'LY' => "Libyan Arab Jamahiriya",
  3430. 'MA' => "Morocco",
  3431. 'MC' => "Monaco",
  3432. 'MD' => "Moldova, Republic of",
  3433. 'ME' => "Montenegro",
  3434. 'MF' => "Saint Martin (French part)",
  3435. 'MG' => "Madagascar",
  3436. 'MH' => "Marshall Islands",
  3437. 'MK' => "Macedonia, the former Yugoslav Republic of",
  3438. 'ML' => "Mali",
  3439. 'MM' => "Myanmar",
  3440. 'MN' => "Mongolia",
  3441. 'MO' => "Macao",
  3442. 'MP' => "Northern Mariana Islands",
  3443. 'MQ' => "Martinique",
  3444. 'MR' => "Mauritania",
  3445. 'MS' => "Montserrat",
  3446. 'MT' => "Malta",
  3447. 'MU' => "Mauritius",
  3448. 'MV' => "Maldives",
  3449. 'MW' => "Malawi",
  3450. 'MX' => "Mexico",
  3451. 'MY' => "Malaysia",
  3452. 'MZ' => "Mozambique",
  3453. 'NA' => "Namibia",
  3454. 'NC' => "New Caledonia",
  3455. 'NE' => "Niger",
  3456. 'NF' => "Norfolk Island",
  3457. 'NG' => "Nigeria",
  3458. 'NI' => "Nicaragua",
  3459. 'NL' => "Netherlands",
  3460. 'NO' => "Norway",
  3461. 'NP' => "Nepal",
  3462. 'NR' => "Nauru",
  3463. 'NU' => "Niue",
  3464. 'NZ' => "New Zealand",
  3465. 'OM' => "Oman",
  3466. 'PA' => "Panama",
  3467. 'PE' => "Peru",
  3468. 'PF' => "French Polynesia",
  3469. 'PG' => "Papua New Guinea",
  3470. 'PH' => "Philippines",
  3471. 'PK' => "Pakistan",
  3472. 'PL' => "Poland",
  3473. 'PM' => "Saint Pierre and Miquelon",
  3474. 'PN' => "Pitcairn",
  3475. 'PR' => "Puerto Rico",
  3476. 'PS' => "Palestinian Territory, Occupied",
  3477. 'PT' => "Portugal",
  3478. 'PW' => "Palau",
  3479. 'PY' => "Paraguay",
  3480. 'QA' => "Qatar",
  3481. 'RE' => "Reunion Réunion",
  3482. 'RO' => "Romania",
  3483. 'RS' => "Serbia",
  3484. 'RU' => "Russian Federation",
  3485. 'RW' => "Rwanda",
  3486. 'SA' => "Saudi Arabia",
  3487. 'SB' => "Solomon Islands",
  3488. 'SC' => "Seychelles",
  3489. 'SD' => "Sudan",
  3490. 'SE' => "Sweden",
  3491. 'SG' => "Singapore",
  3492. 'SH' => "Saint Helena",
  3493. 'SI' => "Slovenia",
  3494. 'SJ' => "Svalbard and Jan Mayen",
  3495. 'SK' => "Slovakia",
  3496. 'SL' => "Sierra Leone",
  3497. 'SM' => "San Marino",
  3498. 'SN' => "Senegal",
  3499. 'SO' => "Somalia",
  3500. 'SR' => "Suriname",
  3501. 'ST' => "Sao Tome and Principe",
  3502. 'SV' => "El Salvador",
  3503. 'SY' => "Syrian Arab Republic",
  3504. 'SZ' => "Swaziland",
  3505. 'TC' => "Turks and Caicos Islands",
  3506. 'TD' => "Chad",
  3507. 'TF' => "French Southern Territories",
  3508. 'TG' => "Togo",
  3509. 'TH' => "Thailand",
  3510. 'TJ' => "Tajikistan",
  3511. 'TK' => "Tokelau",
  3512. 'TL' => "Timor-Leste",
  3513. 'TM' => "Turkmenistan",
  3514. 'TN' => "Tunisia",
  3515. 'TO' => "Tonga",
  3516. 'TR' => "Turkey",
  3517. 'TT' => "Trinidad and Tobago",
  3518. 'TV' => "Tuvalu",
  3519. 'TW' => "Taiwan, Province of China",
  3520. 'TZ' => "Tanzania, United Republic of",
  3521. 'UA' => "Ukraine",
  3522. 'UG' => "Uganda",
  3523. 'UM' => "United States Minor Outlying Islands",
  3524. 'US' => "United States",
  3525. 'UY' => "Uruguay",
  3526. 'UZ' => "Uzbekistan",
  3527. 'VA' => "Holy See (Vatican City State)",
  3528. 'VC' => "Saint Vincent and the Grenadines",
  3529. 'VE' => "Venezuela",
  3530. 'VG' => "Virgin Islands, British",
  3531. 'VI' => "Virgin Islands, U.S.",
  3532. 'VN' => "Viet Nam",
  3533. 'VU' => "Vanuatu",
  3534. 'WF' => "Wallis and Futuna",
  3535. 'WS' => "Samoa",
  3536. 'YE' => "Yemen",
  3537. 'YT' => "Mayotte",
  3538. 'ZA' => "South Africa",
  3539. 'ZM' => "Zambia",
  3540. 'ZW' => "Zimbabwe",
  3541. );
  3542. }
  3543. }
  3544. /**
  3545. * Smarty Template Manager Singleton
  3546. *
  3547. * @ingroup services
  3548. */
  3549. class _DevblocksTemplateManager {
  3550. /**
  3551. * Constructor
  3552. *
  3553. * @private
  3554. */
  3555. private function _DevblocksTemplateManager() {}
  3556. /**
  3557. * Returns an instance of the Smarty Template Engine
  3558. *
  3559. * @static
  3560. * @return Smarty
  3561. */
  3562. static function getInstance() {
  3563. static $instance = null;
  3564. if(null == $instance) {
  3565. define('SMARTY_RESOURCE_CHAR_SET', LANG_CHARSET_CODE);
  3566. require(DEVBLOCKS_PATH . 'libs/smarty/Smarty.class.php');
  3567. $instance = new Smarty();
  3568. $instance->template_dir = APP_PATH . '/templates';
  3569. $instance->compile_dir = APP_TEMP_PATH . '/templates_c';
  3570. $instance->cache_dir = APP_TEMP_PATH . '/cache';
  3571. $instance->use_sub_dirs = false;
  3572. $instance->caching = 0;
  3573. $instance->cache_lifetime = 0;
  3574. $instance->compile_check = (defined('DEVELOPMENT_MODE') && DEVELOPMENT_MODE) ? true : false;
  3575. // Auto-escape HTML output
  3576. $instance->loadFilter('variable','htmlspecialchars');
  3577. //$instance->register->variableFilter(array('_DevblocksTemplateManager','variable_filter_esc'));
  3578. // Devblocks plugins
  3579. $instance->register->block('devblocks_url', array('_DevblocksTemplateManager', 'block_devblocks_url'));
  3580. $instance->register->modifier('devblocks_date', array('_DevblocksTemplateManager', 'modifier_devblocks_date'));
  3581. $instance->register->modifier('devblocks_hyperlinks', array('_DevblocksTemplateManager', 'modifier_devblocks_hyperlinks'));
  3582. $instance->register->modifier('devblocks_hideemailquotes', array('_DevblocksTemplateManager', 'modifier_devblocks_hide_email_quotes'));
  3583. $instance->register->modifier('devblocks_prettytime', array('_DevblocksTemplateManager', 'modifier_devblocks_prettytime'));
  3584. $instance->register->modifier('devblocks_prettybytes', array('_DevblocksTemplateManager', 'modifier_devblocks_prettybytes'));
  3585. $instance->register->modifier('devblocks_translate', array('_DevblocksTemplateManager', 'modifier_devblocks_translate'));
  3586. $instance->register->resource('devblocks', array(
  3587. array('_DevblocksSmartyTemplateResource', 'get_template'),
  3588. array('_DevblocksSmartyTemplateResource', 'get_timestamp'),
  3589. array('_DevblocksSmartyTemplateResource', 'get_secure'),
  3590. array('_DevblocksSmartyTemplateResource', 'get_trusted'),
  3591. ));
  3592. }
  3593. return $instance;
  3594. }
  3595. static function modifier_devblocks_translate($string) {
  3596. $translate = DevblocksPlatform::getTranslationService();
  3597. // Variable number of arguments
  3598. $args = func_get_args();
  3599. array_shift($args); // pop off $string
  3600. $translated = $translate->_($string);
  3601. $translated = @vsprintf($translated,$args);
  3602. return $translated;
  3603. }
  3604. static function block_devblocks_url($params, $content, $smarty, $repeat, $smarty_tpl) {
  3605. $url = DevblocksPlatform::getUrlService();
  3606. $contents = $url->write($content, !empty($params['full']) ? true : false);
  3607. if (!empty($params['assign'])) {
  3608. $smarty->assign($params['assign'], $contents);
  3609. } else {
  3610. return $contents;
  3611. }
  3612. }
  3613. static function modifier_devblocks_date($string, $format=null, $gmt=false) {
  3614. if(empty($string))
  3615. return '';
  3616. $date = DevblocksPlatform::getDateService();
  3617. return $date->formatTime($format, $string, $gmt);
  3618. }
  3619. static function modifier_devblocks_prettytime($string, $is_delta=false) {
  3620. if(empty($string) || !is_numeric($string))
  3621. return '';
  3622. if(!$is_delta) {
  3623. $diffsecs = time() - intval($string);
  3624. } else {
  3625. $diffsecs = intval($string);
  3626. }
  3627. $whole = '';
  3628. if($is_delta) {
  3629. if($diffsecs > 0)
  3630. $whole .= '+';
  3631. if($diffsecs < 0)
  3632. $whole .= '-';
  3633. }
  3634. // The past
  3635. if($diffsecs >= 0) {
  3636. if($diffsecs >= 31557600) { // years
  3637. $whole .= floor($diffsecs/31557600).'yr';
  3638. } elseif($diffsecs >= 2592000) { // mo
  3639. $whole .= floor($diffsecs/2592000).'mo';
  3640. } elseif($diffsecs >= 86400) { // days
  3641. $whole .= floor($diffsecs/86400).'d';
  3642. } elseif($diffsecs >= 3600) { // hours
  3643. $whole .= floor($diffsecs/3600).'h';
  3644. } elseif($diffsecs >= 60) { // mins
  3645. $whole .= floor($diffsecs/60).'m';
  3646. } elseif($diffsecs >= 0) { // secs
  3647. $whole .= $diffsecs.'s';
  3648. }
  3649. if(!$is_delta)
  3650. $whole .= ' ago';
  3651. } else { // The future
  3652. if($diffsecs <= -31557600) { // years
  3653. $whole .= floor($diffsecs/-31557600).'yr';
  3654. } elseif($diffsecs <= -2592000) { // mo
  3655. $whole .= floor($diffsecs/-2592000).'mo';
  3656. } elseif($diffsecs <= -86400) { // days
  3657. $whole .= floor($diffsecs/-86400).'d';
  3658. } elseif($diffsecs <= -3600) { // hours
  3659. $whole .= floor($diffsecs/-3600).'h';
  3660. } elseif($diffsecs <= -60) { // mins
  3661. $whole .= floor($diffsecs/-60).'m';
  3662. } elseif($diffsecs <= 0) { // secs
  3663. $whole .= $diffsecs.'s';
  3664. }
  3665. }
  3666. return $whole;
  3667. }
  3668. static function modifier_devblocks_prettybytes($string, $precision='0') {
  3669. if(!is_numeric($string))
  3670. return '';
  3671. $bytes = floatval($string);
  3672. $precision = floatval($precision);
  3673. $out = '';
  3674. if($bytes >= 1000000000) {
  3675. $out = number_format($bytes/1000000000,$precision) . ' GB';
  3676. } elseif ($bytes >= 1000000) {
  3677. $out = number_format($bytes/1000000,$precision) . ' MB';
  3678. } elseif ($bytes >= 1000) {
  3679. $out = number_format($bytes/1000,$precision) . ' KB';
  3680. } else {
  3681. $out = $bytes . ' bytes';
  3682. }
  3683. return $out;
  3684. }
  3685. function modifier_devblocks_hyperlinks($string, $sanitize = false, $style="") {
  3686. $from = array("&gt;");
  3687. $to = array(">");
  3688. $string = str_replace($from,$to,$string);
  3689. if($sanitize !== false)
  3690. return preg_replace("/((http|https):\/\/(.*?))(\s|\>|&lt;|&quot;|\)|$)/ie","'<a href=\"goto.php?url='.'\\1'.'\" target=\"_blank\">\\1</a>\\4'",$string);
  3691. else
  3692. return preg_replace("/((http|https):\/\/(.*?))(\s|\>|&lt;|&quot;|\)|$)/ie","'<a href=\"'.'\\1'.'\" target=\"_blank\">\\1</a>\\4'",$string);
  3693. }
  3694. function modifier_devblocks_hide_email_quotes($string, $length=3) {
  3695. $string = str_replace("\r\n","\n",$string);
  3696. $string = str_replace("\r","\n",$string);
  3697. $string = preg_replace("/\n{3,99}/", "\n\n", $string);
  3698. $lines = explode("\n", $string);
  3699. $quote_started = false;
  3700. $last_line = count($lines) - 1;
  3701. foreach($lines as $idx => $line) {
  3702. // Check if the line starts with a > before any content
  3703. if(preg_match("/^\s*\>/", $line)) {
  3704. if(false === $quote_started)
  3705. $quote_started = $idx;
  3706. $quote_ended = false;
  3707. } else {
  3708. if(false !== $quote_started)
  3709. $quote_ended = $idx-1;
  3710. }
  3711. // Always finish quoting on the last line
  3712. if(!$quote_ended && $last_line == $idx)
  3713. $quote_ended = $idx;
  3714. if($quote_started && $quote_ended) {
  3715. if($quote_ended - $quote_started >= $length) {
  3716. $lines[$quote_started] = "<div style='margin:5px;'><a href='javascript:;' style='background-color:rgb(255,255,204);' onclick=\"$(this).closest('div').next('div').toggle();$(this).parent().fadeOut();\">-show quote-</a></div><div class='hidden' style='display:none;font-style:italic;color:rgb(66,116,62);'>" . $lines[$quote_started];
  3717. $lines[$quote_ended] = $lines[$quote_ended]."</div>";
  3718. }
  3719. $quote_started = false;
  3720. }
  3721. }
  3722. return implode("\n", $lines);
  3723. }
  3724. };
  3725. class _DevblocksSmartyTemplateResource {
  3726. static function get_template($tpl_name, &$tpl_source, $smarty_obj) {
  3727. list($plugin_id, $tag, $tpl_path) = explode(':',$tpl_name,3);
  3728. if(empty($plugin_id) || empty($tpl_path))
  3729. return false;
  3730. $plugins = DevblocksPlatform::getPluginRegistry();
  3731. $db = DevblocksPlatform::getDatabaseService();
  3732. if(null == ($plugin = @$plugins[$plugin_id])) /* @var $plugin DevblocksPluginManifest */
  3733. return false;
  3734. // Check if template is overloaded in DB/cache
  3735. $matches = DAO_DevblocksTemplate::getWhere(sprintf("plugin_id = %s AND path = %s %s",
  3736. $db->qstr($plugin_id),
  3737. $db->qstr($tpl_path),
  3738. (!empty($tag) ? sprintf("AND tag = %s ",$db->qstr($tag)) : "")
  3739. ));
  3740. if(!empty($matches)) {
  3741. $match = array_shift($matches); /* @var $match Model_DevblocksTemplate */
  3742. $tpl_source = $match->content;
  3743. return true;
  3744. }
  3745. // If not in DB, check plugin's relative path on disk
  3746. $path = APP_PATH . '/' . $plugin->dir . '/templates/' . $tpl_path;
  3747. if(!file_exists($path))
  3748. return false;
  3749. $tpl_source = file_get_contents($path);
  3750. return true;
  3751. }
  3752. static function get_timestamp($tpl_name, &$tpl_timestamp, $smarty_obj) { /* @var $smarty_obj Smarty */
  3753. list($plugin_id, $tag, $tpl_path) = explode(':',$tpl_name,3);
  3754. if(empty($plugin_id) || empty($tpl_path))
  3755. return false;
  3756. $plugins = DevblocksPlatform::getPluginRegistry();
  3757. $db = DevblocksPlatform::getDatabaseService();
  3758. if(null == ($plugin = @$plugins[$plugin_id])) /* @var $plugin DevblocksPluginManifest */
  3759. return false;
  3760. // Check if template is overloaded in DB/cache
  3761. $matches = DAO_DevblocksTemplate::getWhere(sprintf("plugin_id = %s AND path = %s %s",
  3762. $db->qstr($plugin_id),
  3763. $db->qstr($tpl_path),
  3764. (!empty($tag) ? sprintf("AND tag = %s ",$db->qstr($tag)) : "")
  3765. ));
  3766. if(!empty($matches)) {
  3767. $match = array_shift($matches); /* @var $match Model_DevblocksTemplate */
  3768. // echo time(),"==(DB)",$match->last_updated,"<BR>";
  3769. $tpl_timestamp = $match->last_updated;
  3770. return true;
  3771. }
  3772. // If not in DB, check plugin's relative path on disk
  3773. $path = APP_PATH . '/' . $plugin->dir . '/templates/' . $tpl_path;
  3774. if(!file_exists($path))
  3775. return false;
  3776. $stat = stat($path);
  3777. $tpl_timestamp = $stat['mtime'];
  3778. // echo time(),"==(DISK)",$stat['mtime'],"<BR>";
  3779. return true;
  3780. }
  3781. static function get_secure($tpl_name, &$smarty_obj) {
  3782. return false;
  3783. }
  3784. static function get_trusted($tpl_name, &$smarty_obj) {
  3785. // not used
  3786. }
  3787. };
  3788. class _DevblocksTemplateBuilder {
  3789. private $_twig = null;
  3790. private $_errors = array();
  3791. private function _DevblocksTemplateBuilder() {
  3792. $this->_twig = new Twig_Environment(new Twig_Loader_String(), array(
  3793. 'cache' => false, //APP_TEMP_PATH
  3794. 'debug' => false,
  3795. 'auto_reload' => true,
  3796. 'trim_blocks' => true,
  3797. ));
  3798. // [TODO] Add helpful Twig extensions
  3799. }
  3800. /**
  3801. *
  3802. * @return _DevblocksTemplateBuilder
  3803. */
  3804. static function getInstance() {
  3805. static $instance = null;
  3806. if(null == $instance) {
  3807. $instance = new _DevblocksTemplateBuilder();
  3808. }
  3809. return $instance;
  3810. }
  3811. /**
  3812. * @return Twig_Environment
  3813. */
  3814. public function getEngine() {
  3815. return $this->_twig;
  3816. }
  3817. /**
  3818. * @return array
  3819. */
  3820. public function getErrors() {
  3821. return $this->_errors;
  3822. }
  3823. private function _setUp() {
  3824. $this->_errors = array();
  3825. }
  3826. private function _tearDown() {
  3827. }
  3828. /**
  3829. *
  3830. * @param string $template
  3831. * @param array $vars
  3832. * @return string
  3833. */
  3834. function build($template, $vars) {
  3835. $this->_setUp();
  3836. try {
  3837. $template = $this->_twig->loadTemplate($template);
  3838. $out = $template->render($vars);
  3839. } catch(Exception $e) {
  3840. $this->_errors[] = $e->getMessage();
  3841. }
  3842. $this->_tearDown();
  3843. if(!empty($this->_errors))
  3844. return false;
  3845. return $out;
  3846. }
  3847. };
  3848. class _DevblocksDatabaseManager {
  3849. private $_db = null;
  3850. static $instance = null;
  3851. private function _DevblocksDatabaseManager() {}
  3852. static function getInstance() {
  3853. if(null == self::$instance) {
  3854. // Bail out early for pre-install
  3855. if('' == APP_DB_DRIVER || '' == APP_DB_HOST)
  3856. return null;
  3857. self::$instance = new _DevblocksDatabaseManager();
  3858. }
  3859. return self::$instance;
  3860. }
  3861. function __construct() {
  3862. $persistent = (defined('APP_DB_PCONNECT') && APP_DB_PCONNECT) ? true : false;
  3863. $this->Connect(APP_DB_HOST, APP_DB_USER, APP_DB_PASS, APP_DB_DATABASE, $persistent);
  3864. }
  3865. function Connect($host, $user, $pass, $database, $persistent=false) {
  3866. if($persistent) {
  3867. if(false === (@$this->_db = mysql_pconnect($host, $user, $pass)))
  3868. return false;
  3869. } else {
  3870. if(false === (@$this->_db = mysql_connect($host, $user, $pass, true)))
  3871. return false;
  3872. }
  3873. if(false === mysql_select_db($database, $this->_db)) {
  3874. return false;
  3875. }
  3876. // Encoding
  3877. //mysql_set_charset(DB_CHARSET_CODE, $this->_db);
  3878. $this->Execute('SET NAMES ' . DB_CHARSET_CODE);
  3879. return true;
  3880. }
  3881. function getConnection() {
  3882. return $this->_db;
  3883. }
  3884. function isConnected() {
  3885. if(!is_resource($this->_db)) {
  3886. $this->_db = null;
  3887. return false;
  3888. }
  3889. return mysql_ping($this->_db);
  3890. }
  3891. function metaTables() {
  3892. $tables = array();
  3893. $sql = "SHOW TABLES";
  3894. $rs = $this->GetArray($sql);
  3895. foreach($rs as $row) {
  3896. $table = array_shift($row);
  3897. $tables[$table] = $table;
  3898. }
  3899. return $tables;
  3900. }
  3901. function metaTable($table_name) {
  3902. $columns = array();
  3903. $indexes = array();
  3904. $sql = sprintf("SHOW COLUMNS FROM %s", $table_name);
  3905. $rs = $this->GetArray($sql);
  3906. foreach($rs as $row) {
  3907. $field = $row['Field'];
  3908. $columns[$field] = array(
  3909. 'field' => $field,
  3910. 'type' => $row['Type'],
  3911. 'null' => $row['Null'],
  3912. 'key' => $row['Key'],
  3913. 'default' => $row['Default'],
  3914. 'extra' => $row['Extra'],
  3915. );
  3916. }
  3917. $sql = sprintf("SHOW INDEXES FROM %s", $table_name);
  3918. $rs = $this->GetArray($sql);
  3919. foreach($rs as $row) {
  3920. $key_name = $row['Key_name'];
  3921. $column_name = $row['Column_name'];
  3922. if(!isset($indexes[$key_name]))
  3923. $indexes[$key_name] = array(
  3924. 'columns' => array(),
  3925. );
  3926. $indexes[$key_name]['columns'][$column_name] = array(
  3927. 'column_name' => $column_name,
  3928. 'cardinality' => $row['Cardinality'],
  3929. 'index_type' => $row['Index_type'],
  3930. );
  3931. }
  3932. return array(
  3933. $columns,
  3934. $indexes
  3935. );
  3936. }
  3937. function Execute($sql) {
  3938. if(false === ($rs = mysql_query($sql, $this->_db))) {
  3939. error_log(sprintf("[%d] %s ::SQL:: %s",
  3940. mysql_errno(),
  3941. mysql_error(),
  3942. $sql
  3943. ));
  3944. return false;
  3945. }
  3946. return $rs;
  3947. }
  3948. function SelectLimit($sql, $limit, $start=0) {
  3949. $limit = intval($limit);
  3950. $start = intval($start);
  3951. if($limit > 0)
  3952. return $this->Execute($sql . sprintf(" LIMIT %d,%d", $start, $limit));
  3953. else
  3954. return $this->Execute($sql);
  3955. }
  3956. function qstr($string) {
  3957. return "'".mysql_real_escape_string($string, $this->_db)."'";
  3958. }
  3959. function GetArray($sql) {
  3960. $results = array();
  3961. if(false !== ($rs = $this->Execute($sql))) {
  3962. while($row = mysql_fetch_assoc($rs)) {
  3963. $results[] = $row;
  3964. }
  3965. mysql_free_result($rs);
  3966. }
  3967. return $results;
  3968. }
  3969. function GetRow($sql) {
  3970. if($rs = $this->Execute($sql)) {
  3971. $row = mysql_fetch_assoc($rs);
  3972. mysql_free_result($rs);
  3973. return $row;
  3974. }
  3975. return false;
  3976. }
  3977. function GetOne($sql) {
  3978. if(false !== ($rs = $this->Execute($sql))) {
  3979. $row = mysql_fetch_row($rs);
  3980. mysql_free_result($rs);
  3981. return $row[0];
  3982. }
  3983. return false;
  3984. }
  3985. function LastInsertId() {
  3986. return mysql_insert_id($this->_db);
  3987. }
  3988. function Affected_Rows() {
  3989. return mysql_affected_rows($this->_db);
  3990. }
  3991. function ErrorMsg() {
  3992. return mysql_error($this->_db);
  3993. }
  3994. };
  3995. class _DevblocksClassLoadManager {
  3996. const CACHE_CLASS_MAP = 'devblocks_classloader_map';
  3997. private static $instance = null;
  3998. private $classMap = array();
  3999. private function __construct() {
  4000. $cache = DevblocksPlatform::getCacheService();
  4001. if(null !== ($map = $cache->load(self::CACHE_CLASS_MAP))) {
  4002. $this->classMap = $map;
  4003. } else {
  4004. $this->_initLibs();
  4005. $this->_initPlugins();
  4006. $cache->save($this->classMap, self::CACHE_CLASS_MAP);
  4007. }
  4008. }
  4009. /**
  4010. * @return _DevblocksClassLoadManager
  4011. */
  4012. public static function getInstance() {
  4013. if(null == self::$instance) {
  4014. self::$instance = new _DevblocksClassLoadManager();
  4015. }
  4016. return self::$instance;
  4017. }
  4018. public function destroy() {
  4019. self::$instance = null;
  4020. }
  4021. public function loadClass($className) {
  4022. if(class_exists($className))
  4023. return;
  4024. @$file = $this->classMap[$className];
  4025. if(!is_null($file) && file_exists($file)) {
  4026. require_once($file);
  4027. } else {
  4028. // Not found
  4029. }
  4030. }
  4031. public function registerClasses($file,$classes=array()) {
  4032. if(is_array($classes))
  4033. foreach($classes as $class) {
  4034. $this->classMap[$class] = $file;
  4035. }
  4036. }
  4037. private function _initLibs() {
  4038. $this->registerClasses(DEVBLOCKS_PATH . 'libs/markdown/markdown.php', array(
  4039. 'Markdown_Parser'
  4040. ));
  4041. $this->registerClasses(DEVBLOCKS_PATH . 'libs/s3/S3.php', array(
  4042. 'S3'
  4043. ));
  4044. $this->registerClasses(DEVBLOCKS_PATH . 'libs/Twig/Autoloader.php', array(
  4045. 'Twig_Autoloader'
  4046. ));
  4047. }
  4048. private function _initPlugins() {
  4049. // Load all the exported classes defined by plugin manifests
  4050. $class_map = DAO_Platform::getClassLoaderMap();
  4051. if(is_array($class_map) && !empty($class_map))
  4052. foreach($class_map as $path => $classes) {
  4053. $this->registerClasses($path, $classes);
  4054. }
  4055. }
  4056. };
  4057. class _DevblocksLogManager {
  4058. static $_instance = null;
  4059. // Used the ZF classifications
  4060. private static $_log_levels = array(
  4061. 'emerg' => 0, // Emergency: system is unusable
  4062. 'emergency' => 0,
  4063. 'alert' => 1, // Alert: action must be taken immediately
  4064. 'crit' => 2, // Critical: critical conditions
  4065. 'critical' => 2,
  4066. 'err' => 3, // Error: error conditions
  4067. 'error' => 3,
  4068. 'warn' => 4, // Warning: warning conditions
  4069. 'warning' => 4,
  4070. 'notice' => 5, // Notice: normal but significant condition
  4071. 'info' => 6, // Informational: informational messages
  4072. 'debug' => 7, // Debug: debug messages
  4073. );
  4074. private $_log_level = 0;
  4075. private $_fp = null;
  4076. static function getConsoleLog() {
  4077. if(null == self::$_instance) {
  4078. self::$_instance = new _DevblocksLogManager();
  4079. }
  4080. return self::$_instance;
  4081. }
  4082. private function __construct() {
  4083. // Allow query string overloading Devblocks-wide
  4084. @$log_level = DevblocksPlatform::importGPC($_REQUEST['loglevel'],'integer', 0);
  4085. $this->_log_level = intval($log_level);
  4086. // Open file pointer
  4087. $this->_fp = fopen('php://output', 'w+');
  4088. }
  4089. public function __destruct() {
  4090. @fclose($this->_fp);
  4091. }
  4092. public function __call($name, $args) {
  4093. if(empty($args))
  4094. $args = array('');
  4095. if(isset(self::$_log_levels[$name])) {
  4096. if(self::$_log_levels[$name] <= $this->_log_level) {
  4097. $out = sprintf("[%s] %s<BR>\n",
  4098. strtoupper($name),
  4099. $args[0]
  4100. );
  4101. fputs($this->_fp, $out);
  4102. }
  4103. }
  4104. }
  4105. };
  4106. class _DevblocksUrlManager {
  4107. private static $instance = null;
  4108. private function __construct() {}
  4109. /**
  4110. * @return _DevblocksUrlManager
  4111. */
  4112. public static function getInstance() {
  4113. if(null == self::$instance) {
  4114. self::$instance = new _DevblocksUrlManager();
  4115. }
  4116. return self::$instance;
  4117. }
  4118. function parseQueryString($args) {
  4119. $argc = array();
  4120. if(empty($args)) return $argc;
  4121. $query = explode('&', $args);
  4122. if(is_array($query))
  4123. foreach($query as $q) {
  4124. if(empty($q)) continue;
  4125. $v = explode('=',$q);
  4126. if(empty($v)) continue;
  4127. @$argc[strtolower($v[0])] = $v[1];
  4128. }
  4129. return $argc;
  4130. }
  4131. function parseURL($url) {
  4132. // [JAS]: Use the index.php page as a reference to deconstruct the URI
  4133. $pos = stripos($_SERVER['SCRIPT_NAME'],'index.php',0);
  4134. if($pos === FALSE) return array();
  4135. // Decode proxy requests
  4136. if(isset($_SERVER['HTTP_DEVBLOCKSPROXYHOST'])) {
  4137. $url = urldecode($url);
  4138. }
  4139. // [JAS]: Extract the basedir of the path
  4140. $basedir = substr($url,0,$pos);
  4141. // [JAS]: Remove query string
  4142. $pos = stripos($url,'?',0);
  4143. if($pos !== FALSE) {
  4144. $url = substr($url,0,$pos);
  4145. }
  4146. $len = strlen($basedir);
  4147. if(!DEVBLOCKS_REWRITE) $len += strlen("index.php/");
  4148. $request = substr($url, $len);
  4149. if(empty($request)) return array();
  4150. $parts = explode('/', $request);
  4151. if(trim($parts[count($parts)-1]) == '') {
  4152. unset($parts[count($parts)-1]);
  4153. }
  4154. return $parts;
  4155. }
  4156. function write($sQuery='',$full=false,$check_proxy=true) {
  4157. $args = $this->parseQueryString($sQuery);
  4158. $c = @$args['c'];
  4159. // Allow proxy override
  4160. if($check_proxy) {
  4161. @$proxyssl = $_SERVER['HTTP_DEVBLOCKSPROXYSSL'];
  4162. @$proxyhost = $_SERVER['HTTP_DEVBLOCKSPROXYHOST'];
  4163. @$proxybase = $_SERVER['HTTP_DEVBLOCKSPROXYBASE'];
  4164. }
  4165. // Proxy (Community Tool)
  4166. if(!empty($proxyhost)) {
  4167. if($full) {
  4168. $prefix = sprintf("%s://%s%s/",
  4169. (!empty($proxyssl) ? 'https' : 'http'),
  4170. $proxyhost,
  4171. $proxybase
  4172. );
  4173. } else {
  4174. $prefix = $proxybase.'/';
  4175. }
  4176. // Index page
  4177. if(empty($sQuery)) {
  4178. return sprintf("%s",
  4179. $prefix
  4180. );
  4181. }
  4182. // [JAS]: Internal non-component URL (images/css/js/etc)
  4183. if(empty($c)) {
  4184. $contents = sprintf("%s%s",
  4185. $prefix,
  4186. $sQuery
  4187. );
  4188. // [JAS]: Component URL
  4189. } else {
  4190. $contents = sprintf("%s%s",
  4191. $prefix,
  4192. (!empty($args) ? implode('/',array_values($args)) : '')
  4193. );
  4194. }
  4195. // Devblocks App
  4196. } else {
  4197. if($full) {
  4198. $prefix = sprintf("%s://%s%s",
  4199. ($this->isSSL() ? 'https' : 'http'),
  4200. $_SERVER['HTTP_HOST'],
  4201. DEVBLOCKS_APP_WEBPATH
  4202. );
  4203. } else {
  4204. $prefix = DEVBLOCKS_APP_WEBPATH;
  4205. }
  4206. // Index page
  4207. if(empty($sQuery)) {
  4208. return sprintf("%s%s",
  4209. $prefix,
  4210. (DEVBLOCKS_REWRITE) ? '' : 'index.php/'
  4211. );
  4212. }
  4213. // [JAS]: Internal non-component URL (images/css/js/etc)
  4214. if(empty($c)) {
  4215. $contents = sprintf("%s%s",
  4216. $prefix,
  4217. $sQuery
  4218. );
  4219. // [JAS]: Component URL
  4220. } else {
  4221. if(DEVBLOCKS_REWRITE) {
  4222. $contents = sprintf("%s%s",
  4223. $prefix,
  4224. (!empty($args) ? implode('/',array_values($args)) : '')
  4225. );
  4226. } else {
  4227. $contents = sprintf("%sindex.php/%s",
  4228. $prefix,
  4229. (!empty($args) ? implode('/',array_values($args)) : '')
  4230. // (!empty($args) ? $sQuery : '')
  4231. );
  4232. }
  4233. }
  4234. }
  4235. return $contents;
  4236. }
  4237. /**
  4238. * Enter description here...
  4239. *
  4240. * @return boolean
  4241. */
  4242. public function isSSL() {
  4243. if(@$_SERVER["HTTPS"] == "on"){
  4244. return true;
  4245. } elseif (@$_SERVER["HTTPS"] == 1){
  4246. return true;
  4247. } elseif (@$_SERVER['SERVER_PORT'] == 443) {
  4248. return true;
  4249. } else {
  4250. return false;
  4251. }
  4252. }
  4253. /**
  4254. * Useful for converting DevblocksRequest and DevblocksResponse objects to a URL
  4255. */
  4256. function writeDevblocksHttpIO($request, $full=false) {
  4257. $url_parts = '';
  4258. if(is_array($request->path) && count($request->path) > 0)
  4259. $url_parts = 'c=' . array_shift($request->path);
  4260. if(!empty($request->path))
  4261. $url_parts .= '&f=' . implode('/', $request->path);
  4262. // Build the URL
  4263. $url = $this->write($url_parts, $full);
  4264. $query = '';
  4265. foreach($request->query as $key=>$val) {
  4266. $query .=
  4267. (empty($query)?'':'&') . // arg1=val1&arg2=val2
  4268. $key .
  4269. '=' .
  4270. $val
  4271. ;
  4272. }
  4273. if(!empty($query))
  4274. $url .= '?' . $query;
  4275. return $url;
  4276. }
  4277. };
  4278. // [TODO] Rename URLPing or some such nonsense, these don't proxy completely
  4279. class DevblocksProxy {
  4280. /**
  4281. * @return DevblocksProxy
  4282. */
  4283. static function getProxy() {
  4284. $proxy = null;
  4285. // Determine if CURL or FSOCK is available
  4286. if(function_exists('curl_exec')) {
  4287. $proxy = new DevblocksProxy_Curl();
  4288. } elseif(function_exists('fsockopen')) {
  4289. $proxy = new DevblocksProxy_Socket();
  4290. }
  4291. return $proxy;
  4292. }
  4293. function proxy($remote_host, $remote_uri) {
  4294. $this->_get($remote_host, $remote_uri);
  4295. }
  4296. function _get($remote_host, $remote_uri) {
  4297. die("Subclass abstract " . __CLASS__ . "...");
  4298. }
  4299. };
  4300. class DevblocksProxy_Socket extends DevblocksProxy {
  4301. function _get($remote_host, $remote_uri) {
  4302. $fp = fsockopen($remote_host, 80, $errno, $errstr, 10);
  4303. if ($fp) {
  4304. $out = "GET " . $remote_uri . " HTTP/1.1\r\n";
  4305. $out .= "Host: $remote_host\r\n";
  4306. $out .= 'Via: 1.1 ' . $_SERVER['HTTP_HOST'] . "\r\n";
  4307. $out .= "Connection: Close\r\n\r\n";
  4308. $this->_send($fp, $out);
  4309. }
  4310. }
  4311. function _send($fp, $out) {
  4312. fwrite($fp, $out);
  4313. while(!feof($fp)) {
  4314. fgets($fp,4096);
  4315. }
  4316. fclose($fp);
  4317. return;
  4318. }
  4319. };
  4320. class DevblocksProxy_Curl extends DevblocksProxy {
  4321. function _get($remote_host, $remote_uri) {
  4322. $url = 'http://' . $remote_host . $remote_uri;
  4323. $header = array();
  4324. $header[] = 'Via: 1.1 ' . $_SERVER['HTTP_HOST'];
  4325. $ch = curl_init();
  4326. curl_setopt($ch, CURLOPT_URL, $url);
  4327. curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
  4328. curl_setopt($ch, CURLOPT_HEADER, 0);
  4329. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 0);
  4330. // curl_setopt($ch, CURLOPT_TIMEOUT, 1);
  4331. // curl_setopt($ch, CURLOPT_TIMEOUT_MS, 500);
  4332. curl_exec($ch);
  4333. curl_close($ch);
  4334. }
  4335. };
  4336. interface DevblocksExtensionDelegate {
  4337. static function shouldLoadExtension(DevblocksExtensionManifest $extension_manifest);
  4338. };
  4339. function devblocks_autoload($className) {
  4340. return DevblocksPlatform::loadClass($className);
  4341. }
  4342. // Register Devblocks class loader
  4343. spl_autoload_register('devblocks_autoload');
  4344. // Register SwiftMailer
  4345. require_once(DEVBLOCKS_PATH . 'libs/swift/swift_required.php');
  4346. // Twig
  4347. if(class_exists('Twig_Autoloader', true) && method_exists('Twig_Autoloader','register'))
  4348. Twig_Autoloader::register();