<commit_before><?php

	/**
	 * The core class of the B2 engine
	 *
	 * @author <NAME> <<EMAIL>>
	 ** @version 3.0
	 * @license http://www.opensource.org/licenses/mozilla1.1.php Mozilla Public License 1.1 (MPL 1.1)
	 * @package thebuggenie
	 * @subpackage core
	 */

	/**
	 * The core class of the B2 engine
	 *
	 * @package thebuggenie
	 * @subpackage core
	 */
	class TBGContext
	{

		const PREDEFINED_SEARCH_PROJECT_OPEN_ISSUES = 1;
		const PREDEFINED_SEARCH_PROJECT_CLOSED_ISSUES = 2;
		const PREDEFINED_SEARCH_PROJECT_MILESTONE_TODO = 6;
		const PREDEFINED_SEARCH_PROJECT_MOST_VOTED = 7;
		const PREDEFINED_SEARCH_MY_ASSIGNED_OPEN_ISSUES = 3;
		const PREDEFINED_SEARCH_TEAM_ASSIGNED_OPEN_ISSUES = 4;
		const PREDEFINED_SEARCH_MY_REPORTED_ISSUES = 5;
		
		static protected $_environment = 2;

		static protected $debug_mode = true;

		/**
		 * The current user
		 *
		 * @var TBGUser
		 */
		static protected $_user = null;
		
		/**
		 * List of modules 
		 * 
		 * @var array
		 */
		static protected $_modules = null;
		
		/**
		 * List of module permissions
		 * 
		 * @var array
		 */
		static protected $_modulepermissions = array();
		
		/**
		 * List of permissions
		 *  
		 * @var array
		 */
		static protected $_permissions = array();
		
		/**
		 * List of available permissions
		 * 
		 * @var array
		 */
		static protected $_available_permissions = null;
		
		/**
		 * The include path
		 * 
		 * @var string
		 */
		static protected $_includepath = null;
		
		/**
		 * The path to thebuggenie relative from url server root
		 * 
		 * @var string
		 */
		static protected $_tbgpath = null;
		
		/**
		 * Stripped version of the $_tbgpath
		 * 
		 * @see $_tbgpath
		 * 
		 * @var string
		 */
		static protected $_stripped_tbgpath = null;
		
		/**
		 * Whether we're in installmode or not
		 * 
		 * @var boolean
		 */
		static protected $_installmode = false;
		
		/**
		 * The i18n object
		 *
		 * @var TBGI18n
		 */
		static protected $_i18n = null;
		
		/**
		 * The request object
		 * 
		 * @var TBGRequest
		 */
		static protected $_request = null;
		
		/**
		 * The response object
		 * 
		 * @var TBGResponse
		 */
		static protected $_response = null;
		
		/**
		 * The current scope object
		 *
		 * @var TBGScope
		 */
		static protected $_scope = null;

		/**
		 * The TBGFactory instance
		 *
		 * @var TBGFactory
		 */
		static protected $_factory = null;
		
		/**
		 * The currently selected project, if any
		 * 
		 * @var TBGProject
		 */
		static protected $_selected_project = null;
		
		/**
		 * The currently selected client, if any
		 * 
		 * @var TBGClient
		 */
		static protected $_selected_client = null;
		
		/**
		 * Used to determine when the b2 engine started loading
		 * 
		 * @var integer
		 */
		static protected $_loadstart = null;
		
		/**
		 * Used for timing purposes
		 * 
		 * @var integer
		 */
		static protected $_loadend = null;
		
		/**
		 * List of classpaths
		 * 
		 * @var array
		 */
		static protected $_classpaths = array();
		
		/**
		 * List of loaded libraries
		 * 
		 * @var string
		 */
		static protected $_libs = array();
		
		/**
		 * The routing object
		 * 
		 * @var TBGRouting
		 */
		static protected $_routing = null;

		/**
		 * Messages passed on from the previous request
		 *
		 * @var array
		 */
		static protected $_messages = null;

		static protected $_redirect_login = null;

		/**
		 * Returns whether or not we're in install mode
		 * 
		 * @return boolean
		 */
		public static function isInstallmode()
		{
			return self::$_installmode;
		}

		/**
		 * Add a path to the list of searched paths in the autoloader
		 * Class files must contain one class with the same name as the class
		 * in the form of Classname.class.php
		 * 
		 * @param string $classpath The path where the class files are
		 * 
		 * @return null
		 */
		public static function addClasspath($classpath)
		{
			if (!file_exists($classpath)) throw new Exception("Cannot add {$classpath} to classpaths, since it doesn't exist");
			if ($classpath[strlen($classpath) - 1] != DIRECTORY_SEPARATOR)
			{
				TBGLogging::log('Invalid classpath, appending directory separator', 'main', TBGLogging::LEVEL_WARNING);
				$classpath .= DIRECTORY_SEPARATOR;
			}
			if (file_exists($classpath . 'generics.class.php') && !isset(self::$_classpaths[$classpath . 'generics.class.php']))
			{
				require_once $classpath . 'generics.class.php';
			}
			if (file_exists($classpath . 'actions.class.php'))
			{
				require_once $classpath . 'actions.class.php';
			}
			if (file_exists($classpath . 'actioncomponents.class.php'))
			{
				require_once $classpath . 'actioncomponents.class.php';
			}
			if ($dir_handle = opendir($classpath))
			{
				while (false !== ($file = readdir($dir_handle)))
				{
					if ($offset = strpos($file, '.class.php'))
					{
						self::$_classpaths[substr($file, 0, $offset)] = $classpath . $file;
					}
				}
			}
			else
			{
				throw new Exception($classpath . ' is not a valid directory');
			}
		}
		
		/**
		 * Returns the classpaths that has been registered to the autoloader
		 *
		 * @return array
		 */
		public static function getClasspaths()
		{
			return self::$_classpaths;
		}

		/**
		 * Setup the routing object with CLI parameters
		 *
		 * @param string $module
		 * @param string $action
		 */
		public static function setCLIRouting($module, $action)
		{
			self::$_routing->setCurrentRouteModule($module);
			self::$_routing->setCurrentRouteAction($action);
			self::$_routing->setCurrentRouteName('cli');
			self::$_routing->setCurrentRouteCSRFenabled(false);
		}

		/**
		 * Returns the routing object
		 * 
		 * @return TBGRouting
		 */
		public static function getRouting()
		{
			if (!self::$_routing)
			{
				self::$_routing = new TBGRouting();
			}
			return self::$_routing;
		}
		
		/**
		 * Returns the include path (used for including files)
		 * 
		 * @return string
		 */
		public static function getIncludePath()
		{
			return self::$_includepath;
		}
		
		/**
		 * Get when we last loaded the engine
		 * 
		 * @return integer
		 */
		public static function getLastLoadedAt()
		{
			return $_SESSION['b2lastreloadtime'];
		}
		
		/**
		 * Set when we last loaded the engine
		 */
		public static function setLoadedAt()
		{
			$_SESSION['b2lastreloadtime'] = NOW;
		}
		
		/**
		 * Set the include path to a specific path
		 * 
		 * @param string $path the path to change to
		 */
		public static function setIncludePath($path = null)
		{
			if ($path !== null)
			{
				self::$_includepath = $path;
			}
			else
			{
				self::$_includepath = TBGSettings::get('local_path', 'core');
			}
		}

		/**
		 * Get the subdirectory part of the url
		 * 
		 * @return string
		 */
		public static function getTBGPath()
		{
			if (self::$_tbgpath === null)
			{
				self::_setTBGPath();
			}
			return self::$_tbgpath;
		}
		
		/**
		 * Get the subdirectory part of the url, stripped
		 * 
		 * @return string
		 */
		public static function getStrippedTBGPath()
		{
			if (self::$_stripped_tbgpath === null)
			{
				self::$_stripped_tbgpath = substr(self::getTBGPath(), 0, strlen(self::getTBGPath()) - 1);
			}
			return self::$_stripped_tbgpath;
		}

		/**
		 * Set the subdirectory part of the url, from the url
		 */
		protected static function _setTBGPath()
		{
			self::$_tbgpath = dirname($_SERVER['PHP_SELF']);
			if (self::$_tbgpath[strlen(self::$_tbgpath) - 1] != '/') self::$_tbgpath .= '/';
		}
		
		/**
		 * Set that we've started loading
		 * 
		 * @param integer $when
		 */
		public static function setLoadStart($when)
		{
			self::$_loadstart = $when;
		}
		
		/**
		 * Manually ping the loader
		 */
		public static function ping()
		{
			$endtime = explode(' ', microtime());
			self::$_loadend = $endtime[1] + $endtime[0];
		}

		/**
		 * Get the time from when we started loading
		 * 
		 * @param integer $precision
		 * @return integer
		 */
		public static function getLoadtime($precision = 5)
		{
			self::ping();
			return round((self::$_loadend - self::$_loadstart), $precision);
		}
		
		public static function checkInstallMode()
		{
			if (!is_readable(THEBUGGENIE_PATH . 'installed'))
			{
				self::$_installmode = true;
			}
		}

		/**
		 * Initialize the context
		 * 
		 * @return null
		 */
		public static function initialize()
		{
			try
			{
				TBGLogging::log('Loading request');
				self::$_request = new TBGRequest();
				self::$_factory = new TBGFactory();
				TBGLogging::log('...done');
				if (!self::isCLI())
				{
					TBGLogging::log('Loading response');
					self::$_response = new TBGResponse();
					TBGLogging::log('...done');
				}
				self::checkInstallMode();
				if (!self::$_installmode && !class_exists('B2DB'))
				{
					throw new Exception("The Bug Genie seems installed, but B2DB isn't configured. This usually indicates an error with the installation. Try removing the file ".THEBUGGENIE_PATH."installed and try again.");
				}

				TBGLogging::log('Loading first batch of routes', 'routing');
				if (!($routes_1 = TBGCache::get('routes_1')))
				{
					TBGLogging::log('generating routes', 'routing');
					require THEBUGGENIE_PATH . 'core/load_routes.inc.php';
					TBGCache::add('routes_1', self::getRouting()->getRoutes());
				}
				else
				{
					TBGLogging::log('loading routes from cache', 'routing');
					self::getRouting()->setRoutes($routes_1);
				}
				TBGLogging::log('...done', 'routing');
				
				TBGLogging::log("Setting current scope");
				try
				{
					self::setScope();
					TBGLogging::log("Loading settings");
					try
					{
						TBGSettings::loadSettings();
					}
					catch (Exception $e)
					{
						if (!self::isCLI())
						{
							throw $e;
						}
						else
						{
							self::$_installmode = true;
						}
					}
					TBGLogging::log("...done");
				}
				catch (Exception $e)
				{
					if (!self::isInstallmode()) 
					{
						throw $e;
					}
					TBGLogging::log("Ignoring scope exception since we're in installmode");
				}
				TBGLogging::log("...done");
				

				if (self::$_installmode)
				{
					self::$_modules = array();
					return true;
				}

				if (!self::isCLI())
				{
					TBGLogging::log('Loading i18n strings');
					if (!$cached_i18n = TBGCache::get('i18n_'.TBGSettings::get('language')))
					{
						TBGLogging::log('Loading strings from file');
						TBGLogging::log(TBGSettings::get('language'));
						self::$_i18n = new TBGI18n(TBGSettings::get('language'));
						self::$_i18n->initialize();
						TBGCache::add('i18n_'.TBGSettings::get('language'), self::$_i18n);
					}
					else
					{
						TBGLogging::log('Using cached i18n strings');
						self::$_i18n = $cached_i18n;
					}
					TBGLogging::log('...done');
				}

				TBGLogging::log('Loading modules');
				self::loadModules();
				TBGLogging::log('...done');

				TBGLogging::log('Loading user');
				try
				{
					TBGLogging::log('is this logout?');
					if (self::getRequest()->getParameter('logout'))
					{
						TBGLogging::log('yes');
						TBGEvent::createNew('core', 'pre_logout')->trigger();
						self::logout();
						TBGEvent::createNew('core', 'post_logout')->trigger();
					}
					else
					{
						TBGLogging::log('no');
						TBGLogging::log('sets up user object');
						$event = TBGEvent::createNew('core', 'pre_login');
						$event->trigger();

						if ($event->isProcessed())
						{
							self::loadUser($event->getReturnValue());
						}
						else
						{
							self::loadUser();
						}
						TBGEvent::createNew('core', 'post_login', self::getUser())->trigger();

						TBGLogging::log('loaded');
						TBGLogging::log('caches permissions');
						self::cacheAllPermissions();
						TBGLogging::log('...cached');
					}
				}
				catch (Exception $e)
				{
					TBGLogging::log("Something happened while setting up user: ". $e->getMessage(), 'main', TBGLogging::LEVEL_WARNING);
					if (!self::isCLI() && (self::getRouting()->getCurrentRouteModule() != 'main' || self::getRouting()->getCurrentRouteAction() != 'login' && self::getRouting()->getCurrentRouteAction() != 'getBackdropPartial'))
					{
						self::$_redirect_login = true;
					}
					else
					{
						self::$_user = new TBGUser();
					}
				}

				TBGLogging::log('...done');
				TBGLogging::log('Loading last batch of routes', 'routing');
				if (!($routes = TBGCache::get('routes_2')))
				{
					TBGLogging::log('generating routes', 'routing');
					require THEBUGGENIE_PATH . 'core/load_routes_postmodules.inc.php';
					TBGCache::add('routes_2', self::getRouting()->getRoutes());
				}
				else
				{
					TBGLogging::log('loading routes from cache', 'routing');
					self::getRouting()->setRoutes($routes);
				}
				TBGLogging::log('...done', 'routing');

				TBGLogging::log('...done initializing');
			}
			catch (Exception $e)
			{
				throw $e;
			}
		}

		/**
		 * Returns the factory object
		 *
		 * @return TBGFactory
		 */
		public static function factory()
		{
			return self::$_factory;
		}

		/**
		 * Returns the request object
		 * 
		 * @return TBGRequest
		 */
		public static function getRequest()
		{
			return self::$_request;
		}
		
		/**
		 * Returns the response object
		 * 
		 * @return TBGResponse
		 */
		public static function getResponse()
		{
			return self::$_response;
		}
		
		/**
		 * Reinitialize the i18n object, used only when changing the language in the middle of something
		 * 
		 * @param string $language The language code to change to
		 */
		public static function reinitializeI18n($language = null) 
		{
			if (!$language)
			{
				self::$_i18n = new TBGI18n(TBGSettings::get('language'));
			}
			else
			{
				self::$_i18n = new TBGI18n($language);
			}
		}
		
		/**
		 * Get the i18n object
		 *
		 * @return TBGI18n
		 */
		public static function getI18n()
		{
			if (!self::$_i18n instanceof TBGI18n)
			{
				self::reinitializeI18n('en_US');
			}
			return self::$_i18n;
		}
		
		/**
		 * Get available themes
		 * 
		 * @return array
		 */
		public static function getThemes()
		{
			$theme_path_handle = opendir(self::getIncludePath() . 'thebuggenie/themes/');
			$themes = array();
			
			while ($theme = readdir($theme_path_handle))
			{
				if (strstr($theme, '.') == '') 
				{ 
					$themes[] = $theme; 
				}
			}
			
			return $themes;
		}
		
		/**
		 * Load the user object into the user property
		 * 
		 * @return TBGUser
		 */
		public static function loadUser($user = null)
		{
			try
			{
				self::$_user = ($user === null) ? TBGUser::loginCheck() : $user;
			}
			catch (Exception $e)
			{
				throw $e;
			}
			return self::$_user;
		}
		
		/**
		 * Returns the user object
		 *
		 * @return TBGUser
		 */
		public static function getUser()
		{
			return self::$_user;
		}
		
		/**
		 * Loads and initializes all modules
		 */
		public static function loadModules()
		{
			if (self::$_modules === null)
			{
				self::$_modules = array();
				if (self::isInstallmode()) return;
				$modules = array();

				if ($module_paths = TBGCache::get('module_paths'))
				{
					TBGLogging::log('using cached modules');
					foreach ($module_paths as $moduleClassPath)
					{
						try
						{
							self::addClasspath($moduleClassPath);
						}
						catch (Exception $e) { } // ignore "dir not exists" errors
					}
					$modules = TBGCache::get('modules');
					foreach ($modules as $module)
					{
						self::$_modules[$module] = unserialize(TBGCache::get("module_{$module}"));
					}
					TBGLogging::log('done');
				}
				else
				{
					TBGLogging::log('getting modules from database');
					$module_paths = array();

					if ($res = B2DB::getTable('TBGModulesTable')->getAll())
					{
						while ($moduleRow = $res->getNextRow())
						{
							$module_name = $moduleRow->get(TBGModulesTable::MODULE_NAME);
							$modules[$module_name] = $moduleRow;
							$moduleClassPath = self::getIncludePath() . "modules/{$module_name}/classes/";
							try
							{
								self::addClasspath($moduleClassPath);
								$module_paths[] = $moduleClassPath;
								if (file_exists($moduleClassPath . 'B2DB'))
								{
									self::addClasspath($moduleClassPath . 'B2DB/');
									$module_paths[] = $moduleClassPath . 'B2DB/';
								}
							}
							catch (Exception $e) { } // ignore "dir not exists" errors
						}
					}
					TBGLogging::log('done (getting modules from database)');
					TBGCache::add('module_paths', $module_paths);
					TBGCache::add('modules', array_keys($modules));
					TBGLogging::log('setting up module objects');
					foreach ($modules as $module_name => $moduleRow)
					{
						$classname = $moduleRow->get(TBGModulesTable::CLASSNAME);
						if ($classname != '' && $classname != 'TBGModule')
						{
							if (class_exists($classname))
							{
								self::getI18n()->loadModuleStrings($module_name);
								self::$_modules[$module_name] = new $classname($moduleRow->get(TBGModulesTable::ID), $moduleRow);
								TBGCache::add("module_{$module_name}", serialize(self::$_modules[$module_name]));
							}
							else
							{
								TBGLogging::log('Cannot load module "' . $module_name . '" as class "' . $classname . '", the class is not defined in the classpaths.', 'modules', TBGLogging::LEVEL_WARNING_RISK);
								TBGLogging::log('Removing module "' . $module_name . '" as it cannot be loaded', 'modules', TBGLogging::LEVEL_NOTICE);
								TBGModule::removeModule($moduleRow->get(TBGModulesTable::ID));
								//throw new Exception('Cannot load module "' . $module_name . '" as class "' . $classname . '", the class is not defined in the classpaths.');
							}
						}
						else
						{
							throw new Exception('Cannot load module "' . $module_name . '" as class TBGModule - modules should extend the TBGModule class with their own class.');
						}
					}
					TBGLogging::log('done (setting up module objects)');

					TBGLogging::log('caching module access permissions');
					TBGModule::cacheAllAccessPermissions();
					TBGLogging::log('done (caching module access permissions)');
				}

				TBGLogging::log('initializing modules');
				if (!empty(self::$_modules))
				{
					foreach (self::$_modules as $module_name => $module)
					{
						$module->initialize();
					}
					TBGModule::loadModuleListeners(array_keys(self::$_modules));
					TBGLogging::log('done (initializing modules)');
				}
				else
				{
					TBGLogging::log('no modules found');
				}
			}
			else
			{
				TBGLogging::log('Modules already loaded', 'core', TBGLogging::LEVEL_FATAL);
			}
		}
		
		/**
		 * Adds a module to the module list
		 *
		 * @param TBGModule $module
		 */
		public static function addModule($module, $module_name)<commit_msg>don't break cli<commit_after><?php

	/**
	 * The core class of the B2 engine
	 *
	 * @author <NAME> <<EMAIL>>
	 ** @version 3.0
	 * @license http://www.opensource.org/licenses/mozilla1.1.php Mozilla Public License 1.1 (MPL 1.1)
	 * @package thebuggenie
	 * @subpackage core
	 */

	/**
	 * The core class of the B2 engine
	 *
	 * @package thebuggenie
	 * @subpackage core
	 */
	class TBGContext
	{

		const PREDEFINED_SEARCH_PROJECT_OPEN_ISSUES = 1;
		const PREDEFINED_SEARCH_PROJECT_CLOSED_ISSUES = 2;
		const PREDEFINED_SEARCH_PROJECT_MILESTONE_TODO = 6;
		const PREDEFINED_SEARCH_PROJECT_MOST_VOTED = 7;
		const PREDEFINED_SEARCH_MY_ASSIGNED_OPEN_ISSUES = 3;
		const PREDEFINED_SEARCH_TEAM_ASSIGNED_OPEN_ISSUES = 4;
		const PREDEFINED_SEARCH_MY_REPORTED_ISSUES = 5;
		
		static protected $_environment = 2;

		static protected $debug_mode = true;

		/**
		 * The current user
		 *
		 * @var TBGUser
		 */
		static protected $_user = null;
		
		/**
		 * List of modules 
		 * 
		 * @var array
		 */
		static protected $_modules = null;
		
		/**
		 * List of module permissions
		 * 
		 * @var array
		 */
		static protected $_modulepermissions = array();
		
		/**
		 * List of permissions
		 *  
		 * @var array
		 */
		static protected $_permissions = array();
		
		/**
		 * List of available permissions
		 * 
		 * @var array
		 */
		static protected $_available_permissions = null;
		
		/**
		 * The include path
		 * 
		 * @var string
		 */
		static protected $_includepath = null;
		
		/**
		 * The path to thebuggenie relative from url server root
		 * 
		 * @var string
		 */
		static protected $_tbgpath = null;
		
		/**
		 * Stripped version of the $_tbgpath
		 * 
		 * @see $_tbgpath
		 * 
		 * @var string
		 */
		static protected $_stripped_tbgpath = null;
		
		/**
		 * Whether we're in installmode or not
		 * 
		 * @var boolean
		 */
		static protected $_installmode = false;
		
		/**
		 * The i18n object
		 *
		 * @var TBGI18n
		 */
		static protected $_i18n = null;
		
		/**
		 * The request object
		 * 
		 * @var TBGRequest
		 */
		static protected $_request = null;
		
		/**
		 * The response object
		 * 
		 * @var TBGResponse
		 */
		static protected $_response = null;
		
		/**
		 * The current scope object
		 *
		 * @var TBGScope
		 */
		static protected $_scope = null;

		/**
		 * The TBGFactory instance
		 *
		 * @var TBGFactory
		 */
		static protected $_factory = null;
		
		/**
		 * The currently selected project, if any
		 * 
		 * @var TBGProject
		 */
		static protected $_selected_project = null;
		
		/**
		 * The currently selected client, if any
		 * 
		 * @var TBGClient
		 */
		static protected $_selected_client = null;
		
		/**
		 * Used to determine when the b2 engine started loading
		 * 
		 * @var integer
		 */
		static protected $_loadstart = null;
		
		/**
		 * Used for timing purposes
		 * 
		 * @var integer
		 */
		static protected $_loadend = null;
		
		/**
		 * List of classpaths
		 * 
		 * @var array
		 */
		static protected $_classpaths = array();
		
		/**
		 * List of loaded libraries
		 * 
		 * @var string
		 */
		static protected $_libs = array();
		
		/**
		 * The routing object
		 * 
		 * @var TBGRouting
		 */
		static protected $_routing = null;

		/**
		 * Messages passed on from the previous request
		 *
		 * @var array
		 */
		static protected $_messages = null;

		static protected $_redirect_login = null;

		/**
		 * Returns whether or not we're in install mode
		 * 
		 * @return boolean
		 */
		public static function isInstallmode()
		{
			return self::$_installmode;
		}

		/**
		 * Add a path to the list of searched paths in the autoloader
		 * Class files must contain one class with the same name as the class
		 * in the form of Classname.class.php
		 * 
		 * @param string $classpath The path where the class files are
		 * 
		 * @return null
		 */
		public static function addClasspath($classpath)
		{
			if (!file_exists($classpath)) throw new Exception("Cannot add {$classpath} to classpaths, since it doesn't exist");
			if ($classpath[strlen($classpath) - 1] != DIRECTORY_SEPARATOR)
			{
				TBGLogging::log('Invalid classpath, appending directory separator', 'main', TBGLogging::LEVEL_WARNING);
				$classpath .= DIRECTORY_SEPARATOR;
			}
			if (file_exists($classpath . 'generics.class.php') && !isset(self::$_classpaths[$classpath . 'generics.class.php']))
			{
				require_once $classpath . 'generics.class.php';
			}
			if (file_exists($classpath . 'actions.class.php'))
			{
				require_once $classpath . 'actions.class.php';
			}
			if (file_exists($classpath . 'actioncomponents.class.php'))
			{
				require_once $classpath . 'actioncomponents.class.php';
			}
			if ($dir_handle = opendir($classpath))
			{
				while (false !== ($file = readdir($dir_handle)))
				{
					if ($offset = strpos($file, '.class.php'))
					{
						self::$_classpaths[substr($file, 0, $offset)] = $classpath . $file;
					}
				}
			}
			else
			{
				throw new Exception($classpath . ' is not a valid directory');
			}
		}
		
		/**
		 * Returns the classpaths that has been registered to the autoloader
		 *
		 * @return array
		 */
		public static function getClasspaths()
		{
			return self::$_classpaths;
		}

		/**
		 * Setup the routing object with CLI parameters
		 *
		 * @param string $module
		 * @param string $action
		 */
		public static function setCLIRouting($module, $action)
		{
			self::$_routing->setCurrentRouteModule($module);
			self::$_routing->setCurrentRouteAction($action);
			self::$_routing->setCurrentRouteName('cli');
			self::$_routing->setCurrentRouteCSRFenabled(false);
		}

		/**
		 * Returns the routing object
		 * 
		 * @return TBGRouting
		 */
		public static function getRouting()
		{
			if (!self::$_routing)
			{
				self::$_routing = new TBGRouting();
			}
			return self::$_routing;
		}
		
		/**
		 * Returns the include path (used for including files)
		 * 
		 * @return string
		 */
		public static function getIncludePath()
		{
			return self::$_includepath;
		}
		
		/**
		 * Get when we last loaded the engine
		 * 
		 * @return integer
		 */
		public static function getLastLoadedAt()
		{
			return $_SESSION['b2lastreloadtime'];
		}
		
		/**
		 * Set when we last loaded the engine
		 */
		public static function setLoadedAt()
		{
			$_SESSION['b2lastreloadtime'] = NOW;
		}
		
		/**
		 * Set the include path to a specific path
		 * 
		 * @param string $path the path to change to
		 */
		public static function setIncludePath($path = null)
		{
			if ($path !== null)
			{
				self::$_includepath = $path;
			}
			else
			{
				self::$_includepath = TBGSettings::get('local_path', 'core');
			}
		}

		/**
		 * Get the subdirectory part of the url
		 * 
		 * @return string
		 */
		public static function getTBGPath()
		{
			if (self::$_tbgpath === null)
			{
				self::_setTBGPath();
			}
			return self::$_tbgpath;
		}
		
		/**
		 * Get the subdirectory part of the url, stripped
		 * 
		 * @return string
		 */
		public static function getStrippedTBGPath()
		{
			if (self::$_stripped_tbgpath === null)
			{
				self::$_stripped_tbgpath = substr(self::getTBGPath(), 0, strlen(self::getTBGPath()) - 1);
			}
			return self::$_stripped_tbgpath;
		}

		/**
		 * Set the subdirectory part of the url, from the url
		 */
		protected static function _setTBGPath()
		{
			self::$_tbgpath = dirname($_SERVER['PHP_SELF']);
			if (self::$_tbgpath[strlen(self::$_tbgpath) - 1] != '/') self::$_tbgpath .= '/';
		}
		
		/**
		 * Set that we've started loading
		 * 
		 * @param integer $when
		 */
		public static function setLoadStart($when)
		{
			self::$_loadstart = $when;
		}
		
		/**
		 * Manually ping the loader
		 */
		public static function ping()
		{
			$endtime = explode(' ', microtime());
			self::$_loadend = $endtime[1] + $endtime[0];
		}

		/**
		 * Get the time from when we started loading
		 * 
		 * @param integer $precision
		 * @return integer
		 */
		public static function getLoadtime($precision = 5)
		{
			self::ping();
			return round((self::$_loadend - self::$_loadstart), $precision);
		}
		
		public static function checkInstallMode()
		{
			if (!is_readable(THEBUGGENIE_PATH . 'installed'))
			{
				self::$_installmode = true;
			}
		}

		/**
		 * Initialize the context
		 * 
		 * @return null
		 */
		public static function initialize()
		{
			try
			{
				TBGLogging::log('Loading request');
				self::$_request = new TBGRequest();
				self::$_factory = new TBGFactory();
				TBGLogging::log('...done');
				if (!self::isCLI())
				{
					TBGLogging::log('Loading response');
					self::$_response = new TBGResponse();
					TBGLogging::log('...done');
				}
				self::checkInstallMode();
				if (!self::$_installmode && !class_exists('B2DB'))
				{
					throw new Exception("The Bug Genie seems installed, but B2DB isn't configured. This usually indicates an error with the installation. Try removing the file ".THEBUGGENIE_PATH."installed and try again.");
				}

				TBGLogging::log('Loading first batch of routes', 'routing');
				if (!($routes_1 = TBGCache::get('routes_1')))
				{
					TBGLogging::log('generating routes', 'routing');
					require THEBUGGENIE_PATH . 'core/load_routes.inc.php';
					TBGCache::add('routes_1', self::getRouting()->getRoutes());
				}
				else
				{
					TBGLogging::log('loading routes from cache', 'routing');
					self::getRouting()->setRoutes($routes_1);
				}
				TBGLogging::log('...done', 'routing');
				
				TBGLogging::log("Setting current scope");
				try
				{
					self::setScope();
					TBGLogging::log("Loading settings");
					try
					{
						TBGSettings::loadSettings();
					}
					catch (Exception $e)
					{
						if (!self::isCLI())
						{
							throw $e;
						}
						else
						{
							self::$_installmode = true;
						}
					}
					TBGLogging::log("...done");
				}
				catch (Exception $e)
				{
					if (!self::isInstallmode()) 
					{
						throw $e;
					}
					TBGLogging::log("Ignoring scope exception since we're in installmode");
				}
				TBGLogging::log("...done");
				

				if (self::$_installmode)
				{
					self::$_modules = array();
					return true;
				}

				if (!self::isCLI())
				{
					TBGLogging::log('Loading i18n strings');
					if (!$cached_i18n = TBGCache::get('i18n_'.TBGSettings::get('language')))
					{
						TBGLogging::log('Loading strings from file');
						TBGLogging::log(TBGSettings::get('language'));
						self::$_i18n = new TBGI18n(TBGSettings::get('language'));
						self::$_i18n->initialize();
						TBGCache::add('i18n_'.TBGSettings::get('language'), self::$_i18n);
					}
					else
					{
						TBGLogging::log('Using cached i18n strings');
						self::$_i18n = $cached_i18n;
					}
					TBGLogging::log('...done');
				}

				TBGLogging::log('Loading modules');
				self::loadModules();
				TBGLogging::log('...done');

				TBGLogging::log('Loading user');
				try
				{
					TBGLogging::log('is this logout?');
					if (self::getRequest()->getParameter('logout'))
					{
						TBGLogging::log('yes');
						TBGEvent::createNew('core', 'pre_logout')->trigger();
						self::logout();
						TBGEvent::createNew('core', 'post_logout')->trigger();
					}
					else
					{
						TBGLogging::log('no');
						TBGLogging::log('sets up user object');
						$event = TBGEvent::createNew('core', 'pre_login');
						$event->trigger();

						if ($event->isProcessed())
						{
							self::loadUser($event->getReturnValue());
						}
						else
						{
							self::loadUser();
						}
						TBGEvent::createNew('core', 'post_login', self::getUser())->trigger();

						TBGLogging::log('loaded');
						TBGLogging::log('caches permissions');
						self::cacheAllPermissions();
						TBGLogging::log('...cached');
					}
				}
				catch (Exception $e)
				{
					TBGLogging::log("Something happened while setting up user: ". $e->getMessage(), 'main', TBGLogging::LEVEL_WARNING);
					if (!self::isCLI() && (self::getRouting()->getCurrentRouteModule() != 'main' || self::getRouting()->getCurrentRouteAction() != 'login' && self::getRouting()->getCurrentRouteAction() != 'getBackdropPartial'))
					{
						self::$_redirect_login = true;
					}
					else
					{
						self::$_user = new TBGUser();
					}
				}

				TBGLogging::log('...done');
				TBGLogging::log('Loading last batch of routes', 'routing');
				if (!($routes = TBGCache::get('routes_2')))
				{
					TBGLogging::log('generating routes', 'routing');
					require THEBUGGENIE_PATH . 'core/load_routes_postmodules.inc.php';
					TBGCache::add('routes_2', self::getRouting()->getRoutes());
				}
				else
				{
					TBGLogging::log('loading routes from cache', 'routing');
					self::getRouting()->setRoutes($routes);
				}
				TBGLogging::log('...done', 'routing');

				TBGLogging::log('...done initializing');
			}
			catch (Exception $e)
			{
				throw $e;
			}
		}

		/**
		 * Returns the factory object
		 *
		 * @return TBGFactory
		 */
		public static function factory()
		{
			return self::$_factory;
		}

		/**
		 * Returns the request object
		 * 
		 * @return TBGRequest
		 */
		public static function getRequest()
		{
			return self::$_request;
		}
		
		/**
		 * Returns the response object
		 * 
		 * @return TBGResponse
		 */
		public static function getResponse()
		{
			return self::$_response;
		}
		
		/**
		 * Reinitialize the i18n object, used only when changing the language in the middle of something
		 * 
		 * @param string $language The language code to change to
		 */
		public static function reinitializeI18n($language = null) 
		{
			if (!$language)
			{
				self::$_i18n = new TBGI18n(TBGSettings::get('language'));
			}
			else
			{
				self::$_i18n = new TBGI18n($language);
			}
		}
		
		/**
		 * Get the i18n object
		 *
		 * @return TBGI18n
		 */
		public static function getI18n()
		{
			if (!self::$_i18n instanceof TBGI18n)
			{
				self::reinitializeI18n('en_US');
			}
			return self::$_i18n;
		}
		
		/**
		 * Get available themes
		 * 
		 * @return array
		 */
		public static function getThemes()
		{
			$theme_path_handle = opendir(self::getIncludePath() . 'thebuggenie/themes/');
			$themes = array();
			
			while ($theme = readdir($theme_path_handle))
			{
				if (strstr($theme, '.') == '') 
				{ 
					$themes[] = $theme; 
				}
			}
			
			return $themes;
		}
		
		/**
		 * Load the user object into the user property
		 * 
		 * @return TBGUser
		 */
		public static function loadUser($user = null)
		{
			try
			{
				self::$_user = ($user === null) ? TBGUser::loginCheck() : $user;
			}
			catch (Exception $e)
			{
				throw $e;
			}
			return self::$_user;
		}
		
		/**
		 * Returns the user object
		 *
		 * @return TBGUser
		 */
		public static function getUser()
		{
			return self::$_user;
		}
		
		/**
		 * Loads and initializes all modules
		 */
		public static function loadModules()
		{
			if (self::$_modules === null)
			{
				self::$_modules = array();
				if (self::isInstallmode()) return;
				$modules = array();

				if ($module_paths = TBGCache::get('module_paths'))
				{
					TBGLogging::log('using cached modules');
					foreach ($module_paths as $moduleClassPath)
					{
						try
						{
							self::addClasspath($moduleClassPath);
						}
						catch (Exception $e) { } // ignore "dir not exists" errors
					}
					$modules = TBGCache::get('modules');
					foreach ($modules as $module)
					{
						self::$_modules[$module] = unserialize(TBGCache::get("module_{$module}"));
					}
					TBGLogging::log('done');
				}
				else
				{
					TBGLogging::log('getting modules from database');
					$module_paths = array();

					if ($res = B2DB::getTable('TBGModulesTable')->getAll())
					{
						while ($moduleRow = $res->getNextRow())
						{
							$module_name = $moduleRow->get(TBGModulesTable::MODULE_NAME);
							$modules[$module_name] = $moduleRow;
							$moduleClassPath = self::getIncludePath() . "modules/{$module_name}/classes/";
							try
							{
								self::addClasspath($moduleClassPath);
								$module_paths[] = $moduleClassPath;
								if (file_exists($moduleClassPath . 'B2DB'))
								{
									self::addClasspath($moduleClassPath . 'B2DB/');
									$module_paths[] = $moduleClassPath . 'B2DB/';
								}
							}
							catch (Exception $e) { } // ignore "dir not exists" errors
						}
					}
					TBGLogging::log('done (getting modules from database)');
					TBGCache::add('module_paths', $module_paths);
					TBGCache::add('modules', array_keys($modules));
					TBGLogging::log('setting up module objects');
					foreach ($modules as $module_name => $moduleRow)
					{
						$classname = $moduleRow->get(TBGModulesTable::CLASSNAME);
						if ($classname != '' && $classname != 'TBGModule')
						{
							if (class_exists($classname))
							{
								self::getI18n()->loadModuleStrings($module_name);
								self::$_modules[$module_name] = new $classname($moduleRow->get(TBGModulesTable::ID), $moduleRow);
								TBGCache::add("module_{$module_name}", serialize(self::$_modules[$module_name]));
							}
							else
							{
								TBGLogging::log('Cannot load module "' . $module_name . '" as class "' . $classname . '", the class is not defined in the classpaths.', 'modules', TBGLogging::LEVEL_WARNING_RISK);
								TBGLogging::log('Removing module "' . $module_name . '" as it cannot be loaded', 'modules', TBGLogging::LEVEL_NOTICE);
								TBGModule::removeModule($moduleRow->get(TBGModulesTable::ID));
								//throw new Exception('Cannot load module "' . $module_name . '" as class "' . $classname . '", the class is not defined in the classpaths.');
							}
						}
						else
						{
							throw new Exception('Cannot load module "' . $module_name . '" as class TBGModule - modules should extend the TBGModule class with their own class.');
						}
					}
					TBGLogging::log('done (setting up module objects)');

					TBGLogging::log('caching module access permissions');
					if (!self::isCLI()) TBGModule::cacheAllAccessPermissions();
					TBGLogging::log('done (caching module access permissions)');
				}

				TBGLogging::log('initializing modules');
				if (!empty(self::$_modules))
				{
					foreach (self::$_modules as $module_name => $module)
					{
						$module->initialize();
					}
					TBGModule::loadModuleListeners(array_keys(self::$_modules));
					TBGLogging::log('done (initializing modules)');
				}
				else
				{
					TBGLogging::log('no modules found');
				}
			}
			else
			{
				TBGLogging::log('Modules already loaded', 'core', TBGLogging::LEVEL_FATAL);
			}
		}
		
		/**
		 * Adds a module to the module list
		 *
		 * @param TBGModule $module
		 */
		public static function addModule($module, $module_name)
{'fraction_non_alphanumeric': 0.14708573273774286, 'fraction_numerical': 0.005220386416231635, 'mean_word_length': 2.7203924316748425, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 69, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5374777', 'n_tokens_mistral': 15772, 'n_tokens_neox': 11717, 'n_words': 2681}