| | |
| | | <?php |
| | | |
| | | namespace JVBase\rest\routes; |
| | | |
| | | use JVBase\JVB; |
| | | use JVBase\rest\RestRouteManager; |
| | | use JVBase\managers\CacheManager; |
| | | use JVBase\meta\MetaManager; |
| | | use JVBase\utility\Features; |
| | | use JVBase\managers\queue\executors\ContentExecutor; |
| | | use JVBase\managers\queue\TypeConfig; |
| | | use JVBase\meta\Meta; |
| | | use JVBase\registrar\Registrar; |
| | | use JVBase\rest\PermissionHandler; |
| | | use JVBase\rest\Response; |
| | | use JVBase\rest\Rest; |
| | | use JVBase\rest\Route; |
| | | use WP_Post; |
| | | use WP_Query; |
| | | use WP_Error; |
| | | use WP_REST_Request; |
| | | use WP_REST_Response; |
| | | use Exception; |
| | | use WP_Term; |
| | | use WP_Term_Query; |
| | | |
| | | if (!defined('ABSPATH')) { |
| | | exit; // Exit if accessed directly |
| | | exit; // Exit if accessed directly |
| | | } |
| | | |
| | | class ContentRoutes extends RestRouteManager |
| | | class ContentRoutes extends Rest |
| | | { |
| | | protected array $fields = []; |
| | | protected array $taxonomies = []; |
| | | protected MetaManager $meta; |
| | | protected string $post_type = ''; |
| | | protected string $user_id = ''; |
| | | protected array $fields = []; |
| | | protected array $taxonomies = []; |
| | | protected string $post_type = ''; |
| | | protected string $user_id = ''; |
| | | |
| | | //For Timeline-specific posts |
| | | protected array $timelineSharedFields = []; |
| | | protected array $timelineUniqueFields = []; |
| | | protected static ?string $action = 'dash-'; |
| | | protected Meta $meta; |
| | | |
| | | //TODO: Ensure we are handling the bulk operations for all processes |
| | | //TODO: Also invalidate feed caches on updates!! |
| | | public function __construct() |
| | | { |
| | | $this->cacheName = 'user_content_' . get_current_user_id(); |
| | | parent::__construct(); |
| | | if (JVB_TESTING) { |
| | | $this->cache->flush(); |
| | | } |
| | | $this->cache->connect('post', true); |
| | | $this->cache->connect('term', true); |
| | | add_action('init', [$this, 'registerContentExecutors'], 5); |
| | | } |
| | | |
| | | public function __construct() |
| | | { |
| | | $this->cache_name = 'user_content_'.get_current_user_id(); |
| | | parent::__construct(); |
| | | /** |
| | | * Register content operation types with the queue's TypeRegistry |
| | | */ |
| | | public function registerContentExecutors(): void |
| | | { |
| | | $registry = JVB()->queue()->registry(); |
| | | $executor = new ContentExecutor(); |
| | | |
| | | $this->action = 'dash-'; |
| | | $this->operation_type = 'content_update'; |
| | | add_filter(BASE.'handle_bulk_operation', [$this, 'processOperation'], 10, 3); |
| | | } |
| | | // Content updates - chunked at 10 posts |
| | | $registry->register('content_update', new TypeConfig( |
| | | executor: $executor, |
| | | chunkKey: 'posts', |
| | | chunkSize: 10 |
| | | )); |
| | | |
| | | /** |
| | | * Registers content routes |
| | | * @return void |
| | | */ |
| | | public function registerRoutes():void |
| | | { |
| | | // Base content endpoint |
| | | register_rest_route($this->namespace, "/content", [ |
| | | [ |
| | | 'methods' => 'GET', |
| | | 'callback' => [$this, 'handleContentRequest'], |
| | | 'permission_callback' => [$this, 'checkPermission'], |
| | | ], |
| | | [ |
| | | 'methods' => 'POST', |
| | | 'callback' => [$this, 'handleContentUpdate'], |
| | | 'permission_callback' => [$this, 'checkPermission'] |
| | | ] |
| | | ]); |
| | | // Batch creation (from uploads) TODO: I believe this is all handled by UploadExecutor |
| | | // $registry->register('batch_creation', new TypeConfig( |
| | | // executor: $executor |
| | | // )); |
| | | } |
| | | |
| | | //TODO: consolidate create/batch in with create? I don't think we are ever creating a single item |
| | | register_rest_route($this->namespace, "/create", [ |
| | | [ |
| | | 'methods' => 'POST', |
| | | 'callback' => [$this, 'handleContentCreate'], |
| | | 'permission_callback' => [$this, 'checkPermission'] |
| | | ] |
| | | ]); |
| | | register_rest_route($this->namespace, "/create/batch", [ |
| | | [ |
| | | 'methods' => 'POST', |
| | | 'callback' => [$this, 'handleBatchCreation'], |
| | | 'permission_callback' => [$this, 'checkPermission'] |
| | | ] |
| | | ]); |
| | | } |
| | | /** |
| | | * Registers content routes |
| | | * @return void |
| | | */ |
| | | public function registerRoutes(): void |
| | | { |
| | | Route::for('content') |
| | | ->get([$this, 'getContent']) |
| | | ->auth(PermissionHandler::combine(['user', 'nonce', ['actionNonce'=>'dash-']])) |
| | | ->args([ |
| | | 'content' => 'string|required', |
| | | 'status' => 'string|default:all', |
| | | 'page' => 'integer|default:1|min:1', |
| | | 'per_page' => 'integer|default:30|min:1|max:100', |
| | | 'orderby' => 'string|enum:date,alphabetical|default:date', |
| | | 'order' => 'string|enum:asc,desc|default:desc', |
| | | 'search' => 'string', |
| | | 'date-filter' => 'string', |
| | | 'dateFrom' => 'string', |
| | | 'dateTo' => 'string', |
| | | ]) |
| | | ->rateLimit(20) |
| | | ->post([$this, 'postContent']) |
| | | ->auth(PermissionHandler::combine(['user', 'nonce', ['actionNonce'=>'dash-']])) |
| | | ->rateLimit(30) |
| | | ->args([ |
| | | 'user' => 'int|required', |
| | | 'posts' => 'required', |
| | | 'content' => 'string', |
| | | ]) |
| | | ->register(); |
| | | } |
| | | |
| | | protected function initTimelineFields(string $content):void |
| | | protected function initTimelineFields(string $content): void |
| | | { |
| | | $content = jvbNoBase($content); |
| | | if (!Features::forContent($content)->has('is_timeline')){ |
| | | |
| | | $config = Registrar::getInstance($content); |
| | | if (!$config || !$config->hasFeature('is_timeline')) { |
| | | return; |
| | | } |
| | | $config = Features::getConfig($content); |
| | | $this->fields = $config['fields']; |
| | | $this->fields = $config->getFields(); |
| | | |
| | | $this->timelineSharedFields = $this->getTimelineSharedFields($content); |
| | | array_unshift($this->timelineSharedFields, 'post_thumbnail'); |
| | |
| | | |
| | | $this->timelineUniqueFields = $this->getTimelineUniqueFields($content); |
| | | } |
| | | public function getTimelineUniqueFields(string $content):array |
| | | |
| | | public function getTimelineUniqueFields(string $content): array |
| | | { |
| | | $content = jvbNoBase($content); |
| | | if (!Features::forContent($content)->has('is_timeline')){ |
| | | $registrar = Registrar::getInstance($content); |
| | | if (!$registrar || !$registrar->hasFeature('is_timeline')) { |
| | | return []; |
| | | } |
| | | $config = Features::getConfig($content); |
| | | $allFields = $config['fields']; |
| | | |
| | | $allFields = $registrar->getFields(); |
| | | |
| | | return array_keys(array_filter($allFields, function ($field) { |
| | | if (array_key_exists('for_all', $field) && $field['for_all'] === true) { |
| | |
| | | })); |
| | | } |
| | | |
| | | public function getTimelineSharedFields(string $content):array |
| | | public function getTimelineSharedFields(string $content): array |
| | | { |
| | | $content = jvbNoBase($content); |
| | | if (!Features::forContent($content)->has('is_timeline')){ |
| | | $registrar = Registrar::getInstance($content); |
| | | if (!$registrar || !$registrar->hasFeature('is_timeline')) { |
| | | return []; |
| | | } |
| | | $config = Features::getConfig($content); |
| | | if (!$config || empty($config)) { |
| | | return []; |
| | | } |
| | | $allFields = $config['fields']??[]; |
| | | |
| | | $allFields = $registrar->getFields()??[]; |
| | | |
| | | return array_keys(array_filter($allFields, function ($field) { |
| | | if (!array_key_exists('for_all', $field) || $field['for_all'] === false){ |
| | | if (!array_key_exists('for_all', $field) || is_null($field['for_all']) || $field['for_all'] === false) { |
| | | return true; |
| | | } |
| | | return false; |
| | | })); |
| | | } |
| | | |
| | | /** |
| | | * Handle content update/creation |
| | | * @param WP_REST_Request $request |
| | | * |
| | | * @return WP_REST_Response |
| | | */ |
| | | public function handleContentUpdate(WP_REST_Request$request):WP_REST_Response |
| | | { |
| | | $data = $request->get_params(); |
| | | error_log('Received data: '.print_r($data, true)); |
| | | $user_id = $data['user']; |
| | | /** |
| | | * Handle content update/creation |
| | | * @param WP_REST_Request $request |
| | | * |
| | | * @return WP_REST_Response |
| | | */ |
| | | public function postContent(WP_REST_Request $request): WP_REST_Response |
| | | { |
| | | $data = $request->get_params(); |
| | | $user_id = $data['user']; |
| | | |
| | | if (!array_key_exists('posts', $data) || !is_array($data['posts'])) { |
| | | return Response::success(['message'=>'No posts found in request']); |
| | | } |
| | | |
| | | |
| | | if (!isset($data['posts']) || !is_array($data['posts'])) { |
| | | return new WP_REST_Response([ |
| | | 'success' => false, |
| | | 'message' =>'Invalid request format' |
| | | ]); |
| | | } |
| | | $count = count($data['posts']); |
| | | $operationId = $data['id']; |
| | | unset($data['user']); |
| | | unset($data['id']); |
| | | |
| | | $count = count($data['posts']); |
| | | $operationId = $data['id']; |
| | | unset($data['user']); |
| | | unset($data['id']); |
| | | error_log('[CONTENT]:'.print_r($data, true)); |
| | | $queue = JVB()->queue(); |
| | | $queue->queueOperation( |
| | | 'content_update', |
| | | $user_id, |
| | | $data, |
| | | [ |
| | | 'operation_id' => $operationId |
| | | ] |
| | | ); |
| | | |
| | | $queue = JVB()->queue(); |
| | | $queue->queueOperation( |
| | | 'content_update', |
| | | $user_id, |
| | | $data, |
| | | [ |
| | | 'count' => $count, |
| | | 'chunk_key' => 'posts', |
| | | 'chunk_size' => 10, |
| | | 'operation_id' => $operationId |
| | | ] |
| | | ); |
| | | return new WP_REST_Response([ |
| | | 'success' => true, |
| | | 'message' => 'Queued for processing', |
| | | 'operation' => $operationId |
| | | ]); |
| | | } |
| | | |
| | | /** |
| | | * Handle content creation |
| | | * @param WP_REST_Request $request |
| | | * |
| | | * @return WP_REST_Response |
| | | */ |
| | | public function handleContentCreate(WP_REST_Request$request):WP_REST_Response |
| | | { |
| | | $data = $request->get_json_params(); |
| | | $user_id = $data['user']; |
| | | |
| | | if (!isset($data['posts']) || !is_array($data['posts'])) { |
| | | return new WP_REST_Response([ |
| | | 'success' => false, |
| | | 'message' => 'Invalid request format' |
| | | ]); |
| | | } |
| | | |
| | | $count = count($data['posts']); |
| | | $operationId = $data['id']; |
| | | unset($data['user']); |
| | | unset($data['id']); |
| | | JVB()->queue()->queueOperation( |
| | | 'batch_creation', |
| | | $user_id, |
| | | $data, |
| | | [ |
| | | 'count' => $count, |
| | | 'operation_id' => $operationId, |
| | | ] |
| | | ); |
| | | |
| | | return new WP_REST_Response([ |
| | | 'success' => true, |
| | | 'message' => 'Queued for processing', |
| | | 'operation' => $operationId |
| | | ]); |
| | | } |
| | | return Response::queued($operationId); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Handle request |
| | | * @param WP_REST_Request $request |
| | | * |
| | | * @return WP_REST_Response |
| | | */ |
| | | public function handleContentRequest(WP_REST_Request $request):WP_REST_Response |
| | | { |
| | | $params = $request->get_params(); |
| | | $user_id = $params['user']; |
| | | if (!$this->userCheck($user_id)) { |
| | | return new WP_REST_Response([ |
| | | 'success' => false, |
| | | 'message' => 'User does not match up. Are you a bot?', |
| | | ]); |
| | | } |
| | | /** |
| | | * Handle request |
| | | * @param WP_REST_Request $request |
| | | * |
| | | * @return WP_REST_Response |
| | | */ |
| | | public function getContent(WP_REST_Request $request): WP_REST_Response |
| | | { |
| | | $params = $request->get_params(); |
| | | error_log('getContent::params '.print_r($params, true)); |
| | | |
| | | $post_status = $params['status']; |
| | | if ($post_status === 'all') { |
| | | $post_status = ['publish', 'draft']; |
| | | } else { |
| | | $post_status = explode(',', $post_status); |
| | | } |
| | | $post_type = str_replace('-', '_',jvbCheckBase($params['content'])); |
| | | $registrar = Registrar::getInstance($params['content']); |
| | | switch ($registrar->getType()) { |
| | | case 'term': |
| | | return $this->getTerms($request, $params, $registrar); |
| | | case 'user': |
| | | //TODO maybe do something? |
| | | break; |
| | | case 'post': |
| | | return $this->getPosts($request, $params, $registrar); |
| | | } |
| | | |
| | | return $this->error('Something went wrong, this does not appear to have a proper content type'); |
| | | } |
| | | |
| | | public function getPosts(WP_REST_Request $request, array $params, Registrar $registrar):WP_REST_Response |
| | | { |
| | | $user_id = $params['user']; |
| | | |
| | | $post_status = $params['status']; |
| | | if ($post_status === 'all') { |
| | | $post_status = ['publish', 'draft']; |
| | | } else { |
| | | $post_status = explode(',', $post_status); |
| | | } |
| | | $post_type = str_replace('-', '_', jvbCheckBase($params['content'])); |
| | | |
| | | // Build query args |
| | | $args = [ |
| | | 'post_type' => $post_type, |
| | | 'posts_per_page' => $params['per_page'] ?? 30, |
| | | 'paged' => $params['page'], |
| | | 'orderby' => 'date', |
| | | 'order' => 'DESC', |
| | | 'author' => $user_id, |
| | | 'post_status' => $post_status |
| | | ]; |
| | | |
| | | |
| | | // Build query args |
| | | $args = [ |
| | | 'post_type' => $post_type, |
| | | 'posts_per_page' => $params['per_page']??30, |
| | | 'paged' => $params['page'], |
| | | 'orderby' => 'date', |
| | | 'order' => 'DESC', |
| | | 'author' => $user_id, |
| | | 'post_status' => $post_status |
| | | ]; |
| | | //Only top level posts for timeline types |
| | | if (Features::forContent($post_type)->has('is_timeline')) { |
| | | if ($registrar?->hasFeature('is_timeline')) { |
| | | $args['post_parent'] = 0; |
| | | } |
| | | |
| | | //Calendar filters |
| | | if (Features::forContent($post_type)->has('is_calendar')) { |
| | | if ($registrar?->hasFeature('is_calendar')) { |
| | | $args = $this->applyCalendarFilters($args, $params); |
| | | } |
| | | $taxonomies = array_filter($params, function($param) { |
| | | $taxonomies = array_filter($params, function ($param) { |
| | | return str_starts_with($param, 'tax_'); |
| | | }, ARRAY_FILTER_USE_KEY); |
| | | if (!empty($taxonomies)) { |
| | |
| | | if (array_key_exists('taxonomies', $params)) { |
| | | $args = $this->applyTaxonomyFilters($args, $params); |
| | | } |
| | | if (array_key_exists('date', $params) && !empty($params['date'])) { |
| | | if (array_key_exists('date-filter', $params) || array_key_exists('dateFrom', $params)) { |
| | | $args = $this->applyDateFilters($args, $params); |
| | | } |
| | | if (array_key_exists('orderby', $params) || array_key_exists('order', $params)) { |
| | | $args = $this->applyOrderFilters($args, $params); |
| | | } |
| | | |
| | | if (!empty($params['search'])) { |
| | | if (array_key_exists('search', $params)) { |
| | | $args['s'] = sanitize_text_field($params['search']); |
| | | } |
| | | |
| | | $key = $this->cache->generateKey($args); |
| | | $key = $this->cache->generateKey($args); |
| | | $cached = $this->checkCache($key, $request); |
| | | if ($cached) { |
| | | return $cached; |
| | | } |
| | | |
| | | $this->post_type = jvbCheckBase($params['content'] ?? $params['type']); |
| | | |
| | | if (array_key_exists('s', $args)) { |
| | | $args = $this->applySearchFilters($args, $params); |
| | | } |
| | | |
| | | // Run query |
| | | $query = new WP_Query($args); |
| | | |
| | | $registrar = Registrar::getInstance($this->post_type); |
| | | $this->fields = $registrar->getFields()??[]; |
| | | $this->taxonomies = $this->getTaxonomies($this->post_type); |
| | | |
| | | $posts = array_map([$this, 'preparePost'], $query->posts); |
| | | |
| | | $data = [ |
| | | 'items' => $posts, |
| | | 'total' => $query->found_posts, |
| | | 'total_pages' => $query->max_num_pages, |
| | | 'has_more' => $args['paged']??1 < $query->max_num_pages, |
| | | ]; |
| | | |
| | | |
| | | $this->cache->set($key, $data); |
| | | |
| | | $response = Response::success($data); |
| | | return $this->addCacheHeaders($response); |
| | | } |
| | | public function getTerms(WP_REST_Request $request, array $params, Registrar $registrar):WP_REST_Response |
| | | { |
| | | // Build query args |
| | | $args = [ |
| | | 'taxonomy' => jvbCheckBase($params['content']), |
| | | 'number' => $params['per_page'] ?? 30, |
| | | 'orderby' => 'name', |
| | | 'order' => 'DESC', |
| | | 'hide_empty' => false, |
| | | ]; |
| | | $paged = $params['page']??1; |
| | | $args['page'] = $paged; |
| | | if ($paged > 1) { |
| | | $args['offset'] = ($paged-1) * $args['number']; |
| | | } |
| | | |
| | | //TODO |
| | | // if (array_key_exists('taxonomies', $params)) { |
| | | // $args = $this->applyTaxonomyFilters($args, $params); |
| | | // } |
| | | // if (array_key_exists('date-filter', $params) || array_key_exists('dateFrom', $params)) { |
| | | // $args = $this->applyDateFilters($args, $params); |
| | | // } |
| | | if (array_key_exists('orderby', $params) || array_key_exists('order', $params)) { |
| | | $args = $this->applyOrderFilters($args, $params); |
| | | } |
| | | |
| | | if (array_key_exists('search', $params)) { |
| | | $args['s'] = sanitize_text_field($params['search']); |
| | | } |
| | | |
| | | $key = $this->cache->generateKey($args); |
| | | // Check HTTP cache headers with the specific content type |
| | | $content_type = $params['content'] ?? $params['type']; |
| | | $cache_check = $this->checkHeaders($request, $content_type, [ |
| | | 'filter_hash' => $key, |
| | | ]); |
| | | $cache_check = $this->checkHeaders($request, $key); |
| | | if ($cache_check) { |
| | | return $cache_check; |
| | | } |
| | | |
| | | |
| | | $cache = $this->cache->get($key); |
| | | $cache = false; |
| | | if ($cache) { |
| | | $response = new WP_REST_Response($cache); |
| | | $cache = $this->cache->get($key); |
| | | if ($cache) { |
| | | $response = Response::success($cache); |
| | | return $this->addCacheHeaders($response); |
| | | } |
| | | |
| | | // Run query |
| | | $query = new WP_Query($args); |
| | | |
| | | $this->post_type = $params['content']??$params['type']; |
| | | |
| | | $this->fields = jvbGetFields(str_replace('-','_',$this->post_type)); |
| | | $this->taxonomies = $this->getTaxonomies($this->post_type); |
| | | $posts = array_map([$this, 'prepareItem'], $query->posts); |
| | | |
| | | |
| | | $data = [ |
| | | 'items' => $posts, |
| | | 'total' => $query->found_posts, |
| | | 'total_pages' => $query->max_num_pages |
| | | ]; |
| | | |
| | | |
| | | $this->cache->set($key, $data); |
| | | |
| | | $response = new WP_REST_Response($data); |
| | | return $this->addCacheHeaders($response); |
| | | } |
| | | |
| | | /** |
| | | * Gets allowed taxonomies for a particular content |
| | | * @param string $content |
| | | * |
| | | * @return array |
| | | */ |
| | | protected function getTaxonomies(string $content):array |
| | | { |
| | | $taxonomy_for = jvbGlobalTaxonomyFor(); |
| | | $out = []; |
| | | foreach ($taxonomy_for as $tax => $postTypes) { |
| | | if (in_array($content, $postTypes)) { |
| | | $out[BASE.$tax] = [ |
| | | 'label' => JVB_CONTENT[$content]['plural'], |
| | | 'icon' => $tax, |
| | | ]; |
| | | } |
| | | } |
| | | return $out; |
| | | } |
| | | |
| | | /** |
| | | * Processes operation from queue |
| | | * @param object $operation |
| | | * @param array $data |
| | | * |
| | | * @return array |
| | | */ |
| | | protected function processBatches(object $operation, array $data):array |
| | | { |
| | | $this->user_id = $operation->user_id; |
| | | $posts = $data['posts']; |
| | | |
| | | if (empty($posts)) { |
| | | return [ |
| | | 'success' => false, |
| | | 'message' => 'No posts to update' |
| | | ]; |
| | | } |
| | | |
| | | $results = []; |
| | | |
| | | foreach ($posts as $ID => $post_data) { |
| | | if (Features::forContent($post_data['content'])->has('is_timeline') && array_key_exists('timeline', $post_data)) { |
| | | $results[$ID] =$this->processTimelinePost($ID, $post_data); |
| | | continue; |
| | | } |
| | | if (str_starts_with($ID, 'new')) { |
| | | |
| | | error_log('New post detected. Creating... with: '.print_r([ |
| | | 'post_author' => $this->user_id, |
| | | 'post_type' => jvbCheckBase($post_data['content']), |
| | | 'post_title' => $post_data['post_title']??'', |
| | | 'post_status' => $post_data['status']??'draft', |
| | | ], true)); |
| | | error_log('Recieved Data: '.print_r($post_data, true)); |
| | | $ID = wp_insert_post([ |
| | | 'post_author' => $this->user_id, |
| | | 'post_type' => jvbCheckBase($post_data['content']), |
| | | 'post_title' => $post_data['post_title']??'', |
| | | 'post_status' => $post_data['status']??'draft', |
| | | ]); |
| | | if (!$ID || is_wp_error($ID)) { |
| | | $results[$ID] = [ |
| | | 'success' => false, |
| | | 'message' => 'Couldn\'t Create Post' |
| | | ]; |
| | | continue; |
| | | } |
| | | $fields = jvbGetFields($post_data['content']); |
| | | $allowedFields = array_filter($post_data, function($key) use ($fields) { |
| | | return array_key_exists($key, $fields); |
| | | }, ARRAY_FILTER_USE_KEY); |
| | | |
| | | $meta = new MetaManager($ID, 'post'); |
| | | $success = $meta->setAll($allowedFields); |
| | | $results[$ID] = [ |
| | | 'success' => $success |
| | | ]; |
| | | } else { |
| | | if (!$this->verifyOwnership($ID)) { |
| | | $results[$ID] = [ |
| | | 'success' => false, |
| | | 'message' => 'No permission to modify this post' |
| | | ]; |
| | | continue; |
| | | } |
| | | error_log('Saving post data: '.print_r($post_data, true)); |
| | | |
| | | if (array_key_exists('post_status', $post_data)) { |
| | | switch ($post_data['post_status']) { |
| | | case 'publish': |
| | | unset($post_data['post_status']); |
| | | if (user_can($this->user_id, 'manage_options') || user_can($this->user_id, 'skip_moderation')) { |
| | | $result = wp_update_post(['ID' => $ID, 'post_status' => 'publish']); |
| | | } |
| | | break; |
| | | case 'draft': |
| | | $result = wp_update_post([ |
| | | 'ID' => $ID, |
| | | 'post_status' => 'draft' |
| | | ]); |
| | | break; |
| | | case 'trash': |
| | | $result = wp_trash_post($ID); |
| | | break; |
| | | case 'delete': |
| | | $result = wp_delete_post($ID, true); |
| | | return ['success' => (bool)$result]; |
| | | } |
| | | } |
| | | error_log('Updating data: '.print_r($post_data, true)); |
| | | $fields = jvbGetFields($post_data['content']); |
| | | $allowedFields = array_filter($post_data, function($key) use ($fields) { |
| | | return array_key_exists($key, $fields); |
| | | }, ARRAY_FILTER_USE_KEY); |
| | | |
| | | error_log('Allowed Fields: '.print_r($allowedFields, true)); |
| | | $meta = new MetaManager($ID, 'post'); |
| | | $success = $meta->setAll($allowedFields); |
| | | $results[$ID] = [ |
| | | 'success' => $success |
| | | ]; |
| | | |
| | | } |
| | | |
| | | CacheManager::for($post_data['content'])->clear(); |
| | | if (jvbSiteUsesFeedBlock()) { |
| | | CacheManager::for('feed')->clear(); |
| | | } |
| | | } |
| | | |
| | | if (jvbSiteHasNotifications()) { |
| | | $this->notifications = JVB()->notification(); |
| | | $this->notifications->addNotification( |
| | | $this->user_id, |
| | | 'content_update_complete', |
| | | null, |
| | | 'Content updates completed!' |
| | | ); |
| | | } |
| | | $this->post_type = jvbCheckBase($params['content'] ?? $params['type']); |
| | | // Only expand search to taxonomies if we're actually going to query |
| | | if (array_key_exists('s', $args)) { |
| | | $args = $this->applySearchFilters($args, $params); |
| | | } |
| | | |
| | | // Run query |
| | | $query = new WP_Term_Query($args); |
| | | |
| | | return [ |
| | | 'success' => true, |
| | | 'result' => $results |
| | | ]; |
| | | } |
| | | $terms = $query->get_terms(); |
| | | $data = [ |
| | | 'total' => 0, |
| | | 'total_pages' => 0, |
| | | 'has_more' => false |
| | | ]; |
| | | |
| | | /** |
| | | * Extracts the postdata for timeline post child posts from the pseudo-repeater element |
| | | * @param int $parent_id |
| | | * @param array $post_data |
| | | * @return array|true[] |
| | | */ |
| | | protected function processTimelinePost(int $parent_id, array $post_data):array |
| | | { |
| | | if (!$this->verifyOwnership($parent_id)) { |
| | | return ['success' => false, 'message' => 'No permission']; |
| | | } |
| | | |
| | | $ignore = ['content', 'user']; |
| | | $this->fields = jvbGetFields($post_data['content']); |
| | | $this->initTimelineFields($post_data['content']); |
| | | |
| | | // Get parent post details |
| | | $parent_post = get_post($parent_id); |
| | | $parent_title = $parent_post->post_title; |
| | | $parent_is_published = ($parent_post->post_status === 'publish'); |
| | | |
| | | // Extract shared data from top level (excluding post_thumbnail which is unique per post) |
| | | $sharedData = array_filter($post_data, function ($key) use ($ignore) { |
| | | return in_array($key, $this->timelineSharedFields) |
| | | && !in_array($key, $ignore) |
| | | && $key !== 'post_thumbnail'; |
| | | }, ARRAY_FILTER_USE_KEY); |
| | | |
| | | // If no shared post_title at top level, extract from first timeline entry |
| | | if (!isset($sharedData['post_title']) && isset($post_data['timeline'][0]['post_title'])) { |
| | | $sharedData['post_title'] = $post_data['timeline'][0]['post_title']; |
| | | } |
| | | $clearParent = false; |
| | | if (array_key_exists('timeline', $post_data) && is_array($post_data['timeline'])) { |
| | | // Remove post_title and post_thumbnail from shared taxonomies |
| | | $sharedTaxonomies = array_filter($sharedData, function($key) { |
| | | return $key !== 'post_title' && $key !== 'post_thumbnail'; |
| | | }, ARRAY_FILTER_USE_KEY); |
| | | |
| | | // Ensure the parent post exists and is still first in the array |
| | | $index = array_search((string)$parent_id, array_column($post_data['timeline'], 'id')); |
| | | |
| | | if ($index === false) { |
| | | return [ |
| | | 'success' => false, |
| | | 'message' => 'Missing parent id. This should not have happened' |
| | | ]; |
| | | } |
| | | |
| | | if ($index !== 0) { |
| | | $new_parent_id = $post_data['timeline'][0]['id']; |
| | | |
| | | if (is_numeric($new_parent_id) && (int)$new_parent_id > 0) { |
| | | $new_parent_id = (int)$new_parent_id; |
| | | wp_update_post([ |
| | | 'ID' => $new_parent_id, |
| | | 'post_parent' => 0 |
| | | ]); |
| | | |
| | | wp_update_post([ |
| | | 'ID' => $parent_id, |
| | | 'post_parent' => $new_parent_id |
| | | ]); |
| | | |
| | | $existing_children = get_children([ |
| | | 'post_parent' => $parent_id, |
| | | 'fields' => 'ids' |
| | | ]); |
| | | |
| | | foreach ($existing_children as $child_id) { |
| | | if ($child_id !== $new_parent_id) { |
| | | wp_update_post([ |
| | | 'ID' => $child_id, |
| | | 'post_parent' => $new_parent_id |
| | | ]); |
| | | } |
| | | } |
| | | |
| | | // Update parent references |
| | | $parent_id = $new_parent_id; |
| | | $parent_post = get_post($parent_id); |
| | | $parent_title = $parent_post->post_title; |
| | | $parent_is_published = ($parent_post->post_status === 'publish'); |
| | | } else { |
| | | $item = $post_data['timeline'][$index]; |
| | | unset($post_data['timeline'][$index]); |
| | | array_unshift($post_data['timeline'], $item); |
| | | } |
| | | } |
| | | |
| | | $errors = []; |
| | | $success = []; |
| | | $existing_children = get_children([ |
| | | 'post_parent' => $parent_id, |
| | | 'orderby' => 'menu_order', |
| | | 'post_status' => ['publish', 'draft'], |
| | | 'fields'=> 'ids' |
| | | if (!is_wp_error($terms) && !empty($terms)) |
| | | { |
| | | $total = get_terms([ |
| | | 'taxonomy' => $args['taxonomy'], |
| | | 'hide_empty' => false, |
| | | 'fields' => 'count' |
| | | ]); |
| | | |
| | | $prevDate = null; |
| | | |
| | | foreach($post_data['timeline'] as $order => $timeline) { |
| | | // Get unique fields for this specific timeline entry |
| | | $allowedFields = array_filter($timeline, function($key) use ($ignore) { |
| | | return in_array($key, $this->timelineUniqueFields) && !in_array($key, $ignore); |
| | | }, ARRAY_FILTER_USE_KEY); |
| | | |
| | | // Determine the post title |
| | | $is_parent = ((int)$timeline['id'] === $parent_id); |
| | | $provided_title = $timeline['post_title'] ?? ''; |
| | | $auto_generated_pattern = '/^.+Treatment #?\d+$/'; // Matches "Title - Treatment #1" or "Title - Treatment 1" |
| | | |
| | | if ($is_parent) { |
| | | // Parent keeps its own title or uses shared title |
| | | $allowedFields['post_title'] = $provided_title ?: ($sharedData['post_title'] ?? $parent_title); |
| | | } else { |
| | | // For child posts, auto-generate if: |
| | | // 1. No title provided, OR |
| | | // 2. Title matches auto-generated pattern (meaning it wasn't customized) |
| | | if (empty($provided_title) || preg_match($auto_generated_pattern, $provided_title)) { |
| | | $allowedFields['post_title'] = 'Treatment ' . $order; |
| | | } else { |
| | | // Keep custom title |
| | | $allowedFields['post_title'] = $provided_title; |
| | | } |
| | | } |
| | | |
| | | // Merge with shared taxonomies AFTER setting unique fields |
| | | $allowedFields = array_merge($sharedTaxonomies, $allowedFields); |
| | | |
| | | // Handle post creation if needed |
| | | if (!array_key_exists('id', $timeline) || !is_numeric($timeline['id'])) { |
| | | $newChild = wp_insert_post([ |
| | | 'post_author' => $this->user_id, |
| | | 'post_type' => jvbCheckBase($post_data['content']), |
| | | 'post_title' => $allowedFields['post_title'], |
| | | 'post_parent' => $parent_id, |
| | | 'menu_order' => $order, |
| | | 'post_status' => $parent_is_published ? 'publish' : 'draft' |
| | | ]); |
| | | if (!$newChild || is_wp_error($newChild)) { |
| | | $errors[] = [ |
| | | 'message' => 'Could not create child post', |
| | | 'data' => $timeline |
| | | ]; |
| | | continue; |
| | | } |
| | | $timeline['id'] = $newChild; |
| | | } |
| | | |
| | | if (in_array((int)$timeline['id'], $existing_children)) { |
| | | unset($existing_children[array_search((int)$timeline['id'], $existing_children)]); |
| | | } |
| | | |
| | | // Update post status and menu order |
| | | $post_updates = ['ID' => $timeline['id']]; |
| | | |
| | | if (!$is_parent) { |
| | | $post_updates['menu_order'] = $order; |
| | | |
| | | // Auto-publish child if parent is published |
| | | if ($parent_is_published) { |
| | | $current_post = get_post($timeline['id']); |
| | | if ($current_post && $current_post->post_status !== 'publish') { |
| | | $post_updates['post_status'] = 'publish'; |
| | | } |
| | | } |
| | | } |
| | | |
| | | if (count($post_updates) > 1) { |
| | | $result = wp_update_post($post_updates); |
| | | error_log('Updated post '.$timeline['id'].' with: '.print_r($post_updates, true).' Result: '.$result); |
| | | $clearParent = true; |
| | | } |
| | | |
| | | // Update metadata |
| | | $meta = new MetaManager($timeline['id'], 'post'); |
| | | $oldValues = $meta->getAll(array_keys($allowedFields)); |
| | | |
| | | // Set number taxonomy to menu_order (always update for reordering) |
| | | if (!$is_parent) { |
| | | $number_value = $order; |
| | | $term = get_term_by('name', (string)$number_value, BASE.'number'); |
| | | if (!$term) { |
| | | $result = wp_insert_term((string)$number_value, BASE.'number'); |
| | | if ($result && !is_wp_error($result)) { |
| | | $term = $result['term_id']; |
| | | } |
| | | } else { |
| | | $term = $term->term_id; |
| | | } |
| | | $allowedFields['number'] = $term; |
| | | } |
| | | |
| | | // Auto-timeline logic |
| | | if ($prevDate) { |
| | | $newDate = array_key_exists('date', $oldValues) ? $oldValues['date'] : ((array_key_exists('date', $allowedFields)) ? $allowedFields['date'] : null); |
| | | if ($newDate) { |
| | | $date1 = new \DateTime($prevDate); |
| | | $date2 = new \DateTime($newDate); |
| | | $weeks = floor($date1->diff($date2)->days / 7); |
| | | if ($weeks > 0) { |
| | | $termToCheck = $weeks.' Weeks'; |
| | | $term = get_term_by('name', $termToCheck, BASE.'timeline'); |
| | | if (!$term) { |
| | | $result = wp_insert_term($termToCheck, BASE.'timeline'); |
| | | if ($result && !is_wp_error($result)) { |
| | | $term = $result['term_id']; |
| | | } |
| | | } else { |
| | | $term = $term->term_id; |
| | | } |
| | | $allowedFields['timeline'] = $term; |
| | | } |
| | | } |
| | | } |
| | | $prevDate = array_key_exists('date', $oldValues) ? $oldValues['date'] : ((array_key_exists('date', $allowedFields)) ? $allowedFields['date'] : $prevDate); |
| | | |
| | | $updateValues = array_filter($allowedFields, function($value, $key) use ($oldValues) { |
| | | return (!array_key_exists($key, $oldValues) || $value !== $oldValues[$key]); |
| | | }, ARRAY_FILTER_USE_BOTH); |
| | | error_log('Setting values for '.$timeline['id'].': '.print_r($updateValues, true)); |
| | | |
| | | $meta->setAll($updateValues); |
| | | $timeline['id'] = (int) $timeline['id']; |
| | | |
| | | $success[] = $timeline['id']; |
| | | } |
| | | $data['total'] = $total; |
| | | $data['total_pages'] = max($total/$args['number'], 1); |
| | | $data['has_more'] = ($args['page'] * $args['number']) < $total; |
| | | } else { |
| | | $terms = []; |
| | | } |
| | | |
| | | // Delete any remaining children that no longer exist |
| | | if (!empty($existing_children)) { |
| | | foreach ($existing_children as $ID) { |
| | | wp_delete_post($ID); |
| | | } |
| | | } |
| | | $this->fields = $registrar->getFields()??[]; |
| | | |
| | | if ($clearParent) { |
| | | $this->cache->clear(); |
| | | CacheManager::onPostSave($parent_id, $parent_post); |
| | | } |
| | | $this->taxonomies = []; |
| | | $data['items'] =array_map([$this, 'prepareTerm'], $terms); |
| | | |
| | | $this->cache->set($key, $data); |
| | | |
| | | return ['success' => true, 'data' => [ |
| | | 'success' => $success, |
| | | 'errors' => $errors |
| | | ]]; |
| | | $response = Response::success($data); |
| | | return $this->addCacheHeaders($response); |
| | | } |
| | | |
| | | /** |
| | | * Handle batch content creation from uploads |
| | | * @param WP_REST_Request $request |
| | | * |
| | | * @return WP_REST_Response |
| | | */ |
| | | public function handleBatchCreation(WP_REST_Request $request):WP_REST_Response |
| | | { |
| | | //Operation has two parts |
| | | //First, queue image processing |
| | | //Then queue post creation from the stored IDs, depending on mode |
| | | //if direct, each image becomes a new post |
| | | //if selection, each group becomes its own post, |
| | | // and ungrouped items each become their own post |
| | | if (!isset($_FILES['files'])) { |
| | | return new WP_REST_Response([ |
| | | 'success' => false, |
| | | 'message' => 'No files uploaded...', |
| | | ]); |
| | | } |
| | | protected function applySearchFilters(array $args, array $params): array |
| | | { |
| | | $search_term = sanitize_text_field($params['search']); |
| | | |
| | | $data = $request->get_params(); |
| | | // Search term is already in $args['s'] from earlier |
| | | |
| | | // Get all taxonomies registered to this post type |
| | | $taxonomies = get_object_taxonomies($this->post_type, 'names'); |
| | | |
| | | if (empty($taxonomies)) { |
| | | return $args; |
| | | } |
| | | |
| | | // Cache the taxonomy term lookup per search term + post type |
| | | $term_cache_key = 'search_terms_' . md5($search_term . $this->post_type); |
| | | $matching_term_ids = $this->cache->get($term_cache_key); |
| | | |
| | | if ($matching_term_ids === false) { |
| | | $matching_term_ids = []; |
| | | |
| | | foreach ($taxonomies as $taxonomy) { |
| | | $terms = get_terms([ |
| | | 'taxonomy' => $taxonomy, |
| | | 'search' => $search_term, |
| | | 'hide_empty' => false, |
| | | 'fields' => 'ids' |
| | | ]); |
| | | |
| | | if (!is_wp_error($terms) && !empty($terms)) { |
| | | $matching_term_ids = array_merge($matching_term_ids, $terms); |
| | | } |
| | | } |
| | | |
| | | // Cache term IDs for 1 hour |
| | | $this->cache->set($term_cache_key, $matching_term_ids, 3600); |
| | | } |
| | | |
| | | if (empty($matching_term_ids)) { |
| | | return $args; |
| | | } |
| | | |
| | | // Build tax_query for matching terms |
| | | $term_queries = []; |
| | | |
| | | foreach ($taxonomies as $taxonomy) { |
| | | $taxonomy_term_ids = array_filter($matching_term_ids, function ($term_id) use ($taxonomy) { |
| | | $term = get_term($term_id); |
| | | return !is_wp_error($term) && $term->taxonomy === $taxonomy; |
| | | }); |
| | | |
| | | if (!empty($taxonomy_term_ids)) { |
| | | $term_queries[] = [ |
| | | 'taxonomy' => $taxonomy, |
| | | 'field' => 'term_id', |
| | | 'terms' => array_values($taxonomy_term_ids), |
| | | 'operator' => 'IN' |
| | | ]; |
| | | } |
| | | } |
| | | |
| | | if (!empty($term_queries)) { |
| | | if (isset($args['tax_query'])) { |
| | | $args['tax_query'] = [ |
| | | 'relation' => 'OR', |
| | | $args['tax_query'], |
| | | [ |
| | | 'relation' => 'OR', |
| | | ...$term_queries |
| | | ] |
| | | ]; |
| | | } else { |
| | | $args['tax_query'] = [ |
| | | 'relation' => 'OR', |
| | | ...$term_queries |
| | | ]; |
| | | } |
| | | } |
| | | |
| | | return $args; |
| | | } |
| | | |
| | | /** |
| | | * Gets allowed taxonomies for a particular content |
| | | * @param string $content |
| | | * |
| | | * @return array |
| | | */ |
| | | protected function getTaxonomies(string $content): array |
| | | { |
| | | $registrar = Registrar::getInstance($content); |
| | | if (!$registrar || $registrar->getType()!== 'post') { |
| | | return []; |
| | | } |
| | | $out = []; |
| | | foreach ($registrar->registrar->taxonomies as $tax) { |
| | | $taxReg = Registrar::getInstance($tax); |
| | | $out[jvbCheckBase($tax)] = [ |
| | | 'label' => $taxReg->getPlural(), |
| | | 'icon' => $taxReg->getIcon()??jvbDefaultIcon() |
| | | ]; |
| | | } |
| | | |
| | | return $out; |
| | | } |
| | | |
| | | |
| | | $user_id = $data['user']; |
| | | if (!$this->userCheck($user_id)) { |
| | | return new WP_REST_Response([ |
| | | 'success' => 'false', |
| | | 'message' => 'Invalid user match... are you a bot?' |
| | | ]); |
| | | } |
| | | $operation_id = $data['id']; |
| | | $response = new WP_REST_Response([ |
| | | 'success' => true, |
| | | 'message' => 'Successfully sent to server. Added to queue.', |
| | | 'operation_id' => $operation_id, |
| | | 'status' => 'pending' |
| | | ]); |
| | | $this->queue = JVB()->queue(); |
| | | JVB()->routes('uploads')->handleUploadRequest($request, false); |
| | | $this->queue->queueOperation( |
| | | 'batch_creation', |
| | | $user_id, |
| | | [ |
| | | 'content' => $request->get_param('content'), |
| | | 'mode' => $request->get_param('mode') ?: 'direct', |
| | | 'files_data'=> $request->get_param('files_data') |
| | | ], |
| | | [ |
| | | 'operation_id' => $operation_id, |
| | | 'priority' => 'high', |
| | | 'notification' => true, |
| | | 'depends_on' => $operation_id.'_upload' |
| | | ] |
| | | ); |
| | | |
| | | return $response; |
| | | } |
| | | /** |
| | | * Generates a post title, based on content type |
| | | * @param string $content the post type |
| | | * |
| | | * @return string |
| | | */ |
| | | protected function generatePostTitle(string $content): string |
| | | { |
| | | $username = get_user_meta($this->user_id, 'first_name', true); |
| | | $link = get_user_meta($this->user_id, BASE . 'link', true); |
| | | $city = jvbArtistCity($link); |
| | | return ucfirst($content) . ' by ' . $city . ' artist ' . $username; |
| | | } |
| | | |
| | | /** |
| | | * Generates a post title, based on content type |
| | | * @param string $content the post type |
| | | * |
| | | * @return string |
| | | */ |
| | | protected function generatePostTitle(string $content):string |
| | | { |
| | | $username = get_user_meta($this->user_id, 'first_name', true); |
| | | $link = get_user_meta($this->user_id, BASE.'link', true); |
| | | $city = jvbArtistCity($link); |
| | | return ucfirst($content).' by '.$city.' artist '.$username; |
| | | } |
| | | |
| | | /** |
| | | * @param WP_Post $post the wordpress post object |
| | | * |
| | | * @return array |
| | | */ |
| | | protected function prepareItem(WP_Post $post, bool $skip = false, bool $fields = true):array |
| | | { |
| | | if (!$skip && Features::forContent($post->post_type)->has('is_timeline')) { |
| | | /** |
| | | * @param WP_Post $post the post object |
| | | * |
| | | * @return array |
| | | */ |
| | | protected function preparePost(WP_Post $post, bool $skip = false, bool $fields = true): array |
| | | { |
| | | $registrar = Registrar::getInstance($post->post_type); |
| | | if (!$skip && $registrar && $registrar->hasFeature('is_timeline')) { |
| | | $this->initTimelineFields($post->post_type); |
| | | return $this->formatTimeline($post); |
| | | } |
| | | $this->meta = new MetaManager($post->ID, 'post'); |
| | | $data = [ |
| | | 'id' => $post->ID, |
| | | 'title' => $post->post_title, |
| | | 'status' => $post->post_status, |
| | | 'date' => $post->post_date, |
| | | 'modified' => $post->post_modified, |
| | | 'thumbnail' => get_the_post_thumbnail_url($post->ID), |
| | | 'alt' => get_post_meta(get_post_thumbnail_id(), '_wp_attachment_image_alt', true), |
| | | 'icon' => $this->post_type, |
| | | 'taxonomies'=> [], |
| | | 'fields' => ($fields) ? $this->meta->getAll() : [], |
| | | 'images' => [], |
| | | ]; |
| | | $this->meta = Meta::forPost($post->ID); |
| | | $fields = ($fields) ? $this->meta->getAll() : []; |
| | | $data = [ |
| | | 'id' => $post->ID, |
| | | 'title' => $post->post_title, |
| | | 'status' => $post->post_status, |
| | | 'date' => $post->post_date, |
| | | 'modified' => $post->post_modified, |
| | | 'thumbnail' => get_the_post_thumbnail_url($post->ID), |
| | | 'alt' => get_post_meta(get_post_thumbnail_id(), '_wp_attachment_image_alt', true), |
| | | 'icon' => $registrar->getIcon(), |
| | | 'taxonomies' => [], |
| | | 'fields' => $fields, |
| | | 'images' => [], |
| | | ]; |
| | | |
| | | // Add taxonomy terms |
| | | foreach ($this->taxonomies as $taxonomy => $options) { |
| | | $tax = str_replace(BASE, '', $taxonomy); |
| | | $terms = wp_get_object_terms( |
| | | $post->ID, |
| | | $taxonomy, |
| | | ['fields' => 'id=>name'] |
| | | ); |
| | | $data['taxonomies'][$tax] = [ |
| | | 'terms' => (is_wp_error($terms))? [] : $terms, |
| | | 'name' => $options['label'], |
| | | 'icon' => $tax |
| | | ]; |
| | | } |
| | | |
| | | $images = $this->extractImages(); |
| | | |
| | | |
| | | if (!empty($images)) { |
| | | $data['images'] = $images; |
| | | } |
| | | |
| | | return $data; |
| | | } |
| | | protected function extractImages(array $fields = []):array |
| | | { |
| | | //Extract images |
| | | $images = []; |
| | | $get = []; |
| | | $fields = (empty($fields)) ? $this->fields : $fields; |
| | | foreach ($fields as $field => $config) { |
| | | if ($config['type'] === 'gallery' || $config['type'] === 'image' || $field === 'post_thumbnail') { |
| | | $get[] = $field; |
| | | } |
| | | $images = $this->extractImages($fields, $this->meta); |
| | | if (!empty($images)) { |
| | | $data['images'] = $images; |
| | | } |
| | | |
| | | if (!empty($get)) { |
| | | $allImages = $this->meta->getAll($get); |
| | | foreach($allImages as $k => $imgs){ |
| | | $temp = explode(',', $imgs); |
| | | foreach($temp as $img) { |
| | | if (is_numeric($img) && !array_key_exists($img, $images)) { |
| | | $images[$img] = jvbImageData((int) $img); |
| | | } |
| | | } |
| | | } |
| | | |
| | | $taxonomies = $this->extractTerms($fields, $this->meta); |
| | | if (!empty($taxonomies)) { |
| | | $data['taxonomies'] = $taxonomies; |
| | | } |
| | | return $images; |
| | | return $data; |
| | | } |
| | | |
| | | public function formatTimeline(WP_Post $post):array |
| | | /** |
| | | * @param WP_Term $post the post object |
| | | * |
| | | * @return array |
| | | */ |
| | | protected function prepareTerm(WP_Term $post, bool $fields = true): array |
| | | { |
| | | $item = $this->prepareItem($post, true, false); |
| | | $registrar = Registrar::getInstance($post->taxonomy); |
| | | |
| | | $this->meta = Meta::forTerm($post->term_id); |
| | | $fields = ($fields) ? $this->meta->getAll() : []; |
| | | $data = [ |
| | | 'id' => $post->term_id, |
| | | 'title' => $post->name, |
| | | 'date' => $fields['created_date']??'', |
| | | 'modified' => $fields['modified_date']??'', |
| | | 'thumbnail' => '', |
| | | 'icon' => $registrar->getIcon(), |
| | | 'taxonomies' => [], |
| | | 'fields' => $fields, |
| | | 'images' => [], |
| | | ]; |
| | | |
| | | $images = $this->extractImages($fields, $this->meta); |
| | | if (!empty($images)) { |
| | | $data['images'] = $images; |
| | | } |
| | | |
| | | $taxonomies = $this->extractTerms($fields, $this->meta); |
| | | if (!empty($taxonomies)) { |
| | | $data['taxonomies'] = $taxonomies; |
| | | } |
| | | |
| | | error_log('Term data: '.print_r($data, true)); |
| | | return $data; |
| | | } |
| | | |
| | | |
| | | public function formatTimeline(WP_Post $post): array |
| | | { |
| | | $item = $this->preparePost($post, true, false); |
| | | //Step 1: Get the fields that apply to all posts |
| | | $mainMeta = new MetaManager($post->ID, 'post'); |
| | | $mainMeta = Meta::forPost($post->ID); |
| | | $item['fields'] = $mainMeta->getAll($this->timelineSharedFields); |
| | | |
| | | //Step 2: Get the fields for each individual posts |
| | | $children = get_children(['post_parent' => $post->ID, 'orderby' => 'date', 'order' => 'ASC', 'post_status' => ['publish', 'draft'], 'fields'=> 'ids']); |
| | | $children = get_children(['post_parent' => $post->ID, 'orderby' => 'date', 'order' => 'ASC', 'post_status' => ['publish', 'draft'], 'fields' => 'ids']); |
| | | array_unshift($children, $post->ID); |
| | | |
| | | $subFields = []; |
| | | $images = []; |
| | | foreach ($children as $child) { |
| | | $meta = new MetaManager($child, 'post'); |
| | | $meta = Meta::forPost($child); |
| | | $f = $meta->getAll($this->timelineUniqueFields); |
| | | $f = ['id' => $child] + $f; |
| | | $f = ['id' => $child] + $f; |
| | | $subFields[] = $f; |
| | | |
| | | $images[$f['post_thumbnail']] = jvbImageData((int) $f['post_thumbnail']); |
| | | $images[$f['post_thumbnail']] = jvbImageData((int)$f['post_thumbnail']); |
| | | } |
| | | $item['fields']['timeline'] = $subFields; |
| | | $item['fields']['timeline_gallery'] = $subFields; |
| | | $item['images'] = $item['images'] + $images; |
| | | $item['number'] = $mainMeta->get('number'); |
| | | |
| | | return $item; |
| | | } |
| | | |
| | | /** |
| | | * Builds the taxonomy query |
| | | * @param array $taxonomies |
| | | * |
| | | * @return array|string[] |
| | | */ |
| | | protected function buildTaxQuery(array $taxonomies):array |
| | | { |
| | | $tax_query = []; |
| | | error_log('Taxonomies in query: '.print_r($taxonomies, true)); |
| | | |
| | | foreach ($taxonomies as $taxonomy => $terms) { |
| | | if (!empty($terms)) { |
| | | $tax_query[] = [ |
| | | 'taxonomy' => jvbCheckBase($taxonomy), |
| | | 'field' => 'term_id', |
| | | 'terms' => array_map('absint', (array)$terms) |
| | | ]; |
| | | } |
| | | } |
| | | |
| | | |
| | | return count($tax_query) > 1 |
| | | ? array_merge(['relation' => 'AND'], $tax_query) |
| | | : $tax_query; |
| | | } |
| | | |
| | | /** |
| | | * Builds the date query |
| | | * @param array $date_params |
| | | * |
| | | * @return array |
| | | */ |
| | | protected function buildDateQuery(array $date_params):array |
| | | { |
| | | $query = []; |
| | | |
| | | if (!empty($date_params['after'])) { |
| | | $query['after'] = sanitize_text_field($date_params['after']); |
| | | } |
| | | |
| | | if (!empty($date_params['before'])) { |
| | | $query['before'] = sanitize_text_field($date_params['before']); |
| | | } |
| | | |
| | | if (isset($date_params['inclusive'])) { |
| | | $query['inclusive'] = (bool)$date_params['inclusive']; |
| | | } |
| | | |
| | | return empty($query) ? [] : [$query]; |
| | | } |
| | | |
| | | /** |
| | | * @param int $post_id |
| | | * |
| | | * @return bool |
| | | */ |
| | | protected function verifyOwnership(int $post_id):bool |
| | | { |
| | | $post = get_post($post_id); |
| | | return $post && $post->post_author == $this->user_id; |
| | | } |
| | | |
| | | /** |
| | | * Processes operation from Operation Queue |
| | | * @param WP_Error|array $result |
| | | * @param object $operation |
| | | * @param array $data |
| | | * |
| | | * @return array|WP_Error |
| | | */ |
| | | public function processOperation(WP_Error|array $result, object $operation, array $data):array|WP_Error |
| | | { |
| | | if ($operation->type === 'batch_creation') { |
| | | $JVB = JVB(); |
| | | $queue = $JVB->queue(); |
| | | |
| | | $images = $queue->getOperationValue($operation->id.'_upload', 'result')??false; |
| | | |
| | | $this->user_id = $operation->user_id; |
| | | $this->post_type = BASE.$data['content']; |
| | | try { |
| | | $results = []; |
| | | if ($images) { |
| | | if ($data['mode'] == 'selection') { |
| | | $total = count($images); |
| | | foreach ($images as $group => $files) { |
| | | $settings = json_decode($data['files_data'][$group]); |
| | | |
| | | switch ($settings->type) { |
| | | case 'group': |
| | | $featuredIndex = $settings->metadata->featuredFile??0; |
| | | $title = $settings->metadata->title??$this->generatePostTitle($data['content']); |
| | | $new = wp_insert_post([ |
| | | 'post_type' => BASE.$data['content'], |
| | | 'post_title' => $title, |
| | | 'post_status' => 'draft', |
| | | 'post_author' => $operation->user_id |
| | | ]); |
| | | if ($new && !is_wp_error($new)) { |
| | | set_post_thumbnail($new, $files[$featuredIndex]['attachment_id']); |
| | | unset($files[$featuredIndex]); |
| | | if (!empty($files)) { |
| | | $meta = new MetaManager($new, 'post'); |
| | | $IDs = array_column($files, 'attachment_id'); |
| | | $meta->updateValue('gallery', implode(',', $IDs)); |
| | | } |
| | | $results[] = $new; |
| | | // $queue->updateOperationProgress($operation->id, $group + 1, $total); |
| | | } |
| | | break; |
| | | default: |
| | | foreach ($files as $img) { |
| | | $new = wp_insert_post([ |
| | | 'post_type' => BASE. $data['content'], |
| | | 'post_title' => $this->generatePostTitle($data['content']), |
| | | 'post_status' => 'draft', |
| | | 'post_author' => $operation->user_id |
| | | ]); |
| | | |
| | | if ($new && !is_wp_error($new)) { |
| | | set_post_thumbnail($new, $img['attachment_id']); |
| | | $results[] = $new; |
| | | // $queue->updateOperationProgress($operation->id, $group + 1, $total); |
| | | } |
| | | } |
| | | break; |
| | | } |
| | | } |
| | | } else { |
| | | $total = count($images); |
| | | foreach ($images as $key => $img) { |
| | | $new = wp_insert_post([ |
| | | 'post_type' => BASE.$data['content'], |
| | | 'post_title' => $this->generatePostTitle($data['content']), |
| | | 'post_status' => 'draft', |
| | | 'post_author' => $operation->user_id |
| | | ]); |
| | | if ($new && !is_wp_error($new)) { |
| | | set_post_thumbnail($new, $img['attachment_id']); |
| | | } |
| | | $results[] = $new; |
| | | // $queue->updateOperationProgress($operation->id, $key + 1, $total); |
| | | } |
| | | } |
| | | |
| | | //Clear cache |
| | | CacheManager::for($data['content'])->clear(); |
| | | CacheManager::for('feed')->clear(); |
| | | } |
| | | |
| | | return [ |
| | | 'success' => true, |
| | | 'result' => $results |
| | | ]; |
| | | } catch (Exception $e) { |
| | | $JVB->error()->log( |
| | | '[ContentRoutes]:processOperation', |
| | | $e->getMessage() |
| | | ); |
| | | } |
| | | |
| | | return $results; |
| | | } elseif ($operation->type == 'content_update') { |
| | | $result = $this->processBatches($operation, $data); |
| | | } |
| | | |
| | | return $result; |
| | | } |
| | | } |