| 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 |
|