| 1 |
<?php |
| 2 |
/** |
| 3 |
* Plugin Name: Stream |
| 4 |
* Plugin URI: https://wp-stream.com/ |
| 5 |
* Description: Stream tracks logged-in user activity so you can monitor every change made on your WordPress site in beautifully organized detail. All activity is organized by context, action and IP address for easy filtering. Developers can extend Stream with custom connectors to log any kind of action. |
| 6 |
* Version: 2.0.5 |
| 7 |
* Author: Stream |
| 8 |
* Author URI: https://wp-stream.com/ |
| 9 |
* License: GPLv2+ |
| 10 |
* Text Domain: stream |
| 11 |
* Domain Path: /languages |
| 12 |
*/ |
| 13 |
|
| 14 |
/** |
| 15 |
* Copyright (c) 2015 WP Stream Pty Ltd (https://wp-stream.com/) |
| 16 |
* |
| 17 |
* This program is free software; you can redistribute it and/or modify |
| 18 |
* it under the terms of the GNU General Public License, version 2 or, at |
| 19 |
* your discretion, any later version, as published by the Free |
| 20 |
* Software Foundation. |
| 21 |
* |
| 22 |
* This program is distributed in the hope that it will be useful, |
| 23 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 24 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 25 |
* GNU General Public License for more details. |
| 26 |
* |
| 27 |
* You should have received a copy of the GNU General Public License |
| 28 |
* along with this program; if not, write to the Free Software |
| 29 |
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| 30 |
*/ |
| 31 |
|
| 32 |
class WP_Stream { |
| 33 |
|
| 34 |
/** |
| 35 |
* Plugin version number |
| 36 |
* |
| 37 |
* @const string |
| 38 |
*/ |
| 39 |
const VERSION = '2.0.5'; |
| 40 |
|
| 41 |
/** |
| 42 |
* WP-CLI command |
| 43 |
* |
| 44 |
* @const string |
| 45 |
*/ |
| 46 |
const WP_CLI_COMMAND = 'stream'; |
| 47 |
|
| 48 |
/** |
| 49 |
* Hold Stream instance |
| 50 |
* |
| 51 |
* @var string |
| 52 |
*/ |
| 53 |
public static $instance; |
| 54 |
|
| 55 |
/** |
| 56 |
* @var WP_Stream_DB_Base |
| 57 |
*/ |
| 58 |
public static $db; |
| 59 |
|
| 60 |
/** |
| 61 |
* @var WP_Stream_API |
| 62 |
*/ |
| 63 |
public static $api; |
| 64 |
|
| 65 |
/** |
| 66 |
* Admin notices, collected and displayed on proper action |
| 67 |
* |
| 68 |
* @var array |
| 69 |
*/ |
| 70 |
public static $notices = array(); |
| 71 |
|
| 72 |
/** |
| 73 |
* An array of deprecated extension plugin class/dir pairs |
| 74 |
* |
| 75 |
* @var array |
| 76 |
*/ |
| 77 |
public static $deprecated_extensions = array( |
| 78 |
'WP_Stream_Cherry_Pick' => 'stream-cherry-pick/stream-cherry-pick.php', |
| 79 |
'WP_Stream_Data_Exporter' => 'stream-data-exporter/stream-data-exporter.php', |
| 80 |
'WP_Stream_Notifications' => 'stream-notifications/stream-notifications.php', |
| 81 |
'WP_Stream_Reports' => 'stream-reports/stream-reports.php', |
| 82 |
); |
| 83 |
|
| 84 |
/** |
| 85 |
* Class constructor |
| 86 |
*/ |
| 87 |
private function __construct() { |
| 88 |
define( 'WP_STREAM_PLUGIN', plugin_basename( __FILE__ ) ); |
| 89 |
define( 'WP_STREAM_DIR', plugin_dir_path( __FILE__ ) ); |
| 90 |
define( 'WP_STREAM_URL', plugin_dir_url( __FILE__ ) ); |
| 91 |
define( 'WP_STREAM_INC_DIR', WP_STREAM_DIR . 'includes/' ); |
| 92 |
define( 'WP_STREAM_CLASS_DIR', WP_STREAM_DIR . 'classes/' ); |
| 93 |
define( 'WP_STREAM_EXTENSIONS_DIR', WP_STREAM_DIR . 'extensions/' ); |
| 94 |
|
| 95 |
spl_autoload_register( array( $this, 'autoload' ) ); |
| 96 |
|
| 97 |
// Load helper functions |
| 98 |
require_once WP_STREAM_INC_DIR . 'functions.php'; |
| 99 |
|
| 100 |
// Load DB helper interface/class |
| 101 |
$driver = 'WP_Stream_DB'; |
| 102 |
if ( class_exists( $driver ) ) { |
| 103 |
self::$db = new $driver; |
| 104 |
} |
| 105 |
|
| 106 |
if ( ! self::$db ) { |
| 107 |
wp_die( __( 'Stream: Could not load chosen DB driver.', 'stream' ), 'Stream DB Error' ); |
| 108 |
} |
| 109 |
|
| 110 |
/** |
| 111 |
* Filter allows a custom Stream API class to be instantiated |
| 112 |
* |
| 113 |
* @since 2.0.2 |
| 114 |
* |
| 115 |
* @return object The API class object |
| 116 |
*/ |
| 117 |
self::$api = apply_filters( 'wp_stream_api_class', new WP_Stream_API ); |
| 118 |
|
| 119 |
// Install the plugin |
| 120 |
add_action( 'wp_stream_before_db_notices', array( __CLASS__, 'install' ) ); |
| 121 |
|
| 122 |
// Load languages |
| 123 |
add_action( 'plugins_loaded', array( __CLASS__, 'i18n' ) ); |
| 124 |
|
| 125 |
// Load settings, enabling extensions to hook in |
| 126 |
add_action( 'init', array( 'WP_Stream_Settings', 'load' ), 9 ); |
| 127 |
|
| 128 |
// Load logger class |
| 129 |
add_action( 'plugins_loaded', array( 'WP_Stream_Log', 'load' ) ); |
| 130 |
|
| 131 |
// Load connectors after widgets_init, but before the default of 10 |
| 132 |
add_action( 'init', array( 'WP_Stream_Connectors', 'load' ), 9 ); |
| 133 |
|
| 134 |
// Load extensions |
| 135 |
foreach ( glob( WP_STREAM_EXTENSIONS_DIR . '*' ) as $extension ) { |
| 136 |
require_once sprintf( '%s/class-wp-stream-%s.php', $extension, basename( $extension ) ); |
| 137 |
} |
| 138 |
|
| 139 |
// Load support for feeds |
| 140 |
add_action( 'init', array( 'WP_Stream_Feeds', 'load' ) ); |
| 141 |
|
| 142 |
// Add frontend indicator |
| 143 |
add_action( 'wp_head', array( $this, 'frontend_indicator' ) ); |
| 144 |
|
| 145 |
// Load admin area classes |
| 146 |
if ( is_admin() ) { |
| 147 |
add_action( 'init', array( 'WP_Stream_Admin', 'load' ) ); |
| 148 |
add_action( 'init', array( 'WP_Stream_Dashboard_Widget', 'load' ) ); |
| 149 |
add_action( 'init', array( 'WP_Stream_Live_Update', 'load' ) ); |
| 150 |
add_action( 'init', array( 'WP_Stream_Pointers', 'load' ) ); |
| 151 |
add_action( 'init', array( 'WP_Stream_Migrate', 'load' ) ); |
| 152 |
} |
| 153 |
|
| 154 |
// Disable logging during the content import process |
| 155 |
add_filter( 'wp_stream_record_array', array( __CLASS__, 'disable_logging_during_import' ), 10, 1 ); |
| 156 |
|
| 157 |
// Load WP-CLI command |
| 158 |
if ( defined( 'WP_CLI' ) && WP_CLI ) { |
| 159 |
require_once WP_STREAM_INC_DIR . 'wp-cli.php'; |
| 160 |
|
| 161 |
WP_CLI::add_command( self::WP_CLI_COMMAND, 'WP_Stream_WP_CLI_Command' ); |
| 162 |
} |
| 163 |
} |
| 164 |
|
| 165 |
/** |
| 166 |
* Invoked when the PHP version check fails |
| 167 |
* |
| 168 |
* Load up the translations and add the error message to the admin notices. |
| 169 |
* |
| 170 |
* @return void |
| 171 |
*/ |
| 172 |
public static function fail_php_version() { |
| 173 |
add_action( 'plugins_loaded', array( __CLASS__, 'i18n' ) ); |
| 174 |
self::notice( __( 'Stream requires PHP version 5.3+, plugin is currently NOT ACTIVE.', 'stream' ) ); |
| 175 |
} |
| 176 |
|
| 177 |
/** |
| 178 |
* Check for deprecated extension plugins |
| 179 |
* |
| 180 |
* @return bool |
| 181 |
*/ |
| 182 |
public static function deprecated_plugins_exist() { |
| 183 |
foreach ( self::$deprecated_extensions as $class => $dir ) { |
| 184 |
if ( class_exists( $class ) ) { |
| 185 |
return true; |
| 186 |
} |
| 187 |
} |
| 188 |
|
| 189 |
return false; |
| 190 |
} |
| 191 |
|
| 192 |
/** |
| 193 |
* Display admin notices when deprecated extension plugins exist |
| 194 |
* |
| 195 |
* @return void |
| 196 |
*/ |
| 197 |
public static function deprecated_plugins_notice() { |
| 198 |
add_action( 'plugins_loaded', array( __CLASS__, 'i18n' ) ); |
| 199 |
|
| 200 |
if ( ! function_exists( 'get_plugins' ) ) { |
| 201 |
require_once ABSPATH . 'wp-admin/includes/plugin.php'; |
| 202 |
} |
| 203 |
|
| 204 |
$message = null; |
| 205 |
|
| 206 |
foreach ( self::$deprecated_extensions as $class => $dir ) { |
| 207 |
if ( class_exists( $class ) ) { |
| 208 |
$data = get_plugin_data( sprintf( '%s/%s', WP_PLUGIN_DIR, $dir ) ); |
| 209 |
$name = isset( $data['Name'] ) ? $data['Name'] : $dir; |
| 210 |
$message .= sprintf( '<li><strong>%s</strong></li>', esc_html( $name ) ); |
| 211 |
|
| 212 |
deactivate_plugins( $dir ); |
| 213 |
} |
| 214 |
} |
| 215 |
|
| 216 |
if ( ! empty( $message ) ) { |
| 217 |
ob_start(); |
| 218 |
?> |
| 219 |
<h3><?php _e( 'Deprecated Plugins Found', 'stream' ) ?></h3> |
| 220 |
<p><?php _e( 'The following plugins are deprecated and will be deactivated in order to activate', 'stream' ) ?> <strong>Stream <?php echo esc_html( self::VERSION ) ?></strong>:</p> |
| 221 |
<ul> |
| 222 |
<?php |
| 223 |
$start = ob_get_clean(); |
| 224 |
|
| 225 |
ob_start(); |
| 226 |
?> |
| 227 |
</ul> |
| 228 |
<p> |
| 229 |
<a href='#' onclick="location.reload(true); return false;" class="button button-large"><?php _e( 'Continue', 'stream' ) ?></a> |
| 230 |
</p> |
| 231 |
<?php |
| 232 |
$end = ob_get_clean(); |
| 233 |
|
| 234 |
wp_die( $start . $message . $end, __( 'Deprecated Plugins Found', 'stream' ) ); |
| 235 |
} |
| 236 |
} |
| 237 |
|
| 238 |
/** |
| 239 |
* Autoloader for classes |
| 240 |
* |
| 241 |
* @param string $class |
| 242 |
* |
| 243 |
* @return void |
| 244 |
*/ |
| 245 |
function autoload( $class ) { |
| 246 |
$class = strtolower( str_replace( '_', '-', $class ) ); |
| 247 |
$class_file = sprintf( '%sclass-%s.php', WP_STREAM_CLASS_DIR, $class ); |
| 248 |
|
| 249 |
if ( is_readable( $class_file ) ) { |
| 250 |
require_once $class_file; |
| 251 |
} |
| 252 |
} |
| 253 |
|
| 254 |
/** |
| 255 |
* Loads the translation files. |
| 256 |
* |
| 257 |
* @action plugins_loaded |
| 258 |
* |
| 259 |
* @return void |
| 260 |
*/ |
| 261 |
public static function i18n() { |
| 262 |
load_plugin_textdomain( 'stream', false, dirname( plugin_basename( __FILE__ ) ) . '/languages/' ); |
| 263 |
} |
| 264 |
|
| 265 |
/** |
| 266 |
* Whether the current PHP version meets the minimum requirements |
| 267 |
* |
| 268 |
* @return bool |
| 269 |
*/ |
| 270 |
public static function is_valid_php_version() { |
| 271 |
return version_compare( PHP_VERSION, '5.3', '>=' ); |
| 272 |
} |
| 273 |
|
| 274 |
/** |
| 275 |
* Is Stream connected? |
| 276 |
* |
| 277 |
* @return bool |
| 278 |
*/ |
| 279 |
public static function is_connected() { |
| 280 |
return ( self::$api->api_key && self::$api->site_uuid ); |
| 281 |
} |
| 282 |
|
| 283 |
/** |
| 284 |
* Is Stream in development mode? |
| 285 |
* |
| 286 |
* @return bool |
| 287 |
*/ |
| 288 |
public static function is_development_mode() { |
| 289 |
$development_mode = false; |
| 290 |
|
| 291 |
if ( defined( 'WP_STREAM_DEV_DEBUG' ) ) { |
| 292 |
$development_mode = WP_STREAM_DEV_DEBUG; |
| 293 |
} else if ( site_url() && false === strpos( site_url(), '.' ) ) { |
| 294 |
$development_mode = true; |
| 295 |
} |
| 296 |
|
| 297 |
/** |
| 298 |
* Filter allows development mode to be overridden |
| 299 |
* |
| 300 |
* @since 2.0.0 |
| 301 |
* |
| 302 |
* @return bool |
| 303 |
*/ |
| 304 |
return apply_filters( 'wp_stream_development_mode', $development_mode ); |
| 305 |
} |
| 306 |
|
| 307 |
/** |
| 308 |
* Disable logging during the content import process |
| 309 |
* |
| 310 |
* @filter wp_stream_record_array |
| 311 |
* |
| 312 |
* @param array $records |
| 313 |
* |
| 314 |
* @return array |
| 315 |
*/ |
| 316 |
public static function disable_logging_during_import( $records ) { |
| 317 |
if ( defined( 'WP_IMPORTING' ) && WP_IMPORTING ) { |
| 318 |
$records = array(); |
| 319 |
} |
| 320 |
|
| 321 |
return $records; |
| 322 |
} |
| 323 |
|
| 324 |
/** |
| 325 |
* Handle notice messages according to the appropriate context (WP-CLI or the WP Admin) |
| 326 |
* |
| 327 |
* @param string $message |
| 328 |
* @param bool $is_error |
| 329 |
* |
| 330 |
* @return void |
| 331 |
*/ |
| 332 |
public static function notice( $message, $is_error = true ) { |
| 333 |
if ( defined( 'WP_CLI' ) && WP_CLI ) { |
| 334 |
$message = strip_tags( $message ); |
| 335 |
|
| 336 |
if ( $is_error ) { |
| 337 |
WP_CLI::warning( $message ); |
| 338 |
} else { |
| 339 |
WP_CLI::success( $message ); |
| 340 |
} |
| 341 |
} else { |
| 342 |
// Trigger admin notices late, so that any notices which occur during page load are displayed |
| 343 |
add_action( 'shutdown', array( __CLASS__, 'admin_notices' ) ); |
| 344 |
|
| 345 |
$notice = compact( 'message', 'is_error' ); |
| 346 |
|
| 347 |
if ( ! in_array( $notice, self::$notices ) ) { |
| 348 |
self::$notices[] = $notice; |
| 349 |
} |
| 350 |
} |
| 351 |
} |
| 352 |
|
| 353 |
/** |
| 354 |
* Show an error or other message in the WP Admin |
| 355 |
* |
| 356 |
* @action shutdown |
| 357 |
* |
| 358 |
* @return void |
| 359 |
*/ |
| 360 |
public static function admin_notices() { |
| 361 |
global $allowedposttags; |
| 362 |
|
| 363 |
$custom = array( |
| 364 |
'progress' => array( |
| 365 |
'class' => true, |
| 366 |
'id' => true, |
| 367 |
'max' => true, |
| 368 |
'style' => true, |
| 369 |
'value' => true, |
| 370 |
), |
| 371 |
); |
| 372 |
|
| 373 |
$allowed_html = array_merge( $allowedposttags, $custom ); |
| 374 |
|
| 375 |
ksort( $allowed_html ); |
| 376 |
|
| 377 |
foreach ( self::$notices as $notice ) { |
| 378 |
$class_name = empty( $notice['is_error'] ) ? 'updated' : 'error'; |
| 379 |
$html_message = sprintf( '<div class="%s">%s</div>', esc_attr( $class_name ), wpautop( $notice['message'] ) ); |
| 380 |
|
| 381 |
echo wp_kses( $html_message, $allowed_html ); |
| 382 |
} |
| 383 |
} |
| 384 |
|
| 385 |
/** |
| 386 |
* Displays an HTML comment in the frontend head to indicate that Stream is activated, |
| 387 |
* and which version of Stream is currently in use. |
| 388 |
* |
| 389 |
* @action wp_head |
| 390 |
* |
| 391 |
* @return string|void An HTML comment, or nothing if the value is filtered out. |
| 392 |
*/ |
| 393 |
public function frontend_indicator() { |
| 394 |
$comment = sprintf( 'Stream WordPress user activity plugin v%s', esc_html( self::VERSION ) ); // Localization not needed |
| 395 |
|
| 396 |
/** |
| 397 |
* Filter allows the HTML output of the frontend indicator comment |
| 398 |
* to be altered or removed, if desired. |
| 399 |
* |
| 400 |
* @since 1.4.5 |
| 401 |
* |
| 402 |
* @return string The content of the HTML comment |
| 403 |
*/ |
| 404 |
$comment = apply_filters( 'wp_stream_frontend_indicator', $comment ); |
| 405 |
|
| 406 |
if ( ! empty( $comment ) ) { |
| 407 |
echo sprintf( "<!-- %s -->\n", esc_html( $comment ) ); // xss ok |
| 408 |
} |
| 409 |
} |
| 410 |
|
| 411 |
/** |
| 412 |
* Return active instance of WP_Stream, create one if it doesn't exist |
| 413 |
* |
| 414 |
* @return WP_Stream |
| 415 |
*/ |
| 416 |
public static function get_instance() { |
| 417 |
if ( empty( self::$instance ) ) { |
| 418 |
$class = __CLASS__; |
| 419 |
self::$instance = new $class; |
| 420 |
} |
| 421 |
|
| 422 |
return self::$instance; |
| 423 |
} |
| 424 |
|
| 425 |
} |
| 426 |
|
| 427 |
if ( ! WP_Stream::is_valid_php_version() ) { |
| 428 |
WP_Stream::fail_php_version(); |
| 429 |
} elseif ( WP_Stream::deprecated_plugins_exist() ) { |
| 430 |
WP_Stream::deprecated_plugins_notice(); |
| 431 |
} else { |
| 432 |
$GLOBALS['wp_stream'] = WP_Stream::get_instance(); |
| 433 |
} |
| 434 |
|
| 435 |
register_deactivation_hook( __FILE__, array( 'WP_Stream_Admin', 'remove_api_authentication' ) ); |
| 436 |
|