diff --git a/.codeclimate.yml b/.codeclimate.yml index d0e0c94..832e1f0 100644 --- a/.codeclimate.yml +++ b/.codeclimate.yml @@ -23,6 +23,8 @@ engines: enabled: false Controversial/CamelCaseMethodName: enabled: false + Controversial/CamelCasePropertyName: + enabled: false CleanCode/ElseExpression: enabled: false CleanCode/StaticAccess: diff --git a/admin/class-wp-rest-api-log-admin-list-table.php b/admin/class-wp-rest-api-log-admin-list-table.php index 8ba20d3..c52261e 100644 --- a/admin/class-wp-rest-api-log-admin-list-table.php +++ b/admin/class-wp-rest-api-log-admin-list-table.php @@ -28,7 +28,6 @@ public function admin_init() { add_action( 'restrict_manage_posts', array( $this, 'add_method_dropdown' ) ); add_action( 'restrict_manage_posts', array( $this, 'add_status_dropdown' ) ); add_action( 'restrict_manage_posts', array( $this, 'add_source_dropdown' ) ); - } diff --git a/includes/class-wp-rest-api-log-common.php b/includes/class-wp-rest-api-log-common.php index 211ee36..3b588e3 100644 --- a/includes/class-wp-rest-api-log-common.php +++ b/includes/class-wp-rest-api-log-common.php @@ -7,15 +7,9 @@ class WP_REST_API_Log_Common { const PLUGIN_NAME = 'wp-rest-api-log'; - const VERSION = '2016-12-05-01'; + const VERSION = '2017-01-16-01'; const TEXT_DOMAIN = 'wp-rest-api-log'; - - public function plugins_loaded() { - - } - - static public function current_milliseconds() { return self::microtime_to_milliseconds( microtime() ); } diff --git a/includes/class-wp-rest-api-log-controller.php b/includes/class-wp-rest-api-log-controller.php index 1ee6da7..ec87f7f 100644 --- a/includes/class-wp-rest-api-log-controller.php +++ b/includes/class-wp-rest-api-log-controller.php @@ -7,17 +7,17 @@ class WP_REST_API_Log_Controller { - public function plugins_loaded() { - add_action( 'rest_api_init', array( $this, 'register_rest_routes' ) ); + static function plugins_loaded() { + add_action( 'rest_api_init', array( __CLASS__, 'register_rest_routes' ) ); } - public function register_rest_routes() { + static public function register_rest_routes() { register_rest_route( WP_REST_API_Log_Common::PLUGIN_NAME, '/entries', array( 'methods' => array( WP_REST_Server::READABLE ), - 'callback' => array( $this, 'get_items' ), - 'permission_callback' => array( $this, 'get_permissions_check' ), + 'callback' => array( __CLASS__, 'get_items' ), + 'permission_callback' => array( __CLASS__, 'get_permissions_check' ), 'args' => array( 'from' => array( 'default' => '', @@ -66,12 +66,12 @@ public function register_rest_routes() { register_rest_route( WP_REST_API_Log_Common::PLUGIN_NAME, '/entry/(?P[\d]+)', array( 'methods' => array( WP_REST_Server::READABLE ), - 'callback' => array( $this, 'get_item' ), - 'permission_callback' => array( $this, 'get_permissions_check' ), + 'callback' => array( __CLASS__, 'get_item' ), + 'permission_callback' => array( __CLASS__, 'get_permissions_check' ), 'args' => array( 'id' => array( 'sanitize_callback' => 'absint', - 'validate_callback' => array( $this, 'validate_entry_id' ), + 'validate_callback' => array( __CLASS__, 'validate_entry_id' ), 'default' => 0, ), ), @@ -80,8 +80,8 @@ public function register_rest_routes() { register_rest_route( WP_REST_API_Log_Common::PLUGIN_NAME, '/entry', array( 'methods' => array( WP_REST_Server::DELETABLE ), - 'callback' => array( $this, 'delete_items' ), - 'permission_callback' => array( $this, 'delete_items_permissions_check' ), + 'callback' => array( __CLASS__, 'delete_items' ), + 'permission_callback' => array( __CLASS__, 'delete_items_permissions_check' ), 'args' => array( // TODO refator delete, this won't work with $_REQUESTs 'older-than-seconds' => array( 'sanitize_callback' => 'absint', // TODO add validate callback @@ -93,14 +93,14 @@ public function register_rest_routes() { register_rest_route( WP_REST_API_Log_Common::PLUGIN_NAME, '/routes', array( 'methods' => array( WP_REST_Server::READABLE ), - 'callback' => array( $this, 'get_routes' ), - 'permission_callback' => array( $this, 'get_permissions_check' ), + 'callback' => array( __CLASS__, 'get_routes' ), + 'permission_callback' => array( __CLASS__, 'get_permissions_check' ), ) ); } - public function get_items( WP_REST_Request $request ) { + static public function get_items( WP_REST_Request $request ) { $args = array( 'id' => $request['id'], @@ -125,7 +125,7 @@ public function get_items( WP_REST_Request $request ) { } - public function get_item( WP_REST_Request $request ) { + static public function get_item( WP_REST_Request $request ) { $post = get_post( $request['id'] ); $entry = new WP_REST_API_Log_Entry( $args['id'] ); @@ -139,7 +139,7 @@ public function get_item( WP_REST_Request $request ) { } - public function validate_entry_id( $id ) { + static public function validate_entry_id( $id ) { if ( $id < 1 ) { return new WP_Error( 'invalid_entry_id', sprintf( __( 'Invalid REST API Log ID %d.', 'wp-rest-api-log' ), $args['id'] ), array( 'status' => 404 ) ); } else { @@ -147,7 +147,7 @@ public function validate_entry_id( $id ) { } } - public function get_routes( WP_REST_Request $request ) { + static public function get_routes( WP_REST_Request $request ) { global $wpdb; @@ -161,7 +161,7 @@ public function get_routes( WP_REST_Request $request ) { } - public function delete_items( WP_REST_Request $request ) { + static public function delete_items( WP_REST_Request $request ) { // TODO refactor $args = array( 'older_than_seconds' => $request['older-than-seconds'], @@ -172,12 +172,12 @@ public function delete_items( WP_REST_Request $request ) { } - public function get_permissions_check() { + static public function get_permissions_check() { return apply_filters( WP_REST_API_Log_Common::PLUGIN_NAME . '-can-view-entries', current_user_can( 'read_' . WP_REST_API_Log_DB::POST_TYPE ) ); } - public function delete_items_permissions_check() { + static public function delete_items_permissions_check() { return apply_filters( WP_REST_API_Log_Common::PLUGIN_NAME . '-can-delete-entries', current_user_can( 'delete_' . WP_REST_API_Log_DB::POST_TYPE ) ); } diff --git a/includes/class-wp-rest-api-log-filters.php b/includes/class-wp-rest-api-log-filters.php new file mode 100644 index 0000000..653f979 --- /dev/null +++ b/includes/class-wp-rest-api-log-filters.php @@ -0,0 +1,108 @@ + __( 'All', 'wp-rest-api-log' ), + 'log_matches' => __( 'Only Matching Filters', 'wp-rest-api-log' ), + 'exclude_matches' => __( 'Exclude Matching Filters', 'wp-rest-api-log' ), + ); + } + + + /** + * Converts a route filter into regex pattern. + * + * @param string $route_filter Route filter, may include * wildcards. + * @return string + */ + public static function route_to_regex( $route_filter ) { + + if ( ! empty( $route_filter ) ) { + + // If it starts with a carat, treat it as regex and + // make no changes. + if ( '^' === substr( $route_filter, 0, 1 ) ) { + return $route_filter; + } else { + + // Replace wildcard with regex wildcard. + $route_filter = str_replace( '*', '.*', $route_filter ); + + // Add the start of the match. + $route_filter = '^' . $route_filter; + + // Add the end of the match. + $route_filter .= '$'; + + // Convert backslash to literals. + $route_filter = str_replace( '/', "\/", $route_filter ); + } + + } + + return $route_filter; + } + + /** + * Determines if the supplied route can be logged. + * + * @param string $route Route (ex: /wp/v2). + * @return bool + */ + static public function can_log_route( $route ) { + + // Get the filter mode. + $route_logging_mode = apply_filters( 'wp-rest-api-log-setting-get', 'routes', 'route-log-matching-mode' ); + + // If no logging mode is set, we can log the route. + if ( empty( $route_logging_mode ) ) { + return true; + } + + // Get the route filters. + $route_filters = apply_filters( 'wp-rest-api-log-setting-get', 'routes', 'route-filters' ); + $route_filters = array_values( array_map( 'trim', explode( "\n", $route_filters ) ) ); + + // If we're set to exclude matching filters, but we have no filters, + // then the route can be logged + if ( 'exclude_matches' === $route_logging_mode && empty( $route_filters ) ) { + return true; + } + + // Loop through the filters and apply each one to the route. + foreach( $route_filters as $route_filter ) { + if ( empty( $route_filter ) ) { + continue; + } + + $regex = self::route_to_regex( $route_filter ); + + //preg_match() returns 1 if the pattern matches given subject, + //0 if it does not, or FALSE if an error occurred. + $match = preg_match( '/' . $regex . '/', $route ); + + // We can log this if the mode is set to log only matches. + if ( 1 === $match && 'log_matches' === $route_logging_mode ) { + return true; + } + + // We cannot log this if the mode is set to exclude matches. + if ( 1 === $match && 'exclude_matches' === $route_logging_mode ) { + return false; + } + } + + // At this point, we can only log the match if we're set to exclude + // mode and the loop above did not find a match. + return 'exclude_matches' === $route_logging_mode; + } +} diff --git a/includes/class-wp-rest-api-log-i18n.php b/includes/class-wp-rest-api-log-i18n.php index cd7309e..9eb1696 100644 --- a/includes/class-wp-rest-api-log-i18n.php +++ b/includes/class-wp-rest-api-log-i18n.php @@ -7,17 +7,13 @@ class WP_REST_API_Log_i18n { - public function plugins_loaded() { + static public function plugins_loaded() { load_plugin_textdomain( - WP_REST_API_Log_Common::TEXT_DOMAIN, + 'wp-rest-api-log', false, dirname( dirname( plugin_basename( __FILE__ ) ) ) . '/languages/' ); - } - - - } // end class - + } } diff --git a/includes/class-wp-rest-api-log-post-type.php b/includes/class-wp-rest-api-log-post-type.php index f1cadc0..eda0110 100644 --- a/includes/class-wp-rest-api-log-post-type.php +++ b/includes/class-wp-rest-api-log-post-type.php @@ -2,26 +2,25 @@ if ( ! defined( 'ABSPATH' ) ) die( 'restricted access' ); -if ( ! class_exists( 'WP_REST_API_Log_Post_type' ) ) { +if ( ! class_exists( 'WP_REST_API_Log_Post_Type' ) ) { - class WP_REST_API_Log_Post_type { + class WP_REST_API_Log_Post_Type { - public function plugins_loaded() { - add_action( 'init', array( $this, 'register_custom_post_types' ) ); - add_action( 'init', array( $this, 'register_custom_taxonomies' ) ); + static public function plugins_loaded() { + add_action( 'init', array( __CLASS__, 'register_custom_post_types' ) ); + add_action( 'init', array( __CLASS__, 'register_custom_taxonomies' ) ); } - public function register_custom_post_types() { + static public function register_custom_post_types() { - $args = $this->get_post_type_args(); + $args = self::get_post_type_args(); register_post_type( WP_REST_API_Log_DB::POST_TYPE, $args ); - } - public function get_post_type_labels() { + static public function get_post_type_labels() { $labels = array( 'name' => esc_html__( 'REST API Log Entries', 'ms-research' ), @@ -41,10 +40,10 @@ public function get_post_type_labels() { } - public function get_post_type_args() { + static public function get_post_type_args() { $args = array( - 'labels' => $this->get_post_type_labels(), + 'labels' => self::get_post_type_labels(), 'show_in_rest' => true, 'rest_base' => WP_REST_API_Log_DB::POST_TYPE, // allows the CPT to show up in the native API 'hierarchical' => false, @@ -75,7 +74,7 @@ public function get_post_type_args() { } - public function register_custom_taxonomies() { + static public function register_custom_taxonomies() { // HTTP Method @@ -112,12 +111,6 @@ public function register_custom_taxonomies() { $args['labels']['singular_name'] = __( 'Log Sources', 'wp-rest-api-log' ); register_taxonomy( WP_REST_API_Log_DB::TAXONOMY_SOURCE, array( WP_REST_API_Log_DB::POST_TYPE ), $args ); - - // namespace? - } - - } - -} \ No newline at end of file +} diff --git a/includes/class-wp-rest-api-log.php b/includes/class-wp-rest-api-log.php index 62f72eb..b9ac47e 100644 --- a/includes/class-wp-rest-api-log.php +++ b/includes/class-wp-rest-api-log.php @@ -12,28 +12,19 @@ class WP_REST_API_Log { * * @return void */ - public function plugins_loaded() { + static public function plugins_loaded() { - // filter that is called by the REST API right before it sends a response - add_filter( 'rest_pre_serve_request', array( $this, 'log_rest_api_response' ), 9999, 4 ); + // Filter that is called by the REST API right before it sends a response + add_filter( 'rest_pre_serve_request', array( __CLASS__, 'log_rest_api_response' ), 9999, 4 ); - // an example of disabling logging for specific requests - add_filter( 'wp-rest-api-log-bypass-insert', function( $bypass_insert, $result, $request, $rest_server ) { + // Disabling logging for specific requests. + add_filter( 'wp-rest-api-log-bypass-insert', array( __CLASS__, 'bypass_common_routes' ), 10, 4 ); - $ignore_routes = array( - '/wp-rest-api-log', - '/oembed/1.0/embed', - ); + // Create cron job. + add_action( 'admin_init', array( __CLASS__, 'create_purge_cron' ) ); - foreach ( $ignore_routes as $route ) { - if ( stripos( $request->get_route(), $route ) !== false ) { - return true; - } - } - - return $bypass_insert; - - }, 10, 4 ); + // Handler for cron job. + add_action( 'wp-rest-api-log-purge-old-records', array( __CLASS__, 'purge_old_records' ) ); // for local development @@ -50,9 +41,6 @@ public function plugins_loaded() { // } ); - add_action( 'admin_init', array( $this, 'create_purge_cron' ) ); - add_action( 'wp-rest-api-log-purge-old-records', array( $this, 'purge_old_records' ) ); - } @@ -65,7 +53,7 @@ public function plugins_loaded() { * @param object $rest_server REST API server. * @return bool $served */ - public function log_rest_api_response( $served, $result, $request, $rest_server ) { + static public function log_rest_api_response( $served, $result, $request, $rest_server ) { // don't log anything if logging is not enabled $logging_enabled = apply_filters( WP_REST_API_Log_Common::PLUGIN_NAME . '-setting-is-enabled', @@ -79,16 +67,29 @@ public function log_rest_api_response( $served, $result, $request, $rest_server } - // allow specific requests to not be logged + // Allow specific requests to not be logged $bypass_insert = apply_filters( WP_REST_API_Log_Common::PLUGIN_NAME . '-bypass-insert', false, $result, $request, $rest_server ); if ( $bypass_insert ) { return $served; } + // Determine if this route should be logged based on route filters. + $route = $request->get_route(); + $can_log_route = WP_REST_API_Log_Filters::can_log_route( $route ); + + // Allow this to be filtered. + $can_log_route = apply_filters( 'wp-rest-api-log-can-log-route', $can_log_route, $route, $request, $result, $rest_server ); + + + // Exit out if we can't log this route. + if ( ! $can_log_route ) { + return $served; + } + $args = array( 'ip_address' => filter_input( INPUT_SERVER, 'REMOTE_ADDR', FILTER_SANITIZE_STRING ), 'http_x_forwarded_for' => filter_input( INPUT_SERVER, 'HTTP_X_FORWARDED_FOR', FILTER_SANITIZE_STRING ), - 'route' => $request->get_route(), + 'route' => $route, 'method' => $request->get_method(), 'status' => $result->get_status(), 'request' => array( @@ -99,7 +100,7 @@ public function log_rest_api_response( $served, $result, $request, $rest_server ), 'response' => array( 'body' => $result, - 'headers' => $this->get_response_headers( $result ), + 'headers' => self::get_response_headers( $result ), ), ); @@ -110,7 +111,7 @@ public function log_rest_api_response( $served, $result, $request, $rest_server } - private function get_response_headers( $result ) { + static public function get_response_headers( $result ) { // headers_list returns an array of headers like this: Content-Type: application/json; // we want a key/value array if ( function_exists( 'headers_list' ) ) { @@ -134,13 +135,13 @@ private function get_response_headers( $result ) { } } - public function create_purge_cron() { + static public function create_purge_cron() { if ( ! wp_next_scheduled( 'wp-rest-api-log-purge-old-records' ) ) { wp_schedule_event( time() + 60, 'hourly', 'wp-rest-api-log-purge-old-records' ); } } - public function purge_old_records( $days_old = false, $dry_run = false ) { + static public function purge_old_records( $days_old = false, $dry_run = false ) { if ( empty( $days_old ) ) { $days_old = WP_REST_API_Log_Settings_General::setting_get( 'general', 'purge-days' ); @@ -175,6 +176,28 @@ public function purge_old_records( $days_old = false, $dry_run = false ) { } + static public function bypass_common_routes( $bypass_insert, $result, $request, $rest_server ) { + + // Ignore our own plugin. + $ignore_routes = array( + '/wp-rest-api-log', + ); + + // See if the oembed route is ignored. + if ( '1' === apply_filters( 'wp-rest-api-log-setting-get', 'routes', 'ignore-core-oembed' ) ) { + $ignore_routes[] = '/oembed/1.0/embed'; + } + + foreach ( $ignore_routes as $route ) { + if ( stripos( $request->get_route(), $route ) !== false ) { + return true; + } + } + + return $bypass_insert; + + } + } // end class } diff --git a/includes/settings/class-wp-rest-api-log-settings-base.php b/includes/settings/class-wp-rest-api-log-settings-base.php index 012ff7b..c19dd52 100644 --- a/includes/settings/class-wp-rest-api-log-settings-base.php +++ b/includes/settings/class-wp-rest-api-log-settings-base.php @@ -182,24 +182,35 @@ static public function settings_check_radio_list( $args ) { static public function settings_textarea( $args ) { - extract( wp_parse_args( $args, + $args = wp_parse_args( $args, array( 'name' => '', 'key' => '', 'rows' => 10, 'cols' => 40, 'after' => '', - ) - ) ); + ) + ); + + $name = $args['name']; + $key = $args['key']; + $rows = $args['rows']; + $cols = $args['cols']; + $after = $args['after']; $option = get_option( $key ); $value = isset( $option[$name] ) ? esc_attr( $option[$name] ) : ''; - echo "
"; + printf( '
', + esc_attr( $name ), + esc_attr( "{$key}[{$name}]" ), + esc_attr( $rows ), + esc_attr( $cols ), + $value + ); self::output_after( $after ); - } @@ -232,7 +243,7 @@ static public function settings_yes_no( $args ) { static public function output_after( $after ) { if ( ! empty( $after ) ) { - echo '

' . wp_kses_post( $after ) . '

'; + echo wp_kses_post( $after ); } } diff --git a/includes/settings/class-wp-rest-api-log-settings-general.php b/includes/settings/class-wp-rest-api-log-settings-general.php index b8455ad..580c3ec 100644 --- a/includes/settings/class-wp-rest-api-log-settings-general.php +++ b/includes/settings/class-wp-rest-api-log-settings-general.php @@ -40,22 +40,22 @@ static public function register_general_settings() { add_settings_field( 'logging-enabled', __( 'Enabled', 'wp-rest-api-log' ), array( __CLASS__, 'settings_yes_no' ), $key, $section, array( 'key' => $key, 'name' => 'logging-enabled', 'after' => '' ) ); - add_settings_field( 'purge-days', __( 'Days to Retain Old Entries', 'rest-api-toolbox' ), array( __CLASS__, 'settings_input' ), $key, $section, + add_settings_field( 'purge-days', __( 'Days to Retain Old Entries', 'wp-rest-api-log' ), array( __CLASS__, 'settings_input' ), $key, $section, array( 'key' => $key, 'name' => 'purge-days', - 'after' => __( 'Entries older than this will be deleted, leave blank to keep all entries', 'rest-api-toolbox' ), + 'after' => '

' . __( 'Entries older than this will be deleted, leave blank to keep all entries', 'wp-rest-api-log' ) . '

', 'size' => 3, 'maxlength' => 3, ) ); - add_settings_field( 'ip-address-display', __( 'IP Address Display', 'rest-api-toolbox' ), array( __CLASS__, 'settings_check_radio_list' ), $key, $section, + add_settings_field( 'ip-address-display', __( 'IP Address Display', 'wp-rest-api-log' ), array( __CLASS__, 'settings_check_radio_list' ), $key, $section, array( 'key' => $key, 'name' => 'ip-address-display', 'type' => 'radio', - 'after' => __( 'Sets the IP address displayed in the list of log entries.', 'rest-api-toolbox' ), + 'after' => '

' . __( 'Sets the IP address displayed in the list of log entries.', 'wp-rest-api-log' ) . '

', 'items' => array( 'ip_address' => __( 'IP Address', 'wp-rest-api-log' ), 'http_x_forwarded_for' => __( 'HTTP X Forwarded For', 'wp-rest-api-log' ), diff --git a/includes/settings/class-wp-rest-api-log-settings-routes.php b/includes/settings/class-wp-rest-api-log-settings-routes.php index 21a98c8..6eb4896 100644 --- a/includes/settings/class-wp-rest-api-log-settings-routes.php +++ b/includes/settings/class-wp-rest-api-log-settings-routes.php @@ -8,27 +8,46 @@ class WP_REST_API_Log_Settings_Routes extends WP_REST_API_Log_Settings_Base { static $settings_key = 'wp-rest-api-log-settings-routes'; - + /** + * Hooks up WorPress actions and filters. + * + * @return void + */ static public function plugins_loaded() { - add_action( 'admin_init', array( __CLASS__, 'register_general_settings' ) ); + add_action( 'admin_init', array( __CLASS__, 'register_routes_settings' ) ); add_filter( 'wp-rest-api-log-settings-tabs', array( __CLASS__, 'add_tab') ); } - + /** + * Adds a Routes tab. + * + * @param array $tabs List of tabs. + * @return array + */ static public function add_tab( $tabs ) { $tabs[ self::$settings_key ] = __( 'Routes', 'wp-rest-api-log' ); return $tabs; } - + /** + * Gets the default Routes settings. + * + * @return array + */ static public function get_default_settings() { return array( - 'ignore-core-oembed' => '1', + 'ignore-core-oembed' => '1', + 'route-log-matching-mode' => '', + 'route-filters' => '', ); } - - static public function register_general_settings() { + /** + * Registers settings sections and fields for the Routes tab. + * + * @return void + */ + static public function register_routes_settings() { $key = self::$settings_key; register_setting( $key, $key, array( __CLASS__, 'sanitize_settings') ); @@ -37,14 +56,75 @@ static public function register_general_settings() { add_settings_section( $section, '', null, $key ); - add_settings_field( 'ignore-core-oembed', __( 'Ignore core oEmbed', 'wp-rest-api-log' ), array( __CLASS__, 'settings_yes_no' ), $key, $section, - array( 'key' => $key, 'name' => 'ignore-core-oembed', 'after' => 'Built-in /oembed/1.0/embed route' ) ); - + add_settings_field( + 'ignore-core-oembed', + __( 'Ignore core oEmbed', 'wp-rest-api-log' ), + array( __CLASS__, 'settings_yes_no' ), + $key, + $section, + array( + 'key' => $key, + 'name' => 'ignore-core-oembed', + 'after' => '

' . __( 'Built-in /oembed/1.0/embed route', 'wp-rest-api-log' ) . '

', + ) + ); + + add_settings_field( + 'route-log-matching-mode', + __( 'Route Logging Mode', 'wp-rest-api-log' ), + array( __CLASS__, 'settings_check_radio_list' ), + $key, + $section, + array( + 'key' => $key, + 'name' => 'route-log-matching-mode', + 'type' => 'radio', + 'items' => WP_REST_API_Log_Filters::filter_modes(), + 'default' => array( '' ), + ) + ); + + add_settings_field( + 'route-filters', + __( 'Route Filters', 'wp-rest-api-log' ), + array( __CLASS__, 'settings_textarea' ), + $key, + $section, + array( + 'key' => $key, + 'name' => 'route-filters', + 'after' => ' +

' . __( 'One route per line, examples', 'wp-rest-api-log' ) . '

+ +

' . __( 'Regex matches must start with ^', 'wp-rest-api-log' ) . '

', + ) + ); } - + /** + * Sanitzes the route settings. + * + * @param array $settings List of settings. + * @return array + */ static public function sanitize_settings( $settings ) { + // Sanitize string fields. + $string_fields = array( + 'ignore-core-oembed', + 'route-log-matching-mode', + ); + + foreach( $string_fields as $field ) { + if ( isset( $settings[ $field ] ) ) { + $settings[ $field ] = filter_var( $settings[ $field ], FILTER_SANITIZE_STRING ); + } + } + return $settings; } diff --git a/includes/settings/class-wp-rest-api-log-settings.php b/includes/settings/class-wp-rest-api-log-settings.php index f0d526b..6a5c710 100644 --- a/includes/settings/class-wp-rest-api-log-settings.php +++ b/includes/settings/class-wp-rest-api-log-settings.php @@ -6,7 +6,11 @@ class WP_REST_API_Log_Settings extends WP_REST_API_Log_Settings_Base { - + /** + * Wire up WordPress hooks and filters. + * + * @return void + */ static public function plugins_loaded() { // admin menus add_action( 'admin_menu', array( __CLASS__, 'admin_menu' ) ); @@ -15,10 +19,14 @@ static public function plugins_loaded() { // filters to get plugin settings add_filter( 'wp-rest-api-log-setting-is-enabled', array( __CLASS__, 'filter_setting_is_enabled' ), 10, 3 ); add_filter( 'wp-rest-api-log-setting-get', array( __CLASS__, 'setting_get' ), 10, 3 ); - } + /** + * Displays an admin notice if the plugin was just activated. + * + * @return void + */ static public function activation_admin_notice() { if ( '1' === get_option( 'wp-rest-api-log-plugin-activated' ) ) { ?> @@ -37,20 +45,38 @@ static public function deactivation_hook() { // placeholder in case we need deactivation code } - - + /** + * Creates default settings for the plugin. + * + * @return void + */ static public function create_default_settings() { // create default settings add_option( WP_REST_API_Log_Settings_General::$settings_key, WP_REST_API_Log_Settings_General::get_default_settings(), '', $autoload = 'no' ); add_option( WP_REST_API_Log_Settings_Routes::$settings_key, WP_REST_API_Log_Settings_Routes::get_default_settings(), '', $autoload = 'no' ); } - + /** + * Registers the REST API Log admin menu, + * + * @return void + */ static public function admin_menu() { - add_options_page( 'REST API Log ' . __( 'Settings' ), __( 'REST API Log', 'wp-rest-api-log' ), 'manage_options', self::$settings_page, array( __CLASS__, 'options_page' ), 30 ); + add_options_page( + __( 'REST API Log Settings', 'wp-rest-api-log' ), + __( 'REST API Log', 'wp-rest-api-log' ), + 'manage_options', + self::$settings_page, + array( __CLASS__, 'options_page' ), + 30 + ); } - + /** + * Displays the settings page for the plugin. + * + * @return void + */ static public function options_page() { $tab = self::current_tab(); ?> diff --git a/readme.md b/readme.md index 99831ea..b27b53f 100644 --- a/readme.md +++ b/readme.md @@ -1,10 +1,10 @@ # REST API Log # **Contributors:** gungeekatx -**Tags:** wp rest api, rest api, wp api, api, json, log, logging +**Tags:** wp rest api, rest api, wp api, api, json, json api, log, logging, elasticpress, elasticsearch **Donate link:** https://github.com/petenelson/wp-rest-api-log **Requires at least:** 4.4 **Tested up to:** 4.7 -**Stable tag:** 1.3.0 +**Stable tag:** 1.4.0 **License:** GPLv2 or later **License URI:** http://www.gnu.org/licenses/gpl-2.0.html @@ -44,6 +44,9 @@ Roadmap ## Changelog ## +### v1.4.0 January 23, 2017 ### +* Added the ability to filter routes for logging, either include or exclude specific routes. + ### v1.3.0 December 5, 2016 ### * Added support for logging HTTP_X_FORWARDED_FOR, useful for servers behind a proxy or load balancer. * Changed plugin name to REST API Log @@ -81,6 +84,9 @@ Roadmap ## Upgrade Notice ## +### v1.4.0 January 23, 2017 ### +* Added the ability to filter routes for logging, either include or exclude specific routes. + ### v1.3.0 December 5, 2016 ### * Added support for logging HTTP_X_FORWARDED_FOR, useful for servers behind a proxy or load balancer. * Changed plugin name to REST API Log diff --git a/readme.txt b/readme.txt index 066ed31..399d71e 100644 --- a/readme.txt +++ b/readme.txt @@ -1,10 +1,10 @@ === REST API Log === Contributors: gungeekatx -Tags: wp rest api, rest api, wp api, api, json, log, logging +Tags: wp rest api, rest api, wp api, api, json, json api, log, logging, elasticpress, elasticsearch Donate link: https://github.com/petenelson/wp-rest-api-log Requires at least: 4.4 Tested up to: 4.7 -Stable tag: 1.3.0 +Stable tag: 1.4.0 License: GPLv2 or later License URI: http://www.gnu.org/licenses/gpl-2.0.html @@ -40,6 +40,9 @@ Roadmap == Changelog == += v1.4.0 January 23, 2017 = +* Added the ability to filter routes for logging, either include or exclude specific routes. + = v1.3.0 December 5, 2016 = * Added support for logging HTTP_X_FORWARDED_FOR, useful for servers behind a proxy or load balancer. * Changed plugin name to REST API Log @@ -77,6 +80,9 @@ Roadmap == Upgrade Notice == += v1.4.0 January 23, 2017 = +* Added the ability to filter routes for logging, either include or exclude specific routes. + = v1.3.0 December 5, 2016 = * Added support for logging HTTP_X_FORWARDED_FOR, useful for servers behind a proxy or load balancer. * Changed plugin name to REST API Log diff --git a/tests/test-filters.php b/tests/test-filters.php new file mode 100644 index 0000000..408d565 --- /dev/null +++ b/tests/test-filters.php @@ -0,0 +1,136 @@ +assertEquals( "^\/wp\/v2$", $route_regex ); + + // Wildcard matches. + $route_regex = WP_REST_API_Log_Filters::route_to_regex( '/wp/*' ); + $this->assertEquals( "^\/wp\/.*$", $route_regex ); + + $route_regex = WP_REST_API_Log_Filters::route_to_regex( '/wp/v2/*' ); + $this->assertEquals( "^\/wp\/v2\/.*$", $route_regex, '/wp/v2/*' ); + + // Regex should have no changes. + $route_regex = WP_REST_API_Log_Filters::route_to_regex( '^/wp/v2/.*' ); + $this->assertEquals( '^/wp/v2/.*', $route_regex ); + + // This is not treated as regex, so it get mangled. + $route_regex = WP_REST_API_Log_Filters::route_to_regex( '.*/wp/v2/$' ); + $this->assertEquals( "^..*\/wp\/v2\/$$", $route_regex ); + } + + public function test_filter_modes() { + $modes = WP_REST_API_Log_Filters::filter_modes(); + $this->assertArrayHasKey( '', $modes ); + $this->assertArrayHasKey( 'log_matches', $modes ); + $this->assertArrayHasKey( 'exclude_matches', $modes ); + } + + public function test_route_logging_all_routes() { + + $option_key = 'wp-rest-api-log-settings-routes'; + + // Set the options to log everything. + $option_value = array( + 'route-log-matching-mode' => '', + ); + + update_option( 'wp-rest-api-log-settings-routes', $option_value ); + + // Make sure we can log any route. + $this->assertTrue( WP_REST_API_Log_Filters::can_log_route( '/wp/v2' ) ); + $this->assertTrue( WP_REST_API_Log_Filters::can_log_route( '/wp/v2/posts' ) ); + $this->assertTrue( WP_REST_API_Log_Filters::can_log_route( '/wp/v2/users' ) ); + + } + + + public function test_route_logging_only_matched_routes() { + + $option_key = 'wp-rest-api-log-settings-routes'; + + // Set the options to log only matching routes. + $option_value = array( + 'route-log-matching-mode' => 'log_matches', + 'route-filters' => +"/wp/v2 +/route/wildcard* +^\/route\/regex-exact$ +^\/route\/regex-wildcard.*$" + ); + + update_option( 'wp-rest-api-log-settings-routes', $option_value ); + + // Exact match. + $this->assertTrue( WP_REST_API_Log_Filters::can_log_route( '/wp/v2' ), '/wp/v2' ); + + // Basic wildcard. + $this->assertTrue( WP_REST_API_Log_Filters::can_log_route( '/route/wildcard-route' ), '/route/wildcard-route' ); + + // Exact regex + $this->assertTrue( WP_REST_API_Log_Filters::can_log_route( '/route/regex-exact' ), '/route/regex-exact' ); + + // Wildcard regex + $this->assertTrue( WP_REST_API_Log_Filters::can_log_route( '/route/regex-wildcard/test' ), '/route/regex-wildcard/test' ); + + // Test non-matching routes. + $this->assertFalse( WP_REST_API_Log_Filters::can_log_route( '/some/route' ), '/some/route' ); + $this->assertFalse( WP_REST_API_Log_Filters::can_log_route( '/route/wildercard' ), '/route/wildercard' ); + $this->assertFalse( WP_REST_API_Log_Filters::can_log_route( '/route/exact-regex' ), '/route/exact-regex' ); + $this->assertFalse( WP_REST_API_Log_Filters::can_log_route( '/route/regexwildcard' ), '/route/regexwildcard' ); + $this->assertFalse( WP_REST_API_Log_Filters::can_log_route( '/wp/v2/posts' ), '/wp/v2/posts' ); + + } + + public function test_route_logging_excluded_matched_routes() { + + $option_key = 'wp-rest-api-log-settings-routes'; + + // Set the options to log only matching routes. + $option_value = array( + 'route-log-matching-mode' => 'exclude_matches', + 'route-filters' => +"/wp/v2 +/route/wildcard* +^\/route\/regex-exact$ +^\/route\/regex-wildcard.*$" + ); + + update_option( 'wp-rest-api-log-settings-routes', $option_value ); + + // Exact match. + $this->assertFalse( WP_REST_API_Log_Filters::can_log_route( '/wp/v2' ), '/wp/v2' ); + + // Basic wildcard. + $this->assertFalse( WP_REST_API_Log_Filters::can_log_route( '/route/wildcard-route' ), '/route/wildcard-route' ); + + // Exact regex + $this->assertFalse( WP_REST_API_Log_Filters::can_log_route( '/route/regex-exact' ), '/route/regex-exact' ); + + // Wildcard regex + $this->assertFalse( WP_REST_API_Log_Filters::can_log_route( '/route/regex-wildcard/test' ), '/route/regex-wildcard/test' ); + + // Test non-matching routes, should all be logged. + $this->assertTrue( WP_REST_API_Log_Filters::can_log_route( '/some/route' ), '/some/route' ); + $this->assertTrue( WP_REST_API_Log_Filters::can_log_route( '/route/wildercard' ), '/route/wildercard' ); + $this->assertTrue( WP_REST_API_Log_Filters::can_log_route( '/route/exact-regex' ), '/route/exact-regex' ); + $this->assertTrue( WP_REST_API_Log_Filters::can_log_route( '/route/regexwildcard' ), '/route/regexwildcard' ); + $this->assertTrue( WP_REST_API_Log_Filters::can_log_route( '/wp/v2/posts' ), '/wp/v2/posts' ); + + } + +} diff --git a/wp-rest-api-log.php b/wp-rest-api-log.php index 3d5d8d5..463ef00 100644 --- a/wp-rest-api-log.php +++ b/wp-rest-api-log.php @@ -3,7 +3,7 @@ * Plugin Name: REST API Log * Description: Logs requests and responses for the REST API * Author: Pete Nelson - * Version: 1.3.0 + * Version: 1.4.0 * Plugin URI: https://github.com/petenelson/wp-rest-api-log * Text Domain: wp-rest-api-log * Domain Path: /languages @@ -24,6 +24,14 @@ define( 'WP_REST_API_LOG_PATH', trailingslashit( plugin_dir_path( __FILE__ ) ) ); } +if ( ! defined( 'WP_REST_API_LOG_FILE' ) ) { + define( 'WP_REST_API_LOG_FILE', __FILE__ ); +} + +if ( ! defined( 'WP_REST_API_LOG_BASENAME' ) ) { + define( 'WP_REST_API_LOG_BASENAME', plugin_basename( WP_REST_API_LOG_FILE ) ); +} + $plugin_class_file = 'wp-rest-api-log'; $includes = array( @@ -40,6 +48,7 @@ 'includes/class-' . $plugin_class_file . '-delete-response.php', 'includes/class-' . $plugin_class_file . '-routes-response.php', 'includes/class-' . $plugin_class_file . '-elasticpress.php', + 'includes/class-' . $plugin_class_file . '-filters.php', 'includes/class-' . $plugin_class_file . '.php', 'includes/settings/class-' . $plugin_class_file . '-settings-base.php', 'includes/settings/class-' . $plugin_class_file . '-settings-general.php', @@ -59,6 +68,7 @@ $class_base . '_Post_Type', $class_base . '_i18n', $class_base . '_Controller', + $class_base . '_Filters', $class_base . '', $class_base . '_Admin', $class_base . '_Admin_List_Table', @@ -89,13 +99,17 @@ } } -WP_REST_API_Log_ElasticPress::plugins_loaded(); - +// Wire up hooks and filters in static classes. +WP_REST_API_Log_i18n::plugins_loaded(); +WP_REST_API_Log::plugins_loaded(); WP_REST_API_Log_Settings::plugins_loaded(); WP_REST_API_Log_Settings_General::plugins_loaded(); WP_REST_API_Log_Settings_Routes::plugins_loaded(); WP_REST_API_Log_Settings_ElasticPress::plugins_loaded(); WP_REST_API_Log_Settings_Help::plugins_loaded(); +WP_REST_API_Log_Post_Type::plugins_loaded(); +WP_REST_API_Log_Controller::plugins_loaded(); +WP_REST_API_Log_ElasticPress::plugins_loaded(); /* Activation hook */ register_activation_hook( __FILE__, function() {