PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 4.8.2
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v4.8.2
4.9.1 4.9.0 4.8.2 4.8.1 4.8.0 4.7.0 4.6.2 4.6.1 4.6.0 4.5.6 4.5.5 4.5.4 4.5.3 4.5.2 4.5.1 4.5.0 4.4.1 4.4.0 3.3.4 3.4.0 3.4.1 3.4.2 3.5.0 3.5.1 3.5.2 All 199 releases
betterdocs / includes / Core / PluginInstaller.php

PluginInstaller.php in BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot 4.8.2, at includes/Core/PluginInstaller.php

272 lines 8.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace WPDeveloper\BetterDocs\Core;
3
4 if (!defined('ABSPATH')) {
5 exit;
6 } // Exit if accessed directly.
7
8 use \WP_Error;
9
10 class PluginInstaller
11 {
12 public function __construct() {
13 add_action( 'wp_ajax_wpdeveloper_auto_active_even_not_installed', [ $this, 'ajax_auto_active_even_not_installed' ] );
14 add_action( 'wp_ajax_wpdeveloper_install_plugin', [ $this, 'ajax_install_plugin' ] );
15 add_action( 'wp_ajax_wpdeveloper_upgrade_plugin', [ $this, 'ajax_upgrade_plugin' ] );
16 add_action( 'wp_ajax_wpdeveloper_activate_plugin', [ $this, 'ajax_activate_plugin' ] );
17 add_action( 'wp_ajax_wpdeveloper_deactivate_plugin', [ $this, 'ajax_deactivate_plugin' ] );
18 }
19
20 /**
21 * get_local_plugin_data
22 *
23 * @param mixed $basename
24 * @return array|false
25 */
26 public function get_local_plugin_data($basename = '')
27 {
28 if (empty($basename)) {
29 return false;
30 }
31
32 if (!function_exists('get_plugins')) {
33 include_once ABSPATH . 'wp-admin/includes/plugin.php';
34 }
35
36 $plugins = get_plugins();
37
38 if (!isset($plugins[$basename])) {
39 return false;
40 }
41
42 return $plugins[$basename];
43 }
44
45 /**
46 * get_remote_plugin_data
47 *
48 * @param mixed $slug
49 * @return mixed array|WP_Error
50 */
51 public function get_remote_plugin_data($slug = '')
52 {
53 if (empty($slug)) {
54 return new WP_Error('empty_arg', __('Argument should not be empty.', 'betterdocs'));
55 }
56
57 // Use core's plugins_api() instead of a hand-rolled request. The old code
58 // POSTed to plaintext http://api.wordpress.org and passed the response
59 // body straight to unserialize(), so anyone able to intercept that
60 // connection could inject a PHP-object-injection payload or a malicious
61 // download_link. plugins_api() talks to api.wordpress.org over HTTPS and
62 // returns a decoded object — no plaintext transport, no unserialize().
63 if (!function_exists('plugins_api')) {
64 include_once ABSPATH . 'wp-admin/includes/plugin-install.php';
65 }
66
67 $response = plugins_api('plugin_information', [
68 'slug' => $slug,
69 'fields' => [
70 'version' => true,
71 ],
72 ]);
73
74 if (is_wp_error($response) || !is_object($response)) {
75 return is_wp_error($response) ? $response : new WP_Error('plugins_api_failed', __('Could not retrieve plugin information.', 'betterdocs'));
76 }
77
78 // Bind the package to the requested slug and to an https WordPress.org
79 // host before anything installs it.
80 if (isset($response->slug) && $response->slug !== $slug) {
81 return new WP_Error('slug_mismatch', __('Plugin information did not match the requested plugin.', 'betterdocs'));
82 }
83
84 if (isset($response->download_link) && !$this->is_allowed_package_url($response->download_link)) {
85 return new WP_Error('bad_package_host', __('Plugin download URL is not an approved WordPress.org address.', 'betterdocs'));
86 }
87
88 return $response;
89 }
90
91 /**
92 * Whether a package URL is safe to hand to the upgrader: https on a
93 * WordPress.org host. Prevents a tampered response from redirecting the
94 * install to an attacker-controlled archive.
95 *
96 * @param string $url
97 * @return bool
98 */
99 protected function is_allowed_package_url($url)
100 {
101 if (!is_string($url) || $url === '') {
102 return false;
103 }
104
105 $parts = wp_parse_url($url);
106
107 if (empty($parts['scheme']) || strtolower($parts['scheme']) !== 'https' || empty($parts['host'])) {
108 return false;
109 }
110
111 $host = strtolower($parts['host']);
112
113 return in_array($host, ['downloads.wordpress.org', 'wordpress.org', 'www.wordpress.org'], true);
114 }
115
116 /**
117 * install_plugin
118 *
119 * @param mixed $slug
120 * @param bool $active
121 * @return mixed bool|WP_Error
122 */
123 public function install_plugin($slug = '', $active = true)
124 {
125 if (empty($slug)) {
126 return new WP_Error('empty_arg', __('Argument should not be empty.', 'betterdocs'));
127 }
128
129 include_once ABSPATH . 'wp-admin/includes/file.php';
130 include_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
131 include_once ABSPATH . 'wp-admin/includes/class-automatic-upgrader-skin.php';
132
133 $plugin_data = $this->get_remote_plugin_data($slug);
134
135 if (is_wp_error($plugin_data)) {
136 return $plugin_data;
137 }
138
139 $upgrader = new \Plugin_Upgrader(new \Automatic_Upgrader_Skin());
140
141 // install plugin
142 $install = $upgrader->install($plugin_data->download_link);
143
144 if (is_wp_error($install)) {
145 return $install;
146 }
147
148 // activate plugin
149 if ($install === true && $active) {
150 $active = activate_plugin($upgrader->plugin_info(), '', false, true);
151
152 if (is_wp_error($active)) {
153 return $active;
154 }
155
156 return $active === null;
157 }
158
159 return $install;
160 }
161
162 /**
163 * upgrade_plugin
164 *
165 * @param mixed $basename
166 * @return mixed bool|WP_Error
167 */
168 public function upgrade_plugin($basename = '')
169 {
170 if (empty($basename)) {
171 return new WP_Error('empty_arg', __('Argument should not be empty.', 'betterdocs'));
172 }
173
174 include_once ABSPATH . 'wp-admin/includes/file.php';
175 include_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
176 include_once ABSPATH . 'wp-admin/includes/class-automatic-upgrader-skin.php';
177
178 $upgrader = new \Plugin_Upgrader(new \Automatic_Upgrader_Skin());
179
180 // upgrade plugin
181 return $upgrader->upgrade($basename);
182 }
183
184 public function ajax_install_plugin()
185 {
186 check_ajax_referer('betterdocs-wpdeveloper-plugins', 'security');
187
188 if(!current_user_can( 'install_plugins' )) {
189 wp_send_json_error(__('you are not allowed to do this action', 'betterdocs'));
190 }
191
192 $slug = isset( $_POST['slug'] ) ? sanitize_text_field( wp_unslash( $_POST['slug'] ) ) : '';
193 $result = $this->install_plugin( $slug );
194
195 $promotype = isset( $_POST['promotype'] ) ? sanitize_text_field( wp_unslash( $_POST['promotype'] ) ) : '';
196 if ( 'eb-banner' === $promotype ) {
197 wp_remote_get( 'https://essential-addons.com/essential-blocks-install-gutenberg' );
198 }
199
200 if ( is_wp_error( $result ) ) {
201 wp_send_json_error( $result->get_error_message() );
202 }
203
204 wp_send_json_success(__('Plugin is installed successfully!', 'betterdocs'));
205 }
206
207 public function ajax_upgrade_plugin()
208 {
209 check_ajax_referer('betterdocs-wpdeveloper-plugins', 'security');
210 //check user capabilities
211 if(!current_user_can( 'update_plugins' )) {
212 wp_send_json_error(__('you are not allowed to do this action', 'betterdocs'));
213 }
214
215 $basename = isset( $_POST['basename'] ) ? sanitize_text_field( wp_unslash( $_POST['basename'] ) ) : '';
216 $result = $this->upgrade_plugin( $basename );
217
218 if (is_wp_error($result)) {
219 wp_send_json_error($result->get_error_message());
220 }
221
222 wp_send_json_success(__('Plugin is updated successfully!', 'betterdocs'));
223 }
224
225 public function ajax_activate_plugin()
226 {
227 check_ajax_referer('betterdocs-wpdeveloper-plugins', 'security');
228
229 //check user capabilities
230 if(!current_user_can( 'activate_plugins' )) {
231 wp_send_json_error(__('you are not allowed to do this action', 'betterdocs'));
232 }
233
234 $basename = isset( $_POST['basename'] ) ? sanitize_text_field( wp_unslash( $_POST['basename'] ) ) : '';
235 $result = activate_plugin( $basename, '', false, true );
236
237 if ( is_wp_error( $result ) ) {
238 wp_send_json_error( $result->get_error_message() );
239 }
240
241 if ($result === false) {
242 wp_send_json_error(__('Plugin couldn\'t be activated.', 'betterdocs'));
243 }
244 wp_send_json_success(__('Plugin is activated successfully!', 'betterdocs'));
245 }
246
247 public function ajax_deactivate_plugin() {
248 check_ajax_referer( 'betterdocs-wpdeveloper-plugins', 'security' );
249
250 //check user capabilities
251 if ( ! current_user_can( 'activate_plugins' ) ) {
252 wp_send_json_error( __( 'you are not allowed to do this action', 'betterdocs' ) );
253 }
254
255 $basename = isset( $_POST['basename'] ) ? sanitize_text_field( wp_unslash( $_POST['basename'] ) ) : '';
256 deactivate_plugins( $basename, true );
257
258 wp_send_json_success( __( 'Plugin is deactivated successfully!', 'betterdocs' ) );
259 }
260
261 public function ajax_auto_active_even_not_installed() {
262 check_ajax_referer( 'betterdocs-wpdeveloper-plugins', 'security' );
263
264 $basename = isset( $_POST['basename'] ) ? sanitize_text_field( wp_unslash( $_POST['basename'] ) ) : '';
265 if ( $this->get_local_plugin_data( $basename ) === false ) {
266 $this->ajax_install_plugin();
267 } else {
268 $this->ajax_activate_plugin();
269 }
270 }
271 }
272