PluginProbe
Darkify – Dark Mode & Night Mode for Website & Admin (Dark Theme Included) / 2.1.3
Darkify – Dark Mode & Night Mode for Website & Admin (Dark Theme Included) v2.1.3
2.1.3 2.1.2 2.1.1 2.1.0 2.0.4 2.0.3 2.0.2 2.0.1 2.0.0 1.5.5 1.5.4 1.5.3 1.5.2 1.5.1 1.5.0 trunk 1.0.1 1.1.0 1.2.0 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.3.0 All 57 releases
darkify / src / Admin / Rest / HelpRest.php

HelpRest.php in Darkify – Dark Mode & Night Mode for Website & Admin (Dark Theme Included) 2.1.3, at src/Admin/Rest/HelpRest.php

282 lines 11.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Help-page REST controller for the React admin SPA.
5 *
6 * Backs the Get Help page's "Recommended" tab: lists ThemeAtelier's free
7 * plugins from the WordPress.org API (cached in a transient) with live
8 * install/active state, and installs or activates them in one click.
9 *
10 * Ports the recommended-plugins behaviour of the retired
11 * `src/Admin/HelpPage/Help.php` onto this plugin's AbstractRestController
12 * pattern (mirroring the Chat Help plugin's HelpRest), so the classic Help
13 * page's plugin directory keeps working inside the new React admin. The
14 * transient stores the full author list and the visibility filter runs at
15 * response time, so the cache stays valid regardless of which plugins are
16 * hidden.
17 *
18 * @package darkify
19 * @subpackage darkify/src/Admin/Rest
20 * @author ThemeAtelier<themeatelierbd@gmail.com>
21 */
22
23 namespace ThemeAtelier\Darkify\Admin\Rest;
24
25 use WP_REST_Request;
26 use WP_REST_Response;
27
28 if (! defined('ABSPATH')) {
29 die;
30 }
31
32 class HelpRest extends AbstractRestController
33 {
34 /**
35 * Slug => main plugin file for ThemeAtelier plugins whose main file name
36 * differs from the slug. Used to detect installed/active state and to
37 * activate the right file.
38 *
39 * @var array<string,string>
40 */
41 const PLUGIN_FILES = [
42 'greet-bubble' => 'greet-bubble.php',
43 'domain-for-sale' => 'domain-for-sale.php',
44 'ask-faq' => 'ask-faq.php',
45 'attentive-security' => 'attentive-security.php',
46 'better-chat-support' => 'messenger-chat-support.php',
47 'bizreview' => 'bizreview.php',
48 'booklet-booking-system' => 'booklet.php',
49 'skype-chat' => 'skype-chat.php',
50 'chat-help' => 'chat-whatsapp.php',
51 'chat-telegram' => 'telegram-chat.php',
52 'chat-viber' => 'chat-viber-lite.php',
53 'click-to-dial' => 'click-to-dial.php',
54 'click-to-mail' => 'click-to-mail.php',
55 'darkify' => 'darkify.php',
56 'eventful' => 'eventful.php',
57 'eventful-for-elementor' => 'eventful-for-elementor.php',
58 'postify' => 'postify.php',
59 'idonate' => 'idonate.php',
60 ];
61
62 /**
63 * Slugs hidden from the Recommended tab — the same set the classic Darkify
64 * Help page hid (Help.php `$not_show_plugin_list`), which hides Darkify
65 * itself and leaves Chat Help, Eventful, Domain For Sale, Better Chat
66 * Support, Greet Bubble and Eventful for Elementor showing.
67 *
68 * @var array<int,string>
69 */
70 const HIDDEN_PLUGINS = [
71 'darkify',
72 'idonate',
73 'bizreview',
74 'chat-viber',
75 'chat-telegram',
76 'click-to-dial',
77 'chat-skype',
78 'skype-chat',
79 'click-to-mail',
80 'ask-faq',
81 'attentive-security',
82 'booklet-booking-system',
83 'postify',
84 ];
85
86 /** Transient key for the WordPress.org author-plugins response. */
87 const TRANSIENT = 'darkify_ta_plugins';
88
89 public function register_routes(): void
90 {
91 \register_rest_route(self::NS, '/help/recommended-plugins', [
92 'methods' => 'GET',
93 'callback' => [$this, 'get_recommended_plugins'],
94 'permission_callback' => [$this, 'can_manage'],
95 ]);
96
97 \register_rest_route(self::NS, '/help/install-plugin', [
98 'methods' => 'POST',
99 'callback' => [$this, 'install_plugin'],
100 'permission_callback' => static function () {
101 return \current_user_can('install_plugins');
102 },
103 ]);
104
105 \register_rest_route(self::NS, '/help/activate-plugin', [
106 'methods' => 'POST',
107 'callback' => [$this, 'activate_plugin_handler'],
108 'permission_callback' => static function () {
109 return \current_user_can('activate_plugins');
110 },
111 ]);
112 }
113
114 /**
115 * POST /help/install-plugin — install a wp.org plugin by slug.
116 */
117 public function install_plugin(WP_REST_Request $request): WP_REST_Response
118 {
119 $slug = \sanitize_key($request->get_param('slug'));
120 if (! $slug) {
121 return new WP_REST_Response(['success' => false, 'message' => \__('Invalid plugin slug.', 'darkify')], 400);
122 }
123
124 require_once ABSPATH . 'wp-admin/includes/plugin-install.php';
125 require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
126 require_once ABSPATH . 'wp-admin/includes/class-wp-ajax-upgrader-skin.php';
127
128 $api = \plugins_api('plugin_information', ['slug' => $slug, 'fields' => ['sections' => false]]);
129 if (\is_wp_error($api)) {
130 return new WP_REST_Response(['success' => false, 'message' => $api->get_error_message()], 400);
131 }
132
133 $skin = new \WP_Ajax_Upgrader_Skin();
134 $upgrader = new \Plugin_Upgrader($skin);
135 $result = $upgrader->install($api->download_link);
136
137 if (\is_wp_error($result)) {
138 return new WP_REST_Response(['success' => false, 'message' => $result->get_error_message()], 400);
139 }
140
141 if (false === $result) {
142 return new WP_REST_Response(['success' => false, 'message' => $skin->get_error_messages()], 400);
143 }
144
145 return new WP_REST_Response(['success' => true], 200);
146 }
147
148 /**
149 * POST /help/activate-plugin — activate an installed plugin by slug.
150 */
151 public function activate_plugin_handler(WP_REST_Request $request): WP_REST_Response
152 {
153 $slug = \sanitize_key($request->get_param('slug'));
154 if (! $slug) {
155 return new WP_REST_Response(['success' => false, 'message' => \__('Invalid plugin slug.', 'darkify')], 400);
156 }
157
158 $file = self::PLUGIN_FILES[$slug] ?? $slug . '.php';
159 $plugin_file = $slug . '/' . $file;
160
161 if (! \file_exists(WP_PLUGIN_DIR . '/' . $plugin_file)) {
162 return new WP_REST_Response(['success' => false, 'message' => \__('Plugin file not found.', 'darkify')], 400);
163 }
164
165 $result = \activate_plugin($plugin_file);
166 if (\is_wp_error($result)) {
167 return new WP_REST_Response(['success' => false, 'message' => $result->get_error_message()], 400);
168 }
169
170 return new WP_REST_Response(['success' => true], 200);
171 }
172
173 /**
174 * GET /help/recommended-plugins — ThemeAtelier's wp.org plugins with live
175 * install/active state, sorted by active installs.
176 */
177 public function get_recommended_plugins(): WP_REST_Response
178 {
179 $plugins_arr = \get_transient(self::TRANSIENT);
180 if (false === $plugins_arr) {
181 $args = (object) [
182 'author' => 'themeatelier',
183 'per_page' => '120',
184 'page' => '1',
185 'fields' => [
186 'slug',
187 'name',
188 'version',
189 'downloaded',
190 'active_installs',
191 'last_updated',
192 'rating',
193 'num_ratings',
194 'short_description',
195 'author',
196 ],
197 ];
198 $request = [
199 'action' => 'query_plugins',
200 'timeout' => 30,
201 'request' => \serialize($args),
202 ];
203 $response = \wp_remote_post('http://api.wordpress.org/plugins/info/1.0/', ['body' => $request]);
204
205 if (! \is_wp_error($response)) {
206 $plugins_arr = [];
207 $plugins = \unserialize($response['body']);
208
209 if (isset($plugins->plugins) && (\count($plugins->plugins) > 0)) {
210 foreach ($plugins->plugins as $pl) {
211 $plugins_arr[] = [
212 'slug' => $pl->slug,
213 'name' => \html_entity_decode($pl->name, ENT_QUOTES | ENT_HTML5, 'UTF-8'),
214 'version' => $pl->version,
215 'downloaded' => $pl->downloaded,
216 'active_installs' => $pl->active_installs,
217 'last_updated' => \strtotime($pl->last_updated),
218 'rating' => $pl->rating,
219 'num_ratings' => $pl->num_ratings,
220 'short_description' => \html_entity_decode($pl->short_description, ENT_QUOTES | ENT_HTML5, 'UTF-8'),
221 ];
222 }
223 }
224
225 \set_transient(self::TRANSIENT, $plugins_arr, 24 * HOUR_IN_SECONDS);
226 }
227 }
228
229 if (! \is_array($plugins_arr) || \count($plugins_arr) === 0) {
230 return new WP_REST_Response([], 200);
231 }
232
233 \array_multisort(\array_column($plugins_arr, 'active_installs'), SORT_DESC, $plugins_arr);
234
235 if (! \function_exists('is_plugin_active')) {
236 require_once ABSPATH . 'wp-admin/includes/plugin.php';
237 }
238
239 $result = [];
240 foreach ($plugins_arr as $plugin) {
241 $plugin_slug = $plugin['slug'];
242 if (\in_array($plugin_slug, self::HIDDEN_PLUGINS, true)) {
243 continue;
244 }
245
246 $plugin_file = self::PLUGIN_FILES[$plugin_slug] ?? $plugin_slug . '.php';
247
248 // A few wp.org icons aren't PNGs.
249 $image_type = 'png';
250 switch ($plugin_slug) {
251 case 'postify':
252 $image_type = 'jpg';
253 break;
254 case 'darkify':
255 $image_type = 'gif?rev=3301202';
256 break;
257 }
258
259 $is_installed = \file_exists(WP_PLUGIN_DIR . '/' . $plugin_slug . '/' . $plugin_file);
260 $is_active = $is_installed && \is_plugin_active($plugin_slug . '/' . $plugin_file);
261
262 $result[] = [
263 'slug' => $plugin_slug,
264 'name' => \html_entity_decode($plugin['name'], ENT_QUOTES | ENT_HTML5, 'UTF-8'),
265 'version' => $plugin['version'],
266 'active_installs' => $plugin['active_installs'],
267 'rating' => $plugin['rating'],
268 'num_ratings' => $plugin['num_ratings'],
269 'short_description' => \html_entity_decode($plugin['short_description'], ENT_QUOTES | ENT_HTML5, 'UTF-8'),
270 'icon' => 'https://ps.w.org/' . $plugin_slug . '/assets/icon-256x256.' . $image_type,
271 'is_installed' => $is_installed,
272 'is_active' => $is_active,
273 'install_url' => \wp_nonce_url(\self_admin_url('update.php?action=install-plugin&plugin=' . $plugin_slug), 'install-plugin_' . $plugin_slug),
274 'activate_url' => $is_installed ? \wp_nonce_url(\admin_url('plugins.php?action=activate&plugin=' . $plugin_slug . '/' . $plugin_file), 'activate-plugin_' . $plugin_slug . '/' . $plugin_file) : '',
275 'details_url' => \network_admin_url('plugin-install.php?tab=plugin-information&plugin=' . $plugin_slug . '&TB_iframe=true&width=600&height=550'),
276 ];
277 }
278
279 return new WP_REST_Response($result, 200);
280 }
281 }
282