Commit inicial - WordPress Análisis de Precios Unitarios

- WordPress core y plugins
- Tema Twenty Twenty-Four configurado
- Plugin allow-unfiltered-html.php simplificado
- .gitignore configurado para excluir wp-config.php y uploads

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
root
2025-11-03 21:04:30 -06:00
commit a22573bf0b
24068 changed files with 4993111 additions and 0 deletions

View File

@@ -0,0 +1,199 @@
<?php
/**
* @api {get} /redirection/v1/404 Get 404 logs
* @apiName GetLogs
* @apiDescription Get a paged list of 404 logs after applying a set of filters and result ordering.
* @apiGroup 404
*
* @apiUse 404QueryParams
*
* @apiUse 404List
* @apiUse 401Error
* @apiUse 404Error
*/
/**
* @api {post} /redirection/v1/bulk/404/:type Bulk action
* @apiName BulkAction
* @apiDescription Delete 404 logs by ID
* @apiGroup 404
*
* @apiParam (URL) {String="delete"} :type Type of bulk action that is applied to every log ID.
*
* @apiParam (Query Parameter) {String[]} [items] Array of group IDs to perform the action on
* @apiParam (Query Parameter) {Boolean=false} [global] Perform action globally using the filter parameters
* @apiUse 404QueryParams
*
* @apiUse 404List
* @apiUse 401Error
* @apiUse 404Error
* @apiUse 400MissingError
*/
/**
* @apiDefine 404QueryParams 404 log query parameters
*
* @apiParam (Query Parameter) {String} [filterBy[ip]] Filter the results by the supplied IP
* @apiParam (Query Parameter) {String} [filterBy[url]] Filter the results by the supplied URL
* @apiParam (Query Parameter) {String} [filterBy[url-]exact] Filter the results by the exact URL (not a substring match, as per `url`)
* @apiParam (Query Parameter) {String} [filterBy[referrer]] Filter the results by the supplied referrer
* @apiParam (Query Parameter) {String} [filterBy[agent]] Filter the results by the supplied user agent
* @apiParam (Query Parameter) {String} [filterBy[target]] Filter the results by the supplied redirect target
* @apiParam (Query Parameter) {String} [filterBy[domain]] Filter the results by the supplied domain name
* @apiParam (Query Parameter) {String="head","get","post"} [filterBy[method]] Filter the results by the supplied HTTP request method
* @apiParam (Query Parameter) {Integer} [filterBy[http]] Filter the results by the supplied redirect HTTP code
* @apiParam (Query Parameter) {string="ip","url"} [orderby] Order by IP or URL
* @apiParam (Query Parameter) {String="asc","desc"} [direction] Direction to order the results by (ascending or descending)
* @apiParam (Query Parameter) {Integer{1...200}} [per_page=25] Number of results per request
* @apiParam (Query Parameter) {Integer} [page=0] Current page of results
* @apiParam (Query Parameter) {String="ip","url"} [groupBy] Group by IP or URL
*/
/**
* @apiDefine 404List
*
* @apiSuccess {Object[]} items Array of 404 log objects
* @apiSuccess {Integer} items.id ID of 404 log entry
* @apiSuccess {String} items.created Date the 404 log entry was recorded
* @apiSuccess {Integer} items.created_time Unix time value for `created`
* @apiSuccess {Integer} items.url The requested URL that caused the 404 log entry
* @apiSuccess {String} items.agent User agent of the client initiating the request
* @apiSuccess {Integer} items.referrer Referrer of the client initiating the request
* @apiSuccess {Integer} total Number of items
*
* @apiSuccessExample {json} Success 200:
* HTTP/1.1 200 OK
* {
* "items": [
* {
* "id": 3,
* "created": "2019-01-01 12:12:00,
* "created_time": "12345678",
* "url": "/the-url",
* "agent": "FancyBrowser",
* "referrer": "http://site.com/previous/,
* }
* ],
* "total": 1
* }
*/
/**
* 404 API endpoint
*/
class Redirection_Api_404 extends Redirection_Api_Filter_Route {
/**
* 404 API endpoint constructor
*
* @param string $namespace Namespace.
*/
public function __construct( $namespace ) {
$orders = [ 'url', 'ip', 'total', 'count', '' ];
$filters = [ 'ip', 'url-exact', 'referrer', 'agent', 'url', 'domain', 'method', 'http' ];
register_rest_route( $namespace, '/404', array(
'args' => $this->get_filter_args( $orders, $filters ),
$this->get_route( WP_REST_Server::READABLE, 'route_404', [ $this, 'permission_callback_manage' ] ),
) );
register_rest_route( $namespace, '/bulk/404/(?P<bulk>delete)', array(
$this->get_route( WP_REST_Server::EDITABLE, 'route_bulk', [ $this, 'permission_callback_delete' ] ),
'args' => array_merge( $this->get_filter_args( $orders, $filters ), [
'items' => [
'description' => 'Comma separated list of item IDs to perform action on',
'type' => 'array',
'items' => [
'description' => 'Item ID',
'type' => [ 'string', 'number' ],
],
],
] ),
) );
}
/**
* Checks a manage capability
*
* @param WP_REST_Request $request Request.
* @return Bool
*/
public function permission_callback_manage( WP_REST_Request $request ) {
return Redirection_Capabilities::has_access( Redirection_Capabilities::CAP_404_MANAGE );
}
/**
* Checks a delete capability
*
* @param WP_REST_Request $request Request.
* @return Bool
*/
public function permission_callback_delete( WP_REST_Request $request ) {
return Redirection_Capabilities::has_access( Redirection_Capabilities::CAP_404_DELETE );
}
/**
* Get 404 log
*
* @param WP_REST_Request $request The request.
* @return WP_Error|array Return an array of results, or a WP_Error
*/
public function route_404( WP_REST_Request $request ) {
return $this->get_404( $request->get_params() );
}
/**
* Perform action on 404s
*
* @param WP_REST_Request $request The request.
* @return WP_Error|array Return an array of results, or a WP_Error
*/
public function route_bulk( WP_REST_Request $request ) {
$params = $request->get_params();
if ( isset( $params['items'] ) && is_array( $params['items'] ) ) {
$items = $params['items'];
foreach ( $items as $item ) {
if ( is_numeric( $item ) ) {
Red_404_Log::delete( intval( $item, 10 ) );
} elseif ( isset( $params['groupBy'] ) ) {
$group_by = sanitize_text_field( $params['groupBy'] );
$delete_by = 'url-exact';
if ( in_array( $group_by, [ 'ip', 'agent' ], true ) ) {
$delete_by = $group_by;
}
Red_404_Log::delete_all( [ 'filterBy' => [ $delete_by => $item ] ] );
}
}
if ( isset( $params['groupBy'] ) && $params['groupBy'] === 'url-exact' ) {
unset( $params['groupBy'] );
}
} elseif ( isset( $params['global'] ) && $params['global'] ) {
Red_404_Log::delete_all( $params );
}
return $this->get_404( $params );
}
/**
* Get 404 log
*
* @param array $params The request.
* @return WP_Error|array Return an array of results, or a WP_Error
*/
private function get_404( array $params ) {
if ( isset( $params['groupBy'] ) && in_array( $params['groupBy'], [ 'ip', 'url', 'agent', 'url-exact' ], true ) ) {
$group_by = sanitize_text_field( $params['groupBy'] );
if ( $group_by === 'url-exact' ) {
$group_by = 'url';
}
return Red_404_Log::get_grouped( $group_by, $params );
}
return Red_404_Log::get_filtered( $params );
}
}

View File

@@ -0,0 +1,54 @@
<?php
/**
* @api {get} /redirection/v1/export/:module/:format Export redirects
* @apiName Export
* @apiDescription Export redirects for a module to Apache, CSV, Nginx, or JSON format
* @apiGroup Import/Export
*
* @apiParam (URL) {String="1","2","3","all"} :module The module to export, with 1 being WordPress, 2 is Apache, and 3 is Nginx
* @apiParam (URL) {String="csv","apache","nginx","json"} :format The format of the export
*
* @apiSuccess {String} data Exported data
* @apiSuccess {Integer} total Number of items exported
*
* @apiUse 401Error
* @apiUse 404Error
* @apiError (Error 400) redirect_export_invalid_module Invalid module
* @apiErrorExample {json} 404 Error Response:
* HTTP/1.1 400 Bad Request
* {
* "code": "redirect_export_invalid_module",
* "message": "Invalid module"
* }
*/
class Redirection_Api_Export extends Redirection_Api_Route {
public function __construct( $namespace ) {
register_rest_route( $namespace, '/export/(?P<module>1|2|3|all)/(?P<format>csv|apache|nginx|json)', array(
$this->get_route( WP_REST_Server::READABLE, 'route_export', [ $this, 'permission_callback_manage' ] ),
) );
}
public function permission_callback_manage( WP_REST_Request $request ) {
return Redirection_Capabilities::has_access( Redirection_Capabilities::CAP_IO_MANAGE );
}
public function route_export( WP_REST_Request $request ) {
$module = sanitize_text_field( $request['module'] );
$format = 'json';
if ( in_array( $request['format'], [ 'csv', 'apache', 'nginx', 'json' ], true ) ) {
$format = sanitize_text_field( $request['format'] );
}
$export = Red_FileIO::export( $module, $format );
if ( $export === false ) {
return $this->add_error_details( new WP_Error( 'redirect_export_invalid_module', 'Invalid module' ), __LINE__ );
}
return array(
'data' => $export['data'],
'total' => $export['total'],
);
}
}

View File

@@ -0,0 +1,327 @@
<?php
/**
* @api {get} /redirection/v1/group Get groups
* @apiName GetGroups
* @apiDescription Get a paged list of groups based after applying a set of filters and result ordering.
* @apiGroup Group
*
* @apiUse GroupQueryParams
*
* @apiUse GroupList
* @apiUse 401Error
* @apiUse 404Error
*/
/**
* @api {post} /redirection/v1/group Create group
* @apiName CreateGroup
* @apiDescription Create a new group, and return a paged list of groups.
* @apiGroup Group
*
* @apiUse GroupItem
* @apiUse GroupQueryParams
*
* @apiUse GroupList
* @apiUse 401Error
* @apiUse 404Error
* @apiError (Error 400) redirect_group_invalid Invalid group or parameters
* @apiErrorExample {json} 404 Error Response:
* HTTP/1.1 400 Bad Request
* {
* "code": "redirect_group_invalid",
* "message": "Invalid group or parameters"
* }
*/
/**
* @api {post} /redirection/v1/group/:id Update group
* @apiName UpdateGroup
* @apiDescription Update an existing group.
* @apiGroup Group
*
* @apiParam (URL) {Integer} :id Group ID to update
* @apiUse GroupList
*
* @apiSuccess {String} item The updated group
* @apiSuccess {Integer} item.id ID of group
* @apiSuccess {String} item.name Name of this group
* @apiSuccess {Boolean} item.enabled `true` if group (and redirects) are enabled, `false` otherwise
* @apiSuccess {Integer} item.redirects Number of redirects in this group
* @apiSuccess {String} item.moduleName Name of the module this group belongs to
* @apiSuccess {Integer} item.module_id ID of the module this group belongs to
*
* @apiUse 401Error
* @apiUse 404Error
*
* @apiError (Error 400) redirect_group_invalid Invalid group or parameters
* @apiErrorExample {json} 404 Error Response:
* HTTP/1.1 400 Bad Request
* {
* "code": "redirect_group_invalid",
* "message": "Invalid group or parameters"
* }
*/
/**
* @api {post} /redirection/v1/bulk/group/:type Bulk action
* @apiName BulkAction
* @apiDescription Enable, disable, and delete a set of groups. The endpoint will return the next page of results after.
* performing the action, based on the supplied query parameters. This information can be used to refresh a list displayed to the client.
* @apiGroup Group
*
* @apiParam (URL) {String="delete","enable","disable"} :type Type of bulk action that is applied to every group ID.
* Enabling or disabling a group will also enable or disable all redirects in that group
*
* @apiParam (Query Parameter) {String[]} [items] Array of group IDs to perform the action on
* @apiParam (Query Parameter) {Boolean=false} [global] Perform action globally using the filter parameters
* @apiUse GroupQueryParams
*
* @apiUse GroupList
* @apiUse 401Error
* @apiUse 404Error
* @apiUse 400MissingError
*/
/**
* @apiDefine GroupQueryParams
*
* @apiParam (Query Parameter) {String} [filterBy[name]] Filter the results by the supplied name
* @apiParam (Query Parameter) {String="enabled","disabled"} [filterBy[status]] Filter the results by the supplied status
* @apiParam (Query Parameter) {Integer="1","2","3"} [filterBy[module]] Filter the results by the supplied module ID
* @apiParam (Query Parameter) {String="name"} [orderby] Order in which results are returned
* @apiParam (Query Parameter) {String="asc","desc"} [direction=desc] Direction to order the results by (ascending or descending)
* @apiParam (Query Parameter) {Integer{1...200}} [per_page=25] Number of results per request
* @apiParam (Query Parameter) {Integer} [page=0] Current page of results
*/
/**
* @apiDefine GroupItem
*
* @apiParam (JSON Body) {String} name Name of the group
* @apiParam (JSON Body) {Integer="1","2","3"} moduleID Module ID of the group, with 1 being WordPress, 2 is Apache, and 3 is Nginx
*/
/**
* @apiDefine GroupList
*
* @apiSuccess {Object[]} items Array of group objects
* @apiSuccess {Integer} items.id ID of group
* @apiSuccess {String} items.name Name of this group
* @apiSuccess {Boolean} items.enabled `true` if group (and redirects) are enabled, `false` otherwise
* @apiSuccess {Integer} items.redirects Number of redirects in this group
* @apiSuccess {String} items.moduleName Name of the module this group belongs to
* @apiSuccess {Integer} items.module_id ID of the module this group belongs to
* @apiSuccess {Integer} total Number of items
*
* @apiSuccessExample {json} Success 200:
* HTTP/1.1 200 OK
* {
* "items": [
* {
* "id": 3,
* "enabled": true,
* "moduleName": "WordPress",
* "module_id": 1,
* "name": "Redirections",
* "redirects": 0,
* }
* ],
* "total": 1
* }
*/
/**
* Group API endpoint
*/
class Redirection_Api_Group extends Redirection_Api_Filter_Route {
/**
* 404 API endpoint constructor
*
* @param string $namespace Namespace.
*/
public function __construct( $namespace ) {
$orders = [ 'name', 'id', '' ];
$filters = [ 'status', 'module', 'name' ];
register_rest_route( $namespace, '/group', array(
'args' => $this->get_filter_args( $orders, $filters ),
$this->get_route( WP_REST_Server::READABLE, 'route_list', [ $this, 'permission_callback_manage' ] ),
array_merge(
$this->get_route( WP_REST_Server::EDITABLE, 'route_create', [ $this, 'permission_callback_add' ] ),
array( 'args' => $this->get_group_args() )
),
) );
register_rest_route( $namespace, '/group/(?P<id>[\d]+)', array(
'args' => $this->get_group_args(),
$this->get_route( WP_REST_Server::EDITABLE, 'route_update', [ $this, 'permission_callback_add' ] ),
) );
register_rest_route( $namespace, '/bulk/group/(?P<bulk>delete|enable|disable)', array(
$this->get_route( WP_REST_Server::EDITABLE, 'route_bulk', [ $this, 'permission_callback_bulk' ] ),
'args' => array_merge( $this->get_filter_args( $orders, $filters ), [
'items' => [
'description' => 'Comma separated list of item IDs to perform action on',
'type' => 'array',
'items' => [
'description' => 'Item ID',
'type' => [ 'string', 'number' ],
],
],
] ),
) );
}
/**
* Checks a manage capability
*
* Access to group data is required by the CAP_GROUP_MANAGE and CAP_REDIRECT_MANAGE caps
*
* @param WP_REST_Request $request Request.
* @return Bool
*/
public function permission_callback_manage( WP_REST_Request $request ) {
return Redirection_Capabilities::has_access( Redirection_Capabilities::CAP_GROUP_MANAGE ) || Redirection_Capabilities::has_access( Redirection_Capabilities::CAP_REDIRECT_MANAGE );
}
/**
* Checks a bulk capability
*
* @param WP_REST_Request $request Request.
* @return Bool
*/
public function permission_callback_bulk( WP_REST_Request $request ) {
if ( $request['bulk'] === 'delete' ) {
return Redirection_Capabilities::has_access( Redirection_Capabilities::CAP_GROUP_DELETE );
}
return $this->permission_callback_add( $request );
}
/**
* Checks a create capability
*
* @param WP_REST_Request $request Request.
* @return Bool
*/
public function permission_callback_add( WP_REST_Request $request ) {
return Redirection_Capabilities::has_access( Redirection_Capabilities::CAP_GROUP_ADD );
}
private function get_group_args() {
return array(
'moduleId' => array(
'description' => 'Module ID',
'type' => 'integer',
'minimum' => 0,
'maximum' => 3,
'required' => true,
),
'name' => array(
'description' => 'Group name',
'type' => 'string',
'required' => true,
),
'status' => [
'description' => 'Status of the group',
],
);
}
/**
* Get group list
*
* @param WP_REST_Request $request The request.
* @return WP_Error|array Return an array of results, or a WP_Error
*/
public function route_list( WP_REST_Request $request ) {
return Red_Group::get_filtered( $request->get_params() );
}
/**
* Create a group
*
* @param WP_REST_Request $request The request.
* @return WP_Error|array Return an array of results, or a WP_Error
*/
public function route_create( WP_REST_Request $request ) {
$params = $request->get_params( $request );
$name = '';
$module = 0;
if ( isset( $params['name'] ) ) {
$name = sanitize_text_field( $params['name'] );
}
if ( isset( $params['moduleId'] ) ) {
$module = intval( $params['moduleId'], 10 );
}
$group = Red_Group::create( $name, $module );
if ( $group ) {
return Red_Group::get_filtered( $params );
}
return $this->add_error_details( new WP_Error( 'redirect_group_invalid', 'Invalid group or parameters' ), __LINE__ );
}
/**
* Update a 404
*
* @param WP_REST_Request $request The request.
* @return WP_Error|array Return an array of results, or a WP_Error
*/
public function route_update( WP_REST_Request $request ) {
$params = $request->get_params( $request );
$group = Red_Group::get( intval( $request['id'], 10 ) );
if ( $group ) {
$result = $group->update( $params );
if ( $result ) {
return array( 'item' => $group->to_json() );
}
}
return $this->add_error_details( new WP_Error( 'redirect_group_invalid', 'Invalid group details' ), __LINE__ );
}
/**
* Perform action on groups
*
* @param WP_REST_Request $request The request.
* @return WP_Error|array Return an array of results, or a WP_Error
*/
public function route_bulk( WP_REST_Request $request ) {
$params = $request->get_params();
$action = $request['bulk'];
$items = [];
if ( isset( $params['items'] ) && is_array( $params['items'] ) ) {
// Array of integers, sanitized below
$items = $params['items'];
} elseif ( isset( $params['global'] ) && $params['global'] ) {
// Groups have additional actions that fire and so we need to action them individually
$groups = Red_Group::get_all( $params );
$items = array_column( $groups, 'id' );
}
foreach ( $items as $item ) {
$group = Red_Group::get( intval( $item, 10 ) );
if ( is_object( $group ) ) {
if ( $action === 'delete' ) {
$group->delete();
} elseif ( $action === 'disable' ) {
$group->disable();
} elseif ( $action === 'enable' ) {
$group->enable();
}
}
}
return $this->route_list( $request );
}
}

View File

@@ -0,0 +1,92 @@
<?php
/**
* @api {get} /redirection/v1/import/file/:group_id Import redirects
* @apiName Import
* @apiDescription Import redirects from CSV, JSON, or Apache .htaccess
* @apiGroup Import/Export
*
* @apiParam (URL) {Integer} :group_id The group ID to import into
* @apiParam (File) {File} file The multipart form upload containing the file to import
*
* @apiSuccess {Integer} imported Number of items imported
*
* @apiUse 401Error
* @apiUse 404Error
* @apiError (Error 400) redirect_import_invalid_group Invalid group
* @apiErrorExample {json} 404 Error Response:
* HTTP/1.1 400 Bad Request
* {
* "code": "redirect_import_invalid_group",
* "message": "Invalid group"
* }
* @apiError (Error 400) redirect_import_invalid_file Invalid file upload
* @apiErrorExample {json} 404 Error Response:
* HTTP/1.1 400 Bad Request
* {
* "code": "redirect_import_invalid_file",
* "message": "Invalid file upload"
* }
*/
class Redirection_Api_Import extends Redirection_Api_Route {
public function __construct( $namespace ) {
register_rest_route( $namespace, '/import/file/(?P<group_id>\d+)', array(
$this->get_route( WP_REST_Server::EDITABLE, 'route_import_file', [ $this, 'permission_callback_manage' ] ),
) );
register_rest_route( $namespace, '/import/plugin', array(
$this->get_route( WP_REST_Server::READABLE, 'route_plugin_import_list', [ $this, 'permission_callback_manage' ] ),
) );
register_rest_route( $namespace, '/import/plugin', array(
$this->get_route( WP_REST_Server::EDITABLE, 'route_plugin_import', [ $this, 'permission_callback_manage' ] ),
) );
}
public function permission_callback_manage( WP_REST_Request $request ) {
return Redirection_Capabilities::has_access( Redirection_Capabilities::CAP_IO_MANAGE );
}
public function route_plugin_import_list( WP_REST_Request $request ) {
include_once dirname( __DIR__ ) . '/models/importer.php';
return array( 'importers' => Red_Plugin_Importer::get_plugins() );
}
public function route_plugin_import( WP_REST_Request $request ) {
include_once dirname( __DIR__ ) . '/models/importer.php';
$params = $request->get_params( $request );
$groups = Red_Group::get_all();
$plugins = is_array( $request['plugin'] ) ? $request['plugin'] : [ $request['plugin'] ];
$plugins = array_map( 'sanitize_text_field', $plugins );
$total = 0;
foreach ( $plugins as $plugin ) {
$total += Red_Plugin_Importer::import( $plugin, $groups[0]['id'] );
}
return [ 'imported' => $total ];
}
public function route_import_file( WP_REST_Request $request ) {
$upload = $request->get_file_params();
$upload = isset( $upload['file'] ) ? $upload['file'] : false;
$group_id = intval( $request['group_id'], 10 );
if ( $upload && is_uploaded_file( $upload['tmp_name'] ) ) {
$count = Red_FileIO::import( $group_id, $upload );
if ( $count !== false ) {
return array(
'imported' => $count,
);
}
return $this->add_error_details( new WP_Error( 'redirect_import_invalid_group', 'Invalid group' ), __LINE__ );
}
return $this->add_error_details( new WP_Error( 'redirect_import_invalid_file', 'Invalid file' ), __LINE__ );
}
}

View File

@@ -0,0 +1,202 @@
<?php
/**
* @api {get} /redirection/v1/log Get logs
* @apiName GetLogs
* @apiDescription Get a paged list of redirect logs after applying a set of filters and result ordering.
* @apiGroup Log
*
* @apiUse LogQueryParams
*
* @apiUse LogList
* @apiUse 401Error
* @apiUse 404Error
*/
/**
* @api {post} /redirection/v1/log Delete logs
* @apiName DeleteLogs
* @apiDescription Delete logs by filter. If no filter is supplied then all entries will be deleted. The endpoint will return the next page of results after.
* performing the action, based on the supplied query parameters. This information can be used to refresh a list displayed to the client.
* @apiGroup Log
*
* @apiParam (Query Parameter) {String} filterBy[ip] Filter the results by the supplied IP
* @apiParam (Query Parameter) {String} filterBy[url] Filter the results by the supplied URL
* @apiParam (Query Parameter) {String} filterBy[url-exact] Filter the results by the exact URL (not a substring match, as per `url`)
* @apiParam (Query Parameter) {String} filterBy[referrer] Filter the results by the supplied referrer
* @apiParam (Query Parameter) {String} filterBy[agent] Filter the results by the supplied user agent
* @apiParam (Query Parameter) {String} filterBy[target] Filter the results by the supplied redirect target
*
* @apiUse LogList
* @apiUse 401Error
* @apiUse 404Error
*/
/**
* @api {post} /redirection/v1/bulk/log/:type Bulk action
* @apiName BulkAction
* @apiDescription Delete logs by ID
* @apiGroup Log
*
* @apiParam (URL) {String="delete"} :type Type of bulk action that is applied to every log ID.
* @apiParam (Query Parameter) {String[]} [items] Array of group IDs to perform the action on
* @apiParam (Query Parameter) {Boolean=false} [global] Perform action globally using the filter parameters
* @apiUse LogQueryParams
*
* @apiUse LogList
* @apiUse 401Error
* @apiUse 404Error
* @apiUse 400MissingError
*/
/**
* @apiDefine LogQueryParams Log query parameters
*
* @apiParam (Query Parameter) {String} [filterBy[ip]] Filter the results by the supplied IP
* @apiParam (Query Parameter) {String} [filterBy[url]] Filter the results by the supplied URL
* @apiParam (Query Parameter) {String} [filterBy[url-]exact] Filter the results by the exact URL (not a substring match, as per `url`)
* @apiParam (Query Parameter) {String} [filterBy[referrer]] Filter the results by the supplied referrer
* @apiParam (Query Parameter) {String} [filterBy[agent]] Filter the results by the supplied user agent
* @apiParam (Query Parameter) {String} [filterBy[target]] Filter the results by the supplied redirect target
* @apiParam (Query Parameter) {String} [filterBy[domain]] Filter the results by the supplied domain name
* @apiParam (Query Parameter) {String} [filterBy[redirect_by]] Filter the results by the redirect agent
* @apiParam (Query Parameter) {String="head","get","post"} [filterBy[method]] Filter the results by the supplied HTTP request method
* @apiParam (Query Parameter) {String="ip","url"} [orderby] Order by IP or URL
* @apiParam (Query Parameter) {String="asc","desc"} [direction=desc] Direction to order the results by (ascending or descending)
* @apiParam (Query Parameter) {Integer{1...200}} [per_page=25] Number of results per request
* @apiParam (Query Parameter) {Integer} [page=0] Current page of results
* @apiParam (Query Parameter) {String="ip","url"} [groupBy] Group by IP or URL
*/
/**
* @apiDefine LogList
*
* @apiSuccess {Object[]} items Array of log objects
* @apiSuccess {Integer} items.id ID of log entry
* @apiSuccess {String} items.created Date the log entry was recorded
* @apiSuccess {Integer} items.created_time Unix time value for `created`
* @apiSuccess {Integer} items.url The requested URL that caused the log entry
* @apiSuccess {String} items.agent User agent of the client initiating the request
* @apiSuccess {Integer} items.referrer Referrer of the client initiating the request
* @apiSuccess {Integer} total Number of items
*
* @apiSuccessExample {json} Success 200:
* HTTP/1.1 200 OK
* {
* "items": [
* {
* "id": 3,
* "created": "2019-01-01 12:12:00,
* "created_time": "12345678",
* "url": "/the-url",
* "agent": "FancyBrowser",
* "referrer": "http://site.com/previous/,
* }
* ],
* "total": 1
* }
*/
/**
* Log API endpoint
*/
class Redirection_Api_Log extends Redirection_Api_Filter_Route {
/**
* Log API endpoint constructor
*
* @param string $namespace Namespace.
*/
public function __construct( $namespace ) {
$orders = [ 'url', 'ip', 'total', 'count', '' ];
$filters = [ 'ip', 'url-exact', 'referrer', 'agent', 'url', 'target', 'domain', 'method', 'http', 'redirect_by' ];
register_rest_route( $namespace, '/log', array(
'args' => $this->get_filter_args( $orders, $filters ),
$this->get_route( WP_REST_Server::READABLE, 'route_log', [ $this, 'permission_callback_manage' ] ),
) );
register_rest_route( $namespace, '/bulk/log/(?P<bulk>delete)', [
$this->get_route( WP_REST_Server::EDITABLE, 'route_bulk', [ $this, 'permission_callback_delete' ] ),
'args' => array_merge( $this->get_filter_args( $orders, $filters ), [
'items' => [
'description' => 'Comma separated list of item IDs to perform action on',
'type' => 'array',
'items' => [
'description' => 'Item ID',
'type' => [ 'string', 'number' ],
],
],
] ),
] );
}
/**
* Checks a manage capability
*
* @param WP_REST_Request $request Request.
* @return Bool
*/
public function permission_callback_manage( WP_REST_Request $request ) {
return Redirection_Capabilities::has_access( Redirection_Capabilities::CAP_LOG_MANAGE );
}
/**
* Checks a delete capability
*
* @param WP_REST_Request $request Request.
* @return Bool
*/
public function permission_callback_delete( WP_REST_Request $request ) {
return Redirection_Capabilities::has_access( Redirection_Capabilities::CAP_LOG_DELETE );
}
/**
* Get log list
*
* @param WP_REST_Request $request The request.
* @return WP_Error|array Return an array of results, or a WP_Error
*/
public function route_log( WP_REST_Request $request ) {
return $this->get_logs( $request->get_params() );
}
/**
* Perform bulk action on logs
*
* @param WP_REST_Request $request The request.
* @return WP_Error|array Return an array of results, or a WP_Error
*/
public function route_bulk( WP_REST_Request $request ) {
$params = $request->get_params();
if ( isset( $params['items'] ) && is_array( $params['items'] ) ) {
$items = $params['items'];
foreach ( $items as $item ) {
if ( is_numeric( $item ) ) {
Red_Redirect_Log::delete( intval( $item, 10 ) );
} elseif ( isset( $params['groupBy'] ) ) {
$delete_by = 'url-exact';
if ( in_array( $params['groupBy'], [ 'ip', 'agent' ], true ) ) {
$delete_by = sanitize_text_field( $params['groupBy'] );
}
Red_Redirect_Log::delete_all( [ 'filterBy' => [ $delete_by => $item ] ] );
}
}
} elseif ( isset( $params['global'] ) && $params['global'] ) {
Red_Redirect_Log::delete_all( $params );
}
return $this->route_log( $request );
}
private function get_logs( array $params ) {
if ( isset( $params['groupBy'] ) && in_array( $params['groupBy'], [ 'ip', 'url', 'agent' ], true ) ) {
return Red_Redirect_Log::get_grouped( sanitize_text_field( $params['groupBy'] ), $params );
}
return Red_Redirect_Log::get_filtered( $params );
}
}

View File

@@ -0,0 +1,137 @@
<?php
/**
* 'Plugin' functions for Redirection
*/
class Redirection_Api_Plugin extends Redirection_Api_Route {
public function __construct( $namespace ) {
register_rest_route( $namespace, '/plugin', array(
$this->get_route( WP_REST_Server::READABLE, 'route_status', [ $this, 'permission_callback_manage' ] ),
) );
register_rest_route( $namespace, '/plugin', array(
$this->get_route( WP_REST_Server::EDITABLE, 'route_fixit', [ $this, 'permission_callback_manage' ] ),
'args' => [
'name' => array(
'description' => 'Name',
'type' => 'string',
),
'value' => array(
'description' => 'Value',
'type' => 'string',
),
],
) );
register_rest_route( $namespace, '/plugin/delete', array(
$this->get_route( WP_REST_Server::EDITABLE, 'route_delete', [ $this, 'permission_callback_manage' ] ),
) );
register_rest_route( $namespace, '/plugin/test', array(
$this->get_route( WP_REST_Server::ALLMETHODS, 'route_test', [ $this, 'permission_callback_manage' ] ),
) );
register_rest_route( $namespace, '/plugin/data', array(
$this->get_route( WP_REST_Server::EDITABLE, 'route_database', [ $this, 'permission_callback_manage' ] ),
'args' => [
'upgrade' => [
'description' => 'Upgrade parameter',
'type' => 'string',
'enum' => array(
'stop',
'skip',
'retry',
),
],
],
) );
}
public function permission_callback_manage( WP_REST_Request $request ) {
return Redirection_Capabilities::has_access( Redirection_Capabilities::CAP_SUPPORT_MANAGE );
}
public function route_status( WP_REST_Request $request ) {
include_once dirname( REDIRECTION_FILE ) . '/models/fixer.php';
$fixer = new Red_Fixer();
return $fixer->get_json();
}
public function route_fixit( WP_REST_Request $request ) {
include_once dirname( REDIRECTION_FILE ) . '/models/fixer.php';
$params = $request->get_params();
$fixer = new Red_Fixer();
if ( isset( $params['name'] ) && isset( $params['value'] ) ) {
global $wpdb;
$fixer->save_debug( sanitize_text_field( $params['name'] ), sanitize_text_field( $params['value'] ) );
$groups = intval( $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->prefix}redirection_groups" ), 10 );
if ( $groups === 0 ) {
Red_Group::create( 'new group', 1 );
}
} else {
$fixer->fix( $fixer->get_status() );
}
return $fixer->get_json();
}
public function route_delete() {
if ( is_multisite() ) {
return new WP_Error( 'redirect_delete_multi', 'Multisite installations must delete the plugin from the network admin' );
}
$plugin = Redirection_Admin::init();
$plugin->plugin_uninstall();
$current = get_option( 'active_plugins' );
$plugin_position = array_search( basename( dirname( REDIRECTION_FILE ) ) . '/' . basename( REDIRECTION_FILE ), $current );
if ( $plugin_position !== false ) {
array_splice( $current, $plugin_position, 1 );
update_option( 'active_plugins', $current );
}
return array( 'location' => admin_url() . 'plugins.php' );
}
public function route_test( WP_REST_Request $request ) {
return array(
'success' => true,
);
}
public function route_database( WP_REST_Request $request ) {
$params = $request->get_params();
$status = new Red_Database_Status();
$upgrade = false;
if ( isset( $params['upgrade'] ) && in_array( $params['upgrade'], [ 'stop', 'skip' ], true ) ) {
$upgrade = sanitize_text_field( $params['upgrade'] );
}
// Check upgrade
if ( ! $status->needs_updating() && ! $status->needs_installing() ) {
/* translators: version number */
$status->set_error( sprintf( __( 'Your database does not need updating to %s.', 'redirection' ), REDIRECTION_DB_VERSION ) );
return $status->get_json();
}
if ( $upgrade === 'stop' ) {
$status->stop_update();
} elseif ( $upgrade === 'skip' ) {
$status->set_next_stage();
}
if ( $upgrade === false || $status->get_current_stage() ) {
$database = new Red_Database();
$database->apply_upgrade( $status );
}
return $status->get_json();
}
}

View File

@@ -0,0 +1,407 @@
<?php
/**
* @api {get} /redirection/v1/redirect Get redirects
* @apiName GetRedirects
* @apiDescription Get a paged list of redirects based after applying a set of filters and result ordering.
* @apiGroup Redirect
*
* @apiUse RedirectQueryParams
*
* @apiUse RedirectList
* @apiUse 401Error
* @apiUse 404Error
*/
/**
* @api {post} /redirection/v1/redirect Create redirect
* @apiName CreateRedirect
* @apiDescription Create a new redirect, and return a paged list of redirects.
* @apiGroup Redirect
*
* @apiUse RedirectItem
* @apiUse RedirectQueryParams
*
* @apiUse RedirectList
* @apiUse 401Error
* @apiUse 404Error
* @apiError (Error 400) redirect_create_failed Failed to create redirect
* @apiErrorExample {json} 404 Error Response:
* HTTP/1.1 400 Bad Request
* {
* "code": "redirect_create_failed",
* "message": "Failed to create redirect"
* }
*/
/**
* @api {post} /redirection/v1/redirect/:id Update redirect
* @apiName UpdateRedirect
* @apiDescription Update an existing redirect.
* @apiGroup Redirect
*
* @apiParam (URL) {Integer} :id Redirect ID to update
*
* @apiUse RedirectItem
*
* @apiUse RedirectList
* @apiUse 401Error
* @apiUse 404Error
* @apiError (Error 400) redirect_update_failed Failed to update redirect
* @apiErrorExample {json} 404 Error Response:
* HTTP/1.1 400 Bad Request
* {
* "code": "redirect_update_failed",
* "message": "Failed to update redirect"
* }
*/
/**
* @api {post} /redirection/v1/bulk/redirect/:type Bulk action
* @apiName BulkAction
* @apiDescription Enable, disable, and delete a set of redirects. The endpoint will return the next page of results after.
* performing the action, based on the supplied query parameters. This information can be used to refresh a list displayed to the client.
* @apiGroup Redirect
*
* @apiParam (URL) {String="delete","enable","disable","reset"} :type Type of bulk action that is applied to every item.
* @apiParam (Query Parameter) {String[]} [items] Array of redirect IDs to perform the action on
* @apiParam (Query Parameter) {Boolean=false} [global] Perform action globally using the filter parameters
* @apiUse RedirectQueryParams
*
* @apiUse RedirectList
* @apiUse 401Error
* @apiUse 404Error
* @apiUse 400MissingError
*/
/**
* @apiDefine RedirectItem Redirect
* All data associated with a redirect
*
* @apiParam {String="enabled","disabled"} status Status of the redirect
* @apiParam {Integer} position Redirect position, used to determine order multiple redirects occur
* @apiParam {Object} match_data Additional match parameters
* @apiParam {Object} match_data.source Match against the source
* @apiParam {Boolean} match_data.source.flag_regex `true` for regular expression, `false` otherwise
* @apiParam {String="ignore","exact","pass"} match_data.source.flag_query Which query parameter matching to use
* @apiParam {Boolean} match_data.source.flag_case] `true` for case insensitive matches, `false` otherwise
* @apiParam {Boolean} match_data.source.flag_trailing] `true` to ignore trailing slashes, `false` otherwise
* @apiParam {Object} match_data.options Options for the redirect match
* @apiParam {Boolean} match_data.options.log_exclude `true` to exclude this from any logs, `false` otherwise (default)
* @apiParam {Boolean} regex True for regular expression, `false` otherwise
* @apiParam {String} url The source URL
* @apiParam {String="url","referrer","agent","login","header","custom","cookie","role","server","ip","page","language"} match_type What URL matching to use
* @apiParam {String} [title] A descriptive title for the redirect, or empty for no title
* @apiParam {Integer} group_id The group this redirect belongs to
* @apiParam {String} action_type What to do when the URL is matched
* @apiParam {Integer} action_code The HTTP code to return
* @apiParam {Object} action_data Any data associated with the `action_type` and `match_type`. For example, the target URL
*/
/**
* @apiDefine RedirectList A list of redirects
* A list of redirects
*
* @apiSuccess {Object[]} items Array of redirect objects
* @apiSuccess {Integer} items.id ID of redirect
* @apiSuccess {String} items.url Source URL to match
* @apiSuccess {String} items.match_url Match URL
* @apiSuccess {Object} items.match_data Match against the source
* @apiSuccess {String} items.match_type What URL matching to use
* @apiSuccess {String} items.action_type What to do when the URL is matched
* @apiSuccess {Integer} items.action_code The HTTP code to return
* @apiSuccess {String} items.action_data Any data associated with the action_type. For example, the target URL
* @apiSuccess {String} items.title Optional A descriptive title for the redirect, or empty for no title
* @apiSuccess {String} items.hits Number of hits this redirect has received
* @apiSuccess {String} items.regex True for regular expression, false otherwise
* @apiSuccess {String} items.group_id The group this redirect belongs to
* @apiSuccess {String} items.position Redirect position, used to determine order multiple redirects occur
* @apiSuccess {String} items.last_access The date this redirect was last hit
* @apiSuccess {String} items.status Status of the redirect
* @apiSuccess {Integer} total Number of items
*
* @apiSuccessExample {json} Success-Response:
* HTTP/1.1 200 OK
* {
* "items": [
* {
* id: 3,
* url: "/source",
* match_url: "/source",
* match_data: "",
* action_code: "",
* action_type: "",
* action_data: "",
* match_type: "url",
* title: "Redirect title",
* hits: 5,
* regex: true,
* group_id: 15,
* position: 1,
* last_access: "2019-01-01 01:01:01"
* status: "enabled"
* }
* ],
* "total": 1
* }
*/
/**
* @apiDefine RedirectQueryParams
*
* @apiParam (Query Parameter) {String="enabled","disabled"} [filterBy[status]] Filter the results by the supplied status
* @apiParam (Query Parameter) {String} [filterBy[url]] Filter the results by the supplied URL
* @apiParam (Query Parameter) {String="regular","plain"} [filterBy[url-match]] Filter the results by `regular` expressions or non regular expressions
* @apiParam (Query Parameter) {String} [filterBy[match]] Filter the results by the supplied match type
* @apiParam (Query Parameter) {String} [filterBy[action]] Filter the results by the supplied action type
* @apiParam (Query Parameter) {Integer} [filterBy[http]] Filter the results by the supplied redirect HTTP code
* @apiParam (Query Parameter) {String="year","month","all"} [filterBy[access]] Filter the results by how long the redirect was last accessed
* @apiParam (Query Parameter) {String} [filterBy[target]] Filter the results by the supplied redirect target
* @apiParam (Query Parameter) {String} [filterBy[title]] Filter the results by the supplied redirect title
* @apiParam (Query Parameter) {Integer} [filterBy[group]] Filter the results by the supplied redirect group ID
* @apiParam (Query Parameter) {Integer} [filterBy[id]] Filter the results to the redirect ID
* @apiParam (Query Parameter) {Integer="1","2","3"} [filterBy[module]] Filter the results by the supplied module ID
* @apiParam (Query Parameter) {String="source","last_count","last_access","position","id"} [orderby=id] Order in which results are returned
* @apiParam (Query Parameter) {String="asc","desc"} [direction=desc] Direction to order the results by (ascending or descending)
* @apiParam (Query Parameter) {Integer{1...200}} [per_page=25] Number of results per request
* @apiParam (Query Parameter) {Integer} [page=0] Current page of results
*/
/**
* Redirect API endpoint
*/
class Redirection_Api_Redirect extends Redirection_Api_Filter_Route {
/**
* Redirect API endpoint constructor
*
* @param string $namespace Namespace.
*/
public function __construct( $namespace ) {
$orders = [ 'source', 'last_count', 'last_access', 'position', 'id', '' ];
$filters = [ 'status', 'url-match', 'match', 'action', 'http', 'access', 'url', 'target', 'title', 'group', 'id' ];
register_rest_route( $namespace, '/redirect', array(
'args' => $this->get_filter_args( $orders, $filters ),
$this->get_route( WP_REST_Server::READABLE, 'route_list', [ $this, 'permission_callback_manage' ] ),
$this->get_route( WP_REST_Server::EDITABLE, 'route_create', [ $this, 'permission_callback_add' ] ),
) );
register_rest_route( $namespace, '/redirect/(?P<id>[\d]+)', array(
$this->get_route( WP_REST_Server::EDITABLE, 'route_update', [ $this, 'permission_callback_add' ] ),
) );
register_rest_route( $namespace, '/redirect/post', array(
$this->get_route( WP_REST_Server::READABLE, 'route_match_post', [ $this, 'permission_callback_manage' ] ),
'args' => [
'text' => [
'description' => 'Text to match',
'type' => 'string',
'required' => true,
],
],
) );
register_rest_route( $namespace, '/bulk/redirect/(?P<bulk>delete|enable|disable|reset)', array(
$this->get_route( WP_REST_Server::EDITABLE, 'route_bulk', [ $this, 'permission_callback_bulk' ] ),
'args' => array_merge( $this->get_filter_args( $orders, $filters ), [
'global' => [
'description' => 'Apply bulk action globally, as per filters',
'type' => 'boolean',
],
'items' => [
'description' => 'Array of IDs to perform action on',
'type' => 'array',
'items' => [
'description' => 'Item ID',
'type' => [ 'string', 'number' ],
],
],
] ),
) );
}
/**
* Checks a manage capability
*
* @param WP_REST_Request $request Request.
* @return Bool
*/
public function permission_callback_manage( WP_REST_Request $request ) {
return Redirection_Capabilities::has_access( Redirection_Capabilities::CAP_REDIRECT_MANAGE );
}
/**
* Checks a bulk capability
*
* @param WP_REST_Request $request Request.
* @return Bool
*/
public function permission_callback_bulk( WP_REST_Request $request ) {
if ( $request['bulk'] === 'delete' ) {
return Redirection_Capabilities::has_access( Redirection_Capabilities::CAP_REDIRECT_DELETE );
}
return $this->permission_callback_add( $request );
}
/**
* Checks a create capability
*
* @param WP_REST_Request $request Request.
* @return Bool
*/
public function permission_callback_add( WP_REST_Request $request ) {
return Redirection_Capabilities::has_access( Redirection_Capabilities::CAP_REDIRECT_ADD );
}
/**
* Get redirect list
*
* @param WP_REST_Request $request The request.
* @return WP_Error|array Return an array of results, or a WP_Error
*/
public function route_list( WP_REST_Request $request ) {
return Red_Item::get_filtered( $request->get_params() );
}
/**
* Get redirect list
*
* @param WP_REST_Request $request The request.
* @return WP_Error|array Return an array of results, or a WP_Error
*/
public function route_create( WP_REST_Request $request ) {
$params = $request->get_params();
$urls = array();
if ( isset( $params['url'] ) ) {
$urls = array( $params['url'] );
if ( is_array( $params['url'] ) ) {
$urls = $params['url'];
}
// Remove duplicates
$unique = [];
foreach ( $urls as $url ) {
$unique[ $url ] = $url;
}
foreach ( $unique as $url ) {
$params['url'] = $url;
// Data is sanitized in the create function
$redirect = Red_Item::create( $params );
if ( is_wp_error( $redirect ) ) {
return $this->add_error_details( $redirect, __LINE__ );
}
}
}
return $this->route_list( $request );
}
/**
* Update redirect
*
* @param WP_REST_Request $request The request.
* @return WP_Error|array Return an array of results, or a WP_Error
*/
public function route_update( WP_REST_Request $request ) {
$params = $request->get_params();
$redirect = Red_Item::get_by_id( intval( $params['id'], 10 ) );
if ( $redirect ) {
$result = $redirect->update( $params );
if ( is_wp_error( $result ) ) {
return $this->add_error_details( $result, __LINE__ );
}
return [ 'item' => $redirect->to_json() ];
}
return $this->add_error_details( new WP_Error( 'redirect_update_failed', 'Invalid redirect details' ), __LINE__ );
}
/**
* Perform bulk action on redirects
*
* @param WP_REST_Request $request The request.
* @return WP_Error|array Return an array of results, or a WP_Error
*/
public function route_bulk( WP_REST_Request $request ) {
$params = $request->get_params();
$action = sanitize_text_field( $request['bulk'] );
if ( isset( $params['items'] ) && is_array( $params['items'] ) ) {
$items = $params['items'];
foreach ( $items as $item ) {
$redirect = Red_Item::get_by_id( intval( $item, 10 ) );
if ( $redirect === false ) {
return $this->add_error_details( new WP_Error( 'redirect_bulk_failed', 'Invalid redirect' ), __LINE__ );
}
if ( $action === 'delete' ) {
$redirect->delete();
} elseif ( $action === 'disable' ) {
$redirect->disable();
} elseif ( $action === 'enable' ) {
$redirect->enable();
} elseif ( $action === 'reset' ) {
$redirect->reset();
}
}
} elseif ( isset( $params['global'] ) && $params['global'] ) {
// Params are sanitized in the filter class
if ( $action === 'delete' ) {
Red_Item::delete_all( $params );
} elseif ( $action === 'reset' ) {
Red_Item::reset_all( $params );
} elseif ( $action === 'enable' || $action === 'disable' ) {
Red_Item::set_status_all( $action, $params );
}
}
return $this->route_list( $request );
}
/**
* Search for a post
*
* @param WP_REST_Request $request The request.
* @return WP_Error|array Return an array of results, or a WP_Error
*/
public function route_match_post( WP_REST_Request $request ) {
global $wpdb;
$params = $request->get_params();
$search = sanitize_text_field( $params['text'] );
$results = [];
$posts = $wpdb->get_results(
$wpdb->prepare(
"SELECT ID,post_title,post_name FROM $wpdb->posts WHERE post_status='publish' AND (post_title LIKE %s OR post_name LIKE %s) " .
"AND post_type IN ('post','page')",
'%' . $wpdb->esc_like( $search ) . '%', '%' . $wpdb->esc_like( $search ) . '%'
)
);
foreach ( (array) $posts as $post ) {
$title = $post->post_name;
if ( strpos( $post->post_title, $search ) ) {
$title = $post->post_title;
}
$results[] = [
'title' => $title,
'value' => get_permalink( $post->ID ),
];
}
return $results;
}
}

View File

@@ -0,0 +1,145 @@
<?php
/**
* @api {get} /redirection/v1/setting Get settings
* @apiName GetSettings
* @apiDescription Get all settings for Redirection. This includes user-configurable settings, as well as necessary WordPress settings.
* @apiGroup Settings
*
* @apiUse SettingItem
* @apiUse 401Error
* @apiUse 404Error
*/
/**
* @api {post} /redirection/v1/setting Update settings
* @apiName UpdateSettings
* @apiDescription Update Redirection settings. Note you can do partial updates, and only the values specified will be changed.
* @apiGroup Settings
*
* @apiParam {Object} settings An object containing all the settings to update
* @apiParamExample {json} settings:
* {
* "expire_redirect": 14,
* "https": false
* }
*
* @apiUse SettingItem
* @apiUse 401Error
* @apiUse 404Error
*/
/**
* @apiDefine SettingItem Settings
* Redirection settings
*
* @apiSuccess {Object[]} settings An object containing all settings
* @apiSuccess {String} settings.expire_redirect
* @apiSuccess {String} settings.token
* @apiSuccess {String} settings.monitor_post
* @apiSuccess {String[]} settings.monitor_types
* @apiSuccess {String} settings.associated_redirect
* @apiSuccess {String} settings.auto_target
* @apiSuccess {String} settings.expire_redirect
* @apiSuccess {String} settings.expire_404
* @apiSuccess {String} settings.modules
* @apiSuccess {String} settings.redirect_cache
* @apiSuccess {String} settings.ip_logging
* @apiSuccess {String} settings.last_group_id
* @apiSuccess {String} settings.rest_api
* @apiSuccess {String} settings.https
* @apiSuccess {String} settings.headers
* @apiSuccess {String} settings.database
* @apiSuccess {String} settings.relocate Relocate this site to the specified domain (and path)
* @apiSuccess {String="www","nowww",""} settings.preferred_domain Preferred canonical domain
* @apiSuccess {String[]} settings.aliases Array of domains that will be redirected to the current WordPress site
* @apiSuccess {Object[]} groups An array of groups
* @apiSuccess {String} groups.label Name of the group
* @apiSuccess {Integer} groups.value Group ID
* @apiSuccess {String} installed The path that WordPress is installed in
* @apiSuccess {Boolean} canDelete True if Redirection can be deleted, false otherwise (on multisite, for example)
* @apiSuccess {String[]} post_types Array of WordPress post types
*
* @apiSuccessExample {json} Success-Response:
* HTTP/1.1 200 OK
* {
* "settings": {
* "expire_redirect": 7,
* "https": true
* },
* "groups": [
* { label: 'My group', value: 5 }
* ],
* "installed": "/var/html/wordpress",
* "canDelete": true,
* "post_types": [
* "post",
* "page"
* ]
* }
*/
class Redirection_Api_Settings extends Redirection_Api_Route {
public function __construct( $namespace ) {
register_rest_route( $namespace, '/setting', array(
$this->get_route( WP_REST_Server::READABLE, 'route_settings', [ $this, 'permission_callback_manage' ] ),
$this->get_route( WP_REST_Server::EDITABLE, 'route_save_settings', [ $this, 'permission_callback_manage' ] ),
) );
}
public function route_settings( WP_REST_Request $request ) {
if ( ! function_exists( 'get_home_path' ) ) {
include_once ABSPATH . '/wp-admin/includes/file.php';
}
return [
'settings' => red_get_options(),
'groups' => $this->groups_to_json( Red_Group::get_for_select() ),
'installed' => get_home_path(),
'canDelete' => ! is_multisite(),
'post_types' => red_get_post_types(),
];
}
public function permission_callback_manage( WP_REST_Request $request ) {
return Redirection_Capabilities::has_access( Redirection_Capabilities::CAP_OPTION_MANAGE ) || Redirection_Capabilities::has_access( Redirection_Capabilities::CAP_SITE_MANAGE );
}
public function route_save_settings( WP_REST_Request $request ) {
$params = $request->get_params();
$result = true;
if ( isset( $params['location'] ) && strlen( $params['location'] ) > 0 ) {
$module = Red_Module::get( 2 );
$result = $module->can_save( sanitize_text_field( $params['location'] ) );
}
red_set_options( $params );
$settings = $this->route_settings( $request );
if ( is_wp_error( $result ) ) {
$settings['warning'] = $result->get_error_message();
}
return $settings;
}
private function groups_to_json( $groups, $depth = 0 ) {
$items = array();
foreach ( $groups as $text => $value ) {
if ( is_array( $value ) && $depth === 0 ) {
$items[] = (object) array(
'label' => $text,
'value' => $this->groups_to_json( $value, 1 ),
);
} else {
$items[] = (object) array(
'label' => $value,
'value' => $text,
);
}
}
return $items;
}
}

View File

@@ -0,0 +1,196 @@
<?php
require_once __DIR__ . '/api-group.php';
require_once __DIR__ . '/api-redirect.php';
require_once __DIR__ . '/api-log.php';
require_once __DIR__ . '/api-404.php';
require_once __DIR__ . '/api-settings.php';
require_once __DIR__ . '/api-plugin.php';
require_once __DIR__ . '/api-import.php';
require_once __DIR__ . '/api-export.php';
define( 'REDIRECTION_API_NAMESPACE', 'redirection/v1' );
/**
* @apiDefine 401Error
*
* @apiError (Error 401) rest_forbidden You are not authorized to access this API endpoint
* @apiErrorExample {json} 401 Error Response:
* HTTP/1.1 401 Bad Request
* {
* "code": "rest_forbidden",
* "message": "Sorry, you are not allowed to do that."
* }
*/
/**
* @apiDefine 404Error
*
* @apiError (Error 404) rest_no_route Endpoint not found
* @apiErrorExample {json} 404 Error Response:
* HTTP/1.1 404 Not Found
* {
* "code": "rest_no_route",
* "message": "No route was found matching the URL and request method"
* }
*/
/**
* @apiDefine 400Error
*
* @apiError rest_forbidden You are not authorized to access this API endpoint
* @apiErrorExample {json} 400 Error Response:
* HTTP/1.1 400 Bad Request
* {
* "error": "invalid",
* "message": "Invalid request"
* }
*/
/**
* @apiDefine 400MissingError
* @apiError (Error 400) rest_missing_callback_param Some required parameters are not present or not in the correct format
* @apiErrorExample {json} 400 Error Response:
* HTTP/1.1 400 Bad Request
* {
* "code": "rest_missing_callback_param",
* "message": "Missing parameter(s): PARAM"
* }
*/
class Redirection_Api_Route {
protected function add_error_details( WP_Error $error, $line, $code = 400 ) {
global $wpdb;
$data = array(
'status' => $code,
'error_code' => $line,
);
if ( isset( $wpdb->last_error ) && $wpdb->last_error ) {
$data['wpdb'] = $wpdb->last_error;
}
$error->add_data( $data );
return $error;
}
public function permission_callback( WP_REST_Request $request ) {
return Redirection_Capabilities::has_access( Redirection_Capabilities::CAP_PLUGIN );
}
public function get_route( $method, $callback, $permissions = false ) {
return [
'methods' => $method,
'callback' => [ $this, $callback ],
'permission_callback' => $permissions ? $permissions : [ $this, 'permission_callback' ],
];
}
}
class Redirection_Api_Filter_Route extends Redirection_Api_Route {
public function validate_filter( $value, $request, $param ) {
$fields = $request->get_attributes()['args']['filterBy']['filter_fields'];
if ( ! is_array( $value ) ) {
return new WP_Error( 'rest_invalid_param', 'Filter is not an array', array( 'status' => 400 ) );
}
if ( ! empty( $fields ) ) {
foreach ( array_keys( $value ) as $key ) {
if ( ! in_array( $key, $fields, true ) ) {
return new WP_Error( 'rest_invalid_param', 'Filter type is not supported: ' . $key, array( 'status' => 400 ) );
}
}
}
return true;
}
protected function get_filter_args( $order_fields, $filters = [] ) {
return [
'filterBy' => [
'description' => 'Field to filter by',
'validate_callback' => [ $this, 'validate_filter' ],
'filter_fields' => $filters,
],
'orderby' => [
'description' => 'Field to order results by',
'type' => 'string',
'enum' => $order_fields,
],
'direction' => [
'description' => 'Direction of ordered results',
'type' => 'string',
'default' => 'desc',
'enum' => [ 'asc', 'desc' ],
],
'per_page' => [
'description' => 'Number of results per page',
'type' => 'integer',
'default' => 25,
'minimum' => 5,
'maximum' => RED_MAX_PER_PAGE,
],
'page' => [
'description' => 'Page offset',
'type' => 'integer',
'minimum' => 0,
'default' => 0,
],
];
}
/**
* Register a bulk action route
*
* @param string $namespace Namespace.
* @param string $route Route.
* @param Array $orders
* @param Array $filters
* @param Object $callback
* @param boolean $permissions
* @return void
*/
public function register_bulk( $namespace, $route, $orders, $filters, $callback, $permissions = false ) {
register_rest_route( $namespace, $route, array(
$this->get_route( WP_REST_Server::EDITABLE, $callback, $permissions ),
'args' => array_merge( $this->get_filter_args( $orders, $filters ), [
'items' => [
'description' => 'Comma separated list of item IDs to perform action on',
'type' => 'array',
'items' => [
'type' => 'string',
],
],
] ),
) );
}
}
class Redirection_Api {
private static $instance = null;
private $routes = array();
public static function init() {
if ( is_null( self::$instance ) ) {
self::$instance = new Redirection_Api();
}
return self::$instance;
}
public function __construct() {
global $wpdb;
$wpdb->hide_errors();
$this->routes[] = new Redirection_Api_Redirect( REDIRECTION_API_NAMESPACE );
$this->routes[] = new Redirection_Api_Group( REDIRECTION_API_NAMESPACE );
$this->routes[] = new Redirection_Api_Log( REDIRECTION_API_NAMESPACE );
$this->routes[] = new Redirection_Api_404( REDIRECTION_API_NAMESPACE );
$this->routes[] = new Redirection_Api_Settings( REDIRECTION_API_NAMESPACE );
$this->routes[] = new Redirection_Api_Plugin( REDIRECTION_API_NAMESPACE );
$this->routes[] = new Redirection_Api_Import( REDIRECTION_API_NAMESPACE );
$this->routes[] = new Redirection_Api_Export( REDIRECTION_API_NAMESPACE );
}
}