true ) ); if ( ! is_array( $data ) ) { return new WP_Error( 'aieo_form_json', sprintf( '%s is not JSON.', $file ) ); } $out[ (string) $file ] = $data; } if ( ! $out ) { return new WP_Error( 'aieo_form_none', sprintf( 'No *.form.json under %s.', $path ) ); } return $out; } public static function validate( array $def ) { $errors = array(); $warnings = array(); if ( self::SCHEMA !== (string) ( $def['schema'] ?? '' ) ) { $errors[] = 'schema: expected ' . self::SCHEMA . '.'; } $key = (string) ( $def['key'] ?? '' ); if ( '' === $key || sanitize_title( $key ) !== $key ) { $errors[] = 'key: a slug (lower case, digits, hyphens) is required.'; } if ( '' === trim( (string) ( $def['title'] ?? '' ) ) ) { $errors[] = 'title: required.'; } if ( isset( $def['from'] ) && ( ! class_exists( 'AIEO_Form_Premade' ) || ! AIEO_Form_Premade::get( (string) $def['from'] ) ) ) { $errors[] = 'from: no premade form ' . (string) $def['from'] . '.'; } foreach ( array_diff( array_keys( $def ), self::KEYS ) as $extra ) { $warnings[] = 'unknown field ' . $extra . ' (ignored).'; } if ( isset( $def['settings'] ) ) { foreach ( array_diff( array_keys( (array) $def['settings'] ), array_keys( AIEO_Form_CPT::setting_defaults() ) ) as $extra ) { $warnings[] = 'settings: unknown ' . $extra . ' (ignored).'; } } $fields = $def['fields'] ?? null; if ( null === $fields && ! isset( $def['from'] ) ) { $errors[] = 'fields: required (or name a premade form in from).'; } $seen = array(); foreach ( (array) $fields as $i => $f ) { $at = 'fields[' . $i . ']'; if ( ! is_array( $f ) || ! AIEO_Form_CPT::normalise_field( $f ) ) { $errors[] = $at . ': not a field (a key, a known type: ' . implode( ', ', array_keys( AIEO_Form_CPT::field_types() ) ) . '; choices for a choice field).'; continue; } $k = sanitize_key( (string) $f['key'] ); if ( isset( $seen[ $k ] ) ) { $errors[] = $at . ': the key ' . $k . ' is taken by an earlier field.'; } $seen[ $k ] = true; foreach ( array_diff( array_keys( $f ), array_merge( array( 'key', 'type', 'label', 'required', 'choices', 'width' ), AIEO_Form_CPT::PROPS ) ) as $extra ) { $warnings[] = $at . ': unknown ' . $extra . ' (ignored).'; } if ( isset( $f['width'] ) && ! in_array( (string) $f['width'], AIEO_Form_CPT::WIDTHS, true ) ) { $warnings[] = $at . ': width is full or half (full used).'; } } return array( 'errors' => $errors, 'warnings' => $warnings ); } public static function apply( array $def, $dry_run = false ) { $check = self::validate( $def ); $report = array( 'key' => (string) ( $def['key'] ?? '' ), 'action' => '', 'id' => 0, 'warnings' => $check['warnings'], 'errors' => $check['errors'] ); if ( $check['errors'] ) { $report['action'] = 'refused'; return $report; } $premade = isset( $def['from'] ) ? AIEO_Form_Premade::get( (string) $def['from'] ) : null; $fields = isset( $def['fields'] ) ? (array) $def['fields'] : (array) ( $premade['fields'] ?? array() ); $settings = array_merge( (array) ( $premade['settings'] ?? array() ), (array) ( $def['settings'] ?? array() ) ); $notify = array_key_exists( 'notify', $def ) ? (bool) $def['notify'] : ( $premade ? ! empty( $premade['notify'] ) : null ); $title = sanitize_text_field( (string) $def['title'] ); $id = self::find( (string) $def['key'] ); $report['id'] = $id; if ( $dry_run ) { $report['action'] = $id ? 'planned update' : 'planned create'; return $report; } $created = false; if ( ! $id ) { $id = wp_insert_post( array( 'post_type' => AIEO_Form_CPT::POST_TYPE, 'post_status' => 'publish', 'post_title' => $title, 'post_name' => (string) $def['key'], ), true ); if ( is_wp_error( $id ) ) { $report['action'] = 'refused'; $report['errors'][] = $id->get_error_message(); return $report; } $created = true; } elseif ( get_post_field( 'post_title', $id ) !== $title || 'publish' !== get_post_status( $id ) ) { wp_update_post( array( 'ID' => $id, 'post_title' => $title, 'post_status' => 'publish' ) ); $created = null; } $changed = AIEO_Form_CPT::save_definition( (int) $id, $fields, $settings, $notify ); $report['id'] = (int) $id; $report['action'] = true === $created ? 'created' : ( null === $created || $changed ? 'updated' : 'unchanged' ); if ( class_exists( 'AIEO_Form_Render' ) ) { AIEO_Form_Render::reset(); } return $report; } public static function find( $key ) { $ids = get_posts( array( 'post_type' => AIEO_Form_CPT::POST_TYPE, 'name' => sanitize_title( (string) $key ), 'post_status' => array( 'publish', 'draft', 'pending', 'private', 'future' ), 'numberposts' => 1, 'fields' => 'ids', 'no_found_rows' => true, 'suppress_filters' => true, ) ); return $ids ? (int) $ids[0] : 0; } }