PluginProbe
Edit Flow / 0.9
Edit Flow v0.9
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
edit-flow / common / php / class-module.php

class-module.php in Edit Flow 0.9, at common/php/class-module.php

602 lines 18.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * class EF_Module
4 *
5 * @desc Base class any Edit Flow module should extend
6 */
7
8 if ( !class_exists( 'EF_Module' ) ) {
9
10 class EF_Module {
11
12 public $published_statuses = array(
13 'publish',
14 'future',
15 'private',
16 );
17
18 /**
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
25 */
26 protected $compat_hooks = [];
27
28 function __construct() {}
29
30 /**
31 * Returns whether the module with the given name is enabled.
32 *
33 * @since 0.7
34 *
35 * @param string module Slug of the module to check
36 * @return <code>true</code> if the module is enabled, <code>false</code> otherwise
37 */
38 function module_enabled( $slug ) {
39 global $edit_flow;
40
41 return isset( $edit_flow->$slug ) && $edit_flow->$slug->module->options->enabled == 'on';
42 }
43
44 /**
45 * Gets an array of allowed post types for a module
46 *
47 * @return array post-type-slug => post-type-label
48 */
49 function get_all_post_types() {
50
51 $allowed_post_types = array(
52 'post' => __( 'Post' ),
53 'page' => __( 'Page' ),
54 );
55 $custom_post_types = $this->get_supported_post_types_for_module();
56
57 foreach( $custom_post_types as $custom_post_type => $args ) {
58 $allowed_post_types[$custom_post_type] = $args->label;
59 }
60 return $allowed_post_types;
61 }
62
63 /**
64 * Cleans up the 'on' and 'off' for post types on a given module (so we don't get warnings all over)
65 * For every post type that doesn't explicitly have the 'on' value, turn it 'off'
66 * If add_post_type_support() has been used anywhere (legacy support), inherit the state
67 *
68 * @param array $module_post_types Current state of post type options for the module
69 * @param string $post_type_support What the feature is called for post_type_support (e.g. 'ef_calendar')
70 * @return array $normalized_post_type_options The setting for each post type, normalized based on rules
71 *
72 * @since 0.7
73 */
74 function clean_post_type_options( $module_post_types = array(), $post_type_support = null ) {
75 $normalized_post_type_options = array();
76 $all_post_types = array_keys( $this->get_all_post_types() );
77 foreach( $all_post_types as $post_type ) {
78 if ( ( isset( $module_post_types[$post_type] ) && $module_post_types[$post_type] == 'on' ) || post_type_supports( $post_type, $post_type_support ) )
79 $normalized_post_type_options[$post_type] = 'on';
80 else
81 $normalized_post_type_options[$post_type] = 'off';
82 }
83 return $normalized_post_type_options;
84 }
85
86 /**
87 * Get all of the possible post types that can be used with a given module
88 *
89 * @param object $module The full module
90 * @return array $post_types An array of post type objects
91 *
92 * @since 0.7.2
93 */
94 function get_supported_post_types_for_module( $module = null ) {
95
96 $pt_args = array(
97 '_builtin' => false,
98 'public' => true,
99 );
100 $pt_args = apply_filters( 'edit_flow_supported_module_post_types_args', $pt_args, $module );
101 return get_post_types( $pt_args, 'objects' );
102 }
103
104 /**
105 * Collect all of the active post types for a given module
106 *
107 * @param object $module Module's data
108 * @return array $post_types All of the post types that are 'on'
109 *
110 * @since 0.7
111 */
112 function get_post_types_for_module( $module ) {
113
114 $post_types = array();
115 if ( isset( $module->options->post_types ) && is_array( $module->options->post_types ) ) {
116 foreach( $module->options->post_types as $post_type => $value )
117 if ( 'on' == $value )
118 $post_types[] = $post_type;
119 }
120 return $post_types;
121 }
122
123 /**
124 * Get all of the currently available post statuses
125 * This should be used in favor of calling $edit_flow->custom_status->get_custom_statuses() directly
126 *
127 * @return array $post_statuses All of the post statuses that aren't a published state
128 *
129 * @since 0.7
130 */
131 function get_post_statuses() {
132 global $edit_flow;
133
134 if ( $this->module_enabled('custom_status') ) {
135 return $edit_flow->custom_status->get_custom_statuses();
136 } else {
137 return $this->get_core_post_statuses();
138 }
139 }
140
141 /**
142 * Get core's 'draft' and 'pending' post statuses, but include our special attributes
143 *
144 * @since 0.8.1
145 *
146 * @return array
147 */
148 protected function get_core_post_statuses() {
149
150 return array(
151 (object)array(
152 'name' => __( 'Draft' ),
153 'description' => '',
154 'slug' => 'draft',
155 'position' => 1,
156 ),
157 (object)array(
158 'name' => __( 'Pending Review' ),
159 'description' => '',
160 'slug' => 'pending',
161 'position' => 2,
162 ),
163 );
164 }
165
166 /**
167 * Gets the name of the default custom status. If custom statuses are disabled,
168 * returns 'draft'.
169 *
170 * @return str Name of the status
171 */
172 function get_default_post_status(){
173
174 // Check if custom status module is enabled
175 $custom_status_module = EditFlow()->custom_status->module->options;
176
177 if( $custom_status_module->enabled == 'on' )
178 return $custom_status_module->default_status;
179 else
180 return 'draft';
181
182 }
183
184 /**
185 * Filter to all posts with a given post status (can be a custom status or a built-in status) and optional custom post type.
186 *
187 * @since 0.7
188 *
189 * @param string $slug The slug for the post status to which to filter
190 * @param string $post_type Optional post type to which to filter
191 * @return an edit.php link to all posts with the given post status and, optionally, the given post type
192 */
193 function filter_posts_link( $slug, $post_type = 'post' ) {
194 $filter_link = add_query_arg( 'post_status', $slug, get_admin_url( null, 'edit.php' ) );
195 if ( $post_type != 'post' && in_array( $post_type, get_post_types( '', 'names' ) ) )
196 $filter_link = add_query_arg( 'post_type', $post_type, $filter_link );
197 return $filter_link;
198 }
199
200 /**
201 * Enqueue any resources (CSS or JS) associated with datepicker functionality
202 *
203 * @since 0.7
204 */
205 function enqueue_datepicker_resources() {
206
207 // Add the first day of the week as an available variable to wp_head
208 echo "<script type=\"text/javascript\">var ef_week_first_day=\"" . get_option( 'start_of_week' ) . "\";</script>";
209
210 wp_enqueue_script( 'jquery-ui-datepicker' );
211
212 //Timepicker needs to come after jquery-ui-datepicker and jquery
213 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 );
214 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 );
215
216 // Now styles
217 wp_enqueue_style( 'jquery-ui-datepicker', EDIT_FLOW_URL . 'common/css/jquery.ui.datepicker.css', array( 'wp-jquery-ui-dialog' ), EDIT_FLOW_VERSION, 'screen' );
218 wp_enqueue_style( 'jquery-ui-theme', EDIT_FLOW_URL . 'common/css/jquery.ui.theme.css', false, EDIT_FLOW_VERSION, 'screen' );
219 }
220
221 /**
222 * Checks for the current post type
223 *
224 * @since 0.7
225 * @return string|null $post_type The post type we've found, or null if no post type
226 */
227 function get_current_post_type() {
228 global $post, $typenow, $pagenow, $current_screen;
229 //get_post() needs a variable
230 $post_id = isset( $_REQUEST['post'] ) ? (int)$_REQUEST['post'] : false;
231
232 if ( $post && $post->post_type ) {
233 $post_type = $post->post_type;
234 } elseif ( $typenow ) {
235 $post_type = $typenow;
236 } elseif ( $current_screen && !empty( $current_screen->post_type ) ) {
237 $post_type = $current_screen->post_type;
238 } elseif ( isset( $_REQUEST['post_type'] ) ) {
239 $post_type = sanitize_key( $_REQUEST['post_type'] );
240 } elseif ( 'post.php' == $pagenow
241 && $post_id
242 && !empty( get_post( $post_id )->post_type ) ) {
243 $post_type = get_post( $post_id )->post_type;
244 } elseif ( 'edit.php' == $pagenow && empty( $_REQUEST['post_type'] ) ) {
245 $post_type = 'post';
246 } else {
247 $post_type = null;
248 }
249
250 return $post_type;
251 }
252
253 /**
254 * Wrapper for the get_user_meta() function so we can replace it if we need to
255 *
256 * @since 0.7
257 *
258 * @param int $user_id Unique ID for the user
259 * @param string $key Key to search against
260 * @param bool $single Whether or not to return just one value
261 * @return string|bool|array $value Whatever the stored value was
262 */
263 function get_user_meta( $user_id, $key, $string = true ) {
264
265 $response = null;
266 $response = apply_filters( 'ef_get_user_meta', $response, $user_id, $key, $string );
267 if ( !is_null( $response ) )
268 return $response;
269
270 return get_user_meta( $user_id, $key, $string );
271
272 }
273
274 /**
275 * Wrapper for the update_user_meta() function so we can replace it if we need to
276 *
277 * @since 0.7
278 *
279 * @param int $user_id Unique ID for the user
280 * @param string $key Key to search against
281 * @param string|bool|array $value Whether or not to return just one value
282 * @param string|bool|array $previous (optional) Previous value to replace
283 * @return bool $success Whether we were successful in saving
284 */
285 function update_user_meta( $user_id, $key, $value, $previous = null ) {
286
287 $response = null;
288 $response = apply_filters( 'ef_update_user_meta', $response, $user_id, $key, $value, $previous );
289 if ( !is_null( $response ) )
290 return $response;
291
292 return update_user_meta( $user_id, $key, $value, $previous );
293
294 }
295
296 /**
297 * Take a status and a message, JSON encode and print
298 *
299 * @since 0.7
300 *
301 * @param string $status Whether it was a 'success' or an 'error'
302 */
303 function print_ajax_response( $status, $message = '' ) {
304 header( 'Content-type: application/json;' );
305 echo json_encode( array( 'status' => $status, 'message' => $message ) );
306 exit;
307 }
308
309 /**
310 * Whether or not the current page is a user-facing Edit Flow View
311 * @todo Think of a creative way to make this work
312 *
313 * @since 0.7
314 *
315 * @param string $module_name (Optional) Module name to check against
316 */
317 function is_whitelisted_functional_view( $module_name = null ) {
318
319 // @todo complete this method
320
321 return true;
322 }
323
324 /**
325 * Whether or not the current page is an Edit Flow settings view (either main or module)
326 * Determination is based on $pagenow, $_GET['page'], and the module's $settings_slug
327 * If there's no module name specified, it will return true against all Edit Flow settings views
328 *
329 * @since 0.7
330 *
331 * @param string $module_name (Optional) Module name to check against
332 * @return bool $is_settings_view Return true if it is
333 */
334 function is_whitelisted_settings_view( $module_name = null ) {
335 global $pagenow, $edit_flow;
336
337 // All of the settings views are based on admin.php and a $_GET['page'] parameter
338 if ( $pagenow != 'admin.php' || !isset( $_GET['page'] ) )
339 return false;
340
341 // Load all of the modules that have a settings slug/ callback for the settings page
342 foreach ( $edit_flow->modules as $mod_name => $mod_data ) {
343 if ( isset( $mod_data->options->enabled ) && $mod_data->options->enabled == 'on' && $mod_data->configure_page_cb )
344 $settings_view_slugs[] = $mod_data->settings_slug;
345 }
346
347 // The current page better be in the array of registered settings view slugs
348 if ( !in_array( $_GET['page'], $settings_view_slugs ) )
349 return false;
350
351 if ( $module_name && $edit_flow->modules->$module_name->settings_slug != $_GET['page'] )
352 return false;
353
354 return true;
355 }
356
357
358 /**
359 * This is a hack, Hack, HACK!!!
360 * Encode all of the given arguments as a serialized array, and then base64_encode
361 * Used to store extra data in a term's description field
362 *
363 * @since 0.7
364 *
365 * @param array $args The arguments to encode
366 * @return string Arguments encoded in base64
367 */
368 function get_encoded_description( $args = array() ) {
369 return base64_encode( maybe_serialize( $args ) );
370 }
371
372 /**
373 * If given an encoded string from a term's description field,
374 * return an array of values. Otherwise, return the original string
375 *
376 * @since 0.7
377 *
378 * @param string $string_to_unencode Possibly encoded string
379 * @return array Array if string was encoded, otherwise the string as the 'description' field
380 */
381 function get_unencoded_description( $string_to_unencode ) {
382 return maybe_unserialize( base64_decode( $string_to_unencode ) );
383 }
384
385 /**
386 * Get the publicly accessible URL for the module based on the filename
387 *
388 * @since 0.7
389 *
390 * @param string $filepath File path for the module
391 * @return string $module_url Publicly accessible URL for the module
392 */
393 function get_module_url( $file ) {
394 $module_url = plugins_url( '/', $file );
395 return trailingslashit( $module_url );
396 }
397
398 /**
399 * Produce a human-readable version of the time since a timestamp
400 *
401 * @param int $original The UNIX timestamp we're producing a relative time for
402 * @return string $relative_time Human-readable version of the difference between the timestamp and now
403 */
404 function timesince( $original ) {
405 // array of time period chunks
406 $chunks = array(
407 array(60 * 60 * 24 * 365 , 'year'),
408 array(60 * 60 * 24 * 30 , 'month'),
409 array(60 * 60 * 24 * 7, 'week'),
410 array(60 * 60 * 24 , 'day'),
411 array(60 * 60 , 'hour'),
412 array(60 , 'minute'),
413 array(1 , 'second'),
414 );
415
416 $today = time(); /* Current unix time */
417 $since = $today - $original;
418
419 if ( $since > $chunks[2][0] ) {
420 $print = date("M jS", $original);
421
422 if( $since > $chunks[0][0] ) { // Seconds in a year
423 $print .= ", " . date( "Y", $original );
424 }
425
426 return $print;
427 }
428
429 // $j saves performing the count function each time around the loop
430 for ($i = 0, $j = count($chunks); $i < $j; $i++) {
431
432 $seconds = $chunks[$i][0];
433 $name = $chunks[$i][1];
434
435 // finding the biggest chunk (if the chunk fits, break)
436 if (($count = floor($since / $seconds)) != 0) {
437 break;
438 }
439 }
440
441 return sprintf( _n( "1 $name ago", "$count ${name}s ago", $count), $count);
442 }
443
444 /**
445 * Displays a list of users that can be selected!
446 *
447 * @since 0.7
448 *
449 * @todo Add pagination support for blogs with billions of users
450 *
451 * @param ???
452 * @param ???
453 */
454 function users_select_form( $selected = null, $args = null ) {
455
456 // Set up arguments
457 $defaults = array(
458 'list_class' => 'ef-users-select-form',
459 'input_id' => 'ef-selected-users'
460 );
461 $parsed_args = wp_parse_args( $args, $defaults );
462 extract($parsed_args, EXTR_SKIP);
463
464 $args = array(
465 'who' => 'authors',
466 'fields' => array(
467 'ID',
468 'display_name',
469 'user_email'
470 ),
471 'orderby' => 'display_name',
472 );
473 $args = apply_filters( 'ef_users_select_form_get_users_args', $args );
474
475 $users = get_users( $args );
476
477 if ( !is_array($selected) ) $selected = array();
478 ?>
479
480 <?php if( !empty($users) ) : ?>
481 <ul class="<?php echo esc_attr( $list_class ) ?>">
482 <?php foreach( $users as $user ) :
483 $checked = ( in_array($user->ID, $selected) ) ? 'checked="checked"' : '';
484 // Add a class to checkbox of current user so we know not to add them in notified list during notifiedMessage() js function
485 $current_user_class = ( get_current_user_id() == $user->ID ) ? 'class="post_following_list-current_user" ' : '';
486 ?>
487 <li>
488 <label for="<?php echo esc_attr( $input_id .'-'. $user->ID ) ?>">
489 <div class="ef-user-subscribe-actions">
490 <?php do_action( 'ef_user_subscribe_actions', $user->ID, $checked ) ?>
491 <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; ?> />
492 </div>
493
494 <span class="ef-user_displayname"><?php echo esc_html( $user->display_name ); ?></span>
495 <span class="ef-user_useremail"><?php echo esc_html( $user->user_email ); ?></span>
496 </label>
497 </li>
498 <?php endforeach; ?>
499 </ul>
500 <?php endif; ?>
501 <?php
502 }
503
504 /**
505 * Adds an array of capabilities to a role.
506 *
507 * @since 0.7
508 *
509 * @param string $role A standard WP user role like 'administrator' or 'author'
510 * @param array $caps One or more user caps to add
511 */
512 function add_caps_to_role( $role, $caps ) {
513
514 // In some contexts, we don't want to add caps to roles
515 if ( apply_filters( 'ef_kill_add_caps_to_role', false, $role, $caps ) )
516 return;
517
518 global $wp_roles;
519
520 if ( $wp_roles->is_role( $role ) ) {
521 $role = get_role( $role );
522 foreach ( $caps as $cap ) {
523 $role->add_cap( $cap );
524 }
525 }
526 }
527
528 /**
529 * Add settings help menus to our module screens if the values exist
530 * Auto-registered in Edit_Flow::register_module()
531 *
532 * @since 0.7
533 */
534 function action_settings_help_menu() {
535
536 $screen = get_current_screen();
537
538 if ( !method_exists( $screen, 'add_help_tab' ) )
539 return;
540
541 if ( $screen->id != 'edit-flow_page_' . $this->module->settings_slug )
542 return;
543
544 // Make sure we have all of the required values for our tab
545 if ( isset( $this->module->settings_help_tab['id'], $this->module->settings_help_tab['title'], $this->module->settings_help_tab['content'] ) ) {
546 $screen->add_help_tab( $this->module->settings_help_tab );
547
548 if ( isset( $this->module->settings_help_sidebar ) ) {
549 $screen->set_help_sidebar( $this->module->settings_help_sidebar );
550 }
551 }
552 }
553
554 /**
555 * Upgrade the term descriptions for all of the terms in a given taxonomy
556 */
557 function upgrade_074_term_descriptions( $taxonomy ) {
558 $args = array(
559 'hide_empty' => false,
560 );
561 $terms = get_terms( $taxonomy, $args );
562 foreach( $terms as $term ) {
563 // If we can detect that this term already follows the new scheme, let's skip it
564 $maybe_serialized = base64_decode( $term->description );
565 if ( is_serialized( $maybe_serialized ) )
566 continue;
567
568 $description_args = array();
569 // This description has been JSON-encoded, so let's decode it
570 if ( 0 === strpos( $term->description, '{' ) ) {
571 $string_to_unencode = stripslashes( htmlspecialchars_decode( $term->description ) );
572 $unencoded_array = json_decode( $string_to_unencode, true );
573 // Only continue processing if it actually was an array. Otherwise, set to the original string
574 if ( is_array( $unencoded_array ) ) {
575 foreach( $unencoded_array as $key => $value ) {
576 // html_entity_decode only works on strings but sometimes we store nested arrays
577 if ( !is_array( $value ) )
578 $description_args[$key] = html_entity_decode( $value, ENT_QUOTES );
579 else
580 $description_args[$key] = $value;
581 }
582 }
583 } else {
584 $description_args['description'] = $term->description;
585 }
586 $new_description = $this->get_encoded_description( $description_args );
587 wp_update_term( $term->term_id, $taxonomy, array( 'description' => $new_description ) );
588 }
589 }
590
591 /**
592 * Return compatibility hooks for the current instance
593 *
594 * @return array
595 */
596 function get_compat_hooks() {
597 return isset( $this->compat_hooks ) && is_array( $this->compat_hooks ) ? $this->compat_hooks : [];
598 }
599
600 }
601 }
602