PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / trunk
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More vtrunk
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 / FrmGatedContentAction.php

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

316 lines 9.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Gated Content form action
4 *
5 * @package Formidable
6 *
7 * @since 6.33
8 */
9
10 if ( ! defined( 'ABSPATH' ) ) {
11 die( 'You are not allowed to call this page directly.' );
12 }
13
14 class FrmGatedContentAction extends FrmFormAction {
15
16 /**
17 * @var string
18 */
19 public static $slug = 'gated_content';
20
21 /**
22 * Set up action options and register with parent constructor.
23 *
24 * Runs at form action priority 8 — before On Submit (9) and Send Email (10) —
25 * so the raw token is already stored when those actions process [frm_gated_content]
26 * shortcodes on the same request or after a payment redirect.
27 */
28 public function __construct() {
29 $action_ops = array(
30 'classes' => 'frmfont frm_lock_simple',
31 'active' => true,
32 'event' => array( 'create', 'payment-success' ),
33 'limit' => 99,
34 'priority' => 8,
35 'color' => '#F59E0B',
36 'keywords' => __( 'gated, content, payment, access, token, restrict, download', 'formidable' ),
37 );
38 $action_ops = apply_filters( 'frm_' . self::$slug . '_control_settings', $action_ops );
39
40 parent::__construct( self::$slug, self::get_name(), $action_ops );
41 }
42
43 /**
44 * Get the action display name.
45 *
46 * @return string
47 */
48 public static function get_name() {
49 return __( 'Gated Content', 'formidable' );
50 }
51
52 /**
53 * Get the available gated content item types.
54 *
55 * Each entry is an associative array with:
56 * - label (string) Display label shown in the type dropdown.
57 * - disabled (bool) Whether the option is selectable. Default false.
58 * - pro (bool) Whether the type requires Pro. Default false.
59 *
60 * @return array<string, array>
61 */
62 public static function get_types() {
63 $types = array(
64 'page' => array(
65 'label' => __( 'Page', 'formidable' ),
66 'disabled' => false,
67 ),
68 'post' => array(
69 'label' => __( 'Post', 'formidable' ),
70 'disabled' => false,
71 ),
72 'frm_file' => array(
73 'label' => __( 'Formidable file (Pro)', 'formidable' ),
74 'disabled' => true,
75 ),
76 'frm_pdf' => array(
77 'label' => __( 'Formidable PDF (PDFs add-on)', 'formidable' ),
78 'disabled' => true,
79 ),
80 );
81
82 /**
83 * Filter the available gated content item types.
84 *
85 * Use this to register new types or enable Pro types that are greyed out by default.
86 *
87 * @param array<string, array> $types Associative array of type slug => type config.
88 */
89 /** @var array<string, array> */
90 return apply_filters( 'frm_gated_content_item_types', $types );
91 }
92
93 /**
94 * Render the action settings form.
95 *
96 * @param object $instance Form action post object.
97 * @param array $args Contains `form`, `action_key`, `values`.
98 *
99 * @return string
100 */
101 public function form( $instance, $args = array() ) {
102 include FrmAppHelper::plugin_path() . '/classes/views/frm-form-actions/_gated_content_settings.php';
103 return '';
104 }
105
106 /**
107 * Default settings for a new gated content action.
108 *
109 * - items: Array of item objects, each with 'type' and 'id' keys.
110 * One token unlocks all items in this action.
111 * Pro adds the 'frm_file' type.
112 * - expired_hours: Hours until access token expires. Null = never expires.
113 * Set via Pro only; stored here for shared validation logic.
114 * - event: Form events that trigger token generation.
115 *
116 * @return array
117 */
118 public function get_defaults() {
119 return array(
120 'type' => 'post',
121 'items' => array(),
122 'expired_hours' => null,
123 'event' => array( 'create' ),
124 );
125 }
126
127 /**
128 * Get the shortcode reference rows for the action settings UI.
129 *
130 * Returns an array of shortcode row definitions, each with:
131 * - code (string) The shortcode string to display and copy.
132 * - output (string) Human-readable description of what it outputs.
133 *
134 * The `frm_gated_content_shortcodes` filter allows Pro and add-ons to append
135 * additional rows (e.g. show="expired_time" when expiry is configured).
136 *
137 * @since 6.33
138 *
139 * @param int $action_id Gated content action post ID.
140 *
141 * @return array<int, array{code: string, output: string}>
142 */
143 public static function get_shortcodes( $action_id ) {
144 $shortcodes = array(
145 array(
146 'code' => '[frm_gated_content id="' . absint( $action_id ) . '"]',
147 'output' => __( 'Access links for all items', 'formidable' ),
148 ),
149 array(
150 'code' => '[frm_gated_content id="' . absint( $action_id ) . '" item="0"]',
151 'output' => __( 'Access link for the first item (0-indexed)', 'formidable' ),
152 ),
153 array(
154 'code' => '[frm_gated_content id="' . absint( $action_id ) . '" item="0" show="url"]',
155 'output' => __( 'URL only for the first item (no link tag)', 'formidable' ),
156 ),
157 array(
158 'code' => '[frm_gated_content id="' . absint( $action_id ) . '" show="access_token"]',
159 'output' => __( 'Raw access token string', 'formidable' ),
160 ),
161 );
162
163 /**
164 * Filter the shortcode reference rows shown in the gated content action settings UI.
165 *
166 * Each entry must be an array with:
167 * - code (string) The shortcode string to display and copy.
168 * - output (string) Human-readable description of what it outputs.
169 *
170 * @since 6.33
171 *
172 * @param array<int, array{code: string, output: string}> $shortcodes Shortcode rows.
173 * @param int $action_id Gated content action post ID.
174 */
175 /** @var array<int, array{code: string, output: string}> */
176 return (array) apply_filters( 'frm_gated_content_shortcodes', $shortcodes, $action_id );
177 }
178
179 /**
180 * Get posts for all post-type-backed gated content item type selectors.
181 *
182 * Runs one query covering all enabled post types, then groups results by type key.
183 * Only private and password-protected posts are included — plain published posts
184 * are publicly accessible and should not appear as selectable gated content items.
185 *
186 * @return array<string, list<object>> Posts keyed by item type slug (e.g. 'page', 'post').
187 */
188 public static function get_posts() {
189 $post_types = array();
190
191 foreach ( self::get_types() as $type_key => $type_config ) {
192 if ( empty( $type_config['disabled'] ) && post_type_exists( $type_key ) ) {
193 $post_types[] = $type_key;
194 }
195 }
196
197 if ( ! $post_types ) {
198 return array();
199 }
200
201 /** @var object[] $raw_posts */
202 $raw_posts = FrmDb::get_results(
203 'posts',
204 array(
205 'post_type' => $post_types,
206 'post_status' => array( 'publish', 'private' ),
207 ),
208 'ID, post_title, post_password, post_status, post_type',
209 array( 'order_by' => 'post_title ASC' )
210 );
211
212 // Initialise empty buckets in get_types() order.
213 /** @var array<string, list<object>> $grouped */
214 $grouped = array_fill_keys( $post_types, array() );
215
216 foreach ( $raw_posts as $post ) {
217 if ( 'private' !== $post->post_status && '' === $post->post_password ) {
218 // Skip publicly accessible posts.
219 continue;
220 }
221
222 $post_type = (string) $post->post_type;
223
224 if ( isset( $grouped[ $post_type ] ) ) {
225 $grouped[ $post_type ][] = $post;
226 }
227 }
228
229 return $grouped;
230 }
231
232 /**
233 * Build the JSON-encoded autocomplete source array for the "post" item selector.
234 *
235 * Returns a JSON string suitable for passing as a `data-source` attribute to a
236 * jQuery UI autocomplete widget. Each entry has a `value` (post ID string) and
237 * a `label` (post title).
238 *
239 * @param object[] $posts Posts returned by get_posts().
240 *
241 * @return string JSON-encoded array, or an empty string on encoding failure.
242 */
243 public static function get_posts_autocomplete_source( $posts ) {
244 return (string) wp_json_encode(
245 array_map(
246 static function ( $p ) {
247 return array(
248 'value' => (string) $p->ID,
249 'label' => $p->post_title,
250 );
251 },
252 $posts
253 )
254 );
255 }
256
257 /**
258 * Sanitize and validate settings on save.
259 *
260 * @param array $new_instance New settings submitted via form().
261 * @param array $old_instance Previous saved settings.
262 *
263 * @return array Sanitized settings to save. Return false to abort save.
264 */
265 public function update( $new_instance, $old_instance ) {
266 $post_content = $new_instance['post_content'];
267
268 // Sanitize items — each item is an array with 'type' and 'id' keys.
269 $raw_items = isset( $post_content['items'] ) ? (array) $post_content['items'] : array();
270 $sanitized_items = array();
271
272 foreach ( $raw_items as $raw_item ) {
273 if ( ! is_array( $raw_item ) ) {
274 continue;
275 }
276
277 $item = array(
278 'type' => isset( $raw_item['type'] ) ? sanitize_key( $raw_item['type'] ) : 'post',
279 'id' => isset( $raw_item['id'] ) ? sanitize_text_field( $raw_item['id'] ) : '',
280 );
281
282 /**
283 * Filter a sanitized gated content item before it is saved.
284 *
285 * Pro and PDF plugins use this to sanitize their own type-specific fields
286 * and merge them into the item array.
287 *
288 * @param array $item Sanitized item data (keys: type, id).
289 * @param array $args {
290 *
291 * @type array $raw_item Raw submitted item data.
292 * }
293 */
294 $item = apply_filters( 'frm_gated_content_sanitize_item', $item, compact( 'raw_item' ) );
295
296 // Skip items with no ID selected (user left the select at the empty default).
297 if ( empty( $item['id'] ) ) {
298 continue;
299 }
300
301 $sanitized_items[] = $item;
302 }//end foreach
303
304 $post_content['items'] = $sanitized_items;
305
306 // Sanitize expired_hours — positive int or null (Pro may set this).
307 $post_content['expired_hours'] = ! empty( $post_content['expired_hours'] )
308 ? absint( $post_content['expired_hours'] )
309 : null;
310
311 $new_instance['post_content'] = $post_content;
312
313 return $new_instance;
314 }
315 }
316