return apply_filters( 'wsal_request_logging_enabled', false ); } /** * Get options by prefix (notifications stored in json format). * * @param string $opt_prefix - Prefix. * * @return array|null - Options. * * @since 4.5.0 */ public static function get_notifications_setting( $opt_prefix ) { global $wpdb; $prepared_query = $wpdb->prepare( // phpcs:ignore "SELECT * FROM {$wpdb->base_prefix}options WHERE option_name LIKE %s;", $opt_prefix . '%%' ); return $wpdb->get_results($prepared_query); // phpcs:ignore } /** * Loads notification. * * @param int $id Notification ID. * * @return array|object|void|null * * @since 4.5.0 */ public static function get_notification( $id ) { global $wpdb; $prepared_query = $wpdb->prepare( "SELECT * FROM {$wpdb->options} WHERE option_id = %d LIMIT 1;", $id ); return $wpdb->get_row($prepared_query); // phpcs:ignore } /** * Number of options start with prefix. * * @param string $opt_prefix - Prefix. * * @return int Indicates the number of items. * * @since 4.5.0 */ public static function count_notifications( $opt_prefix ) { global $wpdb; $prepared_query = $wpdb->prepare( "SELECT COUNT(option_id) FROM {$wpdb->options} WHERE option_name LIKE %s;", $opt_prefix . '%%' ); return (int) $wpdb->get_var($prepared_query); // phpcs:ignore } /** * Converts a string (e.g. 'yes' or 'no') to a bool. * * @param string $string String to convert. * * @return bool * * @since 4.5.0 */ public static function string_to_bool( $string ) { return is_bool( $string ) ? $string : ( 'yes' === $string || 1 === $string || 'true' === $string || '1' === $string || 'on' === $string || 'enable' === $string ); } /** * Converts a bool to a 'yes' or 'no'. * * @param bool $bool String to convert. * * @return string * * @since 4.5.0 */ public static function bool_to_string( $bool ) { if ( ! is_bool( $bool ) ) { $bool = self::string_to_bool( $bool ); } return true === $bool ? 'yes' : 'no'; } /** * Retrieves the full path to plugin's working directory. Returns a folder path with a trailing slash. It also * creates the folder unless the $skip_creation parameter is set to true. * * Default path is "{uploads folder}/wp-activity-log/" and can be change only using a constant WSAL_WORKING_DIR_PATH. * * @param string $path Optional path relative to the working directory. * @param bool $skip_creation If true, the folder will not be created. * @param bool $ignore_site If true, there will be no sub-site specific subfolder in multisite context. * * @return string|WP_Error * * @since 4.5.0 */ public static function get_working_dir_path_static( $path = '', $skip_creation = false, $ignore_site = false ) { $result = ''; // Work out the working directory base path. if ( defined( '\WSAL_WORKING_DIR_PATH' ) ) { $result = \trailingslashit( \WSAL_WORKING_DIR_PATH ); } else { $upload_dir = wp_upload_dir( null, false ); if ( is_array( $upload_dir ) && array_key_exists( 'basedir', $upload_dir ) ) { $result = $upload_dir['basedir'] . '/wp-activity-log/'; } elseif ( defined( 'WP_CONTENT_DIR' ) ) { // Fallback in case there is a problem with filesystem. $result = WP_CONTENT_DIR . '/uploads/wp-activity-log/'; } } if ( empty( $result ) ) { // Empty result here means invalid custom path or a problem with WordPress (uploads folder issue or mission WP_CONTENT_DIR). return new \WP_Error( 'wsal_working_dir_base_missing', __( 'The base of WSAL working directory cannot be determined. Custom path is invalid or there is some other issue with your WordPress installation.' ) ); } // Append site specific subfolder in multisite context. if ( ! $ignore_site && WP_Helper::is_multisite() ) { $site_id = get_current_blog_id(); if ( $site_id > 0 ) { $result .= 'sites/' . $site_id . '/'; } } // Append optional path passed as a parameter. if ( $path && is_string( $path ) ) { $result .= $path . '/'; } $result = str_replace( '/', DIRECTORY_SEPARATOR, $result ); if ( ! file_exists( $result ) ) { if ( ! $skip_creation ) { if ( ! wp_mkdir_p( $result ) ) { return new \WP_Error( 'mkdir_failed', sprintf( /* translators: %s: Directory path. */ __( 'Unable to create directory %s. Is its parent directory writable by the server?' ), esc_html( $result ) ) ); } } File_Helper::create_index_file( $result ); File_Helper::create_htaccess_file( $result ); } return $result; } /** * Get WSAL's frontend events option. * * @return array * * @since 4.5.0 */ public static function get_frontend_events() { if ( null === self::$frontend_events ) { // Option defaults. $default = array( 'register' => false, 'login' => false, 'woocommerce' => false, 'gravityforms' => false, ); self::$frontend_events = self::get_option_value( self::FRONT_END_EVENTS_OPTION_NAME, $default ); } // Get the option. return self::$frontend_events; } /** * Set WSAL's frontend events option. * * @param array $value - Option values. * * @return bool * * @since 4.5.0 */ public static function set_frontend_events( $value = array() ) { self::$frontend_events = $value; return self::set_option_value( self::FRONT_END_EVENTS_OPTION_NAME, $value, true ); } /** * Returns the timezone of the WordPress * * @return mixed * * @since 4.5.0 */ public static function get_timezone() { return self::get_option_value( 'timezone', 'wp' ); } /** * Helper method to get the stored setting to determine if milliseconds * appear in the admin list view. This should always be a bool. * * @method get_show_milliseconds * * @since 4.6.0 * * @return bool */ public static function get_show_milliseconds() { return self::get_boolean_option_value( 'show_milliseconds', true ); } /** * Date format based on WordPress date settings. It can be optionally sanitized to get format compatible with * JavaScript date and time picker widgets. * * Note: This function must not be used to display actual date and time values anywhere. For that use function GetDateTimeFormat. * * @param bool $sanitized If true, the format is sanitized for use with JavaScript date and time picker widgets. * * @return string */ public static function get_date_format( $sanitized = false ) { if ( $sanitized ) { return 'Y-m-d'; } return get_option( 'date_format' ); } /** * Time format based on WordPress date settings. It can be optionally sanitized to get format compatible with * JavaScript date and time picker widgets. * * Note: This function must not be used to display actual date and time values anywhere. For that use function GetDateTimeFormat. * * @param bool $sanitize If true, the format is sanitized for use with JavaScript date and time picker widgets. * * @return string */ public static function get_time_format( $sanitize = false ) { $result = get_option( 'time_format' ); if ( $sanitize ) { $search = array( 'a', 'A', 'T', ' ' ); $replace = array( '', '', '', '' ); $result = str_replace( $search, $replace, $result ); } return $result; } /** * Determines datetime format to be displayed in any UI in the plugin (logs in administration, emails, reports, * notifications etc.). * * Note: Format returned by this function is not compatible with JavaScript date and time picker widgets. Use * functions GetTimeFormat and GetDateFormat for those. * * @param bool $line_break - True if line break otherwise false. * @param bool $use_nb_space_for_am_pm - True if non-breakable space should be placed before the AM/PM chars. * * @return string */ public static function get_datetime_format( $line_break = true, $use_nb_space_for_am_pm = true ) { $result = self::get_date_format(); $result .= $line_break ? '<\b\r>' : ' '; $time_format = self::get_time_format(); $has_am_pm = false; $am_pm_fraction = false; $am_pm_pattern = '/(?i)(\s+A)/'; if ( preg_match( $am_pm_pattern, $time_format, $am_pm_matches ) ) { $has_am_pm = true; $am_pm_fraction = $am_pm_matches[0]; $time_format = preg_replace( $am_pm_pattern, '', $time_format ); } // Check if the time format does not have seconds. if ( false === stripos( $time_format, 's' ) ) { $time_format .= ':s'; // Add seconds to time format. } if ( self::get_show_milliseconds() ) { $time_format .= '.$$$'; // Add milliseconds to time format. } if ( $has_am_pm ) { $time_format .= preg_replace( '/\s/', $use_nb_space_for_am_pm ? '&\n\b\s\p;' : ' ', $am_pm_fraction ); } $result .= $time_format; return $result; } /** * Check if current user can perform an action. * * @param string $action Type of action, either 'view' or 'edit'. * * @return bool If user has access or not. * * @since 4.6.0 */ public static function current_user_can( $action ) { return self::user_can( User_Helper::get_current_user(), $action ); } /** * Check if user can perform an action. * * @param int|WP_user $user - User object to check. * @param string $action - Type of action, either 'view' or 'edit'. * * @return bool If user has access or not. * * @since 4.6.0 */ public static function user_can( $user, $action ) { if ( is_int( $user ) ) { $user = get_userdata( $user ); } // By default, the user has no privileges. $result = false; $is_multisite = WP_Helper::is_multisite(); switch ( $action ) { case 'view': if ( ! $is_multisite ) { // Non-multisite piggybacks on the plugin settings access. switch ( self::get_option_value( 'restrict-plugin-settings', 'only_admins' ) ) { case 'only_admins': // Allow access only if the user is and admin. $result = in_array( 'administrator', $user->roles, true ); break; case 'only_me': // Allow access only if the user matches the only user allowed access. $result = (int) self::get_option_value( 'only-me-user-id' ) === $user->ID; break; default: // No other options to allow access here. $result = false; } } else { // Multisite MUST respect the log viewer restriction settings plus also additional users and roles // defined in the extra option. switch ( self::get_option_value( 'restrict-log-viewer', 'only_admins' ) ) { case 'only_me': // Allow access only if the user matches the only user allowed access. $result = ( (int) self::get_option_value( 'only-me-user-id' ) === $user->ID ); break; case 'only_superadmins': // Allow access only for super admins. if ( function_exists( 'is_super_admin' ) && is_super_admin( $user->ID ) ) { $result = true; } break; case 'only_admins': // Allow access only for super admins and admins. $result = in_array( 'administrator', $user->roles, true ) || ( function_exists( 'is_super_admin' ) && is_super_admin( $user->ID ) ); break; default: // Fallback for any other cases would go here. break; } } if ( ! $result ) { // User is still not allowed to view the logs, let's check the additional users and roles // settings. $extra_viewers = Plugin_Settings_Helper::get_allowed_plugin_viewers(); if ( in_array( $user->user_login, $extra_viewers, true ) ) { $result = true; } elseif ( ! empty( array_intersect( $extra_viewers, $user->roles ) ) ) { $result = true; } } break; case 'edit': if ( $is_multisite ) { // No one has access to settings on sub site inside a network. if ( wp_doing_ajax() ) { // AJAX calls are an exception. $result = true; } elseif ( ! is_network_admin() ) { $result = false; break; } } $restrict_plugin_setting = self::get_option_value( 'restrict-plugin-settings', 'only_admins' ); if ( 'only_me' === $restrict_plugin_setting ) { $result = ( (int) self::get_option_value( 'only-me-user-id' ) === $user->ID ); } elseif ( 'only_admins' === $restrict_plugin_setting ) { if ( $is_multisite ) { $result = ( function_exists( 'is_super_admin' ) && is_super_admin( $user->ID ) ); } else { $result = in_array( 'administrator', $user->roles, true ); } } break; default: $result = false; } /* * Filters the user permissions result. * * @since 4.1.3 * * @param bool $result User access flag after applying all internal rules. * @param WP_User $user The user in question. * @param string $action Action to check permissions for. * @return bool */ return apply_filters( 'wsal_user_can', $result, $user, $action ); } /** * Deletes all the settings from the option table of the WP * * @return void * * @since 4.6.0 */ public static function delete_all_settings() { global $wpdb; $wpdb->query( "DELETE FROM $wpdb->options WHERE option_name LIKE 'wsal_%'" ); // phpcs:ignore if ( is_multisite() ) { $wpdb->query( "DELETE FROM $wpdb->sitemeta WHERE meta_key LIKE 'wsal_%'" ); } \wp_cache_flush(); // Remove wsal specific Freemius entry. \delete_site_option( 'fs_wsalp' ); // Ensue entry is fully cleared. \delete_site_option( 'wsal_networkwide_tracker_cpts' ); } /** * Enables or disables time based retention period. * * @param bool $enable If true, time based retention period is enabled. * @param string $new_date - The new pruning date. * @param string $new_unit – New value of pruning unit. * * @since 4.6.0 */ public static function set_pruning_date_settings( $enable, $new_date, $new_unit ) { $was_enabled = self::get_boolean_option_value( 'pruning-date-e', false ); $old_period = self::get_option_value( 'pruning-date', '3 months' ); if ( ! $was_enabled && $enable ) { // The retention period is being enabled. self::set_option_value( 'pruning-date', $new_date ); self::set_option_value( 'pruning-unit', $new_unit ); self::set_boolean_option_value( 'pruning-date-e', $enable ); Alert_Manager::trigger_event( 6052, array( 'new_setting' => 'Delete events older than ' . $old_period, 'previous_setting' => 'Keep all data', ) ); return; } if ( $was_enabled && ! $enable ) { // The retention period is being disabled. self::delete_option_value( 'pruning-date' ); self::delete_option_value( 'pruning-unit' ); self::set_boolean_option_value( 'pruning-date-e', $enable ); Alert_Manager::trigger_event( 6052, array( 'new_setting' => 'Keep all data', 'previous_setting' => 'Delete events older than ' . $old_period, ) ); return; } if ( $enable ) { // The retention period toggle has not changed, we need to check if the actual period changed. if ( $new_date !== $old_period ) { self::set_option_value( 'pruning-date', $new_date ); self::set_option_value( 'pruning-unit', $new_unit ); Alert_Manager::trigger_event( 6052, array( 'new_setting' => 'Delete events older than ' . $new_date, 'previous_setting' => 'Delete events older than ' . $old_period, ) ); } } } /** * Switch to Archive DB if is enabled. * * @since 5.0.0 */ public static function switch_to_archive_db() { if ( ! self::is_archiving_enabled() ) { return; } Connection::enable_archive_mode(); } /** * Updates the database option that disables the database logging. If the logging is going to be enabled, the db * options is deleted. * * @param bool $is_disabled True if the database logging should be disabled. * * @since 4.3.2 */ public static function set_database_logging_disabled( $is_disabled ) { if ( $is_disabled ) { self::set_boolean_option_value( 'db_logging_disabled', $is_disabled ); Alert_Manager::trigger_event( 6327, array( 'EventType' => 'disabled' ) ); } else { self::delete_option_value( 'db_logging_disabled' ); Alert_Manager::trigger_event( 6327, array( 'EventType' => 'enabled' ) ); } } } }