-
Notifications
You must be signed in to change notification settings - Fork 13
Expand file tree
/
Copy pathController.php
More file actions
346 lines (319 loc) · 12.4 KB
/
Copy pathController.php
File metadata and controls
346 lines (319 loc) · 12.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
<?php
/**
* The admin-specific functionality of the plugin.
*
* Defines the plugin name, version, and two examples hooks for how to
* enqueue the admin-specific stylesheet and JavaScript.
*
* @package Reactpress
* @subpackage Reactpress/admin
* @author Marco Laspe <marco@rockiger.com>
*/
namespace ReactPress\Admin;
use LengthException;
use ReactPress\Admin\Utils;
use function \repr_log;
class Controller {
// # Controller functions
public static function add_page(string $appname, int $pageId, string $page_title) {
$app_options = Utils::get_app_options($appname);
$apptype = Utils::get_app_type($appname);
//# Check if the app allows adding of more URL slugs
if ($app_options && $app_options['allowsRouting'] && count($app_options['pageIds'])) {
echo wp_json_encode([
'status' => 0,
'message' => 'Apps with client-side routing can only be shown on one single page.'
]);
return;
}
// add slug to existing app_options
$inserted_page = Controller::insert_page($pageId, $page_title);
if (!$inserted_page['ID']) {
echo wp_json_encode([
'status' => 0,
'message' => $inserted_page['message']
]);
return;
}
$permalink = get_permalink($inserted_page['ID']);
$permalink = $permalink ? $permalink : '';
$app_options ? Utils::add_pageId_to_app_options($appname, $inserted_page['ID']) : Utils::add_app_options($appname, $inserted_page['ID']);
Controller::add_build_path($appname, $apptype);
if ($app_options['allowsRouting']) {
add_rewrite_rule('^' . wp_make_link_relative($permalink) . '/(.*)?', 'index.php?pagename=' . wp_make_link_relative($permalink), 'top');
Utils::set_public_url_for_dev_server($appname, $permalink);
}
flush_rewrite_rules();
$html_content = Controller::get_index_html_content($permalink, $apptype, $appname);
if (empty($html_content)) {
echo wp_json_encode([
'status' => 0,
'message' => 'Couldn\'t download page content.'
]);
} elseif (Controller::write_index_html($appname, $html_content, $apptype)) {
echo wp_json_encode([
'status' => 1,
'message' => 'Page added.',
'pageId' => $inserted_page['ID'],
'page_title' => $inserted_page['page_title'],
'permalink' => $permalink
]);
} else {
echo wp_json_encode([
'status' => 0,
'message' => 'Something went wrong.',
]);
}
}
public static function delete_page(string $appname, int $pageId, string $permalink) {
try {
$app_options_list = Utils::get_apps();
Utils::delete_page($app_options_list, $appname, $pageId);
Utils::unset_public_url_for_dev_server($appname, $permalink);
echo wp_json_encode(['status' => 1]);
} catch (\Exception $e) {
repr_log($e);
echo wp_json_encode(['status' => 0, 'message' => $e->getMessage()]);
}
}
public static function delete_react_app(string $appname) {
$options = get_option('repr_apps');
Utils::write_apps_option(array_filter(
$options,
fn ($el) => $el['appname'] !== $appname
));
$is_appdir_removed = repr_delete_directory(Utils::app_path($appname));
if ($is_appdir_removed) {
echo wp_json_encode([
'status' => 1,
'message' => 'App deleted.',
]);
} else {
echo wp_json_encode([
'status' => 1,
'message' => "Couldn't remove files. Please remove directory by hand.",
]);
}
}
public static function get_react_apps() {
$apps = Utils::get_apps();
echo wp_json_encode(['status' => 1, 'apps' => $apps]);
}
public static function update_index_html(string $appname, string $permalink) {
$apptype = Utils::get_app_type($appname);
$html_content = Controller::get_index_html_content($permalink, $apptype, $appname);
if (empty($html_content)) {
echo wp_json_encode([
'status' => 0,
'message' => 'Couldn\'t download page content.'
]);
} elseif (Controller::write_index_html($appname, $html_content, $apptype)) {
echo wp_json_encode([
'status' => 1,
'message' => 'Index.html updated.',
]);
} else {
echo wp_json_encode([
'status' => 0,
'message' => 'Index.html could not be updated.',
]);
}
}
// # Helper functions
/**
* Add the right build path to package.json
*
* @param string $appname
* @return int 0 if no success
* @since 1.2.0
*/
public static function add_build_path(string $appname, string $apptype = 'development_cra'): int {
$apppath = Utils::app_path($appname);
// We need the relative path, that we can deploy our
// built app to another server later.
$relative_apppath = Utils::app_path($appname, true);
$relative_apppath = $relative_apppath ? $relative_apppath : "/wp-content/reactpress/apps/{$appname}/";
if ($apptype === 'development_vite') {
$homepage = "{$relative_apppath}/dist/";
$path_vite_config = is_file("{$apppath}/vite.config.js") ? "{$apppath}/vite.config.js" : "{$apppath}/vite.config.ts";
$vite_config_contents = file_get_contents($path_vite_config);
if (!$vite_config_contents) {
return 0;
} elseif (stripos($vite_config_contents, $homepage)) {
return 1;
} else {
// add the base pathe to vite.config.*
file_put_contents(
$path_vite_config,
str_replace(
"export default defineConfig({\n plugins: [react()],\n})",
"export default defineConfig(({ command }) => {\n if (command === 'build') {\n return {\n base: \"{$homepage}\",\n plugins: [react()],\n }\n } else {\n return {\n plugins: [react()],\n }\n }\n})",
$vite_config_contents
)
);
return 2;
}
} elseif ($apptype === 'development_cra') {
$homepage = "{$relative_apppath}/build";
$path_package_json = "{$apppath}/package.json";
$package_json_contents = file_get_contents($path_package_json);
if (!$package_json_contents) {
return 0;
} elseif (stripos($package_json_contents, $homepage)) {
return 1;
} else {
// add the base path that images are correctly loaded
file_put_contents(
$path_package_json,
str_replace("react-scripts build", REPR_IS_WINDOWS ? "set PUBLIC_URL={$homepage}&&react-scripts build" : "PUBLIC_URL={$homepage} react-scripts build", $package_json_contents)
);
return 2;
}
}
return 0;
}
/**
* Downloads the content of the page with the given permalink and removes the
* the react assets of the build page, that we can use the content for our
* development server.
*
* @param $permalink
* @return string
* @since 1.0.0
*/
public static function get_index_html_content(string $permalink, string $apptype = 'development_cra', string $appname = ''): string {
$resp = wp_remote_get($permalink, ['timeout' => 1000, 'cookies' => $_COOKIE]);
$respCode = wp_remote_retrieve_response_code($resp);
if (200 == $respCode) {
$dom = new \DOMDocument();
libxml_use_internal_errors(true);
$dom->loadHTML(wp_remote_retrieve_body($resp));
$selector = new \DOMXPath($dom);
foreach ($selector->query('//*[starts-with(@id, "rp-react-app-asset-")]') as $node) {
$node->parentNode->removeChild($node);
}
$filtered_contents = $dom->saveHTML();
// re-add script tag for global reactPress variable
$readded_contents = str_replace('var reactPress', "<script>\nvar reactPress", $filtered_contents);
if ($apptype === 'development_vite') {
$apppath = Utils::app_path($appname);
$file_ending = is_file("{$apppath}/src/main.jsx") ? 'jsx' : 'tsx';
// add script tag after root div that link to src/main.tsx
$readded_contents = str_replace(
'<div id="root"></div>',
"<div id=\"root\"></div><script type=\"module\" src=\"/src/main.{$file_ending}\"></script>",
$filtered_contents
);
}
} else {
$readded_contents = '';
}
return $readded_contents;
}
/**
* Creates or updates a page with the given name and title.
*
* @param int $pageId
* @param string $page_title
* @since 1.0.0
*/
public static function insert_page(int $pageId, string $page_title) {
if ($pageId === -1) {
$result = wp_insert_post(
array(
'post_title' => $page_title,
'post_status' => 'publish',
'post_content' => REPR_REACT_ROOT_TAG,
'post_type' => "page",
// Assign page template using the relative path, it will be
// resolved to the fully qualified name at run-time
'page_template' => 'templates/react-page-template.php',
)
);
return $result
? ['status' => 'true', 'message' => 'Page created.', 'ID' => $result, 'page_title' => $page_title]
: ['status' => 'false', 'message' => "Couldn't create page.", "ID" => $result, 'page_title' => $page_title];
} else {
$page = get_post($pageId);
if ($page) {
// already we have data with this post name
if (strpos($page->post_content, '<div id="root"></div>') !== false) {
return ['status' => 'true', 'message' => 'Page with app already exists.', 'ID' => $page->ID, 'page_title' => $page_title];
}
$result = wp_update_post(
[
'ID' => $page->ID,
'post_content' => $page->post_content . "\n\n" . REPR_REACT_ROOT_TAG
]
);
return $result
? ['status' => 'true', 'message' => 'Add app to page.', 'ID' => $result, 'page_title' => $page->post_title]
: ['status' => 'false', 'message' => 'Couldn\'t add app to page.', 'ID' => 0, 'page_title' => ''];
}
}
}
public static function toggle_react_routing(string $appname) {
try {
$app_options = Utils::get_apps();
$new_options = array_map(function ($el) use ($appname) {
if ($el['appname'] === $appname) {
//# create new allowsRouting state
$allowsRouting = $el['allowsRouting'] ?? false;
$el['allowsRouting'] = !$allowsRouting;
//# change rewrite rules in wordpress
if ($el['allowsRouting']) {
// routing is only allowed for one single page
if (count($el['pages']) > 1) {
throw new LengthException('Client-side routing is only possible on one single page.');
}
foreach ($el['pages'] as $page) {
add_rewrite_rule(
'^' .
// Trim leading and trailing slashes to get `^foo/bar/(.*)?` not `^/foo/bar//(.*)?`
trim(wp_make_link_relative($page['permalink']), '/') .
'/(.*)?',
'index.php?pagename=' .
// Trim leading and trailing slashes to get `index.php?pagename=foo/bar` not `index.php?pagename=/foo/bar/`
trim(wp_make_link_relative($page['permalink']), '/'),
'top'
);
Utils::set_public_url_for_dev_server($appname, $page['permalink']);
}
flush_rewrite_rules();
} else {
foreach ($el['pages'] as $page) {
Utils::remove_rewrite_rule('^' . wp_make_link_relative($page['permalink']) . '/(.*)?');
Utils::unset_public_url_for_dev_server($appname, $page['permalink']);
}
flush_rewrite_rules();
}
return $el;
}
return $el;
}, $app_options);
Utils::write_apps_option($new_options);
echo wp_json_encode(['status' => 1]);
} catch (\Exception $e) {
repr_log($e);
echo wp_json_encode(['status' => 0, 'message' => $e->getMessage()]);
}
}
/**
* Writes the given to the index.html file in the app directory of
* the given appname and produces if the writing of the file succeded or not.
*
* @param string $appname
* @param string $content
* @since 1.0.0
*/
public static function write_index_html(string $appname, string $content, string $apptype = 'development_cra') {
if ($apptype === 'development_vite') {
$index_html_path = sprintf("%s/%s/index.html", REPR_APPS_PATH, $appname);
return file_put_contents($index_html_path, $content);
} elseif ($apptype === 'development_cra') {
$index_html_path = sprintf("%s/%s/public/index.html", REPR_APPS_PATH, $appname);
return file_put_contents($index_html_path, $content);
}
return true;
}
}