Completed
Push — 124-feature/notify-admins-on-e... ( 2e956d...ebe3a5 )
by Maria Daniel Deepak
06:33
created

TableManager   A

Complexity

Total Complexity 39

Size/Duplication

Total Lines 339
Duplicated Lines 0 %

Test Coverage

Coverage 4.17%

Importance

Changes 0
Metric Value
eloc 117
dl 0
loc 339
ccs 6
cts 144
cp 0.0417
rs 9.28
c 0
b 0
f 0
wmc 39

15 Methods

Rating   Name   Duplication   Size   Complexity  
A insert_log() 0 5 1
A delete_table_from_deleted_blog() 0 4 1
A get_logs_count() 0 6 1
A create_table_for_new_blog() 0 5 2
A delete_logs_older_than() 0 8 1
B fetch_log_items() 0 45 11
A fetch_log_items_by_id() 0 16 2
B fetch_log_item_by_item_data() 0 40 9
A load() 0 4 1
A get_log_table_name() 0 4 1
A create_table() 0 23 2
A set_log_item_fail_status_by_id() 0 14 1
A delete_all_logs() 0 6 1
A delete_logs() 0 9 1
A on_activate() 0 13 4
1
<?php namespace EmailLog\Core\DB;
2
3
/**
4
 * Handle installation and db table creation.
5
 */
6
use EmailLog\Core\Loadie;
7
use EmailLog\Util;
8
9
defined( 'ABSPATH' ) || exit; // Exit if accessed directly.
10
11
/**
12
 * Helper class to create table.
13
 *
14
 * @since 2.0.0
15
 */
16
class TableManager implements Loadie {
17
18
	/* Database table name */
19
	const LOG_TABLE_NAME = 'email_log';
20
21
	/* Database option name */
22
	const DB_OPTION_NAME = 'email-log-db';
23
24
	/* Database version */
25
	const DB_VERSION = '0.1';
26
27
	/**
28
	 * Setup hooks.
29
	 */
30
	public function load() {
31
		add_action( 'wpmu_new_blog', array( $this, 'create_table_for_new_blog' ) );
32
33
		add_filter( 'wpmu_drop_tables', array( $this, 'delete_table_from_deleted_blog' ) );
34
	}
35
36
	/**
37
	 * On plugin activation, create table if needed.
38
	 *
39
	 * @param bool $network_wide True if the plugin was network activated.
40
	 */
41
	public function on_activate( $network_wide ) {
42
		if ( is_multisite() && $network_wide ) {
43
			// Note: if there are more than 10,000 blogs or
44
			// if `wp_is_large_network` filter is set, then this may fail.
45
			$sites = get_sites();
46
47
			foreach ( $sites as $site ) {
48
				switch_to_blog( $site['blog_id'] );
49
				$this->create_table();
50
				restore_current_blog();
51
			}
52
		} else {
53
			$this->create_table();
54
		}
55
	}
56
57
	/**
58
	 * Create email log table when a new blog is created.
59
	 *
60
	 * @param int $blog_id Blog Id.
61
	 */
62
	public function create_table_for_new_blog( $blog_id ) {
63
		if ( is_plugin_active_for_network( 'email-log/email-log.php' ) ) {
64
			switch_to_blog( $blog_id );
65
			$this->create_table();
66
			restore_current_blog();
67
		}
68
	}
69
70
	/**
71
	 * Add email log table to the list of tables deleted when a blog is deleted.
72
	 *
73
	 * @param array $tables List of tables to be deleted.
74
	 *
75
	 * @return string[] $tables Modified list of tables to be deleted.
76
	 */
77 1
	public function delete_table_from_deleted_blog( $tables ) {
78 1
		$tables[] = $this->get_log_table_name();
79
80 1
		return $tables;
81
	}
82
83
	/**
84
	 * Get email log table name.
85
	 *
86
	 * @return string Email Log Table name.
87
	 */
88 2
	public function get_log_table_name() {
89 2
		global $wpdb;
90
91 2
		return $wpdb->prefix . self::LOG_TABLE_NAME;
92
	}
93
94
	/**
95
	 * Insert log data into DB.
96
	 *
97
	 * @param array $data Data to be inserted.
98
	 */
99
	public function insert_log( $data ) {
100
		global $wpdb;
101
102
		$table_name = $this->get_log_table_name();
103
		$wpdb->insert( $table_name, $data );
104
	}
105
106
	/**
107
	 * Delete log entries by ids.
108
	 *
109
	 * @param string $ids Comma separated list of log ids.
110
	 *
111
	 * @return false|int Number of log entries that got deleted. False on failure.
112
	 */
113
	public function delete_logs( $ids ) {
114
		global $wpdb;
115
116
		$table_name = $this->get_log_table_name();
117
118
		// Can't use wpdb->prepare for the below query. If used it results in this bug // https://github.com/sudar/email-log/issues/13.
119
		$ids = esc_sql( $ids );
120
121
		return $wpdb->query( "DELETE FROM {$table_name} where id IN ( {$ids} )" ); //@codingStandardsIgnoreLine
122
	}
123
124
	/**
125
	 * Delete all log entries.
126
	 *
127
	 * @return false|int Number of log entries that got deleted. False on failure.
128
	 */
129
	public function delete_all_logs() {
130
		global $wpdb;
131
132
		$table_name = $this->get_log_table_name();
133
134
		return $wpdb->query( "DELETE FROM {$table_name}" ); //@codingStandardsIgnoreLine
135
	}
136
137
	/**
138
	 * Deletes Email Logs older than the specified interval.
139
	 *
140
	 * @param int $interval_in_days No. of days beyond which logs are to be deleted.
141
	 *
142
	 * @return int $deleted_rows_count  Count of rows deleted.
143
	 */
144
	public function delete_logs_older_than( $interval_in_days ) {
145
		global $wpdb;
146
		$table_name = $this->get_log_table_name();
147
148
		$query              = $wpdb->prepare( "DELETE FROM {$table_name} WHERE sent_date < DATE_SUB( CURDATE(), INTERVAL %d DAY )", $interval_in_days );
149
		$deleted_rows_count = $wpdb->query( $query );
150
151
		return $deleted_rows_count;
152
	}
153
154
	/**
155
	 * Fetch log item by ID.
156
	 *
157
	 * @param array $ids Optional. Array of IDs of the log items to be retrieved.
158
	 *
159
	 * @return array Log item(s).
160
	 */
161
	public function fetch_log_items_by_id( $ids = array() ) {
162
		global $wpdb;
163
		$table_name = $this->get_log_table_name();
164
165
		$query = "SELECT * FROM {$table_name}";
166
167
		if ( ! empty( $ids ) ) {
168
			$ids = array_map( 'absint', $ids );
169
170
			// Can't use wpdb->prepare for the below query. If used it results in this bug https://github.com/sudar/email-log/issues/13.
171
			$ids_list = esc_sql( implode( ',', $ids ) );
172
173
			$query .= " where id IN ( {$ids_list} )";
174
		}
175
176
		return $wpdb->get_results( $query, 'ARRAY_A' ); //@codingStandardsIgnoreLine
177
	}
178
179
	/**
180
	 * Fetch log items.
181
	 *
182
	 * @param array $request         Request object.
183
	 * @param int   $per_page        Entries per page.
184
	 * @param int   $current_page_no Current page no.
185
	 *
186
	 * @return array Log entries and total items count.
187
	 */
188
	public function fetch_log_items( $request, $per_page, $current_page_no ) {
189
		global $wpdb;
190
		$table_name = $this->get_log_table_name();
191
192
		$query       = 'SELECT * FROM ' . $table_name;
193
		$count_query = 'SELECT count(*) FROM ' . $table_name;
194
		$query_cond  = '';
195
196
		if ( isset( $request['s'] ) && $request['s'] !== '' ) {
197
			$search_term = trim( esc_sql( $request['s'] ) );
0 ignored issues
show
Bug introduced by
It seems like esc_sql($request['s']) can also be of type array; however, parameter $str of trim() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

197
			$search_term = trim( /** @scrutinizer ignore-type */ esc_sql( $request['s'] ) );
Loading history...
198
			$query_cond .= " WHERE ( to_email LIKE '%$search_term%' OR subject LIKE '%$search_term%' ) ";
199
		}
200
201
		if ( isset( $request['d'] ) && $request['d'] !== '' ) {
202
			$search_date = trim( esc_sql( $request['d'] ) );
203
			if ( '' === $query_cond ) {
204
				$query_cond .= " WHERE sent_date BETWEEN '$search_date 00:00:00' AND '$search_date 23:59:59' ";
205
			} else {
206
				$query_cond .= " AND sent_date BETWEEN '$search_date 00:00:00' AND '$search_date 23:59:59' ";
207
			}
208
		}
209
210
		// Ordering parameters.
211
		$orderby = ! empty( $request['orderby'] ) ? esc_sql( $request['orderby'] ) : 'sent_date';
212
		$order   = ! empty( $request['order'] ) ? esc_sql( $request['order'] ) : 'DESC';
213
214
		if ( ! empty( $orderby ) & ! empty( $order ) ) {
0 ignored issues
show
Bug introduced by
Are you sure you want to use the bitwise & or did you mean &&?
Loading history...
215
			$query_cond .= ' ORDER BY ' . $orderby . ' ' . $order;
0 ignored issues
show
Bug introduced by
Are you sure $orderby of type string|array can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

215
			$query_cond .= ' ORDER BY ' . /** @scrutinizer ignore-type */ $orderby . ' ' . $order;
Loading history...
Bug introduced by
Are you sure $order of type string|array can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

215
			$query_cond .= ' ORDER BY ' . $orderby . ' ' . /** @scrutinizer ignore-type */ $order;
Loading history...
216
		}
217
218
		// Find total number of items.
219
		$count_query = $count_query . $query_cond;
220
		$total_items = $wpdb->get_var( $count_query );
221
222
		// Adjust the query to take pagination into account.
223
		if ( ! empty( $current_page_no ) && ! empty( $per_page ) ) {
224
			$offset = ( $current_page_no - 1 ) * $per_page;
225
			$query_cond .= ' LIMIT ' . (int) $offset . ',' . (int) $per_page;
226
		}
227
228
		// Fetch the items.
229
		$query = $query . $query_cond;
230
		$items = $wpdb->get_results( $query );
231
232
		return array( $items, $total_items );
233
	}
234
235
	/**
236
	 * Create email log table.
237
	 *
238
	 * @access private
239
	 *
240
	 * @global object $wpdb
241
	 */
242
	private function create_table() {
243
		global $wpdb;
244
245
		$table_name      = $this->get_log_table_name();
246
		$charset_collate = $wpdb->get_charset_collate();
247
248
		if ( $wpdb->get_var( "show tables like '{$table_name}'" ) != $table_name ) {
249
250
			$sql = 'CREATE TABLE ' . $table_name . ' (
251
				id mediumint(9) NOT NULL AUTO_INCREMENT,
252
				to_email VARCHAR(100) NOT NULL,
253
				subject VARCHAR(250) NOT NULL,
254
				message TEXT NOT NULL,
255
				headers TEXT NOT NULL,
256
				attachments TEXT NOT NULL,
257
				sent_date timestamp NOT NULL,
258
				PRIMARY KEY  (id)
259
			) ' . $charset_collate . ' ;';
260
261
			require_once ABSPATH . 'wp-admin/includes/upgrade.php';
262
			dbDelta( $sql );
263
264
			add_option( self::DB_OPTION_NAME, self::DB_VERSION );
265
		}
266
	}
267
268
	/**
269
	 * Get the total number of email logs.
270
	 *
271
	 * @return int Total email log count
272
	 */
273
	public function get_logs_count() {
274
		global $wpdb;
275
276
		$query = 'SELECT count(*) FROM ' . $this->get_log_table_name();
277
278
		return $wpdb->get_var( $query );
279
	}
280
281
	/**
282
	 * @param array $data {
283
	 *      @type string|array to
284
	 *      @type string       subject
285
	 *      @type string       message
286
	 *      @type string|array headers
287
	 *      @type string|array attachments
288
	 * }
289
	 *
290
	 * @return int
291
	 */
292
	public function fetch_log_item_by_item_data( $data ) {
293
		global $wpdb;
294
		$table_name = $this->get_log_table_name();
295
296
		// Since the value is stored as CSV in DB, convert the values from error data to CSV to compare.
297
		$data['to']      = Util\join_array_elements_with_delimiter( $data['to'], ',' );
0 ignored issues
show
Bug introduced by
The function join_array_elements_with_delimiter was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

297
		$data['to']      = /** @scrutinizer ignore-call */ Util\join_array_elements_with_delimiter( $data['to'], ',' );
Loading history...
298
		$data['headers'] = Util\join_array_elements_with_delimiter( $data['headers'], "\n" );
299
300
		$query = "SELECT ID FROM {$table_name}";
301
		$query_cond  = '';
302
303
		if ( empty( $data ) || ! is_array( $data ) ) {
304
			$query_cond .= " WHERE id = 0";
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal WHERE id = 0 does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
305
		}
306
307
		if ( array_key_exists( 'to', $data ) ) {
308
			$to_email   = trim( esc_sql( $data['to'] ) );
0 ignored issues
show
Bug introduced by
It seems like esc_sql($data['to']) can also be of type array; however, parameter $str of trim() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

308
			$to_email   = trim( /** @scrutinizer ignore-type */ esc_sql( $data['to'] ) );
Loading history...
309
			$query_cond .= " WHERE to_email = '$to_email'";
310
		}
311
312
		if ( array_key_exists( 'subject', $data ) ) {
313
			$subject    = trim( esc_sql( $data['subject'] ) );
314
			$query_cond .= " AND subject = '$subject'";
315
		}
316
317
		if ( array_key_exists( 'attachments', $data ) ) {
318
			if ( is_array( $data['attachments'] ) ) {
319
				$attachments = count( $data['attachments'] ) > 0 ? 'true' : 'false';
320
			} else {
321
				$attachments = empty( $data['attachments'] ) ? 'false' : 'true';
322
			}
323
			$attachments = trim( esc_sql( $attachments ) );
324
			$query_cond  .= " AND attachments = '$attachments'";
325
		}
326
327
		// Get only the latest logged item when multiple rows match.
328
		$query_cond .= ' ORDER BY id DESC LIMIT 1';
329
330
		$query = $query . $query_cond;
331
		return absint( $wpdb->get_var( $query ) );
332
	}
333
334
	/**
335
	 * Sets email sent status as failed for the given log item.
336
	 *
337
	 * @since 2.3.0
338
	 *
339
	 * @param int $id ID of the log item whose email sent status should be set to failed.
340
	 */
341
	public function set_log_item_fail_status_by_id( $id ) {
342
		global $wpdb;
343
		$table_name = $this->get_log_table_name();
344
345
		$wpdb->update(
346
			$table_name,
347
			array(
348
				'result' => '0',
349
			),
350
			array( 'ID' => $id ),
351
			array(
352
				'%d' // VALUE format
353
			),
354
			array( '%d' ) // WHERE format
355
		);
356
	}
357
}
358