PluginProbe
Stream – Activity Log & Audit Trail / 3.6.1
Stream – Activity Log & Audit Trail v3.6.1
4.4.0 4.3.0 4.2.2 4.2.1 trunk 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 3.0.0 3.0.1 3.0.2 3.0.3 3.0.4 3.0.5 3.0.6 3.0.7 3.1 3.1.1 3.10.0 3.2.0 3.2.1 3.2.2 3.2.3 All 50 releases
stream / classes / class-connector.php

class-connector.php in Stream – Activity Log & Audit Trail 3.6.1, at classes/class-connector.php

298 lines 7.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Abstract class serving as the parent for all logger classes AKA "Connectors".
4 * Common functionality for registering log events are defined here.
5 *
6 * @package WP_Stream;
7 */
8
9 namespace WP_Stream;
10
11 /**
12 * Class - Connector
13 */
14 abstract class Connector {
15 /**
16 * Connector slug
17 *
18 * @var string
19 */
20 public $name = null;
21
22 /**
23 * Actions registered for this connector
24 *
25 * @var array
26 */
27 public $actions = array();
28
29 /**
30 * Store delayed logs
31 *
32 * @var array
33 */
34 public $delayed = array();
35
36 /**
37 * Previous Stream entry in same request
38 *
39 * @var int
40 */
41 public $prev_stream = null;
42
43 /**
44 * Register connector in the WP Admin
45 *
46 * @var bool
47 */
48 public $register_admin = true;
49
50 /**
51 * Register connector in the WP Frontend
52 *
53 * @var bool
54 */
55 public $register_frontend = true;
56
57 /**
58 * Holds connector registration status flag.
59 *
60 * @var bool
61 */
62 private $is_registered = false;
63
64 /**
65 * Is the connector currently registered?
66 *
67 * @return boolean
68 */
69 public function is_registered() {
70 return $this->is_registered;
71 }
72
73 /**
74 * Register all context hooks
75 */
76 public function register() {
77 if ( $this->is_registered ) {
78 return;
79 }
80
81 foreach ( $this->actions as $action ) {
82 add_action( $action, array( $this, 'callback' ), 10, 99 );
83 }
84
85 add_filter( 'wp_stream_action_links_' . $this->name, array( $this, 'action_links' ), 10, 2 );
86
87 $this->is_registered = true;
88 }
89
90 /**
91 * Unregister all context hooks
92 */
93 public function unregister() {
94 if ( ! $this->is_registered ) {
95 return;
96 }
97
98 foreach ( $this->actions as $action ) {
99 remove_action( $action, array( $this, 'callback' ), 10, 99 );
100 }
101
102 remove_filter( 'wp_stream_action_links_' . $this->name, array( $this, 'action_links' ), 10, 2 );
103
104 $this->is_registered = false;
105 }
106
107 /**
108 * Callback for all registered hooks throughout Stream
109 * Looks for a class method with the convention: "callback_{action name}"
110 */
111 public function callback() {
112 $action = current_filter();
113 $callback = array( $this, 'callback_' . preg_replace( '/[^a-z0-9_\-]/', '_', $action ) );
114
115 // For the sake of testing, trigger an action with the name of the callback.
116 if ( defined( 'WP_STREAM_TESTS' ) && WP_STREAM_TESTS ) {
117 /**
118 * Action fires during testing to test the current callback
119 *
120 * @param array $callback Callback name
121 */
122 do_action( 'wp_stream_test_' . $callback[1] );
123 }
124
125 // Call the real function.
126 if ( is_callable( $callback ) ) {
127 return call_user_func_array( $callback, func_get_args() );
128 }
129 }
130
131 /**
132 * Add action links to Stream drop row in admin list screen
133 *
134 * @param array $links Previous links registered.
135 * @param object $record Stream record.
136 *
137 * @filter wp_stream_action_links_{connector}
138 *
139 * @return array Action links
140 */
141 public function action_links( $links, $record ) {
142 unset( $record );
143 return $links;
144 }
145
146 /**
147 * Log handler
148 *
149 * @param string $message sprintf-ready error message string.
150 * @param array $args sprintf (and extra) arguments to use.
151 * @param int $object_id Target object id.
152 * @param string $context Context of the event.
153 * @param string $action Action of the event.
154 * @param int $user_id User responsible for the event.
155 *
156 * @return bool
157 */
158 public function log( $message, $args, $object_id, $context, $action, $user_id = null ) {
159 $connector = $this->name;
160
161 $data = apply_filters(
162 'wp_stream_log_data',
163 compact( 'connector', 'message', 'args', 'object_id', 'context', 'action', 'user_id' )
164 );
165
166 if ( ! $data ) {
167 return false;
168 } else {
169 $connector = $data['connector'];
170 $message = $data['message'];
171 $args = $data['args'];
172 $object_id = $data['object_id'];
173 $context = $data['context'];
174 $action = $data['action'];
175 $user_id = $data['user_id'];
176 }
177
178 return call_user_func_array( array( wp_stream_get_instance()->log, 'log' ), compact( 'connector', 'message', 'args', 'object_id', 'context', 'action', 'user_id' ) );
179 }
180
181 /**
182 * Save log data till shutdown, so other callbacks would be able to override
183 *
184 * @param string $handle Special slug to be shared with other actions.
185 * @note param mixed $arg1 Extra arguments to sent to log()
186 * @note param param mixed $arg2, etc..
187 */
188 public function delayed_log( $handle ) {
189 $args = func_get_args();
190
191 array_shift( $args );
192
193 $this->delayed[ $handle ] = $args;
194
195 add_action( 'shutdown', array( $this, 'delayed_log_commit' ) );
196 }
197
198 /**
199 * Commit delayed logs saved by @delayed_log
200 */
201 public function delayed_log_commit() {
202 foreach ( $this->delayed as $handle => $args ) {
203 call_user_func_array( array( $this, 'log' ), $args );
204 }
205 }
206
207 /**
208 * Compare two values and return changed keys if they are arrays
209 *
210 * @param mixed $old_value Value before change.
211 * @param mixed $new_value Value after change.
212 * @param bool|int $deep Get array children changes keys as well, not just parents.
213 *
214 * @return array
215 */
216 public function get_changed_keys( $old_value, $new_value, $deep = false ) {
217 if ( ! is_array( $old_value ) && ! is_array( $new_value ) ) {
218 return array();
219 }
220
221 if ( ! is_array( $old_value ) ) {
222 return array_keys( $new_value );
223 }
224
225 if ( ! is_array( $new_value ) ) {
226 return array_keys( $old_value );
227 }
228
229 $diff = array_udiff_assoc(
230 $old_value,
231 $new_value,
232 function( $value1, $value2 ) {
233 // Compare potentially complex nested arrays.
234 return wp_json_encode( $value1 ) !== wp_json_encode( $value2 );
235 }
236 );
237
238 $result = array_keys( $diff );
239
240 // Find unexisting keys in old or new value.
241 $common_keys = array_keys( array_intersect_key( $old_value, $new_value ) );
242 $unique_keys_old = array_values( array_diff( array_keys( $old_value ), $common_keys ) );
243 $unique_keys_new = array_values( array_diff( array_keys( $new_value ), $common_keys ) );
244
245 $result = array_merge( $result, $unique_keys_old, $unique_keys_new );
246
247 // Remove numeric indexes.
248 $result = array_filter(
249 $result,
250 function( $value ) {
251 // @codingStandardsIgnoreStart
252 // check if is not valid number (is_int, is_numeric and ctype_digit are not enough)
253 return (string) (int) $value !== (string) $value;
254 // @codingStandardsIgnoreEnd
255 }
256 );
257
258 $result = array_values( array_unique( $result ) );
259
260 if ( false === $deep ) {
261 return $result; // Return an numerical based array with changed TOP PARENT keys only.
262 }
263
264 $result = array_fill_keys( $result, null );
265
266 foreach ( $result as $key => $val ) {
267 if ( in_array( $key, $unique_keys_old, true ) ) {
268 $result[ $key ] = false; // Removed.
269 } elseif ( in_array( $key, $unique_keys_new, true ) ) {
270 $result[ $key ] = true; // Added.
271 } elseif ( $deep ) { // Changed, find what changed, only if we're allowed to explore a new level.
272 if ( is_array( $old_value[ $key ] ) && is_array( $new_value[ $key ] ) ) {
273 $inner = array();
274 $parent = $key;
275 $deep--;
276 $changed = $this->get_changed_keys( $old_value[ $key ], $new_value[ $key ], $deep );
277 foreach ( $changed as $child => $change ) {
278 $inner[ $parent . '::' . $child ] = $change;
279 }
280 $result[ $key ] = 0; // Changed parent which has a changed children.
281 $result = array_merge( $result, $inner );
282 }
283 }
284 }
285
286 return $result;
287 }
288
289 /**
290 * Allow connectors to determine if their dependencies is satisfied or not
291 *
292 * @return bool
293 */
294 public function is_dependency_satisfied() {
295 return true;
296 }
297 }
298