PluginProbe
Edit Flow / 0.8
Edit Flow v0.8
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, at common/php/class-module.php

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