Passed
Pull Request — master (#280)
by Brian
05:07
created

ActionScheduler_wpCommentLogger   A

Complexity

Total Complexity 33

Size/Duplication

Total Lines 232
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 91
c 1
b 0
f 0
dl 0
loc 232
rs 9.76
wmc 33

15 Methods

Rating   Name   Duplication   Size   Complexity  
A filter_comment_feed() 0 5 2
A disable_comment_counting() 0 2 1
A delete_comment_count_cache() 0 2 1
B get_comment_count() 0 38 8
A init() 0 13 1
A log() 0 8 2
A enable_comment_counting() 0 2 1
A get_logs() 0 20 4
A get_comment() 0 2 1
A filter_comment_count() 0 8 2
A filter_comment_queries() 0 8 3
A create_wp_comment() 0 14 1
A get_where_clause() 0 3 1
A get_entry() 0 9 3
A filter_comment_query_clauses() 0 5 2
1
<?php
2
3
/**
4
 * Class ActionScheduler_wpCommentLogger
5
 */
6
class ActionScheduler_wpCommentLogger extends ActionScheduler_Logger {
7
	const AGENT = 'ActionScheduler';
8
	const TYPE = 'action_log';
9
10
	/**
11
	 * @param string $action_id
12
	 * @param string $message
13
	 * @param DateTime $date
14
	 *
15
	 * @return string The log entry ID
16
	 */
17
	public function log( $action_id, $message, DateTime $date = NULL ) {
18
		if ( empty($date) ) {
19
			$date = as_get_datetime_object();
20
		} else {
21
			$date = as_get_datetime_object( clone $date );
22
		}
23
		$comment_id = $this->create_wp_comment( $action_id, $message, $date );
24
		return $comment_id;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $comment_id returns the type false which is incompatible with the documented return type string.
Loading history...
25
	}
26
27
	protected function create_wp_comment( $action_id, $message, DateTime $date ) {
28
29
		$comment_date_gmt = $date->format('Y-m-d H:i:s');
30
		ActionScheduler_TimezoneHelper::set_local_timezone( $date );
31
		$comment_data = array(
32
			'comment_post_ID' => $action_id,
33
			'comment_date' => $date->format('Y-m-d H:i:s'),
34
			'comment_date_gmt' => $comment_date_gmt,
35
			'comment_author' => self::AGENT,
36
			'comment_content' => $message,
37
			'comment_agent' => self::AGENT,
38
			'comment_type' => self::TYPE,
39
		);
40
		return wp_insert_comment($comment_data);
41
	}
42
43
	/**
44
	 * @param string $entry_id
45
	 *
46
	 * @return ActionScheduler_LogEntry
47
	 */
48
	public function get_entry( $entry_id ) {
49
		$comment = $this->get_comment( $entry_id );
50
		if ( empty($comment) || $comment->comment_type != self::TYPE ) {
51
			return new ActionScheduler_NullLogEntry();
52
		}
53
54
		$date = as_get_datetime_object( $comment->comment_date_gmt );
55
		ActionScheduler_TimezoneHelper::set_local_timezone( $date );
56
		return new ActionScheduler_LogEntry( $comment->comment_post_ID, $comment->comment_content, $date );
57
	}
58
59
	/**
60
	 * @param string $action_id
61
	 *
62
	 * @return ActionScheduler_LogEntry[]
63
	 */
64
	public function get_logs( $action_id ) {
65
		$status = 'all';
66
		if ( get_post_status($action_id) == 'trash' ) {
0 ignored issues
show
Bug introduced by
$action_id of type string is incompatible with the type WP_Post|integer expected by parameter $post of get_post_status(). ( Ignorable by Annotation )

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

66
		if ( get_post_status(/** @scrutinizer ignore-type */ $action_id) == 'trash' ) {
Loading history...
67
			$status = 'post-trashed';
68
		}
69
		$comments = get_comments(array(
70
			'post_id' => $action_id,
71
			'orderby' => 'comment_date_gmt',
72
			'order' => 'ASC',
73
			'type' => self::TYPE,
74
			'status' => $status,
75
		));
76
		$logs = array();
77
		foreach ( $comments as $c ) {
78
			$entry = $this->get_entry( $c );
79
			if ( !empty($entry) ) {
80
				$logs[] = $entry;
81
			}
82
		}
83
		return $logs;
84
	}
85
86
	protected function get_comment( $comment_id ) {
87
		return get_comment( $comment_id );
88
	}
89
90
91
92
	/**
93
	 * @param WP_Comment_Query $query
94
	 */
95
	public function filter_comment_queries( $query ) {
96
		foreach ( array('ID', 'parent', 'post_author', 'post_name', 'post_parent', 'type', 'post_type', 'post_id', 'post_ID') as $key ) {
97
			if ( !empty($query->query_vars[$key]) ) {
98
				return; // don't slow down queries that wouldn't include action_log comments anyway
99
			}
100
		}
101
		$query->query_vars['action_log_filter'] = TRUE;
102
		add_filter( 'comments_clauses', array( $this, 'filter_comment_query_clauses' ), 10, 2 );
103
	}
104
105
	/**
106
	 * @param array $clauses
107
	 * @param WP_Comment_Query $query
108
	 *
109
	 * @return array
110
	 */
111
	public function filter_comment_query_clauses( $clauses, $query ) {
112
		if ( !empty($query->query_vars['action_log_filter']) ) {
113
			$clauses['where'] .= $this->get_where_clause();
114
		}
115
		return $clauses;
116
	}
117
118
	/**
119
	 * Make sure Action Scheduler logs are excluded from comment feeds, which use WP_Query, not
120
	 * the WP_Comment_Query class handled by @see self::filter_comment_queries().
121
	 *
122
	 * @param string $where
123
	 * @param WP_Query $query
124
	 *
125
	 * @return string
126
	 */
127
	public function filter_comment_feed( $where, $query ) {
0 ignored issues
show
Unused Code introduced by
The parameter $query is not used and could be removed. ( Ignorable by Annotation )

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

127
	public function filter_comment_feed( $where, /** @scrutinizer ignore-unused */ $query ) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
128
		if ( is_comment_feed() ) {
129
			$where .= $this->get_where_clause();
130
		}
131
		return $where;
132
	}
133
134
	/**
135
	 * Return a SQL clause to exclude Action Scheduler comments.
136
	 *
137
	 * @return string
138
	 */
139
	protected function get_where_clause() {
140
		global $wpdb;
141
		return sprintf( " AND {$wpdb->comments}.comment_type != '%s'", self::TYPE );
142
	}
143
144
	/**
145
	 * Remove action log entries from wp_count_comments()
146
	 *
147
	 * @param array $stats
148
	 * @param int $post_id
149
	 *
150
	 * @return object
151
	 */
152
	public function filter_comment_count( $stats, $post_id ) {
153
		global $wpdb;
154
155
		if ( 0 === $post_id ) {
156
			$stats = $this->get_comment_count();
157
		}
158
159
		return $stats;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $stats also could return the type array which is incompatible with the documented return type object.
Loading history...
160
	}
161
162
	/**
163
	 * Retrieve the comment counts from our cache, or the database if the cached version isn't set.
164
	 *
165
	 * @return object
166
	 */
167
	protected function get_comment_count() {
168
		global $wpdb;
169
170
		$stats = get_transient( 'as_comment_count' );
171
172
		if ( ! $stats ) {
173
			$stats = array();
0 ignored issues
show
Unused Code introduced by
The assignment to $stats is dead and can be removed.
Loading history...
174
175
			$count = $wpdb->get_results( "SELECT comment_approved, COUNT( * ) AS num_comments FROM {$wpdb->comments} WHERE comment_type NOT IN('order_note','action_log') GROUP BY comment_approved", ARRAY_A );
176
177
			$total = 0;
178
			$stats = array();
179
			$approved = array( '0' => 'moderated', '1' => 'approved', 'spam' => 'spam', 'trash' => 'trash', 'post-trashed' => 'post-trashed' );
180
181
			foreach ( (array) $count as $row ) {
182
				// Don't count post-trashed toward totals
183
				if ( 'post-trashed' != $row['comment_approved'] && 'trash' != $row['comment_approved'] ) {
184
					$total += $row['num_comments'];
185
				}
186
				if ( isset( $approved[ $row['comment_approved'] ] ) ) {
187
					$stats[ $approved[ $row['comment_approved'] ] ] = $row['num_comments'];
188
				}
189
			}
190
191
			$stats['total_comments'] = $total;
192
			$stats['all']            = $total;
193
194
			foreach ( $approved as $key ) {
195
				if ( empty( $stats[ $key ] ) ) {
196
					$stats[ $key ] = 0;
197
				}
198
			}
199
200
			$stats = (object) $stats;
201
			set_transient( 'as_comment_count', $stats );
202
		}
203
204
		return $stats;
205
	}
206
207
	/**
208
	 * Delete comment count cache whenever there is new comment or the status of a comment changes. Cache
209
	 * will be regenerated next time ActionScheduler_wpCommentLogger::filter_comment_count() is called.
210
	 */
211
	public function delete_comment_count_cache() {
212
		delete_transient( 'as_comment_count' );
213
	}
214
215
	/**
216
	 * @codeCoverageIgnore
217
	 */
218
	public function init() {
219
		add_action( 'action_scheduler_before_process_queue', array( $this, 'disable_comment_counting' ), 10, 0 );
220
		add_action( 'action_scheduler_after_process_queue', array( $this, 'enable_comment_counting' ), 10, 0 );
221
222
		parent::init();
223
224
		add_action( 'pre_get_comments', array( $this, 'filter_comment_queries' ), 10, 1 );
225
		add_action( 'wp_count_comments', array( $this, 'filter_comment_count' ), 20, 2 ); // run after WC_Comments::wp_count_comments() to make sure we exclude order notes and action logs
226
		add_action( 'comment_feed_where', array( $this, 'filter_comment_feed' ), 10, 2 );
227
228
		// Delete comments count cache whenever there is a new comment or a comment status changes
229
		add_action( 'wp_insert_comment', array( $this, 'delete_comment_count_cache' ) );
230
		add_action( 'wp_set_comment_status', array( $this, 'delete_comment_count_cache' ) );
231
	}
232
233
	public function disable_comment_counting() {
234
		wp_defer_comment_counting(true);
235
	}
236
	public function enable_comment_counting() {
237
		wp_defer_comment_counting(false);
238
	}
239
240
}
241