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|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 : []; $download_urls = []; foreach (is_array($pay_meta['downloads'] ?? null) ? $pay_meta['downloads'] : [] as $download) { $url = (string) ($download['url'] ?? ''); if ($url !== '') $download_urls[] = $url; $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); } PendingUploads::claim_ids(array_keys($desired), $user_id); PendingUploads::claim_urls($download_urls, $user_id, null); PendingUploads::claim_text((string) $post->post_content, $user_id, null); } /** * 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('/]*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 !== ''; } }