PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 6.10
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v6.10
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.10, at classes/models/FrmPluginSearch.php

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