PluginProbe
Stream – Activity Log & Audit Trail / 4.1.0
Stream – Activity Log & Audit Trail v4.1.0
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.1.0, at classes/class-plugin.php

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