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

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