1<?php
2/*
3 * e107 website system
4 *
5 * Copyright (C) 2008-2012 e107 Inc (e107.org)
6 * Released under the terms and conditions of the
7 * GNU General Public License (http://www.gnu.org/licenses/gpl.txt)
8 *
9 * e107 Main
10 *
11 * $URL$
12 * $Id$
13*/
14
15if (!defined('e107_INIT')) { exit; }
16
17
18/**
19 *
20 * @package     e107
21 * @category	e107_handlers
22 * @version     $Id$
23 * @author      e107inc
24 *
25 *	e107_class - core class with many system-related methods
26 */
27
28class e107
29{
30	/**
31	 * IPV6 string for localhost - as stored in DB
32	 */
33	const LOCALHOST_IP = '0000:0000:0000:0000:0000:ffff:7f00:0001';
34	const LOCALHOST_IP2 = '0000:0000:0000:0000:0000:0000:0000:0001';
35
36	public $server_path;
37
38	public $e107_dirs = array();
39
40	/**
41	 * @var array  SQL connection data
42	 */
43	protected $e107_config_mysql_info = array();
44
45	public $http_path;
46	public $https_path;
47	public $base_path;
48	public $file_path;
49	public $site_path;
50	public $relative_base_path;
51	public $_ip_cache;
52	public $_host_name_cache;
53
54	public $site_theme; // class2 -> check valid theme
55	public $http_theme_dir; // class2 -> check valid theme
56
57	/**
58	 * Contains reference to global $_E107 array
59	 * Assignment is done inside prepare_request() method
60	 *
61	 * @var array
62	 */
63	protected $_E107 = array();
64
65	/**
66	 * @var string Current request type (http or https)
67	 */
68	protected $HTTP_SCHEME;
69
70	/**
71	 * Used for runtime caching of user extended struct
72	 *
73	 * @var array
74	 * @see e107::user()
75	 */
76	public $extended_struct;
77
78	/**
79	 * User login name
80	 *
81	 * @var array
82	 * @see init_session()
83	 */
84	public $currentUser;
85
86	/**
87	 * Run once load core shortcodes
88	 * while initialize SC parser
89	 *
90	 * @var boolean
91	 */
92	protected static $_sc_core_loaded = false;
93
94	/**
95	 * Singleton instance
96	 * Allow class extends - override {@link getInstance()}
97	 *
98	 * @var e107
99	 */
100	protected static $_instance = null;
101
102	/**
103	 * e107 registry
104	 *
105	 * @var array
106	 */
107	private static $_registry = array();
108
109	/**
110	 * e107 core config object storage
111	 *
112	 * @var array
113	 */
114	protected static $_core_config_arr = array();
115
116	/**
117	 * e107 plugin config object storage
118	 *
119	 * @var array
120	 */
121	protected static $_plug_config_arr = array();
122
123	/**
124	 * e107 theme config object storage
125	 *
126	 * @var array
127	 */
128	protected static $_theme_config_arr = array();
129
130
131
132	/**
133	 * e107 e107::css() on/off flag.
134	 *
135	 * @var bool
136	 */
137	protected static $_css_enabled = true;
138
139	/**
140	 * e107  e107::js() on/off flag.
141	 *
142	 * @var bool
143	 */
144	protected static $_js_enabled = true;
145
146
147
148
149	protected static $_breadcrumb = array();
150
151	/**
152	 * Core handlers array
153	 * For new/missing handler add
154	 * 'class name' => 'path' pair
155	 *
156	 * Used to auto-load core/plugin handlers
157	 * NOTE: aplhabetically sorted! (by class name)
158	 *
159	 * @see addHandler()
160	 * @see setHandlerOverload()
161	 * @see getSingleton()
162	 * @see getObject()
163	 * @var array
164	 */
165	protected static $_known_handlers = array(
166		'UserHandler'                    => '{e_HANDLER}user_handler.php',
167		'comment'                        => '{e_HANDLER}comment_class.php',
168		'e_date'                         => '{e_HANDLER}date_handler.php',
169		'convert'                        => '{e_HANDLER}date_handler.php', // BC Fix.
170		'db'                             => '{e_HANDLER}e_db_pdo_class.php',
171	//	'db'                             => '{e_HANDLER}mysql_class.php',
172		'e107Email'                      => '{e_HANDLER}mail.php',
173		'e107_event'                     => '{e_HANDLER}event_class.php',
174		'e107_db_debug'                  => '{e_HANDLER}db_debug_class.php',
175		'e107_traffic'                   => '{e_HANDLER}traffic_class.php',
176		'e107_user_extended'             => '{e_HANDLER}user_extended_class.php',
177		'e107plugin'                     => '{e_HANDLER}plugin_class.php',
178		'e_chart'                        => '{e_HANDLER}chart_class.php',
179		'e_core_session'                 => '{e_HANDLER}session_handler.php',
180		'e_admin_controller'             => '{e_HANDLER}admin_ui.php',
181		'e_admin_controller_ui'          => '{e_HANDLER}admin_ui.php',
182		'e_admin_dispatcher'             => '{e_HANDLER}admin_ui.php',
183		'e_admin_form_ui'                => '{e_HANDLER}admin_ui.php',
184		'e_admin_log'                    => '{e_HANDLER}admin_log_class.php',
185		'e_front_model'                  => '{e_HANDLER}model_class.php',
186		'e_admin_model'                  => '{e_HANDLER}model_class.php',
187		'e_admin_request'                => '{e_HANDLER}admin_ui.php',
188		'e_admin_response'               => '{e_HANDLER}admin_ui.php',
189		'e_admin_ui'                     => '{e_HANDLER}admin_ui.php',
190		'e_ajax' => '{e_HANDLER}e_ajax_class.php',
191		'e_array'                        => '{e_HANDLER}core_functions.php', // Old ArrayStorage.
192		'e_bbcode'                       => '{e_HANDLER}bbcode_handler.php',
193		'e_bb_base'                      => '{e_HANDLER}bbcode_handler.php',
194		'e_customfields'                 => '{e_HANDLER}e_customfields_class.php',
195		'e_file'                         => '{e_HANDLER}file_class.php',
196		'e_file_inspector_json_phar'     => '{e_HANDLER}e_file_inspector_json_phar.php',
197		'e_form'                         => '{e_HANDLER}form_handler.php',
198		'e_jshelper'                     => '{e_HANDLER}js_helper.php',
199		'e_media'                        => '{e_HANDLER}media_class.php',
200		'e_menu'                         => '{e_HANDLER}menu_class.php',
201		'e_model'                        => '{e_HANDLER}model_class.php',
202		'e_navigation'                   => '{e_HANDLER}sitelinks_class.php',
203		'e_news_item'                    => '{e_HANDLER}news_class.php',
204		'e_news_tree'                    => '{e_HANDLER}news_class.php',
205		'e_news_category_tree'           => '{e_HANDLER}news_class.php',
206		'e_object'                       => '{e_HANDLER}model_class.php',
207		'e_online'                       => '{e_HANDLER}online_class.php',
208		'e_parse'                        => '{e_HANDLER}e_parse_class.php',
209		'e_parser'                       => '{e_HANDLER}e_parse_class.php',
210		'e_parse_shortcode'              => '{e_HANDLER}shortcode_handler.php',
211		'e_plugin'                       => '{e_HANDLER}plugin_class.php',
212		'e_ranks'                        => '{e_HANDLER}e_ranks_class.php',
213		'e_shortcode'                    => '{e_HANDLER}shortcode_handler.php',
214		'e_system_user'                  => '{e_HANDLER}user_model.php',
215		'e_theme'                        => '{e_HANDLER}theme_handler.php',
216		'e_upgrade'                      => '{e_HANDLER}e_upgrade_class.php',
217		'e_user_model'                   => '{e_HANDLER}user_model.php',
218		'e_user'                         => '{e_HANDLER}user_model.php',
219		'e_user_extended_structure_tree' => '{e_HANDLER}user_model.php',
220		'e_user_provider'                => '{e_HANDLER}user_handler.php',
221		'e_userperms'                    => '{e_HANDLER}user_handler.php',
222		'e_validator'                    => '{e_HANDLER}validator_class.php',
223		'e_vars'                         => '{e_HANDLER}model_class.php',
224		'e_url'                          => '{e_HANDLER}application.php',
225		'ecache'                         => '{e_HANDLER}cache_handler.php',
226		'eController'                    => '{e_HANDLER}application.php',
227		'eDispatcher'                    => '{e_HANDLER}application.php',
228		'eException'                     => '{e_HANDLER}application.php',
229		'eFront'                         => '{e_HANDLER}application.php',
230		'eHelper'                        => '{e_HANDLER}application.php',
231		'eIPHandler'                     => '{e_HANDLER}iphandler_class.php',
232		'email_validation_class'         => '{e_HANDLER}mail_validation_class.php',
233		'eMessage'                       => '{e_HANDLER}message_handler.php',
234		'eRequest'                       => '{e_HANDLER}application.php',
235		'eResponse'                      => '{e_HANDLER}application.php',
236		'eRouter'                        => '{e_HANDLER}application.php',
237		'eShims'                         => '{e_HANDLER}Shims/eShims.php',
238		'eUrl'                           => '{e_HANDLER}e107Url.php',
239		'eUrlConfig'                     => '{e_HANDLER}application.php',
240		'eUrlRule'                       => '{e_HANDLER}application.php',
241		'language'                       => '{e_HANDLER}language_class.php',
242		'news'                           => '{e_HANDLER}news_class.php',
243		'notify'                         => '{e_HANDLER}notify_class.php',
244		'override'                       => '{e_HANDLER}override_class.php',
245		'rater'                          => '{e_HANDLER}rate_class.php',
246		'redirection'                    => '{e_HANDLER}redirection_class.php',
247		'secure_image'                   => '{e_HANDLER}secure_img_handler.php',
248		'sitelinks'                      => '{e_HANDLER}sitelinks_class.php',
249		'themeHandler'                   => '{e_HANDLER}theme_handler.php',
250		'user_class'                     => '{e_HANDLER}userclass_class.php',
251		'user_class_admin'               => '{e_HANDLER}userclass_class.php',
252		'userlogin'                      => '{e_HANDLER}login.php',
253		'validatorClass'                 => '{e_HANDLER}validator_class.php',
254		'xmlClass'                       => '{e_HANDLER}xml_class.php',
255		'e107MailManager'                => '{e_HANDLER}mail_manager_class.php',
256		'e_library_manager'              => '{e_HANDLER}library_manager.php',
257		'error_page'                     => '{e_HANDLER}error_page_class.php',
258	);
259
260	/**
261	 * Overload core handlers array
262	 * Format: 'core_class' => array('overload_class', 'overload_path');
263	 *
264	 * NOTE: to overload core singleton objects, you have to add record to
265	 * $_overload_handlers before the first singleton call.
266	 *
267	 * Example:
268	 * <code> array('e_form' => array('plugin_myplugin_form_handler' => '{e_PLUGIN}myplugin/includes/form/handler.php'));</code>
269	 *
270	 * Used to auto-load core handlers
271	 *
272	 * @var array
273	 */
274	protected static $_overload_handlers = array();
275
276
277	/**
278	 * Constructor
279	 *
280	 * Use {@link getInstance()}, direct instantiating
281	 * is not possible for singleton objects
282	 *
283	 */
284	protected function __construct()
285	{
286	  /*  if(defined('e_PDO') && e_PDO === false) // TODO
287        {
288            self::$_known_handlers['db'] = '{e_HANDLER}mysql_class.php';
289        }*/
290		// FIXME registered shutdown functions not executed after the $page output in footer - investigate
291		// Currently manually called in front-end/admin footer
292		//register_shutdown_function(array($this, 'destruct'));
293	}
294
295	private static function die_http_400()
296	{
297		header('HTTP/1.0 400 Bad Request', true, 400);
298		header('Content-Type: text/plain');
299		if (deftrue('e_DEBUG'))
300		{
301			echo "Bad Request: ";
302			debug_print_backtrace(0, 1);
303		}
304		exit();
305	}
306
307	/**
308	 * Cloning is not allowed
309	 *
310	 */
311	private function __clone()
312	{
313	}
314
315	/**
316	 * Get singleton instance (php4 no more supported)
317	 *
318	 * @return e107
319	 */
320	public static function getInstance()
321	{
322		if(null == self::$_instance)
323		{
324		    self::$_instance = new self();
325		}
326	  	return self::$_instance;
327	}
328
329	/**
330	 * Initialize environment path constants
331	 * Public proxy to the protected method {@link _init()}
332	 *
333	 * @param $e107_paths
334	 * @param $e107_root_path
335	 * @param $e107_config_mysql_info
336	 * @param array $e107_config_override
337	 * @return e107
338	 */
339	public function initCore($e107_paths, $e107_root_path, $e107_config_mysql_info, $e107_config_override = array())
340	{
341
342		return $this->_init($e107_paths, $e107_root_path, $e107_config_mysql_info, $e107_config_override);
343	}
344
345	/**
346	 * Initialize environment path constants while installing e107
347	 *
348	 * @param $e107_paths
349	 * @param $e107_root_path
350	 * @param array $e107_config_override
351	 * @return object|boolean e107
352	 */
353	public function initInstall($e107_paths, $e107_root_path, $e107_config_override = array())
354	{
355
356		$e107_config = 'e107_config.php';
357		if (!file_exists($e107_config))  // prevent blank-page with missing file during install.
358		{
359			if(file_put_contents($e107_config, '')===false)
360			{
361				return false;
362			}
363		}
364
365		// Do some security checks/cleanup, prepare the environment
366		$this->prepare_request();
367
368		//generated from mysql data at stage 5 of install.
369		$this->site_path = isset($e107_config_override['site_path']) ? $e107_config_override['site_path'] : "[hash]"; // placeholder
370
371		// folder info
372		//$this->e107_dirs = $e107_paths;
373		$this->setDirs($e107_paths, $e107_config_override);
374
375		// build all paths
376		$this->set_paths();
377		$this->file_path = $this->fix_windows_paths($e107_root_path)."/";
378
379		// set base path, SSL is auto-detected
380		$this->set_base_path();
381
382		// cleanup QUERY_STRING and friends, set  related constants
383		$this->set_request();
384
385		// set some core URLs (e_LOGIN/SIGNUP)
386		$this->set_urls();
387
388		return $this;
389	}
390
391	/**
392	 * Resolve paths, will run only once
393	 *
394	 * @param $e107_paths
395	 * @param $e107_root_path
396	 * @param $e107_config_mysql_info
397	 * @param array $e107_config_override
398	 * @return e107
399	 */
400	protected function _init($e107_paths, $e107_root_path, $e107_config_mysql_info, $e107_config_override = array())
401	{
402		if(!empty($this->e107_dirs)) return $this;
403
404		// Do some security checks/cleanup, prepare the environment
405		$this->prepare_request();
406
407		// mysql connection info
408		$this->e107_config_mysql_info = $e107_config_mysql_info;
409
410		// unique folder for e_MEDIA - support for multiple websites from single-install. Must be set before setDirs()
411	/*	if (!empty($e107_config_override['site_path']))
412		{
413			// $E107_CONFIG['site_path']
414			$this->site_path = $e107_config_override['site_path'];
415		}*/
416
417		if(empty($e107_config_override['site_path']))
418		{
419			$this->site_path = $this->makeSiteHash($e107_config_mysql_info['mySQLdefaultdb'], $e107_config_mysql_info['mySQLprefix']);
420		}
421
422		// Set default folder (and override paths) if missing from e107_config.php
423		$this->setDirs($e107_paths, $e107_config_override);
424
425		// various constants - MAGIC_QUOTES_GPC, MPREFIX, ...
426		$this->set_constants();
427
428		// build all paths
429		$this->set_paths();
430		$this->file_path = $this->fix_windows_paths($e107_root_path);
431
432		// set base path, SSL is auto-detected
433		$this->set_base_path();
434
435		// cleanup QUERY_STRING and friends, set  related constants
436		$this->set_request();
437
438		// set some core URLs (e_LOGIN/SIGNUP)
439		$this->set_urls();
440
441		if(!is_dir(e_SYSTEM))
442		{
443			mkdir(e_SYSTEM, 0755, true);
444		}
445
446		if(!is_dir(e_CACHE_IMAGE))
447		{
448			mkdir(e_CACHE_IMAGE, 0755, true);
449		}
450
451		// Prepare essential directories.
452		$this->prepareDirs();
453
454		return $this;
455	}
456
457	/**
458	 * Create a unique hash for each database configuration (multi-site support).
459	 */
460	function makeSiteHash($db, $prefix) // also used by install.
461	{
462		return substr(md5($db . "." . $prefix), 0, 10);
463	}
464
465	/**
466	 * Set system folders and override paths
467	 * $e107_paths is the 'compact' version of e107_config folder vars ($ADMIN_DIRECTORY, $IMAGES_DIRECTORY, etc)
468	 * $e107_config_override is the new override method - it can do it for all server and http paths via
469	 * the newly introduced $E107_CONFIG array.
470	 *
471	 * Overriding just replace _DIRECTORY with _SERVER or _HTTP:
472	 * - override server path example:
473	 * <code>$E107_CONFIG['SYSTEM_SERVER'] = '/home/user/system/';</code>
474	 *
475	 * - override http path example:
476	 * <code>$E107_CONFIG['MEDIA_VIDEOS_HTTP'] = 'http://static.mydomain.com/videos/';</code>
477	 *
478	 * @param array $e107_dirs Override folder instructions (*_DIRECTORY vars - e107_config.php)
479	 * @param array $e107_config_override Override path insructions ($E107_CONFIG array - e107_config.php)
480	 * @return e107
481	 */
482	public function setDirs($e107_dirs, $e107_config_override = array())
483	{
484		if(!empty($e107_config_override['site_path'])) // $E107_CONFIG['site_path']
485		{
486			$this->site_path = $e107_config_override['site_path'];
487		}
488
489		$override = array_merge((array) $e107_dirs, (array) $e107_config_override);
490
491		// override all
492		$this->e107_dirs = array_merge($this->defaultDirs($override), $override);
493
494		// Required for e_MEDIA_BASE, e_SYSTEM_BASE (free of site path constants);
495	//	$this->e107_dirs['MEDIA_BASE_DIRECTORY'] = $this->e107_dirs['MEDIA_DIRECTORY'];
496	//	$this->e107_dirs['SYSTEM_BASE_DIRECTORY'] = $this->e107_dirs['SYSTEM_BASE_DIRECTORY'];
497
498		// FIXME - remove this condition because:
499		// $this->site_path is appended to MEDIA_DIRECTORY in defaultDirs(), which is called above.
500		if(strpos($this->e107_dirs['MEDIA_DIRECTORY'],$this->site_path) === false)
501		{
502			$this->e107_dirs['MEDIA_DIRECTORY'] .= $this->site_path."/"; // multisite support.
503		}
504
505		// FIXME - remove this condition because:
506		// $this->site_path is appended to SYSTEM_DIRECTORY in defaultDirs(), which is called above.
507		if(strpos($this->e107_dirs['SYSTEM_DIRECTORY'],$this->site_path) === false)
508		{
509			$this->e107_dirs['SYSTEM_DIRECTORY'] .= $this->site_path."/"; // multisite support.
510		}
511
512		// FIXME Quick fix - override base cache folder for legacy configs (e.g. e107_files/cache), discuss
513		if(strpos($this->e107_dirs['CACHE_DIRECTORY'], $this->site_path) === false)
514		{
515			$this->e107_dirs['CACHE_DIRECTORY'] = $this->e107_dirs['SYSTEM_DIRECTORY']."cache/"; // multisite support.
516		}
517
518		return $this;
519	}
520
521	/**
522	 * Prepares essential directories.
523	 */
524	public function prepareDirs()
525	{
526		$file = e107::getFile();
527
528		// Essential directories which should be created and writable.
529		$essential_directories = array(
530			'MEDIA_DIRECTORY',
531			'SYSTEM_DIRECTORY',
532			'CACHE_DIRECTORY',
533
534			'CACHE_CONTENT_DIRECTORY',
535			'CACHE_IMAGE_DIRECTORY',
536			'CACHE_DB_DIRECTORY',
537			'CACHE_URL_DIRECTORY',
538
539			'LOGS_DIRECTORY',
540			'BACKUP_DIRECTORY',
541			'TEMP_DIRECTORY',
542			'IMPORT_DIRECTORY',
543		);
544
545		// Create directories which don't exist.
546		foreach($essential_directories as $directory)
547		{
548			if (!isset($this->e107_dirs[$directory])) {
549				continue;
550			}
551
552			$path = e_ROOT . $this->e107_dirs[$directory];
553			$file->prepareDirectory($path, FILE_CREATE_DIRECTORY);
554		}
555	}
556
557	/**
558	 * Get default e107 folders, root folders can be overridden by passed override array
559	 *
560	 * @param array $override_root
561	 * @param boolean $return_root
562	 * @return array
563	 */
564	public function defaultDirs($override_root = array(), $return_root = false)
565	{
566		$ret = array_merge(array(
567			'ADMIN_DIRECTORY' 		=> 'e107_admin/',
568			'IMAGES_DIRECTORY' 		=> 'e107_images/',
569			'THEMES_DIRECTORY' 		=> 'e107_themes/',
570			'PLUGINS_DIRECTORY' 	=> 'e107_plugins/',
571			'FILES_DIRECTORY' 		=> 'e107_files/', // DEPRECATED!!!
572			'HANDLERS_DIRECTORY' 	=> 'e107_handlers/',
573			'LANGUAGES_DIRECTORY' 	=> 'e107_languages/',
574			'DOCS_DIRECTORY' 		=> 'e107_docs/',
575			'MEDIA_DIRECTORY' 		=> 'e107_media/',
576			'SYSTEM_DIRECTORY' 		=> 'e107_system/',
577			'CORE_DIRECTORY' 		=> 'e107_core/',
578			'WEB_DIRECTORY' 		=> 'e107_web/',
579		), (array) $override_root);
580
581		$ret['MEDIA_BASE_DIRECTORY'] = $ret['MEDIA_DIRECTORY'];
582		$ret['SYSTEM_BASE_DIRECTORY'] = $ret['SYSTEM_DIRECTORY'];
583		$ret['MEDIA_DIRECTORY'] 	.= $this->site_path."/"; // multisite support.
584		$ret['SYSTEM_DIRECTORY'] 	.= $this->site_path."/"; // multisite support.
585
586		if($return_root) return $ret;
587
588		$ret['HELP_DIRECTORY'] 				= $ret['DOCS_DIRECTORY'].'help/';
589
590		$ret['MEDIA_IMAGES_DIRECTORY'] 		= $ret['MEDIA_DIRECTORY'].'images/';
591		$ret['MEDIA_ICONS_DIRECTORY'] 		= $ret['MEDIA_DIRECTORY'].'icons/';
592
593		$ret['MEDIA_VIDEOS_DIRECTORY'] 		= $ret['MEDIA_DIRECTORY'].'videos/';
594		$ret['MEDIA_FILES_DIRECTORY'] 		= $ret['MEDIA_DIRECTORY'].'files/';
595		$ret['MEDIA_UPLOAD_DIRECTORY'] 		= $ret['SYSTEM_DIRECTORY'].'temp/'; // security measure. Media is public, system is private.
596		$ret['AVATARS_DIRECTORY'] 			= $ret['MEDIA_DIRECTORY'].'avatars/';
597
598		$ret['WEB_JS_DIRECTORY'] 			= $ret['WEB_DIRECTORY'].'js/';
599	//	$ret['WEB_JS_DIRECTORY'] 			= $ret['FILES_DIRECTORY'].'jslib/';
600
601
602		$ret['WEB_CSS_DIRECTORY'] 			= $ret['WEB_DIRECTORY'].'css/';
603		$ret['WEB_IMAGES_DIRECTORY'] 		= $ret['WEB_DIRECTORY'].'images/';
604	//	$ret['WEB_PACKS_DIRECTORY'] 		= $ret['WEB_DIRECTORY'].'packages/';
605
606		$ret['DOWNLOADS_DIRECTORY']			= $ret['MEDIA_FILES_DIRECTORY'];
607		$ret['UPLOADS_DIRECTORY'] 			= $ret['MEDIA_UPLOAD_DIRECTORY'];
608
609		$ret['CACHE_DIRECTORY'] 			= $ret['SYSTEM_DIRECTORY'].'cache/';
610		$ret['CACHE_CONTENT_DIRECTORY'] 	= $ret['CACHE_DIRECTORY'].'content/';
611
612		if(defined('e_MEDIA_STATIC')) // experimental - subject to change.
613		{
614			$ret['CACHE_IMAGE_DIRECTORY'] 	= $ret['MEDIA_IMAGES_DIRECTORY'].'cache/';
615		}
616		else
617		{
618			$ret['CACHE_IMAGE_DIRECTORY'] 	= $ret['CACHE_DIRECTORY'].'images/';
619		}
620
621		$ret['CACHE_DB_DIRECTORY'] 			= $ret['CACHE_DIRECTORY'].'db/';
622		$ret['CACHE_URL_DIRECTORY'] 		= $ret['CACHE_DIRECTORY'].'url/';
623
624		$ret['AVATARS_UPLOAD_DIRECTORY'] 	= $ret['AVATARS_DIRECTORY'].'upload/';
625		$ret['AVATARS_DEFAULT_DIRECTORY'] 	= $ret['AVATARS_DIRECTORY'].'default/';
626
627		$ret['LOGS_DIRECTORY'] 				= $ret['SYSTEM_DIRECTORY'].'logs/';
628		$ret['BACKUP_DIRECTORY'] 			= $ret['SYSTEM_DIRECTORY'].'backup/';
629		$ret['TEMP_DIRECTORY'] 				= $ret['SYSTEM_DIRECTORY'].'temp/';
630		$ret['IMPORT_DIRECTORY'] 			= $ret['SYSTEM_DIRECTORY'].'import/';
631
632		return $ret;
633	}
634
635	/**
636	 * Set mysql data
637	 *
638	 * @param $e107_config_mysql_info
639	 * @return e107
640	 */
641	public function initInstallSql($e107_config_mysql_info)
642	{
643		// mysql connection info
644		$this->e107_config_mysql_info = $e107_config_mysql_info;
645
646		// various constants - MAGIC_QUOTES_GPC, MPREFIX, ...
647		$this->set_constants();
648
649		return $this;
650	}
651
652	/**
653	 * Get data from the registry
654	 * Returns $default if data not found
655	 * Replacement of cachevar()
656	 *
657	 * @param string $id
658	 * @param null $default
659	 * @return mixed
660	 */
661	public static function getRegistry($id, $default = null)
662	{
663		if(isset(self::$_registry[$id]))
664		{
665			return self::$_registry[$id];
666		}
667
668		if($id === '_all_')
669		{
670			return self::$_registry;
671		}
672
673		return $default;
674	}
675
676	/**
677	 * Add data to the registry - replacement of getcachedvars().
678	 * $id is path-like unique id bind to the passed data.
679	 * If $data argument is null, $id will be removed from the registry.
680	 * When removing objects from the registry, __destruct() method will be auto-executed
681	 * if available
682	 *
683	 * Naming standards (namespaces):
684	 * 'area/area_id/storage_type'<br>
685	 * where <br>
686	 * - area = 'core'|'plugin'|'external' (everything else)
687	 * - area_id = core handler id|plugin name (depends on area)
688	 * - (optional) storage_type = current data storage stack
689	 *
690	 * Examples:
691	 * - 'core/e107/' - reserved for this class
692	 * - 'core/e107/singleton/' - singleton objects repo {@link getSingleton()}
693	 *
694	 * @param string $id
695	 * @param mixed|null $data
696	 * @param bool $allow_override
697	 */
698	public static function setRegistry($id, $data = null, $allow_override = true)
699	{
700		if(null === $data)
701		{
702			if(isset(self::$_registry[$id]) && is_object(self::$_registry[$id]) && method_exists(self::$_registry[$id], '__destruct'))
703			{
704				self::$_registry[$id]->__destruct();
705			}
706			unset(self::$_registry[$id]);
707			return;
708		}
709
710		if(!$allow_override && null !== self::getRegistry($id))
711		{
712			return;
713		}
714
715		self::$_registry[$id] = $data;
716	}
717
718	/**
719	 * Get folder name (e107_config)
720	 * Replaces all $(*)_DIRECTORY globals
721	 * Example: <code>$e107->getFolder('images')</code>;
722	 *
723	 * @param string $for
724	 * @return string
725	 */
726	public static function getFolder($for)
727	{
728		$key = strtoupper($for).'_DIRECTORY';
729		$self = self::getInstance();
730		return (isset($self->e107_dirs[$key]) ? $self->e107_dirs[$key] : '');
731	}
732
733	/**
734	 * Get value from $_E107 config array
735	 * Note: will always return false if called before prepare_request() method!
736	 *
737	 * @param string $key
738	 * @return boolean
739	 */
740	public static function getE107($key = null)
741	{
742		$self = self::getInstance();
743		if(null === $key) return $self->_E107;
744		return (isset($self->_E107[$key]) && $self->_E107[$key] ? true : false);
745	}
746
747	/**
748	 * Convenient proxy to $_E107 getter - check if
749	 * the system is currently running in cli mode
750	 * Note: will always return false if called before prepare_request() method!
751	 *
752	 * @return boolean
753	 */
754	public static function isCli()
755	{
756		return self::getE107('cli');
757	}
758
759	/**
760	 * Get mysql config var (e107_config.php)
761	 * Replaces all $mySQL(*) globals
762	 * Example: <code>$e107->getMySQLConfig('prefix');</code>
763	 *
764	 * @param string $for prefix|server|user|password|defaultdb - leave blank for full array.
765	 * @return string|array
766	 */
767	public static function getMySQLConfig($for='')
768	{
769		$key = 'mySQL'.$for;
770		$self = self::getInstance();
771
772		if($for == '')
773		{
774			return 	$self->e107_config_mysql_info;
775		}
776
777		return (isset($self->e107_config_mysql_info[$key]) ? $self->e107_config_mysql_info[$key] : '');
778	}
779
780
781	/**
782	 * Return a unique path based on database used. ie. multi-site support from single install.
783	 *
784	 * @return string
785	 * @author
786	 */
787	function getSitePath()
788	{
789		$self = self::getInstance();
790		return $self->site_path;
791	}
792
793	/**
794	 * Get known handler path
795	 *
796	 * @param string $class_name
797	 * @param boolean $parse_path [optional] parse path shortcodes
798	 * @return string|null
799	 */
800	public static function getHandlerPath($class_name, $parse_path = true)
801	{
802		$ret = isset(self::$_known_handlers[$class_name]) ? self::$_known_handlers[$class_name] : null;
803		if($parse_path && $ret)
804		{
805			$ret = self::getParser()->replaceConstants($ret);
806		}
807
808		return $ret;
809	}
810
811	/**
812	 * Add handler to $_known_handlers array on runtime
813	 * If class name is array, method will add it (recursion) and ignore $path argument
814	 *
815	 * @param array|string $class_name
816	 * @param string $path [optional]
817	 * @return void
818	 */
819	public static function addHandler($class_name, $path = '')
820	{
821		if(is_array($class_name))
822		{
823			foreach ($class_name as $cname => $path)
824			{
825				self::addHandler($cname, $path);
826			}
827			return;
828		}
829		if(!self::isHandler($class_name))
830		{
831			self::$_known_handlers[$class_name] = $path;
832		}
833	}
834
835	/**
836	 * Check handler presence
837	 *
838	 * @param string $class_name
839	 * @return boolean
840	 */
841	public static function isHandler($class_name)
842	{
843		return isset(self::$_known_handlers[$class_name]);
844	}
845
846	/**
847	 * Get overlod class and path (if any)
848	 *
849	 * @param string $class_name
850	 * @param bool|object $default_handler [optional] return data from $_known_handlers if no overload data available
851	 * @param bool|object $parse_path [optional] parse path shortcodes
852	 * @return array
853	 */
854	public static function getHandlerOverload($class_name, $default_handler = true, $parse_path = true)
855	{
856		$ret = (isset(self::$_overload_handlers[$class_name]) ? self::$_overload_handlers[$class_name] : ($default_handler ? array($class_name, self::getHandlerPath($class_name, false)) : array()));
857		if ($parse_path && isset($ret[1]))
858		{
859			$ret[1] = self::getParser()->replaceConstants($ret[1]);
860		}
861
862		return $ret;
863	}
864
865	/**
866	 * Overload present handler.
867	 * If class name is array, method will add it (recursion) and
868	 * ignore $overload_class_name and  $overload_path arguments
869	 *
870	 * @param string $class_name
871	 * @param string $overload_class_name [optional]
872	 * @param string $overload_path [optional]
873	 * @return void
874	 */
875	public static function setHandlerOverload($class_name, $overload_class_name = '', $overload_path = '')
876	{
877		if(is_array($class_name))
878		{
879			foreach ($class_name as $cname => $overload_array)
880			{
881				self::setHandlerOverload($cname, $overload_array[0], $overload_array[1]);
882			}
883			return;
884		}
885		if(self::isHandler($class_name) && !self::isHandlerOverloadable($class_name))
886		{
887			self::$_overload_handlers[$class_name] = array($overload_class_name, $overload_path);
888		}
889	}
890
891	/**
892	 * Check if handler is already overloaded
893	 *
894	 * @param string $class_name
895	 * @return boolean
896	 */
897	public static function isHandlerOverloadable($class_name)
898	{
899		return isset(self::$_overload_handlers[$class_name]);
900	}
901
902	/**
903	 * Retrieve singleton object
904	 *
905	 * @param string $class_name
906	 * @param string|boolean $path optional script path
907	 * @param string $regpath additional registry path
908	 * @return Object
909	 */
910	public static function getSingleton($class_name, $path = true, $regpath = '',$vars=null)
911	{
912
913		$id = 'core/e107/singleton/'.$class_name.$regpath;
914
915		if(!empty($vars))
916		{
917			$id .= '/';
918			$id .= is_array($vars) ? crc32(serialize($vars)): crc32($vars);
919		}
920
921		//singleton object found - overload not possible
922		if(self::getRegistry($id))
923		{
924			return self::getRegistry($id);
925		}
926
927		//auto detection + overload check
928		if(is_bool($path))
929		{
930			//overload allowed
931			if(true === $path && self::isHandlerOverloadable($class_name))
932			{
933				$tmp = self::getHandlerOverload($class_name);
934				$class_name = $tmp[0];
935				$path = $tmp[1];
936			}
937			//overload not allowed
938			else
939			{
940				$path = self::getHandlerPath($class_name);
941			}
942		}
943
944		if($path && is_string($path) && !class_exists($class_name, false))
945		{
946			global $_E107;
947
948			if((!empty($_E107['debug']) || (defined('e_DEBUG') && e_DEBUG === true) ))
949			{
950				require_once($path);
951			}
952			else
953			{
954				@require_once($path);
955			}
956
957			// remove the need for external function.
958			//e107_require_once() is available without class2.php. - see core_functions.php
959		}
960		if(class_exists($class_name, false))
961		{
962			self::setRegistry($id, new $class_name($vars));
963		}
964
965		return self::getRegistry($id);
966	}
967
968	/**
969	 * Retrieve object
970	 * Prepare for __autoload
971	 *
972	 * @param string $class_name
973	 * @param mixed $arguments
974	 * @param string|boolean $path optional script path
975	 * @return object|null
976	 */
977	public static function getObject($class_name, $arguments = null, $path = true)
978	{
979		if(true === $path)
980		{
981			if(isset(self::$_known_handlers[$class_name]))
982			{
983				$path = self::getParser()->replaceConstants(self::$_known_handlers[$class_name]);
984			}
985		}
986
987		//auto detection + overload check
988		if(is_bool($path))
989		{
990			//overload allowed
991			if(true === $path && self::isHandlerOverloadable($class_name))
992			{
993				$tmp = self::getHandlerOverload($class_name);
994				$class_name = $tmp[0];
995				$path = $tmp[1];
996			}
997			//overload not allowed
998			else
999			{
1000				$path = self::getHandlerPath($class_name);
1001			}
1002		}
1003
1004		if($path && is_string($path) && !class_exists($class_name, false))
1005		{
1006			e107_require_once($path); //no existence/security checks here!
1007		}
1008
1009		if(class_exists($class_name, false))
1010		{
1011			if(null !== $arguments) return  new $class_name($arguments);
1012			return new $class_name();
1013		}
1014
1015		trigger_error("Class {$class_name} not found!", E_USER_ERROR);
1016		return null;
1017	}
1018
1019	/**
1020	 * Retrieve core config handlers.
1021	 * List of allowed $name values (aliases) could be found
1022	 * in {@link e_core_pref} class
1023	 *
1024	 * @param string $name core|core_backup|emote|menu|search|notify
1025	 * @param bool $load
1026	 * @param bool $refresh
1027	 * @return e_core_pref
1028	 */
1029	public static function getConfig($name = 'core', $load = true, $refresh=false)
1030	{
1031
1032		if(isset(self::$_plug_config_arr[$name])) //FIXME Load pluginPref Object instead - Not quite working with calendar_menu.
1033		{
1034			return self::getPlugConfig($name);
1035		}
1036
1037		if(!isset(self::$_core_config_arr[$name]) || ($refresh == true)) // required by update_routines to clear out earlier values.
1038		{
1039			e107_require_once(e_HANDLER.'pref_class.php');
1040			self::$_core_config_arr[$name] = new e_core_pref($name, $load);
1041
1042			if($name === 'core') // prevent loop between pref and cache handlers.
1043			{
1044				self::getCache()->UserCacheActive = self::getPref('cachestatus');
1045				self::getCache()->SystemCacheActive = self::getPref('syscachestatus');
1046			}
1047		}
1048
1049		return self::$_core_config_arr[$name];
1050	}
1051
1052	/**
1053	 * Retrieve core config handler preference value or the core preference array
1054	 * Shorthand of  self::getConfig()->get()
1055	 *
1056	 * @see e_core_pref::get()
1057	 * @param string $pref_name
1058	 * @param mixed $default default value if preference is not found
1059	 * @return mixed
1060	 */
1061	public static function getPref($pref_name = '', $default = null)
1062	{
1063		return empty($pref_name) ? self::getConfig()->getPref() : self::getConfig()->get($pref_name, $default);
1064	}
1065
1066	/**
1067	 * Advanced version of self::getPref(). $pref_name is parsed,
1068	 * so that $pref_name = 'x/y/z' will search for value pref_data[x][y][z]
1069	 * Shorthand of  self::getConfig()->getPref()
1070	 *
1071	 * @see e_core_pref::getPref()
1072	 * @param string $pref_name
1073	 * @param mixed $default default value if preference is not found
1074	 * @param null $index
1075	 * @return mixed
1076	 */
1077	public static function findPref($pref_name, $default = null, $index = null)
1078	{
1079		return self::getConfig()->getPref($pref_name, $default, $index);
1080	}
1081
1082	/**
1083	 * Retrieve plugin config handlers.
1084	 * Multiple plugin preference DB rows are supported
1085	 * Class overload is supported.
1086	 * Examples:
1087	 * - <code>e107::getPluginConfig('myplug');</code>
1088	 * 	 will search for e107_plugins/myplug/e_pref/myplug_pref.php which
1089	 * 	 should contain class 'e_plugin_myplug_pref' class (child of e_plugin_pref)
1090	 * - <code>e107::getPluginConfig('myplug', 'row2');</code>
1091	 * 	 will search for e107_plugins/myplug/e_pref/myplug_row2_pref.php which
1092	 * 	 should contain class 'e_plugin_myplug_row2_pref' class (child of e_plugin_pref)
1093	 *
1094	 * @param string $plug_name
1095	 * @param string $multi_row
1096	 * @param boolean $load load from DB on startup
1097	 * @return e_plugin_pref
1098	 */
1099	public static function getPlugConfig($plug_name, $multi_row = '', $load = true)
1100	{
1101		if(!isset(self::$_plug_config_arr[$plug_name.$multi_row]))
1102		{
1103			e107_require_once(e_HANDLER.'pref_class.php');
1104			$override_id = $plug_name.($multi_row ? "_{$multi_row}" : '');
1105
1106			//check (once) for custom plugin pref handler
1107			if(is_readable(e_PLUGIN.$plug_name.'/e_pref/'.$override_id.'_pref.php'))
1108			{
1109				require_once(e_PLUGIN.$plug_name.'/e_pref/'.$override_id.'_pref.php');
1110				$class_name = 'e_plugin_'.$override_id.'_pref';
1111
1112				//PHPVER: string parameter for is_subclass_of require PHP 5.0.3+
1113				if(class_exists($class_name, false) && is_subclass_of('e_plugin_pref', $class_name)) //or e_pref ?
1114				{
1115					self::$_plug_config_arr[$plug_name.$multi_row] = new $class_name($load);
1116					return self::$_plug_config_arr[$plug_name.$multi_row];
1117				}
1118			}
1119
1120			self::$_plug_config_arr[$plug_name.$multi_row] = new e_plugin_pref($plug_name, $multi_row, $load);
1121		}
1122
1123		return self::$_plug_config_arr[$plug_name.$multi_row];
1124	}
1125
1126
1127
1128	/**
1129	 * Retrieve the global LAN for a specific plugin.
1130	 * @param $dir
1131	 * @param string $type
1132	 * @return mixed
1133	 */
1134	public static function getPlugLan($dir, $type='name')
1135	{
1136		$lan = "LAN_PLUGIN_".strtoupper($dir)."_".strtoupper($type);
1137
1138		return defset($lan,false);
1139	}
1140
1141	/**
1142	 * Retrieve plugin preference value.
1143	 * Shorthand of  self::getPluginConfig()->get()
1144	 * NOTE: Multiple plugin preference DB rows are NOT supported
1145	 * This will only look for your default plugin config (empty $milti_row)
1146	 *
1147	 * @see e_plugin_pref::get()
1148	 * @param string $plug_name
1149	 * @param string $pref_name
1150	 * @param mixed $default default value if preference is not found
1151	 * @return mixed
1152	 */
1153	public static function getPlugPref($plug_name, $pref_name = '', $default = null)
1154	{
1155		return  empty($pref_name) ? self::getPlugConfig($plug_name)->getPref() : self::getPlugConfig($plug_name)->get($pref_name, $default);
1156	}
1157
1158	/**
1159	 * Advanced version of self::getPlugPref(). $pref_name is parsed,
1160	 * so that $pref_name = 'x/y/z' will search for value pref_data[x][y][z]
1161	 * Shorthand of  self::getPluginConfig()->getPref()
1162	 *
1163	 * @see e_core_pref::getPref()
1164	 * @param $plug_name
1165	 * @param string $pref_name
1166	 * @param mixed $default default value if preference is not found
1167	 * @param null $index
1168	 * @return mixed
1169	 */
1170	public static function findPlugPref($plug_name, $pref_name, $default = null, $index = null)
1171	{
1172		return self::getPlugConfig($plug_name)->getPref($pref_name, $default, $index);
1173	}
1174
1175
1176	/**
1177	 * Retrieve theme config handlers.
1178	 * Multiple theme preference DB rows are supported
1179	 * Class overload is supported.
1180	 * Examples:
1181	 * - <code>e107::getTHemeConfig('mytheme');</code>
1182	 * 	 will search for e107_plugins/myplug/e_pref/myplug_pref.php which
1183	 * 	 should contain class 'e_plugin_myplug_pref' class (child of e_plugin_pref)
1184	 * - <code>e107::getPluginConfig('myplug', 'row2');</code>
1185	 * 	 will search for e107_plugins/myplug/e_pref/myplug_row2_pref.php which
1186	 * 	 should contain class 'e_plugin_myplug_row2_pref' class (child of e_plugin_pref)
1187	 *
1188	 * @param string $theme_name
1189	 * @param string $multi_row
1190	 * @param boolean $load load from DB on startup
1191	 * @return e_plugin_pref
1192	 */
1193	public static function getThemeConfig($theme_name=null, $multi_row = '', $load = true)
1194	{
1195
1196		if(empty($theme_name))
1197		{
1198			$theme_name = self::getPref('sitetheme');
1199		}
1200
1201		if(!isset(self::$_theme_config_arr[$theme_name.$multi_row]))
1202		{
1203			e107_require_once(e_HANDLER.'pref_class.php');
1204
1205			self::$_theme_config_arr[$theme_name.$multi_row] = new e_theme_pref($theme_name, $multi_row, $load);
1206		}
1207
1208		return self::$_theme_config_arr[$theme_name.$multi_row];
1209	}
1210
1211
1212
1213
1214	/**
1215	 * Get current theme preference. $pref_name is parsed,
1216	 * so that $pref_name = 'x/y/z' will search for value pref_data[x][y][z]
1217	 * Shorthand of  self::getConfig()->getPref('current_theme/sitetheme_pref/pref_name')
1218	 *
1219	 * @see e_core_pref::getPref()
1220	 * @param string $pref_name
1221	 * @param mixed $default default value if preference is not found
1222	 * @param null $index
1223	 * @return mixed
1224	 */
1225	public static function getThemePref($pref_name = '', $default = null, $index = null)
1226	{
1227		// new storage method in it's own core table row. eg. theme_bootstrap3
1228		$theme_name = self::getPref('sitetheme');
1229
1230		if(self::getThemeConfig($theme_name)->hasData() === true)
1231		{
1232			return  empty($pref_name) ? self::getThemeConfig($theme_name)->getPref() : self::getThemeConfig($theme_name)->get($pref_name, $default);
1233		}
1234
1235		// old storage method in core prefs.
1236
1237		$legacy_pref_name = ($pref_name) ? $pref_name = '/'.$pref_name : '';
1238		$tprefs = self::getConfig()->getPref('sitetheme_pref'.$legacy_pref_name, $default, $index);
1239
1240		return !empty($tprefs) ? $tprefs : $default;
1241
1242	}
1243
1244	/**
1245	 * Set current theme preference. $pref_name is parsed,
1246	 * so that $pref_name = 'x/y/z' will set value pref_data[x][y][z]
1247	 *
1248	 * @param string|array $pref_name
1249	 * @param mixed $pref_value
1250	 * @return e_pref
1251	 */
1252	public static function setThemePref($pref_name, $pref_value = null)
1253	{
1254		if(is_array($pref_name)) return self::getConfig()->set('sitetheme_pref', $pref_name);
1255		return self::getConfig()->updatePref('sitetheme_pref/'.$pref_name, $pref_value, false);
1256	}
1257
1258
1259	public static function getThemeGlyphs()
1260	{
1261
1262		$custom = self::getConfig()->getPref('sitetheme_glyphicons', false);
1263		$theme = self::getConfig()->getPref('sitetheme', false);
1264
1265		$arr = array();
1266
1267		if(!empty($custom))
1268		{
1269
1270			foreach($custom as $glyphConfig)
1271			{
1272
1273				if(substr($glyphConfig['path'],0,4) !== 'http')
1274				{
1275					$glyphConfig['path'] = e_THEME."$theme/".$glyphConfig['path'];
1276				}
1277
1278				$arr[] = $glyphConfig;
1279
1280				if(E107_DBG_INCLUDES)
1281				{
1282					e107::getDebug()->log("Loading Glyph Icons: ".print_a($glyphConfig,true));
1283				}
1284			}
1285
1286		}
1287
1288		return $arr;
1289
1290	}
1291
1292
1293
1294
1295	/**
1296	 * Retrieve text parser singleton object
1297	 *
1298	 * @return e_parse
1299	 */
1300	public static function getParser()
1301	{
1302		return self::getSingleton('e_parse', e_HANDLER.'e_parse_class.php'); //WARNING - don't change this - infinite loop!!!
1303	}
1304
1305	/**
1306	 * Retrieve sc parser singleton object
1307	 *
1308	 * @return e_parse_shortcode
1309	 */
1310	public static function getScParser()
1311	{
1312		return self::getSingleton('e_parse_shortcode', true);
1313	}
1314
1315
1316	/**
1317	 * Retrieve secure_image singleton object
1318	 *
1319	 * @return secure_image
1320	 */
1321	public static function getSecureImg()
1322	{
1323		return self::getSingleton('secure_image', true); // more flexible.
1324		// return self::getObject('secure_image');
1325	}
1326
1327	/**
1328	 * Retrieve registered sc object (batch) by class name
1329	 * Note - '_shortcodes' part of the class/override is added by the method
1330	 * Override is possible only if class is not already instantiated by shortcode parser
1331	 *
1332	 * <code><?php
1333	 *
1334	 * // Core news shortcodes (news_shortcodes.php using class news_shortcodes )
1335	 * e107::getScObject('news');
1336	 *
1337	 * // Core page shortcodes (page_shortcodes.php.php with class cpage_shortcode)
1338	 * e107::getScObject('page', null,'cpage');
1339	 *
1340	 * // object of plugin_myplugin_my_shortcodes class -> myplugin/shortcodes/batch/my_shortcodes.php
1341	 * e107::getScObject('my', 'myplugin');
1342	 *
1343	 * // news override - plugin_myplugin_news_shortcodes extends news_shortcodes -> myplugin/shortcodes/batch/news_shortcodes.php
1344	 * e107::getScObject('news', 'myplugin', true);
1345	 *
1346	 * // news override - plugin_myplugin_mynews_shortcodes extends news_shortcodes -> myplugin/shortcodes/batch/mynews_shortcodes.php
1347	 * e107::getScObject('news', 'myplugin', 'mynews');
1348	 * </code>
1349	 *
1350	 * @param string $className
1351	 * @param string $pluginName
1352	 * @param string|true $overrideClass
1353	 * @return e_shortcode
1354	 */
1355	public static function getScBatch($className, $pluginName = null, $overrideClass = null)
1356	{
1357		if(is_string($overrideClass)) $overrideClass .= '_shortcodes';
1358		return self::getScParser()->getScObject($className.'_shortcodes', $pluginName, $overrideClass);
1359	}
1360
1361	/**
1362	 * Retrieve DB singleton object based on the
1363	 * $instance_id
1364	 *
1365	 * @param string $instance_id
1366	 * @return e_db
1367	 */
1368	public static function getDb($instance_id = '')
1369	{
1370		 return self::getSingleton('db', true, $instance_id);
1371	}
1372
1373	/**
1374	 * Retrieve cache singleton object
1375	 *
1376	 * @return ecache
1377	 */
1378	public static function getCache()
1379	{
1380		return self::getSingleton('ecache', true);
1381	}
1382
1383	/**
1384	 * Retrieve bbcode singleton object
1385	 *
1386	 * @return e_bbcode
1387	 */
1388	public static function getBB()
1389	{
1390		return self::getSingleton('e_bbcode', true);
1391	}
1392
1393	/**
1394	 * Retrieve user-session singleton object
1395	 *
1396	 * @return UserHandler
1397	 */
1398	public static function getUserSession()
1399	{
1400		return self::getSingleton('UserHandler', true);
1401	}
1402
1403	/**
1404	 * Retrieve core session singleton object(s)
1405	 *
1406	 * @param null $namespace
1407	 * @return e_core_session
1408	 */
1409	public static function getSession($namespace = null)
1410	{
1411		$id = 'core/e107/session/'.(null === $namespace ? 'e107' : $namespace);
1412		if(self::getRegistry($id))
1413		{
1414			return self::getRegistry($id);
1415		}
1416		$session = self::getObject('e_core_session', array('namespace' => $namespace), true);
1417		self::setRegistry($id, $session);
1418		return $session;
1419	}
1420
1421	/**
1422	 * Retrieve redirection singleton object
1423	 *
1424	 * @return redirection
1425	 */
1426	public static function getRedirect()
1427	{
1428		return self::getSingleton('redirection', true);
1429	}
1430
1431
1432	/**
1433	 * Retrieve rater singleton object
1434	 *
1435	 * @return rater
1436	 */
1437	public static function getRate()
1438	{
1439		return self::getSingleton('rater', true);
1440	}
1441
1442	/**
1443	 * Retrieve sitelinks singleton object
1444	 *
1445	 * @return sitelinks
1446	 */
1447	public static function getSitelinks()
1448	{
1449		return self::getSingleton('sitelinks', true);
1450	}
1451
1452
1453	/**
1454	 * Retrieve render singleton object
1455	 *
1456	 * @return e107table
1457	 */
1458	public static function getRender()
1459	{
1460		return self::getSingleton('e107table');
1461	}
1462
1463	/**
1464	 * Retrieve e107Email singleton object
1465	 *
1466	 * @return e107Email
1467	 */
1468	public static function getEmail($overrides=null)
1469	{
1470		return self::getSingleton('e107Email', true, null, $overrides);
1471	}
1472
1473
1474	/**
1475	 * Retrieves PhpThumbFactory object
1476	 *
1477	 * @param $src
1478	 * @return bool|GdThumb
1479	 */
1480	public static function getThumb($src)
1481	{
1482		require_once(e_HANDLER.'phpthumb/ThumbLib.inc.php');
1483		try
1484		{
1485			return PhpThumbFactory::create($src);
1486		}
1487		catch (Exception $e)
1488		{
1489			return false;
1490		}
1491
1492	}
1493
1494
1495	/**
1496	 * Retrieve e107Email mail mailer object.
1497	 *
1498	 * @return e107MailManager
1499	 */
1500	public static function getBulkEmail()
1501	{
1502		return self::getSingleton('e107MailManager', true);
1503	}
1504
1505	/**
1506	 * Retrieve event singleton object
1507	 *
1508	 * @return e107_event
1509	 */
1510	public static function getEvent()
1511	{
1512		return self::getSingleton('e107_event', true);
1513	}
1514
1515	/**
1516	 * Retrieve array storage singleton object
1517	 *
1518	 * @return e_array
1519	 */
1520	public static function getArrayStorage()
1521	{
1522		return self::getSingleton('e_array', true);
1523	}
1524
1525	/**
1526	 * Retrieve menu handler singleton object
1527	 *
1528	 * @return e_menu
1529	 */
1530	public static function getMenu()
1531	{
1532		return self::getSingleton('e_menu', true);
1533	}
1534
1535
1536	/**
1537	 * Retrieve e_theme singleton object
1538	 * @return e_theme
1539	 */
1540	public static function getTheme($themedir='front', $clearCache=false)
1541	{
1542
1543		if(!defined('E107_INSTALL'))
1544		{
1545			if($themedir === 'front')
1546			{
1547				$themedir= self::getPref('sitetheme');
1548			}
1549
1550			if($themedir === 'admin')
1551			{
1552				$themedir = self::getPref('admintheme');
1553			}
1554		}
1555
1556		// Get the currently used theme.
1557		if ($themedir == 'current')
1558		{
1559			// If we are in the admin area.
1560			if (deftrue('e_ADMIN_AREA', false))
1561			{
1562				$themedir = self::getPref('admintheme');
1563			}
1564			else
1565			{
1566				$themedir= self::getPref('sitetheme');
1567			}
1568		}
1569
1570	//	e107::getDb()->db_Mark_time('start e_theme');
1571		/** @var e_theme $ret */
1572		$ret = self::getSingleton('e_theme', true, null, array('themedir'=> $themedir, 'force'=> $clearCache));
1573
1574	//	e107::getDb()->db_Mark_time('end e_theme');
1575	/*	echo "<pre>";
1576		debug_print_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
1577		echo "</pre>";*/
1578
1579		return $ret;
1580	}
1581
1582
1583
1584	/**
1585	 * Retrieve URL singleton object
1586	 *
1587	 * @return eURL
1588	 */
1589	public static function getUrl()
1590	{
1591		return self::getSingleton('eUrl', true);
1592	}
1593
1594	/**
1595	 * Retrieve file handler singleton or new fresh object
1596	 *
1597	 * @param boolean $singleton default true
1598	 * @return e_file
1599	 */
1600	public static function getFile($singleton = false)
1601	{
1602		if($singleton)
1603		{
1604			return self::getSingleton('e_file', true);
1605		}
1606		return self::getObject('e_file', null, true);
1607	}
1608
1609	/**
1610	 * Create a new file inspector object
1611	 *
1612	 * Note: Only the core file inspector is supported right now.
1613	 *
1614	 * @return e_file_inspector
1615	 */
1616    public static function getFileInspector($type = 'core')
1617    {
1618        $fileInspectorPath = realpath(e_SYSTEM_BASE . "core_image.phar");
1619        /** @var e_file_inspector $fileInspector */
1620        $fileInspector = self::getObject('e_file_inspector_json_phar', $fileInspectorPath);
1621
1622        try
1623        {
1624            $fileInspector->loadDatabase();
1625        }
1626        catch (Exception $e)
1627        {
1628            // TODO: LAN
1629            self::getMessage()->addWarning(
1630                "The core integrity image is corrupt. " .
1631                "File Inspector will be inoperative. " .
1632                "Resolve this issue by uploading a good copy of the core image to " .
1633                escapeshellarg($fileInspectorPath) . ". " .
1634                "If uploading with FTP, use binary transfer mode. " .
1635                "Error message: " .
1636                $e->getMessage()
1637            );
1638        }
1639
1640        return $fileInspector;
1641	}
1642
1643	/**
1644	 * Retrieve form handler singleton or new fresh object
1645	 *
1646	 * @param boolean $singleton default false
1647	 * @param boolean $tabindex passed to e_form when initialized as an object (not singleton)
1648	 * @return e_form
1649	 */
1650	public static function getForm($singleton = false, $tabindex = false)
1651	{
1652		if($singleton)
1653		{
1654			return self::getSingleton('e_form', true);
1655		}
1656		return self::getObject('e_form', $tabindex, true);
1657	}
1658
1659	/**
1660	 * Retrieve admin log singleton object
1661	 * @Deprecated - use e107::getLog();
1662	 * @return e_admin_log
1663	 */
1664	public static function getAdminLog()
1665	{
1666		return self::getSingleton('e_admin_log', true);
1667	}
1668
1669	/**
1670	 * Retrieve admin log singleton object
1671	 *
1672	 * @return e_admin_log
1673	 */
1674	public static function getLog()
1675	{
1676		return self::getSingleton('e_admin_log', true);
1677	}
1678
1679	/**
1680	 * Retrieve date handler singleton object
1681	 *
1682	 * @return convert
1683	 */
1684	public static function getDateConvert()
1685	{
1686		return self::getSingleton('e_date', true);
1687	}
1688
1689	/**
1690	 * Retrieve date handler singleton object - preferred method.
1691	 *
1692	 * @return convert
1693	 */
1694	public static function getDate()
1695	{
1696		return self::getSingleton('e_date', true);
1697	}
1698
1699
1700    /**
1701     * Retrieve date handler singleton object - preferred method.
1702     *
1703     * @return e107_db_debug
1704     */
1705    public static function getDebug()
1706    {
1707        return self::getSingleton('e107_db_debug', true);
1708    }
1709
1710	/**
1711	 * Retrieve notify handler singleton object
1712	 *
1713	 * @return notify
1714	 */
1715	public static function getNotify()
1716	{
1717		return self::getSingleton('notify', true);
1718	}
1719
1720
1721	/**
1722	 * Retrieve override handler singleton object
1723	 *
1724	 * @return override
1725	 */
1726	public static function getOverride()
1727	{
1728		return self::getSingleton('override', true);
1729	}
1730
1731
1732
1733	/**
1734	 * Retrieve Language handler singleton object
1735	 *
1736	 * @return language
1737	 */
1738	public static function getLanguage()
1739	{
1740		return self::getSingleton('language', true);
1741	}
1742
1743	/**
1744	 * Retrieve IP/ban handler singleton object
1745	 *
1746	 * @return eIPHandler
1747	 */
1748	public static function getIPHandler()
1749	{
1750		return self::getSingleton('eIPHandler', true);
1751	}
1752
1753	/**
1754	 * Retrieve Xml handler singleton or new instance object
1755	 * @param mixed $singleton false - new instance, true - singleton from default registry location, 'string' - registry path
1756	 * @return xmlClass
1757	 */
1758	public static function getXml($singleton = true)
1759	{
1760		if($singleton)
1761		{
1762			return self::getSingleton('xmlClass', true, (true === $singleton ? '' : $singleton));
1763		}
1764		return self::getObject('xmlClass', null, true);
1765	}
1766
1767	/**
1768	 * Create a new Hybridauth object based on the provided configuration
1769	 *
1770	 * @return Hybridauth\Hybridauth
1771	 * @throws \Hybridauth\Exception\InvalidArgumentException if Hybridauth rejects the provided config
1772	 * @throws ReflectionException if this method is unintentionally broken
1773	 * @deprecated v2.3.0 Use the e_user_provider interfaces instead (e107::getUserProvider()).
1774	 *                    It's the e107 wrapper around Hybridauth.
1775	 * @see e_user_provider for social login features.
1776	 * @see e107::getUser() for getting a user object that may or may not have a social login.
1777	 */
1778	public static function getHybridAuth($config = null)
1779	{
1780		$e_user_provider = new e_user_provider(null, $config);
1781		$reflection = new ReflectionClass('e_user_provider');
1782		$reflection_property = $reflection->getProperty('hybridauth');
1783		$reflection_property->setAccessible(true);
1784		return $reflection_property->getValue($e_user_provider);
1785	}
1786
1787	/**
1788	 * Create a new social login handler
1789	 * @param string|null $providerName
1790	 * @return e_user_provider
1791	 */
1792	public static function getUserProvider($providerName = null)
1793	{
1794		return self::getObject('e_user_provider', $providerName);
1795	}
1796
1797	/**
1798	 * Retrieve userclass singleton object
1799	 *
1800	 * @return user_class
1801	 */
1802	public static function getUserClass()
1803	{
1804		return self::getSingleton('user_class', true);
1805	}
1806
1807	/**
1808	 * Retrieve user model object.
1809	 *
1810	 * @param integer $user_id target user
1811	 * @param boolean $checkIfCurrent if tru user_id will be compared to current user, if there is a match
1812	 * 	current user object will be returned
1813	 * @return e_system_user
1814	 */
1815	public static function getSystemUser($user_id, $checkIfCurrent = true)
1816	{
1817		if($checkIfCurrent && $user_id && $user_id === self::getUser()->getId())
1818		{
1819			return self::getUser();
1820		}
1821
1822		if(!$user_id) return self::getObject('e_system_user');
1823
1824		$user = self::getRegistry('core/e107/user/'.$user_id);
1825		if(null === $user)
1826		{
1827			$user = self::getObject('e_system_user');
1828			if($user_id) $user->load($user_id); // self registered on load
1829		}
1830		return $user;
1831	}
1832
1833	/**
1834	 * Simple replacement for deprecated get_user_data(). e107::user();
1835	 * @param $uid integer user_id or leave empty for currently logged in user.
1836	 * @return array of user data
1837	 */
1838	public static function user($uid=null)
1839	{
1840		$uid = intval($uid);
1841
1842		if(empty($uid)){ return false; }
1843
1844		$user = self::getSystemUser($uid, true);
1845		$var = array();
1846		if($user)
1847		{
1848			$var = $user->getUserData();
1849		}
1850
1851		return $var;
1852	}
1853
1854
1855  /**
1856    * Return a string containg exported array data. - preferred.
1857    *
1858    * @param array $ArrayData array to be stored
1859    * @param bool|string $mode true = var_export with addedslashes, false = var_export (default), 'json' = json encoded
1860    * @return array|string
1861    */
1862    public static function serialize($ArrayData, $mode = false)
1863    {
1864		return self::getArrayStorage()->serialize($ArrayData, $mode);
1865    }
1866
1867	  /**
1868    * Returns an array from stored array data.
1869    *
1870    * @param string $ArrayData
1871    * @return array stored data
1872    */
1873    public static function unserialize($ArrayData)
1874    {
1875    	if(empty($ArrayData))
1876		{
1877			return array();
1878		}
1879
1880		return self::getArrayStorage()->unserialize($ArrayData);
1881    }
1882
1883
1884	/**
1885	 * Retrieve current user model object.
1886	 *
1887	 * @return e_user
1888	 */
1889	public static function getUser()
1890	{
1891		$user = self::getRegistry('core/e107/current_user');
1892		if(null === $user)
1893		{
1894			$user = self::getObject('e_user');
1895			self::setRegistry('core/e107/current_user', $user);
1896		}
1897		return $user;
1898	}
1899
1900
1901	/**
1902	 * Retrieve front or admin Model.
1903	 * @param string $type
1904	 * @return object e_front_model or e_admin_model;
1905	 */
1906	public static function getModel($type='front')
1907	{
1908		if($type === 'front')
1909		{
1910			return self::getObject('e_front_model');
1911		}
1912
1913		return self::getObject('e_admin_model');
1914	}
1915
1916	/**
1917	 * Retrieve user model object.
1918	 *
1919	 * @param integer $user_id target user
1920	 * @return e_user_extended_structure_tree
1921	 */
1922	public static function getUserStructure()
1923	{
1924		return self::getSingleton('e_user_extended_structure_tree', true);
1925	}
1926
1927	/**
1928	 * Retrieve User Extended handler singleton object
1929	 * @return e107_user_extended
1930	 */
1931	public static function getUserExt()
1932	{
1933		return self::getSingleton('e107_user_extended', true);
1934	}
1935
1936	/**
1937	 * Retrieve User Perms (admin perms) handler singleton object
1938	 * @return e_userperms
1939	 */
1940	public static function getUserPerms()
1941	{
1942		return self::getSingleton('e_userperms', true);
1943	}
1944
1945	/**
1946	 * Retrieve online users handler singleton object
1947	 * @return e_ranks
1948	 */
1949	public static function getRank()
1950	{
1951		return self::getSingleton('e_ranks', true);
1952	}
1953
1954	/**
1955	 * Retrieve plugin handler singleton object
1956	 * @return e107plugin
1957	 */
1958	public static function getPlugin()
1959	{
1960		return self::getSingleton('e107plugin', true);
1961	}
1962
1963
1964
1965	/**
1966	 * Retrieve plugin class singleton object
1967	 * @return e_plugin
1968	 */
1969	public static function getPlug()
1970	{
1971		return self::getSingleton('e_plugin', true);
1972	}
1973
1974	/**
1975	 * Retrieve online users handler singleton object
1976	 * @return e_online
1977	 */
1978	public static function getOnline()
1979	{
1980		return self::getSingleton('e_online', true);
1981	}
1982
1983
1984	/**
1985	 * Retrieve chart handler singleton object
1986	 * @return e_chart
1987	 */
1988	public static function getChart()
1989	{
1990		return self::getObject('e_chart', null, true);
1991	}
1992
1993
1994	/**
1995	 * Retrieve comments handler singleton object
1996	 * @return comment
1997	 */
1998	public static function getComment()
1999	{
2000		return self::getSingleton('comment', true);
2001	}
2002
2003	/**
2004	 * Retrieve comments handler singleton object
2005	 * @return e_customfields
2006	 */
2007	public static function getCustomFields()
2008	{
2009		return self::getSingleton('e_customfields', true);
2010	}
2011
2012	/**
2013	 * Retrieve Media handler singleton object
2014	 * @return e_media
2015	 */
2016	public static function getMedia()
2017	{
2018		return self::getSingleton('e_media', true);
2019	}
2020
2021	/**
2022	 * Retrieve Navigation Menu handler singleton object
2023	 * @return e_navigation
2024	 */
2025	public static function getNav()
2026	{
2027		return self::getSingleton('e_navigation', true);
2028	}
2029
2030	/**
2031	 * Retrieve message handler singleton
2032	 * @return eMessage
2033	 */
2034	public static function getMessage()
2035	{
2036		// static $included = false;
2037		// if(!$included)
2038		// {
2039			// e107_require_once(e_HANDLER.'message_handler.php');
2040			// $included = true;
2041		// }
2042		// return eMessage::getInstance();
2043		return self::getSingleton('eMessage', true);
2044	}
2045
2046	/**
2047	 * Retrieve ajax singleton object
2048	 *
2049	 * @return e_ajax
2050	 */
2051	public static function getAjax()
2052	{
2053		return self::getSingleton('e_ajax', true);
2054	}
2055
2056	/**
2057	 * Retrieve Library Manager singleton object (internal use only. Use e107::library())
2058	 *
2059	 * @return e_library_manager
2060	 */
2061	public static function getLibrary()
2062	{
2063		return self::getSingleton('e_library_manager', true);
2064	}
2065
2066	/**
2067	 * Library Common Public Function.
2068	 *
2069	 * @param string $action
2070	 *  - 'detect': Tries to detect a library and its installed version.
2071	 *  - 'load': Loads a library.
2072	 * @param string $library
2073	 *  The name of the library to detect/load.
2074	 * @param string $variant
2075	 *   (Optional for 'load') The name of the variant to load. Note that only one variant of a library can be loaded
2076	 *   within a single request. The variant that has been passed first is used; different variant names in subsequent
2077	 *   calls are ignored.
2078	 *
2079	 * @return array|boolean
2080	 *  - In case of 'detect': An associative array containing registered information for the library specified by
2081	 *    $name, or FALSE if the library $name is not registered.
2082	 *  - In case of 'load': An associative array of the library information.
2083	 *  - In case of 'info': An associative array containing registered information for all libraries, the registered
2084	 *    information for the library specified by $name, or FALSE if the library $name is not registered.
2085	 */
2086	public static function library($action = '', $library = null, $variant = null)
2087	{
2088		$libraryHandler = self::getLibrary();
2089
2090		switch($action)
2091		{
2092			case 'detect':
2093				return $libraryHandler->detect($library);
2094				break;
2095
2096			case 'load':
2097				$cdn = (bool) self::getPref('e_jslib_cdn', true);
2098				$debug = (bool) deftrue('e_DEBUG');
2099				$admin = (bool) defset('e_ADMIN_AREA', false);
2100
2101				// Try to detect and load CDN version.
2102				if(!$admin && $cdn && strpos($library, 'cdn.') !== 0)
2103				{
2104					$lib = $libraryHandler->detect('cdn.' . $library);
2105
2106					// If CDN version is available.
2107					if($lib && !empty($lib['installed']))
2108					{
2109						// If a variant is specified, we need to check if it's installed.
2110						if(!empty($variant) && !empty($lib['variants'][$variant]['installed']))
2111						{
2112							// Load CDN version with the variant.
2113							return $libraryHandler->load('cdn.' . $library, $variant);
2114						}
2115
2116						// If CDN version is available, but no variant is specified,
2117						// and debug mode is on, try to load 'debug' variant.
2118						if(empty($variant) && $debug && !empty($lib['variants']['dev']['installed']))
2119						{
2120							// Load CDN version with 'debug' variant.
2121							return $libraryHandler->load('cdn.' . $library, 'dev');
2122						}
2123
2124						// Load CDN version without variant.
2125						return $libraryHandler->load('cdn.' . $library, $variant);
2126					}
2127				}
2128
2129				// If no variant is specified, and CDN version is not available, and debug mode is on.
2130				if(empty($variant) && $debug)
2131				{
2132					$lib = $libraryHandler->detect($library);
2133
2134					// If 'debug' variant is available.
2135					if($lib && !empty($lib['variants']['dev']['installed']))
2136					{
2137						// Load library with 'debug' variant.
2138						return $libraryHandler->load($library, 'dev');
2139					}
2140				}
2141
2142				return $libraryHandler->load($library, $variant);
2143				break;
2144
2145			case 'info':
2146				return $libraryHandler->info($library);
2147				break;
2148		}
2149	}
2150
2151	/**
2152	 * Retrieve JS Manager singleton object
2153	 *
2154	 * @return e_jsmanager
2155	 */
2156	public static function getJs()
2157	{
2158		static $included = false;
2159		if(!$included)
2160		{
2161			e107_require_once(e_HANDLER.'js_manager.php');
2162			$included = true;
2163		}
2164		return e_jsmanager::getInstance();
2165	}
2166
2167
2168	public static function set($type=null, $val=true)
2169	{
2170		if($type === 'js_enabled')
2171		{
2172			self::$_js_enabled = (bool) $val;
2173		}
2174
2175		if($type === 'css_enabled')
2176		{
2177			self::$_css_enabled = (bool) $val;
2178		}
2179	}
2180
2181
2182
2183	/**
2184	 * JS Common Public Function. Prefered is shortcode script path
2185	 * @param string $type core|theme|footer|inline|footer-inline|url or any existing plugin_name
2186	 * @param string|array $data depends on the type - path/url or inline js source
2187	 * @param integer $zone [optional] leave it null for default zone
2188	 * @param string $dep dependence :  null | prototype | jquery
2189	 */
2190	public static function js($type, $data, $dep = null, $zone = null, $pre = '', $post = '')
2191	{
2192		if(self::$_js_enabled === false)
2193		{
2194			return null;
2195		}
2196
2197		$jshandler = self::getJs();
2198		$jshandler->setDependency($dep);
2199
2200		switch ($type)
2201		{
2202			case 'settings':
2203				$jshandler->jsSettings($data);
2204			break;
2205
2206			case 'core':
2207				// data is e.g. 'core/tabs.js'
2208				if(null !== $zone) $jshandler->requireCoreLib($data, $zone);
2209				else $jshandler->requireCoreLib($data);
2210			break;
2211
2212			case 'bootstrap': //TODO Eventually add own method and render for bootstrap.
2213				if(null !== $zone) $jshandler->requireCoreLib('bootstrap/js/'.$data, $zone);
2214				else $jshandler->requireCoreLib('bootstrap/js/'.$data);
2215			break;
2216
2217			case 'theme':
2218				// data is e.g. 'jslib/mytheme.js'
2219				if(null !== $zone) $jshandler->headerTheme($data, $zone, $pre, $post);
2220				else $jshandler->footerTheme($data, 5, $pre, $post);
2221			break;
2222
2223			case 'inline':
2224				// data is JS source (without script tags)
2225				if(null !== $zone) $jshandler->headerInline($data, $zone);
2226				else $jshandler->headerInline($data);
2227			break;
2228
2229			case 'footer-inline':
2230				// data is JS source (without script tags)
2231				if(null !== $zone) $jshandler->footerInline($data, $zone);
2232				else $jshandler->footerInline($data);
2233			break;
2234
2235			case 'url':
2236				// data is e.g. 'http://cdn.somesite.com/some.js'
2237				if(null !== $zone) $jshandler->headerFile($data, $zone, $pre, $post);
2238				else $jshandler->headerFile($data, 5, $pre, $post);
2239			break;
2240
2241			case 'footer':
2242				// data is e.g. '{e_PLUGIN}myplugin/jslib/myplug.js'
2243				if(null !== $zone) $jshandler->footerFile($data, $zone, $pre, $post);
2244				else $jshandler->footerFile($data, 5, $pre, $post);
2245			break;
2246
2247			// $type is plugin name
2248			default:
2249				// data is e.g. 'jslib/myplug.js'
2250				if(!self::isInstalled($type)) return;
2251				if(null !== $zone) $jshandler->requirePluginLib($type, $data, $zone);
2252				else $jshandler->requirePluginLib($type, $data);
2253			break;
2254		}
2255
2256		$jshandler->resetDependency();
2257	}
2258
2259
2260	/**
2261	 * Add a <link> tag to the head of the html document.
2262	 * @param array $attributes
2263	 * @example e107::link(array('rel'=>"dns-prefetch", "href" => "http://example-domain.com/"));
2264	 */
2265	public static function link($attributes=array())
2266	{
2267		self::getJs()->addLink($attributes);
2268	}
2269
2270
2271
2272
2273	/**
2274	 * CSS Common Public Function. Prefered is shortcode script path
2275	 * @param string $type core|theme|footer|inline|footer-inline|url or any existing plugin_name
2276	 * @param string $data depends on the type - path/url or inline js source
2277	 * @param null $dep
2278	 * @param string $media any valid media attribute string - http://www.w3schools.com/TAGS/att_link_media.asp
2279	 * @param string $preComment possible comment e.g. <!--[if lt IE 7]>
2280	 * @param string $postComment possible comment e.g. <![endif]-->
2281	 * @param null $dependence
2282	 */
2283	public static function css($type, $data, $dep = null, $media = 'all', $preComment = '', $postComment = '', $dependence = null)
2284	{
2285
2286	/*	if((strpos($data,'bootstrap.css')!==false || strpos($data,'bootstrap.min.css')!==false) && !defined("BOOTSTRAP")) // detect bootstrap is enabled. - used in nextprev.sc and forum currently.
2287		{
2288			define("BOOTSTRAP", true);
2289		}*/
2290
2291		if(self::$_css_enabled === false)
2292		{
2293			return null;
2294		}
2295
2296		$jshandler = self::getJs();
2297		$jshandler->setDependency($dep);
2298
2299		if(strpos($data,'http')===0)
2300		{
2301			$type = 'url';
2302		}
2303
2304		switch ($type)
2305		{
2306			case 'core':
2307				// data is path relative to e_FILE/jslib/
2308				$jshandler->coreCSS($data, $media, $preComment, $postComment);
2309			break;
2310
2311			case 'bootstrap':
2312				// data is path relative to e_FILE/jslib/
2313				$jshandler->coreCSS('bootstrap/css/'.$data, $media, $preComment, $postComment);
2314			break;
2315
2316			case 'theme':
2317				// data is path relative to current theme
2318				$jshandler->themeCSS($data, $media, $preComment, $postComment);
2319			break;
2320
2321			case 'inline':
2322				// data is CSS source (without style tags)
2323				$jshandler->inlineCSS($data, $media);
2324			break;
2325
2326			case 'url':
2327				// data is e.g. 'http://cdn.somesite.com/some.css'
2328				$jshandler->otherCSS($data, $media, $preComment, $postComment);
2329			break;
2330
2331			// $type is plugin name
2332			default:
2333				// data is e.g. 'css/myplug.css'
2334				if(self::isInstalled($type)) $jshandler->pluginCSS($type, $data, $media, $preComment, $postComment);
2335			break;
2336		}
2337		$jshandler->resetDependency();
2338	}
2339
2340
2341	/**
2342	 * Throw log/info/warnings/errors to the Chrome/Firefox Console.
2343	 * @param $name
2344	 * @param null $var
2345	 * @param string $type
2346	 */
2347	public static function debug($name, $var = null, $type = 'log')
2348	{
2349
2350	    $nl = "\r\n";
2351	//	echo "alert('hi');";
2352		$text = '';
2353
2354	    switch($type) {
2355	        case 'log':
2356	           $text .= 'console.log("'.$name.'");'.$nl;
2357	        break;
2358	        case 'info':
2359	           $text .= 'console.info("'.$name.'");'.$nl;
2360	        break;
2361	        case 'warning':
2362	           $text .= 'console.warn("'.$name.'");'.$nl;
2363	        break;
2364	        case 'error':
2365	           $text .= 'console.error("'.$name.'");'.$nl;
2366	        break;
2367	    }
2368
2369	    if (!empty($var))
2370	    {
2371	        if (is_object($var) || is_array($var))
2372	        {
2373	            $object = json_encode($var);
2374
2375	           $text .= 'var object'.preg_replace('~[^A-Z|0-9]~i',"_",$name).' = \''.str_replace("'","\'",$object).'\';'.$nl;
2376	           $text .= 'var val'.preg_replace('~[^A-Z|0-9]~i',"_",$name).' = eval("(" + object'.preg_replace('~[^A-Z|0-9]~i',"_",$name).' + ")" );'.$nl;
2377
2378	            switch($type)
2379	            {
2380	                case 'log':
2381	                   $text .= 'console.debug(val'.preg_replace('~[^A-Z|0-9]~i',"_",$name).');'.$nl;
2382	                break;
2383	                case 'info':
2384	                   $text .= 'console.info(val'.preg_replace('~[^A-Z|0-9]~i',"_",$name).');'.$nl;
2385	                break;
2386	                case 'warning':
2387	                   $text .= 'console.warn(val'.preg_replace('~[^A-Z|0-9]~i',"_",$name).');'.$nl;
2388	                break;
2389	                case 'error':
2390	                   $text .= 'console.error(val'.preg_replace('~[^A-Z|0-9]~i',"_",$name).');'.$nl;
2391	                break;
2392	            }
2393	        }
2394	        else
2395	        {
2396	            switch($type)
2397	            {
2398	                case 'log':
2399	                   $text .= 'console.debug("'.str_replace('"','\\"',$var).'");'.$nl;
2400	                break;
2401	                case 'info':
2402	                   $text .= 'console.info("'.str_replace('"','\\"',$var).'");'.$nl;
2403	                break;
2404	                case 'warning':
2405	                   $text .= 'console.warn("'.str_replace('"','\\"',$var).'");'.$nl;
2406	                break;
2407	                case 'error':
2408	                   $text .= 'console.error("'.str_replace('"','\\"',$var).'");'.$nl;
2409	                break;
2410	            }
2411	        }
2412	    }
2413
2414
2415		self::js('footer-inline', $text);
2416
2417	}
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427	/**
2428	 * Retrieve JS Helper object
2429	 *
2430	 * @param boolean|string $singleton if true return singleton, if string return singleton object, use string as namespace, default false
2431	 * @return e_jshelper
2432	 */
2433	public static function getJshelper($singleton = false)
2434	{
2435		if($singleton)
2436		{
2437			return self::getSingleton('e_jshelper', true, (true === $singleton ? '' : $singleton));
2438		}
2439		return self::getObject('e_jshelper', null, true);
2440	}
2441
2442	/**
2443	 * @see eResponse::addMeta()
2444	 * @param null $name
2445	 * @param null $content
2446	 * @param array $extended
2447	 * @return eResponse
2448	 */
2449	public static function meta($name = null, $content = null, $extended = array())
2450	{
2451		/** @var eResponse $response */
2452		$response = self::getSingleton('eResponse');
2453
2454		if($name === 'description')
2455		{
2456			$response->addMetaDescription($content);	//Cam: TBD
2457		}
2458
2459		if($name === 'keywords')
2460		{
2461			$response->addMetaKeywords($content);	//Cam: TBD
2462		}
2463
2464		return $response->addMeta($name, $content, $extended);
2465	//	return self::getUrl()->response()->addMeta($name, $content, $extended);
2466	}
2467
2468	/**
2469	 * Retrieve admin dispatcher instance.
2470	 * It's instance is self registered (for now, this could change in the future) on initialization (__construct())
2471	 *
2472	 * @see e_admin_dispatcher
2473	 * @return e_admin_dispatcher
2474	 */
2475	public static function getAdminUI()
2476	{
2477		return self::getRegistry('admin/ui/dispatcher');
2478	}
2479
2480	/**
2481	 * Retrieves class Object for specific plugin's addon such as e_url.php, e_cron.php, e_sitelink.php
2482	 * FIXME override from e.g. core/override/addons/
2483	 *
2484	 * @param string $pluginName e.g. faq, page
2485	 * @param string $addonName eg. e_cron, e_url, e_module
2486	 * @param mixed $className [optional] true - use default name, false - no object is returned (include only), any string will be used as class name
2487	 * @param mixed $param [optional] construct() param
2488	 * @return object
2489	 */
2490	public static function getAddon($pluginName, $addonName, $className = true)
2491	{
2492		$filename = $addonName; // e.g. 'e_cron';
2493
2494		// fixme, temporary adding 's' to className, should be core fixed, better naming
2495		if(true === $className) $className = $pluginName.'_'.substr($addonName, 2); // remove 'e_'
2496
2497		$elist = self::getPref($filename.'_list');
2498
2499		if($filename === 'e_menu')
2500		{
2501			if(!in_array($pluginName, $elist)) return null;
2502		}
2503		else
2504		{
2505			if(!isset($elist[$pluginName])) return null;
2506		}
2507
2508		// TODO override check comes here
2509		$path = e_PLUGIN.$pluginName.'/'.$filename.'.php';
2510		// e.g. include e_module, e_meta etc
2511		if(false === $className) return include_once($path);
2512
2513		if(!class_exists($className, false))
2514		{
2515			include_once($path);
2516		}
2517
2518		if(!class_exists($className, false))
2519		{
2520			return null;
2521		}
2522		return new $className;
2523	}
2524
2525	/**
2526	 * Retrieves config() from all plugins for addons such as e_url.php, e_cron.php, e_sitelink.php
2527	 * @param string $addonName eg. e_cron, e_url
2528	 * @param string $className [optional] (if different from addonName)
2529	 * @param string $methodName [optional] (if different from 'config')
2530	 * @return array
2531	 */
2532	public static function getAddonConfig($addonName, $className = '', $methodName='config', $param=null,$param2=null )
2533	{
2534		$new_addon = array();
2535
2536		$sql = self::getDb(); // Might be used by older plugins.
2537
2538		$filename = $addonName; // e.g. 'e_cron';
2539		if(!$className)
2540		{
2541			$className = substr($filename, 2); // remove 'e_'
2542		}
2543
2544		$elist = self::getPref($filename.'_list');
2545
2546		if(!empty($elist))
2547		{
2548			foreach(array_keys($elist) as $key)
2549			{
2550				if(is_readable(e_PLUGIN.$key.'/'.$filename.'.php'))
2551				{
2552					include_once(e_PLUGIN.$key.'/'.$filename.'.php');
2553
2554					$class_name = $key.'_'.$className;
2555					$array = self::callMethod($class_name, $methodName,$param,$param2);
2556
2557					if($array)
2558					{
2559						$new_addon[$key] = $array;
2560					}
2561
2562				}
2563			}
2564		}
2565
2566		return $new_addon;
2567	}
2568
2569
2570	/**
2571	 * Safe way to call user methods.
2572	 * @param string|object $class_name
2573	 * @param string $method_name
2574	 * @param mixed $param -1st parameter sent to method
2575	 * @param mixed $param2 - 2nd parameter sent to method
2576	 * @return array|bool FALSE
2577	 */
2578	public static function callMethod($class_name, $method_name, $param=null, $param2=null)
2579	{
2580		$mes = self::getMessage();
2581
2582		if(is_object($class_name) || class_exists($class_name))
2583		{
2584
2585			if(is_object($class_name))
2586			{
2587				$obj = $class_name;
2588				$class_name = get_class($obj);
2589			}
2590			else
2591			{
2592				$obj = new $class_name;
2593			}
2594
2595			if(method_exists($obj, $method_name))
2596			{
2597				if(E107_DBG_INCLUDES)
2598				{
2599					//$debug_backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 6);
2600					$mes->addDebug('Executing <strong>'.$class_name.' :: '.$method_name.'()</strong>');
2601				}
2602				return call_user_func(array($obj, $method_name),$param, $param2);
2603			}
2604			else
2605			{
2606			//	$mes->addDebug('Function <strong>'.$class_name.' :: '.$method_name.'()</strong> NOT found.');
2607			}
2608		}
2609		return FALSE;
2610	}
2611
2612	/**
2613	 * Retrieves the e_url config  - new v2.1.6
2614	 * @param string $mode config | alias | profile
2615	 * @return array
2616	 */
2617	public static function getUrlConfig($mode='config')
2618	{
2619		$new_addon = array();
2620
2621		$filename = 'e_url';
2622		$elist = self::getPref($filename.'_list');
2623		$className = substr($filename, 2); // remove 'e_'
2624		$methodName = 'config';
2625
2626		$url_profiles = e107::getPref('url_profiles');
2627
2628		if(!empty($elist))
2629		{
2630			foreach(array_keys($elist) as $key)
2631			{
2632				if(is_readable(e_PLUGIN.$key.'/'.$filename.'.php'))
2633				{
2634
2635
2636					include_once(e_PLUGIN.$key.'/'.$filename.'.php');
2637
2638					$class_name = $key.'_'.$className;
2639
2640					if(is_object($class_name))
2641					{
2642						$obj = $class_name;
2643						$class_name = get_class($obj);
2644					}
2645					elseif(class_exists($class_name))
2646					{
2647						$obj = new $class_name;
2648					}
2649					else
2650					{
2651						continue;
2652					}
2653
2654					if($mode === 'alias')
2655					{
2656						if(!empty($obj->alias))
2657						{
2658							$new_addon[$key] = $obj->alias;
2659						}
2660
2661						continue;
2662					}
2663
2664					if($mode === 'profiles')
2665					{
2666						if(!empty($obj->profiles))
2667						{
2668							$new_addon[$key] = $obj->profiles;
2669						}
2670
2671						continue;
2672					}
2673
2674					if($mode === 'generate')
2675					{
2676						if(!empty($obj->generate))
2677						{
2678							$new_addon[$key] = $obj->generate;
2679						}
2680
2681						continue;
2682					}
2683
2684					$profile = !empty($url_profiles[$key]) ? $url_profiles[$key] : null;
2685
2686					$array = self::callMethod($obj, $methodName,$profile);
2687
2688					if($array)
2689					{
2690						foreach($array as $k=>$v)
2691						{
2692							if(empty($v['alias']) && !empty($obj->alias))
2693							{
2694								$v['alias'] = $obj->alias;
2695							}
2696							$new_addon[$key][$k] = $v;
2697
2698						}
2699
2700					}
2701
2702				}
2703			}
2704		}
2705
2706		return $new_addon;
2707	}
2708
2709	/**
2710	 * Get theme name or path.
2711	 *
2712	 * @param mixed $for true (default) - auto-detect (current), admin - admin theme, front - site theme
2713	 * @param string $path default empty string (return name only), 'abs' - absolute url path, 'rel' - relative server path
2714	 * @return string
2715	 */
2716	public static function getThemeInfo($for = true, $path = '')
2717	{
2718	//	global $user_pref; // FIXME - user model, kill user_pref global
2719
2720		if(true === $for)
2721		{
2722			$for = e_ADMIN_AREA ? 'admin' : 'front';
2723		}
2724		switch($for )
2725		{
2726			case 'admin':
2727				$for = self::getPref('admintheme');
2728			break;
2729
2730			case 'front':
2731
2732				if(defined('USERTHEME') && USERTHEME !==false)
2733				{
2734					$for = USERTHEME;
2735				}
2736				else
2737				{
2738					$user_pref = self::getUser()->getPref();
2739					$for = !empty($user_pref['sitetheme']) ? $user_pref['sitetheme'] : self::getPref('sitetheme');
2740				}
2741
2742			break;
2743		}
2744		if(!$path) return $for;
2745
2746		switch($path)
2747		{
2748			case 'abs':
2749				$path = e_THEME_ABS.$for.'/';
2750			break;
2751
2752			case 'rel':
2753			default:
2754				$path = e_THEME.$for.'/';
2755			break;
2756		}
2757		return $path;
2758	}
2759
2760	/**
2761	 * Retrieve core template path
2762	 * Example: <code>echo e107::coreTemplatePath('admin_icons');</code>
2763	 *
2764	 * @see getThemeInfo()
2765	 * @param string $id part of the path/file name without _template.php part
2766	 * @param boolean $override default true
2767	 * @return string relative path
2768	 */
2769	public static function coreTemplatePath($id, $override = true)
2770	{
2771		$id 						= str_replace('..', '', $id); //simple security, '/' is allowed
2772		$curTheme 					= self::getThemeInfo($override, 'rel');
2773
2774		$override_path 				= $override ? $curTheme.'templates/'.$id.'_template.php' : null;
2775		$legacy_override_path 		= $override ? $curTheme.$id.'_template.php' : null;
2776
2777		$core_path 					= e_CORE.'templates/'.$id.'_template.php'; // default
2778		$core_path_legacy 			= e_CORE.'templates/legacy/'.$id.'_template.php';
2779		$core_path_bs4				= e_CORE.'templates/bootstrap4/'.$id.'_template.php';
2780
2781		if($override_path && is_readable($override_path)) // v2 override template.
2782		{
2783			return $override_path;
2784		}
2785		elseif($legacy_override_path && is_readable($legacy_override_path)) //v1 override template.
2786		{
2787			return $legacy_override_path;
2788		}
2789		elseif(THEME_LEGACY === true && is_readable($core_path_legacy)) //v1 core template.
2790		{
2791			return $core_path_legacy;
2792		}
2793		elseif(defset('BOOTSTRAP') === 4 && is_readable($core_path_bs4))
2794        {
2795            return $core_path_bs4;
2796        }
2797
2798		return $core_path;
2799	}
2800
2801	/**
2802	 * Retrieve plugin template path
2803	 * Override path could be forced to front- or back-end via
2804	 * the $override parameter e.g. <code> e107::templatePath(plug_name, 'my', 'front')</code>
2805	 * Example:
2806	 * <code>
2807	 * echo e107::templatePath(plug_name, 'my');
2808	 * // result is something like:
2809	 * // e107_themes/current_theme/templates/plug_name/my_template.php
2810	 * // or if not found
2811	 * // e107_plugins/plug_name/templates/my_template.php
2812	 * </code>
2813	 *
2814	 * @see getThemeInfo()
2815	 * @param string $plug_name plugin name
2816	 * @param string $id part of the path/file name without _template.php part
2817	 * @param boolean|string $override default true
2818	 * @return string relative path
2819	 */
2820	public static function templatePath($plug_name, $id, $override = true)
2821	{
2822		$id = str_replace('..', '', $id); //simple security, '/' is allowed
2823		$plug_name = preg_replace('#[^a-z0-9_]#i', '', $plug_name); // only latin allowed, so \w not a solution since PHP5.3
2824		$override_path = $override ? self::getThemeInfo($override, 'rel').'templates/'.$plug_name.'/'.$id.'_template.php' : null;
2825		$default_path = e_PLUGIN.$plug_name.'/templates/'.$id.'_template.php';
2826
2827		return ($override_path && is_readable($override_path) ? $override_path : $default_path);
2828	}
2829
2830	/**
2831	 * Get core template. Use this method for templates, which are following the
2832	 * new template standards:
2833	 * - template variables naming conventions
2834	 * - one array variable per template only
2835	 * - theme override is made now by current_theme/templates/ folder
2836	 *
2837	 * <br><br>Results are cached (depending on $id and $override so it's safe to use
2838	 * this method e.g. in loop for retrieving a template string. If template (or template key) is not
2839	 * found, <b>NULL</b> is returned.<br><br>
2840	 *
2841	 * Example usage: <code>e107::getCoreTemplate('user', 'short_start');</code>
2842	 * Will search for:
2843	 * - e107_themes/current_frontend_theme/templates/user_template.php (if $override is true)
2844	 * - e107_themes/templates/user_template.php (if override not found or $override is false)
2845	 * - $USER_TEMPLATE array which contains all user templates
2846	 * - $USER_TEMPLATE['short_start'] (if key is null, $USER_TEMPLATE will be returned)
2847	 *
2848	 * @param string $id - file prefix, e.g. user for user_template.php
2849	 * @param string|null $key
2850	 * @param mixed $override see {@link getThemeInfo()} true/false,  front or admin.
2851	 * @param boolean $merge merge theme with core templates, default is false
2852	 * @param boolean $info retrieve template info only
2853	 * @return string|array
2854	 */
2855	public static function getCoreTemplate($id, $key = null, $override = true, $merge = false, $info = false)
2856	{
2857		$reg_path = 'core/e107/templates/'.$id.($override ? '/ext' : '');
2858		$path = self::coreTemplatePath($id, $override);
2859		$id = str_replace('/', '_', $id);
2860		$ret = self::_getTemplate($id, $key, $reg_path, $path, $info);
2861
2862		### Attempt to fix merge issues; in case we override - template array not found in theme,
2863		### so we need to continue and merge with core templates
2864		if($merge && $override && empty($ret))
2865		{
2866			$ret = array();
2867		}
2868
2869		if((!$merge && !$override) || is_string($ret))
2870		{
2871			 return $ret;
2872		}
2873
2874		// merge
2875		$reg_path = 'core/e107/templates/'.$id;
2876		$path = self::coreTemplatePath($id, false);
2877		$id = str_replace('/', '_', $id);
2878        // Introducing noWrapper when merging
2879		$ret_core = self::_getTemplate($id, $key, $reg_path, $path, $info, true);
2880
2881		return (is_array($ret_core) ? array_merge($ret_core, $ret) : $ret);
2882	}
2883
2884	/**
2885	 * Get plugin template. Use this method for plugin templates, which are following the
2886	 * new template standards:
2887	 * - template variables naming conventions ie. ${NAME IN CAPS}_TEMPLATE['{ID}'] = "<div>...</div>";
2888	 * - one array variable per template only
2889	 * - theme override is made now by current_theme/templates/plugin_name/ folder
2890	 *
2891	 * <br><br>Results are cached (depending on $id and $override so it's safe to use
2892	 * this method e.g. in loop for retrieving a template string. If template (or template key) is not
2893	 * found, <b>NULL</b> is returned.<br><br>
2894	 *
2895	 * Example usage: <code>e107::getTemplate('user', 'short_start');</code>
2896	 * Will search for:
2897	 * - e107_themes/{current_frontend_theme}/templates/user_template.php (if $override is true) - this is the default.
2898	 * - e107_core/templates/user_template.php (if override not found or $override is false)
2899	 * - $USER_TEMPLATE array which contains all user templates
2900	 * - $USER_TEMPLATE['short_start'] (if key is null, $USER_TEMPLATE will be returned)
2901	 *
2902	 * @param string $plug_name if null getCoreTemplate method will be called
2903	 * @param string $id - file prefix, e.g. calendar for calendar_template.php
2904	 * @param string|null $key
2905	 * @param boolean $override see {@link getThemeInfo()}
2906	 * @param boolean $merge merge theme with plugin templates, default is false
2907	 * @param boolean $info retrieve template info only
2908	 * @return string|array
2909	 */
2910	public static function getTemplate($plug_name, $id = null, $key = null, $override = true, $merge = false, $info = false)
2911	{
2912		if(!$plug_name)
2913		{
2914			return self::getCoreTemplate($id, $key, $override, $merge, $info);
2915		}
2916		if(null == $id || true === $id) // loads {$plug_name}/templates/{$plug_name}_template.php and an array ${PLUG_NAME}_TEMPLATE
2917		{
2918			$id = $plug_name;
2919		}
2920		$reg_path = 'plugin/'.$plug_name.'/templates/'.$id.($override ? '/ext' : '');
2921		$path = self::templatePath($plug_name, $id, $override);
2922
2923		if(ADMIN && E107_DBG_INCLUDES)
2924		{
2925			self::getMessage()->addDebug( "Attempting to load Template File: ".$path );
2926		}
2927
2928		/**
2929		 * "front" and "global" LANs might not be loaded come self::_getTemplate(),
2930		 * so the following calls to self::plugLan() fix that.
2931		 */
2932		self::plugLan($plug_name, null, true);
2933		self::plugLan($plug_name, null, false);
2934		self::plugLan($plug_name, 'global', true);
2935		self::plugLan($plug_name, 'global', false);
2936
2937		$id = str_replace('/', '_', $id);
2938		$ret = self::_getTemplate($id, $key, $reg_path, $path, $info);
2939
2940		if($merge === false || $override === false)
2941		{
2942			return ($ret === false) ? '' : $ret;
2943		}
2944
2945		// merge
2946		$reg_path = 'plugin/'.$plug_name.'/templates/'.$id;
2947		$path = self::templatePath($plug_name, $id, false);
2948
2949		$id = str_replace('/', '_', $id);
2950        // Introduced noWrapper when merging
2951		$ret_plug = self::_getTemplate($id, $key, $reg_path, $path, $info, true);
2952
2953		if($merge === true && $key !== null && $ret === false) // key not set, so send 'core' version instead.
2954		{
2955			return $ret_plug;
2956		}
2957
2958		if($ret === false)
2959		{
2960			return '';
2961		}
2962
2963		return (is_array($ret_plug) ? array_merge($ret_plug, $ret) : $ret);
2964	}
2965
2966	/**
2967	 * Register sc_style registry
2968	 * @param string $templateId e.g. 'contact/form' or 'contact' for all contact template wrappers
2969	 * @param string $scName [optional] shortcode name - if provided, wrapper (string) for the corresponding code will be returned
2970	 * @return array|string
2971	 */
2972	public static function templateWrapper($templateId, $scName = null)
2973	{
2974		if(!$templateId) return array();
2975
2976		list($templateId, $templateKey) = explode('/', $templateId, 2);
2977
2978		$wrapperRegPath = 'templates/wrapper/'.$templateId;
2979
2980		$wrapper = self::getRegistry($wrapperRegPath);
2981
2982		if(empty($wrapper) || !is_array($wrapper)) $wrapper = array();
2983
2984		if(strpos($templateKey,'/')!==false) // quick fix support for 3 keys eg. news/view/item
2985		{
2986			list($templateKey,$templateKey2) = explode("/", $templateKey, 2);
2987			if($templateKey && $templateKey2)
2988			{
2989				 $wrapper = (isset($wrapper[$templateKey][$templateKey2])  ? $wrapper[$templateKey][$templateKey2] : array());
2990			}
2991		}
2992		else // support for 2 keys. eg. contact/form
2993		{
2994			if($templateKey) $wrapper = (isset($wrapper[$templateKey])  ? $wrapper[$templateKey] : array());
2995		}
2996
2997		if(null !== $scName)
2998		{
2999			$scName = strtoupper($scName);
3000			return isset($wrapper[$scName]) ? $wrapper[$scName] : '';
3001		}
3002
3003		return $wrapper;
3004	}
3005
3006	/**
3007	 * Retrieve/set sc_style array (global shortcode wrapper)
3008	 * @param array $set template defined $sc_style, will be merged with current registry content
3009	 * @return array
3010	 */
3011	public static function scStyle($set = null)
3012	{
3013		$_sc_style = self::getRegistry('shortcodes/sc_style');
3014		if(!is_array($_sc_style)) $_sc_style = array();
3015		if(is_array($set) && !empty($set))
3016		{
3017			self::setRegistry('shortcodes/sc_style', array_merge($_sc_style, $set));
3018		}
3019
3020		return $_sc_style;
3021	}
3022
3023	/**
3024	 * Get Template Info array.
3025	 * Note: Available only after getTemplate()/getCoreTemplate() call
3026	 *
3027	 * @param string $plug_name if null - search for core template
3028	 * @param string $id
3029	 * @param string $key
3030	 * @param boolean $override
3031	 * @param boolean $merge
3032	 * @return array
3033	 */
3034	public static function getTemplateInfo($plug_name = null, $id, $key = null, $override = true, $merge = false)
3035	{
3036		if($plug_name)
3037		{
3038			$ret = self::getTemplate($plug_name, $id, null, $override, $merge, true);
3039		}
3040		else
3041		{
3042			$ret = self::getCoreTemplate($id, null, $override, $merge, true);
3043		}
3044		if($key && isset($ret[$key]) && is_array($ret[$key]))
3045		{
3046			return $ret[$key];
3047		}
3048		return $ret;
3049	}
3050
3051	/**
3052	 * Return a list of available template IDs for a plugin(eg. $MYTEMPLATE['my_id'] -> array('id' => 'My Id'))
3053	 *
3054	 * FIXME - the format of $allinfo=true array is not usable at all, convert it so that it's compatible with e_form::selectbox() method
3055	 *
3056	 * @param string $plugin_name
3057	 * @param string $template_id [optional] if different from $plugin_name;
3058	 * @param mixed $where true - current theme, 'admin' - admin theme, 'front' (default)  - front theme
3059	 * @param string $filter_mask
3060	 * @param boolean $merge merge theme with core/plugin layouts, default is false
3061	 * @param boolean $allinfo reutrn nimerical array of templates and all available template information
3062	 * @return array
3063	 */
3064	public static function getLayouts($plugin_name, $template_id = '', $where = 'front', $filter_mask = '', $merge = false, $allinfo = true)
3065	{
3066		if(!$plugin_name) // Core template
3067		{
3068			$tmp = self::getCoreTemplate($template_id, null, $where, $merge);
3069			$tmp_info = self::getTemplateInfo(null, $template_id, null, $where, $merge);
3070		}
3071		else // Plugin template
3072		{
3073			$id = (!$template_id) ? $plugin_name : $template_id;
3074			$tmp = self::getTemplate($plugin_name, $id, null, $where, $merge);
3075			$tmp_info = self::getTemplateInfo($plugin_name, $id, null, $where, $merge);
3076		}
3077
3078		$templates = array();
3079		if(!$filter_mask)
3080		{
3081			$filter_mask = array();
3082		}
3083		elseif(!is_array($filter_mask))
3084		{
3085			$filter_mask = array($filter_mask);
3086		}
3087		foreach($tmp as $key => $val)
3088		{
3089			$match = true;
3090			if($filter_mask)
3091			{
3092				$match = false;
3093				foreach ($filter_mask as $mask)
3094				{
3095					if(preg_match($mask, $key)) //e.g. retrieve only keys starting with 'layout_'
3096					{
3097						$match = true;
3098						break;
3099					}
3100				}
3101				if(!$match) continue;
3102			}
3103			if(isset($tmp_info[$key]))
3104			{
3105				$templates[$key] = defset($tmp_info[$key]['title'], $tmp_info[$key]['title']);
3106				continue;
3107			}
3108			$templates[$key] = implode(' ', array_map('ucfirst', explode('_', $key))); //TODO add LANS?
3109		}
3110		return ($allinfo ? array($templates, $tmp_info) : $templates);
3111	}
3112
3113	/**
3114	 * More abstsract template loader, used
3115	 * internal in {@link getTemplate()} and {@link getCoreTemplate()} methods
3116	 * If $info is set to true, only template informational array will be returned
3117	 *
3118	 * @param string $id
3119	 * @param string|null $key
3120	 * @param string $reg_path
3121	 * @param string $path
3122	 * @param boolean $info
3123     * @param boolean $noWrapper
3124	 * @return string|array
3125	 */
3126	public static function _getTemplate($id, $key, $reg_path, $path, $info = false, $noWrapper = false)
3127	{
3128		$regPath = $reg_path;
3129		$var = strtoupper($id).'_TEMPLATE';
3130		$regPathInfo = $reg_path.'/info';
3131		$var_info = strtoupper($id).'_INFO';
3132
3133		$wrapper = strtoupper($id).'_WRAPPER'; // see contact_template.php
3134		$wrapperRegPath = 'templates/wrapper/'.$id;
3135
3136		// Use: list($pre,$post) = explode("{---}",$text,2);
3137
3138		$tp = self::getParser(); // BC FIx - avoid breaking old templates due to missing globals.
3139
3140		if(null === self::getRegistry($regPath))
3141		{
3142			(deftrue('E107_DEBUG_LEVEL') ? include_once($path) : @include_once($path));
3143			self::setRegistry($regPath, (isset($$var) ? $$var : array()));
3144
3145			// sc_style not a global anymore and uppercase
3146
3147            // Fix template merge issue - no-wrapper sent to avoid sc wrappers confusions
3148            if(!$noWrapper)
3149            {
3150                if(isset($SC_WRAPPER))
3151                {
3152                    if(E107_DBG_BBSC)
3153                    {
3154                        self::getMessage()->addDebug("Found wrapper: ".$SC_WRAPPER);
3155                    }
3156                    self::scStyle($SC_WRAPPER);
3157                }
3158
3159                // ID_WRAPPER support
3160                if(isset($$wrapper) && !empty($$wrapper) && is_array($$wrapper))
3161                {
3162                    if(E107_DBG_BBSC)
3163                    {
3164                        self::getMessage()->addDebug("Found ID wrapper: ".$wrapper);
3165                    }
3166                    self::setRegistry($wrapperRegPath, $$wrapper);
3167                }
3168            }
3169		}
3170		if(null === self::getRegistry($regPathInfo))
3171		{
3172			self::setRegistry($regPathInfo, (isset($$var_info) && is_array($$var_info) ? $$var_info : array()));
3173		}
3174
3175		$ret = (!$info ? self::getRegistry($regPath) : self::getRegistry($regPathInfo));
3176
3177		if(!$key)
3178		{
3179			return $ret;
3180		}
3181
3182		return ($ret && is_array($ret) && isset($ret[$key]) ? $ret[$key] : false);
3183	}
3184
3185	/**
3186	 * Load language file, replacement of include_lan()
3187	 * @outdated use e107::lan() or e107::coreLan(), e107::plugLan(), e107::themeLan()
3188	 * @param string $path
3189	 * @param boolean $force
3190	 * @return string
3191	 */
3192	public static function includeLan($path, $force = false)
3193	{
3194		if (!is_readable($path))
3195		{
3196			if (self::getPref('noLanguageSubs') || (e_LANGUAGE === 'English'))
3197			{
3198				return false;
3199			}
3200
3201			self::getDebug()->log("Couldn't load language file: " . $path);
3202
3203			$path = str_replace(e_LANGUAGE, 'English', $path);
3204
3205			self::getDebug()->log("Attempts to load default language file: " . $path);
3206
3207			if(!is_readable($path))
3208			{
3209				self::getDebug()->log("Couldn't load default language file: " . $path);
3210				return false;
3211			}
3212		}
3213
3214		$adminLanguage = self::getPref('adminlanguage');
3215
3216		if(e_ADMIN_AREA && vartrue($adminLanguage))
3217		{
3218			$path = str_replace(e_LANGUAGE, $adminLanguage, $path);
3219		}
3220
3221		$ret = ($force) ? include($path) : include_once($path);
3222		return (isset($ret)) ? $ret : "";
3223	}
3224
3225	/**
3226	 * Simplify importing of core Language files.
3227	 * All inputs are sanitized.
3228	 * Core Exceptions as e_LANGUAGE.'.php' and e_LANGUAGE.'_custom.php' are manually loaded. (see class2.php)
3229	 *
3230	 * Examples:
3231	 * <code><?php
3232	 * 	// import defeinitions from /e107_languages/[CurrentLanguage]/lan_comment.php</code>
3233	 * 	e107::coreLan('comment');
3234	 *
3235	 * 	// import defeinitions from /e107_languages/[CurrentLanguage]/admin/lan_banlist.php
3236	 * 	self::coreLan('banlist', true);
3237	 * </code>
3238	 *
3239	 * @param string $fname filename without the extension part (e.g. 'comment')
3240	 * @param boolean $admin true if it's an administration language file
3241	 * @return bool
3242	 */
3243	public static function coreLan($fname, $admin = false)
3244	{
3245		if ($admin)
3246		{
3247			e107::includeLan(e_LANGUAGEDIR.e_LANGUAGE.'/admin/lan_admin.php');
3248		}
3249
3250		$cstring  = 'corelan/'.e_LANGUAGE.'_'.$fname.($admin ? '_admin' : '_front');
3251		if(self::getRegistry($cstring)) return;
3252
3253		$fname = ($admin ? 'admin/' : '').'lan_'.preg_replace('/[^\w]/', '', trim($fname, '/')).'.php';
3254		$path = e_LANGUAGEDIR.e_LANGUAGE.'/'.$fname;
3255
3256		self::setRegistry($cstring, true);
3257
3258		return self::includeLan($path, false);
3259	}
3260
3261	/**
3262	 * Simplify importing of plugin Language files (following e107 plugin structure standards).
3263	 * All inputs are sanitized.
3264	 *
3265	 * Examples:
3266	 * <code><?php
3267	 * 	// import defeinitions from /e107_plugins/forum/languages/[CurrentLanguage]/lan_forum.php
3268	 * 	e107::plugLan('forum', 'lan_forum');
3269	 *
3270	 * 	// import defeinitions from /e107_plugins/featurebox/languages/[CurrentLanguage]_admin_featurebox.php
3271	 *  // OR /e107_plugins/featurebox/languages/[CurrentLanguage]/[CurrentLanguage]_admin_featurebox.php (auto-detected)
3272	 * 	e107::plugLan('featurebox', 'admin_featurebox', true);
3273	 *
3274	 * 	// import defeinitions from /e107_plugins/myplug/languages/[CurrentLanguage]_front.php
3275	 * 	e107::plugLan('myplug');
3276	 *
3277	 * 	// import defeinitions from /e107_plugins/myplug/languages/[CurrentLanguage]_admin.php
3278	 * 	e107::plugLan('myplug', true);
3279	 *
3280	 * 	// import defeinitions from /e107_plugins/myplug/languages/[CurrentLanguage]/admin/common.php
3281	 * 	e107::plugLan('myplug', 'admin/common');
3282	 * </code>
3283	 *
3284	 * @param string $plugin plugin name
3285	 * @param string $fname filename without the extension part (e.g. 'common')
3286	 * @param boolean $flat false (default, preferred) Language folder structure; true - prepend Language to file name
3287	 * @return bool
3288	 */
3289	public static function plugLan($plugin, $fname = '', $flat = false)
3290	{
3291		$cstring  = 'pluglan/'.e_LANGUAGE.'_'.$plugin.'_'.$fname.($flat ? '_1' : '_0');
3292		if(self::getRegistry($cstring)) return;
3293
3294		$plugin = preg_replace('/[^\w]/', '', $plugin);
3295
3296		if($fname === 'global') // fix ambiguity
3297		{
3298			 $fname = e_LANGUAGE."_global";
3299		}
3300		elseif($fname && is_string($fname))
3301		{
3302			 $fname = e_LANGUAGE.($flat ? '_' : '/').preg_replace('#[^\w/]#', '', trim($fname, '/'));
3303		}
3304		elseif($fname === true) // admin file.
3305		{
3306			//$fname = "admin/".e_LANGUAGE;
3307			 $fname = e_LANGUAGE."_admin";
3308		}
3309		else
3310		{
3311			// $fname = e_LANGUAGE;
3312			$fname = e_LANGUAGE."_front";
3313		}
3314
3315		if($flat === true) // support for alt_auth/languages/English/English_log.php etc.
3316		{
3317			$path = e_PLUGIN.$plugin.'/languages/'.e_LANGUAGE.'/'.$fname.'.php';
3318		}
3319		else
3320		{
3321			$path = e_PLUGIN.$plugin.'/languages/'.$fname.'.php';
3322		}
3323
3324		if(deftrue('E107_DBG_INCLUDES'))
3325		{
3326			$adminLanguage = self::getPref('adminlanguage');
3327
3328			if(e_ADMIN_AREA && !empty($adminLanguage))
3329			{
3330				$path = str_replace(e_LANGUAGE, $adminLanguage, $path);
3331			}
3332
3333			self::getMessage()->addDebug("Attempting to Load: ".$path);
3334		}
3335
3336
3337		self::setRegistry($cstring, true);
3338
3339		return self::includeLan($path, false);
3340	}
3341
3342	/**
3343	 * Simplify importing of theme Language files (following e107 plugin structure standards).
3344	 * All inputs are sanitized.
3345	 *
3346	 * Examples:
3347	 * <code><?php
3348	 * 	// import defeinitions from /e107_themes/[CurrentTheme]/languages/[CurrentLanguage]/lan.php
3349	 * 	e107::themeLan('lan');
3350	 *
3351	 * 	// import defeinitions from /e107_themes/[currentTheme]/languages/[CurrentLanguage].php
3352	 * 	e107::themeLan();
3353	 *
3354	 * 	// import defeinitions from /e107_themes/[currentTheme]/languages/[CurrentLanguage]_lan.php
3355	 * 	e107::themeLan('lan', null, true);
3356	 *
3357	 * 	// import defeinitions from /e107_themes/[currentTheme]/languages/[CurrentLanguage]/admin/lan.php
3358	 * 	e107::themeLan('admin/lan');
3359	 *
3360	 * 	// import defeinitions from /e107_themes/some_theme/languages/[CurrentLanguage].php
3361	 * 	e107::themeLan('', 'some_theme');
3362	 * </code>
3363	 *
3364	 * @param string $fname filename without the extension part (e.g. 'common' for common.php)
3365	 * @param string $theme theme name, if null current theme will be used
3366	 * @param boolean $flat false (default, preferred) Language folder structure; true - prepend Language to file name
3367	 * @return bool
3368	 */
3369	public static function themeLan($fname = '', $theme = null, $flat = false)
3370	{
3371		if(null === $theme) $theme = THEME.'languages/';
3372		else $theme = e_THEME.preg_replace('#[^\w/]#', '', $theme).'/languages/';
3373
3374		$cstring  = 'themelan/'.$theme.$fname.($flat ? '_1' : '_0');
3375		if(self::getRegistry($cstring)) return;
3376
3377		if($fname) $fname = e_LANGUAGE.($flat ? '_' : '/').preg_replace('#[^\w/]#', '', trim($fname, '/'));
3378		else $fname = e_LANGUAGE;
3379
3380		$path = $theme.$fname.'.php';
3381
3382		if(deftrue('E107_DBG_INCLUDES'))
3383		{
3384			self::getMessage()->addDebug("Attempting to Load: ".$path);
3385		}
3386
3387		self::setRegistry($cstring, true);
3388
3389		return self::includeLan($path, false);
3390	}
3391
3392
3393
3394	/**
3395	 * PREFERRED Generic Language File Loading Function for use by theme and plugin developers.
3396	 * Language-file equivalent to e107::js, e107::meta and e107::css
3397	 *
3398	 * FIXME disallow themes and plugins named 'core' and 'theme'
3399	 *
3400	 * @param string $type
3401	 *   'theme' or plugin name
3402	 * @param string $fname
3403	 *   (optional): relative path to the theme or plugin language folder. (same as in the other functions)
3404	 *   when missing, [e_LANGUAGE]_front.php will be used, when true [e_LANGUAGE]_admin.php will be used
3405	 * @param $options
3406	 *   Set to True for admin.
3407	 *
3408	 * @example e107::lan('theme'); // Loads THEME."languages/English.php (if English is the current language)
3409	 * @example e107::lan('gallery'); // Loads e_PLUGIN."gallery/languages/English_front.php (if English is the current language)
3410	 * @example e107::lan('gallery', 'admin'); // Loads e_PLUGIN."gallery/languages/English/admin.php (if English is the current language)
3411	 * @example e107::lan('gallery', 'admin', true); // Loads e_PLUGIN."gallery/languages/English_admin.php (if English is the current language)
3412	 * @example e107::lan('gallery', 'admin/example'); // Loads e_PLUGIN."gallery/languages/English/admin/example.php (if English is the current language)
3413	 * @example e107::lan('gallery', true); // Loads e_PLUGIN."gallery/languages/English_admin.php (if English is the current language)
3414	 * @example e107::lan('gallery', "something", true); // Loads e_PLUGIN."gallery/languages/English_something.php (if English is the current language)
3415	 * @example e107::lan('gallery', true, true); // Loads e_PLUGIN."gallery/languages/English/English_admin.php (if English is the current language)
3416	 * @example e107::lan('gallery', false, true); // Loads e_PLUGIN."gallery/languages/English/English_front.php (if English is the current language)
3417	 */
3418	public static function lan($type, $fname = null, $options = null)
3419	{
3420		$options = $options ? true : false;
3421		switch ($type)
3422		{
3423			case 'core' :
3424				self::coreLan($fname, $options);
3425			break;
3426
3427			case 'theme' :
3428				self::themeLan($fname, null,  $options);
3429				break;
3430			default :
3431				self::plugLan($type, $fname, $options);
3432				break;
3433		}
3434
3435	}
3436
3437
3438	/**
3439	 * Generic PREF retrieval Method for use by theme and plugin developers.
3440	 * @param string $type : 'core', 'theme', plugin-name
3441	 * @param $pname : name of specific preference, or leave blank for full array.
3442	 * @param null $default
3443	 * @return mixed
3444	 */
3445	public static function pref($type = 'core', $pname = null, $default = null)
3446	{
3447
3448		switch ($type)
3449		{
3450			case 'core' :
3451				return self::getPref($pname, $default);
3452			break;
3453
3454			case 'theme' :
3455				return self::getThemePref($pname, $default);
3456			break;
3457
3458			default:
3459				return self::getPlugPref($type, $pname, $default);
3460			break;
3461		}
3462
3463	}
3464
3465
3466	/**
3467	 * Set or Get the current breadcrumb array.
3468	 * @param array $array
3469	 * @return array|null
3470	 */
3471	public static function breadcrumb($array = array())
3472	{
3473
3474		if(empty($array)) // read
3475		{
3476
3477			if(empty(self::$_breadcrumb)) //Guess what it should be..
3478			{
3479				if(defined('PAGE_NAME'))  // BC search for "PAGE_NAME"
3480				{
3481					return array(0=> array('text'=>PAGE_NAME, 'url'=>null));
3482				}
3483				elseif($caption = e107::getRender()->getMainCaption()) // BC search for primary render caption
3484				{
3485					return array(0=> array('text'=>$caption, 'url'=>null));
3486				}
3487
3488			}
3489
3490			return self::$_breadcrumb;
3491		}
3492
3493
3494
3495
3496
3497		self::$_breadcrumb = $array; // write.
3498
3499		return null;
3500	}
3501
3502	/**
3503	 * Generate a plugin's search engine-friendly URL with HTML special characters escaped
3504	 *
3505	 * Can be spliced directly into HTML code like <a href="…"></a>
3506	 *
3507	 * Output is generated based on the plugin's e_url.php configuration
3508	 *
3509	 * @param string    $plugin - plugin folder name
3510	 * @param string    $key assigned in e_url.php configuration.
3511	 * @param array     $row Array of variables in url config.
3512	 * @param array     $options = [ // (optional) An associative array of additional options
3513	 * 	'mode' => 'abs | full', // @see e_parse::replaceConstants()
3514	 * 	'query' => [], // An array of query key/value-pairs (without any URL encoding) to append to the URL
3515	 * 	'fragment' => '', // A fragment identifier (named anchor) to append to the URL. Do not include the leading '#' character
3516	 * 	'legacy' => false, // When true, legacy URLs will be generated regardless of mod_rewrite status
3517	 * 	]
3518	 * @return string   The SEF URL with HTML special characters escaped
3519	 *                  (equivalent to the htmlspecialchars() output)
3520	 */
3521	public static function url($plugin = '', $key = null, $row = array(), $options = array())
3522	{
3523
3524		/* backward compat - core keys. ie. news/xxx/xxx user/xxx/xxx etc, */
3525		$legacy = array('news', 'page', 'search', 'user', 'download', 'gallery');
3526
3527		if (strpos($plugin, '/') !== false)
3528		{
3529			$tmp = explode("/", $plugin, 2);
3530
3531			if (in_array($tmp[0], $legacy))
3532			{
3533				return self::getUrl()->create($plugin, $key, $row);
3534			}
3535
3536			// shorthand - for internal use.
3537			$plugin = $tmp[0];
3538			$row = $key;
3539			$key = $tmp[1];
3540		}
3541
3542		if (!$tmp = self::getRegistry('core/e107/addons/e_url'))
3543		{
3544			$tmp = self::getUrlConfig();
3545			self::setRegistry('core/e107/addons/e_url', $tmp);
3546		}
3547
3548		$tp = self::getParser();
3549
3550		$pref = self::getPref('e_url_alias');
3551		$sefActive = self::getPref('e_url_list');
3552		$rootNamespace = self::getPref('url_main_module');
3553
3554
3555		if (is_string($options)) // backwards compat.
3556		{
3557			$options = array(
3558				'mode' => $options,
3559			);
3560		}
3561
3562		// Merge in defaults.
3563		$options += array(
3564			'mode' => 'abs',
3565			'fragment' => '',
3566			'query' => array(),
3567		);
3568
3569		if (isset($options['fragment']) && $options['fragment'] !== '')
3570		{
3571			$options['fragment'] = '#' . $options['fragment'];
3572		}
3573
3574		if (!empty($plugin) && empty($tmp[$plugin][$key]['sef']))
3575		{
3576			self::getMessage()->addDebug("e_url.php in <b>" . e_PLUGIN . $plugin . "</b> is missing the key: <b>" . $key . "</b>. Or, you may need to <a href='" . e_ADMIN . "db.php?mode=plugin_scan'>scan your plugin directories</a> to register e_url.php");
3577			return false;
3578		}
3579
3580		if (!empty($tmp[$plugin][$key]['alias']))
3581		{
3582			$alias = (!empty($pref[e_LAN][$plugin][$key])) ? $pref[e_LAN][$plugin][$key] : $tmp[$plugin][$key]['alias'];
3583
3584			if (!empty($rootNamespace) && $rootNamespace === $plugin)
3585			{
3586				$replaceAlias = array('{alias}\/', '{alias}/');
3587				$tmp[$plugin][$key]['sef'] = str_replace($replaceAlias, '', $tmp[$plugin][$key]['sef']);
3588			}
3589			else
3590			{
3591				$tmp[$plugin][$key]['sef'] = str_replace('{alias}', $alias, $tmp[$plugin][$key]['sef']);
3592			}
3593
3594		}
3595
3596
3597		preg_match_all('#{([a-z_]*)}#', $tmp[$plugin][$key]['sef'], $matches);
3598
3599		$active = true;
3600
3601		foreach ($matches[1] as $k => $v) // check if a field value is missing, if so, revert to legacy url.
3602		{
3603			if (!isset($row[$v]))
3604			{
3605				self::getMessage()->addDebug("Missing value for " . $v . " in " . $plugin . "/e_url.php - '" . $key . "'");
3606				$active = false;
3607				break;
3608			}
3609		}
3610
3611		if (empty($sefActive[$plugin])) // SEF disabled.
3612		{
3613			self::getDebug()->log('SEF URL for <b>' . $plugin . '</b> disabled.');
3614			$active = false;
3615		}
3616
3617		$e_MOD_REWRITE = (self::isCli() !==true) ? deftrue('e_MOD_REWRITE') : true;
3618
3619		if ($e_MOD_REWRITE && ($active == true) && empty($options['legacy']))  // Search-Engine-Friendly URLs active.
3620		{
3621			$rawUrl = $tp->simpleParse($tmp[$plugin][$key]['sef'], $row);
3622
3623			if ($options['mode'] === 'full')
3624			{
3625				$sefUrl = SITEURL . $rawUrl;
3626			}
3627			elseif ($options['mode'] === 'raw')
3628			{
3629				$sefUrl = $rawUrl;
3630			}
3631			else
3632			{
3633				$sefUrl = e_HTTP . $rawUrl;
3634			}
3635		}
3636		else // Legacy URL.
3637		{
3638
3639			$srch = array();
3640			$repl = array();
3641
3642			foreach ($matches[0] as $k => $val)
3643			{
3644				$srch[] = '$' . ($k + 1);
3645				$repl[] = $val;
3646			}
3647
3648			$template = isset($tmp[$plugin][$key]['legacy']) ? $tmp[$plugin][$key]['legacy'] : $tmp[$plugin][$key]['redirect'];
3649
3650			$urlTemplate = str_replace($srch, $repl, $template);
3651			$urlTemplate = $tp->replaceConstants($urlTemplate, $options['mode']);
3652			$legacyUrl = $tp->simpleParse($urlTemplate, $row);
3653
3654			$legacyUrl = preg_replace('/&?\$[\d]/i', "", $legacyUrl); // remove any left-over $x (including prefix of '&')
3655
3656
3657			// Avoid duplicate query keys. eg. URL has ?id=x and $options['query']['id'] exists.
3658			// @see forum/e_url.php - topic/redirect and forum/view_shortcodes.php sc_post_url()
3659			list($legacyUrl, $tmp) = array_pad(explode("?", $legacyUrl), 2, null);
3660
3661			if (!empty($tmp))
3662			{
3663				if (strpos($tmp, '=') === false)
3664				{
3665					// required for legacy urls of type "request.php?download.43"
3666					// @see: issue #3275
3667					$legacyUrl .= '?' . $tmp;
3668					$options['query'] = null;
3669				}
3670				else
3671				{
3672
3673					parse_str($tmp, $qry);
3674
3675					foreach ($qry as $k => $v)
3676					{
3677						if (!isset($options['query'][$k])) // $options['query'] overrides any in the original URL.
3678						{
3679							$options['query'][$k] = $v;
3680						}
3681					}
3682
3683				}
3684			}
3685			$sefUrl = $legacyUrl;
3686		}
3687
3688		// Append the query.
3689		if (is_array($options['query']) && !empty($options['query']))
3690		{
3691			$sefUrl .= (strpos($sefUrl, '?') !== FALSE ? '&' : '?') . self::httpBuildQuery($options['query']);
3692		}
3693
3694		return htmlspecialchars($sefUrl . $options['fragment'], ENT_QUOTES, 'UTF-8');
3695	}
3696
3697
3698	/**
3699	 * Simple redirect method for developers.
3700	 *
3701	 * @param string $url
3702	 *  'admin' to redirect to admin entry page or leave blank to go to home page
3703	 *  (SITEURL).
3704	 * @param int $http_response_code
3705	 *  The HTTP status code to use for the redirection, defaults to 302.
3706	 *  The valid values for 3xx redirection status codes are defined in RFC 2616
3707	 *  and the draft for the new HTTP status codes:
3708	 *  - 301: Moved Permanently (the recommended value for most redirects).
3709	 *  - 302: Found (default in PHP, sometimes used for spamming search engines).
3710	 *  - 303: See Other.
3711	 *  - 304: Not Modified.
3712	 *  - 305: Use Proxy.
3713	 *  - 307: Temporary Redirect.
3714	 * @see https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3
3715	 * @see https://tools.ietf.org/html/draft-reschke-http-status-308-07
3716	 */
3717	public static function redirect($url = '', $http_response_code = 301)
3718	{
3719		self::getRedirect()->go($url, true, $http_response_code);
3720	}
3721
3722
3723	/**
3724	 * Retrieve error page handler.
3725	 *
3726	 * @return error_page
3727	 */
3728	public static function getError()
3729	{
3730		return self::getSingleton('error_page', true);
3731	}
3732
3733
3734	/**
3735	 * Parses an array into a valid, rawurlencoded query string. This differs from http_build_query() as we need to
3736	 * rawurlencode() (instead of urlencode()) all query parameters.
3737	 * @param array $query The query parameter array to be processed, e.g. $_GET.
3738	 * @param string $parent Internal use only. Used to build the $query array key for nested items.
3739	 * @return string A rawurlencoded string which can be used as or appended to the URL query string.
3740	 */
3741	public static function httpBuildQuery(array $query, $parent = '')
3742	{
3743		$params = array();
3744
3745		foreach($query as $key => $value)
3746		{
3747			$key = ($parent ? $parent . '[' . rawurlencode($key) . ']' : rawurlencode($key));
3748
3749			// Recurse into children.
3750			if(is_array($value))
3751			{
3752				$params [] = self::httpBuildQuery($value, $key);
3753			}
3754			// If a query parameter value is NULL, only append its key.
3755			elseif(!isset($value))
3756			{
3757				$params [] = $key;
3758			}
3759			else
3760			{
3761				// For better readability of paths in query strings, we decode slashes.
3762				$params [] = $key . '=' . str_replace('%2F', '/', rawurlencode($value));
3763			}
3764		}
3765
3766		return implode('&', $params);
3767	}
3768
3769
3770	public static function minify($js,$options=array())
3771	{
3772		if(empty($js))
3773		{
3774			return null;
3775		}
3776
3777	//	require_once(e_HANDLER."jsshrink/Minifier.php");
3778		try
3779		{
3780			$minified = JShrink\Minifier::minify($js,$options);
3781		}
3782		catch(Exception $e)
3783		{
3784			$minified = $js;
3785		}
3786
3787		return $minified;
3788	}
3789
3790
3791
3792	/**
3793	 * Set or Retrieve WYSIWYG active status. (replaces constant  e_WYSIWYG)
3794	 *
3795	 * @param bool/string $val if null, return current value, otherwise define editor to use
3796	 * @param bool $returnEditor true = return name of active editor, false = return "false" for non wysiwyg editor, return "true" if wysiwyg editor should be used
3797	 * @return bool|mixed
3798	 */
3799	public static function wysiwyg($val=null, $returnEditor=false)
3800	{
3801		static $editor = 'bbcode';
3802		static $availEditors;
3803		$fallbackEditor = 'bbcode';
3804
3805		if (self::getPref('wysiwyg',false) != true)
3806		{
3807			// wysiwyg disabled by global pref
3808			$editor = $fallbackEditor;
3809		}
3810		else
3811		{
3812			if(!isset($availEditors))
3813			{
3814				// init list of installed wysiwyg editors
3815				$availEditors = array_keys(e107::getPlug()->getInstalledWysiwygEditors());
3816			}
3817
3818			if(!is_null($val))
3819			{
3820				// set editor if value given
3821				$editor = empty($val) ? $fallbackEditor : ($val === 'default' ? true : $val);
3822			}
3823
3824
3825			// check if choosen editor is installed,
3826			// if not, but a different editor is available use that one (e.g. tinymce4 choosen, but only simplemde available available, use simplemde)
3827			// if no wysiwyg editor available, use fallback editor (bbcode)
3828			if(is_bool($editor) || ($editor !== $fallbackEditor && !in_array($editor, $availEditors)))
3829			{
3830				$editor = count($availEditors) > 0 ? $availEditors[0] : $fallbackEditor;
3831			}
3832		}
3833		// $returnEditor => false:
3834		// false => fallback editor (bbcode)
3835		// true => default wysiwyg editor
3836		// $returnEditor => true:
3837		// return name of the editor
3838		//return $returnEditor ? $editor : ($editor === $fallbackEditor || $editor === false ? false : true);
3839		return $returnEditor ? $editor : ($editor !== $fallbackEditor);
3840	}
3841
3842
3843	/**
3844	 * Routine looks in standard paths for language files associated with a plugin or
3845	 * theme - primarily for core routines, which won't know for sure where the author has put them.
3846	 * $unitName is the name (directory path) of the plugin or theme
3847	 * $type determines what is to be loaded:
3848	 * - 'runtime' - the standard runtime language file for a plugin
3849	 * - 'admin' - the standard admin language file for a plugin
3850	 * - 'theme' - the standard language file for a plugin (these are usually pretty small, so one is enough)
3851	 * Otherwise, $type is treated as part of a filename within the plugin's language directory,
3852	 * prefixed with the current language.
3853	 * Returns FALSE on failure (not found).
3854	 * Returns the include_once error return if there is one
3855	 * Otherwise returns an empty string.
3856	 * Note - if the code knows precisely where the language file is located, use {@link getLan()}
3857	 * $pref['noLanguageSubs'] can be set TRUE to prevent searching for the English files if
3858	 * the files for the current site language don't exist.
3859	 *
3860	 * @param string $unitName
3861	 * @param string $type predefined types are runtime|admin|theme
3862	 * @return boolean|string
3863	 */
3864	public static function loadLanFiles($unitName, $type='runtime')
3865	{
3866		//global $pref;
3867		switch ($type)
3868		{
3869			case 'runtime' :
3870				$searchPath[1] = e_PLUGIN.$unitName.'/languages/'.e_LANGUAGE.'_'.$unitName.'.php';
3871				$searchPath[2] = e_PLUGIN.$unitName.'/languages/'.e_LANGUAGE.'/'.$unitName.'.php';
3872				$searchPath[3] = e_PLUGIN.$unitName.'/languages/'.e_LANGUAGE.'.php'; // menu language file.
3873				break;
3874			case 'admin' :
3875
3876				$aLangPref = self::getPref('adminlanguage');
3877				$adminLan = vartrue($aLangPref, e_LANGUAGE);
3878
3879				$searchPath[1] = e_PLUGIN.$unitName.'/languages/'.$adminLan.'_admin_'.$unitName.'.php';
3880				$searchPath[2] = e_PLUGIN.$unitName.'/languages/'.$adminLan.'/'.'admin_'.$unitName.'.php';
3881				$searchPath[3] = e_PLUGIN.$unitName.'/languages/'.$adminLan.'/admin/'.$adminLan.'.php';
3882				$searchPath[4] = e_PLUGIN.$unitName.'/languages/'.$adminLan.'/'.$adminLan.'_admin.php'; // Preferred.
3883				$searchPath[5] = e_PLUGIN.$unitName.'/languages/'.$adminLan.'_admin.php'; // consistent with English_global.php, English_log.php etc.
3884
3885				break;
3886			case 'theme' :
3887				$searchPath[1] = e_THEME.$unitName.'/languages/'.e_LANGUAGE.'_'.$unitName.'.php';
3888				$searchPath[2] = e_THEME.$unitName.'/languages/'.e_LANGUAGE.'/'.$unitName.'.php';
3889				break;
3890			default :
3891				$searchPath[1] = e_PLUGIN.$unitName.'/languages/'.e_LANGUAGE.'_'.$type.'.php';
3892				$searchPath[2] = e_PLUGIN.$unitName.'/languages/'.e_LANGUAGE.'/'.$type.'.php';
3893		}
3894		foreach ($searchPath as $s)			// Look for files in current language first - should usually be found
3895		{
3896			if (is_readable($s))
3897			{
3898				$ret = include_once($s);
3899				return (isset($ret)) ? $ret : "";
3900			}
3901		}
3902		if (self::getPref('noLanguageSubs') || (e_LANGUAGE === 'English'))
3903		{
3904			return FALSE;		// No point looking for the English files twice
3905		}
3906
3907		foreach ($searchPath as $s)			// Now look for the English files
3908		{
3909			$s = str_replace(e_LANGUAGE, 'English', $s);
3910			if (is_readable($s))
3911			{
3912				$ret = include_once($s);
3913				return (isset($ret)) ? $ret : "";
3914			}
3915		}
3916		return FALSE;		// Nothing found
3917	}
3918
3919
3920	/**
3921	 * Prepare e107 environment
3922	 * This is done before e107_dirs initilization and config include
3923	 * @param bool $checkS basic security check (0.7 like), will be extended in the future
3924	 * @return e107
3925	 */
3926	public function prepare_request($checkS = true)
3927	{
3928
3929		// Block common bad agents / queries / php issues.
3930		array_walk($_SERVER,  array('self', 'filter_request'), '_SERVER');
3931		if (isset($_GET)) array_walk($_GET,     array('self', 'filter_request'), '_GET');
3932		if (isset($_POST))
3933		{
3934			array_walk($_POST,    array('self', 'filter_request'), '_POST');
3935			reset($_POST);		// Change of behaviour in PHP 5.3.17?
3936		}
3937		if (isset($_COOKIE)) array_walk($_COOKIE,  array('self', 'filter_request'), '_COOKIE');
3938		if (isset($_REQUEST)) array_walk($_REQUEST, array('self', 'filter_request'), '_REQUEST');
3939
3940		// A better way to detect an AJAX request. No need for "ajax_used=1";
3941		if(!empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest')
3942		{
3943  			define('e_AJAX_REQUEST', true);
3944		}
3945		else
3946		{
3947			define('e_AJAX_REQUEST', isset($_REQUEST['ajax_used']));
3948		}
3949
3950		unset($_REQUEST['ajax_used']); // removed because it's auto-appended from JS (AJAX), could break something...
3951
3952		//$GLOBALS['_E107'] - minimal mode - here because of the e_AJAX_REQUEST
3953		if(isset($GLOBALS['_E107']['minimal']) || e_AJAX_REQUEST || deftrue('e_MINIMAL'))
3954		{
3955			$_e107vars = array('forceuserupdate', 'online', 'theme', 'menus', 'prunetmp');
3956			$GLOBALS['_E107']['minimal'] = true;
3957			// lame but quick - allow online when ajax request only, additonal checks are made in e_online class
3958			if(e_AJAX_REQUEST && !isset($GLOBALS['_E107']['online']) && !isset($GLOBALS['_E107']['minimal'])) unset($_e107vars[1]);
3959
3960			foreach($_e107vars as $v)
3961			{
3962				$noname = 'no_'.$v;
3963				if(!isset($GLOBALS['_E107'][$v]))
3964				{
3965					$GLOBALS['_E107'][$noname] = 1;
3966				}
3967				unset($GLOBALS['_E107'][$v]);
3968			}
3969		}
3970
3971		// we can now start use $e107->_E107
3972		if(isset($GLOBALS['_E107']) && is_array($GLOBALS['_E107'])) $this->_E107 = & $GLOBALS['_E107'];
3973
3974		// remove ajax_used=1 from query string to avoid SELF problems, ajax should always be detected via e_AJAX_REQUEST constant
3975		$_SERVER['QUERY_STRING'] = trim(
3976			str_replace(
3977				array('ajax_used=1', '&&'),
3978				array('', '&'),
3979				(isset($_SERVER['QUERY_STRING']) ? $_SERVER['QUERY_STRING'] : '')
3980			), '&');
3981
3982		/* PathInfo doesn't break anything, URLs should be always absolute. Disabling the below forever.
3983		// e107 uses relative url's, which are broken by "pretty" URL's. So for now we don't support / after .php
3984		if(($pos = strpos($_SERVER['PHP_SELF'], '.php/')) !== false) // redirect bad URLs to the correct one.
3985		{
3986			$new_url = substr($_SERVER['PHP_SELF'], 0, $pos+4);
3987			$new_loc = ($_SERVER['QUERY_STRING']) ? $new_url.'?'.$_SERVER['QUERY_STRING'] : $new_url;
3988			header('Location: '.$new_loc);
3989			exit();
3990		}
3991		*/
3992
3993		// If url contains a .php in it, PHP_SELF is set wrong (imho), affecting all paths.  We need to 'fix' it if it does.
3994		$_SERVER['PHP_SELF'] = (($pos = stripos($_SERVER['PHP_SELF'], '.php')) !== false ? substr($_SERVER['PHP_SELF'], 0, $pos+4) : $_SERVER['PHP_SELF']);
3995
3996		// setup some php options
3997		self::ini_set('magic_quotes_runtime',     0);
3998		self::ini_set('magic_quotes_sybase',      0);
3999	//	self::ini_set('arg_separator.output',     '&amp;'); // non-standard and bad for third-party script compatibility. @see https://github.com/e107inc/e107/issues/3116
4000		self::ini_set('session.use_only_cookies', 1);
4001		self::ini_set('session.use_trans_sid',    0);
4002		self::ini_set('session.cookie_httponly',  1); // cookie won't be accessible by scripting languages, such as JavaScript. Can effectively help to reduce identity theft through XSS attacks
4003
4004		//  Ensure thet '.' is the first part of the include path
4005		$inc_path = explode(PATH_SEPARATOR, ini_get('include_path'));
4006		if($inc_path[0] != '.')
4007		{
4008			array_unshift($inc_path, '.');
4009			$inc_path = implode(PATH_SEPARATOR, $inc_path);
4010			self::ini_set('include_path', $inc_path);
4011		}
4012		unset($inc_path);
4013
4014		return $this;
4015	}
4016
4017	/**
4018	 * Filter User Input - used by array_walk in prepare_request method above.
4019	 * @param string $input array value
4020	 * @param string $key array key
4021	 * @param string $type array type _SESSION, _GET etc.
4022	 * @param bool $base64
4023	 * @return bool|null
4024	 */
4025	public static function filter_request($input,$key,$type,$base64=FALSE)
4026	{
4027		if(is_string($input) && trim($input)=="")
4028		{
4029			return '';
4030		}
4031
4032		if (is_array($input))
4033		{
4034			return array_walk($input, array('self', 'filter_request'), $type);
4035		}
4036
4037
4038		if($type == "_POST" || ($type == "_SERVER" && ($key == "QUERY_STRING")))
4039		{
4040			if($type == "_POST" && ($base64 === false))
4041			{
4042				$input = preg_replace("/(\[code\])(.*?)(\[\/code\])/is","",$input);
4043			}
4044
4045			$regex = "/(base64_decode|chr|php_uname|fwrite|fopen|fputs|passthru|popen|proc_open|shell_exec|exec|proc_nice|proc_terminate|proc_get_status|proc_close|pfsockopen|apache_child_terminate|posix_kill|posix_mkfifo|posix_setpgid|posix_setsid|posix_setuid|phpinfo) *?\((.*) ?\;?/i";
4046			if(preg_match($regex,$input))
4047			{
4048				self::die_http_400();
4049			}
4050
4051			// Check for XSS JS
4052			$regex = "/(document\.location|document\.write|document\.cookie)/i";
4053			if(preg_match($regex,$input))
4054			{
4055				self::die_http_400();
4056			}
4057
4058
4059			// Suspicious HTML.
4060			if(strpos($input, '<body/onload')!==false)
4061			{
4062				self::die_http_400();
4063			}
4064
4065			if(preg_match("/system\((.*);.*\)/i",$input))
4066			{
4067				self::die_http_400();
4068			}
4069
4070			$regex = "/(wget |curl -o |lwp-download|onmouse)/i";
4071			if(preg_match($regex,$input))
4072			{
4073				self::die_http_400();
4074			}
4075
4076		}
4077
4078		if($type === '_GET') // Basic XSS check.
4079		{
4080			if(stripos($input, "<script")!==false || stripos($input, "%3Cscript")!==false)
4081			{
4082				self::die_http_400();
4083			}
4084
4085		}
4086
4087		if($type == "_SERVER")
4088		{
4089
4090			if(($key == "QUERY_STRING") && (
4091				strpos(strtolower($input),"../../")!==FALSE
4092				|| stripos($input,"php:")!==FALSE
4093				|| stripos($input,"data:")!==FALSE
4094				|| stripos($input,"%3cscript")!==FALSE
4095				))
4096			{
4097				self::die_http_400();
4098			}
4099
4100			if(($key == "HTTP_USER_AGENT") && strpos($input,"libwww-perl")!==FALSE)
4101			{
4102				self::die_http_400();
4103			}
4104
4105
4106		}
4107
4108		if(strpos(str_replace('.', '', $input), '22250738585072011') !== FALSE) // php-bug 53632
4109		{
4110			self::die_http_400();
4111		}
4112
4113		if($base64 != true)
4114		{
4115			self::filter_request(base64_decode($input, true),$key,$type,true);
4116		}
4117
4118
4119
4120	}
4121
4122
4123
4124	/**
4125	 * Set base system path
4126	 * @return e107
4127	 */
4128	public function set_base_path($force = null)
4129	{
4130		$ssl_enabled = (null !== $force) ? $force : $this->isSecure();//(self::getPref('ssl_enabled') == 1);
4131		$this->base_path = $ssl_enabled ?  $this->https_path : $this->http_path;
4132		return $this;
4133	}
4134
4135	/**
4136	 * Set various system environment constants
4137	 * @return e107
4138	 */
4139	public function set_constants()
4140	{
4141		if(!defined('MAGIC_QUOTES_GPC'))
4142		{
4143			define('MAGIC_QUOTES_GPC', (ini_get('magic_quotes_gpc') ? true : false));
4144		}
4145
4146		define('MPREFIX', self::getMySQLConfig('prefix')); // mysql prefix
4147
4148		define('CHARSET', 'utf-8'); // set CHARSET for backward compatibility
4149
4150		if(!defined('e_MOD_REWRITE')) // Allow e107_config.php to override.
4151		{
4152			define('e_MOD_REWRITE', (getenv('HTTP_MOD_REWRITE')=='On'||  getenv('REDIRECT_HTTP_MOD_REWRITE')=='On' ? true : false));
4153		}
4154
4155		if(!defined('e_MOD_REWRITE_MEDIA')) // Allow e107_config.php to override.
4156		{
4157			define('e_MOD_REWRITE_MEDIA', (getenv('HTTP_MOD_REWRITE_MEDIA')=='On' || getenv('REDIRECT_HTTP_MOD_REWRITE_MEDIA')=='On'  ? true : false));
4158		}
4159
4160		if(!defined('e_MOD_REWRITE_STATIC')) // Allow e107_config.php to override.
4161		{
4162			define('e_MOD_REWRITE_STATIC', (getenv('HTTP_MOD_REWRITE_STATIC')=='On' || getenv('REDIRECT_HTTP_MOD_REWRITE_STATIC')=='On'  ? true : false));
4163		}
4164
4165		$subdomain = false;
4166
4167		// Define the domain name and subdomain name.
4168		if (is_numeric(str_replace(".", "",
4169			(isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : '')
4170		)))
4171		{
4172			$domain = false;
4173			$subdomain = false;
4174		}
4175		else
4176		{
4177			$_SERVER['SERVER_NAME'] = isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : '';
4178			$host = !empty($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : $_SERVER['SERVER_NAME'];
4179			$domain = preg_replace('/^www\.|:\d*$/', '', $host); // remove www. and port numbers.
4180
4181			$dtemp = explode(".", $domain);
4182
4183			if(count($dtemp) > 2 && strlen($dtemp[0]) === 2) // eg. fr.mysite.com or fr.mysite.com.fr
4184			{
4185				$subdomain = $dtemp[0];
4186				unset($dtemp[0]);
4187				$domain = implode('.',$dtemp); // remove subdomain because it's a language-code.
4188			}
4189
4190		}
4191
4192		if($domain === 'localhost') // Fix for chrome.
4193		{
4194			$domain = false;
4195		}
4196
4197		define("e_DOMAIN", $domain);
4198		define("e_SUBDOMAIN", ($subdomain) ? $subdomain : false);
4199
4200		define('e_UC_PUBLIC', 0);
4201		define('e_UC_MAINADMIN', 250);
4202		define('e_UC_READONLY', 251);
4203		define('e_UC_GUEST', 252);
4204		define('e_UC_MEMBER', 253);
4205		define('e_UC_ADMIN', 254);
4206		define('e_UC_NOBODY', 255);
4207
4208
4209		return $this;
4210	}
4211
4212	/**
4213	 * Relaitve server path - set_path() helper
4214	 * @param string $dir
4215	 * @return string
4216	 */
4217	public function get_override_rel($dir)
4218	{
4219		if(isset($this->e107_dirs[$dir.'_SERVER']))
4220		{
4221			return $this->e107_dirs[$dir.'_SERVER'];
4222		}
4223		$ret = e_BASE.$this->e107_dirs[$dir.'_DIRECTORY'];
4224
4225
4226		return $ret;
4227	}
4228
4229	/**
4230	 * Absolute HTTP path - set_path() helper
4231	 * @param string $dir
4232	 * @return string
4233	 */
4234	public function get_override_http($dir)
4235	{
4236		if(isset($this->e107_dirs[$dir.'_HTTP']))
4237		{
4238			return $this->e107_dirs[$dir.'_HTTP'];
4239		}
4240
4241		return e_HTTP.$this->e107_dirs[$dir.'_DIRECTORY'];
4242	}
4243
4244	/**
4245	 * Set all environment vars and constants
4246	 * FIXME - remove globals
4247	 * @return e107
4248	 */
4249	public function set_paths()
4250	{
4251		// ssl_enabled pref not needed anymore, scheme is auto-detected
4252		$this->HTTP_SCHEME = 'http';
4253		if (
4254			(!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off') ||
4255			(!empty($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] == 443)
4256		)
4257		{
4258			$this->HTTP_SCHEME = 'https';
4259		}
4260
4261		$path = ""; $i = 0;
4262
4263		// FIXME - Again, what if someone moves handlers under the webroot?
4264		if(!self::isCli())
4265		{
4266			while (!file_exists("{$path}class2.php"))
4267			{
4268				$path .= "../";
4269				$i++;
4270			}
4271		}
4272
4273		if ($_SERVER['PHP_SELF'] == "" && !empty($_SERVER['SCRIPT_NAME']))
4274		{
4275			$_SERVER['PHP_SELF'] = $_SERVER['SCRIPT_NAME'];
4276		}
4277
4278		$http_path = dirname($_SERVER['PHP_SELF']);
4279		$http_path = explode("/", $http_path);
4280		$http_path = array_reverse($http_path);
4281		$j = 0;
4282		while ($j < $i)
4283		{
4284			unset($http_path[$j]);
4285			$j++;
4286		}
4287		$http_path = array_reverse($http_path);
4288
4289
4290
4291		$this->server_path = implode("/", $http_path)."/";
4292		$this->server_path = $this->fix_windows_paths($this->server_path);
4293
4294//var_dump($this->server_path);
4295//exit;
4296
4297		if ($this->server_path == "//")
4298		{
4299			$this->server_path = "/";
4300		}
4301
4302		// Absolute file-path of directory containing class2.php
4303		//	define("e_ROOT", realpath(dirname(__FILE__)."/../")."/");
4304
4305
4306
4307
4308		$this->relative_base_path = (!self::isCli()) ? $path : e_ROOT;
4309		$_SERVER['HTTP_HOST'] = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'localhost';
4310		$this->http_path =  filter_var("http://{$_SERVER['HTTP_HOST']}{$this->server_path}", FILTER_SANITIZE_URL);
4311		$this->https_path = filter_var("https://{$_SERVER['HTTP_HOST']}{$this->server_path}", FILTER_SANITIZE_URL);
4312
4313		$this->file_path = $path;
4314
4315		if(defined('e_HTTP') && defined('e_ADMIN')) return $this;
4316
4317		if(!defined('e_HTTP'))
4318		{
4319			define('e_HTTP', $this->server_path);			// Directory of site root relative to HTML base directory
4320		}
4321
4322	  	define('e_BASE', $this->relative_base_path);
4323
4324		// Base dir of web stuff in server terms. e_ROOT should always end with e_HTTP, even if e_HTTP = '/'
4325		define('SERVERBASE', substr(e_ROOT, 0, -strlen(e_HTTP) + 1));
4326
4327		if(isset($_SERVER['DOCUMENT_ROOT']))
4328		{
4329		  	define('e_DOCROOT', $_SERVER['DOCUMENT_ROOT']."/");
4330		}
4331		else
4332		{
4333		  	define('e_DOCROOT', false);
4334		}
4335
4336		//BC temporary fixes
4337		if (!isset($this->e107_dirs['UPLOADS_SERVER']) && $this->e107_dirs['UPLOADS_DIRECTORY'][0] == "/")
4338		{
4339			$this->e107_dirs['UPLOADS_SERVER'] = $this->e107_dirs['UPLOADS_DIRECTORY'];
4340		}
4341		if (!isset($this->e107_dirs['DOWNLOADS_SERVER']) && $this->e107_dirs['DOWNLOADS_DIRECTORY'][0] == "/")
4342		{
4343			$this->e107_dirs['DOWNLOADS_SERVER'] = $this->e107_dirs['DOWNLOADS_DIRECTORY'];
4344		}
4345
4346		//
4347		// HTTP relative paths
4348		//
4349		define('e_ADMIN', $this->get_override_rel('ADMIN'));
4350		define('e_IMAGE', $this->get_override_rel('IMAGES'));
4351		define('e_THEME', $this->get_override_rel('THEMES'));
4352		define('e_PLUGIN', $this->get_override_rel('PLUGINS'));
4353		define('e_FILE', $this->get_override_rel('FILES'));
4354		define('e_HANDLER', $this->get_override_rel('HANDLERS'));
4355		define('e_LANGUAGEDIR', $this->get_override_rel('LANGUAGES'));
4356
4357		define('e_DOCS', $this->get_override_rel('HELP')); // WILL CHANGE SOON - $this->_get_override_rel('DOCS')
4358		define('e_HELP', $this->get_override_rel('HELP'));
4359
4360		define('e_MEDIA', $this->get_override_rel('MEDIA'));
4361		define('e_MEDIA_BASE', $this->get_override_rel('MEDIA_BASE'));
4362		define('e_MEDIA_FILE', $this->get_override_rel('MEDIA_FILES'));
4363		define('e_MEDIA_VIDEO', $this->get_override_rel('MEDIA_VIDEOS'));
4364		define('e_MEDIA_IMAGE', $this->get_override_rel('MEDIA_IMAGES'));
4365		define('e_MEDIA_ICON', $this->get_override_rel('MEDIA_ICONS'));
4366	//	define('e_MEDIA_AVATAR', $this->get_override_rel('MEDIA_AVATARS'));
4367
4368		define('e_DOWNLOAD', $this->get_override_rel('DOWNLOADS'));
4369		define('e_UPLOAD', $this->get_override_rel('UPLOADS'));
4370
4371		define('e_CORE', $this->get_override_rel('CORE'));
4372		define('e_SYSTEM', $this->get_override_rel('SYSTEM'));
4373		define('e_SYSTEM_BASE', $this->get_override_rel('SYSTEM_BASE'));
4374
4375		define('e_WEB', $this->get_override_rel('WEB'));
4376		define('e_WEB_JS', $this->get_override_rel('WEB_JS'));
4377		define('e_WEB_CSS', $this->get_override_rel('WEB_CSS'));
4378		define('e_WEB_IMAGE', $this->get_override_rel('WEB_IMAGES'));
4379//		define('e_WEB_PACK', $this->get_override_rel('WEB_PACKS'));
4380
4381		define('e_CACHE', $this->get_override_rel('CACHE'));
4382		define('e_CACHE_CONTENT', $this->get_override_rel('CACHE_CONTENT'));
4383		define('e_CACHE_IMAGE', $this->get_override_rel('CACHE_IMAGE'));
4384		define('e_CACHE_DB', $this->get_override_rel('CACHE_DB'));
4385		define('e_CACHE_URL', $this->get_override_rel('CACHE_URL'));
4386
4387		define('e_LOG', $this->get_override_rel('LOGS'));
4388		define('e_BACKUP', $this->get_override_rel('BACKUP'));
4389		define('e_TEMP', $this->get_override_rel('TEMP'));
4390		define('e_IMPORT', $this->get_override_rel('IMPORT'));
4391		//
4392		// HTTP absolute paths
4393		//
4394		define("e_ADMIN_ABS", $this->get_override_http('ADMIN'));
4395		define("e_IMAGE_ABS", $this->get_override_http('IMAGES'));
4396		define("e_THEME_ABS", $this->get_override_http('THEMES'));
4397		define("e_PLUGIN_ABS", $this->get_override_http('PLUGINS'));
4398		define("e_FILE_ABS", $this->get_override_http('FILES')); // Deprecated!
4399		define("e_DOCS_ABS", $this->get_override_http('DOCS'));
4400		define("e_HELP_ABS", $this->get_override_http('HELP'));
4401		define("e_IMPORT_ABS", false);
4402
4403		// DEPRECATED - not a legal http query now!
4404		//define("e_HANDLER_ABS", $this->get_override_http('HANDLERS'));
4405		//define("e_LANGUAGEDIR_ABS", $this->get_override_http('LANGUAGES'));
4406		//define("e_LOG_ABS", $this->get_override_http('LOGS'));
4407
4408		define("e_MEDIA_ABS", $this->get_override_http('MEDIA'));
4409		define('e_MEDIA_FILE_ABS', $this->get_override_http('MEDIA_FILES'));
4410		define('e_MEDIA_VIDEO_ABS', $this->get_override_http('MEDIA_VIDEOS'));
4411		define('e_MEDIA_IMAGE_ABS', $this->get_override_http('MEDIA_IMAGES'));
4412		define('e_MEDIA_ICON_ABS', $this->get_override_http('MEDIA_ICONS'));
4413//		define('e_MEDIA_AVATAR_ABS', $this->get_override_http('MEDIA_AVATARS'));
4414
4415		// XXX DISCUSSS - e_JS_ABS, e_CSS_ABS etc is not following the naming standards but they're more usable.
4416		// Example: e_JS_ABS vs e_WEB_JS_ABS
4417
4418		//XXX Absolute is assumed.
4419		define('e_WEB_ABS', $this->get_override_http('WEB'));
4420		define('e_JS_ABS', $this->get_override_http('WEB_JS'));
4421		define('e_CSS_ABS', $this->get_override_http('WEB_CSS'));
4422//		define('e_PACK_ABS', $this->get_override_http('WEB_PACKS'));
4423		define('e_WEB_IMAGE_ABS', $this->get_override_http('WEB_IMAGES'));
4424
4425		define('e_JS', $this->get_override_http('WEB_JS')); // ABS Alias
4426		define('e_CSS', $this->get_override_http('WEB_CSS')); // ABS Alias
4427
4428		define('e_AVATAR', $this->get_override_rel('AVATARS'));
4429		define('e_AVATAR_UPLOAD', $this->get_override_rel('AVATARS_UPLOAD'));
4430		define('e_AVATAR_DEFAULT', $this->get_override_rel('AVATARS_DEFAULT'));
4431
4432		define('e_AVATAR_ABS', $this->get_override_http('AVATARS'));
4433		define('e_AVATAR_UPLOAD_ABS', $this->get_override_http('AVATARS_UPLOAD'));
4434		define('e_AVATAR_DEFAULT_ABS', $this->get_override_http('AVATARS_DEFAULT'));
4435
4436		if(defined('e_MEDIA_STATIC')) // experimental - subject to change.
4437		{
4438			define('e_CACHE_IMAGE_ABS', $this->get_override_http('CACHE_IMAGE'));
4439		}
4440
4441		// Special
4442
4443		define('e_BOOTSTRAP', e_WEB."bootstrap/");
4444
4445		return $this;
4446	}
4447
4448	/**
4449	 * Fix Windows server path
4450	 *
4451	 * @param string $path resolved server path
4452	 * @return string fixed path
4453	 */
4454	function fix_windows_paths($path)
4455	{
4456		$fixed_path = str_replace(array('\\\\', '\\'), array('/', '/'), $path);
4457		$fixed_path = (substr($fixed_path, 1, 2) == ":/" ? substr($fixed_path, 2) : $fixed_path);
4458		return $fixed_path;
4459	}
4460
4461	/**
4462	 * Define e_PAGE, e_SELF, e_ADMIN_AREA and USER_AREA;
4463	 * The following files are assumed to use admin theme:
4464	 * 1. Any file in the admin directory (check for non-plugin added to avoid mismatches)
4465	 * 2. any plugin file starting with 'admin_'
4466	 * 3. any plugin file in a folder called admin/
4467	 * 4. any file that specifies $eplug_admin = TRUE; or ADMIN_AREA = TRUE;
4468	 * NOTE: USER_AREA = true; will force e_ADMIN_AREA to FALSE
4469	 *
4470	 * @param boolean $no_cbrace remove curly brackets from the url
4471	 * @return e107
4472	 */
4473	public function set_urls($no_cbrace = true)
4474	{
4475		//global $PLUGINS_DIRECTORY,$ADMIN_DIRECTORY, $eplug_admin;
4476		$PLUGINS_DIRECTORY = $this->getFolder('plugins');
4477		$ADMIN_DIRECTORY = $this->getFolder('admin');
4478
4479		// Outdated
4480		/*$requestQry = '';
4481		$requestUrl = $_SERVER['REQUEST_URI'];
4482		if(strpos($_SERVER['REQUEST_URI'], '?') !== FALSE)
4483			list($requestUrl, $requestQry) = explode("?", $_SERVER['REQUEST_URI'], 2); */
4484
4485		$eplug_admin = vartrue($GLOBALS['eplug_admin'], false);
4486
4487		// Leave e_SELF BC, use e_REQUEST_SELF instead
4488		/*// moved after page check - e_PAGE is important for BC
4489		if($requestUrl && $requestUrl != $_SERVER['PHP_SELF'])
4490		{
4491			$_SERVER['PHP_SELF'] = $requestUrl;
4492		}*/
4493
4494		if(self::isCli() && !empty($_SERVER['_']) && empty($_SERVER['SCRIPT_FILENAME']))
4495		{
4496			$_SERVER['SCRIPT_FILENAME'] = $_SERVER['_'];
4497		}
4498
4499		$eSelf = !empty($_SERVER['PHP_SELF']) ? $_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_FILENAME'];
4500		$_self = $this->HTTP_SCHEME.'://'.$_SERVER['HTTP_HOST'].$eSelf;
4501
4502
4503
4504		// START New - request uri/url detection, XSS protection
4505		// TODO - move it to a separate method
4506		$requestUri = $requestUrl = '';
4507		if (isset($_SERVER['HTTP_X_REWRITE_URL']))
4508		{
4509			// check this first so IIS will catch
4510			$requestUri = $_SERVER['HTTP_X_REWRITE_URL'];
4511			$requestUrl = $this->HTTP_SCHEME.'://'.$_SERVER['HTTP_HOST'].$requestUri;
4512			// fix request uri
4513			$_SERVER['REQUEST_URI'] = $requestUri;
4514		}
4515		elseif (isset($_SERVER['REQUEST_URI']))
4516		{
4517			$requestUri = $_SERVER['REQUEST_URI'];
4518			$requestUrl = $this->HTTP_SCHEME.'://'.$_SERVER['HTTP_HOST'].$requestUri;
4519		}
4520		else
4521		{
4522			// go back to e_SELF
4523			$requestUri = $eSelf;
4524			$requestUrl = $_self;
4525            if(defset('e_QUERY'))
4526			{
4527				$requestUri .= '?'.e_QUERY; // TODO e_SINGLE_ENTRY check, separate static method for cleaning QUERY_STRING
4528				$requestUrl .= '?'.e_QUERY;
4529			}
4530		}
4531		// FIXME - basic security - add url sanitize method to e_parse
4532		$check = rawurldecode($requestUri); // urlencoded by default
4533
4534		// a bit aggressive XSS protection... convert to e.g. htmlentities if you are not a bad guy
4535		$checkregx = $no_cbrace ? '[<>\{\}]' : '[<>]';
4536		if(preg_match('/'.$checkregx.'/', $check))
4537		{
4538			// header('HTTP/1.1 403 Forbidden');
4539			$requestUri = filter_var($requestUri, FILTER_SANITIZE_URL);
4540			// exit;
4541		}
4542
4543		// e_MENU fix
4544		if(e_MENU)
4545		{
4546			$requestUri = str_replace('['.e_MENU.']', '', $requestUri);
4547			$requestUrl = str_replace('['.e_MENU.']', '', $requestUrl);
4548			if(defset('e_QUERY')) parse_str(e_QUERY,$_GET);
4549		}
4550
4551		define('e_REQUEST_URL', str_replace(array("'", '"'), array('%27', '%22'), $requestUrl)); // full request url string (including domain)
4552
4553		$tmp = explode('?', e_REQUEST_URL);
4554		$requestSelf =  array_shift($tmp);
4555
4556		if(substr($requestSelf,-4) !== '.php' && substr($requestSelf,-1) !== '/')
4557		{
4558			$requestSelf .= '/'; // Always include a trailing slash on SEF Urls so that e_REQUEST_SELF."?".e_QUERY doesn't break.
4559		}
4560
4561		// the last anti-XSS measure, XHTML compliant URL to be used in forms instead e_SELF
4562
4563		define('e_REQUEST_SELF', filter_var($requestSelf, FILTER_SANITIZE_URL)); // full URL without the QUERY string
4564		define('e_REQUEST_URI', str_replace(array("'", '"'), array('%27', '%22'), $requestUri)); // absolute http path + query string
4565		$tmp2 = explode('?', e_REQUEST_URI);
4566		define('e_REQUEST_HTTP', array_shift($tmp2)); // SELF URL without the QUERY string and leading domain part
4567
4568		if(!deftrue('e_SINGLE_ENTRY') && !deftrue('e_SELF_OVERRIDE') )
4569		{
4570			$page = substr(strrchr($_SERVER['PHP_SELF'], '/'), 1);
4571
4572			if(self::isCli() && !empty($_SERVER['_']))
4573			{
4574				$page = basename($_SERVER['_']);
4575			}
4576
4577
4578			define('e_PAGE', $page);
4579			define('e_SELF', filter_var($_self, FILTER_SANITIZE_URL));
4580		}
4581		else
4582		{
4583			define('e_SELF', e_REQUEST_SELF);
4584
4585			if(deftrue('e_SELF_OVERRIDE')) // see multisite plugin.
4586			{
4587				define('e_PAGE', basename($_SERVER['SCRIPT_FILENAME']));
4588			}
4589		}
4590
4591
4592
4593		unset($requestUrl, $requestUri);
4594		// END request uri/url detection, XSS protection
4595
4596		// e_SELF has the full HTML path
4597		$inAdminDir = FALSE;
4598		$isPluginDir = strpos($_self,'/'.$PLUGINS_DIRECTORY) !== FALSE;		// True if we're in a plugin
4599		$e107Path = str_replace($this->base_path, '', $_self);				// Knock off the initial bits
4600		$curPage = !empty($_SERVER['SCRIPT_FILENAME']) ? basename($_SERVER['SCRIPT_FILENAME']) : '';
4601		$_SERVER['REQUEST_URI'] = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '';
4602
4603		if	(
4604			 (!$isPluginDir && strpos($e107Path, $ADMIN_DIRECTORY) === 0 ) 									// Core admin directory
4605			  || ($isPluginDir && (strpos($curPage,'_admin.php') !== false || strpos($curPage,'admin_') === 0 || strpos($e107Path, 'admin/') !== FALSE)) // Plugin admin file or directory
4606			  || (vartrue($eplug_admin) || deftrue('ADMIN_AREA'))		// Admin forced
4607			  || (preg_match('/^\/(.*?)\/user(settings\.php|\/edit)(\?|\/)(\d+)$/i', $_SERVER['REQUEST_URI']) && ADMIN)
4608			  || ($isPluginDir && $curPage === 'prefs.php') //BC Fix for old plugins
4609			  || ($isPluginDir && $curPage === 'config.php') // BC Fix for old plugins
4610			  || ($isPluginDir && strpos($curPage,'_config.php')!==false) // BC Fix for old plugins eg. dtree_menu
4611			)
4612		{
4613			$inAdminDir = TRUE;
4614		}
4615		if ($isPluginDir)
4616		{
4617			$temp = substr($e107Path, strpos($e107Path, '/') +1);
4618			$plugDir = substr($temp, 0, strpos($temp, '/'));
4619			define('e_CURRENT_PLUGIN', rtrim($plugDir,'/'));
4620			define('e_PLUGIN_DIR', e_PLUGIN.e_CURRENT_PLUGIN.'/');
4621			define('e_PLUGIN_DIR_ABS', e_PLUGIN_ABS.e_CURRENT_PLUGIN.'/');
4622		}
4623		else
4624		{
4625		//	define('e_CURRENT_PLUGIN', ''); // leave it undefined so it can be added later during sef-url detection.
4626			define('e_PLUGIN_DIR', '');
4627			define('e_PLUGIN_DIR_ABS', '');
4628		}
4629
4630
4631		if(!defined('e_ADMIN_AREA'))
4632		{
4633			define('e_ADMIN_AREA', ($inAdminDir  && !deftrue('USER_AREA')));
4634		}
4635
4636		define('ADMINDIR', $ADMIN_DIRECTORY);
4637
4638		return $this;
4639	}
4640
4641	/**
4642	 * The second part of e107::set_urls()
4643	 * Supposed to load after database has been initialized
4644	 *
4645	 * Implemented out of necessity due to
4646	 * https://github.com/e107inc/e107/issues/3033
4647	 *
4648	 * @return e107
4649	 */
4650	public function set_urls_deferred()
4651	{
4652		if(self::isCli())
4653		{
4654			define('SITEURL', self::getPref('siteurl'));
4655			define('SITEURLBASE', rtrim(SITEURL,'/'));
4656		}
4657		else
4658		{
4659			define('SITEURLBASE', $this->HTTP_SCHEME.'://'. filter_var($_SERVER['HTTP_HOST'], FILTER_SANITIZE_URL));
4660			define('SITEURL', SITEURLBASE.e_HTTP);
4661		}
4662
4663
4664		// login/signup
4665		define('e_SIGNUP', SITEURL.(file_exists(e_BASE.'customsignup.php') ? 'customsignup.php' : 'signup.php'));
4666
4667		if(!defined('e_LOGIN'))
4668		{
4669			define('e_LOGIN', SITEURL.(file_exists(e_BASE.'customlogin.php') ? 'customlogin.php' : 'login.php'));
4670		}
4671
4672		return $this;
4673	}
4674
4675	/**
4676	 * Set request related constants
4677	 * @param boolean $no_cbrace remove curly brackets from the url
4678	 * @return e107
4679	 */
4680	public function set_request($no_cbrace = true)
4681	{
4682
4683		$inArray = array("'", '/**/', '/UNION/', '/SELECT/', 'AS ');
4684		if (strpos($_SERVER['PHP_SELF'], 'trackback') === false)
4685		{
4686			foreach($inArray as $res)
4687			{
4688				if(stristr($_SERVER['QUERY_STRING'], $res))
4689				 {
4690					die('Access denied.');
4691				}
4692			}
4693		}
4694
4695		$eMENUQry = str_replace(array('%5B','%5D'),array('[',']'),$_SERVER['QUERY_STRING']); //FIX for urlencoded QUERY_STRING without breaking the '+' used by debug.
4696		if (strpos($eMENUQry, ']') && preg_match('#\[(.*?)](.*)#', $eMENUQry, $matches))
4697		{
4698			define('e_MENU', $matches[1]);
4699			$e_QUERY = $matches[2];
4700		}
4701		else
4702		{
4703			define('e_MENU', '');
4704			$e_QUERY = $_SERVER['QUERY_STRING'];
4705		}
4706
4707		if ($no_cbrace)	$e_QUERY = str_replace(array('{', '}', '%7B', '%7b', '%7D', '%7d'), '', rawurldecode($e_QUERY));
4708
4709	//	$e_QUERY = htmlentities(self::getParser()->post_toForm($e_QUERY)); //@see https://github.com/e107inc/e107/issues/719
4710		$e_QUERY = htmlspecialchars(self::getParser()->post_toForm($e_QUERY));
4711
4712		// e_QUERY SHOULD NOT BE DEFINED IF IN SNIGLE ENTRY MODE OR ALL URLS WILL BE BROKEN - it's defined later within the the router
4713		if(!deftrue("e_SINGLE_ENTRY"))
4714		{
4715			define('e_QUERY', filter_var($e_QUERY, FILTER_SANITIZE_URL));
4716			$_SERVER['QUERY_STRING'] = e_QUERY;
4717		}
4718		else
4719		{
4720		//	 define('e_QUERY', ''); // breaks news sef-urls and possibly others. Moved to index.php.
4721		}
4722
4723
4724		define('e_TBQS', $_SERVER['QUERY_STRING']);
4725	}
4726
4727	/**
4728	 * Basic implementation of Browser cache control per user session. Awaiting improvement in future versions
4729	 * If no argument is passed it returns
4730	 * boolean (if current page is cacheable).
4731	 * If string is passed, it's asumed to be aboslute request path (e_REQUEST_URI alike)
4732	 * If true is passed, e_REQUEST_URI is registered
4733	 * @param null $set
4734	 * @return bool|null
4735	 */
4736	public static function canCache($set = null)
4737	{
4738		$_data = self::getSession()->get('__sessionBrowserCache');
4739		if(!is_array($_data)) $_data = array();
4740
4741		if(null === $set)
4742		{
4743			return in_array(e_REQUEST_URI, $_data);
4744		}
4745
4746		// remove e_REQUEST_URI from the set
4747		if(false === $set)
4748		{
4749			$check = array_search(e_REQUEST_URI, $_data);
4750			if(false !== $check)
4751			{
4752				unset($_data[$check]);
4753				self::getSession()->set('__sessionBrowserCache', $_data);
4754				return;
4755			}
4756		}
4757
4758		if(true === $set)
4759		{
4760			$set = e_REQUEST_URI;
4761		}
4762
4763		if(empty($set) || !is_string($set) || in_array($set, $_data)) return;
4764
4765		$_data[]  = $set;
4766		self::getSession()->set('__sessionBrowserCache', array_unique($_data));
4767	}
4768
4769	/**
4770	 * Check if current request is secure (https)
4771	 * @return boolean TRUE if https, FALSE if http
4772	 */
4773	public function isSecure()
4774	{
4775		return ($this->HTTP_SCHEME === 'https');
4776	}
4777
4778	/**
4779	 * Check if current user is banned
4780	 *
4781	 * Generates the queries to interrogate the ban list, then calls $this->check_ban().
4782	 * If the user is banned, $check_ban() never returns - so a return from this routine indicates a non-banned user.
4783	 * FIXME -  moved to ban helper, replace all calls
4784	 * @return void
4785	 */
4786	 /* No longer required - moved to eIPHelper class
4787	public function ban()
4788	{
4789	} */
4790
4791	/**
4792	 * Check the banlist table. $query is used to determine the match.
4793	 * If $do_return, will always return with ban status - TRUE for OK, FALSE for banned.
4794	 * If return permitted, will never display a message for a banned user; otherwise will display any message then exit
4795	 * FIXME - moved to ban helper, replace all calls
4796	 *
4797	 *
4798	 * @param string $query
4799	 * @param boolean $show_error
4800	 * @param boolean $do_return
4801	 * @return boolean
4802	 */
4803	 /* No longer required - moved to eIPHelper class
4804	public function check_ban($query, $show_error = TRUE, $do_return = FALSE)
4805	{
4806	} */
4807
4808
4809	/**
4810	 * Add an entry to the banlist. $bantype = 1 for manual, 2 for flooding, 4 for multiple logins
4811	 * Returns TRUE if ban accepted.
4812	 * Returns FALSE if ban not accepted (i.e. because on whitelist, or invalid IP specified)
4813	 * FIXME - moved to IP handler, replace all calls
4814	 * @param string $bantype
4815	 * @param string $ban_message
4816	 * @param string $ban_ip
4817	 * @param integer $ban_user
4818	 * @param string $ban_notes
4819	 *
4820	 * @return boolean check result
4821	 */
4822	 /*
4823	public function add_ban($bantype, $ban_message = '', $ban_ip = '', $ban_user = 0, $ban_notes = '')
4824	{
4825		return e107::getIPHandler()->add_ban($bantype, $ban_message, $ban_ip, $ban_user, $ban_notes);
4826	} */
4827
4828	/**
4829	 * Get the current user's IP address
4830	 * returns the address in internal 'normalised' IPV6 format - so most code should continue to work provided the DB Field is big enougn
4831	 * FIXME - call ipHandler directly (done for core - left temporarily for BC)
4832	 * @return string
4833	 */
4834	public function getip()
4835	{
4836		return self::getIPHandler()->getIP(FALSE);
4837	}
4838
4839	/**
4840	 * Encode an IP address to internal representation. Returns string if successful; FALSE on error
4841	 * Default separates fields with ':'; set $div='' to produce a 32-char packed hex string
4842	 * FIXME - moved to ipHandler - check for calls elsewhere
4843	 * @param string $ip
4844	 * @param string $div divider
4845	 * @return string encoded IP
4846	 */
4847
4848	public function ipEncode($ip, $div = ':')
4849	{
4850		return self::getIPHandler()->ipEncode($ip);
4851	}
4852
4853	/**
4854	 * Takes an encoded IP address - returns a displayable one
4855	 * Set $IP4Legacy TRUE to display 'old' (IPv4) addresses in the familiar dotted format,
4856	 * FALSE to display in standard IPV6 format
4857	 * Should handle most things that can be thrown at it.
4858	 * FIXME - moved to ipHandler - check for calls elsewhere - core done; left temporarily for BC
4859	 * @param string $ip encoded IP
4860	 * @param boolean $IP4Legacy
4861	 * @return string decoded IP
4862	 */
4863	public function ipdecode($ip, $IP4Legacy = TRUE)
4864	{
4865		return self::getIPHandler()->ipDecode($ip, $IP4Legacy);
4866	}
4867
4868	/**
4869	 * Given a string which may be IP address, email address etc, tries to work out what it is
4870	 * Movet to eIPHandler class
4871	 * FIXME - moved to ipHandler - check for calls elsewhere
4872	 * @param string $string
4873	 * @return string ip|email|url|ftp|unknown
4874	 */
4875	 /*
4876	public function whatIsThis($string)
4877	{
4878		//return e107::getIPHandler()->whatIsThis($string);
4879	} */
4880
4881	/**
4882	 * Retrieve & cache host name
4883	 * @deprecated but needed by some old plugins/menus.
4884	 * @todo Find old calls and replace with code within.
4885	 * @param string $ip_address
4886	 * @return string host name
4887	 */
4888	public function get_host_name($ip_address)
4889	{
4890		return self::getIPHandler()->get_host_name($ip_address);
4891	}
4892
4893	/**
4894	 * MOVED TO eHelper::parseMemorySize()
4895	 * FIXME - find all calls, replace with eHelper::parseMemorySize() (once eHelper lives in a separate file)
4896	 *
4897	 * @param integer $size
4898	 * @param integer $dp
4899	 * @return string formatted size
4900	 */
4901	public function parseMemorySize($size, $dp = 2)
4902	{
4903		return eHelper::parseMemorySize($size, $dp);
4904	}
4905
4906
4907	/**
4908	 * Removed, see eHelper::getMemoryUsage()
4909	 * Get the current memory usage of the code
4910	 * If $separator argument is null, raw data (array) will be returned
4911	 *
4912	 * @param null|string $separator
4913	 * @return string|array memory usage
4914	 */
4915	/*
4916	public function get_memory_usage($separator = '/')
4917	{
4918		return eHelper::getMemoryUsage($separator);
4919	}*/
4920
4921
4922	/**
4923	 * Check if plugin is installed
4924	 * @param string $plugname
4925	 * @return boolean
4926	 */
4927	public static function isInstalled($plugname)
4928	{
4929		// Could add more checks here later if appropriate
4930		return self::getConfig()->isData('plug_installed/'.$plugname);
4931	}
4932
4933	/**
4934	 * Safe way to set ini var
4935	 * @param string $var
4936	 * @param string $value
4937	 * @return mixed
4938	 */
4939	public static function ini_set($var, $value)
4940	{
4941		if (function_exists('ini_set'))
4942		{
4943			return ini_set($var, $value);
4944		}
4945		return false;
4946	}
4947
4948	/**
4949	 * Register autoload function (string) or static class method - array('ClassName', 'MethodName')
4950	 * @param string|array $function
4951	 * @param bool $prepend
4952	 * @return bool
4953	 */
4954	public static function autoload_register($function, $prepend = false)
4955	{
4956		### NEW Register Autoload - do it asap
4957		if(!function_exists('spl_autoload_register'))
4958		{
4959			// PHP >= 5.1.2 required
4960			die_fatal_error('Fatal exception - spl_autoload_* required.');
4961		}
4962
4963		if(!$prepend || false === ($registered = spl_autoload_functions()))
4964		{
4965			return spl_autoload_register($function);
4966		}
4967
4968		foreach ($registered as $r)
4969		{
4970			spl_autoload_unregister($r);
4971		}
4972
4973		$result = spl_autoload_register($function);
4974		foreach ($registered as $r)
4975		{
4976			if(!spl_autoload_register($r)) $result = false;
4977		}
4978		return $result;
4979	}
4980
4981	/**
4982	 * Former __autoload, generic core autoload logic
4983	 *
4984	 * Magic class autoload.
4985	 * We are raising plugin structure standard here - plugin auto-loading works ONLY if
4986	 * classes live inside 'includes' folder.
4987	 * Example: plugin_myplug_admin_ui ->
4988	 * <code>
4989	 * <?php
4990	 * // __autoload() will look in e_PLUGIN.'myplug/includes/admin/ui.php for this class
4991	 * // e_admin_ui is core handler, so it'll be autoloaded as well
4992	 * class plugin_myplug_admin_ui extends e_admin_ui
4993	 * {
4994	 *
4995	 * }
4996	 *
4997	 * // __autoload() will look in e_PLUGIN.'myplug/shortcodes/my_shortcodes.php for this class
4998	 * // e_admin_ui is core handler, so it'll be autoloaded as well
4999	 * class plugin_myplug_my_shortcodes extends e_admin_ui
5000	 * {
5001	 *
5002	 * }
5003	 * </code>
5004	 * We use now spl_autoload[_*] for core autoloading (PHP5 > 5.1.2)
5005	 * TODO - at this time we could create e107 version of spl_autoload_register - e_event->register/trigger('autoload')
5006	 *
5007	 * @todo plugname/e_shortcode.php auto-detection (hard, near impossible at this time) - we need 'plugin_' prefix to
5008	 * distinguish them from the core batches
5009	 *
5010	 * @param string $className
5011	 * @return void
5012	 */
5013	public static function autoload($className)
5014	{
5015		//Security...
5016	    if (strpos($className, '/') !== false)
5017		{
5018	        return;
5019	    }
5020
5021		// Detect namespaced class
5022		if (strpos($className, '\\') !== false)
5023		{
5024			self::autoload_namespaced($className);
5025			return;
5026		}
5027
5028		$tmp = explode('_', $className);
5029
5030		//echo 'autoloding...'.$className.'<br />';
5031		switch($tmp[0])
5032		{
5033			case 'plugin': // plugin handlers/shortcode batches
5034				array_shift($tmp); // remove 'plugin'
5035				$end = array_pop($tmp); // check for 'shortcodes' end phrase
5036
5037				if (!isset($tmp[0]) || !$tmp[0])
5038				{
5039					if($end)
5040					{
5041						// plugin root - e.g. plugin_myplug -> plugins/myplug/myplug.php, class plugin_myplug
5042						$filename = e_PLUGIN.$end.'/'.$end.'.php';
5043						break;
5044					}
5045					return; // In case we get an empty class part
5046				}
5047
5048				// Currently only batches inside shortcodes/ folder are auto-detected,
5049				// read the todo for e_shortcode.php related problems
5050				if('shortcodes' == $end)
5051				{
5052					$filename = e_PLUGIN.$tmp[0].'/shortcodes/batch/'; // plugname/shortcodes/batch/
5053					unset($tmp[0]);
5054					$filename .= implode('_', $tmp).'_shortcodes.php'; // my_shortcodes.php
5055					break;
5056				}
5057				if($end)
5058				{
5059					$tmp[] = $end; // not a shortcode batch - append the end phrase again
5060				}
5061
5062				// Handler check
5063				$tmp[0] .= '/includes'; //folder 'includes' is not part of the class name
5064				$filename = e_PLUGIN.implode('/', $tmp).'.php';
5065				//TODO add debug screen Auto-loaded classes - ['plugin: '.$filename.' - '.$className];
5066			break;
5067
5068			default: //core libraries, core shortcode batches
5069				// core SC batch check
5070				$end = array_pop($tmp);
5071				if('shortcodes' == $end)
5072				{
5073					$filename = e_CORE.'shortcodes/batch/'.$className.'.php'; // core shortcode batch
5074					break;
5075				}
5076
5077				$filename = self::getHandlerPath($className, true);
5078				//TODO add debug screen Auto-loaded classes - ['core: '.$filename.' - '.$className];
5079			break;
5080		}
5081
5082		if(!empty($filename) && is_file($filename)) // Test with chatbox_menu
5083		{
5084			// autoload doesn't REQUIRE files, because this will break things like call_user_func()
5085			include($filename);
5086		}
5087	}
5088
5089	/**
5090	 * Autoloading logic for namespaced classes
5091	 *
5092	 * @param $className
5093	 * @return void
5094	 */
5095	private static function autoload_namespaced($className)
5096	{
5097		$levels = explode('\\', $className);
5098
5099		// Guard against classes that are not ours
5100		if ($levels[0] != 'e107') return;
5101
5102		$levels[0] = e_HANDLER;
5103		$classPath = implode('/', $levels).'.php';
5104		if (is_file($classPath) && is_readable($classPath))
5105		{
5106			include($classPath);
5107		}
5108	}
5109
5110	public function __get($name)
5111	{
5112		switch ($name)
5113		{
5114			case 'tp':
5115				$ret = self::getParser();
5116			break;
5117
5118			case 'sql':
5119				$ret = self::getDb();
5120			break;
5121
5122			case 'ecache':
5123				$ret = self::getCache();
5124			break;
5125
5126			case 'arrayStorage':
5127				$ret = self::getArrayStorage();
5128			break;
5129
5130			case 'e_event':
5131				$ret = self::getEvent();
5132			break;
5133
5134			case 'ns':
5135				$ret = self::getRender();
5136			break;
5137
5138			case 'url':
5139				$ret = self::getUrl();
5140			break;
5141
5142			case 'admin_log':
5143				$ret = self::getAdminLog();
5144			break;
5145
5146			case 'override':
5147				$ret = self::getSingleton('override', e_HANDLER.'override_class.php');
5148			break;
5149
5150			case 'notify':
5151				$ret = self::getNotify();
5152			break;
5153
5154			case 'e_online':
5155				$ret = self::getOnline();
5156			break;
5157
5158			case 'eIPHandler':
5159				$ret = self::getIPHandler();
5160				break;
5161
5162			case 'user_class':
5163				$ret = self::getUserClass();
5164			break;
5165
5166			default:
5167				trigger_error('$e107->$'.$name.' not defined', E_USER_WARNING);
5168				return null;
5169			break;
5170		}
5171
5172		$this->$name = $ret;
5173		return $ret;
5174	}
5175
5176
5177	/**
5178	 *
5179	 */
5180	public function destruct() //FIXME $path is not defined anywhere.
5181	{
5182		if(null === self::$_instance) return;
5183
5184		$print = defined('E107_DBG_TIMEDETAILS') && E107_DBG_TIMEDETAILS;
5185
5186		!$print || print('<table class="table table-striped table-condensed"><tr><td colspan="3"><b>Destructing $e107</b></td></tr>');
5187		$vars = get_object_vars($this);
5188		foreach ($vars as $name => $value)
5189		{
5190			if(is_object($value))
5191			{
5192				if(method_exists($value, '__destruct'))
5193				{
5194					!$print || print('<tr><td>object [property] using __destruct()</td><td>'.$name.'</td><td>'.get_class($value).'</td></tr>');
5195					$value->__destruct();
5196				}
5197				else !$print || print('<tr><td>object [property]</td><td>'.$name.'</td><td>'.get_class($value).'</td></tr>');
5198				$this->$name = null;
5199			}
5200		}
5201		foreach (self::$_registry as $path => $reg)
5202		{
5203			if(is_object($reg))
5204			{
5205				if(method_exists($reg, '__destruct'))
5206				{
5207					!$print || print('<tr><td>object [registry] using __destruct()</td><td>'.$path.'</td><td>'.get_class($reg).'</td></tr>');
5208					$reg->__destruct();
5209				}
5210				else !$print || print('<tr><td>object [registry]</td><td>'.$path.'</td><td>'.get_class($reg).'</td></tr>');
5211				unset(self::$_registry[$path]);
5212			}
5213
5214
5215		}
5216
5217		if($print)
5218		{
5219			echo "</table>";
5220		}
5221
5222		self::$_registry = null;
5223		self::$_instance = null;
5224	}
5225
5226
5227	/**
5228	 * Check if there's a core e107 release available
5229	 * @return array|bool - return array of data or false if no update available.
5230	 */
5231	public static function coreUpdateAvailable()
5232	{
5233
5234	    // Get site version
5235	    $e107info= array();
5236
5237	    if(is_readable(e_ADMIN."ver.php"))
5238	    {
5239			include(e_ADMIN."ver.php"); // $e107info['e107_version'];
5240	    }
5241	    else
5242	    {
5243	        return false;
5244	    }
5245
5246        $xml  = self::getXml();
5247        $file = "https://e107.org/releases.php";
5248        if(!$xdata = $xml->loadXMLfile($file,true,false))
5249        {
5250            return false;
5251        }
5252
5253		$curVersion = str_replace(' (git)', '', $e107info['e107_version']);
5254
5255		if(empty($xdata['core'][0]['@attributes']['version']))
5256		{
5257			return false;
5258		}
5259		else
5260		{
5261			$newVersion = $xdata['core'][0]['@attributes']['version'];
5262		}
5263
5264
5265		self::getDebug()->log("New Version:".$newVersion);
5266
5267		if(version_compare($curVersion,$newVersion) === -1)
5268		{
5269			$data = array(
5270				'name'          => $xdata['core'][0]['@attributes']['name'],
5271				'url'           => $xdata['core'][0]['@attributes']['url'],
5272				'date'          => $xdata['core'][0]['@attributes']['date'],
5273				'version'       => $xdata['core'][0]['@attributes']['version'],
5274				'infourl'       => $xdata['core'][0]['@attributes']['infourl'],
5275				'description'   => $xdata['core'][0]['description'],
5276			);
5277
5278			return $data;
5279		}
5280
5281		return false;
5282
5283	}
5284
5285
5286
5287}
5288
5289e107::autoload_register(array(e107::class, 'autoload'));
5290
5291// Forward compatibility with e107 v3 Composer autoloading
5292$vendor_autoload_file = __DIR__."/vendor/autoload.php";
5293if (file_exists($vendor_autoload_file))
5294{
5295	include_once($vendor_autoload_file);
5296}
5297
5298/**
5299 * Interface e_admin_addon_interface @move to separate addons file?
5300 */
5301interface e_admin_addon_interface
5302{
5303
5304	/**
5305	* Return a list of values for the currently viewed list page.
5306	* @param string $event
5307	* @param string $ids comma separated primary ids to return in the array.
5308	* @return array with primary id as keys and array of fields key/pair values.
5309	*/
5310	public function load($event, $ids);
5311
5312
5313	/**
5314	* Extend Admin-ui Parameters
5315	* @param $ui admin-ui object
5316	* @return array
5317	*/
5318	public function config(e_admin_ui $ui);
5319
5320
5321	/**
5322	* Process Posted Data.
5323	* @param $ui admin-ui object
5324	* @param int $id
5325	*/
5326	public function process(e_admin_ui $ui, $id=0);
5327
5328
5329
5330}
5331