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

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