\d+)/entries', array( 'methods' => WP_REST_Server::READABLE, 'callback' => array( $this, 'index' ), 'permission_callback' => array( $this, 'can_manage' ), 'args' => array_merge( $this->id_arg(), array( 'page' => array( 'type' => 'integer', 'default' => 1, 'minimum' => 1, ), 'per_page' => array( 'type' => 'integer', 'default' => 20, 'minimum' => 1, 'maximum' => 200, ), 'search' => array( 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field', ), 'status' => array( 'type' => 'string', 'enum' => array( 'all', 'unread', 'read', 'starred' ), 'default' => 'all', ), 'order' => array( 'type' => 'string', 'enum' => array( 'asc', 'desc' ), 'default' => 'desc', ), 'after' => array( 'type' => 'string', 'pattern' => '^(\\d{4}-\\d{2}-\\d{2})?$', ), 'before' => array( 'type' => 'string', 'pattern' => '^(\\d{4}-\\d{2}-\\d{2})?$', ), 'filters' => array( 'type' => 'string', 'description' => 'JSON list of { field, op, value }.', ), ) ), ) ); register_rest_route( self::NS, '/entries/(?P\d+)', array( 'args' => $this->id_arg(), array( 'methods' => WP_REST_Server::READABLE, 'callback' => array( $this, 'show' ), 'permission_callback' => array( $this, 'can_manage' ), ), array( 'methods' => WP_REST_Server::EDITABLE, 'callback' => array( $this, 'update' ), 'permission_callback' => array( $this, 'can_manage' ), 'args' => array( 'status' => array( 'type' => 'string', 'enum' => array( 'read', 'unread' ), ), 'is_starred' => array( 'type' => 'boolean' ), ), ), array( 'methods' => WP_REST_Server::DELETABLE, 'callback' => array( $this, 'delete' ), 'permission_callback' => array( $this, 'can_manage' ), ), ) ); register_rest_route( self::NS, '/entries/bulk', array( 'methods' => WP_REST_Server::CREATABLE, 'callback' => array( $this, 'bulk' ), 'permission_callback' => array( $this, 'can_manage' ), 'args' => array( 'ids' => array( 'type' => 'array', 'items' => array( 'type' => 'integer' ), 'required' => true, 'maxItems' => 500, ), 'action' => array( 'type' => 'string', 'enum' => array( 'read', 'unread', 'star', 'unstar', 'delete' ), 'required' => true, ), ), ) ); } /** * GET /forms/{id}/entries. * * @param WP_REST_Request $request Request. * @return \WP_REST_Response|\WP_Error */ public function index( WP_REST_Request $request ) { $form_id = (int) $request['id']; $form = FormRepository::find( $form_id ); if ( ! $form ) { return new \WP_Error( 'fillaro_not_found', __( 'Form not found.', 'fillaro' ), array( 'status' => 404 ) ); } $result = EntryRepository::query( $form_id, array( 'page' => $request['page'], 'per_page' => $request['per_page'], 'search' => $request['search'], 'status' => $request['status'], 'order' => $request['order'], 'after' => $request['after'], 'before' => $request['before'], 'filters' => self::filters( $request['filters'], $form ), ) ); $response = $this->paged( $result['items'], $result['total'], (int) $request['per_page'] ); $response->header( 'X-PF-Counts', (string) wp_json_encode( EntryRepository::counts( $form_id ) ) ); return $response; } /** * Clean field filters from a JSON string: known fields and operators only. * * @param mixed $raw JSON list of { field, op, value }. * @param array $form Form. * @return array */ public static function filters( $raw, array $form ) { $list = is_string( $raw ) && '' !== $raw ? json_decode( $raw, true ) : array(); $keys = wp_list_pluck( $form['fields'], 'key' ); $ops = array( 'is', 'is_not', 'contains', 'not_contains', 'empty', 'not_empty' ); $out = array(); foreach ( is_array( $list ) ? array_slice( $list, 0, 10 ) : array() as $f ) { if ( ! is_array( $f ) || ! isset( $f['field'], $f['op'] ) || ! in_array( $f['field'], $keys, true ) || ! in_array( $f['op'], $ops, true ) ) { continue; } $out[] = array( 'field' => (string) $f['field'], 'op' => $f['op'], 'value' => isset( $f['value'] ) && is_scalar( $f['value'] ) ? mb_substr( sanitize_text_field( (string) $f['value'] ), 0, 200 ) : '', ); } return $out; } /** * GET /entries/{id}. Marks the entry read. * * @param WP_REST_Request $request Request. * @return \WP_REST_Response|\WP_Error */ public function show( WP_REST_Request $request ) { $entry = EntryRepository::find( (int) $request['id'] ); if ( ! $entry ) { return $this->not_found(); } if ( 'unread' === $entry['status'] ) { EntryRepository::update_many( array( $entry['id'] ), array( 'status' => 'read' ) ); $entry['status'] = 'read'; } $entry['text'] = $this->as_text( $entry ); return rest_ensure_response( $entry ); } /** * PATCH /entries/{id}. * * @param WP_REST_Request $request Request. * @return \WP_REST_Response|\WP_Error */ public function update( WP_REST_Request $request ) { $id = (int) $request['id']; if ( ! EntryRepository::find( $id ) ) { return $this->not_found(); } $changes = array(); if ( null !== $request['status'] ) { $changes['status'] = $request['status']; } if ( null !== $request['is_starred'] ) { $changes['is_starred'] = (bool) $request['is_starred']; } EntryRepository::update_many( array( $id ), $changes ); return rest_ensure_response( EntryRepository::find( $id ) ); } /** * DELETE /entries/{id}. * * @param WP_REST_Request $request Request. * @return \WP_REST_Response|\WP_Error */ public function delete( WP_REST_Request $request ) { if ( ! EntryRepository::delete_many( array( (int) $request['id'] ) ) ) { return $this->not_found(); } return rest_ensure_response( array( 'deleted' => true ) ); } /** * POST /entries/bulk. * * @param WP_REST_Request $request Request. * @return \WP_REST_Response */ public function bulk( WP_REST_Request $request ) { $ids = array_map( 'absint', (array) $request['ids'] ); switch ( $request['action'] ) { case 'delete': $count = EntryRepository::delete_many( $ids ); break; case 'star': case 'unstar': $count = EntryRepository::update_many( $ids, array( 'is_starred' => 'star' === $request['action'] ) ); break; default: $count = EntryRepository::update_many( $ids, array( 'status' => $request['action'] ) ); } return rest_ensure_response( array( 'updated' => $count ) ); } /** * Plain-text answers keyed by field key, using the form definition. * * @param array $entry Entry. * @return array */ private function as_text( array $entry ) { $form = FormRepository::find( $entry['form_id'] ); $out = array(); if ( ! $form ) { return $out; } foreach ( $form['fields'] as $field ) { if ( ! isset( $entry['values'][ $field['key'] ] ) ) { continue; } $type = Plugin::instance()->fields->get( $field['type'] ); $out[ $field['key'] ] = $type ? $type->to_text( $entry['values'][ $field['key'] ], $field ) : ''; } return $out; } /** * 404 error. * * @return \WP_Error */ private function not_found() { return new \WP_Error( 'fillaro_not_found', __( 'Entry not found.', 'fillaro' ), array( 'status' => 404 ) ); } }