PluginProbe
Redirection / 5.8.1
Redirection v5.8.1
5.10.0 5.9.0 5.8.1 5.8.0 3.7.2 3.7.3 4.0 4.0.1 4.1 4.1.1 4.2 4.2.1 4.2.2 4.2.3 4.3 4.3.1 4.3.2 4.3.3 4.4 4.4.1 4.4.2 4.5 4.5.1 4.6.2 4.7.1 All 130 releases
redirection / api / api-import.php

api-import.php in Redirection 5.8.1, at api/api-import.php

200 lines 6.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * @api {get} /redirection/v1/import/file/:group_id Import redirects
5 * @apiName Import
6 * @apiDescription Import redirects from CSV, JSON, or Apache .htaccess
7 * @apiGroup Import/Export
8 *
9 * @apiParam (URL) {Integer} :group_id The group ID to import into
10 * @apiParam (File) {File} file The multipart form upload containing the file to import
11 *
12 * @apiSuccess {Integer} imported Number of items imported
13 *
14 * @apiUse 401Error
15 * @apiUse 404Error
16 * @apiError (Error 400) redirect_import_invalid_group Invalid group
17 * @apiErrorExample {json} 404 Error Response:
18 * HTTP/1.1 400 Bad Request
19 * {
20 * "code": "redirect_import_invalid_group",
21 * "message": "Invalid group"
22 * }
23 * @apiError (Error 400) redirect_import_invalid_file Invalid file upload
24 * @apiErrorExample {json} 404 Error Response:
25 * HTTP/1.1 400 Bad Request
26 * {
27 * "code": "redirect_import_invalid_file",
28 * "message": "Invalid file upload"
29 * }
30 */
31 /**
32 * @phpstan-type ImportPluginPayload array{
33 * plugin?: string|list<string>
34 * }
35 * @phpstan-type ImportFileParams array{
36 * file?: array{
37 * tmp_name: string,
38 * name: string,
39 * size: int,
40 * type: string,
41 * error: int
42 * }
43 * }
44 */
45 class Redirection_Api_Import extends Redirection_Api_Route {
46 /**
47 * @param non-falsy-string $api_namespace REST namespace.
48 */
49 public function __construct( $api_namespace ) {
50 // POST /import/file/:group_id - Import from file upload
51 register_rest_route(
52 $api_namespace,
53 '/import/file/(?P<group_id>\d+)',
54 [
55 [
56 'methods' => WP_REST_Server::EDITABLE,
57 'callback' => [ $this, 'route_import_file' ],
58 'permission_callback' => [ $this, 'permission_callback_manage' ],
59 ],
60 ]
61 );
62
63 // GET/POST /import/plugin - List or import from plugins
64 register_rest_route(
65 $api_namespace,
66 '/import/plugin',
67 [
68 [
69 'methods' => WP_REST_Server::READABLE,
70 'callback' => [ $this, 'route_plugin_import_list' ],
71 'permission_callback' => [ $this, 'permission_callback_manage' ],
72 ],
73 [
74 'methods' => WP_REST_Server::EDITABLE,
75 'callback' => [ $this, 'route_plugin_import' ],
76 'permission_callback' => [ $this, 'permission_callback_manage' ],
77 ],
78 ]
79 );
80 }
81
82 /**
83 * Permission callback used for import routes.
84 *
85 * @param WP_REST_Request $_request Request (unused).
86 * @phpstan-param WP_REST_Request<array<string, mixed>> $_request
87 * @return bool
88 */
89 public function permission_callback_manage( WP_REST_Request $_request ) {
90 return Redirection_Capabilities::has_access( Redirection_Capabilities::CAP_IO_MANAGE );
91 }
92
93 /**
94 * List available plugin importers.
95 *
96 * @param WP_REST_Request $request Request.
97 * @phpstan-param WP_REST_Request<array<string, mixed>> $request
98 * @phpstan-return array{importers: array<int, mixed>}
99 * @return array{importers: array}
100 */
101 public function route_plugin_import_list( WP_REST_Request $request ) {
102 include_once dirname( __DIR__ ) . '/models/importer.php';
103
104 return array( 'importers' => Red_Plugin_Importer::get_plugins() );
105 }
106
107 /**
108 * Import redirects using selected plugin importers.
109 *
110 * @param WP_REST_Request $request Request.
111 * @phpstan-param WP_REST_Request<array<string, mixed>> $request
112 * @phpstan-return array{imported: int}|WP_Error
113 * @return array{imported: int}|WP_Error
114 */
115 public function route_plugin_import( WP_REST_Request $request ) {
116 include_once dirname( __DIR__ ) . '/models/importer.php';
117
118 $params = $request->get_params();
119 /** @var ImportPluginPayload $params */
120 $groups = Red_Group::get_all();
121 /** @var array<array{id: int, name: string, redirects: int, module_id: int, moduleName: string, enabled: bool, default?: bool}> $groups */
122 $plugin_param = $params['plugin'] ?? $request->get_param( 'plugin' );
123 if ( is_array( $plugin_param ) ) {
124 $plugins = array_map( 'strval', $plugin_param );
125 } elseif ( $plugin_param === null ) {
126 $plugins = [];
127 } else {
128 $plugins = [ (string) $plugin_param ];
129 }
130 /** @var list<string> $plugins */
131 $plugins = array_map( 'sanitize_text_field', $plugins );
132 $total = 0;
133
134 if ( count( $groups ) === 0 ) {
135 return $this->add_error_details(
136 new WP_Error( 'redirect_import_invalid_group', 'No groups are available for import' ),
137 __LINE__
138 );
139 }
140
141 foreach ( $plugins as $plugin ) {
142 $total += Red_Plugin_Importer::import( $plugin, $groups[0]['id'] );
143 }
144
145 return [ 'imported' => $total ];
146 }
147
148 /**
149 * Import redirects from an uploaded file.
150 *
151 * @param WP_REST_Request $request Request.
152 * @phpstan-param WP_REST_Request<array<string, mixed>> $request
153 * @phpstan-return array{imported: int}|WP_Error
154 * @return array{imported: int}|WP_Error
155 */
156 public function route_import_file( WP_REST_Request $request ) {
157 $file_params = $request->get_file_params();
158 /** @var ImportFileParams $file_params */
159 $group_id = intval( $request['group_id'], 10 );
160
161 if ( ! isset( $file_params['file'] ) || ! is_uploaded_file( $file_params['file']['tmp_name'] ) ) {
162 return $this->add_error_details( new WP_Error( 'redirect_import_invalid_file', 'Invalid file upload' ), __LINE__ );
163 }
164
165 $upload = $file_params['file'];
166 $parts = pathinfo( $upload['name'] );
167 $extension = isset( $parts['extension'] ) ? strtolower( $parts['extension'] ) : '';
168
169 // JSON imports don't need a group, but all other formats do
170 if ( $extension !== 'json' ) {
171 $group = Red_Group::get( $group_id );
172 if ( $group === false ) {
173 return $this->add_error_details( new WP_Error( 'redirect_import_invalid_group', 'Invalid group' ), __LINE__ );
174 }
175 }
176
177 $count = Red_FileIO::import( $group_id, $upload );
178
179 // Import failure returns 0, but 0 can also mean no valid redirects in file
180 // For JSON files, pre-validate to distinguish between invalid JSON and empty/no-redirects
181 if ( $count === 0 && $extension === 'json' ) {
182 // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents -- Local file read
183 $content = file_get_contents( $upload['tmp_name'] );
184 if ( $content !== false ) {
185 json_decode( $content, true );
186 if ( json_last_error() !== JSON_ERROR_NONE ) {
187 return $this->add_error_details(
188 new WP_Error( 'redirect_import_invalid_json', 'Invalid JSON file: ' . json_last_error_msg() ),
189 __LINE__
190 );
191 }
192 }
193 }
194
195 return array(
196 'imported' => $count,
197 );
198 }
199 }
200