qa-event-logger.php 6.96 KB
Newer Older
Scott committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
<?php
/*
	Question2Answer by Gideon Greenspan and contributors
	http://www.question2answer.org/

	File: qa-plugin/event-logger/qa-event-logger.php
	Description: Event module class for event logger plugin


	This program is free software; you can redistribute it and/or
	modify it under the terms of the GNU General Public License
	as published by the Free Software Foundation; either version 2
	of the License, or (at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	More about this license: http://www.question2answer.org/license.php
*/

Scott committed
23 24 25 26 27 28 29 30
class qa_event_logger
{
	public function init_queries($table_list)
	{
		if (qa_opt('event_logger_to_database')) {
			$tablename=qa_db_add_table_prefix('eventlog');

			if (!in_array($tablename, $table_list)) {
31
				// table does not exist, so create it
Scott committed
32 33 34 35 36
				require_once QA_INCLUDE_DIR.'app/users.php';
				require_once QA_INCLUDE_DIR.'db/maxima.php';

				return 'CREATE TABLE ^eventlog ('.
					'datetime DATETIME NOT NULL,'.
37
					'ipaddress VARCHAR (45) CHARACTER SET ascii,'.
Scott committed
38 39 40 41 42 43 44 45 46 47
					'userid '.qa_get_mysql_user_column_type().','.
					'handle VARCHAR('.QA_DB_MAX_HANDLE_LENGTH.'),'.
					'cookieid BIGINT UNSIGNED,'.
					'event VARCHAR (20) CHARACTER SET ascii NOT NULL,'.
					'params VARCHAR (800) NOT NULL,'.
					'KEY datetime (datetime),'.
					'KEY ipaddress (ipaddress),'.
					'KEY userid (userid),'.
					'KEY event (event)'.
				') ENGINE=MyISAM DEFAULT CHARSET=utf8';
48 49 50 51 52 53 54
			} else {
				// table exists: check it has the correct schema
				$column = qa_db_read_one_assoc(qa_db_query_sub('SHOW COLUMNS FROM ^eventlog WHERE Field="ipaddress"'));
				if (strtolower($column['Type']) !== 'varchar(45)') {
					// upgrade to handle IPv6
					return 'ALTER TABLE ^eventlog MODIFY ipaddress VARCHAR(45) CHARACTER SET ascii';
				}
Scott committed
55 56
			}
		}
57 58

		return array();
Scott committed
59
	}
Scott committed
60 61


Scott committed
62 63
	public function admin_form(&$qa_content)
	{
Scott committed
64

Scott committed
65
	//	Process form input
Scott committed
66

Scott committed
67
		$saved=false;
Scott committed
68

Scott committed
69 70 71 72 73
		if (qa_clicked('event_logger_save_button')) {
			qa_opt('event_logger_to_database', (int)qa_post_text('event_logger_to_database_field'));
			qa_opt('event_logger_to_files', qa_post_text('event_logger_to_files_field'));
			qa_opt('event_logger_directory', qa_post_text('event_logger_directory_field'));
			qa_opt('event_logger_hide_header', !qa_post_text('event_logger_hide_header_field'));
Scott committed
74

Scott committed
75 76
			$saved=true;
		}
Scott committed
77

Scott committed
78
	//	Check the validity of the currently entered directory (if any)
Scott committed
79

Scott committed
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
		$directory=qa_opt('event_logger_directory');

		$note=null;
		$error=null;

		if (!strlen($directory))
			$note='Please specify a directory that is writable by the web server.';
		elseif (!file_exists($directory))
			$error='This directory cannot be found. Please enter the full path.';
		elseif (!is_dir($directory))
			$error='This is a file. Please enter the full path of a directory.';
		elseif (!is_writable($directory))
			$error='This directory is not writable by the web server. Please choose a different directory, use chown/chmod to change permissions, or contact your web hosting company for assistance.';

	//	Create the form for display
Scott committed
95

Scott committed
96 97 98 99 100 101 102 103 104 105 106 107 108 109
		qa_set_display_rules($qa_content, array(
			'event_logger_directory_display' => 'event_logger_to_files_field',
			'event_logger_hide_header_display' => 'event_logger_to_files_field',
		));

		return array(
			'ok' => ($saved && !isset($error)) ? 'Event log settings saved' : null,

			'fields' => array(
				array(
					'label' => 'Log events to <code>'.QA_MYSQL_TABLE_PREFIX.'eventlog</code> database table',
					'tags' => 'name="event_logger_to_database_field"',
					'value' => qa_opt('event_logger_to_database'),
					'type' => 'checkbox',
Scott committed
110 111
				),

Scott committed
112 113 114 115 116
				array(
					'label' => 'Log events to daily log files',
					'tags' => 'name="event_logger_to_files_field" id="event_logger_to_files_field"',
					'value' => qa_opt('event_logger_to_files'),
					'type' => 'checkbox',
Scott committed
117 118
				),

Scott committed
119 120 121 122 123 124 125 126
				array(
					'id' => 'event_logger_directory_display',
					'label' => 'Directory for log files - enter full path:',
					'value' => qa_html($directory),
					'tags' => 'name="event_logger_directory_field"',
					'note' => $note,
					'error' => qa_html($error),
				),
Scott committed
127

Scott committed
128 129 130 131 132 133 134 135
				array(
					'id' => 'event_logger_hide_header_display',
					'label' => 'Include header lines at top of each log file',
					'type' => 'checkbox',
					'tags' => 'name="event_logger_hide_header_field"',
					'value' => !qa_opt('event_logger_hide_header'),
				),
			),
Scott committed
136

Scott committed
137 138 139 140 141 142 143 144
			'buttons' => array(
				array(
					'label' => 'Save Changes',
					'tags' => 'name="event_logger_save_button"',
				),
			),
		);
	}
Scott committed
145 146


Scott committed
147 148 149 150 151 152 153 154
	public function value_to_text($value)
	{
		if (is_array($value))
			$text='array('.count($value).')';
		elseif (strlen($value)>40)
			$text=substr($value, 0, 38).'...';
		else
			$text=$value;
Scott committed
155

Scott committed
156 157
		return strtr($text, "\t\n\r", '   ');
	}
Scott committed
158 159


Scott committed
160 161 162 163
	public function process_event($event, $userid, $handle, $cookieid, $params)
	{
		if (qa_opt('event_logger_to_database')) {
			$paramstring='';
Scott committed
164

Scott committed
165 166
			foreach ($params as $key => $value)
				$paramstring.=(strlen($paramstring) ? "\t" : '').$key.'='.$this->value_to_text($value);
Scott committed
167

Scott committed
168 169 170 171 172 173
			qa_db_query_sub(
				'INSERT INTO ^eventlog (datetime, ipaddress, userid, handle, cookieid, event, params) '.
				'VALUES (NOW(), $, $, $, #, $, $)',
				qa_remote_ip_address(), $userid, $handle, $cookieid, $event, $paramstring
			);
		}
Scott committed
174

Scott committed
175
		if (qa_opt('event_logger_to_files')) {
Scott committed
176

Scott committed
177
		//	Substitute some placeholders if certain information is missing
Scott committed
178

Scott committed
179 180
			if (!strlen($userid))
				$userid='no_userid';
Scott committed
181

Scott committed
182 183
			if (!strlen($handle))
				$handle='no_handle';
Scott committed
184

Scott committed
185 186
			if (!strlen($cookieid))
				$cookieid='no_cookieid';
Scott committed
187

Scott committed
188 189 190
			$ip=qa_remote_ip_address();
			if (!strlen($ip))
				$ip='no_ipaddress';
Scott committed
191

Scott committed
192
		//	Build the log file line to be written
Scott committed
193

Scott committed
194 195 196 197 198 199 200 201 202
			$fixedfields=array(
				'Date' => date('Y\-m\-d'),
				'Time' => date('H\:i\:s'),
				'IPaddress' => $ip,
				'UserID' => $userid,
				'Username' => $handle,
				'CookieID' => $cookieid,
				'Event' => $event,
			);
Scott committed
203

Scott committed
204
			$fields=$fixedfields;
Scott committed
205

Scott committed
206 207
			foreach ($params as $key => $value)
				$fields['param_'.$key]=$key.'='.$this->value_to_text($value);
Scott committed
208

Scott committed
209
			$string=implode("\t", $fields);
Scott committed
210

Scott committed
211
		//	Build the full path and file name
Scott committed
212

Scott committed
213
			$directory=qa_opt('event_logger_directory');
Scott committed
214

Scott committed
215 216
			if (substr($directory, -1)!='/')
				$directory.='/';
Scott committed
217

Scott committed
218
			$filename=$directory.'q2a-log-'.date('Y\-m\-d').'.txt';
Scott committed
219

Scott committed
220
		//	Open, lock, write, unlock, close (to prevent interference between multiple writes)
Scott committed
221

Scott committed
222
			$exists=file_exists($filename);
Scott committed
223

Scott committed
224 225 226 227 228 229 230 231 232 233 234
			$file=@fopen($filename, 'a');

			if (is_resource($file)) {
				if (flock($file, LOCK_EX)) {
					if ( (!$exists) && (filesize($filename)===0) && !qa_opt('event_logger_hide_header') )
						$string="Question2Answer ".QA_VERSION." log file generated by Event Logger plugin.\n".
							"This file is formatted as tab-delimited text with UTF-8 encoding.\n\n".
							implode("\t", array_keys($fixedfields))."\textras...\n\n".$string;

					fwrite($file, $string."\n");
					flock($file, LOCK_UN);
Scott committed
235
				}
Scott committed
236 237

				fclose($file);
Scott committed
238 239 240
			}
		}
	}
Scott committed
241
}