PluginProbe
Favicon Rotator / trunk
Favicon Rotator vtrunk
1.3.0 trunk 1.0 1.1 1.2 1.2.10 1.2.11 1.2.12 1.2.8 1.2.9
favicon-rotator / model.php

model.php in Favicon Rotator trunk, at model.php

644 lines 18.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 // Do not load directly.
3 if ( ! defined( 'ABSPATH' ) ) {
4 exit;
5 }
6
7 require_once 'includes/class.base.php';
8 require_once 'includes/class.media.php';
9
10 /**
11 * Controller.
12 *
13 * @package Favicon Rotator
14 * @author Archetyped
15 */
16 class FaviconRotator extends FVRT_Base {
17
18 /**
19 * Admin Page hook
20 */
21 private string $page;
22
23 /**
24 * Plugin options
25 */
26 private ?array $options = null;
27
28 /**
29 * Plugin Options key
30 */
31 private string $opt_key = 'options';
32
33 /**
34 * Key to use for icons array in options
35 */
36 private string $opt_icons = 'icons';
37
38 /**
39 * Default icon type
40 */
41 private string $icon_type_default = 'favicon';
42
43 /**
44 * Save action
45 */
46 private string $action_save = 'action_save';
47
48 /**
49 * Form nonce field ID.
50 */
51 private string $action_save_field = 'action_save';
52
53 /**
54 * Path to admin contextual help file
55 */
56 private string $file_admin_help = '/resources/admin_help.html';
57
58 /*-** Instance objects **-*/
59
60 /**
61 * Media instance
62 */
63 private FVRT_Media $media;
64
65 /*-** Initialization **-*/
66
67 public function __construct() {
68 parent::__construct();
69 $this->opt_key = $this->add_prefix( $this->opt_key );
70 $this->action_save = $this->add_prefix( $this->action_save );
71 $this->action_save_field = $this->add_prefix( $this->action_save_field );
72 $this->media = new FVRT_Media();
73 $this->register_hooks();
74 }
75
76 public function register_hooks() {
77 /*-** General **-*/
78
79 add_action( 'init', $this->m( 'register_icon_types' ) );
80
81 /*-** Admin **-*/
82
83 add_action( 'admin_print_scripts-media-upload-popup', $this->m( 'admin_scripts' ) );
84
85 // Menu.
86 add_action( 'admin_menu', $this->m( 'admin_menu' ) );
87 // Plugins page.
88 add_filter( 'plugin_action_links_' . $this->util->get_plugin_base_name(), $this->m( 'admin_plugin_action_links' ), 10, 4 );
89
90 /*-** Main **-*/
91
92 // Template.
93 add_action( 'wp_head', $this->m( 'display_icons' ) );
94
95 // Instance setup.
96 $this->media->register_hooks();
97 }
98
99 /*-** Helpers **-*/
100
101 /**
102 * Retrieve options array (or specific option if specified)
103 *
104 * @param $key Option to retrieve
105 *
106 * @return mixed Full options array or value of specific key (Default: empty array)
107 */
108 public function get_options( $key = null ) {
109 // Retrieve options entry from DB.
110 $ret = $this->options;
111 if ( is_null( $ret ) ) {
112 $ret = get_option( $this->opt_key );
113 if ( false === $ret ) {
114 $ret = array();
115 }
116 $this->options = $ret;
117 }
118
119 if ( ! is_null( $key ) && isset( $ret[ $key ] ) ) {
120 $ret = $ret[ $key ];
121 }
122 return $ret;
123 }
124
125 public function set_option( $key, $val ) {
126 // Make sure options array initialized.
127 $this->get_options();
128 // Set option value.
129 $this->options[ $key ] = $val;
130 // Save updated options to DB.
131 update_option( $this->opt_key, $this->options );
132 }
133
134 /*-** Media **-*/
135
136 /**
137 * Register icon types with Media instance
138 *
139 * @see register_hooks() Hook to method added
140 *
141 * @return array Registered types
142 */
143 public function register_icon_types() {
144 // @var array $types Icon Types.
145 $types = array(
146 'favicon' => array(
147 'lbl_title' => __( 'Browser Icon', 'favicon-rotator' ),
148 'lbl_set' => __( 'Add Browser Icon', 'favicon-rotator' ),
149 ),
150 'touch' => array(
151 'limit' => 1,
152 'lbl_title' => __( 'Touch Icons', 'favicon-rotator' ),
153 'lbl_add' => __( 'Set Icon', 'favicon-rotator' ),
154 'lbl_set' => __( 'Set Touch Icon', 'favicon-rotator' ),
155 'lbl_empty' => __( 'No touch icon set', 'favicon-rotator' ),
156 'display' => '<link rel="apple-touch-icon-precomposed" href="%1$s" />',
157 'file_mime' => array( 'image/png' ),
158 'file_type' => array( 'png' ),
159 'file_desc' => __( 'Touch Icon Files', 'favicon-rotator' ),
160 'width' => 114,
161 'height' => 114,
162 ),
163 );
164
165 // Register types.
166 foreach ( $types as $type => $props ) {
167 $types[ $type ] = $this->media->register_type( $type, array_merge( $this->get_icon_type_defaults(), $props ) );
168 }
169 return $types;
170 }
171
172 /**
173 * Retrieves default icon type properties.
174 *
175 * @return array
176 */
177 private function get_icon_type_defaults(): array {
178 return array(
179 'limit' => null,
180 'lbl_title' => '',
181 'lbl_add' => __( 'Add Icon', 'favicon-rotator' ),
182 'lbl_set' => __( 'Add Icon', 'favicon-rotator' ),
183 'lbl_empty' => __( 'No icons set', 'favicon-rotator' ),
184 'display' => '<link rel="shortcut icon" href="%1$s" />',
185 'file_mime' => array( 'image/png', 'image/gif', 'image/jpeg', 'image/x-icon' ),
186 'file_type' => array( 'png', 'gif', 'jpg', 'ico' ),
187 'file_desc' => __( 'Icon Files', 'favicon-rotator' ),
188 'width' => 16,
189 'height' => 16,
190 );
191 }
192
193 /**
194 * Retrieve icon types
195 *
196 * @return array Icon types (as object of properties)
197 */
198 public function get_icon_types() {
199 static $types = null;
200 if ( is_null( $types ) ) {
201 $types = $this->register_icon_types();
202 }
203 return $types;
204 }
205
206 /**
207 * Retrieve icon type names
208 *
209 * @return array Icon type names
210 */
211 public function get_icon_type_names() {
212 static $names = null;
213 if ( is_null( $names ) ) {
214 $names = array_keys( $this->get_icon_types() );
215 }
216 return $names;
217 }
218
219 /**
220 * Retrieve icon type properties
221 *
222 * @param string $type Icon type
223 *
224 * @return object|bool Type properties (FALSE if type not registered)
225 */
226 public function get_icon_type( $type ) {
227 $ret = null;
228 $types = $this->get_icon_types();
229 if ( isset( $types[ $type ] ) ) {
230 $ret = $types[ $type ];
231 } else {
232 $ret = (object) $this->get_icon_type_defaults();
233 $ret->type_name = $type;
234 }
235 return $ret;
236 }
237
238 /**
239 * Normalize icons array to contain valid icon groups
240 *
241 * @param array $icons Raw Icons array
242 *
243 * @return array Normalized icons
244 */
245 public function normalize_icons( $icons ) {
246 static $groups_default = null;
247 $save = false;
248 // Build default groups array.
249 if ( is_null( $groups_default ) ) {
250 $groups_default = array();
251 $dval = array();
252 foreach ( $this->get_icon_type_names() as $type ) {
253 $groups_default[ $type ] = $dval;
254 }
255 }
256 // Make sure icons array is valid (icons grouped by type).
257 if ( is_array( $icons ) && ! empty( $icons ) ) {
258 $icons_temp = array_values( $icons );
259 // Put icons into default group if necessary.
260 if ( ! is_array( $icons_temp[0] ) ) {
261 $icons = array( $this->icon_type_default => $icons_temp );
262 $save = true;
263 }
264 } elseif ( ! is_array( $icons ) ) {
265 $icons = array();
266 }
267 // Merge and return icons with defaults.
268 $icons = array_merge( $groups_default, $icons );
269 if ( $save ) {
270 $this->save_icons( $icons );
271 }
272 return $icons;
273 }
274
275 /**
276 * Retrieve Post IDs of favicons
277 *
278 * @param string $type Icon type to retrieve
279 *
280 * @return array Icon IDs grouped by icon type
281 */
282 public function get_icon_ids( $type = null ) {
283 // Get array of attachment IDs for icons (grouped by type).
284 $icons = $this->get_options( $this->opt_icons );
285
286 // Normalize icons array.
287 $icons = $this->normalize_icons( $icons );
288
289 // Get specific icon type.
290 if ( is_string( $type ) && ! empty( $type ) ) {
291 $icons = ( isset( $icons[ $type ] ) ) ? $icons[ $type ] : array();
292 }
293
294 return $icons;
295 }
296
297 /**
298 * Retrieve list of icons as string
299 *
300 * @param string $type Icon type
301 *
302 * @return string Icon list
303 */
304 public function get_icon_ids_list( $type ) {
305 return implode( ',', $this->get_icon_ids( $type ) );
306 }
307
308 /**
309 * Retrieve icons saved in options menu
310 *
311 * @return array Media attachment objects
312 */
313 public function get_icons( $type = null ) {
314 $icons = array();
315 // Get icon ids.
316 $ids = $this->get_icon_ids( $type );
317 // Retrieve attachment objects.
318 if ( ! empty( $ids ) ) {
319 // Wrap IDs in assoc array if valid type is specified.
320 $type_valid = false;
321 if ( is_string( $type ) && ! empty( $type ) ) {
322 $ids = array( $type => $ids );
323 $type_valid = true;
324 }
325 foreach ( $ids as $type_key => $type_ids ) {
326 $icons[ $type_key ] = ( ! empty( $type_ids ) ) ? get_posts(
327 array(
328 'post_type' => 'attachment',
329 'include' => $type_ids,
330 )
331 ) : array();
332 // Fix icon option if invalid icons were passed.
333 if ( count( $icons[ $type_key ] ) !== count( $type_ids ) ) {
334 $ids_temp = array();
335 foreach ( $icons[ $type_key ] as $icon ) {
336 $ids_temp[] = $icon->ID;
337 }
338 // Save to DB.
339 $this->save_icons( $ids_temp, $type_key );
340 }
341 }
342 // Break specified type out of assoc array.
343 if ( $type_valid ) {
344 $icons = $icons[ $type ];
345 }
346 }
347
348 return $icons;
349 }
350
351 /**
352 * Save list of selected icons to DB
353 *
354 * @param array $ids (optional) Array of icon IDs
355 * @param string $type (optional) Icon type being saved
356 */
357 public function save_icons( $icons = null, $type = null ) {
358 // Check if valid icon IDs are passed to function.
359 if ( ! is_null( $icons ) ) {
360 if ( ! is_array( $icons ) ) {
361 $icons = ( is_int( $icons ) ) ? array( $icons ) : null;
362 }
363 }
364
365 // Get icon IDs from form submission.
366 if ( is_null( $icons ) && check_admin_referer( $this->action_save, $this->action_save_field ) ) {
367 $icons = array();
368 $field_base = 'fv_id_';
369 foreach ( $this->get_icon_type_names() as $itype ) {
370 $field = $field_base . $itype;
371 if ( isset( $_POST[ $field ] ) ) {
372 $icons[ $itype ] = explode( ',', sanitize_text_field( wp_unslash( $_POST[ $field ] ) ) );
373 }
374 }
375 }
376
377 // Save to DB.
378 if ( is_array( $icons ) ) {
379 // Build full icons array: Combine saved icons and current icons.
380 if ( ! is_null( $type ) ) {
381 $icons_temp = $icons;
382 $icons = array( $type => $icons_temp );
383 unset( $icons_temp );
384 // Merge saved icons with new icon values.
385 $icons = wp_parse_args( $icons, $this->get_icon_ids() );
386 }
387 // Validate values.
388 foreach ( $icons as $itype => $type_ids ) {
389 $changed = false;
390 foreach ( $type_ids as $idx => $id ) {
391 if ( ! intval( $id ) ) {
392 unset( $icons[ $itype ][ $idx ] );
393 $changed = true;
394 }
395 }
396 if ( $changed ) {
397 $icons[ $itype ] = array_values( $icons[ $itype ] );
398 }
399 }
400 // Save icons to DB.
401 $this->set_option( $this->opt_icons, $icons );
402 }
403 }
404
405 /**
406 * Output markup for all icons
407 */
408 public function display_icons() {
409 echo "<!-- Favicon Rotator -->\r\n";
410 foreach ( $this->get_icon_type_names() as $type ) {
411 $this->display_icon( $type );
412 }
413 echo "<!-- End Favicon Rotator -->\r\n";
414 }
415
416 /**
417 * Output markup for specified icon type
418 */
419 public function display_icon( $type ) {
420 // Get icons.
421 $icons = $this->get_icon_ids( $type );
422 $icons_orig = $icons;
423 $icon = null;
424 $icons_count = count( $icons );
425 // Loop through retrieved icons until valid icon is returned.
426 while ( is_null( $icon ) && $icons_count > 0 ) {
427 // Select random icon.
428 $idx = ( $icons_count > 1 ) ? array_rand( $icons ) : 0;
429 $icon_id = $icons[ $idx ];
430 $icon_src = $this->media->get_icon_src( $icon_id, $type );
431 $icon = array_shift( $icon_src );
432 // Validate icon.
433 if ( ! is_string( $icon ) || empty( $icon ) ) {
434 // Reset variable to NULL (will loop to next icon).
435 $icon = null;
436 // Remove invalid icon from list.
437 unset( $icons[ $idx ] );
438 $icons = array_values( $icons );
439 // Update icons count.
440 $icons_count = count( $icons );
441 }
442 }
443
444 // Display icon.
445 if ( ! is_null( $icon ) ) {
446 $t = $this->get_icon_type( $type );
447 $out = sprintf( $t->display, esc_url( $icon ) );
448 $allowed_tags = [
449 'link' => [
450 'rel' => true,
451 'href' => true,
452 ],
453 ];
454
455 echo wp_kses( $out, $allowed_tags ) . "\r\n";
456 }
457
458 // Update icons array (if necessary).
459 if ( count( $icons ) !== count( $icons_orig ) ) {
460 $this->save_icons( $icons, $type );
461 }
462 }
463
464 /*-** Admin **-*/
465
466 /**
467 * Adds custom links below plugin on plugin listing page
468 *
469 * @param $actions
470 * @param $plugin_file
471 * @param $plugin_data
472 * @param $context
473 */
474 public function admin_plugin_action_links( $actions, $plugin_file, $plugin_data, $context ) {
475 // Add link to settings (only if active).
476 if ( is_plugin_active( $this->util->get_plugin_base_name() ) ) {
477 $settings = __( 'Settings', 'favicon-rotator' );
478 $settings_url = add_query_arg( 'page', dirname( $this->util->get_plugin_base_name() ), admin_url( 'themes.php' ) );
479 array_unshift( $actions, '<a href="' . $settings_url . '" title="' . $settings . '">' . $settings . '</a>' );
480 }
481 return $actions;
482 }
483
484 /**
485 * Get ID of settings section on admin page
486 *
487 * @return string ID of settings section
488 */
489 public function admin_get_settings_section() {
490 return $this->add_prefix( 'settings' );
491 }
492
493 /**
494 * Adds admin submenu item to Appearance menu
495 */
496 public function admin_menu() {
497 $p = add_theme_page( __( 'Favicon', 'favicon-rotator' ), __( 'Favicon', 'favicon-rotator' ), 'edit_theme_options', $this->util->get_plugin_base(), $this->m( 'admin_page' ) );
498 $this->page = $p;
499 // Head.
500 add_action( "admin_print_scripts-$p", $this->m( 'admin_scripts' ) );
501 add_action( "admin_print_styles-$p", $this->m( 'admin_styles' ) );
502 add_action( "admin_head-$p", $this->m( 'admin_help' ) );
503 }
504
505 /**
506 * Defines content for admin page
507 */
508 public function admin_page() {
509 if ( ! current_user_can( 'edit_theme_options' ) ) {
510 wp_die( esc_html__( 'You do not have permission to customize favicons.', 'favicon-rotator' ) );
511 }
512
513 // Get saved icons.
514 if ( isset( $_POST['fv_submit'] ) && check_admin_referer( $this->action_save, $this->action_save_field ) ) {
515 $this->save_icons();
516 }
517 $class = 'button thickbox fv_btn';
518 // Setup query arguments.
519 $filter = array( 'limit', 'lbl_title', 'lbl_add', 'lbl_empty', 'display' );
520 $form_action = ( isset( $_SERVER['REQUEST_URI'] ) ) ? sanitize_url( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '';
521 $upload_args_base = array_diff( array_keys( $this->get_icon_type_defaults() ), $filter );
522 $upload_args_base[] = 'type_name';
523 $upload_args_map = array();
524 foreach ( $upload_args_base as $key ) {
525 $upload_args_map[ $this->add_prefix( $key ) ] = $key;
526 }
527
528 ?>
529 <div class="wrap">
530 <h2><?php esc_html_e( 'Favicon Rotator', 'favicon-rotator' ); ?></h2>
531 <form method="post" action="<?php echo esc_url( $form_action ); ?>">
532 <?php
533 foreach ( $this->get_icon_types() as $tname => $t ) : /* Output UI for icon types */
534 $icons = $this->get_icons( $t->type_name );
535 $upload_args = array( 'tab' => 'library' );
536 foreach ( $upload_args_map as $param => $prop ) {
537 if ( isset( $t->$prop ) ) {
538 $upload_args[ $param ] = $t->$prop;
539 }
540 }
541 $upload_link_escaped = sprintf(
542 '<a href="%1$s" class="%2$s" title="%3$s">%4$s</a>',
543 esc_url( $this->media->get_upload_iframe_src( 'image', $upload_args ) ), /* URL */
544 esc_attr( $class ), /* class */
545 esc_attr( $t->lbl_add ), /* title */
546 esc_html( $t->lbl_add ) /* content */
547 );
548
549 ?>
550 <h3><?php echo esc_html( $t->lbl_title ); ?> <?php echo $upload_link_escaped; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></h3>
551 <div class="fv_container">
552 <p id="fv_msg_empty_<?php echo esc_attr( $t->type_name ); ?>" style="<?php echo ( $icons ) ? 'display: none' : ''; ?>">
553 <?php echo esc_html( $t->lbl_empty ); ?>
554 </p>
555 <ul id="fv_item_wrap_<?php echo esc_attr( $t->type_name ); ?>" class="fv_item_wrap <?php echo ( is_null( $t->limit ) ) ? 'multi' : 'single'; ?>">
556 <?php
557 foreach ( $icons as $icon ) : // List icons.
558 $icon_src = $this->media->get_icon_src( $icon->ID, $t->type_name );
559 $icon_src = array_shift( $icon_src );
560 $icon_media = wp_get_attachment_image_src( $icon->ID, 'full' );
561 $src = array_shift( $icon_media );
562 ?>
563 <li class="fv_item">
564 <div>
565 <img class="icon" src="<?php echo esc_url( $icon_src ); ?>" />
566 <div class="details">
567 <div class="name"><?php echo esc_html( basename( $src ) ); ?></div>
568 <div class="options">
569 <a href="#" id="<?php echo esc_attr( 'fv_id_' . $t->type_name . '_' . $icon->ID ); ?>" class="remove">Remove</a>
570 </div>
571 </div>
572 </div>
573 </li>
574 <?php
575 endforeach; // End icon listing.
576 unset( $icon_src, $icon_media, $src );
577 ?>
578 </ul>
579 <div style="display: none">
580 <li id="fv_item_temp_<?php echo esc_attr( $t->type_name ); ?>" class="fv_item">
581 <div>
582 <img class="icon" src="" />
583 <div class="details">
584 <div class="name"></div>
585 <div class="options">
586 <a href="#" class="remove">Remove</a>
587 </div>
588 </div>
589 </div>
590 </li>
591 </div>
592 </div>
593 <input type="hidden" id="fv_id_<?php echo esc_attr( $t->type_name ); ?>" name="fv_id_<?php echo esc_attr( $t->type_name ); ?>" value="<?php echo esc_attr( $this->get_icon_ids_list( $t->type_name ) ); ?>" />
594 <?php endforeach; /* END UI for icon types */ ?>
595 <?php wp_nonce_field( $this->action_save, $this->action_save_field ); ?>
596 <p class="submit"><input type="submit" class="button-primary" name="fv_submit" value="<?php esc_attr_e( 'Save Changes', 'favicon-rotator' ); ?>" /></p>
597 </form>
598 </div>
599 <?php
600 }
601
602 /**
603 * Adds JS to Admin page
604 */
605 public function admin_scripts() {
606 wp_enqueue_script( 'media-upload' );
607 $h_admin = $this->add_prefix( 'admin_script' );
608 $h_media = $this->add_prefix( 'media' );
609 // TODO: Set script version.
610 $ver = null;
611 $args = array(
612 'in_footer' => true,
613 );
614 wp_enqueue_script( $h_admin, $this->util->get_file_url( 'js/admin.js' ), array( 'jquery-migrate', 'jquery', 'thickbox' ), $ver, $args );
615 wp_enqueue_script( $h_media, $this->util->get_file_url( 'js/media.js' ), array( 'jquery-migrate', 'jquery', $h_admin ), $ver, $args );
616 }
617
618 /**
619 * Adds CSS to Admin page
620 */
621 public function admin_styles() {
622 $ver = null;
623 add_thickbox();
624 wp_enqueue_style( $this->add_prefix( 'admin_styles' ), $this->util->get_file_url( 'css/admin_styles.css' ), ver: $ver );
625 }
626
627 /**
628 * Add contextual help to admin page
629 */
630 public function admin_help() {
631 $screen = get_current_screen();
632 if ( $screen->id === $this->page ) {
633 // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents -- local file.
634 $help = file_get_contents( __DIR__ . $this->file_admin_help );
635 $screen->add_help_tab(
636 array(
637 'id' => $this->add_prefix( 'help' ),
638 'title' => __( 'Overview', 'favicon-rotator' ),
639 'content' => $help,
640 )
641 );
642 }
643 }
644 }