PluginProbe
PDF Embedder – PDF Viewer & Embed PDF Files for WordPress / 5.0.2
PDF Embedder – PDF Viewer & Embed PDF Files for WordPress v5.0.2
5.0.2 5.0.1 trunk 1.0.1 1.0.2 1.0.3 1.0.4 1.2 1.2.1 2.0 2.1 2.1.4 2.2 2.2.2 2.2.4 2.2.5 2.4 2.4.1 2.5 2.5.4 2.5.5 2.7.3 2.7.5 2.8 3.0.1 All 48 releases
pdf-embedder / src / Options.php

Options.php in PDF Embedder – PDF Viewer & Embed PDF Files for WordPress 5.0.2, at src/Options.php

457 lines 11.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace PDFEmbedder;
4
5 use PDFEmbedder\Helpers\Multisite;
6 use PDFEmbedder\Tasks\UsageTracking\SendUsageTask;
7
8 /**
9 * Class Options.
10 *
11 * @since 4.7.0
12 */
13 class Options {
14
15 /**
16 * This key used to save/retrieve options from the wp_options table.
17 *
18 * @since 4.7.0
19 */
20 public const KEY = 'pdfemb';
21
22 /**
23 * Default options for the Lite version.
24 *
25 * @since 4.9.0
26 */
27 public const LITE_DEFAULTS = [
28 'pdfemb_width' => 'max',
29 'pdfemb_height' => 'max',
30 'pdfemb_toolbar' => 'bottom',
31 'pdfemb_toolbarfixed' => 'off',
32 'usagetracking' => 'off',
33 ];
34
35 /**
36 * In which context the saving process is performed.
37 * Right now this is a section of the page.
38 *
39 * @since 4.9.0
40 *
41 * @var string
42 */
43 public $saving_context = '';
44
45 /**
46 * Internal cached holder for the options.
47 *
48 * @since 4.7.0
49 *
50 * @var array
51 */
52 private $options = [];
53
54 /**
55 * Default plugin options, hydrated by Lite and Premium plans.
56 *
57 * @since 4.7.0
58 */
59 public function get_defaults(): array {
60
61 /**
62 * Filter the default plugin options.
63 *
64 * @since 4.7.0
65 *
66 * @param array $defaults The default options.
67 */
68 return apply_filters( 'pdfemb_options_defaults', self::LITE_DEFAULTS );
69 }
70
71 /**
72 * Get the plugin settings.
73 *
74 * @since 4.7.0
75 */
76 public function get(): array {
77
78 if ( ! empty( $this->options ) ) {
79 return $this->options;
80 }
81
82 // Get raw options from DB.
83 $options = $this->get_from_db();
84
85 // Inject default options into those that are saved in DB.
86 foreach ( $this->get_defaults() as $k => $v ) {
87 if ( ! isset( $options[ $k ] ) ) {
88 $options[ $k ] = $v;
89 }
90 }
91
92 /**
93 * Filter the plugin options to allow programmatic modification.
94 *
95 * @since 4.7.0
96 *
97 * @param array $options The options.
98 */
99 $this->options = apply_filters( 'pdfemb_options_get', $options );
100
101 return $this->options;
102 }
103
104 /**
105 * Save the plugin options.
106 *
107 * @since 4.7.0
108 * @since 4.9.0 Added a $context parameter.
109 *
110 * @param array $input The options to save, they will be validated.
111 * @param string $context In which context the saving process is performed.
112 */
113 public function save( array $input, string $context = '' ) {
114
115 if ( empty( $context ) ) {
116 return;
117 }
118
119 $this->saving_context = $context;
120
121 $this->options = self::validate( $input );
122
123 if ( Multisite::is_network_activated() ) {
124 update_site_option( self::KEY, $this->options );
125 } else {
126 update_option( self::KEY, $this->options, false );
127 }
128 }
129
130 /**
131 * Drop the in-memory options cache so the next {@see get()} call re-reads
132 * from the DB. Use after writing the options row out-of-band (e.g. via
133 * `update_option()` directly) — {@see save()} keeps the cache coherent
134 * on its own.
135 *
136 * @since 5.0.0
137 */
138 public function reset_cache(): void {
139
140 $this->options = [];
141 }
142
143 /**
144 * Check if the option exists in the database.
145 *
146 * @since 4.9.2
147 *
148 * @param string $key The key to check.
149 */
150 public function exist( string $key = '' ): bool {
151
152 $options = $this->get_from_db();
153
154 if ( empty( $key ) ) {
155 return ! empty( $options );
156 }
157
158 return array_key_exists( $key, $options );
159 }
160
161 /**
162 * Go through each option and sanitize and validate its value before saving into DB.
163 *
164 * @since 4.7.0
165 *
166 * @param array $input Plugin options to validate.
167 */
168 public static function validate( array $input ): array { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh
169
170 /**
171 * Filter the validated plugin options.
172 * The value will be hydrated by each plan individually.
173 *
174 * @since 4.7.0
175 *
176 * @param array $validated Already validated portion of plugin options, to be saved into DB.
177 * @param array $input Plugin options to validate.
178 */
179 return apply_filters( 'pdfemb_options_validated', [], $input );
180 }
181
182 /**
183 * Clean the provided array of options from the specific set of defaults.
184 *
185 * @since 4.9.0
186 *
187 * @param array $input The options to clean.
188 * @param array $defaults The default options to compare against.
189 */
190 public function clean_options_from_defaults( array $input = [], array $defaults = [] ): array {
191
192 if ( empty( $input ) ) {
193 $input = $this->get_from_db();
194 }
195
196 if ( empty( $defaults ) ) {
197 $defaults = self::LITE_DEFAULTS;
198 }
199
200 return array_diff_key( $input, $defaults );
201 }
202
203 /**
204 * Validate the Premium options.
205 * The validation is done in steps according to each plan priority.
206 *
207 * @since 4.9.0
208 * @since 5.0.0 Added the `render` saving context for inline atts on the front end.
209 *
210 * @param array $validated Validated options.
211 * @param array $input Original options coming from the request.
212 */
213 public function validate_options( array $validated, array $input ): array {
214
215 // Render-time validation of inline shortcode/block/widget atts. Only the keys
216 // the user actually passed are returned, so the caller can layer them on top of
217 // the merged defaults+DB options without erasing settings the user didn't override.
218 if ( $this->saving_context === 'render' ) {
219 return $this->validate_options_render( $input );
220 }
221
222 /**
223 * Just return the data from DB when we are validating settings elsewhere.
224 * Lite settings shouldn't be re-validated in this case, as they are absent from $input.
225 */
226 if ( $this->saving_context !== 'settings' ) {
227 return $this->get_from_db();
228 }
229
230 return $this->validate_options_settings( $input );
231 }
232
233 /**
234 * Validate the Lite settings page input on admin save.
235 *
236 * @since 5.0.0
237 *
238 * @param array $input Raw POST input from the Lite settings form.
239 */
240 private function validate_options_settings( array $input ): array { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh
241
242 $validated = $this->clean_options_from_defaults();
243
244 $validated['pdfemb_width'] = isset( $input['pdfemb_width'] ) ? strtolower( trim( $input['pdfemb_width'] ) ) : self::LITE_DEFAULTS['pdfemb_width'];
245
246 if (
247 ! is_numeric( $validated['pdfemb_width'] ) &&
248 $validated['pdfemb_width'] !== 'max' &&
249 $validated['pdfemb_width'] !== 'auto'
250 ) {
251 if ( function_exists( 'add_settings_error' ) ) {
252 add_settings_error(
253 'pdfemb_width',
254 'widtherror',
255 self::get_error_text( 'pdfemb_width|widtherror' ),
256 'error'
257 );
258 }
259
260 // Revert back to max as last resort, don't leave field blank.
261 $validated['pdfemb_width'] = self::LITE_DEFAULTS['pdfemb_width'];
262 }
263
264 $validated['pdfemb_height'] = isset( $input['pdfemb_height'] ) ? strtolower( trim( $input['pdfemb_height'] ) ) : self::LITE_DEFAULTS['pdfemb_height'];
265
266 if (
267 ! is_numeric( $validated['pdfemb_height'] ) &&
268 $validated['pdfemb_height'] !== 'max' &&
269 $validated['pdfemb_height'] !== 'auto'
270 ) {
271 if ( function_exists( 'add_settings_error' ) ) {
272 add_settings_error(
273 'pdfemb_height',
274 'heighterror',
275 self::get_error_text( 'pdfemb_height|heighterror' ),
276 'error'
277 );
278 }
279
280 // Revert back to max as last resort, don't leave field blank.
281 $validated['pdfemb_height'] = self::LITE_DEFAULTS['pdfemb_height'];
282 }
283
284 if (
285 isset( $input['pdfemb_toolbar'] ) &&
286 in_array( $input['pdfemb_toolbar'], [ 'top', 'bottom', 'both', 'none' ], true )
287 ) {
288 $validated['pdfemb_toolbar'] = $input['pdfemb_toolbar'];
289 } else {
290 $validated['pdfemb_toolbar'] = self::LITE_DEFAULTS['pdfemb_toolbar'];
291 }
292
293 if (
294 isset( $input['pdfemb_toolbarfixed'] ) &&
295 in_array( $input['pdfemb_toolbarfixed'], [ 'on', 'off' ], true )
296 ) {
297 $validated['pdfemb_toolbarfixed'] = $input['pdfemb_toolbarfixed'];
298 } else {
299 $validated['pdfemb_toolbarfixed'] = self::LITE_DEFAULTS['pdfemb_toolbarfixed'];
300 }
301
302 if (
303 isset( $input['usagetracking'] ) &&
304 in_array( $input['usagetracking'], [ 'on', 'off' ], true )
305 ) {
306 $validated['usagetracking'] = $input['usagetracking'];
307 } else {
308 pdf_embedder()->tasks()->cancel( SendUsageTask::ACTION );
309 $validated['usagetracking'] = self::LITE_DEFAULTS['usagetracking'];
310 }
311
312 return $validated;
313 }
314
315 /**
316 * Validate inline shortcode/block/widget atts against the Lite defaults.
317 *
318 * Only the keys present in `$input` are returned. Keys the user did not pass inline
319 * are intentionally absent so they fall through to the merged defaults+DB layer in
320 * `Viewer::set_options()` rather than being overwritten with hard-coded defaults.
321 * No side effects (`add_settings_error`, task cancellation) — the settings-context
322 * validator has those, this one is pure validation suitable for the front end.
323 *
324 * `usagetracking` is intentionally not validated here: it's a global admin setting
325 * with no per-instance meaning and should never be passed inline.
326 *
327 * @since 5.0.0
328 *
329 * @param array $input User-provided inline atts (already prefixed with `pdfemb_`).
330 */
331 private function validate_options_render( array $input ): array {
332
333 $validated = [];
334
335 if ( isset( $input['pdfemb_width'] ) ) {
336 $width = strtolower( trim( (string) $input['pdfemb_width'] ) );
337
338 $validated['pdfemb_width'] = ( is_numeric( $width ) || $width === 'max' || $width === 'auto' )
339 ? $width
340 : self::LITE_DEFAULTS['pdfemb_width'];
341 }
342
343 if ( isset( $input['pdfemb_height'] ) ) {
344 $height = strtolower( trim( (string) $input['pdfemb_height'] ) );
345
346 $validated['pdfemb_height'] = ( is_numeric( $height ) || $height === 'max' || $height === 'auto' )
347 ? $height
348 : self::LITE_DEFAULTS['pdfemb_height'];
349 }
350
351 if ( isset( $input['pdfemb_toolbar'] ) ) {
352 $validated['pdfemb_toolbar'] = in_array( $input['pdfemb_toolbar'], [ 'top', 'bottom', 'both', 'none' ], true )
353 ? $input['pdfemb_toolbar']
354 : self::LITE_DEFAULTS['pdfemb_toolbar'];
355 }
356
357 if ( isset( $input['pdfemb_toolbarfixed'] ) ) {
358 $validated['pdfemb_toolbarfixed'] = in_array( $input['pdfemb_toolbarfixed'], [ 'on', 'off' ], true )
359 ? $input['pdfemb_toolbarfixed']
360 : self::LITE_DEFAULTS['pdfemb_toolbarfixed'];
361 }
362
363 return $validated;
364 }
365
366 /**
367 * Get the error string for a given field error.
368 *
369 * @since 4.7.0
370 *
371 * @param string $error The field error to get the string for.
372 */
373 public static function get_error_text( string $error ): string {
374
375 $local_error_strings = [
376 'pdfemb_width|widtherror' => __( 'Width must be "max" or an integer (number of pixels). This setting is reset to "max".', 'pdf-embedder' ),
377 'pdfemb_height|heighterror' => __( 'Height must be "max" or an integer (number of pixels). This setting is reset to "max".', 'pdf-embedder' ),
378 ];
379
380 if ( isset( $local_error_strings[ $error ] ) ) {
381 return $local_error_strings[ $error ];
382 }
383
384 return __( 'Unspecified error. Please review all the settings and try again.', 'pdf-embedder' );
385 }
386
387 /**
388 * Validate whether the option value is truthy.
389 *
390 * @since 4.7.0
391 *
392 * @param mixed $value The option value to validate.
393 */
394 public static function is_on( $value ): bool {
395
396 return is_scalar( $value ) && ( $value === true || $value === 'on' || $value === '1' || $value === 'true' );
397 }
398
399 /**
400 * Prepend "pdfemb_" string to each key in the $atts array.
401 *
402 * @since 4.8.0
403 *
404 * @param array $options Options to prefix.
405 */
406 public static function prefix( array $options ): array {
407
408 return (array) array_combine(
409 array_map(
410 static function ( $key ) {
411
412 return 'pdfemb_' . $key;
413 },
414 array_keys( $options )
415 ),
416 array_values( $options )
417 );
418 }
419
420 /**
421 * Remove "pdfemb_" prefix from each key in the $options array.
422 *
423 * @since 4.8.0
424 *
425 * @param array $options Options to unprefix.
426 */
427 public static function unprefix( $options ): array {
428
429 return (array) array_combine(
430 array_map(
431 static function ( $key ) {
432
433 return str_replace( 'pdfemb_', '', $key );
434 },
435 array_keys( $options )
436 ),
437 array_values( $options )
438 );
439 }
440
441 /**
442 * Get the options from the database, without default values.
443 *
444 * @since 4.9.0
445 */
446 private function get_from_db(): array {
447
448 if ( Multisite::is_network_activated() ) {
449 $options = get_site_option( self::KEY, [] );
450 } else {
451 $options = get_option( self::KEY, [] );
452 }
453
454 return $options;
455 }
456 }
457