PluginProbe
Edit Flow / 0.9.2
Edit Flow v0.9.2
0.11.1 0.11.0 0.7.2 0.7.3 0.7.4 0.7.5 0.7.6 0.8 0.8.1 0.8.2 0.9 0.9.1 0.9.2 0.9.3 0.9.4 0.9.5 0.9.6 0.9.7 0.9.8 0.9.9 trunk 0.1.5 0.10.0 0.10.1 0.10.2 All 44 releases
← All changes | common/php/class-module.php +498 -612 trunk0.9.2 View file →
@@ -1,726 +1,612 @@
1 1 <?php
2 2 /**
3 - * Base class for Edit Flow modules.
3 + * class EF_Module
4 4 *
5 - * @package EditFlow
5 + * @desc Base class any Edit Flow module should extend
6 6 */
7 7
8 -if ( ! class_exists( 'EF_Module' ) ) {
8 +if ( !class_exists( 'EF_Module' ) ) {
9 9
10 +class EF_Module {
11 +
12 + public $published_statuses = array(
13 + 'publish',
14 + 'future',
15 + 'private',
16 + );
17 +
10 18 /**
11 - * Base class any Edit Flow module should extend.
19 + * Associative array of hook_name => callback_name
20 + * This is used for Gutenberg-compat initialization
21 + * [
22 + * 'init' => 'init_callback_on_module_instance'
23 + * ]
24 + * @var array
12 25 */
13 - class EF_Module {
26 + protected $compat_hooks = [];
14 27
15 - /**
16 - * Published post statuses.
17 - *
18 - * @var array
19 - */
20 - public $published_statuses = array(
21 - 'publish',
22 - 'future',
23 - 'private',
24 - );
28 + function __construct() {}
25 29
26 - /**
27 - * URL to the module directory.
28 - *
29 - * @var string
30 - */
31 - public $module_url;
30 + /**
31 + * Returns whether the current module is enabled.
32 + *
33 + * @since 0.9.1
34 + *
35 + * @return <code>true</code> if the module is enabled, <code>false</code> otherwise
36 + */
37 + public function is_enabled() {
38 + return $this->module->options->enabled === 'on';
39 + }
32 40
33 - /**
34 - * Module data object.
35 - *
36 - * @var object
37 - */
38 - public $module;
41 + /**
42 + * Returns whether the module with the given name is enabled.
43 + *
44 + * @since 0.7
45 + *
46 + * @param string module Slug of the module to check
47 + * @return <code>true</code> if the module is enabled, <code>false</code> otherwise
48 + */
49 + function module_enabled( $slug ) {
50 + global $edit_flow;
39 51
40 - /**
41 - * Constructor.
42 - */
43 - public function __construct() {}
52 + return isset( $edit_flow->$slug ) && $edit_flow->$slug->is_enabled();
53 + }
44 54
45 - /**
46 - * Returns whether the current module is enabled.
47 - *
48 - * @since 0.9.1
49 - *
50 - * @return bool True if the module is enabled, false otherwise.
51 - */
52 - public function is_enabled() {
53 - return 'on' === $this->module->options->enabled;
55 + /**
56 + * Gets an array of allowed post types for a module
57 + *
58 + * @return array post-type-slug => post-type-label
59 + */
60 + function get_all_post_types() {
61 +
62 + $allowed_post_types = array(
63 + 'post' => __( 'Post' ),
64 + 'page' => __( 'Page' ),
65 + );
66 + $custom_post_types = $this->get_supported_post_types_for_module();
67 +
68 + foreach( $custom_post_types as $custom_post_type => $args ) {
69 + $allowed_post_types[$custom_post_type] = $args->label;
54 70 }
71 + return $allowed_post_types;
72 + }
55 73
56 - /**
57 - * Returns whether the module with the given name is enabled.
58 - *
59 - * @since 0.7
60 - *
61 - * @param string $slug Slug of the module to check.
62 - * @return bool True if the module is enabled, false otherwise.
63 - */
64 - public function module_enabled( $slug ) {
65 - global $edit_flow;
74 + /**
75 + * Cleans up the 'on' and 'off' for post types on a given module (so we don't get warnings all over)
76 + * For every post type that doesn't explicitly have the 'on' value, turn it 'off'
77 + * If add_post_type_support() has been used anywhere (legacy support), inherit the state
78 + *
79 + * @param array $module_post_types Current state of post type options for the module
80 + * @param string $post_type_support What the feature is called for post_type_support (e.g. 'ef_calendar')
81 + * @return array $normalized_post_type_options The setting for each post type, normalized based on rules
82 + *
83 + * @since 0.7
84 + */
85 + function clean_post_type_options( $module_post_types = array(), $post_type_support = null ) {
86 + $normalized_post_type_options = array();
87 + $all_post_types = array_keys( $this->get_all_post_types() );
88 + foreach( $all_post_types as $post_type ) {
89 + if ( ( isset( $module_post_types[$post_type] ) && $module_post_types[$post_type] == 'on' ) || post_type_supports( $post_type, $post_type_support ) )
90 + $normalized_post_type_options[$post_type] = 'on';
91 + else
92 + $normalized_post_type_options[$post_type] = 'off';
93 + }
94 + return $normalized_post_type_options;
95 + }
66 96
67 - return isset( $edit_flow->$slug ) && $edit_flow->$slug->is_enabled();
68 - }
97 + /**
98 + * Get all of the possible post types that can be used with a given module
99 + *
100 + * @param object $module The full module
101 + * @return array $post_types An array of post type objects
102 + *
103 + * @since 0.7.2
104 + */
105 + function get_supported_post_types_for_module( $module = null ) {
69 106
70 - /**
71 - * Returns whether analytics has been enabled or not.
72 - *
73 - * It's only enabled if the site is a production WPVIP site.
74 - *
75 - * @since 0.10.0
76 - *
77 - * @return bool True if analytics is enabled, false otherwise.
78 - */
79 - public function is_analytics_enabled() {
80 - // Check if the site is a production WPVIP site and only then enable it.
81 - $is_analytics_enabled = $this->is_vip_site( true );
107 + $pt_args = array(
108 + '_builtin' => false,
109 + 'public' => true,
110 + );
111 + $pt_args = apply_filters( 'edit_flow_supported_module_post_types_args', $pt_args, $module );
112 + return get_post_types( $pt_args, 'objects' );
113 + }
82 114
83 - // Filter to disable it.
84 - $is_analytics_enabled = apply_filters( 'ef_should_analytics_be_enabled', $is_analytics_enabled );
115 + /**
116 + * Collect all of the active post types for a given module
117 + *
118 + * @param object $module Module's data
119 + * @return array $post_types All of the post types that are 'on'
120 + *
121 + * @since 0.7
122 + */
123 + function get_post_types_for_module( $module ) {
85 124
86 - return $is_analytics_enabled;
125 + $post_types = array();
126 + if ( isset( $module->options->post_types ) && is_array( $module->options->post_types ) ) {
127 + foreach( $module->options->post_types as $post_type => $value )
128 + if ( 'on' == $value )
129 + $post_types[] = $post_type;
87 130 }
131 + return $post_types;
132 + }
88 133
89 - /**
90 - * Check if the site is a WPVIP site.
91 - *
92 - * @since 0.10.0
93 - *
94 - * @param bool $only_production Whether to only allow production sites to be considered WPVIP sites.
95 - * @return bool True if it is a WPVIP site, false otherwise.
96 - */
97 - protected function is_vip_site( $only_production = false ) {
98 - $is_vip_site = defined( 'VIP_GO_ENV' )
99 - && defined( 'WPCOM_SANDBOXED' ) && constant( 'WPCOM_SANDBOXED' ) === false
100 - && defined( 'FILES_CLIENT_SITE_ID' );
134 + /**
135 + * Get all of the currently available post statuses
136 + * This should be used in favor of calling $edit_flow->custom_status->get_custom_statuses() directly
137 + *
138 + * @return array $post_statuses All of the post statuses that aren't a published state
139 + *
140 + * @since 0.7
141 + */
142 + function get_post_statuses() {
143 + global $edit_flow;
101 144
102 - if ( $only_production ) {
103 - $is_vip_site = $is_vip_site && defined( 'VIP_GO_ENV' ) && 'production' === constant( 'VIP_GO_ENV' );
104 - }
105 -
106 - return $is_vip_site;
145 + if ( $this->module_enabled('custom_status') ) {
146 + return $edit_flow->custom_status->get_custom_statuses();
147 + } else {
148 + return $this->get_core_post_statuses();
107 149 }
150 + }
108 151
109 - /**
110 - * Gets an array of allowed post types for a module.
111 - *
112 - * @return array Post-type-slug => post-type-label.
113 - */
114 - public function get_all_post_types() {
152 + /**
153 + * Get core's 'draft' and 'pending' post statuses, but include our special attributes
154 + *
155 + * @since 0.8.1
156 + *
157 + * @return array
158 + */
159 + protected function get_core_post_statuses() {
115 160
116 - $allowed_post_types = array(
117 - 'post' => __( 'Post', 'edit-flow' ),
118 - 'page' => __( 'Page', 'edit-flow' ),
161 + return array(
162 + (object)array(
163 + 'name' => __( 'Draft' ),
164 + 'description' => '',
165 + 'slug' => 'draft',
166 + 'position' => 1,
167 + ),
168 + (object)array(
169 + 'name' => __( 'Pending Review' ),
170 + 'description' => '',
171 + 'slug' => 'pending',
172 + 'position' => 2,
173 + ),
119 174 );
120 - $custom_post_types = $this->get_supported_post_types_for_module();
175 + }
121 176
122 - foreach ( $custom_post_types as $custom_post_type => $args ) {
123 - $allowed_post_types[ $custom_post_type ] = $args->label;
124 - }
125 - return $allowed_post_types;
126 - }
177 + /**
178 + * Gets the name of the default custom status. If custom statuses are disabled,
179 + * returns 'draft'.
180 + *
181 + * @return str Name of the status
182 + */
183 + function get_default_post_status(){
127 184
128 - /**
129 - * Cleans up the 'on' and 'off' for post types on a given module (so we don't get warnings all over).
130 - *
131 - * For every post type that doesn't explicitly have the 'on' value, turn it 'off'.
132 - * If add_post_type_support() has been used anywhere (legacy support), inherit the state.
133 - *
134 - * @since 0.7
135 - *
136 - * @param array $module_post_types Current state of post type options for the module.
137 - * @param string $post_type_support What the feature is called for post_type_support (e.g. 'ef_calendar').
138 - * @return array The setting for each post type, normalized based on rules.
139 - */
140 - public function clean_post_type_options( $module_post_types = array(), $post_type_support = null ) {
141 - $normalized_post_type_options = array();
142 - $all_post_types = array_keys( $this->get_all_post_types() );
143 - foreach ( $all_post_types as $post_type ) {
144 - if ( ( isset( $module_post_types[ $post_type ] ) && 'on' == $module_post_types[ $post_type ] ) || post_type_supports( $post_type, $post_type_support ) ) {
145 - $normalized_post_type_options[ $post_type ] = 'on';
146 - } else {
147 - $normalized_post_type_options[ $post_type ] = 'off';
148 - }
149 - }
150 - return $normalized_post_type_options;
151 - }
185 + // Check if custom status module is enabled
186 + $custom_status_module = EditFlow()->custom_status->module->options;
152 187
153 - /**
154 - * Get all of the possible post types that can be used with a given module.
155 - *
156 - * @since 0.7.2
157 - *
158 - * @param object $module The full module.
159 - * @return array An array of post type objects.
160 - */
161 - public function get_supported_post_types_for_module( $module = null ) {
188 + if( $custom_status_module->enabled == 'on' )
189 + return $custom_status_module->default_status;
190 + else
191 + return 'draft';
162 192
163 - $pt_args = array(
164 - '_builtin' => false,
165 - 'public' => true,
166 - );
167 - $pt_args = apply_filters( 'edit_flow_supported_module_post_types_args', $pt_args, $module );
168 - return get_post_types( $pt_args, 'objects' );
169 - }
193 + }
170 194
171 - /**
172 - * Collect all of the active post types for a given module.
173 - *
174 - * @since 0.7
175 - *
176 - * @param object $module Module's data.
177 - * @return array All of the post types that are 'on'.
178 - */
179 - public function get_post_types_for_module( $module ) {
195 + /**
196 + * Filter to all posts with a given post status (can be a custom status or a built-in status) and optional custom post type.
197 + *
198 + * @since 0.7
199 + *
200 + * @param string $slug The slug for the post status to which to filter
201 + * @param string $post_type Optional post type to which to filter
202 + * @return an edit.php link to all posts with the given post status and, optionally, the given post type
203 + */
204 + function filter_posts_link( $slug, $post_type = 'post' ) {
205 + $filter_link = add_query_arg( 'post_status', $slug, get_admin_url( null, 'edit.php' ) );
206 + if ( $post_type != 'post' && in_array( $post_type, get_post_types( '', 'names' ) ) )
207 + $filter_link = add_query_arg( 'post_type', $post_type, $filter_link );
208 + return $filter_link;
209 + }
180 210
181 - $post_types = array();
182 - if ( isset( $module->options->post_types ) && is_array( $module->options->post_types ) ) {
183 - foreach ( $module->options->post_types as $post_type => $value ) {
184 - if ( 'on' == $value ) {
185 - $post_types[] = $post_type;
186 - }
187 - }
188 - }
189 - return $post_types;
190 - }
211 + /**
212 + * Enqueue any resources (CSS or JS) associated with datepicker functionality
213 + *
214 + * @since 0.7
215 + */
216 + function enqueue_datepicker_resources() {
191 217
192 - /**
193 - * Get all of the currently available post statuses.
194 - *
195 - * This should be used in favor of calling $edit_flow->custom_status->get_custom_statuses() directly.
196 - *
197 - * @since 0.7
198 - *
199 - * @return array All of the post statuses that aren't a published state.
200 - */
201 - public function get_post_statuses() {
202 - global $edit_flow;
218 + // Add the first day of the week as an available variable to wp_head
219 + echo "<script type=\"text/javascript\">var ef_week_first_day=\"" . get_option( 'start_of_week' ) . "\";</script>";
203 220
204 - if ( $this->module_enabled( 'custom_status' ) ) {
205 - return $edit_flow->custom_status->get_custom_statuses();
206 - } else {
207 - return $this->get_core_post_statuses();
208 - }
209 - }
221 + wp_enqueue_script( 'jquery-ui-datepicker' );
210 222
211 - /**
212 - * Get core's 'draft' and 'pending' post statuses, but include our special attributes.
213 - *
214 - * @since 0.8.1
215 - *
216 - * @return array
217 - */
218 - protected function get_core_post_statuses() {
223 + //Timepicker needs to come after jquery-ui-datepicker and jquery
224 + wp_enqueue_script( 'edit_flow-timepicker', EDIT_FLOW_URL . 'common/js/jquery-ui-timepicker-addon.js', array( 'jquery', 'jquery-ui-datepicker' ), EDIT_FLOW_VERSION, true );
225 + wp_enqueue_script( 'edit_flow-date_picker', EDIT_FLOW_URL . 'common/js/ef_date.js', array( 'jquery', 'jquery-ui-datepicker', 'edit_flow-timepicker' ), EDIT_FLOW_VERSION, true );
219 226
220 - return array(
221 - (object) array(
222 - 'name' => __( 'Draft', 'edit-flow' ),
223 - 'description' => '',
224 - 'slug' => 'draft',
225 - 'position' => 1,
226 - ),
227 - (object) array(
228 - 'name' => __( 'Pending Review', 'edit-flow' ),
229 - 'description' => '',
230 - 'slug' => 'pending',
231 - 'position' => 2,
232 - ),
233 - );
234 - }
227 + // Now styles
228 + wp_enqueue_style( 'jquery-ui-datepicker', EDIT_FLOW_URL . 'common/css/jquery.ui.datepicker.css', array( 'wp-jquery-ui-dialog' ), EDIT_FLOW_VERSION, 'screen' );
229 + wp_enqueue_style( 'jquery-ui-theme', EDIT_FLOW_URL . 'common/css/jquery.ui.theme.css', false, EDIT_FLOW_VERSION, 'screen' );
230 + }
235 231
236 - /**
237 - * Gets the name of the default custom status. If custom statuses are disabled,
238 - * returns 'draft'.
239 - *
240 - * @return string Name of the status.
241 - */
242 - public function get_default_post_status() {
232 + /**
233 + * Checks for the current post type
234 + *
235 + * @since 0.7
236 + * @return string|null $post_type The post type we've found, or null if no post type
237 + */
238 + function get_current_post_type() {
239 + global $post, $typenow, $pagenow, $current_screen;
240 + //get_post() needs a variable
241 + $post_id = isset( $_REQUEST['post'] ) ? (int)$_REQUEST['post'] : false;
243 242
244 - // Check if custom status module is enabled.
245 - $custom_status_module = EditFlow()->custom_status->module->options;
243 + if ( $post && $post->post_type ) {
244 + $post_type = $post->post_type;
245 + } elseif ( $typenow ) {
246 + $post_type = $typenow;
247 + } elseif ( $current_screen && !empty( $current_screen->post_type ) ) {
248 + $post_type = $current_screen->post_type;
249 + } elseif ( isset( $_REQUEST['post_type'] ) ) {
250 + $post_type = sanitize_key( $_REQUEST['post_type'] );
251 + } elseif ( 'post.php' == $pagenow
252 + && $post_id
253 + && !empty( get_post( $post_id )->post_type ) ) {
254 + $post_type = get_post( $post_id )->post_type;
255 + } elseif ( 'edit.php' == $pagenow && empty( $_REQUEST['post_type'] ) ) {
256 + $post_type = 'post';
257 + } else {
258 + $post_type = null;
259 + }
246 260
247 - if ( 'on' == $custom_status_module->enabled ) {
248 - return $custom_status_module->default_status;
249 - } else {
250 - return 'draft';
251 - }
252 - }
261 + return $post_type;
262 + }
253 263
254 - /**
255 - * Filter to all posts with a given post status (can be a custom status or a built-in status) and optional custom post type.
256 - *
257 - * @since 0.7
258 - *
259 - * @param string $slug The slug for the post status to which to filter.
260 - * @param string $post_type Optional post type to which to filter.
261 - * @return string An edit.php link to all posts with the given post status and, optionally, the given post type.
262 - */
263 - public function filter_posts_link( $slug, $post_type = 'post' ) {
264 - $filter_link = add_query_arg( 'post_status', $slug, get_admin_url( null, 'edit.php' ) );
265 - if ( 'post' != $post_type && in_array( $post_type, get_post_types( '', 'names' ) ) ) {
266 - $filter_link = add_query_arg( 'post_type', $post_type, $filter_link );
267 - }
268 - return $filter_link;
269 - }
264 + /**
265 + * Wrapper for the get_user_meta() function so we can replace it if we need to
266 + *
267 + * @since 0.7
268 + *
269 + * @param int $user_id Unique ID for the user
270 + * @param string $key Key to search against
271 + * @param bool $single Whether or not to return just one value
272 + * @return string|bool|array $value Whatever the stored value was
273 + */
274 + function get_user_meta( $user_id, $key, $string = true ) {
270 275
271 - /**
272 - * Enqueue any resources (CSS or JS) associated with datepicker functionality.
273 - *
274 - * @since 0.7
275 - */
276 - public function enqueue_datepicker_resources() {
276 + $response = null;
277 + $response = apply_filters( 'ef_get_user_meta', $response, $user_id, $key, $string );
278 + if ( !is_null( $response ) )
279 + return $response;
277 280
278 - wp_enqueue_script( 'jquery-ui-datepicker' );
281 + return get_user_meta( $user_id, $key, $string );
279 282
280 - // Build script dependencies. Add wp-data for Gutenberg integration if available.
281 - $dependencies = array( 'jquery', 'jquery-ui-datepicker' );
282 - if ( function_exists( 'use_block_editor_for_post' ) && use_block_editor_for_post( get_post() ) ) {
283 - $dependencies[] = 'wp-data';
284 - }
283 + }
285 284
286 - wp_enqueue_script( 'edit_flow-date_picker', EDIT_FLOW_URL . 'common/js/ef_date.js', $dependencies, EDIT_FLOW_VERSION, true );
287 - wp_add_inline_script( 'edit_flow-date_picker', sprintf( 'var ef_week_first_day = %s;', wp_json_encode( get_option( 'start_of_week' ) ) ), 'before' );
285 + /**
286 + * Wrapper for the update_user_meta() function so we can replace it if we need to
287 + *
288 + * @since 0.7
289 + *
290 + * @param int $user_id Unique ID for the user
291 + * @param string $key Key to search against
292 + * @param string|bool|array $value Whether or not to return just one value
293 + * @param string|bool|array $previous (optional) Previous value to replace
294 + * @return bool $success Whether we were successful in saving
295 + */
296 + function update_user_meta( $user_id, $key, $value, $previous = null ) {
288 297
289 - // Now styles.
290 - wp_enqueue_style( 'jquery-ui-datepicker', EDIT_FLOW_URL . 'common/css/jquery.ui.datepicker.css', array( 'wp-jquery-ui-dialog' ), EDIT_FLOW_VERSION, 'screen' );
291 - wp_enqueue_style( 'jquery-ui-theme', EDIT_FLOW_URL . 'common/css/jquery.ui.theme.css', false, EDIT_FLOW_VERSION, 'screen' );
292 - }
298 + $response = null;
299 + $response = apply_filters( 'ef_update_user_meta', $response, $user_id, $key, $value, $previous );
300 + if ( !is_null( $response ) )
301 + return $response;
293 302
294 - /**
295 - * Checks for the current post type.
296 - *
297 - * @since 0.7
298 - *
299 - * @return string|null The post type we've found, or null if no post type.
300 - */
301 - public function get_current_post_type() {
302 - global $post, $typenow, $pagenow, $current_screen;
303 - // get_post() needs a variable.
304 - // phpcs:disable WordPress.Security.NonceVerification.Recommended -- Reading post type from REQUEST for context detection, not processing form data.
305 - $post_id = isset( $_REQUEST['post'] ) ? (int) $_REQUEST['post'] : false;
303 + return update_user_meta( $user_id, $key, $value, $previous );
306 304
307 - if ( $post && $post->post_type ) {
308 - $post_type = $post->post_type;
309 - } elseif ( $typenow ) {
310 - $post_type = $typenow;
311 - } elseif ( $current_screen && ! empty( $current_screen->post_type ) ) {
312 - $post_type = $current_screen->post_type;
313 - } elseif ( isset( $_REQUEST['post_type'] ) ) {
314 - $post_type = sanitize_key( $_REQUEST['post_type'] );
315 - } elseif ( 'post.php' == $pagenow
316 - && $post_id
317 - && ! empty( get_post( $post_id )->post_type ) ) {
318 - $post_type = get_post( $post_id )->post_type;
319 - } elseif ( 'edit.php' == $pagenow && empty( $_REQUEST['post_type'] ) ) {
320 - $post_type = 'post';
321 - } else {
322 - $post_type = null;
323 - }
324 - // phpcs:enable WordPress.Security.NonceVerification.Recommended
305 + }
325 306
326 - return $post_type;
327 - }
307 + /**
308 + * Take a status and a message, JSON encode and print
309 + *
310 + * @since 0.7
311 + *
312 + * @param string $status Whether it was a 'success' or an 'error'
313 + */
314 + function print_ajax_response( $status, $message = '' ) {
315 + header( 'Content-type: application/json;' );
316 + echo json_encode( array( 'status' => $status, 'message' => $message ) );
317 + exit;
318 + }
328 319
329 - /**
330 - * Wrapper for the get_user_meta() function so we can replace it if we need to.
331 - *
332 - * @since 0.7
333 - *
334 - * @param int $user_id Unique ID for the user.
335 - * @param string $key Key to search against.
336 - * @param bool $string Whether or not to return just one value.
337 - * @return string|bool|array Whatever the stored value was.
338 - */
339 - public function get_user_meta( $user_id, $key, $string = true ) { // phpcs:ignore Universal.NamingConventions.NoReservedKeywordParameterNames.stringFound -- Legacy parameter name.
320 + /**
321 + * Whether or not the current page is a user-facing Edit Flow View
322 + * @todo Think of a creative way to make this work
323 + *
324 + * @since 0.7
325 + *
326 + * @param string $module_name (Optional) Module name to check against
327 + */
328 + function is_whitelisted_functional_view( $module_name = null ) {
340 329
341 - $response = null;
342 - $response = apply_filters( 'ef_get_user_meta', $response, $user_id, $key, $string );
343 - if ( ! is_null( $response ) ) {
344 - return $response;
345 - }
330 + // @todo complete this method
346 331
347 - return get_user_meta( $user_id, $key, $string );
348 - }
332 + return true;
333 + }
349 334
350 - /**
351 - * Wrapper for the update_user_meta() function so we can replace it if we need to.
352 - *
353 - * @since 0.7
354 - *
355 - * @param int $user_id Unique ID for the user.
356 - * @param string $key Key to search against.
357 - * @param string|bool|array $value The value to store.
358 - * @param string|bool|array $previous Previous value to replace.
359 - * @return bool Whether we were successful in saving.
360 - */
361 - public function update_user_meta( $user_id, $key, $value, $previous = null ) {
335 + /**
336 + * Whether or not the current page is an Edit Flow settings view (either main or module)
337 + * Determination is based on $pagenow, $_GET['page'], and the module's $settings_slug
338 + * If there's no module name specified, it will return true against all Edit Flow settings views
339 + *
340 + * @since 0.7
341 + *
342 + * @param string $module_name (Optional) Module name to check against
343 + * @return bool $is_settings_view Return true if it is
344 + */
345 + function is_whitelisted_settings_view( $module_name = null ) {
346 + global $pagenow, $edit_flow;
362 347
363 - $response = null;
364 - $response = apply_filters( 'ef_update_user_meta', $response, $user_id, $key, $value, $previous );
365 - if ( ! is_null( $response ) ) {
366 - return $response;
367 - }
348 + // All of the settings views are based on admin.php and a $_GET['page'] parameter
349 + if ( $pagenow != 'admin.php' || !isset( $_GET['page'] ) )
350 + return false;
368 351
369 - return update_user_meta( $user_id, $key, $value, $previous );
352 + // Load all of the modules that have a settings slug/ callback for the settings page
353 + foreach ( $edit_flow->modules as $mod_name => $mod_data ) {
354 + if ( isset( $mod_data->options->enabled ) && $mod_data->options->enabled == 'on' && $mod_data->configure_page_cb )
355 + $settings_view_slugs[] = $mod_data->settings_slug;
370 356 }
371 357
372 - /**
373 - * Take a status and a message, JSON encode and print.
374 - *
375 - * @since 0.7
376 - *
377 - * @param string $status Whether it was a 'success' or an 'error'.
378 - * @param string $message Optional message. SECURITY: the caller is responsible for
379 - * escaping this. Several JS consumers insert it as HTML (e.g.
380 - * via jQuery .html()), so pass only fixed/translated strings,
381 - * esc_html()'d values, or trusted server-generated markup —
382 - * never raw, unescaped user input.
383 - * @param int $http_code HTTP response code.
384 - */
385 - protected function print_ajax_response( $status, $message = '', $http_code = 200 ) {
386 - header( 'Content-type: application/json;' );
387 - http_response_code( $http_code );
388 - echo wp_json_encode(
389 - array(
390 - 'status' => $status,
391 - 'message' => $message,
392 - )
393 - );
394 - wp_die();
395 - }
358 + // The current page better be in the array of registered settings view slugs
359 + if ( !in_array( $_GET['page'], $settings_view_slugs ) )
360 + return false;
396 361
397 - /**
398 - * Whether or not the current page is a post management page.
399 - *
400 - * A post management page is where the module's functionality is actually
401 - * needed, such as post editing pages (post.php, post-new.php) or post listing
402 - * pages (edit.php) for supported post types.
403 - *
404 - * @since 0.7
405 - * @since 0.10.0 Actually implemented instead of returning true. Renamed from
406 - * is_whitelisted_functional_view().
407 - *
408 - * @see https://github.com/Automattic/Edit-Flow/issues/351
409 - *
410 - * @param string $module_name (Optional) Module name to check against.
411 - * @return bool Whether the current page is a post management page for the module.
412 - */
413 - public function is_post_management_page( $module_name = null ) {
414 - global $pagenow, $edit_flow;
362 + if ( $module_name && $edit_flow->modules->$module_name->settings_slug != $_GET['page'] )
363 + return false;
415 364
416 - // Only load on post editing and listing pages.
417 - $functional_pages = [ 'post.php', 'post-new.php', 'edit.php' ];
418 - if ( ! in_array( $pagenow, $functional_pages, true ) ) {
419 - return false;
420 - }
365 + return true;
366 + }
421 367
422 - // Get the current post type.
423 - $current_post_type = $this->get_current_post_type();
424 - if ( ! $current_post_type ) {
425 - return false;
426 - }
427 368
428 - // If a module name is specified, check if this post type is supported by that module.
429 - if ( $module_name && isset( $edit_flow->modules->$module_name ) ) {
430 - $module = $edit_flow->modules->$module_name;
431 - $supported_post_types = $this->get_post_types_for_module( $module );
432 - if ( ! in_array( $current_post_type, $supported_post_types, true ) ) {
433 - return false;
434 - }
435 - }
369 + /**
370 + * This is a hack, Hack, HACK!!!
371 + * Encode all of the given arguments as a serialized array, and then base64_encode
372 + * Used to store extra data in a term's description field
373 + *
374 + * @since 0.7
375 + *
376 + * @param array $args The arguments to encode
377 + * @return string Arguments encoded in base64
378 + */
379 + function get_encoded_description( $args = array() ) {
380 + return base64_encode( maybe_serialize( $args ) );
381 + }
436 382
437 - return true;
438 - }
383 + /**
384 + * If given an encoded string from a term's description field,
385 + * return an array of values. Otherwise, return the original string
386 + *
387 + * @since 0.7
388 + *
389 + * @param string $string_to_unencode Possibly encoded string
390 + * @return array Array if string was encoded, otherwise the string as the 'description' field
391 + */
392 + function get_unencoded_description( $string_to_unencode ) {
393 + return maybe_unserialize( base64_decode( $string_to_unencode ) );
394 + }
439 395
440 - /**
441 - * Whether or not the current page is an Edit Flow settings view (either main or module).
442 - *
443 - * Determination is based on $pagenow, $_GET['page'], and the module's $settings_slug.
444 - * If there's no module name specified, it will return true against all Edit Flow settings views.
445 - *
446 - * @since 0.7
447 - *
448 - * @param string $module_name Optional module name to check against.
449 - * @return bool Return true if it is.
450 - */
451 - public function is_whitelisted_settings_view( $module_name = null ) {
452 - global $pagenow, $edit_flow;
396 + /**
397 + * Get the publicly accessible URL for the module based on the filename
398 + *
399 + * @since 0.7
400 + *
401 + * @param string $filepath File path for the module
402 + * @return string $module_url Publicly accessible URL for the module
403 + */
404 + function get_module_url( $file ) {
405 + $module_url = plugins_url( '/', $file );
406 + return trailingslashit( $module_url );
407 + }
453 408
454 - // All of the settings views are based on admin.php and a $_GET['page'] parameter.
455 - // phpcs:disable WordPress.Security.NonceVerification.Recommended -- Checking page parameter for context, not processing form data.
456 - if ( 'admin.php' != $pagenow || ! isset( $_GET['page'] ) ) {
457 - return false;
458 - }
409 + /**
410 + * Produce a human-readable version of the time since a timestamp
411 + *
412 + * @param int $original The UNIX timestamp we're producing a relative time for
413 + * @return string $relative_time Human-readable version of the difference between the timestamp and now
414 + */
415 + function timesince( $original ) {
416 + // array of time period chunks
417 + $chunks = array(
418 + array(60 * 60 * 24 * 365 , 'year'),
419 + array(60 * 60 * 24 * 30 , 'month'),
420 + array(60 * 60 * 24 * 7, 'week'),
421 + array(60 * 60 * 24 , 'day'),
422 + array(60 * 60 , 'hour'),
423 + array(60 , 'minute'),
424 + array(1 , 'second'),
425 + );
459 426
460 - // Load all of the modules that have a settings slug/ callback for the settings page.
461 - foreach ( $edit_flow->modules as $mod_name => $mod_data ) {
462 - if ( isset( $mod_data->options->enabled ) && 'on' == $mod_data->options->enabled && $mod_data->configure_page_cb ) {
463 - $settings_view_slugs[] = $mod_data->settings_slug;
464 - }
465 - }
427 + $today = time(); /* Current unix time */
428 + $since = $today - $original;
466 429
467 - // The current page better be in the array of registered settings view slugs.
468 - if ( ! in_array( $_GET['page'], $settings_view_slugs, true ) ) {
469 - return false;
470 - }
430 + if ( $since > $chunks[2][0] ) {
431 + $print = date("M jS", $original);
471 432
472 - if ( $module_name && $edit_flow->modules->$module_name->settings_slug !== $_GET['page'] ) {
473 - return false;
433 + if( $since > $chunks[0][0] ) { // Seconds in a year
434 + $print .= ", " . date( "Y", $original );
474 435 }
475 - // phpcs:enable WordPress.Security.NonceVerification.Recommended
476 436
477 - return true;
437 + return $print;
478 438 }
479 439
440 + // $j saves performing the count function each time around the loop
441 + for ($i = 0, $j = count($chunks); $i < $j; $i++) {
480 442
481 - /**
482 - * This is a hack, Hack, HACK!!!
483 - *
484 - * Encode all of the given arguments as a serialized array, and then base64_encode.
485 - * Used to store extra data in a term's description field.
486 - *
487 - * @since 0.7
488 - *
489 - * @param array $args The arguments to encode.
490 - * @return string Arguments encoded in base64.
491 - */
492 - public function get_encoded_description( $args = array() ) {
493 - // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode -- Required for term description storage.
494 - return base64_encode( maybe_serialize( $args ) );
495 - }
443 + $seconds = $chunks[$i][0];
444 + $name = $chunks[$i][1];
496 445
497 - /**
498 - * If given an encoded string from a term's description field,
499 - * return an array of values. Otherwise, return the original string.
500 - *
501 - * @since 0.7
502 - *
503 - * @param string $string_to_unencode Possibly encoded string.
504 - * @return array Array if string was encoded, otherwise the string as the 'description' field.
505 - */
506 - public function get_unencoded_description( $string_to_unencode ) {
507 - // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode -- Required for term description retrieval.
508 - $decoded = base64_decode( $string_to_unencode );
509 -
510 - /*
511 - * Edit Flow only ever stores a serialized array here (see get_encoded_description()),
512 - * so forbid object instantiation during unserialisation. Without allowed_classes a
513 - * crafted term description could trigger PHP object injection. is_serialized() mirrors
514 - * maybe_unserialize()'s own gate, so a plain (unencoded) description still round-trips
515 - * unchanged and the callers' is_array() checks keep working.
516 - */
517 - if ( is_serialized( $decoded ) ) {
518 - // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_unserialize -- Hardened with allowed_classes => false; stored data is always a scalar array.
519 - return unserialize( $decoded, array( 'allowed_classes' => false ) );
446 + // finding the biggest chunk (if the chunk fits, break)
447 + if (($count = floor($since / $seconds)) != 0) {
448 + break;
520 449 }
521 -
522 - return $decoded;
523 450 }
524 451
525 - /**
526 - * Get the publicly accessible URL for the module based on the filename.
527 - *
528 - * @since 0.7
529 - *
530 - * @param string $file File path for the module.
531 - * @return string Publicly accessible URL for the module.
532 - */
533 - public function get_module_url( $file ) {
534 - $module_url = plugins_url( '/', $file );
535 - return trailingslashit( $module_url );
536 - }
452 + return sprintf( _n( "1 $name ago", "$count ${name}s ago", $count), $count);
453 + }
537 454
538 - /**
539 - * Displays a list of users that can be selected!
540 - *
541 - * @since 0.7
542 - *
543 - * @todo Add pagination support for blogs with billions of users.
544 - *
545 - * @param array|null $selected Selected users.
546 - * @param array|null $args Optional arguments for the form.
547 - */
548 - public function users_select_form( $selected = null, $args = null ) {
455 + /**
456 + * Displays a list of users that can be selected!
457 + *
458 + * @since 0.7
459 + *
460 + * @todo Add pagination support for blogs with billions of users
461 + *
462 + * @param ???
463 + * @param ???
464 + */
465 + function users_select_form( $selected = null, $args = null ) {
549 466
550 - // Set up arguments.
551 - $defaults = array(
552 - 'list_class' => 'ef-users-select-form',
553 - 'input_id' => 'ef-selected-users',
554 - );
555 - $parsed_args = wp_parse_args( $args, $defaults );
556 - $list_class = $parsed_args['list_class'];
557 - $input_id = $parsed_args['input_id'];
467 + // Set up arguments
468 + $defaults = array(
469 + 'list_class' => 'ef-users-select-form',
470 + 'input_id' => 'ef-selected-users'
471 + );
472 + $parsed_args = wp_parse_args( $args, $defaults );
473 + extract($parsed_args, EXTR_SKIP);
558 474
559 - $args = array(
560 - 'capability' => 'publish_posts',
561 - 'fields' => array(
562 - 'ID',
563 - 'display_name',
564 - 'user_nicename',
565 - 'user_email',
566 - ),
567 - 'orderby' => 'display_name',
568 - );
569 - $args = apply_filters( 'ef_users_select_form_get_users_args', $args );
475 + $args = array(
476 + 'who' => 'authors',
477 + 'fields' => array(
478 + 'ID',
479 + 'display_name',
480 + 'user_email'
481 + ),
482 + 'orderby' => 'display_name',
483 + );
484 + $args = apply_filters( 'ef_users_select_form_get_users_args', $args );
570 485
571 - $users = get_users( $args );
486 + $users = get_users( $args );
572 487
573 - if ( ! is_array( $selected ) ) {
574 - $selected = array();
575 - }
576 - ?>
488 + if ( !is_array($selected) ) $selected = array();
489 + ?>
577 490
578 - <?php if ( ! empty( $users ) ) : ?>
579 - <ul class="<?php echo esc_attr( $list_class ); ?>">
580 - <?php
581 - foreach ( $users as $user ) :
582 - $checked = in_array( (int) $user->ID, array_map( 'intval', (array) $selected ), true );
583 - ?>
491 + <?php if( !empty($users) ) : ?>
492 + <ul class="<?php echo esc_attr( $list_class ) ?>">
493 + <?php foreach( $users as $user ) :
494 + $checked = ( in_array($user->ID, $selected) ) ? 'checked="checked"' : '';
495 + // Add a class to checkbox of current user so we know not to add them in notified list during notifiedMessage() js function
496 + $current_user_class = ( get_current_user_id() == $user->ID ) ? 'class="post_following_list-current_user" ' : '';
497 + ?>
584 498 <li>
585 - <label for="<?php echo esc_attr( $input_id . '-' . $user->ID ); ?>">
499 + <label for="<?php echo esc_attr( $input_id .'-'. $user->ID ) ?>">
586 500 <div class="ef-user-subscribe-actions">
587 - <?php do_action( 'ef_user_subscribe_actions', $user->ID, $checked ); ?>
588 - <input type="checkbox" id="<?php echo esc_attr( $input_id . '-' . $user->ID ); ?>" name="<?php echo esc_attr( $input_id ); ?>[]" value="<?php echo esc_attr( $user->ID ); ?>"
589 - <?php
590 - if ( $checked ) {
591 - echo "checked='checked' ";
592 - }
593 - // Add a class to checkbox of current user so we know not to add them in notified list during notifiedMessage() js function.
594 - if ( get_current_user_id() == $user->ID ) {
595 - echo 'class="post_following_list-current_user" ';
596 - }
597 - ?>
598 - />
501 + <?php do_action( 'ef_user_subscribe_actions', $user->ID, $checked ) ?>
502 + <input type="checkbox" id="<?php echo esc_attr( $input_id .'-'. $user->ID ) ?>" name="<?php echo esc_attr( $input_id ) ?>[]" value="<?php echo esc_attr( $user->ID ); ?>" <?php echo $checked; echo $current_user_class; ?> />
599 503 </div>
600 504
601 505 <span class="ef-user_displayname"><?php echo esc_html( $user->display_name ); ?></span>
602 - <?php
603 - /**
604 - * Filters the secondary user identifier shown in the notifications list.
605 - *
606 - * By default, shows user_nicename for unique identification without exposing email.
607 - * Return user_email to show email addresses, or empty string to hide.
608 - *
609 - * @since 0.10.1
610 - *
611 - * @param string $identifier The secondary identifier to display.
612 - * @param object $user The user object.
613 - */
614 - $secondary_identifier = apply_filters( 'ef_user_secondary_identifier', $user->user_nicename, $user );
615 - if ( ! empty( $secondary_identifier ) ) :
616 - ?>
617 - <span class="ef-user_useremail"><?php echo esc_html( $secondary_identifier ); ?></span>
618 - <?php endif; ?>
506 + <span class="ef-user_useremail"><?php echo esc_html( $user->user_email ); ?></span>
619 507 </label>
620 508 </li>
621 509 <?php endforeach; ?>
622 510 </ul>
623 511 <?php endif; ?>
624 - <?php
625 - }
512 + <?php
513 + }
626 514
627 - /**
628 - * Adds an array of capabilities to a role.
629 - *
630 - * @since 0.7
631 - *
632 - * @param string $role A standard WP user role like 'administrator' or 'author'.
633 - * @param array $caps One or more user caps to add.
634 - */
635 - public function add_caps_to_role( $role, $caps ) {
515 + /**
516 + * Adds an array of capabilities to a role.
517 + *
518 + * @since 0.7
519 + *
520 + * @param string $role A standard WP user role like 'administrator' or 'author'
521 + * @param array $caps One or more user caps to add
522 + */
523 + function add_caps_to_role( $role, $caps ) {
636 524
637 - // In some contexts, we don't want to add caps to roles.
638 - if ( apply_filters( 'ef_kill_add_caps_to_role', false, $role, $caps ) ) {
639 - return;
640 - }
525 + // In some contexts, we don't want to add caps to roles
526 + if ( apply_filters( 'ef_kill_add_caps_to_role', false, $role, $caps ) )
527 + return;
641 528
642 - global $wp_roles;
529 + global $wp_roles;
643 530
644 - if ( $wp_roles->is_role( $role ) ) {
645 - $role = get_role( $role );
646 - foreach ( $caps as $cap ) {
647 - $role->add_cap( $cap );
648 - }
531 + if ( $wp_roles->is_role( $role ) ) {
532 + $role = get_role( $role );
533 + foreach ( $caps as $cap ) {
534 + $role->add_cap( $cap );
649 535 }
650 536 }
537 + }
651 538
652 - /**
653 - * Add settings help menus to our module screens if the values exist.
654 - *
655 - * Auto-registered in Edit_Flow::register_module().
656 - *
657 - * @since 0.7
658 - */
659 - public function action_settings_help_menu() {
539 + /**
540 + * Add settings help menus to our module screens if the values exist
541 + * Auto-registered in Edit_Flow::register_module()
542 + *
543 + * @since 0.7
544 + */
545 + function action_settings_help_menu() {
660 546
661 - $screen = get_current_screen();
547 + $screen = get_current_screen();
662 548
663 - if ( ! method_exists( $screen, 'add_help_tab' ) ) {
664 - return;
665 - }
549 + if ( !method_exists( $screen, 'add_help_tab' ) )
550 + return;
666 551
667 - if ( 'edit-flow_page_' . $this->module->settings_slug != $screen->id ) {
668 - return;
669 - }
552 + if ( $screen->id != 'edit-flow_page_' . $this->module->settings_slug )
553 + return;
670 554
671 - // Make sure we have all of the required values for our tab.
672 - if ( isset( $this->module->settings_help_tab['id'], $this->module->settings_help_tab['title'], $this->module->settings_help_tab['content'] ) ) {
673 - $screen->add_help_tab( $this->module->settings_help_tab );
555 + // Make sure we have all of the required values for our tab
556 + if ( isset( $this->module->settings_help_tab['id'], $this->module->settings_help_tab['title'], $this->module->settings_help_tab['content'] ) ) {
557 + $screen->add_help_tab( $this->module->settings_help_tab );
674 558
675 - if ( isset( $this->module->settings_help_sidebar ) ) {
676 - $screen->set_help_sidebar( $this->module->settings_help_sidebar );
677 - }
559 + if ( isset( $this->module->settings_help_sidebar ) ) {
560 + $screen->set_help_sidebar( $this->module->settings_help_sidebar );
678 561 }
679 562 }
563 + }
680 564
681 - /**
682 - * Upgrade the term descriptions for all of the terms in a given taxonomy.
683 - *
684 - * @param string $taxonomy The taxonomy to upgrade.
685 - */
686 - public function upgrade_074_term_descriptions( $taxonomy ) {
687 - $args = array(
688 - 'hide_empty' => false,
689 - );
690 - // This is migration code, so it's being left as is for now.
691 - // phpcs:ignore WordPress.WP.DeprecatedParameters.Get_termsParam2Found
692 - $terms = get_terms( $taxonomy, $args );
693 - foreach ( $terms as $term ) {
694 - // If we can detect that this term already follows the new scheme, let's skip it.
695 - // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode -- Required for term description retrieval.
696 - $maybe_serialized = base64_decode( $term->description );
697 - if ( is_serialized( $maybe_serialized ) ) {
698 - continue;
699 - }
565 + /**
566 + * Upgrade the term descriptions for all of the terms in a given taxonomy
567 + */
568 + function upgrade_074_term_descriptions( $taxonomy ) {
569 + $args = array(
570 + 'hide_empty' => false,
571 + );
572 + $terms = get_terms( $taxonomy, $args );
573 + foreach( $terms as $term ) {
574 + // If we can detect that this term already follows the new scheme, let's skip it
575 + $maybe_serialized = base64_decode( $term->description );
576 + if ( is_serialized( $maybe_serialized ) )
577 + continue;
700 578
701 - $description_args = array();
702 - // This description has been JSON-encoded, so let's decode it.
703 - if ( 0 === strpos( $term->description, '{' ) ) {
704 - $string_to_unencode = stripslashes( htmlspecialchars_decode( $term->description ) );
705 - $unencoded_array = json_decode( $string_to_unencode, true );
706 - // Only continue processing if it actually was an array. Otherwise, set to the original string.
707 - if ( is_array( $unencoded_array ) ) {
708 - foreach ( $unencoded_array as $key => $value ) {
709 - // html_entity_decode only works on strings but sometimes we store nested arrays.
710 - if ( ! is_array( $value ) ) {
711 - $description_args[ $key ] = html_entity_decode( $value, ENT_QUOTES );
712 - } else {
713 - $description_args[ $key ] = $value;
714 - }
715 - }
579 + $description_args = array();
580 + // This description has been JSON-encoded, so let's decode it
581 + if ( 0 === strpos( $term->description, '{' ) ) {
582 + $string_to_unencode = stripslashes( htmlspecialchars_decode( $term->description ) );
583 + $unencoded_array = json_decode( $string_to_unencode, true );
584 + // Only continue processing if it actually was an array. Otherwise, set to the original string
585 + if ( is_array( $unencoded_array ) ) {
586 + foreach( $unencoded_array as $key => $value ) {
587 + // html_entity_decode only works on strings but sometimes we store nested arrays
588 + if ( !is_array( $value ) )
589 + $description_args[$key] = html_entity_decode( $value, ENT_QUOTES );
590 + else
591 + $description_args[$key] = $value;
716 592 }
717 - } else {
718 - $description_args['description'] = $term->description;
719 593 }
720 - $new_description = $this->get_encoded_description( $description_args );
721 - wp_update_term( $term->term_id, $taxonomy, array( 'description' => $new_description ) );
594 + } else {
595 + $description_args['description'] = $term->description;
722 596 }
597 + $new_description = $this->get_encoded_description( $description_args );
598 + wp_update_term( $term->term_id, $taxonomy, array( 'description' => $new_description ) );
723 599 }
724 600 }
725 601
602 + /**
603 + * Return compatibility hooks for the current instance
604 + *
605 + * @return array
606 + */
607 + function get_compat_hooks() {
608 + return isset( $this->compat_hooks ) && is_array( $this->compat_hooks ) ? $this->compat_hooks : [];
609 + }
610 +
611 +}
726 612 }