1<?php
2/**
3 * e107 website system
4 *
5 * Copyright (C) 2008-2010 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 * Admin Log Handler
10 *
11 * USAGE:
12 *
13 * @example Log and Add to Message Handler: e107::getAdminLog()->addSuccess("Successfully executed")->save('PREF_01');
14 * @example Log Only: e107::getAdminLog()->addSuccess("Successfully executed",false)->save('PREF_01');
15 * @example Log Array Diff: e107::getAdminLog()->addArray($array1, $array2)->save('PREF_01');
16 * @example Log Array Diff and Add to Message Handler: e107::getAdminLog()->addArray($array1, $array2, E_MESSAGE_ERROR )->save('PREF_01');
17 *
18*/
19
20if (!defined('e107_INIT'))
21{
22	exit;
23}
24
25define('LOG_MESSAGE_NODISPLAY', 	'nodisplay');
26
27/**
28 *	Admin logging class.
29 *
30 *	@package	e107
31 *	@subpackage	e107_handlers
32 *	@version 	$Id$;
33 *  @author 	e107steved
34 */
35class e_admin_log
36{
37
38	/**
39	 * Contains default class options, plus any that are overidden by the constructor
40	 *
41	 * @var array
42	 */
43	protected	$_options = array('log_level'=>2, 'backtrace'=>false, );
44
45	protected	$rldb = NULL; // Database used by logging routine
46
47
48
49	protected 	$logFile = null;
50	/**
51	 * Log messages
52	 * @var array
53	 */
54	protected $_messages;
55
56
57	protected $_allMessages; // similar to $_messages except it is never flushed.
58
59
60	protected $_current_plugin = null;
61
62
63	/**
64	 * Constructor. Sets up constants and overwrites default options where set.
65	 *
66	 * @param array $options
67	 * @return null
68	 */
69	public function __construct($options = array())
70	{
71		if(!empty($options))
72		{
73			foreach ($options as $key=>$val)
74			{
75				$this->_options[$key] = $val;
76			}
77		}
78
79		define("E_LOG_INFORMATIVE", 0); // Minimal Log Level, including really minor stuff
80		define("E_LOG_NOTICE", 1); // More important than informative, but less important than notice
81		define("E_LOG_WARNING", 2); // Not anything serious, but important information
82		define("E_LOG_FATAL", 3); //  An event so bad your site ceased execution.
83		define("E_LOG_PLUGIN", 4); // Plugin information
84
85		// Logging actions
86		define("LOG_TO_ADMIN", 1);
87		define("LOG_TO_AUDIT", 2);
88		define("LOG_TO_ROLLING", 4);
89
90		// User audit logging (intentionally start at 10 - stick to 2 digits)
91		// The last two digits must match that for the corresponding log message
92		define('USER_AUDIT_ADMIN', 10); // User data changed by admin
93		define('USER_AUDIT_SIGNUP', 11); // User signed up
94		define('USER_AUDIT_EMAILACK', 12); // User responded to registration email
95		define('USER_AUDIT_LOGIN', 13); // User logged in
96		define('USER_AUDIT_LOGOUT', 14); // User logged out
97		define('USER_AUDIT_NEW_DN', 15); // User changed display name
98		define('USER_AUDIT_NEW_PW', 16); // User changed password
99		define('USER_AUDIT_NEW_EML', 17); // User changed email
100		define('USER_AUDIT_PW_RES', 18); // Password reset/resent activation email
101		define('USER_AUDIT_NEW_SET', 19); // User changed other settings
102		define('USER_AUDIT_ADD_ADMIN', 20); // User added by admin
103		define('USER_AUDIT_MAIL_BOUNCE', 21); // User mail bounce
104		define('USER_AUDIT_BANNED', 22); // User banned
105		define('USER_AUDIT_BOUNCE_RESET', 23); // User bounce reset
106		define('USER_AUDIT_TEMP_ACCOUNT', 24); // User temporary account
107
108		// Init E_MESSAGE_* constants if not already done
109		// e107::getMessage(); - just include, message handler is creating session in construct
110		// it breaks stuff (see class2 - language detection and comments)
111		require_once(e_HANDLER.'message_handler.php');
112		$this->_messages = array();
113		$this->_allMessages = array();
114
115		return null;
116	}
117
118	/**
119	 * @DEPRECATED
120	 * BC Alias of add();
121	 */
122	public function log_event($event_title, $event_detail, $event_type = E_LOG_INFORMATIVE , $event_code = '')
123	{
124		return $this->add($event_title, $event_detail, $event_type, $event_code);
125	}
126
127
128	/**
129	 * Save all logs in the queue to the database and render any unhidden messages with the message handler.
130	 * @see alias flushMessages() method below.
131	 * @param string $logTitle - title for log entry eg. 'PREF_01'
132	 * @param int $logImportance [optional] default E_LOG_INFORMATIVE - passed directly to admin log
133	 * @param string $logEventCode [optional] - passed directly to admin log
134	 * @param string $mstack [optional] message stack passed to message handler
135	 * @param int LOG_TO_ADMIN|LOG_TO_ROLLING|LOG_TO_AUDIT
136	 * @return \e_admin_log
137	 */
138	public function save($logTitle, $logImportance = E_LOG_INFORMATIVE, $logEventCode = '', $mstack = false, $target = LOG_TO_ADMIN)
139	{
140		return $this->flushMessages($logTitle, $logImportance, $logEventCode, $mstack, $target);
141	}
142
143
144
145
146	/**
147	 * Add and Save an event into the admin, rolling or user log.
148	 * @param string $event_title
149	 * @param mixed $event_details
150	 * @param integer $event_type [optional] Log level eg. E_LOG_INFORMATIVE, E_LOG_NOTICE, E_LOG_WARNING, E_LOG_FATAL
151	 * @param string $event_code [optional] - eg. 'BOUNCE'
152	 * @param integer $target [optional]  LOG_TO_ADMIN, LOG_TO_AUDIT, LOG_TO_ROLLING
153	 * @param array $user - user to attribute the log to. array('user_id'=>2, 'user_name'=>'whoever');
154	 * @return e_admin_log
155	 *
156	 * Alternative admin log entry point - compatible with legacy calls, and a bit simpler to use than the generic entry point.
157	 * ($eventcode has been added - give it a reference to identify the source module, such as 'NEWS_12' or 'ECAL_03')
158	 * We also log everything (unlike 0.7, where admin log and debug stuff were all mixed up together)
159	 *
160	 * For multi-lingual logging (where the event title is shown in the language of the current user), LAN defines may be used in the title
161	 *
162	 * For generic calls, leave $event_code as empty, and specify a constant string STRING_nn of less than 10 characters for the event title
163	 * Typically the 'STRING' part of the name defines the area originating the log event, and the 'nn' is a numeric code
164	 * This is stored as 'LAN_AL_STRING_NN', and must be defined in a language file which is loaded during log display.
165	 *
166
167	 */
168	public function add($event_title, $event_detail, $event_type = E_LOG_INFORMATIVE , $event_code = '', $target = LOG_TO_ADMIN, $userData=null )
169	{
170		if ($event_code == '')
171		{
172			if (strlen($event_title) <= 12)
173			{ // Assume the title is actually a reference to the event
174				$event_code = $event_title;
175				$event_title = 'LAN_AL_'.$event_title;
176			}
177			else
178			{
179				$event_code = 'ADMIN';
180			}
181		}
182
183		if (!is_array($event_detail))
184		{
185			// auto-format long details - TODO - shrink details on administration log page, expand/show in DHTML window full details.
186			$event_detail = str_replace("\n", "[!br!]", $event_detail);
187		}
188
189		if ($this->_options['backtrace'] == true)
190		{
191			$event_detail .= "\n\n".debug_backtrace(false);
192		}
193
194
195		$this->e_log_event($event_type, -1, $event_code, $event_title, $event_detail, FALSE, $target, $userData);
196
197		return $this;
198	}
199
200	/**
201	 * Alias for deprecated e_log_event
202	 * @param        $importance
203	 * @param        $source_call
204	 * @param string $eventcode
205	 * @param string $event_title
206	 * @param string $explain
207	 * @param bool   $finished
208	 * @param int    $target_logs
209	 * @param null   $userData
210	 * @return null
211	 */
212	public function addEvent($importance, $source_call, $eventcode = "GEN", $event_title = "Untitled", $explain = "", $finished = FALSE, $target_logs = LOG_TO_AUDIT, $userData=null )
213	{
214		return $this->e_log_event($importance, $source_call, $eventcode, $event_title, $explain, $finished, $target_logs, $userData);
215
216	}
217
218	/**
219	 Generic log entry point
220	 -----------------------
221	 Example call: (Deliberately pick separators that shouldn't be in file names)
222	 e_log_event(E_LOG_NOTICE,__FILE__."|".__FUNCTION__."@".__LINE__,"ECODE","Event Title","explanatory message",FALSE,LOG_TO_ADMIN);
223	 or:
224	 e_log_event(E_LOG_NOTICE,debug_backtrace(),"ECODE","Event Title","explanatory message",TRUE,LOG_TO_ROLLING);
225	 *
226	 *	@param int $importance - importance of event - 0..4 or so
227	 *	@param mixed $source_call - either:	string identifying calling file/routine
228	 *		or:		a number 0..9 identifying info to log from debug_backtrace()
229	 *		or:		empty string, in which case first entry from debug_backtrace() logged
230	 *		or:		an array, assumed to be from passing debug_backtrace() as a parameter, in which case relevant
231	 *				 information is extracted and the argument list from the first entry logged
232	 *		or:		-1, in which case no information logged
233	 *	@param string $eventcode - abbreviation listing event type
234	 *	@param string $event_title - title of event - pass standard 'LAN_ERROR_nn' defines to allow language translation
235	 *	@param string $explain - detail of event
236	 *	@param bool $finished - if TRUE, aborts execution
237	 *	@param int $target_logs - flags indicating which logs to update - if entry to be posted in several logs, add (or 'OR') their defines:
238	 *		 LOG_TO_ADMIN		- admin log
239	 *		 LOG_TO_AUDIT		- audit log
240	 *		 LOG_TO_ROLLING		- rolling log
241	 * @param array $userData - attribute user to log entry. array('user_id'=>2, 'user_name'=>'whatever');
242	 *	@return null
243
244	 * @todo - check microtime() call
245	 * @deprecated - use add() method instead or addEvent() as a direct replacement.
246	 */
247	public function e_log_event($importance, $source_call, $eventcode = "GEN", $event_title = "Untitled", $explain = "", $finished = FALSE, $target_logs = LOG_TO_AUDIT, $userData=null )
248	{
249		$e107 = e107::getInstance();
250		$pref = e107::getPref();
251		$tp = e107::getParser();
252
253		list($time_usec, $time_sec) = explode(" ", microtime(FALSE)); // Log event time immediately to minimise uncertainty
254		$time_usec = $time_usec * 1000000;
255
256		if ($this->rldb == NULL)
257			$this->rldb = e107::getDb('adminlog'); // Better use our own db - don't know what else is going on
258
259		if (is_bool($target_logs))
260		{ // Handle the legacy stuff for now - some old code used a boolean to select admin or rolling logs
261			$target_logs = $target_logs ? LOG_TO_ADMIN : LOG_TO_ROLLING;
262		}
263
264		//---------------------------------------
265		// Calculations common to all logs
266		//---------------------------------------
267
268		$userid 		= deftrue('USER') ? USERID : 0;
269		$userstring 	= deftrue('USER') ? USERNAME : 'LAN_ANONYMOUS';
270		$userIP 		= e107::getIPHandler()->getIP(FALSE);
271
272		if(!empty($userData['user_id']))
273		{
274			$userid = $userData['user_id'];
275		}
276
277		if(!empty($userData['user_name']))
278		{
279			$userstring  = $userData['user_name'];
280		}
281
282		if(!empty($userData['user_ip']))
283		{
284			$userIP  = $userData['user_ip'];
285		}
286
287		$importance 	= $tp->toDB($importance, true, false, 'no_html');
288		$eventcode 		= $tp->toDB($eventcode, true, false, 'no_html');
289
290		if (is_array($explain))
291		{
292			/*
293			$line = '';
294			$spacer = '';
295			foreach ($explain as $k=>$v)
296			{
297				$line .= $spacer.$k.'=>'.$v;
298				$spacer = '[!br!]';
299			}
300			$explain = $line;
301			unset($line);
302			*/
303			$explain = str_replace("\n",'[!br!]',print_r($explain,true));
304
305		}
306
307
308		$explain = e107::getDb()->escape($tp->toDB($explain, true, false, 'no_html'));
309		$event_title = $tp->toDB($event_title, true, false, 'no_html');
310
311		//---------------------------------------
312		// 			Admin Log
313		//---------------------------------------
314		if ($target_logs & LOG_TO_ADMIN) // Admin log - assume all fields valid
315		{
316		//	$qry = " null, ".intval($time_sec).','.intval($time_usec).", '{$importance}', '{$eventcode}', {$userid}, '{$userIP}', '{$event_title}', '{$explain}' ";
317
318			$adminLogInsert = array(
319				'dblog_id'			=> null,
320				'dblog_type'		=> $importance,
321				'dblog_eventcode'	=> $eventcode,
322				'dblog_datestamp'	=> time(),
323				'dblog_microtime'	=> intval($time_usec),
324				'dblog_user_id'		=> $userid,
325				'dblog_ip'			=> $userIP,
326				'dblog_title'		=> $event_title,
327				'dblog_remarks'		=> $explain
328			);
329
330			$this->rldb->insert("admin_log", $adminLogInsert);
331		}
332
333		//---------------------------------------
334		// 			Audit Log
335		//---------------------------------------
336		// Add in audit log here
337
338		//---------------------------------------
339		// 			Rolling Log
340		//---------------------------------------
341		if (($target_logs & LOG_TO_ROLLING) && vartrue($pref['roll_log_active']))
342		{ //	Rolling log
343
344			// 	Process source_call info
345			//---------------------------------------
346			if (is_numeric($source_call) && ($source_call >= 0))
347			{
348				$back_count = 1;
349				$i = 0;
350				if (is_numeric($source_call) || ($source_call == ''))
351				{
352					$back_count = $source_call + 1;
353					$source_call = debug_backtrace();
354					$i = 1; // Don't want to print the entry parameters to this function - we know all that!
355				}
356			}
357
358			if (is_array($source_call))
359			{ // Print the debug_backtrace() array
360				while ($i < $back_count)
361				{
362					$source_call[$i]['file'] = $e107->fix_windows_paths($source_call[$i]['file']); // Needed for Windoze hosts.
363					$source_call[$i]['file'] = str_replace($e107->file_path, "", $source_call[$i]['file']); // We really just want a e107 root-relative path. Strip out the root bit
364					$tmp = $source_call[$i]['file']."|".$source_call[$i]['class'].$source_call[$i]['type'].$source_call[$i]['function']."@".$source_call[$i]['line'];
365					foreach ($source_call[$i]['args'] as $k=>$v)
366					{ // Add in the arguments
367						$explain .= "[!br!]".$k."=".$v;
368					}
369					$i++;
370					if ($i < $back_count)
371						$explain .= "[!br!]-------------------";
372					if (!isset($tmp1))
373						$tmp1 = $tmp; // Pick off the immediate caller as the source
374				}
375				if (isset($tmp1)) $source_call = $tmp1;
376				else $source_call = 'Root level';
377			}
378			else
379			{
380				$source_call = $e107->fix_windows_paths($source_call); // Needed for Windoze hosts.
381				$source_call = str_replace($e107->file_path, "", $source_call); // We really just want a e107 root-relative path. Strip out the root bit
382				$source_call = $tp->toDB($source_call, true, false, 'no_html');
383			}
384			// else $source_call is a string
385
386			// Save new rolling log record
387			$this->rldb->insert("dblog", "0, ".intval($time_sec).', '.intval($time_usec).", '{$importance}', '{$eventcode}', {$userid}, '{$userstring}', '{$userIP}', '{$source_call}', '{$event_title}', '{$explain}' ");
388
389			// Now delete any old stuff
390			if(!empty($pref['roll_log_days']))
391			{
392				$days = intval($pref['roll_log_days']);
393				$this->rldb->delete("dblog", "dblog_datestamp < '".intval(time() - ($days * 86400))."' ");
394			}
395		}
396
397		if ($finished)
398			exit; // Optional abort for all logs
399
400		return null;
401	}
402
403	public function setCurrentPlugin($plugdir)
404	{
405		$this->_current_plugin = $plugdir;
406
407		return $this;
408	}
409
410	/**--------------------------------------
411	 *		USER AUDIT ENTRY
412	 *--------------------------------------
413	 *	Log user-related events
414	 *	@param int $event_code is a defined constant (see above) which specifies the event
415	 *	@param array $event_data is an array of data fields whose keys and values are logged (usually user data, but doesn't have to be - can add messages here)
416	 *	@param int $id
417	 *	@param string $u_name
418	 *		both $id and $u_name are left blank except for admin edits and user login, where they specify the id and login name of the 'target' user
419	 *
420	 *	@return bool
421	 */
422	function user_audit($event_type, $event_data, $id = '', $u_name = '')
423	{
424		list($time_usec, $time_sec) = explode(" ", microtime()); // Log event time immediately to minimise uncertainty
425
426		$time_usec = $time_usec * 1000000;
427
428		if(!is_numeric($event_type))
429		{
430			$title = "User Audit Event-Type Failure: ";
431			$title .= (string) $event_type;
432			$debug = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS,4);
433			$debug[0] = e_REQUEST_URI;
434
435			$this->e_log_event(4, $debug[1]['file']."|".$debug[1]['function']."@".$debug[1]['line'], "USERAUDIT", $title, $debug, FALSE);
436			return false;
437		}
438
439		// See whether we should log this
440		$user_logging_opts = e107::getConfig()->get('user_audit_opts');
441
442		if (!isset($user_logging_opts[$event_type]))  // Finished if not set to log this event type
443		{
444			return false;
445		}
446
447		if(empty($event_data))
448		{
449			$backt = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS,4);
450			$event_data = $backt;
451		}
452
453
454		if($this->rldb == null)
455		{
456			$this->rldb = e107::getDb('rldb'); // Better use our own db - don't know what else is going on
457		}
458
459		if(!empty($id))
460		{
461			 $userid = $id;
462		}
463		else
464		{
465			 $userid = (USER === true) ? USERID : 0;
466		}
467
468		if(!empty($u_name))
469		{
470			 $userstring = $u_name;
471		}
472		else
473		{
474			$userstring = (USER === true ? USERNAME : "LAN_ANONYMOUS");
475		}
476
477		$userIP = e107::getIPHandler()->getIP(false);
478
479		$eventcode = 'USER_'.$event_type;
480
481		$title = 'LAN_AUDIT_LOG_0'.$event_type; // This creates a string which will be displayed as a constant
482
483		$insertQry = array(
484			'dblog_id'          => 0,
485			'dblog_datestamp'   => intval($time_sec),
486			'dblog_microtime'   => intval($time_usec),
487			'dblog_eventcode'   => $eventcode,
488			'dblog_user_id'     => $userid,
489			'dblog_user_name'   => $userstring,
490			'dblog_ip'          => $userIP,
491			'dblog_title'       => $title,
492			'dblog_remarks'     => print_r($event_data,true),
493		);
494
495		if($this->rldb->insert("audit_log", $insertQry))
496		{
497			return true;
498		}
499
500		return false;
501	}
502
503
504	/* Legacy function probably not needed
505	function get_log_events($count = 15, $offset)
506	{
507		global $sql;
508		$count = intval($count);
509		return "Not implemented yet";
510	}
511	*/
512
513
514
515	/**
516	 * Removes all events older than $days, or truncates the table if $days == false
517	 *
518	 * @param integer|false $days
519	 * @return void
520	 */
521	public function purge_log_events($days)
522	{
523		global $sql;
524		if ($days == false)
525		{ // $days is false, so truncate the log table
526			$sql->gen("TRUNCATE TABLE #dblog ");
527		}
528		else
529		{ // $days is set, so remove all entries older than that.
530			$days = intval($days);
531			$mintime = $days * 24 * 60 * 60;
532			$time = time() - $mintime;
533			$sql->db_Delete("dblog", "WHERE `dblog_datestamp` < {$time}", true);
534		}
535	}
536
537	//--------------------------------------
538	//		HELPER ROUTINES
539	//--------------------------------------
540	/**
541	 *	Generic routine to log changes to an array. Only elements in $new are checked
542	 *
543	 *	@param array $new - most recent data being saved
544	 *	@param array $old existing data - array is updated with changes, but not saved anywhere
545	 *	@param string $event - LAN define or string used as title in log
546	 *
547	 *	@return bool true if changes found and logged, false otherwise.
548	 */
549	function logArrayDiffs($new, $old, $event, $logNow = true)
550	{
551		// $changes = array();
552
553		$changes = array_diff_recursive($new,$old);
554
555		if (count($changes))
556		{
557			if($logNow)
558			{
559				$this->add($event, print_r($changes,true), E_LOG_INFORMATIVE, '');
560			}
561			else
562			{
563				$this->logMessage($changes, LOG_MESSAGE_NODISPLAY, E_MESSAGE_INFO);
564			}
565
566			return TRUE;
567		}
568
569		return FALSE;
570	}
571
572
573	/**
574	 *	Logs an entry with all the data from an array, one field per line.
575	 *  @deprecated
576	 *	@param string $event - LAN define or string used as title in log
577	 *	@param array $target - data to be logged
578	 *	@param string $extra - if non-empty, it goes on the first line.
579	 *	@param array $niceNames - Normally data is logged in the format keyname=>value, one per line.
580	 *		If the $niceName array exists and has a definition, the 'nice Name' is displayed instead of the key name
581	 *
582	 *	@return null
583	 */
584	public function logArrayAll($event, $target, $extra = '', $niceNames = NULL)
585	{
586
587		if($extra == '' && $niceNames == null)
588		{
589			return $this->add($event, $target, E_LOG_INFORMATIVE, '');	// supports arrays
590
591		}
592
593
594		$logString = '';
595		if ($extra)
596		{
597			$logString = $extra.'[!br!]';
598		}
599		$spacer = '';
600		$checkNice = ($niceNames != NULL) && is_array($niceNames);
601		foreach ($target as $k=>$v)
602		{
603			if ($checkNice && isset($niceNames[$k]['niceName']))
604			{
605				$logString .= $spacer.$niceNames[$k]['niceName'].'=>'.$v;
606			}
607			else
608			{
609				$logString .= $spacer.$k.'=>'.$v;
610			}
611			$spacer = '[!br!]';
612		}
613		$this->add($event, $logString, E_LOG_INFORMATIVE, '');
614
615		return null;
616	}
617
618	/**
619	 *	The next two routines accept and buffers messages which are destined for both admin log and message handler
620	 */
621
622	/**
623	 *	Add a message to the queue
624	 *
625	 *	@param string|array $text - the message text for logging/display
626	 *	@param int $type - the 'importance' of the message. E_MESSAGE_SUCCESS|E_MESSAGE_ERROR|E_MESSAGE_INFO|E_MESSAGE_DEBUG|E_MESSAGE_NODISPLAY
627	 *				(Values as used in message handler, apart from the last, which causes the message to not be passed to the message handler
628	 *	@param boolean|int $logLevel - TRUE to give same importance as for message display. FALSE to not log.
629	 *										one of the values specified for $mesLevel to determine the prefix attached to the log message
630	 *  @param boolean $session add session message
631	 *
632	 *	@return e_admin_log
633	 */
634	public function logMessage($text, $type = '', $logLevel = TRUE, $session = FALSE)
635	{
636
637		if(is_array($text))
638		{
639			$text = print_r($text,true);
640		}
641		elseif(empty($text))
642		{
643			$bt = debug_backtrace(true);
644			e107::getMessage()->addDebug("Log Message was empty: ".print_a($bt[1],true));
645			return $this;	// changing to text will break chained methods.
646		}
647
648		if(!$type) $type = E_MESSAGE_INFO;
649		if($logLevel === TRUE) $logLevel = $type;
650
651		$logArray = array('message' => $text, 'dislevel' => $type, 'loglevel' => $logLevel, 'session' => $session, 'time'=>time());
652
653		$this->_messages[] = $logArray;
654		$this->_allMessages[] = $logArray;
655
656		return $this;
657	}
658
659
660
661	/**
662	 * @DEPRECATED
663	 * BC Alias for addSuccess();
664	 */
665	public function logSuccess($text, $message = true, $session = false)
666	{
667		return $this->addSuccess($text,$message,$session);
668	}
669
670
671
672	/**
673	 * @DEPRECATED
674	 * BC Alias for addError();
675	 */
676	public function logError($text, $message = true, $session = false)
677	{
678		return $this->addError($text,$message,$session);
679	}
680
681
682	/**
683	 * Add a success message to the log queue
684	 *
685	 * @param string|array $text
686	 * @param boolean $message if true - register with eMessage handler
687	 * @param boolean $session add session message
688	 * @return e_admin_log
689	 */
690	public function addSuccess($text, $message = true, $session = false)
691	{
692		return $this->logMessage($text, ($message ? E_MESSAGE_SUCCESS : LOG_MESSAGE_NODISPLAY), E_MESSAGE_SUCCESS, $session);
693	}
694
695
696	/**
697	 * Add an error message to the log queue
698	 *
699	 * @param string $text
700	 * @param boolean $message if true (default) - register with eMessage handler, set to false to hide.
701	 * @param boolean $session add session message
702	 * @return e_admin_log
703	 */
704	public function addError($text, $message = true, $session = false)
705	{
706		return $this->logMessage($text, ($message ? E_MESSAGE_ERROR : LOG_MESSAGE_NODISPLAY), E_MESSAGE_ERROR, $session);
707	}
708
709
710	/**
711	 * Add an Debug message to the log queue
712	 *
713	 * @param string $text
714	 * @param boolean $message if true (default) - register with eMessage handler, set to false to hide .
715	 * @param boolean $session add session message
716	 * @return e_admin_log
717	 */
718	public function addDebug($text, $message = true, $session = false)
719	{
720		return $this->logMessage($text, ($message ? E_MESSAGE_DEBUG : LOG_MESSAGE_NODISPLAY), E_MESSAGE_DEBUG, $session);
721	}
722
723
724	/**
725	 * Add an Warning message to the log queue
726	 *
727	 * @param string $text
728	 * @param boolean $message if true (default) - register with eMessage handler, set to false to hide.
729	 * @param boolean $session add session message
730	 * @return e_admin_log
731	 */
732	public function addWarning($text, $message = true, $session = false)
733	{
734		return $this->logMessage($text, ($message ? E_MESSAGE_WARNING : LOG_MESSAGE_NODISPLAY), E_MESSAGE_WARNING, $session);
735	}
736
737
738	/**
739	 * Add an array to the log queue
740	 * @param $array
741	 * @param $oldArray (optional) - when included, only the changes between the arrays is saved.
742	 * @param $type (optional) default: LOG_MESSAGE_NODISPLAY. or E_MESSAGE_WARNING, E_MESSAGE_DEBUG, E_MESSAGE_SUCCESS
743	 */
744	public function addArray($array, $oldArray= null, $type = LOG_MESSAGE_NODISPLAY , $session = false)
745	{
746		if(is_array($oldArray))
747		{
748			$text = array_diff_recursive($array,$oldArray); // Located in core_functions.php
749			if(count($text) < 1)
750			{
751				$text = "No differences found";
752			}
753
754		}
755		else
756		{
757			$text = $array;
758		}
759
760		return $this->logMessage($text, $type, $type, $session);
761	}
762
763	/**
764	 *	Empty the messages - pass to both admin log and message handler
765	 *
766	 *	@param string $logTitle - title for log entry
767	 *	@param int $logImportance - passed directly to admin log
768	 *	@param string $logEventCode - passed directly to admin log
769	 *	@param string $mstack [optional] message stack passed to message handler
770	 *	@return e_admin_log
771	 */
772	public function flushMessages($logTitle, $logImportance = E_LOG_INFORMATIVE, $logEventCode = '', $mstack = false, $target =LOG_TO_ADMIN)
773	{
774		$mes = e107::getMessage();
775
776		$resultTypes = array(E_MESSAGE_SUCCESS => 'Success', E_MESSAGE_ERROR => 'Fail');	// Add LANS here. Could add other codes
777		$separator = '';
778		$logString = '';
779		foreach ($this->_messages as $m)
780		{
781			if ($m['loglevel'] !== FALSE)
782			{
783				$logString .= $separator;
784				if ($m['loglevel'] == LOG_MESSAGE_NODISPLAY) { $logString .= '  '; }		// Indent supplementary messages
785			// Not sure about next line - might want to log the <br /> as text, rather than it forcing a newline
786				$logString .= strip_tags(str_replace(array('<br>', '<br/>', '<br />'), '[!br!]', $m['message']));
787				if (isset($resultTypes[$m['loglevel']]))
788				{
789					$logString .= ' - '.$resultTypes[$m['loglevel']];
790				}
791				$separator = '[!br!]';
792			}
793			if ($m['dislevel'] != LOG_MESSAGE_NODISPLAY)
794			{
795				if($mstack)
796				{
797					$mes->addStack($m['message'], $mstack, $m['dislevel'], $m['session']);
798					// move to main stack OUTSIDE if needed
799				}
800				else $mes->add($m['message'], $m['dislevel'], $m['session']);
801			}
802		}
803		$this->add($logTitle, $logString, $logImportance, $logEventCode, $target);
804		$this->_messages = array();		// Clear the memory for reuse
805
806		return $this;
807	}
808
809
810
811
812
813	/**
814	 * Clear all messages in 'memory'.
815	 */
816	public function clear()
817	{
818		$this->_messages = array();
819
820		return $this;
821	}
822
823
824	/**
825	 * Save Message stack to File.
826	 */
827	private function saveToFile($logTitle='', $append=false, $opts = array())
828	{
829		if($this->logFile == null)
830		{
831			 return null;
832		}
833
834		if(count($this->_allMessages))
835		{
836			$head = "  e107 CMS Log file : ".$logTitle."   ".date('Y-m-d_H-i-s')."\n";
837			$head .= "-------------------------------------------------------------------------------------------\n\n";
838		}
839		else
840		{
841			return null;
842		}
843
844		$text = '';
845
846		foreach($this->_allMessages as $m)
847		{
848			$text .= date('Y-m-d H:i:s', $m['time'])."  \t".str_pad($m['loglevel'],10," ",STR_PAD_RIGHT)."\t".strip_tags($m['message'])."\n";
849		}
850
851		$date = ($append == true) ? date('Y-m-d') : date('Y-m-d_H-i-s').'_'.crc32($text);
852
853
854
855		$dir = e_LOG;
856
857		if(empty($this->_current_plugin))
858		{
859			$this->_current_plugin = deftrue('e_CURRENT_PLUGIN');
860		}
861
862		if(!empty($this->_current_plugin)) // If it's a plugin, create a subfolder.
863		{
864			$dir = e_LOG.$this->_current_plugin."/";
865
866			if(!is_dir($dir))
867			{
868				mkdir($dir,0755);
869			}
870		}
871
872		$fileName = $dir.$date."_".$this->logFile.".log";
873
874		if(!empty($opts['filename']))
875		{
876			$fileName = $dir.basename($opts['filename']);
877		}
878
879		if($append == true)
880		{
881			$app = FILE_APPEND;
882			if(!file_exists($fileName))
883			{
884				$text = $head . $text;
885			}
886		}
887		else
888		{
889			$app = null;
890			$text = $head . $text;
891		}
892
893		if(file_put_contents($fileName, $text, $app))
894		{
895			$this->_allMessages = array();
896			$this->_current_plugin = null;
897			return $this->logFile;
898		}
899		elseif(getperms('0') && E107_DEBUG_LEVEL > 0)
900		{
901			e107::getMessage()->addDebug("Couldn't Save to Log File: ".$fileName);
902		}
903
904		$this->_current_plugin = null;
905
906		return false;
907	}
908
909
910
911
912	/**
913	 * Set and save accumulated log to a file.
914	 * Use addDebug(), addError() or addSuccess() prior to executing.
915	 * @param string name without the extension. (ie. date prefix and .log suffix will be added automatically)
916	 * @param string Title for use inside the Log file
917	 * @param boolean true = append to file, false = new file each save.
918	 */
919	public function toFile($name, $logTitle='',$append=false, $opts=array())
920	{
921
922		$this->logFile	= $name;
923		$file = $this->saveToFile($logTitle,$append,$opts);
924
925		$this->logFile = null;
926		return $file;
927	}
928
929
930}
931