PluginProbe
Share a Draft / trunk
Share a Draft vtrunk
1.7 trunk 0.2 0.3 0.4 0.5 0.6 0.7 1.0 1.1 1.2 1.3 1.4 1.5 1.6
shareadraft / shareadraft.php

shareadraft.php in Share a Draft trunk, at shareadraft.php

558 lines 20.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /*
3 Plugin Name: Share a Draft
4 Plugin URI: http://wordpress.org/plugins/shareadraft/
5 Description: Share private preview links to your drafts
6 Author: Nikolay Bachiyski, Automattic
7 Version: 1.7
8 Author URI: https://extrapolate.me/
9 Text Domain: shareadraft
10 Domain Path: /languages
11 */
12
13 if ( ! class_exists( 'Share_a_Draft' ) ) :
14 class Share_a_Draft {
15 var $admin_options_name = 'ShareADraft_options';
16 var $shared_post = null;
17 var $admin_options = array();
18 var $user_options = array();
19
20 function __construct() {
21 add_action( 'init', array( $this, 'init' ) );
22 }
23
24 function init() {
25 global $current_user;
26 add_action( 'admin_menu', array( $this, 'add_admin_pages' ) );
27 add_filter( 'the_posts', array( $this, 'the_posts_intercept' ), 10, 2 );
28 add_filter( 'posts_results', array( $this, 'posts_results_intercept' ), 10, 2 );
29
30 $this->admin_options = $this->get_admin_options();
31 $this->admin_options = $this->clear_expired( $this->admin_options );
32 $this->user_options = array();
33 if ( $current_user->ID > 0 && isset( $this->admin_options[ $current_user->ID ] ) ) {
34 $this->user_options = $this->admin_options[ $current_user->ID ];
35 }
36 $this->save_admin_options();
37 load_plugin_textdomain( 'shareadraft', false, dirname( plugin_basename( __FILE__ ) ) . '/languages' );
38
39 if ( isset( $_GET['page'] ) && $_GET['page'] === plugin_basename( __FILE__ ) ) {
40 $this->admin_page_init();
41 }
42 }
43
44 function admin_page_init() {
45 wp_enqueue_script( 'jquery' );
46 wp_enqueue_script( 'wp-a11y' );
47 add_action( 'admin_head', array( $this, 'print_admin_css' ) );
48 add_action( 'admin_head', array( $this, 'print_admin_js' ) );
49 }
50
51 function get_admin_options() {
52 $saved_options = get_option( $this->admin_options_name );
53 return is_array( $saved_options )? $saved_options : array();
54 }
55
56 function save_admin_options() {
57 global $current_user;
58 if ( $current_user->ID > 0 ) {
59 $this->admin_options[ $current_user->ID ] = $this->user_options;
60 }
61 update_option( $this->admin_options_name, $this->admin_options );
62 }
63
64 function clear_expired( $all_options ) {
65 $all = array();
66 // Orphan cleanup calls get_post() per share, so only run it in the
67 // admin — not on every front-end request. Expired shares are always
68 // dropped because can_view() relies on it to stop serving lapsed links.
69 $prune_orphans = is_admin();
70 foreach ( $all_options as $user_id => $options ) {
71 $shared = array();
72 if ( ! isset( $options['shared'] ) || ! is_array( $options['shared'] ) ) {
73 continue;
74 }
75 foreach ( $options['shared'] as $share ) {
76 if ( $share['expires'] < time() ) {
77 continue;
78 }
79 // Drop shares whose post has since been deleted — a dead link.
80 if ( $prune_orphans && ! get_post( $share['id'] ) ) {
81 continue;
82 }
83 $shared[] = $share;
84 }
85 $options['shared'] = $shared;
86 $all[ $user_id ] = $options;
87 }
88 return $all;
89 }
90
91 function add_admin_pages() {
92 add_submenu_page( 'edit.php', __( 'Share a Draft', 'shareadraft' ), __( 'Share a Draft', 'shareadraft' ),
93 'edit_posts', __FILE__, array( $this, 'output_existing_menu_sub_admin_page' ) );
94 }
95
96 function calculate_seconds( $params ) {
97 $exp = 60;
98 $multiply = 60;
99 if ( isset( $params['expires'] ) && ( $e = intval( $params['expires'] ) ) ) {
100 $exp = $e;
101 }
102 $mults = array(
103 'm' => MINUTE_IN_SECONDS,
104 'h' => HOUR_IN_SECONDS,
105 'd' => DAY_IN_SECONDS,
106 'w' => WEEK_IN_SECONDS,
107 );
108 if ( isset( $params['measure'] ) && isset( $mults[ $params['measure'] ] ) ) {
109 $multiply = $mults[ $params['measure'] ];
110 }
111 return $exp * $multiply;
112 }
113
114 function process_new_share( $params ) {
115 global $current_user;
116 if ( isset( $params['post_id'] ) ) {
117 $p = get_post( $params['post_id'] );
118 if ( ! $p ) {
119 return __( 'There is no such post!', 'shareadraft' );
120 }
121 if ( 'publish' === get_post_status( $p ) ) {
122 return __( 'The post is published!', 'shareadraft' );
123 }
124 if ( ! current_user_can( 'edit_post', $p->ID ) ) {
125 return __( 'Sorry, you are not allowed to share posts you can’t edit.', 'shareadraft' );
126 }
127 $this->user_options['shared'][] = array(
128 'id' => $p->ID,
129 'expires' => time() + $this->calculate_seconds( $params ),
130 'key' => uniqid( 'baba' . $p->ID . '_' ),
131 );
132 $this->save_admin_options();
133 }
134 }
135
136 function process_delete( $params ) {
137 if ( ! isset( $params['key'] ) ||
138 ! isset( $this->user_options['shared'] ) ||
139 ! is_array( $this->user_options['shared'] ) ) {
140 return '';
141 }
142 $shared = array();
143 foreach ( $this->user_options['shared'] as $share ) {
144 if ( $share['key'] === $params['key'] ) {
145 if ( ! current_user_can( 'edit_post', $share['id'] ) ) {
146 return __( 'Sorry, you are not allowed to share posts you can’t edit.', 'shareadraft' );
147 }
148 continue;
149 }
150 $shared[] = $share;
151 }
152 $this->user_options['shared'] = $shared;
153 $this->save_admin_options();
154 }
155
156 function process_extend( $params ) {
157 if ( ! isset( $params['key'] ) ||
158 ! isset( $this->user_options['shared'] ) ||
159 ! is_array( $this->user_options['shared'] ) ) {
160 return '';
161 }
162 $shared = array();
163 foreach ( $this->user_options['shared'] as $share ) {
164 if ( $share['key'] === $params['key'] ) {
165 if ( ! current_user_can( 'edit_post', $share['id'] ) ) {
166 return __( 'Sorry, you are not allowed to share posts you can’t edit.', 'shareadraft' );
167 }
168 $share['expires'] += $this->calculate_seconds( $params );
169 }
170 $shared[] = $share;
171 }
172 $this->user_options['shared'] = $shared;
173 $this->save_admin_options();
174 }
175
176 function get_drafts() {
177 global $current_user;
178 $unpublished_statuses = array( 'pending', 'draft', 'future', 'private' );
179 $my_unpublished = get_posts( array(
180 'post_status' => $unpublished_statuses,
181 'author' => $current_user->ID,
182 // some environments, like WordPress.com hook on those filters
183 // for an extra caching layer
184 'suppress_filters' => false,
185 ) );
186 $others_unpublished = get_posts( array(
187 'post_status' => $unpublished_statuses,
188 'author' => -$current_user->ID,
189 'suppress_filters' => false,
190 'perm' => 'editable',
191 ) );
192 $draft_groups = array(
193 array(
194 'label' => __( 'My unpublished posts:', 'shareadraft' ),
195 'posts' => $my_unpublished,
196 ),
197 array(
198 'label' => __( 'Others’ unpubilshed posts:', 'shareadraft' ),
199 'posts' => $others_unpublished,
200 ),
201 );
202 return $draft_groups;
203 }
204
205 function get_shared() {
206 if ( ! isset( $this->user_options['shared'] ) || ! is_array( $this->user_options['shared'] ) ) {
207 return array();
208 }
209 return $this->user_options['shared'];
210 }
211
212 function friendly_delta( $s ) {
213 $m = (int) ( $s / MINUTE_IN_SECONDS );
214 $h = (int) ( $s / HOUR_IN_SECONDS );
215 $free_m = (int) ( ( $s - $h * HOUR_IN_SECONDS ) / MINUTE_IN_SECONDS );
216 $d = (int) ( $s / DAY_IN_SECONDS );
217 $free_h = (int) ( ( $s - $d * DAY_IN_SECONDS ) / HOUR_IN_SECONDS );
218 if ( $m < 1 ) {
219 $res = array();
220 } elseif ( $h < 1 ) {
221 $res = array( $m );
222 } elseif ( $d < 1 ) {
223 $res = array( $free_m, $h );
224 } else {
225 $res = array( $free_m, $free_h, $d );
226 }
227 $names = array();
228 // Skip zero-valued units, so we get "1 day" rather than "1 day, 0 hours, 0 minutes".
229 if ( ! empty( $res[0] ) ) {
230 $names[] = sprintf( _n( '%d minute', '%d minutes', $res[0], 'shareadraft' ), $res[0] );
231 }
232 if ( ! empty( $res[1] ) ) {
233 $names[] = sprintf( _n( '%d hour', '%d hours', $res[1], 'shareadraft' ), $res[1] );
234 }
235 if ( ! empty( $res[2] ) ) {
236 $names[] = sprintf( _n( '%d day', '%d days', $res[2], 'shareadraft' ), $res[2] );
237 }
238 if ( empty( $names ) ) {
239 return __( 'less than a minute', 'shareadraft' );
240 }
241 return implode( ', ', array_reverse( $names ) );
242 }
243
244 function output_existing_menu_sub_admin_page() {
245 $msg = '';
246 if ( isset( $_POST['shareadraft_submit'] ) ) {
247 check_admin_referer( 'shareadraft-new-share' );
248 $msg = $this->process_new_share( $_POST );
249 } elseif ( isset( $_POST['action'] ) && $_POST['action'] === 'extend' ) {
250 check_admin_referer( 'shareadraft-extend' );
251 $msg = $this->process_extend( $_POST );
252 } elseif ( isset( $_GET['action'] ) && $_GET['action'] === 'delete' ) {
253 check_admin_referer( 'shareadraft-delete' );
254 $msg = $this->process_delete( $_GET );
255 }
256 $draft_groups = $this->get_drafts();
257 ?>
258 <div class="wrap">
259 <h1 class="wp-heading-inline"><?php _e( 'Share a Draft', 'shareadraft' ); ?></h1>
260 <a href="#" id="shareadraft-add-toggle" class="page-title-action" aria-expanded="false" aria-controls="shareadraft-add"><?php _e( 'Add draft link', 'shareadraft' ); ?></a>
261 <hr class="wp-header-end">
262 <?php if ( $msg ) :?>
263 <div id="message" class="updated fade"><?php echo $msg; ?></div>
264 <?php endif;?>
265 <div id="shareadraft-add" class="shareadraft-add-section" style="display: none;">
266 <h3><?php _e( 'Add draft link', 'shareadraft' ); ?></h3>
267 <form id="shareadraft-share" action="" method="post">
268 <p>
269 <select id="shareadraft-postid" name="post_id">
270 <option value=""><?php _e( 'Choose a draft', 'shareadraft' ); ?></option>
271 <?php
272 foreach ( $draft_groups as $draft_group ) :
273 if ( $draft_group['posts'] ) :
274 ?>
275 <option value="" disabled="disabled"></option>
276 <option value="" disabled="disabled"><?php echo $draft_group['label']; ?></option>
277 <?php
278 foreach ( $draft_group['posts'] as $draft ) :
279 if ( empty( $draft->post_title ) ) {
280 continue;
281 }
282 ?>
283 <option value="<?php echo $draft->ID?>"><?php echo esc_html( $draft->post_title ); ?></option>
284 <?php
285 endforeach;
286 endif;
287 endforeach;
288 ?>
289 </select>
290 </p>
291 <p>
292 <input type="submit" class="button" name="shareadraft_submit"
293 value="<?php echo esc_attr__( 'Share it', 'shareadraft' ); ?>" />
294 <?php _e( 'for', 'shareadraft' ); ?>
295 <?php echo $this->tmpl_measure_select(); ?>
296 </p>
297 <?php wp_nonce_field( 'shareadraft-new-share' ); ?>
298 </form>
299 </div>
300 <h3><?php _e( 'Shareable drafts', 'shareadraft' ); ?></h3>
301 <table class="widefat">
302 <thead>
303 <tr>
304 <th><?php _e( 'Post ID', 'shareadraft' ); ?></th>
305 <th><?php _e( 'Title', 'shareadraft' ); ?></th>
306 <th><?php _e( 'Link', 'shareadraft' ); ?></th>
307 <th><?php _e( 'Expires in', 'shareadraft' ); ?></th>
308 <th colspan="2" class="actions"><?php _e( 'Actions', 'shareadraft' ); ?></th>
309 </tr>
310 </thead>
311 <tbody>
312 <?php
313 $s = $this->get_shared();
314 foreach ( $s as $share ) :
315 $p = get_post( $share['id'] );
316 $friendly_delta = $this->friendly_delta( $share['expires'] - time() );
317 $iso_expires = date_i18n( 'c', $share['expires'] );
318 $delete_url = 'edit.php?page=' . plugin_basename( __FILE__ ) . '&action=delete&key=' . $share['key'];
319 $nonced_delete_url = wp_nonce_url( $delete_url, 'shareadraft-delete' );
320 // get_shared() has already pruned shares whose post was deleted; this is
321 // just a safety net in case the post vanished mid-request.
322 if ( ! $p ) {
323 continue;
324 }
325 $url = get_bloginfo( 'url' ) . '/?p=' . $p->ID . '&shareadraft=' . $share['key'];
326 ?>
327 <tr>
328 <td data-colname="<?php echo esc_attr__( 'Post ID', 'shareadraft' ); ?>"><?php echo $p->ID; ?></td>
329 <td data-colname="<?php echo esc_attr__( 'Title', 'shareadraft' ); ?>"><?php echo esc_html( $p->post_title ); ?></td>
330 <td data-colname="<?php echo esc_attr__( 'Link', 'shareadraft' ); ?>">
331 <a href="<?php echo esc_url( $url ); ?>"><?php echo esc_html( $url ); ?></a>
332 <a href="#" class="shareadraft-copy" data-shareadraft-url="<?php echo esc_url( $url ); ?>"
333 title="<?php echo esc_attr__( 'Copy link to clipboard', 'shareadraft' ); ?>"
334 aria-label="<?php echo esc_attr__( 'Copy link to clipboard', 'shareadraft' ); ?>"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"></path><rect x="8" y="2" width="8" height="4" rx="1" ry="1"></rect></svg></a>
335 <span class="shareadraft-copied"><?php esc_html_e( 'Copied!', 'shareadraft' ); ?></span>
336 </td>
337 <td data-colname="<?php echo esc_attr__( 'Expires in', 'shareadraft' ); ?>"><time title="<?php echo $iso_expires; ?>" datetime="<?php echo $iso_expires; ?>"><?php echo $friendly_delta; ?></time></td>
338 <td class="actions" data-colname="<?php echo esc_attr__( 'Actions', 'shareadraft' ); ?>">
339 <a class="shareadraft-extend edit" id="shareadraft-extend-link-<?php echo $share['key']; ?>"
340 href="javascript:shareadraft.toggle_extend( '<?php echo $share['key']; ?>' );">
341 <?php _e( 'Extend', 'shareadraft' ); ?>
342 </a>
343 </td>
344 <td class="actions">
345 <a class="delete" href="<?php echo esc_url( $nonced_delete_url ); ?>"><?php _e( 'Delete', 'shareadraft' ); ?></a>
346 </td>
347 </tr>
348 <tr class="shareadraft-extend-row" id="shareadraft-extend-form-<?php echo $share['key']; ?>">
349 <td colspan="6">
350 <form class="shareadraft-extend" action="" method="post">
351 <input type="hidden" name="action" value="extend" />
352 <input type="hidden" name="key" value="<?php echo $share['key']; ?>" />
353 <label for="shareadraft-extend-expires-<?php echo $share['key']; ?>"><?php _e( 'Extend by', 'shareadraft' ); ?></label>
354 <?php echo $this->tmpl_measure_select( 'shareadraft-extend-expires-' . $share['key'] ); ?>
355 <input type="submit" class="button button-primary" name="shareadraft_extend_submit"
356 value="<?php echo esc_attr__( 'Extend', 'shareadraft' ); ?>" />
357 <a class="shareadraft-extend-cancel"
358 href="javascript:shareadraft.cancel_extend( '<?php echo $share['key']; ?>' );">
359 <?php _e( 'Cancel', 'shareadraft' ); ?>
360 </a>
361 <?php wp_nonce_field( 'shareadraft-extend' ); ?>
362 </form>
363 </td>
364 </tr>
365 <?php
366 endforeach;
367 if ( empty( $s ) ) :
368 ?>
369 <tr>
370 <td colspan="6"><?php _e( 'No shared drafts!', 'shareadraft' ); ?></td>
371 </tr>
372 <?php
373 endif;
374 ?>
375 </tbody>
376 </table>
377 </div>
378 <?php
379 }
380
381 function can_view( $post_id ) {
382 if ( ! isset( $_GET['shareadraft'] ) || ! is_array( $this->admin_options ) ) {
383 return false;
384 }
385 foreach ( $this->admin_options as $option ) {
386 if ( ! is_array( $option ) || ! isset( $option['shared'] ) ) {
387 continue;
388 }
389 $shares = $option['shared'];
390 foreach ( $shares as $share ) {
391 // Cast both sides: stored ids and ids coming back from the query
392 // are not guaranteed to agree on int vs. string.
393 if ( (int) $share['id'] === (int) $post_id && $share['key'] === $_GET['shareadraft'] ) {
394 return true;
395 }
396 }
397 }
398 return false;
399 }
400
401 function posts_results_intercept( $posts, $query = null ) {
402 // Only ever act on the main front-end query. Block themes run many
403 // secondary queries (template parts, patterns) that must be left alone.
404 if ( ! $query || ! $query->is_main_query() ) {
405 return $posts;
406 }
407 if ( 1 !== count( $posts ) ) {
408 return $posts;
409 }
410 $post = $posts[0];
411 $status = get_post_status( $post );
412 if ( 'publish' !== $status && $this->can_view( $post->ID ) ) {
413 $this->shared_post = $post;
414 }
415 return $posts;
416 }
417
418 function the_posts_intercept( $posts, $query = null ) {
419 // See posts_results_intercept(): never inject into secondary queries,
420 // or the shared post leaks into template-part lookups and breaks them.
421 if ( ! $query || ! $query->is_main_query() ) {
422 return $posts;
423 }
424 if ( empty( $posts ) && ! is_null( $this->shared_post ) ) {
425 return array( $this->shared_post );
426 } else {
427 $this->shared_post = null;
428 return $posts;
429 }
430 }
431
432 function tmpl_measure_select( $expires_id = '' ) {
433 $mins = __( 'minutes', 'shareadraft' );
434 $hours = __( 'hours', 'shareadraft' );
435 $days = __( 'days', 'shareadraft' );
436 $weeks = __( 'weeks', 'shareadraft' );
437 $id_attr = $expires_id ? ' id="' . esc_attr( $expires_id ) . '"' : '';
438 return <<<SELECT
439 <input name="expires"$id_attr type="text" value="2" size="4"/>
440 <select name="measure">
441 <option value="m">$mins</option>
442 <option value="h">$hours</option>
443 <option value="d">$days</option>
444 <option value="w" selected>$weeks</option>
445 </select>
446 SELECT;
447 }
448
449 function print_admin_css() {
450 ?>
451 <style type="text/css">
452 a.shareadraft-extend, a.shareadraft-extend-cancel { display: none; }
453 tr.shareadraft-extend-row { display: none; }
454 tr.shareadraft-extend-row.is-open { display: table-row; }
455 tr.shareadraft-extend-row td { background: #f6f7f7; padding: 12px 10px; }
456 form.shareadraft-extend { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 0; }
457 form.shareadraft-extend label { font-weight: 600; }
458 th.actions, td.actions { text-align: center; }
459 table.widefat td a { padding: 2px; }
460 a.shareadraft-copy { text-decoration: none; vertical-align: middle; margin-left: 6px; }
461 a.shareadraft-copy svg { vertical-align: middle; position: relative; top: -2px; }
462 span.shareadraft-copied { margin-left: 6px; font-size: 11px; color: #268e26; visibility: hidden; }
463 span.shareadraft-copied.is-visible { visibility: visible; }
464 #shareadraft-share input, #shareadraft-share select { vertical-align: middle; }
465 @media screen and (max-width: 782px) {
466 /* Stack the shared-drafts table into labelled cards on small screens. */
467 table.widefat { border: none; box-shadow: none; background: transparent; }
468 table.widefat thead { display: none; }
469 table.widefat tr { display: block; margin-bottom: 1em; border: 1px solid #c3c4c7; background: #fff; }
470 table.widefat td { display: block; width: auto; text-align: left; border: none; border-bottom: 1px solid #f0f0f1; padding: 8px 10px; }
471 table.widefat tr td:last-child { border-bottom: none; }
472 table.widefat td.actions { text-align: left; }
473 table.widefat td[data-colname]::before {
474 content: attr(data-colname);
475 display: block;
476 font-weight: 600;
477 margin-bottom: 2px;
478 }
479 table.widefat td a[href^="http"] { word-break: break-all; }
480 a.shareadraft-copy { margin-left: 0; }
481 /* Higher specificity than 'table.widefat tr { display: block }' above,
482 so the extend row stays collapsed until toggled open. */
483 table.widefat tr.shareadraft-extend-row { display: none; }
484 table.widefat tr.shareadraft-extend-row.is-open { display: block; }
485 }
486 </style>
487 <?php
488 }
489
490 function print_admin_js() {
491 ?>
492 <script type="text/javascript">
493 //<![CDATA[
494 ( function( $ ) {
495 $( function() {
496 $( 'a.shareadraft-extend' ).show();
497 $( 'a.shareadraft-extend-cancel' ).show();
498 $( 'a.shareadraft-extend-cancel' ).css( 'display', 'inline' );
499
500 $( document ).on( 'click', 'a.shareadraft-copy', function( e ) {
501 e.preventDefault();
502 var $link = $( this );
503 var url = $link.attr( 'data-shareadraft-url' );
504 var confirm = function() {
505 var $msg = $link.siblings( 'span.shareadraft-copied' );
506 $msg.addClass( 'is-visible' );
507 if ( window.wp && window.wp.a11y && window.wp.a11y.speak ) {
508 window.wp.a11y.speak( '<?php echo esc_js( __( 'Copied!', 'shareadraft' ) ); ?>' );
509 }
510 window.setTimeout( function() { $msg.removeClass( 'is-visible' ); }, 2000 );
511 };
512 if ( window.navigator.clipboard && window.navigator.clipboard.writeText ) {
513 window.navigator.clipboard.writeText( url ).then( confirm );
514 } else {
515 // Fallback for browsers without the async Clipboard API (e.g. non-HTTPS contexts).
516 var $tmp = $( '<textarea>' ).val( url ).css( { position: 'fixed', top: 0, left: '-9999px' } ).appendTo( 'body' );
517 $tmp[0].select();
518 try { document.execCommand( 'copy' ); } catch ( err ) {}
519 $tmp.remove();
520 confirm();
521 }
522 } );
523
524 $( '#shareadraft-add-toggle' ).on( 'click', function( e ) {
525 e.preventDefault();
526 var $toggle = $( this );
527 var expanded = $toggle.attr( 'aria-expanded' ) === 'true';
528 $toggle.attr( 'aria-expanded', expanded ? 'false' : 'true' );
529 $( '#shareadraft-add' ).slideToggle( 'fast', function() {
530 if ( ! expanded ) {
531 $( '#shareadraft-postid' ).trigger( 'focus' );
532 }
533 } );
534 } );
535 } );
536 window.shareadraft = {
537 toggle_extend: function( key ) {
538 $( '#shareadraft-extend-form-'+key ).addClass( 'is-open' );
539 $( '#shareadraft-extend-link-'+key ).hide();
540 $( '#shareadraft-extend-form-'+key+' input[name="expires"]' ).focus();
541 },
542 cancel_extend: function( key ) {
543 $( '#shareadraft-extend-form-'+key ).removeClass( 'is-open' );
544 $( '#shareadraft-extend-link-'+key ).show();
545 }
546 };
547 } )( jQuery );
548 //]]>
549 </script>
550 <?php
551 }
552 }
553 endif;
554
555 if ( class_exists( 'Share_a_Draft' ) ) {
556 $__share_a_draft = new Share_a_Draft();
557 }
558