PluginProbe ʕ •ᴥ•ʔ
Smart Custom 404 Error Page / trunk
Smart Custom 404 Error Page vtrunk
trunk 11.4.6 11.4.7 11.4.8
404page / inc / ppf / ppf-admin.php
404page / inc / ppf Last commit date
assets 1 year ago loader.php 1 year ago ppf-admin.php 1 year ago ppf-class.php 1 year ago ppf-plugin-addon.php 1 year ago ppf-plugin.php 1 year ago ppf-settings.php 1 year ago ppf-subclass.php 1 year ago
ppf-admin.php
757 lines
1 <?php
2
3 /**
4 * Admin Class
5 *
6 * Peter's Plugins Foundation 09
7 *
8 * @package PPF09
9 * @author Peter Raschendorfer
10 * @license GPL2+
11 */
12
13 if ( ! defined( 'ABSPATH' ) ) {
14 exit; // Exit if accessed directly
15 }
16
17 if ( !class_exists( 'PPF09_Admin' ) ) {
18
19
20 abstract class PPF09_Admin extends PPF09_SubClass {
21
22
23 /**
24 * settings sections
25 *
26 * @since PPF01
27 * @var array
28 * @access private
29 *
30 * as of PPF04 we initialize an empty array
31 */
32 private $_sections = array();
33
34
35 /**
36 * is setting registered?
37 *
38 * @since PPF04
39 * @var bool
40 * @access private
41 */
42 private $_settings_registered = false;
43
44
45 /**
46 * toolbar
47 *
48 * @since PPF01
49 * @var string
50 * @access private
51 */
52 private $_toolbar;
53
54
55 /**
56 * id of screen
57 *
58 * this can be set via set_screen_id()
59 *
60 * @since PPF01
61 * @var array
62 * @access private
63 */
64 private $_my_screen_id;
65
66
67 /**
68 * add multiple setting sections
69 *
70 * @since PPF01
71 * @param array $sections array of setting sections to add
72 * @access public
73 * @see add_settings()
74 *
75 * as of PPF04 we add the sections to the _sections array to allow adding more sections
76 */
77 public function add_setting_sections( $sections ) {
78
79 foreach( $sections as $section ) {
80
81 // as of PPF04 we use add_setting_section()
82 $this->add_setting_section( $section );
83
84
85 }
86
87 // since PPF04
88 $this->maybe_register_setting();
89
90 }
91
92
93 /**
94 * add a single setting section
95 *
96 * @since PPF04
97 * @param array $sections setting section to add
98 * @access public
99 * @see add_settings()
100 */
101 public function add_setting_section( $section ) {
102
103 // as of PPF04 add sections to _sections array one by one
104 $this->_sections[] = $section;
105
106 if ( array_key_exists( 'fields', $section ) ) {
107
108 $this->add_settings( $section );
109
110 }
111
112 $this->maybe_register_setting();
113
114 }
115
116
117 /**
118 * register the setting
119 *
120 * @since PPF04
121 * @access private
122 *
123 * was part of add_setting_sections() before PPF04
124 */
125 public function maybe_register_setting() {
126
127 if ( ! $this->_settings_registered ) {
128
129 // Register the options
130 // since PPF03 only if there is a settings class
131 // so we can use the same function also if we don't need any settings
132 if ( false !== $this->settings() ) {
133
134 register_setting( $this->core()->get_plugin_slug(), $this->settings()->get_option_name(), array( 'sanitize_callback' => array( $this, 'sanitize_callback' ) ) );
135
136 }
137
138 $this->_settings_registered = true;
139
140 }
141
142
143 }
144
145
146 /**
147 * helper function to add a complete setting section
148 *
149 * @since PPF01
150 * @param array $settings array of settings to add
151 * string $section => ID of the section
152 * int $order => sort order
153 * this was added in PPF04, so we check if it exists for backwards compatibility
154 * string $title => title for section (used by print_setting_sections())
155 * string $icon => icon for tab
156 * since PPF05
157 * string $html => HTML code to add to this section
158 * array $fields => multidimensional array of fields to add
159 * string $key => key of the option array
160 * string $callback => function to call
161 * as of PPF04 this can be an array to enable external callbacks
162 * array $callbackargs => array of arguments to pass to callback function
163 * introduced in PPF08
164 * bool $nosubmit => this section should not show the submit button
165 * @access private
166 */
167 private function add_settings( $settings ) {
168
169 $section_id = $this->core()->get_plugin_slug() . '-' . $settings['section'];
170
171 add_settings_section( $section_id, '', null, $this->core()->get_plugin_slug() );
172
173 foreach ( $settings['fields'] as $field ) {
174
175 $field_id = $this->core()->get_plugin_slug() . '-' . $field['key'];
176
177 // since PPF04
178 if ( is_array( $field['callback'] ) ) {
179
180 $callback = $field['callback'];
181
182 } else {
183
184 $callback = array( $this, $field['callback'] );
185
186 }
187
188 $callbackargs = false;
189
190 if ( isset( $field['callbackargs'] ) && is_array( $field['callbackargs'] ) ) {
191
192 $callbackargs = $field['callbackargs'];
193
194 }
195
196 add_settings_field( $field_id, '' , $callback, $this->core()->get_plugin_slug(), $section_id, $callbackargs );
197
198 }
199
200 return;
201
202 }
203
204
205 /**
206 * helper function to print out a slider styled checkbox
207 *
208 * @since PPF01
209 * @param string $key option key name
210 * @param string $title title
211 * @param string $help anchor to link to in manual
212 * @param string $video YouTube video ID
213 * @param string $note second line
214 * @param bool $disabled true/false (optional)
215 * @access public
216 */
217 public function print_slider_check( $key, $title, $help, $video, $note, $disabled = false ) {
218
219 $dis = '';
220 if ( $disabled ) {
221 $dis = ' disabled="disabled"';
222 }
223
224 $hlp = '';
225 if ( ! empty( $help ) ) {
226 $hlp = $this->add_manual_link( $help );
227 }
228
229 $vid = '';
230 if ( ! empty( $video ) ) {
231 $vid = $this->add_video_link( $video );
232 }
233
234 $add = '';
235 if ( ! empty( $note ) ) {
236 $add = '<br />' . $note;
237 }
238
239 echo '<p class="toggle"><span class="slider"><input type="checkbox" name="' . $this->settings()->get_option_key_name( $key ) . '" id="' . $this->core()->get_plugin_slug() . '-' . $key . '" value="1"' . checked( true, $this->settings()->get( $key ), false ) . $dis . ' /><label for="' . $this->core()->get_plugin_slug() . '-' . $key . '" class="check"></label></span><span class="caption">' . $title . $hlp . $vid . $add . '</span></p>';
240
241 }
242
243
244 /**
245 * helper function to add a plugin manual link
246 *
247 * @since PPF01
248 * @param string $anchor name of the anchor to link to
249 * @return string
250 * @access private
251 */
252 private function add_manual_link( $anchor = '' ) {
253
254 return ' <a class="dashicons dashicons-editor-help" href="https://petersplugins.com/' . $this->core()->get_plugin_slug() . '/manual/#' . $anchor . '"></a>';
255
256 }
257
258 /**
259 * helper function to add a video link
260 *
261 * @since PPF01
262 * @param string $youtubeid ID of the YouTube video
263 * @return string
264 * @access private
265 */
266 private function add_video_link( $youtubeid = '' ) {
267
268 return ' <a class="dashicons dashicons-video-alt3" href="https://youtu.be/' . $youtubeid . '" data-lity></a>';
269
270 }
271
272
273 /**
274 * print out setting sections
275 * it is not possible to use do_settings_sections() because we are not able to create a tabbed interface
276 *
277 * @since PPF01
278 * @access public
279 * @see add_settings()
280 */
281 public function print_setting_sections() {
282
283 $currentclass = ' current';
284
285 foreach( $this->_sections as $section ) {
286
287 $section_id = $this->core()->get_plugin_slug() . '-' . $section['section'];
288 $extraclass = '';
289
290 if ( array_key_exists( 'nosubmit', $section ) && true === $section['nosubmit'] ) {
291
292 $extraclass = ' nosubmit';
293 }
294
295 echo '<div class="panel' . $extraclass . $currentclass . '" id="content-' . $section_id . '">';
296
297 if ( array_key_exists( 'html', $section ) ) {
298
299 echo '<div class="pp-admin-section-html">';
300 echo $section['html'];
301 echo '</div>';
302
303 }
304
305 if ( array_key_exists( 'fields', $section ) ) {
306
307 echo '<table class="form-table pp-admin-section-fields">';
308 do_settings_fields( $this->core()->get_plugin_slug(), $section_id );
309 echo '</table>';
310
311 }
312
313 echo '</div>';
314
315 $currentclass = '';
316
317 }
318
319 }
320
321
322 /**
323 * print out setting sections navigation
324 *
325 * @since PPF01
326 * @access public
327 * @see add_settings()
328 */
329 public function print_setting_sections_nav() {
330
331 $currentclass = ' current';
332
333 echo '<ul class="tab-navigation">';
334
335 foreach( $this->_sections as $section ) {
336
337 $section_id = $this->core()->get_plugin_slug() . '-' . $section['section'];
338
339 $iconclass = '';
340
341 if ( isset( $section['icon'] ) ) {
342
343 $iconclass = ' has-icon icon-' . $section['icon'];
344 }
345
346 echo '<li><div class="tabset' . $currentclass . $iconclass . '" id="tab-' . $section_id . '" data-tab-content="content-' . $section_id . '">' . $section['title'] . '</div>';
347
348 $currentclass = '';
349
350 }
351
352 echo '</ul>';
353
354 }
355
356
357 /**
358 * sanitize the posted values
359 *
360 * @since PPF01
361 * @param array $settings array of settings to save
362 * @access public
363 */
364 public function sanitize_callback( $settings ) {
365
366 // since PPF06
367 // if wen don't get anything we need to create an empty array
368 if ( empty( $settings ) ) {
369 $settings = array();
370 }
371
372 foreach ( $this->settings()->get_defaults() as $key => $value ) {
373
374 if ( true === is_bool( $value ) ) {
375
376 if ( ! array_key_exists( $key, $settings ) ) {
377
378 // we have to add the missing keys
379 // HTML forms only send data if a checkbox is checked
380 // missing keys would be overwritten with their default on next load
381 // this concerns only boolean values
382 // if key does not exist the checkbox was not checked
383 // so we have to handle it as false
384 $settings[$key] = false;
385
386 } else {
387
388 // also we check if a given value is boolean
389 // otherwise we reset it to false
390 // this is for security
391 // so it is not possible to pass non boolean values
392 // if a checkbox was checked we get 1
393 // so we only have to check for 1
394
395 if ( 1 != $settings[$key] ) {
396
397 $settings[$key] = false;
398
399 }
400
401 }
402
403 }
404
405 }
406
407 // it is not possible to do other sanitation because we do not know what to do
408 // more sanitation has to be done by the plugin itself
409
410 return $this->sanitize_settings( $settings );
411
412 }
413
414
415 /**
416 * sanitize the settings
417 * called by sanitize_callback()
418 * this can be used to sanitize the settings after missing keys have been added
419 *
420 * @since PPF01
421 * @param array $settings array of settings to save
422 * @access public
423 */
424 public function sanitize_settings( $settings ) {
425
426 return $settings;
427
428 }
429
430
431 /**
432 * add toolbar icons
433 *
434 * @since PPF01
435 * @param array $icons array of icons to show in toolbar
436 * string $link => URL to link to
437 * string $title => title to show
438 * string $icon => icon to use from dashicons
439 * @access public
440 */
441 public function add_toolbar_icons( $icons ) {
442
443 $this->_toolbar = '<nav>';
444
445 foreach ( $icons as $icon ) {
446
447 $this->_toolbar .= '<a href="' . esc_url( $icon['link'] ) . '" title="' . $icon['title'] . '"><span class="dashicons ' . $icon['icon'] . '"></span><span class="text">' . $icon['title'] . '</span></a>';
448
449 }
450
451 $this->_toolbar .= '</nav>';
452
453 }
454
455
456 /**
457 * show the admin page
458 *
459 * @since PPF01
460 * @param string $capability minimum required capability to show page (optional)
461 * @access public
462 */
463 public function show( $capability = 'read' ) {
464
465 if ( !current_user_can( $capability ) ) {
466
467 wp_die( esc_html__( 'You do not have sufficient permissions to access this page.' ) );
468
469 }
470
471
472 // sort the sections
473
474 // see add_settings()
475 $sort = false;
476
477 foreach( $this->_sections as $section ) {
478
479 if ( array_key_exists( 'order', $section ) ) {
480
481 $sort = true;
482 break;
483
484 }
485 }
486
487 if ( $sort ) {
488
489 usort( $this->_sections, function( $a, $b ) {
490 return $a['order'] - $b['order'];
491
492 } );
493
494 }
495
496 // end of sort
497
498 if ( get_current_screen()->parent_base != 'options-general' ) {
499
500 // On Option Screens settings_errors() is called automatically
501 settings_errors();
502
503 }
504
505 echo '<div class="wrap pp-admin-page-wrapper" id="pp-' . $this->core()->get_plugin_slug() . '-settings"><div class="pp-admin-notice-area"><div class="wp-header-end"></div></div>';
506 echo '<div class="pp-admin-page-header">';
507 echo $this->_toolbar;
508 echo '<div class="pp-admin-page-title"><h1>' . $this->core()->get_plugin_shortname() . '</h1>';
509 echo '</div>';
510 $this->print_setting_sections_nav();
511 echo '</div>';
512 echo '<div class="pp-admin-page-inner"><form method="POST" action="options.php">';
513 echo '<div class="tab-panel">';
514 settings_fields( $this->core()->get_plugin_slug() );
515 $this->print_setting_sections();
516 submit_button();
517 echo '</div></form></div>';
518 echo '<div class="pp-admin-page-footer"><p>Need help? <a href="https://wordpress.org/support/plugin/404page/" target="_blank">Get Support</a>.</p>';
519 echo '<p>⭐ Love 404 Page? <a href="https://wordpress.org/support/plugin/404page/reviews/#new-post" target="_blank">Please rate it 5-stars on WordPress.org.</a> Thank you!</p>';
520 echo '<p>Maintained with ❤️ and ☕ by <a href="https://www.nerdpress.net" target="_blank">NerdPress</a>.</p></div></div>';
521
522 echo '<script>var pp_admin_cookie_prefix="' . $this->core()->get_plugin_slug() . '";</script>';
523
524 wp_enqueue_style( $this->core()->get_plugin_slug() . '-ppf03', $this->get_foundation_asset_url( 'css', 'pp-admin-page.css' ) );
525
526 wp_enqueue_script( $this->core()->get_plugin_slug() . '-ppf03-cookie', $this->get_foundation_asset_url( 'js', 'jquery.cookie.js' ), array( 'jquery' ), false, true );
527 wp_enqueue_script( $this->core()->get_plugin_slug() . '-ppf03', $this->get_foundation_asset_url( 'js', 'pp-admin-page.js' ), array( 'jquery', $this->core()->get_plugin_slug() . '-ppf03-cookie' ), false, true );
528
529 }
530
531
532 /**
533 * set screen id
534 *
535 * @since PPF01
536 * @param string $id id of screen
537 * @access public
538 */
539 public function set_screen_id( $id ) {
540
541 $this->_my_screen_id = $id;
542
543 }
544
545
546 /**
547 * get screen id
548 *
549 * @since PPF01
550 * @return string
551 * @access public
552 */
553 public function get_screen_id() {
554
555 return $this->_my_screen_id;
556
557 }
558
559
560 /**
561 * show admin notice to ask for rating
562 * this function does not show the notice immediately
563 * this function just needs to be called and it takes care of everything
564 *
565 * @since PPF01
566 * @param array $content array of texts to show
567 * string $title => e.g. 'Are you happy with the example plugin?'
568 * string $subtitle => e.g. 'You've been using this plugin for a while now. Would be great to get some feedback!'
569 * string $button_yes => e.g. 'Yes, I'm happy with it'
570 * string $button_no => e.g. 'Not really'
571 * string $button_later => e.g. 'Ask me later'
572 * string $button_close => e.g. 'Never show again'
573 * string $like => e.g. 'I'm really glad you like it. I do not ask for a donation. All I'm asking you for is to give it a good rating. Thank you very much.
574 * string $button_rate => e.g. 'Yes, I'd like to rate it'
575 * string $dislike => e.g. 'I'm really sorry you don't like it. Would you please do me a favor and drop me line, why you are not happy with it? Maybe I can do better...'
576 * string $button_contact => e.g. 'Yes sure'
577 * @param array $links array of links
578 * string $rate => e.g. https://wordpress.org/support/plugin/example/reviews/
579 * string $contact => e.g. https://petersplugins.com/contact/
580 * @access public
581 */
582 public function init_rating_notice( $content, $links ) {
583
584 // quit immediately if message has already been closed
585 if ( 'YES' == $this->core()->data_get( 'ask_rating_closed' ) ) {
586 return;
587 }
588
589 $show_notice_start = $this->core()->data_get( 'ask_rating_start' );
590
591 // if start date is not set, set it and quit immediately
592 if ( false === $show_notice_start ) {
593 $this->core()->data_set( 'ask_rating_start', time() + 30 * DAY_IN_SECONDS );
594 $this->core()->data_save();
595 return;
596 }
597
598 // quit immediately if start date is not reached yet
599 if ( time() < $show_notice_start ) {
600 return;
601 }
602
603 // quit immediately if current user is not an admin
604 if ( ! current_user_can( 'manage_options' ) ) {
605 return;
606 }
607
608 $prefix = 'pp-' . $this->core()->get_plugin_slug();
609 $nonce = wp_create_nonce( $prefix );
610
611 // prepare to show notice
612 add_action( 'admin_notices', function() use( $content, $links, $prefix, $nonce ) {
613
614 // show notice only on certain pages
615 // it's not possible to check this earlier, because we need the id of the current screen for that
616 if ( ! in_array( get_current_screen()->id, array( 'dashboard', 'themes', 'plugins', 'options-general' , $this->get_screen_id() ) ) ) {
617 return;
618 }
619
620
621 ?>
622 <div class="notice notice-info" id="<?php echo $prefix; ?>-review-notice">
623 <h3 style="margin-bottom: 0"><?php echo $content['title']; ?></h3>
624 <div class="<?php echo $prefix; ?>-review-notice-container">
625 <div id="<?php echo $prefix; ?>-review-step-1" class="<?php echo $prefix; ?>-review-notice-step">
626 <p><?php echo $content['subtitle']; ?></p>
627 <p><a id="<?php echo $prefix; ?>-review-happy" class="button button-primary" href="javascript:void(0);"><?php echo $content['button_yes']; ?></a> <a id="<?php echo $prefix; ?>-review-unhappy" class="button" href="javascript:void(0);"><?php echo $content['button_no']; ?></a></p>
628 </div>
629 <div id="<?php echo $prefix; ?>-review-step-like" class="<?php echo $prefix; ?>-review-notice-step">
630 <p><?php echo $content['like']; ?></p>
631 <p><a class="button button-primary" href="<?php echo $links['rate']; ?>"><?php echo $content['button_rate']; ?></a></p>
632 </div>
633 <div id="<?php echo $prefix; ?>-review-step-dislike" class="<?php echo $prefix; ?>-review-notice-step">
634 <p><?php echo $content['dislike']; ?></p>
635 <p><a class="button button-primary" href="<?php echo $links['contact']; ?>"><?php echo $content['button_contact']; ?></a></p>
636 </div>
637 </div>
638 <p class="wp-clearfix"><a id="<?php echo $prefix; ?>-review-later" class="<?php echo $prefix; ?>-review-action" href="javascript:void(0);"><?php echo $content['button_later']; ?></a> <a id="<?php echo $prefix; ?>-review-close" class="<?php echo $prefix; ?>-review-action" href="javascript:void(0);"><?php echo $content['button_close']; ?></a></p>
639 </div>
640 <?php
641
642 } );
643
644
645 // Since PPF04 we add CSS and JS to footer for compatibility reasons
646
647 add_action( 'admin_print_footer_scripts', function() use( $content, $links, $prefix, $nonce ) {
648
649 // show notice only on certain pages
650 // it's not possible to check this earlier, because we need the id of the current screen for that
651 if ( ! in_array( get_current_screen()->id, array( 'dashboard', 'themes', 'plugins', 'options-general' , $this->get_screen_id() ) ) ) {
652 return;
653 }
654
655 echo '
656 <style type="text/css">
657 #' . $prefix . '-review-step-like, #' . $prefix . '-review-step-dislike {
658 display: none;
659 }
660 #' . $prefix . 'review-later, #' . $prefix . '-review-close, #' . $prefix . '-review-later:before, #' . $prefix . '-review-close:before {
661 display: block;
662 height: 20px;
663 line-height: 20px;
664 text-decoration: none;
665 }
666 #' . $prefix . '-review-later, #' . $prefix . '-review-close {
667 float: left;
668 position: relative;
669 padding-left: 22px;
670 }
671 #' . $prefix . '-review-later {
672 margin-right: 12px;
673 }
674 #' . $prefix . '-review-later:before, #' . $prefix . '-review-close:before {
675 font-family: dashicons;
676 font-size: 20px;
677 position: absolute;
678 left: 0;
679 top: 0;
680 }
681 #' . $prefix . '-review-later:before {
682 content: "\f508";
683 }
684 #' . $prefix . '-review-close:before {
685 content: "\f153";
686 }
687 </style>
688
689 <script type="text/javascript">
690 jQuery( function( $ ) {
691
692 $( "#' . $prefix . '-review-happy" ).click( function() {
693 $( "#' . $prefix . '-review-step-1" ).fadeOut( 400, function() {
694 $( "#' . $prefix . '-review-step-like" ).fadeIn();
695 });
696 } );
697
698 $( "#' . $prefix . '-review-unhappy" ).click( function() {
699 $( "#' . $prefix . '-review-step-1" ).fadeOut( 400, function() {
700 $( "#' . $prefix . '-review-step-dislike" ).fadeIn();
701 });
702 } );
703
704 $( ".' . $prefix . '-review-action" ).click( function() {
705
706 $.post(
707 ajaxurl, {
708 action : "' . $prefix . '-review-action",
709 command : $(this).attr( "id" ),
710 securekey : "' . $nonce .'"
711 }
712 );
713 $( "#' . $prefix . '-review-notice" ).fadeOut();
714
715 } );
716
717 } );
718 </script>';
719
720
721 } );
722
723
724 // prepare for ajax
725 add_action( 'wp_ajax_' . $prefix . '-review-action', function() use( $prefix ) {
726
727 check_ajax_referer( $prefix, 'securekey' ); // dies if check fails
728
729 if ( isset( $_POST['command'] ) ) {
730
731 if ( $prefix . '-review-later' == $_POST['command'] ) {
732
733 // move start date 14 days into future
734 $this->core()->data_set( 'ask_rating_start', time() + 14 * DAY_IN_SECONDS );
735 $this->core()->data_save();
736
737 }
738
739 if ( $prefix . '-review-close' == $_POST['command'] ) {
740
741 // do not show notice again
742 $this->core()->data_set( 'ask_rating_closed', 'YES' );
743 $this->core()->data_save();
744
745 }
746
747 }
748
749 wp_die();
750
751 } );
752
753 }
754
755 }
756
757 }