<commit_before><?php

	/**
	 * The core class of the B2 engine
	 *
	 * @author <NAME> <<EMAIL>>
	 * @version 3.1
	 * @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;
		
		static protected $_partials_visited = array();

		/**
		 * The current user
		 *
		 * @var TBGUser
		 */
		static protected $_user = null;
		
		/**
		 * List of modules 
		 * 
		 * @var array
		 */
		static protected $_modules = null;
		
		/**
		 * 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;
		
		/**
		 * Whether we're in upgrademode or not
		 * 
		 * @var boolean
		 */
		static protected $_upgrademode = 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;
		
		/**
		 * Do you want to disable minifcation of javascript and css?
		 * 
		 * @var boolean
		 */
		static protected $_minifyoff = false;

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

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

		/**
		 * 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 (!is_dir($classpath))
				throw new Exception("Cannot add {$classpath} to classpaths, since it doesn't exist");

			$classpath = realpath($classpath) . DS;
			
			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';

			self::$_classpaths[$classpath] = $classpath; // . $file;
			return true;
		}

		/**
		 * 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;
		}
		
		/**
		 * 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;
		}
		
		/**
		 * 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 (stristr(PHP_OS, 'WIN')) { self::$_tbgpath = str_replace("\\", "/", self::$_tbgpath); /* Windows adds a \ to the URL which we don't want */ }
			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;
			elseif (is_readable(THEBUGGENIE_PATH . 'upgrade'))
				self::$_installmode = self::$_upgrademode = true;
			elseif (!B2DB::isInitialized())
				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.");
		}

		/**
		 * Initialize the context
		 * 
		 * @return null
		 */
		public static function initialize()
		{
			try
			{
				self::$_request = new TBGRequest();
				self::$_response = new TBGResponse();
				self::$_factory = new TBGFactory();
				
				self::checkInstallMode();
				self::loadPreModuleRoutes();
				self::setScope();

				if (!self::$_installmode)
				{
					self::loadModules();
					self::initializeUser();
					TBGSettings::setTimezone();
				}
				else
					self::$_modules = array();
				
//				var_dump(self::getUser());die();
				self::setupI18n();
				
				self::loadPostModuleRoutes();
				TBGLogging::log('...done initializing');
			}
			catch (Exception $e)
			{
				if (!self::isCLI() && !self::isInstallmode())
					throw $e;
			}
		}
		
		protected static function setupI18n()
		{
			if (TBGContext::isCLI())
				return null;

			$language = (self::$_user instanceof TBGUser) ? self::$_user->getLanguage() : 'en_US';
			
			if (self::$_user instanceof TBGUser && self::$_user->getLanguage() == 'sys')
			{
				$language = 'en_US';
			}
			
			TBGLogging::log('Loading i18n strings');
			if (!self::$_i18n = TBGCache::get("i18n_{$language}"))
			{
				TBGLogging::log("Loading strings from file ({$language})");
				self::$_i18n = new TBGI18n($language);
				self::$_i18n->initialize();
				TBGCache::add("i18n_{$language}", self::$_i18n);
			}
			else
			{
				TBGLogging::log('Using cached i18n strings');
			}
			TBGLogging::log('...done');
		}

		protected static function initializeUser()
		{
			TBGLogging::log('Loading user');
			try
			{
				TBGLogging::log('is this logout?');
				if (self::getRequest()->getParameter('logout'))
				{
					TBGLogging::log('yes');
					self::logout();
				}
				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');
					self::cacheAllPermissions();
				}
			}
			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() != 'register1' && self::getRouting()->getCurrentRouteAction() != 'register2' && self::getRouting()->getCurrentRouteAction() != 'activate' && self::getRouting()->getCurrentRouteAction() != 'reset_password' && self::getRouting()->getCurrentRouteAction() != 'captcha' && self::getRouting()->getCurrentRouteAction() != 'login' && self::getRouting()->getCurrentRouteAction() != 'getBackdropPartial' && self::getRouting()->getCurrentRouteAction() != 'serve'))
					self::$_redirect_login = true;
				else
					self::$_user = self::factory()->TBGUser(TBGSettings::getDefaultUserID());
			}
			TBGLogging::log('...done');
		}

		protected static function loadPreModuleRoutes()
		{
			TBGLogging::log('Loading first batch of routes', 'routing');
			if (!($routes_1 = TBGCache::get(TBGCache::KEY_PREMODULES_ROUTES_CACHE)))
			{
				if (!($routes_1 = TBGCache::fileGet(TBGCache::KEY_PREMODULES_ROUTES_CACHE)))
				{
					TBGLogging::log('generating routes', 'routing');
					require THEBUGGENIE_CORE_PATH . 'load_routes.inc.php';
					TBGCache::fileAdd(TBGCache::KEY_PREMODULES_ROUTES_CACHE, self::getRouting()->getRoutes());
				}
				else
				{
					TBGLogging::log('using disk cached routes', 'routing');
					self::getRouting()->setRoutes($routes_1);
				}
				TBGCache::add(TBGCache::KEY_PREMODULES_ROUTES_CACHE, self::getRouting()->getRoutes());
			}
			else
			{
				TBGLogging::log('loading routes from cache', 'routing');
				self::getRouting()->setRoutes($routes_1);
			}
			TBGLogging::log('...done', 'routing');
		}

		protected static function loadPostModuleRoutes()
		{
			TBGLogging::log('Loading last batch of routes', 'routing');
			if (!($routes = TBGCache::get(TBGCache::KEY_POSTMODULES_ROUTES_CACHE)))
			{
				if (!($routes = TBGCache::fileGet(TBGCache::KEY_POSTMODULES_ROUTES_CACHE)))
				{
					TBGLogging::log('generating postmodule routes', 'routing');
					require THEBUGGENIE_CORE_PATH . 'load_routes_postmodules.inc.php';
					TBGCache::fileAdd(TBGCache::KEY_POSTMODULES_ROUTES_CACHE, self::getRouting()->getRoutes());
				}
				else
				{
					TBGLogging::log('using disk cached postmodule routes', 'routing');
					self::getRouting()->setRoutes($routes);
				}
				TBGCache::add(TBGCache::KEY_POSTMODULES_ROUTES_CACHE, self::getRouting()->getRoutes());
			}
			else
			{
				TBGLogging::log('loading postmodule routes from cache', 'routing');
				self::getRouting()->setRoutes($routes);
			}
			TBGLogging::log('...done', 'routing');
		}

		/**
		 * 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
			{
				TBGLogging::log('Changing language to '.$language);
				self::$_i18n = new TBGI18n($language);
				self::$_i18n->initialize();
			}
		}
		
		/**
		 * Get the i18n object
		 *
		 * @return TBGI18n
		 */
		public static function getI18n()
		{
			if (!self::$_i18n instanceof TBGI18n)
			{
				TBGLogging::log('Cannot access the translation object until the i18n system has been initialized!', 'i18n', TBGLogging::LEVEL_WARNING);
				throw new Exception('Cannot access the translation object until the i18n system has been initialized!');
				//self::reinitializeI18n(self::getUser()->getLanguage());
			}
			return self::$_i18n;
		}
		
		/**
		 * Get available themes
		 * 
		 * @return array
		 */
		public static function getThemes()
		{
			$theme_path_handle = opendir(THEBUGGENIE_PATH . THEBUGGENIE_PUBLIC_FOLDER_NAME . DS . 'themes' . DS);
			$themes = array();
			
			while ($theme = readdir($theme_path_handle))
			{
				if (strstr($theme, '.') == '' && $theme != 'modules') 
				{ 
					$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(self::getRequest()->getParameter('tbg3_username'), self::getRequest()->getParameter('tbg3_password')) : $user;
				if (self::$_user->isAuthenticated())
				{
					if (self::$_user->isOffline() || self::$_user->isAway())
					{
						self::$_user->setOnline();
					}
					self::$_user->updateLastSeen();
					self::$_user->setTimezone(TBGSettings::getUserTimezone());
					self::$_user->setLanguage(TBGSettings::getUserLanguage());
					self::$_user->save();
					if (!(self::$_user->getGroup() instanceof TBGGroup))
					{
						throw new Exception('This user account belongs to a group that does not exist anymore. <br>Please contact the system administrator.');
					}
				}
			}
			catch (Exception $e)
			{
				throw $e;
			}
			return self::$_user;
		}
		
		/**
		 * Returns the user object
		 *
		 * @return TBGUser
		 */
		public static function getUser()
		{
			return self::$_user;
		}
		
		/**
		 * Set the current user
		 * 
		 * @param TBGUser $user 
		 */
		public static function setUser(TBGUser $user)
		{
			self::$_user = $user;
		}
		
		/**
		 * Loads and initializes all modules
		 */
		public static function loadModules()
		{
			TBGLogging::log('Loading modules');
			if (self::$_modules === null)
			{
				self::$_modules = array();
				if (self::isInstallmode()) return;

				if (!TBGCache::has(TBGCache::KEY_MODULE_PATHS) || !TBGCache::has(TBGCache::KEY_MODULES))
				{
					$modules = array();

					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 = THEBUGGENIE_MODULES_PATH . $module_name . DS . "classes" . DS;
							try
							{
								self::addClasspath($moduleClassPath);
								$module_paths[] = $moduleClassPath;
								if (file_exists($moduleClassPath . 'B2DB'))
								{
									self::addClasspath($moduleClassPath . 'B2DB' . DS);
									$module_paths[] = $moduleClassPath . 'B2DB' . DS;
								}
							}
							catch (Exception $e) { } // ignore "dir not exists" errors
						}
					}
					TBGLogging::log('done (getting modules from database)');
					TBGCache::add(TBGCache::KEY_MODULE_PATHS, $module_paths);
					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::$_modules[$module_name] = new $classname($moduleRow->get(TBGModulesTable::ID), $moduleRow);
							}
							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));
							}
						}
						else
						{
							throw new Exception('Cannot load module "' . $module_name . '" as class TBGModule - modules should extend the TBGModule class with their own class.');
						}
					}
					TBGCache::add(TBGCache::KEY_MODULES, self::$_modules);
					TBGLogging::log('done (setting up module objects)');
				}
				else
				{
					TBGLogging::log('using cached modules');
					$module_paths = TBGCache::get(TBGCache::KEY_MODULE_PATHS);
					foreach ($module_paths as $path)
					{
						self::addClasspath($path);
					}
					self::$_modules = TBGCache::get(TBGCache::KEY_MODULES);
					TBGLogging::log('done (using cached modules)');
				}

				TBGLogging::log('initializing modules');
				if (!empty(self::$_modules))
				{
					foreach (self::$_modules as $module_name => $module)
					{
						$module->initialize();
					}
					TBGLogging::log('done (initializing modules)');
				}
				else
				{
					TBGLogging::log('no modules found');
				}
			}
			else
			{
				TBGLogging::log('Modules already loaded', 'core', TBGLogging::LEVEL_FATAL);
			}
			TBGLogging::log('...done');
		}
		
		/**
		 * Adds a module to the module list
		 *
		 * @param TBGModule $module
		 */
		public static function addModule($module, $module_name)
		{
			if (self::$_modules === null)
			{
				self::$_modules = array();
			}
			self::$_modules[$module_name] = $module;
		}
		
		/**
		 * Returns an array of modules
		 *
		 * @return array
		 */
		public static function getModules()
		{
			return self::$_modules;
		}

		/**
		 * Get uninstalled modules
		 *
		 * @return array
		 */
		public static function getUninstalledModules()
		{
			$module_path_handle = opendir(THEBUGGENIE_MODULES_PATH);
			$modules = array();
			while ($module_name = readdir($module_path_handle))
			{
				if (is_dir(THEBUGGENIE_MODULES_PATH . $module_name) && file_exists(THEBUGGENIE_MODULES_PATH . $module_name . DS . 'module'))
				{
					if (self::isModuleLoaded($module_name)) continue;
					$modules[$module_name] = file_get_contents(THEBUGGENIE_MODULES_PATH . $module_name . DS . 'module');
				}
			}
			return $modules;
		}
		
		/**
		 * Returns a specified module
		 *
		 * @param string $module_name
		 * 
		 * @return TBGModule
		 */
		public static function getModule($module_name)
		{
			if (!self::isModuleLoaded($module_name))
			{
				throw new Exception('This module is not loaded');
			}
			else
			{
				return self::$_modules[$module_name];	
			}
		}
		
		/**
		 * Whether or not a module is loaded
		 *
		 * @param string $module_name
		 * 
		 * @return boolean
		 */
		public static function isModuleLoaded($module_name)
		{
			if (isset(self::$_modules[$module_name]))
			{
				return true;
			}
			else
			{
				return false;
			}
		}

		/**
		 * Return all permissions available
		 * 
		 * @param string $type
		 * @param integer $uid
		 * @param integer $tid
		 * @param integer $gid
		 * @param integer $target_id[optional]
		 * @param boolean $all[optional]
		 *
		 * @return array
		 */
		public static function getAllPermissions($type, $uid, $tid, $gid, $target_id = null, $all = false)
		{
			$crit = new B2DBCriteria();
			$crit->addWhere(TBGPermissionsTable::SCOPE, self::getScope()->getID());
			$crit->addWhere(TBGPermissionsTable::PERMISSION_TYPE, $type);

			if (($uid + $tid + $gid) == 0 && !$all)
			{
				$crit->addWhere(TBGPermissionsTable::UID, $uid);
				$crit->addWhere(TBGPermissionsTable::TID, $tid);
				$crit->addWhere(TBGPermissionsTable::GID, $gid);
			}
			else
			{
				switch (true)
				{
					case ($uid != 0):
						$crit->addWhere(TBGPermissionsTable::UID, $uid);
					case ($tid != 0):
						$crit->addWhere(TBGPermissionsTable::TID, $tid);
					case ($gid != 0):
						$crit->addWhere(TBGPermissionsTable::GID, $gid);
				}
			}
			if ($target_id != null)
			{
				$crit->addWhere(TBGPermissionsTable::TARGET_ID, $target_id);
			}
	
			$permissions = array();

			if ($res = B2DB::getTable('TBGPermissionsTable')->doSelect($crit))
			{
				while ($row = $res->getNextRow())
				{
					$permissions[] = array('p_type' => $row->get(TBGPermissionsTable::PERMISSION_TYPE), 'target_id' => $row->get(TBGPermissionsTable::TARGET_ID), 'allowed' => $row->get(TBGPermissionsTable::ALLOWED), 'uid' => $row->get(TBGPermissionsTable::UID), 'gid' => $row->get(TBGPermissionsTable::GID), 'tid' => $row->get(TBGPermissionsTable::TID), 'id' => $row->get(TBGPermissionsTable::ID));
				}
			}
	
			return $permissions;
		}
		
		/**
		 * Cache all permissions
		 */
		public static function cacheAllPermissions()
		{
			TBGLogging::log('caches permissions');
			self::$_permissions = array();
			
			if ($permissions = TBGCache::get('permissions'))
			{
				self::$_permissions = $permissions;
				TBGLogging::log('Using cached permissions');
			}
			else
			{
				if (!$permissions = TBGCache::fileGet(TBGCache::KEY_PERMISSIONS_CACHE))
				{
					TBGLogging::log('starting to cache access permissions');
					if ($res = B2DB::getTable('TBGPermissionsTable')->getAll())
					{
						while ($row = $res->getNextRow())
						{
							if (!array_key_exists($row->get(TBGPermissionsTable::MODULE), self::$_permissions))
							{
								self::$_permissions[$row->get(TBGPermissionsTable::MODULE)] = array();
							}
							if (!array_key_exists($row->get(TBGPermissionsTable::PERMISSION_TYPE), self::$_permissions[$row->get(TBGPermissionsTable::MODULE)]))
							{
								self::$_permissions[$row->get(TBGPermissionsTable::MODULE)][$row->get(TBGPermissionsTable::PERMISSION_TYPE)] = array();
							}
							if (!array_key_exists($row->get(TBGPermissionsTable::TARGET_ID), self::$_permissions[$row->get(TBGPermissionsTable::MODULE)][$row->get(TBGPermissionsTable::PERMISSION_TYPE)]))
							{
								self::$_permissions[$row->get(TBGPermissionsTable::MODULE)][$row->get(TBGPermissionsTable::PERMISSION_TYPE)][$row->get(TBGPermissionsTable::TARGET_ID)] = array();
							}
							self::$_permissions[$row->get(TBGPermissionsTable::MODULE)][$row->get(TBGPermissionsTable::PERMISSION_TYPE)][$row->get(TBGPermissionsTable::TARGET_ID)][] = array('uid' => $row->get(TBGPermissionsTable::UID), 'gid' => $row->get(TBGPermissionsTable::GID), 'tid' => $row->get(TBGPermissionsTable::TID), 'allowed' => (bool) $row->get(TBGPermissionsTable::ALLOWED));
						}
					}
					TBGLogging::log('done (starting to cache access permissions)');
					TBGCache::fileAdd(TBGCache::KEY_PERMISSIONS_CACHE, self::$_permissions);
				}
				else
				{
					self::$_permissions = $permissions;
				}
				TBGCache::add('permissions', self::$_permissions);
			}
			TBGLogging::log('...cached');
		}

		public static function deleteModulePermissions($module_name, $scope)
		{
			if ($scope == TBGContext::getScope()->getID())
			{
				if (array_key_exists($module_name, self::$_permissions))
				{
					unset(self::$_permissions[$module_name]);
				}
			}
			TBGPermissionsTable::getTable()->deleteModulePermissions($module_name, $scope);
		}

		/**
		 * Cache a permission
		 * 
		 * @param array $perm_cache
		 */
		public static function cachePermission($perm_cache)
		{
			self::$_permissions[] = $perm_cache; 
		}

		/**
		 * Remove a saved permission
		 * 
		 * @param string $permission_type The permission type 
		 * @param mixed $target_id The target id
		 * @param string $module The name of the module for which the permission is valid
		 * @param integer $uid The user id for which the permission is valid, 0 for none
		 * @param integer $gid The group id for which the permission is valid, 0 for none
		 * @param integer $tid The team id for which the permission is valid, 0 for none
		 * @param boolean $recache Whether to recache after clearing this permission
		 * @param integer $scope A specified scope if not the default
		 */
		public static function removePermission($permission_type, $target_id, $module, $uid, $gid, $tid, $recache = true, $scope = null)
		{
			if ($scope === null) $scope = self::getScope()->getID();
			
			B2DB::getTable('TBGPermissionsTable')->removeSavedPermission($uid, $gid, $tid, $module, $permission_type, $target_id, $scope);
			
			if ($recache) self::cacheAllPermissions();
		}

		/**
		 * Save a permission setting
		 * 
		 * @param string $permission_type The permission type 
		 * @param mixed $target_id The target id
		 * @param string $module The name of the module for which the permission is valid
		 * @param integer $uid The user id for which the permission is valid, 0 for none
		 * @param integer $gid The group id for which the permission is valid, 0 for none
		 * @param integer $tid The team id for which the permission is valid, 0 for none
		 * @param boolean $allowed Allowed or not
		 * @param integer $scope[optional] A specified scope if not the default
		 */
		public static function setPermission($permission_type, $target_id, $module, $uid, $gid, $tid, $allowed, $scope = null)
		{
			if ($scope === null) $scope = self::getScope()->getID();
			
			self::removePermission($permission_type, $target_id, $module, $uid, $gid, $tid, false, $scope);
			TBGPermissionsTable::getTable()->setPermission($uid, $gid, $tid, $allowed, $module, $permission_type, $target_id, $scope);
			
			self::cacheAllPermissions();
		}

		public static function isPermissionSet($type, $permission_key, $id, $target_id = 0, $module_name = 'core')
		{
			if (array_key_exists($module_name, self::$_permissions) &&
				array_key_exists($permission_key, self::$_permissions[$module_name]) &&
				array_key_exists($target_id, self::$_permissions[$module_name][$permission_key]))
			{
				if ($type == 'group')
				{
					foreach (self::$_permissions[$module_name][$permission_key][$target_id] as $permission)
					{
						if ($permission['gid'] == $id) return $permission['allowed'];
					}
				}
				if ($type == 'user')
				{
					foreach (self::$_permissions[$module_name][$permission_key][$target_id] as $permission)
					{
						if ($permission['uid'] == $id) return $permission['allowed'];
					}
				}
				if ($type == 'team')
				{
					foreach (self::$_permissions[$module_name][$permission_key][$target_id] as $permission)
					{
						if ($permission['tid'] == $id) return $permission['allowed'];
					}
				}
				if ($type == 'everyone')
				{
					foreach (self::$_permissions[$module_name][$permission_key][$target_id] as $permission)
					{
						if ($permission['uid'] + $permission['gid'] + $permission['tid'] == 0)
						{
							return $permission['allowed'];
						}
					}
				}
			}
			return null;
		}
		
		protected static function _permissionsCheck($permissions, $uid, $gid, $tid)
		{
			try
			{
				if ($uid != 0 || $gid != 0 || $tid != 0)
				{
					if ($uid != 0)
					{
						foreach ($permissions as $key => $permission)
						{
							if (!array_key_exists('uid', $permission))
							{
								foreach ($permission as $pkey => $pp)
								{
									if ($pp['uid'] == $uid) {
										return $pp['allowed'];
									}
								}
							}
							elseif ($permission['uid'] == $uid) return $permission['allowed'];
						}
					}
	
					if (is_array($tid) || $tid != 0)
					{
						foreach ($permissions as $key => $permission)
						{
							if (!array_key_exists('tid', $permission))
							{
								foreach ($permission as $pkey => $pp)
								{
									if ((is_array($tid) && in_array($pp['tid'], array_keys($tid))) || $pp['tid'] == $tid)
									{
										return $pp['allowed'];
									}
								}
							}
							elseif ((is_array($tid) && in_array($permission['tid'], array_keys($tid))) || $permission['tid'] == $tid)
							{
								return $permission['allowed'];
							}
						}
					}
	
					if ($gid != 0)
					{
						foreach ($permissions as $key => $permission)
						{
							if (!array_key_exists('gid', $permission))
							{
								foreach ($permission as $pkey => $pp)
								{
									if ($pp['gid'] == $gid) return $pp['allowed'];
								}
							}
							elseif ($permission['gid'] == $gid) return $permission['allowed'];
						}
					}
				}
	
				foreach ($permissions as $key => $permission)
				{
					if (!array_key_exists('uid', $permission))
					{
						foreach ($permission as $pkey => $pp)
						{
							if ($pp['uid'] + $pp['gid'] + $pp['tid'] == 0) return $pp['allowed'];
						}
					}
					elseif ($permission['uid'] + $permission['gid'] + $permission['tid'] == 0) return $permission['allowed'];
				}
			}
			catch (Exception $e) { }
			
			return null;
		}
	
		/**
		 * Check to see if a specified user/group/team has access
		 * 
		 * @param string $permission_type The permission type 
		 * @param integer $uid The user id for which the permission is valid, 0 for all
		 * @param integer $gid The group id for which the permission is valid, 0 for all
		 * @param integer $tid The team id for which the permission is valid, 0 for all
		 * @param integer $target_id[optional] The target id
		 * @param string $module_name[optional] The name of the module for which the permission is valid
		 * @param boolean $explicit[optional] whether to check for an explicit permission and return false if not set
		 * @param boolean $permissive[optional] whether to return false or true when explicit fails
		 * 
		 * @return unknown_type
		 */
		public static function checkPermission($permission_type, $uid, $gid, $tid, $target_id = 0, $module_name = 'core', $explicit = false, $permissive = false)
		{
			$uid = (int) $uid;
			$gid = (int) $gid;
			if (array_key_exists($module_name, self::$_permissions) &&
				array_key_exists($permission_type, self::$_permissions[$module_name]) &&
				(array_key_exists($target_id, self::$_permissions[$module_name][$permission_type]) || $target_id === null))
			{
				$permissions_notarget = self::$_permissions[$module_name][$permission_type];
				$permissions_target = (array_key_exists($target_id, $permissions_notarget)) ? $permissions_notarget[$target_id] : array();
				<commit_msg>use correct permissions array for fallback permissions checking<commit_after><?php

	/**
	 * The core class of the B2 engine
	 *
	 * @author <NAME> <<EMAIL>>
	 * @version 3.1
	 * @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;
		
		static protected $_partials_visited = array();

		/**
		 * The current user
		 *
		 * @var TBGUser
		 */
		static protected $_user = null;
		
		/**
		 * List of modules 
		 * 
		 * @var array
		 */
		static protected $_modules = null;
		
		/**
		 * 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;
		
		/**
		 * Whether we're in upgrademode or not
		 * 
		 * @var boolean
		 */
		static protected $_upgrademode = 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;
		
		/**
		 * Do you want to disable minifcation of javascript and css?
		 * 
		 * @var boolean
		 */
		static protected $_minifyoff = false;

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

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

		/**
		 * 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 (!is_dir($classpath))
				throw new Exception("Cannot add {$classpath} to classpaths, since it doesn't exist");

			$classpath = realpath($classpath) . DS;
			
			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';

			self::$_classpaths[$classpath] = $classpath; // . $file;
			return true;
		}

		/**
		 * 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;
		}
		
		/**
		 * 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;
		}
		
		/**
		 * 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 (stristr(PHP_OS, 'WIN')) { self::$_tbgpath = str_replace("\\", "/", self::$_tbgpath); /* Windows adds a \ to the URL which we don't want */ }
			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;
			elseif (is_readable(THEBUGGENIE_PATH . 'upgrade'))
				self::$_installmode = self::$_upgrademode = true;
			elseif (!B2DB::isInitialized())
				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.");
		}

		/**
		 * Initialize the context
		 * 
		 * @return null
		 */
		public static function initialize()
		{
			try
			{
				self::$_request = new TBGRequest();
				self::$_response = new TBGResponse();
				self::$_factory = new TBGFactory();
				
				self::checkInstallMode();
				self::loadPreModuleRoutes();
				self::setScope();

				if (!self::$_installmode)
				{
					self::loadModules();
					self::initializeUser();
					TBGSettings::setTimezone();
				}
				else
					self::$_modules = array();
				
//				var_dump(self::getUser());die();
				self::setupI18n();
				
				self::loadPostModuleRoutes();
				TBGLogging::log('...done initializing');
			}
			catch (Exception $e)
			{
				if (!self::isCLI() && !self::isInstallmode())
					throw $e;
			}
		}
		
		protected static function setupI18n()
		{
			if (TBGContext::isCLI())
				return null;

			$language = (self::$_user instanceof TBGUser) ? self::$_user->getLanguage() : 'en_US';
			
			if (self::$_user instanceof TBGUser && self::$_user->getLanguage() == 'sys')
			{
				$language = 'en_US';
			}
			
			TBGLogging::log('Loading i18n strings');
			if (!self::$_i18n = TBGCache::get("i18n_{$language}"))
			{
				TBGLogging::log("Loading strings from file ({$language})");
				self::$_i18n = new TBGI18n($language);
				self::$_i18n->initialize();
				TBGCache::add("i18n_{$language}", self::$_i18n);
			}
			else
			{
				TBGLogging::log('Using cached i18n strings');
			}
			TBGLogging::log('...done');
		}

		protected static function initializeUser()
		{
			TBGLogging::log('Loading user');
			try
			{
				TBGLogging::log('is this logout?');
				if (self::getRequest()->getParameter('logout'))
				{
					TBGLogging::log('yes');
					self::logout();
				}
				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');
					self::cacheAllPermissions();
				}
			}
			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() != 'register1' && self::getRouting()->getCurrentRouteAction() != 'register2' && self::getRouting()->getCurrentRouteAction() != 'activate' && self::getRouting()->getCurrentRouteAction() != 'reset_password' && self::getRouting()->getCurrentRouteAction() != 'captcha' && self::getRouting()->getCurrentRouteAction() != 'login' && self::getRouting()->getCurrentRouteAction() != 'getBackdropPartial' && self::getRouting()->getCurrentRouteAction() != 'serve'))
					self::$_redirect_login = true;
				else
					self::$_user = self::factory()->TBGUser(TBGSettings::getDefaultUserID());
			}
			TBGLogging::log('...done');
		}

		protected static function loadPreModuleRoutes()
		{
			TBGLogging::log('Loading first batch of routes', 'routing');
			if (!($routes_1 = TBGCache::get(TBGCache::KEY_PREMODULES_ROUTES_CACHE)))
			{
				if (!($routes_1 = TBGCache::fileGet(TBGCache::KEY_PREMODULES_ROUTES_CACHE)))
				{
					TBGLogging::log('generating routes', 'routing');
					require THEBUGGENIE_CORE_PATH . 'load_routes.inc.php';
					TBGCache::fileAdd(TBGCache::KEY_PREMODULES_ROUTES_CACHE, self::getRouting()->getRoutes());
				}
				else
				{
					TBGLogging::log('using disk cached routes', 'routing');
					self::getRouting()->setRoutes($routes_1);
				}
				TBGCache::add(TBGCache::KEY_PREMODULES_ROUTES_CACHE, self::getRouting()->getRoutes());
			}
			else
			{
				TBGLogging::log('loading routes from cache', 'routing');
				self::getRouting()->setRoutes($routes_1);
			}
			TBGLogging::log('...done', 'routing');
		}

		protected static function loadPostModuleRoutes()
		{
			TBGLogging::log('Loading last batch of routes', 'routing');
			if (!($routes = TBGCache::get(TBGCache::KEY_POSTMODULES_ROUTES_CACHE)))
			{
				if (!($routes = TBGCache::fileGet(TBGCache::KEY_POSTMODULES_ROUTES_CACHE)))
				{
					TBGLogging::log('generating postmodule routes', 'routing');
					require THEBUGGENIE_CORE_PATH . 'load_routes_postmodules.inc.php';
					TBGCache::fileAdd(TBGCache::KEY_POSTMODULES_ROUTES_CACHE, self::getRouting()->getRoutes());
				}
				else
				{
					TBGLogging::log('using disk cached postmodule routes', 'routing');
					self::getRouting()->setRoutes($routes);
				}
				TBGCache::add(TBGCache::KEY_POSTMODULES_ROUTES_CACHE, self::getRouting()->getRoutes());
			}
			else
			{
				TBGLogging::log('loading postmodule routes from cache', 'routing');
				self::getRouting()->setRoutes($routes);
			}
			TBGLogging::log('...done', 'routing');
		}

		/**
		 * 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
			{
				TBGLogging::log('Changing language to '.$language);
				self::$_i18n = new TBGI18n($language);
				self::$_i18n->initialize();
			}
		}
		
		/**
		 * Get the i18n object
		 *
		 * @return TBGI18n
		 */
		public static function getI18n()
		{
			if (!self::$_i18n instanceof TBGI18n)
			{
				TBGLogging::log('Cannot access the translation object until the i18n system has been initialized!', 'i18n', TBGLogging::LEVEL_WARNING);
				throw new Exception('Cannot access the translation object until the i18n system has been initialized!');
				//self::reinitializeI18n(self::getUser()->getLanguage());
			}
			return self::$_i18n;
		}
		
		/**
		 * Get available themes
		 * 
		 * @return array
		 */
		public static function getThemes()
		{
			$theme_path_handle = opendir(THEBUGGENIE_PATH . THEBUGGENIE_PUBLIC_FOLDER_NAME . DS . 'themes' . DS);
			$themes = array();
			
			while ($theme = readdir($theme_path_handle))
			{
				if (strstr($theme, '.') == '' && $theme != 'modules') 
				{ 
					$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(self::getRequest()->getParameter('tbg3_username'), self::getRequest()->getParameter('tbg3_password')) : $user;
				if (self::$_user->isAuthenticated())
				{
					if (self::$_user->isOffline() || self::$_user->isAway())
					{
						self::$_user->setOnline();
					}
					self::$_user->updateLastSeen();
					self::$_user->setTimezone(TBGSettings::getUserTimezone());
					self::$_user->setLanguage(TBGSettings::getUserLanguage());
					self::$_user->save();
					if (!(self::$_user->getGroup() instanceof TBGGroup))
					{
						throw new Exception('This user account belongs to a group that does not exist anymore. <br>Please contact the system administrator.');
					}
				}
			}
			catch (Exception $e)
			{
				throw $e;
			}
			return self::$_user;
		}
		
		/**
		 * Returns the user object
		 *
		 * @return TBGUser
		 */
		public static function getUser()
		{
			return self::$_user;
		}
		
		/**
		 * Set the current user
		 * 
		 * @param TBGUser $user 
		 */
		public static function setUser(TBGUser $user)
		{
			self::$_user = $user;
		}
		
		/**
		 * Loads and initializes all modules
		 */
		public static function loadModules()
		{
			TBGLogging::log('Loading modules');
			if (self::$_modules === null)
			{
				self::$_modules = array();
				if (self::isInstallmode()) return;

				if (!TBGCache::has(TBGCache::KEY_MODULE_PATHS) || !TBGCache::has(TBGCache::KEY_MODULES))
				{
					$modules = array();

					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 = THEBUGGENIE_MODULES_PATH . $module_name . DS . "classes" . DS;
							try
							{
								self::addClasspath($moduleClassPath);
								$module_paths[] = $moduleClassPath;
								if (file_exists($moduleClassPath . 'B2DB'))
								{
									self::addClasspath($moduleClassPath . 'B2DB' . DS);
									$module_paths[] = $moduleClassPath . 'B2DB' . DS;
								}
							}
							catch (Exception $e) { } // ignore "dir not exists" errors
						}
					}
					TBGLogging::log('done (getting modules from database)');
					TBGCache::add(TBGCache::KEY_MODULE_PATHS, $module_paths);
					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::$_modules[$module_name] = new $classname($moduleRow->get(TBGModulesTable::ID), $moduleRow);
							}
							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));
							}
						}
						else
						{
							throw new Exception('Cannot load module "' . $module_name . '" as class TBGModule - modules should extend the TBGModule class with their own class.');
						}
					}
					TBGCache::add(TBGCache::KEY_MODULES, self::$_modules);
					TBGLogging::log('done (setting up module objects)');
				}
				else
				{
					TBGLogging::log('using cached modules');
					$module_paths = TBGCache::get(TBGCache::KEY_MODULE_PATHS);
					foreach ($module_paths as $path)
					{
						self::addClasspath($path);
					}
					self::$_modules = TBGCache::get(TBGCache::KEY_MODULES);
					TBGLogging::log('done (using cached modules)');
				}

				TBGLogging::log('initializing modules');
				if (!empty(self::$_modules))
				{
					foreach (self::$_modules as $module_name => $module)
					{
						$module->initialize();
					}
					TBGLogging::log('done (initializing modules)');
				}
				else
				{
					TBGLogging::log('no modules found');
				}
			}
			else
			{
				TBGLogging::log('Modules already loaded', 'core', TBGLogging::LEVEL_FATAL);
			}
			TBGLogging::log('...done');
		}
		
		/**
		 * Adds a module to the module list
		 *
		 * @param TBGModule $module
		 */
		public static function addModule($module, $module_name)
		{
			if (self::$_modules === null)
			{
				self::$_modules = array();
			}
			self::$_modules[$module_name] = $module;
		}
		
		/**
		 * Returns an array of modules
		 *
		 * @return array
		 */
		public static function getModules()
		{
			return self::$_modules;
		}

		/**
		 * Get uninstalled modules
		 *
		 * @return array
		 */
		public static function getUninstalledModules()
		{
			$module_path_handle = opendir(THEBUGGENIE_MODULES_PATH);
			$modules = array();
			while ($module_name = readdir($module_path_handle))
			{
				if (is_dir(THEBUGGENIE_MODULES_PATH . $module_name) && file_exists(THEBUGGENIE_MODULES_PATH . $module_name . DS . 'module'))
				{
					if (self::isModuleLoaded($module_name)) continue;
					$modules[$module_name] = file_get_contents(THEBUGGENIE_MODULES_PATH . $module_name . DS . 'module');
				}
			}
			return $modules;
		}
		
		/**
		 * Returns a specified module
		 *
		 * @param string $module_name
		 * 
		 * @return TBGModule
		 */
		public static function getModule($module_name)
		{
			if (!self::isModuleLoaded($module_name))
			{
				throw new Exception('This module is not loaded');
			}
			else
			{
				return self::$_modules[$module_name];	
			}
		}
		
		/**
		 * Whether or not a module is loaded
		 *
		 * @param string $module_name
		 * 
		 * @return boolean
		 */
		public static function isModuleLoaded($module_name)
		{
			if (isset(self::$_modules[$module_name]))
			{
				return true;
			}
			else
			{
				return false;
			}
		}

		/**
		 * Return all permissions available
		 * 
		 * @param string $type
		 * @param integer $uid
		 * @param integer $tid
		 * @param integer $gid
		 * @param integer $target_id[optional]
		 * @param boolean $all[optional]
		 *
		 * @return array
		 */
		public static function getAllPermissions($type, $uid, $tid, $gid, $target_id = null, $all = false)
		{
			$crit = new B2DBCriteria();
			$crit->addWhere(TBGPermissionsTable::SCOPE, self::getScope()->getID());
			$crit->addWhere(TBGPermissionsTable::PERMISSION_TYPE, $type);

			if (($uid + $tid + $gid) == 0 && !$all)
			{
				$crit->addWhere(TBGPermissionsTable::UID, $uid);
				$crit->addWhere(TBGPermissionsTable::TID, $tid);
				$crit->addWhere(TBGPermissionsTable::GID, $gid);
			}
			else
			{
				switch (true)
				{
					case ($uid != 0):
						$crit->addWhere(TBGPermissionsTable::UID, $uid);
					case ($tid != 0):
						$crit->addWhere(TBGPermissionsTable::TID, $tid);
					case ($gid != 0):
						$crit->addWhere(TBGPermissionsTable::GID, $gid);
				}
			}
			if ($target_id != null)
			{
				$crit->addWhere(TBGPermissionsTable::TARGET_ID, $target_id);
			}
	
			$permissions = array();

			if ($res = B2DB::getTable('TBGPermissionsTable')->doSelect($crit))
			{
				while ($row = $res->getNextRow())
				{
					$permissions[] = array('p_type' => $row->get(TBGPermissionsTable::PERMISSION_TYPE), 'target_id' => $row->get(TBGPermissionsTable::TARGET_ID), 'allowed' => $row->get(TBGPermissionsTable::ALLOWED), 'uid' => $row->get(TBGPermissionsTable::UID), 'gid' => $row->get(TBGPermissionsTable::GID), 'tid' => $row->get(TBGPermissionsTable::TID), 'id' => $row->get(TBGPermissionsTable::ID));
				}
			}
	
			return $permissions;
		}
		
		/**
		 * Cache all permissions
		 */
		public static function cacheAllPermissions()
		{
			TBGLogging::log('caches permissions');
			self::$_permissions = array();
			
			if ($permissions = TBGCache::get('permissions'))
			{
				self::$_permissions = $permissions;
				TBGLogging::log('Using cached permissions');
			}
			else
			{
				if (!$permissions = TBGCache::fileGet(TBGCache::KEY_PERMISSIONS_CACHE))
				{
					TBGLogging::log('starting to cache access permissions');
					if ($res = B2DB::getTable('TBGPermissionsTable')->getAll())
					{
						while ($row = $res->getNextRow())
						{
							if (!array_key_exists($row->get(TBGPermissionsTable::MODULE), self::$_permissions))
							{
								self::$_permissions[$row->get(TBGPermissionsTable::MODULE)] = array();
							}
							if (!array_key_exists($row->get(TBGPermissionsTable::PERMISSION_TYPE), self::$_permissions[$row->get(TBGPermissionsTable::MODULE)]))
							{
								self::$_permissions[$row->get(TBGPermissionsTable::MODULE)][$row->get(TBGPermissionsTable::PERMISSION_TYPE)] = array();
							}
							if (!array_key_exists($row->get(TBGPermissionsTable::TARGET_ID), self::$_permissions[$row->get(TBGPermissionsTable::MODULE)][$row->get(TBGPermissionsTable::PERMISSION_TYPE)]))
							{
								self::$_permissions[$row->get(TBGPermissionsTable::MODULE)][$row->get(TBGPermissionsTable::PERMISSION_TYPE)][$row->get(TBGPermissionsTable::TARGET_ID)] = array();
							}
							self::$_permissions[$row->get(TBGPermissionsTable::MODULE)][$row->get(TBGPermissionsTable::PERMISSION_TYPE)][$row->get(TBGPermissionsTable::TARGET_ID)][] = array('uid' => $row->get(TBGPermissionsTable::UID), 'gid' => $row->get(TBGPermissionsTable::GID), 'tid' => $row->get(TBGPermissionsTable::TID), 'allowed' => (bool) $row->get(TBGPermissionsTable::ALLOWED));
						}
					}
					TBGLogging::log('done (starting to cache access permissions)');
					TBGCache::fileAdd(TBGCache::KEY_PERMISSIONS_CACHE, self::$_permissions);
				}
				else
				{
					self::$_permissions = $permissions;
				}
				TBGCache::add('permissions', self::$_permissions);
			}
			TBGLogging::log('...cached');
		}

		public static function deleteModulePermissions($module_name, $scope)
		{
			if ($scope == TBGContext::getScope()->getID())
			{
				if (array_key_exists($module_name, self::$_permissions))
				{
					unset(self::$_permissions[$module_name]);
				}
			}
			TBGPermissionsTable::getTable()->deleteModulePermissions($module_name, $scope);
		}

		/**
		 * Cache a permission
		 * 
		 * @param array $perm_cache
		 */
		public static function cachePermission($perm_cache)
		{
			self::$_permissions[] = $perm_cache; 
		}

		/**
		 * Remove a saved permission
		 * 
		 * @param string $permission_type The permission type 
		 * @param mixed $target_id The target id
		 * @param string $module The name of the module for which the permission is valid
		 * @param integer $uid The user id for which the permission is valid, 0 for none
		 * @param integer $gid The group id for which the permission is valid, 0 for none
		 * @param integer $tid The team id for which the permission is valid, 0 for none
		 * @param boolean $recache Whether to recache after clearing this permission
		 * @param integer $scope A specified scope if not the default
		 */
		public static function removePermission($permission_type, $target_id, $module, $uid, $gid, $tid, $recache = true, $scope = null)
		{
			if ($scope === null) $scope = self::getScope()->getID();
			
			B2DB::getTable('TBGPermissionsTable')->removeSavedPermission($uid, $gid, $tid, $module, $permission_type, $target_id, $scope);
			
			if ($recache) self::cacheAllPermissions();
		}

		/**
		 * Save a permission setting
		 * 
		 * @param string $permission_type The permission type 
		 * @param mixed $target_id The target id
		 * @param string $module The name of the module for which the permission is valid
		 * @param integer $uid The user id for which the permission is valid, 0 for none
		 * @param integer $gid The group id for which the permission is valid, 0 for none
		 * @param integer $tid The team id for which the permission is valid, 0 for none
		 * @param boolean $allowed Allowed or not
		 * @param integer $scope[optional] A specified scope if not the default
		 */
		public static function setPermission($permission_type, $target_id, $module, $uid, $gid, $tid, $allowed, $scope = null)
		{
			if ($scope === null) $scope = self::getScope()->getID();
			
			self::removePermission($permission_type, $target_id, $module, $uid, $gid, $tid, false, $scope);
			TBGPermissionsTable::getTable()->setPermission($uid, $gid, $tid, $allowed, $module, $permission_type, $target_id, $scope);
			
			self::cacheAllPermissions();
		}

		public static function isPermissionSet($type, $permission_key, $id, $target_id = 0, $module_name = 'core')
		{
			if (array_key_exists($module_name, self::$_permissions) &&
				array_key_exists($permission_key, self::$_permissions[$module_name]) &&
				array_key_exists($target_id, self::$_permissions[$module_name][$permission_key]))
			{
				if ($type == 'group')
				{
					foreach (self::$_permissions[$module_name][$permission_key][$target_id] as $permission)
					{
						if ($permission['gid'] == $id) return $permission['allowed'];
					}
				}
				if ($type == 'user')
				{
					foreach (self::$_permissions[$module_name][$permission_key][$target_id] as $permission)
					{
						if ($permission['uid'] == $id) return $permission['allowed'];
					}
				}
				if ($type == 'team')
				{
					foreach (self::$_permissions[$module_name][$permission_key][$target_id] as $permission)
					{
						if ($permission['tid'] == $id) return $permission['allowed'];
					}
				}
				if ($type == 'everyone')
				{
					foreach (self::$_permissions[$module_name][$permission_key][$target_id] as $permission)
					{
						if ($permission['uid'] + $permission['gid'] + $permission['tid'] == 0)
						{
							return $permission['allowed'];
						}
					}
				}
			}
			return null;
		}
		
		protected static function _permissionsCheck($permissions, $uid, $gid, $tid)
		{
			try
			{
				if ($uid != 0 || $gid != 0 || $tid != 0)
				{
					if ($uid != 0)
					{
						foreach ($permissions as $key => $permission)
						{
							if (!array_key_exists('uid', $permission))
							{
								foreach ($permission as $pkey => $pp)
								{
									if ($pp['uid'] == $uid) {
										return $pp['allowed'];
									}
								}
							}
							elseif ($permission['uid'] == $uid) return $permission['allowed'];
						}
					}
	
					if (is_array($tid) || $tid != 0)
					{
						foreach ($permissions as $key => $permission)
						{
							if (!array_key_exists('tid', $permission))
							{
								foreach ($permission as $pkey => $pp)
								{
									if ((is_array($tid) && in_array($pp['tid'], array_keys($tid))) || $pp['tid'] == $tid)
									{
										return $pp['allowed'];
									}
								}
							}
							elseif ((is_array($tid) && in_array($permission['tid'], array_keys($tid))) || $permission['tid'] == $tid)
							{
								return $permission['allowed'];
							}
						}
					}
	
					if ($gid != 0)
					{
						foreach ($permissions as $key => $permission)
						{
							if (!array_key_exists('gid', $permission))
							{
								foreach ($permission as $pkey => $pp)
								{
									if ($pp['gid'] == $gid) return $pp['allowed'];
								}
							}
							elseif ($permission['gid'] == $gid) return $permission['allowed'];
						}
					}
				}
	
				foreach ($permissions as $key => $permission)
				{
					if (!array_key_exists('uid', $permission))
					{
						foreach ($permission as $pkey => $pp)
						{
							if ($pp['uid'] + $pp['gid'] + $pp['tid'] == 0) return $pp['allowed'];
						}
					}
					elseif ($permission['uid'] + $permission['gid'] + $permission['tid'] == 0) return $permission['allowed'];
				}
			}
			catch (Exception $e) { }
			
			return null;
		}
	
		/**
		 * Check to see if a specified user/group/team has access
		 * 
		 * @param string $permission_type The permission type 
		 * @param integer $uid The user id for which the permission is valid, 0 for all
		 * @param integer $gid The group id for which the permission is valid, 0 for all
		 * @param integer $tid The team id for which the permission is valid, 0 for all
		 * @param integer $target_id[optional] The target id
		 * @param string $module_name[optional] The name of the module for which the permission is valid
		 * @param boolean $explicit[optional] whether to check for an explicit permission and return false if not set
		 * @param boolean $permissive[optional] whether to return false or true when explicit fails
		 * 
		 * @return unknown_type
		 */
		public static function checkPermission($permission_type, $uid, $gid, $tid, $target_id = 0, $module_name = 'core', $explicit = false, $permissive = false)
		{
			$uid = (int) $uid;
			$gid = (int) $gid;
			if (array_key_exists($module_name, self::$_permissions) &&
				array_key_exists($permission_type, self::$_permissions[$module_name]) &&
				(array_key_exists($target_id, self::$_permissions[$module_name][$permission_type]) || $target_id === null))
			{
				$permissions_notarget = self::$_permissions[$module_name][$permission_type][0];
				$permissions_target = (array_key_exists($target_id, self::$_permissions[$module_name][$permission_type])) ? self::$_permissions[$module_name][$permission_type][$target_id] : array();
				
{'fraction_non_alphanumeric': 0.15025333229402135, 'fraction_numerical': 0.004100085743237976, 'mean_word_length': 3.181071568244036, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 247, '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': '6323774', 'n_tokens_mistral': 26124, 'n_tokens_neox': 20268, 'n_words': 4613}