log( __("My translated message") );` */ public $gettextLatestTranslations = array(); /** * All registered settings tabs */ private $arr_settings_tabs = array(); const DBTABLE = "simple_history"; const DBTABLE_CONTEXTS = "simple_history_contexts"; /** Slug for the settings menu */ const SETTINGS_MENU_SLUG = "simple_history_settings_menu_slug"; /** Slug for the settings menu */ const SETTINGS_GENERAL_OPTION_GROUP = "simple_history_settings_group"; /** ID for the general settings section */ const SETTINGS_SECTION_GENERAL_ID = "simple_history_settings_section_general"; function __construct() { $this->init(); } // construct /** * @since 2.5.2 */ public function init() { /** * Fires before Simple History does it's init stuff * * @since 2.0 * * @param SimpleHistory $SimpleHistory This class. */ do_action( "simple_history/before_init", $this ); $this->setup_variables(); // Actions and filters, ordered by order specified in codex: http://codex.wordpress.org/Plugin_API/Action_Reference add_action( 'after_setup_theme', array( $this, 'load_plugin_textdomain' ) ); add_action( 'after_setup_theme', array( $this, 'add_default_settings_tabs' ) ); // Plugins and dropins are loaded using the "after_setup_theme" filter so // themes can use filters to modify the loading of them. // The drawback with this is that for example logouts done when plugins like // iThemes Security is installed is not logged, because those plugins fire wp_logout() // using filter "plugins_loaded", i.e. before simple history has loaded its filters. add_action( 'after_setup_theme', array( $this, 'load_loggers' ) ); add_action( 'after_setup_theme', array( $this, 'load_dropins' ) ); // Run before loading of loggers and before menu items are added add_action( 'after_setup_theme', array( $this, 'check_for_upgrade' ), 5 ); add_action( 'after_setup_theme', array( $this, 'setup_cron' ) ); // Filters and actions not called during regular boot add_filter( "gettext", array( $this, 'filter_gettext' ), 20, 3 ); add_filter( "gettext_with_context", array( $this, 'filter_gettext_with_context' ), 20, 4 ); add_filter( 'gettext', array( $this, "filter_gettext_storeLatestTranslations" ), 10, 3 ); add_action( 'admin_bar_menu', array( $this, 'add_admin_bar_network_menu_item' ), 40 ); add_action( 'admin_bar_menu', array( $this, 'add_admin_bar_menu_item' ), 40 ); /** * Filter that is used to log things, without the need to check that simple history is available * i.e. you can have simple history acivated and log things and then you can disable the plugin * and no errors will occur * * Usage: * apply_filters("simple_history_log", "This is the log message"); * apply_filters("simple_history_log", "This is the log message with some extra data/info", ["extraThing1" => $variableWIihThing]); * apply_filters("simple_history_log", "This is the log message with severity debug", null, "debug"); * apply_filters("simple_history_log", "This is the log message with severity debug and with some extra info/data logged", ["userData" => $userData, "shoppingCartDebugData" => $shopDebugData], "debug",); * * @since 2.13 */ add_filter( 'simple_history_log', array($this, "on_filter_simple_history_log"), 10, 3 ); if ( is_admin() ) { $this->add_admin_actions(); } /** * Fires after Simple History has done it's init stuff * * @since 2.0 * * @param SimpleHistory $SimpleHistory This class. */ do_action( "simple_history/after_init", $this ); // Add some extra info to each logged context when SIMPLE_HISTORY_LOG_DEBUG is set and true if ( defined( "SIMPLE_HISTORY_LOG_DEBUG" ) && SIMPLE_HISTORY_LOG_DEBUG ) { add_filter( "simple_history/log_argument/context", function( $context, $level, $message, $logger ) { $sh = SimpleHistory::get_instance(); $context["_debug_get"] = $sh->json_encode( $_GET ); $context["_debug_post"] = $sh->json_encode( $_POST ); $context["_debug_server"] = $sh->json_encode( $_SERVER ); $context["_debug_files"] = $sh->json_encode( $_FILES ); $context["_debug_php_sapi_name"] = php_sapi_name(); global $argv; $context["_debug_argv"] = $sh->json_encode( $argv ); $consts = get_defined_constants( true ); $consts = $consts["user"]; $context["_debug_user_constants"] = $sh->json_encode( $consts ); $postdata = file_get_contents( "php://input" ); $context["_debug_http_raw_post_data"] = $sh->json_encode( $postdata ); $context["_debug_wp_debug_backtrace_summary"] = wp_debug_backtrace_summary(); $context["_debug_is_admin"] = json_encode( is_admin() ); $context["_debug_is_doing_cron"] = json_encode( defined('DOING_CRON') && DOING_CRON ); return $context; }, 10, 4 ); } } /** * Log a message * * Function called when running filter "simple_history_log" * * @since 2.13 * @param mixed $logMessage * @param array $context Optional context to add to the logged data * @param string $level The loglevel. Must be one of the existing ones. Defaults to "info". */ public function on_filter_simple_history_log( $message = null, $context = null, $level = "info" ) { if (empty($message)) { return; } if (!is_array($context)) { $context = array(); } SimpleLogger()->log($level, $message, $context); } /** * @since 2.5.2 */ private function add_admin_actions() { add_action( 'admin_menu', array( $this, 'add_admin_pages' ) ); add_action( 'admin_menu', array( $this, 'add_settings' ) ); add_action( 'admin_footer', array( $this, "add_js_templates" ) ); add_action( 'wp_dashboard_setup', array( $this, 'add_dashboard_widget' ) ); add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_scripts' ) ); add_action( 'admin_head', array( $this, "onAdminHead" ) ); add_action( 'admin_footer', array( $this, "onAdminFooter" ) ); add_action( 'simple_history/history_page/before_gui', array( $this, "output_quick_stats" ) ); add_action( 'simple_history/dashboard/before_gui', array( $this, "output_quick_stats" ) ); add_action( 'wp_ajax_simple_history_api', array( $this, 'api' ) ); add_filter( 'plugin_action_links_simple-history/index.php', array( $this, 'plugin_action_links' ), 10, 4 ); } /** * Adds a "View history" item/shortcut to the network admin, on blogs where Simple History is installed * * Useful because Simple History is something at least the author of this plugin often use on a site :) * * @since 2.7.1 */ function add_admin_bar_network_menu_item( $wp_admin_bar ) { /** * Filter to control if admin bar shortcut should be added * * @since 2.7.1 * * @param bool Add item */ $add_items = apply_filters( "simple_history/add_admin_bar_network_menu_item", true ); if ( ! $add_items ) { return; } // Don't show for logged out users or single site mode. if ( ! is_user_logged_in() || ! is_multisite() ) return; // Show only when the user has at least one site, or they're a super admin. if ( count( $wp_admin_bar->user->blogs ) < 1 && ! is_super_admin() ) return; // Setting to show as page must be true if ( ! $this->setting_show_as_page() ) { return; } // User must have capability to view the history page if ( ! current_user_can( $this->get_view_history_capability() ) ) { return; } /* menu_page_url() is defined in the WordPress Plugin Administration API, which is not loaded here by default */ /* dito for is_plugin_active() */ require_once(ABSPATH . 'wp-admin/includes/plugin.php'); foreach ( (array) $wp_admin_bar->user->blogs as $blog ) { switch_to_blog( $blog->userblog_id ); if ( is_plugin_active( SIMPLE_HISTORY_BASENAME ) ) { $menu_id = "simple-history-blog-" . $blog->userblog_id; $parent_menu_id = 'blog-' . $blog->userblog_id; $url = admin_url( "index.php?page=simple_history_page" ); // Each network site is added by WP core with id "blog-1", "blog-2" ... "blog-n" // https://codex.wordpress.org/Function_Reference/add_node $args = array( 'id' => $menu_id, 'parent' => $parent_menu_id, 'title' => _x("View History", "Admin bar network name", "simple-history"), 'href' => $url, 'meta' => array( 'class' => 'ab-item--simplehistory' ) ); $wp_admin_bar->add_node( $args ); } // if plugin active restore_current_blog(); } // foreach blog } // func /** * Adds a "View history" item/shortcut to the admin bar * * Useful because Simple History is something at least the author of this plugin often use on a site :) * * @since 2.7.1 */ function add_admin_bar_menu_item( $wp_admin_bar ) { /** * Filter to control if admin bar shortcut should be added * * @since 2.7.1 * * @param bool Add item */ $add_item = apply_filters( "simple_history/add_admin_bar_menu_item", true ); if ( ! $add_item ) { return; } // Don't show for logged out users if ( ! is_user_logged_in() ) return; // Setting to show as page must be true if ( ! $this->setting_show_as_page() ) { return; } // User must have capability to view the history page if ( ! current_user_can( $this->get_view_history_capability() ) ) { return; } /* menu_page_url() is defined in the WordPress Plugin Administration API, which is not loaded here by default */ /* dito for is_plugin_active() */ require_once(ABSPATH . 'wp-admin/includes/plugin.php'); $menu_id = "simple-history-view-history"; $parent_menu_id = 'site-name'; $url = admin_url( "index.php?page=simple_history_page" ); $args = array( 'id' => $menu_id, 'parent' => $parent_menu_id, 'title' => _x("Simple History", "Admin bar name", "simple-history"), 'href' => $url, 'meta' => array( 'class' => 'ab-item--simplehistory' ) ); $wp_admin_bar->add_node( $args ); } // func /** * Get singleton intance * @return SimpleHistory instance */ public static function get_instance() { if ( ! isset( self::$instance ) ) { self::$instance = new SimpleHistory(); } return self::$instance; } function filter_gettext_storeLatestTranslations( $translation, $text, $domain ) { // Check that translation is a string or integer, i.ex. the valid values for an array key if ( ! is_string( $translation ) || ! is_integer( $translation ) ) { return $translation; } $array_max_size = 5; // Keep a listing of the n latest translation // when SimpleLogger->log() is called from anywhere we can then search for the // translated string among our n latest things and find it there, if it's translated // global $sh_latest_translations; $sh_latest_translations = $this->gettextLatestTranslations; $sh_latest_translations[$translation] = array( "translation" => $translation, "text" => $text, "domain" => $domain, ); $arr_length = sizeof( $sh_latest_translations ); if ( $arr_length > $array_max_size ) { $sh_latest_translations = array_slice( $sh_latest_translations, $arr_length - $array_max_size ); } $this->gettextLatestTranslations = $sh_latest_translations; return $translation; } function setup_cron() { add_filter( "simple_history/maybe_purge_db", array( $this, "maybe_purge_db" ) ); if ( ! wp_next_scheduled( 'simple_history/maybe_purge_db' ) ) { wp_schedule_event( time(), 'daily', 'simple_history/maybe_purge_db' ); //error_log("not scheduled, so do schedule"); } else { //error_log("is scheduled"); } // Remove old schedule (only author dev sites should have it) $old_next_scheduled = wp_next_scheduled( 'simple_history/purge_db' ); if ( $old_next_scheduled ) { wp_unschedule_event( $old_next_scheduled, 'simple_history/purge_db' ); } } public function testlog_old() { // Log that an email has been sent simple_history_add( array( "object_type" => "Email", "object_name" => "Hi there", "action" => "was sent", ) ); // Will show “Plugin your_plugin_name Edited” in the history log simple_history_add( "action=edited&object_type=plugin&object_name=your_plugin_name" ); // Will show the history item "Starship USS Enterprise repaired" simple_history_add( "action=repaired&object_type=Starship&object_name=USS Enterprise" ); // Log with some extra details about the email simple_history_add( array( "object_type" => "Email", "object_name" => "Hi there", "action" => "was sent", "description" => "The database query to generate the email took .3 seconds. This is email number 4 that is sent to this user", ) ); } public function onAdminHead() { if ( $this->is_on_our_own_pages() ) { do_action( "simple_history/admin_head", $this ); } } public function onAdminFooter() { if ( $this->is_on_our_own_pages() ) { do_action( "simple_history/admin_footer", $this ); } } /** * Output JS templated into footer */ public function add_js_templates( $hook ) { if ( $this->is_on_our_own_pages() ) { ?> instantiatedLoggers as $one_logger ) { if ( method_exists( $one_logger["instance"], "adminJS" ) ) { $one_logger["instance"]->adminJS(); } } } } /** * Base url is: * /wp-admin/admin-ajax.php?action=simple_history_api * * Examples: * http://playground-root.ep/wp-admin/admin-ajax.php?action=simple_history_api&posts_per_page=5&paged=1&format=html * */ public function api() { global $wpdb; // Fake slow answers //sleep(2); //sleep(rand(0,3)); $args = $_GET; unset( $args["action"] ); // Type = overview | ... $type = isset( $_GET["type"] ) ? $_GET["type"] : null; if ( empty( $args ) || ! $type ) { wp_send_json_error( array( _x( "Not enough args specified", "API: not enought arguments passed", "simple-history" ), ) ); } // User must have capability to view the history page if ( ! current_user_can( $this->get_view_history_capability() ) ) { wp_send_json_error( array("error" => "CAPABILITY_ERROR") ); } if ( isset( $args["id"] ) ) { $args["post__in"] = array( $args["id"], ); } $data = array(); switch ( $type ) { case "overview": case "occasions": case "single": // API use SimpleHistoryLogQuery, so simply pass args on to that $logQuery = new SimpleHistoryLogQuery(); $data = $logQuery->query( $args ); $data["api_args"] = $args; // Output can be array or HMTL if ( isset( $args["format"] ) && "html" === $args["format"] ) { $data["log_rows_raw"] = array(); foreach ( $data["log_rows"] as $key => $oneLogRow ) { $args = array(); if ( $type == "single" ) { $args["type"] = "single"; } $data["log_rows"][$key] = $this->getLogRowHTMLOutput( $oneLogRow, $args ); $data["num_queries"] = get_num_queries(); } } else { // $data["logRows"] = $logRows; } break; default: $data[] = "Nah."; } wp_send_json_success( $data ); } /** * During the load of info for a logger we want to get a reference * to the untranslated text too, because that's the version we want to store * in the database. */ public function filter_gettext( $translated_text, $untranslated_text, $domain ) { if ( isset( $this->doFilterGettext ) && $this->doFilterGettext ) { $this->doFilterGettext_currentLogger->messages[] = array( "untranslated_text" => $untranslated_text, "translated_text" => $translated_text, "domain" => $domain, "context" => null, ); } return $translated_text; } /** * Store messages with context */ public function filter_gettext_with_context( $translated_text, $untranslated_text, $context, $domain ) { if ( isset( $this->doFilterGettext ) && $this->doFilterGettext ) { $this->doFilterGettext_currentLogger->messages[] = array( "untranslated_text" => $untranslated_text, "translated_text" => $translated_text, "domain" => $domain, "context" => $context, ); } return $translated_text; } /** * Load language files. * Uses the method described here: * http://geertdedeckere.be/article/loading-wordpress-language-files-the-right-way * * @since 2.0 */ public function load_plugin_textdomain() { $domain = 'simple-history'; // The "plugin_locale" filter is also used in load_plugin_textdomain() $locale = apply_filters( 'plugin_locale', get_locale(), $domain ); load_textdomain( $domain, WP_LANG_DIR . '/simple-history/' . $domain . '-' . $locale . '.mo' ); load_plugin_textdomain( $domain, FALSE, dirname( $this->plugin_basename ) . '/languages/' ); } /** * Setup variables and things */ public function setup_variables() { $this->externalLoggers = array(); $this->externalDropins = array(); $this->instantiatedLoggers = array(); $this->instantiatedDropins = array(); $this->plugin_basename = SIMPLE_HISTORY_BASENAME; } /** * Return capability required to view history = for who will the History page be added * * @since 2.1.5 * @return string capability */ public function get_view_history_capability() { $view_history_capability = "edit_pages"; $view_history_capability = apply_filters( "simple_history_view_history_capability", $view_history_capability ); $view_history_capability = apply_filters( "simple_history/view_history_capability", $view_history_capability ); return $view_history_capability; } /** * Return capability required to view settings * * @since 2.1.5 * @return string capability */ public function get_view_settings_capability() { $view_settings_capability = "manage_options"; $view_settings_capability = apply_filters( "simple_history_view_settings_capability", $view_settings_capability ); $view_settings_capability = apply_filters( "simple_history/view_settings_capability", $view_settings_capability ); return $view_settings_capability; } /** * Adds default tabs to settings */ public function add_default_settings_tabs() { // Add default settings tabs $this->arr_settings_tabs = array( array( "slug" => "settings", "name" => __( "Settings", "simple-history" ), "function" => array( $this, "settings_output_general" ), ), ); if ( defined( "SIMPLE_HISTORY_DEV" ) && SIMPLE_HISTORY_DEV ) { $arr_dev_tabs = array( array( "slug" => "log", "name" => __( "Log (debug)", "simple-history" ), "function" => array( $this, "settings_output_log" ), ), array( "slug" => "styles-example", "name" => __( "Styles example (debug)", "simple-history" ), "function" => array( $this, "settings_output_styles_example" ), ), ); $this->arr_settings_tabs = array_merge( $this->arr_settings_tabs, $arr_dev_tabs ); } } /** * Register an external logger so Simple History knows about it. * Does not load the logger, so file with logger class must be loaded already. * * See example-logger.php for an example on how to use this. * * @since 2.1 */ function register_logger( $loggerClassName ) { $this->externalLoggers[] = $loggerClassName; } /** * Register an external dropin so Simple History knows about it. * Does not load the dropin, so file with dropin class must be loaded already. * * See example-dropin.php for an example on how to use this. * * @since 2.1 */ function register_dropin( $dropinClassName ) { $this->externalDropins[] = $dropinClassName; } /** * Load built in loggers from all files in /loggers * and instantiates them */ public function load_loggers() { $loggersDir = SIMPLE_HISTORY_PATH . "loggers/"; $loggersFiles = array( $loggersDir . "SimpleCommentsLogger.php", $loggersDir . "SimpleCoreUpdatesLogger.php", $loggersDir . "SimpleExportLogger.php", $loggersDir . "SimpleLegacyLogger.php", $loggersDir . "SimpleLogger.php", $loggersDir . "SimpleMediaLogger.php", $loggersDir . "SimpleMenuLogger.php", $loggersDir . "SimpleOptionsLogger.php", $loggersDir . "SimplePluginLogger.php", $loggersDir . "SimplePostLogger.php", $loggersDir . "SimpleThemeLogger.php", $loggersDir . "SimpleUserLogger.php", $loggersDir . "SimpleCategoriesLogger.php", $loggersDir . "AvailableUpdatesLogger.php", // Loggers for third party plugins $loggersDir . "PluginUserSwitchingLogger.php", $loggersDir . "PluginEnableMediaReplaceLogger.php", $loggersDir . "Plugin_UltimateMembers_Logger.php", $loggersDir . "Plugin_LimitLoginAttempts.php", $loggersDir . "Plugin_Redirection.php", $loggersDir . "Plugin_DuplicatePost.php", ); // SimpleLogger.php must be loaded first and always since the other loggers extend it // Include it manually so risk of anyone using filters or similar disables it include_once $loggersDir . "SimpleLogger.php"; /** * Filter the array with absolute paths to logger files to be loaded. * * Each file will be loaded and will be assumed to be a logger with a classname * the same as the filename. * * @since 2.0 * * @param array $loggersFiles Array with filenames */ $loggersFiles = apply_filters( "simple_history/loggers_files", $loggersFiles ); // Array with slug of loggers to instantiate // Slug of logger must also be the name of the logger class $arrLoggersToInstantiate = array(); foreach ( $loggersFiles as $oneLoggerFile ) { $load_logger = true; $basename_no_suffix = basename( $oneLoggerFile, ".php" ); /** * Filter to completely skip loading of a logger * * @since 2.0.22 * * @param bool if to load the logger. return false to not load it. * @param string slug of logger */ $load_logger = apply_filters( "simple_history/logger/load_logger", $load_logger, $basename_no_suffix ); if ( ! $load_logger ) { continue; } include_once $oneLoggerFile; $arrLoggersToInstantiate[] = $basename_no_suffix; } /** * Action that plugins can use to add their custom loggers. * See register_logger() for more info. * * @since 2.1 * * @param SimpleHistory instance */ do_action( "simple_history/add_custom_logger", $this ); $arrLoggersToInstantiate = array_merge( $arrLoggersToInstantiate, $this->externalLoggers ); /** * Filter the array with names of loggers to instantiate. * * Array * ( * [0] => SimpleCommentsLogger * [1] => SimpleCoreUpdatesLogger * ... * ) * * @since 2.0 * * @param array $arrLoggersToInstantiate Array with class names */ $arrLoggersToInstantiate = apply_filters( "simple_history/loggers_to_instantiate", $arrLoggersToInstantiate ); // Instantiate each logger foreach ( $arrLoggersToInstantiate as $oneLoggerName ) { if ( ! class_exists( $oneLoggerName ) ) { continue; } $loggerInstance = new $oneLoggerName( $this ); if ( ! is_subclass_of( $loggerInstance, "SimpleLogger" ) && ! is_a( $loggerInstance, "SimpleLogger" ) ) { continue; } $loggerInstance->loaded(); // Tell gettext-filter to add untranslated messages $this->doFilterGettext = true; $this->doFilterGettext_currentLogger = $loggerInstance; $loggerInfo = $loggerInstance->getInfo(); /* $loggerInfo["messages"] [messages] => Array ( [anon_comment_added] => Lade till en kommentar till {comment_post_type} "{comment_post_title}" [user_comment_added] => Lade till en kommentar till {comment_post_type} "{comment_post_title}" [comment_status_approve] => Godkände en kommentar till "{comment_post_title}" av {comment_author} ({comment_author_email}) [comment_status_hold] => Godkände inte en kommentar till "{comment_post_title}" av {comment_author} ({comment_author_email}) */ /* $loggerInstance->messages Array ( [0] => Array ( [untranslated_text] => Added a comment to {comment_post_type} "{comment_post_title}" [translated_text] => Lade till en kommentar till {comment_post_type} "{comment_post_title}" [domain] => simple-history [context] => A comment was added to the database by a non-logged in internet user ) */ // Un-tell gettext filter $this->doFilterGettext = false; $this->doFilterGettext_currentLogger = null; // LoggerInfo contains all messages, both translated an not, by key. // Add messages to the loggerInstance $loopNum = 0; $arr_messages_by_message_key = array(); if ( isset( $loggerInfo["messages"] ) ) { foreach ( (array) $loggerInfo["messages"] as $message_key => $message_translated ) { // Find message in array with both translated and non translated strings foreach ( $loggerInstance->messages as $one_message_with_translation_info ) { /* [0] => Array ( [untranslated_text] => ... [translated_text] => ... [domain] => simple-history [context] => ... ) */ if ( $message_translated == $one_message_with_translation_info["translated_text"] ) { $arr_messages_by_message_key[ $message_key ] = $one_message_with_translation_info; continue; } } } } $loggerInstance->messages = $arr_messages_by_message_key; // Add logger to array of loggers $this->instantiatedLoggers[$loggerInstance->slug] = array( "name" => $loggerInfo["name"], "instance" => $loggerInstance, ); } do_action( "simple_history/loggers_loaded" ); } /** * Load built in dropins from all files in /dropins * and instantiates them */ public function load_dropins() { $dropinsDir = SIMPLE_HISTORY_PATH . "dropins/"; $dropinsFiles = array( $dropinsDir . "SimpleHistoryPluginPatchesDropin.php", $dropinsDir . "SimpleHistoryDonateDropin.php", $dropinsDir . "SimpleHistoryExportDropin.php", $dropinsDir . "SimpleHistoryFilterDropin.php", $dropinsDir . "SimpleHistoryIpInfoDropin.php", $dropinsDir . "SimpleHistoryNewRowsNotifier.php", $dropinsDir . "SimpleHistoryRSSDropin.php", $dropinsDir . "SimpleHistorySettingsLogtestDropin.php", $dropinsDir . "SimpleHistorySettingsStatsDropin.php", $dropinsDir . "SimpleHistorySettingsDebugDropin.php", $dropinsDir . "SimpleHistorySidebarDropin.php", $dropinsDir . "SimpleHistorySidebarStats.php", ); /** * Filter the array with absolute paths to files as returned by glob function. * Each file will be loaded and will be assumed to be a dropin with a classname * the same as the filename. * * @since 2.0 * * @param array $dropinsFiles Array with filenames */ $dropinsFiles = apply_filters( "simple_history/dropins_files", $dropinsFiles ); $arrDropinsToInstantiate = array(); foreach ( $dropinsFiles as $oneDropinFile ) { // path/path/simplehistory/dropins/SimpleHistoryDonateDropin.php => SimpleHistoryDonateDropin $oneDropinFileBasename = basename( $oneDropinFile, ".php" ); $load_dropin = true; /** * Filter to completely skip loading of dropin * complete filer name will be like: * simple_history/dropin/load_dropin_SimpleHistoryRSSDropin * * @since 2.0.6 * * @param bool if to load the dropin. return false to not load it. */ $load_dropin = apply_filters( "simple_history/dropin/load_dropin_{$oneDropinFileBasename}", $load_dropin ); /** * Filter to completely skip loading of a dropin * * @since 2.0.22 * * @param bool if to load the dropin. return false to not load it. * @param string slug of dropin */ $load_dropin = apply_filters( "simple_history/dropin/load_dropin", $load_dropin, $oneDropinFileBasename ); if ( ! $load_dropin ) { continue; } include_once $oneDropinFile; $arrDropinsToInstantiate[] = $oneDropinFileBasename; } /** * Action that dropins can use to add their custom loggers. * See register_dropin() for more info. * * @since 2.3.2 * * @param array $arrDropinsToInstantiate Array with class names */ do_action( "simple_history/add_custom_dropin", $this ); /** * Filter the array with names of dropin to instantiate. * * @since 2.0 * * @param array $arrDropinsToInstantiate Array with class names */ $arrDropinsToInstantiate = apply_filters( "simple_history/dropins_to_instantiate", $arrDropinsToInstantiate ); $arrDropinsToInstantiate = array_merge( $arrDropinsToInstantiate, $this->externalDropins ); // Instantiate each dropin foreach ( $arrDropinsToInstantiate as $oneDropinName ) { if ( ! class_exists( $oneDropinName ) ) { continue; } $this->instantiatedDropins[$oneDropinName] = array( "name" => $oneDropinName, "instance" => new $oneDropinName( $this ), ); } } /** * Gets the pager size, * i.e. the number of items to show on each page in the history * * @return int */ function get_pager_size() { $pager_size = get_option( "simple_history_pager_size", 20 ); /** * Filter the pager size setting * * @since 2.0 * * @param int $pager_size */ $pager_size = apply_filters( "simple_history/pager_size", $pager_size ); return $pager_size; } /** * Gets the pager size, * i.e. the number of items to show on each page in the history * * @since 2.12 * @return int */ function get_pager_size_dashboard() { $pager_size = get_option( "simple_history_pager_size_dashboard", 5 ); /** * Filter the pager size setting * * @since 2.12 * * @param int $pager_size */ $pager_size = apply_filters( "simple_history/pager_size_dashboard", $pager_size ); return $pager_size; } /** * Show a link to our settings page on the Plugins -> Installed Plugins screen */ function plugin_action_links( $actions, $b, $c, $d ) { // Only add link if user has the right to view the settings page if ( ! current_user_can( $this->get_view_settings_capability() ) ) { return $actions; } $settings_page_url = menu_page_url( SimpleHistory::SETTINGS_MENU_SLUG, 0 ); $actions[] = "" . __( "Settings", "simple-history" ) . ""; return $actions; } /** * Maybe add a dashboard widget, * requires current user to have view history capability * and a setting to show dashboard to be set */ function add_dashboard_widget() { if ( $this->setting_show_on_dashboard() && current_user_can( $this->get_view_history_capability() ) ) { /** * Filter to determine if history page should be added to page below dashboard or not * * @since 2.0.23 * * @param bool Show the page or not */ $show_dashboard_widget = apply_filters( "simple_history/show_dashboard_widget", true ); if ( $show_dashboard_widget ) { wp_add_dashboard_widget( "simple_history_dashboard_widget", __( "Simple History", 'simple-history' ), array( $this, "dashboard_widget_output" ) ); } } } /** * Output html for the dashboard widget */ function dashboard_widget_output() { $pager_size = $this->get_pager_size_dashboard(); /** * Filter the pager size setting for the dashboard * * @since 2.0 * * @param int $pager_size */ $pager_size = apply_filters( "simple_history/dashboard_pager_size", $pager_size ); do_action( "simple_history/dashboard/before_gui", $this ); ?>
base == "settings_page_" . SimpleHistory::SETTINGS_MENU_SLUG ) { return true; } else if ( $current_screen && $current_screen->base == "dashboard_page_simple_history_page" ) { return true; } else if ( ( $hook == "settings_page_" . SimpleHistory::SETTINGS_MENU_SLUG ) || ( $this->setting_show_on_dashboard() && $hook == "index.php" ) || ( $this->setting_show_as_page() && $hook == "dashboard_page_simple_history_page" ) ) { return true; } else if ( $current_screen && $current_screen->base == "dashboard" && $this->setting_show_on_dashboard() ) { return true; } return false; } /** * Enqueue styles and scripts for Simple History but only to our own pages. * * Only adds scripts to pages where the log is shown or the settings page. */ function enqueue_admin_scripts( $hook ) { if ( $this->is_on_our_own_pages() ) { add_thickbox(); wp_enqueue_style( "simple_history_styles", SIMPLE_HISTORY_DIR_URL . "css/styles.css", false, SIMPLE_HISTORY_VERSION ); wp_enqueue_script( "simple_history_script", SIMPLE_HISTORY_DIR_URL . "js/scripts.js", array( "jquery", "backbone", "wp-util" ), SIMPLE_HISTORY_VERSION, true ); wp_enqueue_script( "select2", SIMPLE_HISTORY_DIR_URL . "js/select2/select2.min.js", array( "jquery" ) ); wp_enqueue_style( "select2", SIMPLE_HISTORY_DIR_URL . "js/select2/select2.css" ); // Translations that we use in JavaScript wp_localize_script( 'simple_history_script', 'simple_history_script_vars', array( 'settingsConfirmClearLog' => __( "Remove all log items?", 'simple-history' ), 'pagination' => array( 'goToTheFirstPage' => __( "Go to the first page", 'simple-history' ), 'goToThePrevPage' => __( "Go to the previous page", 'simple-history' ), 'goToTheNextPage' => __( "Go to the next page", 'simple-history' ), 'goToTheLastPage' => __( "Go to the last page", 'simple-history' ), 'currentPage' => __( "Current page", 'simple-history' ), ), "loadLogAPIError" => __( "Oups, the log could not be loaded right now.", 'simple-history' ), "ajaxLoadError" => __( "Hm, the log could not be loaded right now. Perhaps another plugin is giving some errors. Anyway, below is the output I got from the server.", 'simple-history' ), "logNoHits" => __( "Your search did not match any history events.", "simple-history" ), ) ); // Call plugins adminCSS-method, so they can add their CSS foreach ( $this->instantiatedLoggers as $one_logger ) { if ( method_exists( $one_logger["instance"], "adminCSS" ) ) { $one_logger["instance"]->adminCSS(); } } // Add timeago.js wp_enqueue_script( 'timeago', SIMPLE_HISTORY_DIR_URL . 'js/timeago/jquery.timeago.js', array("jquery"), '1.5.2', true ); // Determine current locale to load timeago locale $locale = strtolower( substr( get_locale(), 0, 2 ) ); $locale_url_path = SIMPLE_HISTORY_DIR_URL . 'js/timeago/locales/jquery.timeago.%s.js'; $locale_dir_path = SIMPLE_HISTORY_PATH . 'js/timeago/locales/jquery.timeago.%s.js'; // Only enqueue if locale-file exists on file system if ( file_exists( sprintf( $locale_dir_path, $locale ) ) ) { wp_enqueue_script( 'timeago-locale', sprintf( $locale_url_path, $locale ), array("jquery"), '1.5.2', true ); } else { wp_enqueue_script( 'timeago-locale', sprintf( $locale_url_path, "en" ), array("jquery"), '1.5.2', true ); } // end add timeago // Load Select2 locale $locale_url_path = SIMPLE_HISTORY_DIR_URL . 'js/select2/select2_locale_%s.js'; $locale_dir_path = SIMPLE_HISTORY_PATH . 'js/select2/select2_locale_%s.js'; if ( file_exists( sprintf( $locale_dir_path, $locale ) ) ) { wp_enqueue_script( 'select2-locale', sprintf( $locale_url_path, $locale ), array("jquery"), '3.5.1', true ); } /** * Fires when the admin scripts have been enqueued. * Only fires on any of the pages where Simple History is used * * @since 2.0 * * @param SimpleHistory $SimpleHistory This class. */ do_action( "simple_history/enqueue_admin_scripts", $this ); } } function filter_option_page_capability( $capability ) { return $capability; } /** * Check if plugin version have changed, i.e. has been upgraded * If upgrade is detected then maybe modify database and so on for that version */ function check_for_upgrade() { global $wpdb; $db_version = get_option( "simple_history_db_version" ); $table_name = $wpdb->prefix . SimpleHistory::DBTABLE; $table_name_contexts = $wpdb->prefix . SimpleHistory::DBTABLE_CONTEXTS; $first_install = false; // If no db_version is set then this // is a version of Simple History < 0.4 // or it's a first install // Fix database not using UTF-8 if ( false === $db_version || intval( $db_version ) == 0 ) { require_once ABSPATH . 'wp-admin/includes/upgrade.php'; // Table creation, used to be in register_activation_hook // We change the varchar size to add one num just to force update of encoding. dbdelta didn't see it otherwise. $sql = "CREATE TABLE " . $table_name . " ( id bigint(20) NOT NULL AUTO_INCREMENT, date datetime NOT NULL, PRIMARY KEY (id) ) CHARACTER SET=utf8;"; // Upgrade db / fix utf for varchars dbDelta( $sql ); // Fix UTF-8 for table $sql = sprintf( 'alter table %1$s charset=utf8;', $table_name ); $wpdb->query( $sql ); $db_version_prev = $db_version; $db_version = 1; update_option( "simple_history_db_version", $db_version ); // We are not 100% sure that this is a first install, // but it is at least a very old version that is being updated $first_install = true; } // done pre db ver 1 things // If db version is 1 then upgrade to 2 // Version 2 added the action_description column if ( 1 == intval( $db_version ) ) { // V2 used to add column "action_description" // but it's not used any more so don't do i $db_version_prev = $db_version; $db_version = 2; update_option( "simple_history_db_version", $db_version ); } // Check that all options we use are set to their defaults, if they miss value // Each option that is missing a value will make a sql call otherwise = unnecessary $arr_options = array( array( "name" => "simple_history_show_as_page", "default_value" => 1, ), array( "name" => "simple_history_show_on_dashboard", "default_value" => 1, ), ); foreach ( $arr_options as $one_option ) { if ( false === ( $option_value = get_option( $one_option["name"] ) ) ) { // Value is not set in db, so set it to a default update_option( $one_option["name"], $one_option["default_value"] ); } } /** * If db_version is 2 then upgrade to 3: * - Add some fields to existing table wp_simple_history_contexts * - Add all new table wp_simple_history_contexts * * @since 2.0 */ if ( 2 == intval( $db_version ) ) { require_once ABSPATH . 'wp-admin/includes/upgrade.php'; // Update old table $sql = " CREATE TABLE {$table_name} ( id bigint(20) NOT NULL AUTO_INCREMENT, date datetime NOT NULL, logger varchar(30) DEFAULT NULL, level varchar(20) DEFAULT NULL, message varchar(255) DEFAULT NULL, occasionsID varchar(32) DEFAULT NULL, initiator varchar(16) DEFAULT NULL, PRIMARY KEY (id), KEY date (date), KEY loggerdate (logger,date) ) CHARSET=utf8;"; dbDelta( $sql ); // Add context table $sql = " CREATE TABLE IF NOT EXISTS {$table_name_contexts} ( context_id bigint(20) unsigned NOT NULL AUTO_INCREMENT, history_id bigint(20) unsigned NOT NULL, `key` varchar(255) DEFAULT NULL, value longtext, PRIMARY KEY (context_id), KEY history_id (history_id), KEY `key` (`key`) ) CHARSET=utf8; "; $wpdb->query( $sql ); $db_version_prev = $db_version; $db_version = 3; update_option( "simple_history_db_version", $db_version ); // Update possible old items to use SimpleLegacyLogger $sql = sprintf( ' UPDATE %1$s SET logger = "SimpleLegacyLogger", level = "info" WHERE logger IS NULL ', $table_name ); $wpdb->query( $sql ); // Say welcome, however loggers are not added this early so we need to // use a filter to load it later add_action( "simple_history/loggers_loaded", array( $this, "addWelcomeLogMessage" ) ); } // db version 2 » 3 /** * If db version = 3 * then we need to update database to allow null values for some old columns * that used to work in pre wp 4.1 beta, but since 4.1 wp uses STRICT_ALL_TABLES * WordPress Commit: https://github.com/WordPress/WordPress/commit/f17d168a0f72211a9bfd9d3fa680713069871bb6 * * @since 2.0 */ if ( 3 == intval( $db_version ) ) { require_once ABSPATH . 'wp-admin/includes/upgrade.php'; // If old columns exist = this is an old install, then modify the columns so we still can keep them // we want to keep them because user may have logged items that they want to keep $db_cools = $wpdb->get_col( "DESCRIBE $table_name" ); if ( in_array("action", $db_cools) ) { $sql = sprintf( ' ALTER TABLE %1$s MODIFY `action` varchar(255) NULL, MODIFY `object_type` varchar(255) NULL, MODIFY `object_subtype` varchar(255) NULL, MODIFY `user_id` int(10) NULL, MODIFY `object_id` int(10) NULL, MODIFY `object_name` varchar(255) NULL ', $table_name ); $wpdb->query( $sql ); } $db_version_prev = $db_version; $db_version = 4; update_option( "simple_history_db_version", $db_version ); } // end db version 3 » 4 // Some installs on 2.2.2 got failed installs // We detect these by checking for db_version and then running the install stuff again if ( 4 == intval( $db_version ) ) { if ( ! $this->does_database_have_data() ) { // not ok, decrease db number so installs will run again and hopefully fix things $db_version = 0; } else { // all looks ok, upgrade to db version 5, so this part is not done again $db_version = 5; } update_option( "simple_history_db_version", $db_version ); } } // end check_for_upgrade /** * Check if the database has data/rows * * @since 2.1.6 * @return bool True if database is not empty, false if database is empty = contains no data */ function does_database_have_data() { global $wpdb; $tableprefix = $wpdb->prefix; $simple_history_table = SimpleHistory::DBTABLE; $simple_history_context_table = SimpleHistory::DBTABLE_CONTEXTS; $sql_data_exists = "SELECT id AS id_exists FROM {$tableprefix}{$simple_history_table} LIMIT 1"; $data_exists = (bool) $wpdb->get_var( $sql_data_exists, 0 ); return $data_exists; } /** * Greet users to version 2! * Is only called after database has been upgraded, so only on first install (or upgrade). * Not called after only plugin activation. */ public function addWelcomeLogMessage() { $db_data_exists = $this->does_database_have_data(); #$db_data_exists = false; $pluginLogger = $this->getInstantiatedLoggerBySlug( "SimplePluginLogger" ); if ( $pluginLogger ) { // Add plugin installed message $context = array( "plugin_name" => "Simple History", "plugin_description" => "Plugin that logs various things that occur in WordPress and then presents those events in a very nice GUI.", "plugin_url" => "http://simple-history.com", "plugin_version" => SIMPLE_HISTORY_VERSION, "plugin_author" => "Pär Thernström" ); $pluginLogger->infoMessage( "plugin_installed", $context ); // Add plugin activated message $context["plugin_slug"] = "simple-history"; $context["plugin_title"] = 'Simple History'; $pluginLogger->infoMessage( "plugin_activated", $context ); } if ( ! $db_data_exists ) { $welcome_message_1 = " Welcome to Simple History! This is the main history feed. It will contain events that this plugin has logged. "; $welcome_message_2 =" Because Simple History was just recently installed, this feed does not contain much events yet. But keep the plugin activated and soon you will see detailed information about page edits, plugin updates, user logins, and much more. "; SimpleLogger()->info( $welcome_message_2, array( "_initiator" => SimpleLoggerLogInitiators::WORDPRESS, ) ); SimpleLogger()->info( $welcome_message_1, array( "_initiator" => SimpleLoggerLogInitiators::WORDPRESS, ) ); } } public function registerSettingsTab( $arr_tab_settings ) { $this->arr_settings_tabs[] = $arr_tab_settings; } public function getSettingsTabs() { return $this->arr_settings_tabs; } /** * Output HTML for the settings page * Called from add_options_page */ function settings_page_output() { $arr_settings_tabs = $this->getSettingsTabs(); ?>"; if ( $clear_days > 0 ) { echo sprintf( __( 'Items in the database are automatically removed after %1$s days.', "simple-history" ), $clear_days ); } else { _e( 'Items in the database are kept forever.', 'simple-history' ); } echo "
"; printf( '', __( 'Clear log now', 'simple-history' ), $clear_link ); } /** * How old log entried are allowed to be. * 0 = don't delete old entries. * * @return int Number of days. */ function get_clear_history_interval() { $days = 60; /** * Filter to modify number of days of history to keep. * Default is 60 days. * * @param $days Number of days of history to keep */ $days = (int) apply_filters( "simple_history_db_purge_days_interval", $days ); $days = (int) apply_filters( "simple_history/db_purge_days_interval", $days ); return $days; } /** * Removes all items from the log */ function clear_log() { global $wpdb; $tableprefix = $wpdb->prefix; $simple_history_table = SimpleHistory::DBTABLE; $simple_history_context_table = SimpleHistory::DBTABLE_CONTEXTS; // Get number of rows before delete $sql_num_rows = "SELECT count(id) AS num_rows FROM {$tableprefix}{$simple_history_table}"; $num_rows = $wpdb->get_var( $sql_num_rows, 0 ); //$sql = "DELETE FROM {$tableprefix}{$simple_history_table}"; $sql = "TRUNCATE {$tableprefix}{$simple_history_table}"; $wpdb->query( $sql ); //$sql = "DELETE FROM {$tableprefix}{$simple_history_context_table}"; $sql = "TRUNCATE {$tableprefix}{$simple_history_context_table}"; $wpdb->query( $sql ); // Zero state sucks SimpleLogger()->info( __( "The log for Simple History was cleared ({num_rows} rows were removed).", "simple-history" ), array( "num_rows" => $num_rows, ) ); $this->get_cache_incrementor( true ); } /** * Runs the purge_db() method sometimes * We don't want to call it each time because it performs SQL queries * * @since 2.0.17 */ function maybe_purge_db() { /*if ( ! is_admin() ) { return; }*/ // How often should we try to do this? // Once a day = a bit tiresome // Let's go with sundays; purge the log on sundays // day of week, 1 = mon, 7 = sun $day_of_week = date( 'N' ); if ( 7 === (int) $day_of_week ) { $this->purge_db(); } } /** * Removes old entries from the db */ function purge_db() { // SimpleLogger()->debug("Simple History is running purge_db()"); $do_purge_history = true; $do_purge_history = apply_filters( "simple_history_allow_db_purge", $do_purge_history ); $do_purge_history = apply_filters( "simple_history/allow_db_purge", $do_purge_history ); if ( ! $do_purge_history ) { return; } $days = $this->get_clear_history_interval(); // Never clear log if days = 0 if ( 0 == $days ) { return; } global $wpdb; $table_name = $wpdb->prefix . SimpleHistory::DBTABLE; $table_name_contexts = $wpdb->prefix . SimpleHistory::DBTABLE_CONTEXTS; // Get id of rows to delete $sql = $wpdb->prepare( "SELECT id FROM $table_name WHERE DATE_ADD(date, INTERVAL %d DAY) < now()", $days ); $ids_to_delete = $wpdb->get_col( $sql ); if ( empty( $ids_to_delete ) ) { // Nothing to delete return; } $sql_ids_in = implode( ",", $ids_to_delete ); // Add number of deleted rows to total_rows option $prev_total_rows = (int) get_option( "simple_history_total_rows", 0 ); $total_rows = $prev_total_rows + sizeof( $ids_to_delete ); update_option( "simple_history_total_rows", $total_rows ); // Remove rows + contexts $sql_delete_history = "DELETE FROM {$table_name} WHERE id IN ($sql_ids_in)"; $sql_delete_history_context = "DELETE FROM {$table_name_contexts} WHERE history_id IN ($sql_ids_in)"; $wpdb->query( $sql_delete_history ); $wpdb->query( $sql_delete_history_context ); $message = _nx( "Simple History removed one event that were older than {days} days", "Simple History removed {num_rows} events that were older than {days} days", "Database is being cleared automagically", "simple-history" ); SimpleLogger()->info( $message, array( "days" => $days, "num_rows" => sizeof( $ids_to_delete ), ) ); $this->get_cache_incrementor( true ); } /** * Return plain text output for a log row * Uses the getLogRowPlainTextOutput of the logger that logged the row * with fallback to SimpleLogger if logger is not available * * @param array $row * @return string */ public function getLogRowPlainTextOutput( $row ) { $row_logger = $row->logger; $logger = null; $row->context = isset( $row->context ) && is_array( $row->context ) ? $row->context : array(); if ( ! isset( $row->context["_message_key"] ) ) { $row->context["_message_key"] = null; } // Fallback to SimpleLogger if no logger exists for row if ( ! isset( $this->instantiatedLoggers[$row_logger] ) ) { $row_logger = "SimpleLogger"; } $logger = $this->instantiatedLoggers[$row_logger]["instance"]; return $logger->getLogRowPlainTextOutput( $row ); } /** * Return header output for a log row * Uses the getLogRowHeaderOutput of the logger that logged the row * with fallback to SimpleLogger if logger is not available * * Loggers are discouraged to override this in the loggers, * because the output should be the same for all items in the gui * * @param array $row * @return string */ public function getLogRowHeaderOutput( $row ) { $row_logger = $row->logger; $logger = null; $row->context = isset( $row->context ) && is_array( $row->context ) ? $row->context : array(); // Fallback to SimpleLogger if no logger exists for row if ( ! isset( $this->instantiatedLoggers[$row_logger] ) ) { $row_logger = "SimpleLogger"; } $logger = $this->instantiatedLoggers[$row_logger]["instance"]; return $logger->getLogRowHeaderOutput( $row ); } /** * * * @param array $row * @return string */ private function getLogRowSenderImageOutput( $row ) { $row_logger = $row->logger; $logger = null; $row->context = isset( $row->context ) && is_array( $row->context ) ? $row->context : array(); // Fallback to SimpleLogger if no logger exists for row if ( ! isset( $this->instantiatedLoggers[$row_logger] ) ) { $row_logger = "SimpleLogger"; } $logger = $this->instantiatedLoggers[$row_logger]["instance"]; return $logger->getLogRowSenderImageOutput( $row ); } public function getLogRowDetailsOutput( $row ) { $row_logger = $row->logger; $logger = null; $row->context = isset( $row->context ) && is_array( $row->context ) ? $row->context : array(); // Fallback to SimpleLogger if no logger exists for row if ( ! isset( $this->instantiatedLoggers[$row_logger] ) ) { $row_logger = "SimpleLogger"; } $logger = $this->instantiatedLoggers[$row_logger]["instance"]; return $logger->getLogRowDetailsOutput( $row ); } /** * Works like json_encode, but adds JSON_PRETTY_PRINT if the current php version supports it * i.e. PHP is 5.4.0 or greated * * @param $value array|object|string|whatever that is json_encode'able */ public static function json_encode( $value ) { return version_compare( PHP_VERSION, '5.4.0' ) >= 0 ? json_encode( $value, JSON_PRETTY_PRINT ) : json_encode( $value ); } /** * Returns true if $haystack ends with $needle * @param string $haystack * @param string $needle */ public static function ends_with( $haystack, $needle ) { return $needle === substr( $haystack, -strlen( $needle ) ); } /** * Returns the HTML output for a log row, to be used in the GUI/Activity Feed * * @param array $oneLogRow SimpleHistoryLogQuery array with data from SimpleHistoryLogQuery * @return string */ public function getLogRowHTMLOutput( $oneLogRow, $args ) { $defaults = array( "type" => "overview", // or "single" to include more stuff ); $args = wp_parse_args( $args, $defaults ); $header_html = $this->getLogRowHeaderOutput( $oneLogRow ); $plain_text_html = $this->getLogRowPlainTextOutput( $oneLogRow ); $sender_image_html = $this->getLogRowSenderImageOutput( $oneLogRow ); // Details = for example thumbnail of media $details_html = trim( $this->getLogRowDetailsOutput( $oneLogRow ) ); if ( $details_html ) { $details_html = sprintf( '" . __( "This is potentially useful meta data that a logger has saved.", "simple-history" ) . "
"; $more_details_html .= "%1$s | %2$s |
---|---|
%1$s | %2$s |
%1$s | %2$s |
1 && $count_users_today == 1 && ! $count_other_sources ) { $msg_tmpl .= __( '%1$d events today from one user.', "simple-history" ); } // Multiple events from only users // 2 events today from 2 users. if ( $total_row_count > 1 && $count_users_today == $total_row_count ) { $msg_tmpl .= __( '%1$d events today from %2$d users.', "simple-history" ); } // Multiple events from 1 single user and 1 single other source // 2 events today from 1 user and 1 other source. if ( $total_row_count && 1 == $count_users_today && 1 == $count_other_sources ) { $msg_tmpl .= __( '%1$d events today from one user and one other source.', "simple-history" ); } // Multiple events from multple users but from only 1 single other source // 3 events today from 2 users and 1 other source. if ( $total_row_count > 1 && $count_users_today > 1 && $count_other_sources == 1 ) { $msg_tmpl .= __( '%1$d events today from one user and one other source.', "simple-history" ); } // Multiple events from 1 user but from multiple other source // 3 events today from 1 user and 2 other sources. if ( $total_row_count > 1 && 1 == $count_users_today && $count_other_sources > 1 ) { $msg_tmpl .= __( '%1$d events today from one user and %3$d other sources.', "simple-history" ); } // Multiple events from multiple user and from multiple other sources // 4 events today from 2 users and 2 other sources. if ( $total_row_count > 1 && $count_users_today > 1 && $count_other_sources > 1 ) { $msg_tmpl .= __( '%1$s events today from %2$d users and %3$d other sources.', "simple-history" ); } } // only show stats if we have something to output if ( $msg_tmpl ) { printf( $msg_tmpl, $logResults["total_row_count"], // 1 $count_users_today, // 2 $count_other_sources // 3 ); // Space between texts /* echo " "; // http://playground-root.ep/wp-admin/options-general.php?page=simple_history_settings_menu_slug&selected-tab=stats printf( 'View more stats.', add_query_arg("selected-tab", "stats", menu_page_url(SimpleHistory::SETTINGS_MENU_SLUG, 0)) ); */ } ?>
$args[title] | |||
---|---|---|---|
$args[title_left] | \n"; $r .= "\t$args[title_right] | \n"; $r .= "