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

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

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