WP Show Posts - Version 0.1

Version Description

  • Initial release

=

Download this release

Release Info

Developer edge22
Plugin Icon 128x128 WP Show Posts
Version 0.1
Comparing to
See all releases

Version 0.1

Files changed (63) hide show
  1. admin/admin.php +80 -0
  2. admin/ajax.php +105 -0
  3. admin/butterbean/butterbean.php +832 -0
  4. admin/butterbean/changelog.md +3 -0
  5. admin/butterbean/contributing.md +31 -0
  6. admin/butterbean/css/butterbean.css +330 -0
  7. admin/butterbean/css/butterbean.min.css +1 -0
  8. admin/butterbean/inc/class-control.php +399 -0
  9. admin/butterbean/inc/class-manager.php +550 -0
  10. admin/butterbean/inc/class-section.php +291 -0
  11. admin/butterbean/inc/class-setting.php +217 -0
  12. admin/butterbean/inc/controls/class-control-checkboxes.php +43 -0
  13. admin/butterbean/inc/controls/class-control-color.php +75 -0
  14. admin/butterbean/inc/controls/class-control-date.php +135 -0
  15. admin/butterbean/inc/controls/class-control-excerpt.php +73 -0
  16. admin/butterbean/inc/controls/class-control-image.php +101 -0
  17. admin/butterbean/inc/controls/class-control-multi-avatars.php +89 -0
  18. admin/butterbean/inc/controls/class-control-palette.php +43 -0
  19. admin/butterbean/inc/controls/class-control-parent.php +97 -0
  20. admin/butterbean/inc/controls/class-control-radio-image.php +45 -0
  21. admin/butterbean/inc/controls/class-control-radio.php +46 -0
  22. admin/butterbean/inc/controls/class-control-select-group.php +52 -0
  23. admin/butterbean/inc/controls/class-control-textarea.php +42 -0
  24. admin/butterbean/inc/functions-core.php +163 -0
  25. admin/butterbean/inc/settings/class-setting-array.php +142 -0
  26. admin/butterbean/inc/settings/class-setting-date.php +105 -0
  27. admin/butterbean/js/butterbean.js +340 -0
  28. admin/butterbean/js/butterbean.min.js +1 -0
  29. admin/butterbean/license.md +339 -0
  30. admin/butterbean/readme.md +107 -0
  31. admin/butterbean/screenshot-1.png +0 -0
  32. admin/butterbean/tmpl/control-checkbox.php +11 -0
  33. admin/butterbean/tmpl/control-checkboxes.php +22 -0
  34. admin/butterbean/tmpl/control-color.php +11 -0
  35. admin/butterbean/tmpl/control-date.php +55 -0
  36. admin/butterbean/tmpl/control-image.php +24 -0
  37. admin/butterbean/tmpl/control-multi-avatars.php +23 -0
  38. admin/butterbean/tmpl/control-palette.php +23 -0
  39. admin/butterbean/tmpl/control-parent.php +17 -0
  40. admin/butterbean/tmpl/control-radio-image.php +17 -0
  41. admin/butterbean/tmpl/control-radio.php +22 -0
  42. admin/butterbean/tmpl/control-select-group.php +33 -0
  43. admin/butterbean/tmpl/control-select.php +20 -0
  44. admin/butterbean/tmpl/control-textarea.php +11 -0
  45. admin/butterbean/tmpl/control.php +11 -0
  46. admin/butterbean/tmpl/manager.php +2 -0
  47. admin/butterbean/tmpl/nav.php +1 -0
  48. admin/butterbean/tmpl/section.php +3 -0
  49. admin/css/admin.css +37 -0
  50. admin/images/spinner.gif +0 -0
  51. admin/js/admin-scripts.js +205 -0
  52. admin/js/button.js +42 -0
  53. admin/metabox.php +868 -0
  54. admin/post-type.php +57 -0
  55. css/wp-show-posts-min.css +1 -0
  56. css/wp-show-posts.css +216 -0
  57. inc/defaults.php +76 -0
  58. inc/functions.php +417 -0
  59. inc/image-resizer.php +241 -0
  60. js/jquery.matchHeight.js +399 -0
  61. license.txt +23 -0
  62. readme.txt +156 -0
  63. wp-show-posts.php +423 -0
admin/admin.php ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ if ( ! function_exists( 'wpsp_admin_scripts' ) ) :
3
+ /**
4
+ * Add our admin scripts and styles
5
+ * @since 0.1
6
+ */
7
+ add_action( 'admin_print_scripts-post-new.php', 'wpsp_admin_scripts', 11 );
8
+ add_action( 'admin_print_scripts-post.php', 'wpsp_admin_scripts', 11 );
9
+ function wpsp_admin_scripts()
10
+ {
11
+ global $post_type, $post;
12
+ if( 'wp_show_posts' == $post_type ) :
13
+ wp_enqueue_script( 'wpsp-admin-scripts', plugin_dir_url( __FILE__ ) . "js/admin-scripts.js", array( 'jquery' ), '', true );
14
+ wp_localize_script( 'wpsp-admin-scripts', 'wpsp_object', array (
15
+ 'post_id' => ( isset( $post ) ) ? $post->ID : false,
16
+ 'nonce' => wp_create_nonce( 'wpsp_nonce' )
17
+ ));
18
+ endif;
19
+ wp_enqueue_style( 'wpsp-admin', plugin_dir_url( __FILE__ ) . "css/admin.css" );
20
+ }
21
+ endif;
22
+
23
+ if ( ! function_exists( 'wpsp_translatable_strings' ) ) :
24
+ /**
25
+ * Add some javascript variables to the admin head
26
+ * @since 0.1
27
+ */
28
+ add_action( 'admin_head','wpsp_translatable_strings', 0 );
29
+ function wpsp_translatable_strings()
30
+ {
31
+ ?>
32
+ <script type="text/javascript">
33
+ var wpsp_add_posts = '<?php _e( 'WP Show Posts','wp-show-posts' );?>';
34
+ var wpsp_nonce = '<?php echo wp_create_nonce( 'wpsp_nonce' ); ?>';
35
+ </script>
36
+ <?php
37
+ }
38
+ endif;
39
+
40
+ if ( ! function_exists( 'wpsp_add_shortcode_button' ) ) :
41
+ /*
42
+ * Set it up so we can register our TinyMCE button
43
+ * @since 0.1
44
+ */
45
+ add_action('admin_init', 'wpsp_add_shortcode_button');
46
+ function wpsp_add_shortcode_button()
47
+ {
48
+ // check user permissions
49
+ if ( ! current_user_can( 'edit_posts' ) && ! current_user_can( 'edit_pages' ) )
50
+ return;
51
+
52
+ // check if WYSIWYG is enabled
53
+ if ( get_user_option( 'rich_editing' ) == 'true') {
54
+ add_filter( 'mce_external_plugins', 'wpsp_shortcodes_add_tinymce_plugin' );
55
+ add_filter( 'mce_buttons', 'wpsp_shortcodes_register_button' );
56
+ }
57
+ }
58
+ endif;
59
+
60
+ if ( ! function_exists( 'wpsp_shortcodes_add_tinymce_plugin' ) ) :
61
+ /*
62
+ * Register our tinyMCE button javascript
63
+ * @since 0.1
64
+ */
65
+ function wpsp_shortcodes_add_tinymce_plugin( $plugin_array ) {
66
+ $plugin_array[ 'wpsp_shortcode_button' ] = plugin_dir_url( __FILE__ ) . '/js/button.js';
67
+ return $plugin_array;
68
+ }
69
+ endif;
70
+
71
+ if ( ! function_exists( 'wpsp_shortcodes_register_button' ) ) :
72
+ /*
73
+ * Register our TinyMCE button
74
+ * @since 0.1
75
+ */
76
+ function wpsp_shortcodes_register_button( $buttons ) {
77
+ array_push( $buttons, 'wpsp_shortcode_button' );
78
+ return $buttons;
79
+ }
80
+ endif;
admin/ajax.php ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ if ( ! function_exists( 'wpsp_get_json_option' ) ) :
3
+ /**
4
+ * Get the current option value
5
+ * @since 0.1
6
+ */
7
+ add_action( 'wp_ajax_wpsp_get_json_option', 'wpsp_get_json_option' );
8
+ function wpsp_get_json_option()
9
+ {
10
+ if ( ! isset( $_POST[ 'wpsp_nonce' ] ) || ! wp_verify_nonce( $_POST[ 'wpsp_nonce' ], 'wpsp_nonce' ) )
11
+ wp_die( 'Permission declined' );
12
+
13
+ $option = ( get_post_meta( intval( $_POST[ 'id' ] ), sanitize_text_field( $_POST[ 'key' ] ) ) ) ? get_post_meta( intval( $_POST[ 'id' ] ), sanitize_text_field( $_POST[ 'key' ] ), true ) : false;
14
+
15
+ if ( $option )
16
+ echo wp_json_encode( $option );
17
+
18
+ die();
19
+ }
20
+ endif;
21
+
22
+ if ( ! function_exists( 'wpsp_get_terms' ) ) :
23
+ /**
24
+ * Get all of our terms depending on the set taxonomy
25
+ * @since 0.1
26
+ */
27
+ add_action( 'wp_ajax_wpsp_get_terms', 'wpsp_get_terms' );
28
+ function wpsp_get_terms()
29
+ {
30
+ if ( ! isset( $_POST[ 'wpsp_nonce' ] ) || ! wp_verify_nonce( $_POST[ 'wpsp_nonce' ], 'wpsp_nonce' ) )
31
+ wp_die( 'Permission declined' );
32
+
33
+ $terms = get_terms( sanitize_text_field( $_POST[ 'taxonomy' ] ), 'orderby=count&hide_empty=1' );
34
+ $count = count( $terms );
35
+ $types = array();
36
+ if ( $count > 0 ) {
37
+ foreach ( $terms as $term ) {
38
+ $types[] = $term->slug;
39
+ }
40
+ }
41
+
42
+ echo wp_json_encode( $types );
43
+
44
+ die();
45
+ }
46
+ endif;
47
+
48
+ if ( ! function_exists( 'wpsp_get_taxonomies' ) ) :
49
+ /**
50
+ * Get out taxonomies based on the set post type
51
+ * @since 0.1
52
+ */
53
+ add_action( 'wp_ajax_wpsp_get_taxonomies', 'wpsp_get_taxonomies' );
54
+ function wpsp_get_taxonomies()
55
+ {
56
+ if ( ! isset( $_POST[ 'wpsp_nonce' ] ) || ! wp_verify_nonce( $_POST[ 'wpsp_nonce' ], 'wpsp_nonce' ) )
57
+ wp_die( 'Permission declined' );
58
+
59
+ $terms = get_object_taxonomies( sanitize_text_field( $_POST[ 'post_type' ] ) );
60
+ $count = count( $terms );
61
+ $types = array();
62
+ if ( $count > 0 ) {
63
+ foreach ( $terms as $term ) {
64
+ $types[] = $term;
65
+ }
66
+ }
67
+
68
+ echo wp_json_encode( $types );
69
+
70
+ die();
71
+ }
72
+ endif;
73
+
74
+ if ( ! function_exists( 'wpsp_get_post_lists' ) ) :
75
+ /**
76
+ * Get all of our post lists
77
+ * @since 0.1
78
+ */
79
+ add_action( 'wp_ajax_wpsp_get_post_lists', 'wpsp_get_post_lists' );
80
+ function wpsp_get_post_lists()
81
+ {
82
+ if ( ! isset( $_POST[ 'wpsp_nonce' ] ) || ! wp_verify_nonce( $_POST[ 'wpsp_nonce' ], 'wpsp_nonce' ) )
83
+ wp_die( 'Permission declined' );
84
+
85
+ $args = array(
86
+ 'posts_per_page' => -1,
87
+ 'post_type' => 'wp_show_posts',
88
+ 'post_status' => 'publish',
89
+ 'showposts' => -1
90
+ );
91
+ $posts = get_posts( $args );
92
+
93
+ $count = count( $posts );
94
+ $types = array();
95
+ if ( $count > 0 ) {
96
+ foreach ( $posts as $post ) {
97
+ $types[] = array( 'text' => $post->post_title, 'value' => $post->ID );
98
+ }
99
+ }
100
+
101
+ echo wp_json_encode( $types );
102
+
103
+ die();
104
+ }
105
+ endif;
admin/butterbean/butterbean.php ADDED
@@ -0,0 +1,832 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Plugin Name: ButterBean
4
+ * Plugin URI: https://github.com/justintadlock/butterbean
5
+ * Description: A little post meta framework.
6
+ * Version: 1.0.0-dev
7
+ * Author: Justin Tadlock
8
+ * Author URI: http://themehybrid.com
9
+ *
10
+ * @package ButterBean
11
+ * @author Justin Tadlock <justin@justintadlock.com>
12
+ * @copyright Copyright (c) 2015-2016, Justin Tadlock
13
+ * @link https://github.com/justintadlock/butterbean
14
+ * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
15
+ */
16
+
17
+ if ( ! class_exists( 'ButterBean' ) ) {
18
+
19
+ /**
20
+ * Main ButterBean class. Runs the show.
21
+ *
22
+ * @since 1.0.0
23
+ * @access public
24
+ */
25
+ final class ButterBean {
26
+
27
+ /**
28
+ * Directory path to the plugin folder.
29
+ *
30
+ * @since 1.0.0
31
+ * @access public
32
+ * @var string
33
+ */
34
+ public $dir_path = '';
35
+
36
+ /**
37
+ * Directory URI to the plugin folder.
38
+ *
39
+ * @since 1.0.0
40
+ * @access public
41
+ * @var string
42
+ */
43
+ public $dir_uri = '';
44
+
45
+ /**
46
+ * Directory path to the template folder.
47
+ *
48
+ * @since 1.0.0
49
+ * @access public
50
+ * @var string
51
+ */
52
+ public $tmpl_path = '';
53
+
54
+ /**
55
+ * Array of managers.
56
+ *
57
+ * @since 1.0.0
58
+ * @access public
59
+ * @var array
60
+ */
61
+ public $managers = array();
62
+
63
+ /**
64
+ * Array of manager types.
65
+ *
66
+ * @since 1.0.0
67
+ * @access public
68
+ * @var array
69
+ */
70
+ public $manager_types = array();
71
+
72
+ /**
73
+ * Array of section types.
74
+ *
75
+ * @since 1.0.0
76
+ * @access public
77
+ * @var array
78
+ */
79
+ public $section_types = array();
80
+
81
+ /**
82
+ * Array of control types.
83
+ *
84
+ * @since 1.0.0
85
+ * @access public
86
+ * @var array
87
+ */
88
+ public $control_types = array();
89
+
90
+ /**
91
+ * Array of setting types.
92
+ *
93
+ * @since 1.0.0
94
+ * @access public
95
+ * @var array
96
+ */
97
+ public $setting_types = array();
98
+
99
+ /**
100
+ * Whether this is a new post. Once the post is saved and we're
101
+ * no longer on the `post-new.php` screen, this is going to be
102
+ * `false`.
103
+ *
104
+ * @since 1.0.0
105
+ * @access public
106
+ * @var bool
107
+ */
108
+ public $is_new_post = false;
109
+
110
+ /**
111
+ * Returns the instance.
112
+ *
113
+ * @since 1.0.0
114
+ * @access public
115
+ * @return object
116
+ */
117
+ public static function get_instance() {
118
+
119
+ static $instance = null;
120
+
121
+ if ( is_null( $instance ) ) {
122
+ $instance = new self;
123
+ $instance->setup();
124
+ $instance->includes();
125
+ $instance->setup_actions();
126
+ }
127
+
128
+ return $instance;
129
+ }
130
+
131
+ /**
132
+ * Constructor method.
133
+ *
134
+ * @since 1.0.0
135
+ * @access private
136
+ * @return void
137
+ */
138
+ private function __construct() {}
139
+
140
+ /**
141
+ * Initial plugin setup.
142
+ *
143
+ * @since 1.0.0
144
+ * @access private
145
+ * @return void
146
+ */
147
+ private function setup() {
148
+
149
+ $this->dir_path = apply_filters( 'butterbean_dir_path', trailingslashit( plugin_dir_path( __FILE__ ) ) );
150
+ $this->dir_uri = apply_filters( 'butterbean_dir_uri', trailingslashit( plugin_dir_url( __FILE__ ) ) );
151
+
152
+ $this->tmpl_path = trailingslashit( $this->dir_path . 'tmpl' );
153
+ }
154
+
155
+ /**
156
+ * Loads include and admin files for the plugin.
157
+ *
158
+ * @since 1.0.0
159
+ * @access private
160
+ * @return void
161
+ */
162
+ private function includes() {
163
+
164
+ // If not in the admin, bail.
165
+ if ( ! is_admin() )
166
+ return;
167
+
168
+ // Load base classes.
169
+ require_once( $this->dir_path . 'inc/class-manager.php' );
170
+ require_once( $this->dir_path . 'inc/class-section.php' );
171
+ require_once( $this->dir_path . 'inc/class-control.php' );
172
+ require_once( $this->dir_path . 'inc/class-setting.php' );
173
+
174
+ // Load control sub-classes.
175
+ require_once( $this->dir_path . 'inc/controls/class-control-checkboxes.php' );
176
+ require_once( $this->dir_path . 'inc/controls/class-control-color.php' );
177
+ require_once( $this->dir_path . 'inc/controls/class-control-date.php' );
178
+ require_once( $this->dir_path . 'inc/controls/class-control-image.php' );
179
+ require_once( $this->dir_path . 'inc/controls/class-control-palette.php' );
180
+ require_once( $this->dir_path . 'inc/controls/class-control-radio.php' );
181
+ require_once( $this->dir_path . 'inc/controls/class-control-radio-image.php' );
182
+ require_once( $this->dir_path . 'inc/controls/class-control-select-group.php' );
183
+ require_once( $this->dir_path . 'inc/controls/class-control-textarea.php' );
184
+
185
+ require_once( $this->dir_path . 'inc/controls/class-control-excerpt.php' );
186
+ require_once( $this->dir_path . 'inc/controls/class-control-multi-avatars.php' );
187
+ require_once( $this->dir_path . 'inc/controls/class-control-parent.php' );
188
+
189
+ // Load setting sub-classes.
190
+ require_once( $this->dir_path . 'inc/settings/class-setting-date.php' );
191
+ require_once( $this->dir_path . 'inc/settings/class-setting-array.php' );
192
+
193
+ // Load functions.
194
+ require_once( $this->dir_path . 'inc/functions-core.php' );
195
+ }
196
+
197
+ /**
198
+ * Sets up initial actions.
199
+ *
200
+ * @since 1.0.0
201
+ * @access private
202
+ * @return void
203
+ */
204
+ private function setup_actions() {
205
+
206
+ // Call the register function.
207
+ add_action( 'load-post.php', array( $this, 'register' ), 95 );
208
+ add_action( 'load-post-new.php', array( $this, 'register' ), 95 );
209
+
210
+ // Register default types.
211
+ add_action( 'butterbean_register', array( $this, 'register_manager_types' ), -95 );
212
+ add_action( 'butterbean_register', array( $this, 'register_section_types' ), -95 );
213
+ add_action( 'butterbean_register', array( $this, 'register_control_types' ), -95 );
214
+ add_action( 'butterbean_register', array( $this, 'register_setting_types' ), -95 );
215
+ }
216
+
217
+ /**
218
+ * Registration callback. Fires the `butterbean_register` action hook to
219
+ * allow plugins to register their managers.
220
+ *
221
+ * @since 1.0.0
222
+ * @access public
223
+ * @return void
224
+ */
225
+ public function register() {
226
+
227
+ // If this is a new post, set the new post boolean.
228
+ if ( 'load-post-new.php' === current_action() )
229
+ $this->is_new_post = true;
230
+
231
+ // Get the current post type.
232
+ $post_type = get_current_screen()->post_type;
233
+
234
+ // Action hook for registering managers.
235
+ do_action( 'butterbean_register', $this, $post_type );
236
+
237
+ // Loop through the managers to see if we're using on on this screen.
238
+ foreach ( $this->managers as $manager ) {
239
+
240
+ // If we found a matching post type, add our actions/filters.
241
+ if ( ! in_array( $post_type, (array) $manager->post_type ) ) {
242
+ $this->unregister_manager( $manager->name );
243
+ continue;
244
+ }
245
+
246
+ // Sort controls and sections by priority.
247
+ uasort( $manager->controls, array( $this, 'priority_sort' ) );
248
+ uasort( $manager->sections, array( $this, 'priority_sort' ) );
249
+ }
250
+
251
+ // If no managers registered, bail.
252
+ if ( ! $this->managers )
253
+ return;
254
+
255
+ // Add meta boxes.
256
+ add_action( 'add_meta_boxes', array( $this, 'add_meta_boxes' ) );
257
+
258
+ // Save settings.
259
+ add_action( 'save_post', array( $this, 'update' ) );
260
+
261
+ // Load scripts and styles.
262
+ add_action( 'admin_enqueue_scripts', array( $this, 'enqueue' ) );
263
+
264
+ // Localize scripts and Undescore templates.
265
+ add_action( 'admin_footer', array( $this, 'localize_scripts' ) );
266
+ add_action( 'admin_footer', array( $this, 'print_templates' ) );
267
+ }
268
+
269
+ /**
270
+ * Register a manager.
271
+ *
272
+ * @since 1.0.0
273
+ * @access public
274
+ * @param object|string $manager
275
+ * @param array $args
276
+ * @return void
277
+ */
278
+ public function register_manager( $manager, $args = array() ) {
279
+
280
+ if ( ! is_object( $manager ) ) {
281
+
282
+ $type = isset( $args['type'] ) ? $this->get_manager_type( $args['type'] ) : $this->get_manager_type( 'default' );
283
+
284
+ $manager = new $type( $manager, $args );
285
+ }
286
+
287
+ if ( ! $this->manager_exists( $manager->name ) )
288
+ $this->managers[ $manager->name ] = $manager;
289
+
290
+ return $manager;
291
+ }
292
+
293
+ /**
294
+ * Unregisters a manager object.
295
+ *
296
+ * @since 1.0.0
297
+ * @access public
298
+ * @param string $name
299
+ * @return void
300
+ */
301
+ public function unregister_manager( $name ) {
302
+
303
+ if ( $this->manager_exists( $name ) )
304
+ unset( $this->managers[ $name ] );
305
+ }
306
+
307
+ /**
308
+ * Returns a manager object.
309
+ *
310
+ * @since 1.0.0
311
+ * @access public
312
+ * @param string $name
313
+ * @return object|bool
314
+ */
315
+ public function get_manager( $name ) {
316
+
317
+ return $this->manager_exists( $name ) ? $this->managers[ $name ] : false;
318
+ }
319
+
320
+ /**
321
+ * Checks if a manager exists.
322
+ *
323
+ * @since 1.0.0
324
+ * @access public
325
+ * @param string $name
326
+ * @return bool
327
+ */
328
+ public function manager_exists( $name ) {
329
+
330
+ return isset( $this->managers[ $name ] );
331
+ }
332
+
333
+ /**
334
+ * Registers a manager type. This is just a method of telling ButterBean
335
+ * the class of your custom manager type. It allows the manager to be
336
+ * called without having to pass an object to `register_manager()`.
337
+ *
338
+ * @since 1.0.0
339
+ * @access public
340
+ * @param string $type
341
+ * @param string $class
342
+ * @return void
343
+ */
344
+ public function register_manager_type( $type, $class ) {
345
+
346
+ if ( ! $this->manager_type_exists( $type ) )
347
+ $this->manager_types[ $type ] = $class;
348
+ }
349
+
350
+ /**
351
+ * Unregisters a manager type.
352
+ *
353
+ * @since 1.0.0
354
+ * @access public
355
+ * @param string $type
356
+ * @return void
357
+ */
358
+ public function unregister_manager_type( $type ) {
359
+
360
+ if ( $this->manager_type_exists( $type ) )
361
+ unset( $this->manager_types[ $type ] );
362
+ }
363
+
364
+ /**
365
+ * Returns the class name for the manager type.
366
+ *
367
+ * @since 1.0.0
368
+ * @access public
369
+ * @param string $type
370
+ * @return string
371
+ */
372
+ public function get_manager_type( $type ) {
373
+
374
+ return $this->manager_type_exists( $type ) ? $this->manager_types[ $type ] : $this->manager_types[ 'default' ];
375
+ }
376
+
377
+ /**
378
+ * Checks if a manager type exists.
379
+ *
380
+ * @since 1.0.0
381
+ * @access public
382
+ * @param string $type
383
+ * @return bool
384
+ */
385
+ public function manager_type_exists( $type ) {
386
+
387
+ return isset( $this->manager_types[ $type ] );
388
+ }
389
+
390
+ /**
391
+ * Registers a section type. This is just a method of telling ButterBean
392
+ * the class of your custom section type. It allows the section to be
393
+ * called without having to pass an object to `register_section()`.
394
+ *
395
+ * @since 1.0.0
396
+ * @access public
397
+ * @param string $type
398
+ * @param string $class
399
+ * @return void
400
+ */
401
+ public function register_section_type( $type, $class ) {
402
+
403
+ if ( ! $this->section_type_exists( $type ) )
404
+ $this->section_types[ $type ] = $class;
405
+ }
406
+
407
+ /**
408
+ * Unregisters a section type.
409
+ *
410
+ * @since 1.0.0
411
+ * @access public
412
+ * @param string $type
413
+ * @return void
414
+ */
415
+ public function unregister_section_type( $type ) {
416
+
417
+ if ( $this->section_type_exists( $type ) )
418
+ unset( $this->section_types[ $type ] );
419
+ }
420
+
421
+ /**
422
+ * Returns the class name for the section type.
423
+ *
424
+ * @since 1.0.0
425
+ * @access public
426
+ * @param string $type
427
+ * @return string
428
+ */
429
+ public function get_section_type( $type ) {
430
+
431
+ return $this->section_type_exists( $type ) ? $this->section_types[ $type ] : $this->section_types[ 'default' ];
432
+ }
433
+
434
+ /**
435
+ * Checks if a section type exists.
436
+ *
437
+ * @since 1.0.0
438
+ * @access public
439
+ * @param string $type
440
+ * @return bool
441
+ */
442
+ public function section_type_exists( $type ) {
443
+
444
+ return isset( $this->section_types[ $type ] );
445
+ }
446
+
447
+ /**
448
+ * Registers a control type. This is just a method of telling ButterBean
449
+ * the class of your custom control type. It allows the control to be
450
+ * called without having to pass an object to `register_control()`.
451
+ *
452
+ * @since 1.0.0
453
+ * @access public
454
+ * @param string $type
455
+ * @param string $class
456
+ * @return void
457
+ */
458
+ public function register_control_type( $type, $class ) {
459
+
460
+ if ( ! $this->control_type_exists( $type ) )
461
+ $this->control_types[ $type ] = $class;
462
+ }
463
+
464
+ /**
465
+ * Unregisters a control type.
466
+ *
467
+ * @since 1.0.0
468
+ * @access public
469
+ * @param string $type
470
+ * @return void
471
+ */
472
+ public function unregister_control_type( $type ) {
473
+
474
+ if ( $this->control_type_exists( $type ) )
475
+ unset( $this->control_types[ $type ] );
476
+ }
477
+
478
+ /**
479
+ * Returns the class name for the control type.
480
+ *
481
+ * @since 1.0.0
482
+ * @access public
483
+ * @param string $type
484
+ * @return string
485
+ */
486
+ public function get_control_type( $type ) {
487
+
488
+ return $this->control_type_exists( $type ) ? $this->control_types[ $type ] : $this->control_types[ 'default' ];
489
+ }
490
+
491
+ /**
492
+ * Checks if a control type exists.
493
+ *
494
+ * @since 1.0.0
495
+ * @access public
496
+ * @param string $type
497
+ * @return bool
498
+ */
499
+ public function control_type_exists( $type ) {
500
+
501
+ return isset( $this->control_types[ $type ] );
502
+ }
503
+
504
+ /**
505
+ * Registers a setting type. This is just a method of telling ButterBean
506
+ * the class of your custom setting type. It allows the setting to be
507
+ * called without having to pass an object to `register_setting()`.
508
+ *
509
+ * @since 1.0.0
510
+ * @access public
511
+ * @param string $type
512
+ * @param string $class
513
+ * @return void
514
+ */
515
+ public function register_setting_type( $type, $class ) {
516
+
517
+ if ( ! $this->setting_type_exists( $type ) )
518
+ $this->setting_types[ $type ] = $class;
519
+ }
520
+
521
+ /**
522
+ * Unregisters a setting type.
523
+ *
524
+ * @since 1.0.0
525
+ * @access public
526
+ * @param string $type
527
+ * @return void
528
+ */
529
+ public function unregister_setting_type( $type ) {
530
+
531
+ if ( $this->setting_type_exists( $type ) )
532
+ unset( $this->setting_types[ $type ] );
533
+ }
534
+
535
+ /**
536
+ * Returns the class name for the setting type.
537
+ *
538
+ * @since 1.0.0
539
+ * @access public
540
+ * @param string $type
541
+ * @return string
542
+ */
543
+ public function get_setting_type( $type ) {
544
+
545
+ return $this->setting_type_exists( $type ) ? $this->setting_types[ $type ] : $this->setting_types[ 'default' ];
546
+ }
547
+
548
+ /**
549
+ * Checks if a setting type exists.
550
+ *
551
+ * @since 1.0.0
552
+ * @access public
553
+ * @param string $type
554
+ * @return bool
555
+ */
556
+ public function setting_type_exists( $type ) {
557
+
558
+ return isset( $this->setting_types[ $type ] );
559
+ }
560
+
561
+ /**
562
+ * Registers our manager types so that devs don't have to directly instantiate
563
+ * the class each time they register a manager. Instead, they can use the
564
+ * `type` argument.
565
+ *
566
+ * @since 1.0.0
567
+ * @access public
568
+ * @return void
569
+ */
570
+ public function register_manager_types() {
571
+
572
+ $this->register_manager_type( 'default', 'ButterBean_Manager' );
573
+ }
574
+
575
+ /**
576
+ * Registers our section types so that devs don't have to directly instantiate
577
+ * the class each time they register a section. Instead, they can use the
578
+ * `type` argument.
579
+ *
580
+ * @since 1.0.0
581
+ * @access public
582
+ * @return void
583
+ */
584
+ public function register_section_types() {
585
+
586
+ $this->register_section_type( 'default', 'ButterBean_Section' );
587
+ }
588
+
589
+ /**
590
+ * Registers our control types so that devs don't have to directly instantiate
591
+ * the class each time they register a control. Instead, they can use the
592
+ * `type` argument.
593
+ *
594
+ * @since 1.0.0
595
+ * @access public
596
+ * @return void
597
+ */
598
+ public function register_control_types() {
599
+
600
+ $this->register_control_type( 'default', 'ButterBean_Control' );
601
+ $this->register_control_type( 'checkboxes', 'ButterBean_Control_Checkboxes' );
602
+ $this->register_control_type( 'color', 'ButterBean_Control_Color' );
603
+ $this->register_control_type( 'date', 'ButterBean_Control_Date' );
604
+ $this->register_control_type( 'image', 'ButterBean_Control_Image' );
605
+ $this->register_control_type( 'palette', 'ButterBean_Control_Palette' );
606
+ $this->register_control_type( 'radio', 'ButterBean_Control_Radio' );
607
+ $this->register_control_type( 'radio-image', 'ButterBean_Control_Radio_Image' );
608
+ $this->register_control_type( 'select-group', 'ButterBean_Control_Select_Group' );
609
+ $this->register_control_type( 'textarea', 'ButterBean_Control_Textarea' );
610
+ $this->register_control_type( 'multi-avatars', 'ButterBean_Control_Multi_Avatars' );
611
+ $this->register_control_type( 'parent', 'ButterBean_Control_Parent' );
612
+ }
613
+
614
+ /**
615
+ * Registers our setting types so that devs don't have to directly instantiate
616
+ * the class each time they register a setting. Instead, they can use the
617
+ * `type` argument.
618
+ *
619
+ * @since 1.0.0
620
+ * @access public
621
+ * @return void
622
+ */
623
+ public function register_setting_types() {
624
+
625
+ $this->register_setting_type( 'default', 'ButterBean_Setting' );
626
+ $this->register_setting_type( 'array', 'ButterBean_Setting_Array' );
627
+ $this->register_setting_type( 'date', 'ButterBean_Setting_Date' );
628
+ }
629
+
630
+ /**
631
+ * Loads scripts and styles.
632
+ *
633
+ * @since 1.0.0
634
+ * @access public
635
+ * @return void
636
+ */
637
+ public function enqueue() {
638
+ $min = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
639
+
640
+ // Enqueue the main plugin script.
641
+ wp_enqueue_script( 'butterbean', $this->dir_uri . "js/butterbean{$min}.js", array( 'backbone', 'wp-util' ), '', true );
642
+
643
+ // Enqueue the main plugin style.
644
+ wp_enqueue_style( 'butterbean', $this->dir_uri . "css/butterbean{$min}.css" );
645
+
646
+ // Loop through the manager and its controls and call each control's `enqueue()` method.
647
+ foreach ( $this->managers as $manager ) {
648
+
649
+ $manager->enqueue();
650
+
651
+ foreach ( $manager->sections as $section )
652
+ $section->enqueue();
653
+
654
+ foreach ( $manager->controls as $control )
655
+ $control->enqueue();
656
+ }
657
+ }
658
+
659
+ /**
660
+ * Callback function for adding meta boxes. This function adds a meta box
661
+ * for each of the managers.
662
+ *
663
+ * @since 1.0.0
664
+ * @access public
665
+ * @param string $post_type
666
+ * @return void
667
+ */
668
+ public function add_meta_boxes( $post_type ) {
669
+
670
+ foreach ( $this->managers as $manager ) {
671
+
672
+ // If the manager is registered for the current post type, add a meta box.
673
+ if ( in_array( $post_type, (array) $manager->post_type ) && $manager->check_capabilities() ) {
674
+
675
+ add_meta_box(
676
+ "butterbean-ui-{$manager->name}",
677
+ $manager->label,
678
+ array( $this, 'meta_box' ),
679
+ $post_type,
680
+ $manager->context,
681
+ $manager->priority,
682
+ array( 'manager' => $manager )
683
+ );
684
+ }
685
+ }
686
+ }
687
+
688
+ /**
689
+ * Displays the meta box. Note that the actual content of the meta box is
690
+ * handled via Underscore.js templates. The only thing we're outputting here
691
+ * is the nonce field.
692
+ *
693
+ * @since 1.0.0
694
+ * @access public
695
+ * @param object $post
696
+ * @param array $metabox
697
+ * @return void
698
+ */
699
+ public function meta_box( $post, $metabox ) {
700
+
701
+ $manager = $metabox['args']['manager'];
702
+
703
+ $manager->post_id = $this->post_id = $post->ID;
704
+
705
+ // Nonce field to validate on save.
706
+ wp_nonce_field( "butterbean_{$manager->name}_nonce", "butterbean_{$manager->name}" );
707
+ }
708
+
709
+ /**
710
+ * Passes the appropriate section and control json data to the JS file.
711
+ *
712
+ * @since 1.0.0
713
+ * @access public
714
+ * @return void
715
+ */
716
+ public function localize_scripts() {
717
+
718
+ $json = array( 'managers' => array() );
719
+
720
+ foreach ( $this->managers as $manager ) {
721
+
722
+ if ( $manager->check_capabilities() )
723
+ $json['managers'][] = $manager->get_json();
724
+ }
725
+
726
+ wp_localize_script( 'butterbean', 'butterbean_data', $json );
727
+ }
728
+
729
+ /**
730
+ * Prints the Underscore.js templates.
731
+ *
732
+ * @since 1.0.0
733
+ * @access public
734
+ * @return void
735
+ */
736
+ public function print_templates() {
737
+
738
+ $m_templates = array();
739
+ $s_templates = array();
740
+ $c_templates = array(); ?>
741
+
742
+ <script type="text/html" id="tmpl-butterbean-nav">
743
+ <?php butterbean_get_nav_template(); ?>
744
+ </script>
745
+
746
+ <?php foreach ( $this->managers as $manager ) {
747
+
748
+ if ( ! $manager->check_capabilities() )
749
+ continue;
750
+
751
+ if ( ! in_array( $manager->type, $m_templates ) ) {
752
+ $m_templates[] = $manager->type;
753
+
754
+ $manager->print_template();
755
+ }
756
+
757
+ foreach ( $manager->sections as $section ) {
758
+
759
+ if ( ! in_array( $section->type, $s_templates ) ) {
760
+ $s_templates[] = $section->type;
761
+
762
+ $section->print_template();
763
+ }
764
+ }
765
+
766
+ foreach ( $manager->controls as $control ) {
767
+
768
+ if ( ! in_array( $control->type, $c_templates ) ) {
769
+ $c_templates[] = $control->type;
770
+
771
+ $control->print_template();
772
+ }
773
+ }
774
+ }
775
+ }
776
+
777
+ /**
778
+ * Saves the settings.
779
+ *
780
+ * @since 1.0.0
781
+ * @access public
782
+ * @return void
783
+ */
784
+ public function update( $post_id ) {
785
+
786
+ $do_autosave = defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE;
787
+ $is_autosave = wp_is_post_autosave( $post_id );
788
+ $is_revision = wp_is_post_revision( $post_id );
789
+
790
+ if ( $do_autosave || $is_autosave || $is_revision )
791
+ return;
792
+
793
+ foreach ( $this->managers as $manager ) {
794
+
795
+ if ( $manager->check_capabilities() )
796
+ $manager->save( $post_id );
797
+ }
798
+ }
799
+
800
+ /**
801
+ * Helper method for sorting sections and controls by priority.
802
+ *
803
+ * @since 1.0.0
804
+ * @access protected
805
+ * @param object $a
806
+ * @param object $b
807
+ * @return int
808
+ */
809
+ protected function priority_sort( $a, $b ) {
810
+
811
+ if ( $a->priority === $b->priority )
812
+ return $a->instance_number - $b->instance_number;
813
+
814
+ return $a->priority - $b->priority;
815
+ }
816
+ }
817
+
818
+ /**
819
+ * Gets the instance of the `ButterBean` class. This function is useful for quickly grabbing data
820
+ * used throughout the plugin.
821
+ *
822
+ * @since 1.0.0
823
+ * @access public
824
+ * @return object
825
+ */
826
+ function butterbean() {
827
+ return ButterBean::get_instance();
828
+ }
829
+
830
+ // Let's do this thang!
831
+ butterbean();
832
+ }
admin/butterbean/changelog.md ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ # Change Log
2
+
3
+ Work in progress...
admin/butterbean/contributing.md ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Contributing
2
+
3
+ The code for the project is handled via its [GitHub Repository](https://github.com/justintadlock/butterbean). You can open tickets, create patches, and send pull requests there.
4
+
5
+ ## Pull requests
6
+
7
+ Problem first. Solution second.
8
+
9
+ Pull requests should have a ticket open for discussion first. I rarely accept pull requests that aren't for a specific issue for various reasons. It's far better to post an issue and let me or the community provide feedback prior to creating a pull request.
10
+
11
+ Please don't make pull requests against the `master` branch. This is the latest, stable code. You can make a pull request against one of the point branches or the `dev` (future release) branch.
12
+
13
+ ## Coding standards
14
+
15
+ In general, the project follows all WordPress [coding standards](https://make.wordpress.org/core/handbook/best-practices/coding-standards). There are instances where it doesn't, opting for personal choices of my own, but in terms of contributing, following the WordPress standards is best practice.
16
+
17
+ ## Script and style files
18
+
19
+ The project consists of several script and style files. When making patches or pull requests with changes to these files, only do so to the primary file. Don't create patches for the minified (`.min`) versions of the files. Those will be minified after a patch is merged into the code base.
20
+
21
+ ## Language
22
+
23
+ All text strings follow U.S. English by default. While such guides are generally unneeded, in cases where style considerations are necessary, these will typically follow conventions laid out in *Elements of Style* or the *AP Stylebook*.
24
+
25
+ ## Licensing
26
+
27
+ Any code contributed to the project via patches, pull requests, or other means will be licensed under the [GPL version 2](http://www.gnu.org/licenses/old-licenses/gpl-2.0.html) or later. By contributing code to the project, you provide consent to use such code under this license. The exception to this rule is when bringing in third-party code with an alternate open source license.
28
+
29
+ ## Versioning
30
+
31
+ The project uses [semantic versioning](http://semver.org). Version numbers will look like `3.2.1` where `3` is the "major" release, `2` is the minor release, and `1` is the patch release.
admin/butterbean/css/butterbean.css ADDED
@@ -0,0 +1,330 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ /* Wrapper box */
3
+
4
+ .butterbean-ui { }
5
+
6
+ .butterbean-ui > .hndle {
7
+ padding: 10px !important;
8
+ border-bottom: 1px solid #eee;
9
+ }
10
+
11
+ .butterbean-ui .inside {
12
+ margin: 0 !important;
13
+ padding: 0;
14
+ }
15
+
16
+ /* Tabs wrapper. */
17
+
18
+ .butterbean-manager-default {
19
+ overflow: hidden;
20
+ background: #fff;
21
+ background: linear-gradient( 90deg, #fafafa 0%, #fafafa 180px, #fff 180px, #fff 100% );
22
+ }
23
+
24
+ #side-sortables .butterbean-manager-default {
25
+ background: linear-gradient( 90deg, #fafafa 0%, #fafafa 48px, #fff 48px, #fff 100% );
26
+ }
27
+
28
+ @media only screen and ( max-width: 782px ), ( max-width: 980px ) and ( min-width: 851px ) {
29
+
30
+ .butterbean-manager-default {
31
+ background: linear-gradient( 90deg, #fafafa 0%, #fafafa 48px, #fff 48px, #fff 100% );
32
+ }
33
+ }
34
+
35
+ /* Tab nav. */
36
+
37
+ .butterbean-manager-default .butterbean-nav {
38
+ position: relative;
39
+ float: left;
40
+ list-style: none;
41
+ width: 180px;/*20%;*/
42
+ line-height: 1em;
43
+ margin: 0 0 -1px 0;
44
+ padding: 0;
45
+ background-color: #fafafa;
46
+ border-right: 1px solid #eee;
47
+ box-sizing: border-box;
48
+ }
49
+
50
+ .butterbean-manager-default .butterbean-nav li {
51
+ display: block;
52
+ position: relative;
53
+ margin: 0;
54
+ padding: 0;
55
+ line-height: 20px;
56
+ }
57
+
58
+ .butterbean-manager-default .butterbean-nav li a {
59
+ display: block;
60
+ margin: 0;
61
+ padding: 10px;
62
+ line-height: 20px !important;
63
+ text-decoration: none;
64
+ border-bottom: 1px solid #eee;
65
+ box-shadow: none;
66
+ }
67
+
68
+ .butterbean-manager-default .butterbean-nav .dashicons {
69
+ line-height: 20px;
70
+ margin-right: 3px;
71
+ }
72
+
73
+ .butterbean-manager-default .butterbean-nav li[aria-selected="true"] a {
74
+ position: relative;
75
+ font-weight: bold;
76
+ color: #555;
77
+ background-color: #e0e0e0;
78
+ }
79
+
80
+ @media only screen and ( max-width: 782px ), ( max-width: 980px ) and ( min-width: 851px ) {
81
+ .butterbean-manager-default .butterbean-nav { width: 48px; }
82
+
83
+ .butterbean-manager-default .butterbean-nav .dashicons {
84
+ width: 24px;
85
+ height: 24px;
86
+ font-size: 24px;
87
+ line-height: 24px;
88
+ }
89
+
90
+ .butterbean-manager-default .butterbean-nav .dashicons::before {
91
+ width: 24px;
92
+ height: 24px;
93
+ }
94
+
95
+ .butterbean-manager-default .butterbean-nav .label {
96
+ overflow: hidden;
97
+ position: absolute;
98
+ top: -1000em;
99
+ left: -1000em;
100
+ width: 1px;
101
+ height: 1px;
102
+ }
103
+ }
104
+
105
+ /* Tab content wrapper */
106
+
107
+ .butterbean-manager-default .butterbean-content {
108
+ float: left;
109
+ width: calc( 100% - 180px );
110
+ margin-left: -1px;
111
+ border-left: 1px solid #eee;
112
+ }
113
+
114
+ @media only screen and ( max-width: 782px ), ( max-width: 980px ) and ( min-width: 851px ) {
115
+
116
+ .butterbean-manager-default .butterbean-content {
117
+ width: calc( 100% - 48px );
118
+ }
119
+ }
120
+
121
+ /* === Manager when in the side meta box. === */
122
+
123
+ @media only screen and ( min-width: 850px ) {
124
+
125
+ #side-sortables .butterbean-manager-default { background: #fff; }
126
+
127
+ #side-sortables .butterbean-manager-default .butterbean-content { width: 100%; }
128
+
129
+ #side-sortables .butterbean-manager-default .butterbean-nav {
130
+ display: table;
131
+ width: 100%;
132
+ }
133
+
134
+ #side-sortables .butterbean-manager-default .butterbean-nav li {
135
+ display: table-cell;
136
+ text-align: center;
137
+ border-right: 1px solid #eee;
138
+ }
139
+
140
+ #side-sortables .butterbean-manager-default .butterbean-nav li:last-of-type { border-right: none; }
141
+
142
+ #side-sortables .butterbean-manager-default .butterbean-nav li a {
143
+ padding: 10px 0;
144
+ }
145
+
146
+ #side-sortables .butterbean-manager-default .butterbean-nav .dashicons {
147
+ width: 24px;
148
+ height: 24px;
149
+ font-size: 24px;
150
+ line-height: 24px;
151
+ }
152
+
153
+ #side-sortables .butterbean-manager-default .butterbean-nav .dashicons::before {
154
+ width: 24px;
155
+ height: 24px;
156
+ }
157
+
158
+ #side-sortables .butterbean-manager-default .butterbean-nav .label {
159
+ overflow: hidden;
160
+ position: absolute;
161
+ top: -1000em;
162
+ left: -1000em;
163
+ width: 1px;
164
+ height: 1px;
165
+ }
166
+ }
167
+
168
+
169
+ /* === Content === */
170
+
171
+ .butterbean-manager-default .butterbean-section {
172
+ padding: 12px 12px 0;
173
+ box-sizing: border-box;
174
+ }
175
+
176
+ .butterbean-manager-default .butterbean-section[aria-hidden="true"] { display: none; }
177
+ .butterbean-manager-default .butterbean-section[aria-hidden="false"] { display: block; }
178
+
179
+ .butterbean-manager-default .butterbean-control {
180
+ margin-bottom: 20px;
181
+ }
182
+
183
+ .butterbean-manager-default .butterbean-label {
184
+ display : block !important; /* this is getting overwritten somewhere */
185
+ font-weight : bold;
186
+ display : inline-block;
187
+ margin-bottom : 4px;
188
+ }
189
+
190
+ .butterbean-manager-default .butterbean-control-checkbox .butterbean-label {
191
+ display: inline !important;
192
+ }
193
+
194
+ .butterbean-manager-default .butterbean-description {
195
+ display : block;
196
+ font-style : italic;
197
+ margin-top : 4px;
198
+ }
199
+
200
+ .butterbean-manager-default .butterbean-label + .butterbean-description {
201
+ margin-top : 0;
202
+ margin-bottom : 4px;
203
+ }
204
+
205
+ /* === Media === */
206
+
207
+ .butterbean-control-image .butterbean-img {
208
+ display: block;
209
+ max-width: 100%;
210
+ max-height: 300px;
211
+ height: auto;
212
+ }
213
+
214
+ .butterbean-placeholder {
215
+ width: 100%;
216
+ position: relative;
217
+ text-align: center;
218
+ padding: 9px 0;
219
+ line-height: 20px;
220
+ border: 1px dashed rgb(180, 185, 190);
221
+ box-sizing: border-box;
222
+ }
223
+
224
+ /* === Textarea Control === */
225
+
226
+ .butterbean-control-textarea textarea,
227
+ .butterbean-control-excerpt textarea {
228
+ display: block;
229
+ width: 100%;
230
+ height: 105px;
231
+ }
232
+
233
+ /* === Date Control === */
234
+
235
+ .butterbean-control-date select {
236
+ vertical-align: top;
237
+ }
238
+
239
+ /* === Palette Control === */
240
+
241
+ .butterbean-control-palette label {
242
+ display: block;
243
+ padding: 0 10px 10px;
244
+ }
245
+
246
+ .butterbean-control-palette label[aria-selected="true"] {
247
+ padding-top: 5px;
248
+ background-color: #ddd;
249
+ }
250
+
251
+ .butterbean-palette-label {
252
+ line-height: 28px;
253
+ }
254
+
255
+ .butterbean-palette-block {
256
+ display: table;
257
+ width: 100%;
258
+ height: 45px;
259
+ border: 1px solid rgb(238, 238, 238);
260
+ box-sizing: border-box;
261
+ }
262
+
263
+ .butterbean-palette-color {
264
+ display: table-cell;
265
+ height: 100%;
266
+ }
267
+
268
+ /* === Radio Image Control === */
269
+
270
+ .butterbean-control-radio-image input[type="radio"] {
271
+ clip: rect(1px, 1px, 1px, 1px);
272
+ height: 1px;
273
+ overflow: hidden;
274
+ position: absolute !important;
275
+ width: 1px;
276
+ }
277
+
278
+ .butterbean-control-radio-image img {
279
+ box-sizing: border-box;
280
+ max-width: 100%;
281
+ height: auto;
282
+ padding: 1px;
283
+ border: 4px solid transparent;
284
+ }
285
+
286
+ .butterbean-control-radio-image img:hover,
287
+ .butterbean-control-radio-image img:focus {
288
+ border-color: #ccc;
289
+ }
290
+
291
+ .butterbean-control-radio-image input:checked + span + img {
292
+ border-color: #00a0d2;
293
+ }
294
+
295
+ /* === Multi-avatars Control === */
296
+
297
+ .butterbean-multi-avatars-wrap label {
298
+ display: inline-block;
299
+ margin-top: 8px;
300
+ }
301
+
302
+ .butterbean-multi-avatars-wrap input[type="checkbox"] {
303
+ clip: rect( 1px, 1px, 1px, 1px );
304
+ height: 1px;
305
+ overflow: hidden;
306
+ position: absolute !important;
307
+ width: 1px;
308
+ }
309
+
310
+ .butterbean-multi-avatars-wrap .avatar {
311
+ box-sizing: border-box;
312
+ max-width: 100%;
313
+ height: auto;
314
+ padding: 1px;
315
+ border: 4px solid transparent;
316
+ }
317
+
318
+ #side-sortables .butterbean-multi-avatars-wrap .avatar {
319
+ max-width: 60px;
320
+ max-height: 60px;
321
+ }
322
+
323
+ .butterbean-multi-avatars-wrap img:hover,
324
+ .butterbean-multi-avatars-wrap img:focus {
325
+ border-color: #ccc;
326
+ }
327
+
328
+ .butterbean-multi-avatars-wrap input:checked + span + img {
329
+ border-color: #00a0d2;
330
+ }
admin/butterbean/css/butterbean.min.css ADDED
@@ -0,0 +1 @@
 
1
+ .butterbean-ui>.hndle{padding:10px!important;border-bottom:1px solid #eee}.butterbean-ui .inside{margin:0!important;padding:0}.butterbean-manager-default{overflow:hidden;background:#fff;background:linear-gradient(90deg,#fafafa 0,#fafafa 180px,#fff 180px,#fff 100%)}#side-sortables .butterbean-manager-default{background:linear-gradient(90deg,#fafafa 0,#fafafa 48px,#fff 48px,#fff 100%)}@media only screen and (max-width:782px),(max-width:980px) and (min-width:851px){.butterbean-manager-default{background:linear-gradient(90deg,#fafafa 0,#fafafa 48px,#fff 48px,#fff 100%)}}.butterbean-manager-default .butterbean-nav{position:relative;float:left;list-style:none;width:180px;line-height:1em;margin:0 0 -1px 0;padding:0;background-color:#fafafa;border-right:1px solid #eee;box-sizing:border-box}.butterbean-manager-default .butterbean-nav li{display:block;position:relative;margin:0;padding:0;line-height:20px}.butterbean-manager-default .butterbean-nav li a{display:block;margin:0;padding:10px;line-height:20px!important;text-decoration:none;border-bottom:1px solid #eee;box-shadow:none}.butterbean-manager-default .butterbean-nav .dashicons{line-height:20px;margin-right:3px}.butterbean-manager-default .butterbean-nav li[aria-selected=true] a{position:relative;font-weight:700;color:#555;background-color:#e0e0e0}@media only screen and (max-width:782px),(max-width:980px) and (min-width:851px){.butterbean-manager-default .butterbean-nav{width:48px}.butterbean-manager-default .butterbean-nav .dashicons{width:24px;height:24px;font-size:24px;line-height:24px}.butterbean-manager-default .butterbean-nav .dashicons::before{width:24px;height:24px}.butterbean-manager-default .butterbean-nav .label{overflow:hidden;position:absolute;top:-1000em;left:-1000em;width:1px;height:1px}}.butterbean-manager-default .butterbean-content{float:left;width:calc(100% - 180px);margin-left:-1px;border-left:1px solid #eee}@media only screen and (max-width:782px),(max-width:980px) and (min-width:851px){.butterbean-manager-default .butterbean-content{width:calc(100% - 48px)}}@media only screen and (min-width:850px){#side-sortables .butterbean-manager-default{background:#fff}#side-sortables .butterbean-manager-default .butterbean-content{width:100%}#side-sortables .butterbean-manager-default .butterbean-nav{display:table;width:100%}#side-sortables .butterbean-manager-default .butterbean-nav li{display:table-cell;text-align:center;border-right:1px solid #eee}#side-sortables .butterbean-manager-default .butterbean-nav li:last-of-type{border-right:0}#side-sortables .butterbean-manager-default .butterbean-nav li a{padding:10px 0}#side-sortables .butterbean-manager-default .butterbean-nav .dashicons{width:24px;height:24px;font-size:24px;line-height:24px}#side-sortables .butterbean-manager-default .butterbean-nav .dashicons::before{width:24px;height:24px}#side-sortables .butterbean-manager-default .butterbean-nav .label{overflow:hidden;position:absolute;top:-1000em;left:-1000em;width:1px;height:1px}}.butterbean-manager-default .butterbean-section{padding:12px 12px 0;box-sizing:border-box}.butterbean-manager-default .butterbean-section[aria-hidden=true]{display:none}.butterbean-manager-default .butterbean-section[aria-hidden=false]{display:block}.butterbean-manager-default .butterbean-control{margin-bottom:20px}.butterbean-manager-default .butterbean-label{display:block!important;font-weight:700;display:inline-block;margin-bottom:4px}.butterbean-manager-default .butterbean-control-checkbox .butterbean-label{display:inline!important}.butterbean-manager-default .butterbean-description{display:block;font-style:italic;margin-top:4px}.butterbean-manager-default .butterbean-label+.butterbean-description{margin-top:0;margin-bottom:4px}.butterbean-control-image .butterbean-img{display:block;max-width:100%;max-height:300px;height:auto}.butterbean-placeholder{width:100%;position:relative;text-align:center;padding:9px 0;line-height:20px;border:1px dashed #b4b9be;box-sizing:border-box}.butterbean-control-textarea textarea,.butterbean-control-excerpt textarea{display:block;width:100%;height:105px}.butterbean-control-date select{vertical-align:top}.butterbean-control-palette label{display:block;padding:0 10px 10px}.butterbean-control-palette label[aria-selected=true]{padding-top:5px;background-color:#ddd}.butterbean-palette-label{line-height:28px}.butterbean-palette-block{display:table;width:100%;height:45px;border:1px solid #eee;box-sizing:border-box}.butterbean-palette-color{display:table-cell;height:100%}.butterbean-control-radio-image input[type=radio]{clip:rect(1px,1px,1px,1px);height:1px;overflow:hidden;position:absolute!important;width:1px}.butterbean-control-radio-image img{box-sizing:border-box;max-width:100%;height:auto;padding:1px;border:4px solid transparent}.butterbean-control-radio-image img:hover,.butterbean-control-radio-image img:focus{border-color:#ccc}.butterbean-control-radio-image input:checked+span+img{border-color:#00a0d2}.butterbean-multi-avatars-wrap label{display:inline-block;margin-top:8px}.butterbean-multi-avatars-wrap input[type=checkbox]{clip:rect(1px,1px,1px,1px);height:1px;overflow:hidden;position:absolute!important;width:1px}.butterbean-multi-avatars-wrap .avatar{box-sizing:border-box;max-width:100%;height:auto;padding:1px;border:4px solid transparent}#side-sortables .butterbean-multi-avatars-wrap .avatar{max-width:60px;max-height:60px}.butterbean-multi-avatars-wrap img:hover,.butterbean-multi-avatars-wrap img:focus{border-color:#ccc}.butterbean-multi-avatars-wrap input:checked+span+img{border-color:#00a0d2}
admin/butterbean/inc/class-control.php ADDED
@@ -0,0 +1,399 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Base class for handling controls. Controls are the form fields for the manager. Each
4
+ * control should be tied to a section.
5
+ *
6
+ * @package ButterBean
7
+ * @author Justin Tadlock <justin@justintadlock.com>
8
+ * @copyright Copyright (c) 2015-2016, Justin Tadlock
9
+ * @link https://github.com/justintadlock/butterbean
10
+ * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
11
+ */
12
+
13
+ /**
14
+ * Base control class.
15
+ *
16
+ * @since 1.0.0
17
+ * @access public
18
+ */
19
+ class ButterBean_Control {
20
+
21
+ /**
22
+ * Stores the manager object.
23
+ *
24
+ * @since 1.0.0
25
+ * @access public
26
+ * @var object
27
+ */
28
+ public $manager;
29
+
30
+ /**
31
+ * Name/ID of the control.
32
+ *
33
+ * @since 1.0.0
34
+ * @access public
35
+ * @var string
36
+ */
37
+ public $name = '';
38
+
39
+ /**
40
+ * Label for the control.
41
+ *
42
+ * @since 1.0.0
43
+ * @access public
44
+ * @var string
45
+ */
46
+ public $label = '';
47
+
48
+ /**
49
+ * Description for the control.
50
+ *
51
+ * @since 1.0.0
52
+ * @access public
53
+ * @var string
54
+ */
55
+ public $description = '';
56
+
57
+ /**
58
+ * ID of the section the control is for.
59
+ *
60
+ * @since 1.0.0
61
+ * @access public
62
+ * @var string
63
+ */
64
+ public $section = '';
65
+
66
+ /**
67
+ * The setting key for the specific setting the control is tied to.
68
+ * Controls can have multiple settings attached to them. The default
69
+ * setting is `default`.
70
+ *
71
+ * @since 1.0.0
72
+ * @access public
73
+ * @var string
74
+ */
75
+ public $setting = 'default';
76
+
77
+ /**
78
+ * Array of settings if the control has multiple settings.
79
+ *
80
+ * @since 1.0.0
81
+ * @access public
82
+ * @var array
83
+ */
84
+ public $settings = array();
85
+
86
+ /**
87
+ * The type of control.
88
+ *
89
+ * @since 1.0.0
90
+ * @access public
91
+ * @var string
92
+ */
93
+ public $type = 'text';
94
+
95
+ /**
96
+ * Form field attributes.
97
+ *
98
+ * @since 1.0.0
99
+ * @access public
100
+ * @var array
101
+ */
102
+ public $attr = '';
103
+
104
+ /**
105
+ * Choices for fields with multiple choices.
106
+ *
107
+ * @since 1.0.0
108
+ * @access public
109
+ * @var array
110
+ */
111
+ public $choices = array();
112
+
113
+ /**
114
+ * Priority (order) the control should be output.
115
+ *
116
+ * @since 1.0.0
117
+ * @access public
118
+ * @var int
119
+ */
120
+ public $priority = 10;
121
+
122
+ /**
123
+ * The number of instances created.
124
+ *
125
+ * @since 1.0.0
126
+ * @access protected
127
+ * @var int
128
+ */
129
+ protected static $instance_count = 0;
130
+
131
+ /**
132
+ * The instance of the current control.
133
+ *
134
+ * @since 1.0.0
135
+ * @access public
136
+ * @var int
137
+ */
138
+ public $instance_number;
139
+
140
+ /**
141
+ * A callback function for deciding if a control is active.
142
+ *
143
+ * @since 1.0.0
144
+ * @access public
145
+ * @var callable
146
+ */
147
+ public $active_callback = '';
148
+
149
+ /**
150
+ * A user role capability required to show the control.
151
+ *
152
+ * @since 1.0.0
153
+ * @access public
154
+ * @var string|array
155
+ */
156
+ public $capability = '';
157
+
158
+ /**
159
+ * A feature that the current post type must support to show the control.
160
+ *
161
+ * @since 1.0.0
162
+ * @access public
163
+ * @var string
164
+ */
165
+ public $post_type_supports = '';
166
+
167
+ /**
168
+ * A feature that the current theme must support to show the control.
169
+ *
170
+ * @since 1.0.0
171
+ * @access public
172
+ * @var string|array
173
+ */
174
+ public $theme_supports = '';
175
+
176
+ /**
177
+ * Stores the JSON data for the control.
178
+ *
179
+ * @since 1.0.0
180
+ * @access public
181
+ * @var array()
182
+ */
183
+ public $json = array();
184
+
185
+ /**
186
+ * Creates a new control object.
187
+ *
188
+ * @since 1.0.0
189
+ * @access public
190
+ * @param object $manager
191
+ * @param string $name
192
+ * @param array $args
193
+ * @return void
194
+ */
195
+ public function __construct( $manager, $name, $args = array() ) {
196
+
197
+ foreach ( array_keys( get_object_vars( $this ) ) as $key ) {
198
+
199
+ if ( isset( $args[ $key ] ) )
200
+ $this->$key = $args[ $key ];
201
+ }
202
+
203
+ $this->manager = $manager;
204
+ $this->name = $name;
205
+
206
+ if ( empty( $args['settings'] ) || ! is_array( $args['settings'] ) )
207
+ $this->settings['default'] = $name;
208
+
209
+ // Increment the instance count and set the instance number.
210
+ self::$instance_count += 1;
211
+ $this->instance_number = self::$instance_count;
212
+
213
+ // Set the active callback function if not set.
214
+ if ( ! $this->active_callback )
215
+ $this->active_callback = array( $this, 'active_callback' );
216
+ }
217
+
218
+ /**
219
+ * Enqueue scripts/styles for the control.
220
+ *
221
+ * @since 1.0.0
222
+ * @access public
223
+ * @return void
224
+ */
225
+ public function enqueue() {}
226
+
227
+ /**
228
+ * Get the value for the setting.
229
+ *
230
+ * @since 1.0.0
231
+ * @access public
232
+ * @param string $setting
233
+ * @return mixed
234
+ */
235
+ public function get_value( $setting = 'default' ) {
236
+
237
+ $setting = $this->get_setting( $setting );
238
+
239
+ return $setting ? $setting->get_value() : '';
240
+ }
241
+
242
+ /**
243
+ * Returns the setting object associated with this control. If no setting is
244
+ * found, `false` is returned.
245
+ *
246
+ * @since 1.0.0
247
+ * @access public
248
+ * @param string $setting
249
+ * @return object|bool
250
+ */
251
+ public function get_setting( $setting = 'default' ) {
252
+
253
+ return $this->manager->get_setting( $this->settings[ $setting ] );
254
+ }
255
+
256
+ /**
257
+ * Gets the attributes for the control.
258
+ *
259
+ * @since 1.0.0
260
+ * @access public
261
+ * @return array
262
+ */
263
+ public function get_attr() {
264
+
265
+ $defaults = array();
266
+
267
+ if ( isset( $this->settings[ $this->setting ] ) )
268
+ $defaults['name'] = $this->get_field_name( $this->setting );
269
+
270
+ return wp_parse_args( $this->attr, $defaults );
271
+ }
272
+
273
+ /**
274
+ * Returns the HTML field name for the control.
275
+ *
276
+ * @since 1.0.0
277
+ * @access public
278
+ * @param string $setting
279
+ * @return array
280
+ */
281
+ public function get_field_name( $setting = 'default' ) {
282
+
283
+ return "butterbean_{$this->manager->name}_setting_{$this->settings[ $setting ]}";
284
+ }
285
+
286
+ /**
287
+ * Returns the json array.
288
+ *
289
+ * @since 1.0.0
290
+ * @access public
291
+ * @return array
292
+ */
293
+ public function get_json() {
294
+ $this->to_json();
295
+
296
+ return $this->json;
297
+ }
298
+
299
+ /**
300
+ * Adds custom data to the json array. This data is passed to the Underscore template.
301
+ *
302
+ * @since 1.0.0
303
+ * @access public
304
+ * @return void
305
+ */
306
+ public function to_json() {
307
+
308
+ $this->json['manager'] = $this->manager->name;
309
+ $this->json['section'] = $this->section;
310
+ $this->json['setting'] = $this->setting;
311
+ $this->json['settings'] = $this->settings;
312
+ $this->json['name'] = $this->name;
313
+ $this->json['label'] = $this->label;
314
+ $this->json['type'] = $this->type;
315
+ $this->json['description'] = $this->description;
316
+ $this->json['choices'] = $this->choices;
317
+ $this->json['active'] = $this->is_active();
318
+
319
+ $this->json['value'] = isset( $this->settings[ $this->setting ] ) ? $this->get_value( $this->setting ) : '';
320
+ $this->json['field_name'] = isset( $this->settings[ $this->setting ] ) ? $this->get_field_name( $this->setting ) : '';
321
+
322
+ $this->json['attr'] = '';
323
+
324
+ foreach ( $this->get_attr() as $attr => $value ) {
325
+ $this->json['attr'] .= sprintf( '%s="%s" ', esc_html( $attr ), esc_attr( $value ) );
326
+ }
327
+ }
328
+
329
+ /**
330
+ * Returns whether the control is active.
331
+ *
332
+ * @since 1.0.0
333
+ * @access public
334
+ * @return bool
335
+ */
336
+ public function is_active() {
337
+
338
+ $is_active = call_user_func( $this->active_callback, $this );
339
+
340
+ return apply_filters( 'butterbean_is_control_active', $is_active, $this );
341
+ }
342
+
343
+ /**
344
+ * Default active callback.
345
+ *
346
+ * @since 1.0.0
347
+ * @access public
348
+ * @return bool
349
+ */
350
+ public function active_callback() {
351
+ return true;
352
+ }
353
+
354
+ /**
355
+ * Checks if the control should be allowed at all.
356
+ *
357
+ * @since 1.0.0
358
+ * @access public
359
+ * @return bool
360
+ */
361
+ public function check_capabilities() {
362
+
363
+ if ( $this->capability && ! call_user_func_array( 'current_user_can', (array) $this->capability ) )
364
+ return false;
365
+
366
+ if ( $this->post_type_supports && ! call_user_func_array( 'post_type_supports', array( get_post_type( $this->manager->post_id ), $this->post_type_supports ) ) )
367
+ return false;
368
+
369
+ if ( $this->theme_supports && ! call_user_func_array( 'theme_supports', (array) $this->theme_supports ) )
370
+ return false;
371
+
372
+ return true;
373
+ }
374
+
375
+ /**
376
+ * Prints Underscore.js template.
377
+ *
378
+ * @since 1.0.0
379
+ * @access public
380
+ * @return void
381
+ */
382
+ public function print_template() { ?>
383
+
384
+ <script type="text/html" id="tmpl-butterbean-control-<?php echo esc_attr( $this->type ); ?>">
385
+ <?php $this->get_template(); ?>
386
+ </script>
387
+ <?php }
388
+
389
+ /**
390
+ * Gets the Underscore.js template.
391
+ *
392
+ * @since 1.0.0
393
+ * @access public
394
+ * @return void
395
+ */
396
+ public function get_template() {
397
+ butterbean_get_control_template( $this->type );
398
+ }
399
+ }
admin/butterbean/inc/class-manager.php ADDED
@@ -0,0 +1,550 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Base class for handling managers. Managers are groups of sections, which are groups of
4
+ * controls + settings. Managers are output as a metabox. This essentially allows
5
+ * developers to output multiple post meta fields within a single metabox.
6
+ *
7
+ * @package ButterBean
8
+ * @author Justin Tadlock <justin@justintadlock.com>
9
+ * @copyright Copyright (c) 2015-2016, Justin Tadlock
10
+ * @link https://github.com/justintadlock/butterbean
11
+ * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
12
+ */
13
+
14
+ /**
15
+ * Base manager class.
16
+ *
17
+ * @since 1.0.0
18
+ * @access public
19
+ */
20
+ class ButterBean_Manager {
21
+
22
+ /**
23
+ * The type of manager.
24
+ *
25
+ * @since 1.0.0
26
+ * @access public
27
+ * @var string
28
+ */
29
+ public $type = 'default';
30
+
31
+ /**
32
+ * Name of this instance of the manager.
33
+ *
34
+ * @since 1.0.0
35
+ * @access public
36
+ * @var string
37
+ */
38
+ public $name = '';
39
+
40
+ /**
41
+ * Label for the control.
42
+ *
43
+ * @since 1.0.0
44
+ * @access public
45
+ * @var string
46
+ */
47
+ public $label = '';
48
+
49
+ /**
50
+ * Post type this manager is used on.
51
+ *
52
+ * @since 1.0.0
53
+ * @access public
54
+ * @var string|array
55
+ */
56
+ public $post_type = 'post';
57
+
58
+ /**
59
+ * Location of the meta box.
60
+ *
61
+ * @link https://developer.wordpress.org/reference/functions/add_meta_box/
62
+ * @since 1.0.0
63
+ * @access public
64
+ * @var string
65
+ */
66
+ public $context = 'advanced';
67
+
68
+ /**
69
+ * Priority of the meta box.
70
+ *
71
+ * @link https://developer.wordpress.org/reference/functions/add_meta_box/
72
+ * @since 1.0.0
73
+ * @access public
74
+ * @var string
75
+ */
76
+ public $priority = 'default';
77
+
78
+ /**
79
+ * Array of sections.
80
+ *
81
+ * @since 1.0.0
82
+ * @access public
83
+ * @var array
84
+ */
85
+ public $sections = array();
86
+
87
+ /**
88
+ * Array of controls.
89
+ *
90
+ * @since 1.0.0
91
+ * @access public
92
+ * @var array
93
+ */
94
+ public $controls = array();
95
+
96
+ /**
97
+ * Array of settings.
98
+ *
99
+ * @since 1.0.0
100
+ * @access public
101
+ * @var array
102
+ */
103
+ public $settings = array();
104
+
105
+ /**
106
+ * A user role capability required to show the manager.
107
+ *
108
+ * @since 1.0.0
109
+ * @access public
110
+ * @var string|array
111
+ */
112
+ public $capability = '';
113
+
114
+ /**
115
+ * A feature that the current post type must support to show the manager.
116
+ *
117
+ * @since 1.0.0
118
+ * @access public
119
+ * @var string
120
+ */
121
+ public $post_type_supports = '';
122
+
123
+ /**
124
+ * A feature that the current theme must support to show the manager.
125
+ *
126
+ * @since 1.0.0
127
+ * @access public
128
+ * @var string|array
129
+ */
130
+ public $theme_supports = '';
131
+
132
+ /**
133
+ * Stores the JSON data for the manager.
134
+ *
135
+ * @since 1.0.0
136
+ * @access public
137
+ * @var array()
138
+ */
139
+ public $json = array();
140
+
141
+ /**
142
+ * ID of the post that's being edited.
143
+ *
144
+ * @since 1.0.0
145
+ * @access public
146
+ * @var int
147
+ */
148
+ public $post_id = 0;
149
+
150
+ /**
151
+ * Sets up the manager.
152
+ *
153
+ * @since 1.0.0
154
+ * @access public
155
+ * @param string $name
156
+ * @param array $args
157
+ * @return void
158
+ */
159
+ public function __construct( $name, $args = array() ) {
160
+
161
+ foreach ( array_keys( get_object_vars( $this ) ) as $key ) {
162
+
163
+ if ( isset( $args[ $key ] ) )
164
+ $this->$key = $args[ $key ];
165
+ }
166
+
167
+ // Make sure the post type is an array.
168
+ $this->post_type = (array) $this->post_type;
169
+
170
+ // Set the manager name.
171
+ $this->name = sanitize_key( $name );
172
+ }
173
+
174
+ /**
175
+ * Enqueue scripts/styles for the manager.
176
+ *
177
+ * @since 1.0.0
178
+ * @access public
179
+ * @return void
180
+ */
181
+ public function enqueue() {}
182
+
183
+ /**
184
+ * Register a section.
185
+ *
186
+ * @since 1.0.0
187
+ * @access public
188
+ * @param object|string $section
189
+ * @param array $args
190
+ * @return void
191
+ */
192
+ public function register_section( $section, $args = array() ) {
193
+
194
+ if ( ! is_object( $section ) ) {
195
+
196
+ $type = isset( $args['type'] ) ? butterbean()->get_section_type( $args['type'] ) : butterbean()->get_section_type( 'default' );
197
+
198
+ $section = new $type( $this, $section, $args );
199
+ }
200
+
201
+ if ( ! $this->section_exists( $section->name ) )
202
+ $this->sections[ $section->name ] = $section;
203
+ }
204
+
205
+ /**
206
+ * Register a control.
207
+ *
208
+ * @since 1.0.0
209
+ * @access public
210
+ * @param object|string $control
211
+ * @param array $args
212
+ * @return void
213
+ */
214
+ public function register_control( $control, $args = array() ) {
215
+
216
+ if ( ! is_object( $control ) ) {
217
+
218
+ $type = isset( $args['type'] ) ? butterbean()->get_control_type( $args['type'] ) : butterbean()->get_control_type( 'default' );
219
+
220
+ $control = new $type( $this, $control, $args );
221
+ }
222
+
223
+ if ( ! $this->control_exists( $control->name ) )
224
+ $this->controls[ $control->name ] = $control;
225
+ }
226
+
227
+ /**
228
+ * Register a setting.
229
+ *
230
+ * @since 1.0.0
231
+ * @access public
232
+ * @param object|string $setting
233
+ * @param array $args
234
+ * @return void
235
+ */
236
+ public function register_setting( $setting, $args = array() ) {
237
+
238
+ if ( ! is_object( $setting ) ) {
239
+
240
+ $type = isset( $args['type'] ) ? butterbean()->get_setting_type( $args['type'] ) : butterbean()->get_setting_type( 'default' );
241
+
242
+ $setting = new $type( $this, $setting, $args );
243
+ }
244
+
245
+ if ( ! $this->setting_exists( $setting->name ) )
246
+ $this->settings[ $setting->name ] = $setting;
247
+ }
248
+
249
+ /**
250
+ * Register a control and setting object.
251
+ *
252
+ * @since 1.0.0
253
+ * @access public
254
+ * @param string $name
255
+ * @param object|array $control Control object or array of control arguments.
256
+ * @param object|array $setting Setting object or array of setting arguments.
257
+ * @return void
258
+ */
259
+ public function register_field( $name, $control, $setting ) {
260
+
261
+ is_object( $control ) ? $this->register_control( $control ) : $this->register_control( $name, $control );
262
+ is_object( $setting ) ? $this->register_setting( $setting ) : $this->register_setting( $name, $setting );
263
+ }
264
+
265
+ /**
266
+ * Unregisters a section object.
267
+ *
268
+ * @since 1.0.0
269
+ * @access public
270
+ * @param string $name
271
+ * @return void
272
+ */
273
+ public function unregister_section( $name ) {
274
+
275
+ if ( $this->section_exists( $name ) )
276
+ unset( $this->sections[ $name ] );
277
+ }
278
+
279
+ /**
280
+ * Unregisters a control object.
281
+ *
282
+ * @since 1.0.0
283
+ * @access public
284
+ * @param string $name
285
+ * @return void
286
+ */
287
+ public function unregister_control( $name ) {
288
+
289
+ if ( $this->control_exists( $name ) )
290
+ unset( $this->controls[ $name ] );
291
+ }
292
+
293
+ /**
294
+ * Unregisters a setting object.
295
+ *
296
+ * @since 1.0.0
297
+ * @access public
298
+ * @param string $name
299
+ * @return void
300
+ */
301
+ public function unregister_setting( $name ) {
302
+
303
+ if ( $this->setting_exists( $name ) )
304
+ unset( $this->settings[ $name ] );
305
+ }
306
+
307
+ /**
308
+ * Unregisters a control and setting object.
309
+ *
310
+ * @since 1.0.0
311
+ * @access public
312
+ * @param string $name
313
+ * @return void
314
+ */
315
+ public function unregister_field( $name ) {
316
+
317
+ $this->unregister_control( $name );
318
+ $this->unregister_setting( $name );
319
+ }
320
+
321
+ /**
322
+ * Returns a section object.
323
+ *
324
+ * @since 1.0.0
325
+ * @access public
326
+ * @param string $name
327
+ * @return object|bool
328
+ */
329
+ public function get_section( $name ) {
330
+
331
+ return $this->section_exists( $name ) ? $this->sections[ $name ] : false;
332
+ }
333
+
334
+ /**
335
+ * Returns a control object.
336
+ *
337
+ * @since 1.0.0
338
+ * @access public
339
+ * @param string $name
340
+ * @return object|bool
341
+ */
342
+ public function get_control( $name ) {
343
+
344
+ return $this->control_exists( $name ) ? $this->controls[ $name ] : false;
345
+ }
346
+
347
+ /**
348
+ * Returns a setting object.
349
+ *
350
+ * @since 1.0.0
351
+ * @access public
352
+ * @param string $name
353
+ * @return object|bool
354
+ */
355
+ public function get_setting( $name ) {
356
+
357
+ return $this->setting_exists( $name ) ? $this->settings[ $name ] : false;
358
+ }
359
+
360
+ /**
361
+ * Returns an object that contains both the control and setting objects.
362
+ *
363
+ * @since 1.0.0
364
+ * @access public
365
+ * @param string $name
366
+ * @return object|bool
367
+ */
368
+ public function get_field( $name ) {
369
+
370
+ $control = $this->get_control( $name );
371
+ $setting = $this->get_setting( $name );
372
+
373
+ $field = array( 'name' => $name, 'control' => $control, 'setting' => $setting );
374
+
375
+ return $control && $setting ? (object) $field : false;
376
+ }
377
+
378
+ /**
379
+ * Checks if a section exists.
380
+ *
381
+ * @since 1.0.0
382
+ * @access public
383
+ * @param string $name
384
+ * @return bool
385
+ */
386
+ public function section_exists( $name ) {
387
+
388
+ return isset( $this->sections[ $name ] );
389
+ }
390
+
391
+ /**
392
+ * Checks if a control exists.
393
+ *
394
+ * @since 1.0.0
395
+ * @access public
396
+ * @param string $name
397
+ * @return bool
398
+ */
399
+ public function control_exists( $name ) {
400
+
401
+ return isset( $this->controls[ $name ] );
402
+ }
403
+
404
+ /**
405
+ * Checks if a setting exists.
406
+ *
407
+ * @since 1.0.0
408
+ * @access public
409
+ * @param string $name
410
+ * @return bool
411
+ */
412
+ public function setting_exists( $name ) {
413
+
414
+ return isset( $this->settings[ $name ] );
415
+ }
416
+
417
+ /**
418
+ * Checks if a both a control and setting exist.
419
+ *
420
+ * @since 1.0.0
421
+ * @access public
422
+ * @param string $name
423
+ * @return bool
424
+ */
425
+ public function field_exists( $name ) {
426
+
427
+ return $this->control_exists( $name ) && $this->setting_exists( $name );
428
+ }
429
+
430
+ /**
431
+ * Returns the json array.
432
+ *
433
+ * @since 1.0.0
434
+ * @access public
435
+ * @return array
436
+ */
437
+ public function get_json() {
438
+ $this->to_json();
439
+
440
+ return $this->json;
441
+ }
442
+
443
+ /**
444
+ * Adds custom data to the JSON array. This data is passed to the Underscore template.
445
+ *
446
+ * @since 1.0.0
447
+ * @access public
448
+ * @return void
449
+ */
450
+ public function to_json() {
451
+
452
+ $sections_with_controls = array();
453
+ $blocked_sections = array();
454
+
455
+ $this->json['name'] = $this->name;
456
+ $this->json['type'] = $this->type;
457
+
458
+ // Get all sections that have controls.
459
+ foreach ( $this->controls as $control )
460
+ $sections_with_controls[] = $control->section;
461
+
462
+ $sections_with_controls = array_unique( $sections_with_controls );
463
+
464
+ // Get the JSON data for each section.
465
+ foreach ( $this->sections as $section ) {
466
+
467
+ $caps = $section->check_capabilities();
468
+
469
+ if ( $caps && in_array( $section->name, $sections_with_controls ) )
470
+ $this->json['sections'][] = $section->get_json();
471
+
472
+ if ( ! $caps )
473
+ $blocked_sections[] = $section->name;
474
+ }
475
+
476
+ // Get the JSON data for each control.
477
+ foreach ( $this->controls as $control ) {
478
+
479
+ if ( $control->check_capabilities() && ! in_array( $control->section, $blocked_sections ) )
480
+ $this->json['controls'][] = $control->get_json();
481
+ }
482
+ }
483
+
484
+ /**
485
+ * Saves each of the settings for the manager.
486
+ *
487
+ * @since 1.0.0
488
+ * @access public
489
+ * @return void
490
+ */
491
+ public function save( $post_id ) {
492
+
493
+ if ( ! $this->post_id )
494
+ $this->post_id = $post_id;
495
+
496
+ // Verify the nonce for this manager.
497
+ if ( ! isset( $_POST["butterbean_{$this->name}"] ) || ! wp_verify_nonce( $_POST["butterbean_{$this->name}"], "butterbean_{$this->name}_nonce" ) )
498
+ return;
499
+
500
+ // Loop through each setting and save it.
501
+ foreach ( $this->settings as $setting )
502
+ $setting->save();
503
+ }
504
+
505
+ /**
506
+ * Checks if the control should be allowed at all.
507
+ *
508
+ * @since 1.0.0
509
+ * @access public
510
+ * @return bool
511
+ */
512
+ public function check_capabilities() {
513
+
514
+ if ( $this->capability && ! call_user_func_array( 'current_user_can', (array) $this->capability ) )
515
+ return false;
516
+
517
+ if ( $this->post_type_supports && ! call_user_func_array( 'post_type_supports', array( get_post_type( $this->manager->post_id ), $this->post_type_supports ) ) )
518
+ return false;
519
+
520
+ if ( $this->theme_supports && ! call_user_func_array( 'theme_supports', (array) $this->theme_supports ) )
521
+ return false;
522
+
523
+ return true;
524
+ }
525
+
526
+ /**
527
+ * Prints Underscore.js template.
528
+ *
529
+ * @since 1.0.0
530
+ * @access public
531
+ * @return void
532
+ */
533
+ public function print_template() { ?>
534
+
535
+ <script type="text/html" id="tmpl-butterbean-manager-<?php echo esc_attr( $this->type ); ?>">
536
+ <?php $this->get_template(); ?>
537
+ </script>
538
+ <?php }
539
+
540
+ /**
541
+ * Gets the Underscore.js template.
542
+ *
543
+ * @since 1.0.0
544
+ * @access public
545
+ * @return void
546
+ */
547
+ public function get_template() {
548
+ butterbean_get_manager_template( $this->type );
549
+ }
550
+ }
admin/butterbean/inc/class-section.php ADDED
@@ -0,0 +1,291 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Base class for handling sections. Sections house groups of controls. Multiple sections can
4
+ * be added to a manager.
5
+ *
6
+ * @package ButterBean
7
+ * @subpackage Admin
8
+ * @author Justin Tadlock <justin@justintadlock.com>
9
+ * @copyright Copyright (c) 2015-2016, Justin Tadlock
10
+ * @link https://github.com/justintadlock/butterbean
11
+ * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
12
+ */
13
+
14
+ /**
15
+ * Base section class.
16
+ *
17
+ * @since 1.0.0
18
+ * @access public
19
+ */
20
+ class ButterBean_Section {
21
+
22
+ /**
23
+ * Stores the project details manager object.
24
+ *
25
+ * @since 1.0.0
26
+ * @access public
27
+ * @var object
28
+ */
29
+ public $manager;
30
+
31
+ /**
32
+ * Name/ID of the section.
33
+ *
34
+ * @since 1.0.0
35
+ * @access public
36
+ * @var string
37
+ */
38
+ public $name = '';
39
+
40
+ /**
41
+ * The type of section.
42
+ *
43
+ * @since 1.0.0
44
+ * @access public
45
+ * @var string
46
+ */
47
+ public $type = 'default';
48
+
49
+ /**
50
+ * Dashicons icon for the section.
51
+ *
52
+ * @since 1.0.0
53
+ * @access public
54
+ * @var string
55
+ */
56
+ public $icon = 'dashicons-admin-generic';
57
+
58
+ /**
59
+ * Label for the section.
60
+ *
61
+ * @since 1.0.0
62
+ * @access public
63
+ * @var string
64
+ */
65
+ public $label = '';
66
+
67
+ /**
68
+ * Description for the section.
69
+ *
70
+ * @since 1.0.0
71
+ * @access public
72
+ * @var string
73
+ */
74
+ public $description = '';
75
+
76
+ /**
77
+ * Priority (order) the section should be output.
78
+ *
79
+ * @since 1.0.0
80
+ * @access public
81
+ * @var int
82
+ */
83
+ public $priority = 10;
84
+
85
+ /**
86
+ * The number of instances created.
87
+ *
88
+ * @since 1.0.0
89
+ * @access protected
90
+ * @var int
91
+ */
92
+ protected static $instance_count = 0;
93
+
94
+ /**
95
+ * The instance of the current section.
96
+ *
97
+ * @since 1.0.0
98
+ * @access public
99
+ * @var int
100
+ */
101
+ public $instance_number;
102
+
103
+ /**
104
+ * A callback function for deciding if a section is active.
105
+ *
106
+ * @since 1.0.0
107
+ * @access public
108
+ * @var callable
109
+ */
110
+ public $active_callback = '';
111
+
112
+ /**
113
+ * A user role capability required to show the section.
114
+ *
115
+ * @since 1.0.0
116
+ * @access public
117
+ * @var string|array
118
+ */
119
+ public $capability = '';
120
+
121
+ /**
122
+ * A feature that the current post type must support to show the section.
123
+ *
124
+ * @since 1.0.0
125
+ * @access public
126
+ * @var string
127
+ */
128
+ public $post_type_supports = '';
129
+
130
+ /**
131
+ * A feature that the current theme must support to show the section.
132
+ *
133
+ * @since 1.0.0
134
+ * @access public
135
+ * @var string|array
136
+ */
137
+ public $theme_supports = '';
138
+
139
+ /**
140
+ * Stores the JSON data for the manager.
141
+ *
142
+ * @since 1.0.0
143
+ * @access public
144
+ * @var array()
145
+ */
146
+ public $json = array();
147
+
148
+ /**
149
+ * Creates a new section object.
150
+ *
151
+ * @since 1.0.0
152
+ * @access public
153
+ * @param object $manager
154
+ * @param string $section
155
+ * @param array $args
156
+ * @return void
157
+ */
158
+ public function __construct( $manager, $name, $args = array() ) {
159
+
160
+ foreach ( array_keys( get_object_vars( $this ) ) as $key ) {
161
+
162
+ if ( isset( $args[ $key ] ) )
163
+ $this->$key = $args[ $key ];
164
+ }
165
+
166
+ $this->manager = $manager;
167
+ $this->name = $name;
168
+
169
+ // Increment the instance count and set the instance number.
170
+ self::$instance_count += 1;
171
+ $this->instance_number = self::$instance_count;
172
+
173
+ // Set the active callback function if not set.
174
+ if ( ! $this->active_callback )
175
+ $this->active_callback = array( $this, 'active_callback' );
176
+ }
177
+
178
+ /**
179
+ * Enqueue scripts/styles for the section.
180
+ *
181
+ * @since 1.0.0
182
+ * @access public
183
+ * @return void
184
+ */
185
+ public function enqueue() {}
186
+
187
+ /**
188
+ * Returns the json array.
189
+ *
190
+ * @since 1.0.0
191
+ * @access public
192
+ * @return array
193
+ */
194
+ public function get_json() {
195
+ $this->to_json();
196
+
197
+ return $this->json;
198
+ }
199
+
200
+ /**
201
+ * Adds custom data to the json array. This data is passed to the Underscore template.
202
+ *
203
+ * @since 1.0.0
204
+ * @access public
205
+ * @return void
206
+ */
207
+ public function to_json() {
208
+
209
+ $this->json['manager'] = $this->manager->name;
210
+ $this->json['name'] = $this->name;
211
+ $this->json['type'] = $this->type;
212
+ $this->json['icon'] = preg_match( '/dashicons-/', $this->icon ) ? sprintf( 'dashicons %s', sanitize_html_class( $this->icon ) ) : esc_attr( $this->icon );
213
+ $this->json['label'] = $this->label;
214
+ $this->json['description'] = $this->description;
215
+ $this->json['active'] = $this->is_active();
216
+ }
217
+
218
+ /**
219
+ * Returns whether the section is active.
220
+ *
221
+ * @since 1.0.0
222
+ * @access public
223
+ * @return bool
224
+ */
225
+ public function is_active() {
226
+
227
+ $is_active = call_user_func( $this->active_callback, $this );
228
+
229
+ if ( $is_active )
230
+ $is_active = $this->check_capabilities();
231
+
232
+ return apply_filters( 'butterbean_is_section_active', $is_active, $this );
233
+ }
234
+
235
+ /**
236
+ * Default active callback.
237
+ *
238
+ * @since 1.0.0
239
+ * @access public
240
+ * @return bool
241
+ */
242
+ public function active_callback() {
243
+ return true;
244
+ }
245
+
246
+ /**
247
+ * Checks if the section should be allowed at all.
248
+ *
249
+ * @since 1.0.0
250
+ * @access public
251
+ * @return bool
252
+ */
253
+ public function check_capabilities() {
254
+
255
+ if ( $this->capability && ! call_user_func_array( 'current_user_can', (array) $this->capability ) )
256
+ return false;
257
+
258
+ if ( $this->post_type_supports && ! call_user_func_array( 'post_type_supports', array( get_post_type( $this->manager->post_id ), $this->post_type_supports ) ) )
259
+ return false;
260
+
261
+ if ( $this->theme_supports && ! call_user_func_array( 'theme_supports', (array) $this->theme_supports ) )
262
+ return false;
263
+
264
+ return true;
265
+ }
266
+
267
+ /**
268
+ * Prints Underscore.js template.
269
+ *
270
+ * @since 1.0.0
271
+ * @access public
272
+ * @return void
273
+ */
274
+ public function print_template() { ?>
275
+
276
+ <script type="text/html" id="tmpl-butterbean-section-<?php echo esc_attr( $this->type ); ?>">
277
+ <?php $this->get_template(); ?>
278
+ </script>
279
+ <?php }
280
+
281
+ /**
282
+ * Gets the Underscore.js template.
283
+ *
284
+ * @since 1.0.0
285
+ * @access public
286
+ * @return void
287
+ */
288
+ public function get_template() {
289
+ butterbean_get_section_template( $this->type );
290
+ }
291
+ }
admin/butterbean/inc/class-setting.php ADDED
@@ -0,0 +1,217 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Base setting class for the fields manager.
4
+ *
5
+ * @package ButterBean
6
+ * @subpackage Admin
7
+ * @author Justin Tadlock <justin@justintadlock.com>
8
+ * @copyright Copyright (c) 2015-2016, Justin Tadlock
9
+ * @link https://github.com/justintadlock/butterbean
10
+ * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
11
+ */
12
+
13
+ /**
14
+ * Base setting class.
15
+ *
16
+ * @since 1.0.0
17
+ * @access public
18
+ */
19
+ class ButterBean_Setting {
20
+
21
+ /**
22
+ * Stores the manager object.
23
+ *
24
+ * @since 1.0.0
25
+ * @access public
26
+ * @var object
27
+ */
28
+ public $manager;
29
+
30
+ /**
31
+ * Name/ID of the setting.
32
+ *
33
+ * @since 1.0.0
34
+ * @access public
35
+ * @var string
36
+ */
37
+ public $name = '';
38
+
39
+ /**
40
+ * Value of the setting.
41
+ *
42
+ * @since 1.0.0
43
+ * @access public
44
+ * @var string
45
+ */
46
+ public $value = '';
47
+
48
+ /**
49
+ * Default value of the setting.
50
+ *
51
+ * @since 1.0.0
52
+ * @access public
53
+ * @var string
54
+ */
55
+ public $default = '';
56
+
57
+ /**
58
+ * Sanitization/Validation callback function.
59
+ *
60
+ * @since 1.0.0
61
+ * @access public
62
+ * @var string
63
+ */
64
+ public $sanitize_callback = '';
65
+
66
+ /**
67
+ * A user role capability required to save the setting.
68
+ *
69
+ * @since 1.0.0
70
+ * @access public
71
+ * @var string|array
72
+ */
73
+ public $capability = '';
74
+
75
+ /**
76
+ * A feature that the current post type must support to save the setting.
77
+ *
78
+ * @since 1.0.0
79
+ * @access public
80
+ * @var string
81
+ */
82
+ public $post_type_supports = '';
83
+
84
+ /**
85
+ * A feature that the current theme must support to save the setting.
86
+ *
87
+ * @since 1.0.0
88
+ * @access public
89
+ * @var string|array
90
+ */
91
+ public $theme_supports = '';
92
+
93
+ /**
94
+ * Creates a new setting object.
95
+ *
96
+ * @since 1.0.0
97
+ * @access public
98
+ * @param object $manager
99
+ * @param string $cap
100
+ * @param array $args
101
+ * @return void
102
+ */
103
+ public function __construct( $manager, $name, $args = array() ) {
104
+
105
+ foreach ( array_keys( get_object_vars( $this ) ) as $key ) {
106
+
107
+ if ( isset( $args[ $key ] ) )
108
+ $this->$key = $args[ $key ];
109
+ }
110
+
111
+ $this->manager = $manager;
112
+ $this->name = $name;
113
+
114
+ if ( $this->sanitize_callback )
115
+ add_filter( "butterbean_{$this->manager->name}_sanitize_{$this->name}", $this->sanitize_callback, 10, 2 );
116
+ }
117
+
118
+ /**
119
+ * Gets the value of the setting.
120
+ *
121
+ * @since 1.0.0
122
+ * @access public
123
+ * @return mixed
124
+ */
125
+ public function get_value() {
126
+
127
+ $value = get_post_meta( $this->manager->post_id, $this->name, true );
128
+
129
+ return ! $value && butterbean()->is_new_post ? $this->default : $value;
130
+ }
131
+
132
+ /**
133
+ * Gets the posted value of the setting.
134
+ *
135
+ * @since 1.0.0
136
+ * @access public
137
+ * @return mixed
138
+ */
139
+ public function get_posted_value() {
140
+
141
+ $value = '';
142
+
143
+ if ( isset( $_POST[ $this->get_field_name() ] ) )
144
+ $value = $_POST[ $this->get_field_name() ];
145
+
146
+ return $this->sanitize( $value );
147
+ }
148
+
149
+ /**
150
+ * Retuns the correct field name for the setting.
151
+ *
152
+ * @since 1.0.0
153
+ * @access public
154
+ * @return string
155
+ */
156
+ public function get_field_name() {
157
+
158
+ return "butterbean_{$this->manager->name}_setting_{$this->name}";
159
+ }
160
+
161
+ /**
162
+ * Sanitizes the value of the setting.
163
+ *
164
+ * @since 1.0.0
165
+ * @access public
166
+ * @return mixed
167
+ */
168
+ public function sanitize( $value ) {
169
+
170
+ return apply_filters( "butterbean_{$this->manager->name}_sanitize_{$this->name}", $value, $this );
171
+ }
172
+
173
+ /**
174
+ * Saves the value of the setting.
175
+ *
176
+ * @since 1.0.0
177
+ * @access public
178
+ * @return void
179
+ */
180
+ public function save() {
181
+
182
+ if ( ! $this->check_capabilities() )
183
+ return;
184
+
185
+ $old_value = $this->get_value();
186
+ $new_value = $this->get_posted_value();
187
+
188
+ // If we have don't have a new value but do have an old one, delete it.
189
+ if ( ! $new_value && $old_value )
190
+ delete_post_meta( $this->manager->post_id, $this->name );
191
+
192
+ // If the new value doesn't match the old value, set it.
193
+ else if ( $new_value !== $old_value )
194
+ update_post_meta( $this->manager->post_id, $this->name, $new_value );
195
+ }
196
+
197
+ /**
198
+ * Checks if the setting should be saved at all.
199
+ *
200
+ * @since 1.0.0
201
+ * @access public
202
+ * @return bool
203
+ */
204
+ public function check_capabilities() {
205
+
206
+ if ( $this->capability && ! call_user_func_array( 'current_user_can', (array) $this->capability ) )
207
+ return false;
208
+
209
+ if ( $this->post_type_supports && ! call_user_func_array( 'post_type_supports', array( get_post_type( $this->manager->post_id ), $this->post_type_supports ) ) )
210
+ return false;
211
+
212
+ if ( $this->theme_supports && ! call_user_func_array( 'theme_supports', (array) $this->theme_supports ) )
213
+ return false;
214
+
215
+ return true;
216
+ }
217
+ }
admin/butterbean/inc/controls/class-control-checkboxes.php ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Multiple checkbox control class. This is for array-type settings, so you'll need to
4
+ * use it with either the `ButterBean_Setting_Array` class or a class that handles
5
+ * arrays.
6
+ *
7
+ * @package ButterBean
8
+ * @author Justin Tadlock <justin@justintadlock.com>
9
+ * @copyright Copyright (c) 2015-2016, Justin Tadlock
10
+ * @link https://github.com/justintadlock/butterbean
11
+ * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
12
+ */
13
+
14
+ /**
15
+ * Multiple checkboxes control class.
16
+ *
17
+ * @since 1.0.0
18
+ * @access public
19
+ */
20
+ class ButterBean_Control_CheckBoxes extends ButterBean_Control {
21
+
22
+ /**
23
+ * The type of control.
24
+ *
25
+ * @since 1.0.0
26
+ * @access public
27
+ * @var string
28
+ */
29
+ public $type = 'checkboxes';
30
+
31
+ /**
32
+ * Adds custom data to the json array. This data is passed to the Underscore template.
33
+ *
34
+ * @since 1.0.0
35
+ * @access public
36
+ * @return void
37
+ */
38
+ public function to_json() {
39
+ parent::to_json();
40
+
41
+ $this->json['value'] = (array) $this->get_value();
42
+ }
43
+ }
admin/butterbean/inc/controls/class-control-color.php ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Text control class.
4
+ *
5
+ * @package ButterBean
6
+ * @author Justin Tadlock <justin@justintadlock.com>
7
+ * @copyright Copyright (c) 2015-2016, Justin Tadlock
8
+ * @link https://github.com/justintadlock/butterbean
9
+ * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
10
+ */
11
+
12
+ /**
13
+ * Text control class.
14
+ *
15
+ * @since 1.0.0
16
+ * @access public
17
+ */
18
+ class ButterBean_Control_Color extends ButterBean_Control {
19
+
20
+ /**
21
+ * The type of control.
22
+ *
23
+ * @since 1.0.0
24
+ * @access public
25
+ * @var string
26
+ */
27
+ public $type = 'color';
28
+
29
+ public function __construct( $manager, $name, $args = array() ) {
30
+ parent::__construct( $manager, $name, $args );
31
+ }
32
+
33
+ public function get_attr() {
34
+ $attr = parent::get_attr();
35
+
36
+ $setting = $this->get_setting();
37
+
38
+ $attr['class'] = 'butterbean-color-picker';
39
+ $attr['type'] = 'text';
40
+ $attr['maxlength'] = 7;
41
+ $attr['data-default-color'] = $setting ? $setting->default : '';
42
+
43
+ return $attr;
44
+ }
45
+
46
+ public function enqueue() {
47
+ wp_enqueue_script( 'wp-color-picker' );
48
+ wp_enqueue_style( 'wp-color-picker' );
49
+
50
+ add_action( 'admin_footer', array( $this, 'print_scripts' ) );
51
+ }
52
+
53
+ public function print_scripts() { ?>
54
+ <script type="text/javascript">
55
+ jQuery( document ).ready( function( $ ) {
56
+ $( '.butterbean-color-picker' ).wpColorPicker();
57
+ } );
58
+ </script>
59
+ <?php }
60
+
61
+ /**
62
+ * Get the value for the setting.
63
+ *
64
+ * @since 1.0.0
65
+ * @access public
66
+ * @param string $setting
67
+ * @return mixed
68
+ */
69
+ public function get_value( $setting = 'default' ) {
70
+
71
+ $value = parent::get_value( $setting );
72
+
73
+ return ltrim( $value, '#' );
74
+ }
75
+ }
admin/butterbean/inc/controls/class-control-date.php ADDED
@@ -0,0 +1,135 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Date control class.
4
+ *
5
+ * @package ButterBean
6
+ * @subpackage Admin
7
+ * @author Justin Tadlock <justin@justintadlock.com>
8
+ * @copyright Copyright (c) 2015-2016, Justin Tadlock
9
+ * @link https://github.com/justintadlock/butterbean
10
+ * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
11
+ */
12
+
13
+ /**
14
+ * Date control class.
15
+ *
16
+ * @since 1.0.0
17
+ * @access public
18
+ */
19
+ class ButterBean_Control_Date extends ButterBean_Control {
20
+
21
+ /**
22
+ * The type of control.
23
+ *
24
+ * @since 1.0.0
25
+ * @access public
26
+ * @var string
27
+ */
28
+ public $type = 'date';
29
+
30
+ /**
31
+ * Whether to show the time. Note that settings, particularly the
32
+ * `ButterBean_Setting_Date` class will store the time as `00:00:00` if
33
+ * no time is provided.
34
+ *
35
+ * @since 1.0.0
36
+ * @access public
37
+ * @var bool
38
+ */
39
+ public $show_time = true;
40
+
41
+ /**
42
+ * Adds custom data to the json array. This data is passed to the Underscore template.
43
+ *
44
+ * @since 1.0.0
45
+ * @access public
46
+ * @globl object $wp_locale
47
+ * @return void
48
+ */
49
+ public function to_json() {
50
+ global $wp_locale;
51
+
52
+ parent::to_json();
53
+
54
+ $this->json['show_time'] = $this->show_time;
55
+
56
+ $field_name = $this->get_field_name();
57
+
58
+ // Get project start/end dates.
59
+ $date = $this->get_value();
60
+
61
+ // Get the year, month, and day.
62
+ $year = $date ? mysql2date( 'Y', $date, false ) : '';
63
+ $month = $date ? mysql2date( 'm', $date, false ) : '';
64
+ $day = $date ? mysql2date( 'd', $date, false ) : '';
65
+
66
+ // Get the hour, minute, and second.
67
+ $hour = $date ? mysql2date( 'H', $date, false ) : '';
68
+ $minute = $date ? mysql2date( 'i', $date, false ) : '';
69
+ $second = $date ? mysql2date( 's', $date, false ) : '';
70
+
71
+ // Year
72
+ $this->json['year'] = array(
73
+ 'value' => esc_attr( $year ),
74
+ 'label' => esc_html__( 'Year', 'butterbean' ),
75
+ 'name' => esc_attr( "{$field_name}_year" ),
76
+ 'attr' => sprintf( 'placeholder="%s" size="4" maxlength="4" autocomplete="off"', esc_attr( date_i18n( 'Y' ) ) )
77
+ );
78
+
79
+ // Month
80
+ $this->json['month'] = array(
81
+ 'value' => esc_attr( $month ),
82
+ 'name' => esc_attr( "{$field_name}_month" ),
83
+ 'label' => esc_html__( 'Month', 'butterbean' ),
84
+ 'choices' => array(
85
+ array(
86
+ 'num' => '',
87
+ 'label' => ''
88
+ )
89
+ )
90
+ );
91
+
92
+ for ( $i = 1; $i < 13; $i = $i +1 ) {
93
+
94
+ $monthnum = zeroise( $i, 2 );
95
+ $monthtext = $wp_locale->get_month_abbrev( $wp_locale->get_month( $i ) );
96
+
97
+ $this->json['month']['choices'][] = array(
98
+ 'num' => $monthnum,
99
+ 'label' => $monthtext
100
+ );
101
+ }
102
+
103
+ // Day
104
+ $this->json['day'] = array(
105
+ 'value' => esc_attr( $day ),
106
+ 'name' => esc_attr( "{$field_name}_day" ),
107
+ 'label' => esc_html__( 'Day', 'butterbean' ),
108
+ 'attr' => sprintf( 'placeholder="%s" size="2" maxlength="2" autocomplete="off"', esc_attr( date_i18n( 'd' ) ) )
109
+ );
110
+
111
+ // Hour
112
+ $this->json['hour'] = array(
113
+ 'value' => esc_attr( $hour ),
114
+ 'name' => esc_attr( "{$field_name}_hour" ),
115
+ 'label' => esc_html__( 'Hour', 'butterbean' ),
116
+ 'attr' => 'placeholder="00" size="2" maxlength="2" autocomplete="off"'
117
+ );
118
+
119
+ // Minute
120
+ $this->json['minute'] = array(
121
+ 'value' => esc_attr( $minute ),
122
+ 'name' => esc_attr( "{$field_name}_minute" ),
123
+ 'label' => esc_html__( 'Minute', 'butterbean' ),
124
+ 'attr' => 'placeholder="00" size="2" maxlength="2" autocomplete="off"'
125
+ );
126
+
127
+ // Second
128
+ $this->json['second'] = array(
129
+ 'value' => esc_attr( $second ),
130
+ 'name' => esc_attr( "{$field_name}_second" ),
131
+ 'label' => esc_html__( 'Second', 'butterbean' ),
132
+ 'attr' => 'placeholder="00" size="2" maxlength="2" autocomplete="off"'
133
+ );
134
+ }
135
+ }
admin/butterbean/inc/controls/class-control-excerpt.php ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Excerpt control class. Note that this control isn't meant to be tied to a setting. Core
4
+ * WP will save the excerpt. Also, make sure to disable the core excerpt metabox if using
5
+ * this control.
6
+ *
7
+ * @package ButterBean
8
+ * @subpackage Admin
9
+ * @author Justin Tadlock <justin@justintadlock.com>
10
+ * @copyright Copyright (c) 2015-2016, Justin Tadlock
11
+ * @link https://github.com/justintadlock/butterbean
12
+ * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
13
+ */
14
+
15
+ /**
16
+ * Excerpt control class.
17
+ *
18
+ * @since 1.0.0
19
+ * @access public
20
+ */
21
+ class ButterBean_Control_Excerpt extends ButterBean_Control_Textarea {
22
+
23
+ /**
24
+ * Gets the attributes for the control.
25
+ *
26
+ * @since 1.0.0
27
+ * @access public
28
+ * @return array
29
+ */
30
+ public function get_attr() {
31
+ $attr = parent::get_attr();
32
+
33
+ $attr['id'] = 'post_excerpt';
34
+
35
+ return $attr;
36
+ }
37
+
38
+ /**
39
+ * Returns the HTML field name for the control.
40
+ *
41
+ * @since 1.0.0
42
+ * @access public
43
+ * @param string $setting
44
+ * @return string
45
+ */
46
+ public function get_field_name( $setting = 'default' ) {
47
+ return 'post_excerpt';
48
+ }
49
+
50
+ /**
51
+ * Get the value for the setting.
52
+ *
53
+ * @since 1.0.0
54
+ * @access public
55
+ * @param string $setting
56
+ * @return mixed
57
+ */
58
+ public function get_value( $setting = 'default' ) {
59
+
60
+ return get_post( $this->manager->post_id )->post_excerpt;
61
+ }
62
+
63
+ /**
64
+ * Gets the Underscore.js template.
65
+ *
66
+ * @since 1.0.0
67
+ * @access public
68
+ * @return void
69
+ */
70
+ public function get_template() {
71
+ butterbean_get_control_template( 'textarea' );
72
+ }
73
+ }
admin/butterbean/inc/controls/class-control-image.php ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Image control class. This control allows users to set an image. It passes the attachment
4
+ * ID the setting, so you'll need a custom control class if you want to store anything else,
5
+ * such as the URL or other data.
6
+ *
7
+ * @package ButterBean
8
+ * @author Justin Tadlock <justin@justintadlock.com>
9
+ * @copyright Copyright (c) 2015-2016, Justin Tadlock
10
+ * @link https://github.com/justintadlock/butterbean
11
+ * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
12
+ */
13
+
14
+ /**
15
+ * Image control class.
16
+ *
17
+ * @since 1.0.0
18
+ * @access public
19
+ */
20
+ class ButterBean_Control_Image extends ButterBean_Control {
21
+
22
+ /**
23
+ * The type of control.
24
+ *
25
+ * @since 1.0.0
26
+ * @access public
27
+ * @var string
28
+ */
29
+ public $type = 'image';
30
+
31
+ /**
32
+ * Array of text labels to use for the media upload frame.
33
+ *
34
+ * @since 1.0.0
35
+ * @access public
36
+ * @var string
37
+ */
38
+ public $l10n = array();
39
+
40
+ /**
41
+ * Creates a new control object.
42
+ *
43
+ * @since 1.0.0
44
+ * @access public
45
+ * @param object $manager
46
+ * @param string $name
47
+ * @param array $args
48
+ * @return void
49
+ */
50
+ public function __construct( $manager, $name, $args = array() ) {
51
+ parent::__construct( $manager, $name, $args );
52
+
53
+ $this->l10n = wp_parse_args(
54
+ $this->l10n,
55
+ array(
56
+ 'upload' => esc_html__( 'Add image', 'butterbean' ),
57
+ 'set' => esc_html__( 'Set as image', 'butterbean' ),
58
+ 'choose' => esc_html__( 'Choose image', 'butterbean' ),
59
+ 'change' => esc_html__( 'Change image', 'butterbean' ),
60
+ 'remove' => esc_html__( 'Remove image', 'butterbean' ),
61
+ 'placeholder' => esc_html__( 'No image selected', 'butterbean' )
62
+ )
63
+ );
64
+ }
65
+
66
+ /**
67
+ * Enqueue scripts/styles for the control.
68
+ *
69
+ * @since 1.0.0
70
+ * @access public
71
+ * @return void
72
+ */
73
+ public function enqueue() {
74
+
75
+ wp_enqueue_script( 'media-views' );
76
+ }
77
+
78
+ /**
79
+ * Adds custom data to the json array.
80
+ *
81
+ * @since 1.0.0
82
+ * @access public
83
+ * @return void
84
+ */
85
+ public function to_json() {
86
+ parent::to_json();
87
+
88
+ $this->json['l10n'] = $this->l10n;
89
+
90
+ $value = $this->get_value();
91
+ $image = $alt = '';
92
+
93
+ if ( $value ) {
94
+ $image = wp_get_attachment_image_src( absint( $value ), 'large' );
95
+ $alt = get_post_meta( absint( $value ), '_wp_attachment_image_alt', true );
96
+ }
97
+
98
+ $this->json['src'] = $image ? esc_url( $image[0] ) : '';
99
+ $this->json['alt'] = $alt ? esc_attr( $alt ) : '';
100
+ }
101
+ }
admin/butterbean/inc/controls/class-control-multi-avatars.php ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Multi-avatars control. This control is for outputting multiple users who can create,
4
+ * edit, or publish posts of the given post type. Multiple users can be selected. The
5
+ * data is expected to be an array. For use with the `Butterbean_Setting_Array` class.
6
+ *
7
+ * @package ButterBean
8
+ * @author Justin Tadlock <justin@justintadlock.com>
9
+ * @copyright Copyright (c) 2015-2016, Justin Tadlock
10
+ * @link https://github.com/justintadlock/butterbean
11
+ * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
12
+ */
13
+
14
+ /**
15
+ * Multi-avatars control class.
16
+ *
17
+ * @since 1.0.0
18
+ * @access public
19
+ */
20
+ class ButterBean_Control_Multi_Avatars extends ButterBean_Control {
21
+
22
+ /**
23
+ * The type of control.
24
+ *
25
+ * @since 1.0.0
26
+ * @access public
27
+ * @var string
28
+ */
29
+ public $type = 'multi-avatars';
30
+
31
+ /**
32
+ * Adds custom data to the json array. This data is passed to the Underscore template.
33
+ *
34
+ * @since 1.0.0
35
+ * @access public
36
+ * @return void
37
+ */
38
+ public function to_json() {
39
+ parent::to_json();
40
+
41
+ $this->json['value'] = is_array( $this->get_value() ) ? array_map( 'absint', $this->get_value() ) : array();
42
+ $this->json['choices'] = array();
43
+
44
+ $users = get_users( array( 'role__in' => $this->get_roles() ) );
45
+
46
+ foreach ( $users as $user ) {
47
+ $this->json['choices'][] = array(
48
+ 'id' => $user->ID,
49
+ 'name' => $user->display_name,
50
+ 'avatar' => get_avatar( $user->ID, 70 )
51
+ );
52
+ }
53
+ }
54
+
55
+ /**
56
+ * Returns an array of user roles that are allowed to edit, publish, or create
57
+ * posts of the given post type.
58
+ *
59
+ * @since 1.0.0
60
+ * @access public
61
+ * @global object $wp_roles
62
+ * @return array
63
+ */
64
+ public function get_roles() {
65
+ global $wp_roles;
66
+
67
+ $roles = array();
68
+ $type = get_post_type_object( get_post_type( $this->manager->post_id ) );
69
+
70
+ // Get the post type object caps.
71
+ $caps = array( $type->cap->edit_posts, $type->cap->publish_posts, $type->cap->create_posts );
72
+ $caps = array_unique( $caps );
73
+
74
+ // Loop through the available roles.
75
+ foreach ( $wp_roles->roles as $name => $role ) {
76
+
77
+ foreach ( $caps as $cap ) {
78
+
79
+ // If the role is granted the cap, add it.
80
+ if ( isset( $role['capabilities'][ $cap ] ) && true === $role['capabilities'][ $cap ] ) {
81
+ $roles[] = $name;
82
+ break;
83
+ }
84
+ }
85
+ }
86
+
87
+ return $roles;
88
+ }
89
+ }
admin/butterbean/inc/controls/class-control-palette.php ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Radio control class.
4
+ *
5
+ * @package ButterBean
6
+ * @author Justin Tadlock <justin@justintadlock.com>
7
+ * @copyright Copyright (c) 2015-2016, Justin Tadlock
8
+ * @link https://github.com/justintadlock/butterbean
9
+ * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
10
+ */
11
+
12
+ /**
13
+ * Radio control class.
14
+ *
15
+ * @since 1.0.0
16
+ * @access public
17
+ */
18
+ class ButterBean_Control_Palette extends ButterBean_Control {
19
+
20
+ /**
21
+ * The type of control.
22
+ *
23
+ * @since 1.0.0
24
+ * @access public
25
+ * @var string
26
+ */
27
+ public $type = 'palette';
28
+
29
+ public function to_json() {
30
+ parent::to_json();
31
+
32
+ $value = $this->get_value();
33
+
34
+ // Make sure the colors have a hash.
35
+ foreach ( $this->choices as $choice => $palette ) {
36
+ $this->choices[ $choice ]['colors'] = array_map( 'butterbean_maybe_hash_hex_color', $palette['colors'] );
37
+
38
+ $this->choices[ $choice ]['selected'] = $value && $choice === $value;
39
+ }
40
+
41
+ $this->json['choices'] = $this->choices;
42
+ }
43
+ }
admin/butterbean/inc/controls/class-control-parent.php ADDED
@@ -0,0 +1,97 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Post parent control class. This class is a specialty class meant for use in unique
4
+ * scenarios where you're not using the core post parent drop-down. This is often the
5
+ * case with flat post types that have a parent post. This control is not meant to be
6
+ * used with a setting. Core WP will store the data in the `post.post_parent` field.
7
+ *
8
+ * @package ButterBean
9
+ * @author Justin Tadlock <justin@justintadlock.com>
10
+ * @copyright Copyright (c) 2015-2016, Justin Tadlock
11
+ * @link https://github.com/justintadlock/butterbean
12
+ * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
13
+ */
14
+
15
+ /**
16
+ * Post parent control class.
17
+ *
18
+ * @since 1.0.0
19
+ * @access public
20
+ */
21
+ class ButterBean_Control_Parent extends ButterBean_Control {
22
+
23
+ /**
24
+ * The type of control.
25
+ *
26
+ * @since 1.0.0
27
+ * @access public
28
+ * @var string
29
+ */
30
+ public $type = 'parent';
31
+
32
+ /**
33
+ * The post type to select posts from.
34
+ *
35
+ * @since 1.0.0
36
+ * @access public
37
+ * @var string
38
+ */
39
+ public $post_type = '';
40
+
41
+ /**
42
+ * Returns the HTML field name for the control.
43
+ *
44
+ * @since 1.0.0
45
+ * @access public
46
+ * @param string $setting
47
+ * @return array
48
+ */
49
+ public function get_field_name( $setting = 'default' ) {
50
+
51
+ return 'post_parent';
52
+ }
53
+
54
+ /**
55
+ * Get the value for the setting.
56
+ *
57
+ * @since 1.0.0
58
+ * @access public
59
+ * @param string $setting
60
+ * @return mixed
61
+ */
62
+ public function get_value( $setting = 'default' ) {
63
+
64
+ return get_post( $this->manager->post_id )->post_parent;
65
+ }
66
+
67
+ /**
68
+ * Adds custom data to the json array. This data is passed to the Underscore template.
69
+ *
70
+ * @since 1.0.0
71
+ * @access public
72
+ * @return void
73
+ */
74
+ public function to_json() {
75
+ parent::to_json();
76
+
77
+ $_post = get_post( $this->manager->post_id );
78
+
79
+ $posts = get_posts(
80
+ array(
81
+ 'post_type' => $this->post_type ? $this->post_type : get_post_type( $this->manager->post_id ),
82
+ 'post_status' => 'any',
83
+ 'post__not_in' => array( $this->manager->post_id ),
84
+ 'posts_per_page' => -1,
85
+ 'post_parent' => 0,
86
+ 'orderby' => 'title',
87
+ 'order' => 'ASC',
88
+ 'fields' => array( 'ID', 'post_title' )
89
+ )
90
+ );
91
+
92
+ $this->json['choices'] = array( array( 'value' => 0, 'label' => '' ) );
93
+
94
+ foreach ( $posts as $post )
95
+ $this->json['choices'][] = array( 'value' => $post->ID, 'label' => $post->post_title );
96
+ }
97
+ }
admin/butterbean/inc/controls/class-control-radio-image.php ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Radio image control class.
4
+ *
5
+ * @package ButterBean
6
+ * @subpackage Admin
7
+ * @author Justin Tadlock <justin@justintadlock.com>
8
+ * @copyright Copyright (c) 2015-2016, Justin Tadlock
9
+ * @link https://github.com/justintadlock/butterbean
10
+ * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
11
+ */
12
+
13
+ /**
14
+ * Radio image control class.
15
+ *
16
+ * @since 1.0.0
17
+ * @access public
18
+ */
19
+ class ButterBean_Control_Radio_Image extends ButterBean_Control_Radio {
20
+
21
+ /**
22
+ * The type of control.
23
+ *
24
+ * @since 1.0.0
25
+ * @access public
26
+ * @var string
27
+ */
28
+ public $type = 'radio-image';
29
+
30
+ /**
31
+ * Adds custom data to the json array. This data is passed to the Underscore template.
32
+ *
33
+ * @since 1.0.0
34
+ * @access public
35
+ * @return void
36
+ */
37
+ public function to_json() {
38
+ parent::to_json();
39
+
40
+ foreach ( $this->choices as $value => $args )
41
+ $this->choices[ $value ]['url'] = esc_url( sprintf( $args['url'], get_template_directory_uri(), get_stylesheet_directory_uri() ) );
42
+
43
+ $this->json['choices'] = $this->choices;
44
+ }
45
+ }
admin/butterbean/inc/controls/class-control-radio.php ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Radio control class.
4
+ *
5
+ * @package ButterBean
6
+ * @subpackage Admin
7
+ * @author Justin Tadlock <justin@justintadlock.com>
8
+ * @copyright Copyright (c) 2015-2016, Justin Tadlock
9
+ * @link https://github.com/justintadlock/butterbean
10
+ * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
11
+ */
12
+
13
+ /**
14
+ * Radio control class.
15
+ *
16
+ * @since 1.0.0
17
+ * @access public
18
+ */
19
+ class ButterBean_Control_Radio extends ButterBean_Control {
20
+
21
+ /**
22
+ * The type of control.
23
+ *
24
+ * @since 1.0.0
25
+ * @access public
26
+ * @var string
27
+ */
28
+ public $type = 'radio';
29
+
30
+ /**
31
+ * Radio controls imply that a value should be set. Therefore, we will return
32
+ * the default if there is no value.
33
+ *
34
+ * @since 1.0.0
35
+ * @access public
36
+ * @param string $setting
37
+ * @return mixed
38
+ */
39
+ public function get_value( $setting = 'default' ) {
40
+
41
+ $value = parent::get_value( $setting );
42
+ $object = $this->get_setting( $setting );
43
+
44
+ return ! $value && $object ? $object->default : $value;
45
+ }
46
+ }
admin/butterbean/inc/controls/class-control-select-group.php ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Select group control class.
4
+ *
5
+ * @package ButterBean
6
+ * @author Justin Tadlock <justin@justintadlock.com>
7
+ * @copyright Copyright (c) 2015-2016, Justin Tadlock
8
+ * @link https://github.com/justintadlock/butterbean
9
+ * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
10
+ */
11
+
12
+ /**
13
+ * Select group control class.
14
+ *
15
+ * @since 1.0.0
16
+ * @access public
17
+ */
18
+ class ButterBean_Control_Select_Group extends ButterBean_Control {
19
+
20
+ /**
21
+ * The type of control.
22
+ *
23
+ * @since 1.0.0
24
+ * @access public
25
+ * @var string
26
+ */
27
+ public $type = 'select-group';
28
+
29
+ /**
30
+ * Adds custom data to the json array.
31
+ *
32
+ * @since 1.0.0
33
+ * @access public
34
+ * @return void
35
+ */
36
+ public function to_json() {
37
+ parent::to_json();
38
+
39
+ $choices = $group = array();
40
+
41
+ foreach ( $this->choices as $choice => $maybe_group ) {
42
+
43
+ if ( is_array( $maybe_group ) )
44
+ $group[ $choice ] = $maybe_group;
45
+ else
46
+ $choices[ $choice ] = $maybe_group;
47
+ }
48
+
49
+ $this->json['choices'] = $choices;
50
+ $this->json['group'] = $group;
51
+ }
52
+ }
admin/butterbean/inc/controls/class-control-textarea.php ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Textarea control class.
4
+ *
5
+ * @package ButterBean
6
+ * @subpackage Admin
7
+ * @author Justin Tadlock <justin@justintadlock.com>
8
+ * @copyright Copyright (c) 2015-2016, Justin Tadlock
9
+ * @link https://github.com/justintadlock/butterbean
10
+ * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
11
+ */
12
+
13
+ /**
14
+ * Textarea control class.
15
+ *
16
+ * @since 1.0.0
17
+ * @access public
18
+ */
19
+ class ButterBean_Control_Textarea extends ButterBean_Control {
20
+
21
+ /**
22
+ * The type of control.
23
+ *
24
+ * @since 1.0.0
25
+ * @access public
26
+ * @var string
27
+ */
28
+ public $type = 'textarea';
29
+
30
+ /**
31
+ * Adds custom data to the json array. This data is passed to the Underscore template.
32
+ *
33
+ * @since 1.0.0
34
+ * @access public
35
+ * @return void
36
+ */
37
+ public function to_json() {
38
+ parent::to_json();
39
+
40
+ $this->json['value'] = esc_textarea( $this->get_value() );
41
+ }
42
+ }
admin/butterbean/inc/functions-core.php ADDED
@@ -0,0 +1,163 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Helper functions.
4
+ *
5
+ * @package ButterBean
6
+ * @subpackage Admin
7
+ * @author Justin Tadlock <justin@justintadlock.com>
8
+ * @copyright Copyright (c) 2015-2016, Justin Tadlock
9
+ * @link https://github.com/justintadlock/butterbean
10
+ * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
11
+ */
12
+
13
+ /**
14
+ * Function for validating booleans before saving them as metadata. If the value is
15
+ * `true`, we'll return a `1` to be stored as the meta value. Else, we return `false`.
16
+ *
17
+ * @since 1.0.0
18
+ * @access public
19
+ * @param mixed
20
+ * @return bool|int
21
+ */
22
+ function butterbean_validate_boolean( $value ) {
23
+
24
+ return wp_validate_boolean( $value ) ? 1 : false;
25
+ }
26
+
27
+ /**
28
+ * Pre-WP 4.6 function for sanitizing hex colors.
29
+ *
30
+ * @since 1.0.0
31
+ * @access public
32
+ * @param string $color
33
+ * @return string
34
+ */
35
+ function butterbean_sanitize_hex_color( $color ) {
36
+
37
+ if ( function_exists( 'sanitize_hex_color' ) )
38
+ return sanitize_hex_color( $color );
39
+
40
+ return $color && preg_match('|^#([A-Fa-f0-9]{3}){1,2}$|', $color ) ? $color : '';
41
+ }
42
+
43
+ /**
44
+ * Pre-WP 4.6 function for sanitizing hex colors without a hash.
45
+ *
46
+ * @since 1.0.0
47
+ * @access public
48
+ * @param string $color
49
+ * @return string
50
+ */
51
+ function butterbean_sanitize_hex_color_no_hash( $color ) {
52
+
53
+ if ( function_exists( 'sanitize_hex_color_no_hash' ) )
54
+ return sanitize_hex_color_no_hash( $color );
55
+
56
+ $color = ltrim( $color, '#' );
57
+
58
+ if ( '' === $color )
59
+ return '';
60
+
61
+ return butterbean_sanitize_hex_color( '#' . $color ) ? $color : null;
62
+ }
63
+
64
+ /**
65
+ * Pre-WP 4.6 function for sanitizing a color and adding a hash.
66
+ *
67
+ * @since 1.0.0
68
+ * @access public
69
+ * @param string $color
70
+ * @return string
71
+ */
72
+ function butterbean_maybe_hash_hex_color( $color ) {
73
+
74
+ if ( function_exists( 'maybe_has_hex_color' ) )
75
+ return maybe_has_hex_color( $color );
76
+
77
+ if ( $unhashed = butterbean_sanitize_hex_color_no_hash( $color ) )
78
+ return '#' . $unhashed;
79
+
80
+ return $color;
81
+ }
82
+
83
+ /**
84
+ * Gets Underscore.js templates for managers.
85
+ *
86
+ * @since 1.0.0
87
+ * @param string $slug
88
+ * @return void
89
+ */
90
+ function butterbean_get_manager_template( $slug = '' ) {
91
+ butterbean_get_template( 'manager', $slug );
92
+ }
93
+
94
+ /**
95
+ * Gets Underscore.js templates for navs.
96
+ *
97
+ * @since 1.0.0
98
+ * @param string $slug
99
+ * @return void
100
+ */
101
+ function butterbean_get_nav_template( $slug = '' ) {
102
+ butterbean_get_template( 'nav', $slug );
103
+ }
104
+
105
+ /**
106
+ * Gets Underscore.js templates for sections.
107
+ *
108
+ * @since 1.0.0
109
+ * @param string $slug
110
+ * @return void
111
+ */
112
+ function butterbean_get_section_template( $slug = '' ) {
113
+ butterbean_get_template( 'section', $slug );
114
+ }
115
+
116
+ /**
117
+ * Gets Underscore.js templates for controls.
118
+ *
119
+ * @since 1.0.0
120
+ * @param string $slug
121
+ * @return void
122
+ */
123
+ function butterbean_get_control_template( $slug = '' ) {
124
+ butterbean_get_template( 'control', $slug );
125
+ }
126
+
127
+ /**
128
+ * Helper function for getting Underscore.js templates.
129
+ *
130
+ * @since 1.0.0
131
+ * @param string $name
132
+ * @param string $slug
133
+ * @return void
134
+ */
135
+ function butterbean_get_template( $name, $slug = '' ) {
136
+
137
+ $templates = array();
138
+ $located = '';
139
+
140
+ if ( $slug )
141
+ $templates[] = "{$name}-{$slug}.php";
142
+
143
+ $templates[] = "{$name}.php";
144
+
145
+ // Allow devs to filter the template hierarchy.
146
+ $templates = apply_filters( "butterbean_{$name}_template_hierarchy", $templates, $slug );
147
+
148
+ // Loop through the templates and locate one.
149
+ foreach ( $templates as $template ) {
150
+
151
+ if ( file_exists( butterbean()->tmpl_path . $template ) ) {
152
+ $located = butterbean()->tmpl_path . $template;
153
+ break;
154
+ }
155
+ }
156
+
157
+ // Allow devs to filter the final template.
158
+ $located = apply_filters( "butterbean_{$name}_template", $located, $slug );
159
+
160
+ // Load the template.
161
+ if ( $located )
162
+ require( $located );
163
+ }
admin/butterbean/inc/settings/class-setting-array.php ADDED
@@ -0,0 +1,142 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Setting class for storing multiple post meta values for a single key.
4
+ *
5
+ * @package ButterBean
6
+ * @author Justin Tadlock <justin@justintadlock.com>
7
+ * @copyright Copyright (c) 2015-2016, Justin Tadlock
8
+ * @link https://github.com/justintadlock/butterbean
9
+ * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
10
+ */
11
+
12
+ /**
13
+ * Array setting class.
14
+ *
15
+ * @since 1.0.0
16
+ * @access public
17
+ */
18
+ class ButterBean_Setting_Array extends ButterBean_Setting {
19
+
20
+ /**
21
+ * Gets the value of the setting.
22
+ *
23
+ * @since 1.0.0
24
+ * @access public
25
+ * @return mixed
26
+ */
27
+ public function get_value() {
28
+
29
+ return get_post_meta( $this->manager->post_id, $this->name );
30
+ }
31
+
32
+ /**
33
+ * Sanitizes the value of the setting.
34
+ *
35
+ * @since 1.0.0
36
+ * @access public
37
+ * @param array $value
38
+ * @return array
39
+ */
40
+ public function sanitize( $values ) {
41
+
42
+ $multi_values = $values && ! is_array( $values ) ? explode( ',', $values ) : $values;
43
+
44
+ return $multi_values ? array_map( array( $this, 'map' ), $multi_values ) : array();
45
+ }
46
+
47
+ /**
48
+ * Helper function for sanitizing each value of the array.
49
+ *
50
+ * @since 1.0.0
51
+ * @access public
52
+ * @param mixed $value
53
+ * @return mixed
54
+ */
55
+ public function map( $value ) {
56
+
57
+ return apply_filters( "butterbean_{$this->manager->name}_sanitize_{$this->name}", $value, $this );
58
+ }
59
+
60
+ /**
61
+ * Saves the value of the setting.
62
+ *
63
+ * @since 1.0.0
64
+ * @access public
65
+ * @return void
66
+ */
67
+ public function save() {
68
+
69
+ $old_values = $this->get_value();
70
+ $new_values = $this->get_posted_value();
71
+
72
+ // If there's an array of posted values, set them.
73
+ if ( is_array( $new_values ) )
74
+ $this->set_values( $new_values, $old_values );
75
+
76
+ // If no array of posted values but we have old values, delete them.
77
+ else if ( $old_values )
78
+ $this->delete_values();
79
+ }
80
+
81
+ /**
82
+ * Loops through new and old meta values and updates.
83
+ *
84
+ * @since 1.0.0
85
+ * @access public
86
+ * @param array $new_values
87
+ * @param array $old_values
88
+ * @return void
89
+ */
90
+ public function set_values( $new_values, $old_values ) {
91
+
92
+ foreach ( $new_values as $new ) {
93
+
94
+ if ( ! in_array( $new, $old_values ) )
95
+ $this->add_value( $new );
96
+ }
97
+
98
+ foreach ( $old_values as $old ) {
99
+
100
+ if ( ! in_array( $old, $new_values ) )
101
+ $this->remove_value( $old );
102
+ }
103
+ }
104
+
105
+ /**
106
+ * Deletes old meta values.
107
+ *
108
+ * @since 1.0.0
109
+ * @access public
110
+ * @return void
111
+ */
112
+ public function delete_values() {
113
+
114
+ return delete_post_meta( $this->manager->post_id, $this->name );
115
+ }
116
+
117
+ /**
118
+ * Adds a single meta value.
119
+ *
120
+ * @since 1.0.0
121
+ * @access public
122
+ * @param mixed $value
123
+ * @return bool
124
+ */
125
+ public function add_value( $value ) {
126
+
127
+ return add_post_meta( $this->manager->post_id, $this->name, $value, false );
128
+ }
129
+
130
+ /**
131
+ * Deletes a single meta value.
132
+ *
133
+ * @since 1.0.0
134
+ * @access public
135
+ * @param mixed $value
136
+ * @return bool
137
+ */
138
+ public function remove_value( $value ) {
139
+
140
+ return delete_post_meta( $this->manager->post_id, $this->name, $value );
141
+ }
142
+ }
admin/butterbean/inc/settings/class-setting-date.php ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Date setting class. This is meant to be used in conjunction with the built-in
4
+ * `ButterBean_Date_Control` or a sub-class that passes the appropriate values.
5
+ *
6
+ * @package ButterBean
7
+ * @subpackage Admin
8
+ * @author Justin Tadlock <justin@justintadlock.com>
9
+ * @copyright Copyright (c) 2015-2016, Justin Tadlock
10
+ * @link https://github.com/justintadlock/butterbean
11
+ * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
12
+ */
13
+
14
+ /**
15
+ * Date setting class.
16
+ *
17
+ * @since 1.0.0
18
+ * @access public
19
+ */
20
+ class ButterBean_Setting_Date extends ButterBean_Setting {
21
+
22
+ /**
23
+ * The type of setting.
24
+ *
25
+ * @since 1.0.0
26
+ * @access public
27
+ * @var string
28
+ */
29
+ public $type = 'date';
30
+
31
+ /**
32
+ * Gets the posted value of the setting.
33
+ *
34
+ * @since 1.0.0
35
+ * @access public
36
+ * @return mixed
37
+ */
38
+ public function get_posted_value() {
39
+
40
+ $field_name = $this->get_field_name();
41
+
42
+ // Get the posted date.
43
+ $year = ! empty( $_POST[ "{$field_name}_year" ] ) ? zeroise( absint( $_POST[ "{$field_name}_year" ] ), 4 ) : '';
44
+ $month = ! empty( $_POST[ "{$field_name}_month" ] ) ? zeroise( absint( $_POST[ "{$field_name}_month" ] ), 2 ) : '';
45
+ $day = ! empty( $_POST[ "{$field_name}_day" ] ) ? zeroise( absint( $_POST[ "{$field_name}_day" ] ), 2 ) : '';
46
+
47
+ // Get the posted time.
48
+ $hour = ! empty( $_POST[ "{$field_name}_hour" ] ) ? $this->validate_hour( $_POST[ "{$field_name}_hour" ] ) : '00';
49
+ $minute = ! empty( $_POST[ "{$field_name}_minute" ] ) ? $this->validate_minute( $_POST[ "{$field_name}_minute" ] ) : '00';
50
+ $second = ! empty( $_POST[ "{$field_name}_second" ] ) ? $this->validate_second( $_POST[ "{$field_name}_second" ] ) : '00';
51
+
52
+ $date = "{$year}-{$month}-{$day}";
53
+ $time = "{$hour}:{$minute}:{$second}";
54
+
55
+ if ( $year && $month && $day && wp_checkdate( absint( $month ), absint( $day ), absint( $year ), $date ) )
56
+ return "{$date} {$time}";
57
+
58
+ return '';
59
+ }
60
+
61
+ /**
62
+ * Validates the hour.
63
+ *
64
+ * @since 1.0.0
65
+ * @access public
66
+ * @param int|string $hour
67
+ * @return string
68
+ */
69
+ public function validate_hour( $hour ) {
70
+
71
+ $hour = absint( $hour );
72
+
73
+ return $hour < 0 || $hour > 23 ? zeroise( $hour, 2 ) : '00';
74
+ }
75
+
76
+ /**
77
+ * Validates the minute.
78
+ *
79
+ * @since 1.0.0
80
+ * @access public
81
+ * @param int|string $minute
82
+ * @return string
83
+ */
84
+ public function validate_minute( $minute ) {
85
+
86
+ $minute = absint( $minute );
87
+
88
+ return $minute < 0 || $minute > 59 ? zeroise( $minute, 2 ) : '00';
89
+ }
90
+
91
+ /**
92
+ * Validates the second.
93
+ *
94
+ * @since 1.0.0
95
+ * @access public
96
+ * @param int|string $second
97
+ * @return string
98
+ */
99
+ public function validate_second( $second ) {
100
+
101
+ $second = absint( $second );
102
+
103
+ return $second < 0 || $second > 59 ? zeroise( $second, 2 ) : '00';
104
+ }
105
+ }
admin/butterbean/js/butterbean.js ADDED
@@ -0,0 +1,340 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ( function() {
2
+
3
+ // Bail if we don't have the JSON, which is passed in via `wp_localize_script()`.
4
+ if ( _.isUndefined( butterbean_data ) ) {
5
+ return;
6
+ }
7
+
8
+ /* === Backbone + Underscore === */
9
+
10
+ // Set up a variable to house our views.
11
+ var views = { managers : {}, sections : {}, controls : {} };
12
+
13
+ // Set up a variable to house our templates.
14
+ var templates = { managers : {}, sections : {}, controls : {} };
15
+
16
+ // Nav template.
17
+ var nav_template = wp.template( 'butterbean-nav' );
18
+
19
+ /* === Models === */
20
+
21
+ // Manager model (each manager is housed within a meta box).
22
+ var Manager = Backbone.Model.extend( {
23
+ defaults : {
24
+ name : '',
25
+ type : '',
26
+ sections : {},
27
+ controls : {}
28
+ }
29
+ } );
30
+
31
+ // Section model (each section belongs to a manager).
32
+ var Section = Backbone.Model.extend( {
33
+ defaults : {
34
+ name : '',
35
+ type : '',
36
+ label : '',
37
+ description : '',
38
+ icon : '',
39
+ manager : '',
40
+ active : '',
41
+ selected : false
42
+ }
43
+ } );
44
+
45
+ // Control model (each control belongs to a manager and section).
46
+ var Control = Backbone.Model.extend( {
47
+ defaults : {
48
+ name : '',
49
+ type : '',
50
+ label : '',
51
+ description : '',
52
+ icon : '',
53
+ value : '',
54
+ choices : {},
55
+ attr : '',
56
+ active : '',
57
+ manager : '',
58
+ section : '',
59
+ setting : ''
60
+ }
61
+ } );
62
+
63
+ /* === Collections === */
64
+
65
+ // Collection of sections.
66
+ var Sections = Backbone.Collection.extend( {
67
+ model : Section
68
+ } );
69
+
70
+ /* === Views === */
71
+
72
+ // Manager view. Handles the output of a manager.
73
+ views.managers.default = Backbone.View.extend( {
74
+ tagName : 'div',
75
+ attributes : function() {
76
+ return {
77
+ 'id' : 'butterbean-manager-' + this.model.get( 'name' ),
78
+ 'class' : 'butterbean-manager butterbean-manager-' + this.model.get( 'type' )
79
+ };
80
+ },
81
+ initialize : function() {
82
+
83
+ var type = this.model.get( 'type' );
84
+
85
+ if ( _.isUndefined( templates.managers[ type ] ) ) {
86
+ templates.managers[ type ] = wp.template( 'butterbean-manager-' + type );
87
+ }
88
+
89
+ this.template = templates.managers[ type ];
90
+ },
91
+ render : function() {
92
+ this.el.innerHTML = this.template( this.model.toJSON() );
93
+ return this;
94
+ },
95
+ subview_render : function() {
96
+
97
+ // Create a new section collection.
98
+ var sections = new Sections();
99
+
100
+ // Loop through each section and add it to the collection.
101
+ _.each( this.model.get( 'sections' ), function( data ) {
102
+
103
+ sections.add( new Section( data ) );
104
+ } );
105
+
106
+ // Loop through each manager in the collection and render its view.
107
+ sections.forEach( function( section, i ) {
108
+
109
+ var nav_view = new Nav_View( { model : section } );
110
+ var section_view = new views.sections.default( { model : section } );
111
+
112
+ document.querySelector( '#butterbean-ui-' + section.get( 'manager' ) + ' .butterbean-nav' ).appendChild( nav_view.render().el );
113
+ document.querySelector( '#butterbean-ui-' + section.get( 'manager' ) + ' .butterbean-content' ).appendChild( section_view.render().el );
114
+
115
+ // If the first model, set it to selected.
116
+ section.set( 'selected', 0 === i );
117
+ }, this );
118
+
119
+ // Loop through each control for the manager and render its view.
120
+ _.each( this.model.get( 'controls' ), function( data ) {
121
+
122
+ var control = new Control( data );
123
+
124
+ var callback = _.isUndefined( views.controls[ data.type ] ) ? views.controls.default : views.controls[ data.type ];
125
+
126
+ var view = new callback( { model : control } );
127
+
128
+ document.getElementById( 'butterbean-' + control.get( 'manager' ) + '-section-' + control.get( 'section' ) ).appendChild( view.render().el );
129
+ } );
130
+
131
+ return this;
132
+ }
133
+ } );
134
+
135
+ // Section view. Handles the output of a section.
136
+ views.sections.default = Backbone.View.extend( {
137
+ tagName : 'div',
138
+ attributes : function() {
139
+ return {
140
+ 'id' : 'butterbean-' + this.model.get( 'manager' ) + '-section-' + this.model.get( 'name' ),
141
+ 'class' : 'butterbean-section butterbean-section-' + this.model.get( 'type' ),
142
+ 'aria-hidden' : ! this.model.get( 'selected' )
143
+ };
144
+ },
145
+ initialize : function() {
146
+ this.model.on('change', this.onchange, this);
147
+
148
+ var type = this.model.get( 'type' );
149
+
150
+ if ( _.isUndefined( templates.sections[ type ] ) ) {
151
+ templates.sections[ type ] = wp.template( 'butterbean-section-' + type );
152
+ }
153
+
154
+ this.template = templates.sections[ type ];
155
+ },
156
+ render : function() {
157
+
158
+ // Only render template if model is active.
159
+ if ( this.model.get( 'active' ) ) {
160
+ this.el.innerHTML = this.template( this.model.toJSON() );
161
+ }
162
+
163
+ return this;
164
+ },
165
+ onchange : function() {
166
+
167
+ // Set the view's `aria-hidden` attribute based on whether the model is selected.
168
+ this.el.setAttribute( 'aria-hidden', ! this.model.get( 'selected' ) );
169
+ }
170
+ } );
171
+
172
+ // Nav view.
173
+ var Nav_View = Backbone.View.extend( {
174
+ template : nav_template,
175
+ tagName : 'li',
176
+ attributes : function() {
177
+ return {
178
+ 'aria-selected' : this.model.get( 'selected' )
179
+ };
180
+ },
181
+ initialize : function() {
182
+ this.model.on('change', this.render, this);
183
+ this.model.on('change', this.onchange, this);
184
+ },
185
+ render : function() {
186
+
187
+ // Only render template if model is active.
188
+ if ( this.model.get( 'active' ) ) {
189
+ this.el.innerHTML = this.template( this.model.toJSON() );
190
+ }
191
+
192
+ return this;
193
+ },
194
+ events : {
195
+ 'click a' : 'onselect'
196
+ },
197
+ onchange : function() {
198
+
199
+ // Set the `aria-selected` attibute based on the model selected state.
200
+ this.el.setAttribute( 'aria-selected', this.model.get( 'selected' ) );
201
+ },
202
+ onselect : function( event ) {
203
+ event.preventDefault();
204
+
205
+ // Loop through each of the models in the collection and set them to inactive.
206
+ _.each( this.model.collection.models, function( m ) {
207
+
208
+ m.set( 'selected', false );
209
+ }, this );
210
+
211
+ // Set this view's model to selected.
212
+ this.model.set( 'selected', true );
213
+ }
214
+ } );
215
+
216
+ // Control view. Handles the output of a control.
217
+ views.controls.default = Backbone.View.extend( {
218
+ tagName : 'div',
219
+ attributes : function() {
220
+ return {
221
+ 'id' : 'butterbean-control-' + this.model.get( 'name' ),
222
+ 'class' : 'butterbean-control butterbean-control-' + this.model.get( 'type' )
223
+ };
224
+ },
225
+ initialize : function() {
226
+ var type = this.model.get( 'type' );
227
+
228
+ // Only add a new control template if we have a different control type.
229
+ if ( _.isUndefined( templates.controls[ type ] ) ) {
230
+ templates.controls[ type ] = wp.template( 'butterbean-control-' + type );
231
+ }
232
+
233
+ this.template = templates.controls[ type ];
234
+
235
+ this.ready();
236
+ },
237
+ render : function() {
238
+
239
+ // Only render template if model is active.
240
+ if ( this.model.get( 'active' ) ) {
241
+ this.el.innerHTML = this.template( this.model.toJSON() );
242
+ }
243
+
244
+ return this;
245
+ },
246
+ ready : function() {}
247
+ } );
248
+
249
+ // Palette control view.
250
+ views.controls.palette = views.controls.default.extend( {
251
+ events : {
252
+ 'change input' : 'onselect'
253
+ },
254
+ ready : function() {
255
+
256
+ _.bindAll( this, 'render' );
257
+ this.model.bind( 'change', this.render );
258
+ },
259
+ onselect : function() {
260
+
261
+ var value = document.querySelector( '#' + this.el.id + ' input:checked' ).getAttribute( 'value' );
262
+
263
+ var choices = this.model.get( 'choices' );
264
+
265
+ _.each( choices, function( choice, key ) {
266
+ choice.selected = key === value;
267
+ } );
268
+
269
+ this.model.set( 'choices', choices ).trigger( 'change', this.model );
270
+ }
271
+ } );
272
+
273
+ // Image control view.
274
+ views.controls.image = views.controls.default.extend( {
275
+ events : {
276
+ 'click .butterbean-add-media' : 'showmodal',
277
+ 'click .butterbean-change-media' : 'showmodal',
278
+ 'click .butterbean-remove-media' : 'removemedia'
279
+ },
280
+ ready : function() {
281
+
282
+ _.bindAll( this, 'render' );
283
+ this.model.bind( 'change', this.render );
284
+ },
285
+ showmodal : function() {
286
+
287
+ if ( ! _.isUndefined( this.modal ) ) {
288
+
289
+ this.modal.open();
290
+ return;
291
+ }
292
+
293
+ this.modal = wp.media( {
294
+ frame : 'select',
295
+ multiple : false,
296
+ editing : true,
297
+ title : this.model.get( 'l10n' ).choose,
298
+ library : { type : 'image' },
299
+ button : { text: this.model.get( 'l10n' ).set }
300
+ } );
301
+
302
+ this.modal.on( 'select', function() {
303
+
304
+ var media = this.modal.state().get( 'selection' ).first().toJSON();
305
+
306
+ this.model.set( {
307
+ src : media.sizes.large ? media.sizes.large.url : media.url,
308
+ alt : media.alt,
309
+ value : media.id
310
+ } );
311
+ }, this );
312
+
313
+ this.modal.open();
314
+ },
315
+ removemedia : function() {
316
+
317
+ this.model.set( { src : '', alt : '', value : '' } );
318
+ }
319
+ } );
320
+
321
+ // Loop through each of the managers and render their views.
322
+ _.each( butterbean_data.managers, function( data ) {
323
+
324
+ // Create a new manager model.
325
+ var manager = new Manager( data );
326
+
327
+ // Create a new manager view.
328
+ var view = new views.managers.default( { model : manager } );
329
+
330
+ // Add the `.butterbean-ui` class to the meta box.
331
+ document.getElementById( 'butterbean-ui-' + manager.get( 'name' ) ).className += ' butterbean-ui';
332
+
333
+ // Render the manager view.
334
+ document.querySelector( '#butterbean-ui-' + manager.get( 'name' ) + ' .inside' ).appendChild( view.render().el );
335
+
336
+ // Render the manager subviews.
337
+ view.subview_render();
338
+ } );
339
+
340
+ }() );
admin/butterbean/js/butterbean.min.js ADDED
@@ -0,0 +1 @@
 
1
+ !function(){if(!_.isUndefined(butterbean_data)){var e={managers:{},sections:{},controls:{}},t={managers:{},sections:{},controls:{}},n=wp.template("butterbean-nav"),i=Backbone.Model.extend({defaults:{name:"",type:"",sections:{},controls:{}}}),a=Backbone.Model.extend({defaults:{name:"",type:"",label:"",description:"",icon:"",manager:"",active:"",selected:!1}}),o=Backbone.Model.extend({defaults:{name:"",type:"",label:"",description:"",icon:"",value:"",choices:{},attr:"",active:"",manager:"",section:"",setting:""}}),s=Backbone.Collection.extend({model:a});e.managers.default=Backbone.View.extend({tagName:"div",attributes:function(){return{id:"butterbean-manager-"+this.model.get("name"),"class":"butterbean-manager butterbean-manager-"+this.model.get("type")}},initialize:function(){var e=this.model.get("type");_.isUndefined(t.managers[e])&&(t.managers[e]=wp.template("butterbean-manager-"+e)),this.template=t.managers[e]},render:function(){return this.el.innerHTML=this.template(this.model.toJSON()),this},subview_render:function(){var t=new s;return _.each(this.model.get("sections"),function(e){t.add(new a(e))}),t.forEach(function(t,n){var i=new l({model:t}),a=new e.sections.default({model:t});document.querySelector("#butterbean-ui-"+t.get("manager")+" .butterbean-nav").appendChild(i.render().el),document.querySelector("#butterbean-ui-"+t.get("manager")+" .butterbean-content").appendChild(a.render().el),t.set("selected",0===n)},this),_.each(this.model.get("controls"),function(t){var n=new o(t),i=_.isUndefined(e.controls[t.type])?e.controls.default:e.controls[t.type],a=new i({model:n});document.getElementById("butterbean-"+n.get("manager")+"-section-"+n.get("section")).appendChild(a.render().el)}),this}}),e.sections.default=Backbone.View.extend({tagName:"div",attributes:function(){return{id:"butterbean-"+this.model.get("manager")+"-section-"+this.model.get("name"),"class":"butterbean-section butterbean-section-"+this.model.get("type"),"aria-hidden":!this.model.get("selected")}},initialize:function(){this.model.on("change",this.onchange,this);var e=this.model.get("type");_.isUndefined(t.sections[e])&&(t.sections[e]=wp.template("butterbean-section-"+e)),this.template=t.sections[e]},render:function(){return this.model.get("active")&&(this.el.innerHTML=this.template(this.model.toJSON())),this},onchange:function(){this.el.setAttribute("aria-hidden",!this.model.get("selected"))}});var l=Backbone.View.extend({template:n,tagName:"li",attributes:function(){return{"aria-selected":this.model.get("selected")}},initialize:function(){this.model.on("change",this.render,this),this.model.on("change",this.onchange,this)},render:function(){return this.model.get("active")&&(this.el.innerHTML=this.template(this.model.toJSON())),this},events:{"click a":"onselect"},onchange:function(){this.el.setAttribute("aria-selected",this.model.get("selected"))},onselect:function(e){e.preventDefault(),_.each(this.model.collection.models,function(e){e.set("selected",!1)},this),this.model.set("selected",!0)}});e.controls.default=Backbone.View.extend({tagName:"div",attributes:function(){return{id:"butterbean-control-"+this.model.get("name"),"class":"butterbean-control butterbean-control-"+this.model.get("type")}},initialize:function(){var e=this.model.get("type");_.isUndefined(t.controls[e])&&(t.controls[e]=wp.template("butterbean-control-"+e)),this.template=t.controls[e],this.ready()},render:function(){return this.model.get("active")&&(this.el.innerHTML=this.template(this.model.toJSON())),this},ready:function(){}}),e.controls.palette=e.controls.default.extend({events:{"change input":"onselect"},ready:function(){_.bindAll(this,"render"),this.model.bind("change",this.render)},onselect:function(){var e=document.querySelector("#"+this.el.id+" input:checked").getAttribute("value"),t=this.model.get("choices");_.each(t,function(t,n){t.selected=n===e}),this.model.set("choices",t).trigger("change",this.model)}}),e.controls.image=e.controls.default.extend({events:{"click .butterbean-add-media":"showmodal","click .butterbean-change-media":"showmodal","click .butterbean-remove-media":"removemedia"},ready:function(){_.bindAll(this,"render"),this.model.bind("change",this.render)},showmodal:function(){return _.isUndefined(this.modal)?(this.modal=wp.media({frame:"select",multiple:!1,editing:!0,title:this.model.get("l10n").choose,library:{type:"image"},button:{text:this.model.get("l10n").set}}),this.modal.on("select",function(){var e=this.modal.state().get("selection").first().toJSON();this.model.set({src:e.sizes.large?e.sizes.large.url:e.url,alt:e.alt,value:e.id})},this),this.modal.open(),void 0):(this.modal.open(),void 0)},removemedia:function(){this.model.set({src:"",alt:"",value:""})}}),_.each(butterbean_data.managers,function(t){var n=new i(t),a=new e.managers.default({model:n});document.getElementById("butterbean-ui-"+n.get("name")).className+=" butterbean-ui",document.querySelector("#butterbean-ui-"+n.get("name")+" .inside").appendChild(a.render().el),a.subview_render()})}}();
admin/butterbean/license.md ADDED
@@ -0,0 +1,339 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ GNU GENERAL PUBLIC LICENSE
2
+ Version 2, June 1991
3
+
4
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
5
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6
+ Everyone is permitted to copy and distribute verbatim copies
7
+ of this license document, but changing it is not allowed.
8
+
9
+ Preamble
10
+
11
+ The licenses for most software are designed to take away your
12
+ freedom to share and change it. By contrast, the GNU General Public
13
+ License is intended to guarantee your freedom to share and change free
14
+ software--to make sure the software is free for all its users. This
15
+ General Public License applies to most of the Free Software
16
+ Foundation's software and to any other program whose authors commit to
17
+ using it. (Some other Free Software Foundation software is covered by
18
+ the GNU Lesser General Public License instead.) You can apply it to
19
+ your programs, too.
20
+
21
+ When we speak of free software, we are referring to freedom, not
22
+ price. Our General Public Licenses are designed to make sure that you
23
+ have the freedom to distribute copies of free software (and charge for
24
+ this service if you wish), that you receive source code or can get it
25
+ if you want it, that you can change the software or use pieces of it
26
+ in new free programs; and that you know you can do these things.
27
+
28
+ To protect your rights, we need to make restrictions that forbid
29
+ anyone to deny you these rights or to ask you to surrender the rights.
30
+ These restrictions translate to certain responsibilities for you if you
31
+ distribute copies of the software, or if you modify it.
32
+
33
+ For example, if you distribute copies of such a program, whether
34
+ gratis or for a fee, you must give the recipients all the rights that
35
+ you have. You must make sure that they, too, receive or can get the
36
+ source code. And you must show them these terms so they know their
37
+ rights.
38
+
39
+ We protect your rights with two steps: (1) copyright the software, and
40
+ (2) offer you this license which gives you legal permission to copy,
41
+ distribute and/or modify the software.
42
+
43
+ Also, for each author's protection and ours, we want to make certain
44
+ that everyone understands that there is no warranty for this free
45
+ software. If the software is modified by someone else and passed on, we
46
+ want its recipients to know that what they have is not the original, so
47
+ that any problems introduced by others will not reflect on the original
48
+ authors' reputations.
49
+
50
+ Finally, any free program is threatened constantly by software
51
+ patents. We wish to avoid the danger that redistributors of a free
52
+ program will individually obtain patent licenses, in effect making the
53
+ program proprietary. To prevent this, we have made it clear that any
54
+ patent must be licensed for everyone's free use or not licensed at all.
55
+
56
+ The precise terms and conditions for copying, distribution and
57
+ modification follow.
58
+
59
+ GNU GENERAL PUBLIC LICENSE
60
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61
+
62
+ 0. This License applies to any program or other work which contains
63
+ a notice placed by the copyright holder saying it may be distributed
64
+ under the terms of this General Public License. The "Program", below,
65
+ refers to any such program or work, and a "work based on the Program"
66
+ means either the Program or any derivative work under copyright law:
67
+ that is to say, a work containing the Program or a portion of it,
68
+ either verbatim or with modifications and/or translated into another
69
+ language. (Hereinafter, translation is included without limitation in
70
+ the term "modification".) Each licensee is addressed as "you".
71
+
72
+ Activities other than copying, distribution and modification are not
73
+ covered by this License; they are outside its scope. The act of
74
+ running the Program is not restricted, and the output from the Program
75
+ is covered only if its contents constitute a work based on the
76
+ Program (independent of having been made by running the Program).
77
+ Whether that is true depends on what the Program does.
78
+
79
+ 1. You may copy and distribute verbatim copies of the Program's
80
+ source code as you receive it, in any medium, provided that you
81
+ conspicuously and appropriately publish on each copy an appropriate
82
+ copyright notice and disclaimer of warranty; keep intact all the
83
+ notices that refer to this License and to the absence of any warranty;
84
+ and give any other recipients of the Program a copy of this License
85
+ along with the Program.
86
+
87
+ You may charge a fee for the physical act of transferring a copy, and
88
+ you may at your option offer warranty protection in exchange for a fee.
89
+
90
+ 2. You may modify your copy or copies of the Program or any portion
91
+ of it, thus forming a work based on the Program, and copy and
92
+ distribute such modifications or work under the terms of Section 1
93
+ above, provided that you also meet all of these conditions:
94
+
95
+ a) You must cause the modified files to carry prominent notices
96
+ stating that you changed the files and the date of any change.
97
+
98
+ b) You must cause any work that you distribute or publish, that in
99
+ whole or in part contains or is derived from the Program or any
100
+ part thereof, to be licensed as a whole at no charge to all third
101
+ parties under the terms of this License.
102
+
103
+ c) If the modified program normally reads commands interactively
104
+ when run, you must cause it, when started running for such
105
+ interactive use in the most ordinary way, to print or display an
106
+ announcement including an appropriate copyright notice and a
107
+ notice that there is no warranty (or else, saying that you provide
108
+ a warranty) and that users may redistribute the program under
109
+ these conditions, and telling the user how to view a copy of this
110
+ License. (Exception: if the Program itself is interactive but
111
+ does not normally print such an announcement, your work based on
112
+ the Program is not required to print an announcement.)
113
+
114
+ These requirements apply to the modified work as a whole. If
115
+ identifiable sections of that work are not derived from the Program,
116
+ and can be reasonably considered independent and separate works in
117
+ themselves, then this License, and its terms, do not apply to those
118
+ sections when you distribute them as separate works. But when you
119
+ distribute the same sections as part of a whole which is a work based
120
+ on the Program, the distribution of the whole must be on the terms of
121
+ this License, whose permissions for other licensees extend to the
122
+ entire whole, and thus to each and every part regardless of who wrote it.
123
+
124
+ Thus, it is not the intent of this section to claim rights or contest
125
+ your rights to work written entirely by you; rather, the intent is to
126
+ exercise the right to control the distribution of derivative or
127
+ collective works based on the Program.
128
+
129
+ In addition, mere aggregation of another work not based on the Program
130
+ with the Program (or with a work based on the Program) on a volume of
131
+ a storage or distribution medium does not bring the other work under
132
+ the scope of this License.
133
+
134
+ 3. You may copy and distribute the Program (or a work based on it,
135
+ under Section 2) in object code or executable form under the terms of
136
+ Sections 1 and 2 above provided that you also do one of the following:
137
+
138
+ a) Accompany it with the complete corresponding machine-readable
139
+ source code, which must be distributed under the terms of Sections
140
+ 1 and 2 above on a medium customarily used for software interchange; or,
141
+
142
+ b) Accompany it with a written offer, valid for at least three
143
+ years, to give any third party, for a charge no more than your
144
+ cost of physically performing source distribution, a complete
145
+ machine-readable copy of the corresponding source code, to be
146
+ distributed under the terms of Sections 1 and 2 above on a medium
147
+ customarily used for software interchange; or,
148
+
149
+ c) Accompany it with the information you received as to the offer
150
+ to distribute corresponding source code. (This alternative is
151
+ allowed only for noncommercial distribution and only if you
152
+ received the program in object code or executable form with such
153
+ an offer, in accord with Subsection b above.)
154
+
155
+ The source code for a work means the preferred form of the work for
156
+ making modifications to it. For an executable work, complete source
157
+ code means all the source code for all modules it contains, plus any
158
+ associated interface definition files, plus the scripts used to
159
+ control compilation and installation of the executable. However, as a
160
+ special exception, the source code distributed need not include
161
+ anything that is normally distributed (in either source or binary
162
+ form) with the major components (compiler, kernel, and so on) of the
163
+ operating system on which the executable runs, unless that component
164
+ itself accompanies the executable.
165
+
166
+ If distribution of executable or object code is made by offering
167
+ access to copy from a designated place, then offering equivalent
168
+ access to copy the source code from the same place counts as
169
+ distribution of the source code, even though third parties are not
170
+ compelled to copy the source along with the object code.
171
+
172
+ 4. You may not copy, modify, sublicense, or distribute the Program
173
+ except as expressly provided under this License. Any attempt
174
+ otherwise to copy, modify, sublicense or distribute the Program is
175
+ void, and will automatically terminate your rights under this License.
176
+ However, parties who have received copies, or rights, from you under
177
+ this License will not have their licenses terminated so long as such
178
+ parties remain in full compliance.
179
+
180
+ 5. You are not required to accept this License, since you have not
181
+ signed it. However, nothing else grants you permission to modify or
182
+ distribute the Program or its derivative works. These actions are
183
+ prohibited by law if you do not accept this License. Therefore, by
184
+ modifying or distributing the Program (or any work based on the
185
+ Program), you indicate your acceptance of this License to do so, and
186
+ all its terms and conditions for copying, distributing or modifying
187
+ the Program or works based on it.
188
+
189
+ 6. Each time you redistribute the Program (or any work based on the
190
+ Program), the recipient automatically receives a license from the
191
+ original licensor to copy, distribute or modify the Program subject to
192
+ these terms and conditions. You may not impose any further
193
+ restrictions on the recipients' exercise of the rights granted herein.
194
+ You are not responsible for enforcing compliance by third parties to
195
+ this License.
196
+
197
+ 7. If, as a consequence of a court judgment or allegation of patent
198
+ infringement or for any other reason (not limited to patent issues),
199
+ conditions are imposed on you (whether by court order, agreement or
200
+ otherwise) that contradict the conditions of this License, they do not
201
+ excuse you from the conditions of this License. If you cannot
202
+ distribute so as to satisfy simultaneously your obligations under this
203
+ License and any other pertinent obligations, then as a consequence you
204
+ may not distribute the Program at all. For example, if a patent
205
+ license would not permit royalty-free redistribution of the Program by
206
+ all those who receive copies directly or indirectly through you, then
207
+ the only way you could satisfy both it and this License would be to
208
+ refrain entirely from distribution of the Program.
209
+
210
+ If any portion of this section is held invalid or unenforceable under
211
+ any particular circumstance, the balance of the section is intended to
212
+ apply and the section as a whole is intended to apply in other
213
+ circumstances.
214
+
215
+ It is not the purpose of this section to induce you to infringe any
216
+ patents or other property right claims or to contest validity of any
217
+ such claims; this section has the sole purpose of protecting the
218
+ integrity of the free software distribution system, which is
219
+ implemented by public license practices. Many people have made
220
+ generous contributions to the wide range of software distributed
221
+ through that system in reliance on consistent application of that
222
+ system; it is up to the author/donor to decide if he or she is willing
223
+ to distribute software through any other system and a licensee cannot
224
+ impose that choice.
225
+
226
+ This section is intended to make thoroughly clear what is believed to
227
+ be a consequence of the rest of this License.
228
+
229
+ 8. If the distribution and/or use of the Program is restricted in
230
+ certain countries either by patents or by copyrighted interfaces, the
231
+ original copyright holder who places the Program under this License
232
+ may add an explicit geographical distribution limitation excluding
233
+ those countries, so that distribution is permitted only in or among
234
+ countries not thus excluded. In such case, this License incorporates
235
+ the limitation as if written in the body of this License.
236
+
237
+ 9. The Free Software Foundation may publish revised and/or new versions
238
+ of the General Public License from time to time. Such new versions will
239
+ be similar in spirit to the present version, but may differ in detail to
240
+ address new problems or concerns.
241
+
242
+ Each version is given a distinguishing version number. If the Program
243
+ specifies a version number of this License which applies to it and "any
244
+ later version", you have the option of following the terms and conditions
245
+ either of that version or of any later version published by the Free
246
+ Software Foundation. If the Program does not specify a version number of
247
+ this License, you may choose any version ever published by the Free Software
248
+ Foundation.
249
+
250
+ 10. If you wish to incorporate parts of the Program into other free
251
+ programs whose distribution conditions are different, write to the author
252
+ to ask for permission. For software which is copyrighted by the Free
253
+ Software Foundation, write to the Free Software Foundation; we sometimes
254
+ make exceptions for this. Our decision will be guided by the two goals
255
+ of preserving the free status of all derivatives of our free software and
256
+ of promoting the sharing and reuse of software generally.
257
+
258
+ NO WARRANTY
259
+
260
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261
+ FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262
+ OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263
+ PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264
+ OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266
+ TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267
+ PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268
+ REPAIR OR CORRECTION.
269
+
270
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271
+ WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272
+ REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273
+ INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274
+ OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275
+ TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276
+ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277
+ PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278
+ POSSIBILITY OF SUCH DAMAGES.
279
+
280
+ END OF TERMS AND CONDITIONS
281
+
282
+ How to Apply These Terms to Your New Programs
283
+
284
+ If you develop a new program, and you want it to be of the greatest
285
+ possible use to the public, the best way to achieve this is to make it
286
+ free software which everyone can redistribute and change under these terms.
287
+
288
+ To do so, attach the following notices to the program. It is safest
289
+ to attach them to the start of each source file to most effectively
290
+ convey the exclusion of warranty; and each file should have at least
291
+ the "copyright" line and a pointer to where the full notice is found.
292
+
293
+ <one line to give the program's name and a brief idea of what it does.>
294
+ Copyright (C) <year> <name of author>
295
+
296
+ This program is free software; you can redistribute it and/or modify
297
+ it under the terms of the GNU General Public License as published by
298
+ the Free Software Foundation; either version 2 of the License, or
299
+ (at your option) any later version.
300
+
301
+ This program is distributed in the hope that it will be useful,
302
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
303
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304
+ GNU General Public License for more details.
305
+
306
+ You should have received a copy of the GNU General Public License along
307
+ with this program; if not, write to the Free Software Foundation, Inc.,
308
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
309
+
310
+ Also add information on how to contact you by electronic and paper mail.
311
+
312
+ If the program is interactive, make it output a short notice like this
313
+ when it starts in an interactive mode:
314
+
315
+ Gnomovision version 69, Copyright (C) year name of author
316
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
317
+ This is free software, and you are welcome to redistribute it
318
+ under certain conditions; type `show c' for details.
319
+
320
+ The hypothetical commands `show w' and `show c' should show the appropriate
321
+ parts of the General Public License. Of course, the commands you use may
322
+ be called something other than `show w' and `show c'; they could even be
323
+ mouse-clicks or menu items--whatever suits your program.
324
+
325
+ You should also get your employer (if you work as a programmer) or your
326
+ school, if any, to sign a "copyright disclaimer" for the program, if
327
+ necessary. Here is a sample; alter the names:
328
+
329
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
330
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
331
+
332
+ <signature of Ty Coon>, 1 April 1989
333
+ Ty Coon, President of Vice
334
+
335
+ This General Public License does not permit incorporating your program into
336
+ proprietary programs. If your program is a subroutine library, you may
337
+ consider it more useful to permit linking proprietary applications with the
338
+ library. If this is what you want to do, use the GNU Lesser General
339
+ Public License instead of this License.
admin/butterbean/readme.md ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ButterBean
2
+
3
+ ButterBean is a framework for creating cool post meta boxes.
4
+
5
+ **Development environments only:** Please note that this is a development version of the code. Don't use on a live site yet.
6
+
7
+ ## Usage
8
+
9
+ This is a quick guide. If you're familiar with the WordPress Customization API, you should probably pick this up fairly quickly. A lot of the same concepts are used here.
10
+
11
+ ### Installation
12
+
13
+ Drop the `butterbean` folder into your plugin. That's the simple part.
14
+
15
+ You're only going to want to load this on the edit post screen for whatever post type you're using it on.
16
+
17
+ add_action( 'load-post.php', 'th_load' );
18
+ add_action( 'load-post-new.php', 'th_load' );
19
+
20
+ function th_load() {
21
+
22
+ // Bail if not our post type.
23
+ if ( 'your_post_type' !== get_current_screen()->post_type )
24
+ return;
25
+
26
+ require_once( 'path/to/butterbean/butterbean.php' );
27
+ }
28
+
29
+ ### Registration
30
+
31
+ There's a built-in action hook called `butterbean_register`. You're going to use that to register everything. So, you need to set up a callback function for that.
32
+
33
+ add_action( 'butterbean_register', 'th_register', 10, 2 );
34
+
35
+ function th_register( $butterbean, $post_type ) {
36
+
37
+ // Register managers, sections, controls, and settings here.
38
+ }
39
+
40
+ #### Registering a manager
41
+
42
+ A **manager** is a group of sections, controls, and settings. It's displayed as a single meta box. There can be multiple managers per screen (don't try multiples yet).
43
+
44
+ $butterbean->register_manager(
45
+ 'example',
46
+ array(
47
+ 'label' => esc_html__( 'Example', 'your-textdomain' ),
48
+ 'post_type' => 'your_post_type',
49
+ 'context' => 'normal',
50
+ 'priority' => 'high'
51
+ )
52
+ );
53
+
54
+ $manager = $butterbean->get_manager( 'example' );
55
+
56
+ #### Registering a section
57
+
58
+ A **section** is a group of controls within a manager. They are presented as "tabbed" sections in the UI.
59
+
60
+ $manager->register_section(
61
+ 'section_1',
62
+ array(
63
+ 'label' => esc_html__( 'Section 1', 'your-textdomain' ),
64
+ 'icon' => 'dashicons-admin-generic'
65
+ )
66
+ );
67
+
68
+ #### Registering a control
69
+
70
+ A **control** is essentially a form field. It's the field(s) that a user enters data into. Each control belongs to a section. Each control should also be tied to a setting (below).
71
+
72
+ $manager->register_control(
73
+ 'abc_xyz', // Same as setting name.
74
+ array(
75
+ 'type' => 'text',
76
+ 'section' => 'section_1',
77
+ 'label' => esc_html__( 'Control ABC', 'your-textdomain' ),
78
+ 'attr' => array( 'class' => 'widefat' )
79
+ )
80
+ );
81
+
82
+ #### Registering a setting
83
+
84
+ A **setting** is nothing more than some post metadata and how it gets stored. A setting belongs to a specific control.
85
+
86
+ $manager->register_setting(
87
+ 'abc_xyz', // Same as control name.
88
+ array(
89
+ 'sanitize_callback' => 'wp_filter_nohtml_kses'
90
+ )
91
+ );
92
+
93
+ ## Professional Support
94
+
95
+ If you need professional plugin support from me, the plugin author, you can access the support forums at [Theme Hybrid](http://themehybrid.com/board/topics), which is a professional WordPress help/support site where I handle support for all my plugins and themes for a community of 60,000+ users (and growing).
96
+
97
+ ## Copyright and License
98
+
99
+ Various ideas from different projects have made their way into ButterBean. A few of the projects that had an important impact on the direction of this project are:
100
+
101
+ * Architecturally, the PHP code was modeled after the core WordPress Customization API. - [GPL 2+](http://www.gnu.org/licenses/old-licenses/gpl-2.0.html)
102
+ * The design concept of the default tabbed interface was taken from [WooCommerce](http://www.woothemes.com/woocommerce/). &copy; WooThemes - [GPL 3+](http://www.gnu.org/licenses/gpl.html)
103
+ * Code ideas for the media frame were borrowed from [WP Term Images](https://wordpress.org/plugins/wp-term-images/). &copy; John James Jacoby - [GPL 2+](http://www.gnu.org/licenses/old-licenses/gpl-2.0.html)
104
+
105
+ This project is licensed under the [GNU GPL](http://www.gnu.org/licenses/old-licenses/gpl-2.0.html), version 2 or later.
106
+
107
+ 2015-2016 &copy; [Justin Tadlock](http://justintadlock.com).
admin/butterbean/screenshot-1.png ADDED
Binary file
admin/butterbean/tmpl/control-checkbox.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <label>
2
+ <input type="checkbox" value="true" {{{ data.attr }}} <# if ( data.value ) { #> checked="checked" <# } #> />
3
+
4
+ <# if ( data.label ) { #>
5
+ <span class="butterbean-label">{{ data.label }}</span>
6
+ <# } #>
7
+
8
+ <# if ( data.description ) { #>
9
+ <span class="butterbean-description">{{{ data.description }}}</span>
10
+ <# } #>
11
+ </label>
admin/butterbean/tmpl/control-checkboxes.php ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <# if ( data.label ) { #>
2
+ <span class="butterbean-label">{{ data.label }}</span>
3
+ <# } #>
4
+
5
+ <# if ( data.description ) { #>
6
+ <span class="butterbean-description">{{{ data.description }}}</span>
7
+ <# } #>
8
+
9
+ <ul class="butterbean-checkbox-list">
10
+
11
+ <# _.each( data.choices, function( label, choice ) { #>
12
+
13
+ <li>
14
+ <label>
15
+ <input type="checkbox" value="{{ choice }}" name="{{ data.field_name }}[]" <# if ( -1 !== _.indexOf( data.value, choice ) ) { #> checked="checked" <# } #> />
16
+ {{ label }}
17
+ </label>
18
+ </li>
19
+
20
+ <# } ) #>
21
+
22
+ </ul>
admin/butterbean/tmpl/control-color.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <label>
2
+ <# if ( data.label ) { #>
3
+ <span class="butterbean-label">{{ data.label }}</span>
4
+ <# } #>
5
+
6
+ <# if ( data.description ) { #>
7
+ <span class="butterbean-description">{{{ data.description }}}</span>
8
+ <# } #>
9
+
10
+ <input {{{ data.attr }}} value="<# if ( data.value ) { #>#{{ data.value }}<# } #>" />
11
+ </label>
admin/butterbean/tmpl/control-date.php ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <# if ( data.label ) { #>
2
+ <span class="butterbean-label">{{ data.label }}</span>
3
+ <# } #>
4
+
5
+ <# if ( data.description ) { #>
6
+ <span class="butterbean-description">{{{ data.description }}}</span>
7
+ <# } #>
8
+
9
+ <?php $month = '<label>
10
+ <span class="screen-reader-text">{{ data.month.label }}</span>
11
+ <select name="{{ data.month.name }}">
12
+ <# _.each( data.month.choices, function( choice ) { #>
13
+ <option value="{{ choice.num }}" <# if ( choice.num === data.month.value ) { #> selected="selected" <# } #>>{{ choice.label }}</option>
14
+ <# } ) #>
15
+ </select>
16
+ </label>';
17
+
18
+ $day = '<label>
19
+ <span class="screen-reader-text">{{ data.day.label }}</span>
20
+ <input type="text" name="{{ data.day.name }}" value="{{ data.day.value }}" {{{ data.day.attr }}} />
21
+ </label>';
22
+
23
+ $year = '<label>
24
+ <span class="screen-reader-text">{{ data.year.label }}</span>
25
+ <input type="text" name="{{ data.year.name }}" value="{{ data.year.value }}" {{{ data.year.attr }}} />
26
+ </label>';
27
+
28
+ $hour = '<label>
29
+ <span class="screen-reader-text">{{ data.hour.label }}</span>
30
+ <input type="text" name="{{ data.hour.name }}" value="{{ data.hour.value }}" {{{ data.hour.attr }}} />
31
+ </label>';
32
+
33
+ $minute = '<label>
34
+ <span class="screen-reader-text">{{ data.minute.label }}</span>
35
+ <input type="text" name="{{ data.minute.name }}" value="{{ data.minute.value }}" {{{ data.minute.attr }}} />
36
+ </label>';
37
+
38
+ $second = '<label>
39
+ <span class="screen-reader-text">{{ data.second.label }}</span>
40
+ <input type="text" name="{{ data.second.name }}" value="{{ data.second.value }}" {{{ data.second.attr }}} />
41
+ </label>'; ?>
42
+
43
+ <# if ( data.show_time ) { #>
44
+
45
+ <?php // Translators: 1: month, 2: day, 3: year, 4: hour, 5: minute, 6: second.
46
+ printf( __( '%1$s %2$s, %3$s @ %4$s:%5$s:%6$s', 'butterbean' ), $month, $day, $year, $hour, $minute, $second );
47
+ ?>
48
+
49
+ <# } else { #>
50
+
51
+ <?php // Translators: 1: month, 2: day, 3: year.
52
+ printf( __( '%1$s %2$s, %3$s', 'butterbean' ), $month, $day, $year );
53
+ ?>
54
+
55
+ <# } #>
admin/butterbean/tmpl/control-image.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <# if ( data.label ) { #>
2
+ <span class="butterbean-label">{{ data.label }}</span>
3
+ <# } #>
4
+
5
+ <# if ( data.description ) { #>
6
+ <span class="butterbean-description">{{{ data.description }}}</span>
7
+ <# } #>
8
+
9
+ <input type="hidden" class="butterbean-attachment-id" name="{{ data.field_name }}" value="{{ data.value }}" />
10
+
11
+ <# if ( data.src ) { #>
12
+ <img class="butterbean-img" src="{{ data.src }}" alt="{{ data.alt }}" />
13
+ <# } else { #>
14
+ <div class="butterbean-placeholder">{{ data.l10n.placeholder }}</div>
15
+ <# } #>
16
+
17
+ <p>
18
+ <# if ( data.src ) { #>
19
+ <button type="button" class="button button-secondary butterbean-change-media">{{ data.l10n.change }}</button>
20
+ <button type="button" class="button button-secondary butterbean-remove-media">{{ data.l10n.remove }}</button>
21
+ <# } else { #>
22
+ <button type="button" class="button button-secondary butterbean-add-media">{{ data.l10n.upload }}</button>
23
+ <# } #>
24
+ </p>
admin/butterbean/tmpl/control-multi-avatars.php ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <# if ( data.label ) { #>
2
+ <span class="butterbean-label">{{ data.label }}</span>
3
+ <# } #>
4
+
5
+ <# if ( data.description ) { #>
6
+ <span class="butterbean-description">{{{ data.description }}}</span>
7
+ <# } #>
8
+
9
+ <div class="butterbean-multi-avatars-wrap">
10
+
11
+ <# _.each( data.choices, function( user ) { #>
12
+
13
+ <label>
14
+ <input type="checkbox" value="{{ user.id }}" name="{{ data.field_name }}[]" <# if ( -1 !== _.indexOf( data.value, user.id ) ) { #> checked="checked" <# } #> />
15
+
16
+ <span class="screen-reader-text">{{ user.name }}</span>
17
+
18
+ {{{ user.avatar }}}
19
+ </label>
20
+
21
+ <# } ) #>
22
+
23
+ </div><!-- .butterbean-multi-avatars-wrap -->
admin/butterbean/tmpl/control-palette.php ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <# if ( data.label ) { #>
2
+ <span class="butterbean-label">{{ data.label }}</span>
3
+ <# } #>
4
+
5
+ <# if ( data.description ) { #>
6
+ <span class="butterbean-description">{{{ data.description }}}</span>
7
+ <# } #>
8
+
9
+ <# _.each( data.choices, function( palette, choice ) { #>
10
+ <label aria-selected="{{ palette.selected }}">
11
+ <input type="radio" value="{{ choice }}" name="{{ data.field_name }}" <# if ( palette.selected ) { #> checked="checked" <# } #> />
12
+
13
+ <span class="butterbean-palette-label">{{ palette.label }}</span>
14
+
15
+ <div class="butterbean-palette-block">
16
+
17
+ <# _.each( palette.colors, function( color ) { #>
18
+ <span class="butterbean-palette-color" style="background-color: {{ color }}">&nbsp;</span>
19
+ <# } ) #>
20
+
21
+ </div>
22
+ </label>
23
+ <# } ) #>
admin/butterbean/tmpl/control-parent.php ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <label>
2
+ <# if ( data.label ) { #>
3
+ <span class="butterbean-label">{{ data.label }}</span>
4
+ <# } #>
5
+
6
+ <select name="{{ data.field_name }}" id="{{ data.field_name }}">
7
+
8
+ <# _.each( data.choices, function( choice ) { #>
9
+ <option value="{{ choice.value }}" <# if ( choice.value === data.value ) { #> selected="selected" <# } #>>{{ choice.label }}</option>
10
+ <# } ) #>
11
+
12
+ </select>
13
+
14
+ <# if ( data.description ) { #>
15
+ <span class="butterbean-description">{{{ data.description }}}</span>
16
+ <# } #>
17
+ </label>
admin/butterbean/tmpl/control-radio-image.php ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <# if ( data.label ) { #>
2
+ <span class="butterbean-label">{{ data.label }}</span>
3
+ <# } #>
4
+
5
+ <# if ( data.description ) { #>
6
+ <span class="butterbean-description">{{{ data.description }}}</span>
7
+ <# } #>
8
+
9
+ <# _.each( data.choices, function( args, choice ) { #>
10
+
11
+ <label>
12
+ <input type="radio" value="{{ choice }}" name="{{ data.field_name }}" <# if ( data.value === choice ) { #> checked="checked" <# } #> />
13
+ <span class="screen-reader-text">{{ args.label }}</span>
14
+ <img src="{{ args.url }}" alt="{{ args.label }}" />
15
+ </label>
16
+
17
+ <# } ) #>
admin/butterbean/tmpl/control-radio.php ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <# if ( data.label ) { #>
2
+ <span class="butterbean-label">{{ data.label }}</span>
3
+ <# } #>
4
+
5
+ <# if ( data.description ) { #>
6
+ <span class="butterbean-description">{{{ data.description }}}</span>
7
+ <# } #>
8
+
9
+ <ul class="butterbean-radio-list">
10
+
11
+ <# _.each( data.choices, function( label, choice ) { #>
12
+
13
+ <li>
14
+ <label>
15
+ <input type="radio" value="{{ choice }}" name="{{ data.field_name }}" <# if ( data.value === choice ) { #> checked="checked" <# } #> />
16
+ {{ label }}
17
+ </label>
18
+ </li>
19
+
20
+ <# } ) #>
21
+
22
+ </ul>
admin/butterbean/tmpl/control-select-group.php ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <label>
2
+
3
+ <# if ( data.label ) { #>
4
+ <span class="butterbean-label">{{ data.label }}</span>
5
+ <# } #>
6
+
7
+ <# if ( data.description ) { #>
8
+ <span class="butterbean-description">{{{ data.description }}}</span>
9
+ <# } #>
10
+
11
+ <select {{{ data.attr }}}>
12
+
13
+ <# _.each( data.choices, function( label, choice ) { #>
14
+
15
+ <option value="{{ choice }}" <# if ( choice === data.value ) { #> selected="selected" <# } #>>{{ label }}</option>
16
+
17
+ <# } ) #>
18
+
19
+ <# _.each( data.group, function( group ) { #>
20
+
21
+ <optgroup label="{{ group.label }}">
22
+
23
+ <# _.each( group.choices, function( label, choice ) { #>
24
+
25
+ <option value="{{ choice }}" <# if ( choice === data.value ) { #> selected="selected" <# } #>>{{ label }}</option>
26
+
27
+ <# } ) #>
28
+
29
+ </optgroup>
30
+ <# } ) #>
31
+
32
+ </select>
33
+ </label>
admin/butterbean/tmpl/control-select.php ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <label>
2
+
3
+ <# if ( data.label ) { #>
4
+ <span class="butterbean-label">{{ data.label }}</span>
5
+ <# } #>
6
+
7
+ <# if ( data.description ) { #>
8
+ <span class="butterbean-description">{{{ data.description }}}</span>
9
+ <# } #>
10
+
11
+ <select {{{ data.attr }}}>
12
+
13
+ <# _.each( data.choices, function( label, choice ) { #>
14
+
15
+ <option value="{{ choice }}" <# if ( data.value === choice ) { #> selected="selected" <# } #>>{{ label }}</option>
16
+
17
+ <# } ) #>
18
+
19
+ </select>
20
+ </label>
admin/butterbean/tmpl/control-textarea.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <label>
2
+ <# if ( data.label ) { #>
3
+ <span class="butterbean-label">{{ data.label }}</span>
4
+ <# } #>
5
+
6
+ <textarea {{{ data.attr }}}>{{{ data.value }}}</textarea>
7
+
8
+ <# if ( data.description ) { #>
9
+ <span class="butterbean-description">{{{ data.description }}}</span>
10
+ <# } #>
11
+ </label>
admin/butterbean/tmpl/control.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <label>
2
+ <# if ( data.label ) { #>
3
+ <span class="butterbean-label">{{ data.label }}</span>
4
+ <# } #>
5
+
6
+ <input type="{{ data.type }}" value="{{ data.value }}" {{{ data.attr }}} />
7
+
8
+ <# if ( data.description ) { #>
9
+ <span class="butterbean-description">{{{ data.description }}}</span>
10
+ <# } #>
11
+ </label>
admin/butterbean/tmpl/manager.php ADDED
@@ -0,0 +1,2 @@
 
 
1
+ <ul class="butterbean-nav"></ul>
2
+ <div class="butterbean-content"></div>
admin/butterbean/tmpl/nav.php ADDED
@@ -0,0 +1 @@
 
1
+ <a href="#butterbean-{{ data.manager }}-section-{{ data.name }}"><i class="{{ data.icon }}" aria-hidden="true"></i><span class="label">{{ data.label }}</span></a>
admin/butterbean/tmpl/section.php ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ <# if ( data.description ) { #>
2
+ <span class="butterbean-description description">{{{ data.description }}}</span>
3
+ <# } #>
admin/css/admin.css ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #butterbean-control-taxonomy,
2
+ #butterbean-control-terms {
3
+ display: none;
4
+ }
5
+
6
+ #butterbean-ui-wp_show_posts input,
7
+ #butterbean-ui-wp_show_posts select,
8
+ #butterbean-ui-wp_show_posts textarea {
9
+ padding: 10px;
10
+ width: 100%;
11
+ height: auto;
12
+ }
13
+
14
+ .wpsp-pro {
15
+ background: orange;
16
+ padding: 5px;
17
+ color: white;
18
+ font-size: 80%;
19
+ }
20
+
21
+ #butterbean-ui-wp_show_posts .butterbean-control-color input {
22
+ width: 80px;
23
+ padding: 3px 5px;
24
+ line-height: 16px;
25
+ }
26
+
27
+ #butterbean-ui-wp_show_posts input[type="checkbox"] {
28
+ width: initial;
29
+ height: 16px;
30
+ }
31
+
32
+ .mce-i-wpsp-add-icon:before {
33
+ content: "\f163";
34
+ font: normal 20px/1 'dashicons';
35
+ -webkit-font-smoothing: antialiased;
36
+ -moz-osx-font-smoothing: grayscale;
37
+ }
admin/images/spinner.gif ADDED
Binary file
admin/js/admin-scripts.js ADDED
@@ -0,0 +1,205 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ function wpsp_get_taxonomy( type = 'post' ) {
2
+ var response = jQuery.getJSON({
3
+ type: 'POST',
4
+ url: ajaxurl,
5
+ data: {
6
+ action: 'wpsp_get_taxonomies',
7
+ wpsp_nonce: wpsp_object.nonce,
8
+ post_type: type
9
+ },
10
+ async: false,
11
+ dataType: 'json'
12
+ });
13
+
14
+ return response.responseJSON;
15
+ }
16
+
17
+ function wpsp_get_terms( type = 'post' ) {
18
+ var response = jQuery.getJSON({
19
+ type: 'POST',
20
+ url: ajaxurl,
21
+ data: {
22
+ action: 'wpsp_get_terms',
23
+ wpsp_nonce: wpsp_object.nonce,
24
+ taxonomy: type
25
+ },
26
+ async: false,
27
+ dataType: 'json'
28
+ });
29
+
30
+ return response.responseJSON;
31
+ }
32
+
33
+ function wpsp_get_option( key = 'taxonomy' ) {
34
+ var response = jQuery.getJSON({
35
+ type: 'POST',
36
+ url: ajaxurl,
37
+ data: {
38
+ action: 'wpsp_get_json_option',
39
+ wpsp_nonce: wpsp_object.nonce,
40
+ key: key,
41
+ id: wpsp_object.post_id
42
+ },
43
+ async: false,
44
+ dataType: 'json',
45
+ }).done( function() {
46
+ jQuery( '#butterbean-control-wpsp_taxonomy' ).css( 'display', 'block' );
47
+ jQuery( '#butterbean-control-wpsp_tax_term' ).css( 'display', 'block' );
48
+ });
49
+
50
+ return response.responseJSON;
51
+ }
52
+
53
+ jQuery( document ).ready( function( $ ) {
54
+
55
+ // Populate taxonomy select based on current post type value
56
+ var taxonomies = wpsp_get_taxonomy( $( '#wpsp-post-type' ).val() );
57
+ $('#wpsp-taxonomy').append( $( '<option></option>' ) );
58
+ $.each(taxonomies, function(key, value) {
59
+ $('#wpsp-taxonomy').append( $( '<option></option>' ).attr( 'value', value ).text( value ) );
60
+ });
61
+
62
+ // Set the selected taxonomy value on load
63
+ $( '#wpsp-taxonomy' ).val( wpsp_get_option( 'taxonomy' ) );
64
+
65
+ var terms = wpsp_get_terms( $( '#wpsp-taxonomy' ).val() );
66
+ $('#wpsp-terms').append( $( '<option></option>' ) );
67
+ $.each(terms, function(key, value) {
68
+ $('#wpsp-terms').append( $( '<option></option>' ).attr( 'value', value ).text( value ) );
69
+ });
70
+
71
+ // Set the selected term value on load
72
+ $( '#wpsp-terms' ).val( wpsp_get_option( 'terms' ) );
73
+
74
+ // Hide the terms of taxonomy is empty on load
75
+ if ( '' == $( '#wpsp-taxonomy' ).val() )
76
+ $( '#butterbean-control-wpsp_tax_term' ).hide();
77
+
78
+ // When changing the post type option
79
+ $( '#wpsp-post-type' ).change(function() {
80
+
81
+ $( '#butterbean-control-wpsp_tax_term' ).hide();
82
+
83
+ $( '#wpsp-taxonomy' ).empty();
84
+
85
+ $( '#wpsp-terms' ).empty();
86
+ $( '#wpsp-terms' ).append( $( '<option></option>' ) );
87
+
88
+ var selectValues = wpsp_get_taxonomy( $(this).val(), false );
89
+
90
+ $('#wpsp-taxonomy').append( $( '<option></option>' ) );
91
+ $.each(selectValues, function(key, value) {
92
+ $('#wpsp-taxonomy').append( $( '<option></option>' ).attr( 'value', value ).text( value ) );
93
+ });
94
+ if ( '' == selectValues ) {
95
+ $( '#butterbean-control-wpsp_taxonomy' ).hide();
96
+ } else {
97
+ $( '#butterbean-control-wpsp_taxonomy' ).show();
98
+ }
99
+ });
100
+
101
+ // When changing the taxonomy option
102
+ $( '#wpsp-taxonomy' ).change(function() {
103
+
104
+ $( '#wpsp-terms' ).empty();
105
+ var selectValues = wpsp_get_terms( $(this).val() );
106
+
107
+ $('#wpsp-terms').append( $( '<option></option>' ) );
108
+ $.each(selectValues, function(key, value) {
109
+ $('#wpsp-terms').append( $( '<option></option>' ).attr( 'value', value ).text( value ) );
110
+ });
111
+
112
+ if ( '' == selectValues || ',' == selectValues )
113
+ $( '#butterbean-control-wpsp_tax_term' ).hide();
114
+ else
115
+ $( '#butterbean-control-wpsp_tax_term' ).show();
116
+ });
117
+
118
+ // Dealing with the image options
119
+ if ( ! $( '#wpsp-image' ).is( ':checked' ) ) {
120
+ $( this ).parent().parent().siblings().hide();
121
+ }
122
+
123
+ $( '#wpsp-image' ).change(function() {
124
+ if ( ! this.checked ) {
125
+ $( this ).parent().parent().siblings().hide();
126
+ } else {
127
+ $( this ).parent().parent().siblings().show();
128
+ }
129
+ });
130
+
131
+ // Excerpt or full content
132
+ $( '#wpsp-content-type' ).change(function() {
133
+ if ( 'excerpt' == $( this ).val() ) {
134
+ $( '#butterbean-control-wpsp_excerpt_length' ).show();
135
+ } else {
136
+ $( '#butterbean-control-wpsp_excerpt_length' ).hide();
137
+ }
138
+ });
139
+
140
+ // Author location
141
+ if ( ! $( '#wpsp-include-author' ).is( ':checked' ) ) {
142
+ $( '#butterbean-control-wpsp_author_location' ).hide();
143
+ }
144
+
145
+ $( '#wpsp-include-author' ).change(function() {
146
+ if ( ! this.checked ) {
147
+ $( '#butterbean-control-wpsp_author_location' ).hide();
148
+ } else {
149
+ $( '#butterbean-control-wpsp_author_location' ).show();
150
+ }
151
+ });
152
+
153
+ // Date location
154
+ if ( ! $( '#wpsp-include-date' ).is( ':checked' ) ) {
155
+ $( '#butterbean-control-wpsp_date_location' ).hide();
156
+ }
157
+
158
+ $( '#wpsp-include-date' ).change(function() {
159
+ if ( ! this.checked ) {
160
+ $( '#butterbean-control-wpsp_date_location' ).hide();
161
+ } else {
162
+ $( '#butterbean-control-wpsp_date_location' ).show();
163
+ }
164
+ });
165
+
166
+ // Terms location
167
+ if ( ! $( '#wpsp-include-terms' ).is( ':checked' ) ) {
168
+ $( '#butterbean-control-wpsp_terms_location' ).hide();
169
+ }
170
+
171
+ $( '#wpsp-include-terms' ).change(function() {
172
+ if ( ! this.checked ) {
173
+ $( '#butterbean-control-wpsp_terms_location' ).hide();
174
+ } else {
175
+ $( '#butterbean-control-wpsp_terms_location' ).show();
176
+ }
177
+ });
178
+
179
+ // Dealing with the social options
180
+ $( '#wpsp-social-sharing' ).parent().parent().siblings().hide();
181
+ if ( $( '#wpsp-social-sharing' ).is( ':checked' ) ) {
182
+ $( '#wpsp-social-sharing' ).parent().parent().siblings().show();
183
+ }
184
+
185
+ $( '#wpsp-social-sharing' ).change(function() {
186
+ if ( ! this.checked ) {
187
+ $( this ).parent().parent().siblings().hide();
188
+ } else {
189
+ $( this ).parent().parent().siblings().show();
190
+ }
191
+ });
192
+
193
+ // Pagination
194
+ if ( ! $( '#wpsp-pagination' ).is( ':checked' ) ) {
195
+ $( '#butterbean-control-wpsp_ajax_pagination' ).hide();
196
+ }
197
+
198
+ $( '#wpsp-pagination' ).change(function() {
199
+ if ( ! this.checked ) {
200
+ $( '#butterbean-control-wpsp_ajax_pagination' ).hide();
201
+ } else {
202
+ $( '#butterbean-control-wpsp_ajax_pagination' ).show();
203
+ }
204
+ });
205
+ });
admin/js/button.js ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ function wpsp_get_post_lists() {
2
+ var response = jQuery.getJSON({
3
+ type: 'POST',
4
+ url: ajaxurl,
5
+ data: {
6
+ action: 'wpsp_get_post_lists',
7
+ wpsp_nonce: wpsp_nonce
8
+ },
9
+ async: false,
10
+ dataType: 'json'
11
+ });
12
+
13
+ return response.responseJSON;
14
+ }
15
+
16
+ console.log(wpsp_get_post_lists());
17
+
18
+ (function() {
19
+ tinymce.PluginManager.add('wpsp_shortcode_button', function( editor, url ) {
20
+ editor.addButton( 'wpsp_shortcode_button', {
21
+ title: wpsp_add_posts,
22
+ icon: 'wpsp-add-icon',
23
+ onclick: function() {
24
+ editor.windowManager.open( {
25
+ width: 300,
26
+ height: 75,
27
+ title: wpsp_add_posts,
28
+ body: [
29
+ {
30
+ type: 'listbox',
31
+ name: 'wpsp_add_posts',
32
+ label: false,
33
+ 'values': wpsp_get_post_lists(),
34
+ }],
35
+ onsubmit: function( e ) {
36
+ editor.insertContent( '[wp_show_posts id="' + e.data.wpsp_add_posts + '"]');
37
+ }
38
+ });
39
+ }
40
+ });
41
+ });
42
+ })();
admin/metabox.php ADDED
@@ -0,0 +1,868 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ if ( ! function_exists( 'wpsp_remove_metaboxes' ) ) :
3
+ /**
4
+ * Remove all metaboxes from our WP Show Posts post type
5
+ * @since 0.1
6
+ */
7
+ add_action('add_meta_boxes', 'wpsp_remove_metaboxes', 99, 2);
8
+ function wpsp_remove_metaboxes( $post_type, $post ){
9
+
10
+ // If we're not in the wp_show_posts post type, bail.
11
+ if( ! in_array( $post_type, array( 'wp_show_posts' ) ) )
12
+ return false;
13
+
14
+ global $wp_meta_boxes;
15
+
16
+ // Don't remove the below
17
+ $exceptions = array(
18
+ 'submitdiv',
19
+ 'butterbean-ui-wp_show_posts',
20
+ 'wpsp_shortcode_metabox'
21
+ );
22
+
23
+ // Loop through all our metaboxes
24
+ if( ! empty( $wp_meta_boxes ) ) :
25
+ foreach( $wp_meta_boxes as $page => $page_boxes ) :
26
+ if ( ! empty( $page_boxes ) ) :
27
+ foreach( $page_boxes as $context => $box_context ) :
28
+ if( ! empty( $box_context ) ) :
29
+ foreach( $box_context as $box_type ) :
30
+ if( ! empty( $box_type ) ) :
31
+ foreach( $box_type as $id => $box ) :
32
+ /** Check to see if the meta box should be removed... */
33
+ if( ! in_array( $id, $exceptions ) ) :
34
+ remove_meta_box( $id, $page, $context );
35
+ endif;
36
+ endforeach;
37
+ endif;
38
+ endforeach;
39
+ endif;
40
+ endforeach;
41
+ endif;
42
+ endforeach;
43
+ endif;
44
+ }
45
+ endif;
46
+
47
+ if ( ! function_exists( 'wpsp_get_post_types' ) ) :
48
+ /**
49
+ * List of all our post types exluding our own
50
+ * @since 0.1
51
+ */
52
+ function wpsp_get_post_types()
53
+ {
54
+ $post_types = get_post_types( array( 'public' => true ) );
55
+ $types = array();
56
+ foreach ( $post_types as $type ) {
57
+ if ( 'wp_show_posts' !== $type )
58
+ $types[ $type ] = $type;
59
+ }
60
+
61
+ return $types;
62
+ }
63
+ endif;
64
+
65
+ if ( ! function_exists( 'wpsp_load_butterbean' ) ) :
66
+ /**
67
+ * Load butterbean inside our post type
68
+ * @since 0.1
69
+ */
70
+ add_action( 'load-post.php', 'wpsp_load_butterbean' );
71
+ add_action( 'load-post-new.php', 'wpsp_load_butterbean' );
72
+ function wpsp_load_butterbean() {
73
+
74
+ // Bail if not our post type.
75
+ if ( 'wp_show_posts' !== get_current_screen()->post_type )
76
+ return;
77
+
78
+ require_once( trailingslashit( dirname( __FILE__ ) ) . '/butterbean/butterbean.php' );
79
+ }
80
+ endif;
81
+
82
+ if ( ! function_exists( 'wpsp_register' ) ) :
83
+ /**
84
+ * Create all of our metabox options
85
+ * @since 0.1
86
+ */
87
+ add_action( 'butterbean_register', 'wpsp_register', 10, 2 );
88
+ function wpsp_register( $butterbean, $post_type ) {
89
+
90
+ $defaults = wpsp_get_defaults();
91
+
92
+ // Register managers, sections, controls, and settings here.
93
+ $butterbean->register_manager(
94
+ 'wp_show_posts',
95
+ array(
96
+ 'label' => esc_html__( 'WP Show Posts', 'wp-show-posts' ),
97
+ 'post_type' => 'wp_show_posts',
98
+ 'context' => 'normal',
99
+ 'priority' => 'high'
100
+ )
101
+ );
102
+
103
+ $manager = $butterbean->get_manager( 'wp_show_posts' );
104
+
105
+ $manager->register_section(
106
+ 'wpsp_posts',
107
+ array(
108
+ 'label' => esc_html__( 'Posts', 'wp-show-posts' ),
109
+ 'icon' => 'dashicons-admin-post'
110
+ )
111
+ );
112
+
113
+ $manager->register_control(
114
+ 'wpsp_post_type', // Same as setting name.
115
+ array(
116
+ 'type' => 'select',
117
+ 'section' => 'wpsp_posts',
118
+ 'label' => esc_html__( 'Post Type', 'wp-show-posts' ),
119
+ 'choices' => wpsp_get_post_types(),
120
+ 'attr' => array( 'id' => 'wpsp-post-type' )
121
+ )
122
+ );
123
+
124
+ $manager->register_setting(
125
+ 'wpsp_post_type', // Same as control name.
126
+ array(
127
+ 'sanitize_callback' => 'sanitize_text_field',
128
+ 'default' => $defaults[ 'wpsp_post_type' ] ? $defaults[ 'wpsp_post_type' ] : ''
129
+ )
130
+ );
131
+
132
+ $manager->register_control(
133
+ 'wpsp_taxonomy', // Same as setting name.
134
+ array(
135
+ 'type' => 'select',
136
+ 'section' => 'wpsp_posts',
137
+ 'label' => esc_html__( 'Taxonomy', 'wp-show-posts' ),
138
+ 'choices' => array(),
139
+ 'attr' => array( 'id' => 'wpsp-taxonomy' )
140
+ )
141
+ );
142
+
143
+ $manager->register_setting(
144
+ 'wpsp_taxonomy', // Same as control name.
145
+ array(
146
+ 'sanitize_callback' => 'sanitize_text_field',
147
+ 'default' => $defaults[ 'wpsp_taxonomy' ] ? $defaults[ 'wpsp_taxonomy' ] : ''
148
+ )
149
+ );
150
+
151
+ $manager->register_control(
152
+ 'wpsp_tax_term', // Same as setting name.
153
+ array(
154
+ 'type' => 'select',
155
+ 'section' => 'wpsp_posts',
156
+ 'label' => esc_html__( 'Terms', 'wp-show-posts' ),
157
+ 'choices' => array(),
158
+ 'attr' => array( 'id' => 'wpsp-terms' )
159
+ )
160
+ );
161
+
162
+ $manager->register_setting(
163
+ 'wpsp_tax_term', // Same as control name.
164
+ array(
165
+ 'sanitize_callback' => 'sanitize_text_field',
166
+ 'default' => $defaults[ 'wpsp_tax_term' ] ? $defaults[ 'wpsp_tax_term' ] : ''
167
+ )
168
+ );
169
+
170
+ $manager->register_control(
171
+ 'wpsp_posts_per_page', // Same as setting name.
172
+ array(
173
+ 'type' => 'number',
174
+ 'section' => 'wpsp_posts',
175
+ 'label' => esc_html__( 'Posts per page', 'wp-show-posts' )
176
+ )
177
+ );
178
+
179
+ $manager->register_setting(
180
+ 'wpsp_posts_per_page', // Same as control name.
181
+ array(
182
+ 'sanitize_callback' => 'wpsp_sanitize_intval',
183
+ 'default' => $defaults[ 'wpsp_posts_per_page' ] ? $defaults[ 'wpsp_posts_per_page' ] : 10
184
+ )
185
+ );
186
+
187
+ $manager->register_control(
188
+ 'wpsp_pagination',
189
+ array(
190
+ 'type' => 'checkbox',
191
+ 'section' => 'wpsp_posts',
192
+ 'label' => __( 'Pagination','wp-show-posts-pro' ),
193
+ 'description' => __( 'Pagination should only be used if your posts are the only thing in the content area to prevent duplicate content issues.','wp-show-posts' ),
194
+ 'attr' => array( 'id' => 'wpsp-pagination' )
195
+ )
196
+ );
197
+
198
+ $manager->register_setting(
199
+ 'wpsp_pagination',
200
+ array(
201
+ 'sanitize_callback' => 'butterbean_validate_boolean',
202
+ 'default' => $defaults[ 'wpsp_pagination' ] ? $defaults[ 'wpsp_pagination' ] : false
203
+ )
204
+ );
205
+
206
+ $manager->register_section(
207
+ 'wpsp_columns',
208
+ array(
209
+ 'label' => esc_html__( 'Columns', 'wp-show-posts' ),
210
+ 'icon' => 'dashicons-grid-view'
211
+ )
212
+ );
213
+
214
+ $manager->register_control(
215
+ 'wpsp_columns', // Same as setting name.
216
+ array(
217
+ 'type' => 'select',
218
+ 'section' => 'wpsp_columns',
219
+ 'label' => esc_html__( 'Columns', 'wp-show-posts' ),
220
+ 'choices' => array(
221
+ 'col-12' => '1',
222
+ 'col-6' => '2',
223
+ 'col-4' => '3',
224
+ 'col-3' => '4',
225
+ 'col-20' => '5'
226
+ ),
227
+ 'attr' => array( 'id' => 'wpsp-columns' )
228
+ )
229
+ );
230
+
231
+ $manager->register_setting(
232
+ 'wpsp_columns', // Same as control name.
233
+ array(
234
+ 'sanitize_callback' => 'sanitize_text_field',
235
+ 'default' => $defaults[ 'wpsp_columns' ] ? $defaults[ 'wpsp_columns' ] : '12'
236
+ )
237
+ );
238
+
239
+ $manager->register_control(
240
+ 'wpsp_columns_gutter', // Same as setting name.
241
+ array(
242
+ 'type' => 'text',
243
+ 'section' => 'wpsp_columns',
244
+ 'label' => esc_html__( 'Columns gutter', 'wp-show-posts' ),
245
+ 'attr' => array( 'class' => 'widefat' ),
246
+ )
247
+ );
248
+
249
+ $manager->register_setting(
250
+ 'wpsp_columns_gutter', // Same as control name.
251
+ array(
252
+ 'sanitize_callback' => 'sanitize_text_field',
253
+ 'default' => $defaults[ 'wpsp_columns_gutter' ] ? $defaults[ 'wpsp_columns_gutter' ] : ''
254
+ )
255
+ );
256
+
257
+ $manager->register_section(
258
+ 'wpsp_images',
259
+ array(
260
+ 'label' => esc_html__( 'Images', 'wp-show-posts' ),
261
+ 'icon' => 'dashicons-format-image'
262
+ )
263
+ );
264
+
265
+ $manager->register_control(
266
+ 'wpsp_image',
267
+ array(
268
+ 'type' => 'checkbox',
269
+ 'section' => 'wpsp_images',
270
+ 'label' => __( 'Images','wp-show-posts' ),
271
+ 'attr' => array( 'id' => 'wpsp-image' )
272
+ )
273
+ );
274
+
275
+ $manager->register_setting(
276
+ 'wpsp_image',
277
+ array(
278
+ 'sanitize_callback' => 'butterbean_validate_boolean',
279
+ 'default' => $defaults[ 'wpsp_image' ]
280
+ )
281
+ );
282
+
283
+ $manager->register_control(
284
+ 'wpsp_image_width', // Same as setting name.
285
+ array(
286
+ 'type' => 'number',
287
+ 'section' => 'wpsp_images',
288
+ 'label' => esc_html__( 'Image width (px)', 'wp-show-posts' ),
289
+ 'attr' => array( 'id' => 'wpsp-image-width' )
290
+ )
291
+ );
292
+
293
+ $manager->register_setting(
294
+ 'wpsp_image_width', // Same as control name.
295
+ array(
296
+ 'sanitize_callback' => 'wpsp_sanitize_absint',
297
+ 'default' => $defaults[ 'wpsp_image_width' ] ? $defaults[ 'wpsp_image_width' ] : ''
298
+ )
299
+ );
300
+
301
+ $manager->register_control(
302
+ 'wpsp_image_height', // Same as setting name.
303
+ array(
304
+ 'type' => 'number',
305
+ 'section' => 'wpsp_images',
306
+ 'label' => esc_html__( 'Image height (px)', 'wp-show-posts' ),
307
+ 'attr' => array( 'id' => 'wpsp-image-height' )
308
+ )
309
+ );
310
+
311
+ $manager->register_setting(
312
+ 'wpsp_image_height', // Same as control name.
313
+ array(
314
+ 'sanitize_callback' => 'wpsp_sanitize_absint',
315
+ 'default' => $defaults[ 'wpsp_image_height' ] ? $defaults[ 'wpsp_image_height' ] : ''
316
+ )
317
+ );
318
+
319
+ $manager->register_control(
320
+ 'wpsp_image_alignment', // Same as setting name.
321
+ array(
322
+ 'type' => 'select',
323
+ 'section' => 'wpsp_images',
324
+ 'label' => esc_html__( 'Image alignment', 'wp-show-posts' ),
325
+ 'choices' => array(
326
+ 'left' => __( 'Left','wp-show-posts' ),
327
+ 'center' => __( 'Center','wp-show-posts' ),
328
+ 'right' => __( 'Right','wp-show-posts' )
329
+ ),
330
+ 'attr' => array( 'id' => 'wpsp-image-alignment' )
331
+ )
332
+ );
333
+
334
+ $manager->register_setting(
335
+ 'wpsp_image_alignment', // Same as control name.
336
+ array(
337
+ 'sanitize_callback' => 'sanitize_text_field',
338
+ 'default' => $defaults[ 'wpsp_image_alignment' ] ? $defaults[ 'wpsp_image_alignment' ] : ''
339
+ )
340
+ );
341
+
342
+ $manager->register_control(
343
+ 'wpsp_image_location', // Same as setting name.
344
+ array(
345
+ 'type' => 'select',
346
+ 'section' => 'wpsp_images',
347
+ 'label' => esc_html__( 'Image location', 'wp-show-posts' ),
348
+ 'choices' => array(
349
+ 'below-title' => __( 'Below title','wp-show-posts' ),
350
+ 'above-title' => __( 'Above title','wp-show-posts' )
351
+ ),
352
+ 'attr' => array( 'id' => 'wpsp-image-location' )
353
+ )
354
+ );
355
+
356
+ $manager->register_setting(
357
+ 'wpsp_image_location', // Same as control name.
358
+ array(
359
+ 'sanitize_callback' => 'sanitize_text_field',
360
+ 'default' => $defaults[ 'wpsp_image_location' ] ? $defaults[ 'wpsp_image_location' ] : ''
361
+ )
362
+ );
363
+
364
+ $manager->register_section(
365
+ 'wpsp_content',
366
+ array(
367
+ 'label' => esc_html__( 'Content', 'wp-show-posts' ),
368
+ 'icon' => 'dashicons-editor-alignleft'
369
+ )
370
+ );
371
+
372
+ $manager->register_control(
373
+ 'wpsp_content_type', // Same as setting name.
374
+ array(
375
+ 'type' => 'select',
376
+ 'section' => 'wpsp_content',
377
+ 'label' => esc_html__( 'Content type', 'wp-show-posts' ),
378
+ 'choices' => array(
379
+ 'excerpt' => __( 'Excerpt','wp-show-posts' ),
380
+ 'full' => __( 'Full','wp-show-posts' ),
381
+ 'none' => __( 'None','wp-show-posts' )
382
+ ),
383
+ 'attr' => array( 'id' => 'wpsp-content-type' )
384
+ )
385
+ );
386
+
387
+ $manager->register_setting(
388
+ 'wpsp_content_type', // Same as control name.
389
+ array(
390
+ 'sanitize_callback' => 'sanitize_text_field',
391
+ 'default' => $defaults[ 'wpsp_content_type' ] ? $defaults[ 'wpsp_content_type' ] : ''
392
+ )
393
+ );
394
+
395
+ $manager->register_control(
396
+ 'wpsp_excerpt_length', // Same as setting name.
397
+ array(
398
+ 'type' => 'number',
399
+ 'section' => 'wpsp_content',
400
+ 'label' => esc_html__( 'Excerpt length (words)', 'wp-show-posts' ),
401
+ 'attr' => array( 'id' => 'wpsp-excerpt-length' )
402
+ )
403
+ );
404
+
405
+ $manager->register_setting(
406
+ 'wpsp_excerpt_length', // Same as control name.
407
+ array(
408
+ 'sanitize_callback' => 'wpsp_sanitize_absint',
409
+ 'default' => $defaults[ 'wpsp_excerpt_length' ] ? $defaults[ 'wpsp_excerpt_length' ] : ''
410
+ )
411
+ );
412
+
413
+ $manager->register_control(
414
+ 'wpsp_include_title',
415
+ array(
416
+ 'type' => 'checkbox',
417
+ 'section' => 'wpsp_content',
418
+ 'label' => __( 'Include title','wp-show-posts-pro' ),
419
+ 'attr' => array( 'id' => 'wpsp-include-title' )
420
+ )
421
+ );
422
+
423
+ $manager->register_setting(
424
+ 'wpsp_include_title',
425
+ array(
426
+ 'sanitize_callback' => 'butterbean_validate_boolean',
427
+ 'default' => true
428
+ )
429
+ );
430
+
431
+ $manager->register_control(
432
+ 'wpsp_read_more_text', // Same as setting name.
433
+ array(
434
+ 'type' => 'text',
435
+ 'section' => 'wpsp_content',
436
+ 'label' => esc_html__( 'Read more text', 'wp-show-posts' )
437
+ )
438
+ );
439
+
440
+ $manager->register_setting(
441
+ 'wpsp_read_more_text', // Same as control name.
442
+ array(
443
+ 'sanitize_callback' => 'wp_kses_post',
444
+ 'default' => $defaults[ 'wpsp_read_more_text' ] ? $defaults[ 'wpsp_read_more_text' ] : ''
445
+ )
446
+ );
447
+
448
+ $manager->register_section(
449
+ 'wpsp_post_meta',
450
+ array(
451
+ 'label' => esc_html__( 'Meta', 'wp-show-posts' ),
452
+ 'icon' => 'dashicons-editor-ul'
453
+ )
454
+ );
455
+
456
+ $manager->register_control(
457
+ 'wpsp_include_author',
458
+ array(
459
+ 'type' => 'checkbox',
460
+ 'section' => 'wpsp_post_meta',
461
+ 'label' => __( 'Include author','wp-show-posts-pro' ),
462
+ 'attr' => array( 'id' => 'wpsp-include-author' )
463
+ )
464
+ );
465
+
466
+ $manager->register_setting(
467
+ 'wpsp_include_author',
468
+ array(
469
+ 'sanitize_callback' => 'butterbean_validate_boolean',
470
+ 'default' => $defaults[ 'wpsp_include_author' ] ? $defaults[ 'wpsp_include_author' ] : false
471
+ )
472
+ );
473
+
474
+ $manager->register_control(
475
+ 'wpsp_author_location', // Same as setting name.
476
+ array(
477
+ 'type' => 'select',
478
+ 'section' => 'wpsp_post_meta',
479
+ 'label' => esc_html__( 'Author location', 'wp-show-posts' ),
480
+ 'choices' => array(
481
+ 'below-title' => __( 'Below title','wp-show-posts' ),
482
+ 'below-post' => __( 'Below post','wp-show-posts' )
483
+ ),
484
+ 'attr' => array( 'id' => 'wpsp-author-location' )
485
+ )
486
+ );
487
+
488
+ $manager->register_setting(
489
+ 'wpsp_author_location', // Same as control name.
490
+ array(
491
+ 'sanitize_callback' => 'sanitize_text_field',
492
+ 'default' => $defaults[ 'wpsp_author_location' ] ? $defaults[ 'wpsp_author_location' ] : ''
493
+ )
494
+ );
495
+
496
+ $manager->register_control(
497
+ 'wpsp_include_date',
498
+ array(
499
+ 'type' => 'checkbox',
500
+ 'section' => 'wpsp_post_meta',
501
+ 'label' => __( 'Include date','wp-show-posts-pro' ),
502
+ 'attr' => array( 'id' => 'wpsp-include-date' )
503
+ )
504
+ );
505
+
506
+ $manager->register_setting(
507
+ 'wpsp_include_date',
508
+ array(
509
+ 'sanitize_callback' => 'butterbean_validate_boolean',
510
+ 'default' => $defaults[ 'wpsp_include_date' ] ? $defaults[ 'wpsp_include_date' ] : false
511
+ )
512
+ );
513
+
514
+ $manager->register_control(
515
+ 'wpsp_date_location', // Same as setting name.
516
+ array(
517
+ 'type' => 'select',
518
+ 'section' => 'wpsp_post_meta',
519
+ 'label' => esc_html__( 'Date location', 'wp-show-posts' ),
520
+ 'choices' => array(
521
+ 'below-title' => __( 'Below title','wp-show-posts' ),
522
+ 'below-post' => __( 'Below post','wp-show-posts' )
523
+ ),
524
+ 'attr' => array( 'id' => 'wpsp-date-location' )
525
+ )
526
+ );
527
+
528
+ $manager->register_setting(
529
+ 'wpsp_date_location', // Same as control name.
530
+ array(
531
+ 'sanitize_callback' => 'sanitize_text_field',
532
+ 'default' => $defaults[ 'wpsp_date_location' ] ? $defaults[ 'wpsp_date_location' ] : ''
533
+ )
534
+ );
535
+
536
+ $manager->register_control(
537
+ 'wpsp_include_terms',
538
+ array(
539
+ 'type' => 'checkbox',
540
+ 'section' => 'wpsp_post_meta',
541
+ 'label' => __( 'Include terms','wp-show-posts-pro' ),
542
+ 'attr' => array( 'id' => 'wpsp-include-terms' )
543
+ )
544
+ );
545
+
546
+ $manager->register_setting(
547
+ 'wpsp_include_terms',
548
+ array(
549
+ 'sanitize_callback' => 'butterbean_validate_boolean',
550
+ 'default' => $defaults[ 'wpsp_include_terms' ] ? $defaults[ 'wpsp_include_terms' ] : false
551
+ )
552
+ );
553
+
554
+ $manager->register_control(
555
+ 'wpsp_terms_location', // Same as setting name.
556
+ array(
557
+ 'type' => 'select',
558
+ 'section' => 'wpsp_post_meta',
559
+ 'label' => esc_html__( 'Terms location', 'wp-show-posts' ),
560
+ 'choices' => array(
561
+ 'below-title' => __( 'Below title','wp-show-posts' ),
562
+ 'below-post' => __( 'Below post','wp-show-posts' )
563
+ ),
564
+ 'attr' => array( 'id' => 'wpsp-terms-location' )
565
+ )
566
+ );
567
+
568
+ $manager->register_setting(
569
+ 'wpsp_terms_location', // Same as control name.
570
+ array(
571
+ 'sanitize_callback' => 'sanitize_text_field',
572
+ 'default' => $defaults[ 'wpsp_terms_location' ] ? $defaults[ 'wpsp_terms_location' ] : ''
573
+ )
574
+ );
575
+
576
+ $manager->register_section(
577
+ 'wpsp_query_args',
578
+ array(
579
+ 'label' => esc_html__( 'More query args', 'wp-show-posts' ),
580
+ 'icon' => 'dashicons-admin-generic',
581
+ 'priority' => 999
582
+ )
583
+ );
584
+
585
+ $manager->register_control(
586
+ 'wpsp_author', // Same as setting name.
587
+ array(
588
+ 'type' => 'number',
589
+ 'section' => 'wpsp_query_args',
590
+ 'label' => esc_html__( 'Author ID', 'wp-show-posts' )
591
+ )
592
+ );
593
+
594
+ $manager->register_setting(
595
+ 'wpsp_author', // Same as control name.
596
+ array(
597
+ 'sanitize_callback' => 'wpsp_sanitize_absint',
598
+ 'default' => $defaults[ 'wpsp_author' ] ? $defaults[ 'wpsp_author' ] : ''
599
+ )
600
+ );
601
+
602
+ $manager->register_control(
603
+ 'wpsp_exclude_current',
604
+ array(
605
+ 'type' => 'checkbox',
606
+ 'section' => 'wpsp_query_args',
607
+ 'label' => __( 'Exclude current','wp-show-posts-pro' ),
608
+ 'attr' => array( 'id' => 'wpsp-exclude-current' )
609
+ )
610
+ );
611
+
612
+ $manager->register_setting(
613
+ 'wpsp_exclude_current',
614
+ array(
615
+ 'sanitize_callback' => 'butterbean_validate_boolean',
616
+ 'default' => $defaults[ 'wpsp_exclude_current' ] ? $defaults[ 'wpsp_exclude_current' ] : false
617
+ )
618
+ );
619
+
620
+ $manager->register_control(
621
+ 'wpsp_post_id', // Same as setting name.
622
+ array(
623
+ 'type' => 'number',
624
+ 'section' => 'wpsp_query_args',
625
+ 'label' => esc_html__( 'Post ID', 'wp-show-posts' )
626
+ )
627
+ );
628
+
629
+ $manager->register_setting(
630
+ 'wpsp_post_id', // Same as control name.
631
+ array(
632
+ 'sanitize_callback' => 'wpsp_sanitize_intval',
633
+ 'default' => $defaults[ 'wpsp_post_id' ] ? $defaults[ 'wpsp_post_id' ] : ''
634
+ )
635
+ );
636
+
637
+ $manager->register_control(
638
+ 'wpsp_ignore_sticky_posts',
639
+ array(
640
+ 'type' => 'checkbox',
641
+ 'section' => 'wpsp_query_args',
642
+ 'label' => __( 'Ignore sticky posts','wp-show-posts-pro' ),
643
+ 'attr' => array( 'id' => 'wpsp-ignore-sticky-posts' )
644
+ )
645
+ );
646
+
647
+ $manager->register_setting(
648
+ 'wpsp_ignore_sticky_posts',
649
+ array(
650
+ 'sanitize_callback' => 'butterbean_validate_boolean',
651
+ 'default' => $defaults[ 'wpsp_ignore_sticky_posts' ] ? $defaults[ 'wpsp_ignore_sticky_posts' ] : false
652
+ )
653
+ );
654
+
655
+ $manager->register_control(
656
+ 'wpsp_offset', // Same as setting name.
657
+ array(
658
+ 'type' => 'number',
659
+ 'section' => 'wpsp_query_args',
660
+ 'label' => esc_html__( 'Offset', 'wp-show-posts' )
661
+ )
662
+ );
663
+
664
+ $manager->register_setting(
665
+ 'wpsp_offset', // Same as control name.
666
+ array(
667
+ 'sanitize_callback' => 'wpsp_sanitize_absint',
668
+ 'default' => $defaults[ 'wpsp_offset' ] ? $defaults[ 'wpsp_offset' ] : ''
669
+ )
670
+ );
671
+
672
+ $manager->register_control(
673
+ 'wpsp_order', // Same as setting name.
674
+ array(
675
+ 'type' => 'select',
676
+ 'section' => 'wpsp_query_args',
677
+ 'label' => esc_html__( 'Order', 'wp-show-posts' ),
678
+ 'choices' => array(
679
+ 'DESC' => __( 'Descending','wp-show-posts' ),
680
+ 'ASC' => __( 'Ascending','wp-show-posts' )
681
+ ),
682
+ 'attr' => array( 'id' => 'wpsp-order' )
683
+ )
684
+ );
685
+
686
+ $manager->register_setting(
687
+ 'wpsp_order', // Same as control name.
688
+ array(
689
+ 'sanitize_callback' => 'sanitize_text_field',
690
+ 'default' => $defaults[ 'wpsp_order' ] ? $defaults[ 'wpsp_order' ] : 'DESC'
691
+ )
692
+ );
693
+
694
+ $manager->register_control(
695
+ 'wpsp_orderby', // Same as setting name.
696
+ array(
697
+ 'type' => 'select',
698
+ 'section' => 'wpsp_query_args',
699
+ 'label' => esc_html__( 'Order by', 'wp-show-posts' ),
700
+ 'choices' => array(
701
+ 'none' => __( 'No order','wp-show-posts' ),
702
+ 'ID' => __( 'ID','wp-show-posts' ),
703
+ 'author' => __( 'Author','wp-show-posts' ),
704
+ 'title' => __( 'Title','wp-show-posts' ),
705
+ 'name' => __( 'Slug','wp-show-posts' ),
706
+ 'type' => __( 'Post type','wp-show-posts' ),
707
+ 'date' => __( 'Date','wp-show-posts' ),
708
+ 'modified' => __( 'Modified','wp-show-posts' ),
709
+ 'parent' => __( 'Parent','wp-show-posts' ),
710
+ 'rand' => __( 'Random','wp-show-posts' ),
711
+ 'comment_count' => __( 'Comment count','wp-show-posts' )
712
+ ),
713
+ 'attr' => array( 'id' => 'wpsp-orderby' )
714
+ )
715
+ );
716
+
717
+ $manager->register_setting(
718
+ 'wpsp_orderby', // Same as control name.
719
+ array(
720
+ 'sanitize_callback' => 'sanitize_text_field',
721
+ 'default' => $defaults[ 'wpsp_orderby' ] ? $defaults[ 'wpsp_orderby' ] : 'date'
722
+ )
723
+ );
724
+
725
+ $manager->register_control(
726
+ 'wpsp_post_status', // Same as setting name.
727
+ array(
728
+ 'type' => 'select',
729
+ 'section' => 'wpsp_query_args',
730
+ 'label' => esc_html__( 'Status', 'wp-show-posts' ),
731
+ 'choices' => array(
732
+ 'publish' => __( 'Published','wp-show-posts' ),
733
+ 'pending' => __( 'Pending','wp-show-posts' ),
734
+ 'draft' => __( 'Draft','wp-show-posts' ),
735
+ 'auto-draft' => __( 'Auto draft','wp-show-posts' ),
736
+ 'future' => __( 'Future','wp-show-posts' ),
737
+ 'private' => __( 'Private','wp-show-posts' ),
738
+ 'inherit' => __( 'Inherit','wp-show-posts' ),
739
+ 'trash' => __( 'Trash','wp-show-posts' ),
740
+ 'any' => __( 'Any','wp-show-posts' )
741
+ ),
742
+ 'attr' => array( 'id' => 'wpsp-post-status' )
743
+ )
744
+ );
745
+
746
+ $manager->register_setting(
747
+ 'wpsp_post_status', // Same as control name.
748
+ array(
749
+ 'sanitize_callback' => 'sanitize_text_field',
750
+ 'default' => $defaults[ 'wpsp_post_status' ] ? $defaults[ 'wpsp_post_status' ] : 'publish'
751
+ )
752
+ );
753
+
754
+ $manager->register_control(
755
+ 'wpsp_meta_key', // Same as setting name.
756
+ array(
757
+ 'type' => 'text',
758
+ 'section' => 'wpsp_query_args',
759
+ 'label' => esc_html__( 'Meta key', 'wp-show-posts' )
760
+ )
761
+ );
762
+
763
+ $manager->register_setting(
764
+ 'wpsp_meta_key', // Same as control name.
765
+ array(
766
+ 'sanitize_callback' => 'sanitize_text_field',
767
+ 'default' => $defaults[ 'wpsp_meta_key' ] ? $defaults[ 'wpsp_meta_key' ] : ''
768
+ )
769
+ );
770
+
771
+ $manager->register_control(
772
+ 'wpsp_meta_value', // Same as setting name.
773
+ array(
774
+ 'type' => 'text',
775
+ 'section' => 'wpsp_query_args',
776
+ 'label' => esc_html__( 'Meta value', 'wp-show-posts' )
777
+ )
778
+ );
779
+
780
+ $manager->register_setting(
781
+ 'wpsp_meta_value', // Same as control name.
782
+ array(
783
+ 'sanitize_callback' => 'sanitize_text_field',
784
+ 'default' => $defaults[ 'wpsp_meta_value' ] ? $defaults[ 'wpsp_meta_value' ] : ''
785
+ )
786
+ );
787
+
788
+ $manager->register_control(
789
+ 'wpsp_tax_operator', // Same as setting name.
790
+ array(
791
+ 'type' => 'select',
792
+ 'section' => 'wpsp_query_args',
793
+ 'label' => esc_html__( 'Tax operator', 'wp-show-posts' ),
794
+ 'choices' => array(
795
+ 'IN' => 'IN',
796
+ 'NOT IN' => 'NOT IN',
797
+ 'AND' => 'AND',
798
+ 'EXISTS' => 'EXISTS',
799
+ 'NOT EXISTS' => 'NOT EXISTS'
800
+ ),
801
+ 'attr' => array( 'id' => 'wpsp-tax-operator' )
802
+ )
803
+ );
804
+
805
+ $manager->register_setting(
806
+ 'wpsp_tax_operator', // Same as control name.
807
+ array(
808
+ 'sanitize_callback' => 'sanitize_text_field',
809
+ 'default' => $defaults[ 'wpsp_tax_operator' ] ? $defaults[ 'wpsp_tax_operator' ] : 'IN'
810
+ )
811
+ );
812
+ }
813
+ endif;
814
+
815
+ if ( ! function_exists( 'wpsp_sanitize_intval' ) ) :
816
+ /**
817
+ * Sanitize our value so it has to be a positive integer
818
+ * @since 0.1
819
+ */
820
+ function wpsp_sanitize_intval( $input ) {
821
+ if ( '' == $input )
822
+ return $input;
823
+
824
+ return intval( $input );
825
+ }
826
+ endif;
827
+
828
+ if ( ! function_exists( 'wpsp_sanitize_absint' ) ) :
829
+ /**
830
+ * Sanitize our value so it can be a negative or positive integer
831
+ * @since 0.1
832
+ */
833
+ function wpsp_sanitize_absint( $input ) {
834
+ if ( '' == $input )
835
+ return $input;
836
+
837
+ return absint( $input );
838
+ }
839
+ endif;
840
+
841
+ if ( ! function_exists( 'wpsp_add_meta_boxes' ) ) :
842
+ /**
843
+ * Add our usage metabox
844
+ * @since 0.1
845
+ */
846
+ add_action( 'add_meta_boxes_wp_show_posts', 'wpsp_add_meta_boxes' );
847
+ function wpsp_add_meta_boxes( $post ){
848
+ add_meta_box( 'wpsp_shortcode', __( 'Usage', 'wp-show-posts' ), 'wpsp_shortcode_metabox', 'wp_show_posts', 'side', 'low' );
849
+ }
850
+ endif;
851
+
852
+ if ( ! function_exists( 'wpsp_shortcode_metabox' ) ) :
853
+ /**
854
+ * Meta box display callback.
855
+ *
856
+ * @param WP_Post $post Current post object.
857
+ * @since 0.1
858
+ */
859
+ function wpsp_shortcode_metabox( $post ) {
860
+ ?>
861
+ <h4 style="margin-bottom:5px;"><?php _e( 'Shortcode','wp-show-posts' ); ?></h4>
862
+ <input type="text" class="widefat" value='[wp_show_posts id="<?php echo $post->ID;?>"]' readonly />
863
+
864
+ <h4 style="margin-bottom:5px;"><?php _e( 'Function','wp-show-posts' ); ?></h4>
865
+ <input type="text" class="widefat" value='<?php echo esc_attr( "<?php if ( function_exists( 'wpsp_display' ) ) wpsp_display( " . $post->ID . " ); ?>" ); ?>' readonly />
866
+ <?php
867
+ }
868
+ endif;
admin/post-type.php ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ if ( ! function_exists( 'wp_show_posts_type' ) ) :
3
+ /**
4
+ * Create our WP Show Posts post type
5
+ * @since 0.1
6
+ */
7
+ add_action( 'init', 'wp_show_posts_type', 0 );
8
+ function wp_show_posts_type() {
9
+
10
+ $labels = array(
11
+ 'name' => _x( 'Post Lists', 'Post Type General Name', 'wp-show-posts' ),
12
+ 'singular_name' => _x( 'Post List', 'Post Type Singular Name', 'wp-show-posts' ),
13
+ 'menu_name' => __( 'WP Show Posts', 'wp-show-posts' ),
14
+ 'name_admin_bar' => __( 'WP Show Posts', 'wp-show-posts' ),
15
+ 'archives' => __( 'List Archives', 'wp-show-posts' ),
16
+ 'parent_item_colon' => __( 'Parent List:', 'wp-show-posts' ),
17
+ 'all_items' => __( 'All Lists', 'wp-show-posts' ),
18
+ 'add_new_item' => __( 'Add New List', 'wp-show-posts' ),
19
+ 'add_new' => __( 'Add New', 'wp-show-posts' ),
20
+ 'new_item' => __( 'New List', 'wp-show-posts' ),
21
+ 'edit_item' => __( 'Edit List', 'wp-show-posts' ),
22
+ 'update_item' => __( 'Update List', 'wp-show-posts' ),
23
+ 'view_item' => __( 'View List', 'wp-show-posts' ),
24
+ 'search_items' => __( 'Search List', 'wp-show-posts' ),
25
+ 'not_found' => __( 'Not found', 'wp-show-posts' ),
26
+ 'not_found_in_trash' => __( 'Not found in Trash', 'wp-show-posts' ),
27
+ 'featured_image' => __( 'Featured Image', 'wp-show-posts' ),
28
+ 'set_featured_image' => __( 'Set featured image', 'wp-show-posts' ),
29
+ 'remove_featured_image' => __( 'Remove featured image', 'wp-show-posts' ),
30
+ 'use_featured_image' => __( 'Use as featured image', 'wp-show-posts' ),
31
+ 'insert_into_item' => __( 'Insert into list', 'wp-show-posts' ),
32
+ 'uploaded_to_this_item' => __( 'Uploaded to this list', 'wp-show-posts' ),
33
+ 'items_list' => __( 'Items list', 'wp-show-posts' ),
34
+ 'items_list_navigation' => __( 'Items list navigation', 'wp-show-posts' ),
35
+ 'filter_items_list' => __( 'Filter items list', 'wp-show-posts' ),
36
+ );
37
+ $args = array(
38
+ 'label' => __( 'Post List', 'wp-show-posts' ),
39
+ 'labels' => $labels,
40
+ 'supports' => array( 'title' ),
41
+ 'hierarchical' => false,
42
+ 'public' => true,
43
+ 'show_ui' => true,
44
+ 'show_in_menu' => true,
45
+ 'menu_position' => 5,
46
+ 'show_in_admin_bar' => false,
47
+ 'show_in_nav_menus' => false,
48
+ 'can_export' => true,
49
+ 'has_archive' => false,
50
+ 'exclude_from_search' => true,
51
+ 'publicly_queryable' => false,
52
+ 'capability_type' => 'page',
53
+ );
54
+ register_post_type( 'wp_show_posts', $args );
55
+
56
+ }
57
+ endif;
css/wp-show-posts-min.css ADDED
@@ -0,0 +1 @@
 
1
+ body .wp-show-posts a{box-shadow:0 0 0 transparent}.wp-show-posts-entry-title a{text-decoration:none}a.wp-show-posts-read-more,a.wp-show-posts-read-more:visited{display:inline-block;margin:0 0 1em;padding:8px 15px;border:2px solid #222;border-radius:3px;color:#222;font-size:0.8em}a.wp-show-posts-read-more:hover,a.wp-show-posts-read-more:focus{border:2px solid transparent;color:#fff;background:#222}.wp-show-posts-image{margin-bottom:1em}.wp-show-posts-image.wpsp-image-left{float:left;margin-right:1.5em}.wp-show-posts-image.wpsp-image-right{float:right;margin-left:1.5em}.wp-show-posts-image.wpsp-image-center{display:block;text-align:center}.wp-show-posts-image img{margin:0 !important;vertical-align:bottom}.wp-show-posts-entry-header{margin:0 0 1em;padding:0}.wp-show-posts .wp-show-posts-entry-title{font-size:30px;line-height:35px;margin:0}.wp-show-posts-updated{display:none}.wp-show-posts-entry-summary,.wp-show-posts-entry-content{margin-bottom:1em}.wp-show-posts-entry-meta{font-size:0.8em;color:rgba( 0,0,0,0.5 )}.wp-show-posts-meta a,.wp-show-posts-meta a:visited{color:rgba( 0,0,0,0.5 )}.stack-wp-show-posts-byline,.stack-wp-show-posts-posted-on{display:block}.wp-show-posts-entry-meta-below-post{margin-bottom:1em}.wp-show-posts-columns:not(.wp-show-posts-masonry){display:flex;flex-wrap:wrap}.wp-show-posts-columns .wp-show-posts-single:not(.wp-show-posts-masonry-block){display:flex;flex-direction:row}.wp-show-posts-columns .wp-show-posts-single:not(.wp-show-posts-masonry-block) .wp-show-posts-image img{flex:0 0 auto;object-fit:scale-down}.wpsp-clear{clear:both;display:block;overflow:hidden;visibility:hidden;width:0;height:0}.wp-show-posts:not(.wp-show-posts-columns) .wp-show-posts-single:not(:last-child){margin-bottom:2em}.wp-show-posts-columns .wp-show-posts-entry-title{font-size:25px}.wp-show-posts-columns .wp-show-posts-single.col-md-4 .wp-show-posts-entry-title,.wp-show-posts-columns .wp-show-posts-single.col-md-3 .wp-show-posts-entry-title,.wp-show-posts-columns .wp-show-posts-single.col-md-20 .wp-show-posts-entry-title{font-size:20px}@media (min-width: 768px){.col-1,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-10,.col-11,.col-12,.col-20{float:left}.col-1{width:8.333333%}.col-2{width:16.666667%}.col-3{width:25%}.col-4{width:33.333%}.col-5{width:41.666667%}.col-6{width:50%}.col-7{width:58.333333%}.col-8{width:66.666667%}.col-9{width:75%}.col-10{width:83.333333%}.col-11{width:91.666667%}.col-12{width:100%}.col-20{width:20%}}.wp-show-posts-inner *:last-child{margin-bottom:0}.screen-reader-text{clip:rect(1px, 1px, 1px, 1px);position:absolute !important}.screen-reader-text:hover,.screen-reader-text:active,.screen-reader-text:focus{background-color:#f1f1f1;border-radius:3px;box-shadow:0 0 2px 2px rgba(0, 0, 0, 0.6);clip:auto !important;color:#21759b;display:block;font-size:14px;font-weight:bold;height:auto;left:5px;line-height:normal;padding:15px 23px 14px;text-decoration:none;top:5px;width:auto;z-index:100000}.wpsp-clearfix:after{content:".";display:block;overflow:hidden;visibility:hidden;font-size:0;line-height:0;width:0;height:0}
css/wp-show-posts.css ADDED
@@ -0,0 +1,216 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ body .wp-show-posts a {
2
+ box-shadow: 0 0 0 transparent;
3
+ }
4
+
5
+ .wp-show-posts-entry-title a {
6
+ text-decoration: none;
7
+ }
8
+
9
+ a.wp-show-posts-read-more,
10
+ a.wp-show-posts-read-more:visited {
11
+ display: inline-block;
12
+ margin: 0 0 1em;
13
+ padding: 8px 15px;
14
+ border: 2px solid #222;
15
+ border-radius: 3px;
16
+ color: #222;
17
+ font-size: 0.8em;
18
+ }
19
+
20
+ a.wp-show-posts-read-more:hover,
21
+ a.wp-show-posts-read-more:focus {
22
+ border: 2px solid transparent;
23
+ color: #fff;
24
+ background: #222;
25
+ }
26
+
27
+ .wp-show-posts-image {
28
+ margin-bottom: 1em;
29
+ }
30
+
31
+ .wp-show-posts-image.wpsp-image-left {
32
+ float: left;
33
+ margin-right: 1.5em;
34
+ }
35
+
36
+ .wp-show-posts-image.wpsp-image-right {
37
+ float: right;
38
+ margin-left: 1.5em;
39
+ }
40
+
41
+ .wp-show-posts-image.wpsp-image-center {
42
+ display: block;
43
+ text-align: center;
44
+ }
45
+
46
+ .wp-show-posts-image img {
47
+ margin: 0 !important;
48
+ vertical-align: bottom;
49
+ }
50
+
51
+ .wp-show-posts-entry-header {
52
+ margin: 0 0 1em;
53
+ padding: 0;
54
+ }
55
+
56
+ .wp-show-posts .wp-show-posts-entry-title {
57
+ font-size: 30px;
58
+ line-height: 35px;
59
+ margin: 0;
60
+ }
61
+
62
+ .wp-show-posts-updated {
63
+ display: none;
64
+ }
65
+
66
+ .wp-show-posts-entry-summary,
67
+ .wp-show-posts-entry-content {
68
+ margin-bottom: 1em;
69
+ }
70
+
71
+ .wp-show-posts-entry-meta {
72
+ font-size: 0.8em;
73
+ color: rgba( 0,0,0,0.5 );
74
+ }
75
+
76
+ .wp-show-posts-meta a,
77
+ .wp-show-posts-meta a:visited {
78
+ color: rgba( 0,0,0,0.5 );
79
+ }
80
+
81
+ .stack-wp-show-posts-byline,
82
+ .stack-wp-show-posts-posted-on {
83
+ display: block;
84
+ }
85
+
86
+ .wp-show-posts-entry-meta-below-post {
87
+ margin-bottom: 1em;
88
+ }
89
+
90
+ .wp-show-posts-columns:not(.wp-show-posts-masonry) {
91
+ display: flex;
92
+ flex-wrap: wrap;
93
+ }
94
+ .wp-show-posts-columns .wp-show-posts-single:not(.wp-show-posts-masonry-block) {
95
+ display: flex;
96
+ flex-direction: row;
97
+ }
98
+
99
+ .wp-show-posts-columns .wp-show-posts-single:not(.wp-show-posts-masonry-block) .wp-show-posts-image img {
100
+ flex: 0 0 auto;
101
+ object-fit: scale-down;
102
+ }
103
+
104
+ .wpsp-clear {
105
+ clear: both;
106
+ display: block;
107
+ overflow: hidden;
108
+ visibility: hidden;
109
+ width: 0;
110
+ height: 0;
111
+ }
112
+
113
+ .wp-show-posts:not(.wp-show-posts-columns) .wp-show-posts-single:not(:last-child) {
114
+ margin-bottom: 2em;
115
+ }
116
+
117
+ .wp-show-posts-columns .wp-show-posts-entry-title {
118
+ font-size: 25px;
119
+ }
120
+
121
+ .wp-show-posts-columns .wp-show-posts-single.col-md-4 .wp-show-posts-entry-title,
122
+ .wp-show-posts-columns .wp-show-posts-single.col-md-3 .wp-show-posts-entry-title,
123
+ .wp-show-posts-columns .wp-show-posts-single.col-md-20 .wp-show-posts-entry-title {
124
+ font-size: 20px;
125
+ }
126
+
127
+ /* Bootstrap grid system */
128
+ @media (min-width: 768px) {
129
+ .col-1, .col-2, .col-3, .col-4, .col-5, .col-6, .col-7, .col-8, .col-9, .col-10, .col-11, .col-12, .col-20 {
130
+ float: left;
131
+ }
132
+ .col-1 {
133
+ width: 8.333333%;
134
+ }
135
+ .col-2 {
136
+ width: 16.666667%;
137
+ }
138
+ .col-3 {
139
+ width: 25%;
140
+ }
141
+ .col-4 {
142
+ width: 33.333%;
143
+ }
144
+ .col-5 {
145
+ width: 41.666667%;
146
+ }
147
+ .col-6 {
148
+ width: 50%;
149
+ }
150
+ .col-7 {
151
+ width: 58.333333%;
152
+ }
153
+ .col-8 {
154
+ width: 66.666667%;
155
+ }
156
+ .col-9 {
157
+ width: 75%;
158
+ }
159
+ .col-10 {
160
+ width: 83.333333%;
161
+ }
162
+ .col-11 {
163
+ width: 91.666667%;
164
+ }
165
+ .col-12 {
166
+ width: 100%;
167
+ }
168
+
169
+ /* 5 columns */
170
+ .col-20 {
171
+ width: 20%;
172
+ }
173
+ }
174
+
175
+ /* Spacing */
176
+ .wp-show-posts-inner *:last-child {
177
+ margin-bottom: 0;
178
+ }
179
+
180
+ /* Screen reader text*/
181
+ .screen-reader-text {
182
+ clip: rect(1px, 1px, 1px, 1px);
183
+ position: absolute !important;
184
+ }
185
+
186
+ .screen-reader-text:hover,
187
+ .screen-reader-text:active,
188
+ .screen-reader-text:focus {
189
+ background-color: #f1f1f1;
190
+ border-radius: 3px;
191
+ box-shadow: 0 0 2px 2px rgba(0, 0, 0, 0.6);
192
+ clip: auto !important;
193
+ color: #21759b;
194
+ display: block;
195
+ font-size: 14px;
196
+ font-weight: bold;
197
+ height: auto;
198
+ left: 5px;
199
+ line-height: normal;
200
+ padding: 15px 23px 14px;
201
+ text-decoration: none;
202
+ top: 5px;
203
+ width: auto;
204
+ z-index: 100000; /* Above WP toolbar */
205
+ }
206
+
207
+ .wpsp-clearfix:after {
208
+ content: ".";
209
+ display: block;
210
+ overflow: hidden;
211
+ visibility: hidden;
212
+ font-size: 0;
213
+ line-height: 0;
214
+ width: 0;
215
+ height: 0;
216
+ }
inc/defaults.php ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ if ( ! function_exists( 'wpsp_get_defaults' ) ) :
3
+ /**
4
+ * Set all of our defaults
5
+ * @since 0.1
6
+ */
7
+ function wpsp_get_defaults()
8
+ {
9
+ $defaults = array(
10
+ 'wpsp_author' => '',
11
+ 'wpsp_author_location' => 'below-post',
12
+ 'wpsp_columns' => 'col-6',
13
+ 'wpsp_columns_gutter' => '2em',
14
+ 'wpsp_content_type' => 'excerpt',
15
+ 'wpsp_date_location' => 'below-title',
16
+ 'wpsp_exclude_current' => false,
17
+ 'wpsp_excerpt_length' => 30,
18
+ 'wpsp_post_id' => '',
19
+ 'wpsp_ignore_sticky_posts' => false,
20
+ 'wpsp_image' => true,
21
+ 'wpsp_image_alignment' => 'center',
22
+ 'wpsp_image_height' => '',
23
+ 'wpsp_image_location' => 'below-title',
24
+ 'wpsp_image_lightbox' => false,
25
+ 'wpsp_image_gallery' => false,
26
+ 'wpsp_image_overlay_color' => '',
27
+ 'wpsp_image_overlay_icon' => '',
28
+ 'wpsp_image_width' => '',
29
+ 'wpsp_include_title' => true,
30
+ 'wpsp_include_terms' => false,
31
+ 'wpsp_include_author' => false,
32
+ 'wpsp_include_date' => true,
33
+ 'wpsp_inner_wrapper' => 'article',
34
+ 'wpsp_inner_wrapper_class' => '',
35
+ 'wpsp_inner_wrapper_style' => '',
36
+ 'wpsp_itemtype' => 'CreativeWork',
37
+ 'wpsp_meta_key' => '',
38
+ 'wpsp_meta_value' => '',
39
+ 'wpsp_offset' => 0,
40
+ 'wpsp_order' => 'DESC',
41
+ 'wpsp_orderby' => 'date',
42
+ 'wpsp_pagination' => false,
43
+ 'wpsp_post_meta_bottom_style' => 'stack',
44
+ 'wpsp_post_meta_top_style' => 'inline',
45
+ 'wpsp_post_status' => 'publish',
46
+ 'wpsp_post_type' => 'post',
47
+ 'wpsp_posts_per_page' => 10,
48
+ 'wpsp_read_more_text' => '',
49
+ 'wpsp_tax_operator' => 'IN',
50
+ 'wpsp_tax_term' => '',
51
+ 'wpsp_taxonomy' => 'category',
52
+ 'wpsp_terms_location' => 'below-post',
53
+ 'wpsp_wrapper' => 'section',
54
+ 'wpsp_wrapper_class' => '',
55
+ 'wpsp_wrapper_id' => false,
56
+ 'wpsp_wrapper_style' => '',
57
+ 'wpsp_ajax_pagination' => false,
58
+ 'wpsp_masonry' => false,
59
+ 'wpsp_social_sharing' => false,
60
+ 'wpsp_social_sharing_alignment' => 'right',
61
+ 'wpsp_twitter' => false,
62
+ 'wpsp_facebook' => false,
63
+ 'wpsp_googleplus' => false,
64
+ 'wpsp_pinterest' => false,
65
+ 'wpsp_love' => false,
66
+ 'wpsp_featured_post' => false,
67
+ 'wpsp_image_hover_effect' => '',
68
+ 'wpsp_read_more_style' => 'hollow',
69
+ 'wpsp_read_more_color' => 'black',
70
+ 'wpsp_border' => '',
71
+ 'wpsp_padding' => ''
72
+ );
73
+
74
+ return apply_filters( 'wpsp_defaults', $defaults );
75
+ }
76
+ endif;
inc/functions.php ADDED
@@ -0,0 +1,417 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ if ( ! function_exists( 'wpsp_excerpt' ) ) :
3
+ /**
4
+ * Create our own excerpt function
5
+ * @since 0.1
6
+ */
7
+ function wpsp_excerpt( $text = '', $excerpt_length = 55 ) {
8
+ if ( ! $excerpt_length )
9
+ return;
10
+
11
+ $raw_excerpt = $text;
12
+ if ( '' == $text ) {
13
+ $text = get_the_content('');
14
+
15
+ $text = strip_shortcodes( $text );
16
+
17
+ /** This filter is documented in wp-includes/post-template.php */
18
+ $text = apply_filters( 'the_content', $text );
19
+ $text = str_replace(']]>', ']]&gt;', $text);
20
+
21
+ /**
22
+ * Filter the string in the "more" link displayed after a trimmed excerpt.
23
+ *
24
+ * @since 2.9.0
25
+ *
26
+ * @param string $more_string The string shown within the more link.
27
+ */
28
+ $excerpt_more = apply_filters( 'wpsp_excerpt_more', '...' );
29
+ $text = wp_trim_words( $text, $excerpt_length, $excerpt_more );
30
+ }
31
+ /**
32
+ * Filter the trimmed excerpt string.
33
+ *
34
+ * @since 2.8.0
35
+ *
36
+ * @param string $text The trimmed text.
37
+ * @param string $raw_excerpt The text prior to trimming.
38
+ */
39
+ echo apply_filters( 'wp_trim_excerpt', $text, $raw_excerpt );
40
+ }
41
+ endif;
42
+
43
+ if ( ! function_exists( 'wpsp_meta' ) ) :
44
+ /**
45
+ * Build our post meta
46
+ * @since 0.1
47
+ */
48
+ function wpsp_meta( $location, $post_meta_style )
49
+ {
50
+ global $wpsp_id;
51
+
52
+ if ( ! isset( $wpsp_id ) )
53
+ return;
54
+
55
+ $author_location = sanitize_text_field( wpsp_get_setting( $wpsp_id, 'wpsp_author_location' ) );
56
+ $date_location = sanitize_text_field( wpsp_get_setting( $wpsp_id, 'wpsp_date_location' ) );
57
+ $include_author = filter_var( wpsp_get_setting( $wpsp_id, 'wpsp_include_author' ), FILTER_VALIDATE_BOOLEAN );
58
+ $include_terms = filter_var( wpsp_get_setting( $wpsp_id, 'wpsp_include_terms' ), FILTER_VALIDATE_BOOLEAN );
59
+ $include_date = filter_var( get_post_meta( $wpsp_id, 'wpsp_include_date', true ), FILTER_VALIDATE_BOOLEAN );
60
+ $terms_location = sanitize_text_field( wpsp_get_setting( $wpsp_id, 'wpsp_terms_location' ) );
61
+ $taxonomy = sanitize_key( wpsp_get_setting( $wpsp_id, 'wpsp_taxonomy' ) );
62
+
63
+ $output = array();
64
+ if ( ( $include_author && $location == $author_location ) || ( $include_date && $location == $date_location ) || ( $include_terms && $location == $terms_location ) )
65
+ echo '<div class="wp-show-posts-entry-meta wp-show-posts-entry-meta-' . $location . ' post-meta-' . $post_meta_style . '">';
66
+
67
+ // If our author is enabled, show it
68
+ if ( $include_author && $location == $author_location ) :
69
+ $output[] = sprintf( '<span class="wp-show-posts-byline wp-show-posts-meta">%1$s</span>',
70
+ sprintf( '<span class="wp-show-posts-author vcard" itemtype="http://schema.org/Person" itemscope="itemscope" itemprop="author"><a class="url fn n" href="%1$s" title="%2$s" rel="author" itemprop="url"><span class="author-name" itemprop="name">%3$s</span></a></span>',
71
+ esc_url( get_author_posts_url( get_the_author_meta( 'ID' ) ) ),
72
+ esc_attr( sprintf( __( 'View all posts by %s', 'wp-show-posts' ), get_the_author() ) ),
73
+ esc_html( get_the_author() )
74
+ )
75
+ );
76
+ endif;
77
+
78
+ // Show the date
79
+ if ( $include_date && $location == $date_location ) :
80
+ $time_string = '<time class="wp-show-posts-entry-date published" datetime="%1$s" itemprop="datePublished">%2$s</time>';
81
+ if ( get_the_time( 'U' ) !== get_the_modified_time( 'U' ) )
82
+ $time_string .= '<time class="wp-show-posts-updated" datetime="%3$s" itemprop="dateModified">%4$s</time>';
83
+
84
+ $time_string = sprintf( $time_string,
85
+ esc_attr( get_the_date( 'c' ) ),
86
+ esc_html( get_the_date() ),
87
+ esc_attr( get_the_modified_date( 'c' ) ),
88
+ esc_html( get_the_modified_date() )
89
+ );
90
+
91
+ // If our date is enabled, show it
92
+ $output[] = sprintf( '<span class="wp-show-posts-posted-on wp-show-posts-meta">%1$s</span>',
93
+ sprintf( '<a href="%1$s" title="%2$s" rel="bookmark">%3$s</a>',
94
+ esc_url( get_permalink() ),
95
+ esc_attr( get_the_time() ),
96
+ $time_string
97
+ )
98
+ );
99
+ endif;
100
+
101
+ // Show the terms
102
+ if ( $include_terms && $location == $terms_location ) :
103
+ $output[] = sprintf( '<span class="wp-show-posts-terms wp-show-posts-meta">%1$s</span>',
104
+ get_the_term_list( get_the_ID(), $taxonomy, '', ', ' )
105
+ );
106
+ endif;
107
+
108
+ $separator = ( 'inline' == $post_meta_style ) ? ' <span class="wp-show-posts-separator">|</span> ' : '<br />';
109
+ echo implode( $separator, $output);
110
+
111
+ if ( ( $include_author && $location == $author_location ) || ( $include_date && $location == $date_location ) || ( $include_terms && $location == $terms_location ) )
112
+ echo '</div>';
113
+ }
114
+ endif;
115
+
116
+ if ( ! function_exists( 'wpsp_add_post_meta_after_title' ) ) :
117
+ add_action( 'wpsp_after_title','wpsp_add_post_meta_after_title' );
118
+ function wpsp_add_post_meta_after_title()
119
+ {
120
+ global $wpsp_id;
121
+
122
+ if ( ! isset( $wpsp_id ) )
123
+ return;
124
+
125
+ $author_location = sanitize_text_field( wpsp_get_setting( $wpsp_id, 'wpsp_author_location' ) );
126
+ $date_location = sanitize_text_field( wpsp_get_setting( $wpsp_id, 'wpsp_date_location' ) );
127
+ $terms_location = sanitize_text_field( wpsp_get_setting( $wpsp_id, 'wpsp_terms_location' ) );
128
+ $include_author = filter_var( wpsp_get_setting( $wpsp_id, 'wpsp_include_author' ), FILTER_VALIDATE_BOOLEAN );
129
+ $include_terms = filter_var( wpsp_get_setting( $wpsp_id, 'wpsp_include_terms' ), FILTER_VALIDATE_BOOLEAN );
130
+ $include_date = filter_var( get_post_meta( $wpsp_id, 'wpsp_include_date', true ), FILTER_VALIDATE_BOOLEAN );
131
+ $post_meta_top_style = sanitize_text_field( wpsp_get_setting( $wpsp_id, 'wpsp_post_meta_top_style' ) );
132
+
133
+ if ( ( $include_author && 'below-title' == $author_location ) || ( $include_date && 'below-title' == $date_location ) || ( $include_terms && 'below-title' == $terms_location ) )
134
+ wpsp_meta( 'below-title', $post_meta_top_style );
135
+
136
+ }
137
+ endif;
138
+
139
+ if ( ! function_exists( 'wpsp_add_post_meta_after_content' ) ) :
140
+ add_action( 'wpsp_after_content','wpsp_add_post_meta_after_content', 10 );
141
+ function wpsp_add_post_meta_after_content()
142
+ {
143
+ global $wpsp_id;
144
+
145
+ if ( ! isset( $wpsp_id ) )
146
+ return;
147
+
148
+ $author_location = sanitize_text_field( wpsp_get_setting( $wpsp_id, 'wpsp_author_location' ) );
149
+ $date_location = sanitize_text_field( wpsp_get_setting( $wpsp_id, 'wpsp_date_location' ) );
150
+ $terms_location = sanitize_text_field( wpsp_get_setting( $wpsp_id, 'wpsp_terms_location' ) );
151
+ $include_author = filter_var( wpsp_get_setting( $wpsp_id, 'wpsp_include_author' ), FILTER_VALIDATE_BOOLEAN );
152
+ $include_terms = filter_var( wpsp_get_setting( $wpsp_id, 'wpsp_include_terms' ), FILTER_VALIDATE_BOOLEAN );
153
+ $include_date = filter_var( get_post_meta( $wpsp_id, 'wpsp_include_date', true ), FILTER_VALIDATE_BOOLEAN );
154
+ $post_meta_bottom_style = sanitize_text_field( wpsp_get_setting( $wpsp_id, 'wpsp_post_meta_bottom_style' ) );
155
+
156
+ if ( ( $include_author && 'below-post' == $author_location ) || ( $include_date && 'below-post' == $date_location ) || ( $include_terms && 'below-post' == $terms_location ) )
157
+ wpsp_meta( 'below-post', $post_meta_bottom_style );
158
+ }
159
+ endif;
160
+
161
+ if ( ! function_exists( 'wpsp_post_image' ) ) :
162
+ /**
163
+ * Build our post image
164
+ * @since 0.1
165
+ */
166
+ function wpsp_post_image()
167
+ {
168
+ if ( ! has_post_thumbnail() )
169
+ return;
170
+
171
+ global $wpsp_id;
172
+
173
+ if ( ! isset( $wpsp_id ) )
174
+ return;
175
+
176
+ $image = sanitize_text_field( get_post_meta( $wpsp_id, 'wpsp_image', true ), FILTER_VALIDATE_BOOLEAN );
177
+ if ( ! isset( $image ) || ! $image )
178
+ return;
179
+
180
+ $image_alignment = sanitize_text_field( wpsp_get_setting( $wpsp_id, 'wpsp_image_alignment' ) );
181
+ $image_height = absint( wpsp_get_setting( $wpsp_id, 'wpsp_image_height' ) );
182
+ $image_width = absint( wpsp_get_setting( $wpsp_id, 'wpsp_image_width' ) );
183
+ $image_overlay_color = wpsp_sanitize_hex_color( wpsp_get_setting( $wpsp_id, 'wpsp_image_overlay_color' ) );
184
+ $image_overlay_icon = sanitize_text_field( wpsp_get_setting( $wpsp_id, 'wpsp_image_overlay_icon' ) );
185
+ $image_hover_effect = sanitize_text_field( wpsp_get_setting( $wpsp_id, 'wpsp_image_hover_effect' ) );
186
+ $columns = sanitize_text_field( wpsp_get_setting( $wpsp_id, 'wpsp_columns' ) );
187
+
188
+ $image_id = get_post_thumbnail_id( get_the_ID(), 'full' );
189
+ $image_url = wp_get_attachment_image_src( $image_id, 'full', true );
190
+ $image_atts = wpsp_image_attributes( $image_url[1], $image_url[2], $image_width, $image_height );
191
+ $hover = ( '' !== $image_hover_effect ) ? $image_hover_effect : '';
192
+ ?>
193
+ <div class="wp-show-posts-image <?php echo $hover . ' wpsp-image-' . $image_alignment; ?> ">
194
+ <?php printf( '<a href="%1$s" %2$s title="%3$s">',
195
+ apply_filters( 'wpsp_image_href', esc_url( get_the_permalink() ) ),
196
+ apply_filters( 'wpsp_image_data', '' ),
197
+ apply_filters( 'wpsp_image_title', esc_attr( get_the_title() ) )
198
+ ); ?>
199
+ <?php if ( ! empty( $image_atts ) ) : ?>
200
+ <img src="<?php echo WPSP_Resize( $image_url[0], $image_atts[ 'width' ], $image_atts[ 'height' ], $image_atts[ 'crop' ], true, $image_atts[ 'upscale' ] ); ?>" alt="<?php esc_attr( the_title() ); ?>" itemprop="image" class="<?php echo $image_alignment; ?>" />
201
+ <?php else :
202
+ the_post_thumbnail( apply_filters( 'wpsp_default_image_size', 'full' ), array( 'itemprop' => 'image' ) );
203
+ endif;
204
+
205
+ if ( '' !== $image_overlay_color || '' !== $image_overlay_icon ) :
206
+ $color = ( $image_overlay_color ) ? 'style="background-color:' . wpsp_hex2rgba( $image_overlay_color, apply_filters( 'wpsp_overlay_opacity', 0.7 ) ) . '"' : '';
207
+ $icon = ( $image_overlay_icon ) ? $image_overlay_icon : 'no-icon';
208
+ echo '<span class="wp-show-posts-image-overlay ' . $icon . '" ' . $color . '></span>';
209
+ endif;
210
+ ?>
211
+ </a>
212
+ </div>
213
+ <?php
214
+ }
215
+ endif;
216
+
217
+ if ( ! function_exists( 'wpsp_add_post_image_before_title' ) ) :
218
+ add_action( 'wpsp_before_title','wpsp_add_post_image_before_title' );
219
+ function wpsp_add_post_image_before_title()
220
+ {
221
+ global $wpsp_id;
222
+
223
+ if ( ! isset( $wpsp_id ) )
224
+ return;
225
+
226
+ $image_location = sanitize_text_field( wpsp_get_setting( $wpsp_id, 'wpsp_image_location' ) );
227
+
228
+ if ( 'above-title' == $image_location )
229
+ wpsp_post_image();
230
+ }
231
+ endif;
232
+
233
+ if ( ! function_exists( 'wpsp_add_post_image_before_content' ) ) :
234
+ add_action( 'wpsp_before_content','wpsp_add_post_image_before_content' );
235
+ function wpsp_add_post_image_before_content()
236
+ {
237
+ global $wpsp_id;
238
+
239
+ if ( ! isset( $wpsp_id ) )
240
+ return;
241
+
242
+ $image_location = sanitize_text_field( wpsp_get_setting( $wpsp_id, 'wpsp_image_location' ) );
243
+
244
+ if ( 'below-title' == $image_location )
245
+ wpsp_post_image();
246
+ }
247
+ endif;
248
+
249
+ if ( ! function_exists( 'wpsp_read_more' ) ) :
250
+ add_action( 'wpsp_after_content','wpsp_read_more', 5 );
251
+ function wpsp_read_more()
252
+ {
253
+ global $wpsp_id;
254
+
255
+ if ( ! isset( $wpsp_id ) )
256
+ return;
257
+
258
+ $read_more_text = wp_kses_post( wpsp_get_setting( $wpsp_id, 'wpsp_read_more_text' ) );
259
+ $read_more_style = esc_attr( wpsp_get_setting( $wpsp_id, 'wpsp_read_more_style' ) );
260
+ $read_more_color = sanitize_text_field( wpsp_get_setting( $wpsp_id, 'wpsp_read_more_color' ) );
261
+
262
+ // The read more button
263
+ if ( $read_more_text ) : ?>
264
+ <a title="<?php echo esc_attr( get_the_title() ); ?>" class="wp-show-posts-read-more <?php echo $read_more_style; ?> <?php echo $read_more_color; ?>" href="<?php esc_url( the_permalink() ); ?>"><?php echo $read_more_text; ?></a>
265
+ <?php endif;
266
+ }
267
+ endif;
268
+
269
+ if ( ! function_exists( 'wpsp_hex2rgba' ) ) :
270
+ /**
271
+ * Convert hex to RGBA
272
+ * @since 0.1
273
+ */
274
+ function wpsp_hex2rgba($color, $opacity = false) {
275
+
276
+ $default = 'rgb(0,0,0)';
277
+
278
+ // Return default if no color provided
279
+ if(empty($color))
280
+ return $default;
281
+
282
+ // Sanitize $color if "#" is provided
283
+ if ($color[0] == '#' ) {
284
+ $color = substr( $color, 1 );
285
+ }
286
+
287
+ // Check if color has 6 or 3 characters and get values
288
+ if (strlen($color) == 6) {
289
+ $hex = array( $color[0] . $color[1], $color[2] . $color[3], $color[4] . $color[5] );
290
+ } elseif ( strlen( $color ) == 3 ) {
291
+ $hex = array( $color[0] . $color[0], $color[1] . $color[1], $color[2] . $color[2] );
292
+ } else {
293
+ return $default;
294
+ }
295
+
296
+ // Convert hexadec to rgb
297
+ $rgb = array_map('hexdec', $hex);
298
+
299
+ // Check if opacity is set(rgba or rgb)
300
+ if($opacity){
301
+ if(abs($opacity) > 1)
302
+ $opacity = 1.0;
303
+ $output = 'rgba('.implode(",",$rgb).','.$opacity.')';
304
+ } else {
305
+ $output = 'rgb('.implode(",",$rgb).')';
306
+ }
307
+
308
+ // Return rgb(a) color string
309
+ return $output;
310
+ }
311
+ endif;
312
+
313
+ if ( ! function_exists( 'wpsp_sanitize_hex_color' ) ) :
314
+ function wpsp_sanitize_hex_color( $color ) {
315
+ if ( '' === $color )
316
+ return '';
317
+
318
+ // 3 or 6 hex digits, or the empty string.
319
+ if ( preg_match('|^#([A-Fa-f0-9]{3}){1,2}$|', $color ) )
320
+ return $color;
321
+ }
322
+ endif;
323
+
324
+ if ( ! function_exists( 'wpsp_image_attributes' ) ) :
325
+ /**
326
+ * Build our image attributes
327
+ * @since 0.1
328
+ */
329
+ function wpsp_image_attributes( $og_width = '', $og_height = '', $new_width = '', $new_height = '' )
330
+ {
331
+ $ignore_crop = array( '', '0', '9999' );
332
+
333
+ $image_atts = array();
334
+
335
+ $image_atts = array(
336
+ 'width' => ( in_array( $new_width, $ignore_crop ) ) ? 9999 : intval( $new_width ),
337
+ 'height' => ( in_array( $new_height, $ignore_crop ) ) ? 9999 : intval( $new_height ),
338
+ 'crop' => ( in_array( $new_width, $ignore_crop ) || in_array( $new_height, $ignore_crop ) ) ? false : true
339
+ );
340
+
341
+ // If there's no height or width, empty the array
342
+ if ( 9999 == $image_atts[ 'width' ] && 9999 == $image_atts[ 'height' ] )
343
+ $image_atts = array();
344
+
345
+ if ( ! empty( $image_atts ) ) :
346
+ // Is our width larger than the OG image and not proportional?
347
+ $width_upscale = $image_atts[ 'width' ] > $og_width && $image_atts[ 'width' ] < 9999 ? true : false;
348
+
349
+ // Is our height larger than the OG image and not proportional?
350
+ $height_upscale = $image_atts[ 'height' ] > $og_height && $image_atts[ 'height' ] < 9999 ? true : false;
351
+
352
+ // If both the height and width are larger than the OG image, upscale
353
+ $image_atts[ 'upscale' ] = $width_upscale && $height_upscale ? true : false;
354
+
355
+ // If the width is larger than the OG image and the height isn't proportional, upscale
356
+ $image_atts[ 'upscale' ] = $width_upscale && $image_atts[ 'height' ] < 9999 ? true : $image_atts[ 'upscale' ];
357
+
358
+ // If the height is larger than the OG image and width isn't proportional, upscale
359
+ $image_atts[ 'upscale' ] = $height_upscale && $image_atts[ 'width' ] < 9999 ? true : $image_atts[ 'upscale' ];
360
+
361
+ // If we're upscaling, set crop to true
362
+ $image_atts[ 'crop' ] = $image_atts[ 'upscale' ] ? true : $image_atts[ 'crop' ];
363
+
364
+ // If one of our sizes is upscaling but the other is proportional, show the full image
365
+ if ( $width_upscale && $image_atts[ 'height' ] == 9999 || $height_upscale && $image_atts[ 'width' ] == 9999 )
366
+ $image_atts = array();
367
+ endif;
368
+
369
+ return apply_filters( 'generate_blog_image_attributes', $image_atts );
370
+ }
371
+ endif;
372
+
373
+ if ( ! function_exists( 'wpsp_pagination' ) ) :
374
+ /**
375
+ * Build our regular pagination
376
+ * @since 0.1
377
+ */
378
+ function wpsp_pagination( $max_num_pages ) {
379
+ // Don't print empty markup if there's only one page.
380
+ if ( $max_num_pages < 2 ) {
381
+ return;
382
+ }
383
+
384
+ $paged = get_query_var( 'paged' ) ? intval( get_query_var( 'paged' ) ) : 1;
385
+ $pagenum_link = html_entity_decode( get_pagenum_link() );
386
+ $query_args = array();
387
+ $url_parts = explode( '?', $pagenum_link );
388
+
389
+ if ( isset( $url_parts[1] ) ) {
390
+ wp_parse_str( $url_parts[1], $query_args );
391
+ }
392
+
393
+ $pagenum_link = remove_query_arg( array_keys( $query_args ), $pagenum_link );
394
+ $pagenum_link = trailingslashit( $pagenum_link ) . '%_%';
395
+
396
+ $format = $GLOBALS['wp_rewrite']->using_index_permalinks() && ! strpos( $pagenum_link, 'index.php' ) ? 'index.php/' : '';
397
+ $format .= $GLOBALS['wp_rewrite']->using_permalinks() ? user_trailingslashit( 'page/%#%', 'paged' ) : '?paged=%#%';
398
+
399
+ // Set up paginated links.
400
+ $links = paginate_links( array(
401
+ 'base' => $pagenum_link,
402
+ 'format' => $format,
403
+ 'total' => $max_num_pages,
404
+ 'current' => $paged,
405
+ 'mid_size' => apply_filters( 'wpsp_pagination_mid_size', 1 ),
406
+ 'add_args' => array_map( 'urlencode', $query_args ),
407
+ 'prev_text' => __( '&larr; Previous', 'wp-show-posts' ),
408
+ 'next_text' => __( 'Next &rarr;', 'wp-show-posts' ),
409
+ ) );
410
+
411
+ if ( $links ) :
412
+
413
+ echo $links;
414
+
415
+ endif;
416
+ }
417
+ endif;
inc/image-resizer.php ADDED
@@ -0,0 +1,241 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Title : Aqua Resizer
4
+ * Description : Resizes WordPress images on the fly
5
+ * Version : 1.2.1
6
+ * Author : Syamil MJ
7
+ * Author URI : http://aquagraphite.com
8
+ * License : WTFPL - http://sam.zoy.org/wtfpl/
9
+ * Documentation : https://github.com/sy4mil/Aqua-Resizer/
10
+ *
11
+ * @param string $url - (required) must be uploaded using wp media uploader
12
+ * @param int $width - (required)
13
+ * @param int $height - (optional)
14
+ * @param bool $crop - (optional) default to soft crop
15
+ * @param bool $single - (optional) returns an array if false
16
+ * @param bool $upscale - (optional) resizes smaller images
17
+ * @uses wp_upload_dir()
18
+ * @uses image_resize_dimensions()
19
+ * @uses wp_get_image_editor()
20
+ *
21
+ * @return str|array
22
+ */
23
+
24
+ if( ! class_exists( 'WPSP_Resize' ) ) {
25
+ class WPSP_Exception extends Exception {}
26
+
27
+ class WPSP_Resize
28
+ {
29
+ /**
30
+ * The singleton instance
31
+ */
32
+ static private $instance = null;
33
+
34
+ /**
35
+ * Should an WPSP_Exception be thrown on error?
36
+ * If false (default), then the error will just be logged.
37
+ */
38
+ public $throwOnError = false;
39
+
40
+ /**
41
+ * No initialization allowed
42
+ */
43
+ private function __construct() {}
44
+
45
+ /**
46
+ * No cloning allowed
47
+ */
48
+ private function __clone() {}
49
+
50
+ /**
51
+ * For your custom default usage you may want to initialize an WPSP_Resize object by yourself and then have own defaults
52
+ */
53
+ static public function getInstance() {
54
+ if(self::$instance == null) {
55
+ self::$instance = new self;
56
+ }
57
+
58
+ return self::$instance;
59
+ }
60
+
61
+ /**
62
+ * Run, forest.
63
+ */
64
+ public function process( $url, $width = null, $height = null, $crop = null, $single = true, $upscale = false ) {
65
+ try {
66
+ // Validate inputs.
67
+ if (!$url)
68
+ throw new WPSP_Exception('$url parameter is required');
69
+ if (!$width)
70
+ throw new WPSP_Exception('$width parameter is required');
71
+ if (!$height)
72
+ throw new WPSP_Exception('$height parameter is required');
73
+
74
+ // Caipt'n, ready to hook.
75
+ if ( true === $upscale ) add_filter( 'image_resize_dimensions', array($this, 'WPSP_upscale'), 10, 6 );
76
+
77
+ // Define upload path & dir.
78
+ $upload_info = wp_upload_dir();
79
+ $upload_dir = $upload_info['basedir'];
80
+ $upload_url = $upload_info['baseurl'];
81
+
82
+ $http_prefix = "http://";
83
+ $https_prefix = "https://";
84
+ $relative_prefix = "//"; // The protocol-relative URL
85
+
86
+ /* if the $url scheme differs from $upload_url scheme, make them match
87
+ if the schemes differe, images don't show up. */
88
+ if(!strncmp($url,$https_prefix,strlen($https_prefix))){ //if url begins with https:// make $upload_url begin with https:// as well
89
+ $upload_url = str_replace($http_prefix,$https_prefix,$upload_url);
90
+ }
91
+ elseif(!strncmp($url,$http_prefix,strlen($http_prefix))){ //if url begins with http:// make $upload_url begin with http:// as well
92
+ $upload_url = str_replace($https_prefix,$http_prefix,$upload_url);
93
+ }
94
+ elseif(!strncmp($url,$relative_prefix,strlen($relative_prefix))){ //if url begins with // make $upload_url begin with // as well
95
+ $upload_url = str_replace(array( 0 => "$http_prefix", 1 => "$https_prefix"),$relative_prefix,$upload_url);
96
+ }
97
+
98
+
99
+ // Check if $img_url is local.
100
+ if ( false === strpos( $url, $upload_url ) )
101
+ throw new WPSP_Exception('Image must be local: ' . $url);
102
+
103
+ // Define path of image.
104
+ $rel_path = str_replace( $upload_url, '', $url );
105
+ $img_path = $upload_dir . $rel_path;
106
+
107
+ // Check if img path exists, and is an image indeed.
108
+ if ( ! file_exists( $img_path ) or ! getimagesize( $img_path ) )
109
+ throw new WPSP_Exception('Image file does not exist (or is not an image): ' . $img_path);
110
+
111
+ // Get image info.
112
+ $info = pathinfo( $img_path );
113
+ $ext = $info['extension'];
114
+ list( $orig_w, $orig_h ) = getimagesize( $img_path );
115
+
116
+ // Get image size after cropping.
117
+ $dims = image_resize_dimensions( $orig_w, $orig_h, $width, $height, $crop );
118
+ $dst_w = $dims[4];
119
+ $dst_h = $dims[5];
120
+
121
+ // Return the original image only if it exactly fits the needed measures.
122
+ if ( ! $dims && ( ( ( null === $height && $orig_w == $width ) xor ( null === $width && $orig_h == $height ) ) xor ( $height == $orig_h && $width == $orig_w ) ) ) {
123
+ $img_url = $url;
124
+ $dst_w = $orig_w;
125
+ $dst_h = $orig_h;
126
+ } else {
127
+ // Use this to check if cropped image already exists, so we can return that instead.
128
+ $suffix = "{$dst_w}x{$dst_h}";
129
+ $dst_rel_path = str_replace( '.' . $ext, '', $rel_path );
130
+ $destfilename = "{$upload_dir}{$dst_rel_path}-{$suffix}.{$ext}";
131
+
132
+ if ( ! $dims || ( true == $crop && false == $upscale && ( $dst_w < $width || $dst_h < $height ) ) ) {
133
+ // Can't resize, so return false saying that the action to do could not be processed as planned.
134
+ throw new WPSP_Exception('Unable to resize image because image_resize_dimensions() failed');
135
+ }
136
+ // Else check if cache exists.
137
+ elseif ( file_exists( $destfilename ) && getimagesize( $destfilename ) ) {
138
+ $img_url = "{$upload_url}{$dst_rel_path}-{$suffix}.{$ext}";
139
+ }
140
+ // Else, we resize the image and return the new resized image url.
141
+ else {
142
+
143
+ $editor = wp_get_image_editor( $img_path );
144
+
145
+ if ( is_wp_error( $editor ) || is_wp_error( $editor->resize( $width, $height, $crop ) ) ) {
146
+ throw new WPSP_Exception('Unable to get WP_Image_Editor: ' .
147
+ $editor->get_error_message() . ' (is GD or ImageMagick installed?)');
148
+ }
149
+
150
+ $resized_file = $editor->save();
151
+
152
+ if ( ! is_wp_error( $resized_file ) ) {
153
+ $resized_rel_path = str_replace( $upload_dir, '', $resized_file['path'] );
154
+ $img_url = $upload_url . $resized_rel_path;
155
+ } else {
156
+ throw new WPSP_Exception('Unable to save resized image file: ' . $editor->get_error_message());
157
+ }
158
+
159
+ }
160
+ }
161
+
162
+ // Okay, leave the ship.
163
+ if ( true === $upscale ) remove_filter( 'image_resize_dimensions', array( $this, 'WPSP_upscale' ) );
164
+
165
+ // Return the output.
166
+ if ( $single ) {
167
+ // str return.
168
+ $image = $img_url;
169
+ } else {
170
+ // array return.
171
+ $image = array (
172
+ 0 => $img_url,
173
+ 1 => $dst_w,
174
+ 2 => $dst_h
175
+ );
176
+ }
177
+
178
+ return $image;
179
+ }
180
+ catch (WPSP_Exception $ex) {
181
+ error_log('WPSP_Resize.process() error: ' . $ex->getMessage());
182
+
183
+ if ($this->throwOnError) {
184
+ // Bubble up exception.
185
+ throw $ex;
186
+ }
187
+ else {
188
+ // Return false, so that this patch is backwards-compatible.
189
+ return false;
190
+ }
191
+ }
192
+ }
193
+
194
+ /**
195
+ * Callback to overwrite WP computing of thumbnail measures
196
+ */
197
+ function WPSP_upscale( $default, $orig_w, $orig_h, $dest_w, $dest_h, $crop ) {
198
+ if ( ! $crop ) return null; // Let the wordpress default function handle this.
199
+ // Here is the point we allow to use larger image size than the original one.
200
+ $aspect_ratio = $orig_w / $orig_h;
201
+ $new_w = $dest_w;
202
+ $new_h = $dest_h;
203
+ if ( ! $new_w ) {
204
+ $new_w = intval( $new_h * $aspect_ratio );
205
+ }
206
+ if ( ! $new_h ) {
207
+ $new_h = intval( $new_w / $aspect_ratio );
208
+ }
209
+ $size_ratio = max( $new_w / $orig_w, $new_h / $orig_h );
210
+ $crop_w = round( $new_w / $size_ratio );
211
+ $crop_h = round( $new_h / $size_ratio );
212
+ $s_x = floor( ( $orig_w - $crop_w ) / 2 );
213
+ $s_y = floor( ( $orig_h - $crop_h ) / 2 );
214
+ return array( 0, 0, (int) $s_x, (int) $s_y, (int) $new_w, (int) $new_h, (int) $crop_w, (int) $crop_h );
215
+ }
216
+ }
217
+ }
218
+
219
+ if ( ! function_exists( 'WPSP_Resize' ) ) :
220
+ /**
221
+ * This is just a tiny wrapper function for the class above so that there is no
222
+ * need to change any code in your own WP themes. Usage is still the same :)
223
+ */
224
+ function WPSP_Resize( $url, $width = null, $height = null, $crop = null, $single = true, $upscale = false ) {
225
+ /* WPML Fix */
226
+ if ( defined( 'ICL_SITEPRESS_VERSION' ) ){
227
+ global $sitepress;
228
+ $url = $sitepress->convert_url( $url, $sitepress->get_default_language() );
229
+ }
230
+
231
+ /* Jetpack Photon fix */
232
+ if ( class_exists( 'Jetpack' ) && Jetpack::is_module_active( 'photon' ) && function_exists( 'jetpack_photon_url' ) ) {
233
+ $type = ( $crop ) ? 'resize' : 'fit';
234
+ $args = array( $type => $width . ',' . $height );
235
+ return jetpack_photon_url( $url, $args );
236
+ }
237
+
238
+ $WPSP_Resize = WPSP_Resize::getInstance();
239
+ return $WPSP_Resize->process( $url, $width, $height, $crop, $single, $upscale );
240
+ }
241
+ endif;
js/jquery.matchHeight.js ADDED
@@ -0,0 +1,399 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * jquery-match-height master by @liabru
3
+ * http://brm.io/jquery-match-height/
4
+ * License: MIT
5
+ */
6
+
7
+ ;(function(factory) { // eslint-disable-line no-extra-semi
8
+ 'use strict';
9
+ if (typeof define === 'function' && define.amd) {
10
+ // AMD
11
+ define(['jquery'], factory);
12
+ } else if (typeof module !== 'undefined' && module.exports) {
13
+ // CommonJS
14
+ module.exports = factory(require('jquery'));
15
+ } else {
16
+ // Global
17
+ factory(jQuery);
18
+ }
19
+ })(function($) {
20
+ /*
21
+ * internal
22
+ */
23
+
24
+ var _previousResizeWidth = -1,
25
+ _updateTimeout = -1;
26
+
27
+ /*
28
+ * _parse
29
+ * value parse utility function
30
+ */
31
+
32
+ var _parse = function(value) {
33
+ // parse value and convert NaN to 0
34
+ return parseFloat(value) || 0;
35
+ };
36
+
37
+ /*
38
+ * _rows
39
+ * utility function returns array of jQuery selections representing each row
40
+ * (as displayed after float wrapping applied by browser)
41
+ */
42
+
43
+ var _rows = function(elements) {
44
+ var tolerance = 1,
45
+ $elements = $(elements),
46
+ lastTop = null,
47
+ rows = [];
48
+
49
+ // group elements by their top position
50
+ $elements.each(function(){
51
+ var $that = $(this),
52
+ top = $that.offset().top - _parse($that.css('margin-top')),
53
+ lastRow = rows.length > 0 ? rows[rows.length - 1] : null;
54
+
55
+ if (lastRow === null) {
56
+ // first item on the row, so just push it
57
+ rows.push($that);
58
+ } else {
59
+ // if the row top is the same, add to the row group
60
+ if (Math.floor(Math.abs(lastTop - top)) <= tolerance) {
61
+ rows[rows.length - 1] = lastRow.add($that);
62
+ } else {
63
+ // otherwise start a new row group
64
+ rows.push($that);
65
+ }
66
+ }
67
+
68
+ // keep track of the last row top
69
+ lastTop = top;
70
+ });
71
+
72
+ return rows;
73
+ };
74
+
75
+ /*
76
+ * _parseOptions
77
+ * handle plugin options
78
+ */
79
+
80
+ var _parseOptions = function(options) {
81
+ var opts = {
82
+ byRow: true,
83
+ property: 'height',
84
+ target: null,
85
+ remove: false
86
+ };
87
+
88
+ if (typeof options === 'object') {
89
+ return $.extend(opts, options);
90
+ }
91
+
92
+ if (typeof options === 'boolean') {
93
+ opts.byRow = options;
94
+ } else if (options === 'remove') {
95
+ opts.remove = true;
96
+ }
97
+
98
+ return opts;
99
+ };
100
+
101
+ /*
102
+ * matchHeight
103
+ * plugin definition
104
+ */
105
+
106
+ var matchHeight = $.fn.matchHeight = function(options) {
107
+ var opts = _parseOptions(options);
108
+
109
+ // handle remove
110
+ if (opts.remove) {
111
+ var that = this;
112
+
113
+ // remove fixed height from all selected elements
114
+ this.css(opts.property, '');
115
+
116
+ // remove selected elements from all groups
117
+ $.each(matchHeight._groups, function(key, group) {
118
+ group.elements = group.elements.not(that);
119
+ });
120
+
121
+ // TODO: cleanup empty groups
122
+
123
+ return this;
124
+ }
125
+
126
+ if (this.length <= 1 && !opts.target) {
127
+ return this;
128
+ }
129
+
130
+ // keep track of this group so we can re-apply later on load and resize events
131
+ matchHeight._groups.push({
132
+ elements: this,
133
+ options: opts
134
+ });
135
+
136
+ // match each element's height to the tallest element in the selection
137
+ matchHeight._apply(this, opts);
138
+
139
+ return this;
140
+ };
141
+
142
+ /*
143
+ * plugin global options
144
+ */
145
+
146
+ matchHeight.version = 'master';
147
+ matchHeight._groups = [];
148
+ matchHeight._throttle = 80;
149
+ matchHeight._maintainScroll = false;
150
+ matchHeight._beforeUpdate = null;
151
+ matchHeight._afterUpdate = null;
152
+ matchHeight._rows = _rows;
153
+ matchHeight._parse = _parse;
154
+ matchHeight._parseOptions = _parseOptions;
155
+
156
+ /*
157
+ * matchHeight._apply
158
+ * apply matchHeight to given elements
159
+ */
160
+
161
+ matchHeight._apply = function(elements, options) {
162
+ var opts = _parseOptions(options),
163
+ $elements = $(elements),
164
+ rows = [$elements];
165
+
166
+ // take note of scroll position
167
+ var scrollTop = $(window).scrollTop(),
168
+ htmlHeight = $('html').outerHeight(true);
169
+
170
+ // get hidden parents
171
+ var $hiddenParents = $elements.parents().filter(':hidden');
172
+
173
+ // cache the original inline style
174
+ $hiddenParents.each(function() {
175
+ var $that = $(this);
176
+ $that.data('style-cache', $that.attr('style'));
177
+ });
178
+
179
+ // temporarily must force hidden parents visible
180
+ $hiddenParents.css('display', 'block');
181
+
182
+ // get rows if using byRow, otherwise assume one row
183
+ if (opts.byRow && !opts.target) {
184
+
185
+ // must first force an arbitrary equal height so floating elements break evenly
186
+ $elements.each(function() {
187
+ var $that = $(this),
188
+ display = $that.css('display');
189
+
190
+ // temporarily force a usable display value
191
+ if (display !== 'inline-block' && display !== 'flex' && display !== 'inline-flex') {
192
+ display = 'block';
193
+ }
194
+
195
+ // cache the original inline style
196
+ $that.data('style-cache', $that.attr('style'));
197
+
198
+ $that.css({
199
+ 'display': display,
200
+ 'padding-top': '0',
201
+ 'padding-bottom': '0',
202
+ 'margin-top': '0',
203
+ 'margin-bottom': '0',
204
+ 'border-top-width': '0',
205
+ 'border-bottom-width': '0',
206
+ 'height': '100px',
207
+ 'overflow': 'hidden'
208
+ });
209
+ });
210
+
211
+ // get the array of rows (based on element top position)
212
+ rows = _rows($elements);
213
+
214
+ // revert original inline styles
215
+ $elements.each(function() {
216
+ var $that = $(this);
217
+ $that.attr('style', $that.data('style-cache') || '');
218
+ });
219
+ }
220
+
221
+ $.each(rows, function(key, row) {
222
+ var $row = $(row),
223
+ targetHeight = 0;
224
+
225
+ if (!opts.target) {
226
+ // skip apply to rows with only one item
227
+ if (opts.byRow && $row.length <= 1) {
228
+ $row.css(opts.property, '');
229
+ return;
230
+ }
231
+
232
+ // iterate the row and find the max height
233
+ $row.each(function(){
234
+ var $that = $(this),
235
+ style = $that.attr('style'),
236
+ display = $that.css('display');
237
+
238
+ // temporarily force a usable display value
239
+ if (display !== 'inline-block' && display !== 'flex' && display !== 'inline-flex') {
240
+ display = 'block';
241
+ }
242
+
243
+ // ensure we get the correct actual height (and not a previously set height value)
244
+ var css = { 'display': display };
245
+ css[opts.property] = '';
246
+ $that.css(css);
247
+
248
+ // find the max height (including padding, but not margin)
249
+ if ($that.outerHeight(false) > targetHeight) {
250
+ targetHeight = $that.outerHeight(false);
251
+ }
252
+
253
+ // revert styles
254
+ if (style) {
255
+ $that.attr('style', style);
256
+ } else {
257
+ $that.css('display', '');
258
+ }
259
+ });
260
+ } else {
261
+ // if target set, use the height of the target element
262
+ targetHeight = opts.target.outerHeight(false);
263
+ }
264
+
265
+ // iterate the row and apply the height to all elements
266
+ $row.each(function(){
267
+ var $that = $(this),
268
+ verticalPadding = 0;
269
+
270
+ // don't apply to a target
271
+ if (opts.target && $that.is(opts.target)) {
272
+ return;
273
+ }
274
+
275
+ // handle padding and border correctly (required when not using border-box)
276
+ if ($that.css('box-sizing') !== 'border-box') {
277
+ verticalPadding += _parse($that.css('border-top-width')) + _parse($that.css('border-bottom-width'));
278
+ verticalPadding += _parse($that.css('padding-top')) + _parse($that.css('padding-bottom'));
279
+ }
280
+
281
+ // set the height (accounting for padding and border)
282
+ $that.css(opts.property, (targetHeight - verticalPadding) + 'px');
283
+ });
284
+ });
285
+
286
+ // revert hidden parents
287
+ $hiddenParents.each(function() {
288
+ var $that = $(this);
289
+ $that.attr('style', $that.data('style-cache') || null);
290
+ });
291
+
292
+ // restore scroll position if enabled
293
+ if (matchHeight._maintainScroll) {
294
+ $(window).scrollTop((scrollTop / htmlHeight) * $('html').outerHeight(true));
295
+ }
296
+
297
+ return this;
298
+ };
299
+
300
+ /*
301
+ * matchHeight._applyDataApi
302
+ * applies matchHeight to all elements with a data-match-height attribute
303
+ */
304
+
305
+ matchHeight._applyDataApi = function() {
306
+ var groups = {};
307
+
308
+ // generate groups by their groupId set by elements using data-match-height
309
+ $('[data-match-height], [data-mh]').each(function() {
310
+ var $this = $(this),
311
+ groupId = $this.attr('data-mh') || $this.attr('data-match-height');
312
+
313
+ if (groupId in groups) {
314
+ groups[groupId] = groups[groupId].add($this);
315
+ } else {
316
+ groups[groupId] = $this;
317
+ }
318
+ });
319
+
320
+ // apply matchHeight to each group
321
+ $.each(groups, function() {
322
+ this.matchHeight(true);
323
+ });
324
+ };
325
+
326
+ /*
327
+ * matchHeight._update
328
+ * updates matchHeight on all current groups with their correct options
329
+ */
330
+
331
+ var _update = function(event) {
332
+ if (matchHeight._beforeUpdate) {
333
+ matchHeight._beforeUpdate(event, matchHeight._groups);
334
+ }
335
+
336
+ $.each(matchHeight._groups, function() {
337
+ matchHeight._apply(this.elements, this.options);
338
+ });
339
+
340
+ if (matchHeight._afterUpdate) {
341
+ matchHeight._afterUpdate(event, matchHeight._groups);
342
+ }
343
+ };
344
+
345
+ matchHeight._update = function(throttle, event) {
346
+ // prevent update if fired from a resize event
347
+ // where the viewport width hasn't actually changed
348
+ // fixes an event looping bug in IE8
349
+ if (event && event.type === 'resize') {
350
+ var windowWidth = $(window).width();
351
+ if (windowWidth === _previousResizeWidth) {
352
+ return;
353
+ }
354
+ _previousResizeWidth = windowWidth;
355
+ }
356
+
357
+ // throttle updates
358
+ if (!throttle) {
359
+ _update(event);
360
+ } else if (_updateTimeout === -1) {
361
+ _updateTimeout = setTimeout(function() {
362
+ _update(event);
363
+ _updateTimeout = -1;
364
+ }, matchHeight._throttle);
365
+ }
366
+ };
367
+
368
+ /*
369
+ * bind events
370
+ */
371
+
372
+ // apply on DOM ready event
373
+ $(matchHeight._applyDataApi);
374
+
375
+ // update heights on load and resize events
376
+ $(window).bind('load', function(event) {
377
+ matchHeight._update(false, event);
378
+ });
379
+
380
+ // throttled update heights on resize events
381
+ $(window).bind('resize orientationchange', function(event) {
382
+ matchHeight._update(true, event);
383
+ });
384
+
385
+ });
386
+
387
+ jQuery(document).ready( function($) {
388
+ var s = document.body || document.documentElement, s = s.style;
389
+ if ( s.webkitFlexWrap == '' || s.msFlexWrap == '' || s.flexWrap == '' ) return false;
390
+
391
+ jQuery('.wp-show-posts-columns .wp-show-posts-inner').matchHeight();
392
+
393
+ var rows = $.fn.matchHeight._rows($('.wp-show-posts-columns .wp-show-posts-inner'));
394
+
395
+ $.each(rows, function(i, row) {
396
+ row.first().addClass('generate-column-first');
397
+ row.last().addClass('generate-column-last');
398
+ });
399
+ });
license.txt ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ WP Show Posts is licensed under the GNU General Public License v2 or later
2
+ More details here: http://www.gnu.org/licenses/gpl-2.0.html
3
+
4
+ == Bundled resources ==
5
+
6
+ = Butterbean =
7
+
8
+ Licensed under the GNU GPL, version 2 or later.
9
+ 2015-2016 � Justin Tadlock.
10
+
11
+ = MatchHeight.js =
12
+
13
+ jquery.matchHeight.js is licensed under The MIT License (MIT)
14
+ Copyright (c) 2014 Liam Brummitt
15
+
16
+ = FontAwesome =
17
+
18
+ Icons generated with IcoMoon with the following licenses:
19
+
20
+ Font License: SIL OFL 1.1 - http://scripts.sil.org/OFL
21
+ Code License: MIT License - http://opensource.org/licenses/mit-license.html
22
+
23
+ All brand icons are trademarks of their respective owners.
readme.txt ADDED
@@ -0,0 +1,156 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ === WP Show Posts ===
2
+ Contributors: edge22
3
+ Donate link: https://generatepress.com/ongoing-development
4
+ Tags: show posts, display posts shortcode, portfolio, gallery, post columns
5
+ Requires at least: 4.0
6
+ Tested up to: 4.6
7
+ Stable tag: 0.1
8
+ License: GPLv2 or later
9
+ License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
+
11
+ Add posts to your website from any post type using a simple shortcode.
12
+
13
+ == Description ==
14
+
15
+ https://vimeo.com/175638957
16
+
17
+ WP Show Posts allows you to display posts anywhere on your website using an easy to use shortcode.
18
+
19
+ You can pull posts from any post type like WooCommerce, Easy Digital Downloads etc..
20
+
21
+ This plugin works with any theme.
22
+
23
+ Here are the features in the free version:
24
+
25
+ = Posts =
26
+
27
+ * Post type
28
+ * Taxonomy
29
+ * Terms
30
+ * Posts per page
31
+ * Pagination
32
+
33
+ = Columns =
34
+
35
+ * Columns
36
+ * Columns gutter
37
+
38
+ = Images =
39
+
40
+ * Show images
41
+ * Image width
42
+ * Image height
43
+ * Image alignment
44
+ * Image location
45
+
46
+ = Content =
47
+
48
+ * Content type (excerpt or full post)
49
+ * Excerpt length
50
+ * Include title
51
+ * Read more text
52
+
53
+ = Meta =
54
+
55
+ * Include author
56
+ * Author location
57
+ * Include date
58
+ * Date location
59
+ * Include terms
60
+ * Terms location
61
+
62
+ = More query args =
63
+
64
+ * Author ID
65
+ * Exclude current
66
+ * Post ID
67
+ * Ignore sticky posts
68
+ * Offset
69
+ * Order
70
+ * Order by
71
+ * Status
72
+ * Meta key
73
+ * Meta value
74
+ * Tax operator
75
+
76
+ = Our *Pro* version has these features =
77
+
78
+ https://vimeo.com/175660953
79
+
80
+ [Check out Pro](https://wpshowposts.com/ "Check out Pro")
81
+
82
+ = Posts =
83
+
84
+ * AJAX pagination
85
+
86
+ = Columns =
87
+
88
+ * Masonry
89
+ * Featured post
90
+
91
+ = Images =
92
+
93
+ * Image overlay color
94
+ * Image overlay icon
95
+ * Image hover effect
96
+ * Image lightbox
97
+ * Image lightbox gallery
98
+
99
+ = Content =
100
+
101
+ * Read more style
102
+ * Read more color
103
+
104
+ = Styling =
105
+
106
+ * Border color
107
+ * Padding
108
+
109
+ = Social =
110
+
111
+ * Twitter
112
+ * Facebook
113
+ * Google+
114
+ * Pinterest
115
+ * Love it
116
+ * Alignment
117
+
118
+ Check out GeneratePress, our awesome WordPress theme! (http://wordpress.org/themes/generatepress)
119
+
120
+ == Installation ==
121
+
122
+ There's two ways to install WP Show Posts.
123
+
124
+ 1. Go to "Plugins > Add New" in your Dashboard and search for: WP Show Posts
125
+ 2. Download the .zip from WordPress.org, and upload the folder to the `/wp-content/plugins/` directory via FTP.
126
+
127
+ In most cases, #1 will work fine and is way easier.
128
+
129
+ == Frequently Asked Questions ==
130
+
131
+ = How do I create a post list? =
132
+
133
+ * Make sure WP Show Posts is activated.
134
+ * Navigate to "WP Show Posts > Add New" and configure your list.
135
+ * Copy the shortcode provided for you when adding your new list.
136
+ * Add your shortcode to your desired page or post.
137
+
138
+ == Screenshots ==
139
+
140
+ 1. All of your created post lists.
141
+ 2. The "Posts" settings tab.
142
+ 3. The "Columns" settings tab.
143
+ 4. The "Images" settings tab.
144
+ 5. The "Content" settings tab.
145
+ 6. The "Meta" settings tab.
146
+ 7. The "More query ars" settings tab.
147
+
148
+ == Changelog ==
149
+
150
+ = 0.1 =
151
+ * Initial release
152
+
153
+ == Upgrade Notice ==
154
+
155
+ = 0.1 =
156
+ * Initial release
wp-show-posts.php ADDED
@@ -0,0 +1,423 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Plugin Name: WP Show Posts
4
+ Plugin URI: https://wpshowposts.com
5
+ Description: WP Show Posts allows you to list posts (from any post type) anywhere on your site. This includes WooCommerce products or any other post type you might have! Check out the pro version for even more features at https://wpshowposts.com.
6
+ Version: 0.1
7
+ Author: Tom Usborne
8
+ Author URI: https://tomusborne.com
9
+ License: GNU General Public License v2 or later
10
+ License URI: http://www.gnu.org/licenses/gpl-2.0.html
11
+ Text Domain: wp-show-posts
12
+ */
13
+
14
+ // Define the current version
15
+ define( 'WPSP_VERSION', 0.1 );
16
+
17
+ // Add defaults
18
+ require_once trailingslashit( plugin_dir_path( __FILE__ ) ) . 'inc/defaults.php';
19
+
20
+ // Add post type
21
+ require_once trailingslashit( plugin_dir_path( __FILE__ ) ) . 'admin/post-type.php';
22
+
23
+ // Add admin metabox
24
+ require_once trailingslashit( plugin_dir_path( __FILE__ ) ) . 'admin/metabox.php';
25
+
26
+ // Add admin AJAX
27
+ require_once trailingslashit( plugin_dir_path( __FILE__ ) ) . 'admin/ajax.php';
28
+
29
+ // Add resizer script
30
+ if ( ! class_exists( 'WPSP_Resize' ) ) require_once plugin_dir_path( __FILE__ ) . 'inc/image-resizer.php';
31
+
32
+ // Add functions
33
+ require_once trailingslashit( plugin_dir_path( __FILE__ ) ) . 'inc/functions.php';
34
+
35
+ // Add admin functions
36
+ require_once trailingslashit( plugin_dir_path( __FILE__ ) ) . 'admin/admin.php';
37
+
38
+ if ( ! function_exists( 'wpsp_get_min_suffix' ) ) :
39
+ /**
40
+ * Figure out if we should use minified scripts or not
41
+ * @since 0.1
42
+ */
43
+ function wpsp_get_min_suffix()
44
+ {
45
+ return defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '-min';
46
+ }
47
+ endif;
48
+
49
+ if ( ! function_exists( 'wpsp_enqueue_scripts' ) ) :
50
+ /*
51
+ * Enqueue our CSS to the front end
52
+ * @since 0.1
53
+ */
54
+ add_action( 'wp_enqueue_scripts', 'wpsp_enqueue_scripts' );
55
+ function wpsp_enqueue_scripts()
56
+ {
57
+ $suffix = wpsp_get_min_suffix();
58
+ wp_enqueue_style( 'wp-show-posts', plugins_url( "css/wp-show-posts{$suffix}.css", __FILE__ ), array(), WPSP_VERSION );
59
+ }
60
+ endif;
61
+
62
+ if ( ! function_exists( 'wpsp_get_setting' ) ) :
63
+ /*
64
+ * Create a helpful wrapper to get our settings and defaults
65
+ * @since 0.1
66
+ */
67
+ function wpsp_get_setting( $id, $key )
68
+ {
69
+ $defaults = wpsp_get_defaults();
70
+ return get_post_meta( $id, $key ) ? get_post_meta( $id, $key, true ) : $defaults[ $key ];
71
+ }
72
+ endif;
73
+
74
+ if ( ! function_exists( 'wpsp_display' ) ) :
75
+ /*
76
+ * Build the front end of the plugin
77
+ * $id parameter needs to match ID of custom post type entry
78
+ * @since 0.1
79
+ */
80
+ function wpsp_display( $id )
81
+ {
82
+ // Set the global ID of our object
83
+ global $wpsp_id;
84
+ $wpsp_id = $id;
85
+
86
+ // Build our setting variables
87
+ $author = sanitize_text_field( wpsp_get_setting( $id, 'wpsp_author' ) );
88
+ $columns = sanitize_text_field( wpsp_get_setting( $id, 'wpsp_columns' ) );
89
+ $columns_gutter = sanitize_text_field( wpsp_get_setting( $id, 'wpsp_columns_gutter' ) );
90
+ $content_type = sanitize_text_field( wpsp_get_setting( $id, 'wpsp_content_type' ) );
91
+ $exclude_current = wp_validate_boolean( wpsp_get_setting( $id, 'wpsp_exclude_current' ) );
92
+ $excerpt_length = absint( wpsp_get_setting( $id, 'wpsp_excerpt_length' ) );
93
+ $post_id = absint( wpsp_get_setting( $id, 'wpsp_post_id' ) );
94
+ $ignore_sticky_posts = wp_validate_boolean( wpsp_get_setting( $id, 'wpsp_ignore_sticky_posts' ) );
95
+ $image_gallery = sanitize_text_field( wpsp_get_setting( $id, 'wpsp_image_gallery' ) );
96
+ $image_lightbox = sanitize_text_field( wpsp_get_setting( $id, 'wpsp_image_lightbox' ) );
97
+ $include_title = wp_validate_boolean( get_post_meta( $id, 'wpsp_include_title', true ) );
98
+ $author_location = sanitize_text_field( wpsp_get_setting( $id, 'wpsp_author_location' ) );
99
+ $date_location = sanitize_text_field( wpsp_get_setting( $id, 'wpsp_date_location' ) );
100
+ $terms_location = sanitize_text_field( wpsp_get_setting( $id, 'wpsp_terms_location' ) );
101
+ $include_author = wp_validate_boolean( wpsp_get_setting( $id, 'wpsp_include_author' ) );
102
+ $include_terms = wp_validate_boolean( wpsp_get_setting( $id, 'wpsp_include_terms' ) );
103
+ $include_date = wp_validate_boolean( get_post_meta( $id, 'wpsp_include_date', true ) );
104
+ $inner_wrapper = sanitize_text_field( wpsp_get_setting( $id, 'wpsp_inner_wrapper' ) );
105
+ $inner_wrapper_class = array_map( 'sanitize_html_class', ( explode( ' ', wpsp_get_setting( $id, 'wpsp_inner_wrapper_class' ) ) ) );
106
+ $inner_wrapper_style = explode( ' ', esc_attr( wpsp_get_setting( $id, 'wpsp_inner_wrapper_style' ) ) );
107
+ $itemtype = sanitize_text_field( wpsp_get_setting( $id, 'wpsp_itemtype' ) );
108
+ $meta_key = sanitize_text_field( wpsp_get_setting( $id, 'wpsp_meta_key' ) );
109
+ $meta_value = sanitize_text_field( wpsp_get_setting( $id, 'wpsp_meta_value' ) );
110
+ $offset = absint( wpsp_get_setting( $id, 'wpsp_offset' ) );
111
+ $order = sanitize_key( wpsp_get_setting( $id, 'wpsp_order' ) );
112
+ $orderby = sanitize_key( wpsp_get_setting( $id, 'wpsp_orderby' ) );
113
+ $pagination = wp_validate_boolean( wpsp_get_setting( $id, 'wpsp_pagination' ) );
114
+ $post_type = sanitize_text_field( wpsp_get_setting( $id, 'wpsp_post_type' ) );
115
+ $post_status = wpsp_get_setting( $id, 'wpsp_post_status' ); // Validated later
116
+ $posts_per_page = intval( wpsp_get_setting( $id, 'wpsp_posts_per_page' ) );
117
+ $tax_operator = wpsp_get_setting( $id, 'wpsp_tax_operator' ); // Validated later
118
+ $tax_term = sanitize_text_field( wpsp_get_setting( $id, 'wpsp_tax_term' ) );
119
+ $taxonomy = sanitize_key( wpsp_get_setting( $id, 'wpsp_taxonomy' ) );
120
+ $wrapper = sanitize_text_field( wpsp_get_setting( $id, 'wpsp_wrapper' ) );
121
+ $wrapper_id = sanitize_html_class( wpsp_get_setting( $id, 'wpsp_wrapper_id' ) );
122
+ $wrapper_class = array_map( 'sanitize_html_class', ( explode( ' ', wpsp_get_setting( $id, 'wpsp_wrapper_class' ) ) ) );
123
+ $wrapper_style = explode( ' ', esc_attr( wpsp_get_setting( $id, 'wpsp_wrapper_style' ) ) );
124
+ $ajax_pagination = wp_validate_boolean( wpsp_get_setting( $id, 'wpsp_ajax_pagination' ) );
125
+ $masonry = wp_validate_boolean( wpsp_get_setting( $id, 'wpsp_masonry' ) );
126
+ $featured_post = wp_validate_boolean( wpsp_get_setting( $id, 'wpsp_featured_post' ) );
127
+ $border = wpsp_sanitize_hex_color( wpsp_get_setting( $id, 'wpsp_border' ) );
128
+ $padding = sanitize_text_field( wpsp_get_setting( $id, 'wpsp_padding' ) );
129
+
130
+ // Grab initiate args for query
131
+ $args = array();
132
+
133
+ if ( '' !== $order )
134
+ $args[ 'order' ] = $order;
135
+
136
+ if ( '' !== $orderby )
137
+ $args[ 'orderby' ] = $orderby;
138
+
139
+ if ( '' !== $post_type )
140
+ $args[ 'post_type' ] = $post_type;
141
+
142
+ if ( '' !== $posts_per_page )
143
+ $args[ 'posts_per_page' ] = $posts_per_page;
144
+
145
+ if ( $ignore_sticky_posts )
146
+ $args[ 'ignore_sticky_posts' ] = $ignore_sticky_posts;
147
+
148
+ if ( '' !== $meta_key )
149
+ $args[ 'meta_key' ] = $meta_key;
150
+
151
+ if ( '' !== $meta_value )
152
+ $args[ 'meta_value' ] = $meta_value;
153
+
154
+ if ( $offset > 0 )
155
+ $args[ 'offset' ] = $offset;
156
+
157
+ if ( '' !== $author )
158
+ $args[ 'author' ] = array( $author );
159
+
160
+ if ( $pagination )
161
+ $args[ 'paged' ] = get_query_var( 'paged' );
162
+
163
+ // Post Status
164
+ $post_status = explode( ', ', $post_status );
165
+ $validated = array();
166
+ $available = array( 'publish', 'pending', 'draft', 'auto-draft', 'future', 'private', 'inherit', 'trash', 'any' );
167
+
168
+ foreach ( $post_status as $unvalidated )
169
+ if ( in_array( $unvalidated, $available ) )
170
+ $validated[] = $unvalidated;
171
+
172
+ if( !empty( $validated ) )
173
+ $args['post_status'] = $validated;
174
+
175
+ // If taxonomy attributes, create a taxonomy query
176
+ if ( !empty( $taxonomy ) && !empty( $tax_term ) ) {
177
+
178
+ if ( 'current' == $tax_term ) {
179
+ global $post;
180
+ $terms = wp_get_post_terms(get_the_ID(), $taxonomy);
181
+ $tax_term = array();
182
+ foreach ($terms as $term) {
183
+ $tax_term[] = $term->slug;
184
+ }
185
+ } else {
186
+ // Term string to array
187
+ $tax_term = explode( ', ', $tax_term );
188
+ }
189
+
190
+ // Validate operator
191
+ if( !in_array( $tax_operator, array( 'IN', 'NOT IN', 'AND' ) ) )
192
+ $tax_operator = 'IN';
193
+
194
+ $tax_args = array(
195
+ 'tax_query' => array(
196
+ array(
197
+ 'taxonomy' => $taxonomy,
198
+ 'field' => 'slug',
199
+ 'terms' => $tax_term,
200
+ 'operator' => $tax_operator
201
+ )
202
+ )
203
+ );
204
+
205
+ $args = array_merge( $args, $tax_args );
206
+ }
207
+
208
+ // If Post IDs
209
+ if( $post_id ) {
210
+ $posts_in = array_map( 'intval', explode( ',', $post_id ) );
211
+ $args['post__in'] = $posts_in;
212
+ }
213
+
214
+ // If Exclude Current
215
+ if( is_singular() && $exclude_current )
216
+ $args['post__not_in'] = array( get_the_ID() );
217
+
218
+ // Border
219
+ if ( '' !== $border ) {
220
+ $wrapper_class[] = 'include-border';
221
+ $border = 'border-color: ' . $border . ';';
222
+ }
223
+
224
+ // Padding
225
+ if ( '' !== $padding ) {
226
+ $wrapper_class[] = 'include-padding';
227
+ $padding = 'padding:' . $padding . ';';
228
+ }
229
+
230
+ // Columns
231
+ if ( $columns !== 'col-12' ) :
232
+ wp_enqueue_script( 'wpsp-matchHeight', trailingslashit( plugin_dir_url( __FILE__ ) ) . 'js/jquery.matchHeight.js', array( 'jquery' ), WPSP_VERSION, true );
233
+ $wrapper_class[] = 'wp-show-posts-columns';
234
+ $wrapper_style[] = 'margin-right:-' . $columns_gutter . ';';
235
+ $inner_wrapper_style[] = 'margin: 0 ' . $columns_gutter . ' ' . $columns_gutter . ' 0;' . $border . $padding;
236
+ endif;
237
+
238
+ // Featured post class
239
+ $current_post = '';
240
+ if ( $columns !== 'col-12' && $featured_post ) :
241
+ if ( $columns == 'col-6' )
242
+ $current_post = 'col-12';
243
+
244
+ if ( $columns == 'col-4' )
245
+ $current_post = 'col-8';
246
+
247
+ if ( $columns == 'col-3' )
248
+ $current_post = 'col-6';
249
+
250
+ if ( $columns == 'col-20' )
251
+ $current_post = 'col-6';
252
+ endif;
253
+
254
+ // Masonry
255
+ if ( $masonry ) :
256
+ $wrapper_class[] = 'wp-show-posts-masonry';
257
+ $inner_wrapper_class[] = ' wp-show-posts-masonry-' . $columns;
258
+ $inner_wrapper_class[] = ' wp-show-posts-masonry-block';
259
+
260
+ wp_enqueue_script( 'wpsp-imagesloaded' );
261
+ wp_enqueue_script( 'jquery-masonry' );
262
+ wp_add_inline_script( 'jquery-masonry', 'jQuery(function($){var $container = $(".wp-show-posts-masonry");$container.imagesLoaded( function(){$container.fadeIn( 1000 ).masonry({itemSelector : ".wp-show-posts-masonry-block",columnWidth: ".grid-sizer"}).css("opacity","1");});});' );
263
+ endif;
264
+
265
+ // Add the default inner wrapper class
266
+ // We don't create the class element up here like below, as we need to add classes inside the loop below as well
267
+ $inner_wrapper_class[] = 'wp-show-posts-single';
268
+
269
+ if ( 'col-12' == $columns )
270
+ $inner_wrapper_class[] = 'wpsp-clearfix';
271
+
272
+ // Add the default wrapper class
273
+ $wrapper_class[] = 'wp-show-posts';
274
+
275
+ // Get the wrapper class
276
+ if( !empty( $wrapper_class ) )
277
+ $wrapper_class = ' class="' . implode( ' ', $wrapper_class ) . '"';
278
+
279
+ // Get the wrapper style
280
+ if( !empty( $wrapper_style ) )
281
+ $wrapper_style = ' style="' . implode( ' ', $wrapper_style ) . '"';
282
+
283
+ // Get the inner wrapper class
284
+ if( !empty( $inner_wrapper_style ) )
285
+ $inner_wrapper_style = ' style="' . implode( ' ', $inner_wrapper_style ) . '"';
286
+
287
+ // Get the wrapper ID
288
+ if( !empty( $wrapper_id ) )
289
+ $wrapper_id = ' id="' . $wrapper_id . '"';
290
+
291
+ do_action( 'wpsp_before_wrapper' );
292
+
293
+ // Start the wrapper
294
+ echo '<' . $wrapper . $wrapper_id . $wrapper_class . $wrapper_style . '>';
295
+
296
+ if ( $masonry )
297
+ echo '<div class="grid-sizer ' . $columns . '"></div>';
298
+
299
+ // Start the query
300
+ $query = new WP_Query( apply_filters( 'wp_show_posts_shortcode_args', $args ) );
301
+
302
+ // Start the loop
303
+ if ( $query->have_posts() ) {
304
+ while ( $query->have_posts() ) {
305
+ $query->the_post();
306
+
307
+ // Get page
308
+ $paged = ( get_query_var( 'paged' ) ) ? get_query_var( 'paged' ) : 1;
309
+
310
+ $featured = '';
311
+ $column_class = '';
312
+ // Featured post
313
+ if ( $columns !== 'col-12' && $featured_post ) :
314
+ if ( $query->current_post == 0 && $paged == 1 ) {
315
+ $featured = ' featured-column ' . $current_post;
316
+ } else {
317
+ $featured = ' ' . $columns;
318
+ }
319
+ elseif ( $columns !== 'col-12' ) :
320
+ $column_class .= ' ' . $columns;
321
+ endif;
322
+
323
+ // Start inner container
324
+ echo '<' . $inner_wrapper . ' class="' . implode( ' ', $inner_wrapper_class ) . $column_class . $featured . '" itemtype="http://schema.org/' . $itemtype . '" itemscope>';
325
+ echo '<div class="wp-show-posts-inner"' . $inner_wrapper_style . '>';
326
+
327
+ do_action( 'wpsp_before_title' );
328
+
329
+ // The title
330
+ if ( $include_title || ( $include_author && 'below-title' == $author_location ) || ( $include_date && 'below-title' == $date_location ) || ( $include_terms && 'below-title' == $terms_location ) ) : ?>
331
+ <header class="wp-show-posts-entry-header">
332
+ <?php
333
+ if ( $include_title )
334
+ the_title( sprintf( '<h2 class="wp-show-posts-entry-title" itemprop="headline"><a href="%s" rel="bookmark">', esc_url( get_permalink() ) ), '</a></h2>' );
335
+
336
+ do_action( 'wpsp_after_title' );
337
+ ?>
338
+ </header><!-- .entry-header -->
339
+ <?php endif;
340
+
341
+ do_action( 'wpsp_before_content' );
342
+
343
+ // The excerpt or full content
344
+ if ( 'excerpt' == $content_type && $excerpt_length ) : ?>
345
+ <div class="wp-show-posts-entry-summary" itemprop="text">
346
+ <?php wpsp_excerpt( '', $excerpt_length ); ?>
347
+ </div><!-- .entry-summary -->
348
+ <?php elseif ( 'full' == $content_type ) : ?>
349
+ <div class="wp-show-posts-entry-content" itemprop="text">
350
+ <?php the_content(); ?>
351
+ </div><!-- .entry-content -->
352
+ <?php endif;
353
+
354
+ do_action( 'wpsp_after_content' );
355
+
356
+ echo '</div><!-- wp-show-posts-inner -->';
357
+ if ( 'col-12' == $columns ) echo '<div class="wpsp-clear"></div>';
358
+ // End inner container
359
+ echo '</' . $inner_wrapper . '>';
360
+
361
+
362
+ }
363
+ } else {
364
+ // no posts found
365
+ echo apply_filters( 'wpsp_shortcode_no_results', wpautop( __( 'Sorry, no posts were found.','wp-show-posts' ) ) );
366
+ }
367
+ if ( $columns !== 'col-12' ) echo '<div class="wpsp-clear"></div>';
368
+ echo '</' . $wrapper . '><!-- .wp-show-posts -->';
369
+
370
+ do_action( 'wpsp_after_wrapper' );
371
+
372
+ // Pagination
373
+ if ( $pagination ) :
374
+ if ( $ajax_pagination && function_exists( 'wpsp_ajax_pagination' ) ) :
375
+ $max_page = $query->max_num_pages;
376
+ $nextpage = intval( $paged ) + 1;
377
+
378
+ if ( $nextpage <= $max_page )
379
+ $next_page_url = next_posts( $max_page, false );
380
+
381
+ wpsp_ajax_pagination( $next_page_url, $paged, $max_page );
382
+ wp_enqueue_script( 'wpsp-imagesloaded' );
383
+ wp_enqueue_script( 'wpsp-ajax-pagination' );
384
+ else :
385
+ wpsp_pagination( $query->max_num_pages );
386
+ endif;
387
+ endif;
388
+
389
+ // Lightbox and gallery
390
+ if ( $image_lightbox ) {
391
+ wp_enqueue_script( 'wpsp-featherlight' );
392
+ wp_enqueue_style( 'wpsp-featherlight' );
393
+
394
+ if ( $image_gallery ) {
395
+ wp_enqueue_script( 'wpsp-featherlight-gallery' );
396
+ wp_enqueue_style( 'wpsp-featherlight-gallery' );
397
+ }
398
+ }
399
+
400
+ // Restore original Post Data
401
+ wp_reset_postdata();
402
+ }
403
+ endif;
404
+
405
+ if ( ! function_exists( 'wpsp_shortcode_function' ) ) :
406
+ /*
407
+ * Build the shortcode
408
+ * @since 0.1
409
+ */
410
+ add_shortcode( 'wp_show_posts', 'wpsp_shortcode_function' );
411
+ function wpsp_shortcode_function( $atts , $content = null ) {
412
+ // Attributes
413
+ $atts = shortcode_atts(
414
+ array(
415
+ 'id' => '',
416
+ ), $atts, 'wp_show_posts'
417
+ );
418
+ if ( $atts[ 'id' ] )
419
+ return wpsp_display( $atts[ 'id' ] );
420
+ else
421
+ return false;
422
+ }
423
+ endif;