PluginProbe ʕ •ᴥ•ʔ
Admin and Site Enhancements (ASE) / 4.7.2
Admin and Site Enhancements (ASE) v4.7.2
9.1.0 9.0.2 9.0.1 9.0.0 8.9.2 8.9.1 8.9.0 8.8.8 8.8.7 8.8.6 8.8.5 8.8.4 8.8.3 8.8.2 8.8.1 8.8.0 8.7.3 8.7.2 8.7.1 8.2.1 8.2.2 8.2.3 8.3.0 8.3.1 8.3.2 8.4.0 8.4.1 8.4.2 8.5.0 8.5.1 8.5.2 8.6.0 8.6.1 8.6.2 8.7.0 5.0.1 5.0.2 5.0.2.1 5.0.2.2 5.0.2.3 5.0.2.4 5.1.0 5.2.0 5.2.1 5.2.10 5.2.11 5.2.2 5.2.3 5.2.4 5.2.5 5.2.6 5.2.7 5.2.8 5.2.9 5.3.0 5.3.1 5.3.2 5.4.0 5.4.1 5.5.0 5.5.1 5.5.2 5.6.0 5.6.1 5.6.2 5.7.0 5.7.1 5.8.0 5.8.1 6.0.0 6.0.3 6.0.4 6.0.5 6.0.5.1 6.0.6 6.0.7 6.0.8.1 6.1.0 6.1.3 6.2.0 6.2.1 6.2.2 6.2.3 6.2.4 6.2.5 6.2.6 6.2.7 6.3.0 6.3.1 6.3.2 6.4.0 6.5.0 6.5.1 6.6.0 6.7.0 6.8.0 6.8.2 6.8.3 6.9.0 6.9.1 6.9.10 6.9.11 6.9.12 6.9.13.1 6.9.13.2 6.9.2 6.9.3 6.9.4 6.9.5 6.9.6 6.9.7 6.9.8 6.9.9 7.0.0 7.0.2 7.0.2.1 7.0.2.2 7.0.2.3 7.0.3 7.1.0 7.1.1 7.1.2 7.1.3 7.1.4 7.1.5 7.2.0 7.2.1 7.3.0 7.3.1 7.3.2 7.3.3 7.4.0 7.4.2 7.4.4 7.4.5 7.4.6 7.4.7 7.4.8 7.5.0 7.5.1 7.5.2 7.5.3 7.5.4 7.6.0 7.6.1 7.6.1.1 7.6.10 7.6.11 7.6.2 7.6.3 7.6.4 7.6.5 7.6.6 7.6.7 7.6.7.1 7.6.8 7.6.9 7.7.0 7.8.0 7.8.1 7.8.10 7.8.11 7.8.12 7.8.13 7.8.14 7.8.15 7.8.16 7.8.17 7.8.18 7.8.2 7.8.3 7.8.4 7.8.5 7.8.5.1 7.8.6 7.8.7 7.8.8 7.8.9 7.9.0 7.9.1 7.9.10 7.9.11 7.9.2 7.9.3 7.9.4 7.9.5 7.9.6 7.9.7 7.9.8 7.9.9 8.0.0 8.0.1 8.0.2 8.0.3 8.0.4 8.0.5 8.0.6 8.0.7 8.0.8 8.1.0 8.1.1 8.1.2 8.1.3 8.1.4 8.2.0 trunk 1.0.0 1.1.0 1.2.0 1.3.0 1.4.0 1.5.0 1.6.0 1.7.0 1.8.0 1.9.0 2.0.0 2.1.0 2.2.0 2.3.0 2.4.0 2.5.0 2.6.0 2.7.0 2.8.0 2.8.1 2.8.2 2.8.3 2.9.0 3.0.0 3.0.1 3.0.2 3.0.3 3.0.4 3.0.5 3.0.6 3.1.0 3.2.0 3.3.0 3.4.0 3.5.0 3.6.1 3.7.0 3.8.0 3.9.0 3.9.1 3.9.2 4.0.0 4.0.1 4.1.0 4.2.0 4.2.1 4.2.2 4.3.0 4.3.1 4.4.0 4.5.0 4.6.0 4.7.0 4.7.1 4.7.2 4.7.3 4.7.4 4.8.0 4.8.1 4.8.2 4.8.3 4.9.0 4.9.1 4.9.2 4.9.3 5.0.0
admin-site-enhancements / classes / class-content-management.php
admin-site-enhancements / classes Last commit date
class-activation.php 3 years ago class-admin-interface.php 3 years ago class-common-methods.php 3 years ago class-content-management.php 3 years ago class-custom-code.php 3 years ago class-deactivation.php 3 years ago class-disable-components.php 3 years ago class-login-logout.php 3 years ago class-optimizations.php 3 years ago class-security.php 3 years ago class-settings-fields-render.php 3 years ago class-settings-sanitization.php 3 years ago class-settings-sections-fields.php 3 years ago class-utilities.php 3 years ago
class-content-management.php
1103 lines
1 <?php
2
3 namespace ASENHA\Classes;
4 use enshrined\svgSanitize\Sanitizer; // For Enable SVG Upload
5
6 /**
7 * Class related to Content Management features
8 *
9 * @since 1.0.0
10 */
11 class Content_Management {
12
13 /**
14 * Current post type. For Content Admin >> Show Custom Taxonomy Filters functionality.
15 */
16 public $post_type;
17
18 /**
19 * Show featured images column in list tables for pages and post types that support featured image
20 *
21 * @since 1.0.0
22 */
23 public function show_featured_image_column() {
24
25 $post_types = get_post_types( array( 'public' => true ), 'names' );
26
27 foreach ( $post_types as $post_type_key => $post_type_name ) {
28
29 if ( post_type_supports( $post_type_key, 'thumbnail' ) ) {
30
31 add_filter( "manage_{$post_type_name}_posts_columns",[ $this, 'add_featured_image_column' ] );
32 add_action( "manage_{$post_type_name}_posts_custom_column", [ $this, 'add_featured_image' ], 10, 2 );
33
34 }
35
36 }
37
38 }
39
40 /**
41 * Add a column called Featured Image as the first column
42 *
43 * @param mixed $columns
44 * @return void
45 * @since 1.0.0
46 */
47 public function add_featured_image_column( $columns ) {
48
49 $new_columns = array();
50
51 foreach ( $columns as $key => $value ) {
52
53 if ( $key == 'title' ) {
54
55 $new_columns['asenha-featured-image'] = 'Featured Image';
56
57 }
58
59 $new_columns[$key] = $value;
60
61 }
62
63 return $new_columns;
64
65 }
66
67 /**
68 * Echo featured image's in thumbnail size to a column
69 *
70 * @param mixed $column_name
71 * @param mixed $id
72 * @since 1.0.0
73 */
74 public function add_featured_image( $column_name, $id ) {
75
76 if ( 'asenha-featured-image' === $column_name ) {
77
78 if ( has_post_thumbnail( $id ) ) {
79
80 $size = 'thumbnail';
81
82 echo get_the_post_thumbnail( $id, $size, '' );
83
84 } else {
85
86 echo '<img src="' . esc_url( plugins_url( 'assets/img/default_featured_image.jpg', __DIR__ ) ) . '" />';
87
88 }
89 }
90
91 }
92
93 /**
94 * Show excerpt column in list tables for pages and post types that support excerpt.
95 *
96 * @since 1.0.0
97 */
98 public function show_excerpt_column() {
99
100 $post_types = get_post_types( array( 'public' => true ), 'names' );
101
102 foreach ( $post_types as $post_type_key => $post_type_name ) {
103
104 if ( post_type_supports( $post_type_key, 'excerpt' ) ) {
105
106 add_filter( "manage_{$post_type_name}_posts_columns",[ $this, 'add_excerpt_column' ] );
107 add_action( "manage_{$post_type_name}_posts_custom_column", [ $this, 'add_excerpt' ], 10, 2 );
108
109 }
110
111 }
112
113 }
114
115 /**
116 * Add a column called Featured Image as the first column
117 *
118 * @param mixed $columns
119 * @return void
120 * @since 1.0.0
121 */
122 public function add_excerpt_column( $columns ) {
123
124 $new_columns = array();
125
126 foreach ( $columns as $key => $value ) {
127
128 $new_columns[$key] = $value;
129
130 if ( $key == 'title' ) {
131
132 $new_columns['asenha-excerpt'] = 'Excerpt';
133
134 }
135
136 }
137
138 return $new_columns;
139
140 }
141
142 /**
143 * Echo featured image's in thumbnail size to a column
144 *
145 * @param mixed $column_name
146 * @param mixed $id
147 * @since 1.0.0
148 */
149 public function add_excerpt( $column_name, $id ) {
150
151 if ( 'asenha-excerpt' === $column_name ) {
152
153 $excerpt = get_the_excerpt( $id ); // about 310 characters
154 $excerpt = substr( $excerpt, 0, 160 ); // truncate to 160 characters
155 $short_excerpt = substr( $excerpt, 0, strrpos( $excerpt, ' ' ) );
156
157 echo wp_kses_post( $short_excerpt );
158
159 }
160
161 }
162
163 /**
164 * Add ID column list table of pages, posts, custom post types, media, taxonomies, custom taxonomies, users amd comments
165 *
166 * @since 1.0.0
167 */
168 public function show_id_column() {
169
170 // For pages and hierarchical post types list table
171
172 add_filter( 'manage_pages_columns', [ $this, 'add_id_column' ] );
173 add_action( 'manage_pages_custom_column', [ $this, 'add_id_echo_value' ], 10, 2 );
174
175 // For posts and non-hierarchical custom posts list table
176
177 add_filter( 'manage_posts_columns', [ $this, 'add_id_column' ] );
178 add_action( 'manage_posts_custom_column', [ $this, 'add_id_echo_value' ], 10, 2 );
179
180 // For media list table
181
182 add_filter( 'manage_media_columns', [ $this, 'add_id_column' ] );
183 add_action( 'manage_media_custom_column', [ $this, 'add_id_echo_value' ], 10, 2 );
184
185 // For list table of all taxonomies
186
187 $taxonomies = get_taxonomies( [ 'public' => true ], 'names' );
188
189 foreach ( $taxonomies as $taxonomy ) {
190
191 add_filter( 'manage_edit-' . $taxonomy . '_columns', [ $this, 'add_id_column' ] );
192 add_action( 'manage_' . $taxonomy . '_custom_column', [ $this, 'add_id_return_value' ], 10, 3 );
193
194 }
195
196 // For users list table
197
198 add_filter( 'manage_users_columns', [ $this, 'add_id_column' ]);
199 add_action( 'manage_users_custom_column', [ $this, 'add_id_return_value' ], 10, 3 );
200
201 // For comments list table
202
203 add_filter( 'manage_edit-comments_columns', [ $this, 'add_id_column' ]);
204 add_action( 'manage_comments_custom_column', [ $this, 'add_id_echo_value' ], 10, 3 );
205
206 }
207
208 /**
209 * Add a column called ID
210 *
211 * @param mixed $columns
212 * @return void
213 * @since 1.0.0
214 */
215 public function add_id_column( $columns ) {
216
217 $columns['asenha-id'] = 'ID';
218
219 return $columns;
220
221 }
222
223 /**
224 * Echo post ID value to a column
225 *
226 * @param mixed $column_name
227 * @param mixed $id
228 * @since 1.0.0
229 */
230 public function add_id_echo_value( $column_name, $id ) {
231
232 if ( 'asenha-id' === $column_name ) {
233 echo esc_html( $id );
234 }
235
236 }
237
238 /**
239 * Return post ID value to a column
240 *
241 * @param mixed $value
242 * @param mixed $column_name
243 * @param mixed $id
244 * @since 1.0.0
245 */
246 public function add_id_return_value( $value, $column_name, $id ) {
247
248 if ( 'asenha-id' === $column_name ) {
249 $value = $id;
250 }
251
252 return $value;
253
254 }
255
256 /**
257 * Hide comments column in list tables for pages, post types that support comments, and alse media/attachments.
258 *
259 * @since 1.0.0
260 */
261 public function hide_comments_column() {
262
263 $post_types = get_post_types( array( 'public' => true ), 'names' );
264
265 foreach ( $post_types as $post_type_key => $post_type_name ) {
266
267 if ( post_type_supports( $post_type_key, 'comments' ) ) {
268
269 if ( 'attachment' != $post_type_name ) {
270
271 // For list tables of pages, posts and other post types
272 add_filter( "manage_{$post_type_name}_posts_columns", [ $this, 'remove_comment_column' ] );
273
274 } else {
275
276 // For list table of media/attachment
277 add_filter( 'manage_media_columns', [ $this, 'remove_comment_column' ] );
278
279 }
280
281 }
282
283 }
284
285 }
286
287 /**
288 * Add a column called ID
289 *
290 * @param mixed $columns
291 * @return void
292 * @since 1.0.0
293 */
294 public function remove_comment_column( $columns ) {
295
296 unset( $columns['comments'] );
297
298 return $columns;
299
300 }
301
302 /**
303 * Hide tags column in list tables for posts.
304 *
305 * @since 1.0.0
306 */
307 public function hide_post_tags_column() {
308
309 $post_types = get_post_types( array( 'public' => true ), 'names' );
310
311 foreach ( $post_types as $post_type_key => $post_type_name ) {
312
313 if ( $post_type_name == 'post' ) {
314
315 add_filter( "manage_posts_columns", [ $this, 'remove_post_tags_column' ] );
316
317 }
318
319 }
320
321 }
322
323 /**
324 * Add a column called ID
325 *
326 * @param mixed $columns
327 * @return void
328 * @since 1.0.0
329 */
330 public function remove_post_tags_column( $columns ) {
331
332 unset( $columns['tags'] );
333
334 return $columns;
335
336 }
337
338 /**
339 * Show custom (hierarchical) taxonomy filter(s) for all post types.
340 *
341 * @since 1.0.0
342 */
343 public function show_custom_taxonomy_filters( $post_type ) {
344
345 $post_taxonomies = get_object_taxonomies( $post_type, 'objects' );
346
347 // Only show custom taxonomy filters for post types other than 'post'
348
349 if ( 'post' != $post_type ) {
350
351 array_walk( $post_taxonomies, [ $this, 'output_taxonomy_filter' ] );
352
353 }
354
355 }
356
357 /**
358 * Output filter on the post type's list table for a taxonomy
359 *
360 * @since 1.0.0
361 */
362 public function output_taxonomy_filter( $post_taxonomy ) {
363
364 // Only show taxonomy filter when the taxonomy is hierarchical
365
366 if ( true === $post_taxonomy->hierarchical ) {
367
368 wp_dropdown_categories( array(
369 'show_option_all' => sprintf( 'All %s', $post_taxonomy->label ),
370 'orderby' => 'name',
371 'order' => 'ASC',
372 'hide_empty' => false,
373 'hide_if_empty' => true,
374 'selected' => filter_input( INPUT_GET, $post_taxonomy->query_var, FILTER_SANITIZE_STRING ),
375 'hierarchical' => true,
376 'name' => $post_taxonomy->query_var,
377 'taxonomy' => $post_taxonomy->name,
378 'value_field' => 'slug',
379 ) );
380
381 }
382
383 }
384
385 /**
386 * Enable duplication of pages, posts and custom posts
387 *
388 * @since 1.0.0
389 */
390 public function asenha_enable_duplication() {
391
392 $original_post_id = intval( sanitize_text_field( $_REQUEST['post'] ) );
393 $nonce = sanitize_text_field( $_REQUEST['nonce'] );
394
395 if ( wp_verify_nonce( $nonce, 'asenha-duplicate-' . $original_post_id ) && current_user_can( 'edit_posts' ) ) {
396
397 $original_post = get_post( $original_post_id );
398
399 // Set some attributes for the duplicate post
400
401 $new_post_title_suffix = ' (DUPLICATE)';
402 $new_post_status = 'draft';
403 $current_user = wp_get_current_user();
404 $new_post_author_id = $current_user->ID;
405
406 // Create the duplicate post and store the ID
407
408 $args = array(
409
410 'comment_status' => $original_post->comment_status,
411 'ping_status' => $original_post->ping_status,
412 'post_author' => $new_post_author_id,
413 'post_content' => $original_post->post_content,
414 'post_excerpt' => $original_post->post_excerpt,
415 'post_parent' => $original_post->post_parent,
416 'post_password' => $original_post->post_password,
417 'post_status' => $new_post_status,
418 'post_title' => $original_post->post_title . $new_post_title_suffix,
419 'post_type' => $original_post->post_type,
420 'to_ping' => $original_post->to_ping,
421 'menu_order' => $original_post->menu_order,
422
423 );
424
425 $new_post_id = wp_insert_post( $args );
426
427 // Copy over the taxonomies
428
429 $original_taxonomies = get_object_taxonomies( $original_post->post_type );
430
431 if ( ! empty( $original_taxonomies ) && is_array( $original_taxonomies ) ) {
432
433 foreach( $original_taxonomies as $taxonomy ) {
434
435 $original_post_terms = wp_get_object_terms( $original_post_id, $taxonomy, array( 'fields' => 'slugs' ) );
436
437 wp_set_object_terms( $new_post_id, $original_post_terms, $taxonomy, false );
438
439 }
440
441 }
442
443 // Copy over the post meta
444
445 $original_post_metas = get_post_meta( $original_post_id ); // all meta keys and the corresponding values
446
447 if ( ! empty( $original_post_metas ) ) {
448
449 foreach( $original_post_metas as $meta_key => $meta_values ) {
450
451 foreach( $meta_values as $meta_value ) {
452
453 add_post_meta( $new_post_id, $meta_key, $meta_value );
454
455 }
456
457 }
458
459 }
460
461 // Redirect to list table of the corresponding post type of original post
462
463 $post_type = get_post_type( $original_post_id );
464
465 if ( 'post' == $post_type ) {
466
467 wp_redirect( admin_url( 'edit.php' ) );
468
469 } else {
470
471 wp_redirect( admin_url( 'edit.php?post_type=' . $post_type ) );
472
473 }
474
475 } else {
476
477 wp_die( 'You do not have permission to perform this action.' );
478
479 }
480
481 }
482
483 /**
484 * Add row action link to perform duplication in page/post list tables
485 *
486 * @since 1.0.0
487 */
488 public function add_duplication_action_link( $actions, $post ) {
489
490 if ( current_user_can( 'edit_posts' ) ) {
491
492 $actions['asenha-duplicate'] = '<a href="admin.php?action=asenha_enable_duplication&amp;post=' . $post->ID . '&amp;nonce=' . wp_create_nonce( 'asenha-duplicate-' . $post->ID ) . '" title="Duplicate this as draft">Duplicate</a>';
493
494 }
495
496 return $actions;
497
498 }
499
500 /**
501 * Modify the 'Edit' link to be 'Edit or Replace'
502 *
503 */
504 public function modify_media_list_table_edit_link( $actions, $post ) {
505
506 $new_actions = array();
507
508 foreach( $actions as $key => $value ) {
509
510 if ( $key == 'edit' ) {
511
512 $new_actions['edit'] = '<a href="' . get_edit_post_link( $post ) . '" aria-label="Edit or Replace">Edit or Replace</a>';
513
514 } else {
515
516 $new_actions[$key] = $value;
517
518 }
519
520 }
521
522 return $new_actions;
523
524 }
525
526 /**
527 * Add media replacement button in the edit screen of media/attachment
528 *
529 * @since 1.1.0
530 */
531 public function add_media_replacement_button( $fields, $post ) {
532
533 // Enqueues all scripts, styles, settings, and templates necessary to use all media JS APIs.
534 // Reference: https://codex.wordpress.org/Javascript_Reference/wp.media
535 wp_enqueue_media();
536
537 // Add new field to attachment fields for the media replace functionality
538 $fields['asenha-media-replace'] = array();
539 $fields['asenha-media-replace']['label'] = '';
540 $fields['asenha-media-replace']['input'] = 'html';
541 $fields['asenha-media-replace']['html'] = '
542 <div id="media-replace-div" class="postbox">
543 <div class="postbox-header">
544 <h2 class="hndle ui-sortable-handle">Replace Media</h2>
545 </div>
546 <div class="inside">
547 <button type="button" id="asenha-media-replace" class="button-secondary button-large asenha-media-replace-button">Select New Media File</button>
548 <input type="hidden" id="new-attachment-id" name="new-attachment-id" />
549 <div class="asenha-media-replace-notes">The current file will be replaced with the uploaded and/or selected file while retaining the current ID, publish date and file name. Thus, no existing links will break.</div>
550 </div>
551 </div>
552 ';
553
554 return $fields;
555
556 }
557
558 /**
559 * Replace existing media with the newly updated file
560 *
561 * @link https://plugins.trac.wordpress.org/browser/replace-image/tags/1.1.7/hm-replace-image.php#L55
562 * @since 1.1.0
563 */
564 public function replace_media( $old_attachment_id ) {
565
566 $old_post_meta = get_post( $old_attachment_id, ARRAY_A );
567 $old_post_mime = $old_post_meta['post_mime_type']; // e.g. 'image/jpeg'
568
569 // Get the new attachment/media ID, meta and mime type
570 $new_attachment_id = intval( sanitize_text_field( $_POST['new-attachment-id'] ) );
571 $new_post_meta = get_post( $new_attachment_id, ARRAY_A );
572 $new_post_mime = $new_post_meta['post_mime_type']; // e.g. 'image/jpeg'
573
574 // Check if the media file ID selected via the media frame and passed on to the #new-attachment-id hidden field
575 // Ensure the mime type matches too
576 if ( ( ! empty( $new_attachment_id ) ) && is_numeric( $new_attachment_id ) && ( $old_post_mime == $new_post_mime ) ) {
577
578 $new_attachment_meta = wp_get_attachment_metadata( $new_attachment_id );
579
580 // If original file is larger than 2560 pixel
581 // https://make.wordpress.org/core/2019/10/09/introducing-handling-of-big-images-in-wordpress-5-3/
582 if ( array_key_exists( 'original_image', $new_attachment_meta ) ) {
583
584 // Get the original media file path
585 $new_media_file_path = wp_get_original_image_path( $new_attachment_id );
586
587 } else {
588
589 // Get the path to newly uploaded media file. An image file name may end with '-scaled'.
590 $new_attachment_file = get_post_meta( $new_attachment_id, '_wp_attached_file', true );
591 $upload_dir = wp_upload_dir();
592 $new_media_file_path = $upload_dir['basedir'] . '/' . $new_attachment_file;
593
594 }
595
596 // Check if the new media file exist / was successfully uploaded
597 if ( ! is_file( $new_media_file_path ) ) {
598 return false;
599 }
600
601 // Delete existing/old media files. Post and post meta entries for it are still there in the database.
602 $this->delete_media_files( $old_attachment_id );
603
604 // If original file is larger than 2560 pixel
605 // https://make.wordpress.org/core/2019/10/09/introducing-handling-of-big-images-in-wordpress-5-3/
606 if ( array_key_exists( 'original_image', $new_attachment_meta ) ) {
607
608 // Get the original media file path
609 $old_media_file_path = wp_get_original_image_path( $old_attachment_id );
610
611 } else {
612
613 // Get the path to the old/existing media file that will be replaced and deleted. An image file name may end with '-scaled'.
614 $old_attachment_file = get_post_meta( $old_attachment_id, '_wp_attached_file', true );
615 $old_media_file_path = $upload_dir['basedir'] . '/' . $old_attachment_file;
616
617 }
618
619 // Check if the directory path to the old media file is still intact
620 if ( ! file_exists( dirname( $old_media_file_path ) ) ) {
621
622 // Recreate the directory path
623 mkdir( dirname( $old_media_file_path ), 0755, true );
624
625 }
626
627 // Copy the new media file into the old media file's path
628 copy( $new_media_file_path, $old_media_file_path );
629
630 // Regenerate attachment meta data and image sub-sizes from the new media file that was just copied to the old path
631 $old_media_post_meta_updated = wp_generate_attachment_metadata( $old_attachment_id, $old_media_file_path );
632
633 // Update new media file's meta data with the ones from the old media. i.e. new media file will carry over the post ID and post meta of the old media file. i.e. only the files are replaced for the old media's ID and post meta in the database.
634 wp_update_attachment_metadata( $old_attachment_id, $old_media_post_meta_updated );
635
636 // Delete the newly uploaded media file and it's sub-sizes, and also delete post and post meta entries for it in the database.
637 wp_delete_attachment( $new_attachment_id, true );
638
639 }
640
641 }
642
643 /**
644 * Delete the existing/old media files when performing media replacement
645 *
646 * @link https://plugins.trac.wordpress.org/browser/replace-image/tags/1.1.7/hm-replace-image.php#L80
647 * @since 1.1.0
648 */
649 public function delete_media_files( $post_id ) {
650
651 $attachment_meta = wp_get_attachment_metadata( $post_id );
652
653 // Will get '-scaled' version if it exists, e.g. /path/to/uploads/year/month/file-name.jpg
654 $attachment_file_path = get_attached_file( $post_id );
655
656 // e.g. file-name.jpg
657 $attachment_file_basename = basename( $attachment_file_path );
658
659 // Delete intermediate images if there are any
660
661 if ( isset( $attachment_meta['sizes'] ) && is_array( $attachment_meta['sizes'] ) ) {
662
663 foreach( $attachment_meta['sizes'] as $size => $size_info) {
664
665 // /path/to/uploads/year/month/file-name.jpg --> /path/to/uploads/year/month/file-name-1024x768.jpg
666 $intermediate_file_path = str_replace( $attachment_file_basename, $size_info['file'], $attachment_file_path );
667 wp_delete_file( $intermediate_file_path );
668
669 }
670
671 }
672
673 // Delete the attachment file, which maybe the '-scaled' version
674 wp_delete_file( $attachment_file_path );
675
676 // If original file is larger than 2560 pixel
677 // https://make.wordpress.org/core/2019/10/09/introducing-handling-of-big-images-in-wordpress-5-3/
678 if ( array_key_exists( 'original_image', $attachment_meta ) ) {
679
680 $attachment_original_file_path = wp_get_original_image_path( $post_id );
681
682 // Delete the original file
683 wp_delete_file( $attachment_original_file_path );
684
685 }
686
687 }
688
689 /**
690 * Customize the attachment updated message
691 *
692 * @link https://github.com/WordPress/wordpress-develop/blob/6.0.2/src/wp-admin/edit-form-advanced.php#L180
693 * @since 1.1.0
694 */
695 public function attachment_updated_custom_message( $messages ) {
696
697 $new_messages = array();
698
699 foreach( $messages as $post_type => $messages_array ) {
700
701 if ( $post_type == 'attachment' ) {
702
703 // Message ID for successful edit/update of an attachment is 4. e.g. /wp-admin/post.php?post=a&action=edit&classic-editor&message=4 Customize it here.
704 $messages_array[4] = 'Media file updated. You may need to <a href="https://fabricdigital.co.nz/blog/how-to-hard-refresh-your-browser-and-clear-cache" target="_blank">hard refresh</a> your browser to see the updated media preview image below.';
705
706 }
707
708 $new_messages[$post_type] = $messages_array;
709
710 }
711
712 return $new_messages;
713
714 }
715
716 /**
717 * Add SVG mime type for media library uploads
718 *
719 * @link https://developer.wordpress.org/reference/hooks/upload_mimes/
720 * @since 2.6.0
721 */
722 public function add_svg_mime( $mimes ) {
723
724 global $roles_svg_upload_enabled;
725
726 $current_user = wp_get_current_user();
727 $current_user_roles = (array) $current_user->roles; // single dimensional array of role slugs
728
729 if ( count( $roles_svg_upload_enabled ) > 0 ) {
730
731 // Add mime type for user roles set to enable SVG upload
732 foreach ( $current_user_roles as $role ) {
733 if ( in_array( $role, $roles_svg_upload_enabled ) ) {
734 $mimes['svg'] = 'image/svg+xml';
735 }
736 }
737
738 }
739
740 return $mimes;
741
742 }
743
744 /**
745 * Check and confirm if the real file type is indeed SVG
746 *
747 * @link https://developer.wordpress.org/reference/functions/wp_check_filetype_and_ext/
748 * @since 2.6.0
749 */
750 public function confirm_file_type_is_svg( $filetypes_extensions, $file, $filename, $mimes ) {
751
752 global $roles_svg_upload_enabled;
753
754 $current_user = wp_get_current_user();
755 $current_user_roles = (array) $current_user->roles; // single dimensional array of role slugs
756
757 if ( count( $roles_svg_upload_enabled ) > 0 ) {
758
759 // Check file extension
760 if ( substr( $filename, -4 ) == '.svg' ) {
761
762 // Add mime type for user roles set to enable SVG upload
763 foreach ( $current_user_roles as $role ) {
764 if ( in_array( $role, $roles_svg_upload_enabled ) ) {
765 $filetypes_extensions['type'] = 'image/svg+xml';
766 $filetypes_extensions['ext'] = 'svg';
767 }
768 }
769
770 }
771
772 }
773
774 return $filetypes_extensions;
775
776 }
777
778 /**
779 * Sanitize the SVG file and maybe allow upload based on the result
780 *
781 * @since 2.6.0
782 */
783 public function sanitize_and_maybe_allow_svg_upload( $file ) {
784
785 if ( ! isset( $file['tmp_name'] ) ) {
786 return $file;
787 }
788
789 $file_tmp_name = $file['tmp_name']; // full path
790 $file_name = isset( $file['name'] ) ? $file['name'] : '';
791 $file_type_ext = wp_check_filetype_and_ext( $file_tmp_name, $file_name );
792 $file_type = ! empty( $file_type_ext['type'] ) ? $file_type_ext['type'] : '';
793
794 // Load sanitizer library - https://github.com/darylldoyle/svg-sanitizer
795 $sanitizer = new Sanitizer();
796
797 if ( 'image/svg+xml' === $file_type ) {
798
799 $original_svg = file_get_contents( $file_tmp_name );
800 $sanitized_svg = $sanitizer->sanitize( $original_svg ); // boolean
801
802 if ( false === $sanitized_svg ) {
803
804 $file['error'] = 'This SVG file could not be sanitized, so, was not uploaded for security reasons.';
805
806 }
807
808 file_put_contents( $file_tmp_name, $sanitized_svg );
809
810 }
811
812 return $file;
813
814 }
815
816 /**
817 * Generate metadata for the svg attachment
818 *
819 * @link https://developer.wordpress.org/reference/functions/wp_generate_attachment_metadata/
820 * @since 2.6.0
821 */
822 public function generate_svg_metadata( $metadata, $attachment_id, $context ) {
823
824 if ( get_post_mime_type( $attachment_id ) == 'image/svg+xml' ) {
825
826 // Get SVG dimensions
827 $svg_path = get_attached_file( $attachment_id );
828 $svg = simplexml_load_file( $svg_path );
829 $width = 0;
830 $height = 0;
831
832 if ( $svg ) {
833
834 $attributes = $svg->attributes();
835 if ( isset( $attributes->width, $attributes->height ) ) {
836 $width = intval( floatval( $attributes->width ) );
837 $height = intval( floatval( $attributes->height ) );
838 } elseif ( isset( $attributes->viewBox ) ) {
839 $sizes = explode( ' ', $attributes->viewBox );
840 if ( isset( $sizes[2], $sizes[3] ) ) {
841 $width = intval( floatval( $sizes[2] ) );
842 $height = intval( floatval( $sizes[3] ) );
843 }
844 }
845
846 }
847
848 $metadata['width'] = $width;
849 $metadata['height'] = $height;
850
851 // Get SVG filename
852 $svg_url = wp_get_original_image_url( $attachment_id );
853 $svg_url_path = str_replace( wp_upload_dir()['baseurl'] .'/' , '', $svg_url );
854 $metadata['file'] = $svg_url_path;
855
856 }
857
858 return $metadata;
859
860 }
861
862 /**
863 * Return svg file URL to show preview in media library
864 *
865 * @link https://developer.wordpress.org/reference/hooks/wp_ajax_action/
866 * @link https://developer.wordpress.org/reference/functions/wp_get_attachment_url/
867 * @since 2.6.0
868 */
869 public function get_svg_attachment_url() {
870
871 $attachment_url = '';
872 $attachment_id = isset( $_REQUEST['attachmentID'] ) ? $_REQUEST['attachmentID'] : '';
873
874 // Check response mime type
875 if ( $attachment_id ) {
876
877 $attachment_url = wp_get_attachment_url( $attachment_id );
878
879 echo $attachment_url;
880
881 die();
882
883 }
884
885 }
886
887 /**
888 * Return svg file URL to show preview in media library
889 *
890 * @link https://developer.wordpress.org/reference/functions/wp_prepare_attachment_for_js/
891 * @since 2.6.0
892 */
893 public function get_svg_url_in_media_library( $response ) {
894
895 // Check response mime type
896 if ( $response['mime'] === 'image/svg+xml' ) {
897
898 $response['image'] = array(
899 'src' => $response['url'],
900 );
901
902 }
903
904 return $response;
905
906 }
907
908 /**
909 * Add external permalink meta box for enabled post types
910 *
911 * @since 3.9.0
912 */
913 public function add_external_permalink_meta_box( $post_type, $post ) {
914
915 $options = get_option( ASENHA_SLUG_U, array() );
916 $enable_external_permalinks_for = $options['enable_external_permalinks_for'];
917
918 foreach ( $enable_external_permalinks_for as $post_type_slug => $is_external_permalink_enabled ) {
919 if ( ( get_post_type() == $post_type_slug ) && $is_external_permalink_enabled ) {
920
921 // Skip adding meta box for post types where Gutenberg is enabled
922 // if (
923 // function_exists( 'use_block_editor_for_post_type' )
924 // && use_block_editor_for_post_type( $post_type_slug )
925 // ) {
926 // continue; // go to the beginning of next iteration
927 // }
928
929 add_meta_box(
930 'asenha-external-permalink', // ID of meta box
931 'External Permalink', // Title of meta box
932 [ $this, 'output_external_permalink_meta_box' ], // Callback function
933 $post_type_slug, // The screen on which the meta box should be output to
934 'normal', // context
935 'high', // priority
936 // array(), // $args to pass to callback function. Ref: https://developer.wordpress.org/reference/functions/add_meta_box/#comment-342
937 );
938
939 }
940 }
941
942 }
943
944 /**
945 * Render External Permalink meta box
946 *
947 * @since 3.9.0
948 */
949 public function output_external_permalink_meta_box( $post ) {
950 ?>
951 <div class="external-permalink-input">
952 <input name="<?php echo esc_attr( 'external_permalink' ); ?>" class="large-text" id="<?php echo esc_attr( 'external_permalink' ); ?>" type="text" value="<?php echo esc_url( get_post_meta( $post->ID, '_links_to', true ) ); ?>" placeholder="https://" />
953 <div class="external-permalink-input-description">Keep empty to use the default WordPress permalink. External permalink will open in a new browser tab.</div>
954 <?php wp_nonce_field( 'external_permalink_' . $post->ID, 'external_permalink_nonce', false, true ); ?>
955 </div>
956 <?php
957 }
958
959 /**
960 * Save external permalink input
961 *
962 * @since 3.9.0
963 */
964 public function save_external_permalink( $post_id ) {
965
966 // Only proceed if nonce is verified
967 if ( isset( $_POST['external_permalink_nonce'] ) && wp_verify_nonce( $_POST['external_permalink_nonce'], 'external_permalink_' . $post_id ) ) {
968
969 // Get the value of external permalink from input field
970 $external_permalink = isset( $_POST['external_permalink'] ) ? esc_url_raw( trim( $_POST['external_permalink'] ) ) : '';
971
972 // Update or delete external permalink post meta
973 if ( ! empty( $external_permalink ) ) {
974 update_post_meta( $post_id, '_links_to', $external_permalink );
975 } else {
976 delete_post_meta( $post_id, '_links_to' );
977 }
978
979 }
980
981 }
982
983 /**
984 * Change WordPress default permalink into external permalink for pages
985 *
986 * @since 3.9.0
987 */
988 public function use_external_permalink_for_pages( $permalink, $post_id ) {
989
990 $external_permalink = get_post_meta( $post_id, '_links_to', true );
991
992 if ( ! empty( $external_permalink ) ) {
993 $permalink = $external_permalink;
994 }
995
996 return $permalink;
997
998 }
999
1000 /**
1001 * Change WordPress default permalink into external permalink for posts and custom post types
1002 *
1003 * @since 3.9.0
1004 */
1005 public function use_external_permalink_for_posts( $permalink, $post ) {
1006
1007 $external_permalink = get_post_meta( $post->ID, '_links_to', true );
1008
1009 if ( ! empty( $external_permalink ) ) {
1010 $permalink = $external_permalink;
1011
1012 if ( ! is_admin() ) {
1013 $permalink = $permalink . '#new_tab';
1014 }
1015 }
1016
1017 return $permalink;
1018
1019 }
1020
1021 /**
1022 * Redirect page/post to external permalink if it's loaded directly from the WP default permalink
1023 *
1024 * @since 3.9.0
1025 */
1026 public function redirect_to_external_permalink() {
1027
1028 global $post;
1029
1030 // If not on/loading the single page/post URL, do nothing
1031 if ( ! is_singular() ) {
1032 return;
1033 }
1034
1035 $external_permalink = get_post_meta( $post->ID, '_links_to', true );
1036
1037 if ( ! empty( $external_permalink ) ) {
1038 wp_redirect( $external_permalink, 302 ); // temporary redirect
1039 exit;
1040 }
1041
1042 }
1043
1044 /**
1045 * Publish posts of any type with missed schedule.
1046 * We use the Transients API to reduce straining the site with DB queries on busy sites.
1047 * So, this function will only query the DB once every 15 minutes at most.
1048 *
1049 * @since 3.1.0
1050 */
1051 public function publish_missed_schedule_posts() {
1052
1053 if ( is_front_page() || is_home() || is_page() || is_single() || is_singular() || is_archive() || is_admin() || is_blog_admin() || is_robots() || is_ssl() ) {
1054
1055 // Get missed schedule posts data from cache
1056 $missed_schedule_posts = get_transient( 'asenha_missed_schedule_posts' );
1057
1058 // Nothing found in cache
1059 if ( false === $missed_schedule_posts ) {
1060
1061 global $wpdb;
1062
1063 $current_gmt_datetime = gmdate( 'Y-m-d H:i:00' );
1064
1065 $args = array(
1066 'public' => true,
1067 '_builtin' => false, // not post, page, attachment, revision or nav_menu_item
1068 );
1069
1070 $custom_post_types = get_post_types( $args, 'names' ); // array, e.g. array( 'project', 'book', 'staff' )
1071
1072 if ( count( $custom_post_types ) > 0 ) {
1073 $custom_post_types = "'" . implode( "','", $custom_post_types ) . "'"; // string, e.g. 'project','book','staff'
1074 $post_types = "'page','post'," . $custom_post_types; // 'page','post','project','book','staff'
1075 } else {
1076 $post_types = "'page','post'";
1077 }
1078
1079 $sql = "SELECT ID FROM $wpdb->posts WHERE post_type IN ($post_types) AND post_status='future' AND post_date_gmt<'$current_gmt_datetime'";
1080
1081 // The following does not work as backslashes are inserted before single quotes in $post_types
1082 // $sql = $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_type IN (%s) AND post_status='future' AND post_date_gmt<'%s'", array( $post_types, $current_gmt_datetime ) );
1083
1084 $missed_schedule_posts = $wpdb->get_results( $sql, ARRAY_A );
1085
1086 // Save query results as a transient with expiry of 15 minutes
1087 set_transient( 'asenha_missed_schedule_posts', $missed_schedule_posts, 15 * MINUTE_IN_SECONDS );
1088
1089 }
1090
1091 if ( empty( $missed_schedule_posts ) || ! is_array( $missed_schedule_posts ) ) {
1092 return;
1093 }
1094
1095 foreach( $missed_schedule_posts as $post ) {
1096 wp_publish_post( $post['ID'] );
1097 }
1098
1099 }
1100
1101 }
1102
1103 }