PluginProbe
Optimole – Optimize Images | Convert WebP & AVIF | CDN & Lazy Load | Image Optimization / 4.2.11
Optimole – Optimize Images | Convert WebP & AVIF | CDN & Lazy Load | Image Optimization v4.2.11
4.2.13 4.2.12 4.2.11 4.2.10 4.2.9 4.2.8 4.2.7 4.2.6 4.2.5 2.5.5 2.5.6 2.5.7 3.0.0 3.0.1 3.1.0 3.1.1 3.1.2 3.1.3 3.10.0 3.11.0 3.11.1 3.11.2 3.11.3 3.12.0 3.12.1 All 134 releases
optimole-wp / vendor / codeinwp / themeisle-sdk / src / Modules / Featured_plugins.php

Featured_plugins.php in Optimole – Optimize Images | Convert WebP & AVIF | CDN & Lazy Load | Image Optimization 4.2.11, at vendor/codeinwp/themeisle-sdk/src/Modules/Featured_plugins.php

352 lines 9.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * File responsible for showing plugins inside the featured tab.
4 *
5 * This is used to display information about limited events, such as Black Friday.
6 *
7 * @package ThemeIsleSDK
8 * @subpackage Modules
9 * @copyright Copyright (c) 2017, Marius Cristea
10 * @license http://opensource.org/licenses/gpl-3.0.php GNU Public License
11 * @since 3.3.0
12 */
13 namespace ThemeisleSDK\Modules;
14
15 use ThemeisleSDK\Common\Abstract_Module;
16 use ThemeisleSDK\Loader;
17 use ThemeisleSDK\Product;
18
19 // Exit if accessed directly.
20 if ( ! defined( 'ABSPATH' ) ) {
21 exit;
22 }
23
24 /**
25 * Featured_Plugins module for the ThemeIsle SDK.
26 */
27 class Featured_Plugins extends Abstract_Module {
28
29 /**
30 * The transient key prefix.
31 *
32 * @var string $transient_key
33 */
34 private $transient_key = 'themeisle_sdk_featured_plugins_';
35
36 /**
37 * The current product instance.
38 *
39 * @var Product|null
40 */
41 protected $product = null;
42
43 /**
44 * Check if the module can be loaded.
45 *
46 * @param Product $product Product data.
47 *
48 * @return bool
49 */
50 public function can_load( $product ) {
51 if ( $this->is_from_partner( $product ) ) {
52 return false;
53 }
54
55 if ( $product->is_wordpress_available() ) {
56 return false;
57 }
58
59 return ! apply_filters( 'themeisle_sdk_disable_featured_plugins', false );
60 }
61
62 /**
63 * Load the module for the selected product.
64 *
65 * @param Product $product Product data.
66 *
67 * @return void
68 */
69 public function load( $product ) {
70 $this->product = $product;
71
72 if ( ! current_user_can( 'install_plugins' ) ) {
73 return;
74 }
75
76 // bail if we already registered a filter for the plugin API.
77 if ( apply_filters( 'themeisle_sdk_plugin_api_filter_registered', false ) ) {
78 return;
79 }
80 add_filter( 'themeisle_sdk_plugin_api_filter_registered', '__return_true' );
81
82 add_filter( 'plugins_api_result', [ $this, 'filter_plugin_api_results' ], 11, 3 );
83
84 // Enqueue inline JS only on plugin-install.php.
85 add_action( 'admin_enqueue_scripts', [ $this, 'maybe_add_inline_js' ] );
86 }
87
88 /**
89 * Enqueue inline JavaScript only on plugin-install.php.
90 *
91 * @return void
92 */
93 public function maybe_add_inline_js() {
94 $screen = get_current_screen();
95 if ( isset( $screen->base ) && 'plugin-install' === $screen->base ) {
96 add_action(
97 'admin_footer',
98 function() {
99 $text = esc_html( sprintf( Loader::$labels['promotions']['recommended'], $this->product->get_friendly_name() ) );
100
101 echo '<script>(function(){
102 function onPluginCardFound(card) {
103 var recommendedDiv = document.createElement("div");
104 Object.assign(recommendedDiv.style, {
105 display: "block",
106 textAlign: "center",
107 padding: "0 12px 12px",
108 background: "#f6f7f7"
109 });
110 recommendedDiv.innerHTML = "' . esc_html( $text ) . '";
111 card.appendChild(recommendedDiv);
112 }
113
114 function checkAndRun() {
115 var card = document.querySelector(".plugin-card-learning-management-system");
116 if (card && !card.dataset.recommendedAdded) {
117 onPluginCardFound(card);
118 card.dataset.recommendedAdded = "true";
119 }
120 }
121
122 var observer = new MutationObserver(function(mutations) {
123 checkAndRun();
124 });
125
126 observer.observe(document.body, { childList: true, subtree: true });
127
128 // Initial check in case the card is already present.
129 checkAndRun();
130 })();</script>';
131 }
132 );
133 }
134 }
135
136 /**
137 * Filter the plugin API results to include the featured plugins.
138 *
139 * @param object $res The result object.
140 * @param string $action The type of information being requested from the Plugin Install API.
141 * @param object $args Plugin API arguments.
142 *
143 * @return object
144 */
145 public function filter_plugin_api_results( $res, $action, $args ) {
146
147 if ( 'query_plugins' !== $action ) {
148 return $res;
149 }
150
151 if ( isset( $args->page ) && 1 === (int) $args->page && isset( $args->search ) && ! empty( $args->search ) ) {
152 $original_count = count( (array) $res->plugins );
153 $res->plugins = $this->maybe_prepend_lms_plugin( $res->plugins, $args );
154
155 return $this->adjust_results_count( $res, count( (array) $res->plugins ) - $original_count );
156 }
157
158 if ( ! isset( $args->browse ) || $args->browse !== 'featured' ) {
159 return $res;
160 }
161
162 // Inject only on the first page so the same plugins are not repeated on every page.
163 if ( isset( $args->page ) && (int) $args->page > 1 ) {
164 return $res;
165 }
166
167 $featured = $this->query_plugins_by_author( $args );
168
169 $original_count = count( (array) $res->plugins );
170 $plugins = $this->remove_plugins_by_slug( (array) $res->plugins, $this->get_plugin_slugs( $featured ) );
171 $res->plugins = array_merge( $featured, $plugins );
172
173 return $this->adjust_results_count( $res, count( $res->plugins ) - $original_count );
174 }
175
176 /**
177 * Prepend the LMS plugin if the search query matches LMS-related terms.
178 *
179 * @param array $plugins The plugins array.
180 * @param object $args The plugin API arguments.
181 * @return array
182 */
183 private function maybe_prepend_lms_plugin( $plugins, $args ) {
184 $search = isset( $args->search ) ? strtolower( $args->search ) : '';
185 if ( $this->matches_lms_search_keywords( $search ) ) {
186 $filter_slugs = apply_filters( 'themeisle_sdk_masteriyo_filter_slugs', [ 'learning-management-system' ] );
187 $masteriyo = $this->get_plugins_filtered_from_author( $args, $filter_slugs, 'masteriyo' );
188
189 if ( ! empty( $masteriyo ) ) {
190 // Remove existing copies of the injected plugins to avoid duplicates.
191 $plugins = $this->remove_plugins_by_slug( (array) $plugins, $this->get_plugin_slugs( $masteriyo ) );
192 $plugins = array_merge( $masteriyo, $plugins );
193 }
194 }
195 return $plugins;
196 }
197
198 /**
199 * Extract the slugs from a list of plugin entries.
200 *
201 * @param array $plugins The plugins list.
202 *
203 * @return array
204 */
205 private function get_plugin_slugs( $plugins ) {
206 $slugs = [];
207 foreach ( $plugins as $plugin ) {
208 $plugin = (array) $plugin;
209 if ( isset( $plugin['slug'] ) ) {
210 $slugs[] = $plugin['slug'];
211 }
212 }
213
214 return $slugs;
215 }
216
217 /**
218 * Remove the plugins matching the provided slugs from the list.
219 *
220 * @param array $plugins The plugins list.
221 * @param array $slugs The slugs to remove.
222 *
223 * @return array
224 */
225 private function remove_plugins_by_slug( $plugins, $slugs ) {
226 return array_values(
227 array_filter(
228 $plugins,
229 function( $plugin ) use ( $slugs ) {
230 $plugin = (array) $plugin;
231
232 return ! isset( $plugin['slug'] ) || ! in_array( $plugin['slug'], $slugs, true );
233 }
234 )
235 );
236 }
237
238 /**
239 * Adjust the reported total results count after injecting plugins.
240 *
241 * @param object $res The result object.
242 * @param int $delta The net number of plugins added to the list.
243 *
244 * @return object
245 */
246 private function adjust_results_count( $res, $delta ) {
247 if ( 0 !== $delta && isset( $res->info['results'] ) && is_numeric( $res->info['results'] ) ) {
248 $res->info['results'] = max( 0, (int) $res->info['results'] + $delta );
249 }
250
251 return $res;
252 }
253
254 /**
255 * Check if a plugin search query matches LMS-related terms.
256 *
257 * @param string $search Search query.
258 *
259 * @return bool True if the search query matches LMS-related terms.
260 */
261 private function matches_lms_search_keywords( $search ) {
262 $lms_keywords = array(
263 'lms',
264 'learn',
265 'course',
266 'courses',
267 'learning',
268 'academy',
269 'training',
270 'student',
271 'students',
272 'quiz',
273 );
274
275 foreach ( $lms_keywords as $keyword ) {
276 if ( preg_match( '/(^|[^a-z0-9])' . preg_quote( $keyword, '/' ) . '([^a-z0-9]|$)/', $search ) === 1 ) {
277 return true;
278 }
279 }
280
281 return false;
282 }
283
284 /**
285 * Query plugins by author.
286 *
287 * @param object $args The arguments for the query.
288 *
289 * @return array
290 */
291 private function query_plugins_by_author( $args ) {
292 $featured = [];
293
294 $optimole_filter_slugs = apply_filters( 'themeisle_sdk_optimole_filter_slugs', [ 'optimole-wp' ] );
295 $filtered_from_optimole = $this->get_plugins_filtered_from_author( $args, $optimole_filter_slugs, 'Optimole' );
296 $featured = array_merge( $featured, $filtered_from_optimole );
297
298 $themeisle_filter_slugs = apply_filters( 'themeisle_sdk_themeisle_filter_slugs', [ 'otter-blocks', 'wp-cloudflare-page-cache' ] );
299 $filtered_from_themeisle = $this->get_plugins_filtered_from_author( $args, $themeisle_filter_slugs );
300 $featured = array_merge( $featured, $filtered_from_themeisle );
301
302 return $featured;
303 }
304
305 /**
306 * Get plugins filtered from an author.
307 *
308 * @param object $args The arguments for the query.
309 * @param array $filter_slugs The slugs to filter.
310 * @param string $author The author to filter.
311 *
312 * @return array
313 */
314 protected function get_plugins_filtered_from_author( $args, $filter_slugs = [], $author = 'Themeisle' ) {
315
316 $cached = get_transient( $this->transient_key . $author );
317 if ( false !== $cached ) {
318 return (array) $cached;
319 }
320
321 $new_args = [
322 'page' => 1,
323 'per_page' => 36,
324 'locale' => get_user_locale(),
325 'author' => $author,
326 'wp_version' => isset( $args->wp_version ) ? $args->wp_version : get_bloginfo( 'version' ),
327 ];
328
329 $api = plugins_api( 'query_plugins', $new_args );
330 if ( is_wp_error( $api ) ) {
331 // Cache the failure for a shorter period to avoid hammering the API on every request.
332 set_transient( $this->transient_key . $author, [], HOUR_IN_SECONDS );
333
334 return [];
335 }
336
337 $filtered = array_values(
338 array_filter(
339 $api->plugins,
340 function( $plugin ) use ( $filter_slugs ) {
341 $array_plugin = (array) $plugin;
342 return in_array( $array_plugin['slug'], $filter_slugs );
343 }
344 )
345 );
346
347 set_transient( $this->transient_key . $author, $filtered, 12 * HOUR_IN_SECONDS );
348
349 return $filtered;
350 }
351 }
352