feat(submit): add user media library
This commit is contained in:
1 parent
44e165d34e
commit
f3fcfe45da
23 files changed
+1321
-945
No files matched your search
@@ -0,0 +1,294 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
*/
|
||||
|
||||
namespace AKSRT\Core;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
/**
|
||||
* Front-submit media library, attachment normalization, and post relations.
|
||||
*/
|
||||
trait FrontSubmitMedia {
|
||||
|
||||
/**
|
||||
* Check whether the current user may browse their front-submit media.
|
||||
*/
|
||||
public function can_list_attachments(\WP_REST_Request $request): bool {
|
||||
$user_id = get_current_user_id();
|
||||
return $user_id > 0 && $this->can_use_media($request, $user_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a user may use the submit media routes for this post.
|
||||
*/
|
||||
private function can_use_media(\WP_REST_Request $request, int $user_id): bool {
|
||||
$post_id = absint($request->get_param('post_id'));
|
||||
if ($post_id > 0) {
|
||||
return $this->can_edit_post($post_id, $user_id);
|
||||
}
|
||||
return $this->can_submit($user_id);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sanitize a REST attachment exclusion list.
|
||||
*
|
||||
* @param mixed $value Comma-separated or array attachment IDs.
|
||||
* @return int[]
|
||||
*/
|
||||
public function sanitize_attachment_ids($value): array {
|
||||
if (!is_array($value)) {
|
||||
$value = preg_split('/[,\s]+/', (string) $value) ?: [];
|
||||
}
|
||||
return array_values(array_unique(array_filter(array_map(static function ($id): int {
|
||||
return is_scalar($id) ? absint($id) : 0;
|
||||
}, $value))));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current user's front-submit attachments.
|
||||
*/
|
||||
public function rest_list_attachments(\WP_REST_Request $request): \WP_REST_Response {
|
||||
$user_id = get_current_user_id();
|
||||
$type = sanitize_key((string) $request->get_param('type'));
|
||||
$page = max(1, absint($request->get_param('paged')));
|
||||
$search = sanitize_text_field((string) $request->get_param('search'));
|
||||
$exclude = $this->sanitize_attachment_ids($request->get_param('exclude'));
|
||||
|
||||
if ($user_id <= 0 || !in_array($type, ['image', 'video', 'file'], true)) {
|
||||
return new \WP_REST_Response([
|
||||
'items' => [],
|
||||
'total' => 0,
|
||||
'total_pages' => 0,
|
||||
'page' => $page,
|
||||
'per_page' => 48,
|
||||
], 200);
|
||||
}
|
||||
|
||||
$args = [
|
||||
'post_type' => 'attachment',
|
||||
'post_status' => ['inherit', 'private'],
|
||||
'author' => $user_id,
|
||||
'posts_per_page' => 48,
|
||||
'paged' => $page,
|
||||
'orderby' => 'ID',
|
||||
'order' => 'DESC',
|
||||
'no_found_rows' => false,
|
||||
];
|
||||
if ($type === 'image') {
|
||||
$args['post_mime_type'] = 'image';
|
||||
} elseif ($type === 'video') {
|
||||
$args['post_mime_type'] = 'video';
|
||||
}
|
||||
if ($exclude !== []) {
|
||||
$args['post__not_in'] = $exclude;
|
||||
}
|
||||
|
||||
if ($search !== '') {
|
||||
$args['s'] = $search;
|
||||
add_filter('wp_allow_query_attachment_by_filename', '__return_true');
|
||||
}
|
||||
|
||||
$query = new \WP_Query($args);
|
||||
if ($search !== '') {
|
||||
remove_filter('wp_allow_query_attachment_by_filename', '__return_true');
|
||||
}
|
||||
$items = [];
|
||||
foreach ($query->posts as $attachment) {
|
||||
$prepared = $this->prepare_attachment_response($attachment);
|
||||
if ($prepared !== null) {
|
||||
$items[] = $prepared;
|
||||
}
|
||||
}
|
||||
|
||||
$total = (int) $query->found_posts;
|
||||
return new \WP_REST_Response([
|
||||
'items' => $items,
|
||||
'total' => $total,
|
||||
'total_pages' => $total > 0 ? (int) ceil($total / 48) : 0,
|
||||
'page' => $page,
|
||||
'per_page' => 48,
|
||||
], 200);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize an attachment with WordPress's media response model.
|
||||
*
|
||||
* Only fields needed by the front-submit picker are returned. In
|
||||
* particular, admin links, icons, nonces, and author data are omitted.
|
||||
*
|
||||
* @param int|\WP_Post $attachment Attachment object or ID.
|
||||
* @return array<string, mixed>|null
|
||||
*/
|
||||
private function prepare_attachment_response($attachment): ?array {
|
||||
$attachment = $attachment instanceof \WP_Post ? $attachment : get_post(absint($attachment));
|
||||
if (!$attachment || $attachment->post_type !== 'attachment') {
|
||||
return null;
|
||||
}
|
||||
if (!function_exists('wp_prepare_attachment_for_js')) {
|
||||
require_once ABSPATH . 'wp-admin/includes/media.php';
|
||||
}
|
||||
$data = wp_prepare_attachment_for_js($attachment->ID);
|
||||
if (!is_array($data)) {
|
||||
return null;
|
||||
}
|
||||
$sizes = is_array($data['sizes'] ?? null) ? $data['sizes'] : [];
|
||||
$size_url = static function (array $sizes, string $size, string $fallback): string {
|
||||
return !empty($sizes[$size]['url']) ? (string) $sizes[$size]['url'] : $fallback;
|
||||
};
|
||||
$url = (string) ($data['url'] ?? wp_get_attachment_url($attachment->ID) ?? '');
|
||||
$filename = (string) ($data['filename'] ?? wp_basename((string) get_attached_file($attachment->ID)));
|
||||
$mime = (string) ($data['mime'] ?? get_post_mime_type($attachment->ID));
|
||||
return [
|
||||
'attachment_id' => (int) $attachment->ID,
|
||||
'id' => (int) $attachment->ID,
|
||||
'title' => (string) ($data['title'] ?? get_the_title($attachment->ID)),
|
||||
'name' => (string) ($data['name'] ?? $filename),
|
||||
'filename' => $filename,
|
||||
'type' => (string) ($data['type'] ?? (strpos($mime, '/') !== false ? strtok($mime, '/') : '')),
|
||||
'subtype' => (string) ($data['subtype'] ?? (strpos($mime, '/') !== false ? substr($mime, strpos($mime, '/') + 1) : '')),
|
||||
'url' => $url,
|
||||
'large_url' => $size_url($sizes, 'large', $url),
|
||||
'medium_url' => $size_url($sizes, 'medium', $url),
|
||||
'thumbnail_url' => $size_url($sizes, 'thumbnail', $url),
|
||||
'filesizeInBytes' => (int) ($data['filesizeInBytes'] ?? (@filesize((string) get_attached_file($attachment->ID)) ?: 0)),
|
||||
'mime' => $mime,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach IDs referenced by a saved submission to that post.
|
||||
*
|
||||
* @param mixed $post Saved post passed by the existing submission hooks.
|
||||
*/
|
||||
public function sync_post_attachments($post): void {
|
||||
if (!$post instanceof \WP_Post || $post->post_type !== 'post') {
|
||||
return;
|
||||
}
|
||||
$user_id = get_current_user_id();
|
||||
$post_id = (int) $post->ID;
|
||||
if ($user_id <= 0 || (int) $post->post_author !== $user_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
$image_ids = $this->extract_attachment_ids((string) $post->post_content, 'image');
|
||||
$video_ids = $this->extract_attachment_ids((string) $post->post_content, 'video');
|
||||
$file_ids = $this->extract_attachment_ids((string) $post->post_content, 'file');
|
||||
$desired = [];
|
||||
foreach ($image_ids as $id) {
|
||||
if ($this->attachment_can_attach($id, $user_id, $post_id, 'image')) {
|
||||
$desired[$id] = 'image';
|
||||
}
|
||||
}
|
||||
foreach ($video_ids as $id) {
|
||||
if ($this->attachment_can_attach($id, $user_id, $post_id, 'video')) {
|
||||
$desired[$id] = 'video';
|
||||
}
|
||||
}
|
||||
foreach ($file_ids as $id) {
|
||||
if ($this->attachment_can_attach($id, $user_id, $post_id, 'file')) {
|
||||
$desired[$id] = 'file';
|
||||
}
|
||||
}
|
||||
|
||||
$pay_meta = function_exists('aksrt_get_pay_meta')
|
||||
? aksrt_get_pay_meta($post_id)
|
||||
: get_post_meta($post_id, DomainModel::PAY_META_KEY, true);
|
||||
$pay_meta = is_array($pay_meta) ? $pay_meta : [];
|
||||
foreach (is_array($pay_meta['downloads'] ?? null) ? $pay_meta['downloads'] : [] as $download) {
|
||||
$id = absint($download['attachment_id'] ?? 0);
|
||||
if ($id > 0 && $this->attachment_can_attach($id, $user_id, $post_id, 'file')) {
|
||||
$desired[$id] = 'file';
|
||||
}
|
||||
}
|
||||
|
||||
$post_meta = get_post_meta($post_id, DomainModel::POST_META_KEY, true);
|
||||
$post_meta = is_array($post_meta) ? $post_meta : [];
|
||||
$featured_ids = [];
|
||||
$video_id = absint($post_meta['featured_video_attachment_id'] ?? 0);
|
||||
if ($video_id > 0 && $this->attachment_can_attach($video_id, $user_id, $post_id, 'video')) {
|
||||
$desired[$video_id] = 'video';
|
||||
}
|
||||
$thumb_id = get_post_thumbnail_id($post_id);
|
||||
if ($thumb_id > 0) {
|
||||
$featured_ids[] = $thumb_id;
|
||||
}
|
||||
foreach (preg_split('/[\\s,]+/', (string) ($post_meta['featured_slide'] ?? '')) ?: [] as $token) {
|
||||
$id = absint($token);
|
||||
if ($id > 0) $featured_ids[] = $id;
|
||||
}
|
||||
foreach ($featured_ids as $id) {
|
||||
if ($this->attachment_can_attach($id, $user_id, $post_id, 'image')) {
|
||||
$desired[$id] = 'image';
|
||||
}
|
||||
}
|
||||
|
||||
global $wpdb;
|
||||
$existing = get_posts([
|
||||
'post_type' => 'attachment',
|
||||
'post_status' => ['inherit', 'private'],
|
||||
'post_parent' => $post_id,
|
||||
'author' => $user_id,
|
||||
'posts_per_page' => -1,
|
||||
'fields' => 'ids',
|
||||
'no_found_rows' => true,
|
||||
'meta_key' => '_aksrt_front_submit_media',
|
||||
]);
|
||||
foreach (array_map('absint', $existing) as $attachment_id) {
|
||||
if (!isset($desired[$attachment_id])) {
|
||||
$wpdb->update($wpdb->posts, ['post_parent' => 0], ['ID' => $attachment_id], ['%d'], ['%d']);
|
||||
clean_post_cache($attachment_id);
|
||||
delete_post_meta($attachment_id, '_aksrt_front_submit_media');
|
||||
}
|
||||
}
|
||||
foreach ($desired as $attachment_id => $kind) {
|
||||
$attachment_id = (int) $attachment_id;
|
||||
update_post_meta($attachment_id, '_aksrt_front_submit_media', $kind);
|
||||
$wpdb->update($wpdb->posts, ['post_parent' => $post_id], ['ID' => $attachment_id], ['%d'], ['%d']);
|
||||
clean_post_cache($attachment_id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract IDs from image/video edit markers or attachment relation markers.
|
||||
*
|
||||
* @return int[]
|
||||
*/
|
||||
private function extract_attachment_ids(string $content, string $type): array {
|
||||
$ids = [];
|
||||
if ($type === 'file') {
|
||||
preg_match_all('/data-aksrt-file-id\s*=\s*["\']([^"\']+)["\']/i', $content, $matches);
|
||||
} elseif ($type === 'image') {
|
||||
preg_match_all('/<img\b[^>]*data-edit-file-id\s*=\s*["\']([^"\']+)["\'][^>]*>/i', $content, $matches);
|
||||
} else {
|
||||
preg_match_all('/<(?:div|video)\b[^>]*data-edit-file-id\s*=\s*["\']([^"\']+)["\'][^>]*data-video-url[^>]*>/i', $content, $matches);
|
||||
if (empty($matches[1])) {
|
||||
preg_match_all('/<(?:div|video)\b[^>]*data-video-url[^>]*data-edit-file-id\s*=\s*["\']([^"\']+)["\'][^>]*>/i', $content, $matches);
|
||||
}
|
||||
}
|
||||
return array_values(array_unique(array_filter(array_map('absint', $matches[1] ?? []))));
|
||||
}
|
||||
|
||||
private function attachment_can_attach(int $attachment_id, int $user_id, int $post_id, string $type): bool {
|
||||
$attachment = get_post($attachment_id);
|
||||
if (!$attachment || $attachment->post_type !== 'attachment' || (int) $attachment->post_author !== $user_id) {
|
||||
return false;
|
||||
}
|
||||
if (!in_array($attachment->post_status, ['inherit', 'private'], true)) {
|
||||
return false;
|
||||
}
|
||||
if ((int) $attachment->post_parent !== 0 && (int) $attachment->post_parent !== $post_id) {
|
||||
return false;
|
||||
}
|
||||
$mime = (string) $attachment->post_mime_type;
|
||||
if ($type === 'image') {
|
||||
return wp_attachment_is_image($attachment_id);
|
||||
}
|
||||
if ($type === 'video') {
|
||||
return strpos($mime, 'video/') === 0;
|
||||
}
|
||||
return $mime !== '';
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user