Version Description
(Nov. 12, 2012) = * Create guest author profiles for bylines you'd like to assign without creating WordPress user accounts. Guest authors can have all of the same fields as normal users including display name, biography, and avatars. * Support for non-Latin characters in usernames and guest author names * wp-cli subcommands for creating, assigning, and reassigning co-authors * For themes using core template tags like the_author() or the_author_posts_link(), you enable Co-Authors Plus support with a simple filter * New author terms are now prefixed with 'cap-' to avoid collisions with global scope * Bug fix: Apply query filters to only post_types registered with the taxonomy. Props Tom Ransom * Filter coauthors_posts_link_single() with 'coauthors_posts_link'. Also adds rel="author". Props Amit Sannad and Gabriel Koen * Filter for the context and priorities of the Co-Authors meta boxes. Props Tom Kapler * Renamed the post meta box for selecting authors so it applies to many post types. Props John Blackbourn
Release Info
Developer | danielbachhuber |
Plugin | ![]() |
Version | 3.0 |
Comparing to | |
See all releases |
Code changes from version 2.6.4 to 3.0
- co-authors-plus.php +637 -345
- css/guest-authors.css +8 -0
- js/guest-authors.js +31 -0
- js/jquery.aceditable.dev.js +0 -1176
- languages/co-authors-plus-de_DE.mo +0 -0
- co-authors-plus.pot → languages/co-authors-plus-de_DE.po +30 -17
- languages/co-authors-plus.pot +502 -27
- lib/select2/select2.css +524 -0
- lib/select2/select2.min.js +82 -0
- lib/select2/select2.png +0 -0
- lib/select2/select2x2.png +0 -0
- lib/select2/spinner.gif +0 -0
- php/class-coauthors-guest-authors.php +1214 -0
- php/class-coauthors-template-filters.php +21 -0
- php/class-coauthors-wp-list-table.php +247 -0
- php/class-wp-cli.php +403 -0
- readme.txt +51 -124
- screenshot-1.png +0 -0
- screenshot-2.png +0 -0
- template-tags.php +18 -9
- upgrade.php +2 -11
@@ -3,7 +3,7 @@
|
|
3 |
Plugin Name: Co-Authors Plus
|
4 |
Plugin URI: http://wordpress.org/extend/plugins/co-authors-plus/
|
5 |
Description: Allows multiple authors to be assigned to a post. This plugin is an extended version of the Co-Authors plugin developed by Weston Ruter.
|
6 |
-
Version:
|
7 |
Author: Mohammad Jangda, Daniel Bachhuber, Automattic
|
8 |
Copyright: 2008-2012 Shared and distributed between Mohammad Jangda, Daniel Bachhuber, Weston Ruter
|
9 |
|
@@ -24,70 +24,83 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
24 |
|
25 |
*/
|
26 |
|
27 |
-
define( 'COAUTHORS_PLUS_VERSION', '
|
28 |
|
29 |
define( 'COAUTHORS_PLUS_PATH', dirname( __FILE__ ) );
|
30 |
define( 'COAUTHORS_PLUS_URL', plugin_dir_url( __FILE__ ) );
|
31 |
|
32 |
require_once( dirname( __FILE__ ) . '/template-tags.php' );
|
33 |
|
|
|
|
|
|
|
|
|
|
|
34 |
class coauthors_plus {
|
35 |
-
|
36 |
-
// Name for the taxonomy we're using to store
|
|
|
37 |
var $coauthor_taxonomy = 'author';
|
38 |
-
|
39 |
var $coreauthors_meta_box_name = 'authordiv';
|
40 |
var $coauthors_meta_box_name = 'coauthorsdiv';
|
41 |
-
|
|
|
42 |
var $gravatar_size = 25;
|
43 |
-
|
44 |
var $_pages_whitelist = array( 'post.php', 'post-new.php' );
|
45 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
46 |
/**
|
47 |
* __construct()
|
48 |
*/
|
49 |
function __construct() {
|
50 |
|
51 |
-
|
|
|
|
|
52 |
|
53 |
// Load admin_init function
|
54 |
add_action( 'admin_init', array( $this,'admin_init' ) );
|
55 |
-
|
56 |
-
// Register new taxonomy so that we can store all our authors
|
57 |
-
register_taxonomy( $this->coauthor_taxonomy, 'post', array('hierarchical' => false, 'update_count_callback' => array( &$this, '_update_users_posts_count' ), 'label' => false, 'query_var' => false, 'rewrite' => false, 'sort' => true, 'show_ui' => false ) );
|
58 |
-
|
59 |
// Modify SQL queries to include coauthors
|
60 |
-
add_filter( 'posts_where', array( $this, 'posts_where_filter' ) );
|
61 |
-
add_filter( 'posts_join', array( $this, 'posts_join_filter' ) );
|
62 |
-
add_filter( 'posts_groupby', array( $this, 'posts_groupby_filter' ) );
|
63 |
-
|
64 |
// Action to set users when a post is saved
|
65 |
add_action( 'save_post', array( $this, 'coauthors_update_post' ), 10, 2 );
|
66 |
// Filter to set the post_author field when wp_insert_post is called
|
67 |
add_filter( 'wp_insert_post_data', array( $this, 'coauthors_set_post_author_field' ), 10, 2 );
|
68 |
-
|
69 |
// Action to reassign posts when a user is deleted
|
70 |
add_action( 'delete_user', array( $this, 'delete_user_action' ) );
|
71 |
-
|
72 |
add_filter( 'get_usernumposts', array( $this, 'filter_count_user_posts' ), 10, 2 );
|
73 |
-
|
74 |
// Action to set up author auto-suggest
|
75 |
add_action( 'wp_ajax_coauthors_ajax_suggest', array( $this, 'ajax_suggest' ) );
|
76 |
-
|
77 |
// Filter to allow coauthors to edit posts
|
78 |
-
add_filter( 'user_has_cap', array( $this, '
|
79 |
|
80 |
// Handle the custom author meta box
|
81 |
add_action( 'add_meta_boxes', array( $this, 'add_coauthors_box' ) );
|
82 |
add_action( 'add_meta_boxes', array( $this, 'remove_authors_box' ) );
|
83 |
-
|
84 |
-
// Removes the author dropdown from the post quick edit
|
85 |
-
add_action( '
|
86 |
|
87 |
// Restricts WordPress from blowing away term order on bulk edit
|
88 |
-
add_filter( 'wp_get_object_terms', array(
|
89 |
-
|
90 |
// Fix for author info not properly displaying on author pages
|
|
|
91 |
add_action( 'the_post', array( $this, 'fix_author_page' ) );
|
92 |
|
93 |
// Support for Edit Flow's calendar and story budget
|
@@ -98,18 +111,68 @@ class coauthors_plus {
|
|
98 |
|
99 |
function coauthors_plus() {
|
100 |
$this->__construct();
|
101 |
-
}
|
102 |
-
|
103 |
/**
|
104 |
-
*
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
105 |
*/
|
106 |
function admin_init() {
|
107 |
global $pagenow;
|
108 |
|
109 |
-
//
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
|
114 |
// Hooks to add additional coauthors to author column to Edit page
|
115 |
add_filter( 'manage_posts_columns', array( $this, '_filter_manage_posts_columns' ) );
|
@@ -119,101 +182,141 @@ class coauthors_plus {
|
|
119 |
|
120 |
// Hooks to modify the published post number count on the Users WP List Table
|
121 |
add_filter( 'manage_users_columns', array( $this, '_filter_manage_users_columns' ) );
|
122 |
-
add_filter( 'manage_users_custom_column', array(
|
123 |
-
|
|
|
|
|
|
|
124 |
}
|
125 |
-
|
126 |
-
function admin_load_page() {
|
127 |
-
|
128 |
-
// Add the main JS script and CSS file
|
129 |
-
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
|
130 |
-
|
131 |
-
// Add necessary JS variables
|
132 |
-
add_action( 'admin_print_scripts', array( $this, 'js_vars' ) );
|
133 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
134 |
}
|
135 |
-
|
136 |
-
/**
|
137 |
-
*
|
|
|
|
|
|
|
|
|
138 |
*/
|
139 |
-
function
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
151 |
return false;
|
|
|
152 |
}
|
153 |
-
|
154 |
/**
|
155 |
-
*
|
|
|
|
|
|
|
|
|
|
|
|
|
156 |
*/
|
157 |
-
function
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
if( $post && $post->post_type )
|
165 |
-
$post_type = $post->post_type;
|
166 |
-
elseif( $typenow )
|
167 |
-
$post_type = $typenow;
|
168 |
-
elseif( $current_screen && isset( $current_screen->post_type ) )
|
169 |
-
$post_type = $current_screen->post_type;
|
170 |
-
elseif( isset( $_REQUEST['post_type'] ) )
|
171 |
-
$post_type = sanitize_key( $_REQUEST['post_type'] );
|
172 |
-
else
|
173 |
-
$post_type = '';
|
174 |
-
|
175 |
-
if( $post_type )
|
176 |
-
$this->_current_post_type = $post_type;
|
177 |
-
|
178 |
-
return $post_type;
|
179 |
}
|
180 |
-
|
181 |
/**
|
182 |
* Removes the standard WordPress Author box.
|
183 |
* We don't need it because the Co-Authors one is way cooler.
|
184 |
*/
|
185 |
function remove_authors_box() {
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
if( $this->authors_supported( $post_type ) )
|
190 |
-
remove_meta_box( $this->coreauthors_meta_box_name, $post_type, 'normal' );
|
191 |
}
|
192 |
-
|
193 |
/**
|
194 |
* Adds a custom Authors box
|
195 |
*/
|
196 |
function add_coauthors_box() {
|
197 |
-
|
198 |
-
$
|
199 |
-
|
200 |
-
if( $this->authors_supported( $post_type ) && $this->current_user_can_set_authors() )
|
201 |
-
add_meta_box($this->coauthors_meta_box_name, __('Post Authors', 'co-authors-plus'), array( &$this, 'coauthors_meta_box' ), $post_type, 'normal', 'high');
|
202 |
}
|
203 |
-
|
204 |
/**
|
205 |
* Callback for adding the custom author box
|
206 |
*/
|
207 |
function coauthors_meta_box( $post ) {
|
208 |
-
global $post;
|
209 |
-
|
210 |
$post_id = $post->ID;
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
215 |
$coauthors = get_coauthors();
|
216 |
-
|
|
|
217 |
$count = 0;
|
218 |
if( !empty( $coauthors ) ) :
|
219 |
?>
|
@@ -234,66 +337,76 @@ class coauthors_plus {
|
|
234 |
<?php
|
235 |
endforeach;
|
236 |
?>
|
237 |
-
</ul>
|
238 |
<div class="clear"></div>
|
239 |
<p><?php _e( '<strong>Note:</strong> To edit post authors, please enable javascript or use a javascript-capable browser', 'co-authors-plus' ); ?></p>
|
240 |
</div>
|
241 |
<?php
|
242 |
endif;
|
243 |
?>
|
244 |
-
|
245 |
<div id="coauthors-edit" class="hide-if-no-js">
|
246 |
<p><?php _e( 'Click on an author to change them. Drag to change their order. Click on <strong>Remove</strong> to remove them.', 'co-authors-plus' ); ?></p>
|
247 |
</div>
|
248 |
-
|
249 |
<?php wp_nonce_field( 'coauthors-edit', 'coauthors-nonce' ); ?>
|
250 |
-
|
251 |
<?php
|
252 |
}
|
253 |
-
|
254 |
/**
|
255 |
* Removes the author dropdown from the post quick edit
|
256 |
-
* It's a bit hacky, but the only way I can figure out :(
|
257 |
*/
|
258 |
function remove_quick_edit_authors_box() {
|
259 |
-
|
260 |
-
|
261 |
-
|
262 |
-
remove_post_type_support(
|
263 |
-
}
|
264 |
}
|
265 |
-
|
266 |
/**
|
267 |
* Add coauthors to author column on edit pages
|
|
|
268 |
* @param array $post_columns
|
269 |
*/
|
270 |
-
function _filter_manage_posts_columns($posts_columns) {
|
|
|
271 |
$new_columns = array();
|
272 |
-
|
|
|
|
|
273 |
foreach ($posts_columns as $key => $value) {
|
274 |
$new_columns[$key] = $value;
|
275 |
if( $key == 'title' )
|
276 |
$new_columns['coauthors'] = __( 'Authors', 'co-authors-plus' );
|
277 |
-
|
278 |
if ( $key == 'author' )
|
279 |
unset($new_columns[$key]);
|
280 |
}
|
281 |
return $new_columns;
|
282 |
-
}
|
283 |
-
|
284 |
/**
|
285 |
* Insert coauthors into post rows on Edit Page
|
|
|
286 |
* @param string $column_name
|
287 |
-
|
288 |
-
function _filter_manage_posts_custom_column($column_name) {
|
289 |
if ($column_name == 'coauthors') {
|
290 |
global $post;
|
291 |
$authors = get_coauthors( $post->ID );
|
292 |
-
|
293 |
$count = 1;
|
294 |
foreach( $authors as $author ) :
|
|
|
|
|
|
|
|
|
|
|
|
|
295 |
?>
|
296 |
-
<a href="<?php echo esc_url(
|
297 |
<?php
|
298 |
$count++;
|
299 |
endforeach;
|
@@ -324,8 +437,9 @@ class coauthors_plus {
|
|
324 |
return $value;
|
325 |
// We filter count_user_posts() so it provides an accurate number
|
326 |
$numposts = count_user_posts( $user_id );
|
|
|
327 |
if ( $numposts > 0 ) {
|
328 |
-
$value .= "<a href='edit.php?
|
329 |
$value .= $numposts;
|
330 |
$value .= '</a>';
|
331 |
} else {
|
@@ -337,125 +451,174 @@ class coauthors_plus {
|
|
337 |
/**
|
338 |
* When we update the terms at all, we should update the published post count for each author
|
339 |
*/
|
340 |
-
function _update_users_posts_count( $
|
341 |
global $wpdb;
|
342 |
|
343 |
-
$
|
|
|
344 |
|
345 |
-
foreach (
|
346 |
-
|
|
|
347 |
}
|
|
|
|
|
348 |
|
349 |
-
|
350 |
-
$object_types = esc_sql( array_filter( $object_types, 'post_type_exists' ) );
|
351 |
-
|
352 |
-
$object_types = array_unique( $object_types );
|
353 |
-
|
354 |
-
foreach( (array)$terms as $term_taxonomy_id ) {
|
355 |
-
$count = 0;
|
356 |
-
if ( 0 == $term_taxonomy_id )
|
357 |
-
continue;
|
358 |
-
// Get the post IDs for all published posts with this co-author
|
359 |
-
$query = $wpdb->prepare( "SELECT $wpdb->posts.ID FROM $wpdb->term_relationships, $wpdb->posts WHERE $wpdb->posts.ID = $wpdb->term_relationships.object_id AND post_status = 'publish' AND post_type IN ('" . implode("', '", $object_types ) . "') AND term_taxonomy_id = %d", $term_taxonomy_id );
|
360 |
-
$all_coauthor_posts = $wpdb->get_results( $query );
|
361 |
-
|
362 |
-
// Find the term_id from the term_taxonomy_id, and then get the user's user_login from that
|
363 |
-
$query = $wpdb->prepare( "SELECT $wpdb->terms.slug FROM $wpdb->term_taxonomy INNER JOIN $wpdb->terms ON $wpdb->terms.term_id = $wpdb->term_taxonomy.term_id WHERE $wpdb->term_taxonomy.term_taxonomy_id = %d", $term_taxonomy_id );
|
364 |
-
$term_slug = $wpdb->get_var( $query );
|
365 |
-
$author = get_user_by( 'login', $term_slug );
|
366 |
-
|
367 |
-
// Get all of the post IDs where the user is the primary author
|
368 |
-
$query = $wpdb->prepare( "SELECT $wpdb->posts.ID FROM $wpdb->posts WHERE post_status = 'publish' AND post_type IN ('" . implode("', '", $object_types ) . "') AND post_author = %d;", $author->ID );
|
369 |
-
$all_author_posts = $wpdb->get_results( $query );
|
370 |
-
|
371 |
-
// Dedupe the post IDs and then provide a final count
|
372 |
-
$all_posts = array();
|
373 |
-
foreach( $all_coauthor_posts as $coauthor_post ) {
|
374 |
-
$all_posts[] = $coauthor_post->ID;
|
375 |
-
}
|
376 |
-
foreach( $all_author_posts as $author_post ) {
|
377 |
-
$all_posts[] = $author_post->ID;
|
378 |
-
}
|
379 |
-
$count = count( array_unique( $all_posts ) );
|
380 |
|
381 |
-
|
382 |
-
|
383 |
-
|
384 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
385 |
}
|
386 |
-
|
387 |
/**
|
388 |
* Modify the author query posts SQL to include posts co-authored
|
389 |
*/
|
390 |
-
function posts_join_filter( $join ){
|
391 |
-
global $wpdb
|
392 |
-
|
393 |
-
if( is_author() ){
|
|
|
|
|
|
|
|
|
394 |
// Check to see that JOIN hasn't already been added. Props michaelingp and nbaxley
|
395 |
$term_relationship_join = " INNER JOIN {$wpdb->term_relationships} ON ({$wpdb->posts}.ID = {$wpdb->term_relationships}.object_id)";
|
396 |
$term_taxonomy_join = " INNER JOIN {$wpdb->term_taxonomy} ON ( {$wpdb->term_relationships}.term_taxonomy_id = {$wpdb->term_taxonomy}.term_taxonomy_id )";
|
397 |
-
|
398 |
if( strpos( $join, trim( $term_relationship_join ) ) === false ) {
|
399 |
-
$join .= $term_relationship_join;
|
400 |
}
|
401 |
if( strpos( $join, trim( $term_taxonomy_join ) ) === false ) {
|
402 |
-
$join .= $term_taxonomy_join;
|
403 |
}
|
404 |
}
|
405 |
-
|
406 |
return $join;
|
407 |
}
|
408 |
-
|
409 |
/**
|
410 |
-
* Modify
|
411 |
*/
|
412 |
-
function posts_where_filter( $where ){
|
413 |
-
global $wpdb
|
414 |
-
|
415 |
-
if( is_author() ) {
|
416 |
-
|
417 |
-
|
418 |
-
|
419 |
-
if( $author ) {
|
420 |
-
$where = preg_replace( '/(\b(?:' . $wpdb->posts . '\.)?post_author\s*=\s*(\d+))/', '($1 OR (' . $wpdb->term_taxonomy . '.taxonomy = \''. $this->coauthor_taxonomy.'\' AND '. $wpdb->term_taxonomy .'.term_id = \''. $term->term_id .'\'))', $where, 1 ); #' . $wpdb->postmeta . '.meta_id IS NOT NULL AND
|
421 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
422 |
}
|
|
|
423 |
}
|
424 |
return $where;
|
425 |
}
|
426 |
-
|
427 |
/**
|
428 |
-
*
|
429 |
*/
|
430 |
-
function posts_groupby_filter( $groupby ) {
|
431 |
global $wpdb;
|
432 |
-
|
433 |
-
if( is_author() ) {
|
434 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
435 |
}
|
436 |
return $groupby;
|
437 |
}
|
438 |
-
|
439 |
/**
|
440 |
* Filters post data before saving to db to set post_author
|
441 |
*/
|
442 |
function coauthors_set_post_author_field( $data, $postarr ) {
|
443 |
-
|
444 |
// Bail on autosave
|
445 |
if ( defined( 'DOING_AUTOSAVE' ) && !DOING_AUTOSAVE )
|
446 |
return $data;
|
447 |
-
|
448 |
// Bail on revisions
|
449 |
-
if( $data['post_type'] == 'revision' )
|
450 |
return $data;
|
451 |
|
452 |
-
//
|
453 |
-
|
454 |
if( isset( $_REQUEST['coauthors-nonce'] ) && isset( $_POST['coauthors'] ) && is_array( $_POST['coauthors'] ) ) {
|
455 |
-
$author =
|
456 |
-
if( $author ) {
|
457 |
-
$author_data =
|
458 |
-
|
|
|
|
|
|
|
|
|
|
|
459 |
}
|
460 |
}
|
461 |
|
@@ -464,8 +627,20 @@ class coauthors_plus {
|
|
464 |
// 'post_author' is set to current user if the $_REQUEST value doesn't exist
|
465 |
if ( isset( $_REQUEST['action'] ) && $_REQUEST['action'] == 'inline-save' ) {
|
466 |
$coauthors = get_coauthors( $postarr['ID'] );
|
467 |
-
if ( is_array( $coauthors ) )
|
468 |
-
$
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
469 |
}
|
470 |
|
471 |
// If for some reason we don't have the coauthors fields set
|
@@ -473,65 +648,59 @@ class coauthors_plus {
|
|
473 |
$user = wp_get_current_user();
|
474 |
$data['post_author'] = $user->ID;
|
475 |
}
|
476 |
-
|
|
|
|
|
|
|
477 |
return $data;
|
478 |
}
|
479 |
-
|
480 |
/**
|
481 |
-
* Update a post's co-authors
|
|
|
482 |
* @param $post_ID
|
483 |
-
* @return
|
484 |
*/
|
485 |
function coauthors_update_post( $post_id, $post ) {
|
486 |
$post_type = $post->post_type;
|
487 |
-
|
488 |
if ( defined( 'DOING_AUTOSAVE' ) && !DOING_AUTOSAVE )
|
489 |
return;
|
490 |
-
|
491 |
if( isset( $_POST['coauthors-nonce'] ) && isset( $_POST['coauthors'] ) ) {
|
492 |
check_admin_referer( 'coauthors-edit', 'coauthors-nonce' );
|
493 |
-
|
494 |
if( $this->current_user_can_set_authors() ){
|
495 |
$coauthors = (array) $_POST['coauthors'];
|
496 |
-
$coauthors = array_map( '
|
497 |
return $this->add_coauthors( $post_id, $coauthors );
|
498 |
}
|
499 |
}
|
500 |
}
|
501 |
-
|
502 |
/**
|
503 |
-
* Add
|
504 |
*/
|
505 |
function add_coauthors( $post_id, $coauthors, $append = false ) {
|
506 |
global $current_user;
|
507 |
-
|
508 |
$post_id = (int) $post_id;
|
509 |
$insert = false;
|
510 |
-
|
511 |
// if an array isn't returned, create one and populate with default author
|
512 |
if ( !is_array( $coauthors ) || 0 == count( $coauthors ) || empty( $coauthors ) ) {
|
513 |
$coauthors = array( $current_user->user_login );
|
514 |
}
|
515 |
-
|
516 |
// Add each co-author to the post meta
|
517 |
-
foreach( array_unique( $coauthors ) as $
|
518 |
-
|
519 |
-
|
520 |
-
$
|
521 |
-
|
522 |
-
// Add user as a term if they don't exist
|
523 |
-
if( !term_exists( $name, $this->coauthor_taxonomy ) ) {
|
524 |
-
$args = array( 'slug' => sanitize_title( $name ) );
|
525 |
-
$insert = wp_insert_term( $name, $this->coauthor_taxonomy, $args );
|
526 |
-
}
|
527 |
-
}
|
528 |
-
|
529 |
-
// Add authors as post terms
|
530 |
-
if( !is_wp_error( $insert ) ) {
|
531 |
-
$set = wp_set_post_terms( $post_id, $coauthors, $this->coauthor_taxonomy, $append );
|
532 |
}
|
|
|
533 |
}
|
534 |
-
|
535 |
/**
|
536 |
* Action taken when user is deleted.
|
537 |
* - User term is removed from all associated posts
|
@@ -540,9 +709,9 @@ class coauthors_plus {
|
|
540 |
*/
|
541 |
function delete_user_action($delete_id){
|
542 |
global $wpdb;
|
543 |
-
|
544 |
$reassign_id = absint( $_POST['reassign_user'] );
|
545 |
-
|
546 |
// If reassign posts, do that -- use coauthors_update_post
|
547 |
if($reassign_id) {
|
548 |
// Get posts belonging to deleted author
|
@@ -558,7 +727,7 @@ class coauthors_plus {
|
|
558 |
}
|
559 |
}
|
560 |
}
|
561 |
-
|
562 |
$delete_user = get_user_by( 'id', $delete_id );
|
563 |
if ( is_object( $delete_user ) ) {
|
564 |
// Delete term
|
@@ -577,55 +746,55 @@ class coauthors_plus {
|
|
577 |
if ( !isset( $_REQUEST['bulk_edit'] ) || $taxonomies != "'author'" )
|
578 |
return $terms;
|
579 |
|
580 |
-
global $wpdb;
|
581 |
-
$orderby = 'ORDER BY tr.term_order';
|
582 |
$order = 'ASC';
|
583 |
$object_ids = (int)$object_ids;
|
584 |
$query = $wpdb->prepare( "SELECT t.slug, t.term_id FROM $wpdb->terms AS t INNER JOIN $wpdb->term_taxonomy AS tt ON tt.term_id = t.term_id INNER JOIN $wpdb->term_relationships AS tr ON tr.term_taxonomy_id = tt.term_taxonomy_id WHERE tt.taxonomy IN (%s) AND tr.object_id IN (%s) $orderby $order", $taxonomies, $object_ids );
|
585 |
-
$raw_coauthors = $wpdb->get_results( $query );
|
586 |
$terms = array();
|
587 |
-
foreach( $raw_coauthors as $author ) {
|
588 |
-
$terms[] = $author->slug;
|
589 |
}
|
590 |
|
591 |
return $terms;
|
592 |
-
|
593 |
}
|
594 |
-
|
595 |
/**
|
596 |
-
* Filter the count_users_posts() core function
|
597 |
*/
|
598 |
function filter_count_user_posts( $count, $user_id ) {
|
599 |
$user = get_userdata( $user_id );
|
600 |
-
|
601 |
-
$term =
|
602 |
-
|
603 |
// Only modify the count if the author already exists as a term
|
604 |
if( $term && !is_wp_error( $term ) ) {
|
605 |
$count = $term->count;
|
606 |
}
|
607 |
-
|
608 |
return $count;
|
609 |
}
|
610 |
-
|
611 |
/**
|
612 |
* Checks to see if the current user can set authors or not
|
613 |
*/
|
614 |
function current_user_can_set_authors( ) {
|
615 |
global $post, $typenow;
|
616 |
-
|
617 |
-
|
618 |
-
|
619 |
-
$post_type = $this->get_current_post_type();
|
620 |
// TODO: need to fix this; shouldn't just say no if don't have post_type
|
621 |
if( ! $post_type ) return false;
|
622 |
-
|
623 |
$post_type_object = get_post_type_object( $post_type );
|
624 |
-
$
|
625 |
-
|
|
|
|
|
|
|
626 |
return apply_filters( 'coauthors_plus_edit_authors', $can_set_authors );
|
627 |
}
|
628 |
-
|
629 |
/**
|
630 |
* Fix for author info not properly displaying on author pages
|
631 |
*
|
@@ -633,41 +802,42 @@ class coauthors_plus {
|
|
633 |
* the first author is NOT the same as the author for the archive,
|
634 |
* the query_var is changed.
|
635 |
*
|
|
|
636 |
*/
|
637 |
-
function fix_author_page(
|
638 |
-
|
639 |
-
if( is_author() )
|
640 |
-
|
641 |
-
|
642 |
-
|
643 |
-
|
644 |
-
|
645 |
-
|
646 |
-
if(
|
647 |
-
|
648 |
-
$
|
649 |
}
|
650 |
}
|
651 |
}
|
652 |
-
|
653 |
/**
|
654 |
* Main function that handles search-as-you-type for adding authors
|
655 |
*/
|
656 |
function ajax_suggest() {
|
657 |
-
|
658 |
if( ! isset( $_REQUEST['_wpnonce'] ) || ! wp_verify_nonce( $_REQUEST['_wpnonce'], 'coauthors-search' ) )
|
659 |
die();
|
660 |
-
|
661 |
if( empty( $_REQUEST['q'] ) )
|
662 |
die();
|
663 |
|
664 |
$search = sanitize_text_field( strtolower( $_REQUEST['q'] ) );
|
665 |
-
$ignore = array_map( '
|
666 |
|
667 |
$authors = $this->search_authors( $search, $ignore );
|
668 |
-
|
669 |
foreach( $authors as $author ) {
|
670 |
-
echo $author->ID ." | ". $author->user_login ." | ". $author->display_name ." | ". $author->user_email ."\n";
|
671 |
}
|
672 |
|
673 |
die();
|
@@ -676,9 +846,13 @@ class coauthors_plus {
|
|
676 |
|
677 |
/**
|
678 |
* Get matching authors based on a search value
|
679 |
-
*/
|
680 |
function search_authors( $search = '', $ignored_authors = array() ) {
|
681 |
|
|
|
|
|
|
|
|
|
682 |
$args = array(
|
683 |
'count_total' => false,
|
684 |
'search' => sprintf( '*%s*', $search ),
|
@@ -694,13 +868,39 @@ class coauthors_plus {
|
|
694 |
$found_users = get_users( $args );
|
695 |
remove_filter( 'pre_user_query', array( $this, 'filter_pre_user_query' ) );
|
696 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
697 |
// Allow users to always filter out certain users if needed (e.g. administrators)
|
698 |
$ignored_authors = apply_filters( 'coauthors_edit_ignored_authors', $ignored_authors );
|
699 |
foreach( $found_users as $key => $found_user ) {
|
700 |
// Make sure the user is contributor and above (or a custom cap)
|
701 |
if ( in_array( $found_user->user_login, $ignored_authors ) )
|
702 |
unset( $found_users[$key] );
|
703 |
-
else if ( false === $found_user->has_cap( apply_filters( 'coauthors_edit_author_cap', 'edit_posts' ) ) )
|
704 |
unset( $found_users[$key] );
|
705 |
}
|
706 |
return (array) $found_users;
|
@@ -716,25 +916,34 @@ class coauthors_plus {
|
|
716 |
return $user_query;
|
717 |
}
|
718 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
719 |
/**
|
720 |
* Functions to add scripts and css
|
721 |
*/
|
722 |
function enqueue_scripts($hook_suffix) {
|
723 |
global $pagenow, $post;
|
724 |
-
|
725 |
-
|
726 |
-
|
727 |
-
if ( !$this->is_valid_page() || !$this->authors_supported( $post_type ) || !$this->current_user_can_set_authors() )
|
728 |
return;
|
729 |
-
|
730 |
wp_enqueue_script( 'jquery' );
|
731 |
wp_enqueue_script( 'jquery-ui-sortable' );
|
732 |
wp_enqueue_style( 'co-authors-plus-css', COAUTHORS_PLUS_URL . 'css/co-authors-plus.css', false, COAUTHORS_PLUS_VERSION, 'all' );
|
733 |
wp_enqueue_script( 'co-authors-plus-js', COAUTHORS_PLUS_URL . 'js/co-authors-plus.js', array('jquery', 'suggest'), COAUTHORS_PLUS_VERSION, true);
|
734 |
-
|
735 |
$js_strings = array(
|
736 |
'edit_label' => __( 'Edit', 'co-authors-plus' ),
|
737 |
-
'delete_label' => __( 'Remove', 'co-authors-plus' ),
|
738 |
'confirm_delete' => __( 'Are you sure you want to remove this author?', 'co-authors-plus' ),
|
739 |
'input_box_title' => __( 'Click to change this author, or drag to change their position', 'co-authors-plus' ),
|
740 |
'search_box_text' => __( 'Search for an author', 'co-authors-plus' ),
|
@@ -742,112 +951,195 @@ class coauthors_plus {
|
|
742 |
);
|
743 |
wp_localize_script( 'co-authors-plus-js', 'coAuthorsPlusStrings', $js_strings );
|
744 |
|
745 |
-
}
|
746 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
747 |
/**
|
748 |
* Adds necessary javascript variables to admin pages
|
749 |
*/
|
750 |
function js_vars() {
|
751 |
-
|
752 |
-
$
|
753 |
-
|
754 |
-
|
755 |
-
?>
|
756 |
<script type="text/javascript">
|
757 |
// AJAX link used for the autosuggest
|
758 |
var coAuthorsPlus_ajax_suggest_link = '<?php echo add_query_arg(
|
759 |
array(
|
760 |
'action' => 'coauthors_ajax_suggest',
|
761 |
-
'post_type' =>
|
762 |
),
|
763 |
wp_nonce_url( 'admin-ajax.php', 'coauthors-search' )
|
764 |
); ?>';
|
765 |
</script>
|
766 |
-
|
767 |
-
|
768 |
-
|
769 |
-
|
770 |
/**
|
771 |
* Helper to only add javascript to necessary pages. Avoids bloat in admin.
|
772 |
*/
|
773 |
function is_valid_page() {
|
774 |
global $pagenow;
|
775 |
-
|
776 |
-
return in_array( $pagenow, $this->_pages_whitelist );
|
777 |
-
}
|
778 |
-
|
779 |
function get_post_id() {
|
780 |
global $post;
|
781 |
$post_id = 0;
|
782 |
-
|
783 |
if ( is_object( $post ) ) {
|
784 |
$post_id = $post->ID;
|
785 |
}
|
786 |
-
|
787 |
if( ! $post_id ) {
|
788 |
if ( isset( $_GET['post'] ) )
|
789 |
$post_id = (int) $_GET['post'];
|
790 |
elseif ( isset( $_POST['post_ID'] ) )
|
791 |
$post_id = (int) $_POST['post_ID'];
|
792 |
}
|
793 |
-
|
794 |
return $post_id;
|
795 |
}
|
796 |
-
|
797 |
/**
|
798 |
* Allows coauthors to edit the post they're coauthors of
|
799 |
-
* Pieces of code borrowed from http://pastebin.ca/1909968
|
800 |
-
*
|
801 |
*/
|
802 |
-
function
|
803 |
-
|
804 |
-
|
805 |
$user_id = isset( $args[1] ) ? $args[1] : 0;
|
806 |
$post_id = isset( $args[2] ) ? $args[2] : 0;
|
807 |
-
|
808 |
-
if( ! $post_id )
|
809 |
-
$post_id = $this->get_post_id();
|
810 |
-
|
811 |
-
if( ! $post_id )
|
812 |
-
return $allcaps;
|
813 |
-
|
814 |
-
$post = get_post( $post_id );
|
815 |
-
|
816 |
-
if( ! $post )
|
817 |
-
return $allcaps;
|
818 |
-
|
819 |
-
$post_type_object = get_post_type_object( $post->post_type );
|
820 |
|
821 |
-
|
822 |
-
if ( !
|
823 |
return $allcaps;
|
824 |
|
825 |
-
|
826 |
-
|
827 |
-
|
828 |
-
|
829 |
-
|
830 |
-
if (
|
831 |
return $allcaps;
|
832 |
-
|
833 |
-
//
|
834 |
-
if
|
835 |
return $allcaps;
|
836 |
-
|
837 |
-
|
838 |
-
if(
|
839 |
-
|
840 |
-
|
841 |
-
|
842 |
-
|
843 |
-
|
|
|
844 |
return $allcaps;
|
845 |
}
|
846 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
847 |
/**
|
848 |
* Filter Edit Flow's 'ef_calendar_item_information_fields' to add co-authors
|
849 |
*
|
850 |
-
* @see https://github.com/
|
851 |
*/
|
852 |
function filter_ef_calendar_item_information_fields( $information_fields, $post_id ) {
|
853 |
|
@@ -869,7 +1161,7 @@ class coauthors_plus {
|
|
869 |
/**
|
870 |
* Filter Edit Flow's 'ef_story_budget_term_column_value' to add co-authors to the story budget
|
871 |
*
|
872 |
-
* @see https://github.com/
|
873 |
*/
|
874 |
function filter_ef_story_budget_term_column_value( $column_name, $post, $parent_term ) {
|
875 |
|
@@ -984,9 +1276,9 @@ function wp_notify_postauthor( $comment_id, $comment_type = '' ) {
|
|
984 |
if ( isset($reply_to) )
|
985 |
$message_headers .= $reply_to . "\n";
|
986 |
|
987 |
-
$notify_message = apply_filters('comment_notification_text', $notify_message, $comment_id);
|
988 |
-
$subject = apply_filters('comment_notification_subject', $subject, $comment_id);
|
989 |
-
$message_headers = apply_filters('comment_notification_headers', $message_headers, $comment_id);
|
990 |
|
991 |
@wp_mail( $author->user_email, $subject, $notify_message, $message_headers );
|
992 |
}
|
@@ -1079,4 +1371,4 @@ function wp_notify_moderator( $comment_id ) {
|
|
1079 |
|
1080 |
return true;
|
1081 |
}
|
1082 |
-
endif;
|
3 |
Plugin Name: Co-Authors Plus
|
4 |
Plugin URI: http://wordpress.org/extend/plugins/co-authors-plus/
|
5 |
Description: Allows multiple authors to be assigned to a post. This plugin is an extended version of the Co-Authors plugin developed by Weston Ruter.
|
6 |
+
Version: 3.0
|
7 |
Author: Mohammad Jangda, Daniel Bachhuber, Automattic
|
8 |
Copyright: 2008-2012 Shared and distributed between Mohammad Jangda, Daniel Bachhuber, Weston Ruter
|
9 |
|
24 |
|
25 |
*/
|
26 |
|
27 |
+
define( 'COAUTHORS_PLUS_VERSION', '3.0' );
|
28 |
|
29 |
define( 'COAUTHORS_PLUS_PATH', dirname( __FILE__ ) );
|
30 |
define( 'COAUTHORS_PLUS_URL', plugin_dir_url( __FILE__ ) );
|
31 |
|
32 |
require_once( dirname( __FILE__ ) . '/template-tags.php' );
|
33 |
|
34 |
+
require_once( dirname( __FILE__ ) . '/php/class-coauthors-template-filters.php' );
|
35 |
+
|
36 |
+
if ( defined('WP_CLI') && WP_CLI )
|
37 |
+
require_once( dirname( __FILE__ ) . '/php/class-wp-cli.php' );
|
38 |
+
|
39 |
class coauthors_plus {
|
40 |
+
|
41 |
+
// Name for the taxonomy we're using to store relationships
|
42 |
+
// and the post type we're using to store co-authors
|
43 |
var $coauthor_taxonomy = 'author';
|
44 |
+
|
45 |
var $coreauthors_meta_box_name = 'authordiv';
|
46 |
var $coauthors_meta_box_name = 'coauthorsdiv';
|
47 |
+
var $force_guest_authors = false;
|
48 |
+
|
49 |
var $gravatar_size = 25;
|
50 |
+
|
51 |
var $_pages_whitelist = array( 'post.php', 'post-new.php' );
|
52 |
+
|
53 |
+
var $supported_post_types = array();
|
54 |
+
|
55 |
+
var $ajax_search_fields = array( 'display_name', 'first_name', 'last_name', 'user_login', 'ID', 'user_email' );
|
56 |
+
|
57 |
+
var $having_terms = '';
|
58 |
+
|
59 |
/**
|
60 |
* __construct()
|
61 |
*/
|
62 |
function __construct() {
|
63 |
|
64 |
+
// Register our models
|
65 |
+
add_action( 'init', array( $this, 'action_init' ) );
|
66 |
+
add_action( 'init', array( $this, 'action_init_late' ), 100 );
|
67 |
|
68 |
// Load admin_init function
|
69 |
add_action( 'admin_init', array( $this,'admin_init' ) );
|
70 |
+
|
|
|
|
|
|
|
71 |
// Modify SQL queries to include coauthors
|
72 |
+
add_filter( 'posts_where', array( $this, 'posts_where_filter' ), 10, 2 );
|
73 |
+
add_filter( 'posts_join', array( $this, 'posts_join_filter' ), 10, 2 );
|
74 |
+
add_filter( 'posts_groupby', array( $this, 'posts_groupby_filter' ), 10, 2 );
|
75 |
+
|
76 |
// Action to set users when a post is saved
|
77 |
add_action( 'save_post', array( $this, 'coauthors_update_post' ), 10, 2 );
|
78 |
// Filter to set the post_author field when wp_insert_post is called
|
79 |
add_filter( 'wp_insert_post_data', array( $this, 'coauthors_set_post_author_field' ), 10, 2 );
|
80 |
+
|
81 |
// Action to reassign posts when a user is deleted
|
82 |
add_action( 'delete_user', array( $this, 'delete_user_action' ) );
|
83 |
+
|
84 |
add_filter( 'get_usernumposts', array( $this, 'filter_count_user_posts' ), 10, 2 );
|
85 |
+
|
86 |
// Action to set up author auto-suggest
|
87 |
add_action( 'wp_ajax_coauthors_ajax_suggest', array( $this, 'ajax_suggest' ) );
|
88 |
+
|
89 |
// Filter to allow coauthors to edit posts
|
90 |
+
add_filter( 'user_has_cap', array( $this, 'filter_user_has_cap' ), 10, 3 );
|
91 |
|
92 |
// Handle the custom author meta box
|
93 |
add_action( 'add_meta_boxes', array( $this, 'add_coauthors_box' ) );
|
94 |
add_action( 'add_meta_boxes', array( $this, 'remove_authors_box' ) );
|
95 |
+
|
96 |
+
// Removes the author dropdown from the post quick edit
|
97 |
+
add_action( 'admin_head', array( $this, 'remove_quick_edit_authors_box' ) );
|
98 |
|
99 |
// Restricts WordPress from blowing away term order on bulk edit
|
100 |
+
add_filter( 'wp_get_object_terms', array( $this, 'filter_wp_get_object_terms' ), 10, 4 );
|
101 |
+
|
102 |
// Fix for author info not properly displaying on author pages
|
103 |
+
add_action( 'template_redirect', array( $this, 'fix_author_page' ) );
|
104 |
add_action( 'the_post', array( $this, 'fix_author_page' ) );
|
105 |
|
106 |
// Support for Edit Flow's calendar and story budget
|
111 |
|
112 |
function coauthors_plus() {
|
113 |
$this->__construct();
|
114 |
+
}
|
115 |
+
|
116 |
/**
|
117 |
+
* Register the taxonomy used to managing relationships,
|
118 |
+
* and the custom post type to store our author data
|
119 |
+
*/
|
120 |
+
function action_init() {
|
121 |
+
|
122 |
+
// Allow Co-Authors Plus to be easily translated
|
123 |
+
load_plugin_textdomain( 'co-authors-plus', null, dirname( plugin_basename( __FILE__ ) ) . '/languages/' );
|
124 |
+
|
125 |
+
// Load the Guest Authors functionality if needed
|
126 |
+
if ( $this->is_guest_authors_enabled() ) {
|
127 |
+
require_once( dirname( __FILE__ ) . '/php/class-coauthors-guest-authors.php' );
|
128 |
+
$this->guest_authors = new CoAuthors_Guest_Authors;
|
129 |
+
if ( apply_filters( 'coauthors_guest_authors_force', false ) ) {
|
130 |
+
$this->force_guest_authors = true;
|
131 |
+
}
|
132 |
+
}
|
133 |
+
|
134 |
+
// Maybe automatically apply our template tags
|
135 |
+
if ( apply_filters( 'coauthors_auto_apply_template_tags', false ) ) {
|
136 |
+
new CoAuthors_Template_Filters;
|
137 |
+
}
|
138 |
+
|
139 |
+
}
|
140 |
+
|
141 |
+
/**
|
142 |
+
* Register the 'author' taxonomy and add post type support
|
143 |
+
*/
|
144 |
+
function action_init_late() {
|
145 |
+
|
146 |
+
// Register new taxonomy so that we can store all of the relationships
|
147 |
+
$args = array(
|
148 |
+
'hierarchical' => false,
|
149 |
+
'update_count_callback' => array( $this, '_update_users_posts_count' ),
|
150 |
+
'label' => false,
|
151 |
+
'query_var' => false,
|
152 |
+
'rewrite' => false,
|
153 |
+
'public' => false,
|
154 |
+
'sort' => true,
|
155 |
+
'show_ui' => false
|
156 |
+
);
|
157 |
+
$post_types_with_authors = array_values( get_post_types() );
|
158 |
+
foreach( $post_types_with_authors as $key => $name ) {
|
159 |
+
if ( ! post_type_supports( $name, 'author' ) )
|
160 |
+
unset( $post_types_with_authors[$key] );
|
161 |
+
}
|
162 |
+
$this->supported_post_types = apply_filters( 'coauthors_supported_post_types', $post_types_with_authors );
|
163 |
+
register_taxonomy( $this->coauthor_taxonomy, $this->supported_post_types, $args );
|
164 |
+
}
|
165 |
+
|
166 |
+
/**
|
167 |
+
* Initialize the plugin for the admin
|
168 |
*/
|
169 |
function admin_init() {
|
170 |
global $pagenow;
|
171 |
|
172 |
+
// Add the main JS script and CSS file
|
173 |
+
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
|
174 |
+
// Add necessary JS variables
|
175 |
+
add_action( 'admin_head', array( $this, 'js_vars' ) );
|
176 |
|
177 |
// Hooks to add additional coauthors to author column to Edit page
|
178 |
add_filter( 'manage_posts_columns', array( $this, '_filter_manage_posts_columns' ) );
|
182 |
|
183 |
// Hooks to modify the published post number count on the Users WP List Table
|
184 |
add_filter( 'manage_users_columns', array( $this, '_filter_manage_users_columns' ) );
|
185 |
+
add_filter( 'manage_users_custom_column', array( $this, '_filter_manage_users_custom_column' ), 10, 3 );
|
186 |
+
|
187 |
+
// Apply some targeted filters
|
188 |
+
add_action( 'load-edit.php', array( $this, 'load_edit' ) );
|
189 |
+
|
190 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
191 |
|
192 |
+
/**
|
193 |
+
* Check whether the guest authors functionality is enabled or not
|
194 |
+
* Guest authors can be disabled entirely with:
|
195 |
+
* add_filter( 'coauthors_guest_authors_enabled', '__return_false' )
|
196 |
+
*
|
197 |
+
* @since 3.0
|
198 |
+
*/
|
199 |
+
function is_guest_authors_enabled() {
|
200 |
+
return apply_filters( 'coauthors_guest_authors_enabled', true );
|
201 |
}
|
202 |
+
|
203 |
+
/**
|
204 |
+
* Get a co-author object by a specific type of key
|
205 |
+
*
|
206 |
+
* @param string $key Key to search by (slug,email)
|
207 |
+
* @param string $value Value to search for
|
208 |
+
* @param object|false $coauthor The co-author on success, false on failure
|
209 |
*/
|
210 |
+
function get_coauthor_by( $key, $value ) {
|
211 |
+
|
212 |
+
// If Guest Authors are enabled, prioritize those profiles
|
213 |
+
if ( $this->is_guest_authors_enabled() ) {
|
214 |
+
$guest_author = $this->guest_authors->get_guest_author_by( $key, $value );
|
215 |
+
if ( is_object( $guest_author ) ) {
|
216 |
+
return $guest_author;
|
217 |
+
}
|
218 |
+
}
|
219 |
+
|
220 |
+
switch( $key ) {
|
221 |
+
case 'id':
|
222 |
+
case 'login':
|
223 |
+
case 'user_login':
|
224 |
+
case 'email':
|
225 |
+
case 'user_nicename':
|
226 |
+
case 'user_email':
|
227 |
+
if ( 'user_login' == $key )
|
228 |
+
$key = 'login';
|
229 |
+
if ( 'user_email' == $key )
|
230 |
+
$key = 'email';
|
231 |
+
if ( 'user_nicename' == $key )
|
232 |
+
$key = 'slug';
|
233 |
+
// Ensure we aren't doing the lookup by the prefixed value
|
234 |
+
if ( 'login' == $key || 'slug' == $key )
|
235 |
+
$value = preg_replace( '#^cap\-#', '', $value );
|
236 |
+
$user = get_user_by( $key, $value );
|
237 |
+
if ( !$user || !is_user_member_of_blog( $user->ID ) )
|
238 |
+
return false;
|
239 |
+
$user->type = 'wpuser';
|
240 |
+
// However, if guest authors are enabled and there's a guest author linked to this
|
241 |
+
// user account, we want to use that instead
|
242 |
+
if ( $this->is_guest_authors_enabled() ) {
|
243 |
+
$guest_author = $this->guest_authors->get_guest_author_by( 'linked_account', $user->user_login );
|
244 |
+
if ( is_object( $guest_author ) )
|
245 |
+
$user = $guest_author;
|
246 |
+
}
|
247 |
+
return $user;
|
248 |
+
break;
|
249 |
+
}
|
250 |
return false;
|
251 |
+
|
252 |
}
|
253 |
+
|
254 |
/**
|
255 |
+
* Whether or not Co-Authors Plus is enabled for this post type
|
256 |
+
* Must be called after init
|
257 |
+
*
|
258 |
+
* @since 3.0
|
259 |
+
*
|
260 |
+
* @param string $post_type The name of the post type we're considering
|
261 |
+
* @return bool Whether or not it's enabled
|
262 |
*/
|
263 |
+
function is_post_type_enabled( $post_type = null ) {
|
264 |
+
|
265 |
+
if ( ! $post_type )
|
266 |
+
$post_type = get_post_type();
|
267 |
+
|
268 |
+
return (bool) in_array( $post_type, $this->supported_post_types );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
269 |
}
|
270 |
+
|
271 |
/**
|
272 |
* Removes the standard WordPress Author box.
|
273 |
* We don't need it because the Co-Authors one is way cooler.
|
274 |
*/
|
275 |
function remove_authors_box() {
|
276 |
+
|
277 |
+
if ( $this->is_post_type_enabled() )
|
278 |
+
remove_meta_box( $this->coreauthors_meta_box_name, get_post_type(), 'normal' );
|
|
|
|
|
279 |
}
|
280 |
+
|
281 |
/**
|
282 |
* Adds a custom Authors box
|
283 |
*/
|
284 |
function add_coauthors_box() {
|
285 |
+
|
286 |
+
if( $this->is_post_type_enabled() && $this->current_user_can_set_authors() )
|
287 |
+
add_meta_box( $this->coauthors_meta_box_name, __('Authors', 'co-authors-plus'), array( $this, 'coauthors_meta_box' ), get_post_type(), apply_filters( 'coauthors_meta_box_context', 'normal'), apply_filters( 'coauthors_meta_box_priority', 'high'));
|
|
|
|
|
288 |
}
|
289 |
+
|
290 |
/**
|
291 |
* Callback for adding the custom author box
|
292 |
*/
|
293 |
function coauthors_meta_box( $post ) {
|
294 |
+
global $post, $coauthors_plus, $current_screen;
|
295 |
+
|
296 |
$post_id = $post->ID;
|
297 |
+
|
298 |
+
// @daniel, $post_id and $post->post_author are always set when a new post is created due to auto draft,
|
299 |
+
// and the else case below was always able to properly assign users based on wp_posts.post_author,
|
300 |
+
// but that's not possible with force_guest_authors = true.
|
301 |
+
if( !$post_id || $post_id == 0 || ( !$post->post_author && !$coauthors_plus->force_guest_authors ) || ( $current_screen->base == 'post' && $current_screen->action == 'add' ) ) {
|
302 |
+
$coauthors = array();
|
303 |
+
// If guest authors is enabled, try to find a guest author attached to this user ID
|
304 |
+
if ( $this->is_guest_authors_enabled() ) {
|
305 |
+
$coauthor = $coauthors_plus->guest_authors->get_guest_author_by( 'linked_account', wp_get_current_user()->user_login );
|
306 |
+
if ( $coauthor ) {
|
307 |
+
$coauthors[] = $coauthor;
|
308 |
+
}
|
309 |
+
}
|
310 |
+
// If the above block was skipped, or if it failed to find a guest author, use the current
|
311 |
+
// logged in user, so long as force_guest_authors is false. If force_guest_authors = true, we are
|
312 |
+
// OK with having an empty authoring box.
|
313 |
+
if ( !$coauthors_plus->force_guest_authors && empty( $coauthors ) ) {
|
314 |
+
$coauthors[] = wp_get_current_user();
|
315 |
+
}
|
316 |
+
} else {
|
317 |
$coauthors = get_coauthors();
|
318 |
+
}
|
319 |
+
|
320 |
$count = 0;
|
321 |
if( !empty( $coauthors ) ) :
|
322 |
?>
|
337 |
<?php
|
338 |
endforeach;
|
339 |
?>
|
340 |
+
</ul>
|
341 |
<div class="clear"></div>
|
342 |
<p><?php _e( '<strong>Note:</strong> To edit post authors, please enable javascript or use a javascript-capable browser', 'co-authors-plus' ); ?></p>
|
343 |
</div>
|
344 |
<?php
|
345 |
endif;
|
346 |
?>
|
347 |
+
|
348 |
<div id="coauthors-edit" class="hide-if-no-js">
|
349 |
<p><?php _e( 'Click on an author to change them. Drag to change their order. Click on <strong>Remove</strong> to remove them.', 'co-authors-plus' ); ?></p>
|
350 |
</div>
|
351 |
+
|
352 |
<?php wp_nonce_field( 'coauthors-edit', 'coauthors-nonce' ); ?>
|
353 |
+
|
354 |
<?php
|
355 |
}
|
356 |
+
|
357 |
/**
|
358 |
* Removes the author dropdown from the post quick edit
|
359 |
+
* It's a bit hacky, but the only way I can figure out :(
|
360 |
*/
|
361 |
function remove_quick_edit_authors_box() {
|
362 |
+
global $pagenow;
|
363 |
+
|
364 |
+
if ( 'edit.php' == $pagenow && $this->is_post_type_enabled() )
|
365 |
+
remove_post_type_support( get_post_type(), 'author' );
|
|
|
366 |
}
|
367 |
+
|
368 |
/**
|
369 |
* Add coauthors to author column on edit pages
|
370 |
+
*
|
371 |
* @param array $post_columns
|
372 |
*/
|
373 |
+
function _filter_manage_posts_columns( $posts_columns ) {
|
374 |
+
|
375 |
$new_columns = array();
|
376 |
+
if ( ! $this->is_post_type_enabled() )
|
377 |
+
return $posts_columns;
|
378 |
+
|
379 |
foreach ($posts_columns as $key => $value) {
|
380 |
$new_columns[$key] = $value;
|
381 |
if( $key == 'title' )
|
382 |
$new_columns['coauthors'] = __( 'Authors', 'co-authors-plus' );
|
383 |
+
|
384 |
if ( $key == 'author' )
|
385 |
unset($new_columns[$key]);
|
386 |
}
|
387 |
return $new_columns;
|
388 |
+
}
|
389 |
+
|
390 |
/**
|
391 |
* Insert coauthors into post rows on Edit Page
|
392 |
+
*
|
393 |
* @param string $column_name
|
394 |
+
*/
|
395 |
+
function _filter_manage_posts_custom_column( $column_name ) {
|
396 |
if ($column_name == 'coauthors') {
|
397 |
global $post;
|
398 |
$authors = get_coauthors( $post->ID );
|
399 |
+
|
400 |
$count = 1;
|
401 |
foreach( $authors as $author ) :
|
402 |
+
$args = array(
|
403 |
+
'author_name' => $author->user_nicename,
|
404 |
+
);
|
405 |
+
if ( 'post' != $post->post_type )
|
406 |
+
$args['post_type'] = $post->post_type;
|
407 |
+
$author_filter_url = add_query_arg( $args, admin_url( 'edit.php' ) );
|
408 |
?>
|
409 |
+
<a href="<?php echo esc_url( $author_filter_url ); ?>"><?php echo esc_html( $author->display_name ); ?></a><?php echo ( $count < count( $authors ) ) ? ',' : ''; ?>
|
410 |
<?php
|
411 |
$count++;
|
412 |
endforeach;
|
437 |
return $value;
|
438 |
// We filter count_user_posts() so it provides an accurate number
|
439 |
$numposts = count_user_posts( $user_id );
|
440 |
+
$user = get_user_by( 'id', $user_id );
|
441 |
if ( $numposts > 0 ) {
|
442 |
+
$value .= "<a href='edit.php?author_name=$user->user_nicename' title='" . esc_attr__( 'View posts by this author', 'co-authors-plus' ) . "' class='edit'>";
|
443 |
$value .= $numposts;
|
444 |
$value .= '</a>';
|
445 |
} else {
|
451 |
/**
|
452 |
* When we update the terms at all, we should update the published post count for each author
|
453 |
*/
|
454 |
+
function _update_users_posts_count( $tt_ids, $taxonomy ) {
|
455 |
global $wpdb;
|
456 |
|
457 |
+
$tt_ids = implode( ', ', array_map( 'intval', $tt_ids ) );
|
458 |
+
$term_ids = $wpdb->get_results( "SELECT term_id FROM $wpdb->term_taxonomy WHERE term_taxonomy_id IN ($tt_ids)" );
|
459 |
|
460 |
+
foreach( (array)$term_ids as $term_id_result ) {
|
461 |
+
$term = get_term_by( 'id', $term_id_result->term_id, $this->coauthor_taxonomy );
|
462 |
+
$this->update_author_term_post_count( $term );
|
463 |
}
|
464 |
+
$tt_ids = explode( ', ', $tt_ids );
|
465 |
+
clean_term_cache( $tt_ids, '', false );
|
466 |
|
467 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
468 |
|
469 |
+
/**
|
470 |
+
* Update the post count associated with an author term
|
471 |
+
*
|
472 |
+
* @since 3.0
|
473 |
+
*
|
474 |
+
* @param object $term The co-author term
|
475 |
+
*/
|
476 |
+
public function update_author_term_post_count( $term ) {
|
477 |
+
global $wpdb;
|
478 |
+
|
479 |
+
$coauthor = $this->get_coauthor_by( 'user_nicename', $term->slug );
|
480 |
+
if ( ! $coauthor )
|
481 |
+
return new WP_Error( 'missing-coauthor', __( 'No co-author exists for that term', 'co-authors-plus' ) );
|
482 |
+
|
483 |
+
$query = "SELECT COUNT({$wpdb->posts}.ID) FROM {$wpdb->posts}";
|
484 |
+
|
485 |
+
$query .= " LEFT JOIN {$wpdb->term_relationships} ON ({$wpdb->posts}.ID = {$wpdb->term_relationships}.object_id)";
|
486 |
+
$query .= " LEFT JOIN {$wpdb->term_taxonomy} ON ( {$wpdb->term_relationships}.term_taxonomy_id = {$wpdb->term_taxonomy}.term_taxonomy_id )";
|
487 |
+
|
488 |
+
$having_terms_and_authors = $having_terms = $wpdb->prepare( "{$wpdb->term_taxonomy}.term_id = %d", $term->term_id );
|
489 |
+
if ( 'wpuser' == $coauthor->type )
|
490 |
+
$having_terms_and_authors .= $wpdb->prepare( " OR {$wpdb->posts}.post_author = %d", $coauthor->ID );
|
491 |
+
|
492 |
+
$query .= " WHERE ({$having_terms_and_authors}) AND {$wpdb->posts}.post_type = 'post' AND {$wpdb->posts}.post_status = 'publish'";
|
493 |
+
|
494 |
+
$query .= $wpdb->prepare( " GROUP BY {$wpdb->posts}.ID HAVING MAX( IF( {$wpdb->term_taxonomy}.taxonomy = '%s', IF( {$having_terms},2,1 ),0 ) ) <> 1 ", $this->coauthor_taxonomy );
|
495 |
+
|
496 |
+
$count = $wpdb->query( $query );
|
497 |
+
$wpdb->update( $wpdb->term_taxonomy, array( 'count' => $count ), array( 'term_taxonomy_id' => $term->term_taxonomy_id ) );
|
498 |
+
|
499 |
+
wp_cache_delete( 'author-term-' . $coauthor->user_nicename, 'co-authors-plus' );
|
500 |
}
|
501 |
+
|
502 |
/**
|
503 |
* Modify the author query posts SQL to include posts co-authored
|
504 |
*/
|
505 |
+
function posts_join_filter( $join, $query ){
|
506 |
+
global $wpdb;
|
507 |
+
|
508 |
+
if( $query->is_author() ) {
|
509 |
+
|
510 |
+
if ( !empty( $query->query_vars['post_type'] ) && !is_object_in_taxonomy( $query->query_vars['post_type'], $this->coauthor_taxonomy ) )
|
511 |
+
return $join;
|
512 |
+
|
513 |
// Check to see that JOIN hasn't already been added. Props michaelingp and nbaxley
|
514 |
$term_relationship_join = " INNER JOIN {$wpdb->term_relationships} ON ({$wpdb->posts}.ID = {$wpdb->term_relationships}.object_id)";
|
515 |
$term_taxonomy_join = " INNER JOIN {$wpdb->term_taxonomy} ON ( {$wpdb->term_relationships}.term_taxonomy_id = {$wpdb->term_taxonomy}.term_taxonomy_id )";
|
516 |
+
|
517 |
if( strpos( $join, trim( $term_relationship_join ) ) === false ) {
|
518 |
+
$join .= str_replace( "INNER JOIN", "LEFT JOIN", $term_relationship_join );
|
519 |
}
|
520 |
if( strpos( $join, trim( $term_taxonomy_join ) ) === false ) {
|
521 |
+
$join .= str_replace( "INNER JOIN", "LEFT JOIN", $term_taxonomy_join );
|
522 |
}
|
523 |
}
|
524 |
+
|
525 |
return $join;
|
526 |
}
|
527 |
+
|
528 |
/**
|
529 |
+
* Modify the author query posts SQL to include posts co-authored
|
530 |
*/
|
531 |
+
function posts_where_filter( $where, $query ){
|
532 |
+
global $wpdb;
|
533 |
+
|
534 |
+
if ( $query->is_author() ) {
|
535 |
+
|
536 |
+
if ( !empty( $query->query_vars['post_type'] ) && !is_object_in_taxonomy( $query->query_vars['post_type'], $this->coauthor_taxonomy ) )
|
537 |
+
return $where;
|
|
|
|
|
538 |
|
539 |
+
if ( $query->get( 'author_name' ) )
|
540 |
+
$author_name = sanitize_title( $query->get( 'author_name' ) );
|
541 |
+
else
|
542 |
+
$author_name = get_userdata( $query->get( 'author' ) )->user_nicename;
|
543 |
+
|
544 |
+
$terms = array();
|
545 |
+
$coauthor = $this->get_coauthor_by( 'user_nicename', $author_name );
|
546 |
+
if ( $author_term = $this->get_author_term( $coauthor ) )
|
547 |
+
$terms[] = $author_term;
|
548 |
+
// If this coauthor has a linked account, we also need to get posts with those terms
|
549 |
+
if ( ! empty( $coauthor->linked_account ) ) {
|
550 |
+
$linked_account = get_user_by( 'login', $coauthor->linked_account );
|
551 |
+
if ( $guest_author_term = $this->get_author_term( $linked_account ) )
|
552 |
+
$terms[] = $guest_author_term;
|
553 |
+
}
|
554 |
+
|
555 |
+
// Whether or not to include the original 'post_author' value in the query
|
556 |
+
if ( $this->force_guest_authors )
|
557 |
+
$maybe_both = false;
|
558 |
+
else
|
559 |
+
$maybe_both = apply_filters( 'coauthors_plus_should_query_post_author', true );
|
560 |
+
|
561 |
+
$maybe_both_query = $maybe_both ? '$1 OR' : '';
|
562 |
+
|
563 |
+
if ( !empty( $terms ) ) {
|
564 |
+
$terms_implode = '';
|
565 |
+
$this->having_terms = '';
|
566 |
+
foreach( $terms as $term ) {
|
567 |
+
$terms_implode .= '(' . $wpdb->term_taxonomy . '.taxonomy = \''. $this->coauthor_taxonomy.'\' AND '. $wpdb->term_taxonomy .'.term_id = \''. $term->term_id .'\') OR ';
|
568 |
+
$this->having_terms .= ' ' . $wpdb->term_taxonomy .'.term_id = \''. $term->term_id .'\' OR ';
|
569 |
+
}
|
570 |
+
$terms_implode = rtrim( $terms_implode, ' OR' );
|
571 |
+
$this->having_terms = rtrim( $this->having_terms, ' OR' );
|
572 |
+
$where = preg_replace( '/(\b(?:' . $wpdb->posts . '\.)?post_author\s*=\s*(\d+))/', '(' . $maybe_both_query . ' ' . $terms_implode . ')', $where, 1 ); #' . $wpdb->postmeta . '.meta_id IS NOT NULL AND
|
573 |
}
|
574 |
+
|
575 |
}
|
576 |
return $where;
|
577 |
}
|
578 |
+
|
579 |
/**
|
580 |
+
* Modify the author query posts SQL to include posts co-authored
|
581 |
*/
|
582 |
+
function posts_groupby_filter( $groupby, $query ) {
|
583 |
global $wpdb;
|
584 |
+
|
585 |
+
if( $query->is_author() ) {
|
586 |
+
|
587 |
+
if ( !empty( $query->query_vars['post_type'] ) && !is_object_in_taxonomy( $query->query_vars['post_type'], $this->coauthor_taxonomy ) )
|
588 |
+
return $groupby;
|
589 |
+
|
590 |
+
if ( $this->having_terms ) {
|
591 |
+
$having = 'MAX( IF( ' . $wpdb->term_taxonomy . '.taxonomy = \''. $this->coauthor_taxonomy.'\', IF( ' . $this->having_terms . ',2,1 ),0 ) ) <> 1 ';
|
592 |
+
$groupby = $wpdb->posts . '.ID HAVING ' . $having;
|
593 |
+
}
|
594 |
}
|
595 |
return $groupby;
|
596 |
}
|
597 |
+
|
598 |
/**
|
599 |
* Filters post data before saving to db to set post_author
|
600 |
*/
|
601 |
function coauthors_set_post_author_field( $data, $postarr ) {
|
602 |
+
|
603 |
// Bail on autosave
|
604 |
if ( defined( 'DOING_AUTOSAVE' ) && !DOING_AUTOSAVE )
|
605 |
return $data;
|
606 |
+
|
607 |
// Bail on revisions
|
608 |
+
if ( $data['post_type'] == 'revision' )
|
609 |
return $data;
|
610 |
|
611 |
+
// This action happens when a post is saved while editing a post
|
|
|
612 |
if( isset( $_REQUEST['coauthors-nonce'] ) && isset( $_POST['coauthors'] ) && is_array( $_POST['coauthors'] ) ) {
|
613 |
+
$author = sanitize_text_field( $_POST['coauthors'][0] );
|
614 |
+
if ( $author ) {
|
615 |
+
$author_data = $this->get_coauthor_by( 'user_nicename', $author );
|
616 |
+
// If it's a guest author and has a linked account, store that information in post_author
|
617 |
+
// because it'll be the valid user ID
|
618 |
+
if ( 'guest-author' == $author_data->type && ! empty( $author_data->linked_account ) ) {
|
619 |
+
$data['post_author'] = get_user_by( 'login', $author_data->linked_account )->ID;
|
620 |
+
} else if ( $author_data->type == 'wpuser' )
|
621 |
+
$data['post_author'] = $author_data->ID;
|
622 |
}
|
623 |
}
|
624 |
|
627 |
// 'post_author' is set to current user if the $_REQUEST value doesn't exist
|
628 |
if ( isset( $_REQUEST['action'] ) && $_REQUEST['action'] == 'inline-save' ) {
|
629 |
$coauthors = get_coauthors( $postarr['ID'] );
|
630 |
+
if ( is_array( $coauthors ) ) {
|
631 |
+
$coauthor = $this->get_coauthor_by( 'user_nicename', $coauthors[0]->user_nicename );
|
632 |
+
if ( 'guest-author' == $coauthor->type && ! empty( $coauthor->linked_account ) ) {
|
633 |
+
$data['post_author'] = get_user_by( 'user_login', $coauthor->linked_account )->ID;
|
634 |
+
} else if ( $coauthor->type == 'wpuser' )
|
635 |
+
$data['post_author'] = $coauthor->ID;
|
636 |
+
// Refresh their post publish count too
|
637 |
+
if ( 'publish' == $postarr['post_status'] || 'publish' == get_post_status( $postarr['ID'] ) ) {
|
638 |
+
foreach( $coauthors as $coauthor ) {
|
639 |
+
if ( $author_term = $this->get_author_term( $coauthor ) )
|
640 |
+
$this->update_author_term_post_count( $author_term );
|
641 |
+
}
|
642 |
+
}
|
643 |
+
}
|
644 |
}
|
645 |
|
646 |
// If for some reason we don't have the coauthors fields set
|
648 |
$user = wp_get_current_user();
|
649 |
$data['post_author'] = $user->ID;
|
650 |
}
|
651 |
+
|
652 |
+
// Allow the 'post_author' to be forced to generic user if it doesn't match any users on the post
|
653 |
+
$data['post_author'] = apply_filters( 'coauthors_post_author_value', $data['post_author'], $postarr['ID'] );
|
654 |
+
|
655 |
return $data;
|
656 |
}
|
657 |
+
|
658 |
/**
|
659 |
+
* Update a post's co-authors on the 'save_post' hook
|
660 |
+
*
|
661 |
* @param $post_ID
|
|
|
662 |
*/
|
663 |
function coauthors_update_post( $post_id, $post ) {
|
664 |
$post_type = $post->post_type;
|
665 |
+
|
666 |
if ( defined( 'DOING_AUTOSAVE' ) && !DOING_AUTOSAVE )
|
667 |
return;
|
668 |
+
|
669 |
if( isset( $_POST['coauthors-nonce'] ) && isset( $_POST['coauthors'] ) ) {
|
670 |
check_admin_referer( 'coauthors-edit', 'coauthors-nonce' );
|
671 |
+
|
672 |
if( $this->current_user_can_set_authors() ){
|
673 |
$coauthors = (array) $_POST['coauthors'];
|
674 |
+
$coauthors = array_map( 'sanitize_text_field', $coauthors );
|
675 |
return $this->add_coauthors( $post_id, $coauthors );
|
676 |
}
|
677 |
}
|
678 |
}
|
679 |
+
|
680 |
/**
|
681 |
+
* Add one or more co-authors as bylines for a post
|
682 |
*/
|
683 |
function add_coauthors( $post_id, $coauthors, $append = false ) {
|
684 |
global $current_user;
|
685 |
+
|
686 |
$post_id = (int) $post_id;
|
687 |
$insert = false;
|
688 |
+
|
689 |
// if an array isn't returned, create one and populate with default author
|
690 |
if ( !is_array( $coauthors ) || 0 == count( $coauthors ) || empty( $coauthors ) ) {
|
691 |
$coauthors = array( $current_user->user_login );
|
692 |
}
|
693 |
+
|
694 |
// Add each co-author to the post meta
|
695 |
+
foreach( array_unique( $coauthors ) as $key => $author_name ){
|
696 |
+
|
697 |
+
$author = $this->get_coauthor_by( 'user_login', $author_name );
|
698 |
+
$term = $this->update_author_term( $author );
|
699 |
+
$coauthors[$key] = $term->slug;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
700 |
}
|
701 |
+
wp_set_post_terms( $post_id, $coauthors, $this->coauthor_taxonomy, $append );
|
702 |
}
|
703 |
+
|
704 |
/**
|
705 |
* Action taken when user is deleted.
|
706 |
* - User term is removed from all associated posts
|
709 |
*/
|
710 |
function delete_user_action($delete_id){
|
711 |
global $wpdb;
|
712 |
+
|
713 |
$reassign_id = absint( $_POST['reassign_user'] );
|
714 |
+
|
715 |
// If reassign posts, do that -- use coauthors_update_post
|
716 |
if($reassign_id) {
|
717 |
// Get posts belonging to deleted author
|
727 |
}
|
728 |
}
|
729 |
}
|
730 |
+
|
731 |
$delete_user = get_user_by( 'id', $delete_id );
|
732 |
if ( is_object( $delete_user ) ) {
|
733 |
// Delete term
|
746 |
if ( !isset( $_REQUEST['bulk_edit'] ) || $taxonomies != "'author'" )
|
747 |
return $terms;
|
748 |
|
749 |
+
global $wpdb;
|
750 |
+
$orderby = 'ORDER BY tr.term_order';
|
751 |
$order = 'ASC';
|
752 |
$object_ids = (int)$object_ids;
|
753 |
$query = $wpdb->prepare( "SELECT t.slug, t.term_id FROM $wpdb->terms AS t INNER JOIN $wpdb->term_taxonomy AS tt ON tt.term_id = t.term_id INNER JOIN $wpdb->term_relationships AS tr ON tr.term_taxonomy_id = tt.term_taxonomy_id WHERE tt.taxonomy IN (%s) AND tr.object_id IN (%s) $orderby $order", $taxonomies, $object_ids );
|
754 |
+
$raw_coauthors = $wpdb->get_results( $query );
|
755 |
$terms = array();
|
756 |
+
foreach( $raw_coauthors as $author ) {
|
757 |
+
$terms[] = $author->slug;
|
758 |
}
|
759 |
|
760 |
return $terms;
|
761 |
+
|
762 |
}
|
763 |
+
|
764 |
/**
|
765 |
+
* Filter the count_users_posts() core function to include our correct count
|
766 |
*/
|
767 |
function filter_count_user_posts( $count, $user_id ) {
|
768 |
$user = get_userdata( $user_id );
|
769 |
+
|
770 |
+
$term = $this->get_author_term( $user );
|
|
|
771 |
// Only modify the count if the author already exists as a term
|
772 |
if( $term && !is_wp_error( $term ) ) {
|
773 |
$count = $term->count;
|
774 |
}
|
775 |
+
|
776 |
return $count;
|
777 |
}
|
778 |
+
|
779 |
/**
|
780 |
* Checks to see if the current user can set authors or not
|
781 |
*/
|
782 |
function current_user_can_set_authors( ) {
|
783 |
global $post, $typenow;
|
784 |
+
|
785 |
+
$post_type = get_post_type();
|
|
|
|
|
786 |
// TODO: need to fix this; shouldn't just say no if don't have post_type
|
787 |
if( ! $post_type ) return false;
|
788 |
+
|
789 |
$post_type_object = get_post_type_object( $post_type );
|
790 |
+
$current_user = wp_get_current_user();
|
791 |
+
if ( ! $current_user )
|
792 |
+
return false;
|
793 |
+
$can_set_authors = isset( $current_user->allcaps['edit_others_posts'] ) ? $current_user->allcaps['edit_others_posts'] : false;
|
794 |
+
|
795 |
return apply_filters( 'coauthors_plus_edit_authors', $can_set_authors );
|
796 |
}
|
797 |
+
|
798 |
/**
|
799 |
* Fix for author info not properly displaying on author pages
|
800 |
*
|
802 |
* the first author is NOT the same as the author for the archive,
|
803 |
* the query_var is changed.
|
804 |
*
|
805 |
+
* Also, we have to do some hacky WP_Query modification for guest authors
|
806 |
*/
|
807 |
+
function fix_author_page() {
|
808 |
+
|
809 |
+
if ( !is_author() )
|
810 |
+
return;
|
811 |
+
|
812 |
+
global $wp_query, $authordata;
|
813 |
+
|
814 |
+
if ( $author_name = sanitize_title( get_query_var( 'author_name' ) ) ) {
|
815 |
+
$authordata = $this->get_coauthor_by( 'user_nicename', $author_name );
|
816 |
+
if ( is_object( $authordata ) ) {
|
817 |
+
$wp_query->queried_object = $authordata;
|
818 |
+
$wp_query->queried_object_id = $authordata->ID;
|
819 |
}
|
820 |
}
|
821 |
}
|
822 |
+
|
823 |
/**
|
824 |
* Main function that handles search-as-you-type for adding authors
|
825 |
*/
|
826 |
function ajax_suggest() {
|
827 |
+
|
828 |
if( ! isset( $_REQUEST['_wpnonce'] ) || ! wp_verify_nonce( $_REQUEST['_wpnonce'], 'coauthors-search' ) )
|
829 |
die();
|
830 |
+
|
831 |
if( empty( $_REQUEST['q'] ) )
|
832 |
die();
|
833 |
|
834 |
$search = sanitize_text_field( strtolower( $_REQUEST['q'] ) );
|
835 |
+
$ignore = array_map( 'sanitize_text_field', explode( ',', $_REQUEST['existing_authors'] ) );
|
836 |
|
837 |
$authors = $this->search_authors( $search, $ignore );
|
838 |
+
|
839 |
foreach( $authors as $author ) {
|
840 |
+
echo $author->ID ." | ". $author->user_login ." | ". $author->display_name ." | ". $author->user_email ."\n";
|
841 |
}
|
842 |
|
843 |
die();
|
846 |
|
847 |
/**
|
848 |
* Get matching authors based on a search value
|
849 |
+
*/
|
850 |
function search_authors( $search = '', $ignored_authors = array() ) {
|
851 |
|
852 |
+
// Since 2.7, we're searching against the term description for the fields
|
853 |
+
// instead of the user details. If the term is missing, we probably need to
|
854 |
+
// backfill with user details. Let's do this first... easier than running
|
855 |
+
// an upgrade script that could break on a lot of users
|
856 |
$args = array(
|
857 |
'count_total' => false,
|
858 |
'search' => sprintf( '*%s*', $search ),
|
868 |
$found_users = get_users( $args );
|
869 |
remove_filter( 'pre_user_query', array( $this, 'filter_pre_user_query' ) );
|
870 |
|
871 |
+
foreach( $found_users as $found_user ) {
|
872 |
+
$term = $this->get_author_term( $found_user );
|
873 |
+
if ( empty( $term ) || empty( $term->description ) ) {
|
874 |
+
$this->update_author_term( $found_user );
|
875 |
+
}
|
876 |
+
}
|
877 |
+
|
878 |
+
$args = array(
|
879 |
+
'search' => $search,
|
880 |
+
'get' => 'all',
|
881 |
+
'number' => 10,
|
882 |
+
);
|
883 |
+
add_filter( 'terms_clauses', array( $this, 'filter_terms_clauses' ) );
|
884 |
+
$found_terms = get_terms( $this->coauthor_taxonomy, $args );
|
885 |
+
remove_filter( 'terms_clauses', array( $this, 'filter_terms_clauses' ) );
|
886 |
+
if ( empty( $found_terms ) )
|
887 |
+
return array();
|
888 |
+
|
889 |
+
// Get the co-author objects
|
890 |
+
$found_users = array();
|
891 |
+
foreach( $found_terms as $found_term ) {
|
892 |
+
$found_user = $this->get_coauthor_by( 'user_nicename', $found_term->slug );
|
893 |
+
if ( !empty( $found_user ) )
|
894 |
+
$found_users[$found_user->user_login] = $found_user;
|
895 |
+
}
|
896 |
+
|
897 |
// Allow users to always filter out certain users if needed (e.g. administrators)
|
898 |
$ignored_authors = apply_filters( 'coauthors_edit_ignored_authors', $ignored_authors );
|
899 |
foreach( $found_users as $key => $found_user ) {
|
900 |
// Make sure the user is contributor and above (or a custom cap)
|
901 |
if ( in_array( $found_user->user_login, $ignored_authors ) )
|
902 |
unset( $found_users[$key] );
|
903 |
+
else if ( $found_user->type == 'wpuser' && false === $found_user->has_cap( apply_filters( 'coauthors_edit_author_cap', 'edit_posts' ) ) )
|
904 |
unset( $found_users[$key] );
|
905 |
}
|
906 |
return (array) $found_users;
|
916 |
return $user_query;
|
917 |
}
|
918 |
|
919 |
+
/**
|
920 |
+
* Modify get_terms() to LIKE against the term description instead of the term name
|
921 |
+
*
|
922 |
+
* @since 3.0
|
923 |
+
*/
|
924 |
+
function filter_terms_clauses( $pieces ) {
|
925 |
+
|
926 |
+
$pieces['where'] = str_replace( 't.name LIKE', 'tt.description LIKE', $pieces['where'] );
|
927 |
+
return $pieces;
|
928 |
+
}
|
929 |
+
|
930 |
/**
|
931 |
* Functions to add scripts and css
|
932 |
*/
|
933 |
function enqueue_scripts($hook_suffix) {
|
934 |
global $pagenow, $post;
|
935 |
+
|
936 |
+
if ( !$this->is_valid_page() || ! $this->is_post_type_enabled() || !$this->current_user_can_set_authors() )
|
|
|
|
|
937 |
return;
|
938 |
+
|
939 |
wp_enqueue_script( 'jquery' );
|
940 |
wp_enqueue_script( 'jquery-ui-sortable' );
|
941 |
wp_enqueue_style( 'co-authors-plus-css', COAUTHORS_PLUS_URL . 'css/co-authors-plus.css', false, COAUTHORS_PLUS_VERSION, 'all' );
|
942 |
wp_enqueue_script( 'co-authors-plus-js', COAUTHORS_PLUS_URL . 'js/co-authors-plus.js', array('jquery', 'suggest'), COAUTHORS_PLUS_VERSION, true);
|
943 |
+
|
944 |
$js_strings = array(
|
945 |
'edit_label' => __( 'Edit', 'co-authors-plus' ),
|
946 |
+
'delete_label' => __( 'Remove', 'co-authors-plus' ),
|
947 |
'confirm_delete' => __( 'Are you sure you want to remove this author?', 'co-authors-plus' ),
|
948 |
'input_box_title' => __( 'Click to change this author, or drag to change their position', 'co-authors-plus' ),
|
949 |
'search_box_text' => __( 'Search for an author', 'co-authors-plus' ),
|
951 |
);
|
952 |
wp_localize_script( 'co-authors-plus-js', 'coAuthorsPlusStrings', $js_strings );
|
953 |
|
954 |
+
}
|
955 |
+
|
956 |
+
/**
|
957 |
+
* load-edit.php is when the screen has been set up
|
958 |
+
*/
|
959 |
+
function load_edit() {
|
960 |
+
|
961 |
+
$screen = get_current_screen();
|
962 |
+
if ( in_array( $screen->post_type, $this->supported_post_types ) )
|
963 |
+
add_filter( 'views_' . $screen->id, array( $this, 'filter_views' ) );
|
964 |
+
}
|
965 |
+
|
966 |
+
/**
|
967 |
+
* Filter the view links that appear at the top of the Manage Posts view
|
968 |
+
*
|
969 |
+
* @since 3.0
|
970 |
+
*/
|
971 |
+
function filter_views( $views ) {
|
972 |
+
|
973 |
+
if ( array_key_exists( 'mine', $views ) )
|
974 |
+
return $views;
|
975 |
+
|
976 |
+
$views = array_reverse( $views );
|
977 |
+
$all_view = array_pop( $views );
|
978 |
+
$mine_args = array(
|
979 |
+
'author_name' => wp_get_current_user()->user_nicename,
|
980 |
+
);
|
981 |
+
if ( 'post' != get_post_type() )
|
982 |
+
$mine_args['post_type'] = get_post_type();
|
983 |
+
if ( ! empty( $_REQUEST['author_name'] ) && wp_get_current_user()->user_nicename == $_REQUEST['author_name'] )
|
984 |
+
$class = ' class="current"';
|
985 |
+
else
|
986 |
+
$class = '';
|
987 |
+
$views['mine'] = $view_mine = '<a' . $class . ' href="' . add_query_arg( $mine_args, admin_url( 'edit.php' ) ) . '">' . __( 'Mine', 'co-authors-plus' ) . '</a>';
|
988 |
+
|
989 |
+
$views['all'] = str_replace( $class, '', $all_view );
|
990 |
+
$views = array_reverse( $views );
|
991 |
+
|
992 |
+
return $views;
|
993 |
+
}
|
994 |
+
|
995 |
/**
|
996 |
* Adds necessary javascript variables to admin pages
|
997 |
*/
|
998 |
function js_vars() {
|
999 |
+
|
1000 |
+
if ( ! $this->is_valid_page() || ! $this->is_post_type_enabled() || ! $this-> current_user_can_set_authors() )
|
1001 |
+
return;
|
1002 |
+
?>
|
|
|
1003 |
<script type="text/javascript">
|
1004 |
// AJAX link used for the autosuggest
|
1005 |
var coAuthorsPlus_ajax_suggest_link = '<?php echo add_query_arg(
|
1006 |
array(
|
1007 |
'action' => 'coauthors_ajax_suggest',
|
1008 |
+
'post_type' => get_post_type(),
|
1009 |
),
|
1010 |
wp_nonce_url( 'admin-ajax.php', 'coauthors-search' )
|
1011 |
); ?>';
|
1012 |
</script>
|
1013 |
+
<?php
|
1014 |
+
}
|
1015 |
+
|
|
|
1016 |
/**
|
1017 |
* Helper to only add javascript to necessary pages. Avoids bloat in admin.
|
1018 |
*/
|
1019 |
function is_valid_page() {
|
1020 |
global $pagenow;
|
1021 |
+
|
1022 |
+
return (bool)in_array( $pagenow, $this->_pages_whitelist );
|
1023 |
+
}
|
1024 |
+
|
1025 |
function get_post_id() {
|
1026 |
global $post;
|
1027 |
$post_id = 0;
|
1028 |
+
|
1029 |
if ( is_object( $post ) ) {
|
1030 |
$post_id = $post->ID;
|
1031 |
}
|
1032 |
+
|
1033 |
if( ! $post_id ) {
|
1034 |
if ( isset( $_GET['post'] ) )
|
1035 |
$post_id = (int) $_GET['post'];
|
1036 |
elseif ( isset( $_POST['post_ID'] ) )
|
1037 |
$post_id = (int) $_POST['post_ID'];
|
1038 |
}
|
1039 |
+
|
1040 |
return $post_id;
|
1041 |
}
|
1042 |
+
|
1043 |
/**
|
1044 |
* Allows coauthors to edit the post they're coauthors of
|
|
|
|
|
1045 |
*/
|
1046 |
+
function filter_user_has_cap( $allcaps, $caps, $args ) {
|
1047 |
+
|
1048 |
+
$cap = $args[0];
|
1049 |
$user_id = isset( $args[1] ) ? $args[1] : 0;
|
1050 |
$post_id = isset( $args[2] ) ? $args[2] : 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1051 |
|
1052 |
+
$obj = get_post_type_object( get_post_type( $post_id ) );
|
1053 |
+
if ( ! $obj )
|
1054 |
return $allcaps;
|
1055 |
|
1056 |
+
$caps_to_modify = array(
|
1057 |
+
$obj->cap->edit_post,
|
1058 |
+
'edit_post', // Need to filter this too, unfortunately: http://core.trac.wordpress.org/ticket/22415
|
1059 |
+
$obj->cap->edit_others_posts, // This as well: http://core.trac.wordpress.org/ticket/22417
|
1060 |
+
);
|
1061 |
+
if ( ! in_array( $cap, $caps_to_modify ) )
|
1062 |
return $allcaps;
|
1063 |
+
|
1064 |
+
// We won't be doing any modification if they aren't already a co-author on the post
|
1065 |
+
if( ! is_coauthor_for_post( $user_id, $post_id ) )
|
1066 |
return $allcaps;
|
1067 |
+
|
1068 |
+
$current_user = wp_get_current_user();
|
1069 |
+
if ( 'publish' == get_post_status( $post_id ) && ! empty( $current_user->allcaps[$obj->cap->edit_published_posts] ) )
|
1070 |
+
$allcaps[$obj->cap->edit_published_posts] = true;
|
1071 |
+
elseif ( 'private' == get_post_status( $post_id ) && ! empty( $current_user->allcaps[$obj->cap->edit_private_posts] ) )
|
1072 |
+
$allcaps[$obj->cap->edit_private_posts] = true;
|
1073 |
+
|
1074 |
+
$allcaps[$obj->cap->edit_others_posts] = true;
|
1075 |
+
|
1076 |
return $allcaps;
|
1077 |
}
|
1078 |
|
1079 |
+
/**
|
1080 |
+
* Get the author term for a given co-author
|
1081 |
+
*
|
1082 |
+
* @since 3.0
|
1083 |
+
*
|
1084 |
+
* @param object $coauthor The co-author object
|
1085 |
+
* @return object|false $author_term The author term on success
|
1086 |
+
*/
|
1087 |
+
public function get_author_term( $coauthor ) {
|
1088 |
+
|
1089 |
+
if ( ! is_object( $coauthor ) )
|
1090 |
+
return;
|
1091 |
+
|
1092 |
+
$cache_key = 'author-term-' . $coauthor->user_nicename;
|
1093 |
+
if ( false !== ( $term = wp_cache_get( $cache_key, 'co-authors-plus' ) ) )
|
1094 |
+
return $term;
|
1095 |
+
|
1096 |
+
// See if the prefixed term is available, otherwise default to just the nicename
|
1097 |
+
$term = get_term_by( 'slug', 'cap-' . $coauthor->user_nicename, $this->coauthor_taxonomy );
|
1098 |
+
if ( ! $term ) {
|
1099 |
+
$term = get_term_by( 'slug', $coauthor->user_nicename, $this->coauthor_taxonomy );
|
1100 |
+
}
|
1101 |
+
wp_cache_set( $cache_key, $term, 'co-authors-plus' );
|
1102 |
+
return $term;
|
1103 |
+
}
|
1104 |
+
|
1105 |
+
/**
|
1106 |
+
* Update the author term for a given co-author
|
1107 |
+
*
|
1108 |
+
* @since 3.0
|
1109 |
+
*
|
1110 |
+
* @param object $coauthor The co-author object (user or guest author)
|
1111 |
+
* @return object|false $success Term object if successful, false if not
|
1112 |
+
*/
|
1113 |
+
public function update_author_term( $coauthor ) {
|
1114 |
+
|
1115 |
+
if ( ! is_object( $coauthor ) )
|
1116 |
+
return false;
|
1117 |
+
|
1118 |
+
// Update the taxonomy term to include details about the user for searching
|
1119 |
+
$search_values = array();
|
1120 |
+
foreach( $this->ajax_search_fields as $search_field ) {
|
1121 |
+
$search_values[] = $coauthor->$search_field;
|
1122 |
+
}
|
1123 |
+
$term_description = implode( ' ', $search_values );
|
1124 |
+
|
1125 |
+
if ( $term = $this->get_author_term( $coauthor ) ) {
|
1126 |
+
wp_update_term( $term->term_id, $this->coauthor_taxonomy, array( 'description' => $term_description ) );
|
1127 |
+
} else {
|
1128 |
+
$coauthor_slug = 'cap-' . $coauthor->user_nicename;
|
1129 |
+
$args = array(
|
1130 |
+
'slug' => $coauthor_slug,
|
1131 |
+
'description' => $term_description,
|
1132 |
+
);
|
1133 |
+
$new_term = wp_insert_term( $coauthor->user_login, $this->coauthor_taxonomy, $args );
|
1134 |
+
}
|
1135 |
+
wp_cache_delete( 'author-term-' . $coauthor->user_nicename, 'co-authors-plus' );
|
1136 |
+
return $this->get_author_term( $coauthor );
|
1137 |
+
}
|
1138 |
+
|
1139 |
/**
|
1140 |
* Filter Edit Flow's 'ef_calendar_item_information_fields' to add co-authors
|
1141 |
*
|
1142 |
+
* @see https://github.com/Automattic/Co-Authors-Plus/issues/2
|
1143 |
*/
|
1144 |
function filter_ef_calendar_item_information_fields( $information_fields, $post_id ) {
|
1145 |
|
1161 |
/**
|
1162 |
* Filter Edit Flow's 'ef_story_budget_term_column_value' to add co-authors to the story budget
|
1163 |
*
|
1164 |
+
* @see https://github.com/Automattic/Co-Authors-Plus/issues/2
|
1165 |
*/
|
1166 |
function filter_ef_story_budget_term_column_value( $column_name, $post, $parent_term ) {
|
1167 |
|
1276 |
if ( isset($reply_to) )
|
1277 |
$message_headers .= $reply_to . "\n";
|
1278 |
|
1279 |
+
$notify_message = apply_filters( 'comment_notification_text', $notify_message, $comment_id );
|
1280 |
+
$subject = apply_filters( 'comment_notification_subject', $subject, $comment_id );
|
1281 |
+
$message_headers = apply_filters( 'comment_notification_headers', $message_headers, $comment_id );
|
1282 |
|
1283 |
@wp_mail( $author->user_email, $subject, $notify_message, $message_headers );
|
1284 |
}
|
1371 |
|
1372 |
return true;
|
1373 |
}
|
1374 |
+
endif;
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.co-authors.wp-list-table {
|
2 |
+
}
|
3 |
+
|
4 |
+
.co-authors.wp-list-table .column-display_name img.avatar {
|
5 |
+
float: left;
|
6 |
+
margin-right: 10px;
|
7 |
+
margin-top: 1px;
|
8 |
+
}
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
jQuery(document).ready(function($){
|
2 |
+
$('.reassign-option').on('click',function(){
|
3 |
+
$('#wpbody-content input#submit').addClass('button-primary').removeAttr('disabled');
|
4 |
+
});
|
5 |
+
$('#leave-assigned-to').select2({
|
6 |
+
minimumInputLength: 2,
|
7 |
+
width: 'copy',
|
8 |
+
multiple: false,
|
9 |
+
ajax: {
|
10 |
+
url: ajaxurl,
|
11 |
+
dataType: 'json',
|
12 |
+
data: function( term, page ) {
|
13 |
+
return {
|
14 |
+
q: term,
|
15 |
+
action: 'search_coauthors_to_assign'
|
16 |
+
};
|
17 |
+
},
|
18 |
+
results: function( data, page ) {
|
19 |
+
return { results: data };
|
20 |
+
}
|
21 |
+
},
|
22 |
+
formatResult: function( object, container, query ) {
|
23 |
+
return object.display_name;
|
24 |
+
},
|
25 |
+
formatSelection: function( object, container ) {
|
26 |
+
return object.display_name;
|
27 |
+
}
|
28 |
+
}).on('change', function() {
|
29 |
+
$('#reassign-another').trigger('click');
|
30 |
+
});
|
31 |
+
});
|
@@ -1,1176 +0,0 @@
|
|
1 |
-
/*
|
2 |
-
* jQuery ContentEditable AC Autocompletion Plugin
|
3 |
-
*
|
4 |
-
* A signifigant fork of the Original AutoComplete by J�rn Zaeffererrequest
|
5 |
-
*
|
6 |
-
* Copyright (c) 2009 J�rn Zaeffererrequest and Aaron Raddon
|
7 |
-
*
|
8 |
-
* Dual licensed under the MIT and GPL licenses:
|
9 |
-
* http://www.opensource.org/licenses/mit-license.php
|
10 |
-
* http://www.gnu.org/licenses/gpl.html
|
11 |
-
*
|
12 |
-
* Revision: $Id: jquery.autocomplete.js 15 2009-08-22 10:30:27Z joern.zaefferer $
|
13 |
-
*/
|
14 |
-
|
15 |
-
(function($) {
|
16 |
-
|
17 |
-
if (window['log'] == undefined){
|
18 |
-
window['log'] = {
|
19 |
-
toggle: function() {},
|
20 |
-
move: function() {},
|
21 |
-
resize: function() {},
|
22 |
-
clear: function() {},
|
23 |
-
debug: function() {},
|
24 |
-
info: function() {},
|
25 |
-
warn: function() {},
|
26 |
-
error: function() {},
|
27 |
-
profile: function() {}
|
28 |
-
};
|
29 |
-
}
|
30 |
-
|
31 |
-
$.fn.extend({
|
32 |
-
autocomplete: function(urlOrData, options) {
|
33 |
-
var isUrl = typeof urlOrData == "string";
|
34 |
-
options = $.extend({
|
35 |
-
formatEditableResult: function(row) { return '<a contenteditable="false" href="#" tabindex="-1" >@' + row[options.jsonterm] + '</a> ';},
|
36 |
-
formatResult: function(row) { return row[options.jsonterm];},
|
37 |
-
formatItem: function(row) { return row[options.jsonterm]; }
|
38 |
-
}, $.Autocompleter.defaults, {
|
39 |
-
url: isUrl ? urlOrData : null,
|
40 |
-
data: isUrl ? null : urlOrData,
|
41 |
-
delay: isUrl ? $.Autocompleter.defaults.delay : 10,
|
42 |
-
max: options && !options.scroll ? 10 : 150
|
43 |
-
}, options);
|
44 |
-
|
45 |
-
// if highlight is set to false, replace it with a do-nothing function
|
46 |
-
options.highlight = options.highlight || function(value) { return value; };
|
47 |
-
|
48 |
-
// if the formatMatch option is not specified, then use formatItem for backwards compatibility
|
49 |
-
options.formatMatch = options.formatMatch || options.formatItem;
|
50 |
-
|
51 |
-
return this.each(function() {
|
52 |
-
new $.Autocompleter(this, options);
|
53 |
-
});
|
54 |
-
},
|
55 |
-
result: function(handler) {
|
56 |
-
return this.bind("result", handler);
|
57 |
-
},
|
58 |
-
search: function(handler) {
|
59 |
-
return this.trigger("search", [handler]);
|
60 |
-
},
|
61 |
-
flushCache: function() {
|
62 |
-
return this.trigger("flushCache");
|
63 |
-
},
|
64 |
-
setOptions: function(options){
|
65 |
-
return this.trigger("setOptions", [options]);
|
66 |
-
},
|
67 |
-
unautocomplete: function() {
|
68 |
-
return this.trigger("unautocomplete");
|
69 |
-
},
|
70 |
-
notfound: function() {
|
71 |
-
return this.trigger("ace_notfound");
|
72 |
-
}
|
73 |
-
});
|
74 |
-
|
75 |
-
$.Autocompleter = function(input, options) {
|
76 |
-
|
77 |
-
var KEY = {
|
78 |
-
UP: 38,
|
79 |
-
DOWN: 40,
|
80 |
-
DEL: 46,
|
81 |
-
TAB: 9,
|
82 |
-
RETURN: 13,
|
83 |
-
SHIFT: 16,
|
84 |
-
ESC: 27,
|
85 |
-
COMMA: 188,
|
86 |
-
PAGEUP: 33,
|
87 |
-
PAGEDOWN: 34,
|
88 |
-
BACKSPACE: 8,
|
89 |
-
SPACE: 32,
|
90 |
-
LEFT: 37,
|
91 |
-
RIGHT: 39,
|
92 |
-
AT: 50,
|
93 |
-
POUND:34,
|
94 |
-
DOLLAR:52,
|
95 |
-
SEMIC:59
|
96 |
-
};
|
97 |
-
// Create $ object for input element
|
98 |
-
var $input = $(input).attr("autocomplete", "off").addClass(options.inputClass);
|
99 |
-
|
100 |
-
var timeout;
|
101 |
-
var previousValue = "";
|
102 |
-
var cache = $.Autocompleter.Cache(options);
|
103 |
-
var hasFocus = 0;
|
104 |
-
var cursorStart = 0;
|
105 |
-
var editable = null;
|
106 |
-
var editableSelection, editableRange;
|
107 |
-
var lastKeyPressCode;
|
108 |
-
var preKeyPressCode;
|
109 |
-
var prePreKeyPressCode;
|
110 |
-
var hotKey = "@";
|
111 |
-
var autocActive = options.hotkeymode ? false : true;
|
112 |
-
var config = {
|
113 |
-
mouseDownOnSelect: false
|
114 |
-
};
|
115 |
-
var select = $.Autocompleter.Select(options, input, selectCurrent, config);
|
116 |
-
|
117 |
-
var blockSubmit;
|
118 |
-
|
119 |
-
if (options.hotkeymode) {
|
120 |
-
options.multiple = false;
|
121 |
-
}
|
122 |
-
|
123 |
-
// prevent form submit in opera when selecting with return key
|
124 |
-
$.browser.opera && $(input.form).bind("submit.autocomplete", function() {
|
125 |
-
if (blockSubmit) {
|
126 |
-
blockSubmit = false;
|
127 |
-
return false;
|
128 |
-
}
|
129 |
-
});
|
130 |
-
if (input.value == undefined && options.width < 1){
|
131 |
-
editable = $(input)[0];
|
132 |
-
options.width = $(input).parent().parent().width();
|
133 |
-
options.left = $(input).parent().parent().offset().left;
|
134 |
-
}
|
135 |
-
|
136 |
-
// only opera doesn't trigger keydown multiple times while pressed, others don't work with keypress at all
|
137 |
-
$input.bind(($.browser.opera ? "keypress" : "keydown") + ".autocomplete", function(event) {
|
138 |
-
// a keypress means the input has focus
|
139 |
-
// avoids issue where input had focus before the autocomplete was applied
|
140 |
-
hasFocus = 1;
|
141 |
-
var k=event.keyCode || event.which; // keyCode == 0 in Gecko/FF on keypress
|
142 |
-
//log.debug("keypress: " + k);
|
143 |
-
if (k == KEY.RETURN){
|
144 |
-
log.debug("in k = " + k + ' options.supressReturn =' + options.supressReturn)
|
145 |
-
}
|
146 |
-
// track history, probably should push/pop an array
|
147 |
-
prePreKeyPressCode = preKeyPressCode;
|
148 |
-
preKeyPressCode = lastKeyPressCode;
|
149 |
-
lastKeyPressCode = k;
|
150 |
-
if (options.hotkeymode && autocActive === false){
|
151 |
-
if (k == KEY.AT && event.shiftKey && (KEY.SPACE == prePreKeyPressCode || prePreKeyPressCode == undefined)){
|
152 |
-
autocActive = true;
|
153 |
-
log.debug("AutoComplete now active in Hotkey mode")
|
154 |
-
clearTimeout(timeout);
|
155 |
-
timeout = setTimeout(onChange, options.delay);
|
156 |
-
}
|
157 |
-
return;
|
158 |
-
}
|
159 |
-
switch(k) {
|
160 |
-
|
161 |
-
case KEY.UP:
|
162 |
-
event.preventDefault();
|
163 |
-
if ( select.visible() ) {
|
164 |
-
select.prev();
|
165 |
-
} else {
|
166 |
-
onChange(0, true);
|
167 |
-
}
|
168 |
-
break;
|
169 |
-
|
170 |
-
case KEY.DOWN:
|
171 |
-
event.preventDefault();
|
172 |
-
if ( select.visible() ) {
|
173 |
-
select.next();
|
174 |
-
} else {
|
175 |
-
onChange(0, true);
|
176 |
-
}
|
177 |
-
break;
|
178 |
-
|
179 |
-
case KEY.PAGEUP:
|
180 |
-
event.preventDefault();
|
181 |
-
if ( select.visible() ) {
|
182 |
-
select.pageUp();
|
183 |
-
} else {
|
184 |
-
onChange(0, true);
|
185 |
-
}
|
186 |
-
break;
|
187 |
-
|
188 |
-
case KEY.PAGEDOWN:
|
189 |
-
event.preventDefault();
|
190 |
-
if ( select.visible() ) {
|
191 |
-
select.pageDown();
|
192 |
-
} else {
|
193 |
-
onChange(0, true);
|
194 |
-
}
|
195 |
-
break;
|
196 |
-
|
197 |
-
// matches also semicolon
|
198 |
-
case options.multiple && $.trim(options.multipleSeparator) == "," && KEY.COMMA:
|
199 |
-
case KEY.TAB:
|
200 |
-
case KEY.RETURN:
|
201 |
-
case KEY.RIGHT:
|
202 |
-
case KEY.SEMIC:
|
203 |
-
if (k == KEY.RETURN)
|
204 |
-
event.preventDefault();
|
205 |
-
if( selectCurrent() ) {
|
206 |
-
// stop default to prevent a form submit, Opera needs special handling
|
207 |
-
log.debug("in k = " + k + ' options.supressReturn =' + options.supressReturn)
|
208 |
-
if (k == KEY.RETURN && options.supressReturn)
|
209 |
-
event.preventDefault();
|
210 |
-
blockSubmit = true;
|
211 |
-
hideResultsNow();
|
212 |
-
if (options.hotkeymode || options.multiple === true){
|
213 |
-
log.debug("in return false")
|
214 |
-
return false;
|
215 |
-
}
|
216 |
-
} else {
|
217 |
-
log.debug("nada found? trigger notfound?");
|
218 |
-
$input.trigger("ace_notfound", {});
|
219 |
-
hideResultsNow();
|
220 |
-
if (k == KEY.RETURN && options.supressReturn)
|
221 |
-
event.preventDefault();
|
222 |
-
}
|
223 |
-
if (k == KEY.TAB || k == KEY.RETURN){
|
224 |
-
log.debug("was tab")
|
225 |
-
}
|
226 |
-
break;
|
227 |
-
|
228 |
-
case KEY.ESC:
|
229 |
-
select.hide();
|
230 |
-
break;
|
231 |
-
|
232 |
-
default:
|
233 |
-
if( autocActive === true ) {
|
234 |
-
clearTimeout(timeout);
|
235 |
-
timeout = setTimeout(onChange, options.delay);
|
236 |
-
}
|
237 |
-
break;
|
238 |
-
}
|
239 |
-
}).focus(function(){
|
240 |
-
// track whether the field has focus, we shouldn't process any
|
241 |
-
// results if the field no longer has focus
|
242 |
-
log.debug("has focus")
|
243 |
-
hasFocus++;
|
244 |
-
if( autocActive === true && options.hotkeymode) {
|
245 |
-
onChange(0, true);
|
246 |
-
} else if (!options.hotkeymode) {
|
247 |
-
if (!autocActive)
|
248 |
-
autocActive = true;
|
249 |
-
onChange(0, true);
|
250 |
-
}
|
251 |
-
}).blur(function() {
|
252 |
-
hasFocus = 0;
|
253 |
-
if (!config.mouseDownOnSelect) {
|
254 |
-
hideResults();
|
255 |
-
}
|
256 |
-
}).click(function() {
|
257 |
-
// show select when clicking in a focused field
|
258 |
-
if ( hasFocus++ > 1 && !select.visible() ) {
|
259 |
-
onChange(0, true);
|
260 |
-
} else {
|
261 |
-
log.debug('hasfocus = ' + hasFocus);
|
262 |
-
log.debug('hasfocus = ' + (hasFocus > 1));
|
263 |
-
log.debug('visible()' + (!select.visible()))
|
264 |
-
log.debug('visible()' + select.visible())
|
265 |
-
}
|
266 |
-
}).bind('result',function(){
|
267 |
-
hasFocus = 0;
|
268 |
-
hideResults();
|
269 |
-
}).bind("search", function() {
|
270 |
-
// TODO why not just specifying both arguments?
|
271 |
-
var fn = (arguments.length > 1) ? arguments[1] : null;
|
272 |
-
function findValueCallback(q, data) {
|
273 |
-
var result;
|
274 |
-
if( data && data.length ) {
|
275 |
-
for (var i=0; i < data.length; i++) {
|
276 |
-
if( data[i].result.toLowerCase() == q.toLowerCase() ) {
|
277 |
-
result = data[i];
|
278 |
-
break;
|
279 |
-
}
|
280 |
-
}
|
281 |
-
}
|
282 |
-
if( typeof fn == "function" ) {
|
283 |
-
fn(result);
|
284 |
-
} else {
|
285 |
-
$input.trigger("result", result && [result.data, result.value]);
|
286 |
-
}
|
287 |
-
}
|
288 |
-
$.each(trimWords(smartVal()), function(i, value) {
|
289 |
-
request(value, findValueCallback, findValueCallback);
|
290 |
-
});
|
291 |
-
}).bind("flushCache", function() {
|
292 |
-
cache.flush();
|
293 |
-
}).bind("setOptions", function() {
|
294 |
-
$.extend(options, arguments[1]);
|
295 |
-
// if we've updated the data, repopulate
|
296 |
-
if ( "data" in arguments[1] )
|
297 |
-
cache.populate();
|
298 |
-
}).bind("unautocomplete", function() {
|
299 |
-
select.unbind();
|
300 |
-
$input.unbind();
|
301 |
-
$(input.form).unbind(".autocomplete");
|
302 |
-
});
|
303 |
-
|
304 |
-
|
305 |
-
function selectCurrent() {
|
306 |
-
var selected = select.selected();
|
307 |
-
if( !selected )
|
308 |
-
return false;
|
309 |
-
|
310 |
-
var v = selected.result;
|
311 |
-
previousValue = v;
|
312 |
-
|
313 |
-
if ( options.multiple ) {
|
314 |
-
var words = trimWords(smartVal());
|
315 |
-
if ( words.length > 1 ) {
|
316 |
-
var seperator = options.multipleSeparator.length;
|
317 |
-
var cursorAt = $(input).selection().start;
|
318 |
-
var wordAt, progress = 0;
|
319 |
-
$.each(words, function(i, word) {
|
320 |
-
progress += word.length;
|
321 |
-
if (cursorAt <= progress) {
|
322 |
-
wordAt = i;
|
323 |
-
return false;
|
324 |
-
}
|
325 |
-
progress += seperator;
|
326 |
-
});
|
327 |
-
words[wordAt] = v;
|
328 |
-
// TODO this should set the cursor to the right position, but it gets overriden somewhere
|
329 |
-
//$.Autocompleter.Selection(input, progress + seperator, progress + seperator);
|
330 |
-
v = words.join( options.multipleSeparator );
|
331 |
-
}
|
332 |
-
v += options.multipleSeparator;
|
333 |
-
|
334 |
-
} else if ( options.hotkeymode) {
|
335 |
-
autocActive = false;
|
336 |
-
var cur = smartVal();
|
337 |
-
cur = cur.substring(0,cursorStart -1);
|
338 |
-
log.info("found Data! " + selected.data[0] + ' ' + selected.data[1])
|
339 |
-
v = cur + options.formatResult(selected.data);
|
340 |
-
if (input.value == undefined) {
|
341 |
-
v = v + '<span id="cursorStart">�</span>';
|
342 |
-
}
|
343 |
-
|
344 |
-
//storeContentEditableCursor();
|
345 |
-
}
|
346 |
-
smartVal(v);
|
347 |
-
hideResultsNow();
|
348 |
-
$input.trigger("result", [selected.data, selected.value]);
|
349 |
-
if ( options.hotkeymode && input.value == undefined)
|
350 |
-
editableReturnCursor();
|
351 |
-
return true;
|
352 |
-
}
|
353 |
-
|
354 |
-
function setupContentEditable(){
|
355 |
-
if(editableSelection.getRangeAt !== undefined) {
|
356 |
-
//ok
|
357 |
-
// Get range (Safari 2)
|
358 |
-
} else if(
|
359 |
-
document.createRange &&
|
360 |
-
editableSelection.anchorNode &&
|
361 |
-
editableSelection.anchorOffset &&
|
362 |
-
editableSelection.focusNode &&
|
363 |
-
editableSelection.focusOffset
|
364 |
-
) {
|
365 |
-
var temp = '';
|
366 |
-
}
|
367 |
-
}
|
368 |
-
|
369 |
-
function storeContentEditableCursor() {
|
370 |
-
// editable is the contentEditable div
|
371 |
-
|
372 |
-
// Don't capture selection outside editable region
|
373 |
-
var isOrContainsAnchor = false,
|
374 |
-
isOrContainsFocus = false,
|
375 |
-
sel = window.getSelection(),
|
376 |
-
parentAnchor = sel.anchorNode,
|
377 |
-
parentFocus = sel.focusNode;
|
378 |
-
|
379 |
-
while(parentAnchor && parentAnchor != document.documentElement) {
|
380 |
-
if(parentAnchor == editable) {
|
381 |
-
isOrContainsAnchor = true;
|
382 |
-
}
|
383 |
-
parentAnchor = parentAnchor.parentNode;
|
384 |
-
}
|
385 |
-
|
386 |
-
while(parentFocus && parentFocus != document.documentElement) {
|
387 |
-
if(parentFocus == editable) {
|
388 |
-
isOrContainsFocus = true;
|
389 |
-
}
|
390 |
-
parentFocus = parentFocus.parentNode;
|
391 |
-
}
|
392 |
-
|
393 |
-
if(!isOrContainsAnchor || !isOrContainsFocus) {
|
394 |
-
return;
|
395 |
-
}
|
396 |
-
|
397 |
-
editableSelection = window.getSelection();
|
398 |
-
|
399 |
-
//editableSelection, editableRange;
|
400 |
-
// Get range (standards)
|
401 |
-
if(editableSelection.getRangeAt !== undefined) {
|
402 |
-
editableRange = editableSelection.getRangeAt(0);
|
403 |
-
//log.debug("in contenteditable keyup")
|
404 |
-
// Get range (Safari 2)
|
405 |
-
} else if(
|
406 |
-
document.createRange &&
|
407 |
-
editableSelection.anchorNode &&
|
408 |
-
editableSelection.anchorOffset &&
|
409 |
-
editableSelection.focusNode &&
|
410 |
-
editableSelection.focusOffset
|
411 |
-
) {
|
412 |
-
editableRange = document.createRange();
|
413 |
-
editableRange.setStart(selection.anchorNode, editableSelection.anchorOffset);
|
414 |
-
editableRange.setEnd(selection.focusNode, editableSelection.focusOffset);
|
415 |
-
} else {
|
416 |
-
// Failure here, not handled by the rest of the script.
|
417 |
-
// Probably IE or some older browser
|
418 |
-
// TODO: gracefully degrate to textarea?
|
419 |
-
}
|
420 |
-
|
421 |
-
var cursorStartSpan = document.createElement('span'),
|
422 |
-
collapsed = !!editableRange.collapsed;
|
423 |
-
|
424 |
-
cursorStartSpan.id = 'cursorStart';
|
425 |
-
cursorStartSpan.appendChild(document.createTextNode('�'));
|
426 |
-
|
427 |
-
// Insert beginning cursor marker
|
428 |
-
editableRange.insertNode(cursorStartSpan);
|
429 |
-
|
430 |
-
// Insert end cursor marker if any text is selected
|
431 |
-
if(!collapsed) {
|
432 |
-
var cursorEnd = document.createElement('span');
|
433 |
-
cursorEnd.id = 'cursorEnd';
|
434 |
-
editableRange.collapse();
|
435 |
-
editableRange.insertNode(cursorEnd);
|
436 |
-
}
|
437 |
-
}
|
438 |
-
|
439 |
-
function editableReturnCursor(){
|
440 |
-
// Slight delay will avoid the initial selection
|
441 |
-
// (at start or of contents depending on browser) being mistaken
|
442 |
-
setTimeout(function() {
|
443 |
-
var cursorStartSpan = document.getElementById('cursorStart'),
|
444 |
-
cursorEnd = document.getElementById('cursorEnd');
|
445 |
-
|
446 |
-
if (window.getSelection) { // Firefox, Safari, Opera
|
447 |
-
editableSelection = window.getSelection();
|
448 |
-
var range = document.createRange();
|
449 |
-
range.selectNode(cursorStartSpan);
|
450 |
-
// Select range
|
451 |
-
editableSelection.removeAllRanges();
|
452 |
-
editableSelection.addRange(range);
|
453 |
-
// Delete cursor marker
|
454 |
-
document.execCommand('delete', false, null);
|
455 |
-
} else {
|
456 |
-
if (document.body.createTextRange) { // Internet Explorer
|
457 |
-
var rangeToSelect = document.body.createTextRange();
|
458 |
-
rangeToSelect.moveToElementText(cursorStartSpan);
|
459 |
-
rangeToSelect.select();
|
460 |
-
document.selection.clear();
|
461 |
-
}
|
462 |
-
}
|
463 |
-
|
464 |
-
|
465 |
-
// Register selection again
|
466 |
-
//captureSelection();
|
467 |
-
}, 10);
|
468 |
-
}
|
469 |
-
|
470 |
-
function onChange(crap, skipPrevCheck) {
|
471 |
-
if( lastKeyPressCode == KEY.DEL ) {
|
472 |
-
select.hide();
|
473 |
-
return;
|
474 |
-
}
|
475 |
-
|
476 |
-
var currentValue = smartVal();
|
477 |
-
if ( !skipPrevCheck && currentValue == previousValue ){
|
478 |
-
return;
|
479 |
-
}
|
480 |
-
|
481 |
-
currentValue = findSearchTerm(currentValue);
|
482 |
-
previousValue = currentValue;
|
483 |
-
log.debug("onChange curVal = " + currentValue)
|
484 |
-
|
485 |
-
if ( currentValue.length >= options.minChars) {
|
486 |
-
$input.addClass(options.loadingClass);
|
487 |
-
if (!options.matchCase)
|
488 |
-
currentValue = currentValue.toLowerCase();
|
489 |
-
request(currentValue, receiveData, hideResultsNow);
|
490 |
-
} else {
|
491 |
-
log.debug("in else")
|
492 |
-
stopLoading();
|
493 |
-
if (options.startmsg != null) {
|
494 |
-
select.emptyList();
|
495 |
-
select.display({}, null);
|
496 |
-
select.show();
|
497 |
-
} else {
|
498 |
-
select.hide();
|
499 |
-
}
|
500 |
-
}
|
501 |
-
};
|
502 |
-
|
503 |
-
function trimWords(value) {
|
504 |
-
if (!value)
|
505 |
-
return [""];
|
506 |
-
if (!options.multiple && !options.hotkeymode) {
|
507 |
-
return [$.trim(value)];
|
508 |
-
} else if (options.multiple) {
|
509 |
-
return $.map(value.split(options.multipleSeparator), function(word) {
|
510 |
-
return $.trim(value).length ? $.trim(word) : null;
|
511 |
-
});
|
512 |
-
} else if (options.hotkeymode) {
|
513 |
-
log.error("should not get here, remove this section, not user")
|
514 |
-
return fake.raise.error;
|
515 |
-
}
|
516 |
-
}
|
517 |
-
|
518 |
-
// find word currently being searched for, anything after
|
519 |
-
// previous results, or non query text
|
520 |
-
function findSearchTerm(value) {
|
521 |
-
if ( !options.multiple && !options.hotkeymode) {
|
522 |
-
return value;
|
523 |
-
} else if (options.multiple) {
|
524 |
-
var words = trimWords(value);
|
525 |
-
if (words.length == 1)
|
526 |
-
return words[0];
|
527 |
-
var cursorAt = $(input).selection().start;
|
528 |
-
if (cursorAt == value.length) {
|
529 |
-
words = trimWords(value)
|
530 |
-
} else {
|
531 |
-
words = trimWords(value.replace(value.substring(cursorAt), ""));
|
532 |
-
}
|
533 |
-
return words[words.length - 1];
|
534 |
-
// hotkeymode
|
535 |
-
} else {
|
536 |
-
if (value && value.lastIndexOf('@') >= 0){
|
537 |
-
cursorStart = value.lastIndexOf('@') + 1;
|
538 |
-
} else {
|
539 |
-
log.error("found no @ in hotkeymode?" + value)
|
540 |
-
cursorStart = value.length + 2;
|
541 |
-
}
|
542 |
-
value = value.substring(cursorStart);
|
543 |
-
log.info("findSearchTerm: cursorStart,val " + cursorStart + ', ' + value)
|
544 |
-
return value; //.trim();
|
545 |
-
}
|
546 |
-
}
|
547 |
-
|
548 |
-
// fills in the input box w/the first match (assumed to be the best match)
|
549 |
-
// q: the term entered
|
550 |
-
// sValue: the first matching result
|
551 |
-
function autoFill(q, sValue){
|
552 |
-
// autofill in the complete box w/the first match as long as the user hasn't entered in more data
|
553 |
-
// if the last user key pressed was backspace, don't autofill
|
554 |
-
if( options.autoFill && (findSearchTerm(smartVal()).toLowerCase() == q.toLowerCase()) && lastKeyPressCode != KEY.BACKSPACE ) {
|
555 |
-
// fill in the value (keep the case the user has typed)
|
556 |
-
smartVal(smartVal() + sValue.substring(findSearchTerm(previousValue).length));
|
557 |
-
// select the portion of the value not typed by the user (so the next character will erase)
|
558 |
-
$(input).selection(previousValue.length, previousValue.length + sValue.length);
|
559 |
-
}
|
560 |
-
};
|
561 |
-
|
562 |
-
// replace .val() with something to handle content-editable fields
|
563 |
-
function smartVal(val) {
|
564 |
-
var field = $input[0];
|
565 |
-
//if form input field, vs contenteditable div
|
566 |
-
if (field.value != undefined){
|
567 |
-
if (val != undefined) {
|
568 |
-
return $input.val(val);
|
569 |
-
} else {
|
570 |
-
return $input.val();
|
571 |
-
}
|
572 |
-
} else {
|
573 |
-
if (val != undefined) {
|
574 |
-
return $input.html(val);
|
575 |
-
} else {
|
576 |
-
val = $input.html();
|
577 |
-
if (val != undefined && val.length != undefined && val.length > 0) {
|
578 |
-
val = $.trim(val);
|
579 |
-
} else {
|
580 |
-
val = ''
|
581 |
-
}
|
582 |
-
var endval = '', li = 0;
|
583 |
-
// replace <br> or BUT, only in last bit (10) or past hotkey
|
584 |
-
// contenteditable appends <br> to end a lot, or the last space as
|
585 |
-
if (val.lastIndexOf('@') > 0 || val.length > 10){
|
586 |
-
li = val.lastIndexOf('@') > 0 ? val.lastIndexOf('@') + 1 : val.length - 10;
|
587 |
-
endval = val.substring(li);
|
588 |
-
val = val.substring(0,val.length - endval.length);
|
589 |
-
//log.debug('used substring @ ' + val)
|
590 |
-
} else {
|
591 |
-
endval = val;
|
592 |
-
val = '';
|
593 |
-
}
|
594 |
-
// only clean up end of markup, where they are doing insertion?
|
595 |
-
endval = endval.replace("<br>",'').replace("<br/>",'').replace(" ",' ');
|
596 |
-
val = val + endval;
|
597 |
-
//log.debug("smartVal endval = " + escape(endval) + ' val= ' + escape(val))
|
598 |
-
return val;
|
599 |
-
}
|
600 |
-
}
|
601 |
-
}
|
602 |
-
|
603 |
-
function hideResults() {
|
604 |
-
clearTimeout(timeout);
|
605 |
-
timeout = setTimeout(hideResultsNow, 200);
|
606 |
-
};
|
607 |
-
|
608 |
-
function hideResultsNow() {
|
609 |
-
var wasVisible = select.visible();
|
610 |
-
autocActive = false;
|
611 |
-
select.hide();
|
612 |
-
clearTimeout(timeout);
|
613 |
-
stopLoading();
|
614 |
-
if (options.mustMatch) {
|
615 |
-
// call search and run callback
|
616 |
-
$input.search(
|
617 |
-
function (result){
|
618 |
-
// if no value found, clear the input box
|
619 |
-
if( !result ) {
|
620 |
-
if (options.multiple) {
|
621 |
-
var words = trimWords(smartVal()).slice(0, -1);
|
622 |
-
smartVal( words.join(options.multipleSeparator) + (words.length ? options.multipleSeparator : "") );
|
623 |
-
} else if (options.hotkeymode) {
|
624 |
-
smartVal( "" );
|
625 |
-
$input.trigger("result", null);
|
626 |
-
} else {
|
627 |
-
smartVal( "" );
|
628 |
-
$input.trigger("result", null);
|
629 |
-
}
|
630 |
-
}
|
631 |
-
}
|
632 |
-
);
|
633 |
-
}
|
634 |
-
};
|
635 |
-
|
636 |
-
function receiveData(q, data) {
|
637 |
-
if ( data && data.length && hasFocus ) {
|
638 |
-
stopLoading();
|
639 |
-
select.display(data, q);
|
640 |
-
autoFill(q, data[0].value);
|
641 |
-
select.show();
|
642 |
-
} else {
|
643 |
-
hideResultsNow();
|
644 |
-
}
|
645 |
-
};
|
646 |
-
|
647 |
-
function request(term, success, failure) {
|
648 |
-
log.debug("in request term = " + term)
|
649 |
-
if (!options.matchCase)
|
650 |
-
term = term.toLowerCase();
|
651 |
-
|
652 |
-
log.debug("in request term2 = " + term)
|
653 |
-
var data = cache.load(term);
|
654 |
-
log.debug("in request term3 = " + data)
|
655 |
-
// recieve the cached data
|
656 |
-
if (data && data.length) {
|
657 |
-
log.debug('found cache, not loading ' + term)
|
658 |
-
success(term, data);
|
659 |
-
return;
|
660 |
-
// if an AJAX url has been supplied, try loading the data now
|
661 |
-
} else if( (typeof options.url == "string") && (options.url.length > 0) ){
|
662 |
-
|
663 |
-
var extraParams = {
|
664 |
-
timestamp: +new Date()
|
665 |
-
};
|
666 |
-
$.each(options.extraParams, function(key, param) {
|
667 |
-
extraParams[key] = typeof param == "function" ? param() : param;
|
668 |
-
});
|
669 |
-
var found = false;
|
670 |
-
log.debug("calling ajax, term = " + term, ' dataType = ' + options.dataType)
|
671 |
-
$.ajax({
|
672 |
-
// try to leverage ajaxQueue plugin to abort previous requests
|
673 |
-
mode: "abort",
|
674 |
-
// limit abortion to this input
|
675 |
-
port: "autocomplete" + input.name,
|
676 |
-
dataType: options.dataType,
|
677 |
-
url: options.url,
|
678 |
-
data: $.extend({
|
679 |
-
q: term,
|
680 |
-
limit: options.max
|
681 |
-
}, extraParams),
|
682 |
-
success: function(data) {
|
683 |
-
if (data.length > 0){
|
684 |
-
found = true;
|
685 |
-
var parsed = options.parse && options.parse(data) || parse_json(data);
|
686 |
-
cache.add(term, parsed);
|
687 |
-
success(term, parsed);
|
688 |
-
}
|
689 |
-
}
|
690 |
-
});
|
691 |
-
if (found === true){
|
692 |
-
log.debug("returning?")
|
693 |
-
return;
|
694 |
-
}
|
695 |
-
}
|
696 |
-
log.debug("after load in request?")
|
697 |
-
select.emptyList();
|
698 |
-
if (options.noresultsmsg != null) {
|
699 |
-
stopLoading();
|
700 |
-
select.display({}, term);
|
701 |
-
select.show();
|
702 |
-
} else {
|
703 |
-
// if we have a failure, we need to empty the list -- this prevents the the [TAB] key from selecting the last successful match
|
704 |
-
failure(term);
|
705 |
-
}
|
706 |
-
};
|
707 |
-
|
708 |
-
function parse_json(json){
|
709 |
-
var parsed = [];
|
710 |
-
log.debug("parsing json, len=" + json.length)
|
711 |
-
for ( var i = 0, ol = json.length; i < ol; i++ ) {
|
712 |
-
parsed[i] = {
|
713 |
-
data: json[i],
|
714 |
-
value: json[i][options.jsonterm],
|
715 |
-
result: options.formatResult && options.formatResult(json[i]) || json[i][options.jsonterm]
|
716 |
-
};
|
717 |
-
}
|
718 |
-
return parsed;
|
719 |
-
}
|
720 |
-
|
721 |
-
function stopLoading() {
|
722 |
-
$input.removeClass(options.loadingClass);
|
723 |
-
};
|
724 |
-
|
725 |
-
};
|
726 |
-
|
727 |
-
$.Autocompleter.defaults = {
|
728 |
-
inputClass: "ac_input",
|
729 |
-
resultsClass: "ac_results",
|
730 |
-
loadingClass: "ac_loading",
|
731 |
-
minChars: 1,
|
732 |
-
live:false,
|
733 |
-
startmsg: 'Start typing to get options...',
|
734 |
-
msgonenter:false,
|
735 |
-
endmsg: null,
|
736 |
-
noresultsmsg: null,
|
737 |
-
delay: 400,
|
738 |
-
matchCase: false,
|
739 |
-
matchSubset: true,
|
740 |
-
matchContains: false,
|
741 |
-
cacheLength: 10,
|
742 |
-
supressReturn: false,
|
743 |
-
max: 100,
|
744 |
-
mustMatch: false,
|
745 |
-
extraParams: {},
|
746 |
-
jsonterm: 0, // 'name' ??
|
747 |
-
formatResult: null,//placeholder, function in above options
|
748 |
-
formatItem: null, //placeholder, function in above options
|
749 |
-
dataType: 'json',
|
750 |
-
selectFirst: true,
|
751 |
-
formatMatch: null,
|
752 |
-
autoFill: false,
|
753 |
-
width: 0,
|
754 |
-
left: 0,
|
755 |
-
multiple: false,
|
756 |
-
multipleSeparator: ", ",
|
757 |
-
hotkeymode: false,
|
758 |
-
highlight: function(value, term) {
|
759 |
-
return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term.replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi, "\\$1") + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<strong>$1</strong>");
|
760 |
-
},
|
761 |
-
scroll: true,
|
762 |
-
scrollHeight: 180
|
763 |
-
};
|
764 |
-
|
765 |
-
$.Autocompleter.Cache = function(options) {
|
766 |
-
|
767 |
-
var data = {};
|
768 |
-
var length = 0;
|
769 |
-
|
770 |
-
function matchSubset(s, sub) {
|
771 |
-
if (!options.matchCase)
|
772 |
-
s = s.toLowerCase();
|
773 |
-
var i = s.indexOf(sub);
|
774 |
-
if (options.matchContains == "word"){
|
775 |
-
i = s.toLowerCase().search("\\b" + sub.toLowerCase());
|
776 |
-
}
|
777 |
-
if (i == -1) return false;
|
778 |
-
return i == 0 || options.matchContains;
|
779 |
-
};
|
780 |
-
|
781 |
-
function add(q, value) {
|
782 |
-
if (length > options.cacheLength){
|
783 |
-
flush();
|
784 |
-
}
|
785 |
-
if (!data[q]){
|
786 |
-
length++;
|
787 |
-
}
|
788 |
-
data[q] = value;
|
789 |
-
}
|
790 |
-
|
791 |
-
function populate(){
|
792 |
-
if( !options.data ) return false;
|
793 |
-
// track the matches
|
794 |
-
var stMatchSets = {},
|
795 |
-
nullData = 0;
|
796 |
-
|
797 |
-
// no url was specified, we need to adjust the cache length to make sure it fits the local data store
|
798 |
-
if( !options.url ) options.cacheLength = 1;
|
799 |
-
|
800 |
-
// track all options for minChars = 0
|
801 |
-
stMatchSets[""] = [];
|
802 |
-
|
803 |
-
// loop through the array and create a lookup structure
|
804 |
-
for ( var i = 0, ol = options.data.length; i < ol; i++ ) {
|
805 |
-
var rawValue = options.data[i];
|
806 |
-
// if rawValue is a string, make an array otherwise just reference the array
|
807 |
-
rawValue = (typeof rawValue == "string") ? [rawValue] : rawValue;
|
808 |
-
|
809 |
-
var value = options.formatMatch(rawValue, i+1, options.data.length);
|
810 |
-
if ( value === false || value === undefined)
|
811 |
-
continue;
|
812 |
-
|
813 |
-
var firstChar = value.charAt(0).toLowerCase();
|
814 |
-
// if no lookup array for this character exists, look it up now
|
815 |
-
if( !stMatchSets[firstChar] )
|
816 |
-
stMatchSets[firstChar] = [];
|
817 |
-
|
818 |
-
// if the match is a string
|
819 |
-
var row = {
|
820 |
-
value: value,
|
821 |
-
data: rawValue,
|
822 |
-
result: options.formatResult && options.formatResult(rawValue) || value
|
823 |
-
};
|
824 |
-
|
825 |
-
// push the current match into the set list
|
826 |
-
stMatchSets[firstChar].push(row);
|
827 |
-
|
828 |
-
// keep track of minChars zero items
|
829 |
-
if ( nullData++ < options.max ) {
|
830 |
-
stMatchSets[""].push(row);
|
831 |
-
}
|
832 |
-
};
|
833 |
-
|
834 |
-
// add the data items to the cache
|
835 |
-
$.each(stMatchSets, function(i, value) {
|
836 |
-
// increase the cache size
|
837 |
-
options.cacheLength++;
|
838 |
-
// add to the cache
|
839 |
-
add(i, value);
|
840 |
-
});
|
841 |
-
}
|
842 |
-
|
843 |
-
// populate any existing data
|
844 |
-
setTimeout(populate, 25);
|
845 |
-
|
846 |
-
function flush(){
|
847 |
-
data = {};
|
848 |
-
length = 0;
|
849 |
-
}
|
850 |
-
|
851 |
-
return {
|
852 |
-
flush: flush,
|
853 |
-
add: add,
|
854 |
-
populate: populate,
|
855 |
-
load: function(q) {
|
856 |
-
if (!options.cacheLength || !length)
|
857 |
-
return null;
|
858 |
-
/*
|
859 |
-
* if dealing w/local data and matchContains than we must make sure
|
860 |
-
* to loop through all the data collections looking for matches
|
861 |
-
*/
|
862 |
-
if( !options.url && options.matchContains ){
|
863 |
-
// track all matches
|
864 |
-
var csub = [];
|
865 |
-
// loop through all the data grids for matches
|
866 |
-
for( var k in data ){
|
867 |
-
// don't search through the stMatchSets[""] (minChars: 0) cache
|
868 |
-
// this prevents duplicates
|
869 |
-
if( k.length > 0 ){
|
870 |
-
var c = data[k];
|
871 |
-
//log.debug("629 " + c + ' ' + k + ' ' + q)
|
872 |
-
$.each(c, function(i, x) {
|
873 |
-
// if we've got a match, add it to the array
|
874 |
-
if (matchSubset(x.value, q)) {
|
875 |
-
csub.push(x);
|
876 |
-
}
|
877 |
-
});
|
878 |
-
}
|
879 |
-
}
|
880 |
-
return csub;
|
881 |
-
} else
|
882 |
-
// if the exact item exists, use it
|
883 |
-
if (data[q]){
|
884 |
-
return data[q];
|
885 |
-
} else
|
886 |
-
if (options.matchSubset) {
|
887 |
-
for (var i = q.length - 1; i >= options.minChars; i--) {
|
888 |
-
var c = data[q.substr(0, i)];
|
889 |
-
if (c) {
|
890 |
-
var csub = [];
|
891 |
-
$.each(c, function(i, x) {
|
892 |
-
if (matchSubset(x.value, q)) {
|
893 |
-
csub[csub.length] = x;
|
894 |
-
}
|
895 |
-
});
|
896 |
-
return csub;
|
897 |
-
}
|
898 |
-
}
|
899 |
-
}
|
900 |
-
return null;
|
901 |
-
}
|
902 |
-
};
|
903 |
-
};
|
904 |
-
|
905 |
-
$.Autocompleter.Select = function (options, input, select, config) {
|
906 |
-
var CLASSES = {
|
907 |
-
ACTIVE: "ac_over"
|
908 |
-
};
|
909 |
-
|
910 |
-
var listItems,
|
911 |
-
active = -1,
|
912 |
-
data,
|
913 |
-
term = "",
|
914 |
-
needsInit = true,
|
915 |
-
element,
|
916 |
-
list;
|
917 |
-
|
918 |
-
// Create results
|
919 |
-
function init() {
|
920 |
-
if (!needsInit)
|
921 |
-
return;
|
922 |
-
element = $("<div/>")
|
923 |
-
.hide()
|
924 |
-
.addClass(options.resultsClass)
|
925 |
-
.css("position", "absolute")
|
926 |
-
.appendTo(document.body);
|
927 |
-
list = $("<ul/>").appendTo(element).css({
|
928 |
-
width: typeof options.width == "string" || options.width > 0 ? options.width -2 : $(input).width() -2
|
929 |
-
}).mouseover( function(event) {
|
930 |
-
if(target(event).nodeName && target(event).nodeName.toUpperCase() == 'LI') {
|
931 |
-
active = $("li", list).removeClass(CLASSES.ACTIVE).index(target(event));
|
932 |
-
$(target(event)).addClass(CLASSES.ACTIVE);
|
933 |
-
}
|
934 |
-
}).click(function(event) {
|
935 |
-
$(target(event)).addClass(CLASSES.ACTIVE);
|
936 |
-
select();
|
937 |
-
// TODO provide option to avoid setting focus again after selection? useful for cleanup-on-focus
|
938 |
-
input.focus();
|
939 |
-
return false;
|
940 |
-
}).mousedown(function() {
|
941 |
-
config.mouseDownOnSelect = true;
|
942 |
-
}).mouseup(function() {
|
943 |
-
config.mouseDownOnSelect = false;
|
944 |
-
});
|
945 |
-
|
946 |
-
if( options.width > 0 )
|
947 |
-
element.css("width", options.width);
|
948 |
-
|
949 |
-
needsInit = false;
|
950 |
-
}
|
951 |
-
|
952 |
-
function target(event) {
|
953 |
-
var element = event.target;
|
954 |
-
while(element && element.tagName != "LI")
|
955 |
-
element = element.parentNode;
|
956 |
-
// more fun with IE, sometimes event.target is empty, just ignore it then
|
957 |
-
if(!element)
|
958 |
-
return [];
|
959 |
-
return element;
|
960 |
-
}
|
961 |
-
|
962 |
-
function moveSelect(step) {
|
963 |
-
listItems.slice(active, active + 1).removeClass(CLASSES.ACTIVE);
|
964 |
-
movePosition(step);
|
965 |
-
var activeItem = listItems.slice(active, active + 1).addClass(CLASSES.ACTIVE);
|
966 |
-
if(options.scroll) {
|
967 |
-
var offset = 0;
|
968 |
-
listItems.slice(0, active).each(function() {
|
969 |
-
offset += this.offsetHeight;
|
970 |
-
});
|
971 |
-
if((offset + activeItem[0].offsetHeight - list.scrollTop()) > list[0].clientHeight) {
|
972 |
-
list.scrollTop(offset + activeItem[0].offsetHeight - list.innerHeight());
|
973 |
-
} else if(offset < list.scrollTop()) {
|
974 |
-
list.scrollTop(offset);
|
975 |
-
}
|
976 |
-
}
|
977 |
-
};
|
978 |
-
|
979 |
-
function movePosition(step) {
|
980 |
-
active += step;
|
981 |
-
if (active < 0) {
|
982 |
-
active = listItems.size() - 1;
|
983 |
-
} else if (active >= listItems.size()) {
|
984 |
-
active = 0;
|
985 |
-
}
|
986 |
-
}
|
987 |
-
|
988 |
-
function limitNumberOfItems(available) {
|
989 |
-
return options.max && options.max < available
|
990 |
-
? options.max
|
991 |
-
: available;
|
992 |
-
}
|
993 |
-
|
994 |
-
function fillList(q) {
|
995 |
-
list.empty();
|
996 |
-
var max = limitNumberOfItems(data.length);
|
997 |
-
for (var i=0; i < max; i++) {
|
998 |
-
if (!data[i])
|
999 |
-
continue;
|
1000 |
-
var formatted = options.formatItem(data[i].data, i+1, max, data[i].value, term);
|
1001 |
-
if ( formatted === false )
|
1002 |
-
continue;
|
1003 |
-
var li = $("<li/>").html( options.highlight(formatted, term) ).addClass(i%2 == 0 ? "ac_even" : "ac_odd").appendTo(list)[0];
|
1004 |
-
$.data(li, "ac_data", data[i]);
|
1005 |
-
}
|
1006 |
-
listItems = list.find("li");
|
1007 |
-
if ( options.selectFirst ) {
|
1008 |
-
listItems.slice(0, 1).addClass(CLASSES.ACTIVE);
|
1009 |
-
active = 0;
|
1010 |
-
}
|
1011 |
-
if (options.startmsg && (max == 0 || max == undefined) && q == null) {
|
1012 |
-
var li = $("<li/>").html( options.startmsg )
|
1013 |
-
.click(function(){
|
1014 |
-
$(input).trigger("ac_start_msg_click");
|
1015 |
-
}).addClass("start_msg").appendTo(list)[0];
|
1016 |
-
$.data(li, "start_msg", data[max + 1]);
|
1017 |
-
}
|
1018 |
-
if (options.noresultsmsg && (max == 0 || max == undefined) && q != null) {
|
1019 |
-
var val_noresult = options.noresultsmsg;
|
1020 |
-
if( typeof options.noresultsmsg == "function" ) {
|
1021 |
-
val_noresult = fn(q);
|
1022 |
-
} else if (options.noresultsmsg.indexOf('{q}' > 0)) {
|
1023 |
-
val_noresult = val_noresult.replace('{q}',q);
|
1024 |
-
}
|
1025 |
-
var li = $("<li/>").html( val_noresult )
|
1026 |
-
.click(function(){
|
1027 |
-
$(input).trigger("ac_noresult_click",q);
|
1028 |
-
}).addClass("noresult_msg").appendTo(list)[0];
|
1029 |
-
$.data(li, "noresult_msg", data[max + 1]);
|
1030 |
-
}
|
1031 |
-
if (options.endmsg && (max != undefined && max > 0)) {
|
1032 |
-
var li = $("<li/>").html( options.endmsg )
|
1033 |
-
.click(function(){
|
1034 |
-
$(input).trigger("ac_end_message_click",q);
|
1035 |
-
}).addClass("end_msg").appendTo(list)[0];
|
1036 |
-
$.data(li, "end_msg", data[max + 1]);
|
1037 |
-
}
|
1038 |
-
// apply bgiframe if available
|
1039 |
-
if ( $.fn.bgiframe )
|
1040 |
-
list.bgiframe();
|
1041 |
-
}
|
1042 |
-
|
1043 |
-
return {
|
1044 |
-
display: function(d, q) {
|
1045 |
-
init();
|
1046 |
-
data = d;
|
1047 |
-
term = q;
|
1048 |
-
fillList(q);
|
1049 |
-
},
|
1050 |
-
next: function() {
|
1051 |
-
moveSelect(1);
|
1052 |
-
},
|
1053 |
-
prev: function() {
|
1054 |
-
moveSelect(-1);
|
1055 |
-
},
|
1056 |
-
pageUp: function() {
|
1057 |
-
if (active != 0 && active - 8 < 0) {
|
1058 |
-
moveSelect( -active );
|
1059 |
-
} else {
|
1060 |
-
moveSelect(-8);
|
1061 |
-
}
|
1062 |
-
},
|
1063 |
-
pageDown: function() {
|
1064 |
-
if (active != listItems.size() - 1 && active + 8 > listItems.size()) {
|
1065 |
-
moveSelect( listItems.size() - 1 - active );
|
1066 |
-
} else {
|
1067 |
-
moveSelect(8);
|
1068 |
-
}
|
1069 |
-
},
|
1070 |
-
hide: function() {
|
1071 |
-
log.info("hiding?")
|
1072 |
-
element && element.hide();
|
1073 |
-
listItems && listItems.removeClass(CLASSES.ACTIVE);
|
1074 |
-
active = -1;
|
1075 |
-
},
|
1076 |
-
visible : function() {
|
1077 |
-
return element && element.is(":visible");
|
1078 |
-
},
|
1079 |
-
current: function() {
|
1080 |
-
return this.visible() && (listItems.filter("." + CLASSES.ACTIVE)[0] || options.selectFirst && listItems[0]);
|
1081 |
-
},
|
1082 |
-
show: function() {
|
1083 |
-
var offset = $(input).offset();
|
1084 |
-
element.css({
|
1085 |
-
width: typeof options.width == "string" || options.width > 0 ? options.width : $(input).width(),
|
1086 |
-
top: offset.top + input.offsetHeight,
|
1087 |
-
left: options.left > 0 ? options.left : offset.left
|
1088 |
-
}).show();
|
1089 |
-
|
1090 |
-
if(options.scroll) {
|
1091 |
-
list.scrollTop(0);
|
1092 |
-
list.css({
|
1093 |
-
maxHeight: options.scrollHeight,
|
1094 |
-
overflow: 'auto'
|
1095 |
-
});
|
1096 |
-
|
1097 |
-
if($.browser.msie && typeof document.body.style.maxHeight === "undefined") {
|
1098 |
-
var listHeight = 0;
|
1099 |
-
listItems.each(function() {
|
1100 |
-
listHeight += this.offsetHeight;
|
1101 |
-
});
|
1102 |
-
var scrollbarsVisible = listHeight > options.scrollHeight;
|
1103 |
-
list.css('height', scrollbarsVisible ? options.scrollHeight : listHeight );
|
1104 |
-
if (!scrollbarsVisible) {
|
1105 |
-
// IE doesn't recalculate width when scrollbar disappears
|
1106 |
-
listItems.width( list.width() - parseInt(listItems.css("padding-left")) - parseInt(listItems.css("padding-right")) );
|
1107 |
-
}
|
1108 |
-
}
|
1109 |
-
}
|
1110 |
-
},
|
1111 |
-
selected: function() {
|
1112 |
-
var selected = listItems && listItems.filter("." + CLASSES.ACTIVE).removeClass(CLASSES.ACTIVE);
|
1113 |
-
return selected && selected.length && $.data(selected[0], "ac_data");
|
1114 |
-
},
|
1115 |
-
emptyList: function (){
|
1116 |
-
list && list.empty();
|
1117 |
-
},
|
1118 |
-
unbind: function() {
|
1119 |
-
element && element.remove();
|
1120 |
-
}
|
1121 |
-
};
|
1122 |
-
};
|
1123 |
-
$.fn.selection = function(start, end) {
|
1124 |
-
if (start !== undefined) {
|
1125 |
-
log.debug("in selection, no start/end")
|
1126 |
-
return this.each(function() {
|
1127 |
-
if( this.createTextRange ){
|
1128 |
-
var selRange = this.createTextRange();
|
1129 |
-
if (end === undefined || start == end) {
|
1130 |
-
selRange.move("character", start);
|
1131 |
-
selRange.select();
|
1132 |
-
} else {
|
1133 |
-
selRange.collapse(true);
|
1134 |
-
selRange.moveStart("character", start);
|
1135 |
-
selRange.moveEnd("character", end);
|
1136 |
-
selRange.select();
|
1137 |
-
}
|
1138 |
-
} else if( this.setSelectionRange ){
|
1139 |
-
this.setSelectionRange(start, end);
|
1140 |
-
} else if( this.selectionStart ){
|
1141 |
-
this.selectionStart = start;
|
1142 |
-
this.selectionEnd = end;
|
1143 |
-
}
|
1144 |
-
});
|
1145 |
-
}
|
1146 |
-
var field = this[0];
|
1147 |
-
if ( field.createTextRange ) {
|
1148 |
-
log.debug("in selection with createTextRange")
|
1149 |
-
var range = document.selection.createRange(),
|
1150 |
-
orig = field.value,
|
1151 |
-
teststring = "<->",
|
1152 |
-
textLength = range.text.length;
|
1153 |
-
range.text = teststring;
|
1154 |
-
var caretAt = field.value.indexOf(teststring);
|
1155 |
-
field.value = orig;
|
1156 |
-
this.selection(caretAt, caretAt + textLength);
|
1157 |
-
return {
|
1158 |
-
start: caretAt,
|
1159 |
-
end: caretAt + textLength
|
1160 |
-
}
|
1161 |
-
} else if( field.selectionStart !== undefined ){
|
1162 |
-
return {
|
1163 |
-
start: field.selectionStart,
|
1164 |
-
end: field.selectionEnd
|
1165 |
-
}
|
1166 |
-
} else {
|
1167 |
-
if(field.getRangeAt !== undefined) {
|
1168 |
-
log.error("ah, getRangeAt, ?")
|
1169 |
-
} else {
|
1170 |
-
log.error("ah, none of above, ?")
|
1171 |
-
}
|
1172 |
-
|
1173 |
-
}
|
1174 |
-
};
|
1175 |
-
|
1176 |
-
})(jQuery);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Binary file
|
@@ -8,90 +8,103 @@ msgstr ""
|
|
8 |
"MIME-Version: 1.0\n"
|
9 |
"Content-Type: text/plain; charset=UTF-8\n"
|
10 |
"Content-Transfer-Encoding: 8bit\n"
|
11 |
-
"PO-Revision-Date:
|
12 |
-
"Last-Translator:
|
13 |
-
"Language-Team:
|
|
|
|
|
14 |
|
15 |
#: co-authors-plus.php:39
|
16 |
msgid " and "
|
17 |
-
msgstr ""
|
18 |
|
19 |
#: co-authors-plus.php:216
|
20 |
msgid "Post Authors"
|
21 |
-
msgstr ""
|
22 |
|
23 |
#: co-authors-plus.php:254
|
24 |
msgid ""
|
25 |
"<strong>Note:</strong> To edit post authors, please enable javascript or use "
|
26 |
"a javascript-capable browser"
|
27 |
msgstr ""
|
|
|
|
|
28 |
|
29 |
#: co-authors-plus.php:261 co-authors-plus.php:743
|
30 |
msgid ""
|
31 |
"Click on an author to change them. Drag to change their order. Click on "
|
32 |
"<strong>Remove</strong> to remove them."
|
33 |
msgstr ""
|
|
|
|
|
34 |
|
35 |
#: co-authors-plus.php:291
|
36 |
msgid "Authors"
|
37 |
-
msgstr ""
|
38 |
|
39 |
#: co-authors-plus.php:327
|
40 |
msgid "Posts"
|
41 |
-
msgstr ""
|
42 |
|
43 |
#: co-authors-plus.php:343
|
44 |
msgid "View posts by this author"
|
45 |
-
msgstr ""
|
46 |
|
47 |
#: co-authors-plus.php:738
|
48 |
msgid "Edit"
|
49 |
-
msgstr ""
|
50 |
|
51 |
#: co-authors-plus.php:739
|
52 |
msgid "Remove"
|
53 |
-
msgstr ""
|
54 |
|
55 |
#: co-authors-plus.php:740
|
56 |
msgid "Are you sure you want to remove this author?"
|
57 |
-
msgstr ""
|
58 |
|
59 |
#: co-authors-plus.php:741
|
60 |
msgid "Click to change this author, or drag to change their position"
|
61 |
msgstr ""
|
|
|
|
|
62 |
|
63 |
#: co-authors-plus.php:742
|
64 |
msgid "Search for an author"
|
65 |
-
msgstr ""
|
66 |
|
67 |
#: template-tags.php:71
|
68 |
msgid ""
|
69 |
"No post ID provided for CoAuthorsIterator constructor. Are you not in a loop "
|
70 |
"or is $post not set?"
|
71 |
msgstr ""
|
|
|
|
|
72 |
|
73 |
#: template-tags.php:189 template-tags.php:329
|
74 |
msgid "Posts by %s"
|
75 |
-
msgstr ""
|
76 |
|
77 |
#: template-tags.php:230
|
78 |
msgid "Visit %s’s website"
|
79 |
-
msgstr ""
|
80 |
|
81 |
#. Plugin Name of the plugin/theme
|
82 |
msgid "Co-Authors Plus"
|
83 |
-
msgstr ""
|
84 |
|
85 |
#. Plugin URI of the plugin/theme
|
86 |
msgid "http://wordpress.org/extend/plugins/co-authors-plus/"
|
87 |
-
msgstr ""
|
88 |
|
89 |
#. Description of the plugin/theme
|
90 |
msgid ""
|
91 |
"Allows multiple authors to be assigned to a post. This plugin is an extended "
|
92 |
"version of the Co-Authors plugin developed by Weston Ruter."
|
93 |
msgstr ""
|
|
|
|
|
|
|
94 |
|
95 |
#. Author of the plugin/theme
|
96 |
msgid "Mohammad Jangda, Daniel Bachhuber"
|
97 |
-
msgstr ""
|
8 |
"MIME-Version: 1.0\n"
|
9 |
"Content-Type: text/plain; charset=UTF-8\n"
|
10 |
"Content-Transfer-Encoding: 8bit\n"
|
11 |
+
"PO-Revision-Date: 2012-08-20 23:57+0100\n"
|
12 |
+
"Last-Translator: \n"
|
13 |
+
"Language-Team: \n"
|
14 |
+
"Language: deutsch\n"
|
15 |
+
"X-Poedit-SourceCharset: UTF-8\n"
|
16 |
|
17 |
#: co-authors-plus.php:39
|
18 |
msgid " and "
|
19 |
+
msgstr "und"
|
20 |
|
21 |
#: co-authors-plus.php:216
|
22 |
msgid "Post Authors"
|
23 |
+
msgstr "Artikel Autoren"
|
24 |
|
25 |
#: co-authors-plus.php:254
|
26 |
msgid ""
|
27 |
"<strong>Note:</strong> To edit post authors, please enable javascript or use "
|
28 |
"a javascript-capable browser"
|
29 |
msgstr ""
|
30 |
+
"<strong>Notiz:</strong> Um Artikel Autoren zu ändern, bitte JavaScript "
|
31 |
+
"aktivieren oder einen JavaScript fähigen Browser nutzen"
|
32 |
|
33 |
#: co-authors-plus.php:261 co-authors-plus.php:743
|
34 |
msgid ""
|
35 |
"Click on an author to change them. Drag to change their order. Click on "
|
36 |
"<strong>Remove</strong> to remove them."
|
37 |
msgstr ""
|
38 |
+
"Klicke auf einen Autor um sie zu ändern. Durch verschieben änderst du ihre "
|
39 |
+
"Reihenfolge. Klicke auf <strong>Entfernen</strong> um sie zu entfernen."
|
40 |
|
41 |
#: co-authors-plus.php:291
|
42 |
msgid "Authors"
|
43 |
+
msgstr "Autoren"
|
44 |
|
45 |
#: co-authors-plus.php:327
|
46 |
msgid "Posts"
|
47 |
+
msgstr "Artikel"
|
48 |
|
49 |
#: co-authors-plus.php:343
|
50 |
msgid "View posts by this author"
|
51 |
+
msgstr "Schaue dir Artikel von diesem Autor an"
|
52 |
|
53 |
#: co-authors-plus.php:738
|
54 |
msgid "Edit"
|
55 |
+
msgstr "Bearbeiten"
|
56 |
|
57 |
#: co-authors-plus.php:739
|
58 |
msgid "Remove"
|
59 |
+
msgstr "Entfernen"
|
60 |
|
61 |
#: co-authors-plus.php:740
|
62 |
msgid "Are you sure you want to remove this author?"
|
63 |
+
msgstr "Bist du sicher, dass du diesen Autor entfernen möchtest?"
|
64 |
|
65 |
#: co-authors-plus.php:741
|
66 |
msgid "Click to change this author, or drag to change their position"
|
67 |
msgstr ""
|
68 |
+
"Klicken um diesen Autor zu ändern oder verschiebe sie um ihre Position zu "
|
69 |
+
"ändern"
|
70 |
|
71 |
#: co-authors-plus.php:742
|
72 |
msgid "Search for an author"
|
73 |
+
msgstr "Suche nach einem Autor"
|
74 |
|
75 |
#: template-tags.php:71
|
76 |
msgid ""
|
77 |
"No post ID provided for CoAuthorsIterator constructor. Are you not in a loop "
|
78 |
"or is $post not set?"
|
79 |
msgstr ""
|
80 |
+
"Keine Artikel ID für den CoAuthorsIterator Konstruktor verfügbar. Bist du "
|
81 |
+
"nicht im Loop oder wurde $post nicht gesetzt?"
|
82 |
|
83 |
#: template-tags.php:189 template-tags.php:329
|
84 |
msgid "Posts by %s"
|
85 |
+
msgstr "Artikel von %s"
|
86 |
|
87 |
#: template-tags.php:230
|
88 |
msgid "Visit %s’s website"
|
89 |
+
msgstr "Besuche %s’s Website"
|
90 |
|
91 |
#. Plugin Name of the plugin/theme
|
92 |
msgid "Co-Authors Plus"
|
93 |
+
msgstr "Co-Authors Plus"
|
94 |
|
95 |
#. Plugin URI of the plugin/theme
|
96 |
msgid "http://wordpress.org/extend/plugins/co-authors-plus/"
|
97 |
+
msgstr "http://wordpress.org/extend/plugins/co-authors-plus/"
|
98 |
|
99 |
#. Description of the plugin/theme
|
100 |
msgid ""
|
101 |
"Allows multiple authors to be assigned to a post. This plugin is an extended "
|
102 |
"version of the Co-Authors plugin developed by Weston Ruter."
|
103 |
msgstr ""
|
104 |
+
"Erlaubt es mehrere Autoren einem Artikel zuzuweisen. Dieses Plugin ist eine "
|
105 |
+
"erweiterte Version des Co-Authors Plugins, dass von Weston Ruter entwickelt "
|
106 |
+
"wurde."
|
107 |
|
108 |
#. Author of the plugin/theme
|
109 |
msgid "Mohammad Jangda, Daniel Bachhuber"
|
110 |
+
msgstr "Mohammad Jangda, Daniel Bachhuber"
|
@@ -1,80 +1,555 @@
|
|
1 |
-
# Copyright (C)
|
2 |
# This file is distributed under the same license as the Co-Authors Plus package.
|
3 |
msgid ""
|
4 |
msgstr ""
|
5 |
-
"Project-Id-Version: Co-Authors Plus
|
6 |
"Report-Msgid-Bugs-To: http://wordpress.org/tag/co-authors-plus\n"
|
7 |
-
"POT-Creation-Date:
|
8 |
"MIME-Version: 1.0\n"
|
9 |
"Content-Type: text/plain; charset=UTF-8\n"
|
10 |
"Content-Transfer-Encoding: 8bit\n"
|
11 |
-
"PO-Revision-Date:
|
12 |
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
13 |
"Language-Team: LANGUAGE <LL@li.org>\n"
|
14 |
|
15 |
-
#: co-authors-plus.php:
|
16 |
-
msgid "
|
17 |
-
msgstr ""
|
18 |
-
|
19 |
-
#: co-authors-plus.php:216
|
20 |
-
msgid "Post Authors"
|
21 |
msgstr ""
|
22 |
|
23 |
-
#: co-authors-plus.php:
|
24 |
msgid ""
|
25 |
"<strong>Note:</strong> To edit post authors, please enable javascript or use "
|
26 |
"a javascript-capable browser"
|
27 |
msgstr ""
|
28 |
|
29 |
-
#: co-authors-plus.php:
|
30 |
msgid ""
|
31 |
"Click on an author to change them. Drag to change their order. Click on "
|
32 |
"<strong>Remove</strong> to remove them."
|
33 |
msgstr ""
|
34 |
|
35 |
-
#: co-authors-plus.php:
|
36 |
-
msgid "Authors"
|
37 |
-
msgstr ""
|
38 |
-
|
39 |
-
#: co-authors-plus.php:327
|
40 |
msgid "Posts"
|
41 |
msgstr ""
|
42 |
|
43 |
-
#: co-authors-plus.php:
|
44 |
msgid "View posts by this author"
|
45 |
msgstr ""
|
46 |
|
47 |
-
#: co-authors-plus.php:
|
|
|
|
|
|
|
|
|
48 |
msgid "Edit"
|
49 |
msgstr ""
|
50 |
|
51 |
-
#: co-authors-plus.php:
|
52 |
msgid "Remove"
|
53 |
msgstr ""
|
54 |
|
55 |
-
#: co-authors-plus.php:
|
56 |
msgid "Are you sure you want to remove this author?"
|
57 |
msgstr ""
|
58 |
|
59 |
-
#: co-authors-plus.php:
|
60 |
msgid "Click to change this author, or drag to change their position"
|
61 |
msgstr ""
|
62 |
|
63 |
-
#: co-authors-plus.php:
|
64 |
msgid "Search for an author"
|
65 |
msgstr ""
|
66 |
|
67 |
-
#:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
68 |
msgid ""
|
69 |
"No post ID provided for CoAuthorsIterator constructor. Are you not in a loop "
|
70 |
"or is $post not set?"
|
71 |
msgstr ""
|
72 |
|
73 |
-
#: template-tags.php:
|
|
|
|
|
|
|
|
|
74 |
msgid "Posts by %s"
|
75 |
msgstr ""
|
76 |
|
77 |
-
#: template-tags.php:
|
78 |
msgid "Visit %s’s website"
|
79 |
msgstr ""
|
80 |
|
@@ -93,5 +568,5 @@ msgid ""
|
|
93 |
msgstr ""
|
94 |
|
95 |
#. Author of the plugin/theme
|
96 |
-
msgid "Mohammad Jangda, Daniel Bachhuber"
|
97 |
msgstr ""
|
1 |
+
# Copyright (C) 2012 Co-Authors Plus
|
2 |
# This file is distributed under the same license as the Co-Authors Plus package.
|
3 |
msgid ""
|
4 |
msgstr ""
|
5 |
+
"Project-Id-Version: Co-Authors Plus 3.0-beta1\n"
|
6 |
"Report-Msgid-Bugs-To: http://wordpress.org/tag/co-authors-plus\n"
|
7 |
+
"POT-Creation-Date: 2012-11-12 05:25:35+00:00\n"
|
8 |
"MIME-Version: 1.0\n"
|
9 |
"Content-Type: text/plain; charset=UTF-8\n"
|
10 |
"Content-Transfer-Encoding: 8bit\n"
|
11 |
+
"PO-Revision-Date: 2012-MO-DA HO:MI+ZONE\n"
|
12 |
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
13 |
"Language-Team: LANGUAGE <LL@li.org>\n"
|
14 |
|
15 |
+
#: co-authors-plus.php:314 co-authors-plus.php:408 co-authors-plus.php:1173
|
16 |
+
msgid "Authors"
|
|
|
|
|
|
|
|
|
17 |
msgstr ""
|
18 |
|
19 |
+
#: co-authors-plus.php:369
|
20 |
msgid ""
|
21 |
"<strong>Note:</strong> To edit post authors, please enable javascript or use "
|
22 |
"a javascript-capable browser"
|
23 |
msgstr ""
|
24 |
|
25 |
+
#: co-authors-plus.php:376 co-authors-plus.php:973
|
26 |
msgid ""
|
27 |
"Click on an author to change them. Drag to change their order. Click on "
|
28 |
"<strong>Remove</strong> to remove them."
|
29 |
msgstr ""
|
30 |
|
31 |
+
#: co-authors-plus.php:450 php/class-coauthors-wp-list-table.php:137
|
|
|
|
|
|
|
|
|
32 |
msgid "Posts"
|
33 |
msgstr ""
|
34 |
|
35 |
+
#: co-authors-plus.php:467
|
36 |
msgid "View posts by this author"
|
37 |
msgstr ""
|
38 |
|
39 |
+
#: co-authors-plus.php:502
|
40 |
+
msgid "No co-author exists for that term"
|
41 |
+
msgstr ""
|
42 |
+
|
43 |
+
#: co-authors-plus.php:968 php/class-coauthors-wp-list-table.php:188
|
44 |
msgid "Edit"
|
45 |
msgstr ""
|
46 |
|
47 |
+
#: co-authors-plus.php:969
|
48 |
msgid "Remove"
|
49 |
msgstr ""
|
50 |
|
51 |
+
#: co-authors-plus.php:970
|
52 |
msgid "Are you sure you want to remove this author?"
|
53 |
msgstr ""
|
54 |
|
55 |
+
#: co-authors-plus.php:971
|
56 |
msgid "Click to change this author, or drag to change their position"
|
57 |
msgstr ""
|
58 |
|
59 |
+
#: co-authors-plus.php:972
|
60 |
msgid "Search for an author"
|
61 |
msgstr ""
|
62 |
|
63 |
+
#: co-authors-plus.php:1008
|
64 |
+
msgid "Mine"
|
65 |
+
msgstr ""
|
66 |
+
|
67 |
+
#: co-authors-plus.php:1245
|
68 |
+
msgid "New comment on your post \"%s\""
|
69 |
+
msgstr ""
|
70 |
+
|
71 |
+
#. translators: 1: comment author, 2: author IP, 3: author domain
|
72 |
+
#: co-authors-plus.php:1247 co-authors-plus.php:1364
|
73 |
+
msgid "Author : %1$s (IP: %2$s , %3$s)"
|
74 |
+
msgstr ""
|
75 |
+
|
76 |
+
#: co-authors-plus.php:1248 co-authors-plus.php:1365
|
77 |
+
msgid "E-mail : %s"
|
78 |
+
msgstr ""
|
79 |
+
|
80 |
+
#: co-authors-plus.php:1249 co-authors-plus.php:1259 co-authors-plus.php:1268
|
81 |
+
#: co-authors-plus.php:1351 co-authors-plus.php:1358 co-authors-plus.php:1366
|
82 |
+
msgid "URL : %s"
|
83 |
+
msgstr ""
|
84 |
+
|
85 |
+
#: co-authors-plus.php:1250 co-authors-plus.php:1367
|
86 |
+
msgid "Whois : http://whois.arin.net/rest/ip/%s"
|
87 |
+
msgstr ""
|
88 |
+
|
89 |
+
#: co-authors-plus.php:1251 co-authors-plus.php:1368
|
90 |
+
msgid "Comment: "
|
91 |
+
msgstr ""
|
92 |
+
|
93 |
+
#: co-authors-plus.php:1252
|
94 |
+
msgid "You can see all comments on this post here: "
|
95 |
+
msgstr ""
|
96 |
+
|
97 |
+
#. translators: 1: blog name, 2: post title
|
98 |
+
#: co-authors-plus.php:1254
|
99 |
+
msgid "[%1$s] Comment: \"%2$s\""
|
100 |
+
msgstr ""
|
101 |
+
|
102 |
+
#: co-authors-plus.php:1256
|
103 |
+
msgid "New trackback on your post \"%s\""
|
104 |
+
msgstr ""
|
105 |
+
|
106 |
+
#. translators: 1: website name, 2: author IP, 3: author domain
|
107 |
+
#. translators: 1: comment author, 2: author IP, 3: author domain
|
108 |
+
#: co-authors-plus.php:1258 co-authors-plus.php:1267
|
109 |
+
msgid "Website: %1$s (IP: %2$s , %3$s)"
|
110 |
+
msgstr ""
|
111 |
+
|
112 |
+
#: co-authors-plus.php:1260 co-authors-plus.php:1269
|
113 |
+
msgid "Excerpt: "
|
114 |
+
msgstr ""
|
115 |
+
|
116 |
+
#: co-authors-plus.php:1261
|
117 |
+
msgid "You can see all trackbacks on this post here: "
|
118 |
+
msgstr ""
|
119 |
+
|
120 |
+
#. translators: 1: blog name, 2: post title
|
121 |
+
#: co-authors-plus.php:1263
|
122 |
+
msgid "[%1$s] Trackback: \"%2$s\""
|
123 |
+
msgstr ""
|
124 |
+
|
125 |
+
#: co-authors-plus.php:1265
|
126 |
+
msgid "New pingback on your post \"%s\""
|
127 |
+
msgstr ""
|
128 |
+
|
129 |
+
#: co-authors-plus.php:1270
|
130 |
+
msgid "You can see all pingbacks on this post here: "
|
131 |
+
msgstr ""
|
132 |
+
|
133 |
+
#. translators: 1: blog name, 2: post title
|
134 |
+
#: co-authors-plus.php:1272
|
135 |
+
msgid "[%1$s] Pingback: \"%2$s\""
|
136 |
+
msgstr ""
|
137 |
+
|
138 |
+
#: co-authors-plus.php:1275
|
139 |
+
msgid "Permalink: %s"
|
140 |
+
msgstr ""
|
141 |
+
|
142 |
+
#: co-authors-plus.php:1277 co-authors-plus.php:1374
|
143 |
+
msgid "Trash it: %s"
|
144 |
+
msgstr ""
|
145 |
+
|
146 |
+
#: co-authors-plus.php:1279 co-authors-plus.php:1376
|
147 |
+
msgid "Delete it: %s"
|
148 |
+
msgstr ""
|
149 |
+
|
150 |
+
#: co-authors-plus.php:1280 co-authors-plus.php:1377
|
151 |
+
msgid "Spam it: %s"
|
152 |
+
msgstr ""
|
153 |
+
|
154 |
+
#: co-authors-plus.php:1348
|
155 |
+
msgid "A new trackback on the post \"%s\" is waiting for your approval"
|
156 |
+
msgstr ""
|
157 |
+
|
158 |
+
#: co-authors-plus.php:1350 co-authors-plus.php:1357
|
159 |
+
msgid "Website : %1$s (IP: %2$s , %3$s)"
|
160 |
+
msgstr ""
|
161 |
+
|
162 |
+
#: co-authors-plus.php:1352
|
163 |
+
msgid "Trackback excerpt: "
|
164 |
+
msgstr ""
|
165 |
+
|
166 |
+
#: co-authors-plus.php:1355
|
167 |
+
msgid "A new pingback on the post \"%s\" is waiting for your approval"
|
168 |
+
msgstr ""
|
169 |
+
|
170 |
+
#: co-authors-plus.php:1359
|
171 |
+
msgid "Pingback excerpt: "
|
172 |
+
msgstr ""
|
173 |
+
|
174 |
+
#: co-authors-plus.php:1362
|
175 |
+
msgid "A new comment on the post \"%s\" is waiting for your approval"
|
176 |
+
msgstr ""
|
177 |
+
|
178 |
+
#: co-authors-plus.php:1372
|
179 |
+
msgid "Approve it: %s"
|
180 |
+
msgstr ""
|
181 |
+
|
182 |
+
#: co-authors-plus.php:1379
|
183 |
+
msgid ""
|
184 |
+
"Currently %s comment is waiting for approval. Please visit the moderation "
|
185 |
+
"panel:"
|
186 |
+
msgid_plural ""
|
187 |
+
"Currently %s comments are waiting for approval. Please visit the moderation "
|
188 |
+
"panel:"
|
189 |
+
msgstr[0] ""
|
190 |
+
msgstr[1] ""
|
191 |
+
|
192 |
+
#: co-authors-plus.php:1383
|
193 |
+
msgid "[%1$s] Please moderate: \"%2$s\""
|
194 |
+
msgstr ""
|
195 |
+
|
196 |
+
#: php/class-coauthors-guest-authors.php:78
|
197 |
+
msgid "Guest Author"
|
198 |
+
msgstr ""
|
199 |
+
|
200 |
+
#: php/class-coauthors-guest-authors.php:79
|
201 |
+
msgid "Guest Authors"
|
202 |
+
msgstr ""
|
203 |
+
|
204 |
+
#: php/class-coauthors-guest-authors.php:80
|
205 |
+
msgid "All Guest Authors"
|
206 |
+
msgstr ""
|
207 |
+
|
208 |
+
#: php/class-coauthors-guest-authors.php:81
|
209 |
+
msgid "Add New Guest Author"
|
210 |
+
msgstr ""
|
211 |
+
|
212 |
+
#: php/class-coauthors-guest-authors.php:82
|
213 |
+
msgid "Edit Guest Author"
|
214 |
+
msgstr ""
|
215 |
+
|
216 |
+
#: php/class-coauthors-guest-authors.php:83
|
217 |
+
msgid "New Guest Author"
|
218 |
+
msgstr ""
|
219 |
+
|
220 |
+
#: php/class-coauthors-guest-authors.php:84
|
221 |
+
msgid "View Guest Author"
|
222 |
+
msgstr ""
|
223 |
+
|
224 |
+
#: php/class-coauthors-guest-authors.php:85
|
225 |
+
msgid "Search Guest Authors"
|
226 |
+
msgstr ""
|
227 |
+
|
228 |
+
#: php/class-coauthors-guest-authors.php:86
|
229 |
+
msgid "No guest authors found"
|
230 |
+
msgstr ""
|
231 |
+
|
232 |
+
#: php/class-coauthors-guest-authors.php:87
|
233 |
+
msgid "No guest authors found in Trash"
|
234 |
+
msgstr ""
|
235 |
+
|
236 |
+
#: php/class-coauthors-guest-authors.php:88
|
237 |
+
msgid "Update Guest Author"
|
238 |
+
msgstr ""
|
239 |
+
|
240 |
+
#: php/class-coauthors-guest-authors.php:89
|
241 |
+
msgid "About the guest author"
|
242 |
+
msgstr ""
|
243 |
+
|
244 |
+
#: php/class-coauthors-guest-authors.php:98
|
245 |
+
msgctxt "co-authors-plus"
|
246 |
+
msgid "Add New"
|
247 |
+
msgstr ""
|
248 |
+
|
249 |
+
#: php/class-coauthors-guest-authors.php:142
|
250 |
+
#: php/class-coauthors-guest-authors.php:148
|
251 |
+
msgid "Guest author updated. <a href=\"%s\">View profile</a>"
|
252 |
+
msgstr ""
|
253 |
+
|
254 |
+
#: php/class-coauthors-guest-authors.php:143
|
255 |
+
msgid "Custom field updated."
|
256 |
+
msgstr ""
|
257 |
+
|
258 |
+
#: php/class-coauthors-guest-authors.php:144
|
259 |
+
msgid "Custom field deleted."
|
260 |
+
msgstr ""
|
261 |
+
|
262 |
+
#: php/class-coauthors-guest-authors.php:145
|
263 |
+
msgid "Guest author updated."
|
264 |
+
msgstr ""
|
265 |
+
|
266 |
+
#. translators: %s: date and time of the revision
|
267 |
+
#: php/class-coauthors-guest-authors.php:147
|
268 |
+
msgid "Guest author restored to revision from %s"
|
269 |
+
msgstr ""
|
270 |
+
|
271 |
+
#: php/class-coauthors-guest-authors.php:149
|
272 |
+
msgid "Guest author saved."
|
273 |
+
msgstr ""
|
274 |
+
|
275 |
+
#: php/class-coauthors-guest-authors.php:150
|
276 |
+
msgid ""
|
277 |
+
"Guest author submitted. <a target=\"_blank\" href=\"%s\">Preview profile</a>"
|
278 |
+
msgstr ""
|
279 |
+
|
280 |
+
#: php/class-coauthors-guest-authors.php:151
|
281 |
+
msgid ""
|
282 |
+
"Guest author scheduled for: <strong>%1$s</strong>. <a target=\"_blank\" href="
|
283 |
+
"\"%2$s\">Preview profile</a>"
|
284 |
+
msgstr ""
|
285 |
+
|
286 |
+
#. translators: Publish box date format, see http:php.net/date
|
287 |
+
#: php/class-coauthors-guest-authors.php:153
|
288 |
+
msgid "M j, Y @ G:i"
|
289 |
+
msgstr ""
|
290 |
+
|
291 |
+
#: php/class-coauthors-guest-authors.php:154
|
292 |
+
msgid ""
|
293 |
+
"Guest author updated. <a target=\"_blank\" href=\"%s\">Preview profile</a>"
|
294 |
+
msgstr ""
|
295 |
+
|
296 |
+
#: php/class-coauthors-guest-authors.php:171
|
297 |
+
#: php/class-coauthors-guest-authors.php:205
|
298 |
+
#: php/class-coauthors-guest-authors.php:388
|
299 |
+
msgid "Doin' something fishy, huh?"
|
300 |
+
msgstr ""
|
301 |
+
|
302 |
+
#: php/class-coauthors-guest-authors.php:174
|
303 |
+
#: php/class-coauthors-guest-authors.php:209
|
304 |
+
msgid "You don't have permission to perform this action."
|
305 |
+
msgstr ""
|
306 |
+
|
307 |
+
#: php/class-coauthors-guest-authors.php:214
|
308 |
+
#: php/class-coauthors-guest-authors.php:393
|
309 |
+
msgid "Guest author can't be deleted because it doesn't exist."
|
310 |
+
msgstr ""
|
311 |
+
|
312 |
+
#: php/class-coauthors-guest-authors.php:228
|
313 |
+
msgid "Co-author does not exists. Try again?"
|
314 |
+
msgstr ""
|
315 |
+
|
316 |
+
#: php/class-coauthors-guest-authors.php:236
|
317 |
+
msgid "Please make sure to pick an option."
|
318 |
+
msgstr ""
|
319 |
+
|
320 |
+
#: php/class-coauthors-guest-authors.php:348
|
321 |
+
msgid "Guest author deleted."
|
322 |
+
msgstr ""
|
323 |
+
|
324 |
+
#: php/class-coauthors-guest-authors.php:370
|
325 |
+
msgid "Save"
|
326 |
+
msgstr ""
|
327 |
+
|
328 |
+
#: php/class-coauthors-guest-authors.php:371
|
329 |
+
msgid "Unique Slug"
|
330 |
+
msgstr ""
|
331 |
+
|
332 |
+
#: php/class-coauthors-guest-authors.php:373
|
333 |
+
msgid "Name"
|
334 |
+
msgstr ""
|
335 |
+
|
336 |
+
#: php/class-coauthors-guest-authors.php:374
|
337 |
+
msgid "Contact Info"
|
338 |
+
msgstr ""
|
339 |
+
|
340 |
+
#: php/class-coauthors-guest-authors.php:397
|
341 |
+
msgid "Delete %s"
|
342 |
+
msgstr ""
|
343 |
+
|
344 |
+
#: php/class-coauthors-guest-authors.php:398
|
345 |
+
msgid "You have specified this guest author for deletion:"
|
346 |
+
msgstr ""
|
347 |
+
|
348 |
+
#: php/class-coauthors-guest-authors.php:400
|
349 |
+
msgid "What should be done with posts assigned to this guest author?"
|
350 |
+
msgstr ""
|
351 |
+
|
352 |
+
#: php/class-coauthors-guest-authors.php:401
|
353 |
+
msgid ""
|
354 |
+
"Note: If you'd like to delete the guest author and all of their posts, you "
|
355 |
+
"should delete their posts first and then come back to delete the guest "
|
356 |
+
"author."
|
357 |
+
msgstr ""
|
358 |
+
|
359 |
+
#: php/class-coauthors-guest-authors.php:410
|
360 |
+
msgid "Reassign to another co-author:"
|
361 |
+
msgstr ""
|
362 |
+
|
363 |
+
#: php/class-coauthors-guest-authors.php:416
|
364 |
+
msgid "Leave posts assigned to the mapped user, %s."
|
365 |
+
msgstr ""
|
366 |
+
|
367 |
+
#: php/class-coauthors-guest-authors.php:421
|
368 |
+
msgid "Remove byline from posts (but leave each post in its current status)."
|
369 |
+
msgstr ""
|
370 |
+
|
371 |
+
#: php/class-coauthors-guest-authors.php:424
|
372 |
+
msgid "Confirm Deletion"
|
373 |
+
msgstr ""
|
374 |
+
|
375 |
+
#: php/class-coauthors-guest-authors.php:433
|
376 |
+
msgid "Add New"
|
377 |
+
msgstr ""
|
378 |
+
|
379 |
+
#: php/class-coauthors-guest-authors.php:490
|
380 |
+
msgid "WordPress User Mapping"
|
381 |
+
msgstr ""
|
382 |
+
|
383 |
+
#: php/class-coauthors-guest-authors.php:492
|
384 |
+
msgid "-- Not mapped --"
|
385 |
+
msgstr ""
|
386 |
+
|
387 |
+
#: php/class-coauthors-guest-authors.php:594
|
388 |
+
msgid "Guest authors cannot be created without display names."
|
389 |
+
msgstr ""
|
390 |
+
|
391 |
+
#: php/class-coauthors-guest-authors.php:601
|
392 |
+
msgid ""
|
393 |
+
"Guest authors cannot be created with the same user_login value as a user. "
|
394 |
+
"Try creating a profile from the user instead"
|
395 |
+
msgstr ""
|
396 |
+
|
397 |
+
#: php/class-coauthors-guest-authors.php:606
|
398 |
+
msgid "Display name conflicts with another guest author display name."
|
399 |
+
msgstr ""
|
400 |
+
|
401 |
+
#: php/class-coauthors-guest-authors.php:750
|
402 |
+
msgid "ID"
|
403 |
+
msgstr ""
|
404 |
+
|
405 |
+
#: php/class-coauthors-guest-authors.php:756
|
406 |
+
#: php/class-coauthors-wp-list-table.php:132
|
407 |
+
msgid "Display Name"
|
408 |
+
msgstr ""
|
409 |
+
|
410 |
+
#: php/class-coauthors-guest-authors.php:762
|
411 |
+
#: php/class-coauthors-wp-list-table.php:133
|
412 |
+
msgid "First Name"
|
413 |
+
msgstr ""
|
414 |
+
|
415 |
+
#: php/class-coauthors-guest-authors.php:767
|
416 |
+
#: php/class-coauthors-wp-list-table.php:134
|
417 |
+
msgid "Last Name"
|
418 |
+
msgstr ""
|
419 |
+
|
420 |
+
#: php/class-coauthors-guest-authors.php:772
|
421 |
+
msgid "Slug"
|
422 |
+
msgstr ""
|
423 |
+
|
424 |
+
#: php/class-coauthors-guest-authors.php:779
|
425 |
+
#: php/class-coauthors-wp-list-table.php:135
|
426 |
+
msgid "E-mail"
|
427 |
+
msgstr ""
|
428 |
+
|
429 |
+
#: php/class-coauthors-guest-authors.php:784
|
430 |
+
#: php/class-coauthors-wp-list-table.php:136
|
431 |
+
msgid "Linked Account"
|
432 |
+
msgstr ""
|
433 |
+
|
434 |
+
#: php/class-coauthors-guest-authors.php:789
|
435 |
+
msgid "Website"
|
436 |
+
msgstr ""
|
437 |
+
|
438 |
+
#: php/class-coauthors-guest-authors.php:794
|
439 |
+
msgid "AIM"
|
440 |
+
msgstr ""
|
441 |
+
|
442 |
+
#: php/class-coauthors-guest-authors.php:799
|
443 |
+
msgid "Yahoo IM"
|
444 |
+
msgstr ""
|
445 |
+
|
446 |
+
#: php/class-coauthors-guest-authors.php:804
|
447 |
+
msgid "Jabber / Google Talk"
|
448 |
+
msgstr ""
|
449 |
+
|
450 |
+
#: php/class-coauthors-guest-authors.php:809
|
451 |
+
msgid "Biographical Info"
|
452 |
+
msgstr ""
|
453 |
+
|
454 |
+
#: php/class-coauthors-guest-authors.php:938
|
455 |
+
msgid "%s is a required field"
|
456 |
+
msgstr ""
|
457 |
+
|
458 |
+
#: php/class-coauthors-guest-authors.php:944
|
459 |
+
msgid "user_login cannot duplicate existing guest author or mapped user"
|
460 |
+
msgstr ""
|
461 |
+
|
462 |
+
#: php/class-coauthors-guest-authors.php:989
|
463 |
+
msgid "Guest author does not exist"
|
464 |
+
msgstr ""
|
465 |
+
|
466 |
+
#: php/class-coauthors-guest-authors.php:1001
|
467 |
+
msgid "Reassignment co-author does not exist"
|
468 |
+
msgstr ""
|
469 |
+
|
470 |
+
#: php/class-coauthors-guest-authors.php:1033
|
471 |
+
msgid "No user exists with that ID"
|
472 |
+
msgstr ""
|
473 |
+
|
474 |
+
#: php/class-coauthors-guest-authors.php:1088
|
475 |
+
msgid "Edit Profile"
|
476 |
+
msgstr ""
|
477 |
+
|
478 |
+
#: php/class-coauthors-guest-authors.php:1096
|
479 |
+
msgid "Create Profile"
|
480 |
+
msgstr ""
|
481 |
+
|
482 |
+
#: php/class-coauthors-wp-list-table.php:19
|
483 |
+
msgid "Co-Authors"
|
484 |
+
msgstr ""
|
485 |
+
|
486 |
+
#: php/class-coauthors-wp-list-table.php:20
|
487 |
+
msgid "Co-Author"
|
488 |
+
msgstr ""
|
489 |
+
|
490 |
+
#: php/class-coauthors-wp-list-table.php:68
|
491 |
+
msgid "Show all"
|
492 |
+
msgstr ""
|
493 |
+
|
494 |
+
#: php/class-coauthors-wp-list-table.php:69
|
495 |
+
msgid "With linked account"
|
496 |
+
msgstr ""
|
497 |
+
|
498 |
+
#: php/class-coauthors-wp-list-table.php:70
|
499 |
+
msgid "Without linked account"
|
500 |
+
msgstr ""
|
501 |
+
|
502 |
+
#: php/class-coauthors-wp-list-table.php:122
|
503 |
+
msgid "No matching guest authors were found."
|
504 |
+
msgstr ""
|
505 |
+
|
506 |
+
#: php/class-coauthors-wp-list-table.php:189
|
507 |
+
msgid "Delete"
|
508 |
+
msgstr ""
|
509 |
+
|
510 |
+
#: php/class-coauthors-wp-list-table.php:190
|
511 |
+
msgid "View Posts"
|
512 |
+
msgstr ""
|
513 |
+
|
514 |
+
#: php/class-coauthors-wp-list-table.php:238
|
515 |
+
msgid "Filter"
|
516 |
+
msgstr ""
|
517 |
+
|
518 |
+
#: php/class-wp-cli.php:167
|
519 |
+
msgid "Please specify a valid user_login"
|
520 |
+
msgstr ""
|
521 |
+
|
522 |
+
#: php/class-wp-cli.php:170
|
523 |
+
msgid "Please specify a valid co-author login"
|
524 |
+
msgstr ""
|
525 |
+
|
526 |
+
#: php/class-wp-cli.php:177
|
527 |
+
msgid "Skipping - Post #%d already has co-authors assigned: %s"
|
528 |
+
msgstr ""
|
529 |
+
|
530 |
+
#: php/class-wp-cli.php:182
|
531 |
+
msgid "Updating - Adding %s's byline to post #%d"
|
532 |
+
msgstr ""
|
533 |
+
|
534 |
+
#: php/class-wp-cli.php:187
|
535 |
+
msgid "All done! %d posts were affected."
|
536 |
+
msgstr ""
|
537 |
+
|
538 |
+
#: template-tags.php:79
|
539 |
msgid ""
|
540 |
"No post ID provided for CoAuthorsIterator constructor. Are you not in a loop "
|
541 |
"or is $post not set?"
|
542 |
msgstr ""
|
543 |
|
544 |
+
#: template-tags.php:133
|
545 |
+
msgid " and "
|
546 |
+
msgstr ""
|
547 |
+
|
548 |
+
#: template-tags.php:205 template-tags.php:353
|
549 |
msgid "Posts by %s"
|
550 |
msgstr ""
|
551 |
|
552 |
+
#: template-tags.php:254
|
553 |
msgid "Visit %s’s website"
|
554 |
msgstr ""
|
555 |
|
568 |
msgstr ""
|
569 |
|
570 |
#. Author of the plugin/theme
|
571 |
+
msgid "Mohammad Jangda, Daniel Bachhuber, Automattic"
|
572 |
msgstr ""
|
@@ -0,0 +1,524 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*
|
2 |
+
Version: 3.2 Timestamp: Mon Sep 10 10:38:04 PDT 2012
|
3 |
+
*/
|
4 |
+
.select2-container {
|
5 |
+
position: relative;
|
6 |
+
display: inline-block;
|
7 |
+
/* inline-block for ie7 */
|
8 |
+
zoom: 1;
|
9 |
+
*display: inline;
|
10 |
+
vertical-align: top;
|
11 |
+
}
|
12 |
+
|
13 |
+
.select2-container,
|
14 |
+
.select2-drop,
|
15 |
+
.select2-search,
|
16 |
+
.select2-search input{
|
17 |
+
/*
|
18 |
+
Force border-box so that % widths fit the parent
|
19 |
+
container without overlap because of margin/padding.
|
20 |
+
|
21 |
+
More Info : http://www.quirksmode.org/css/box.html
|
22 |
+
*/
|
23 |
+
-moz-box-sizing: border-box; /* firefox */
|
24 |
+
-ms-box-sizing: border-box; /* ie */
|
25 |
+
-webkit-box-sizing: border-box; /* webkit */
|
26 |
+
-khtml-box-sizing: border-box; /* konqueror */
|
27 |
+
box-sizing: border-box; /* css3 */
|
28 |
+
}
|
29 |
+
|
30 |
+
.select2-container .select2-choice {
|
31 |
+
background-color: #fff;
|
32 |
+
background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #eeeeee), color-stop(0.5, white));
|
33 |
+
background-image: -webkit-linear-gradient(center bottom, #eeeeee 0%, white 50%);
|
34 |
+
background-image: -moz-linear-gradient(center bottom, #eeeeee 0%, white 50%);
|
35 |
+
background-image: -o-linear-gradient(bottom, #eeeeee 0%, #ffffff 50%);
|
36 |
+
background-image: -ms-linear-gradient(top, #eeeeee 0%, #ffffff 50%);
|
37 |
+
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr = '#eeeeee', endColorstr = '#ffffff', GradientType = 0);
|
38 |
+
background-image: linear-gradient(top, #eeeeee 0%, #ffffff 50%);
|
39 |
+
-webkit-border-radius: 4px;
|
40 |
+
-moz-border-radius: 4px;
|
41 |
+
border-radius: 4px;
|
42 |
+
-moz-background-clip: padding;
|
43 |
+
-webkit-background-clip: padding-box;
|
44 |
+
background-clip: padding-box;
|
45 |
+
border: 1px solid #aaa;
|
46 |
+
display: block;
|
47 |
+
overflow: hidden;
|
48 |
+
white-space: nowrap;
|
49 |
+
position: relative;
|
50 |
+
height: 26px;
|
51 |
+
line-height: 26px;
|
52 |
+
padding: 0 0 0 8px;
|
53 |
+
color: #444;
|
54 |
+
text-decoration: none;
|
55 |
+
}
|
56 |
+
|
57 |
+
.select2-container.select2-drop-above .select2-choice
|
58 |
+
{
|
59 |
+
border-bottom-color: #aaa;
|
60 |
+
-webkit-border-radius:0px 0px 4px 4px;
|
61 |
+
-moz-border-radius:0px 0px 4px 4px;
|
62 |
+
border-radius:0px 0px 4px 4px;
|
63 |
+
background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #eeeeee), color-stop(0.9, white));
|
64 |
+
background-image: -webkit-linear-gradient(center bottom, #eeeeee 0%, white 90%);
|
65 |
+
background-image: -moz-linear-gradient(center bottom, #eeeeee 0%, white 90%);
|
66 |
+
background-image: -o-linear-gradient(bottom, #eeeeee 0%, white 90%);
|
67 |
+
background-image: -ms-linear-gradient(top, #eeeeee 0%,#ffffff 90%);
|
68 |
+
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#eeeeee', endColorstr='#ffffff',GradientType=0 );
|
69 |
+
background-image: linear-gradient(top, #eeeeee 0%,#ffffff 90%);
|
70 |
+
}
|
71 |
+
|
72 |
+
.select2-container .select2-choice span {
|
73 |
+
margin-right: 26px;
|
74 |
+
display: block;
|
75 |
+
overflow: hidden;
|
76 |
+
white-space: nowrap;
|
77 |
+
-o-text-overflow: ellipsis;
|
78 |
+
-ms-text-overflow: ellipsis;
|
79 |
+
text-overflow: ellipsis;
|
80 |
+
}
|
81 |
+
|
82 |
+
.select2-container .select2-choice abbr {
|
83 |
+
display: block;
|
84 |
+
position: absolute;
|
85 |
+
right: 26px;
|
86 |
+
top: 8px;
|
87 |
+
width: 12px;
|
88 |
+
height: 12px;
|
89 |
+
font-size: 1px;
|
90 |
+
background: url('select2.png') right top no-repeat;
|
91 |
+
cursor: pointer;
|
92 |
+
text-decoration: none;
|
93 |
+
border:0;
|
94 |
+
outline: 0;
|
95 |
+
}
|
96 |
+
.select2-container .select2-choice abbr:hover {
|
97 |
+
background-position: right -11px;
|
98 |
+
cursor: pointer;
|
99 |
+
}
|
100 |
+
|
101 |
+
.select2-drop {
|
102 |
+
background: #fff;
|
103 |
+
color: #000;
|
104 |
+
border: 1px solid #aaa;
|
105 |
+
border-top: 0;
|
106 |
+
position: absolute;
|
107 |
+
top: 100%;
|
108 |
+
-webkit-box-shadow: 0 4px 5px rgba(0, 0, 0, .15);
|
109 |
+
-moz-box-shadow: 0 4px 5px rgba(0, 0, 0, .15);
|
110 |
+
-o-box-shadow: 0 4px 5px rgba(0, 0, 0, .15);
|
111 |
+
box-shadow: 0 4px 5px rgba(0, 0, 0, .15);
|
112 |
+
z-index: 9999;
|
113 |
+
width:100%;
|
114 |
+
margin-top:-1px;
|
115 |
+
|
116 |
+
-webkit-border-radius: 0 0 4px 4px;
|
117 |
+
-moz-border-radius: 0 0 4px 4px;
|
118 |
+
border-radius: 0 0 4px 4px;
|
119 |
+
}
|
120 |
+
|
121 |
+
.select2-drop.select2-drop-above {
|
122 |
+
-webkit-border-radius: 4px 4px 0px 0px;
|
123 |
+
-moz-border-radius: 4px 4px 0px 0px;
|
124 |
+
border-radius: 4px 4px 0px 0px;
|
125 |
+
margin-top:1px;
|
126 |
+
border-top: 1px solid #aaa;
|
127 |
+
border-bottom: 0;
|
128 |
+
|
129 |
+
-webkit-box-shadow: 0 -4px 5px rgba(0, 0, 0, .15);
|
130 |
+
-moz-box-shadow: 0 -4px 5px rgba(0, 0, 0, .15);
|
131 |
+
-o-box-shadow: 0 -4px 5px rgba(0, 0, 0, .15);
|
132 |
+
box-shadow: 0 -4px 5px rgba(0, 0, 0, .15);
|
133 |
+
}
|
134 |
+
|
135 |
+
.select2-container .select2-choice div {
|
136 |
+
-webkit-border-radius: 0 4px 4px 0;
|
137 |
+
-moz-border-radius: 0 4px 4px 0;
|
138 |
+
border-radius: 0 4px 4px 0;
|
139 |
+
-moz-background-clip: padding;
|
140 |
+
-webkit-background-clip: padding-box;
|
141 |
+
background-clip: padding-box;
|
142 |
+
background: #ccc;
|
143 |
+
background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #ccc), color-stop(0.6, #eee));
|
144 |
+
background-image: -webkit-linear-gradient(center bottom, #ccc 0%, #eee 60%);
|
145 |
+
background-image: -moz-linear-gradient(center bottom, #ccc 0%, #eee 60%);
|
146 |
+
background-image: -o-linear-gradient(bottom, #ccc 0%, #eee 60%);
|
147 |
+
background-image: -ms-linear-gradient(top, #cccccc 0%, #eeeeee 60%);
|
148 |
+
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr = '#cccccc', endColorstr = '#eeeeee', GradientType = 0);
|
149 |
+
background-image: linear-gradient(top, #cccccc 0%, #eeeeee 60%);
|
150 |
+
border-left: 1px solid #aaa;
|
151 |
+
position: absolute;
|
152 |
+
right: 0;
|
153 |
+
top: 0;
|
154 |
+
display: block;
|
155 |
+
height: 100%;
|
156 |
+
width: 18px;
|
157 |
+
}
|
158 |
+
|
159 |
+
.select2-container .select2-choice div b {
|
160 |
+
background: url('select2.png') no-repeat 0 1px;
|
161 |
+
display: block;
|
162 |
+
width: 100%;
|
163 |
+
height: 100%;
|
164 |
+
}
|
165 |
+
|
166 |
+
.select2-search {
|
167 |
+
display: inline-block;
|
168 |
+
white-space: nowrap;
|
169 |
+
z-index: 10000;
|
170 |
+
min-height: 26px;
|
171 |
+
width: 100%;
|
172 |
+
margin: 0;
|
173 |
+
padding-left: 4px;
|
174 |
+
padding-right: 4px;
|
175 |
+
}
|
176 |
+
|
177 |
+
.select2-search-hidden {
|
178 |
+
display: block;
|
179 |
+
position: absolute;
|
180 |
+
left: -10000px;
|
181 |
+
}
|
182 |
+
|
183 |
+
.select2-search input {
|
184 |
+
background: #fff url('select2.png') no-repeat 100% -22px;
|
185 |
+
background: url('select2.png') no-repeat 100% -22px, -webkit-gradient(linear, left bottom, left top, color-stop(0.85, white), color-stop(0.99, #eeeeee));
|
186 |
+
background: url('select2.png') no-repeat 100% -22px, -webkit-linear-gradient(center bottom, white 85%, #eeeeee 99%);
|
187 |
+
background: url('select2.png') no-repeat 100% -22px, -moz-linear-gradient(center bottom, white 85%, #eeeeee 99%);
|
188 |
+
background: url('select2.png') no-repeat 100% -22px, -o-linear-gradient(bottom, white 85%, #eeeeee 99%);
|
189 |
+
background: url('select2.png') no-repeat 100% -22px, -ms-linear-gradient(top, #ffffff 85%, #eeeeee 99%);
|
190 |
+
background: url('select2.png') no-repeat 100% -22px, linear-gradient(top, #ffffff 85%, #eeeeee 99%);
|
191 |
+
padding: 4px 20px 4px 5px;
|
192 |
+
outline: 0;
|
193 |
+
border: 1px solid #aaa;
|
194 |
+
font-family: sans-serif;
|
195 |
+
font-size: 1em;
|
196 |
+
width:100%;
|
197 |
+
margin:0;
|
198 |
+
height:auto !important;
|
199 |
+
min-height: 26px;
|
200 |
+
-webkit-box-shadow: none;
|
201 |
+
-moz-box-shadow: none;
|
202 |
+
box-shadow: none;
|
203 |
+
border-radius: 0;
|
204 |
+
-moz-border-radius: 0;
|
205 |
+
-webkit-border-radius: 0;
|
206 |
+
}
|
207 |
+
|
208 |
+
.select2-drop.select2-drop-above .select2-search input
|
209 |
+
{
|
210 |
+
margin-top:4px;
|
211 |
+
}
|
212 |
+
|
213 |
+
.select2-search input.select2-active {
|
214 |
+
background: #fff url('spinner.gif') no-repeat 100%;
|
215 |
+
background: url('spinner.gif') no-repeat 100%, -webkit-gradient(linear, left bottom, left top, color-stop(0.85, white), color-stop(0.99, #eeeeee));
|
216 |
+
background: url('spinner.gif') no-repeat 100%, -webkit-linear-gradient(center bottom, white 85%, #eeeeee 99%);
|
217 |
+
background: url('spinner.gif') no-repeat 100%, -moz-linear-gradient(center bottom, white 85%, #eeeeee 99%);
|
218 |
+
background: url('spinner.gif') no-repeat 100%, -o-linear-gradient(bottom, white 85%, #eeeeee 99%);
|
219 |
+
background: url('spinner.gif') no-repeat 100%, -ms-linear-gradient(top, #ffffff 85%, #eeeeee 99%);
|
220 |
+
background: url('spinner.gif') no-repeat 100%, linear-gradient(top, #ffffff 85%, #eeeeee 99%);
|
221 |
+
}
|
222 |
+
|
223 |
+
|
224 |
+
.select2-container-active .select2-choice,
|
225 |
+
.select2-container-active .select2-choices {
|
226 |
+
-webkit-box-shadow: 0 0 5px rgba(0,0,0,.3);
|
227 |
+
-moz-box-shadow : 0 0 5px rgba(0,0,0,.3);
|
228 |
+
-o-box-shadow : 0 0 5px rgba(0,0,0,.3);
|
229 |
+
box-shadow : 0 0 5px rgba(0,0,0,.3);
|
230 |
+
border: 1px solid #5897fb;
|
231 |
+
outline: none;
|
232 |
+
}
|
233 |
+
|
234 |
+
.select2-dropdown-open .select2-choice {
|
235 |
+
border: 1px solid #aaa;
|
236 |
+
border-bottom-color: transparent;
|
237 |
+
-webkit-box-shadow: 0 1px 0 #fff inset;
|
238 |
+
-moz-box-shadow : 0 1px 0 #fff inset;
|
239 |
+
-o-box-shadow : 0 1px 0 #fff inset;
|
240 |
+
box-shadow : 0 1px 0 #fff inset;
|
241 |
+
background-color: #eee;
|
242 |
+
background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, white), color-stop(0.5, #eeeeee));
|
243 |
+
background-image: -webkit-linear-gradient(center bottom, white 0%, #eeeeee 50%);
|
244 |
+
background-image: -moz-linear-gradient(center bottom, white 0%, #eeeeee 50%);
|
245 |
+
background-image: -o-linear-gradient(bottom, white 0%, #eeeeee 50%);
|
246 |
+
background-image: -ms-linear-gradient(top, #ffffff 0%,#eeeeee 50%);
|
247 |
+
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#eeeeee',GradientType=0 );
|
248 |
+
background-image: linear-gradient(top, #ffffff 0%,#eeeeee 50%);
|
249 |
+
-webkit-border-bottom-left-radius : 0;
|
250 |
+
-webkit-border-bottom-right-radius: 0;
|
251 |
+
-moz-border-radius-bottomleft : 0;
|
252 |
+
-moz-border-radius-bottomright: 0;
|
253 |
+
border-bottom-left-radius : 0;
|
254 |
+
border-bottom-right-radius: 0;
|
255 |
+
}
|
256 |
+
|
257 |
+
.select2-dropdown-open .select2-choice div {
|
258 |
+
background: transparent;
|
259 |
+
border-left: none;
|
260 |
+
}
|
261 |
+
.select2-dropdown-open .select2-choice div b {
|
262 |
+
background-position: -18px 1px;
|
263 |
+
}
|
264 |
+
|
265 |
+
/* results */
|
266 |
+
.select2-results {
|
267 |
+
margin: 4px 4px 4px 0;
|
268 |
+
padding: 0 0 0 4px;
|
269 |
+
position: relative;
|
270 |
+
overflow-x: hidden;
|
271 |
+
overflow-y: auto;
|
272 |
+
max-height: 200px;
|
273 |
+
}
|
274 |
+
|
275 |
+
.select2-results ul.select2-result-sub {
|
276 |
+
margin: 0 0 0 0;
|
277 |
+
}
|
278 |
+
|
279 |
+
.select2-results ul.select2-result-sub > li .select2-result-label { padding-left: 20px }
|
280 |
+
.select2-results ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 40px }
|
281 |
+
.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 60px }
|
282 |
+
.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 80px }
|
283 |
+
.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 100px }
|
284 |
+
.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 110px }
|
285 |
+
.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 120px }
|
286 |
+
|
287 |
+
.select2-results li {
|
288 |
+
list-style: none;
|
289 |
+
display: list-item;
|
290 |
+
}
|
291 |
+
|
292 |
+
.select2-results li.select2-result-with-children > .select2-result-label {
|
293 |
+
font-weight: bold;
|
294 |
+
}
|
295 |
+
|
296 |
+
.select2-results .select2-result-label {
|
297 |
+
padding: 3px 7px 4px;
|
298 |
+
margin: 0;
|
299 |
+
cursor: pointer;
|
300 |
+
}
|
301 |
+
|
302 |
+
.select2-results .select2-highlighted {
|
303 |
+
background: #3875d7;
|
304 |
+
color: #fff;
|
305 |
+
}
|
306 |
+
.select2-results li em {
|
307 |
+
background: #feffde;
|
308 |
+
font-style: normal;
|
309 |
+
}
|
310 |
+
.select2-results .select2-highlighted em {
|
311 |
+
background: transparent;
|
312 |
+
}
|
313 |
+
.select2-results .select2-no-results,
|
314 |
+
.select2-results .select2-searching,
|
315 |
+
.select2-results .select2-selection-limit {
|
316 |
+
background: #f4f4f4;
|
317 |
+
display: list-item;
|
318 |
+
}
|
319 |
+
|
320 |
+
/*
|
321 |
+
disabled look for already selected choices in the results dropdown
|
322 |
+
.select2-results .select2-disabled.select2-highlighted {
|
323 |
+
color: #666;
|
324 |
+
background: #f4f4f4;
|
325 |
+
display: list-item;
|
326 |
+
cursor: default;
|
327 |
+
}
|
328 |
+
.select2-results .select2-disabled {
|
329 |
+
background: #f4f4f4;
|
330 |
+
display: list-item;
|
331 |
+
cursor: default;
|
332 |
+
}
|
333 |
+
*/
|
334 |
+
.select2-results .select2-disabled {
|
335 |
+
display: none;
|
336 |
+
}
|
337 |
+
|
338 |
+
.select2-more-results.select2-active {
|
339 |
+
background: #f4f4f4 url('spinner.gif') no-repeat 100%;
|
340 |
+
}
|
341 |
+
|
342 |
+
.select2-more-results {
|
343 |
+
background: #f4f4f4;
|
344 |
+
display: list-item;
|
345 |
+
}
|
346 |
+
|
347 |
+
/* disabled styles */
|
348 |
+
|
349 |
+
.select2-container.select2-container-disabled .select2-choice {
|
350 |
+
background-color: #f4f4f4;
|
351 |
+
background-image: none;
|
352 |
+
border: 1px solid #ddd;
|
353 |
+
cursor: default;
|
354 |
+
}
|
355 |
+
|
356 |
+
.select2-container.select2-container-disabled .select2-choice div {
|
357 |
+
background-color: #f4f4f4;
|
358 |
+
background-image: none;
|
359 |
+
border-left: 0;
|
360 |
+
}
|
361 |
+
|
362 |
+
|
363 |
+
/* multiselect */
|
364 |
+
|
365 |
+
.select2-container-multi .select2-choices {
|
366 |
+
background-color: #fff;
|
367 |
+
background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(1%, #eeeeee), color-stop(15%, #ffffff));
|
368 |
+
background-image: -webkit-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
|
369 |
+
background-image: -moz-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
|
370 |
+
background-image: -o-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
|
371 |
+
background-image: -ms-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
|
372 |
+
background-image: linear-gradient(top, #eeeeee 1%, #ffffff 15%);
|
373 |
+
border: 1px solid #aaa;
|
374 |
+
margin: 0;
|
375 |
+
padding: 0;
|
376 |
+
cursor: text;
|
377 |
+
overflow: hidden;
|
378 |
+
height: auto !important;
|
379 |
+
height: 1%;
|
380 |
+
position: relative;
|
381 |
+
}
|
382 |
+
|
383 |
+
.select2-container-multi .select2-choices {
|
384 |
+
min-height: 26px;
|
385 |
+
}
|
386 |
+
|
387 |
+
.select2-container-multi.select2-container-active .select2-choices {
|
388 |
+
-webkit-box-shadow: 0 0 5px rgba(0,0,0,.3);
|
389 |
+
-moz-box-shadow : 0 0 5px rgba(0,0,0,.3);
|
390 |
+
-o-box-shadow : 0 0 5px rgba(0,0,0,.3);
|
391 |
+
box-shadow : 0 0 5px rgba(0,0,0,.3);
|
392 |
+
border: 1px solid #5897fb;
|
393 |
+
outline: none;
|
394 |
+
}
|
395 |
+
.select2-container-multi .select2-choices li {
|
396 |
+
float: left;
|
397 |
+
list-style: none;
|
398 |
+
}
|
399 |
+
.select2-container-multi .select2-choices .select2-search-field {
|
400 |
+
white-space: nowrap;
|
401 |
+
margin: 0;
|
402 |
+
padding: 0;
|
403 |
+
}
|
404 |
+
|
405 |
+
.select2-container-multi .select2-choices .select2-search-field input {
|
406 |
+
color: #666;
|
407 |
+
background: transparent !important;
|
408 |
+
font-family: sans-serif;
|
409 |
+
font-size: 100%;
|
410 |
+
height: 15px;
|
411 |
+
padding: 5px;
|
412 |
+
margin: 1px 0;
|
413 |
+
outline: 0;
|
414 |
+
border: 0;
|
415 |
+
-webkit-box-shadow: none;
|
416 |
+
-moz-box-shadow : none;
|
417 |
+
-o-box-shadow : none;
|
418 |
+
box-shadow : none;
|
419 |
+
}
|
420 |
+
|
421 |
+
.select2-container-multi .select2-choices .select2-search-field input.select2-active {
|
422 |
+
background: #fff url('spinner.gif') no-repeat 100% !important;
|
423 |
+
}
|
424 |
+
|
425 |
+
.select2-default {
|
426 |
+
color: #999 !important;
|
427 |
+
}
|
428 |
+
|
429 |
+
.select2-container-multi .select2-choices .select2-search-choice {
|
430 |
+
-webkit-border-radius: 3px;
|
431 |
+
-moz-border-radius : 3px;
|
432 |
+
border-radius : 3px;
|
433 |
+
-moz-background-clip : padding;
|
434 |
+
-webkit-background-clip: padding-box;
|
435 |
+
background-clip : padding-box;
|
436 |
+
background-color: #e4e4e4;
|
437 |
+
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#f4f4f4', endColorstr='#eeeeee', GradientType=0 );
|
438 |
+
background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(20%, #f4f4f4), color-stop(50%, #f0f0f0), color-stop(52%, #e8e8e8), color-stop(100%, #eeeeee));
|
439 |
+
background-image: -webkit-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
|
440 |
+
background-image: -moz-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
|
441 |
+
background-image: -o-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
|
442 |
+
background-image: -ms-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
|
443 |
+
background-image: linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
|
444 |
+
-webkit-box-shadow: 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05);
|
445 |
+
-moz-box-shadow : 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05);
|
446 |
+
box-shadow : 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05);
|
447 |
+
color: #333;
|
448 |
+
border: 1px solid #aaaaaa;
|
449 |
+
line-height: 13px;
|
450 |
+
padding: 3px 5px 3px 18px;
|
451 |
+
margin: 3px 0 3px 5px;
|
452 |
+
position: relative;
|
453 |
+
cursor: default;
|
454 |
+
}
|
455 |
+
.select2-container-multi .select2-choices .select2-search-choice span {
|
456 |
+
cursor: default;
|
457 |
+
}
|
458 |
+
.select2-container-multi .select2-choices .select2-search-choice-focus {
|
459 |
+
background: #d4d4d4;
|
460 |
+
}
|
461 |
+
|
462 |
+
.select2-search-choice-close {
|
463 |
+
display: block;
|
464 |
+
position: absolute;
|
465 |
+
right: 3px;
|
466 |
+
top: 4px;
|
467 |
+
width: 12px;
|
468 |
+
height: 13px;
|
469 |
+
font-size: 1px;
|
470 |
+
background: url('select2.png') right top no-repeat;
|
471 |
+
outline: none;
|
472 |
+
}
|
473 |
+
|
474 |
+
.select2-container-multi .select2-search-choice-close {
|
475 |
+
left: 3px;
|
476 |
+
}
|
477 |
+
|
478 |
+
|
479 |
+
.select2-container-multi .select2-choices .select2-search-choice .select2-search-choice-close:hover {
|
480 |
+
background-position: right -11px;
|
481 |
+
}
|
482 |
+
.select2-container-multi .select2-choices .select2-search-choice-focus .select2-search-choice-close {
|
483 |
+
background-position: right -11px;
|
484 |
+
}
|
485 |
+
|
486 |
+
/* disabled styles */
|
487 |
+
|
488 |
+
.select2-container-multi.select2-container-disabled .select2-choices{
|
489 |
+
background-color: #f4f4f4;
|
490 |
+
background-image: none;
|
491 |
+
border: 1px solid #ddd;
|
492 |
+
cursor: default;
|
493 |
+
}
|
494 |
+
|
495 |
+
.select2-container-multi.select2-container-disabled .select2-choices .select2-search-choice {
|
496 |
+
background-image: none;
|
497 |
+
background-color: #f4f4f4;
|
498 |
+
border: 1px solid #ddd;
|
499 |
+
padding: 3px 5px 3px 5px;
|
500 |
+
}
|
501 |
+
|
502 |
+
.select2-container-multi.select2-container-disabled .select2-choices .select2-search-choice .select2-search-choice-close {
|
503 |
+
display: none;
|
504 |
+
}
|
505 |
+
/* end multiselect */
|
506 |
+
|
507 |
+
.select2-result-selectable .select2-match,
|
508 |
+
.select2-result-unselectable .select2-result-selectable .select2-match { text-decoration: underline; }
|
509 |
+
.select2-result-unselectable .select2-match { text-decoration: none; }
|
510 |
+
|
511 |
+
.select2-offscreen { position: absolute; left: -10000px; }
|
512 |
+
|
513 |
+
/* Retina-ize icons */
|
514 |
+
|
515 |
+
@media only screen and (-webkit-min-device-pixel-ratio: 1.5) {
|
516 |
+
.select2-search input, .select2-search-choice-close, .select2-container .select2-choice abbr, .select2-container .select2-choice div b {
|
517 |
+
background-image: url(select2x2.png) !important;
|
518 |
+
background-repeat: no-repeat !important;
|
519 |
+
background-size: 60px 40px !important;
|
520 |
+
}
|
521 |
+
.select2-search input {
|
522 |
+
background-position: 100% -21px !important;
|
523 |
+
}
|
524 |
+
}
|
@@ -0,0 +1,82 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*
|
2 |
+
Copyright 2012 Igor Vaynberg
|
3 |
+
|
4 |
+
Version: 3.2 Timestamp: Mon Sep 10 10:38:04 PDT 2012
|
5 |
+
|
6 |
+
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in
|
7 |
+
compliance with the License. You may obtain a copy of the License in the LICENSE file, or at:
|
8 |
+
|
9 |
+
http://www.apache.org/licenses/LICENSE-2.0
|
10 |
+
|
11 |
+
Unless required by applicable law or agreed to in writing, software distributed under the License is
|
12 |
+
distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13 |
+
See the License for the specific language governing permissions and limitations under the License.
|
14 |
+
*/
|
15 |
+
(function(e){"undefined"==typeof e.fn.each2&&e.fn.extend({each2:function(g){for(var i=e([0]),m=-1,s=this.length;++m<s&&(i.context=i[0]=this[m])&&!1!==g.call(i[0],m,i););return this}})})(jQuery);
|
16 |
+
(function(e,g){function i(a,b){var c=0,d=b.length,j;if("undefined"===typeof a)return-1;if(a.constructor===String)for(;c<d;c+=1){if(0===a.localeCompare(b[c]))return c}else for(;c<d;c+=1)if(j=b[c],j.constructor===String){if(0===j.localeCompare(a))return c}else if(j===a)return c;return-1}function m(a,b){return a===b?!0:a===g||b===g||null===a||null===b?!1:a.constructor===String?0===a.localeCompare(b):b.constructor===String?0===b.localeCompare(a):!1}function s(a,b){var c,d,j;if(null===a||1>a.length)return[];
|
17 |
+
c=a.split(b);d=0;for(j=c.length;d<j;d+=1)c[d]=e.trim(c[d]);return c}function A(a,b,c){var c=c||g,d;return function(){var j=arguments;window.clearTimeout(d);d=window.setTimeout(function(){b.apply(c,j)},a)}}function l(a){a.preventDefault();a.stopPropagation()}function B(a,b,c){var d=a.toUpperCase().indexOf(b.toUpperCase()),b=b.length;0>d?c.push(a):(c.push(a.substring(0,d)),c.push("<span class='select2-match'>"),c.push(a.substring(d,d+b)),c.push("</span>"),c.push(a.substring(d+b,a.length)))}function C(a){var b,
|
18 |
+
c=0,d=null,j=a.quietMillis||100;return function(h){window.clearTimeout(b);b=window.setTimeout(function(){var b=c+=1,j=a.data,n=a.transport||e.ajax,f=a.traditional||!1,g=a.type||"GET",j=j.call(this,h.term,h.page,h.context);null!==d&&d.abort();d=n.call(null,{url:a.url,dataType:a.dataType,data:j,type:g,traditional:f,success:function(d){b<c||(d=a.results(d,h.page),h.callback(d))}})},j)}}function D(a){var b=a,c,d=function(a){return""+a.text};e.isArray(b)||(d=b.text,e.isFunction(d)||(c=b.text,d=function(a){return a[c]}),
|
19 |
+
b=b.results);return function(a){var c=a.term,f={results:[]},k;if(c==="")a.callback({results:b});else{k=function(b,f){var g,t,b=b[0];if(b.children){g={};for(t in b)b.hasOwnProperty(t)&&(g[t]=b[t]);g.children=[];e(b.children).each2(function(a,b){k(b,g.children)});g.children.length&&f.push(g)}else a.matcher(c,d(b))&&f.push(b)};e(b).each2(function(a,b){k(b,f.results)});a.callback(f)}}}function E(a){return e.isFunction(a)?a:function(b){var c=b.term,d={results:[]};e(a).each(function(){var a=this.text!==
|
20 |
+
g,e=a?this.text:this;if(""===c||b.matcher(c,e))d.results.push(a?this:{id:this,text:this})});b.callback(d)}}function u(a){if(e.isFunction(a))return!0;if(!a)return!1;throw Error("formatterName must be a function or a falsy value");}function v(a){return e.isFunction(a)?a():a}function F(a){var b=0;e.each(a,function(a,d){d.children?b+=F(d.children):b++});return b}function H(a,b,c,d){var e=a,h=!1,f,k,n,o;if(!d.createSearchChoice||!d.tokenSeparators||1>d.tokenSeparators.length)return g;for(;;){h=-1;k=0;
|
21 |
+
for(n=d.tokenSeparators.length;k<n&&!(o=d.tokenSeparators[k],h=a.indexOf(o),0<=h);k++);if(0>h)break;f=a.substring(0,h);a=a.substring(h+o.length);if(0<f.length&&(f=d.createSearchChoice(f,b),f!==g&&null!==f&&d.id(f)!==g&&null!==d.id(f))){h=!1;k=0;for(n=b.length;k<n;k++)if(m(d.id(f),d.id(b[k]))){h=!0;break}h||c(f)}}if(0!=e.localeCompare(a))return a}function x(a,b){var c=function(){};c.prototype=new a;c.prototype.constructor=c;c.prototype.parent=a.prototype;c.prototype=e.extend(c.prototype,b);return c}
|
22 |
+
if(window.Select2===g){var f,w,y,z,G,q;f={TAB:9,ENTER:13,ESC:27,SPACE:32,LEFT:37,UP:38,RIGHT:39,DOWN:40,SHIFT:16,CTRL:17,ALT:18,PAGE_UP:33,PAGE_DOWN:34,HOME:36,END:35,BACKSPACE:8,DELETE:46,isArrow:function(a){a=a.which?a.which:a;switch(a){case f.LEFT:case f.RIGHT:case f.UP:case f.DOWN:return!0}return!1},isControl:function(a){switch(a.which){case f.SHIFT:case f.CTRL:case f.ALT:return!0}return a.metaKey?!0:!1},isFunctionKey:function(a){a=a.which?a.which:a;return 112<=a&&123>=a}};var I=1;G=function(){return I++};
|
23 |
+
e(document).delegate("body","mousemove",function(a){e.data(document,"select2-lastpos",{x:a.pageX,y:a.pageY})});e(document).ready(function(){e(document).delegate("body","mousedown touchend",function(a){var b=e(a.target).closest("div.select2-container").get(0),c;b?e(document).find("div.select2-container-active").each(function(){this!==b&&e(this).data("select2").blur()}):(b=e(a.target).closest("div.select2-drop").get(0),e(document).find("div.select2-drop-active").each(function(){this!==b&&e(this).data("select2").blur()}));
|
24 |
+
b=e(a.target);c=b.attr("for");"LABEL"===a.target.tagName&&(c&&0<c.length)&&(b=e("#"+c),b=b.data("select2"),b!==g&&(b.focus(),a.preventDefault()))})});w=x(Object,{bind:function(a){var b=this;return function(){a.apply(b,arguments)}},init:function(a){var b,c;this.opts=a=this.prepareOpts(a);this.id=a.id;a.element.data("select2")!==g&&null!==a.element.data("select2")&&this.destroy();this.enabled=!0;this.container=this.createContainer();this.containerId="s2id_"+(a.element.attr("id")||"autogen"+G());this.containerSelector=
|
25 |
+
"#"+this.containerId.replace(/([;&,\.\+\*\~':"\!\^#$%@\[\]\(\)=>\|])/g,"\\$1");this.container.attr("id",this.containerId);var d=!1,j;this.body=function(){!1===d&&(j=a.element.closest("body"),d=!0);return j};a.element.attr("class")!==g&&this.container.addClass(a.element.attr("class").replace(/validate\[[\S ]+] ?/,""));this.container.css(v(a.containerCss));this.container.addClass(v(a.containerCssClass));this.opts.element.data("select2",this).hide().before(this.container);this.container.data("select2",
|
26 |
+
this);this.dropdown=this.container.find(".select2-drop");this.dropdown.addClass(v(a.dropdownCssClass));this.dropdown.data("select2",this);this.results=b=this.container.find(".select2-results");this.search=c=this.container.find("input.select2-input");c.attr("tabIndex",this.opts.element.attr("tabIndex"));this.resultsPage=0;this.context=null;this.initContainer();this.initContainerWidth();this.results.bind("mousemove",function(a){var b=e.data(document,"select2-lastpos");(b===g||b.x!==a.pageX||b.y!==a.pageY)&&
|
27 |
+
e(a.target).trigger("mousemove-filtered",a)});this.dropdown.delegate(".select2-results","mousemove-filtered",this.bind(this.highlightUnderEvent));var h=this.results,f=A(80,function(a){h.trigger("scroll-debounced",a)});h.bind("scroll",function(a){0<=i(a.target,h.get())&&f(a)});this.dropdown.delegate(".select2-results","scroll-debounced",this.bind(this.loadMoreIfNeeded));e.fn.mousewheel&&b.mousewheel(function(a,c,d,e){c=b.scrollTop();0<e&&0>=c-e?(b.scrollTop(0),l(a)):0>e&&b.get(0).scrollHeight-b.scrollTop()+
|
28 |
+
e<=b.height()&&(b.scrollTop(b.get(0).scrollHeight-b.height()),l(a))});c.bind("keydown",function(){e.data(c,"keyup-change-value")===g&&e.data(c,"keyup-change-value",c.val())});c.bind("keyup",function(){var a=e.data(c,"keyup-change-value");a!==g&&c.val()!==a&&(e.removeData(c,"keyup-change-value"),c.trigger("keyup-change"))});c.bind("keyup-change",this.bind(this.updateResults));c.bind("focus",function(){c.addClass("select2-focused");" "===c.val()&&c.val("")});c.bind("blur",function(){c.removeClass("select2-focused")});
|
29 |
+
this.dropdown.delegate(".select2-results","mouseup",this.bind(function(a){0<e(a.target).closest(".select2-result-selectable:not(.select2-disabled)").length?(this.highlightUnderEvent(a),this.selectHighlighted(a)):this.focusSearch();l(a)}));this.dropdown.bind("click mouseup mousedown",function(a){a.stopPropagation()});e.isFunction(this.opts.initSelection)&&(this.initSelection(),this.monitorSource());(a.element.is(":disabled")||a.element.is("[readonly='readonly']"))&&this.disable()},destroy:function(){var a=
|
30 |
+
this.opts.element.data("select2");a!==g&&(a.container.remove(),a.dropdown.remove(),a.opts.element.removeData("select2").unbind(".select2").show())},prepareOpts:function(a){var b,c,d;b=a.element;"select"===b.get(0).tagName.toLowerCase()&&(this.select=c=a.element);c&&e.each("id multiple ajax query createSearchChoice initSelection data tags".split(" "),function(){if(this in a)throw Error("Option '"+this+"' is not allowed for Select2 when attached to a <select> element.");});a=e.extend({},{populateResults:function(b,
|
31 |
+
c,d){var f,n=this.opts.id,o=this;f=function(b,c,j){var h,l,i,m,r,p,q;h=0;for(l=b.length;h<l;h=h+1){i=b[h];m=n(i)!==g;r=i.children&&i.children.length>0;p=e("<li></li>");p.addClass("select2-results-dept-"+j);p.addClass("select2-result");p.addClass(m?"select2-result-selectable":"select2-result-unselectable");r&&p.addClass("select2-result-with-children");p.addClass(o.opts.formatResultCssClass(i));m=e("<div></div>");m.addClass("select2-result-label");q=a.formatResult(i,m,d);q!==g&&m.html(o.opts.escapeMarkup(q));
|
32 |
+
p.append(m);if(r){r=e("<ul></ul>");r.addClass("select2-result-sub");f(i.children,r,j+1);p.append(r)}p.data("select2-data",i);c.append(p)}};f(c,b,0)}},e.fn.select2.defaults,a);"function"!==typeof a.id&&(d=a.id,a.id=function(a){return a[d]});if(c)a.query=this.bind(function(a){var c={results:[],more:false},d=a.term,f,n,o;o=function(b,c){var e;if(b.is("option"))a.matcher(d,b.text(),b)&&c.push({id:b.attr("value"),text:b.text(),element:b.get(),css:b.attr("class")});else if(b.is("optgroup")){e={text:b.attr("label"),
|
33 |
+
children:[],element:b.get(),css:b.attr("class")};b.children().each2(function(a,b){o(b,e.children)});e.children.length>0&&c.push(e)}};f=b.children();if(this.getPlaceholder()!==g&&f.length>0){n=f[0];e(n).text()===""&&(f=f.not(n))}f.each2(function(a,b){o(b,c.results)});a.callback(c)}),a.id=function(a){return a.id},a.formatResultCssClass=function(a){return a.css};else if(!("query"in a))if("ajax"in a){if((c=a.element.data("ajax-url"))&&0<c.length)a.ajax.url=c;a.query=C(a.ajax)}else"data"in a?a.query=D(a.data):
|
34 |
+
"tags"in a&&(a.query=E(a.tags),a.createSearchChoice=function(a){return{id:a,text:a}},a.initSelection=function(b,c){var d=[];e(s(b.val(),a.separator)).each(function(){var b=this,c=this,j=a.tags;e.isFunction(j)&&(j=j());e(j).each(function(){if(m(this.id,b)){c=this.text;return false}});d.push({id:b,text:c})});c(d)});if("function"!==typeof a.query)throw"query function not defined for Select2 "+a.element.attr("id");return a},monitorSource:function(){this.opts.element.bind("change.select2",this.bind(function(){!0!==
|
35 |
+
this.opts.element.data("select2-change-triggered")&&this.initSelection()}))},triggerChange:function(a){a=a||{};a=e.extend({},a,{type:"change",val:this.val()});this.opts.element.data("select2-change-triggered",!0);this.opts.element.trigger(a);this.opts.element.data("select2-change-triggered",!1);this.opts.element.click();this.opts.blurOnChange&&this.opts.element.blur()},enable:function(){this.enabled||(this.enabled=!0,this.container.removeClass("select2-container-disabled"))},disable:function(){this.enabled&&
|
36 |
+
(this.close(),this.enabled=!1,this.container.addClass("select2-container-disabled"))},opened:function(){return this.container.hasClass("select2-dropdown-open")},positionDropdown:function(){var a=this.container.offset(),b=this.container.outerHeight(),c=this.container.outerWidth(),d=this.dropdown.outerHeight(),j=e(window).scrollTop()+document.documentElement.clientHeight,b=a.top+b,f=a.left,j=b+d<=j,g=a.top-d>=this.body().scrollTop(),k=this.dropdown.hasClass("select2-drop-above"),n;"static"!==this.body().css("position")&&
|
37 |
+
(n=this.body().offset(),b-=n.top,f-=n.left);k?(k=!0,!g&&j&&(k=!1)):(k=!1,!j&&g&&(k=!0));k?(b=a.top-d,this.container.addClass("select2-drop-above"),this.dropdown.addClass("select2-drop-above")):(this.container.removeClass("select2-drop-above"),this.dropdown.removeClass("select2-drop-above"));a=e.extend({top:b,left:f,width:c},v(this.opts.dropdownCss));this.dropdown.css(a)},shouldOpen:function(){var a;if(this.opened())return!1;a=e.Event("open");this.opts.element.trigger(a);return!a.isDefaultPrevented()},
|
38 |
+
clearDropdownAlignmentPreference:function(){this.container.removeClass("select2-drop-above");this.dropdown.removeClass("select2-drop-above")},open:function(){if(!this.shouldOpen())return!1;window.setTimeout(this.bind(this.opening),1);return!0},opening:function(){var a=this.containerId,b=this.containerSelector,c="scroll."+a,d="resize."+a;this.container.parents().each(function(){e(this).bind(c,function(){var a=e(b);0==a.length&&e(this).unbind(c);a.select2("close")})});e(window).bind(d,function(){var a=
|
39 |
+
e(b);0==a.length&&e(window).unbind(d);a.select2("close")});this.clearDropdownAlignmentPreference();" "===this.search.val()&&this.search.val("");this.container.addClass("select2-dropdown-open").addClass("select2-container-active");this.updateResults(!0);this.dropdown[0]!==this.body().children().last()[0]&&this.dropdown.detach().appendTo(this.body());this.dropdown.show();this.positionDropdown();this.dropdown.addClass("select2-drop-active");this.ensureHighlightVisible();this.focusSearch()},close:function(){if(this.opened()){var a=
|
40 |
+
this;this.container.parents().each(function(){e(this).unbind("scroll."+a.containerId)});e(window).unbind("resize."+this.containerId);this.clearDropdownAlignmentPreference();this.dropdown.hide();this.container.removeClass("select2-dropdown-open").removeClass("select2-container-active");this.results.empty();this.clearSearch();this.opts.element.trigger(e.Event("close"))}},clearSearch:function(){},ensureHighlightVisible:function(){var a=this.results,b,c,d,f;c=this.highlight();0>c||(0==c?a.scrollTop(0):
|
41 |
+
(b=a.find(".select2-result-selectable"),d=e(b[c]),f=d.offset().top+d.outerHeight(),c===b.length-1&&(b=a.find("li.select2-more-results"),0<b.length&&(f=b.offset().top+b.outerHeight())),b=a.offset().top+a.outerHeight(),f>b&&a.scrollTop(a.scrollTop()+(f-b)),d=d.offset().top-a.offset().top,0>d&&a.scrollTop(a.scrollTop()+d)))},moveHighlight:function(a){for(var b=this.results.find(".select2-result-selectable"),c=this.highlight();-1<c&&c<b.length;){var c=c+a,d=e(b[c]);if(d.hasClass("select2-result-selectable")&&
|
42 |
+
!d.hasClass("select2-disabled")){this.highlight(c);break}}},highlight:function(a){var b=this.results.find(".select2-result-selectable").not(".select2-disabled");if(0===arguments.length)return i(b.filter(".select2-highlighted")[0],b.get());a>=b.length&&(a=b.length-1);0>a&&(a=0);b.removeClass("select2-highlighted");e(b[a]).addClass("select2-highlighted");this.ensureHighlightVisible()},countSelectableResults:function(){return this.results.find(".select2-result-selectable").not(".select2-disabled").length},
|
43 |
+
highlightUnderEvent:function(a){a=e(a.target).closest(".select2-result-selectable");if(0<a.length&&!a.is(".select2-highlighted")){var b=this.results.find(".select2-result-selectable");this.highlight(b.index(a))}else 0==a.length&&this.results.find(".select2-highlighted").removeClass("select2-highlighted")},loadMoreIfNeeded:function(){var a=this.results,b=a.find("li.select2-more-results"),c,d=this.resultsPage+1,e=this,f=this.search.val(),g=this.context;0!==b.length&&(c=b.offset().top-a.offset().top-
|
44 |
+
a.height(),0>=c&&(b.addClass("select2-active"),this.opts.query({term:f,page:d,context:g,matcher:this.opts.matcher,callback:this.bind(function(c){e.opened()&&(e.opts.populateResults.call(this,a,c.results,{term:f,page:d,context:g}),!0===c.more?(b.detach().appendTo(a).text(e.opts.formatLoadMore(d+1)),window.setTimeout(function(){e.loadMoreIfNeeded()},10)):b.remove(),e.positionDropdown(),e.resultsPage=d)})})))},tokenize:function(){},updateResults:function(a){function b(){f.scrollTop(0);d.removeClass("select2-active");
|
45 |
+
k.positionDropdown()}function c(a){f.html(k.opts.escapeMarkup(a));b()}var d=this.search,f=this.results,h=this.opts,i,k=this;if(!(!0!==a&&(!1===this.showSearchInput||!this.opened()))){d.addClass("select2-active");if(1<=h.maximumSelectionSize&&(i=this.data(),e.isArray(i)&&i.length>=h.maximumSelectionSize&&u(h.formatSelectionTooBig,"formatSelectionTooBig"))){c("<li class='select2-selection-limit'>"+h.formatSelectionTooBig(h.maximumSelectionSize)+"</li>");return}d.val().length<h.minimumInputLength&&u(h.formatInputTooShort,
|
46 |
+
"formatInputTooShort")?c("<li class='select2-no-results'>"+h.formatInputTooShort(d.val(),h.minimumInputLength)+"</li>"):(c("<li class='select2-searching'>"+h.formatSearching()+"</li>"),i=this.tokenize(),i!=g&&null!=i&&d.val(i),this.resultsPage=1,h.query({term:d.val(),page:this.resultsPage,context:null,matcher:h.matcher,callback:this.bind(function(i){var l;this.opened()&&((this.context=i.context===g?null:i.context,this.opts.createSearchChoice&&""!==d.val()&&(l=this.opts.createSearchChoice.call(null,
|
47 |
+
d.val(),i.results),l!==g&&null!==l&&k.id(l)!==g&&null!==k.id(l)&&0===e(i.results).filter(function(){return m(k.id(this),k.id(l))}).length&&i.results.unshift(l)),0===i.results.length&&u(h.formatNoMatches,"formatNoMatches"))?c("<li class='select2-no-results'>"+h.formatNoMatches(d.val())+"</li>"):(f.empty(),k.opts.populateResults.call(this,f,i.results,{term:d.val(),page:this.resultsPage,context:null}),!0===i.more&&u(h.formatLoadMore,"formatLoadMore")&&(f.append("<li class='select2-more-results'>"+k.opts.escapeMarkup(h.formatLoadMore(this.resultsPage))+
|
48 |
+
"</li>"),window.setTimeout(function(){k.loadMoreIfNeeded()},10)),this.postprocessResults(i,a),b()))})}))}},cancel:function(){this.close()},blur:function(){this.close();this.container.removeClass("select2-container-active");this.dropdown.removeClass("select2-drop-active");this.search[0]===document.activeElement&&this.search.blur();this.clearSearch();this.selection.find(".select2-search-choice-focus").removeClass("select2-search-choice-focus")},focusSearch:function(){this.search.show();this.search.focus();
|
49 |
+
window.setTimeout(this.bind(function(){this.search.show();this.search.focus();this.search.val(this.search.val())}),10)},selectHighlighted:function(){var a=this.highlight(),b=this.results.find(".select2-highlighted").not(".select2-disabled"),c=b.closest(".select2-result-selectable").data("select2-data");c&&(b.addClass("select2-disabled"),this.highlight(a),this.onSelect(c))},getPlaceholder:function(){return this.opts.element.attr("placeholder")||this.opts.element.attr("data-placeholder")||this.opts.element.data("placeholder")||
|
50 |
+
this.opts.placeholder},initContainerWidth:function(){var a=function(){var a,c,d,f;if("off"===this.opts.width)return null;if("element"===this.opts.width)return 0===this.opts.element.outerWidth()?"auto":this.opts.element.outerWidth()+"px";if("copy"===this.opts.width||"resolve"===this.opts.width){a=this.opts.element.attr("style");if(a!==g){a=a.split(";");d=0;for(f=a.length;d<f;d+=1)if(c=a[d].replace(/\s/g,"").match(/width:(([-+]?([0-9]*\.)?[0-9]+)(px|em|ex|%|in|cm|mm|pt|pc))/),null!==c&&1<=c.length)return c[1]}return"resolve"===
|
51 |
+
this.opts.width?(a=this.opts.element.css("width"),0<a.indexOf("%")?a:0===this.opts.element.outerWidth()?"auto":this.opts.element.outerWidth()+"px"):null}return e.isFunction(this.opts.width)?this.opts.width():this.opts.width}.call(this);null!==a&&this.container.attr("style","width: "+a)}});y=x(w,{createContainer:function(){return e("<div></div>",{"class":"select2-container"}).html(" <a href='#' onclick='return false;' class='select2-choice'> <span></span><abbr class='select2-search-choice-close' style='display:none;'></abbr> <div><b></b></div></a> <div class='select2-drop select2-offscreen'> <div class='select2-search'> <input type='text' autocomplete='off' class='select2-input'/> </div> <ul class='select2-results'> </ul></div>")},
|
52 |
+
opening:function(){this.search.show();this.parent.opening.apply(this,arguments);this.dropdown.removeClass("select2-offscreen")},close:function(){this.opened()&&(this.parent.close.apply(this,arguments),this.dropdown.removeAttr("style").addClass("select2-offscreen").insertAfter(this.selection).show())},focus:function(){this.close();this.selection.focus()},isFocused:function(){return this.selection[0]===document.activeElement},cancel:function(){this.parent.cancel.apply(this,arguments);this.selection.focus()},
|
53 |
+
initContainer:function(){var a,b=this.dropdown;this.selection=a=this.container.find(".select2-choice");this.search.bind("keydown",this.bind(function(a){if(this.enabled)if(a.which===f.PAGE_UP||a.which===f.PAGE_DOWN)l(a);else if(this.opened())switch(a.which){case f.UP:case f.DOWN:this.moveHighlight(a.which===f.UP?-1:1);l(a);break;case f.TAB:case f.ENTER:this.selectHighlighted();l(a);break;case f.ESC:this.cancel(a),l(a)}else a.which===f.TAB||f.isControl(a)||f.isFunctionKey(a)||a.which===f.ESC||!1===
|
54 |
+
this.opts.openOnEnter&&a.which===f.ENTER||this.open()}));this.search.bind("focus",this.bind(function(){this.selection.attr("tabIndex","-1")}));this.search.bind("blur",this.bind(function(){this.opened()||this.container.removeClass("select2-container-active");window.setTimeout(this.bind(function(){this.selection.attr("tabIndex",this.opts.element.attr("tabIndex"))}),10)}));a.bind("mousedown",this.bind(function(){this.opened()?(this.close(),this.selection.focus()):this.enabled&&this.open()}));b.bind("mousedown",
|
55 |
+
this.bind(function(){this.search.focus()}));a.bind("focus",this.bind(function(){this.container.addClass("select2-container-active");this.search.attr("tabIndex","-1")}));a.bind("blur",this.bind(function(){this.opened()||this.container.removeClass("select2-container-active");window.setTimeout(this.bind(function(){this.search.attr("tabIndex",this.opts.element.attr("tabIndex"))}),10)}));a.bind("keydown",this.bind(function(a){if(this.enabled)if(a.which===f.PAGE_UP||a.which===f.PAGE_DOWN)l(a);else if(!(a.which===
|
56 |
+
f.TAB||f.isControl(a)||f.isFunctionKey(a)||a.which===f.ESC)&&!(!1===this.opts.openOnEnter&&a.which===f.ENTER))if(a.which==f.DELETE)this.opts.allowClear&&this.clear();else{this.open();if(a.which!==f.ENTER&&!(48>a.which)){var b=String.fromCharCode(a.which).toLowerCase();a.shiftKey&&(b=b.toUpperCase());this.search.focus();this.search.val(b)}l(a)}}));a.delegate("abbr","mousedown",this.bind(function(a){this.enabled&&(this.clear(),l(a),this.close(),this.triggerChange(),this.selection.focus())}));this.setPlaceholder();
|
57 |
+
this.search.bind("focus",this.bind(function(){this.container.addClass("select2-container-active")}))},clear:function(){this.opts.element.val("");this.selection.find("span").empty();this.selection.removeData("select2-data");this.setPlaceholder()},initSelection:function(){if(""===this.opts.element.val())this.close(),this.setPlaceholder();else{var a=this;this.opts.initSelection.call(null,this.opts.element,function(b){b!==g&&null!==b&&(a.updateSelection(b),a.close(),a.setPlaceholder())})}},prepareOpts:function(){var a=
|
58 |
+
this.parent.prepareOpts.apply(this,arguments);"select"===a.element.get(0).tagName.toLowerCase()&&(a.initSelection=function(a,c){var d=a.find(":selected");e.isFunction(c)&&c({id:d.attr("value"),text:d.text()})});return a},setPlaceholder:function(){var a=this.getPlaceholder();""===this.opts.element.val()&&a!==g&&!(this.select&&""!==this.select.find("option:first").text())&&(this.selection.find("span").html(this.opts.escapeMarkup(a)),this.selection.addClass("select2-default"),this.selection.find("abbr").hide())},
|
59 |
+
postprocessResults:function(a,b){var c=0,d=this,f=!0;this.results.find(".select2-result-selectable").each2(function(a,b){if(m(d.id(b.data("select2-data")),d.opts.element.val()))return c=a,!1});this.highlight(c);!0===b&&(f=this.showSearchInput=F(a.results)>=this.opts.minimumResultsForSearch,this.dropdown.find(".select2-search")[f?"removeClass":"addClass"]("select2-search-hidden"),e(this.dropdown,this.container)[f?"addClass":"removeClass"]("select2-with-searchbox"))},onSelect:function(a){var b=this.opts.element.val();
|
60 |
+
this.opts.element.val(this.id(a));this.updateSelection(a);this.close();this.selection.focus();m(b,this.id(a))||this.triggerChange()},updateSelection:function(a){var b=this.selection.find("span");this.selection.data("select2-data",a);b.empty();a=this.opts.formatSelection(a,b);a!==g&&b.append(this.opts.escapeMarkup(a));this.selection.removeClass("select2-default");this.opts.allowClear&&this.getPlaceholder()!==g&&this.selection.find("abbr").show()},val:function(){var a,b=null,c=this;if(0===arguments.length)return this.opts.element.val();
|
61 |
+
a=arguments[0];if(this.select)this.select.val(a).find(":selected").each2(function(a,c){b={id:c.attr("value"),text:c.text()};return!1}),this.updateSelection(b),this.setPlaceholder();else{if(this.opts.initSelection===g)throw Error("cannot call val() if initSelection() is not defined");a?(this.opts.element.val(a),this.opts.initSelection(this.opts.element,function(a){c.opts.element.val(!a?"":c.id(a));c.updateSelection(a);c.setPlaceholder()})):this.clear()}},clearSearch:function(){this.search.val("")},
|
62 |
+
data:function(a){var b;if(0===arguments.length)return b=this.selection.data("select2-data"),b==g&&(b=null),b;!a||""===a?this.clear():(this.opts.element.val(!a?"":this.id(a)),this.updateSelection(a))}});z=x(w,{createContainer:function(){return e("<div></div>",{"class":"select2-container select2-container-multi"}).html(" <ul class='select2-choices'> <li class='select2-search-field'> <input type='text' autocomplete='off' class='select2-input'> </li></ul><div class='select2-drop select2-drop-multi' style='display:none;'> <ul class='select2-results'> </ul></div>")},
|
63 |
+
prepareOpts:function(){var a=this.parent.prepareOpts.apply(this,arguments);"select"===a.element.get(0).tagName.toLowerCase()&&(a.initSelection=function(a,c){var d=[];a.find(":selected").each2(function(a,b){d.push({id:b.attr("value"),text:b.text()})});e.isFunction(c)&&c(d)});return a},initContainer:function(){var a;this.searchContainer=this.container.find(".select2-search-field");this.selection=a=this.container.find(".select2-choices");this.search.bind("keydown",this.bind(function(b){if(this.enabled){if(b.which===
|
64 |
+
f.BACKSPACE&&""===this.search.val()){this.close();var c;c=a.find(".select2-search-choice-focus");if(0<c.length){this.unselect(c.first());this.search.width(10);l(b);return}c=a.find(".select2-search-choice");0<c.length&&c.last().addClass("select2-search-choice-focus")}else a.find(".select2-search-choice-focus").removeClass("select2-search-choice-focus");if(this.opened())switch(b.which){case f.UP:case f.DOWN:this.moveHighlight(b.which===f.UP?-1:1);l(b);return;case f.ENTER:case f.TAB:this.selectHighlighted();
|
65 |
+
l(b);return;case f.ESC:this.cancel(b);l(b);return}if(!(b.which===f.TAB||f.isControl(b)||f.isFunctionKey(b)||b.which===f.BACKSPACE||b.which===f.ESC)&&!(!1===this.opts.openOnEnter&&b.which===f.ENTER))this.open(),(b.which===f.PAGE_UP||b.which===f.PAGE_DOWN)&&l(b)}}));this.search.bind("keyup",this.bind(this.resizeSearch));this.search.bind("blur",this.bind(function(a){this.container.removeClass("select2-container-active");this.search.removeClass("select2-focused");this.clearSearch();a.stopImmediatePropagation()}));
|
66 |
+
this.container.delegate(".select2-choices","mousedown",this.bind(function(a){this.enabled&&!(0<e(a.target).closest(".select2-search-choice").length)&&(this.clearPlaceholder(),this.open(),this.focusSearch(),a.preventDefault())}));this.container.delegate(".select2-choices","focus",this.bind(function(){this.enabled&&(this.container.addClass("select2-container-active"),this.dropdown.addClass("select2-drop-active"),this.clearPlaceholder())}));this.clearSearch()},enable:function(){this.enabled||(this.parent.enable.apply(this,
|
67 |
+
arguments),this.search.removeAttr("disabled"))},disable:function(){this.enabled&&(this.parent.disable.apply(this,arguments),this.search.attr("disabled",!0))},initSelection:function(){""===this.opts.element.val()&&(this.updateSelection([]),this.close(),this.clearSearch());if(this.select||""!==this.opts.element.val()){var a=this;this.opts.initSelection.call(null,this.opts.element,function(b){if(b!==g&&b!==null){a.updateSelection(b);a.close();a.clearSearch()}})}},clearSearch:function(){var a=this.getPlaceholder();
|
68 |
+
a!==g&&0===this.getVal().length&&!1===this.search.hasClass("select2-focused")?(this.search.val(a).addClass("select2-default"),this.resizeSearch()):this.search.val(" ").width(10)},clearPlaceholder:function(){this.search.hasClass("select2-default")?this.search.val("").removeClass("select2-default"):" "===this.search.val()&&this.search.val("")},opening:function(){this.parent.opening.apply(this,arguments);this.clearPlaceholder();this.resizeSearch();this.focusSearch()},close:function(){this.opened()&&
|
69 |
+
this.parent.close.apply(this,arguments)},focus:function(){this.close();this.search.focus()},isFocused:function(){return this.search.hasClass("select2-focused")},updateSelection:function(a){var b=[],c=[],d=this;e(a).each(function(){0>i(d.id(this),b)&&(b.push(d.id(this)),c.push(this))});a=c;this.selection.find(".select2-search-choice").remove();e(a).each(function(){d.addSelectedChoice(this)});d.postprocessResults()},tokenize:function(){var a=this.search.val(),a=this.opts.tokenizer(a,this.data(),this.bind(this.onSelect),
|
70 |
+
this.opts);null!=a&&a!=g&&(this.search.val(a),0<a.length&&this.open())},onSelect:function(a){this.addSelectedChoice(a);this.select&&this.postprocessResults();this.opts.closeOnSelect?(this.close(),this.search.width(10)):0<this.countSelectableResults()?(this.search.width(10),this.resizeSearch(),this.positionDropdown()):this.close();this.triggerChange({added:a});this.focusSearch()},cancel:function(){this.close();this.focusSearch()},addSelectedChoice:function(a){var b=e("<li class='select2-search-choice'> <div></div> <a href='#' onclick='return false;' class='select2-search-choice-close' tabindex='-1'></a></li>"),
|
71 |
+
c=this.id(a),d=this.getVal(),f;f=this.opts.formatSelection(a,b);b.find("div").replaceWith("<div>"+this.opts.escapeMarkup(f)+"</div>");b.find(".select2-search-choice-close").bind("mousedown",l).bind("click dblclick",this.bind(function(a){this.enabled&&(e(a.target).closest(".select2-search-choice").fadeOut("fast",this.bind(function(){this.unselect(e(a.target));this.selection.find(".select2-search-choice-focus").removeClass("select2-search-choice-focus");this.close();this.focusSearch()})).dequeue(),
|
72 |
+
l(a))})).bind("focus",this.bind(function(){this.enabled&&(this.container.addClass("select2-container-active"),this.dropdown.addClass("select2-drop-active"))}));b.data("select2-data",a);b.insertBefore(this.searchContainer);d.push(c);this.setVal(d)},unselect:function(a){var b=this.getVal(),c,d,a=a.closest(".select2-search-choice");if(0===a.length)throw"Invalid argument: "+a+". Must be .select2-search-choice";c=a.data("select2-data");d=i(this.id(c),b);0<=d&&(b.splice(d,1),this.setVal(b),this.select&&
|
73 |
+
this.postprocessResults());a.remove();this.triggerChange({removed:c})},postprocessResults:function(){var a=this.getVal(),b=this.results.find(".select2-result-selectable"),c=this.results.find(".select2-result-with-children"),d=this;b.each2(function(b,c){var e=d.id(c.data("select2-data"));0<=i(e,a)?c.addClass("select2-disabled").removeClass("select2-result-selectable"):c.removeClass("select2-disabled").addClass("select2-result-selectable")});c.each2(function(a,b){0==b.find(".select2-result-selectable").length?
|
74 |
+
b.addClass("select2-disabled"):b.removeClass("select2-disabled")});b.each2(function(a,b){if(!b.hasClass("select2-disabled")&&b.hasClass("select2-result-selectable"))return d.highlight(0),!1})},resizeSearch:function(){var a,b,c,d,f=this.search.outerWidth()-this.search.width();a=this.search;q||(c=a[0].currentStyle||window.getComputedStyle(a[0],null),q=e("<div></div>").css({position:"absolute",left:"-10000px",top:"-10000px",display:"none",fontSize:c.fontSize,fontFamily:c.fontFamily,fontStyle:c.fontStyle,
|
75 |
+
fontWeight:c.fontWeight,letterSpacing:c.letterSpacing,textTransform:c.textTransform,whiteSpace:"nowrap"}),e("body").append(q));q.text(a.val());a=q.width()+10;b=this.search.offset().left;c=this.selection.width();d=this.selection.offset().left;b=c-(b-d)-f;b<a&&(b=c-f);40>b&&(b=c-f);this.search.width(b)},getVal:function(){var a;if(this.select)return a=this.select.val(),null===a?[]:a;a=this.opts.element.val();return s(a,this.opts.separator)},setVal:function(a){var b;this.select?this.select.val(a):(b=
|
76 |
+
[],e(a).each(function(){0>i(this,b)&&b.push(this)}),this.opts.element.val(0===b.length?"":b.join(this.opts.separator)))},val:function(){var a,b=[],c=this;if(0===arguments.length)return this.getVal();if(a=arguments[0])if(this.setVal(a),this.select)this.select.find(":selected").each(function(){b.push({id:e(this).attr("value"),text:e(this).text()})}),this.updateSelection(b);else{if(this.opts.initSelection===g)throw Error("val() cannot be called if initSelection() is not defined");this.opts.initSelection(this.opts.element,
|
77 |
+
function(a){var b=e(a).map(c.id);c.setVal(b);c.updateSelection(a);c.clearSearch()})}else this.opts.element.val(""),this.updateSelection([]);this.clearSearch()},onSortStart:function(){if(this.select)throw Error("Sorting of elements is not supported when attached to <select>. Attach to <input type='hidden'/> instead.");this.search.width(0);this.searchContainer.hide()},onSortEnd:function(){var a=[],b=this;this.searchContainer.show();this.searchContainer.appendTo(this.searchContainer.parent());this.resizeSearch();
|
78 |
+
this.selection.find(".select2-search-choice").each(function(){a.push(b.opts.id(e(this).data("select2-data")))});this.setVal(a);this.triggerChange()},data:function(a){var b=this,c;if(0===arguments.length)return this.selection.find(".select2-search-choice").map(function(){return e(this).data("select2-data")}).get();a||(a=[]);c=e.map(a,function(a){return b.opts.id(a)});this.setVal(c);this.updateSelection(a);this.clearSearch()}});e.fn.select2=function(){var a=Array.prototype.slice.call(arguments,0),b,
|
79 |
+
c,d,f,h="val destroy opened open close focus isFocused container onSortStart onSortEnd enable disable positionDropdown data".split(" ");this.each(function(){if(0===a.length||"object"===typeof a[0])b=0===a.length?{}:e.extend({},a[0]),b.element=e(this),"select"===b.element.get(0).tagName.toLowerCase()?f=b.element.attr("multiple"):(f=b.multiple||!1,"tags"in b&&(b.multiple=f=!0)),c=f?new z:new y,c.init(b);else if("string"===typeof a[0]){if(0>i(a[0],h))throw"Unknown method: "+a[0];d=g;c=e(this).data("select2");
|
80 |
+
if(c!==g&&(d="container"===a[0]?c.container:c[a[0]].apply(c,a.slice(1)),d!==g))return!1}else throw"Invalid arguments to select2 plugin: "+a;});return d===g?this:d};e.fn.select2.defaults={width:"copy",closeOnSelect:!0,openOnEnter:!0,containerCss:{},dropdownCss:{},containerCssClass:"",dropdownCssClass:"",formatResult:function(a,b,c){b=[];B(a.text,c.term,b);return b.join("")},formatSelection:function(a){return a?a.text:g},formatResultCssClass:function(){return g},formatNoMatches:function(){return"No matches found"},
|
81 |
+
formatInputTooShort:function(a,b){return"Please enter "+(b-a.length)+" more characters"},formatSelectionTooBig:function(a){return"You can only select "+a+" item"+(1==a?"":"s")},formatLoadMore:function(){return"Loading more results..."},formatSearching:function(){return"Searching..."},minimumResultsForSearch:0,minimumInputLength:0,maximumSelectionSize:0,id:function(a){return a.id},matcher:function(a,b){return 0<=b.toUpperCase().indexOf(a.toUpperCase())},separator:",",tokenSeparators:[],tokenizer:H,
|
82 |
+
escapeMarkup:function(a){return a&&"string"===typeof a?a.replace(/&/g,"&"):a},blurOnChange:!1};window.Select2={query:{ajax:C,local:D,tags:E},util:{debounce:A,markMatch:B},"class":{"abstract":w,single:y,multi:z}}}})(jQuery);
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,1214 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Co-Authors Guest Authors
|
4 |
+
*
|
5 |
+
* Key idea: Create guest authors to assign as bylines on a post without having
|
6 |
+
* to give them access to the dashboard through a WP_User account
|
7 |
+
*/
|
8 |
+
|
9 |
+
class CoAuthors_Guest_Authors
|
10 |
+
{
|
11 |
+
|
12 |
+
var $post_type = 'guest-author';
|
13 |
+
var $parent_page = 'users.php';
|
14 |
+
var $list_guest_authors_cap = 'list_users';
|
15 |
+
|
16 |
+
public static $cache_group = 'coauthors-plus-guest-authors';
|
17 |
+
|
18 |
+
/**
|
19 |
+
* Initialize our Guest Authors class and establish common hooks
|
20 |
+
*/
|
21 |
+
function __construct() {
|
22 |
+
global $coauthors_plus;
|
23 |
+
|
24 |
+
// Add the guest author management menu
|
25 |
+
add_action( 'admin_menu', array( $this, 'action_admin_menu' ) );
|
26 |
+
|
27 |
+
// WP List Table for breaking out our Guest Authors
|
28 |
+
require_once( dirname( __FILE__ ) . '/class-coauthors-wp-list-table.php' );
|
29 |
+
|
30 |
+
// Get a co-author based on a query
|
31 |
+
add_action( 'wp_ajax_search_coauthors_to_assign', array( $this, 'handle_ajax_search_coauthors_to_assign' ) );
|
32 |
+
|
33 |
+
// Any CSS or JS
|
34 |
+
add_action( 'admin_enqueue_scripts', array( $this, 'action_admin_enqueue_scripts' ) );
|
35 |
+
|
36 |
+
// Extra notices
|
37 |
+
add_action( 'admin_notices', array( $this, 'action_admin_notices' ) );
|
38 |
+
|
39 |
+
// Handle actions to create or delete guest author accounts
|
40 |
+
add_action( 'admin_init', array( $this, 'handle_create_guest_author_action' ) );
|
41 |
+
add_action( 'admin_init', array( $this, 'handle_delete_guest_author_action' ) );
|
42 |
+
|
43 |
+
// Redirect if the user is mapped to a guest author
|
44 |
+
add_action( 'parse_request', array( $this, 'action_parse_request' ) );
|
45 |
+
|
46 |
+
// Filter author links and such
|
47 |
+
add_filter( 'author_link', array( $this, 'filter_author_link' ), 10, 3 );
|
48 |
+
|
49 |
+
// Validate new guest authors
|
50 |
+
add_filter( 'wp_insert_post_empty_content', array( $this, 'filter_wp_insert_post_empty_content' ), 10, 2 );
|
51 |
+
|
52 |
+
// Add metaboxes for our guest author management interface
|
53 |
+
add_action( 'add_meta_boxes', array( $this, 'action_add_meta_boxes' ), 10, 2 );
|
54 |
+
add_action( 'wp_insert_post_data', array( $this, 'manage_guest_author_filter_post_data' ), 10, 2 );
|
55 |
+
add_action( 'save_post', array( $this, 'manage_guest_author_save_meta_fields' ), 10, 2 );
|
56 |
+
|
57 |
+
// Empty associated caches when the guest author profile is updated
|
58 |
+
add_filter( 'update_post_metadata', array( $this, 'filter_update_post_metadata' ), 10, 5 );
|
59 |
+
|
60 |
+
// Modify the messages that appear when saving or creating
|
61 |
+
add_filter( 'post_updated_messages', array( $this, 'filter_post_updated_messages' ) );
|
62 |
+
|
63 |
+
// Allow admins to create or edit guest author profiles from the Manage Users listing
|
64 |
+
add_filter( 'user_row_actions', array( $this, 'filter_user_row_actions' ), 10, 2 );
|
65 |
+
|
66 |
+
// Add support for featured thumbnails that we can use for guest author avatars
|
67 |
+
add_action( 'after_setup_theme', array( $this, 'action_after_setup_theme' ) );
|
68 |
+
add_filter( 'get_avatar', array( $this, 'filter_get_avatar' ),10 ,5 );
|
69 |
+
|
70 |
+
// Allow users to change where this is placed in the WordPress admin
|
71 |
+
$this->parent_page = apply_filters( 'coauthors_guest_author_parent_page', $this->parent_page );
|
72 |
+
|
73 |
+
// Allow users to change the required cap for modifying guest authors
|
74 |
+
$this->list_guest_authors_cap = apply_filters( 'coauthors_guest_author_manage_cap', $this->list_guest_authors_cap );
|
75 |
+
|
76 |
+
// Set up default labels, but allow themes to modify
|
77 |
+
$this->labels = apply_filters( 'coauthors_guest_author_labels', array(
|
78 |
+
'singular' => __( 'Guest Author', 'co-authors-plus' ),
|
79 |
+
'plural' => __( 'Guest Authors', 'co-authors-plus' ),
|
80 |
+
'all_items' => __( 'All Guest Authors', 'co-authors-plus' ),
|
81 |
+
'add_new_item' => __( 'Add New Guest Author', 'co-authors-plus' ),
|
82 |
+
'edit_item' => __( 'Edit Guest Author', 'co-authors-plus' ),
|
83 |
+
'new_item' => __( 'New Guest Author', 'co-authors-plus' ),
|
84 |
+
'view_item' => __( 'View Guest Author', 'co-authors-plus' ),
|
85 |
+
'search_items' => __( 'Search Guest Authors', 'co-authors-plus' ),
|
86 |
+
'not_found' => __( 'No guest authors found', 'co-authors-plus' ),
|
87 |
+
'not_found_in_trash' => __( 'No guest authors found in Trash', 'co-authors-plus' ),
|
88 |
+
'update_item' => __( 'Update Guest Author', 'co-authors-plus' ),
|
89 |
+
'metabox_about' => __( 'About the guest author', 'co-authors-plus' ),
|
90 |
+
) );
|
91 |
+
|
92 |
+
// Register a post type to store our authors that aren't WP.com users
|
93 |
+
$args = array(
|
94 |
+
'label' => $this->labels['singular'],
|
95 |
+
'labels' => array(
|
96 |
+
'name' => $this->labels['plural'],
|
97 |
+
'singular_name' => $this->labels['singular'],
|
98 |
+
'add_new' => _x( 'Add New', 'co-authors-plus' ),
|
99 |
+
'all_items' => $this->labels['all_items'],
|
100 |
+
'add_new_item' => $this->labels['add_new_item'],
|
101 |
+
'edit_item' => $this->labels['edit_item'],
|
102 |
+
'new_item' => $this->labels['new_item'],
|
103 |
+
'view_item' => $this->labels['view_item'],
|
104 |
+
'search_items' => $this->labels['search_items'],
|
105 |
+
'not_found' => $this->labels['not_found'],
|
106 |
+
'not_found_in_trash' => $this->labels['not_found_in_trash'],
|
107 |
+
),
|
108 |
+
'public' => true,
|
109 |
+
'publicly_queryable' => false,
|
110 |
+
'exclude_from_search' => true,
|
111 |
+
'show_in_menu' => false,
|
112 |
+
'supports' => array(
|
113 |
+
'thumbnail',
|
114 |
+
),
|
115 |
+
'taxonomies' => array(
|
116 |
+
$coauthors_plus->coauthor_taxonomy,
|
117 |
+
),
|
118 |
+
'rewrite' => false,
|
119 |
+
'query_var' => false,
|
120 |
+
);
|
121 |
+
register_post_type( $this->post_type, $args );
|
122 |
+
|
123 |
+
// Hacky way to remove the title and the editor
|
124 |
+
remove_post_type_support( $this->post_type, 'title' );
|
125 |
+
remove_post_type_support( $this->post_type, 'editor' );
|
126 |
+
|
127 |
+
}
|
128 |
+
|
129 |
+
/**
|
130 |
+
* Filter the messages that appear when saving or updating a guest author
|
131 |
+
*
|
132 |
+
* @since 3.0
|
133 |
+
*/
|
134 |
+
function filter_post_updated_messages( $messages ) {
|
135 |
+
global $post;
|
136 |
+
|
137 |
+
$guest_author = $this->get_guest_author_by( 'ID', $post->ID );
|
138 |
+
$guest_author_link = $this->filter_author_link( '', $guest_author->ID, $guest_author->user_nicename );
|
139 |
+
|
140 |
+
$messages[$this->post_type] = array(
|
141 |
+
0 => '', // Unused. Messages start at index 1.
|
142 |
+
1 => sprintf( __( 'Guest author updated. <a href="%s">View profile</a>', 'co-authors-plus' ), esc_url( $guest_author_link ) ),
|
143 |
+
2 => __( 'Custom field updated.', 'co-authors-plus' ),
|
144 |
+
3 => __( 'Custom field deleted.', 'co-authors-plus' ),
|
145 |
+
4 => __( 'Guest author updated.', 'co-authors-plus' ),
|
146 |
+
/* translators: %s: date and time of the revision */
|
147 |
+
5 => isset($_GET['revision']) ? sprintf( __( 'Guest author restored to revision from %s', 'co-authors-plus' ), wp_post_revision_title( (int) $_GET['revision'], false ) ) : false,
|
148 |
+
6 => sprintf( __( 'Guest author updated. <a href="%s">View profile</a>', 'co-authors-plus' ), esc_url( $guest_author_link ) ),
|
149 |
+
7 => __( 'Guest author saved.', 'co-authors-plus' ),
|
150 |
+
8 => sprintf( __( 'Guest author submitted. <a target="_blank" href="%s">Preview profile</a>', 'co-authors-plus' ), esc_url( add_query_arg( 'preview', 'true', $guest_author_link ) ) ),
|
151 |
+
9 => sprintf( __( 'Guest author scheduled for: <strong>%1$s</strong>. <a target="_blank" href="%2$s">Preview profile</a>', 'co-authors-plus' ),
|
152 |
+
// translators: Publish box date format, see http://php.net/date
|
153 |
+
date_i18n( __( 'M j, Y @ G:i' ), strtotime( $post->post_date ) ), esc_url( $guest_author_link ) ),
|
154 |
+
10 => sprintf( __('Guest author updated. <a target="_blank" href="%s">Preview profile</a>', 'co-authors-plus' ), esc_url( add_query_arg( 'preview', 'true', $guest_author_link ) ) ),
|
155 |
+
);
|
156 |
+
return $messages;
|
157 |
+
}
|
158 |
+
|
159 |
+
/**
|
160 |
+
* Handle the admin action to create a guest author based
|
161 |
+
* on an existing WordPress user
|
162 |
+
*
|
163 |
+
* @since 3.0
|
164 |
+
*/
|
165 |
+
function handle_create_guest_author_action() {
|
166 |
+
|
167 |
+
if ( !isset( $_GET['action'], $_GET['nonce'], $_GET['user_id'] ) || $_GET['action'] != 'cap-create-guest-author' )
|
168 |
+
return;
|
169 |
+
|
170 |
+
if ( !wp_verify_nonce( $_GET['nonce'], 'create-guest-author' ) )
|
171 |
+
wp_die( __( "Doin' something fishy, huh?", 'co-authors-plus' ) );
|
172 |
+
|
173 |
+
if ( ! current_user_can( $this->list_guest_authors_cap ) )
|
174 |
+
wp_die( __( "You don't have permission to perform this action.", 'co-authors-plus' ) );
|
175 |
+
|
176 |
+
$user_id = intval( $_GET['user_id'] );
|
177 |
+
|
178 |
+
// Create the guest author
|
179 |
+
$post_id = $this->create_guest_author_from_user_id( $user_id );
|
180 |
+
if ( is_wp_error( $post_id ) )
|
181 |
+
wp_die( $post_id->get_error_message() );
|
182 |
+
|
183 |
+
// Redirect to the edit Guest Author screen
|
184 |
+
$edit_link = get_edit_post_link( $post_id, 'redirect' );
|
185 |
+
$redirect_to = add_query_arg( 'message', 'guest-author-created', $edit_link );
|
186 |
+
wp_safe_redirect( $redirect_to );
|
187 |
+
exit;
|
188 |
+
|
189 |
+
}
|
190 |
+
|
191 |
+
/**
|
192 |
+
* Handle the admin action to delete a guest author and possibly reassign their posts
|
193 |
+
*
|
194 |
+
* @since 3.0
|
195 |
+
*/
|
196 |
+
function handle_delete_guest_author_action() {
|
197 |
+
global $coauthors_plus;
|
198 |
+
|
199 |
+
if ( !isset( $_POST['action'], $_POST['reassign'], $_POST['_wpnonce'], $_POST['id'] ) || 'delete-guest-author' != $_POST['action'] )
|
200 |
+
return;
|
201 |
+
|
202 |
+
// Verify the user is who they say they are
|
203 |
+
if ( !wp_verify_nonce( $_POST['_wpnonce'], 'delete-guest-author' ) )
|
204 |
+
wp_die( __( "Doin' something fishy, huh?", 'co-authors-plus' ) );
|
205 |
+
|
206 |
+
// Make sure they can perform the action
|
207 |
+
if ( ! current_user_can( $this->list_guest_authors_cap ) )
|
208 |
+
wp_die( __( "You don't have permission to perform this action.", 'co-authors-plus' ) );
|
209 |
+
|
210 |
+
// Make sure the guest author actually exists
|
211 |
+
$guest_author = $this->get_guest_author_by( 'ID', (int)$_POST['id'] );
|
212 |
+
if ( ! $guest_author )
|
213 |
+
wp_die( __( "Guest author can't be deleted because it doesn't exist.", 'co-authors-plus' ) );
|
214 |
+
|
215 |
+
// Perform the reassignment if needed
|
216 |
+
$guest_author_term = $coauthors_plus->get_author_term( $guest_author );
|
217 |
+
switch( $_POST['reassign'] ) {
|
218 |
+
// Leave assigned to the current linked account
|
219 |
+
case 'leave-assigned':
|
220 |
+
$reassign_to = $guest_author->linked_account;
|
221 |
+
break;
|
222 |
+
// Reassign to a different user
|
223 |
+
case 'reassign-another':
|
224 |
+
$user_nicename = sanitize_title( $_POST['leave-assigned-to'] );
|
225 |
+
$reassign_to = $coauthors_plus->get_coauthor_by( 'user_nicename', $user_nicename );
|
226 |
+
if ( ! $reassign_to )
|
227 |
+
wp_die( __( 'Co-author does not exists. Try again?', 'co-authors-plus' ) );
|
228 |
+
$reassign_to = $reassign_to->user_login;
|
229 |
+
break;
|
230 |
+
// Remove the byline, but don't delete the post
|
231 |
+
case 'remove-byline':
|
232 |
+
$reassign_to = false;
|
233 |
+
break;
|
234 |
+
default:
|
235 |
+
wp_die( __( "Please make sure to pick an option.", 'co-authors-plus' ) );
|
236 |
+
break;
|
237 |
+
}
|
238 |
+
|
239 |
+
$retval = $this->delete( $guest_author->ID, $reassign_to );
|
240 |
+
|
241 |
+
$args = array(
|
242 |
+
'page' => 'view-guest-authors',
|
243 |
+
);
|
244 |
+
if ( is_wp_error( $retval ) )
|
245 |
+
$args['message'] = 'delete-error';
|
246 |
+
else
|
247 |
+
$args['message'] = 'guest-author-deleted';
|
248 |
+
|
249 |
+
// Redirect to safety
|
250 |
+
$redirect_to = add_query_arg( $args, admin_url( $this->parent_page ) );
|
251 |
+
wp_safe_redirect( $redirect_to );
|
252 |
+
exit;
|
253 |
+
}
|
254 |
+
|
255 |
+
/**
|
256 |
+
* Given a search query, suggest some co-authors that might match it
|
257 |
+
*
|
258 |
+
* @since 3.0
|
259 |
+
*/
|
260 |
+
function handle_ajax_search_coauthors_to_assign() {
|
261 |
+
global $coauthors_plus;
|
262 |
+
|
263 |
+
if ( ! current_user_can( $this->list_guest_authors_cap ) )
|
264 |
+
die();
|
265 |
+
|
266 |
+
$search = sanitize_text_field( $_GET['q'] );
|
267 |
+
|
268 |
+
$results = wp_list_pluck( $coauthors_plus->search_authors( $search ), 'user_login' );
|
269 |
+
$retval = array();
|
270 |
+
foreach( $results as $user_login ) {
|
271 |
+
$coauthor = $coauthors_plus->get_coauthor_by( 'user_login', $user_login );
|
272 |
+
$retval[] = (object)array(
|
273 |
+
'display_name' => $coauthor->display_name,
|
274 |
+
'user_login' => $coauthor->user_login,
|
275 |
+
'id' => $coauthor->user_nicename,
|
276 |
+
);
|
277 |
+
}
|
278 |
+
echo json_encode( $retval );
|
279 |
+
die();
|
280 |
+
}
|
281 |
+
|
282 |
+
|
283 |
+
/**
|
284 |
+
* Some redirection we need to do for linked accounts
|
285 |
+
*
|
286 |
+
* @todo support author ID query vars
|
287 |
+
*/
|
288 |
+
function action_parse_request( $query ) {
|
289 |
+
|
290 |
+
if ( !isset( $query->query_vars['author_name'] ) )
|
291 |
+
return $query;
|
292 |
+
|
293 |
+
$coauthor = $this->get_guest_author_by( 'linked_account', sanitize_title( $query->query_vars['author_name'] ) );
|
294 |
+
if ( is_object( $coauthor ) && $query->query_vars['author_name'] != $coauthor->linked_account ) {
|
295 |
+
global $wp_rewrite;
|
296 |
+
$link = $wp_rewrite->get_author_permastruct();
|
297 |
+
|
298 |
+
if ( empty($link) ) {
|
299 |
+
$file = home_url( '/' );
|
300 |
+
$link = $file . '?author_name=' . $coauthor->user_login;
|
301 |
+
} else {
|
302 |
+
$link = str_replace('%author%', $coauthor->user_login, $link);
|
303 |
+
$link = home_url( user_trailingslashit( $link ) );
|
304 |
+
}
|
305 |
+
wp_safe_redirect( $link );
|
306 |
+
exit;
|
307 |
+
}
|
308 |
+
|
309 |
+
return $query;
|
310 |
+
}
|
311 |
+
|
312 |
+
/**
|
313 |
+
* Add the admin menus for seeing all co-authors
|
314 |
+
*
|
315 |
+
* @since 3.0
|
316 |
+
*/
|
317 |
+
function action_admin_menu() {
|
318 |
+
|
319 |
+
add_submenu_page( $this->parent_page, $this->labels['plural'], $this->labels['plural'], $this->list_guest_authors_cap, 'view-guest-authors', array( $this, 'view_guest_authors_list' ) );
|
320 |
+
|
321 |
+
}
|
322 |
+
|
323 |
+
/**
|
324 |
+
* Enqueue any scripts or styles used for Guest Authors
|
325 |
+
*
|
326 |
+
* @since 3.0
|
327 |
+
*/
|
328 |
+
function action_admin_enqueue_scripts() {
|
329 |
+
global $pagenow;
|
330 |
+
// Enqueue our guest author CSS on the related pages
|
331 |
+
if ( $this->parent_page == $pagenow && isset( $_GET['page'] ) && $_GET['page'] == 'view-guest-authors' ) {
|
332 |
+
wp_enqueue_script( 'jquery-select2', COAUTHORS_PLUS_URL . 'lib/select2/select2.min.js', array( 'jquery' ), COAUTHORS_PLUS_VERSION );
|
333 |
+
wp_enqueue_style( 'cap-jquery-select2-css', COAUTHORS_PLUS_URL . 'lib/select2/select2.css', false, COAUTHORS_PLUS_VERSION );
|
334 |
+
|
335 |
+
wp_enqueue_style( 'guest-authors-css', COAUTHORS_PLUS_URL . 'css/guest-authors.css', false, COAUTHORS_PLUS_VERSION );
|
336 |
+
wp_enqueue_script( 'guest-authors-js', COAUTHORS_PLUS_URL . 'js/guest-authors.js', false, COAUTHORS_PLUS_VERSION );
|
337 |
+
}
|
338 |
+
}
|
339 |
+
|
340 |
+
/**
|
341 |
+
* Show some extra notices to the user
|
342 |
+
*
|
343 |
+
* @since 3.0
|
344 |
+
*/
|
345 |
+
function action_admin_notices() {
|
346 |
+
global $pagenow;
|
347 |
+
|
348 |
+
if ( $this->parent_page != $pagenow || ! isset( $_REQUEST['message'] ) )
|
349 |
+
return;
|
350 |
+
|
351 |
+
switch( $_REQUEST['message'] ) {
|
352 |
+
case 'guest-author-deleted':
|
353 |
+
$message = __( 'Guest author deleted.', 'co-authors-plus' );
|
354 |
+
break;
|
355 |
+
default:
|
356 |
+
$message = false;
|
357 |
+
break;
|
358 |
+
}
|
359 |
+
|
360 |
+
if ( $message )
|
361 |
+
echo '<div class="updated"><p>' . esc_html( $message ) . '</p></div>';
|
362 |
+
|
363 |
+
}
|
364 |
+
|
365 |
+
/**
|
366 |
+
* Register the metaboxes used for Guest Authors
|
367 |
+
*
|
368 |
+
* @since 3.0
|
369 |
+
*/
|
370 |
+
function action_add_meta_boxes() {
|
371 |
+
global $coauthors_plus;
|
372 |
+
|
373 |
+
if ( get_post_type() == $this->post_type ) {
|
374 |
+
// Remove the submitpost metabox because we have our own
|
375 |
+
remove_meta_box( 'submitdiv', $this->post_type, 'side' );
|
376 |
+
remove_meta_box( 'slugdiv', $this->post_type, 'normal' );
|
377 |
+
add_meta_box( 'coauthors-manage-guest-author-save', __( 'Save', 'co-authors-plus'), array( $this, 'metabox_manage_guest_author_save' ), $this->post_type, 'side', 'default' );
|
378 |
+
add_meta_box( 'coauthors-manage-guest-author-slug', __( 'Unique Slug', 'co-authors-plus'), array( $this, 'metabox_manage_guest_author_slug' ), $this->post_type, 'side', 'default' );
|
379 |
+
// Our metaboxes with co-author details
|
380 |
+
add_meta_box( 'coauthors-manage-guest-author-name', __( 'Name', 'co-authors-plus'), array( $this, 'metabox_manage_guest_author_name' ), $this->post_type, 'normal', 'default' );
|
381 |
+
add_meta_box( 'coauthors-manage-guest-author-contact-info', __( 'Contact Info', 'co-authors-plus'), array( $this, 'metabox_manage_guest_author_contact_info' ), $this->post_type, 'normal', 'default' );
|
382 |
+
add_meta_box( 'coauthors-manage-guest-author-bio', $this->labels['metabox_about'], array( $this, 'metabox_manage_guest_author_bio' ), $this->post_type, 'normal', 'default' );
|
383 |
+
}
|
384 |
+
}
|
385 |
+
|
386 |
+
/**
|
387 |
+
* View a list table of all guest authors
|
388 |
+
*
|
389 |
+
* @since 3.0
|
390 |
+
*/
|
391 |
+
function view_guest_authors_list() {
|
392 |
+
|
393 |
+
// Allow guest authors to be deleted
|
394 |
+
if ( isset( $_GET['action'], $_GET['id'], $_GET['_wpnonce'] ) && 'delete' == $_GET['action'] ) {
|
395 |
+
// Make sure the user is who they say they are
|
396 |
+
if ( ! wp_verify_nonce( $_GET['_wpnonce'], 'guest-author-delete' ) )
|
397 |
+
wp_die( __( "Doin' something fishy, huh?", 'co-authors-plus' ) );
|
398 |
+
|
399 |
+
// Make sure the guest author actually exists
|
400 |
+
$guest_author = $this->get_guest_author_by( 'ID', (int)$_GET['id'] );
|
401 |
+
if ( ! $guest_author )
|
402 |
+
wp_die( __( "Guest author can't be deleted because it doesn't exist.", 'co-authors-plus' ) );
|
403 |
+
|
404 |
+
echo '<div class="wrap">';
|
405 |
+
echo '<div class="icon32" id="icon-users"><br/></div>';
|
406 |
+
echo '<h2>' . sprintf( __( 'Delete %s', 'co-authors-plus ' ), $this->labels['plural'] ) . '</h2>';
|
407 |
+
echo '<p>' . __( 'You have specified this guest author for deletion:', 'co-authors-plus' ) . '</p>';
|
408 |
+
echo '<p>#' . $guest_author->ID . ': ' . esc_html( $guest_author->display_name ) . '</p>';
|
409 |
+
echo '<p>' . __( "What should be done with posts assigned to this guest author?", 'co-authors-plus' ) . '</p>';
|
410 |
+
echo '<p class="description">' . __( "Note: If you'd like to delete the guest author and all of their posts, you should delete their posts first and then come back to delete the guest author.", 'co-authors-plus' ) . '</p>';
|
411 |
+
echo '<form method="POST" action="' . esc_url( add_query_arg( 'page', 'view-guest-authors', admin_url( $this->parent_page ) ) ) . '">';
|
412 |
+
// Hidden stuffs
|
413 |
+
echo '<input type="hidden" name="action" value="delete-guest-author" />';
|
414 |
+
wp_nonce_field( 'delete-guest-author' );
|
415 |
+
echo '<input type="hidden" name="id" value="' . esc_attr( (int)$_GET['id'] ) . '" />';
|
416 |
+
echo '<fieldset><ul style="list-style-type:none;">';
|
417 |
+
// Reassign to another user
|
418 |
+
echo '<li class="hide-if-no-js"><label for="reassign-another">';
|
419 |
+
echo '<input type="radio" id="reassign-another" name="reassign" class="reassign-option" value="reassign-another" /> ' . __( 'Reassign to another co-author:', 'co-authors-plus' ) . ' </label>';
|
420 |
+
echo '<input type="hidden" id="leave-assigned-to" name="leave-assigned-to" style="width:200px;" />';
|
421 |
+
echo '</li>';
|
422 |
+
// Leave mapped to a linked account
|
423 |
+
if ( get_user_by( 'login', $guest_author->linked_account ) ) {
|
424 |
+
echo '<li><label for="leave-assigned">';
|
425 |
+
echo '<input type="radio" id="leave-assigned" class="reassign-option" name="reassign" value="leave-assigned" /> ' . sprintf( __( 'Leave posts assigned to the mapped user, %s.', 'co-authors-plus' ), $guest_author->linked_account );
|
426 |
+
echo '</label></li>';
|
427 |
+
}
|
428 |
+
// Remove bylines from the posts
|
429 |
+
echo '<li><label for="remove-byline">';
|
430 |
+
echo '<input type="radio" id="remove-byline" class="reassign-option" name="reassign" value="remove-byline" /> ' . __( 'Remove byline from posts (but leave each post in its current status).', 'co-authors-plus' );
|
431 |
+
echo '</label></li>';
|
432 |
+
echo '</ul></fieldset>';
|
433 |
+
submit_button( __( 'Confirm Deletion', 'co-authors-plus' ), 'secondary', 'submit', true, array( 'disabled' => 'disabled' ) );
|
434 |
+
echo '</form>';
|
435 |
+
echo '</div>';
|
436 |
+
} else {
|
437 |
+
echo '<div class="wrap">';
|
438 |
+
echo '<div class="icon32" id="icon-users"><br/></div>';
|
439 |
+
echo '<h2>' . $this->labels['plural'];
|
440 |
+
// @todo caps check for creating a new user
|
441 |
+
$add_new_link = admin_url( "post-new.php?post_type=$this->post_type" );
|
442 |
+
echo '<a href="' . $add_new_link . '" class="add-new-h2">' . esc_html( __( 'Add New', 'co-authors-plus' ) ) . '</a>';
|
443 |
+
echo '</h2>';
|
444 |
+
$cap_list_table = new CoAuthors_WP_List_Table();
|
445 |
+
$cap_list_table->prepare_items();
|
446 |
+
echo '<form id="guest-authors-filter" action="" method="GET">';
|
447 |
+
echo '<input type="hidden" name="page" value="view-guest-authors" />';
|
448 |
+
$cap_list_table->display();
|
449 |
+
echo '</form>';
|
450 |
+
echo '</div>';
|
451 |
+
}
|
452 |
+
|
453 |
+
}
|
454 |
+
|
455 |
+
/**
|
456 |
+
* Metabox for saving or updating a Guest Author
|
457 |
+
*
|
458 |
+
* @since 3.0
|
459 |
+
*/
|
460 |
+
function metabox_manage_guest_author_save() {
|
461 |
+
global $post, $coauthors_plus;
|
462 |
+
|
463 |
+
if ( in_array( $post->post_status, array( 'pending', 'publish', 'draft' ) ) )
|
464 |
+
$button_text = $this->labels['update_item'];
|
465 |
+
else
|
466 |
+
$button_text = $this->labels['add_new_item'];
|
467 |
+
submit_button( $button_text, 'primary', 'publish', false );
|
468 |
+
|
469 |
+
// Secure all of our requests
|
470 |
+
wp_nonce_field( 'guest-author-nonce', 'guest-author-nonce' );
|
471 |
+
|
472 |
+
}
|
473 |
+
|
474 |
+
/**
|
475 |
+
* Metabox for editing this guest author's slug or changing the linked account
|
476 |
+
*
|
477 |
+
* @since 3.0
|
478 |
+
*/
|
479 |
+
function metabox_manage_guest_author_slug() {
|
480 |
+
global $post;
|
481 |
+
|
482 |
+
$pm_key = $this->get_post_meta_key( 'user_login' );
|
483 |
+
$existing_slug = get_post_meta( $post->ID, $pm_key, true );
|
484 |
+
|
485 |
+
echo '<input type="text" disabled="disabled" name="' . esc_attr( $pm_key ) . '" value="' . esc_attr( urldecode( $existing_slug ) ) . '" />';
|
486 |
+
|
487 |
+
// Taken from grist_authors.
|
488 |
+
$linked_account_key = $this->get_post_meta_key( 'linked_account' );
|
489 |
+
$linked_account = get_post_meta( $post->ID, $linked_account_key, true );
|
490 |
+
if ( $user = get_user_by( 'login', $linked_account ) )
|
491 |
+
$linked_account_id = $user->ID;
|
492 |
+
else
|
493 |
+
$linked_account_id = -1;
|
494 |
+
|
495 |
+
// If user_login is the same as linked account, don't let the association be removed
|
496 |
+
if ( $linked_account == $existing_slug )
|
497 |
+
add_filter( 'wp_dropdown_users', array( $this, 'filter_wp_dropdown_users_to_disable' ) );
|
498 |
+
|
499 |
+
$linked_account_user_ids = wp_list_pluck( $this->get_all_linked_accounts(), 'ID' );
|
500 |
+
if ( false !== ( $key = array_search( $linked_account_id, $linked_account_user_ids ) ) )
|
501 |
+
unset( $linked_account_user_ids[$key] );
|
502 |
+
|
503 |
+
echo '<p><label>' . __( 'WordPress User Mapping', 'co-authors-plus' ) . '</label> ';
|
504 |
+
wp_dropdown_users( array(
|
505 |
+
'show_option_none' => __( '-- Not mapped --', 'co-authors-plus' ),
|
506 |
+
'name' => esc_attr( $this->get_post_meta_key( 'linked_account' ) ),
|
507 |
+
// If we're adding an author or if there is no post author (0), then use -1 (which is show_option_none).
|
508 |
+
// We then take -1 on save and convert it back to 0. (#blamenacin)
|
509 |
+
'selected' => $linked_account_id,
|
510 |
+
// Don't let user accounts to be linked to more than one guest author
|
511 |
+
'exclude' => $linked_account_user_ids,
|
512 |
+
) );
|
513 |
+
echo '</p>';
|
514 |
+
|
515 |
+
remove_filter( 'wp_dropdown_users', array( $this, 'filter_wp_dropdown_users_to_disable' ) );
|
516 |
+
}
|
517 |
+
|
518 |
+
/**
|
519 |
+
* Make a wp_dropdown_users disabled
|
520 |
+
* Only applied if the user_login value for the guest author matches its linked account
|
521 |
+
*
|
522 |
+
* @since 3.0
|
523 |
+
*/
|
524 |
+
public function filter_wp_dropdown_users_to_disable( $output ) {
|
525 |
+
return str_replace( '<select ', '<select disabled="disabled" ', $output );
|
526 |
+
}
|
527 |
+
|
528 |
+
/**
|
529 |
+
* Metabox to display all of the pertient names for a Guest Author without a user account
|
530 |
+
*
|
531 |
+
* @since 3.0
|
532 |
+
*/
|
533 |
+
function metabox_manage_guest_author_name() {
|
534 |
+
global $post;
|
535 |
+
|
536 |
+
$fields = $this->get_guest_author_fields( 'name' );
|
537 |
+
echo '<table class="form-table"><tbody>';
|
538 |
+
foreach( $fields as $field ) {
|
539 |
+
$pm_key = $this->get_post_meta_key( $field['key'] );
|
540 |
+
$value = get_post_meta( $post->ID, $pm_key, true );
|
541 |
+
echo '<tr><th>';
|
542 |
+
echo '<label for="' . esc_attr( $pm_key ) . '">' . $field['label'] . '</label>';
|
543 |
+
echo '</th><td>';
|
544 |
+
echo '<input type="text" name="' . esc_attr( $pm_key ) . '" value="' . esc_attr( $value ) . '" class="regular-text" />';
|
545 |
+
echo '</td></tr>';
|
546 |
+
}
|
547 |
+
echo '</tbody></table>';
|
548 |
+
|
549 |
+
}
|
550 |
+
|
551 |
+
/**
|
552 |
+
* Metabox to display all of the pertient contact details for a Guest Author without a user account
|
553 |
+
*
|
554 |
+
* @since 3.0
|
555 |
+
*/
|
556 |
+
function metabox_manage_guest_author_contact_info() {
|
557 |
+
global $post;
|
558 |
+
|
559 |
+
$fields = $this->get_guest_author_fields( 'contact-info' );
|
560 |
+
echo '<table class="form-table"><tbody>';
|
561 |
+
foreach( $fields as $field ) {
|
562 |
+
$pm_key = $this->get_post_meta_key( $field['key'] );
|
563 |
+
$value = get_post_meta( $post->ID, $pm_key, true );
|
564 |
+
echo '<tr><th>';
|
565 |
+
echo '<label for="' . esc_attr( $pm_key ) . '">' . $field['label'] . '</label>';
|
566 |
+
echo '</th><td>';
|
567 |
+
echo '<input type="text" name="' . esc_attr( $pm_key ) . '" value="' . esc_attr( $value ) . '" class="regular-text" />';
|
568 |
+
echo '</td></tr>';
|
569 |
+
}
|
570 |
+
echo '</tbody></table>';
|
571 |
+
|
572 |
+
}
|
573 |
+
|
574 |
+
/**
|
575 |
+
* Metabox to edit the bio and other biographical details of the Guest Author
|
576 |
+
*
|
577 |
+
* @since 3.0
|
578 |
+
*/
|
579 |
+
function metabox_manage_guest_author_bio() {
|
580 |
+
global $post;
|
581 |
+
|
582 |
+
$fields = $this->get_guest_author_fields( 'about' );
|
583 |
+
echo '<table class="form-table"><tbody>';
|
584 |
+
foreach( $fields as $field ) {
|
585 |
+
$pm_key = $this->get_post_meta_key( $field['key'] );
|
586 |
+
$value = get_post_meta( $post->ID, $pm_key, true );
|
587 |
+
echo '<tr><th>';
|
588 |
+
echo '<label for="' . esc_attr( $pm_key ) . '">' . $field['label'] . '</label>';
|
589 |
+
echo '</th><td>';
|
590 |
+
echo '<textarea style="width:300px;margin-bottom:6px;" name="' . esc_attr( $pm_key ) . '">' . esc_textarea( $value ) . '</textarea>';
|
591 |
+
echo '</td></tr>';
|
592 |
+
}
|
593 |
+
echo '</tbody></table>';
|
594 |
+
|
595 |
+
}
|
596 |
+
|
597 |
+
/**
|
598 |
+
* When a guest author is created or updated, we need to properly create
|
599 |
+
* the post_name based on some data provided by the user
|
600 |
+
*
|
601 |
+
* @since 3.0
|
602 |
+
*/
|
603 |
+
function manage_guest_author_filter_post_data( $post_data, $original_args ) {
|
604 |
+
|
605 |
+
if ( $post_data['post_type'] != $this->post_type )
|
606 |
+
return $post_data;
|
607 |
+
|
608 |
+
// @todo caps check
|
609 |
+
if ( !isset( $_POST['guest-author-nonce'] ) || !wp_verify_nonce( $_POST['guest-author-nonce'], 'guest-author-nonce' ) )
|
610 |
+
return $post_data;
|
611 |
+
|
612 |
+
$post_data['post_title'] = sanitize_text_field( $_POST['cap-display_name'] );
|
613 |
+
$slug = sanitize_title( get_post_meta( $original_args['ID'], $this->get_post_meta_key( 'user_login' ), true ) );
|
614 |
+
if ( ! $slug )
|
615 |
+
$slug = sanitize_title( $_POST['cap-display_name'] );
|
616 |
+
// Uh oh, no guest authors without slugs
|
617 |
+
if ( ! $slug )
|
618 |
+
wp_die( __( 'Guest authors cannot be created without display names.', 'co-authors-plus' ) );
|
619 |
+
$post_data['post_name'] = $this->get_post_meta_key( $slug );
|
620 |
+
|
621 |
+
// Guest authors can't be created with the same user_login as a user
|
622 |
+
$user_nicename = str_replace( 'cap-', '', $slug );
|
623 |
+
$user = get_user_by( 'slug', $user_nicename );
|
624 |
+
if ( $user && $user->user_login != get_post_meta( $original_args['ID'], $this->get_post_meta_key( 'linked_account' ), true ) )
|
625 |
+
wp_die( __( 'Guest authors cannot be created with the same user_login value as a user. Try creating a profile from the user instead', 'co-authors-plus' ) );
|
626 |
+
|
627 |
+
// Guest authors can't have the same post_name value
|
628 |
+
$guest_author = $this->get_guest_author_by( 'post_name', $post_data['post_name'] );
|
629 |
+
if ( $guest_author && $guest_author->ID != $original_args['ID'] )
|
630 |
+
wp_die( __( 'Display name conflicts with another guest author display name.', 'co-authors-plus' ) );
|
631 |
+
|
632 |
+
return $post_data;
|
633 |
+
}
|
634 |
+
|
635 |
+
/**
|
636 |
+
* Save the various meta fields associated with our guest author model
|
637 |
+
*
|
638 |
+
* @since 3.0
|
639 |
+
*/
|
640 |
+
function manage_guest_author_save_meta_fields( $post_id, $post ) {
|
641 |
+
global $coauthors_plus;
|
642 |
+
|
643 |
+
if ( $post->post_type != $this->post_type )
|
644 |
+
return;
|
645 |
+
|
646 |
+
// @todo caps check
|
647 |
+
if ( !isset( $_POST['guest-author-nonce'] ) || !wp_verify_nonce( $_POST['guest-author-nonce'], 'guest-author-nonce' ) )
|
648 |
+
return;
|
649 |
+
|
650 |
+
// Save our data to post meta
|
651 |
+
$author_fields = $this->get_guest_author_fields();
|
652 |
+
foreach( $author_fields as $author_field ) {
|
653 |
+
|
654 |
+
$key = $this->get_post_meta_key( $author_field['key'] );
|
655 |
+
// 'user_login' should only be saved on post update if it doesn't exist
|
656 |
+
if ( 'user_login' == $author_field['key'] && !get_post_meta( $post_id, $key, true ) ) {
|
657 |
+
$display_name_key = $this->get_post_meta_key( 'display_name' );
|
658 |
+
$temp_slug = sanitize_title( $_POST[$display_name_key] );
|
659 |
+
update_post_meta( $post_id, $key, $temp_slug );
|
660 |
+
continue;
|
661 |
+
}
|
662 |
+
if ( 'linked_account' == $author_field['key'] ) {
|
663 |
+
$linked_account_key = $this->get_post_meta_key( 'linked_account' );
|
664 |
+
if ( ! empty( $_POST[$linked_account_key] ) )
|
665 |
+
$user_id = intval( $_POST[$linked_account_key] );
|
666 |
+
else
|
667 |
+
continue;
|
668 |
+
$user = get_user_by( 'id', $user_id );
|
669 |
+
if ( $user_id > 0 && is_object( $user ) )
|
670 |
+
$user_login = $user->user_login;
|
671 |
+
else
|
672 |
+
$user_login = '';
|
673 |
+
update_post_meta( $post_id, $key, $user_login );
|
674 |
+
continue;
|
675 |
+
}
|
676 |
+
if ( !isset( $_POST[$key] ) )
|
677 |
+
continue;
|
678 |
+
if ( isset( $author_field['sanitize_function'] ) && function_exists( $author_field['sanitize_function'] ) )
|
679 |
+
$value = $author_field['sanitize_function']( $_POST[$key] );
|
680 |
+
else
|
681 |
+
$value = sanitize_text_field( $_POST[$key] );
|
682 |
+
update_post_meta( $post_id, $key, $value );
|
683 |
+
}
|
684 |
+
|
685 |
+
$author = $this->get_guest_author_by( 'ID', $post_id );
|
686 |
+
$author_term = $coauthors_plus->update_author_term( $author );
|
687 |
+
// Add the author as a post term
|
688 |
+
wp_set_post_terms( $post_id, array( $author_term->slug ), $coauthors_plus->coauthor_taxonomy, false );
|
689 |
+
}
|
690 |
+
|
691 |
+
/**
|
692 |
+
* Return a simulated WP_User object based on the post ID
|
693 |
+
* of a guest author
|
694 |
+
*
|
695 |
+
* @since 3.0
|
696 |
+
*
|
697 |
+
* @param string $key Key to search by (login,email)
|
698 |
+
* @param string $value Value to search for
|
699 |
+
* @param object|false $coauthor The guest author on success, false on failure
|
700 |
+
*/
|
701 |
+
function get_guest_author_by( $key, $value ) {
|
702 |
+
global $wpdb;
|
703 |
+
|
704 |
+
$cache_key = md5( 'guest-author-' . $key . '-' . $value );
|
705 |
+
if ( false !== ( $retval = wp_cache_get( $cache_key, self::$cache_group ) ) )
|
706 |
+
return $retval;
|
707 |
+
|
708 |
+
switch( $key ) {
|
709 |
+
case 'ID':
|
710 |
+
case 'id':
|
711 |
+
$query = $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE ID=%d", $value );
|
712 |
+
$post_id = $wpdb->get_var( $query );
|
713 |
+
if ( empty( $post_id ) )
|
714 |
+
return false;
|
715 |
+
break;
|
716 |
+
case 'user_nicename':
|
717 |
+
case 'post_name':
|
718 |
+
$value = $this->get_post_meta_key( $value );
|
719 |
+
$query = $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_name=%s AND post_type = %s", $value, $this->post_type );
|
720 |
+
$post_id = $wpdb->get_var( $query );
|
721 |
+
if ( empty( $post_id ) )
|
722 |
+
return false;
|
723 |
+
break;
|
724 |
+
case 'login':
|
725 |
+
case 'user_login':
|
726 |
+
case 'linked_account':
|
727 |
+
if ( 'login' == $key )
|
728 |
+
$key = 'user_login';
|
729 |
+
// Ensure we aren't doing the lookup by the prefixed value
|
730 |
+
if ( 'user_login' == $key )
|
731 |
+
$value = preg_replace( '#^cap\-#', '', $value );
|
732 |
+
$query = $wpdb->prepare( "SELECT post_id FROM $wpdb->postmeta WHERE meta_key=%s AND meta_value=%s;", $this->get_post_meta_key( $key ), $value );
|
733 |
+
$post_id = $wpdb->get_var( $query );
|
734 |
+
if ( empty( $post_id ) ) {
|
735 |
+
if ( 'user_login' == $key )
|
736 |
+
return $this->get_guest_author_by( 'post_name', $value ); // fallback to post_name in case the guest author isn't a linked account
|
737 |
+
return false;
|
738 |
+
}
|
739 |
+
break;
|
740 |
+
default:
|
741 |
+
$post_id = false;
|
742 |
+
break;
|
743 |
+
}
|
744 |
+
|
745 |
+
if ( !$post_id )
|
746 |
+
return false;
|
747 |
+
|
748 |
+
$guest_author = array(
|
749 |
+
'ID' => $post_id,
|
750 |
+
);
|
751 |
+
|
752 |
+
// Load the guest author fields
|
753 |
+
$fields = $this->get_guest_author_fields();
|
754 |
+
foreach( $fields as $field ) {
|
755 |
+
$key = $field['key'];
|
756 |
+
$pm_key = $this->get_post_meta_key( $field['key'] );
|
757 |
+
$guest_author[$key] = get_post_meta( $post_id, $pm_key, true );
|
758 |
+
}
|
759 |
+
// Support for non-Latin characters. They're stored as urlencoded slugs
|
760 |
+
$guest_author['user_login'] = urldecode( $guest_author['user_login'] );
|
761 |
+
|
762 |
+
// Hack to model the WP_User object
|
763 |
+
$guest_author['user_nicename'] = sanitize_title( $guest_author['user_login'] );
|
764 |
+
$guest_author['type'] = 'guest-author';
|
765 |
+
|
766 |
+
wp_cache_set( $cache_key, (object)$guest_author, self::$cache_group );
|
767 |
+
|
768 |
+
return (object)$guest_author;
|
769 |
+
}
|
770 |
+
|
771 |
+
/**
|
772 |
+
* Get all of the meta fields that can be associated with a guest author
|
773 |
+
*
|
774 |
+
* @since 3.0
|
775 |
+
*/
|
776 |
+
function get_guest_author_fields( $groups = 'all' ) {
|
777 |
+
|
778 |
+
$groups = (array)$groups;
|
779 |
+
$global_fields = array(
|
780 |
+
// Hidden (included in object, no UI elements)
|
781 |
+
array(
|
782 |
+
'key' => 'ID',
|
783 |
+
'label' => __( 'ID', 'co-authors-plus' ),
|
784 |
+
'group' => 'hidden',
|
785 |
+
),
|
786 |
+
// Name
|
787 |
+
array(
|
788 |
+
'key' => 'display_name',
|
789 |
+
'label' => __( 'Display Name', 'co-authors-plus'),
|
790 |
+
'group' => 'name',
|
791 |
+
'required' => true,
|
792 |
+
),
|
793 |
+
array(
|
794 |
+
'key' => 'first_name',
|
795 |
+
'label' => __( 'First Name', 'co-authors-plus'),
|
796 |
+
'group' => 'name',
|
797 |
+
),
|
798 |
+
array(
|
799 |
+
'key' => 'last_name',
|
800 |
+
'label' => __( 'Last Name', 'co-authors-plus'),
|
801 |
+
'group' => 'name',
|
802 |
+
),
|
803 |
+
array(
|
804 |
+
'key' => 'user_login',
|
805 |
+
'label' => __( 'Slug', 'co-authors-plus'),
|
806 |
+
'group' => 'slug',
|
807 |
+
'required' => true,
|
808 |
+
),
|
809 |
+
// Contact info
|
810 |
+
array(
|
811 |
+
'key' => 'user_email',
|
812 |
+
'label' => __( 'E-mail', 'co-authors-plus' ),
|
813 |
+
'group' => 'contact-info',
|
814 |
+
),
|
815 |
+
array(
|
816 |
+
'key' => 'linked_account',
|
817 |
+
'label' => __( 'Linked Account', 'co-authors-plus' ),
|
818 |
+
'group' => 'slug',
|
819 |
+
),
|
820 |
+
array(
|
821 |
+
'key' => 'website',
|
822 |
+
'label' => __( 'Website', 'co-authors-plus' ),
|
823 |
+
'group' => 'contact-info',
|
824 |
+
),
|
825 |
+
array(
|
826 |
+
'key' => 'aim',
|
827 |
+
'label' => __( 'AIM', 'co-authors-plus' ),
|
828 |
+
'group' => 'contact-info',
|
829 |
+
),
|
830 |
+
array(
|
831 |
+
'key' => 'yahooim',
|
832 |
+
'label' => __( 'Yahoo IM', 'co-authors-plus' ),
|
833 |
+
'group' => 'contact-info',
|
834 |
+
),
|
835 |
+
array(
|
836 |
+
'key' => 'jabber',
|
837 |
+
'label' => __( 'Jabber / Google Talk', 'co-authors-plus' ),
|
838 |
+
'group' => 'contact-info',
|
839 |
+
),
|
840 |
+
array(
|
841 |
+
'key' => 'description',
|
842 |
+
'label' => __( 'Biographical Info', 'co-authors-plus' ),
|
843 |
+
'group' => 'about',
|
844 |
+
'sanitize_function' => 'wp_filter_post_kses',
|
845 |
+
),
|
846 |
+
);
|
847 |
+
$fields_to_return = array();
|
848 |
+
foreach( $global_fields as $single_field ) {
|
849 |
+
if ( in_array( $single_field['group'], $groups ) || $groups[0] == 'all' && $single_field['group'] != 'hidden' )
|
850 |
+
$fields_to_return[] = $single_field;
|
851 |
+
}
|
852 |
+
|
853 |
+
return apply_filters( 'coauthors_guest_author_fields', $fields_to_return, $groups );
|
854 |
+
|
855 |
+
}
|
856 |
+
|
857 |
+
/**
|
858 |
+
* Gets a postmeta key by prefixing it with 'cap-'
|
859 |
+
* if not yet prefixed
|
860 |
+
*
|
861 |
+
* @since 3.0
|
862 |
+
*/
|
863 |
+
function get_post_meta_key( $key ) {
|
864 |
+
|
865 |
+
if ( 0 !== stripos( $key, 'cap-' ) )
|
866 |
+
$key = 'cap-' . $key;
|
867 |
+
|
868 |
+
return $key;
|
869 |
+
}
|
870 |
+
|
871 |
+
/**
|
872 |
+
* Get all of the user accounts that have been linked
|
873 |
+
*
|
874 |
+
* @since 3.0
|
875 |
+
*/
|
876 |
+
function get_all_linked_accounts( $force = false ) {
|
877 |
+
global $wpdb;
|
878 |
+
|
879 |
+
$cache_key = 'all-linked-accounts';
|
880 |
+
$retval = wp_cache_get( $cache_key, self::$cache_group );
|
881 |
+
|
882 |
+
if ( true === $force || false === $retval ) {
|
883 |
+
$user_logins = $wpdb->get_col( $wpdb->prepare( "SELECT meta_value FROM $wpdb->postmeta WHERE meta_key=%s AND meta_value !=''", $this->get_post_meta_key( 'linked_account' ) ) );
|
884 |
+
$users = array();
|
885 |
+
foreach( $user_logins as $user_login ) {
|
886 |
+
$user = get_user_by( 'login', $user_login );
|
887 |
+
if ( ! $user )
|
888 |
+
continue;
|
889 |
+
$users[] = array(
|
890 |
+
'ID' => $user->ID,
|
891 |
+
'user_login' => $user->user_login,
|
892 |
+
);
|
893 |
+
}
|
894 |
+
$retval = $users;
|
895 |
+
wp_cache_set( $cache_key, $retval, self::$cache_group );
|
896 |
+
}
|
897 |
+
return ( $retval ) ? $retval : array();
|
898 |
+
}
|
899 |
+
|
900 |
+
/**
|
901 |
+
* Filter update post metadata
|
902 |
+
* Clean caches when any of the values have been changed
|
903 |
+
*
|
904 |
+
* @since 3.0
|
905 |
+
*/
|
906 |
+
function filter_update_post_metadata( $retnull, $object_id, $meta_key, $meta_value, $prev_value ) {
|
907 |
+
|
908 |
+
if ( $this->post_type != get_post_type( $object_id ) )
|
909 |
+
return null;
|
910 |
+
|
911 |
+
// If the linked_account is changing, invalidate the cache of all linked accounts
|
912 |
+
// Don't regenerate though, as we haven't saved the new value
|
913 |
+
$linked_account_key = $this->get_post_meta_key( 'linked_account' );
|
914 |
+
if ( $linked_account_key == $meta_key && $meta_value != get_post_meta( $object_id, $linked_account_key, true ) ) {
|
915 |
+
$this->delete_guest_author_cache( $object_id );
|
916 |
+
}
|
917 |
+
|
918 |
+
// If one of the guest author meta values has changed, we'll need to invalidate all keys
|
919 |
+
if ( false !== strpos( $meta_key, 'cap-' ) && $meta_value != get_post_meta( $object_id, $meta_key, true ) ) {
|
920 |
+
$this->delete_guest_author_cache( $object_id );
|
921 |
+
}
|
922 |
+
|
923 |
+
return null;
|
924 |
+
}
|
925 |
+
|
926 |
+
/**
|
927 |
+
* Delete all of the cache values associated with a guest author
|
928 |
+
*
|
929 |
+
* @since 3.0
|
930 |
+
*
|
931 |
+
* @param int|object $guest_author The guest author ID or object
|
932 |
+
*/
|
933 |
+
public function delete_guest_author_cache( $id_or_object ) {
|
934 |
+
|
935 |
+
if ( is_object( $id_or_object ) )
|
936 |
+
$guest_author = $id_or_object;
|
937 |
+
else
|
938 |
+
$guest_author = $this->get_guest_author_by( 'ID', $id_or_object );
|
939 |
+
|
940 |
+
// Delete the lookup cache associated with each old co-author value
|
941 |
+
$keys = wp_list_pluck( $this->get_guest_author_fields(), 'key' );
|
942 |
+
$keys = array_merge( $keys, array( 'login', 'post_name', 'user_nicename', 'ID' ) );
|
943 |
+
foreach( $keys as $key ) {
|
944 |
+
if ( 'post_name' == $key )
|
945 |
+
$key = 'user_nicename';
|
946 |
+
else if ( 'login' == $key )
|
947 |
+
$key = 'user_login';
|
948 |
+
$cache_key = md5( 'guest-author-' . $key . '-' . $guest_author->$key );
|
949 |
+
wp_cache_delete( $cache_key, self::$cache_group );
|
950 |
+
}
|
951 |
+
|
952 |
+
// Delete the 'all-linked-accounts' cache
|
953 |
+
wp_cache_delete( 'all-linked-accounts', self::$cache_group );
|
954 |
+
|
955 |
+
}
|
956 |
+
|
957 |
+
|
958 |
+
/**
|
959 |
+
* Create a guest author
|
960 |
+
*
|
961 |
+
* @since 3.0
|
962 |
+
*/
|
963 |
+
function create( $args ) {
|
964 |
+
global $coauthors_plus;
|
965 |
+
|
966 |
+
// Validate the arguments that have been passed
|
967 |
+
$fields = $this->get_guest_author_fields();
|
968 |
+
foreach( $fields as $field ) {
|
969 |
+
|
970 |
+
// Make sure required fields are there
|
971 |
+
if ( isset( $field['required'] ) && $field['required'] && empty( $args[$field['key']] ) ) {
|
972 |
+
return new WP_Error( 'field-required', sprintf( __( '%s is a required field', 'co-authors-plus' ), $field['key'] ) );
|
973 |
+
}
|
974 |
+
|
975 |
+
// The user login field shouldn't collide with any existing users
|
976 |
+
if ( 'user_login' == $field['key'] && $existing_coauthor = $coauthors_plus->get_coauthor_by( 'user_login', $args['user_login'] ) ) {
|
977 |
+
if ( 'guest-author' == $existing_coauthor->type )
|
978 |
+
return new WP_Error( 'duplicate-field', __( 'user_login cannot duplicate existing guest author or mapped user', 'co-authors-plus' ) );
|
979 |
+
}
|
980 |
+
|
981 |
+
}
|
982 |
+
|
983 |
+
// Create the primary post object
|
984 |
+
$new_post = array(
|
985 |
+
'post_title' => $args['display_name'],
|
986 |
+
'post_name' => sanitize_title( $this->get_post_meta_key( $args['user_login'] ) ),
|
987 |
+
'post_type' => $this->post_type,
|
988 |
+
);
|
989 |
+
$post_id = wp_insert_post( $new_post, true );
|
990 |
+
if ( is_wp_error( $post_id ) )
|
991 |
+
return $post_id;
|
992 |
+
|
993 |
+
// Add all of the fields for the new guest author
|
994 |
+
foreach( $fields as $field ) {
|
995 |
+
$key = $field['key'];
|
996 |
+
if ( empty( $args[$key] ) )
|
997 |
+
continue;
|
998 |
+
$pm_key = $this->get_post_meta_key( $key );
|
999 |
+
update_post_meta( $post_id, $pm_key, $args[$key] );
|
1000 |
+
}
|
1001 |
+
|
1002 |
+
// Make sure the author term exists and that we're assigning it to this post type
|
1003 |
+
$author_term = $coauthors_plus->update_author_term( $this->get_guest_author_by( 'ID', $post_id ) );
|
1004 |
+
wp_set_post_terms( $post_id, array( $author_term->slug ), $coauthors_plus->coauthor_taxonomy, false );
|
1005 |
+
|
1006 |
+
return $post_id;
|
1007 |
+
}
|
1008 |
+
|
1009 |
+
/**
|
1010 |
+
* Delete a guest author
|
1011 |
+
*
|
1012 |
+
* @since 3.0
|
1013 |
+
*
|
1014 |
+
* @param int $post_id The ID for the guest author profile
|
1015 |
+
* @param string $reassign_to User login value for the co-author to reassign posts to
|
1016 |
+
* @return bool|WP_Error $success True on success, WP_Error on a failure
|
1017 |
+
*/
|
1018 |
+
public function delete( $id, $reassign_to = false ) {
|
1019 |
+
global $coauthors_plus;
|
1020 |
+
|
1021 |
+
$guest_author = $this->get_guest_author_by( 'ID', $id );
|
1022 |
+
if ( ! $guest_author )
|
1023 |
+
return new WP_Error( 'guest-author-missing', __( 'Guest author does not exist', 'co-authors-plus' ) );
|
1024 |
+
|
1025 |
+
$guest_author_term = $coauthors_plus->get_author_term( $guest_author );
|
1026 |
+
|
1027 |
+
if ( $reassign_to ) {
|
1028 |
+
|
1029 |
+
// We're reassigning the guest author's posts user to its linked account
|
1030 |
+
if ( $guest_author->linked_account == $reassign_to )
|
1031 |
+
$reassign_to_author = get_user_by( 'login', $reassign_to );
|
1032 |
+
else
|
1033 |
+
$reassign_to_author = $coauthors_plus->get_coauthor_by( 'user_login', $reassign_to );
|
1034 |
+
if ( ! $reassign_to_author )
|
1035 |
+
return new WP_Error( 'reassign-to-missing', __( 'Reassignment co-author does not exist', 'co-authors-plus' ) );
|
1036 |
+
|
1037 |
+
$reassign_to_term = $coauthors_plus->get_author_term( $reassign_to_author );
|
1038 |
+
// In the case where the guest author and its linked account shared the same term, we don't want to reassign
|
1039 |
+
if ( $guest_author_term->term_id != $reassign_to_term->term_id )
|
1040 |
+
wp_delete_term( $guest_author_term->term_id, $coauthors_plus->coauthor_taxonomy, array( 'default' => $reassign_to_term->term_id, 'force_default' => true ) );
|
1041 |
+
|
1042 |
+
} else {
|
1043 |
+
wp_delete_term( $guest_author_term->term_id, $coauthors_plus->coauthor_taxonomy );
|
1044 |
+
}
|
1045 |
+
|
1046 |
+
// Delete the guest author profile
|
1047 |
+
wp_delete_post( $guest_author->ID, true );
|
1048 |
+
|
1049 |
+
// Make sure all of the caches are reset
|
1050 |
+
$this->delete_guest_author_cache( $guest_author );
|
1051 |
+
return true;
|
1052 |
+
}
|
1053 |
+
|
1054 |
+
|
1055 |
+
/**
|
1056 |
+
* Create a guest author from an existing WordPress user
|
1057 |
+
*
|
1058 |
+
* @since 3.0
|
1059 |
+
*
|
1060 |
+
* @param int $user_id ID for a WordPress user
|
1061 |
+
* @return int|WP_Error $retval ID for the new guest author on success, WP_Error on failure
|
1062 |
+
*/
|
1063 |
+
function create_guest_author_from_user_id( $user_id ) {
|
1064 |
+
|
1065 |
+
$user = get_user_by( 'id', $user_id );
|
1066 |
+
if ( ! $user )
|
1067 |
+
return new WP_Error( 'invalid-user', __( 'No user exists with that ID', 'co-authors-plus' ) );
|
1068 |
+
|
1069 |
+
$guest_author = array();
|
1070 |
+
foreach( $this->get_guest_author_fields() as $field ) {
|
1071 |
+
$key = $field['key'];
|
1072 |
+
if ( ! empty( $user->$key ) )
|
1073 |
+
$guest_author[$key] = $user->$key;
|
1074 |
+
else
|
1075 |
+
$guest_author[$key] = '';
|
1076 |
+
}
|
1077 |
+
// Don't need the old user ID
|
1078 |
+
unset( $guest_author['ID'] );
|
1079 |
+
// Retain the user mapping and try to produce an unique user_login based on the name.
|
1080 |
+
$guest_author['linked_account'] = $guest_author['user_login'];
|
1081 |
+
if ( ! empty( $guest_author['display_name'] ) && $guest_author['display_name'] != $guest_author['user_login'] )
|
1082 |
+
$guest_author['user_login'] = sanitize_title( $guest_author['display_name'] );
|
1083 |
+
else if ( ! empty( $guest_author['first_name'] ) && ! empty( $guest_author['last_name'] ) )
|
1084 |
+
$guest_author['user_login'] = sanitize_title( $guest_author['first_name'] . ' ' . $guest_author['last_name'] );
|
1085 |
+
|
1086 |
+
$retval = $this->create( $guest_author );
|
1087 |
+
return $retval;
|
1088 |
+
}
|
1089 |
+
|
1090 |
+
/**
|
1091 |
+
* Guest authors must have Display Names
|
1092 |
+
*
|
1093 |
+
* @since 3.0
|
1094 |
+
*/
|
1095 |
+
function filter_wp_insert_post_empty_content( $maybe_empty, $postarr ) {
|
1096 |
+
|
1097 |
+
if ( $this->post_type != $postarr['post_type'] )
|
1098 |
+
return $maybe_empty;
|
1099 |
+
|
1100 |
+
if ( empty( $postarr['post_title'] ) )
|
1101 |
+
return true;
|
1102 |
+
|
1103 |
+
return $maybe_empty;
|
1104 |
+
}
|
1105 |
+
|
1106 |
+
/**
|
1107 |
+
* On the User Management view, add action links to create or edit
|
1108 |
+
* guest author profiles
|
1109 |
+
*
|
1110 |
+
* @since 3.0
|
1111 |
+
*
|
1112 |
+
* @param array $actions The existing actions to perform on a user
|
1113 |
+
* @param object $user_object A WP_User object
|
1114 |
+
* @return array $actions Modified actions
|
1115 |
+
*/
|
1116 |
+
function filter_user_row_actions( $actions, $user_object ) {
|
1117 |
+
|
1118 |
+
if ( ! current_user_can( $this->list_guest_authors_cap ) )
|
1119 |
+
return $actions;
|
1120 |
+
|
1121 |
+
$new_actions = array();
|
1122 |
+
if ( $guest_author = $this->get_guest_author_by( 'linked_account', $user_object->user_login ) ) {
|
1123 |
+
$edit_guest_author_link = get_edit_post_link( $guest_author->ID );
|
1124 |
+
$new_actions['edit-guest-author'] = '<a href="' . esc_url( $edit_guest_author_link ) . '">' . __( 'Edit Profile', 'co-authors-plus' ) . '</a>';
|
1125 |
+
} else {
|
1126 |
+
$query_args = array(
|
1127 |
+
'action' => 'cap-create-guest-author',
|
1128 |
+
'user_id' => $user_object->ID,
|
1129 |
+
'nonce' => wp_create_nonce( 'create-guest-author' ),
|
1130 |
+
);
|
1131 |
+
$create_guest_author_link = add_query_arg( $query_args, admin_url( $this->parent_page ) );
|
1132 |
+
$new_actions['create-guest-author'] = '<a href="' . esc_url( $create_guest_author_link ) . '">' . __( 'Create Profile', 'co-authors-plus' ) . '</a>';
|
1133 |
+
}
|
1134 |
+
|
1135 |
+
return $new_actions + $actions;
|
1136 |
+
}
|
1137 |
+
|
1138 |
+
/**
|
1139 |
+
* Anything to do after the theme has been set up
|
1140 |
+
*
|
1141 |
+
* @since 3.0
|
1142 |
+
*/
|
1143 |
+
function action_after_setup_theme() {
|
1144 |
+
add_theme_support( 'post-thumbnails', array( $this->post_type ) );
|
1145 |
+
|
1146 |
+
// Some of the common sizes used by get_avatar
|
1147 |
+
$this->avatar_sizes = array(
|
1148 |
+
32,
|
1149 |
+
64,
|
1150 |
+
96,
|
1151 |
+
128
|
1152 |
+
);
|
1153 |
+
$this->avatar_sizes = apply_filters( 'coauthors_guest_author_avatar_sizes', $this->avatar_sizes );
|
1154 |
+
foreach( $this->avatar_sizes as $size ) {
|
1155 |
+
add_image_size( 'guest-author-' . $size, $size, $size, true );
|
1156 |
+
}
|
1157 |
+
}
|
1158 |
+
|
1159 |
+
/**
|
1160 |
+
* Filter 'get_avatar' to replace with our own avatar if one exists
|
1161 |
+
*
|
1162 |
+
* @since 3.0
|
1163 |
+
*/
|
1164 |
+
function filter_get_avatar( $avatar, $id_or_email, $size, $default ) {
|
1165 |
+
|
1166 |
+
if ( is_object( $id_or_email ) || !is_email( $id_or_email ) )
|
1167 |
+
return $avatar;
|
1168 |
+
|
1169 |
+
// See if this matches a guest author
|
1170 |
+
$guest_author = $this->get_guest_author_by( 'user_email', $id_or_email );
|
1171 |
+
if ( ! $guest_author )
|
1172 |
+
return $avatar;
|
1173 |
+
|
1174 |
+
// See if the guest author as an avatar
|
1175 |
+
if ( ! has_post_thumbnail( $guest_author->ID ) )
|
1176 |
+
return $avatar;
|
1177 |
+
|
1178 |
+
$args = array(
|
1179 |
+
'class' => "avatar avatar-{$size} photo",
|
1180 |
+
);
|
1181 |
+
if ( in_array( $size, $this->avatar_sizes ) )
|
1182 |
+
$size = 'guest-author-' . $size;
|
1183 |
+
else
|
1184 |
+
$size = array( $size, $size );
|
1185 |
+
$avatar = get_the_post_thumbnail( $guest_author->ID, $size, $args );
|
1186 |
+
|
1187 |
+
return $avatar;
|
1188 |
+
}
|
1189 |
+
|
1190 |
+
/**
|
1191 |
+
* Filter the URL used in functions like the_author_posts_link()
|
1192 |
+
*
|
1193 |
+
* @since 3.0
|
1194 |
+
*/
|
1195 |
+
function filter_author_link( $link, $author_id, $author_nicename ) {
|
1196 |
+
|
1197 |
+
// If we're using this at the top of the loop on author.php,
|
1198 |
+
// our queried object should be set correctly
|
1199 |
+
if ( !$author_nicename && is_author() && get_queried_object() )
|
1200 |
+
$author_nicename = get_queried_object()->user_nicename;
|
1201 |
+
|
1202 |
+
if ( empty($link) ) {
|
1203 |
+
$link = add_query_arg( 'author_name', $author_nicename, home_url() );
|
1204 |
+
} else {
|
1205 |
+
global $wp_rewrite;
|
1206 |
+
$link = $wp_rewrite->get_author_permastruct();
|
1207 |
+
$link = str_replace('%author%', $author_nicename, $link);
|
1208 |
+
$link = home_url( user_trailingslashit( $link ) );
|
1209 |
+
}
|
1210 |
+
return $link;
|
1211 |
+
|
1212 |
+
}
|
1213 |
+
|
1214 |
+
}
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* For themes where it's easily doable, add support for Co-Authors Plus on the frontend
|
4 |
+
* by filtering the common template tags
|
5 |
+
*/
|
6 |
+
|
7 |
+
class CoAuthors_Template_Filters {
|
8 |
+
|
9 |
+
function __construct() {
|
10 |
+
add_filter( 'the_author', array( $this, 'filter_the_author' ) );
|
11 |
+
add_filter( 'the_author_posts_link', array( $this, 'filter_the_author_posts_link' ) );
|
12 |
+
}
|
13 |
+
|
14 |
+
function filter_the_author() {
|
15 |
+
return coauthors( null, null, null, null, false );
|
16 |
+
}
|
17 |
+
|
18 |
+
function filter_the_author_posts_link() {
|
19 |
+
return coauthors_posts_links( null, null, null, null, false );
|
20 |
+
}
|
21 |
+
}
|
@@ -0,0 +1,247 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
//Our class extends the WP_List_Table class, so we need to make sure that it's there
|
3 |
+
|
4 |
+
require_once( ABSPATH . 'wp-admin/includes/screen.php' );
|
5 |
+
require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
|
6 |
+
|
7 |
+
/**
|
8 |
+
* List all of the available Co-Authors within the system
|
9 |
+
*/
|
10 |
+
class CoAuthors_WP_List_Table extends WP_List_Table {
|
11 |
+
|
12 |
+
var $is_search = false;
|
13 |
+
|
14 |
+
function __construct() {
|
15 |
+
if( !empty( $_REQUEST['s'] ) )
|
16 |
+
$this->is_search = true;
|
17 |
+
|
18 |
+
parent::__construct( array(
|
19 |
+
'plural' => __( 'Co-Authors', 'co-authors-plus' ),
|
20 |
+
'singular' => __( 'Co-Author', 'co-authors-plus' ),
|
21 |
+
) );
|
22 |
+
}
|
23 |
+
|
24 |
+
/**
|
25 |
+
* Perform Co-Authors Query
|
26 |
+
*/
|
27 |
+
function prepare_items() {
|
28 |
+
global $coauthors_plus;
|
29 |
+
|
30 |
+
$columns = $this->get_columns();
|
31 |
+
$hidden = array();
|
32 |
+
$sortable = array(
|
33 |
+
'display_name' => array( 'display_name', 'ASC' ),
|
34 |
+
'first_name' => array( 'first_name', 'ASC' ),
|
35 |
+
'last_name' => array( 'last_name', 'ASC' ),
|
36 |
+
);
|
37 |
+
$this->_column_headers = array( $columns, $hidden, $sortable );
|
38 |
+
|
39 |
+
$paged = ( isset( $_REQUEST['paged'] ) ) ? intval( $_REQUEST['paged'] ) : 1;
|
40 |
+
$per_page = 20;
|
41 |
+
|
42 |
+
$args = array(
|
43 |
+
'paged' => $paged,
|
44 |
+
'posts_per_page' => $per_page,
|
45 |
+
'post_type' => $coauthors_plus->guest_authors->post_type,
|
46 |
+
'post_status' => 'any',
|
47 |
+
'orderby' => 'title',
|
48 |
+
'order' => 'ASC',
|
49 |
+
);
|
50 |
+
|
51 |
+
if ( isset( $_REQUEST['orderby'] ) ) {
|
52 |
+
switch( $_REQUEST['orderby'] ) {
|
53 |
+
case 'display_name':
|
54 |
+
$args['orderby'] = 'title';
|
55 |
+
break;
|
56 |
+
case 'first_name':
|
57 |
+
case 'last_name':
|
58 |
+
$args['orderby'] = 'meta_value';
|
59 |
+
$args['meta_key'] = $coauthors_plus->guest_authors->get_post_meta_key( $_REQUEST['orderby'] );
|
60 |
+
break;
|
61 |
+
}
|
62 |
+
}
|
63 |
+
if ( isset( $_REQUEST['order'] ) && in_array( strtoupper( $_REQUEST['order'] ), array( 'ASC', 'DESC' ) ) ) {
|
64 |
+
$args['order'] = strtoupper( $_REQUEST['order'] );
|
65 |
+
}
|
66 |
+
|
67 |
+
$this->filters = array(
|
68 |
+
'show-all' => __( 'Show all', 'co-authors-plus' ),
|
69 |
+
'with-linked-account' => __( 'With linked account', 'co-authors-plus' ),
|
70 |
+
'without-linked-account' => __( 'Without linked account', 'co-authors-plus' ),
|
71 |
+
);
|
72 |
+
|
73 |
+
if ( isset( $_REQUEST['filter'] ) && array_key_exists( $_REQUEST['filter'], $this->filters ) ) {
|
74 |
+
$this->active_filter = sanitize_key( $_REQUEST['filter'] );
|
75 |
+
} else {
|
76 |
+
$this->active_filter = 'show-all';
|
77 |
+
}
|
78 |
+
|
79 |
+
switch( $this->active_filter ) {
|
80 |
+
case 'with-linked-account':
|
81 |
+
case 'without-linked-account':
|
82 |
+
$args['meta_key'] = $coauthors_plus->guest_authors->get_post_meta_key( 'linked_account' );
|
83 |
+
if ( 'with-linked-account' == $this->active_filter )
|
84 |
+
$args['meta_compare'] = '!=';
|
85 |
+
else
|
86 |
+
$args['meta_compare'] = '=';
|
87 |
+
$args['meta_value'] = '0';
|
88 |
+
break;
|
89 |
+
}
|
90 |
+
|
91 |
+
if( $this->is_search )
|
92 |
+
add_filter( 'posts_where', array( $this, 'filter_query_for_search' ) );
|
93 |
+
|
94 |
+
$author_posts = new WP_Query( $args );
|
95 |
+
$items = array();
|
96 |
+
foreach( $author_posts->get_posts() as $author_post ) {
|
97 |
+
$items[] = $coauthors_plus->guest_authors->get_guest_author_by( 'id', $author_post->ID );
|
98 |
+
}
|
99 |
+
|
100 |
+
if( $this->is_search )
|
101 |
+
remove_filter( 'posts_where', array( $this, 'filter_query_for_search' ) );
|
102 |
+
|
103 |
+
$this->items = $items;
|
104 |
+
|
105 |
+
$this->set_pagination_args( array(
|
106 |
+
'total_items' => $author_posts->found_posts,
|
107 |
+
'per_page' => $per_page,
|
108 |
+
) );
|
109 |
+
}
|
110 |
+
|
111 |
+
function filter_query_for_search( $where ) {
|
112 |
+
global $wpdb;
|
113 |
+
$var = '%' . sanitize_text_field( $_REQUEST['s'] ) . '%';
|
114 |
+
$where .= $wpdb->prepare( ' AND (post_title LIKE %s OR post_name LIKE %s )', $var, $var);
|
115 |
+
return $where;
|
116 |
+
}
|
117 |
+
|
118 |
+
/**
|
119 |
+
* Either there are no guest authors, or the search doesn't match any
|
120 |
+
*/
|
121 |
+
function no_items() {
|
122 |
+
_e( 'No matching guest authors were found.', 'co-authors-plus' );
|
123 |
+
}
|
124 |
+
|
125 |
+
/**
|
126 |
+
* Generate the columns of information to be displayed on our list table
|
127 |
+
*/
|
128 |
+
function get_columns() {
|
129 |
+
$columns = array(
|
130 |
+
'display_name' => __( 'Display Name', 'co-authors-plus' ),
|
131 |
+
'first_name' => __( 'First Name', 'co-authors-plus' ),
|
132 |
+
'last_name' => __( 'Last Name', 'co-authors-plus' ),
|
133 |
+
'user_email' => __( 'E-mail', 'co-authors-plus' ),
|
134 |
+
'linked_account' => __( 'Linked Account', 'co-authors-plus' ),
|
135 |
+
'posts' => __( 'Posts', 'co-authors-plus' ),
|
136 |
+
);
|
137 |
+
return $columns;
|
138 |
+
}
|
139 |
+
|
140 |
+
/**
|
141 |
+
* Render a single row
|
142 |
+
*/
|
143 |
+
function single_row( $item ) {
|
144 |
+
static $alternate_class = '';
|
145 |
+
$alternate_class = ( $alternate_class == '' ? ' alternate' : '' );
|
146 |
+
$row_class = ' class="guest-author-static' . $alternate_class . '"';
|
147 |
+
|
148 |
+
echo '<tr id="guest-author-' . $item->ID . '"' . $row_class . '>';
|
149 |
+
echo $this->single_row_columns( $item );
|
150 |
+
echo '</tr>';
|
151 |
+
}
|
152 |
+
|
153 |
+
/**
|
154 |
+
* Render columns, some are overridden below
|
155 |
+
*/
|
156 |
+
function column_default( $item, $column_name ) {
|
157 |
+
|
158 |
+
switch( $column_name ) {
|
159 |
+
case 'first_name':
|
160 |
+
case 'last_name':
|
161 |
+
return $item->$column_name;
|
162 |
+
case 'user_email':
|
163 |
+
return '<a href="' . esc_attr( 'mailto:' . $item->user_email ) . '">' . esc_html( $item->user_email ) . '</a>';
|
164 |
+
}
|
165 |
+
}
|
166 |
+
|
167 |
+
/**
|
168 |
+
* Render display name, e.g. author name
|
169 |
+
*/
|
170 |
+
function column_display_name( $item ) {
|
171 |
+
|
172 |
+
$item_edit_link = get_edit_post_link( $item->ID );
|
173 |
+
$args = array(
|
174 |
+
'action' => 'delete',
|
175 |
+
'id' => $item->ID,
|
176 |
+
'_wpnonce' => wp_create_nonce( 'guest-author-delete' ),
|
177 |
+
);
|
178 |
+
$item_delete_link = add_query_arg( $args, menu_page_url( 'view-guest-authors', false ) );
|
179 |
+
$item_view_link = get_author_posts_url( $item->ID, $item->user_nicename );
|
180 |
+
|
181 |
+
$output = get_avatar( $item->user_email, 32 );
|
182 |
+
// @todo caps check to see whether the user can edit. Otherwise, just show the name
|
183 |
+
$output .= '<a href="' . esc_url( $item_edit_link ) . '">' . esc_html( $item->display_name ) . '</a>';
|
184 |
+
|
185 |
+
$actions = array();
|
186 |
+
$actions['edit'] = '<a href="' . esc_url( $item_edit_link ) . '">' . __( 'Edit', 'co-authors-plus' ) . '</a>';
|
187 |
+
$actions['delete'] = '<a href="' . esc_url( $item_delete_link ) . '">' . __( 'Delete', 'co-authors-plus' ) . '</a>';
|
188 |
+
$actions['view'] = '<a href="' . esc_url( $item_view_link ) . '">' . __( 'View Posts', 'co-authors-plus' ) . '</a>';
|
189 |
+
$actions = apply_filters( 'coauthors_guest_author_row_actions', $actions, $item );
|
190 |
+
$output .= $this->row_actions( $actions, false );
|
191 |
+
|
192 |
+
return $output;
|
193 |
+
}
|
194 |
+
|
195 |
+
/**
|
196 |
+
* Render linked account
|
197 |
+
*/
|
198 |
+
function column_linked_account( $item ) {
|
199 |
+
if ( $item->linked_account ) {
|
200 |
+
$account = get_user_by( 'login', $item->linked_account );
|
201 |
+
if ( $account ) {
|
202 |
+
if ( current_user_can( 'edit_users' ) ) {
|
203 |
+
return '<a href="' . admin_url( 'user-edit.php?user_id=' . $account->ID ) . '">' . esc_html( $item->linked_account ) . '</a>';
|
204 |
+
}
|
205 |
+
return $item->linked_account;
|
206 |
+
}
|
207 |
+
}
|
208 |
+
return '';
|
209 |
+
}
|
210 |
+
|
211 |
+
/**
|
212 |
+
* Render the published post count column
|
213 |
+
*/
|
214 |
+
function column_posts( $item ) {
|
215 |
+
global $coauthors_plus;
|
216 |
+
$term = $coauthors_plus->get_author_term( $item );
|
217 |
+
if ( ! $term )
|
218 |
+
return '';
|
219 |
+
return '<a href="' . esc_url( add_query_arg( 'author_name', $item->user_login, admin_url( 'edit.php' ) ) ) . '">' . $term->count . '</a>';
|
220 |
+
}
|
221 |
+
|
222 |
+
/**
|
223 |
+
* Allow users to filter the guest authors by various criteria
|
224 |
+
*/
|
225 |
+
function extra_tablenav( $which ) {
|
226 |
+
|
227 |
+
?><div class="alignleft actions"><?php
|
228 |
+
if ( 'top' == $which ) {
|
229 |
+
if ( !empty( $this->filters ) ) {
|
230 |
+
echo '<select name="filter">';
|
231 |
+
foreach( $this->filters as $key => $value ) {
|
232 |
+
echo '<option value="' . esc_attr( $key ) . '" ' . selected( $this->active_filter, $key, false ) . '>' . esc_attr( $value ) . '</option>';
|
233 |
+
}
|
234 |
+
echo '</select>';
|
235 |
+
}
|
236 |
+
submit_button( __( 'Filter', 'co-authors-plus' ), 'secondary', false, false );
|
237 |
+
}
|
238 |
+
?></div><?php
|
239 |
+
}
|
240 |
+
|
241 |
+
function display() {
|
242 |
+
global $coauthors_plus;
|
243 |
+
$this->search_box( $coauthors_plus->guest_authors->labels['search_items'], 'guest-authors' );
|
244 |
+
parent::display();
|
245 |
+
}
|
246 |
+
|
247 |
+
}
|
@@ -0,0 +1,403 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Co-Authors Plus commands for the WP-CLI framework
|
4 |
+
*
|
5 |
+
* @package wp-cli
|
6 |
+
* @since 3.0
|
7 |
+
* @see https://github.com/wp-cli/wp-cli
|
8 |
+
*/
|
9 |
+
WP_CLI::add_command( 'co-authors-plus', 'CoAuthorsPlus_Command' );
|
10 |
+
|
11 |
+
class CoAuthorsPlus_Command extends WP_CLI_Command {
|
12 |
+
|
13 |
+
/**
|
14 |
+
* Subcommand to create guest authors based on users
|
15 |
+
*
|
16 |
+
* @since 3.0
|
17 |
+
*
|
18 |
+
* @subcommand create-guest-authors
|
19 |
+
*/
|
20 |
+
public function create_guest_authors( $args, $assoc_args ) {
|
21 |
+
global $coauthors_plus;
|
22 |
+
|
23 |
+
$defaults = array(
|
24 |
+
// There are no arguments at this time
|
25 |
+
);
|
26 |
+
$this->args = wp_parse_args( $assoc_args, $defaults );
|
27 |
+
|
28 |
+
$users = get_users();
|
29 |
+
$created = 0;
|
30 |
+
$skipped = 0;
|
31 |
+
foreach( $users as $user ) {
|
32 |
+
|
33 |
+
$result = $coauthors_plus->guest_authors->create_guest_author_from_user_id( $user->ID );
|
34 |
+
if ( is_wp_error( $result ) ) {
|
35 |
+
$skipped++;
|
36 |
+
} else {
|
37 |
+
$created++;
|
38 |
+
}
|
39 |
+
}
|
40 |
+
|
41 |
+
WP_CLI::line( "All done! Here are your results:" );
|
42 |
+
WP_CLI::line( "- {$created} guest author profiles were created" );
|
43 |
+
WP_CLI::line( "- {$skipped} users already had guest author profiles" );
|
44 |
+
|
45 |
+
}
|
46 |
+
|
47 |
+
/**
|
48 |
+
* Subcommand to assign coauthors to a post based on a given meta key
|
49 |
+
*
|
50 |
+
* @since 3.0
|
51 |
+
*
|
52 |
+
* @subcommand assign-coauthors
|
53 |
+
* @synopsis [--meta_key=<key>] [--post_type=<ptype>]
|
54 |
+
*/
|
55 |
+
public function assign_coauthors( $args, $assoc_args ) {
|
56 |
+
global $coauthors_plus;
|
57 |
+
|
58 |
+
$defaults = array(
|
59 |
+
'meta_key' => '_original_import_author',
|
60 |
+
'post_type' => 'post',
|
61 |
+
'order' => 'ASC',
|
62 |
+
'orderby' => 'ID',
|
63 |
+
'posts_per_page' => 100,
|
64 |
+
'paged' => 1,
|
65 |
+
'append_coauthors' => false,
|
66 |
+
);
|
67 |
+
$this->args = wp_parse_args( $assoc_args, $defaults );
|
68 |
+
|
69 |
+
// For global use and not a part of WP_Query
|
70 |
+
$append_coauthors = $this->args['append_coauthors'];
|
71 |
+
unset( $this->args['append_coauthors'] );
|
72 |
+
|
73 |
+
$posts_total = 0;
|
74 |
+
$posts_already_associated = 0;
|
75 |
+
$posts_missing_coauthor = 0;
|
76 |
+
$posts_associated = 0;
|
77 |
+
$missing_coauthors = array();
|
78 |
+
|
79 |
+
$posts = new WP_Query( $this->args );
|
80 |
+
while( $posts->post_count ) {
|
81 |
+
|
82 |
+
foreach( $posts->posts as $single_post ) {
|
83 |
+
$posts_total++;
|
84 |
+
|
85 |
+
// See if the value in the post meta field is the same as any of the existing coauthors
|
86 |
+
$original_author = get_post_meta( $single_post->ID, $this->args['meta_key'], true );
|
87 |
+
$existing_coauthors = get_coauthors( $single_post->ID );
|
88 |
+
$already_associated = false;
|
89 |
+
foreach( $existing_coauthors as $existing_coauthor ) {
|
90 |
+
if ( $original_author == $existing_coauthor->user_login )
|
91 |
+
$already_associated = true;
|
92 |
+
}
|
93 |
+
if ( $already_associated ) {
|
94 |
+
$posts_already_associated++;
|
95 |
+
WP_CLI::line( $posts_total . ': Post #' . $single_post->ID . ' already has "' . $original_author . '" associated as a coauthor' );
|
96 |
+
continue;
|
97 |
+
}
|
98 |
+
|
99 |
+
// Make sure this original author exists as a co-author
|
100 |
+
if ( !$coauthors_plus->get_coauthor_by( 'user_login', $original_author ) ) {
|
101 |
+
$posts_missing_coauthor++;
|
102 |
+
$missing_coauthors[] = $original_author;
|
103 |
+
WP_CLI::line( $posts_total . ': Post #' . $single_post->ID . ' does not have "' . $original_author . '" associated as a coauthor but there is not a coauthor profile' );
|
104 |
+
continue;
|
105 |
+
}
|
106 |
+
|
107 |
+
// Assign the coauthor to the post
|
108 |
+
$coauthors_plus->add_coauthors( $single_post->ID, array( $original_author ), $append_coauthors );
|
109 |
+
WP_CLI::line( $posts_total . ': Post #' . $single_post->ID . ' has been assigned "' . $original_author . '" as the author' );
|
110 |
+
$posts_associated++;
|
111 |
+
clean_post_cache( $single_post->ID );
|
112 |
+
}
|
113 |
+
|
114 |
+
$this->args['paged']++;
|
115 |
+
$this->stop_the_insanity();
|
116 |
+
$posts = new WP_Query( $this->args );
|
117 |
+
}
|
118 |
+
|
119 |
+
WP_CLI::line( "All done! Here are your results:" );
|
120 |
+
if ( $posts_already_associated )
|
121 |
+
WP_CLI::line( "- {$posts_already_associated} posts already had the coauthor assigned" );
|
122 |
+
if ( $posts_missing_coauthor ) {
|
123 |
+
WP_CLI::line( "- {$posts_missing_coauthor} posts reference coauthors that don't exist. These are:" );
|
124 |
+
WP_CLI::line( " " . implode( ', ', array_unique( $missing_coauthors ) ) );
|
125 |
+
}
|
126 |
+
if ( $posts_associated )
|
127 |
+
WP_CLI::line( "- {$posts_associated} posts now have the proper coauthor" );
|
128 |
+
|
129 |
+
}
|
130 |
+
|
131 |
+
/**
|
132 |
+
* Assign posts associated with a WordPress user to a co-author
|
133 |
+
* Only apply the changes if there aren't yet co-authors associated with the post
|
134 |
+
*
|
135 |
+
* @since 3.0
|
136 |
+
*
|
137 |
+
* @subcommand assign-user-to-coauthor
|
138 |
+
* @synopsis --user_login=<user-login> --coauthor=<coauthor>
|
139 |
+
*/
|
140 |
+
public function assign_user_to_coauthor( $args, $assoc_args ) {
|
141 |
+
global $coauthors_plus, $wpdb;
|
142 |
+
|
143 |
+
$defaults = array(
|
144 |
+
'user_login' => '',
|
145 |
+
'coauthor' => '',
|
146 |
+
);
|
147 |
+
$assoc_args = wp_parse_args( $assoc_args, $defaults );
|
148 |
+
|
149 |
+
$user = get_user_by( 'login', $assoc_args['user_login'] );
|
150 |
+
$coauthor = $coauthors_plus->get_coauthor_by( 'login', $assoc_args['coauthor'] );
|
151 |
+
|
152 |
+
if ( ! $user )
|
153 |
+
WP_CLI::error( __( 'Please specify a valid user_login', 'co-authors-plus' ) );
|
154 |
+
|
155 |
+
if ( ! $coauthor )
|
156 |
+
WP_CLI::error( __( 'Please specify a valid co-author login', 'co-authors-plus' ) );
|
157 |
+
|
158 |
+
$post_types = implode( "','", $coauthors_plus->supported_post_types );
|
159 |
+
$posts = $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_author=%d AND post_type IN ('$post_types')", $user->ID ) );
|
160 |
+
$affected = 0;
|
161 |
+
foreach( $posts as $post_id ) {
|
162 |
+
if ( $coauthors = wp_get_post_terms( $post_id, $coauthors_plus->coauthor_taxonomy ) ) {
|
163 |
+
WP_CLI::line( sprintf( __( "Skipping - Post #%d already has co-authors assigned: %s", 'co-authors-plus' ), $post_id, implode( ', ', wp_list_pluck( $coauthors, 'slug' ) ) ) );
|
164 |
+
continue;
|
165 |
+
}
|
166 |
+
|
167 |
+
$coauthors_plus->add_coauthors( $post_id, array( $coauthor->user_login ) );
|
168 |
+
WP_CLI::line( sprintf( __( "Updating - Adding %s's byline to post #%d", 'co-authors-plus' ), $coauthor->user_login, $post_id ) );
|
169 |
+
$affected++;
|
170 |
+
if ( $affected && $affected % 20 == 0 )
|
171 |
+
sleep( 5 );
|
172 |
+
}
|
173 |
+
WP_CLI::success( sprintf( __( "All done! %d posts were affected.", 'co-authors-plus' ), $affected ) );
|
174 |
+
|
175 |
+
}
|
176 |
+
|
177 |
+
/**
|
178 |
+
* Subcommand to reassign co-authors based on some given format
|
179 |
+
* This will look for terms with slug 'x' and rename to term with slug and name 'y'
|
180 |
+
* This subcommand can be helpful for cleaning up after an import if the usernames
|
181 |
+
* for authors have changed. During the import process, 'author' terms will be
|
182 |
+
* created with the old user_login value. We can use this to migrate to the new user_login
|
183 |
+
*
|
184 |
+
* @todo support reassigning by CSV
|
185 |
+
*
|
186 |
+
* @since 3.0
|
187 |
+
*
|
188 |
+
* @subcommand reassign-terms
|
189 |
+
* @synopsis [--author-mapping=<file>] [--old_term=<slug>] [--new_term=<slug>]
|
190 |
+
*/
|
191 |
+
public function reassign_terms( $args, $assoc_args ) {
|
192 |
+
global $coauthors_plus;
|
193 |
+
|
194 |
+
$defaults = array(
|
195 |
+
'author_mapping' => null,
|
196 |
+
'old_term' => null,
|
197 |
+
'new_term' => null,
|
198 |
+
);
|
199 |
+
$this->args = wp_parse_args( $assoc_args, $defaults );
|
200 |
+
|
201 |
+
$author_mapping = $this->args['author_mapping'];
|
202 |
+
$old_term = $this->args['old_term'];
|
203 |
+
$new_term = $this->args['new_term'];
|
204 |
+
|
205 |
+
// Get the reassignment data
|
206 |
+
if ( $author_mapping && file_exists( $author_mapping ) ) {
|
207 |
+
require_once( $author_mapping );
|
208 |
+
$authors_to_migrate = $cli_user_map;
|
209 |
+
} else if ( $author_mapping ) {
|
210 |
+
WP_CLI::error( "author_mapping doesn't exist: " . $author_mapping );
|
211 |
+
exit;
|
212 |
+
}
|
213 |
+
|
214 |
+
// Alternate reassigment approach
|
215 |
+
if ( $old_term && $new_term ) {
|
216 |
+
$authors_to_migrate = array(
|
217 |
+
$old_term => $new_term,
|
218 |
+
);
|
219 |
+
}
|
220 |
+
|
221 |
+
// For each author to migrate, check whether the term exists,
|
222 |
+
// whether the target term exists, and only do the migration if both are met
|
223 |
+
$results = (object)array(
|
224 |
+
'old_term_missing' => 0,
|
225 |
+
'new_term_exists' => 0,
|
226 |
+
'success' => 0,
|
227 |
+
);
|
228 |
+
foreach( $authors_to_migrate as $old_user => $new_user ) {
|
229 |
+
|
230 |
+
if ( is_numeric( $new_user ) )
|
231 |
+
$new_user = get_user_by( 'id', $new_user )->user_login;
|
232 |
+
|
233 |
+
// The old user should exist as a term
|
234 |
+
$old_term = $coauthors_plus->get_author_term( $coauthors_plus->get_coauthor_by( 'login', $old_user ) );
|
235 |
+
if ( !$old_term ) {
|
236 |
+
WP_CLI::line( "Error: Term '{$old_user}' doesn't exist, skipping" );
|
237 |
+
$results->old_term_missing++;
|
238 |
+
continue;
|
239 |
+
}
|
240 |
+
|
241 |
+
// If the new user exists as a term already, we want to reassign all posts to that
|
242 |
+
// new term and delete the original
|
243 |
+
// Otherwise, simply rename the old term
|
244 |
+
$new_term = $coauthors_plus->get_author_term( $coauthors_plus->get_coauthor_by( 'login', $new_user ) );
|
245 |
+
if ( is_object( $new_term ) ) {
|
246 |
+
$args = array(
|
247 |
+
'default' => $new_term->term_id,
|
248 |
+
'force_default' => true,
|
249 |
+
);
|
250 |
+
wp_delete_term( $old_term->term_id, $coauthors_plus->coauthor_taxonomy, $args );
|
251 |
+
WP_CLI::line( "Success: There's already a '{$new_user}' term for '{$old_user}'. Reassigning posts and then deleting the term" );
|
252 |
+
$results->new_term_exists++;
|
253 |
+
} else {
|
254 |
+
$args = array(
|
255 |
+
'slug' => $new_user,
|
256 |
+
'name' => $new_user,
|
257 |
+
);
|
258 |
+
wp_update_term( $old_term->term_id, $coauthors_plus->coauthor_taxonomy, $args );
|
259 |
+
WP_CLI::line( "Success: Converted '{$old_user}' term to '{$new_user}'" );
|
260 |
+
$results->success++;
|
261 |
+
}
|
262 |
+
clean_term_cache( $old_term->term_id, $coauthors_plus->coauthor_taxonomy );
|
263 |
+
}
|
264 |
+
|
265 |
+
WP_CLI::line( "Reassignment complete. Here are your results:" );
|
266 |
+
WP_CLI::line( "- $results->success authors were successfully reassigned terms" );
|
267 |
+
WP_CLI::line( "- $results->new_term_exists authors had their old term merged to their new term" );
|
268 |
+
WP_CLI::line( "- $results->old_term_missing authors were missing old terms" );
|
269 |
+
|
270 |
+
}
|
271 |
+
|
272 |
+
/**
|
273 |
+
* List all of the posts without assigned co-authors terms
|
274 |
+
*
|
275 |
+
* @since 3.0
|
276 |
+
*
|
277 |
+
* @subcommand list-posts-without-terms
|
278 |
+
* @synopsis [--post_type=<ptype>]
|
279 |
+
*/
|
280 |
+
public function list_posts_without_terms( $args, $assoc_args ) {
|
281 |
+
global $coauthors_plus;
|
282 |
+
|
283 |
+
$defaults = array(
|
284 |
+
'post_type' => 'post',
|
285 |
+
'order' => 'ASC',
|
286 |
+
'orderby' => 'ID',
|
287 |
+
'year' => '',
|
288 |
+
'posts_per_page' => 300,
|
289 |
+
'paged' => 1,
|
290 |
+
'no_found_rows' => true,
|
291 |
+
'update_meta_cache' => false,
|
292 |
+
);
|
293 |
+
$this->args = wp_parse_args( $assoc_args, $defaults );
|
294 |
+
|
295 |
+
$posts = new WP_Query( $this->args );
|
296 |
+
while( $posts->post_count ) {
|
297 |
+
|
298 |
+
foreach( $posts->posts as $single_post ) {
|
299 |
+
|
300 |
+
$terms = wp_get_post_terms( $single_post->ID, $coauthors_plus->coauthor_taxonomy );
|
301 |
+
if ( empty( $terms ) ) {
|
302 |
+
$saved = array(
|
303 |
+
$single_post->ID,
|
304 |
+
addslashes( $single_post->post_title ),
|
305 |
+
get_permalink( $single_post->ID ),
|
306 |
+
$single_post->post_date,
|
307 |
+
);
|
308 |
+
WP_CLI::line( '"' . implode( '","', $saved ) . '"' );
|
309 |
+
}
|
310 |
+
}
|
311 |
+
|
312 |
+
$this->stop_the_insanity();
|
313 |
+
|
314 |
+
$this->args['paged']++;
|
315 |
+
$posts = new WP_Query( $this->args );
|
316 |
+
}
|
317 |
+
|
318 |
+
}
|
319 |
+
|
320 |
+
/**
|
321 |
+
* Migrate author terms without prefixes to ones with prefixes
|
322 |
+
* Pre-3.0, all author terms didn't have a 'cap-' prefix, which means
|
323 |
+
* they can easily collide with terms in other taxonomies
|
324 |
+
*
|
325 |
+
* @since 3.0
|
326 |
+
*
|
327 |
+
* @subcommand migrate-author-terms
|
328 |
+
*/
|
329 |
+
public function migrate_author_terms( $args, $assoc_args ) {
|
330 |
+
global $coauthors_plus;
|
331 |
+
|
332 |
+
$author_terms = get_terms( $coauthors_plus->coauthor_taxonomy, array( 'hide_empty' => false ) );
|
333 |
+
WP_CLI::line( "Now migrating up to " . count( $author_terms ) . " terms" );
|
334 |
+
foreach( $author_terms as $author_term ) {
|
335 |
+
// Term is already prefixed. We're good.
|
336 |
+
if ( preg_match( '#^cap\-#', $author_term->slug, $matches ) ) {
|
337 |
+
WP_CLI::line( "Term {$author_term->slug} ({$author_term->term_id}) is already prefixed, skipping" );
|
338 |
+
continue;
|
339 |
+
}
|
340 |
+
// A prefixed term was accidentally created, and the old term needs to be merged into the new (WordPress.com VIP)
|
341 |
+
if ( $prefixed_term = get_term_by( 'slug', 'cap-' . $author_term->slug, $coauthors_plus->coauthor_taxonomy ) ) {
|
342 |
+
WP_CLI::line( "Term {$author_term->slug} ({$author_term->term_id}) has a new term too: $prefixed_term->slug ($prefixed_term->term_id). Merging" );
|
343 |
+
$args = array(
|
344 |
+
'default' => $author_term->term_id,
|
345 |
+
'force_default' => true,
|
346 |
+
);
|
347 |
+
wp_delete_term( $prefixed_term->term_id, $coauthors_plus->coauthor_taxonomy, $args );
|
348 |
+
}
|
349 |
+
|
350 |
+
// Term isn't prefixed, doesn't have a sibling, and should be updated
|
351 |
+
WP_CLI::line( "Term {$author_term->slug} ({$author_term->term_id}) isn't prefixed, adding one" );
|
352 |
+
$args = array(
|
353 |
+
'slug' => 'cap-' . $author_term->slug,
|
354 |
+
);
|
355 |
+
wp_update_term( $author_term->term_id, $coauthors_plus->coauthor_taxonomy, $args );
|
356 |
+
}
|
357 |
+
WP_CLI::success( "All done! Grab a cold one (Affogatto)" );
|
358 |
+
}
|
359 |
+
|
360 |
+
/**
|
361 |
+
* Update the post count and description for each author
|
362 |
+
*
|
363 |
+
* @since 3.0
|
364 |
+
*
|
365 |
+
* @subcommand update-author-terms
|
366 |
+
*/
|
367 |
+
public function update_author_terms() {
|
368 |
+
global $coauthors_plus;
|
369 |
+
$author_terms = get_terms( $coauthors_plus->coauthor_taxonomy, array( 'hide_empty' => false ) );
|
370 |
+
WP_CLI::line( "Now updating " . count( $author_terms ) . " terms" );
|
371 |
+
foreach( $author_terms as $author_term ) {
|
372 |
+
$old_count = $author_term->count;
|
373 |
+
$coauthor = $coauthors_plus->get_coauthor_by( 'user_nicename', $author_term->slug );
|
374 |
+
$coauthors_plus->update_author_term( $coauthor );
|
375 |
+
$coauthors_plus->update_author_term_post_count( $author_term );
|
376 |
+
wp_cache_delete( $author_term->term_id, $coauthors_plus->coauthor_taxonomy );
|
377 |
+
$new_count = get_term_by( 'id', $author_term->term_id, $coauthors_plus->coauthor_taxonomy )->count;
|
378 |
+
WP_CLI::line( "Term {$author_term->slug} ({$author_term->term_id}) changed from {$old_count} to {$new_count} and the description was refreshed" );
|
379 |
+
}
|
380 |
+
WP_CLI::success( "All done" );
|
381 |
+
}
|
382 |
+
|
383 |
+
/**
|
384 |
+
* Clear all of the caches for memory management
|
385 |
+
*/
|
386 |
+
private function stop_the_insanity() {
|
387 |
+
global $wpdb, $wp_object_cache;
|
388 |
+
|
389 |
+
$wpdb->queries = array(); // or define( 'WP_IMPORTING', true );
|
390 |
+
|
391 |
+
if ( !is_object( $wp_object_cache ) )
|
392 |
+
return;
|
393 |
+
|
394 |
+
$wp_object_cache->group_ops = array();
|
395 |
+
$wp_object_cache->stats = array();
|
396 |
+
$wp_object_cache->memcache_debug = array();
|
397 |
+
$wp_object_cache->cache = array();
|
398 |
+
|
399 |
+
if( is_callable( $wp_object_cache, '__remoteset' ) )
|
400 |
+
$wp_object_cache->__remoteset(); // important
|
401 |
+
}
|
402 |
+
|
403 |
+
}
|
@@ -1,34 +1,56 @@
|
|
1 |
=== Co-Authors Plus ===
|
2 |
Contributors: batmoo, danielbachhuber, automattic
|
3 |
-
Donate link: http://digitalize.ca/donate
|
4 |
Tags: authors, users, multiple authors, coauthors, multi-author, publishing
|
5 |
-
Tested up to: 3.
|
6 |
-
Requires at least: 3.
|
7 |
-
Stable tag:
|
8 |
|
9 |
-
|
10 |
|
11 |
== Description ==
|
12 |
|
13 |
-
|
|
|
|
|
|
|
|
|
14 |
|
15 |
This plugin is an almost complete rewrite of the Co-Authors plugin originally developed at [Shepherd Interactive](http://www.shepherd-interactive.com/) (2007). The original plugin was inspired by the 'Multiple Authors' plugin by Mark Jaquith (2005).
|
16 |
|
17 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
18 |
|
19 |
== Changelog ==
|
20 |
|
21 |
-
=
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
22 |
* Bug fix: Properly filter the user query so users can AJAX search against the display name field again
|
23 |
* If https is used for the admin, also use the secure Gravatar URL. Props [rmcfrazier](https://github.com/rmcfrazier)
|
24 |
|
25 |
-
=
|
26 |
* AJAX user search is back to searching against user login, display name, email address and user ID. The method introduced in v2.6.2 didn't scale well
|
27 |
* French translation courtesy of Sylvain Bérubé
|
28 |
* Spanish translation courtesy of Alejandro Arcos
|
29 |
* Bug fix: Resolved incorrect caps check against user editing an already published post. [See forum thread](http://wordpress.org/support/topic/multiple-authors-cant-edit-pages?replies=17#post-2741243)
|
30 |
|
31 |
-
=
|
32 |
* AJAX user search matches against first name, last name, and nickname fields too, in addition to display name, user login, and email address
|
33 |
* Comment moderation and approved notifications are properly sent to all co-authors with the correct caps
|
34 |
* Filter required capability for user to be returned in an AJAX search with 'coauthors_edit_author_cap'
|
@@ -39,11 +61,11 @@ This plugin is an almost complete rewrite of the Co-Authors plugin originally de
|
|
39 |
* Bug fix: If a user has already been added as an author to a post, don't show them in the AJAX search again
|
40 |
* Bug fix: Allow output constants to be defined in a theme's functions.php file and include filters you can use instead
|
41 |
|
42 |
-
=
|
43 |
|
44 |
* Fix mangled usernames because of sanitize_key http://wordpress.org/support/topic/plugin-co-authors-plus-26-not-working-with-wp-33
|
45 |
|
46 |
-
=
|
47 |
|
48 |
* Sortable authors: Drag and drop the order of the authors as you'd like them to appear ([props kingkool68](http://profiles.wordpress.org/users/kingkool68/))
|
49 |
* Search for authors by display name (instead of nicename which was essentially the same as user_login)
|
@@ -51,22 +73,22 @@ This plugin is an almost complete rewrite of the Co-Authors plugin originally de
|
|
51 |
* Bumped requirements to WordPress 3.1
|
52 |
* Bug fix: Update the published post count for each user more reliably
|
53 |
|
54 |
-
=
|
55 |
|
56 |
* Bug fix: Removed extra comma when only two authors were listed. If you used the COAUTHORS_DEFAULT_BETWEEN_LAST constant, double-check what you have
|
57 |
|
58 |
-
=
|
59 |
|
60 |
* Bug: Couldn't query terms and authors at the same time (props nbaxley)
|
61 |
* Bug: Authors with empty fields (e.g. first name) were displaying blank in some cases
|
62 |
* Bug: authors with spaces in usernames not getting saved (props MLmsw, Ruben S. and others!)
|
63 |
* Bug: revisions getting wrong user attached (props cliquenoir!)
|
64 |
|
65 |
-
=
|
66 |
|
67 |
* Fix with author post count (throwing errors)
|
68 |
|
69 |
-
=
|
70 |
|
71 |
* Custom Post Type Support
|
72 |
* Compatibility with WP 3.0 and 3.1
|
@@ -74,17 +96,17 @@ This plugin is an almost complete rewrite of the Co-Authors plugin originally de
|
|
74 |
* Lots and lots and lots of bug fixes
|
75 |
* Thanks to everyone who submitted bugs, fixes, and suggestions! And for your patience!
|
76 |
|
77 |
-
=
|
78 |
|
79 |
* Fix for coauthors not being added if their username is different from display name
|
80 |
* Fixes to readme.txt (fixes for textual and punctuation errors, language clarification, minor formatting changes) courtesy of [Waldo Jaquith](http://www.vqronline.org)
|
81 |
|
82 |
-
=
|
83 |
|
84 |
* Fixed issues related to localization. Thanks to Jan Zombik <zombik@students.uni-mainz.de> for the fixes.
|
85 |
* Added set_time_limit to update function to get around timeout issues when upgrading plugin
|
86 |
|
87 |
-
=
|
88 |
|
89 |
* Plugin mostly rewritten to make use of taxonomy instead of post_meta
|
90 |
* Can now see all authors of a post under the author column from Edit Posts page
|
@@ -95,7 +117,7 @@ This plugin is an almost complete rewrite of the Co-Authors plugin originally de
|
|
95 |
* FIX: Issues with wp_coauthors_list function
|
96 |
* FIX: Issues with coauthored posts not showing up on author archives
|
97 |
|
98 |
-
=
|
99 |
|
100 |
* FIX: Added compatibility for WordPress 2.8
|
101 |
* FIX: Added new template tags (get_the_coauthor_meta & the_coauthor_meta) to fix issues related to displaying author info on author archive pages. See [Other Notes](http://wordpress.org/extend/plugins/co-authors-plus/other_notes/) for details.
|
@@ -104,140 +126,45 @@ This plugin is an almost complete rewrite of the Co-Authors plugin originally de
|
|
104 |
* FIX: Plugin now used WordPress native AJAX calls to tighten security
|
105 |
* DOCS: Added details about the new template tags
|
106 |
|
107 |
-
=
|
108 |
|
109 |
* FIX: Not searching Updated SQL query for autosuggest to search through first name, last name, and nickname
|
110 |
* FIX: When editing an author, and clicking on a suggested author, the original author was not be removed
|
111 |
* DOCS: Added code comments to javascript; more still to be added
|
112 |
* DOCS: Updated readme information
|
113 |
|
114 |
-
=
|
115 |
|
116 |
* Disabled "New Author" output in suggest box, for now
|
117 |
* Hopefully fixed SVN issue (if you're having trouble with the plugin, please delete the plugin and reinstall)
|
118 |
|
119 |
-
=
|
120 |
|
121 |
* Add blur event to disable input box
|
122 |
* Limit only one edit at a time.
|
123 |
* Checked basic cross-browser compatibility (Firefox 3 OS X, Safari 3 OS X, IE7 Vista).
|
124 |
* Add suggest javascript plugin to Edit Page.
|
125 |
|
126 |
-
=
|
127 |
|
128 |
* Disabled form submit when enter pressed.
|
129 |
|
130 |
-
=
|
131 |
|
132 |
* Changed SQL query to return only contributor-level and above users.
|
133 |
|
134 |
-
=
|
135 |
|
136 |
* Initial beta release.
|
137 |
|
138 |
-
|
139 |
== Installation ==
|
140 |
|
141 |
1. IMPORTANT: Please disable the original Co-Authors plugin (if you are using it) before installing Co-Authors Plus
|
142 |
1. Extract the coauthors-plus.zip file and upload its contents to the `/wp-content/plugins/` directory. Alternately, you can install directly from the Plugin directory within your WordPress Install.
|
143 |
1. Activate the plugin through the "Plugins" menu in WordPress.
|
144 |
-
1. Place the appropriate
|
145 |
1. Add co-authors to your posts and pages.
|
146 |
|
147 |
-
|
148 |
-
== Basic Usage and Other Notes ==
|
149 |
-
|
150 |
-
* Contributor-level and above can be added as co-authors.
|
151 |
-
* As per WordPress design, when an editor creates a new Post or Page, they are by default added as an author. However, they can be replaced by clicking on their name and typing in the name of the new author.
|
152 |
-
* The search-as-you-type box starts searching once two letters have been added, and executes a new search with every subsequent letter.
|
153 |
-
* The search-as-you-type box searches through the following user fields: a) user login; b) user nicename; c) display name; d) user email; e) first name; f) last name; and g) nickname.
|
154 |
-
|
155 |
-
|
156 |
-
== Template Tags ==
|
157 |
-
|
158 |
-
New template tags enable listing of co-authors:
|
159 |
-
|
160 |
-
* <code>coauthors()</code>
|
161 |
-
* <code>coauthors_posts_links()</code>
|
162 |
-
* <code>coauthors_firstnames()</code>
|
163 |
-
* <code>coauthors_lastnames()</code>
|
164 |
-
* <code>coauthors_nicknames()</code>
|
165 |
-
* <code>coauthors_links()</code>
|
166 |
-
* <code>coauthors_IDs()</code>
|
167 |
-
|
168 |
-
These template tags correspond to their "<code>the_author*</code>" equivalents; take special note of the pluralization.
|
169 |
-
Each of these template tags accept four optional arguments:
|
170 |
-
|
171 |
-
1. <code>between</code>: default ", "
|
172 |
-
1. <code>betweenLast</code>: default " and "
|
173 |
-
1. <code>before</code>: default ""
|
174 |
-
1. <code>after</code>: default ""
|
175 |
-
|
176 |
-
To use them, simply modify the code surrounding all instances of <code>the_author*()</code> to something like the following example:
|
177 |
-
|
178 |
-
if(function_exists('coauthors_posts_links'))
|
179 |
-
coauthors_posts_links();
|
180 |
-
else
|
181 |
-
the_author_posts_link();
|
182 |
-
|
183 |
-
The result of this would be formatted like "John Smith, Jane Doe and Joe Public".
|
184 |
-
|
185 |
-
Note that as of this writing, WordPress does provide a means of extending <code>wp_list_authors()</code>, so
|
186 |
-
included in this plugin is the function <code>coauthors_wp_list_authors()</code> modified
|
187 |
-
to take into account co-authored posts; the same arguments are accepted.
|
188 |
-
|
189 |
-
Sometimes you may need fine-grained control over the display of a posts's authors, and in this case you may use
|
190 |
-
the <code>CoAuthorsIterator</code> class. This class may be instantiated anywhere you may place <code>the_author()</code>
|
191 |
-
or everywhere if the post ID is provided to the constructor. The instantiated class has the following methods:
|
192 |
-
|
193 |
-
1. <code>iterate()</code>: advances <code>$authordata</code> to the next co-author; returns <code>false</code> and restores the original <code>$authordata</code> if there are no more authors to iterate.
|
194 |
-
1. <code>get_position()</code>: returns the zero-based index of the current author; returns -1 if the iterator is invalid.
|
195 |
-
1. <code>is_last()</code>: returns <code>true</code> if the current author is the last.
|
196 |
-
1. <code>is_first()</code>: returns <code>true</code> if the current author is the first.
|
197 |
-
1. <code>count()</code>: returns the total number of authors.
|
198 |
-
1. <code>get_all()</code>: returns an array of all of the authors' user data.
|
199 |
-
|
200 |
-
For example:
|
201 |
-
|
202 |
-
$i = new CoAuthorsIterator();
|
203 |
-
print $i->count() == 1 ? 'Author: ' : 'Authors: ';
|
204 |
-
$i->iterate();
|
205 |
-
the_author();
|
206 |
-
while($i->iterate()){
|
207 |
-
print $i->is_last() ? ' and ' : ', ';
|
208 |
-
the_author();
|
209 |
-
}
|
210 |
-
|
211 |
-
= the coauthor meta =
|
212 |
-
|
213 |
-
* <code>get_the_coauthor_meta( $field )</code> (2.8 only)
|
214 |
-
* <code>the_coauthor_meta( $field )</code> (2.8 only)
|
215 |
-
|
216 |
-
Note: The $field variable corresponds with the same values accepted by the [the author meta](http://codex.wordpress.org/Template_Tags/the_author_meta) function.
|
217 |
-
|
218 |
-
= get coauthors =
|
219 |
-
|
220 |
-
* <code>get_coauthors( [$post_id], [$args] )</code>
|
221 |
-
|
222 |
-
This function returns an array of coauthors for the specified post, or if used inside the Loop, the current post active in the Loop. the $args parameter is an array that allows you to specify the order in which the authors should be returned.
|
223 |
-
|
224 |
-
= is coauthor for post =
|
225 |
-
|
226 |
-
* <code>is_coauthor_for_post( $user, $post_id )</code>
|
227 |
-
|
228 |
-
This function allows you to check whether the specified user is coauthor for a post. The $user attribute can be the user ID or username.
|
229 |
-
|
230 |
-
|
231 |
-
== Frequently Asked Questions ==
|
232 |
-
|
233 |
-
= What is the main difference between Co-Authors and Co-Authors Plus? =
|
234 |
-
|
235 |
-
The most notable difference is the replacement of the standard WordPress authors drop-downs with search-as-you-type/auto-suggest/whatever-you-call-them input boxes. As a result, major bits of the JavaScript code was changed to be more jQuery-friendly. Eventually, I hope to include the ability to add new Users from within the Edit Post/Page screen and possibly Gravatar support.
|
236 |
-
|
237 |
-
= What happens to posts and pages when I delete a user assigned to a post or page as a coauthor? =
|
238 |
-
|
239 |
-
When a user is deleted from WordPress, they will be removed from all posts for which they are co-authors. If you chose to reassign their posts to another user, that user will be set as the coauthor instead.
|
240 |
-
|
241 |
== Screenshots ==
|
242 |
-
1. "
|
243 |
-
2.
|
1 |
=== Co-Authors Plus ===
|
2 |
Contributors: batmoo, danielbachhuber, automattic
|
|
|
3 |
Tags: authors, users, multiple authors, coauthors, multi-author, publishing
|
4 |
+
Tested up to: 3.4.2
|
5 |
+
Requires at least: 3.3
|
6 |
+
Stable tag: 3.0
|
7 |
|
8 |
+
Assign multiple bylines to posts, pages, and custom post types via a search-as-you-type input box
|
9 |
|
10 |
== Description ==
|
11 |
|
12 |
+
Assign multiple bylines to posts, pages, and custom post types via a search-as-you-type input box. Co-authored posts appear on a co-author's archive page and in their feed. Co-authors may edit the posts they are associated with, and co-authors who are contributors may only edit posts if they have not been published (as is core behavior).
|
13 |
+
|
14 |
+
Add writers as bylines without creating WordPress user accounts. Simply [create a guest author profile](http://vip.wordpress.com/documentation/add-guest-bylines-to-your-content-with-co-authors-plus/) for the writer and assign the byline as you normally would.
|
15 |
+
|
16 |
+
On the frontend, use the [Co-Authors Plus template tags](http://vip.wordpress.com/documentation/incorporate-co-authors-plus-template-tags-into-your-theme/) to list co-authors anywhere you'd normally list the author.
|
17 |
|
18 |
This plugin is an almost complete rewrite of the Co-Authors plugin originally developed at [Shepherd Interactive](http://www.shepherd-interactive.com/) (2007). The original plugin was inspired by the 'Multiple Authors' plugin by Mark Jaquith (2005).
|
19 |
|
20 |
+
== Frequently Asked Questions ==
|
21 |
+
|
22 |
+
= How do I add Co-Authors Plus support to my theme? =
|
23 |
+
|
24 |
+
If you've just installed Co-Authors Plus, you might notice that the bylines are being added in the backend but aren't appearing on the frontend. You'll need to [add the template tags to your theme](http://vip.wordpress.com/documentation/incorporate-co-authors-plus-template-tags-into-your-theme/) before the bylines will appear.
|
25 |
+
|
26 |
+
= What happens to posts and pages when I delete a user assigned to a post or page as a coauthor? =
|
27 |
+
|
28 |
+
When a user is deleted from WordPress, they will be removed from all posts for which they are co-authors. If you chose to reassign their posts to another user, that user will be set as the coauthor instead.
|
29 |
|
30 |
== Changelog ==
|
31 |
|
32 |
+
= 3.0 (Nov. 12, 2012) =
|
33 |
+
* Create guest author profiles for bylines you'd like to assign without creating WordPress user accounts. Guest authors can have all of the same fields as normal users including display name, biography, and avatars.
|
34 |
+
* Support for non-Latin characters in usernames and guest author names
|
35 |
+
* wp-cli subcommands for creating, assigning, and reassigning co-authors
|
36 |
+
* For themes using core template tags like the_author() or the_author_posts_link(), you enable Co-Authors Plus support with a simple filter
|
37 |
+
* New author terms are now prefixed with 'cap-' to avoid collisions with global scope
|
38 |
+
* Bug fix: Apply query filters to only post_types registered with the taxonomy. Props [Tom Ransom](https://github.com/1bigidea)
|
39 |
+
* Filter coauthors_posts_link_single() with 'coauthors_posts_link'. Also adds rel="author". Props [Amit Sannad](https://github.com/asannad) and [Gabriel Koen](https://github.com/mintindeed)
|
40 |
+
* Filter for the context and priorities of the Co-Authors meta boxes. Props [Tomáš Kapler](https://github.com/tkapler)
|
41 |
+
* Renamed the post meta box for selecting authors so it applies to many post types. Props [John Blackbourn](https://github.com/johnbillion)
|
42 |
+
|
43 |
+
= 2.6.4 (May 7, 2012) =
|
44 |
* Bug fix: Properly filter the user query so users can AJAX search against the display name field again
|
45 |
* If https is used for the admin, also use the secure Gravatar URL. Props [rmcfrazier](https://github.com/rmcfrazier)
|
46 |
|
47 |
+
= 2.6.3 (Apr. 30, 2012) =
|
48 |
* AJAX user search is back to searching against user login, display name, email address and user ID. The method introduced in v2.6.2 didn't scale well
|
49 |
* French translation courtesy of Sylvain Bérubé
|
50 |
* Spanish translation courtesy of Alejandro Arcos
|
51 |
* Bug fix: Resolved incorrect caps check against user editing an already published post. [See forum thread](http://wordpress.org/support/topic/multiple-authors-cant-edit-pages?replies=17#post-2741243)
|
52 |
|
53 |
+
= 2.6.2 (Mar. 6, 2012) =
|
54 |
* AJAX user search matches against first name, last name, and nickname fields too, in addition to display name, user login, and email address
|
55 |
* Comment moderation and approved notifications are properly sent to all co-authors with the correct caps
|
56 |
* Filter required capability for user to be returned in an AJAX search with 'coauthors_edit_author_cap'
|
61 |
* Bug fix: If a user has already been added as an author to a post, don't show them in the AJAX search again
|
62 |
* Bug fix: Allow output constants to be defined in a theme's functions.php file and include filters you can use instead
|
63 |
|
64 |
+
= 2.6.1 (Dec. 30, 2011) =
|
65 |
|
66 |
* Fix mangled usernames because of sanitize_key http://wordpress.org/support/topic/plugin-co-authors-plus-26-not-working-with-wp-33
|
67 |
|
68 |
+
= 2.6 (Dec. 22, 2011) =
|
69 |
|
70 |
* Sortable authors: Drag and drop the order of the authors as you'd like them to appear ([props kingkool68](http://profiles.wordpress.org/users/kingkool68/))
|
71 |
* Search for authors by display name (instead of nicename which was essentially the same as user_login)
|
73 |
* Bumped requirements to WordPress 3.1
|
74 |
* Bug fix: Update the published post count for each user more reliably
|
75 |
|
76 |
+
= 2.5.3 (Aug. 14, 2011) =
|
77 |
|
78 |
* Bug fix: Removed extra comma when only two authors were listed. If you used the COAUTHORS_DEFAULT_BETWEEN_LAST constant, double-check what you have
|
79 |
|
80 |
+
= 2.5.2 (Apr. 23, 2011) =
|
81 |
|
82 |
* Bug: Couldn't query terms and authors at the same time (props nbaxley)
|
83 |
* Bug: Authors with empty fields (e.g. first name) were displaying blank in some cases
|
84 |
* Bug: authors with spaces in usernames not getting saved (props MLmsw, Ruben S. and others!)
|
85 |
* Bug: revisions getting wrong user attached (props cliquenoir!)
|
86 |
|
87 |
+
= 2.5.1 (Mar. 26, 2011) =
|
88 |
|
89 |
* Fix with author post count (throwing errors)
|
90 |
|
91 |
+
= 2.5 (Mar. 26, 2011) =
|
92 |
|
93 |
* Custom Post Type Support
|
94 |
* Compatibility with WP 3.0 and 3.1
|
96 |
* Lots and lots and lots of bug fixes
|
97 |
* Thanks to everyone who submitted bugs, fixes, and suggestions! And for your patience!
|
98 |
|
99 |
+
= 2.1.1 (Oct. 16, 2009) =
|
100 |
|
101 |
* Fix for coauthors not being added if their username is different from display name
|
102 |
* Fixes to readme.txt (fixes for textual and punctuation errors, language clarification, minor formatting changes) courtesy of [Waldo Jaquith](http://www.vqronline.org)
|
103 |
|
104 |
+
= 2.1 (Oct. 11, 2009) =
|
105 |
|
106 |
* Fixed issues related to localization. Thanks to Jan Zombik <zombik@students.uni-mainz.de> for the fixes.
|
107 |
* Added set_time_limit to update function to get around timeout issues when upgrading plugin
|
108 |
|
109 |
+
= 2.0 (Oct. 11, 2009) =
|
110 |
|
111 |
* Plugin mostly rewritten to make use of taxonomy instead of post_meta
|
112 |
* Can now see all authors of a post under the author column from Edit Posts page
|
117 |
* FIX: Issues with wp_coauthors_list function
|
118 |
* FIX: Issues with coauthored posts not showing up on author archives
|
119 |
|
120 |
+
= 1.2.0 (Jun. 16, 2012) =
|
121 |
|
122 |
* FIX: Added compatibility for WordPress 2.8
|
123 |
* FIX: Added new template tags (get_the_coauthor_meta & the_coauthor_meta) to fix issues related to displaying author info on author archive pages. See [Other Notes](http://wordpress.org/extend/plugins/co-authors-plus/other_notes/) for details.
|
126 |
* FIX: Plugin now used WordPress native AJAX calls to tighten security
|
127 |
* DOCS: Added details about the new template tags
|
128 |
|
129 |
+
= 1.1.5 (Apr. 26, 2009) =
|
130 |
|
131 |
* FIX: Not searching Updated SQL query for autosuggest to search through first name, last name, and nickname
|
132 |
* FIX: When editing an author, and clicking on a suggested author, the original author was not be removed
|
133 |
* DOCS: Added code comments to javascript; more still to be added
|
134 |
* DOCS: Updated readme information
|
135 |
|
136 |
+
= 1.1.4 (Apr. 25, 2009) =
|
137 |
|
138 |
* Disabled "New Author" output in suggest box, for now
|
139 |
* Hopefully fixed SVN issue (if you're having trouble with the plugin, please delete the plugin and reinstall)
|
140 |
|
141 |
+
= 1.1.3 (Apr. 23, 2009) =
|
142 |
|
143 |
* Add blur event to disable input box
|
144 |
* Limit only one edit at a time.
|
145 |
* Checked basic cross-browser compatibility (Firefox 3 OS X, Safari 3 OS X, IE7 Vista).
|
146 |
* Add suggest javascript plugin to Edit Page.
|
147 |
|
148 |
+
= 1.1.2 (Apr. 19, 2009) =
|
149 |
|
150 |
* Disabled form submit when enter pressed.
|
151 |
|
152 |
+
= 1.1.1 (Apr. 15, 2009) =
|
153 |
|
154 |
* Changed SQL query to return only contributor-level and above users.
|
155 |
|
156 |
+
= 1.1.0 (Apr. 14, 2009) =
|
157 |
|
158 |
* Initial beta release.
|
159 |
|
|
|
160 |
== Installation ==
|
161 |
|
162 |
1. IMPORTANT: Please disable the original Co-Authors plugin (if you are using it) before installing Co-Authors Plus
|
163 |
1. Extract the coauthors-plus.zip file and upload its contents to the `/wp-content/plugins/` directory. Alternately, you can install directly from the Plugin directory within your WordPress Install.
|
164 |
1. Activate the plugin through the "Plugins" menu in WordPress.
|
165 |
+
1. Place the appropriate [co-authors template tags](http://vip.wordpress.com/documentation/incorporate-co-authors-plus-template-tags-into-your-theme/) in your template.
|
166 |
1. Add co-authors to your posts and pages.
|
167 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
168 |
== Screenshots ==
|
169 |
+
1. "Authors" box with multiple authors added
|
170 |
+
2. Guest authors allow you to assign bylines without creating WordPress user accounts
|
Binary file
|
Binary file
|
@@ -18,12 +18,13 @@ function get_coauthors( $post_id = 0, $args = array() ) {
|
|
18 |
|
19 |
if ( is_array( $coauthor_terms ) && !empty( $coauthor_terms ) ) {
|
20 |
foreach( $coauthor_terms as $coauthor ) {
|
21 |
-
$
|
|
|
22 |
// In case the user has been deleted while plugin was deactivated
|
23 |
if ( !empty( $post_author ) )
|
24 |
$coauthors[] = $post_author;
|
25 |
}
|
26 |
-
} else {
|
27 |
if ( $post ) {
|
28 |
$post_author = get_userdata( $post->post_author );
|
29 |
} else {
|
@@ -31,7 +32,7 @@ function get_coauthors( $post_id = 0, $args = array() ) {
|
|
31 |
}
|
32 |
if ( !empty( $post_author ) )
|
33 |
$coauthors[] = $post_author;
|
34 |
-
}
|
35 |
}
|
36 |
return $coauthors;
|
37 |
}
|
@@ -56,7 +57,7 @@ function is_coauthor_for_post( $user, $post_id = 0 ) {
|
|
56 |
}
|
57 |
|
58 |
foreach( $coauthors as $coauthor ) {
|
59 |
-
if ( $user == $coauthor->user_login )
|
60 |
return true;
|
61 |
}
|
62 |
return false;
|
@@ -165,7 +166,7 @@ function coauthors__echo( $tag, $type = 'tag', $separators = array(), $tag_args
|
|
165 |
$output .= $separators['between'];
|
166 |
|
167 |
if ( $i->is_last() && $i->count() > 1 ) {
|
168 |
-
$output = rtrim( $output,
|
169 |
$output .= ' ' . $separators['betweenLast'];
|
170 |
}
|
171 |
|
@@ -198,11 +199,19 @@ function coauthors_posts_links( $between = null, $betweenLast = null, $before =
|
|
198 |
), null, $echo );
|
199 |
}
|
200 |
function coauthors_posts_links_single( $author ) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
201 |
return sprintf(
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
|
|
206 |
);
|
207 |
}
|
208 |
|
18 |
|
19 |
if ( is_array( $coauthor_terms ) && !empty( $coauthor_terms ) ) {
|
20 |
foreach( $coauthor_terms as $coauthor ) {
|
21 |
+
$coauthor_slug = preg_replace( '#^cap\-#', '', $coauthor->slug );
|
22 |
+
$post_author = $coauthors_plus->get_coauthor_by( 'user_nicename', $coauthor_slug );
|
23 |
// In case the user has been deleted while plugin was deactivated
|
24 |
if ( !empty( $post_author ) )
|
25 |
$coauthors[] = $post_author;
|
26 |
}
|
27 |
+
} else if ( !$coauthors_plus->force_guest_authors ) {
|
28 |
if ( $post ) {
|
29 |
$post_author = get_userdata( $post->post_author );
|
30 |
} else {
|
32 |
}
|
33 |
if ( !empty( $post_author ) )
|
34 |
$coauthors[] = $post_author;
|
35 |
+
} // the empty else case is because if we force guest authors, we don't ever care what value wp_posts.post_author has.
|
36 |
}
|
37 |
return $coauthors;
|
38 |
}
|
57 |
}
|
58 |
|
59 |
foreach( $coauthors as $coauthor ) {
|
60 |
+
if ( $user == $coauthor->user_login || $user == $coauthor->linked_account )
|
61 |
return true;
|
62 |
}
|
63 |
return false;
|
166 |
$output .= $separators['between'];
|
167 |
|
168 |
if ( $i->is_last() && $i->count() > 1 ) {
|
169 |
+
$output = rtrim( $output, " {$separators['between']}" );
|
170 |
$output .= ' ' . $separators['betweenLast'];
|
171 |
}
|
172 |
|
199 |
), null, $echo );
|
200 |
}
|
201 |
function coauthors_posts_links_single( $author ) {
|
202 |
+
$args = array(
|
203 |
+
'href' => get_author_posts_url( $author->ID, $author->user_nicename ),
|
204 |
+
'rel' => 'author',
|
205 |
+
'title' => sprintf( __( 'Posts by %s', 'co-authors-plus' ), get_the_author() ),
|
206 |
+
'text' => get_the_author(),
|
207 |
+
);
|
208 |
+
$args = apply_filters( 'coauthors_posts_link', $args, $author );
|
209 |
return sprintf(
|
210 |
+
'<a href="%1$s" title="%2$s" rel="%3$s">%4$s</a>',
|
211 |
+
esc_url( $args['href'] ),
|
212 |
+
esc_attr( $args['title'] ),
|
213 |
+
esc_attr( $args['rel'] ),
|
214 |
+
esc_html( $args['text'] )
|
215 |
);
|
216 |
}
|
217 |
|
@@ -3,9 +3,6 @@ function coauthors_plus_upgrade( $from ) {
|
|
3 |
// TODO: handle upgrade failures
|
4 |
|
5 |
if( $from < 2.0 ) coauthors_plus_upgrade_20();
|
6 |
-
|
7 |
-
// Update to the current global version
|
8 |
-
coauthors_plus_update_version(COAUTHORS_PLUS_VERSION);
|
9 |
}
|
10 |
|
11 |
/**
|
@@ -16,7 +13,7 @@ function coauthors_plus_upgrade_20 () {
|
|
16 |
global $coauthors_plus;
|
17 |
|
18 |
// Get all posts with meta_key _coauthor
|
19 |
-
$all_posts = get_posts(array('numberposts' => '-1'));
|
20 |
|
21 |
if(is_array($all_posts)) {
|
22 |
foreach($all_posts as $single_post) {
|
@@ -37,7 +34,7 @@ function coauthors_plus_upgrade_20 () {
|
|
37 |
//echo '<p>Has Legacy coauthors';
|
38 |
foreach($legacy_coauthors as $legacy_coauthor) {
|
39 |
$legacy_coauthor_login = get_user_by( 'id', (int)$legacy_coauthor );
|
40 |
-
if ( is_object( $legacy_coauthor_login ) ) $coauthors[] = $legacy_coauthor_login->user_login;
|
41 |
}
|
42 |
} else {
|
43 |
// No Legacy coauthors
|
@@ -46,10 +43,4 @@ function coauthors_plus_upgrade_20 () {
|
|
46 |
|
47 |
}
|
48 |
}
|
49 |
-
coauthors_plus_update_version( '2.0' );
|
50 |
-
}
|
51 |
-
|
52 |
-
function coauthors_plus_update_version( $version ) {
|
53 |
-
global $coauthors_plus;
|
54 |
-
update_option($co_authors_plus->get_plugin_option_fullname('version'), $version);
|
55 |
}
|
3 |
// TODO: handle upgrade failures
|
4 |
|
5 |
if( $from < 2.0 ) coauthors_plus_upgrade_20();
|
|
|
|
|
|
|
6 |
}
|
7 |
|
8 |
/**
|
13 |
global $coauthors_plus;
|
14 |
|
15 |
// Get all posts with meta_key _coauthor
|
16 |
+
$all_posts = get_posts(array('numberposts' => '-1', 'meta_key' => '_coauthor'));
|
17 |
|
18 |
if(is_array($all_posts)) {
|
19 |
foreach($all_posts as $single_post) {
|
34 |
//echo '<p>Has Legacy coauthors';
|
35 |
foreach($legacy_coauthors as $legacy_coauthor) {
|
36 |
$legacy_coauthor_login = get_user_by( 'id', (int)$legacy_coauthor );
|
37 |
+
if ( is_object( $legacy_coauthor_login ) && ! in_array( $legacy_coauthor_login->user_login, $coauthors ) ) $coauthors[] = $legacy_coauthor_login->user_login;
|
38 |
}
|
39 |
} else {
|
40 |
// No Legacy coauthors
|
43 |
|
44 |
}
|
45 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
46 |
}
|