PageRenderTime 8ms CodeModel.GetById 12ms app.highlight 46ms RepoModel.GetById 1ms app.codeStats 0ms

/Devblocks.class.php

https://github.com/Hildy/devblocks
PHP | 1005 lines | 570 code | 178 blank | 257 comment | 100 complexity | 590d5aae378d20b9847d6c27b32c7deb MD5 | raw file
   1<?php
   2include_once(DEVBLOCKS_PATH . "api/Engine.php");
   3include_once(DEVBLOCKS_PATH . "api/Model.php");
   4include_once(DEVBLOCKS_PATH . "api/DAO.php");
   5include_once(DEVBLOCKS_PATH . "api/Extension.php");
   6
   7define('PLATFORM_BUILD',305);
   8
   9/**
  10 *  @defgroup core Devblocks Framework Core
  11 *  Core data structures of the framework
  12 */
  13
  14/**
  15 *  @defgroup plugin Devblocks Framework Plugins
  16 *  Components for plugin/extensions
  17 */
  18
  19/**
  20 *  @defgroup services Devblocks Framework Services
  21 *  Services provided by the framework
  22 */
  23
  24/**
  25 * A platform container for plugin/extension registries.
  26 *
  27 * @ingroup core
  28 * @author Jeff Standen <jeff@webgroupmedia.com>
  29 */
  30class DevblocksPlatform extends DevblocksEngine {
  31    const CACHE_ACL = 'devblocks_acl';
  32    const CACHE_EVENT_POINTS = 'devblocks_event_points';
  33    const CACHE_EVENTS = 'devblocks_events';
  34    const CACHE_EXTENSIONS = 'devblocks_extensions';
  35    const CACHE_PLUGINS = 'devblocks_plugins';
  36    const CACHE_POINTS = 'devblocks_points';
  37    const CACHE_SETTINGS = 'devblocks_settings';
  38    const CACHE_TABLES = 'devblocks_tables';
  39    const CACHE_TAG_TRANSLATIONS = 'devblocks_translations';
  40    
  41    static private $extensionDelegate = null;
  42    
  43    static private $start_time = 0;
  44    static private $start_memory = 0;
  45    static private $start_peak_memory = 0;
  46    
  47    static private $locale = 'en_US';
  48    
  49    private function __construct() {}
  50
  51	/**
  52	 * @param mixed $var
  53	 * @param string $cast
  54	 * @param mixed $default
  55	 * @return mixed
  56	 */
  57	static function importGPC($var,$cast=null,$default=null) {
  58	    if(!is_null($var)) {
  59	        if(is_string($var)) {
  60	            $var = get_magic_quotes_gpc() ? stripslashes($var) : $var;
  61	        } elseif(is_array($var)) {
  62                foreach($var as $k => $v) {
  63                    $var[$k] = get_magic_quotes_gpc() ? stripslashes($v) : $v;
  64                }
  65	        }
  66	        
  67	    } elseif (is_null($var) && !is_null($default)) {
  68	        $var = $default;
  69	    }
  70	    
  71	    if(!is_null($cast))
  72	        @settype($var, $cast);
  73
  74	    return $var;
  75	}
  76
  77	/**
  78	 * Returns a string as a regexp. 
  79	 * "*bob" returns "/(.*?)bob/".
  80	 */
  81	static function parseStringAsRegExp($string) {
  82		$pattern = str_replace(array('*'),'__any__', $string);
  83		$pattern = sprintf("/%s/i",str_replace(array('__any__'),'(.*?)', preg_quote($pattern)));
  84		return $pattern;
  85	}
  86	
  87	static function parseCrlfString($string) {
  88		$parts = preg_split("/[\r\n]/", $string);
  89		
  90		// Remove any empty tokens
  91		foreach($parts as $idx => $part) {
  92			$parts[$idx] = trim($part);
  93			if(0 == strlen($parts[$idx])) 
  94				unset($parts[$idx]);
  95		}
  96		
  97		return $parts;
  98	}
  99	
 100	/**
 101	 * Returns a string as alphanumerics delimited by underscores.
 102	 * For example: "Devs: 1000 Ways to Improve Sales" becomes 
 103	 * "devs_1000_ways_to_improve_sales", which is suitable for 
 104	 * displaying in a URL of a blog, faq, etc.
 105	 *
 106	 * @param string $str
 107	 * @return string
 108	 */
 109	static function getStringAsURI($str) {
 110		$str = strtolower($str);
 111		
 112		// turn non [a-z, 0-9, _] into whitespace
 113		$str = preg_replace("/[^0-9a-z]/",' ',$str);
 114		
 115		// condense whitespace to a single underscore
 116		$str = preg_replace('/\s\s+/', ' ', $str);
 117
 118		// replace spaces with underscore
 119		$str = str_replace(' ','_',$str);
 120
 121		// remove a leading/trailing underscores
 122		$str = trim($str, '_');
 123		
 124		return $str;
 125	}
 126	
 127	/**
 128	 * Takes a comma-separated value string and returns an array of tokens.
 129	 * [TODO] Move to a FormHelper service?
 130	 * 
 131	 * @param string $string
 132	 * @return array
 133	 */
 134	static function parseCsvString($string) {
 135		$tokens = explode(',', $string);
 136
 137		if(!is_array($tokens))
 138			return array();
 139		
 140		foreach($tokens as $k => $v) {
 141			$tokens[$k] = trim($v);
 142			if(0==strlen($tokens[$k]))
 143				unset($tokens[$k]);
 144		}
 145		
 146		return $tokens;
 147	}
 148	
 149	/**
 150	 * Clears any platform-level plugin caches.
 151	 * 
 152	 */
 153	static function clearCache() {
 154	    $cache = self::getCacheService(); /* @var $cache Zend_Cache_Core */
 155	    $cache->remove(self::CACHE_ACL);
 156	    $cache->remove(self::CACHE_PLUGINS);
 157	    $cache->remove(self::CACHE_EVENT_POINTS);
 158	    $cache->remove(self::CACHE_EVENTS);
 159	    $cache->remove(self::CACHE_EXTENSIONS);
 160	    $cache->remove(self::CACHE_POINTS);
 161	    $cache->remove(self::CACHE_SETTINGS);
 162	    $cache->remove(self::CACHE_TABLES);
 163	    $cache->remove(_DevblocksClassLoadManager::CACHE_CLASS_MAP);
 164
 165	    // Clear all locale caches
 166	    $langs = DAO_Translation::getDefinedLangCodes();
 167	    if(is_array($langs) && !empty($langs))
 168	    foreach($langs as $lang_code => $lang_name) {
 169	    	$cache->remove(self::CACHE_TAG_TRANSLATIONS . '_' . $lang_code);
 170	    }
 171	    
 172	    // Recache plugins
 173		self::getPluginRegistry();
 174		self::getExtensionRegistry();
 175	}
 176
 177	public static function loadClass($className) {
 178		$classloader = self::getClassLoaderService();
 179		return $classloader->loadClass($className);
 180	}
 181	
 182	public static function registerClasses($file,$classes=array()) {
 183		$classloader = self::getClassLoaderService();
 184		return $classloader->registerClasses($file,$classes);
 185	}
 186	
 187	public static function getStartTime() {
 188		return self::$start_time;
 189	}
 190	
 191	public static function getStartMemory() {
 192		return self::$start_memory;
 193	}
 194	
 195	public static function getStartPeakMemory() {
 196		return self::$start_peak_memory;
 197	}
 198	
 199	/**
 200	 * Checks whether the active database has any tables.
 201	 * 
 202	 * @return boolean
 203	 */
 204	static function isDatabaseEmpty() {
 205		$tables = self::getDatabaseTables();
 206	    return empty($tables);
 207	}
 208	
 209	static function getDatabaseTables() {
 210	    $cache = self::getCacheService();
 211	    $tables = array();
 212	    
 213	    if(null === ($tables = $cache->load(self::CACHE_TABLES))) {
 214	        $db = self::getDatabaseService(); /* @var $db ADODB_Connection */
 215	        
 216	        // Make sure the database connection is valid or error out.
 217	        if(is_null($db) || !$db->IsConnected())
 218	        	return array();
 219	        
 220	        $tables = $db->MetaTables('TABLE',false);
 221	        $cache->save($tables, self::CACHE_TABLES);
 222	    }
 223	    return $tables;
 224	}
 225
 226	/**
 227	 * Checks to see if the application needs to patch
 228	 *
 229	 * @return boolean
 230	 */
 231	static function versionConsistencyCheck() {
 232		$cache = DevblocksPlatform::getCacheService(); /* @var Zend_Cache_Core $cache */ 
 233		
 234		if(null === ($build_cache = $cache->load("devblocks_app_build"))
 235			|| $build_cache != APP_BUILD) {
 236				
 237			// If build changed, clear cache regardless of patch status
 238			// [TODO] We need to find a nicer way to not clear a shared memcached cluster when only one desk needs to
 239			$cache = DevblocksPlatform::getCacheService(); /* @var $cache Zend_Cache_Core */
 240			$cache->clean('all');
 241			
 242			// Re-read manifests
 243			DevblocksPlatform::readPlugins();
 244				
 245			if(self::_needsToPatch()) {
 246				return false; // the update script will handle new caches
 247			} else {
 248				$cache->save(APP_BUILD, "devblocks_app_build");
 249				DAO_Translation::reloadPluginStrings(); // reload strings even without DB changes
 250				return true;
 251			}
 252		}
 253		
 254		return true;
 255	}
 256	
 257	/**
 258	 * Enter description here...
 259	 *
 260	 * @return boolean
 261	 */
 262	static private function _needsToPatch() {
 263		 $plugins = DevblocksPlatform::getPluginRegistry();
 264		 $containers = DevblocksPlatform::getExtensions("devblocks.patch.container", true, true);
 265
 266		 // [JAS]: Devblocks
 267		 array_unshift($containers, new PlatformPatchContainer());
 268		 
 269		 foreach($containers as $container) { /* @var $container DevblocksPatchContainerExtension */
 270			foreach($container->getPatches() as $patch) { /* @var $patch DevblocksPatch */
 271				if(!$patch->hasRun()) {
 272//					echo "Need to run a patch: ",$patch->getPluginId(),$patch->getRevision();
 273					return true;
 274				}
 275			}
 276		 }
 277		 
 278//		 echo "Don't need to run any patches.";
 279		 return false;
 280	}
 281	
 282	/**
 283	 * Runs patches for all active plugins
 284	 *
 285	 * @return boolean
 286	 */
 287	static function runPluginPatches() {
 288	    // Log out all sessions before patching
 289		$db = DevblocksPlatform::getDatabaseService();
 290		$db->Execute(sprintf("DELETE FROM %s_session", APP_DB_PREFIX));
 291		
 292		$patchMgr = DevblocksPlatform::getPatchService();
 293		
 294//		echo "Patching platform... ";
 295		
 296		// [JAS]: Run our overloaded container for the platform
 297		$patchMgr->registerPatchContainer(new PlatformPatchContainer());
 298		
 299		// Clean script
 300		if(!$patchMgr->run()) {
 301			return false;
 302		    
 303		} else { // success
 304			// Read in plugin information from the filesystem to the database
 305			DevblocksPlatform::readPlugins();
 306			
 307			$plugins = DevblocksPlatform::getPluginRegistry();
 308			
 309//			DevblocksPlatform::clearCache();
 310			
 311			// Run enabled plugin patches
 312			$patches = DevblocksPlatform::getExtensions("devblocks.patch.container",false,true);
 313			
 314			if(is_array($patches))
 315			foreach($patches as $patch_manifest) { /* @var $patch_manifest DevblocksExtensionManifest */ 
 316				if(null != ($container = $patch_manifest->createInstance())) { /* @var $container DevblocksPatchContainerExtension */
 317					if(!is_null($container)) {
 318						$patchMgr->registerPatchContainer($container);
 319					}
 320				}
 321			}
 322			
 323//			echo "Patching plugins... ";
 324			
 325			if(!$patchMgr->run()) { // fail
 326				return false;
 327			}
 328			
 329//			echo "done!<br>";
 330
 331			$cache = self::getCacheService();
 332			$cache->save(APP_BUILD, "devblocks_app_build");
 333
 334			return true;
 335		}
 336	}
 337	
 338	/**
 339	 * Returns the list of extensions on a given extension point.
 340	 *
 341	 * @static
 342	 * @param string $point
 343	 * @return DevblocksExtensionManifest[]
 344	 */
 345	static function getExtensions($point,$as_instances=false, $ignore_acl=false) {
 346	    $results = array();
 347	    $extensions = DevblocksPlatform::getExtensionRegistry($ignore_acl);
 348
 349	    if(is_array($extensions))
 350	    foreach($extensions as $extension) { /* @var $extension DevblocksExtensionManifest */
 351	        if(0 == strcasecmp($extension->point,$point)) {
 352	            $results[$extension->id] = ($as_instances) ? $extension->createInstance() : $extension;
 353	        }
 354	    }
 355	    return $results;
 356	}
 357
 358	/**
 359	 * Returns the manifest of a given extension ID.
 360	 *
 361	 * @static
 362	 * @param string $extension_id
 363	 * @param boolean $as_instance
 364	 * @return DevblocksExtensionManifest
 365	 */
 366	static function getExtension($extension_id, $as_instance=false, $ignore_acl=false) {
 367	    $result = null;
 368	    $extensions = DevblocksPlatform::getExtensionRegistry($ignore_acl);
 369
 370	    if(is_array($extensions))
 371	    foreach($extensions as $extension) { /* @var $extension DevblocksExtensionManifest */
 372	        if(0 == strcasecmp($extension->id,$extension_id)) {
 373	            $result = $extension;
 374	            break;
 375	        }
 376	    }
 377
 378	    if($as_instance && !is_null($result)) {
 379	    	return $result->createInstance();
 380	    }
 381	    
 382	    return $result;
 383	}
 384
 385//	static function getExtensionPoints() {
 386//	    $cache = self::getCacheService();
 387//	    if(null !== ($points = $cache->load(self::CACHE_POINTS)))
 388//	        return $points;
 389//
 390//	    $extensions = DevblocksPlatform::getExtensionRegistry();
 391//	    foreach($extensions as $extension) { /* @var $extension DevblocksExtensionManifest */
 392//	        $point = $extension->point;
 393//	        if(!isset($points[$point])) {
 394//	            $p = new DevblocksExtensionPoint();
 395//	            $p->id = $point;
 396//	            $points[$point] = $p;
 397//	        }
 398//	        	
 399//	        $points[$point]->extensions[$extension->id] = $extension;
 400//	    }
 401//
 402//	    $cache->save($points, self::CACHE_POINTS);
 403//	    return $points;
 404//	}
 405
 406	/**
 407	 * Returns an array of all contributed extension manifests.
 408	 *
 409	 * @static 
 410	 * @return DevblocksExtensionManifest[]
 411	 */
 412	static function getExtensionRegistry($ignore_acl=false) {
 413	    $cache = self::getCacheService();
 414	    
 415	    if(null === ($extensions = $cache->load(self::CACHE_EXTENSIONS))) {
 416		    $db = DevblocksPlatform::getDatabaseService();
 417		    if(is_null($db)) return;
 418	
 419		    $prefix = (APP_DB_PREFIX != '') ? APP_DB_PREFIX.'_' : ''; // [TODO] Cleanup
 420	
 421		    $sql = sprintf("SELECT e.id , e.plugin_id, e.point, e.pos, e.name , e.file , e.class, e.params ".
 422				"FROM %sextension e ".
 423				"INNER JOIN %splugin p ON (e.plugin_id=p.id) ".
 424				"WHERE p.enabled = 1 ".
 425				"ORDER BY e.plugin_id ASC, e.pos ASC",
 426					$prefix,
 427					$prefix
 428				);
 429			$rs = $db->Execute($sql); /* @var $rs ADORecordSet */
 430				
 431			if(is_a($rs,'ADORecordSet'))
 432			while(!$rs->EOF) {
 433			    $extension = new DevblocksExtensionManifest();
 434			    $extension->id = $rs->fields['id'];
 435			    $extension->plugin_id = $rs->fields['plugin_id'];
 436			    $extension->point = $rs->fields['point'];
 437			    $extension->name = $rs->fields['name'];
 438			    $extension->file = $rs->fields['file'];
 439			    $extension->class = $rs->fields['class'];
 440			    $extension->params = @unserialize($rs->fields['params']);
 441		
 442			    if(empty($extension->params))
 443					$extension->params = array();
 444				$extensions[$extension->id] = $extension;
 445			    $rs->MoveNext();
 446			}
 447
 448			$cache->save($extensions, self::CACHE_EXTENSIONS);
 449		}
 450		
 451		// Check with an extension delegate if we have one
 452		if(!$ignore_acl && class_exists(self::$extensionDelegate) && method_exists('DevblocksExtensionDelegate','shouldLoadExtension')) {
 453			if(is_array($extensions))
 454			foreach($extensions as $id => $extension) {
 455				// Ask the delegate if we should load the extension
 456				if(!call_user_func(array(self::$extensionDelegate,'shouldLoadExtension'),$extension))
 457					unset($extensions[$id]);
 458			}
 459		}
 460		
 461		return $extensions;
 462	}
 463
 464	/**
 465	 * @return DevblocksEventPoint[]
 466	 */
 467	static function getEventPointRegistry() {
 468	    $cache = self::getCacheService();
 469	    if(null !== ($events = $cache->load(self::CACHE_EVENT_POINTS)))
 470    	    return $events;
 471
 472        $events = array();
 473        $plugins = self::getPluginRegistry();
 474    	 
 475		// [JAS]: Event point hashing/caching
 476		if(is_array($plugins))
 477		foreach($plugins as $plugin) { /* @var $plugin DevblocksPluginManifest */
 478            $events = array_merge($events,$plugin->event_points);
 479		}
 480    	
 481		$cache->save($events, self::CACHE_EVENT_POINTS);
 482		return $events;
 483	}
 484	
 485	/**
 486	 * @return DevblocksAclPrivilege[]
 487	 */
 488	static function getAclRegistry() {
 489	    $cache = self::getCacheService();
 490	    if(null !== ($acl = $cache->load(self::CACHE_ACL)))
 491    	    return $acl;
 492
 493        $acl = array();
 494
 495	    $db = DevblocksPlatform::getDatabaseService();
 496	    if(is_null($db)) return;
 497
 498        //$plugins = self::getPluginRegistry();
 499	    $prefix = (APP_DB_PREFIX != '') ? APP_DB_PREFIX.'_' : ''; // [TODO] Cleanup
 500
 501	    $sql = sprintf("SELECT a.id, a.plugin_id, a.label ".
 502			"FROM %sacl a ".
 503			"INNER JOIN %splugin p ON (a.plugin_id=p.id) ".
 504			"WHERE p.enabled = 1 ".
 505			"ORDER BY a.plugin_id, a.id ASC",
 506			$prefix,
 507			$prefix
 508		);
 509		$rs = $db->Execute($sql); /* @var $rs ADORecordSet */
 510		
 511		if(is_a($rs,'ADORecordSet'))
 512		while(!$rs->EOF) {
 513			$priv = new DevblocksAclPrivilege();
 514			$priv->id = $rs->fields['id'];
 515			$priv->plugin_id = $rs->fields['plugin_id'];
 516			$priv->label = $rs->fields['label'];
 517			
 518		    $acl[$priv->id] = $priv;
 519		    
 520		    $rs->MoveNext();
 521		}
 522        
 523		$cache->save($acl, self::CACHE_ACL);
 524		return $acl;
 525	}
 526	
 527	static function getEventRegistry() {
 528	    $cache = self::getCacheService();
 529	    if(null !== ($events = $cache->load(self::CACHE_EVENTS)))
 530    	    return $events;
 531	    
 532    	$extensions = self::getExtensions('devblocks.listener.event',false,true);
 533    	$events = array('*');
 534    	 
 535		// [JAS]: Event point hashing/caching
 536		if(is_array($extensions))
 537		foreach($extensions as $extension) { /* @var $extension DevblocksExtensionManifest */
 538            @$evts = $extension->params['events'][0];
 539            
 540            // Global listeners (every point)
 541            if(empty($evts) && !is_array($evts)) {
 542                $events['*'][] = $extension->id;
 543                continue;
 544            }
 545            
 546            if(is_array($evts))
 547            foreach(array_keys($evts) as $evt) {
 548                $events[$evt][] = $extension->id; 
 549            }
 550		}
 551    	
 552		$cache->save($events, self::CACHE_EVENTS);
 553		return $events;
 554	}
 555	
 556	/**
 557	 * Returns an array of all contributed plugin manifests.
 558	 *
 559	 * @static
 560	 * @return DevblocksPluginManifest[]
 561	 */
 562	static function getPluginRegistry() {
 563	    $cache = self::getCacheService();
 564	    if(null !== ($plugins = $cache->load(self::CACHE_PLUGINS)))
 565    	    return $plugins;
 566
 567	    $db = DevblocksPlatform::getDatabaseService();
 568	    if(is_null($db)) return;
 569	    $prefix = (APP_DB_PREFIX != '') ? APP_DB_PREFIX.'_' : ''; // [TODO] Cleanup
 570
 571	    $sql = sprintf("SELECT p.id, p.enabled, p.name, p.description, p.author, p.revision, p.link, p.dir, p.templates_json ".
 572			"FROM %splugin p ".
 573			"ORDER BY p.enabled DESC, p.name ASC ",
 574			$prefix
 575		);
 576		$rs = $db->Execute($sql); /* @var $rs ADORecordSet */
 577		
 578		if(is_a($rs,'ADORecordSet'))
 579		while(!$rs->EOF) {
 580		    $plugin = new DevblocksPluginManifest();
 581		    @$plugin->id = $rs->fields['id'];
 582		    @$plugin->enabled = intval($rs->fields['enabled']);
 583		    @$plugin->name = $rs->fields['name'];
 584		    @$plugin->description = $rs->fields['description'];
 585		    @$plugin->author = $rs->fields['author'];
 586		    @$plugin->revision = intval($rs->fields['revision']);
 587		    @$plugin->link = $rs->fields['link'];
 588		    @$plugin->dir = $rs->fields['dir'];
 589
 590		    // JSON decode templates
 591		    if(null != ($templates_json = $rs->Fields('templates_json'))) {
 592		    	$plugin->templates = json_decode($templates_json, true);
 593		    }
 594		    
 595		    if(file_exists(APP_PATH . DIRECTORY_SEPARATOR . $plugin->dir . DIRECTORY_SEPARATOR . 'plugin.xml')) {
 596		        $plugins[$plugin->id] = $plugin;
 597		    }
 598		    	
 599		    $rs->MoveNext();
 600		}
 601
 602		$sql = sprintf("SELECT p.id, p.name, p.params, p.plugin_id ".
 603		    "FROM %sevent_point p ",
 604		    $prefix
 605		);
 606		$rs = $db->Execute($sql); /* @var $rs ADORecordSet */
 607
 608		if(is_a($rs,'ADORecordSet'))
 609		while(!$rs->EOF) {
 610		    $point = new DevblocksEventPoint();
 611		    $point->id = $rs->fields['id'];
 612		    $point->name = $rs->fields['name'];
 613		    $point->plugin_id = $rs->fields['plugin_id'];
 614		    
 615		    $params = $rs->fields['params'];
 616		    $point->params = !empty($params) ? unserialize($params) : array();
 617
 618		    if(isset($plugins[$point->plugin_id])) {
 619		    	$plugins[$point->plugin_id]->event_points[$point->id] = $point;
 620		    }
 621		    
 622		    $rs->MoveNext();
 623		}
 624			
 625		$cache->save($plugins, self::CACHE_PLUGINS);
 626		return $plugins;
 627	}
 628
 629	/**
 630	 * Enter description here...
 631	 *
 632	 * @param string $id
 633	 * @return DevblocksPluginManifest
 634	 */
 635	static function getPlugin($id) {
 636	    $plugins = DevblocksPlatform::getPluginRegistry();
 637
 638	    if(isset($plugins[$id]))
 639	    	return $plugins[$id];
 640		
 641	    return null;
 642	}
 643
 644	/**
 645	 * Reads and caches manifests from the features + plugins directories.
 646	 *
 647	 * @static 
 648	 * @return DevblocksPluginManifest[]
 649	 */
 650	static function readPlugins() {
 651		$scan_dirs = array(
 652			'features',
 653			'storage/plugins',
 654		);
 655		
 656	    $plugins = array();
 657
 658	    if(is_array($scan_dirs))
 659	    foreach($scan_dirs as $scan_dir) {
 660	    	$scan_path = APP_PATH . '/' . $scan_dir;
 661		    if (is_dir($scan_path)) {
 662		        if ($dh = opendir($scan_path)) {
 663		            while (($file = readdir($dh)) !== false) {
 664		                if($file=="." || $file == "..")
 665		                	continue;
 666		                	
 667		                $plugin_path = $scan_path . '/' . $file;
 668		                $rel_path = $scan_dir . '/' . $file;
 669		                
 670		                if(is_dir($plugin_path) && file_exists($plugin_path.'/plugin.xml')) {
 671		                    $manifest = self::_readPluginManifest($rel_path); /* @var $manifest DevblocksPluginManifest */
 672	
 673		                    if(null != $manifest) {
 674		                        $plugins[] = $manifest;
 675		                    }
 676		                }
 677		            }
 678		            closedir($dh);
 679		        }
 680		    }
 681	    }
 682	    
 683		// [TODO] Instance the plugins in dependency order
 684
 685	    DAO_Platform::cleanupPluginTables();
 686	    DevblocksPlatform::clearCache();
 687	    
 688	    return $plugins;
 689	}
 690
 691	/**
 692	 * @return _DevblocksPluginSettingsManager
 693	 */
 694	static function getPluginSettingsService() {
 695		return _DevblocksPluginSettingsManager::getInstance();
 696	}
 697
 698	/**
 699	 * @return Zend_Log
 700	 */
 701	static function getConsoleLog() {
 702		return _DevblocksLogManager::getConsoleLog();
 703	}
 704	
 705	/**
 706	 * @return _DevblocksCacheManager
 707	 */
 708	static function getCacheService() {
 709	    return _DevblocksCacheManager::getInstance();
 710	}
 711	
 712	/**
 713	 * Enter description here...
 714	 *
 715	 * @return ADOConnection
 716	 */
 717	static function getDatabaseService() {
 718	    return _DevblocksDatabaseManager::getInstance();
 719	}
 720
 721	/**
 722	 * @return _DevblocksPatchManager
 723	 */
 724	static function getPatchService() {
 725	    return _DevblocksPatchManager::getInstance();
 726	}
 727
 728	/**
 729	 * @return _DevblocksUrlManager
 730	 */
 731	static function getUrlService() {
 732	    return _DevblocksUrlManager::getInstance();
 733	}
 734
 735	/**
 736	 * @return _DevblocksEmailManager
 737	 */
 738	static function getMailService() {
 739	    return _DevblocksEmailManager::getInstance();
 740	}
 741
 742	/**
 743	 * @return _DevblocksEventManager
 744	 */
 745	static function getEventService() {
 746	    return _DevblocksEventManager::getInstance();
 747	}
 748	
 749	/**
 750	 * @return DevblocksProxy
 751	 */
 752	static function getProxyService() {
 753	    return DevblocksProxy::getProxy();
 754	}
 755	
 756	/**
 757	 * @return _DevblocksClassLoadManager
 758	 */
 759	static function getClassLoaderService() {
 760		return _DevblocksClassLoadManager::getInstance();
 761	}
 762	
 763	/**
 764	 * @return _DevblocksSessionManager
 765	 */
 766	static function getSessionService() {
 767	    return _DevblocksSessionManager::getInstance();
 768	}
 769
 770	/**
 771	 * @return Smarty
 772	 */
 773	static function getTemplateService() {
 774	    return _DevblocksTemplateManager::getInstance();
 775	}
 776
 777	/**
 778	 * 
 779	 * @param string $set
 780	 * @return DevblocksTemplate[]
 781	 */
 782	static function getTemplates($set=null) {
 783		$templates = array();
 784		$plugins = self::getPluginRegistry();
 785		
 786		if(is_array($plugins))
 787		foreach($plugins as $plugin) {
 788			if(is_array($plugin->templates))
 789			foreach($plugin->templates as $tpl) {
 790				if(null === $set || 0 == strcasecmp($set, $tpl['set'])) {
 791					$template = new DevblocksTemplate();
 792					$template->plugin_id = $tpl['plugin_id'];
 793					$template->set = $tpl['set'];
 794					$template->path = $tpl['path'];
 795					$templates[] = $template;
 796				}
 797			}
 798		}
 799		
 800		return $templates;
 801	}
 802	
 803	/**
 804	 * @return _DevblocksTemplateBuilder
 805	 */
 806	static function getTemplateBuilder() {
 807	    return _DevblocksTemplateBuilder::getInstance();
 808	}
 809
 810	/**
 811	 * @return _DevblocksDateManager
 812	 */
 813	static function getDateService($datestamp=null) {
 814		return _DevblocksDateManager::getInstance();
 815	}
 816
 817	static function setLocale($locale) {
 818		@setlocale(LC_ALL, $locale);
 819		self::$locale = $locale;
 820	}
 821	
 822	static function getLocale() {
 823		if(!empty(self::$locale))
 824			return self::$locale;
 825			
 826		return 'en_US';
 827	}
 828	
 829	/**
 830	 * @return _DevblocksTranslationManager
 831	 */
 832	static function getTranslationService() {
 833		$locale = DevblocksPlatform::getLocale();
 834
 835	    if(Zend_Registry::isRegistered('Devblocks:getTranslationService:'.$locale)) {
 836			return Zend_Registry::get('Devblocks:getTranslationService:'.$locale);
 837		}
 838						
 839		$cache = self::getCacheService();
 840	    
 841	    if(null === ($map = $cache->load(self::CACHE_TAG_TRANSLATIONS.'_'.$locale))) { /* @var $cache Zend_Cache_Core */
 842			$map = array();
 843			$map_en = DAO_Translation::getMapByLang('en_US');
 844			if(0 != strcasecmp('en_US', $locale))
 845				$map_loc = DAO_Translation::getMapByLang($locale);
 846			
 847			// Loop through the English string objects
 848			if(is_array($map_en))
 849			foreach($map_en as $string_id => $obj_string_en) {
 850				$string = '';
 851				
 852				// If we have a locale to check
 853				if(isset($map_loc) && is_array($map_loc)) {
 854					@$obj_string_loc = $map_loc[$string_id];
 855					@$string =
 856						(!empty($obj_string_loc->string_override))
 857						? $obj_string_loc->string_override
 858						: $obj_string_loc->string_default;
 859				}
 860				
 861				// If we didn't hit, load the English default
 862				if(empty($string))
 863				@$string = 
 864					(!empty($obj_string_en->string_override))
 865					? $obj_string_en->string_override
 866					: $obj_string_en->string_default;
 867					
 868				// If we found any match
 869				if(!empty($string))
 870					$map[$string_id] = $string;
 871			}
 872			unset($obj_string_en);
 873			unset($obj_string_loc);
 874			unset($map_en);
 875			unset($map_loc);
 876			
 877			// Cache with tag (tag allows easy clean for multiple langs at once)
 878			$cache->save($map,self::CACHE_TAG_TRANSLATIONS.'_'.$locale,array(self::CACHE_TAG_TRANSLATIONS));
 879	    }
 880	    
 881		$translate = _DevblocksTranslationManager::getInstance();
 882		$translate->addLocale($locale, $map);
 883		$translate->setLocale($locale);
 884	    
 885		Zend_Registry::set('Devblocks:getTranslationService:'.$locale, $translate);
 886
 887	    return $translate;
 888	}
 889
 890	/**
 891	 * Enter description here...
 892	 *
 893	 * @return DevblocksHttpRequest
 894	 */
 895	static function getHttpRequest() {
 896	    return self::$request;
 897	}
 898
 899	/**
 900	 * @param DevblocksHttpRequest $request
 901	 */
 902	static function setHttpRequest(DevblocksHttpRequest $request) {
 903	    self::$request = $request;
 904	}
 905
 906	/**
 907	 * Enter description here...
 908	 *
 909	 * @return DevblocksHttpRequest
 910	 */
 911	static function getHttpResponse() {
 912	    return self::$response;
 913	}
 914
 915	/**
 916	 * @param DevblocksHttpResponse $response
 917	 */
 918	static function setHttpResponse(DevblocksHttpResponse $response) {
 919	    self::$response = $response;
 920	}
 921
 922	/**
 923	 * Initializes the plugin platform (paths, etc).
 924	 *
 925	 * @static 
 926	 * @return void
 927	 */
 928	static function init() {
 929		self::$start_time = microtime(true);
 930		if(function_exists('memory_get_usage') && function_exists('memory_get_peak_usage')) {
 931			self::$start_memory = memory_get_usage();
 932			self::$start_peak_memory = memory_get_peak_usage();
 933		}
 934		
 935		// Encoding (mbstring)
 936		mb_internal_encoding(LANG_CHARSET_CODE);
 937		
 938	    // [JAS] [MDF]: Automatically determine the relative webpath to Devblocks files
 939	    @$proxyhost = $_SERVER['HTTP_DEVBLOCKSPROXYHOST'];
 940	    @$proxybase = $_SERVER['HTTP_DEVBLOCKSPROXYBASE'];
 941    
 942		// App path (always backend)
 943	
 944		$app_self = $_SERVER["PHP_SELF"];
 945		
 946        if(DEVBLOCKS_REWRITE) {
 947            $pos = strrpos($app_self,'/');
 948            $app_self = substr($app_self,0,$pos) . '/';
 949        } else {
 950            $pos = strrpos($app_self,'index.php');
 951            if(false === $pos) $pos = strrpos($app_self,'ajax.php');
 952            $app_self = substr($app_self,0,$pos);
 953        }
 954		
 955		// Context path (abstracted: proxies or backend)
 956		
 957        if(!empty($proxybase)) { // proxy
 958            $context_self = $proxybase . '/';
 959		} else { // non-proxy
 960			$context_self = $app_self;
 961		}
 962		
 963        @define('DEVBLOCKS_WEBPATH',$context_self);
 964        @define('DEVBLOCKS_APP_WEBPATH',$app_self);
 965	}
 966
 967//	static function setPluginDelegate($class) {
 968//		if(!empty($class) && class_exists($class, true))
 969//			self::$pluginDelegate = $class;
 970//	}
 971	
 972	static function setExtensionDelegate($class) {
 973		if(!empty($class) && class_exists($class, true))
 974			self::$extensionDelegate = $class;
 975	}
 976	
 977	static function redirect(DevblocksHttpIO $httpIO) {
 978		$url_service = self::getUrlService();
 979		session_write_close();
 980		$url = $url_service->writeDevblocksHttpIO($httpIO, true);
 981		header('Location: '.$url);
 982		exit;
 983	}
 984};
 985
 986// [TODO] This doesn't belong! (ENGINE)
 987class PlatformPatchContainer extends DevblocksPatchContainerExtension {
 988	
 989	function __construct() {
 990		parent::__construct(null);
 991		
 992		/*
 993		 * [JAS]: Just add a build number here (from your commit revision) and write a
 994		 * case in runBuild().  You should comment the milestone next to your build 
 995		 * number.
 996		 */
 997
 998		$file_prefix = dirname(__FILE__) . '/patches/';
 999
1000		$this->registerPatch(new DevblocksPatch('devblocks.core',1,$file_prefix.'1.0.0.php',''));
1001		$this->registerPatch(new DevblocksPatch('devblocks.core',253,$file_prefix.'1.0.0_beta.php',''));
1002		$this->registerPatch(new DevblocksPatch('devblocks.core',290,$file_prefix.'1.1.0.php',''));
1003		$this->registerPatch(new DevblocksPatch('devblocks.core',297,$file_prefix.'2.0.0.php',''));
1004	}
1005};