PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 6.31
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v6.31
6.35 6.34 6.33.1 6.33 6.32.1 6.32 6.31 6.25 6.25.1 6.26 6.26.1 6.27 6.28 6.29 6.3 6.3.1 6.3.2 6.30 6.4 6.4.1 6.4.2 6.5 6.5.1 6.5.2 6.5.3 All 141 releases
formidable / classes / models / FrmPluginSearch.php

FrmPluginSearch.php in Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More 6.31, at classes/models/FrmPluginSearch.php

387 lines 10.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if ( ! defined( 'ABSPATH' ) ) {
3 die( 'You are not allowed to call this page directly.' );
4 }
5
6 class FrmPluginSearch {
7
8 /**
9 * PSH slug name.
10 *
11 * @var string
12 */
13 public static $slug = 'frm-plugin-search';
14
15 /**
16 * Option name that holds dismissed suggestions.
17 *
18 * @var string
19 */
20 protected static $dismissed_opt = 'frm_dismissed_hints';
21
22 public function __construct() {
23 add_action( 'current_screen', array( $this, 'start' ) );
24 }
25
26 /**
27 * Add actions and filters only if this is the plugin installation screen and it's the first page.
28 *
29 * @since 4.12
30 *
31 * @param object $screen WP Screen object.
32 *
33 * @return void
34 */
35 public function start( $screen ) {
36 // phpcs:ignore WordPress.Security.NonceVerification.Recommended
37 if ( 'plugin-install' !== $screen->base || ( isset( $_GET['paged'] ) && 1 !== intval( $_GET['paged'] ) ) ) {
38 return;
39 }
40
41 add_filter( 'plugins_api_result', array( $this, 'inject_suggestion' ), 10, 3 );
42 add_filter( 'self_admin_url', array( $this, 'plugin_details' ) );
43 add_filter( 'plugin_install_action_links', array( $this, 'insert_related_links' ), 10, 2 );
44 add_action( 'admin_enqueue_scripts', array( $this, 'load_plugins_search_script' ) );
45 $this->maybe_dismiss();
46 }
47
48 /**
49 * Intercept the plugins API response and add in an appropriate card.
50 *
51 * @param object $result Plugin search results.
52 * @param string $action unused.
53 * @param object $args Search args.
54 *
55 * @return object
56 */
57 public function inject_suggestion( $result, $action, $args ) {
58 // Looks like a search query; it's matching time.
59 if ( empty( $args->search ) ) {
60 return $result;
61 }
62
63 if ( is_wp_error( $result ) ) {
64 // This may happen if the request failed due to an internet connection issue.
65 return $result;
66 }
67
68 $addon_list = $this->get_addons();
69
70 // Lowercase, trim, remove punctuation/special chars, decode url, remove 'formidable'.
71 $normalized_term = $this->search_to_array( $args->search );
72
73 if ( ! $normalized_term ) {
74 // Don't add anything extra.
75 return $result;
76 }
77
78 $matching_addon = $this->matching_addon( $addon_list, $normalized_term );
79
80 if ( ! $matching_addon || ! $this->should_display_hint( $matching_addon ) ) {
81 return $result;
82 }
83
84 $inject = (array) $this->get_plugin_data();
85 $overrides = array(
86 // Helps to determine if that an injected card.
87 'plugin-search' => true,
88 'name' => sprintf(
89 /* translators: Formidable addon name */
90 esc_html_x( 'Formidable %s', 'Formidable Addon Name', 'formidable' ),
91 $addon_list[ $matching_addon ]['name']
92 ),
93 'addon' => $addon_list[ $matching_addon ]['slug'],
94 'short_description' => $addon_list[ $matching_addon ]['excerpt'],
95 'slug' => self::$slug,
96 'version' => $addon_list[ $matching_addon ]['version'],
97 );
98
99 if ( ! empty( $addon_list[ $matching_addon ]['external'] ) ) {
100 unset( $overrides['name'] );
101 }
102
103 // Splice in the base addon data.
104 $inject = array_merge( $inject, $addon_list[ $matching_addon ], $overrides );
105
106 // Add it to the top of the list.
107 array_unshift( $result->plugins, $inject );
108
109 return $result;
110 }
111
112 /**
113 * Search for any addons that match the searched terms.
114 *
115 * @since 4.12
116 *
117 * @param array $addon_list
118 * @param array $normalized_term
119 *
120 * @return int|null
121 */
122 private function matching_addon( $addon_list, $normalized_term ) {
123 $matching_addon = null;
124
125 // Try to match a passed search term with addon's search terms.
126 foreach ( $addon_list as $addon_id => $addon_opts ) {
127 if ( ! is_array( $addon_opts ) || empty( $addon_opts['excerpt'] ) ) {
128 continue;
129 }
130
131 if ( ! isset( $addon_opts['search_terms'] ) ) {
132 $addon_opts['search_terms'] = '';
133 }
134
135 $addon_terms = $this->search_to_array( $addon_opts['search_terms'] . ' ' . $addon_opts['name'] );
136 $matched_terms = array_intersect( $addon_terms, $normalized_term );
137
138 if ( count( $matched_terms ) === count( $normalized_term ) ) {
139 $matching_addon = $addon_id;
140 break;
141 }
142 }
143
144 return $matching_addon;
145 }
146
147 /**
148 * @since 4.12
149 *
150 * @return array
151 */
152 private function get_addons() {
153 $api = new FrmFormApi();
154 $addons = $api->get_api_info();
155 return apply_filters( 'frm_plugin_search', $addons );
156 }
157
158 /**
159 * Get the plugin repo's data to populate the fields with.
160 *
161 * @return array|mixed|object|WP_Error
162 */
163 private function get_plugin_data() {
164 $data = get_transient( 'formidable_plugin_data' );
165
166 if ( false !== $data && ! is_wp_error( $data ) ) {
167 return $data;
168 }
169
170 include_once ABSPATH . 'wp-admin/includes/plugin-install.php';
171 $data = plugins_api(
172 'plugin_information',
173 array(
174 'slug' => 'formidable',
175 'is_ssl' => is_ssl(),
176 'fields' => array(
177 'banners' => true,
178 'reviews' => true,
179 'active_installs' => true,
180 'versions' => false,
181 'sections' => false,
182 ),
183 )
184 );
185 set_transient( 'formidable_plugin_data', $data, DAY_IN_SECONDS );
186
187 return $data;
188 }
189
190 /**
191 * Modify URL used to fetch to plugin information so it pulls Formidable plugin page.
192 *
193 * @since 4.12
194 *
195 * @param string $url URL to load in dialog pulling the plugin page from wporg.
196 *
197 * @return string The URL with 'formidable' instead of 'frm-plugin-search'.
198 */
199 public function plugin_details( $url ) {
200 // phpcs:disable Generic.WhiteSpace.ScopeIndent
201 return false !== stripos( $url, 'tab=plugin-information&amp;plugin=' . self::$slug )
202 ? 'plugin-install.php?tab=plugin-information&amp;plugin=formidable&amp;TB_iframe=true&amp;width=600&amp;height=550'
203 : $url;
204 // phpcs:enable Generic.WhiteSpace.ScopeIndent
205 }
206
207 /**
208 * @since 4.12
209 *
210 * @return void
211 */
212 private function maybe_dismiss() {
213 $addon = FrmAppHelper::get_param( 'frm-dismiss', '', 'get', 'absint' );
214
215 if ( $addon ) {
216 $this->add_to_dismissed_hints( $addon );
217 }
218 }
219
220 /**
221 * Returns a list of previously dismissed hints.
222 *
223 * @since 4.12
224 *
225 * @return array List of dismissed hints.
226 */
227 protected function get_dismissed_hints() {
228 $dismissed_hints = get_option( self::$dismissed_opt );
229 return $dismissed_hints && is_array( $dismissed_hints ) ? $dismissed_hints : array();
230 }
231
232 /**
233 * Save the hint in the list of dismissed hints.
234 *
235 * @since 4.12
236 *
237 * @param string $hint The hint id, which is a Formidable addon slug.
238 *
239 * @return bool Whether the card was added to the list and hence dismissed.
240 */
241 protected function add_to_dismissed_hints( $hint ) {
242 $hints = array_merge( $this->get_dismissed_hints(), array( $hint ) );
243 return update_option( self::$dismissed_opt, $hints, false );
244 }
245
246 /**
247 * Checks that the addon slug passed should be displayed.
248 *
249 * A feature hint will be displayed if it has not been dismissed before or if 2 or fewer other hints have been dismissed.
250 *
251 * @since 7.2.1
252 *
253 * @param string $hint The hint id, which is a Formidable addon slug.
254 *
255 * @return bool True if $hint should be displayed.
256 */
257 protected function should_display_hint( $hint ) {
258 $dismissed_hints = $this->get_dismissed_hints();
259
260 // If more than 3 hints have been dismissed, then show no more.
261 if ( 3 < count( $dismissed_hints ) ) {
262 return false;
263 }
264
265 return ! in_array( $hint, $dismissed_hints, true );
266 }
267
268 /**
269 * Take a raw search query and return something a bit more standardized and
270 * easy to work with.
271 *
272 * @param string $term The raw search term.
273 *
274 * @return string A simplified/sanitized version.
275 */
276 private function sanitize_search_term( $term ) {
277 $term = strtolower( urldecode( $term ) );
278
279 // Remove non-alpha/space chars.
280 $term = preg_replace( '/[^a-z ]/', '', $term );
281
282 // Remove strings that don't help matches.
283 return trim( str_replace( array( 'formidable', 'free', 'wordpress', 'wp ', 'plugin' ), '', $term ) );
284 }
285
286 /**
287 * @since 4.12
288 *
289 * @param string $terms
290 *
291 * @return array
292 */
293 private function search_to_array( $terms ) {
294 $terms = $this->sanitize_search_term( $terms );
295 return array_filter( explode( ' ', $terms ) );
296 }
297
298 /**
299 * Put some more appropriate links on our custom result cards.
300 *
301 * @param array $links Related links.
302 * @param array $plugin Plugin result information.
303 *
304 * @return array
305 */
306 public function insert_related_links( $links, $plugin ) {
307 if ( self::$slug !== $plugin['slug'] ) {
308 return $links;
309 }
310
311 // By the time this filter is applied, self_admin_url was already applied and we don't need it anymore.
312 remove_filter( 'self_admin_url', array( $this, 'plugin_details' ) );
313
314 $links = array();
315 $is_installed = $this->is_installed( $plugin['plugin'] );
316 $is_active = is_plugin_active( $plugin['plugin'] );
317
318 // Plugin installed, active, feature not enabled; prompt to enable.
319 if ( ! $is_active && $is_installed ) {
320 if ( current_user_can( 'activate_plugins' ) ) {
321 $activate_url = add_query_arg(
322 array(
323 'action' => 'activate',
324 '_wpnonce' => wp_create_nonce( 'activate-plugin_' . $plugin['plugin'] ),
325 'plugin' => $plugin['plugin'],
326 ),
327 admin_url( 'plugins.php' )
328 );
329 $links['frm_get_started'] = '<a href="' . esc_url( $activate_url ) . '" class="button activate-now" aria-label="Activate ' . esc_attr( $plugin['name'] ) . '">' . esc_html__( 'Activate', 'formidable' ) . '</a>'; // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong
330 }
331 } elseif ( ! $is_active && isset( $plugin['url'] ) ) {
332 // Go to the add-ons page to install.
333 $links[] = '<a
334 class="button-secondary"
335 href="' . esc_url( admin_url( 'admin.php?page=formidable-addons' ) ) . '"
336 >' . esc_html__( 'Install Now', 'formidable' ) . '</a>';
337 } elseif ( ! empty( $plugin['link'] ) ) {
338 // Add link pointing to a relevant doc page in formidable.com.
339 $links[] = '<a
340 class="button-primary frm-plugin-search__learn-more"
341 href="' . esc_url( FrmAppHelper::admin_upgrade_link( 'plugin-learn-more', $plugin['link'] ) ) . '"
342 target="_blank"
343 data-addon="' . esc_attr( $plugin['addon'] ) . '"
344 >' . esc_html__( 'Learn more', 'formidable' ) . '</a>';
345 }//end if
346
347 // Dismiss link.
348 $dismiss = add_query_arg( array( 'frm-dismiss' => $plugin['id'] ) );
349 $links[] = '<a
350 href="' . esc_url( $dismiss ) . '"
351 class="frm-plugin-search__dismiss"
352 data-addon="' . esc_attr( $plugin['addon'] ) . '"
353 >' . esc_html__( 'Hide this suggestion', 'formidable' ) . '</a>';
354
355 return $links;
356 }
357
358 /**
359 * @param string $plugin
360 *
361 * @return bool
362 */
363 protected function is_installed( $plugin ) {
364 $all_plugins = FrmAppHelper::get_plugins();
365 return isset( $all_plugins[ $plugin ] );
366 }
367
368 /**
369 * Load the search scripts and CSS for PSH.
370 *
371 * @return void
372 */
373 public function load_plugins_search_script() {
374 wp_enqueue_script( self::$slug, FrmAppHelper::plugin_url() . '/js/plugin-search.js', array(), FrmAppHelper::plugin_version(), true );
375 wp_localize_script(
376 self::$slug,
377 'frmPlugSearch',
378 array(
379 'legend' => esc_html__(
380 'This suggestion was made by Formidable Forms, the form builder and application plugin already installed on your site.',
381 'formidable'
382 ),
383 )
384 );
385 }
386 }
387