'GET', 'callback' => array( $this, 'get' ), 'permission_callback' => array( Routes::class, 'can_admin' ), ), array( 'methods' => 'POST', 'callback' => array( $this, 'save' ), 'permission_callback' => array( Routes::class, 'can_admin' ), ), ) ); register_rest_route( Routes::NS, '/settings/defaults', array( 'methods' => 'GET', 'callback' => array( $this, 'defaults' ), 'permission_callback' => array( Routes::class, 'can_admin' ), ) ); register_rest_route( Routes::NS, '/design/preview', array( 'methods' => 'POST', 'callback' => array( $this, 'preview' ), 'permission_callback' => array( Routes::class, 'can_admin' ), ) ); register_rest_route( Routes::NS, '/setup', array( 'methods' => 'POST', 'callback' => array( $this, 'setup' ), 'args' => array( 'done' => array( 'type' => 'boolean' ) ), 'permission_callback' => array( Routes::class, 'can_admin' ), ) ); register_rest_route( Routes::NS, '/dashboard', array( 'methods' => 'GET', 'callback' => array( $this, 'dashboard' ), 'permission_callback' => array( Routes::class, 'can_edit' ), ) ); } /** * GET settings. * * @return \WP_REST_Response */ public function get() { return rest_ensure_response( Settings::for_client() ); } /** * GET settings/defaults: lets the app mark modified fields and reset sections. * * @return \WP_REST_Response */ public function defaults() { $defaults = Settings::defaults(); foreach ( Settings::secret_keys() as $key ) { unset( $defaults[ $key ] ); } return rest_ensure_response( $defaults ); } /** * POST design/preview: stores unsaved design values for the studio's preview frame. * * @param \WP_REST_Request $request Request. * @return \WP_REST_Response */ public function preview( $request ) { return rest_ensure_response( Settings::store_preview( (array) $request->get_json_params() ) ); } /** * POST setup: marks the setup wizard finished (or reopens it). * * @param \WP_REST_Request $request Request. * @return \WP_REST_Response */ public function setup( $request ) { if ( false === $request['done'] ) { delete_option( 'kdocs_setup_done' ); } else { update_option( 'kdocs_setup_done', time(), false ); } return rest_ensure_response( array( 'done' => (bool) get_option( 'kdocs_setup_done' ) ) ); } /** * POST settings. * * @param \WP_REST_Request $request Request. * @return \WP_REST_Response */ public function save( $request ) { Settings::update( (array) $request->get_json_params() ); return rest_ensure_response( Settings::for_client() ); } /** * Dashboard overview. * * @return \WP_REST_Response */ public function dashboard() { global $wpdb; $counts = wp_count_posts( PostTypes::ARTICLE ); $published = (int) ( $counts->publish ?? 0 ); $drafts = (int) ( $counts->draft ?? 0 ) + (int) ( $counts->pending ?? 0 ); // phpcs:disable WordPress.DB.DirectDatabaseQuery $sum = static function ( $key ) use ( $wpdb ) { return (int) $wpdb->get_var( $wpdb->prepare( "SELECT SUM(CAST(pm.meta_value AS UNSIGNED)) FROM {$wpdb->postmeta} pm INNER JOIN {$wpdb->posts} p ON p.ID = pm.post_id WHERE pm.meta_key = %s AND p.post_type = %s AND p.post_status = 'publish'", $key, PostTypes::ARTICLE ) ); }; // phpcs:enable $views = $sum( '_kdocs_views' ); $yes = $sum( '_kdocs_helpful_yes' ); $no = $sum( '_kdocs_helpful_no' ); $format = static function ( $post ) { $kb = Taxonomies::article_kb( $post ); return array( 'id' => $post->ID, 'title' => html_entity_decode( get_the_title( $post ), ENT_QUOTES, 'UTF-8' ), 'status' => $post->post_status, 'kb' => $kb ? html_entity_decode( $kb->name, ENT_QUOTES, 'UTF-8' ) : '', 'views' => (int) get_post_meta( $post->ID, '_kdocs_views', true ), 'yes' => (int) get_post_meta( $post->ID, '_kdocs_helpful_yes', true ), 'no' => (int) get_post_meta( $post->ID, '_kdocs_helpful_no', true ), 'modified' => get_post_modified_time( 'U', true, $post ), 'editLink' => get_edit_post_link( $post->ID, 'raw' ), 'link' => get_permalink( $post ), ); }; $popular = get_posts( array( 'post_type' => PostTypes::ARTICLE, 'posts_per_page' => 6, 'meta_key' => '_kdocs_views', // phpcs:ignore WordPress.DB.SlowDBQuery 'orderby' => 'meta_value_num', 'order' => 'DESC', ) ); $recent = get_posts( array( 'post_type' => PostTypes::ARTICLE, 'post_status' => array( 'publish', 'draft', 'pending', 'private' ), 'posts_per_page' => 6, 'orderby' => 'modified', 'order' => 'DESC', ) ); $unhelpful = get_posts( array( 'post_type' => PostTypes::ARTICLE, 'posts_per_page' => 5, 'meta_key' => '_kdocs_helpful_no', // phpcs:ignore WordPress.DB.SlowDBQuery 'orderby' => 'meta_value_num', 'order' => 'DESC', 'meta_query' => array( // phpcs:ignore WordPress.DB.SlowDBQuery array( 'key' => '_kdocs_helpful_no', 'value' => 0, 'compare' => '>', 'type' => 'NUMERIC', ), ), ) ); $data = array( 'kpis' => \KarmaDocs\Analytics\Stats::kpis( 30, 0, Features::is_pro() ), 'countsEditors' => \KarmaDocs\Analytics\Identity::count_editors(), ); /** * Filters the dashboard data (Pro adds the "Do next" list and health scores). * * @param array $data Data. */ $data = apply_filters( 'kdocs_dashboard_data', $data ); return rest_ensure_response( $data + array( 'totals' => array( 'kbs' => count( Taxonomies::get_kbs() ), 'articles' => $published, 'drafts' => $drafts, 'categories' => (int) wp_count_terms( array( 'taxonomy' => Taxonomies::CATEGORY, 'hide_empty' => false, ) ), 'views' => $views, 'helpful' => $yes, 'unhelpful' => $no, 'score' => ( $yes + $no ) ? round( $yes / ( $yes + $no ) * 100 ) : null, ), 'popular' => array_map( $format, $popular ), 'recent' => array_map( $format, $recent ), 'unhelpful' => array_map( $format, $unhelpful ), 'isPro' => Features::is_pro(), ) ); } }