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 * Message Handler
10 *
11 * $URL$
12 * $Id$
13 *
14*/
15
16if (!defined('e107_INIT')) { exit; }
17
18/*
19 * Type defines
20 */
21define('E_MESSAGE_INFO',      'info');
22define('E_MESSAGE_SUCCESS',   'success');
23define('E_MESSAGE_WARNING',   'warning');
24define('E_MESSAGE_ERROR',     'error');
25define('E_MESSAGE_DEBUG',     'debug');
26define('E_MESSAGE_NODISPLAY', 'nodisplay'); // Appears to be needed by update_routine
27
28//FIXME - language file! new?
29
30/**
31 * Handle system messages
32 *
33 * @package e107
34 * @subpackage	e107_handlers
35 * @version $Id$
36 * @author SecretR
37 * @copyright Copyright (C) 2008-2010 e107 Inc (e107.org)
38 */
39class eMessage
40{
41	/**
42	 * Type defines
43	 */
44	 const E_INFO       = 'info';
45	 const E_SUCCESS    = 'success';
46	 const E_WARNING    = 'warning';
47	 const E_ERROR      = 'error';
48	 const E_DEBUG      = 'debug';
49	 const E_NODISPLAY  = 'nodisplay';
50
51
52	/**
53	 * System Message Array
54	 * in format [type][message_stack] = array(message[, ...])
55	 *
56	 * @var array
57	 */
58	protected $_sysmsg = array();
59
60	/**
61	 * Session key for storing session messages
62	 *
63	 * @var string
64	 */
65	protected $_session_id;
66
67	/**
68	 * @var e_core_session
69	 */
70	protected $_session_handler = null;
71
72	/**
73	 * @var array
74	 */
75	protected $_unique = array();
76
77	/**
78	 * @var array
79	 */
80	static $_customTitle = array();
81
82	/**
83	 * Custom font-awesome icon
84	 * @var array
85	 */
86	static $_customIcon = array();
87
88
89	static $_close = array('info'=>true,'success'=>true,'warning'=>true,'error'=>true,'debug'=>true);
90	/**
91	 * Singleton instance
92	 *
93	 * @var eMessage
94	 */
95	//protected static $_instance = null;
96
97	/**
98	 * Constructor
99	 *
100	 * Use {@link getInstance()}, direct instantiating
101	 * is not possible for signleton objects
102	 *
103	 * @return void
104	 */
105	public function __construct()
106	{
107		//if(!session_id()) session_start();
108
109		// require_once(e_HANDLER.'e107_class.php');
110		$this->_session_id = '_system_messages';
111
112		$this->reset()->mergeWithSession();
113	}
114
115	/**
116	 * Cloning is not allowed
117	 *
118	 */
119	// private function __clone()
120	// {
121	// }
122
123	/**
124	 * Singleton is not required, we go for factory instead
125	 * @return eMessage
126	 */
127	public static function getInstance()
128	{
129		// if(null == self::$_instance)
130		// {
131		    // self::$_instance = new self();
132		// }
133	  	return e107::getMessage();
134	}
135
136	/**
137	 * Set message session id
138	 * @param string $name
139	 * @return eMessage
140	 */
141	public function setSessionId($name = '')
142	{
143		$sid = $name.'_system_messages';
144		if($this->_session_id != $sid)
145		{
146			if(session_id())
147			{
148				$session = $this->getSessionHandler();
149				$session->set($sid, $session->get($this->_session_id, true)); // move
150				if(!$session->has($sid)) $session->set($sid, array()); // be sure it's array
151			}
152			$this->_session_id = $sid;
153		}
154		return $this;
155	}
156
157	/**
158	 * Get session handler
159	 * @return e_core_session
160	 */
161	public function getSessionHandler()
162	{
163		if(null === $this->_session_handler)
164		{
165			$session = e107::getSession();
166			if(!$session->has($this->_session_id)) $session->set($this->_session_id, array());
167			$this->_session_handler = $session;
168		}
169		return $this->_session_handler;
170	}
171
172
173	/**
174	 * Set unique message stacks
175	 * @param string $mstack message stack which should have only unique message values
176	 * @return eMessage
177	 */
178	public function setUnique($mstack='default')
179	{
180		if(!in_array($mstack, $this->_unique))
181		{
182			$this->_unique[] = $mstack;
183		}
184		return $this;
185	}
186
187
188	/**
189	 * Add message to a type stack and default message stack
190	 * If $message is array, $message[0] will be the message stack and
191	 * $message[1] the message itself
192	 *
193	 * @param string|array $message
194	 * @param string $type
195	 * @param boolean $session
196	 * @return eMessage
197	 */
198	public function add($message, $type = eMessage::E_INFO, $session = false)
199	{
200		if(empty($message)) return $this;
201
202		$mstack = 'default';
203		$msg = $message;
204		if(is_array($message))
205		{
206			$mstack = $message[1];
207			$msg = $message[0];
208		}
209		if(empty($msg)) return $this;
210
211		if(!$session)
212		{
213			// unique messages only
214			if(in_array($mstack, $this->_unique) && isset($this->_sysmsg[$type][$mstack]) && in_array($msg, $this->_sysmsg[$type][$mstack])) return $this;
215			if($this->isType($type)) $this->_sysmsg[$type][$mstack][] = $msg;
216			return $this;
217		}
218
219		$this->addSession($message, $type);
220		return $this;
221	}
222
223	/**
224	 * Alias of {@link add()}.
225	 * Should be used for dealing with messages with custom message stacks.
226	 * Supports message arrays.
227	 *
228	 * @param string|array $message message(s)
229	 * @param string $mstack defaults to 'default'
230	 * @param string $type [optional]
231	 * @param boolean $session [optional]
232	 * @return eMessage
233	 */
234	public function addStack($message, $mstack = 'default', $type = E_MESSAGE_INFO, $session = false)
235	{
236		if(!is_array($message))
237		{
238			$message = array($message);
239		}
240
241		foreach ($message as $m)
242		{
243			$this->add(array($m, $mstack), $type, $session);
244		}
245		return $this;
246	}
247
248	/**
249	 * Add success message
250	 *
251	 * @param string $message
252	 * @param string $mstack message stack, default value is 'default'
253	 * @param boolean $session
254	 * @return eMessage
255	 */
256	public function addSuccess($message, $mstack = 'default', $session = false)
257	{
258		return $this->addStack($message, $mstack, E_MESSAGE_SUCCESS, $session);
259	}
260
261	/**
262	 * Add error message
263	 *
264	 * @param string $message
265	 * @param string $mstack message stack, default value is 'default'
266	 * @param boolean $session
267	 * @return eMessage
268	 */
269	public function addError($message, $mstack = 'default', $session = false)
270	{
271		return $this->addStack($message, $mstack, E_MESSAGE_ERROR, $session);
272	}
273
274	/**
275	 * Add warning message
276	 *
277	 * @param string $message
278	 * @param string $mstack message stack, default value is 'default'
279	 * @param boolean $session
280	 * @return eMessage
281	 */
282	public function addWarning($message, $mstack = 'default', $session = false)
283	{
284		return $this->addStack($message, $mstack, E_MESSAGE_WARNING, $session);
285	}
286
287	/**
288	 * Add info message
289	 *
290	 * @param string $message
291	 * @param string $mstack message stack, default value is 'default'
292	 * @param boolean $session
293	 * @return eMessage
294	 */
295	public function addInfo($message, $mstack = 'default', $session = false)
296	{
297		return $this->addStack($message, $mstack, E_MESSAGE_INFO, $session);
298	}
299
300	/**
301	 * Add debug message
302	 *
303	 * @param string $message
304	 * @param string $mstack message stack, default value is 'default'
305	 * @param boolean $session
306	 * @return eMessage
307	 */
308	public function addDebug($message, $mstack = 'default', $session = false) //TODO Add different types of DEBUG depending on the debug mode.
309	{
310		return $this->addStack($message, $mstack, E_MESSAGE_DEBUG, $session);
311	}
312
313	/**
314	 * Add message to a _SESSION type stack
315	 * If $message is array, $message[0] will be the message stack and
316	 * $message[1] the message itself
317	 *
318	 * @param string|array $message
319	 * @param string $type
320	 * @return eMessage
321	 */
322	public function addSession($message, $type = E_MESSAGE_INFO)
323	{
324		if(empty($message) || !session_id()) return $this;
325
326		$mstack = 'default';
327		if(is_array($message))
328		{
329			$mstack = $message[1];
330			$message = $message[0];
331		}
332		$SESSION = $this->getSessionHandler()->get($this->_session_id);
333
334		if($this->isType($type))
335		{
336			// unique messages only
337			if(in_array($mstack, $this->_unique) && in_array($message, $SESSION[$type][$mstack])) return $this;
338
339			$SESSION[$type][$mstack][] = $message;
340			$this->getSessionHandler()->set($this->_session_id, $SESSION);
341		}
342		return $this;
343	}
344
345	/**
346	 * Alias of {@link addSession()}.
347	 * Should be used for dealing with messages with custom message stacks.
348	 * Supports message arrays.
349	 *
350	 * @param string|array $message message(s)
351	 * @param string $mstack defaults to 'default'
352	 * @param string $type [optional]
353	 * @return eMessage
354	 */
355	public function addSessionStack($message, $mstack = 'default', $type = E_MESSAGE_INFO)
356	{
357		if(!is_array($message))
358		{
359			$message = array($message);
360		}
361		foreach ($message as $m)
362		{
363			$this->addSession(array($m, $mstack), $type);
364		}
365		return $this;
366	}
367
368	/**
369	 * Get type title (multi-language)
370	 *
371	 * @param string $type
372	 * @param string $message_stack
373	 * @return string title
374	 */
375	public static function getTitle($type, $message_stack = 'default')
376	{
377		if(!empty(self::$_customTitle[$type]))
378		{
379			return self::$_customTitle[$type];
380		}
381
382		if($message_stack && $message_stack != 'default' && defined('EMESSLAN_TITLE_'.strtoupper($type.'_'.$message_stack)))
383		{
384			return constant('EMESSLAN_TITLE_'.strtoupper($type.'_'.$message_stack));
385		}
386		return deftrue('EMESSLAN_TITLE_'.strtoupper($type), '');
387	}
388
389
390	/**
391	 * Set a custom title/caption (useful for front-end)
392	 *
393	 * @param string $title
394	 * @param string $type E_MESSAGE_SUCCESS,E_MESSAGE_ERROR, E_MESSAGE_WARNING, E_MESSAGE_INFO
395	 * @return $this
396	 * @example e107::getMessage()->setTitle('Custom Title', E_MESSAGE_INFO);
397	 */
398	public function setTitle($title, $type)
399	{
400		$tp = e107::getParser();
401		self::$_customTitle[$type] = $tp->toText($title);
402
403		return $this;
404	}
405
406	/**
407	 * Set a custom icon (useful for front-end)
408	 *
409	 * @param string $fa FontAwesome reference. eg. fa-cog
410	 * @param string $type E_MESSAGE_SUCCESS,E_MESSAGE_ERROR, E_MESSAGE_WARNING, E_MESSAGE_INFO
411	 * @return $this
412	 * @example e107::getMessage()->setIcon('fa-cog', E_MESSAGE_INFO);
413	 */
414	public function setIcon($fa, $type)
415	{
416		$tp = e107::getParser();
417		self::$_customIcon[$type] = $tp->toText($fa);
418
419		return $this;
420	}
421
422
423	/**
424	 * Enable the 'x' close functionality of an alert.
425	 *
426	 * @param boolean $toggle
427	 * @param string $type E_MESSAGE_SUCCESS,E_MESSAGE_ERROR, E_MESSAGE_WARNING, E_MESSAGE_INFO
428	 * @return $this
429	 * @example e107::getMessage()->setClose(false, E_MESSAGE_INFO);
430	 */
431	public function setClose($toggle, $type)
432	{
433		self::$_close[$type] = $toggle;
434		return $this;
435	}
436
437
438
439	/**
440	 * Message getter
441	 *
442	 * @param string $type valid type
443	 * @param string $mstack message stack name
444	 * @param bool $raw force array return
445	 * @param bool $reset reset message type stack
446	 * @return string|array message
447	 */
448	public function get($type, $mstack = 'default', $raw = false, $reset = true)
449	{
450		$message = isset($this->_sysmsg[$type][$mstack]) ? $this->_sysmsg[$type][$mstack] : '';
451		if($reset) $this->reset($type, $mstack, false);
452
453		return (true === $raw ? $message : self::formatMessage($mstack, $type, $message));
454	}
455
456	/**
457	 * Get all messages for a stack
458	 *
459	 * @param string $mstack message stack name
460	 * @param bool $raw force array return
461	 * @param bool $reset reset message type stack
462	 * @return array messages
463	 */
464	public function getAll($mstack = 'default', $raw = false, $reset = true)
465	{
466		$ret = array();
467		foreach ($this->_get_types() as $type)
468		{
469			$message = $this->get($type, $mstack, $raw, $reset);
470			if(!empty($message))
471			{
472				$ret[$type] = $message;
473			}
474		}
475
476		return $ret;
477	}
478
479	/**
480	 * Session message getter
481	 *
482	 * @param string $type valid type
483	 * @param string $mstack message stack
484	 * @param bool $raw force array return
485	 * @param bool $reset reset session message type stack
486	 * @return string|array session message
487	 */
488	public function getSession($type, $mstack = 'default', $raw = false, $reset = true)
489	{
490		if(!session_id()) return null;
491		$SESSION = $this->getSessionHandler()->get($this->_session_id);
492		$message = isset($SESSION[$type][$mstack]) ? $SESSION[$type][$mstack] : '';
493		if($reset) $this->resetSession($type, $mstack);
494
495		return (true === $raw ? $message : self::formatMessage($mstack, $type, $message));
496	}
497
498	/**
499	 * Get all session messages for a stack
500	 *
501	 * @param string $mstack message stack name
502	 * @param bool $raw force array return
503	 * @param bool $reset reset message type stack
504	 * @return array session messages
505	 */
506	public function getAllSession($mstack = 'default', $raw = false, $reset = true)
507	{
508		if(!session_id()) return array();
509		$ret = array();
510		foreach ($this->_get_types() as $type)
511		{
512			$message = $this->getSession($type, $mstack, $raw, $reset);
513			if(!empty($message))
514			{
515				$ret[$type] = $message;
516			}
517		}
518
519		return $ret;
520	}
521
522	/**
523	 * Output all accumulated messages OR a specific type of messages. eg. 'info', 'warning', 'error', 'success'
524	 *
525	 * @param string $mstack message stack name
526	 * @param bool|string $options  - true : merge with session messages or enter a type 'info', 'warning', 'error', 'success'
527	 * @param bool $reset reset all messages
528	 * @param bool $raw force return type array
529	 * @return array|string messages
530	 */
531	public function render($mstack = 'default', $options = false, $reset = true, $raw = false)
532	{
533		if($options === true )
534		{
535			$this->mergeWithSession(true, $mstack);
536		}
537		$ret = array();
538
539		$typesArray = (is_string($options) && in_array($options, $this->_get_types()))  ? array($options) : $this->_get_types();
540
541		foreach ($typesArray as $type)
542		{
543			if(E_MESSAGE_DEBUG === $type && (!deftrue('E107_DEBUG_LEVEL') || !ADMIN))
544			{
545				continue;
546			}
547			$message = $this->get($type, $mstack, $raw);
548
549			if(!empty($message))
550			{
551				$ret[$type] = $message;
552			}
553		}
554
555		if($reset) $this->reset(false, $mstack);
556		if(true === $raw || empty($ret)) return ($raw ? $ret : '');
557
558
559		return "
560			<div class='s-message'>
561				".implode("\n", $ret)."
562			</div>
563		";
564	}
565
566	/**
567	 * Create message block markup based on its type.
568	 *
569	 * @param string $mstack
570	 * @param string $type
571	 * @param array|string $message
572	 * @return string
573	 */
574	public static function formatMessage($mstack, $type, $message)
575	{
576		$bstrap = array('info'=>'alert-info','error'=>'alert-error alert-danger','warning'=>'alert-warning','success'=>'alert-success','debug'=>'alert-warning');
577		$bclass = vartrue($bstrap[$type]) ? " ".$bstrap[$type] : "";
578
579		if (empty($message))
580		{
581			 return '';
582		}
583		elseif (is_array($message))
584		{
585			// XXX quick fix disabled because of various troubles - fix attempt made inside pref handler (the source of the problem)
586			// New feature added - setUnique($mstack) -> array_unique only for given message stacks
587			//$message = array_unique($message); // quick fix for duplicates.
588			$message = "<div class='s-message-item'>".implode("</div>\n<div class='s-message-item'>", $message)."</div>";
589		}
590
591		$icon = !empty(self::$_customIcon[$type]) ? "s-message-empty fa fa-2x ".self::$_customIcon[$type] : "s-message-".$type;
592
593
594		$text = "<div class='s-message alert alert-block alert-dismissible fade in show {$type} {$bclass}' role='alert'>";
595		$text .= (self::$_close[$type] === true) ? "<a class='close' data-dismiss='alert' aria-label='".LAN_CLOSE."'>×</a>" : "";
596		$text .= "<i class='s-message-icon ".$icon."'></i>
597				<h4 class='s-message-title'>".self::getTitle($type, $mstack)."</h4>
598				<div class='s-message-body'>
599					{$message}
600				</div>
601			</div>
602		";
603
604
605		return $text;
606	}
607
608	/**
609	 * Reset message array
610	 *
611	 * @param mixed $type false for reset all or type string
612	 * @param mixed $mstack false for reset all or stack name string
613	 * @param boolean $session reset session messages as well
614	 * @return eMessage
615	 */
616	public function reset($type = false, $mstack = false, $session = false)
617	{
618		if(false === $type)
619		{
620			if(false === $mstack)
621			{
622				$this->_sysmsg = $this->_type_map();
623			}
624			elseif(is_array($this->_sysmsg))
625			{
626				foreach ($this->_sysmsg as $t => $_mstack)
627				{
628					if(is_array($_mstack))
629					{
630						unset($this->_sysmsg[$t][$mstack]);
631					}
632				}
633			}
634		}
635		elseif(isset($this->_sysmsg[$type]))
636		{
637			if(false === $mstack)
638			{
639				$this->_sysmsg[$type] = array();
640			}
641			elseif(is_array($this->_sysmsg[$type]))
642			{
643				unset($this->_sysmsg[$type][$mstack]);
644			}
645		}
646
647		if($session) $this->resetSession($type, $mstack);
648
649		return $this;
650	}
651
652	/**
653	 * Reset _SESSION message array
654	 *
655	 * @param mixed $type false for reset all, or valid type constant
656	 * @param mixed $mstack false for reset all or stack name string
657	 * @return eMessage
658	 */
659	public function resetSession($type = false, $mstack = false)
660	{
661		if(!session_id()) return $this;
662		$SESSION = $this->getSessionHandler()->get($this->_session_id);
663		if(false === $type)
664		{
665			if(false === $mstack)
666			{
667				$SESSION = $this->_type_map();
668			}
669			elseif($SESSION)
670			{
671				foreach ($SESSION as $t => $_mstack)
672				{
673					if(is_array($_mstack))
674					{
675						unset($SESSION[$t][$mstack]);
676					}
677				}
678			}
679		}
680		elseif(isset($SESSION[$type]))
681		{
682			if(false === $mstack)
683			{
684				$SESSION[$type] = array();
685			}
686			elseif(is_array($SESSION[$type]))
687			{
688				unset($SESSION[$type][$mstack]);
689			}
690		}
691		$this->getSessionHandler()->set($this->_session_id, $SESSION);
692		return $this;
693	}
694
695	/**
696	 * Merge _SESSION message array with the current messages
697	 *
698	 * @param boolean $reset
699	 * @param boolean $mstack
700	 * @return eMessage
701	 */
702	public function mergeWithSession($reset = true, $mstack = false)
703	{
704		// do nothing if there is still no session
705		if(!session_id()) return $this;
706		$SESSION = $this->getSessionHandler()->get($this->_session_id);
707
708		if(!empty($SESSION))
709		{
710			foreach (array_keys($SESSION) as $type)
711			{
712				if(!$this->isType($type))
713				{
714					unset($SESSION[$type]);
715					continue;
716				}
717				if(false === $mstack)
718				{
719					$this->_sysmsg[$type] = array_merge_recursive($this->_sysmsg[$type], $SESSION[$type]);
720					continue;
721				}
722
723				if(isset($SESSION[$type][$mstack]))
724				{
725					$this->_sysmsg[$type][$mstack] = $SESSION[$type][$mstack];
726				}
727			}
728			$this->getSessionHandler()->set($this->_session_id, $SESSION);
729		}
730		if($reset) $this->resetSession(false, $mstack);
731		return $this;
732	}
733
734	/**
735	 * Convert current messages to Session messages
736	 *
737	 * @param bool $mstack false - move all message stacks
738	 * @param bool $message_type false - move all types
739	 * @return eMessage
740	 */
741	public function moveToSession($mstack = false, $message_type = false)
742	{
743		// do nothing if there is still no session
744		if(!session_id()) return $this;
745		$SESSION = $this->getSessionHandler()->get($this->_session_id);
746
747		foreach (array_keys($this->_sysmsg) as $type)
748		{
749			if(!$this->isType($type) || ($message_type && $message_type !== $type))
750			{
751				unset($this->_sysmsg[$type]);
752				continue;
753			}
754			if(false === $mstack)
755			{
756				$SESSION[$type] = array_merge_recursive($SESSION[$type], $this->_sysmsg[$type]);
757				continue;
758			}
759
760			if(isset($this->_sysmsg[$type][$mstack]))
761			{
762				$SESSION[$type][$mstack] = $this->_sysmsg[$type][$mstack];
763			}
764		}
765		$this->getSessionHandler()->set($this->_session_id, $SESSION);
766		$this->reset($message_type, $mstack, false);
767		return $this;
768	}
769
770	/**
771	 * Merge messages from source stack with destination stack
772	 * and reset source stack
773	 *
774	 * @param string $from_stack source stack
775	 * @param string $to_stack [optional] destination stack
776	 * @param bool $type [optional] merge for a given type only
777	 * @param bool $session [optional] merge session as well
778	 * @return eMessage
779	 */
780	public function moveStack($from_stack, $to_stack = 'default', $type = false, $session = true)
781	{
782		if($from_stack == $to_stack) return $this;
783		foreach ($this->_sysmsg as $_type => $stacks)
784		{
785			if($type && $type !== $_type)
786			{
787				continue;
788			}
789
790			if(isset($stacks[$from_stack]))
791			{
792				if(!isset($this->_sysmsg[$_type][$to_stack]))
793				{
794					$this->_sysmsg[$_type][$to_stack] = array();
795				}
796				if(in_array($from_stack, $this->_unique))
797				{
798					// check the destination stack messages, remove duplicates
799					foreach ($this->_sysmsg[$_type][$from_stack] as $i => $_m)
800					{
801						if(in_array($_m, $this->_sysmsg[$_type][$to_stack])) unset($this->_sysmsg[$_type][$from_stack][$i]);
802					}
803				}
804				$this->_sysmsg[$_type][$to_stack] = array_merge($this->_sysmsg[$_type][$to_stack], $this->_sysmsg[$_type][$from_stack]);
805				unset($this->_sysmsg[$_type][$from_stack]);
806			}
807		}
808
809		if($session) $this->moveSessionStack($from_stack, $to_stack, $type);
810
811		return $this;
812	}
813
814	/**
815	 * Merge session messages from source stack with destination stack
816	 * and reset source stack
817	 *
818	 * @param string $from_stack source stack
819	 * @param string $to_stack [optional] destination stack
820	 * @param string|bool $type [optional] merge for a given type only
821	 * @return eMessage
822	 */
823	public function moveSessionStack($from_stack, $to_stack = 'default', $type = false)
824	{
825		// do nothing if there is still no session
826		if(!session_id() || $from_stack == $to_stack) return $this;
827		$SESSION = $this->getSessionHandler()->get($this->_session_id);
828
829		foreach ($SESSION as $_type => $stacks)
830		{
831			if($type && $type !== $_type)
832			{
833				continue;
834			}
835			if(isset($stacks[$from_stack]))
836			{
837				if(!isset($SESSION[$_type][$to_stack]))
838				{
839					$SESSION[$_type][$to_stack] = array();
840				}
841				$SESSION[$_type][$to_stack] = array_merge($SESSION[$_type][$to_stack], $this->_sysmsg[$_type][$from_stack]);
842				unset($SESSION[$_type][$from_stack]);
843			}
844		}
845		$this->getSessionHandler()->set($this->_session_id, $SESSION);
846
847		return $this;
848	}
849
850	/**
851	 * Check passed type against the type map
852	 *
853	 * @param mixed $type
854	 * @return boolean
855	 */
856	public function isType($type)
857	{
858		return (array_key_exists($type, $this->_type_map()));
859	}
860
861	/**
862	 * Check for messages
863	 *
864	 * @param mixed $type
865	 * @param string|bool $mstack
866	 * @param boolean $session
867	 * @return boolean
868	 */
869	public function hasMessage($type = false, $mstack = false, $session = true)
870	{
871		if(!$mstack) $mstack = 'default';
872
873		if(false === $type)
874		{
875			foreach ($this->_get_types() as $_type)
876			{
877				if($this->get($_type, $mstack, true, false) || ($session && $this->getSession($_type, $mstack, true, false)))
878				{
879					return true;
880				}
881			}
882		}
883		return ($this->get($type, $mstack, true, false) || ($session && $this->getSession($type, $mstack, true, false)));
884	}
885
886	/**
887	 * Balnk type array structure
888	 *
889	 * @return array type map
890	 */
891	protected function _type_map()
892	{
893		//show them in this order!
894		return array(
895			E_MESSAGE_ERROR 	=> array(),
896			E_MESSAGE_WARNING 	=> array(),
897			E_MESSAGE_SUCCESS 	=> array(),
898			E_MESSAGE_INFO 		=> array(),
899			E_MESSAGE_DEBUG		=> array()
900		);
901	}
902
903	/**
904	 * Get all valid message types
905	 *
906	 * @return array valid message types
907	 */
908	protected function _get_types()
909	{
910		return array_keys($this->_type_map());
911	}
912
913	/**
914	 * Proxy for undefined methods. It allows quick (less arguments)
915	 * call to {@link addStack()}.
916	 * Name of the method should equal to valid eMessage type - {@link _type_map()}
917	 *
918	 * Example:
919	 * <code>
920	 * e107::getMessage()->success('Success', false);
921	 * //calls internal $this->addStack('Success', E_MESSAGE_SUCCESS, false);
922	 * </code>
923	 * @param string $method valid message type
924	 * @param array $arguments array(0 => (string) message, [optional] 1 =>(boolean) session, [optional] 2=> message stack )
925	 * @return eMessage
926	 * @throws Exception
927	 */
928	function __call($method, $arguments) {
929		if($this->isType($method))
930		{
931			$this->addStack($arguments[0], vartrue($arguments[2], 'default'), $method, (isset($arguments[1]) && !empty($arguments[1])));
932			return $this;
933		}
934		throw new Exception('Method eMessage::'.$method.' does not exist!');//FIXME - e107Exception handler
935	}
936
937
938
939	/**
940	 * Automate DB system messages
941	 * NOTE: default value of $output parameter will be changed to false (no output by default) in the future
942	 *
943	 * @param integer|bool $update return result of db::db_Query
944	 * @param string $type update|insert|update
945	 * @param string|bool $success forced success message
946	 * @param string|bool $failed forced error message
947	 * @param bool $output false suppress any function output
948	 * @return integer|bool db::db_Query result
949	 */
950	 // TODO - This function often needs to be available BEFORE header.php is loaded.
951	 // It has been copied from admin_update() in e107_admin/header.php
952
953	public function addAuto($update, $type = 'update', $success = false, $failed = false, $output = false)
954	{
955
956		$sql = e107::getDb();
957
958		if (($type == 'update' && $update) || ($type == 'insert' && $update !== false))
959		{
960			$this->add(($success ? $success : ($type == 'update' ? LAN_UPDATED : LAN_CREATED)), E_MESSAGE_SUCCESS);
961		}
962		elseif ($type == 'delete' && $update)
963		{
964			$this->add(($success ? $success : LAN_DELETED), E_MESSAGE_SUCCESS);
965		}
966		elseif (!$sql->getLastErrorNumber())
967		{
968			if ($type == 'update')
969			{
970				$this->add(LAN_NO_CHANGE.' '.LAN_TRY_AGAIN, E_MESSAGE_INFO);
971			}
972			elseif ($type == 'delete')
973			{
974				$this->add(LAN_DELETED_FAILED.' '.LAN_TRY_AGAIN, E_MESSAGE_INFO);
975			}
976		}
977		else
978		{
979			switch ($type)
980			{
981				case 'insert':
982					$msg = LAN_CREATED_FAILED;
983				break;
984				case 'delete':
985					$msg = LAN_DELETED_FAILED;
986				break;
987				default:
988					$msg = LAN_UPDATED_FAILED;
989				break;
990			}
991
992			$text = ($failed ? $failed : $msg." - ".LAN_TRY_AGAIN)."<br />".LAN_ERROR." ".$sql->getLastErrorNumber().": ".$sql->getLastErrorText();
993			$this->add($text, E_MESSAGE_ERROR);
994		}
995
996		if ($output) echo $this->render();
997		return $update;
998	}
999
1000
1001
1002}
1003
1004function show_emessage($mode, $message, $line = 0, $file = "") {
1005	global $tp;
1006
1007	// For critical errors where no theme is available.
1008	$errorHead = '
1009			<!doctype html>
1010		<html lang="en">
1011		<head>
1012		<meta charset="utf-8" />
1013		<title>Error</title>
1014		<link rel="stylesheet" media="all" type="text/css" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" />
1015		<link rel="stylesheet" media="all" type="text/css" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css" />
1016		<link rel="stylesheet" media="all" type="text/css" href="/e107_web/css/e107.css" />
1017		</head>
1018		<body >
1019		<div class="container" style="margin-top:100px">';
1020
1021
1022	$errorFoot = "</div></body></html>";
1023
1024
1025
1026	if(is_numeric($message))
1027	{
1028		if(!defined('e_LANGUAGE'))
1029		{
1030			define('e_LANGUAGE', 'English');
1031		}
1032
1033		if(!defined('e_LANGUAGEDIR'))
1034		{
1035			define('e_LANGUAGEDIR','e107_languages/');
1036		}
1037
1038		$path = e_LANGUAGEDIR.e_LANGUAGE."/lan_error.php";
1039
1040		if(is_readable($path))
1041		{
1042			include($path);
1043		}
1044
1045    //	include_lan(e_LANGUAGEDIR.e_LANGUAGE."/lan_error.php");
1046		global $mySQLdefaultdb;
1047
1048		$emessage[1] = "<b>".LAN_ERROR_25."</b>";
1049		$emessage[2] = "<b>".LAN_ERROR_26."</b>";
1050		$emessage[3] = "<b>".LAN_ERROR_27."</b>";
1051		$emessage[4] = "<b>".LAN_ERROR_28."</b>";
1052		$emessage[5] = LAN_ERROR_29;
1053		$emessage[6] = "<b>".LAN_ERROR_30."</b>";
1054		$emessage[7] = "<b>".$tp->lanVars(LAN_ERROR_31, $mySQLdefaultdb)."</b>";
1055		/*$emessage[8] = "
1056			<div style='text-align:center; font: 12px Verdana, Tahoma'><b>".LAN_ERROR_32." </b><br /><br />
1057			".chr(36)."ADMIN_DIRECTORY = \"e107_admin/\";<br />
1058			".chr(36)."FILES_DIRECTORY = \"e107_files/\";<br />
1059			".chr(36)."IMAGES_DIRECTORY = \"e107_images/\"; <br />
1060			".chr(36)."THEMES_DIRECTORY = \"e107_themes/\"; <br />
1061			".chr(36)."PLUGINS_DIRECTORY = \"e107_plugins/\"; <br />
1062			".chr(36)."HANDLERS_DIRECTORY = \"e107_handlers/\"; <br />
1063			".chr(36)."LANGUAGES_DIRECTORY = \"e107_languages/\"; <br />
1064			".chr(36)."HELP_DIRECTORY = \"e107_docs/help/\";  <br />
1065			".chr(36)."DOWNLOADS_DIRECTORY =  \"e107_files/downloads/\";\n
1066			</div>";*/
1067			//v2.x
1068		$emessage[8] = '<b>'.LAN_ERROR_32.' </b><br /><br /><pre>
1069$ADMIN_DIRECTORY     = "e107_admin/";
1070$IMAGES_DIRECTORY    = "e107_images/";
1071$THEMES_DIRECTORY    = "e107_themes/";
1072$PLUGINS_DIRECTORY   = "e107_plugins/";
1073$HANDLERS_DIRECTORY  = "e107_handlers/";
1074$LANGUAGES_DIRECTORY = "e107_languages/";
1075$HELP_DIRECTORY	     = "e107_docs/help/";
1076$MEDIA_DIRECTORY     = "e107_media/";
1077$SYSTEM_DIRECTORY    = "e107_system/";</pre>
1078
1079		';
1080
1081	}
1082
1083
1084	if (class_exists('e107table'))
1085	{
1086	  $ns = new e107table;
1087	}
1088
1089	switch($mode)
1090	{
1091		case "CRITICAL_ERROR" :
1092
1093			$message = !empty($emessage[$message]) ? $emessage[$message] : $message;
1094
1095			//FIXME - this breaks class2 pref check!!! ?
1096
1097		    if (is_readable(e_THEME.'error.html'))
1098			{
1099				require_once(e_THEME.'error.html');
1100				exit;
1101			}
1102
1103
1104			if(defined('e_LOG_CRITICAL'))
1105			{
1106				$date = date('r');
1107				@file_put_contents(e_LOG.'criticalError.log',$date."\t\t". strip_tags($message)."\n", FILE_APPEND);
1108				$message = LAN_ERROR_46; // "Check log for details";
1109				$line = null;
1110				$file = null;
1111			}
1112
1113
1114			if(!defined('HEADERF'))
1115			{
1116				echo $errorHead;
1117			}
1118
1119			echo "<div class='alert alert-block alert-error alert-danger' style='font: 11px verdana, tahoma, arial, helvetica, sans-serif;'><h4>CRITICAL ERROR: </h4>";
1120			echo (!empty($line)) ? "Line $line " : "";
1121			echo (!empty($file)) ? $file : "";
1122			echo "<div>".$message."</div>";
1123			echo "</div>";
1124
1125			if(!defined('FOOTERF'))
1126			{
1127				echo $errorFoot;
1128			}
1129
1130			break;
1131
1132		case "MESSAGE":
1133			if(strstr(e_SELF, "forum_post.php")) //FIXME Shouldn't be here.
1134			{
1135				return;
1136			}
1137			$ns->tablerender("", "<div class='alert alert-block' style='text-align:center'><b>{$message}</b></div>");
1138			break;
1139
1140		case "ADMIN_MESSAGE":
1141			$ns->tablerender("Admin Message", "<div class='alert'><b>{$message}</b></div>");
1142			break;
1143
1144		case "ALERT":
1145			$message = isset($emessage[$message]) ? $emessage[$message] : $message;
1146			echo "<noscript>$message</noscript><script type='text/javascript'>alert(\"".$tp->toJS($message)."\"); window.history.go(-1); </script>\n"; exit;
1147			break;
1148
1149		case "P_ALERT":
1150			echo "<script type='text/javascript'>alert(\"".$tp->toJS($message)."\"); </script>\n";
1151			break;
1152
1153		case 'POPUP':
1154
1155			$mtext = "<html><head><title>Message</title><link rel=stylesheet href=" . THEME . "style.css></head><body style=padding-left:2px;padding-right:2px;padding:2px;padding-bottom:2px;margin:0px;align;center marginheight=0 marginleft=0 topmargin=0 leftmargin=0><table width=100% align=center style=width:100%;height:99%padding-bottom:2px class=bodytable height=99% ><tr><td width=100% style='text-align:center'><b>--- Message ---</b><br /><br />".$message."<br /><br /><form><input class=button type=submit onclick=self.close() value = ok /></form></td></tr></table></body></html> ";
1156
1157			echo "
1158			<script type='text/javascript'>
1159			winl=(screen.width-200)/2;
1160			wint = (screen.height-100)/2;
1161			winProp = 'width=200,height=100,left='+winl+',top='+wint+',scrollbars=no';
1162			window.open('javascript:document.write(\"".$mtext."\");', \"message\", winProp);
1163			</script >";
1164
1165			break;
1166
1167	}
1168}
1169
1170
1171