cache_name = 'news'; parent::__construct(); $this->action = 'dash-'; $this->per_page = 20; add_filter(BASE.'handle_bulk_operation', [$this, 'processOperation'], 10, 3); } /** * Registers news routes * @return void */ public function registerRoutes():void { register_rest_route($this->namespace, '/news', [ [ 'methods' => 'GET', 'callback' => [$this, 'getNews'], 'permission_callback' => [$this, 'checkPermission'] ], [ 'methods' => 'POST', 'callback' => [$this, 'handleNewsOperation'], 'permission_callback' => [$this, 'checkPermission'] ] ]); } /** * @param WP_REST_Request $request * * @return WP_REST_Response */ public function handleNewsOperation(WP_REST_Request $request):WP_REST_Response { $queue = JVB()->queue(); $data = $request->get_params(); $user = $data['user']; $operationID = $data['id']; unset($data['user']); unset($data['id']); $queue->queueOperation( 'new_news', $user, $data, [ 'operation_id' => 'u'.$user.'_'.$operationID, 'priority' => 'high', 'notification' => true, ] ); return new WP_REST_Response([ 'success' => true, 'message' => 'Queued for processing.' ]); } /** * @param WP_REST_Request $request * * @return WP_REST_Response */ public function getNews(WP_REST_Request $request):WP_REST_Response { $args = $this->buildQueryArgs($request); $key = $this->cache->generateKey($args); $cache = $this->cache->get($key); if ($cache) { return new WP_REST_Response($cache); } $args['post_type'] = BASE.'news'; $query = new WP_Query($args); $items = array_map([$this, 'formatItem'], $query->posts); $results = [ 'items' => $items, 'has_more' => $query->max_num_pages > $args['paged'], 'total_items' => $query->found_posts, 'total_pages' => $query->max_num_pages ]; $this->cache->set($key, $results); return new WP_REST_Response($results); } /** * @param WP_REST_Request $request * * @return array */ protected function buildQueryArgs(WP_REST_Request $request):array { $data = $request->get_params(); $page = intval($request->get_param('page') ?? 1); $args = [ 'paged' => $page, 'post_status' => 'publish' ]; $args = $this->applyTaxonomyFilters($args, $data); $args = $this->applyOrderFilters($args, $data); $args = $this->applyDateFilters($args, $data); return $this->applyWatchedFilter($args, $data); } /** * @return void */ protected function checkRelationshipManager():void { if (!$this->manager) { $this->manager = new NewsRelationships(); } } /** * @param array $args * @param array $data * * @return array */ protected function applyTaxonomyFilters(array $args, array $data):array { if (array_key_exists('shop', $data)) { $this->checkRelationshipManager(); $shop = (int) $data['shop']; $artists = $this->manager->getShopArtistsWithNews($shop); $args['author__in'] = $artists; } if (array_key_exists('type', $data)) { $args['tax_query'] = [[ 'taxonomy' => BASE.'ntype', 'terms' => (int) $data['type'], ]]; } if (array_key_exists('artist', $data)) { $artist_ids = array_map('intval', (array)$data['artist']); $args['author__in'] = $artist_ids; } return $args; } /** * @param array $args * @param array $data * * @return array */ protected function applyOrderFilters(array $args, array $data):array { if (array_key_exists('orderby', $data) && $data['orderby'] === 'random') { // Handle random ordering $current_seed = jvbGetRandomSeed(); $args['orderby'] = 'RAND(' . $current_seed . ')'; unset($args['order']); } else { // Standard ordering if (in_array($data['orderby'], ['date', 'title', 'name'])) { $args['orderby'] = $data['orderby']; } else { switch ($data['orderby']) { case 'popularity': $args['meta_key'] = BASE.'upvotes'; $args['orderby'] = 'meta_value_num'; break; case 'karma': $args['meta_key'] = BASE.'karma'; $args['orderby'] = 'meta_value_num'; break; default: $args['orderby'] = 'date'; } } $args['order'] = (in_array($data['order'], ['ASC', 'DESC'])) ? $data['order'] : 'DESC'; } return $args; } /** * @param array $args * @param array $data * * @return array */ protected function applyDateFilters(array $args, array $data):array { if (!array_key_exists('date-filter', $data) && !array_key_exists('dateFrom', $data)) { return $args; } if (array_key_exists('dateFrom', $data)) { $dateFrom = strtotime($data['dateFrom']); $dateTo = strtotime($data['dateTo']); if ($dateFrom !== false && $dateTo !== false) { $args['date_query'] = [ [ 'after' => date("c", $dateFrom), 'before' => date("c", $dateTo), 'inclusive' => true, ] ]; } } else { switch ($data['date-filter']) { case 'today': $args['date_query'] = [['after' => '1 day ago']]; break; case 'week': $args['date_query'] = [['after' => '1 week ago']]; break; case 'month': $args['date_query'] = [['after' => '1 month ago']]; break; case 'year': $args['date_query'] = [['after' => '1 year ago']]; break; } } return $args; } /** * @param array $args * @param array $data * * @return array */ protected function applyWatchedFilter(array $args, array $data):array { if (!array_key_exists('watched', $data)) { return $args; } global $wpdb; $favourites_table = $wpdb->prefix . BASE . 'favourites'; $post_types = [BASE.'news']; $placeholders = implode(',', array_fill(0, count($post_types), '%s')); $favourited_ids = $wpdb->get_col($wpdb->prepare( "SELECT target_id FROM {$favourites_table} WHERE user_id = %d AND type IN ($placeholders)", array_merge( [get_current_user_id()], $post_types ) )); if (empty($favourited_ids)) { $favourited_ids = [0]; } $args['post__in'] = isset($args['post__in']) ? array_intersect($args['post__in'], $favourited_ids) : $favourited_ids; return $args; } /** * @param int|object $post * * @return array */ public function formatItem(int|object $post):array { if (is_int($post)) { $post = get_post($post); if (!$post || is_wp_error($post)) { return []; } } $artist = jvbContentFromUser($post->post_author); $upvotes = (int) get_post_meta($post->ID, BASE.'upvotes', true); $downvotes = (int) get_post_meta($post->ID, BASE.'downvotes', true); $comments = JVB()->routes('comments')->getItemResponse($post->ID, BASE.'news'); return [ 'id' => $post->ID, 'title' => $post->post_title, 'tldr' => $post->post_excerpt, 'post_content' => $post->post_content, 'content' => 'news', 'date' => $post->post_date, 'artist' => [ 'id' => $artist['id'], 'name' => $artist['name'], 'url' => $artist['url'] ], 'shop' => $artist['shop'], 'upvotes' => $upvotes, 'downvotes' => $downvotes, 'comments' => $comments, 'type' => $artist['type'], ]; } /** * @param WP_Error|array $result * @param object $operation * @param array $data * * @return WP_Error|array */ public function processOperation(WP_Error|array $result, object $operation, array $data):array|WP_Error { if ($operation->type != 'new_news') { return $result; } $user_id = $operation->user_id; $title = sanitize_text_field($data['post_title']); $tldr = sanitize_textarea_field($data['post_excerpt']); $content = wp_kses_post($data['post_content']); unset($data['post_title']); unset($data['post_excerpt']); unset($data['post_content']); $result = []; $ID = wp_insert_post([ 'post_type' => BASE.'news', 'post_author' => $user_id, 'post_title' => $title, 'post_excerpt' => $tldr, 'post_content' => $content, 'post_status' => 'publish' ]); $result['post_id'] = ($ID && !is_wp_error($ID)); $type = get_term((int) $data['type'], BASE.'ntype'); if ($type && !is_wp_error($type)) { $terms = wp_set_post_terms($ID, $type, BASE.'ntype'); $result['term_update'] = ($terms && !is_wp_error($terms)); } unset($data['type']); if ($ID) { $meta = new MetaManager($ID, 'post'); foreach ($data as $key => $value) { $m = $meta->updateValue($key, $value); $result[$key] = $m; } } $this->cache->flush(); return [ 'success' => true, 'result' => $result ]; } }