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

592 lines 18.7 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 //get_post() needs a variable
200 $post_int;
201 if( isset( $_REQUEST['post'] ) )
202 $post_int = (int)$_REQUEST['post'];
203
204 if ( $post && $post->post_type )
205 $post_type = $post->post_type;
206 elseif ( $typenow )
207 $post_type = $typenow;
208 elseif ( $current_screen && isset( $current_screen->post_type ) )
209 $post_type = $current_screen->post_type;
210 elseif ( isset( $_REQUEST['post_type'] ) )
211 $post_type = sanitize_key( $_REQUEST['post_type'] );
212 elseif ( 'post.php' == $pagenow
213 && isset( $_REQUEST['post'] )
214 && isset( $post_int )
215 && ! empty( get_post( $post_int )->post_type ) )
216 $post_type = get_post( $post_int )->post_type;
217 elseif ( 'edit.php' == $pagenow && empty( $_REQUEST['post_type'] ) )
218 $post_type = 'post';
219 else
220 $post_type = null;
221
222 return $post_type;
223 }
224
225 /**
226 * Wrapper for the get_user_meta() function so we can replace it if we need to
227 *
228 * @since 0.7
229 *
230 * @param int $user_id Unique ID for the user
231 * @param string $key Key to search against
232 * @param bool $single Whether or not to return just one value
233 * @return string|bool|array $value Whatever the stored value was
234 */
235 function get_user_meta( $user_id, $key, $string = true ) {
236
237 $response = null;
238 $response = apply_filters( 'ef_get_user_meta', $response, $user_id, $key, $string );
239 if ( !is_null( $response ) )
240 return $response;
241
242 return get_user_meta( $user_id, $key, $string );
243
244 }
245
246 /**
247 * Wrapper for the update_user_meta() function so we can replace it if we need to
248 *
249 * @since 0.7
250 *
251 * @param int $user_id Unique ID for the user
252 * @param string $key Key to search against
253 * @param string|bool|array $value Whether or not to return just one value
254 * @param string|bool|array $previous (optional) Previous value to replace
255 * @return bool $success Whether we were successful in saving
256 */
257 function update_user_meta( $user_id, $key, $value, $previous = null ) {
258
259 $response = null;
260 $response = apply_filters( 'ef_update_user_meta', $response, $user_id, $key, $value, $previous );
261 if ( !is_null( $response ) )
262 return $response;
263
264 return update_user_meta( $user_id, $key, $value, $previous );
265
266 }
267
268 /**
269 * Take a status and a message, JSON encode and print
270 *
271 * @since 0.7
272 *
273 * @param string $status Whether it was a 'success' or an 'error'
274 */
275 function print_ajax_response( $status, $message = '' ) {
276 header( 'Content-type: application/json;' );
277 echo json_encode( array( 'status' => $status, 'message' => $message ) );
278 exit;
279 }
280
281 /**
282 * Whether or not the current page is a user-facing Edit Flow View
283 * @todo Think of a creative way to make this work
284 *
285 * @since 0.7
286 *
287 * @param string $module_name (Optional) Module name to check against
288 */
289 function is_whitelisted_functional_view( $module_name = null ) {
290
291 // @todo complete this method
292
293 return true;
294 }
295
296 /**
297 * Whether or not the current page is an Edit Flow settings view (either main or module)
298 * Determination is based on $pagenow, $_GET['page'], and the module's $settings_slug
299 * If there's no module name specified, it will return true against all Edit Flow settings views
300 *
301 * @since 0.7
302 *
303 * @param string $module_name (Optional) Module name to check against
304 * @return bool $is_settings_view Return true if it is
305 */
306 function is_whitelisted_settings_view( $module_name = null ) {
307 global $pagenow, $edit_flow;
308
309 // All of the settings views are based on admin.php and a $_GET['page'] parameter
310 if ( $pagenow != 'admin.php' || !isset( $_GET['page'] ) )
311 return false;
312
313 // Load all of the modules that have a settings slug/ callback for the settings page
314 foreach ( $edit_flow->modules as $mod_name => $mod_data ) {
315 if ( isset( $mod_data->options->enabled ) && $mod_data->options->enabled == 'on' && $mod_data->configure_page_cb )
316 $settings_view_slugs[] = $mod_data->settings_slug;
317 }
318
319 // The current page better be in the array of registered settings view slugs
320 if ( !in_array( $_GET['page'], $settings_view_slugs ) )
321 return false;
322
323 if ( $module_name && $edit_flow->modules->$module_name->settings_slug != $_GET['page'] )
324 return false;
325
326 return true;
327 }
328
329 /**
330 * Remove term(s) associated with a given object(s). Core doesn't have this as of 3.2
331 * @see http://core.trac.wordpress.org/ticket/15475
332 *
333 * @author ericmann
334 * @compat 3.3?
335 *
336 * @param int|array $object_ids The ID(s) of the object(s) to retrieve.
337 * @param int|array $terms The ids of the terms to remove.
338 * @param string|array $taxonomies The taxonomies to retrieve terms from.
339 * @return bool|WP_Error Affected Term IDs
340 */
341 function remove_object_terms( $object_id, $terms, $taxonomy ) {
342 global $wpdb;
343
344 if ( !taxonomy_exists( $taxonomy ) )
345 return new WP_Error( 'invalid_taxonomy', __( 'Invalid Taxonomy' ) );
346
347 if ( !is_array( $object_id ) )
348 $object_id = array( $object_id );
349
350 if ( !is_array($terms) )
351 $terms = array( $terms );
352
353 $delete_objects = array_map( 'intval', $object_id );
354 $delete_terms = array_map( 'intval', $terms );
355
356 if ( $delete_terms ) {
357 $in_delete_terms = "'" . implode( "', '", $delete_terms ) . "'";
358 $in_delete_objects = "'" . implode( "', '", $delete_objects ) . "'";
359 $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 ) );
360 wp_update_term_count( $delete_terms, $taxonomy );
361 return true;
362 }
363 return false;
364 }
365
366 /**
367 * This is a hack, Hack, HACK!!!
368 * Encode all of the given arguments as a serialized array, and then base64_encode
369 * Used to store extra data in a term's description field
370 *
371 * @since 0.7
372 *
373 * @param array $args The arguments to encode
374 * @return string Arguments encoded in base64
375 */
376 function get_encoded_description( $args = array() ) {
377 return base64_encode( maybe_serialize( $args ) );
378 }
379
380 /**
381 * If given an encoded string from a term's description field,
382 * return an array of values. Otherwise, return the original string
383 *
384 * @since 0.7
385 *
386 * @param string $string_to_unencode Possibly encoded string
387 * @return array Array if string was encoded, otherwise the string as the 'description' field
388 */
389 function get_unencoded_description( $string_to_unencode ) {
390 return maybe_unserialize( base64_decode( $string_to_unencode ) );
391 }
392
393 /**
394 * Get the publicly accessible URL for the module based on the filename
395 *
396 * @since 0.7
397 *
398 * @param string $filepath File path for the module
399 * @return string $module_url Publicly accessible URL for the module
400 */
401 function get_module_url( $file ) {
402 $module_url = plugins_url( '/', $file );
403 return trailingslashit( $module_url );
404 }
405
406 /**
407 * Produce a human-readable version of the time since a timestamp
408 *
409 * @param int $original The UNIX timestamp we're producing a relative time for
410 * @return string $relative_time Human-readable version of the difference between the timestamp and now
411 */
412 function timesince( $original ) {
413 // array of time period chunks
414 $chunks = array(
415 array(60 * 60 * 24 * 365 , 'year'),
416 array(60 * 60 * 24 * 30 , 'month'),
417 array(60 * 60 * 24 * 7, 'week'),
418 array(60 * 60 * 24 , 'day'),
419 array(60 * 60 , 'hour'),
420 array(60 , 'minute'),
421 array(1 , 'second'),
422 );
423
424 $today = time(); /* Current unix time */
425 $since = $today - $original;
426
427 if ( $since > $chunks[2][0] ) {
428 $print = date("M jS", $original);
429
430 if( $since > $chunks[0][0] ) { // Seconds in a year
431 $print .= ", " . date( "Y", $original );
432 }
433
434 return $print;
435 }
436
437 // $j saves performing the count function each time around the loop
438 for ($i = 0, $j = count($chunks); $i < $j; $i++) {
439
440 $seconds = $chunks[$i][0];
441 $name = $chunks[$i][1];
442
443 // finding the biggest chunk (if the chunk fits, break)
444 if (($count = floor($since / $seconds)) != 0) {
445 break;
446 }
447 }
448
449 return sprintf( _n( "1 $name ago", "$count ${name}s ago", $count), $count);
450 }
451
452 /**
453 * Displays a list of users that can be selected!
454 *
455 * @since 0.7
456 *
457 * @todo Add pagination support for blogs with billions of users
458 *
459 * @param ???
460 * @param ???
461 */
462 function users_select_form( $selected = null, $args = null ) {
463
464 // Set up arguments
465 $defaults = array(
466 'list_class' => 'ef-users-select-form',
467 'input_id' => 'ef-selected-users'
468 );
469 $parsed_args = wp_parse_args( $args, $defaults );
470 extract($parsed_args, EXTR_SKIP);
471
472 $args = array(
473 'who' => 'authors',
474 'fields' => array(
475 'ID',
476 'display_name',
477 'user_email'
478 ),
479 'orderby' => 'display_name',
480 );
481 $args = apply_filters( 'ef_users_select_form_get_users_args', $args );
482 $users = get_users( $args );
483
484 if ( !is_array($selected) ) $selected = array();
485 ?>
486
487 <?php if( !empty($users) ) : ?>
488 <ul class="<?php echo esc_attr( $list_class ) ?>">
489 <?php foreach( $users as $user ) : ?>
490 <?php $checked = ( in_array($user->ID, $selected) ) ? 'checked="checked"' : ''; ?>
491 <li>
492 <label for="<?php echo esc_attr( $input_id .'-'. $user->ID ) ?>">
493 <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; ?> />
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 * Add settings help menus to our module screens if the values exist
529 * Auto-registered in Edit_Flow::register_module()
530 *
531 * @since 0.7
532 */
533 function action_settings_help_menu() {
534
535 $screen = get_current_screen();
536
537 if ( !method_exists( $screen, 'add_help_tab' ) )
538 return;
539
540 if ( $screen->id != 'edit-flow_page_' . $this->module->settings_slug )
541 return;
542
543 // Make sure we have all of the required values for our tab
544 if ( isset( $this->module->settings_help_tab['id'], $this->module->settings_help_tab['title'], $this->module->settings_help_tab['content'] ) ) {
545 $screen->add_help_tab( $this->module->settings_help_tab );
546
547 if ( isset( $this->module->settings_help_sidebar ) ) {
548 $screen->set_help_sidebar( $this->module->settings_help_sidebar );
549 }
550 }
551 }
552
553 /**
554 * Upgrade the term descriptions for all of the terms in a given taxonomy
555 */
556 function upgrade_074_term_descriptions( $taxonomy ) {
557 $args = array(
558 'hide_empty' => false,
559 );
560 $terms = get_terms( $taxonomy, $args );
561 foreach( $terms as $term ) {
562 // If we can detect that this term already follows the new scheme, let's skip it
563 $maybe_serialized = base64_decode( $term->description );
564 if ( is_serialized( $maybe_serialized ) )
565 continue;
566
567 $description_args = array();
568 // This description has been JSON-encoded, so let's decode it
569 if ( 0 === strpos( $term->description, '{' ) ) {
570 $string_to_unencode = stripslashes( htmlspecialchars_decode( $term->description ) );
571 $unencoded_array = json_decode( $string_to_unencode, true );
572 // Only continue processing if it actually was an array. Otherwise, set to the original string
573 if ( is_array( $unencoded_array ) ) {
574 foreach( $unencoded_array as $key => $value ) {
575 // html_entity_decode only works on strings but sometimes we store nested arrays
576 if ( !is_array( $value ) )
577 $description_args[$key] = html_entity_decode( $value, ENT_QUOTES );
578 else
579 $description_args[$key] = $value;
580 }
581 }
582 } else {
583 $description_args['description'] = $term->description;
584 }
585 $new_description = $this->get_encoded_description( $description_args );
586 wp_update_term( $term->term_id, $taxonomy, array( 'description' => $new_description ) );
587 }
588 }
589
590 }
591 }
592