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

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