diff --git a/connectors/blogs.php b/connectors/blogs.php new file mode 100644 index 000000000..9ca59c820 --- /dev/null +++ b/connectors/blogs.php @@ -0,0 +1,309 @@ + __( 'Updated', 'stream' ), + 'created' => __( 'Created', 'stream' ), + 'archive_blog' => __( 'Archived', 'stream' ), + 'deleted' => __( 'Deleted', 'stream' ), + ); + } + + /** + * Return translated context labels + * + * @return array Context label translations + */ + public static function get_context_labels() { + $labels = array(); + if ( is_multisite() ) { + $blogs = wp_get_sites(); + foreach ( $blogs as $blog ) { + $blog_details = get_blog_details( $blog['blog_id'] ); + $key = sanitize_key( $blog_details->blogname ); + $labels[ $key ] = $blog_details->blogname; + } + } + return $labels; + } + + /** + * Add action links to Stream drop row in admin list screen + * + * @filter wp_stream_action_links_{connector} + * @param array $links Previous links registered + * @param int $record Stream record + * @return array Action links + */ + public static function action_links( $links, $record ) { + $links [ __( 'Site Admin', 'stream' ) ] = get_admin_url( $record->object_id ); + if ( $record->object_id ) { + $site_admin_link = get_admin_url( $record->object_id ); + + if ( $site_admin_link ) { + $links [ __( 'Site Admin', 'stream' ) ] = $site_admin_link; + } + + $site_settings_link = add_query_arg( + array( + 'id' => $record->object_id, + ), + network_admin_url( 'site-info.php' ) + ); + + if ( $site_settings_link ) { + $links [ __( 'Site Settings', 'stream' ) ] = $site_settings_link; + } + } + return $links; + } + + /** + * @param $blog_id + * @action wpmu_new_blog + */ + public static function callback_wpmu_new_blog( $blog_id ) { + $blog = get_blog_details( $blog_id ); + $context = sanitize_key( $blog->blogname ); + self::log( + _x( + 'A new site called "%1$s" has been created.', + '1. Site name', + 'stream' + ), + array( + 'site_name' => $blog->blogname, + ), + $blog_id, + array( $context => 'created' ) + ); + } + + /** + * @param $blog_id + * @param $user_id + * @action wpmu_activate_blog + */ + public static function callback_wpmu_activate_blog( $blog_id, $user_id ) { + $blog = get_blog_details( $blog_id ); + $context = sanitize_key( $blog->blogname ); + self::log( + _x( + 'A new site called "%1$s" has been registered.', + '1. Site name', + 'stream' + ), + array( + 'site_name' => $blog->blogname, + ), + $blog_id, + array( $context => 'created' ), + $user_id + ); + } + + /** + * @param $user_id + * @param $role + * @param $blog_id + * @action add_user_to_blog + */ + public static function callback_add_user_to_blog( $user_id, $role, $blog_id ) { + $blog = get_blog_details( $blog_id ); + $user = get_user_by( 'id', $user_id ); + $context = sanitize_key( $blog->blogname ); + + if ( ! is_a( $user, 'WP_User' ) ) { + return; + } + + self::log( + _x( + '%1$s has been added to the site "%2$s" with %3$s capabilities.', + '1. User\'s name, 2. Site name, 3. Role', + 'stream' + ), + array( + 'user_name' => $user->display_name, + 'site_name' => $blog->blogname, + 'role_name' => $role, + ), + $blog_id, + array( $context => 'updated' ) + ); + } + + /** + * @param $user_id + * @param $blog_id + * @action remove_user_from_blog + */ + public static function callback_remove_user_from_blog( $user_id, $blog_id ) { + $blog = get_blog_details( $blog_id ); + $user = get_user_by( 'id', $user_id ); + $context = sanitize_key( $blog->blogname ); + + if ( ! is_a( $user, 'WP_User' ) ) { + return; + } + + self::log( + _x( + '%1$s has been removed from the site "%2$s".', + '1. User\'s name, 2. Site name', + 'stream' + ), + array( + 'user_name' => $user->display_name, + 'site_name' => $blog->blogname, + ), + $blog_id, + array( $context => 'updated' ) + ); + } + + /** + * @param $blog_id + * @action make_spam_blog + */ + public static function callback_make_spam_blog( $blog_id ) { + self::callback_update_blog_status( $blog_id, __( 'marked as spam', 'stream' ), 'updated' ); + } + + /** + * @param $blog_id + * @action make_spam_blog + */ + public static function callback_make_ham_blog( $blog_id ) { + self::callback_update_blog_status( $blog_id, __( 'marked as not spam', 'stream' ), 'updated' ); + } + + /** + * @param $blog_id + * @action mature_blog + */ + public static function callback_mature_blog( $blog_id ) { + self::callback_update_blog_status( $blog_id, __( 'marked as mature', 'stream' ), 'updated' ); + } + + /** + * @param $blog + * @action unmature_blog + */ + public static function callback_unmature_blog( $blog ) { + self::callback_update_blog_status( $blog_id, __( 'marked as not mature', 'stream' ), 'updated' ); + } + + /** + * @param $blog + * @action archive_blog + */ + public static function callback_archive_blog( $blog ) { + self::callback_update_blog_status( $blog_id, __( 'archived', 'stream' ), 'archive_blog' ); + } + + /** + * @param $blog_id + * @action unarchive_blog + */ + public static function callback_unarchive_blog( $blog_id ) { + self::callback_update_blog_status( $blog_id, __( 'restored from archive', 'stream' ), 'updated' ); + } + + /** + * @param $blog_id + * @action make_delete_blog + */ + public static function callback_make_delete_blog( $blog_id ) { + self::callback_update_blog_status( $blog_id, __( 'deleted', 'stream' ), 'deleted' ); + } + + /** + * @param $blog_id + * @action undelete_blog + */ + public static function callback_make_undelete_blog( $blog_id ) { + self::callback_update_blog_status( $blog_id, __( 'restored', 'stream' ), 'updated' ); + } + + /** + * @param $blog_id + * @param $value + * @action update_blog_public + */ + public static function callback_update_blog_public( $blog_id, $value ) { + if ( $value ) { + $status = __( 'marked as public', 'stream' ); + } else { + $status = __( 'marked as private', 'stream' ); + } + self::callback_update_blog_status( $blog_id, $status, 'updated' ); + } + + /** + * @param $blog_id + * @action update_blog_public + */ + public static function callback_update_blog_status( $blog_id, $status, $action ) { + $blog = get_blog_details( $blog_id ); + $context = sanitize_key( $blog->blogname ); + self::log( + _x( + '"%1$s" has been %2$s.', + '1. Site name, 2. Status', + 'stream' + ), + array( + 'site_name' => $blog->blogname, + 'status' => $status, + ), + $blog_id, + array( $context => $action ) + ); + + } + +} diff --git a/connectors/settings.php b/connectors/settings.php index abd1771d0..07414e87d 100644 --- a/connectors/settings.php +++ b/connectors/settings.php @@ -18,6 +18,7 @@ class WP_Stream_Connector_Settings extends WP_Stream_Connector { */ public static $actions = array( 'whitelist_options', + 'update_site_option', 'update_option_permalink_structure', 'update_option_category_base', 'update_option_tag_base', @@ -34,6 +35,37 @@ class WP_Stream_Connector_Settings extends WP_Stream_Connector { 'tag_base', ); + /** + * Option names used in network/settings.php + * + * @var array + */ + public static $network_options = array( + 'registrationnotification', + 'registration', + 'add_new_users', + 'menu_items', + 'upload_space_check_disabled', + 'blog_upload_space', + 'upload_filetypes', + 'site_name', + 'first_post', + 'first_page', + 'first_comment', + 'first_comment_url', + 'first_comment_author', + 'welcome_email', + 'welcome_user_email', + 'fileupload_maxk', + 'global_terms_enabled', + 'illegal_names', + 'limited_email_domains', + 'banned_email_domains', + 'WPLANG', + 'admin_email', + ); + + /** * Register all context hooks * @@ -81,16 +113,19 @@ public static function get_action_labels() { */ public static function get_context_labels() { return array( - 'settings' => __( 'Settings', 'stream' ), - 'general' => __( 'General', 'stream' ), - 'writing' => __( 'Writing', 'stream' ), - 'reading' => __( 'Reading', 'stream' ), - 'discussion' => __( 'Discussion', 'stream' ), - 'media' => __( 'Media', 'stream' ), - 'permalink' => __( 'Permalinks', 'stream' ), - 'wp_stream' => __( 'Stream', 'stream' ), - 'custom_background' => __( 'Custom Background', 'stream' ), - 'custom_header' => __( 'Custom Header', 'stream' ), + 'settings' => __( 'Settings', 'stream' ), + 'general' => __( 'General', 'stream' ), + 'writing' => __( 'Writing', 'stream' ), + 'reading' => __( 'Reading', 'stream' ), + 'discussion' => __( 'Discussion', 'stream' ), + 'media' => __( 'Media', 'stream' ), + 'permalink' => __( 'Permalinks', 'stream' ), + 'network' => __( 'Network', 'stream' ), + 'wp_stream' => __( 'Stream', 'stream' ), + 'wp_stream_network' => __( 'Stream Network', 'stream' ), + 'wp_stream_defaults' => __( 'Stream Defaults', 'stream' ), + 'custom_background ' => __( 'Custom Background', 'stream' ), + 'custom_header' => __( 'Custom Header', 'stream' ), ); } @@ -221,6 +256,29 @@ public static function get_field_label( $field_key ) { 'permalink_structure' => __( 'Permalink structure', 'stream' ), 'category_base' => __( 'Category base', 'stream' ), 'tag_base' => __( 'Tag base', 'stream' ), + // Network + 'registrationnotification' => __( 'Registration notification', 'stream' ), + 'registration' => __( 'Allow new registrations', 'stream' ), + 'add_new_users' => __( 'Add New Users', 'stream' ), + 'menu_items' => __( 'Enable administration menus', 'stream' ), + 'upload_space_check_disabled' => __( 'Site upload space check', 'stream' ), + 'blog_upload_space' => __( 'Site upload space', 'stream' ), + 'upload_filetypes' => __( 'Upload file types', 'stream' ), + 'site_name' => __( 'Network Title', 'stream' ), + 'first_post' => __( 'First Post', 'stream' ), + 'first_page' => __( 'First Page', 'stream' ), + 'first_comment' => __( 'First Comment', 'stream' ), + 'first_comment_url' => __( 'First Comment URL', 'stream' ), + 'first_comment_author' => __( 'First Comment Author', 'stream' ), + 'welcome_email' => __( 'Welcome Email', 'stream' ), + 'welcome_user_email' => __( 'Welcome User Email', 'stream' ), + 'fileupload_maxk' => __( 'Max upload file size', 'stream' ), + 'global_terms_enabled' => __( 'Terms Enabled', 'stream' ), + 'illegal_names' => __( 'Banned Names', 'stream' ), + 'limited_email_domains' => __( 'Limited Email Registrations', 'stream' ), + 'banned_email_domains' => __( 'Banned Email Domains', 'stream' ), + 'WPLANG' => __( 'Network Language', 'stream' ), + 'admin_email' => __( 'Network Admin Email', 'stream' ), ); if ( isset( $labels[ $field_key ] ) ) { @@ -342,6 +400,21 @@ public static function action_links( $links, $record ) { return ! empty( $submenu['options-general.php'] ); }, ), + 'network' => array( + 'menu_slug' => 'settings.php', + 'submenu_slug' => function( $record ) { + return 'settings.php'; + }, + 'url' => function( $rule, $record ) { + return network_admin_url( $rule['menu_slug'] ); + }, + 'applicable' => function( $submenu, $record ) { + if ( ! $record->blog_id ) { + return ! empty( $submenu['settings.php'] ); + } + return false; + }, + ), ); if ( 'settings' !== $record->context && in_array( $record->context, array_keys( $context_labels ) ) ) { @@ -414,6 +487,15 @@ public static function callback_update_option_permalink_structure( $old_value, $ self::callback_updated_option( 'permalink_structure', $old_value, $value ); } + /** + * Trigger this connector core tracker, only on network/settings.php page + * + * @action update_site_option + */ + public static function callback_update_site_option( $option, $value, $old_value ) { + self::callback_updated_option( $option, $value, $old_value ); + } + /** * Trigger this connector core tracker, only on options-permalink.php page * @@ -440,14 +522,15 @@ public static function callback_update_option_tag_base( $old_value, $value ) { public static function callback_updated_option( $option, $old_value, $value ) { global $whitelist_options, $new_whitelist_options; - if ( 0 === strpos( $option, '_transient_' ) ) { + if ( 0 === strpos( $option, '_transient_' ) || 0 === strpos( $option, '_site_transient_' ) ) { return; } $options = array_merge( (array) $whitelist_options, (array) $new_whitelist_options, - array( 'permalink' => self::$permalink_options ) + array( 'permalink' => self::$permalink_options ), + array( 'network' => self::$network_options ) ); foreach ( $options as $key => $opts ) { @@ -462,8 +545,14 @@ public static function callback_updated_option( $option, $old_value, $value ) { } $changed_options = array(); - + $option_group = false; if ( is_array( $old_value ) || is_array( $value ) ) { + if ( count( array_filter( array_keys( $value ), 'is_string' ) ) > 0 ) { + $option_group = true; + } + } + + if ( $option_group ) { foreach ( self::get_changed_keys( $old_value, $value ) as $field_key ) { if ( ! self::is_key_ignored( $option, $field_key ) ) { $key_context = self::get_context_by_key( $option, $field_key ); diff --git a/includes/admin.php b/includes/admin.php index f0a8d68ea..b6d9067ca 100644 --- a/includes/admin.php +++ b/includes/admin.php @@ -16,6 +16,13 @@ class WP_Stream_Admin { */ public static $list_table = null; + /** + * Option to disable access to Stream + * + * @var bool + */ + public static $disable_access = false; + const ADMIN_BODY_CLASS = 'wp_stream_screen'; const RECORDS_PAGE_SLUG = 'wp_stream'; const SETTINGS_PAGE_SLUG = 'wp_stream_settings'; @@ -29,14 +36,20 @@ public static function load() { add_filter( 'user_has_cap', array( __CLASS__, '_filter_user_caps' ), 10, 4 ); add_filter( 'role_has_cap', array( __CLASS__, '_filter_role_caps' ), 10, 3 ); - // Add admin body class - add_filter( 'admin_body_class', array( __CLASS__, 'admin_body_class' ) ); + self::$disable_access = apply_filters( 'wp_stream_disable_admin_access', false ); // Register settings page add_action( 'admin_menu', array( __CLASS__, 'register_menu' ) ); + // Admin notices + add_action( 'admin_notices', array( __CLASS__, 'admin_notices' ) ); + + // Add admin body class + add_filter( 'admin_body_class', array( __CLASS__, 'admin_body_class' ) ); + // Plugin action links add_filter( 'plugin_action_links', array( __CLASS__, 'plugin_action_links' ), 10, 2 ); + add_filter( 'network_admin_plugin_action_links', array( __CLASS__, 'plugin_action_links' ), 10, 2 ); // Load admin scripts and styles add_action( 'admin_enqueue_scripts', array( __CLASS__, 'admin_enqueue_scripts' ) ); @@ -45,6 +58,9 @@ public static function load() { // Reset Streams database add_action( 'wp_ajax_wp_stream_reset', array( __CLASS__, 'wp_ajax_reset' ) ); + // Reset Streams settings + add_action( 'wp_ajax_wp_stream_defaults', array( __CLASS__, 'wp_ajax_defaults' ) ); + // Uninstall Streams and Deactivate plugin add_action( 'wp_ajax_wp_stream_uninstall', array( __CLASS__, 'uninstall_plugin' ) ); @@ -78,6 +94,9 @@ public static function admin_notices() { case 'data_erased': printf( '

%s

', __( 'All records have been successfully erased.', 'stream' ) ); break; + case 'settings_reset': + printf( '

%s

', __( 'All site settings have been successfully reset.', 'stream' ) ); + break; } } @@ -85,9 +104,17 @@ public static function admin_notices() { * Register menu page * * @action admin_menu - * @return void + * @return bool|void */ public static function register_menu() { + if ( is_network_admin() && ! is_plugin_active_for_network( WP_STREAM_PLUGIN ) ) { + return false; + } + + if ( self::$disable_access ) { + return false; + } + self::$screen_id['main'] = add_menu_page( __( 'Stream', 'stream' ), __( 'Stream', 'stream' ), @@ -107,8 +134,12 @@ public static function register_menu() { array( __CLASS__, 'render_page' ) ); - // Register the list table early, so it associates the column headers with 'Screen settings' - add_action( 'load-' . self::$screen_id['main'], array( __CLASS__, 'register_list_table' ) ); + do_action( 'wp_stream_admin_menu_screens' ); + + if ( ! WP_Stream_Install::$update_required ) { + // Register the list table early, so it associates the column headers with 'Screen settings' + add_action( 'load-' . self::$screen_id['main'], array( __CLASS__, 'register_list_table' ) ); + } } /** @@ -123,10 +154,11 @@ public static function register_menu() { public static function admin_enqueue_scripts( $hook ) { wp_register_script( 'select2', WP_STREAM_URL . 'ui/select2/select2.min.js', array( 'jquery' ), '3.4.5', true ); wp_register_style( 'select2', WP_STREAM_URL . 'ui/select2/select2.css', array(), '3.4.5' ); - wp_register_script( 'timeago', WP_STREAM_URL . 'ui/timeago/timeago.js', array(), '0.2.0', true ); + $locale = substr( get_locale(), 0, 2 ); $file_tmpl = 'ui/timeago/locale/jquery.timeago.%s.js'; + if ( file_exists( WP_STREAM_DIR . sprintf( $file_tmpl, $locale ) ) ) { wp_register_script( 'timeago-locale', WP_STREAM_URL . sprintf( $file_tmpl, $locale ), array( 'timeago' ), '1' ); } else { @@ -151,14 +183,15 @@ public static function admin_enqueue_scripts( $hook ) { array( 'i18n' => array( 'confirm_purge' => __( 'Are you sure you want to delete all Stream activity records from the database? This cannot be undone.', 'stream' ), + 'confirm_defaults' => __( 'Are you sure you want to reset all site settings to default? This cannot be undone.', 'stream' ), 'confirm_uninstall' => __( 'Are you sure you want to uninstall and deactivate Stream? This will delete all Stream tables from the database and cannot be undone.', 'stream' ), ), - 'gmt_offset' => get_option( 'gmt_offset' ), - 'current_screen' => $hook, - 'current_page' => isset( $_GET['paged'] ) ? esc_js( $_GET['paged'] ) : '1', - 'current_order' => isset( $_GET['order'] ) ? esc_js( $_GET['order'] ) : 'desc', - 'current_query' => json_encode( $_GET ), - 'filter_controls' => get_user_meta( get_current_user_id(), 'stream_toggle_filters', true ), + 'gmt_offset' => get_option( 'gmt_offset' ), + 'current_screen' => $hook, + 'current_page' => isset( $_GET['paged'] ) ? esc_js( $_GET['paged'] ) : '1', + 'current_order' => isset( $_GET['order'] ) ? esc_js( $_GET['order'] ) : 'desc', + 'current_query' => json_encode( $_GET ), + 'filters' => self::$list_table ? self::$list_table->get_filters() : false, ) ); } @@ -184,8 +217,9 @@ public static function admin_body_class( $classes ) { /** * Add menu styles for various WP Admin skins * + * @uses wp_add_inline_style() * @action admin_enqueue_scripts - * @return void + * @return bool true on success false on failure */ public static function admin_menu_css() { wp_register_style( 'jquery-ui', '//ajax.googleapis.com/ajax/libs/jqueryui/1.10.1/themes/base/jquery-ui.css', array(), '1.10.1' ); @@ -254,10 +288,16 @@ public static function admin_menu_css() { */ public static function plugin_action_links( $links, $file ) { if ( plugin_basename( WP_STREAM_DIR . 'stream.php' ) === $file ) { - $admin_page_url = add_query_arg( array( 'page' => self::SETTINGS_PAGE_SLUG ), admin_url( self::ADMIN_PARENT_PAGE ) ); + + // Don't show links in Network Admin if Stream isn't network enabled + if ( is_network_admin() && is_multisite() && ! is_plugin_active_for_network( WP_STREAM_PLUGIN ) ) { + return $links; + } + + $admin_page_url = add_query_arg( array( 'page' => self::SETTINGS_PAGE_SLUG ), is_network_admin() ? network_admin_url( self::ADMIN_PARENT_PAGE ) : admin_url( self::ADMIN_PARENT_PAGE ) ); $links[] = sprintf( '%s', esc_url( $admin_page_url ), esc_html__( 'Settings', 'stream' ) ); - $url = add_query_arg( + $url = add_query_arg( array( 'action' => 'wp_stream_uninstall', 'wp_stream_nonce' => wp_create_nonce( 'stream_nonce' ), @@ -316,43 +356,62 @@ public static function register_update_hook( $file, $callback, $version ) { * @return void */ public static function render_page() { + + $option_key = WP_Stream_Settings::$option_key; + $form_action = apply_filters( 'wp_stream_settings_form_action', admin_url( 'options.php' ) ); + + $page_title = apply_filters( 'wp_stream_settings_form_title', get_admin_page_title() ); + $page_description = apply_filters( 'wp_stream_settings_form_description', '' ); + + $sections = WP_Stream_Settings::get_fields(); + $active_tab = wp_stream_filter_input( INPUT_GET, 'tab' ); + + if ( WP_Stream_Install::$update_required ) { + printf( '

%s

', $page_title ); + return; + } + ?>
-

+

+ + +

+ + - - - + 1 ) : ?> + +
@@ -365,10 +424,22 @@ public static function register_list_table() { } public static function stream_page() { - self::$list_table->prepare_items(); + $page_title = __( 'Stream Records', 'stream' ); echo '
'; - printf( '

%s

', __( 'Stream Records', 'stream' ) ); // xss ok + + if ( is_network_admin() ) { + $site_count = sprintf( _n( '1 site', '%d sites', get_blog_count(), 'stream' ), get_blog_count() ); + printf( '

%s (%s)

', __( 'Stream Records', 'stream' ), $site_count ); // xss ok + } else { + printf( '

%s

', __( 'Stream Records', 'stream' ) ); // xss ok + } + + if ( WP_Stream_Install::$update_required ) { + return; + } + + self::$list_table->prepare_items(); self::$list_table->display(); echo '
'; } @@ -381,10 +452,10 @@ public static function wp_ajax_reset() { wp_redirect( add_query_arg( array( - 'page' => 'wp_stream_settings', + 'page' => is_network_admin() ? 'wp_stream_network_settings' : 'wp_stream_settings', 'message' => 'data_erased', ), - admin_url( self::ADMIN_PARENT_PAGE ) + is_plugin_active_for_network( WP_STREAM_PLUGIN ) ? network_admin_url( self::ADMIN_PARENT_PAGE ) : admin_url( self::ADMIN_PARENT_PAGE ) ) ); exit; @@ -393,9 +464,14 @@ public static function wp_ajax_reset() { } } - public static function erase_stream_records() { + private static function erase_stream_records() { global $wpdb; + $where = ''; + if ( is_multisite() && ! is_plugin_active_for_network( WP_STREAM_PLUGIN ) ) { + $where .= $wpdb->prepare( ' AND `blog_id` = %d', get_current_blog_id() ); + } + $wpdb->query( $wpdb->prepare( "DELETE `stream`, `context`, `meta` @@ -404,12 +480,51 @@ public static function erase_stream_records() { ON `context`.`record_id` = `stream`.`ID` LEFT JOIN {$wpdb->streammeta} AS `meta` ON `meta`.`record_id` = `stream`.`ID` - WHERE `stream`.`type` = %s;", + WHERE `stream`.`type` = %s + $where;", 'stream' ) ); } + public static function wp_ajax_defaults() { + check_ajax_referer( 'stream_nonce', 'wp_stream_nonce' ); + + if ( ! is_plugin_active_for_network( WP_STREAM_PLUGIN ) ) { + wp_die( "You don't have sufficient privileges to do this action." ); + } + + if ( current_user_can( self::SETTINGS_CAP ) ) { + self::reset_stream_settings(); + wp_redirect( + add_query_arg( + array( + 'page' => is_network_admin() ? 'wp_stream_network_settings' : 'wp_stream_settings', + 'message' => 'settings_reset', + ), + is_plugin_active_for_network( WP_STREAM_PLUGIN ) ? network_admin_url( self::ADMIN_PARENT_PAGE ) : admin_url( self::ADMIN_PARENT_PAGE ) + ) + ); + exit; + } else { + wp_die( "You don't have sufficient privileges to do this action." ); + } + } + + private static function reset_stream_settings() { + global $wpdb; + + $blogs = wp_get_sites(); + + if ( $blogs ) { + foreach ( $blogs as $blog ) { + switch_to_blog( $blog['blog_id'] ); + delete_option( WP_Stream_Settings::KEY ); + } + restore_current_blog(); + } + } + /** * This function is used to uninstall all custom tables and uninstall the plugin * It will also uninstall custom actions @@ -423,30 +538,52 @@ public static function uninstall_plugin() { // Prevent stream action from being fired on plugin remove_action( 'deactivate_plugin', array( 'WP_Stream_Connector_Installer', 'callback' ), null ); - // Deactivate the plugin - deactivate_plugins( plugin_basename( WP_STREAM_DIR ) . '/stream.php' ); + // Plugin is being uninstalled from only one of the multisite blogs + if ( is_multisite() && ! is_plugin_active_for_network( WP_STREAM_PLUGIN ) ) { + $blog_id = get_current_blog_id(); + + $wpdb->query( "DELETE FROM {$wpdb->base_prefix}stream WHERE blog_id = $blog_id" ); + + delete_option( plugin_basename( WP_STREAM_DIR ) . '_db' ); + delete_option( WP_Stream_Settings::KEY ); + } else { + // Delete all tables + foreach ( WP_Stream_DB::get_instance()->get_table_names() as $table ) { + $wpdb->query( "DROP TABLE $table" ); + } + + // Delete database options + if ( is_multisite() ) { + $blogs = wp_get_sites(); + foreach ( $blogs as $blog ) { + switch_to_blog( $blog['blog_id'] ); + delete_option( plugin_basename( WP_STREAM_DIR ) . '_db' ); + delete_option( WP_Stream_Settings::KEY ); + } + restore_current_blog(); + } + + // Delete database option + delete_site_option( plugin_basename( WP_STREAM_DIR ) . '_db' ); + delete_site_option( WP_Stream_Settings::KEY ); + delete_site_option( WP_Stream_Settings::DEFAULTS_KEY ); + delete_site_option( WP_Stream_Settings::NETWORK_KEY ); + delete_site_option( 'dashboard_stream_activity_options' ); + } // Delete scheduled cron event hooks wp_clear_scheduled_hook( 'stream_auto_purge' ); // Deprecated hook wp_clear_scheduled_hook( 'wp_stream_auto_purge' ); - // Delete all tables - foreach ( WP_Stream_DB::get_instance()->get_table_names() as $table ) { - $wpdb->query( "DROP TABLE $table" ); - } - - // Delete database option - delete_option( WP_Stream_Install::KEY ); - delete_option( WP_Stream_Settings::KEY ); - delete_option( 'dashboard_stream_activity_options' ); + // Deactivate the plugin + deactivate_plugins( plugin_basename( WP_STREAM_DIR ) . '/stream.php' ); // Redirect to plugin page - wp_redirect( add_query_arg( array( 'deactivate' => true ), admin_url( 'plugins.php' ) ) ); + wp_redirect( add_query_arg( array( 'deactivate' => true ), is_network_admin() ? network_admin_url( 'plugins.php' ) : admin_url( 'plugins.php' ) ) ); exit; } else { wp_die( "You don't have sufficient privileges to do this action." ); } - } public static function purge_schedule_setup() { @@ -458,12 +595,28 @@ public static function purge_schedule_setup() { public static function purge_scheduled_action() { global $wpdb; - $options = WP_Stream_Settings::get_options(); - $days = $options['general_records_ttl']; - $date = new DateTime( 'now', $timezone = new DateTimeZone( 'UTC' ) ); + // Don't purge if in Network Admin if Stream isn't network enabled + if ( is_network_admin() && is_multisite() && ! is_plugin_active_for_network( WP_STREAM_PLUGIN ) ) { + return; + } + + if ( is_multisite() && is_plugin_active_for_network( WP_STREAM_PLUGIN ) ) { + $options = (array) get_site_option( WP_Stream_Settings::NETWORK_KEY, array() ); + } else { + $options = WP_Stream_Settings::get_options(); + } + + $days = $options['general_records_ttl']; + $date = new DateTime( 'now', $timezone = new DateTimeZone( 'UTC' ) ); $date->sub( DateInterval::createFromDateString( "$days days" ) ); + $where = $wpdb->prepare( ' AND `stream`.`created` < %s', $date->format( 'Y-m-d H:i:s' ) ); + + if ( is_multisite() && ! is_plugin_active_for_network( WP_STREAM_PLUGIN ) ) { + $where .= $wpdb->prepare( ' AND `blog_id` = %d', get_current_blog_id() ); + } + $wpdb->query( $wpdb->prepare( "DELETE `stream`, `context`, `meta` @@ -473,7 +626,7 @@ public static function purge_scheduled_action() { LEFT JOIN {$wpdb->streammeta} AS `meta` ON `meta`.`record_id` = `stream`.`ID` WHERE `stream`.`type` = %s - AND `stream`.`created` < %s;", + $where;", 'stream', $date->format( 'Y-m-d H:i:s' ) ) diff --git a/includes/dashboard.php b/includes/dashboard.php index c6ced8575..127c8e075 100644 --- a/includes/dashboard.php +++ b/includes/dashboard.php @@ -22,7 +22,7 @@ public static function stream_activity() { wp_add_dashboard_widget( 'dashboard_stream_activity', - esc_html__( 'Stream Activity', 'stream' ), + is_network_admin() ? esc_html__( 'Network Stream Activity', 'stream' ) : esc_html__( 'Stream Activity', 'stream' ), array( __CLASS__, 'stream_activity_initial_contents' ), array( __CLASS__, 'stream_activity_options' ) ); @@ -95,7 +95,7 @@ public static function pagination( $args = array() ) { $records_link = add_query_arg( array( 'page' => WP_Stream_Admin::RECORDS_PAGE_SLUG ), - admin_url( WP_Stream_Admin::ADMIN_PARENT_PAGE ) + is_network_admin() ? network_admin_url( WP_Stream_Admin::ADMIN_PARENT_PAGE ) : admin_url( WP_Stream_Admin::ADMIN_PARENT_PAGE ) ); $html_view_all = sprintf( diff --git a/includes/db-updates.php b/includes/db-updates.php index 709773e49..549654fd1 100644 --- a/includes/db-updates.php +++ b/includes/db-updates.php @@ -4,6 +4,8 @@ * Version 1.4.0 * * Create `author_role` column in `stream` table if it doesn't already exist. + * Create `blog_id` column in `stream` table if it doesn't already exist. + * Merge multisite Stream tables. * * @param string $db_version Database version updating from * @param string $current_version Database version updating to @@ -17,12 +19,85 @@ function wp_stream_update_140( $db_version, $current_version ) { do_action( 'wp_stream_before_db_update_' . $db_version, $current_version ); + // Check to see if the blog_id column already exists + $rows = $wpdb->get_results( "SHOW COLUMNS FROM `{$prefix}stream` WHERE field = 'blog_id'" ); + + // If the blog_id doesn't exist, then create it and update records retroactively + if ( empty( $rows ) ) { + $wpdb->query( "ALTER TABLE {$prefix}stream ADD blog_id bigint(20) unsigned NOT NULL DEFAULT '0' AFTER site_id" ); + } + + // If multisite, merge the site stream tables into one + if ( is_multisite() ) { + $blogs = wp_get_sites(); + + foreach ( $blogs as $blog ) { + switch_to_blog( $blog['blog_id'] ); + + // No need to merge the primary site, but update the blog_id + if ( $wpdb->prefix === $prefix ) { + $wpdb->update( + $prefix . 'stream', + array( 'blog_id' => $blog['blog_id'] ), + array( 'blog_id' => '0' ), + array( '%d' ), + array( '%d' ) + ); + continue; + } + + $sql = "SELECT * FROM {$wpdb->prefix}stream"; + + $blog_stream = $wpdb->get_results( $sql, ARRAY_A ); + + foreach ( $blog_stream as $key => $stream_entry ) { + $prev_entry_id = $stream_entry['ID']; + + unset( $stream_entry['ID'] ); + $stream_entry['blog_id'] = $blog['blog_id']; + + $wpdb->insert( $wpdb->base_prefix . 'stream', $stream_entry ); + $stream_entry_id = $wpdb->insert_id; + + $sql = "SELECT * FROM {$wpdb->prefix}stream_context WHERE record_id = $prev_entry_id"; + + $blog_stream_context = $wpdb->get_results( $sql, ARRAY_A ); + + foreach ( $blog_stream_context as $key => $stream_context ) { + unset( $stream_context['meta_id'] ); + $stream_context['record_id'] = $stream_entry_id; + + $wpdb->insert( $wpdb->base_prefix . 'stream_context', $stream_context ); + } + + $sql = "SELECT * FROM {$wpdb->prefix}stream_meta WHERE record_id = $prev_entry_id"; + + $blog_stream_meta = $wpdb->get_results( $sql, ARRAY_A ); + + foreach ( $blog_stream_meta as $key => $stream_meta ) { + unset( $stream_meta['meta_id'] ); + $stream_meta['record_id'] = $stream_entry_id; + + $wpdb->insert( $wpdb->base_prefix . 'stream_meta', $stream_meta ); + } + } + + $wpdb->query( "DROP TABLE IF EXISTS {$wpdb->prefix}stream, {$wpdb->prefix}stream_context, {$wpdb->prefix}stream_meta" ); + } + + restore_current_blog(); + } + + if ( $wpdb->last_error ) { + return __( 'Database Update Error', 'stream' ); + } + // Check to see if the author_role column already exists $rows = $wpdb->get_results( "SHOW COLUMNS FROM `{$prefix}stream` WHERE field = 'author_role'" ); // If the author_role doesn't exist, then create it and update records retroactively if ( empty( $rows ) ) { - $wpdb->query( "ALTER TABLE {$prefix}stream ADD author_role varchar(50) NOT NULL AFTER author" ); + $wpdb->query( "ALTER TABLE {$prefix}stream ADD author_role varchar(50) NULL AFTER author" ); $records = wp_stream_query( array( 'records_per_page' => -1 ) ); @@ -46,7 +121,7 @@ function wp_stream_update_140( $db_version, $current_version ) { do_action( 'wp_stream_after_db_update_' . $db_version, $current_version, $wpdb->last_error ); if ( $wpdb->last_error ) { - return esc_html__( 'Database Update Error', 'stream' ); + return __( 'Database Update Error', 'stream' ); } // Clear an old cron event hook that is lingering, replaced by `wp_stream_auto_purge` diff --git a/includes/db.php b/includes/db.php index a47197106..6b6c78ce6 100644 --- a/includes/db.php +++ b/includes/db.php @@ -19,7 +19,7 @@ public function __construct() { * @param string database prefix * @return string udpated database prefix */ - $prefix = apply_filters( 'wp_stream_db_tables_prefix', $wpdb->prefix ); + $prefix = apply_filters( 'wp_stream_db_tables_prefix', $wpdb->base_prefix ); self::$table = $prefix . 'stream'; self::$table_meta = $prefix . 'stream_meta'; @@ -71,7 +71,7 @@ public function insert( $recordarr ) { return; } - $fields = array( 'object_id', 'author', 'author_role', 'created', 'summary', 'parent', 'visibility', 'ip' ); + $fields = array( 'object_id', 'site_id', 'blog_id', 'author', 'author_role', 'created', 'summary', 'parent', 'visibility', 'ip' ); $data = array_intersect_key( $recordarr, array_flip( $fields ) ); $data = array_filter( $data ); diff --git a/includes/feeds.php b/includes/feeds.php index 2e943323a..77e4a3c82 100644 --- a/includes/feeds.php +++ b/includes/feeds.php @@ -5,14 +5,30 @@ class WP_Stream_Feeds { const FEED_QUERY_VAR = 'stream'; + const FEED_NETWORK_QUERY_VAR = 'network-stream'; const FEED_KEY_QUERY_VAR = 'key'; const FEED_TYPE_QUERY_VAR = 'type'; const USER_FEED_KEY = 'stream_user_feed_key'; const GENERATE_KEY_QUERY_VAR = 'stream_new_user_feed_key'; + public static $is_network_feed = false; + public static function load() { - if ( ! isset( WP_Stream_Settings::$options['general_private_feeds'] ) || ! WP_Stream_Settings::$options['general_private_feeds'] ) { - return; + if ( is_network_admin() ) { + self::$is_network_feed = true; + } + + if ( ! is_admin() ) { + $feed_path = parse_url( $_SERVER['REQUEST_URI'], PHP_URL_PATH ); + if ( self::FEED_NETWORK_QUERY_VAR === basename( $feed_path ) ) { + self::$is_network_feed = true; + } + } + + if ( ! self::$is_network_feed ) { + if ( ! isset( WP_Stream_Settings::$options['general_private_feeds'] ) || ! WP_Stream_Settings::$options['general_private_feeds'] ) { + return; + } } add_action( 'show_user_profile', array( __CLASS__, 'save_user_feed_key' ) ); @@ -22,6 +38,7 @@ public static function load() { add_action( 'edit_user_profile', array( __CLASS__, 'user_feed_key' ) ); add_feed( self::FEED_QUERY_VAR, array( __CLASS__, 'feed_template' ) ); + add_feed( self::FEED_NETWORK_QUERY_VAR, array( __CLASS__, 'feed_template' ) ); } /** @@ -52,18 +69,23 @@ public static function save_user_feed_key( $user ) { * @return string */ public static function user_feed_key( $user ) { - if ( ! array_intersect( $user->roles, WP_Stream_Settings::$options['general_role_access'] ) ) { + if ( ! is_network_admin() && ! array_intersect( $user->roles, WP_Stream_Settings::$options['general_role_access'] ) ) { + return; + } + + if ( is_network_admin() && ! is_super_admin( $user->ID ) ) { return; } $key = get_user_meta( $user->ID, self::USER_FEED_KEY, true ); + $query_var = is_network_admin() ? self::FEED_NETWORK_QUERY_VAR : self::FEED_QUERY_VAR; $pretty_permalinks = get_option( 'permalink_structure' ); if ( empty( $pretty_permalinks ) ) { $link = add_query_arg( array( - 'feed' => self::FEED_QUERY_VAR, + 'feed' => $query_var, self::FEED_KEY_QUERY_VAR => $key, ), home_url( '/' ) @@ -76,7 +98,7 @@ public static function user_feed_key( $user ) { home_url( sprintf( '/feed/%s/', - self::FEED_QUERY_VAR + $query_var ) ) ); @@ -94,9 +116,9 @@ public static function user_feed_key( $user ) {

- + | - +

@@ -124,13 +146,22 @@ public static function feed_template() { ); $user = get_users( $args ); - $roles = isset( $user[0]->roles ) ? (array) $user[0]->roles : array(); + if ( ! is_super_admin( $user[0]->ID ) ) { + $roles = isset( $user[0]->roles ) ? (array) $user[0]->roles : array(); - if ( ! $roles || ! array_intersect( $roles, WP_Stream_Settings::$options['general_role_access'] ) ) { - wp_die( $die_message, $die_title ); + if ( self::$is_network_feed ) { + wp_die( $die_message, $die_title ); + } + + if ( ! $roles || ! array_intersect( $roles, WP_Stream_Settings::$options['general_role_access'] ) ) { + wp_die( $die_message, $die_title ); + } } + $blog_id = self::$is_network_feed ? null : get_current_blog_id(); + $args = array( + 'blog_id' => $blog_id, 'records_per_page' => wp_stream_filter_input( INPUT_GET, 'records_per_page', FILTER_SANITIZE_NUMBER_INT, array( 'options' => array( 'default' => get_option( 'posts_per_rss' ) ) ) ), 'search' => wp_stream_filter_input( INPUT_GET, 'search' ), 'object_id' => wp_stream_filter_input( INPUT_GET, 'object_id', FILTER_SANITIZE_NUMBER_INT ), @@ -168,7 +199,7 @@ public static function feed_template() { header( 'Content-Type: ' . feed_content_type( 'rss-http' ) . '; charset=' . get_option( 'blog_charset' ), true ); - echo ''; + printf( '', esc_attr( get_option( 'blog_charset' ) ) ); ?> prefix; + $prefix = $wpdb->base_prefix; self::$table_prefix = apply_filters( 'wp_stream_db_tables_prefix', $prefix ); self::check(); @@ -109,7 +117,10 @@ private static function check() { if ( empty( self::$db_version ) ) { $current = self::install( self::$current ); } elseif ( self::$db_version !== self::$current ) { - add_action( 'admin_notices', array( __CLASS__, 'update_notice_hook' ) ); + if ( ! isset( $_REQUEST['wp_stream_update'] ) ) { + self::$update_required = true; + } + add_action( 'all_admin_notices', array( __CLASS__, 'update_notice_hook' ) ); } } @@ -117,7 +128,7 @@ public static function get_db_version() { global $wpdb; $version = get_site_option( self::KEY ); - if ( ! $version && version_compare( self::$current, '1.3.2', '<=' ) ) { + if ( ! $version && version_compare( self::$current, '1.4.0', '<=' ) ) { $old_key = $wpdb->get_col( "SELECT option_name FROM $wpdb->options WHERE option_name LIKE '%stream%_db'" ); if ( ! empty( $old_key ) && is_array( $old_key ) ) { $version = get_option( $old_key[0] ); @@ -213,7 +224,7 @@ private static function db_update_versions() { '1.2.8'/** @version 1.2.8 Change the context for Media connectors to the attachment type */, '1.3.0'/** @version 1.3.0 Backward settings compatibility for old version plugins */, '1.3.1'/** @version 1.3.1 Update records of Installer to Theme Editor connector */, - '1.4.0'/** @version 1.4.0 Add the author_role column */, + '1.4.0'/** @version 1.4.0 Add the author_role column and prepare tables for multisite support */, ); } @@ -268,6 +279,7 @@ public static function install( $current ) { $sql = "CREATE TABLE {$prefix}stream ( ID bigint(20) unsigned NOT NULL AUTO_INCREMENT, site_id bigint(20) unsigned NOT NULL DEFAULT '1', + blog_id bigint(20) unsigned NOT NULL DEFAULT '0', object_id bigint(20) unsigned NULL, author bigint(20) unsigned NOT NULL DEFAULT '0', author_role varchar(20) NOT NULL DEFAULT '', @@ -279,6 +291,7 @@ public static function install( $current ) { ip varchar(39) NULL, PRIMARY KEY (ID), KEY site_id (site_id), + KEY blog_id (blog_id), KEY parent (parent), KEY author (author), KEY created (created) diff --git a/includes/list-table.php b/includes/list-table.php index 3b29385d0..de0618976 100644 --- a/includes/list-table.php +++ b/includes/list-table.php @@ -3,11 +3,15 @@ class WP_Stream_List_Table extends WP_List_Table { function __construct( $args = array() ) { + + $screen_id = isset( $args['screen'] ) ? $args['screen'] : null; + $screen_id = apply_filters( 'wp_stream_list_table_screen_id', $screen_id ); + parent::__construct( array( 'post_type' => 'stream', 'plural' => 'records', - 'screen' => isset( $args['screen'] ) ? $args['screen'] : null, + 'screen' => $screen_id, ) ); @@ -23,8 +27,8 @@ function __construct( $args = array() ) { // Check for default hidden columns $this->get_hidden_columns(); + add_filter( 'screen_settings', array( $this, 'screen_controls' ), 10, 2 ); add_filter( 'set-screen-option', array( __CLASS__, 'set_screen_option' ), 10, 3 ); - add_filter( 'screen_settings', array( __CLASS__, 'screen_controls' ), 10, 2 ); add_action( 'wp_ajax_wp_stream_filters', array( __CLASS__, 'ajax_filters' ) ); set_screen_options(); @@ -51,7 +55,7 @@ function extra_tablenav( $which ) { } } - function get_columns() { + function get_columns(){ /** * Allows devs to add new columns to table * @@ -90,7 +94,7 @@ function get_hidden_columns() { $hidden = get_user_meta( $user->ID, 'manage' . $this->screen->id . 'columnshidden', true ); // If user meta is not found; add the default hidden column 'id' - if ( false === $hidden ) { + if ( ! $hidden ) { $hidden = array( 'id' ); update_user_meta( $user->ID, 'manage' . $this->screen->id . 'columnshidden', $hidden ); } @@ -101,7 +105,7 @@ function get_hidden_columns() { function prepare_items() { $columns = $this->get_columns(); $sortable = $this->get_sortable_columns(); - $hidden = get_hidden_columns( $this->screen ); + $hidden = $this->get_hidden_columns(); $this->_column_headers = array( $columns, $hidden, $sortable ); @@ -160,6 +164,7 @@ function get_records() { 'date_from', 'date_to', 'record__in', + 'blog_id', 'ip', ); @@ -262,7 +267,7 @@ function column_default( $item, $column_name ) { 'page' => WP_Stream_Admin::RECORDS_PAGE_SLUG, 'author' => absint( $author_ID ), ), - admin_url( WP_Stream_Admin::ADMIN_PARENT_PAGE ) + is_network_admin() ? network_admin_url( WP_Stream_Admin::ADMIN_PARENT_PAGE ) : admin_url( WP_Stream_Admin::ADMIN_PARENT_PAGE ) ), $author_avatar, $author_name, @@ -285,6 +290,15 @@ function column_default( $item, $column_name ) { $out = absint( $item->ID ); break; + case 'blog_id': + $blog = ( $item->blog_id && is_multisite() ) ? get_blog_details( $item->blog_id ) : WP_Stream_Network::get_network_blog(); + $out = sprintf( + '%s', + add_query_arg( array( 'blog_id' => $blog->blog_id ), network_admin_url( 'admin.php?page=wp_stream' ) ), + esc_html( $blog->blogname ) + ); + break; + default : /** * Registers new Columns to be inserted into the table. The cell contents of this column is set @@ -384,7 +398,7 @@ function column_link( $display, $key, $value = null, $title = null ) { array( 'page' => WP_Stream_Admin::RECORDS_PAGE_SLUG, ), - admin_url( WP_Stream_Admin::ADMIN_PARENT_PAGE ) + is_network_admin() ? network_admin_url( WP_Stream_Admin::ADMIN_PARENT_PAGE ) : admin_url( WP_Stream_Admin::ADMIN_PARENT_PAGE ) ); $args = ! is_array( $key ) ? array( $key => $value ) : $key; @@ -429,12 +443,14 @@ function assemble_records( $column, $table = '' ) { $setting_key = self::get_column_excluded_setting_key( $column ); + $exclude_hide_previous_records = isset( WP_Stream_Settings::$options['exclude_hide_previous_records'] ) ? WP_Stream_Settings::$options['exclude_hide_previous_records'] : 0; + /** * Toggle visibility of disabled connectors/actions/contexts on list table filter dropdown * * @param bool $hidden Visibility status, default is Hide Previous Record value set in Exclude setting. */ - $hide_disabled_column_filter = apply_filters( 'wp_stream_list_table_hide_disabled_ ' . $setting_key, ( WP_Stream_Settings::$options[ 'exclude_hide_previous_records' ] === 0 ) ? false : true ); + $hide_disabled_column_filter = apply_filters( 'wp_stream_list_table_hide_disabled_ ' . $setting_key, ( 0 === $exclude_hide_previous_records ) ? false : true ); if ( 'author' === $column ) { $all_records = array(); @@ -487,17 +503,16 @@ function assemble_records( $column, $table = '' ) { return $all_records; } - function filters_form() { - - $user_id = get_current_user_id(); - - $filters_option = get_user_meta( $user_id, 'stream_toggle_filters', true ); - + public function get_filters() { $filters = array(); - $filters_string = sprintf( '', 'wp_stream' ); + require_once WP_STREAM_INC_DIR . 'date-interval.php'; + $date_interval = new WP_Stream_Date_Interval(); - $filters_string .= sprintf( __( '%1$sShow filter controls via the screen options tab above%2$s', 'stream' ), '' ); + $filters['date'] = array( + 'title' => __( 'dates', 'stream' ), + 'items' => $date_interval->intervals, + ); $authors_records = $this->assemble_records( 'author', 'stream' ); @@ -542,6 +557,7 @@ function filters_form() { 'items' => $this->assemble_records( 'action' ), ); + /** * Filter allows additional filters in the list table dropdowns * Note the format of the filters above, with they key and array @@ -551,22 +567,35 @@ function filters_form() { * * @return array Updated array of filters */ - $filters = apply_filters( 'wp_stream_list_table_filters', $filters ); - $filters_string .= $this->filter_date(); + return apply_filters( 'wp_stream_list_table_filters', $filters ); + } + + function filters_form() { + + $user_id = get_current_user_id(); + + $filters = $this->get_filters(); + + $filters_string = sprintf( '', 'wp_stream' ); + $filters_string .= sprintf( __( '%1$sShow filter controls via the screen options tab above%2$s', 'stream' ), '' ); foreach ( $filters as $name => $data ) { - $filters_string .= $this->filter_select( $name, $data['title'], isset( $data['items'] ) ? $data['items'] : array(), isset( $data['ajax'] ) && $data['ajax'], $filters_option[ $name ] ); + if ( 'date' === $name ) { + $filters_string .= $this->filter_date( $data['items'] ); + continue; + } + $filters_string .= $this->filter_select( $name, $data['title'], isset( $data['items'] ) ? $data['items'] : array(), isset( $data['ajax'] ) && $data['ajax'] ); } $filters_string .= sprintf( '', __( 'Filter', 'stream' ) ); - $url = admin_url( WP_Stream_Admin::ADMIN_PARENT_PAGE ); + + $url = is_network_admin() ? network_admin_url( WP_Stream_Admin::ADMIN_PARENT_PAGE ) : admin_url( WP_Stream_Admin::ADMIN_PARENT_PAGE ); printf( '
%s
', $filters_string ); // xss ok } - function filter_select( $name, $title, $items, $ajax, $enabled = true ) { - + function filter_select( $name, $title, $items, $ajax ) { if ( $ajax ) { $out = sprintf( @@ -614,17 +643,13 @@ function filter_search() { return $out; } - function filter_date() { - - require_once WP_STREAM_INC_DIR . 'date-interval.php'; + function filter_date( $items ) { wp_enqueue_style( 'jquery-ui' ); wp_enqueue_style( 'wp-stream-datepicker' ); wp_enqueue_script( 'jquery-ui-datepicker' ); - $date_interval = new WP_Stream_Date_Interval(); - $date_predefined = wp_stream_filter_input( INPUT_GET, 'date_predefined' ); $date_from = wp_stream_filter_input( INPUT_GET, 'date_from' ); $date_to = wp_stream_filter_input( INPUT_GET, 'date_to' ); @@ -636,7 +661,7 @@ function filter_date() {
- __( 'Date Range', 'stream' ), - 'author' => __( 'Authors', 'stream' ), - 'connector' => __( 'Connectors', 'stream' ), - 'context' => __( 'Contexts', 'stream' ), - 'action' => __( 'Actions', 'stream' ), - ) - ); - - foreach ( $filters as $key => $val ) : ?> - + $val ) : ?> + -
@@ -829,5 +852,4 @@ function get_column_excluded_setting_key( $column ) { return $output; } - } diff --git a/includes/live-update.php b/includes/live-update.php index 91c615184..3aa3ddbb3 100644 --- a/includes/live-update.php +++ b/includes/live-update.php @@ -103,7 +103,7 @@ public static function heartbeat_received( $response, $data ) { // Register list table require_once WP_STREAM_INC_DIR . 'list-table.php'; - self::$list_table = new WP_Stream_List_Table( array( 'screen' => WP_Stream_Admin::RECORDS_PAGE_SLUG ) ); + self::$list_table = new WP_Stream_List_Table( array( 'screen' => 'toplevel_page_' . WP_Stream_Admin::RECORDS_PAGE_SLUG ) ); self::$list_table->prepare_items(); extract( self::$list_table->_pagination_args, EXTR_SKIP ); diff --git a/includes/log.php b/includes/log.php index 518cdb6b5..d48db02dc 100644 --- a/includes/log.php +++ b/includes/log.php @@ -91,6 +91,8 @@ function ( $var ) { $recordarr = array( 'object_id' => $object_id, + 'site_id' => is_multisite() ? get_current_site()->id : 1, + 'blog_id' => apply_filters( 'blog_id_logged', is_network_admin() ? 0 : get_current_blog_id() ), 'author' => $user_id, 'author_role' => $user->roles[0], 'created' => current_time( 'mysql', 1 ), diff --git a/includes/network.php b/includes/network.php new file mode 100644 index 000000000..62c2b9cb7 --- /dev/null +++ b/includes/network.php @@ -0,0 +1,504 @@ + + * @author Chris Olbekson + * + */ + +class WP_Stream_Network { + + function __construct() { + $this->actions(); + $this->filters(); + } + + function actions() { + add_action( 'init', array( $this, 'ajax_network_admin' ), 1 ); + add_action( 'admin_bar_menu', array( $this, 'network_admin_bar_menu' ), 99, 1 ); + add_action( 'network_admin_menu', array( 'WP_Stream_Admin', 'register_menu' ) ); + add_action( 'network_admin_notices', array( 'WP_Stream_Admin', 'admin_notices' ) ); + add_action( 'wpmuadminedit', array( $this, 'network_options_action' ) ); + add_action( 'wp_network_dashboard_setup', array( 'WP_Stream_Dashboard_Widget', 'stream_activity' ) ); + add_action( 'wp_stream_admin_menu_screens', array( $this, 'admin_menu_screens' ) ); + add_action( 'update_site_option_' . WP_Stream_Settings::NETWORK_KEY, array( $this, 'updated_option_ttl_remove_records' ), 10, 3 ); + } + + function filters() { + add_filter( 'wp_stream_disable_admin_access', array( __CLASS__, 'disable_admin_access' ) ); + add_filter( 'wp_stream_settings_form_action', array( $this, 'settings_form_action' ) ); + add_filter( 'wp_stream_settings_form_description', array( $this, 'settings_form_description' ) ); + add_filter( 'wp_stream_options_fields', array( $this, 'get_network_admin_fields' ) ); + add_filter( 'wp_stream_options', array( $this, 'get_network_options' ), 10, 2 ); + add_filter( 'wp_stream_serialized_labels', array( $this, 'get_settings_translations' ) ); + add_filter( 'wp_stream_list_table_filters', array( $this, 'list_table_filters' ) ); + add_filter( 'stream_toggle_filters', array( $this, 'toggle_filters' ) ); + add_filter( 'wp_stream_list_table_screen_id', array( $this, 'list_table_screen_id' ) ); + add_filter( 'wp_stream_query_args', array( __CLASS__, 'set_network_option_value' ) ); + add_filter( 'wp_stream_list_table_columns', array( $this, 'network_admin_columns' ) ); + } + + /** + * Workaround to get admin-ajax.php to know when the request is from the network admin + * See https://core.trac.wordpress.org/ticket/22589 + */ + function ajax_network_admin() { + if ( defined( 'DOING_AJAX' ) && DOING_AJAX && is_multisite() && preg_match( '#^' . network_admin_url() . '#i', $_SERVER['HTTP_REFERER'] ) ) { + define( 'WP_NETWORK_ADMIN', true ); + } + } + + /** + * Adds Stream to the admin bar under the "My Sites > Network Admin" menu + */ + function network_admin_bar_menu( $admin_bar ) { + $href = add_query_arg( + array( + 'page' => WP_Stream_Admin::RECORDS_PAGE_SLUG, + ), + network_admin_url( WP_Stream_Admin::ADMIN_PARENT_PAGE ) + ); + + $admin_bar->add_menu( + array( + 'id' => 'network-admin-stream', + 'parent' => 'network-admin', + 'title' => esc_html__( 'Stream', 'stream' ), + 'href' => esc_url( $href ), + ) + ); + } + + /** + * Builds a stdClass object used when displaying actions done in network administration + * @return stdClass + */ + public static function get_network_blog() { + $blog = new stdClass; + $blog->blog_id = 0; + $blog->blogname = __( 'Network Admin', 'stream' ); + + return $blog; + } + + /** + * If site access has been disabled from the network admin, disallow access + * + * @param $disable_access + * + * @return boolean + */ + public static function disable_admin_access( $disable_access ) { + if ( ! function_exists( 'is_plugin_active_for_network' ) ) { + require_once( ABSPATH . '/wp-admin/includes/plugin.php' ); + } + if ( ! is_network_admin() && is_plugin_active_for_network( WP_STREAM_PLUGIN ) ) { + $settings = (array) get_site_option( WP_Stream_Settings::NETWORK_KEY, array() ); + + if ( isset( $settings['general_enable_site_access'] ) && false === $settings['general_enable_site_access'] ) { + return true; + } + } + + return $disable_access; + } + + /** + * Add Network Settings and Default Settings menu items + * + * @param $screen_id + * + * @return array + */ + function admin_menu_screens() { + if ( ! is_network_admin() ) { + return; + } + + remove_submenu_page( WP_Stream_Admin::RECORDS_PAGE_SLUG, 'wp_stream_settings' ); + + WP_Stream_Admin::$screen_id['network_settings'] = add_submenu_page( + WP_Stream_Admin::RECORDS_PAGE_SLUG, + __( 'Stream Network Settings', 'stream' ), + __( 'Network Settings', 'stream' ), + WP_Stream_Admin::SETTINGS_CAP, + 'wp_stream_network_settings', + array( 'WP_Stream_Admin', 'render_page' ) + ); + + if ( ! WP_Stream_Admin::$disable_access ) { + WP_Stream_Admin::$screen_id['default_settings'] = add_submenu_page( + WP_Stream_Admin::RECORDS_PAGE_SLUG, + __( 'New Site Settings', 'stream' ), + __( 'Site Defaults', 'stream' ), + WP_Stream_Admin::SETTINGS_CAP, + 'wp_stream_default_settings', + array( 'WP_Stream_Admin', 'render_page' ) + ); + } + } + + /** + * Remove records when records TTL is shortened + * + * @param string $option_key + * @param array $old_value + * @param array $new_value + * + * @action update_option_wp_stream + * @return void + */ + function updated_option_ttl_remove_records( $option_key, $new_value, $old_value ) { + WP_Stream_Settings::updated_option_ttl_remove_records( $old_value, $new_value ); + } + + /** + * Adjust the action of the settings form when in the Network Admin + * + * @param $action + * + * @return string + */ + function settings_form_action( $action ) { + if ( is_network_admin() ) { + $current_page = wp_stream_filter_input( INPUT_GET, 'page' ); + $action = add_query_arg( array( 'action' => $current_page ), 'edit.php' ); + } + + return $action; + } + + /** + * Add a description to each of the Settings pages in the Network Admin + * + * @param $description + * + * @return string + */ + function settings_form_description( $description ) { + if ( ! is_network_admin() ) { + return; + } + + $current_page = wp_stream_filter_input( INPUT_GET, 'page' ); + + switch ( $current_page ) { + case 'wp_stream_network_settings' : + $description = __( 'These settings apply to all sites on the network.', 'stream' ); + break; + case 'wp_stream_default_settings' : + $description = __( 'These default settings will apply to new sites created on the network. These settings do not alter existing sites.', 'stream' ); + break; + } + + return $description; + } + + /** + * Adjusts the settings fields displayed in various network admin screens + * + * @param $fields + * + * @return mixed + */ + function get_network_admin_fields( $fields ) { + if ( ! function_exists( 'is_plugin_active_for_network' ) ) { + require_once( ABSPATH . '/wp-admin/includes/plugin.php' ); + } + + if ( ! is_plugin_active_for_network( WP_STREAM_PLUGIN ) ) { + return $fields; + } + + $stream_hidden_options = apply_filters( + 'wp_stream_hidden_option_fields', + array( + 'general' => array( + 'delete_all_records', + 'records_ttl', + ), + ) + ); + + $network_hidden_options = apply_filters( + 'wp_stream_network_option_fields', + array( + 'general' => array( + 'role_access', + 'private_feeds', + ), + 'exclude' => array( + 'authors_and_roles', + 'connectors', + 'contexts', + 'actions', + 'ip_addresses', + 'hide_previous_records', + ), + ) + ); + + // Remove settings based on context + if ( WP_Stream_Settings::NETWORK_KEY === WP_Stream_Settings::$option_key ) { + $hidden_options = $network_hidden_options; + } else { + $hidden_options = $stream_hidden_options; + } + + foreach ( $fields as $section_key => $section ) { + foreach ( $section['fields'] as $key => $field ) { + if ( ! isset( $hidden_options[ $section_key ] ) ) { + continue; + } + if ( in_array( $field['name'], $hidden_options[ $section_key ] ) ) { + unset( $fields[ $section_key ]['fields'][ $key ] ); + } + } + } + + // Add settings based on context + if ( WP_Stream_Settings::NETWORK_KEY === WP_Stream_Settings::$option_key ) { + $new_fields['general']['fields'][] = array( + 'name' => 'enable_site_access', + 'title' => __( 'Enable Site Access', 'stream' ), + 'after_field' => __( 'Enabled' ), + 'default' => 1, + 'desc' => __( 'When site access is disabled Stream can only be accessed from the network administration.', 'stream' ), + 'type' => 'checkbox', + ); + + $fields = array_merge_recursive( $new_fields, $fields ); + + $reset_site_settings_href = add_query_arg( + array( + 'action' => 'wp_stream_defaults', + 'wp_stream_nonce' => wp_create_nonce( 'stream_nonce' ), + ), + admin_url( 'admin-ajax.php' ) + ); + + $fields['general']['fields'][] = array( + 'name' => 'reset_site_settings', + 'title' => __( 'Reset Site Settings', 'stream' ), + 'type' => 'link', + 'href' => $reset_site_settings_href, + 'desc' => __( 'Warning: Clicking this will override all site settings with defaults.', 'stream' ), + 'default' => 0, + ); + } + + // Remove empty settings sections + foreach ( $fields as $section_key => $section ) { + if ( empty( $section['fields'] ) ) { + unset( $fields[ $section_key ] ); + } + } + + return $fields; + } + + /** + * Get translations of serialized Stream Network and Stream Default settings + * + * @filter wp_stream_serialized_labels + * @return array Multidimensional array of fields + */ + function get_settings_translations( $labels ) { + $network_key = WP_Stream_Settings::NETWORK_KEY; + $defaults_key = WP_Stream_Settings::DEFAULTS_KEY; + + if ( ! isset( $labels[ $network_key ] ) ) { + $labels[ $network_key ] = array(); + } + + if ( ! isset( $labels[ $defaults_key ] ) ) { + $labels[ $defaults_key ] = array(); + } + + foreach ( WP_Stream_Settings::get_fields() as $section_slug => $section ) { + foreach ( $section['fields'] as $field ) { + $labels[ $network_key ][ sprintf( '%s_%s', $section_slug, $field['name'] ) ] = $field['title']; + $labels[ $defaults_key ][ sprintf( '%s_%s', $section_slug, $field['name'] ) ] = $field['title']; + } + } + + return $labels; + } + + /** + * Wrapper for the settings API to work on the network settings page + */ + function network_options_action() { + $allowed_referers = array( + 'wp_stream_network_settings', + 'wp_stream_default_settings', + ); + if ( ! isset( $_GET['action'] ) || ! in_array( $_GET['action'], $allowed_referers ) ) { + return; + } + + $options = isset( $_POST['option_page'] ) ? explode( ',', stripslashes( $_POST['option_page'] ) ) : null; + + if ( $options ) { + + foreach ( $options as $option ) { + $option = trim( $option ); + $value = null; + + $sections = WP_Stream_Settings::get_fields(); + foreach ( $sections as $section_name => $section ) { + foreach ( $section['fields'] as $field_idx => $field ) { + $option_key = $section_name . '_' . $field['name']; + if ( isset( $_POST[ $option ][ $option_key ] ) ) { + $value[ $option_key ] = $_POST[ $option ][ $option_key ]; + } else { + $value[ $option_key ] = false; + } + } + } + + if ( ! is_array( $value ) ) { + $value = trim( $value ); + } + + update_site_option( $option, $value ); + } + } + + if ( ! count( get_settings_errors() ) ) { + add_settings_error( 'general', 'settings_updated', __( 'Settings saved.', 'stream' ), 'updated' ); + } + + set_transient( 'settings_errors', get_settings_errors(), 30 ); + + $go_back = add_query_arg( 'settings-updated', 'true', wp_get_referer() ); + wp_redirect( $go_back ); + exit; + } + + /** + * Uses network options when on the network settings page + * + * @param $options + * + * @return array + */ + function get_network_options( $options, $option_key ) { + if ( is_network_admin() ) { + $options = wp_parse_args( + (array) get_site_option( $option_key, array() ), + WP_Stream_Settings::get_defaults( $option_key ) + ); + } + + return $options; + } + + /** + * Add the Site filter to the stream activity in network admin + * + * @param $filters + * + * @return array + */ + function list_table_filters( $filters ) { + if ( is_network_admin() ) { + $blogs = array(); + + // display network blog as the first option + $network_blog = self::get_network_blog(); + + $blogs['network'] = array( + 'label' => $network_blog->blogname, + 'disabled' => '', + ); + + // add all sites + foreach ( (array) wp_get_sites() as $blog ) { + $blog_data = get_blog_details( $blog ); + + $blogs[ $blog['blog_id'] ] = array( + 'label' => $blog_data->blogname, + 'disabled' => '', + ); + } + + $filters['blog_id'] = array( + 'title' => __( 'sites', 'stream' ), + 'items' => $blogs, + ); + } + + return $filters; + } + + /** + * Add the Site toggle to screen options in network admin + * + * @param $filters + * + * @return array + */ + function toggle_filters( $filters ) { + if ( is_network_admin() ) { + $filters['blog_id'] = esc_html__( 'Site', 'stream' ); + } + + return $filters; + } + + /** + * Add the network suffix to the $screen_id when in the network admin + * + * @param $screen_id + * + * @return string + */ + function list_table_screen_id( $screen_id ) { + if ( $screen_id && is_network_admin() ) { + if ( '-network' !== substr( $screen_id, -8 ) ) { + $screen_id .= '-network'; + } + } + + return $screen_id; + } + + /** + * Adjust the stream query to work with changes on the network level + * + * @param $args + * + * @return mixed + */ + public static function set_network_option_value( $args ) { + if ( isset( $args['blog_id'] ) && 'network' === $args['blog_id'] ) { + $args['blog_id'] = 0; + } + + return $args; + } + + /** + * Add the Site column to the network stream records + * + * @param $args + * + * @return mixed + */ + function network_admin_columns( $columns ) { + if ( is_network_admin() ) { + $columns = array_merge( + array_slice( $columns, 0, -1 ), + array( + 'blog_id' => esc_html__( 'Site', 'stream' ), + ), + array_slice( $columns, -1 ) + ); + } + + return $columns; + } + +} diff --git a/includes/query.php b/includes/query.php index 0cd3cd436..caed6ff24 100755 --- a/includes/query.php +++ b/includes/query.php @@ -24,15 +24,17 @@ public function query( $args ) { $defaults = array( // Pagination params - 'records_per_page' => 10, + 'records_per_page' => get_option( 'posts_per_page' ), 'paged' => 1, - // Search params + // Search param 'search' => null, // Stream core fields filtering 'type' => 'stream', 'object_id' => null, 'ip' => null, - // Author param + 'site_id' => is_multisite() ? get_current_site()->id : 1, + 'blog_id' => is_network_admin() ? null : get_current_blog_id(), + // Author params 'author' => null, 'author_role' => null, // Date-based filters @@ -107,6 +109,14 @@ public function query( $args ) { $where .= $wpdb->prepare( " AND $wpdb->stream.ip = %s", wp_stream_filter_var( $args['ip'], FILTER_VALIDATE_IP ) ); } + if ( is_numeric( $args['site_id'] ) ) { + $where .= $wpdb->prepare( " AND $wpdb->stream.site_id = %d", $args['site_id'] ); + } + + if ( is_numeric( $args['blog_id'] ) ) { + $where .= $wpdb->prepare( " AND $wpdb->stream.blog_id = %d", $args['blog_id'] ); + } + if ( $args['search'] ) { $where .= $wpdb->prepare( " AND $wpdb->stream.summary LIKE %s", "%{$args['search']}%" ); } @@ -264,7 +274,7 @@ public function query( $args ) { */ $order = esc_sql( $args['order'] ); $orderby = esc_sql( $args['orderby'] ); - $orderable = array( 'ID', 'site_id', 'object_id', 'author', 'author_role', 'summary', 'visibility', 'parent', 'type', 'created' ); + $orderable = array( 'ID', 'site_id', 'blog_id', 'object_id', 'author', 'author_role', 'summary', 'visibility', 'parent', 'type', 'created' ); if ( in_array( $orderby, $orderable ) ) { $orderby = $wpdb->stream . '.' . $orderby; diff --git a/includes/settings.php b/includes/settings.php index c4b76383b..e63dbe750 100644 --- a/includes/settings.php +++ b/includes/settings.php @@ -12,6 +12,16 @@ class WP_Stream_Settings { */ const KEY = 'wp_stream'; + /** + * Settings key/identifier + */ + const NETWORK_KEY = 'wp_stream_network'; + + /** + * Default Settings key/identifier + */ + const DEFAULTS_KEY = 'wp_stream_defaults'; + /** * Plugin settings * @@ -19,6 +29,13 @@ class WP_Stream_Settings { */ public static $options = array(); + /** + * Option key for current screen + * + * @var array + */ + public static $option_key = ''; + /** * Settings fields * @@ -26,22 +43,6 @@ class WP_Stream_Settings { */ public static $fields = array(); - public static function get_options() { - /** - * Filter allows for modification of options - * - * @param array array of options - * @return array updated array of options - */ - return apply_filters( - 'wp_stream_options', - wp_parse_args( - (array) get_option( self::KEY, array() ), - self::get_defaults() - ) - ); - } - /** * Public constructor * @@ -49,7 +50,8 @@ public static function get_options() { */ public static function load() { - self::$options = self::get_options(); + self::$option_key = self::get_option_key(); + self::$options = self::get_options(); // Register settings, and fields add_action( 'admin_init', array( __CLASS__, 'register_settings' ) ); @@ -198,6 +200,29 @@ public static function add_display_name_search_columns( $search_columns, $search return $search_columns; } + /** + * Returns the option key depending on which settings page is being viewed + * + * @return string Option key for this page + */ + public static function get_option_key() { + $option_key = self::KEY; + + $current_page = wp_stream_filter_input( INPUT_GET, 'page' ); + if ( ! $current_page ) { + $current_page = wp_stream_filter_input( INPUT_GET, 'action' ); + } + + if ( 'wp_stream_default_settings' === $current_page ) { + $option_key = self::DEFAULTS_KEY; + } + + if ( 'wp_stream_network_settings' === $current_page ) { + $option_key = self::NETWORK_KEY; + } + + return apply_filters( 'wp_stream_settings_option_key', $option_key ); + } /** * Return settings fields @@ -319,15 +344,45 @@ public static function get_fields() { ), ), ); - /** - * Filter allows for modification of options fields - * - * @param array array of fields - * @return array updated array of fields - */ - self::$fields = apply_filters( 'wp_stream_options_fields', $fields ); } - return self::$fields; + + /** + * Filter allows for modification of options fields + * + * @param array array of fields + * @return array updated array of fields + */ + return apply_filters( 'wp_stream_options_fields', $fields ); + } + + /** + * Returns a list of options based on the current screen. + * + * @return array Options + */ + public static function get_options() { + $option_key = self::$option_key; + + $defaults = self::get_defaults( $option_key ); + + if ( self::DEFAULTS_KEY === $option_key ) { + return $defaults; + } + + /** + * Filter allows for modification of options + * + * @param array array of options + * @return array updated array of options + */ + return apply_filters( + 'wp_stream_options', + wp_parse_args( + (array) get_option( $option_key, array() ), + $defaults + ), + $option_key + ); } /** @@ -338,6 +393,7 @@ public static function get_fields() { public static function get_defaults() { $fields = self::get_fields(); $defaults = array(); + foreach ( $fields as $section_name => $section ) { foreach ( $section['fields'] as $field ) { $defaults[ $section_name.'_'.$field['name'] ] = isset( $field['default'] ) @@ -345,7 +401,21 @@ public static function get_defaults() { : null; } } - return $defaults; + + /** + * Filter allows for modification of options defaults + * + * @param array array of options + * @return array updated array of option defaults + */ + return apply_filters( + 'wp_stream_option_defaults', + wp_parse_args( + (array) get_site_option( self::DEFAULTS_KEY, array() ), + $defaults + ) + ); + } /** @@ -357,14 +427,14 @@ public static function register_settings() { $sections = self::get_fields(); - register_setting( self::KEY, self::KEY ); + register_setting( self::$option_key, self::$option_key ); foreach ( $sections as $section_name => $section ) { add_settings_section( $section_name, null, '__return_false', - self::KEY + self::$option_key ); foreach ( $section['fields'] as $field_idx => $field ) { @@ -375,11 +445,11 @@ public static function register_settings() { $field['name'], $field['title'], ( isset( $field['callback'] ) ? $field['callback'] : array( __CLASS__, 'output_field' ) ), - self::KEY, + self::$option_key, $section_name, $field + array( 'section' => $section_name, - 'label_for' => sprintf( '%s_%s_%s', self::KEY, $section_name, $field['name'] ), // xss ok + 'label_for' => sprintf( '%s_%s_%s', self::$option_key, $section_name, $field['name'] ), // xss ok ) ); } @@ -414,7 +484,6 @@ public static function updated_option_trigger_flush_rules( $old_value, $new_valu * @return string HTML to be displayed */ public static function render_field( $field ) { - $output = null; $type = isset( $field['type'] ) ? $field['type'] : null; @@ -425,9 +494,11 @@ public static function render_field( $field ) { $description = isset( $field['desc'] ) ? $field['desc'] : null; $href = isset( $field['href'] ) ? $field['href'] : null; $after_field = isset( $field['after_field'] ) ? $field['after_field'] : null; + $default = isset( $field['default'] ) ? $field['default'] : null; $title = isset( $field['title'] ) ? $field['title'] : null; $nonce = isset( $field['nonce'] ) ? $field['nonce'] : null; $current_value = self::$options[ $section . '_' . $name ]; + $option_key = self::$option_key; if ( is_callable( $current_value ) ) { $current_value = call_user_func( $current_value ); @@ -449,7 +520,7 @@ public static function render_field( $field ) { $output = sprintf( ' %8$s', esc_attr( $type ), - esc_attr( self::KEY ), + esc_attr( $option_key ), esc_attr( $section ), esc_attr( $name ), esc_attr( $class ), @@ -461,7 +532,7 @@ public static function render_field( $field ) { case 'checkbox': $output = sprintf( '', - esc_attr( self::KEY ), + esc_attr( $option_key ), esc_attr( $section ), esc_attr( $name ), checked( $current_value, 1, false ), @@ -471,14 +542,14 @@ public static function render_field( $field ) { case 'multi_checkbox': $output = sprintf( '
', - esc_attr( self::KEY ), + esc_attr( $option_key ), esc_attr( $section ), esc_attr( $name ) ); // Fallback if nothing is selected $output .= sprintf( '', - esc_attr( self::KEY ), + esc_attr( $option_key ), esc_attr( $section ), esc_attr( $name ) ); @@ -492,7 +563,7 @@ public static function render_field( $field ) { '
', sprintf( '', - esc_attr( self::KEY ), + esc_attr( $option_key ), esc_attr( $section ), esc_attr( $name ), esc_attr( $value ), @@ -503,10 +574,37 @@ public static function render_field( $field ) { } $output .= '
'; break; + case 'select': + $current_value = (array) self::$options[$section . '_' . $name]; + $default_value = isset( $default['value'] ) ? $default['value'] : '-1'; + $default_name = isset( $default['name'] ) ? $default['name'] : 'Choose Setting'; + + $output = sprintf( + ''; + break; case 'file': $output = sprintf( '', - esc_attr( self::KEY ), + esc_attr( $option_key ), esc_attr( $section ), esc_attr( $name ), esc_attr( $class ) @@ -515,7 +613,7 @@ public static function render_field( $field ) { case 'link': $output = sprintf( '%6$s', - esc_attr( self::KEY ), + esc_attr( $option_key ), esc_attr( $section ), esc_attr( $name ), esc_attr( $class ), @@ -527,6 +625,7 @@ public static function render_field( $field ) { if ( ! isset ( $current_value ) ) { $current_value = array(); } + if ( false !== ( $key = array_search( '__placeholder__', $current_value ) ) ) { unset( $current_value[ $key ] ); } @@ -557,7 +656,7 @@ public static function render_field( $field ) { $output = sprintf( '
', - esc_attr( self::KEY ), + esc_attr( $option_key ), esc_attr( $section ), esc_attr( $name ) ); @@ -574,7 +673,7 @@ public static function render_field( $field ) { // to store data with default value if nothing is selected $output .= sprintf( '', - esc_attr( self::KEY ), + esc_attr( $option_key ), esc_attr( $section ), esc_attr( $name ) ); @@ -628,7 +727,7 @@ public static function render_field( $field ) { $output = sprintf( '
', - esc_attr( self::KEY ), + esc_attr( $option_key ), esc_attr( $section ), esc_attr( $name ) ); @@ -644,7 +743,7 @@ public static function render_field( $field ) { // to store data with default value if nothing is selected $output .= sprintf( '', - esc_attr( self::KEY ), + esc_attr( $option_key ), esc_attr( $section ), esc_attr( $name ) ); diff --git a/languages/stream-pl_PL.mo b/languages/stream-pl_PL.mo index 4f70ce2c2..58439d579 100644 Binary files a/languages/stream-pl_PL.mo and b/languages/stream-pl_PL.mo differ diff --git a/stream.php b/stream.php index 33cb793a4..28a40c7e7 100755 --- a/stream.php +++ b/stream.php @@ -50,6 +50,11 @@ class WP_Stream { */ public $db = null; + /** + * @var WP_Stream_Network + */ + public $network = null; + /** * Admin notices messages * @@ -61,6 +66,7 @@ class WP_Stream { * Class constructor */ private function __construct() { + define( 'WP_STREAM_PLUGIN', plugin_basename( __FILE__ ) ); define( 'WP_STREAM_DIR', plugin_dir_path( __FILE__ ) ); define( 'WP_STREAM_URL', plugin_dir_url( __FILE__ ) ); define( 'WP_STREAM_INC_DIR', WP_STREAM_DIR . 'includes/' ); @@ -77,7 +83,7 @@ private function __construct() { } // Check DB and add message if not present - $this->verify_database_present(); + add_action( 'plugins_loaded', array( $this, 'verify_database_present' ) ); // Load languages add_action( 'plugins_loaded', array( __CLASS__, 'i18n' ) ); @@ -86,6 +92,12 @@ private function __construct() { require_once WP_STREAM_INC_DIR . 'settings.php'; add_action( 'init', array( 'WP_Stream_Settings', 'load' ) ); + // Load network class + if ( is_multisite() ) { + require_once WP_STREAM_INC_DIR . 'network.php'; + $this->network = new WP_Stream_Network; + } + // Load logger class require_once WP_STREAM_INC_DIR . 'log.php'; add_action( 'plugins_loaded', array( 'WP_Stream_Log', 'load' ) ); @@ -173,7 +185,11 @@ public static function install() { * * @return void */ - private function verify_database_present() { + public function verify_database_present() { + if ( ! function_exists( 'is_plugin_active_for_network' ) ) { + require_once( ABSPATH . '/wp-admin/includes/plugin.php' ); + } + /** * Filter will halt verify_database_present() if set to true * @@ -186,20 +202,30 @@ private function verify_database_present() { global $wpdb; - $message = ''; + $database_message = ''; + $uninstall_message = ''; // Check if all needed DB is present foreach ( $this->db->get_table_names() as $table_name ) { if ( $wpdb->get_var( "SHOW TABLES LIKE '$table_name'" ) !== $table_name ) { - $message .= sprintf( '

%s %s

', __( 'The following table is not present in the WordPress database :', 'stream' ), $table_name ); + $database_message .= sprintf( '

%s %s

', __( 'The following table is not present in the WordPress database :', 'stream' ), $table_name ); } } - if ( ! empty( $message ) ) { + if ( is_plugin_active_for_network( WP_STREAM_PLUGIN ) && current_user_can( 'manage_network_plugins' ) ) { + $uninstall_message = sprintf( __( 'Please uninstall the Stream plugin and activate it again.', 'stream' ), network_admin_url( 'plugins.php#stream' ) ); + } elseif ( current_user_can( 'activate_plugins' ) ) { + $uninstall_message = sprintf( __( 'Please uninstall the Stream plugin and activate it again.', 'stream' ), admin_url( 'plugins.php#stream' ) ); + } + + // Check upgrade routine + self::install(); + + if ( ! empty( $database_message ) ) { self::$messages['wp_stream_db_error'] = sprintf( '
%s

%s

', - $message, - sprintf( __( 'Please uninstall the Stream plugin and activate it again.', 'stream' ), admin_url( 'plugins.php#stream' ) ) + $database_message, + $uninstall_message ); // xss ok } } diff --git a/ui/admin.js b/ui/admin.js index b8478eab8..4f6d47304 100644 --- a/ui/admin.js +++ b/ui/admin.js @@ -75,7 +75,7 @@ jQuery(function($){ $placeholder.after($placeholder.clone(true).attr('class', $placeholder_child_class).val(key)); }); }; - $('.stream_page_wp_stream_settings input[type=hidden].select2-select.with-source').each(function (k, el) { + $('#tab-content-settings input[type=hidden].select2-select.with-source').each(function (k, el) { var $input = $(el); $input.select2({ multiple: true, @@ -99,7 +99,7 @@ jQuery(function($){ stream_select2_change_handler( e , $input ); }).trigger('change'); }); - $( '.stream_page_wp_stream_settings input[type=hidden].select2-select.ip-addresses').each(function( k, el ){ + $( '#tab-content-settings input[type=hidden].select2-select.ip-addresses').each(function( k, el ){ var $input = $(el); $input.select2({ @@ -178,7 +178,7 @@ jQuery(function($){ }).trigger('change'); }); var $input_user; - $('.stream_page_wp_stream_settings input[type=hidden].select2-select.authors_and_roles').each(function (k, el) { + $('#tab-content-settings input[type=hidden].select2-select.authors_and_roles').each(function (k, el) { $input_user = $(el); $input_user.select2({ @@ -240,6 +240,8 @@ jQuery(function($){ if ('undefined' !== typeof object.icon) { result = '' + result; + // Add more info to the container + container.attr('title', object.tooltip); } // Add more info to the container if ( 'undefined' !== typeof object.tooltip ) { @@ -269,12 +271,18 @@ jQuery(function($){ }); // Confirmation on some important actions - $('#wp_stream_general_delete_all_records').click(function(e){ + $('#wp_stream_general_delete_all_records, #wp_stream_network_general_delete_all_records').click(function(e){ if ( ! confirm( wp_stream.i18n.confirm_purge ) ) { e.preventDefault(); } }); + $('#wp_stream_general_reset_site_settings, #wp_stream_network_general_reset_site_settings').click(function(e){ + if ( ! confirm( wp_stream.i18n.confirm_defaults ) ) { + e.preventDefault(); + } + }); + $('#wp_stream_uninstall').click(function(e){ if ( ! confirm( wp_stream.i18n.confirm_uninstall ) ) { e.preventDefault(); @@ -283,13 +291,13 @@ jQuery(function($){ // Admin page tabs var $tabs = $('.nav-tab-wrapper'), - $panels = $('table.form-table'), + $panels = $('.nav-tab-content table.form-table'), $activeTab = $tabs.find('.nav-tab-active'), defaultIndex = $activeTab.length > 0 ? $tabs.find('a').index( $activeTab ) : 0, hashIndex = window.location.hash.match(/^#(\d+)$/), currentHash = ( hashIndex !== null ? hashIndex[1] : defaultIndex ), syncFormAction = function( index ) { - var $optionsForm = $('input[name="option_page"][value="wp_stream"]').parent('form'); + var $optionsForm = $('input[name="option_page"][value^="wp_stream"]').parent('form'); var currentAction = $optionsForm.attr('action'); $optionsForm.prop('action', currentAction.replace( /(^[^#]*).*$/, '$1#' + index )); @@ -458,18 +466,17 @@ jQuery(function($){ } } - if ( $( 'div.stream-toggle-filters [id="date_range"]' ).is( ':checked' ) ) { + if ( $( 'div.stream-toggle-filters [id="date"]' ).is( ':checked' ) ) { $( 'div.date-interval' ).show(); } else { $( 'div.date-interval' ).hide(); } - var filters = [ 'date_range', 'author', 'connector', 'context', 'action' ]; - for( var i=0; i < filters.length; i++ ) { - if ( $( 'div.stream-toggle-filters [id="' + filters[i] + '"]' ).is( ':checked' ) ) { - $( '[name="' + filters[i] + '"]' ).prev( '.select2-container' ).show(); + for( var filter in wp_stream.filters ) { + if ( $( 'div.stream-toggle-filters [id="' + filter + '"]' ).is( ':checked' ) ) { + $( '[name="' + filter + '"]' ).prev( '.select2-container' ).show(); } else { - $( '[name="' + filters[i] + '"]' ).prev( '.select2-container' ).hide(); + $( '[name="' + filter + '"]' ).prev( '.select2-container' ).hide(); } } @@ -502,7 +509,7 @@ jQuery(function($){ var date_interval_div = $( 'div.date-interval' ); // toggle visibility of input whose name attr matches checkbox ID - if ( data.control === 'date_range' ) { + if ( data.control === 'date' ) { date_interval_div.toggle(); } else { var control = $( '[name="' + data.control + '"]');