PluginProbe
Stream – Activity Log & Audit Trail / 4.2.2
Stream – Activity Log & Audit Trail v4.2.2
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-plugin.php

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

585 lines 14.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Initializes plugin
4 *
5 * @package WP_Stream;
6 */
7
8 namespace WP_Stream;
9
10 use RuntimeException;
11
12 /**
13 * Class Plugin
14 */
15 class Plugin {
16 /**
17 * Plugin version number.
18 *
19 * TODO Maybe pass this as a constructor dependency?
20 *
21 * @const string
22 */
23 const VERSION = '4.2.2';
24
25 /**
26 * WP-CLI command
27 *
28 * @const string
29 */
30 const WP_CLI_COMMAND = 'stream';
31
32
33 /**
34 * Used to check if it's a single site, not multisite.
35 *
36 * @const string
37 */
38 const SINGLE_SITE = 'single';
39
40 /**
41 * Used to check if it's a multisite with the plugin network enabled.
42 *
43 * @const string
44 */
45 const MULTI_NETWORK = 'multisite-network';
46
47 /**
48 * Used to check if it's a multisite with the plugin not network enabled.
49 *
50 * @const string
51 */
52 const MULTI_NOT_NETWORK = 'multisite-not-network';
53
54 /**
55 * Holds and manages WordPress Admin configurations.
56 *
57 * @var Admin
58 */
59 public $admin;
60
61 /**
62 * Holds and manages alerts.
63 *
64 * @var Alerts
65 */
66 public $alerts;
67
68 /**
69 * Holds and manages alerts lists.
70 *
71 * @var Alerts_List
72 */
73 public $alerts_list;
74
75 /**
76 * Holds and manages WordPress Abilities API integration.
77 *
78 * @var Abilities
79 */
80 public $abilities;
81
82 /**
83 * Holds and manages connectors
84 *
85 * @var Connectors
86 */
87 public $connectors;
88
89 /**
90 * Holds and manages DB connections.
91 *
92 * @var DB
93 */
94 public $db;
95
96 /**
97 * Holds and manages records.
98 *
99 * @var Log
100 */
101 public $log;
102
103 /**
104 * Stores and manages WordPress settings.
105 *
106 * @var Settings
107 */
108 public $settings;
109
110 /**
111 * Process DB migrations.
112 *
113 * @var Install
114 */
115 public $install;
116
117 /**
118 * URLs and Paths used by the plugin
119 *
120 * @var array
121 */
122 public $locations = array();
123
124 /**
125 * IP address for the current request to be associated with the log entry.
126 *
127 * @var null|false|string Valid IP address, null if not set, false if invalid.
128 */
129 protected $client_ip_address;
130
131 /**
132 * Class constructor
133 */
134 public function __construct() {
135 $locate = $this->locate_plugin();
136
137 $this->locations = array(
138 'plugin' => $locate['plugin_basename'],
139 'dir' => $locate['dir_path'],
140 'url' => $locate['dir_url'],
141 'inc_dir' => $locate['dir_path'] . 'includes/',
142 'class_dir' => $locate['dir_path'] . 'classes/',
143 );
144
145 spl_autoload_register( array( $this, 'autoload' ) );
146
147 // Load Action Scheduler.
148 require_once $this->locations['dir'] . '/vendor/woocommerce/action-scheduler/action-scheduler.php';
149
150 // Load helper functions.
151 require_once $this->locations['inc_dir'] . 'functions.php';
152
153 // Load DB helper interface/class.
154 $driver_class = apply_filters( 'wp_stream_db_driver', '\WP_Stream\DB_Driver_WPDB' );
155 $driver = null;
156
157 if ( class_exists( $driver_class ) ) {
158 $driver = new $driver_class();
159 $this->db = new DB( $driver );
160 }
161
162 $error = false;
163 if ( ! $this->db ) {
164 $error = esc_html__( 'Stream: Could not load chosen DB driver.', 'stream' );
165 } elseif ( ! $driver instanceof DB_Driver ) {
166 $error = esc_html__( 'Stream: DB driver must implement DB Driver interface.', 'stream' );
167 }
168
169 if ( $error ) {
170 wp_die(
171 esc_html( $error ),
172 esc_html__( 'Stream DB Error', 'stream' )
173 );
174 }
175
176 // Load languages.
177 add_action( 'plugins_loaded', array( $this, 'i18n' ) );
178
179 // Load logger class.
180 $this->log = apply_filters( 'wp_stream_log_handler', new Log( $this ) );
181
182 // Set the IP address for the current request.
183 $this->client_ip_address = wp_stream_filter_input( INPUT_SERVER, 'REMOTE_ADDR', FILTER_VALIDATE_IP );
184
185 // Load settings and connectors after widgets_init and before the default init priority.
186 add_action( 'init', array( $this, 'init' ), 9 );
187
188 // Add frontend indicator.
189 add_action( 'wp_head', array( $this, 'frontend_indicator' ) );
190
191 // Change DB driver after plugin loaded if any add-ons want to replace.
192 add_action( 'plugins_loaded', array( $this, 'plugins_loaded' ), 20 );
193
194 // Load admin area classes.
195 if ( is_admin() || ( defined( 'WP_STREAM_DEV_DEBUG' ) && WP_STREAM_DEV_DEBUG ) || ( defined( 'WP_CLI' ) && WP_CLI ) ) {
196 $this->admin = new Admin( $this );
197 $this->install = $driver->setup_storage( $this );
198 } elseif ( defined( 'DOING_CRON' ) && DOING_CRON ) {
199 $this->admin = new Admin( $this, $driver );
200 }
201
202 // Load WP-CLI command.
203 if ( defined( 'WP_CLI' ) && WP_CLI ) {
204 \WP_CLI::add_command( self::WP_CLI_COMMAND, 'WP_Stream\CLI' );
205 }
206 }
207
208 /**
209 * Autoloader for classes
210 *
211 * @param string $class_name Fully qualified classname to be loaded.
212 */
213 public function autoload( $class_name ) {
214 if ( ! preg_match( '/^(?P<namespace>.+)\\\\(?P<autoload>[^\\\\]+)$/', $class_name, $matches ) ) {
215 return;
216 }
217
218 static $reflection;
219
220 if ( empty( $reflection ) ) {
221 $reflection = new \ReflectionObject( $this );
222 }
223
224 if ( $reflection->getNamespaceName() !== $matches['namespace'] ) {
225 return;
226 }
227
228 $autoload_name = $matches['autoload'];
229 $autoload_dir = \trailingslashit( $this->locations['class_dir'] );
230 $autoload_path = sprintf( '%sclass-%s.php', $autoload_dir, strtolower( str_replace( '_', '-', $autoload_name ) ) );
231
232 if ( is_readable( $autoload_path ) ) {
233 require_once $autoload_path;
234 }
235 }
236
237 /**
238 * Loads the translation files.
239 *
240 * @action plugins_loaded
241 */
242 public function i18n() {
243 load_plugin_textdomain( 'stream', false, dirname( $this->locations['plugin'] ) . '/languages/' );
244 }
245
246 /**
247 * Load Settings, Notifications, and Connectors
248 *
249 * @action init
250 */
251 public function init() {
252 $this->settings = new Settings( $this );
253 $this->connectors = new Connectors( $this );
254 $this->alerts = new Alerts( $this );
255 $this->alerts_list = new Alerts_List( $this );
256 $this->abilities = new Abilities( $this );
257 }
258
259 /**
260 * Displays an HTML comment in the frontend head to indicate that Stream is activated,
261 * and which version of Stream is currently in use.
262 *
263 * @action wp_head
264 *
265 * @return string|void An HTML comment, or nothing if the value is filtered out.
266 */
267 public function frontend_indicator() {
268 /* translators: Localization not needed */
269 $comment = sprintf( 'Stream WordPress user activity plugin v%s', esc_html( $this->get_version() ) );
270
271 /**
272 * Filter allows the HTML output of the frontend indicator comment
273 * to be altered or removed, if desired.
274 *
275 * @return string The content of the HTML comment
276 */
277 $comment = apply_filters( 'wp_stream_frontend_indicator', $comment );
278
279 if ( ! empty( $comment ) ) {
280 printf( "<!-- %s -->\n", esc_html( $comment ) );
281 }
282 }
283
284 /**
285 * Version of plugin_dir_url() which works for plugins installed in the plugins directory,
286 * and for plugins bundled with themes.
287 *
288 * @return array
289 */
290 private function locate_plugin() {
291 $dir_url = trailingslashit( plugins_url( '', __DIR__ ) );
292 $dir_path = plugin_dir_path( __DIR__ );
293 $dir_basename = basename( $dir_path );
294 $plugin_basename = trailingslashit( $dir_basename ) . 'stream.php';
295
296 return compact( 'dir_url', 'dir_path', 'dir_basename', 'plugin_basename' );
297 }
298
299 /**
300 * Getter for the version number.
301 *
302 * @return string
303 */
304 public function get_version() {
305 return self::VERSION;
306 }
307
308 /**
309 * Change plugin database driver in case driver plugin loaded after stream
310 */
311 public function plugins_loaded() {
312 // Load DB helper interface/class.
313 $driver_class = apply_filters( 'wp_stream_db_driver', '\WP_Stream\DB_Driver_WPDB' );
314
315 if ( class_exists( $driver_class ) ) {
316 $driver = new $driver_class();
317 $this->db = new DB( $driver );
318 }
319 }
320
321 /**
322 * Returns true if Stream is network activated, otherwise false
323 *
324 * @return bool
325 */
326 public function is_network_activated() {
327
328 $is_network_activated = false;
329
330 if ( $this->is_mustuse() ) {
331 $is_network_activated = true;
332 } else {
333 if ( ! function_exists( 'is_plugin_active_for_network' ) ) {
334 require_once ABSPATH . '/wp-admin/includes/plugin.php';
335 }
336 $is_network_activated = is_plugin_active_for_network( $this->locations['plugin'] );
337 }
338
339 /**
340 * Filter allows the network activated detection to be overridden.
341 *
342 * @param string $is_network_activated Whether the plugin is network activated.
343 * @param WP_Stream\Plugin $plugin The stream plugin object.
344 */
345 return apply_filters( 'wp_stream_is_network_activated', $is_network_activated, $this );
346 }
347
348 /**
349 * Returns true if Stream is a must-use plugin, otherwise false
350 *
351 * @return bool
352 */
353 public function is_mustuse() {
354 $stream_php = trailingslashit( WPMU_PLUGIN_DIR ) . $this->locations['plugin'];
355
356 if ( file_exists( $stream_php ) && class_exists( 'WP_Stream\Plugin' ) ) {
357 return true;
358 }
359
360 return false;
361 }
362
363 /**
364 * Get the IP address for the current request.
365 *
366 * @return false|null|string Valid IP address, null if not set, false if invalid.
367 */
368 public function get_client_ip_address() {
369 return apply_filters( 'wp_stream_client_ip_address', $this->client_ip_address );
370 }
371
372 /**
373 * Get the site type.
374 *
375 * This function determines the type of site based on whether it is a single site or a multisite.
376 * If it is a multisite, it also checks if it is network activated or not.
377 *
378 * @return string The site type
379 */
380 public function get_site_type(): string {
381
382 // If it's a multisite, is it network activated or not?
383 if ( is_multisite() ) {
384 return $this->is_network_activated() ? self::MULTI_NETWORK : self::MULTI_NOT_NETWORK;
385 }
386
387 return self::SINGLE_SITE;
388 }
389
390 /**
391 * Should the number of records which need to be processed be considered "large"?
392 *
393 * @param int $record_number The number of rows in the {$wpdb->prefix}_stream table to be processed.
394 * @return bool Whether or not this should be considered large.
395 */
396 public function is_large_records_table( int $record_number ): bool {
397 /**
398 * Filters whether or not the number of records should be considered a large table.
399 *
400 * @since 4.1.0
401 *
402 * @param bool $is_large_table Whether or not the number of records should be considered large.
403 * @param int $record_number The number of records being checked.
404 */
405 return apply_filters( 'wp_stream_is_large_records_table', $record_number > 1000000, $record_number );
406 }
407
408 /**
409 * Checks if the plugin is running on a single site installation.
410 *
411 * @return bool True if the plugin is running on a single site installation, false otherwise.
412 */
413 public function is_single_site() {
414 return self::SINGLE_SITE === $this->get_site_type();
415 }
416
417 /**
418 * Check if the plugin is activated on a multisite installation but not network activated.
419 *
420 * @return bool True if the plugin is activated on a multisite installation but not network activated, false otherwise.
421 */
422 public function is_multisite_not_network_activated() {
423 return self::MULTI_NOT_NETWORK === $this->get_site_type();
424 }
425
426 /**
427 * Check if the plugin is activated on a multisite network.
428 *
429 * @return bool True if the plugin is network activated on a multisite, false otherwise.
430 */
431 public function is_multisite_network_activated() {
432 return self::MULTI_NETWORK === $this->get_site_type();
433 }
434
435 /**
436 * Enqueue a script along with a stylesheet if it exists.
437 *
438 * @param string $handle Script handle.
439 * @param array $additional_dependencies Additional dependencies.
440 * @param array $data Data to pass to the script.
441 *
442 * @throws RuntimeException If built JavaScript assets are not found.
443 * @return void
444 */
445 public function enqueue_asset( $handle, $additional_dependencies = array(), $data = array() ) {
446 // If is enqueued already, bail out.
447 if ( wp_script_is( $handle ) ) {
448 return;
449 }
450
451 $path = untrailingslashit( $this->locations['dir'] );
452 $url = untrailingslashit( $this->locations['url'] );
453
454 $script_asset_path = "$path/build/$handle.asset.php";
455
456 if ( ! file_exists( $script_asset_path ) ) {
457 throw new RuntimeException( 'Built JavaScript assets not found. Please run `npm run build`' );
458 }
459
460 $script_asset = require $script_asset_path; // phpcs:disable WPThemeReview.CoreFunctionality.FileInclude.FileIncludeFound
461
462 wp_enqueue_script(
463 "wp-stream-$handle",
464 "$url/build/$handle.js",
465 array_merge(
466 $script_asset['dependencies'],
467 (array) $additional_dependencies
468 ),
469 $script_asset['version'],
470 true
471 );
472
473 if ( file_exists( "$path/build/$handle.css" ) ) {
474 wp_enqueue_style(
475 "wp-stream-$handle",
476 "$url/build/$handle.css",
477 array(),
478 $script_asset['version']
479 );
480 }
481
482 if ( ! empty( $data ) ) {
483 wp_add_inline_script(
484 "wp-stream-$handle",
485 sprintf( 'window["%s"] = %s;', esc_attr( "wp-stream-$handle" ), wp_json_encode( $data ) ),
486 'before'
487 );
488 }
489 }
490
491 /**
492 * Enqueue select2 script and locale file if exists.
493 *
494 * @return string Script handle.
495 */
496 public function with_select2() {
497 $handle = 'wp-stream-select2';
498
499 // If is enqueued already, bail out.
500 if ( wp_script_is( $handle ) ) {
501 return $handle;
502 }
503
504 $path = untrailingslashit( $this->locations['dir'] );
505 $url = untrailingslashit( $this->locations['url'] );
506
507 wp_enqueue_script(
508 $handle,
509 "$url/build/select2/js/select2.full.min.js",
510 array( 'jquery' ),
511 filemtime( "$path/build/select2/js/select2.full.min.js" ),
512 true
513 );
514 wp_enqueue_style(
515 $handle,
516 "$url/build/select2/css/select2.min.css",
517 array(),
518 filemtime( "$path/build/select2/css/select2.min.css" )
519 );
520
521 $locale = get_locale();
522 $lang = substr( $locale, 0, 2 );
523 $search_files = array( $locale, $lang, 'en' );
524
525 foreach ( $search_files as $search_file ) {
526 if ( file_exists( "$path/build/select2/js/i18n/$search_file.js" ) ) {
527 wp_enqueue_script(
528 sanitize_title( "$handle-$search_file" ),
529 "$url/build/select2/js/i18n/$search_file.js",
530 array( $handle ),
531 filemtime( "$path/build/select2/js/i18n/$search_file.js" ),
532 true
533 );
534 break;
535 }
536 }
537
538 return $handle;
539 }
540
541 /**
542 * Enqueue jquery.timeago script and locale file if exists.
543 *
544 * @return string Script handle.
545 */
546 public function with_jquery_timeago() {
547 $handle = 'wp-stream-jquery-timeago';
548
549 // If is enqueued already, bail out.
550 if ( wp_script_is( $handle ) ) {
551 return $handle;
552 }
553
554 $path = untrailingslashit( $this->locations['dir'] );
555 $url = untrailingslashit( $this->locations['url'] );
556
557 wp_enqueue_script(
558 $handle,
559 "$url/build/timeago/js/jquery.timeago.js",
560 array( 'jquery' ),
561 filemtime( "$path/build/timeago/js/jquery.timeago.js" ),
562 true
563 );
564
565 $locale = get_locale();
566 $lang = substr( $locale, 0, 2 );
567 $search_files = array( $locale, $lang, 'en' );
568
569 foreach ( $search_files as $search_file ) {
570 if ( file_exists( "$path/build/timeago/js/locales/jquery.timeago.$search_file.js" ) ) {
571 wp_enqueue_script(
572 sanitize_title( "$handle-$search_file" ),
573 "$url/build/timeago/js/locales/jquery.timeago.$search_file.js",
574 array( $handle ),
575 filemtime( "$path/build/timeago/js/locales/jquery.timeago.$search_file.js" ),
576 true
577 );
578 break;
579 }
580 }
581
582 return $handle;
583 }
584 }
585