Fix array schema retrieval for annex repeaters#38
Conversation
| private function build_schema_from_fields( $fields ) { | ||
| $raw_schema = Resolate_Template_Parser::build_schema_from_field_definitions( $fields ); | ||
| if ( ! is_array( $raw_schema ) ) { | ||
| return array(); | ||
| } | ||
|
|
||
| $schema = array(); | ||
| foreach ( $raw_schema as $entry ) { | ||
| if ( ! is_array( $entry ) || empty( $entry['slug'] ) ) { | ||
| continue; | ||
| } | ||
|
|
||
| $slug = sanitize_key( $entry['slug'] ); | ||
| $label = isset( $entry['label'] ) ? sanitize_text_field( $entry['label'] ) : $this->humanize_slug( $slug ); | ||
| $type = isset( $entry['type'] ) ? sanitize_key( $entry['type'] ) : 'textarea'; | ||
| $placeholder = isset( $entry['placeholder'] ) ? $this->sanitize_placeholder_name( $entry['placeholder'] ) : $slug; | ||
| $data_type = isset( $entry['data_type'] ) ? sanitize_key( $entry['data_type'] ) : 'text'; | ||
|
|
||
| if ( '' === $slug ) { | ||
| continue; | ||
| } | ||
| if ( '' === $label ) { | ||
| $label = $this->humanize_slug( $slug ); | ||
| } | ||
| if ( '' === $placeholder ) { | ||
| $placeholder = $slug; | ||
| } | ||
|
|
||
| if ( 'array' === $type ) { | ||
| $item_schema = array(); | ||
| if ( isset( $entry['item_schema'] ) && is_array( $entry['item_schema'] ) ) { | ||
| foreach ( $entry['item_schema'] as $key => $item ) { | ||
| $item_key = sanitize_key( $key ); | ||
| if ( '' === $item_key ) { | ||
| continue; | ||
| } | ||
| $item_label = isset( $item['label'] ) ? sanitize_text_field( $item['label'] ) : $this->humanize_slug( $item_key ); | ||
| $item_type = isset( $item['type'] ) ? sanitize_key( $item['type'] ) : 'textarea'; | ||
| if ( ! in_array( $item_type, array( 'single', 'textarea', 'rich' ), true ) ) { | ||
| $item_type = 'textarea'; | ||
| } | ||
| $item_data_type = isset( $item['data_type'] ) ? sanitize_key( $item['data_type'] ) : 'text'; | ||
| if ( ! in_array( $item_data_type, array( 'text', 'number', 'boolean', 'date' ), true ) ) { | ||
| $item_data_type = 'text'; | ||
| } | ||
| $item_schema[ $item_key ] = array( | ||
| 'label' => $item_label, | ||
| 'type' => $item_type, | ||
| 'data_type' => $item_data_type, | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| $schema[] = array( | ||
| 'slug' => $slug, | ||
| 'label' => $label, | ||
| 'type' => 'array', | ||
| 'placeholder' => $placeholder, | ||
| 'data_type' => 'array', | ||
| 'item_schema' => $item_schema, | ||
| ); | ||
| continue; | ||
| } | ||
|
|
||
| if ( ! in_array( $type, array( 'single', 'textarea', 'rich' ), true ) ) { | ||
| $type = 'textarea'; | ||
| } | ||
| if ( ! in_array( $data_type, array( 'text', 'number', 'boolean', 'date' ), true ) ) { | ||
| $data_type = 'text'; | ||
| } | ||
|
|
||
| $schema[] = array( | ||
| 'slug' => $slug, | ||
| 'label' => $label, | ||
| 'type' => $type, | ||
| 'placeholder' => $placeholder, | ||
| 'data_type' => $data_type, | ||
| ); | ||
| } | ||
|
|
||
| return $schema; | ||
| } |
Check warning
Code scanning / PHPMD
Code Size Rules: CyclomaticComplexity Warning
| private function build_schema_from_fields( $fields ) { | ||
| $raw_schema = Resolate_Template_Parser::build_schema_from_field_definitions( $fields ); | ||
| if ( ! is_array( $raw_schema ) ) { | ||
| return array(); | ||
| } | ||
|
|
||
| $schema = array(); | ||
| foreach ( $raw_schema as $entry ) { | ||
| if ( ! is_array( $entry ) || empty( $entry['slug'] ) ) { | ||
| continue; | ||
| } | ||
|
|
||
| $slug = sanitize_key( $entry['slug'] ); | ||
| $label = isset( $entry['label'] ) ? sanitize_text_field( $entry['label'] ) : $this->humanize_slug( $slug ); | ||
| $type = isset( $entry['type'] ) ? sanitize_key( $entry['type'] ) : 'textarea'; | ||
| $placeholder = isset( $entry['placeholder'] ) ? $this->sanitize_placeholder_name( $entry['placeholder'] ) : $slug; | ||
| $data_type = isset( $entry['data_type'] ) ? sanitize_key( $entry['data_type'] ) : 'text'; | ||
|
|
||
| if ( '' === $slug ) { | ||
| continue; | ||
| } | ||
| if ( '' === $label ) { | ||
| $label = $this->humanize_slug( $slug ); | ||
| } | ||
| if ( '' === $placeholder ) { | ||
| $placeholder = $slug; | ||
| } | ||
|
|
||
| if ( 'array' === $type ) { | ||
| $item_schema = array(); | ||
| if ( isset( $entry['item_schema'] ) && is_array( $entry['item_schema'] ) ) { | ||
| foreach ( $entry['item_schema'] as $key => $item ) { | ||
| $item_key = sanitize_key( $key ); | ||
| if ( '' === $item_key ) { | ||
| continue; | ||
| } | ||
| $item_label = isset( $item['label'] ) ? sanitize_text_field( $item['label'] ) : $this->humanize_slug( $item_key ); | ||
| $item_type = isset( $item['type'] ) ? sanitize_key( $item['type'] ) : 'textarea'; | ||
| if ( ! in_array( $item_type, array( 'single', 'textarea', 'rich' ), true ) ) { | ||
| $item_type = 'textarea'; | ||
| } | ||
| $item_data_type = isset( $item['data_type'] ) ? sanitize_key( $item['data_type'] ) : 'text'; | ||
| if ( ! in_array( $item_data_type, array( 'text', 'number', 'boolean', 'date' ), true ) ) { | ||
| $item_data_type = 'text'; | ||
| } | ||
| $item_schema[ $item_key ] = array( | ||
| 'label' => $item_label, | ||
| 'type' => $item_type, | ||
| 'data_type' => $item_data_type, | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| $schema[] = array( | ||
| 'slug' => $slug, | ||
| 'label' => $label, | ||
| 'type' => 'array', | ||
| 'placeholder' => $placeholder, | ||
| 'data_type' => 'array', | ||
| 'item_schema' => $item_schema, | ||
| ); | ||
| continue; | ||
| } | ||
|
|
||
| if ( ! in_array( $type, array( 'single', 'textarea', 'rich' ), true ) ) { | ||
| $type = 'textarea'; | ||
| } | ||
| if ( ! in_array( $data_type, array( 'text', 'number', 'boolean', 'date' ), true ) ) { | ||
| $data_type = 'text'; | ||
| } | ||
|
|
||
| $schema[] = array( | ||
| 'slug' => $slug, | ||
| 'label' => $label, | ||
| 'type' => $type, | ||
| 'placeholder' => $placeholder, | ||
| 'data_type' => $data_type, | ||
| ); | ||
| } | ||
|
|
||
| return $schema; | ||
| } |
Check warning
Code scanning / PHPMD
Code Size Rules: NPathComplexity Warning
| @@ -404,81 +453,81 @@ private static function build_output_path( $post_id, $extension ) { | |||
| * | |||
| * @return string Absolute directory path. | |||
| */ | |||
| private static function ensure_output_dir() { | |||
| $upload_dir = wp_upload_dir(); | |||
| $dir = trailingslashit( $upload_dir['basedir'] ) . 'resolate'; | |||
| if ( ! is_dir( $dir ) ) { | |||
| wp_mkdir_p( $dir ); | |||
| } | |||
|
|
|||
| return $dir; | |||
| } | |||
|
|
|||
| /** | |||
| * Sanitize placeholders preserving TinyButStrong supported characters. | |||
| * | |||
| * @param string $placeholder Placeholder name. | |||
| * @return string | |||
| */ | |||
| private static function sanitize_placeholder_name( $placeholder ) { | |||
| $placeholder = (string) $placeholder; | |||
| $placeholder = preg_replace( '/[^A-Za-z0-9._:-]/', '', $placeholder ); | |||
| return $placeholder; | |||
| } | |||
|
|
|||
| /** | |||
| * Normalize a field value based on the detected data type. | |||
| * | |||
| * @param string $value Original value. | |||
| * @param string $data_type Detected data type. | |||
| * @return mixed | |||
| */ | |||
| private static function normalize_field_value( $value, $data_type ) { | |||
| $value = is_string( $value ) ? trim( $value ) : $value; | |||
| $data_type = sanitize_key( $data_type ); | |||
|
|
|||
| switch ( $data_type ) { | |||
| case 'number': | |||
| if ( '' === $value ) { | |||
| return ''; | |||
| } | |||
| if ( is_numeric( $value ) ) { | |||
| return 0 + $value; | |||
| } | |||
| $filtered = preg_replace( '/[^0-9.,\-]/', '', (string) $value ); | |||
| if ( '' === $filtered ) { | |||
| return ''; | |||
| } | |||
| $normalized = str_replace( ',', '.', $filtered ); | |||
| if ( is_numeric( $normalized ) ) { | |||
| return 0 + $normalized; | |||
| } | |||
| return $value; | |||
| case 'boolean': | |||
| if ( is_bool( $value ) ) { | |||
| return $value ? 1 : 0; | |||
| } | |||
| $value = strtolower( (string) $value ); | |||
| if ( in_array( $value, array( '1', 'true', 'si', 'sí', 'yes', 'on' ), true ) ) { | |||
| return 1; | |||
| } | |||
| if ( in_array( $value, array( '0', 'false', 'no', 'off' ), true ) ) { | |||
| return 0; | |||
| } | |||
| return '' === $value ? 0 : 0; | |||
| case 'date': | |||
| if ( '' === $value ) { | |||
| return ''; | |||
| } | |||
| $timestamp = strtotime( (string) $value ); | |||
| if ( false === $timestamp ) { | |||
| return $value; | |||
| } | |||
| return wp_date( 'Y-m-d', $timestamp ); | |||
| default: | |||
| return $value; | |||
| } | |||
| } | |||
| private static function ensure_output_dir() { | |||
| $upload_dir = wp_upload_dir(); | |||
| $dir = trailingslashit( $upload_dir['basedir'] ) . 'resolate'; | |||
| if ( ! is_dir( $dir ) ) { | |||
| wp_mkdir_p( $dir ); | |||
| } | |||
|
|
|||
| return $dir; | |||
| } | |||
|
|
|||
| /** | |||
| * Sanitize placeholders preserving TinyButStrong supported characters. | |||
| * | |||
| * @param string $placeholder Placeholder name. | |||
| * @return string | |||
| */ | |||
| private static function sanitize_placeholder_name( $placeholder ) { | |||
| $placeholder = (string) $placeholder; | |||
| $placeholder = preg_replace( '/[^A-Za-z0-9._:-]/', '', $placeholder ); | |||
| return $placeholder; | |||
| } | |||
|
|
|||
| /** | |||
| * Normalize a field value based on the detected data type. | |||
| * | |||
| * @param string $value Original value. | |||
| * @param string $data_type Detected data type. | |||
| * @return mixed | |||
| */ | |||
| private static function normalize_field_value( $value, $data_type ) { | |||
| $value = is_string( $value ) ? trim( $value ) : $value; | |||
| $data_type = sanitize_key( $data_type ); | |||
|
|
|||
| switch ( $data_type ) { | |||
| case 'number': | |||
| if ( '' === $value ) { | |||
| return ''; | |||
| } | |||
| if ( is_numeric( $value ) ) { | |||
| return 0 + $value; | |||
| } | |||
Check warning
Code scanning / PHPMD
Code Size Rules: CyclomaticComplexity Warning
| public static function build_schema_from_field_definitions( $fields ) { | ||
| if ( ! is_array( $fields ) ) { | ||
| return array(); | ||
| } | ||
|
|
||
| $array_defs = array(); | ||
| $array_order = array(); | ||
| $repeat_hints = array(); | ||
| $pending = array(); | ||
| $order_counter = 0; | ||
|
|
||
| foreach ( $fields as $index => $field ) { | ||
| if ( ! is_array( $field ) ) { | ||
| continue; | ||
| } | ||
|
|
||
| $placeholder = isset( $field['placeholder'] ) ? trim( (string) $field['placeholder'] ) : ''; | ||
| $parameters = isset( $field['parameters'] ) && is_array( $field['parameters'] ) ? $field['parameters'] : array(); | ||
| $label = isset( $field['label'] ) ? sanitize_text_field( $field['label'] ) : ''; | ||
| $data_type = isset( $field['data_type'] ) ? sanitize_key( $field['data_type'] ) : 'text'; | ||
|
|
||
| $array_match = self::detect_array_placeholder_with_index( $placeholder ); | ||
| if ( $array_match ) { | ||
| $base = $array_match['base']; | ||
| $key = $array_match['key']; | ||
|
|
||
| if ( '' === $base || '' === $key ) { | ||
| continue; | ||
| } | ||
|
|
||
| $repeat_hints[ $base ] = true; | ||
|
|
||
| if ( ! isset( $array_defs[ $base ] ) ) { | ||
| $array_defs[ $base ] = array( | ||
| 'slug' => $base, | ||
| 'label' => self::humanize_key( $base ), | ||
| 'type' => 'array', | ||
| 'placeholder' => $base, | ||
| 'data_type' => 'array', | ||
| 'item_schema' => array(), | ||
| '_order' => $order_counter++, | ||
| ); | ||
| $array_order[] = $base; | ||
| } | ||
|
|
||
| if ( '' === $label ) { | ||
| $label = self::humanize_key( $array_match['raw_key'] ); | ||
| } | ||
|
|
||
| if ( ! isset( $array_defs[ $base ]['item_schema'][ $key ] ) ) { | ||
| $item_data_type = self::detect_data_type( $placeholder, $parameters ); | ||
| if ( '' === $item_data_type ) { | ||
| $item_data_type = 'text'; | ||
| } | ||
|
|
||
| $array_defs[ $base ]['item_schema'][ $key ] = array( | ||
| 'label' => $label, | ||
| 'type' => self::infer_array_item_type( $key, $item_data_type ), | ||
| 'data_type' => $item_data_type, | ||
| ); | ||
| } | ||
|
|
||
| continue; | ||
| } | ||
|
|
||
| if ( isset( $parameters['repeat'] ) ) { | ||
| $repeat_base = sanitize_key( $parameters['repeat'] ); | ||
| if ( '' !== $repeat_base ) { | ||
| $repeat_hints[ $repeat_base ] = true; | ||
| } | ||
| } | ||
|
|
||
| $pending[] = array( | ||
| 'field' => $field, | ||
| 'placeholder' => $placeholder, | ||
| 'parameters' => $parameters, | ||
| 'label' => $label, | ||
| 'data_type' => $data_type, | ||
| 'index' => $index, | ||
| ); | ||
| } | ||
|
|
||
| $schema = array(); | ||
| $scalar_fields = array(); | ||
|
|
||
| foreach ( $pending as $entry ) { | ||
| $placeholder = $entry['placeholder']; | ||
| $parameters = $entry['parameters']; | ||
| $label = $entry['label']; | ||
| $data_type = $entry['data_type']; | ||
|
|
||
| $dot_match = self::detect_array_placeholder_without_index( $placeholder ); | ||
| if ( $dot_match && ( isset( $repeat_hints[ $dot_match['base'] ] ) || isset( $array_defs[ $dot_match['base'] ] ) ) ) { | ||
| $base = $dot_match['base']; | ||
| $key = $dot_match['key']; | ||
|
|
||
| if ( '' === $base || '' === $key ) { | ||
| continue; | ||
| } | ||
|
|
||
| if ( ! isset( $array_defs[ $base ] ) ) { | ||
| $array_defs[ $base ] = array( | ||
| 'slug' => $base, | ||
| 'label' => self::humanize_key( $base ), | ||
| 'type' => 'array', | ||
| 'placeholder' => $base, | ||
| 'data_type' => 'array', | ||
| 'item_schema' => array(), | ||
| '_order' => $order_counter++, | ||
| ); | ||
| $array_order[] = $base; | ||
| } | ||
|
|
||
| if ( ! isset( $array_defs[ $base ]['item_schema'][ $key ] ) ) { | ||
| $item_data_type = self::detect_data_type( $placeholder, $parameters ); | ||
| if ( '' === $label ) { | ||
| $label = self::humanize_key( $dot_match['raw_key'] ); | ||
| } | ||
| if ( '' === $item_data_type ) { | ||
| $item_data_type = 'text'; | ||
| } | ||
| $array_defs[ $base ]['item_schema'][ $key ] = array( | ||
| 'label' => ( '' !== $label ) ? $label : self::humanize_key( $dot_match['raw_key'] ), | ||
| 'type' => self::infer_array_item_type( $key, $item_data_type ), | ||
| 'data_type' => $item_data_type, | ||
| ); | ||
| } | ||
|
|
||
| continue; | ||
| } | ||
|
|
||
| $slug = isset( $entry['field']['slug'] ) ? sanitize_key( $entry['field']['slug'] ) : ''; | ||
| if ( '' === $slug ) { | ||
| $slug = sanitize_key( $placeholder ); | ||
| } | ||
|
|
||
| if ( '' === $slug ) { | ||
| continue; | ||
| } | ||
|
|
||
| $normalized_placeholder = ''; | ||
| if ( '' !== $placeholder ) { | ||
| $normalized_placeholder = preg_replace( '/[^A-Za-z0-9._:-]/', '', $placeholder ); | ||
| } | ||
| if ( '' === $label ) { | ||
| $source = '' !== $normalized_placeholder ? $normalized_placeholder : $slug; | ||
| $label = self::humanize_key( $source ); | ||
| } | ||
|
|
||
| if ( '' === $normalized_placeholder ) { | ||
| $normalized_placeholder = $slug; | ||
| } | ||
|
|
||
| if ( ! in_array( $data_type, array( 'text', 'number', 'boolean', 'date' ), true ) ) { | ||
| $data_type = 'text'; | ||
| } | ||
|
|
||
| if ( in_array( $slug, array( 'onshow', 'ondata', 'block', 'var' ), true ) ) { | ||
| continue; | ||
| } | ||
|
|
||
| $scalar_fields[] = array( | ||
| 'slug' => $slug, | ||
| 'label' => $label, | ||
| 'placeholder' => $normalized_placeholder, | ||
| 'data_type' => $data_type, | ||
| '_order' => $entry['index'], | ||
| ); | ||
| } | ||
|
|
||
| if ( ! empty( $array_defs ) ) { | ||
| uasort( | ||
| $array_defs, | ||
| static function ( $a, $b ) { | ||
| $order_a = isset( $a['_order'] ) ? intval( $a['_order'] ) : 0; | ||
| $order_b = isset( $b['_order'] ) ? intval( $b['_order'] ) : 0; | ||
| return $order_a <=> $order_b; | ||
| } | ||
| ); | ||
|
|
||
| foreach ( $array_defs as $base => $def ) { | ||
| unset( $def['_order'] ); | ||
| $schema[] = $def; | ||
| } | ||
| } | ||
|
|
||
| if ( ! empty( $scalar_fields ) ) { | ||
| usort( | ||
| $scalar_fields, | ||
| static function ( $a, $b ) { | ||
| $order_a = isset( $a['_order'] ) ? intval( $a['_order'] ) : 0; | ||
| $order_b = isset( $b['_order'] ) ? intval( $b['_order'] ) : 0; | ||
| return $order_a <=> $order_b; | ||
| } | ||
| ); | ||
|
|
||
| foreach ( $scalar_fields as $field ) { | ||
| unset( $field['_order'] ); | ||
| $field['type'] = 'textarea'; | ||
| $schema[] = $field; | ||
| } | ||
| } | ||
|
|
||
| return $schema; | ||
| } |
Check warning
Code scanning / PHPMD
Code Size Rules: CyclomaticComplexity Warning
| public static function build_schema_from_field_definitions( $fields ) { | ||
| if ( ! is_array( $fields ) ) { | ||
| return array(); | ||
| } | ||
|
|
||
| $array_defs = array(); | ||
| $array_order = array(); | ||
| $repeat_hints = array(); | ||
| $pending = array(); | ||
| $order_counter = 0; | ||
|
|
||
| foreach ( $fields as $index => $field ) { | ||
| if ( ! is_array( $field ) ) { | ||
| continue; | ||
| } | ||
|
|
||
| $placeholder = isset( $field['placeholder'] ) ? trim( (string) $field['placeholder'] ) : ''; | ||
| $parameters = isset( $field['parameters'] ) && is_array( $field['parameters'] ) ? $field['parameters'] : array(); | ||
| $label = isset( $field['label'] ) ? sanitize_text_field( $field['label'] ) : ''; | ||
| $data_type = isset( $field['data_type'] ) ? sanitize_key( $field['data_type'] ) : 'text'; | ||
|
|
||
| $array_match = self::detect_array_placeholder_with_index( $placeholder ); | ||
| if ( $array_match ) { | ||
| $base = $array_match['base']; | ||
| $key = $array_match['key']; | ||
|
|
||
| if ( '' === $base || '' === $key ) { | ||
| continue; | ||
| } | ||
|
|
||
| $repeat_hints[ $base ] = true; | ||
|
|
||
| if ( ! isset( $array_defs[ $base ] ) ) { | ||
| $array_defs[ $base ] = array( | ||
| 'slug' => $base, | ||
| 'label' => self::humanize_key( $base ), | ||
| 'type' => 'array', | ||
| 'placeholder' => $base, | ||
| 'data_type' => 'array', | ||
| 'item_schema' => array(), | ||
| '_order' => $order_counter++, | ||
| ); | ||
| $array_order[] = $base; | ||
| } | ||
|
|
||
| if ( '' === $label ) { | ||
| $label = self::humanize_key( $array_match['raw_key'] ); | ||
| } | ||
|
|
||
| if ( ! isset( $array_defs[ $base ]['item_schema'][ $key ] ) ) { | ||
| $item_data_type = self::detect_data_type( $placeholder, $parameters ); | ||
| if ( '' === $item_data_type ) { | ||
| $item_data_type = 'text'; | ||
| } | ||
|
|
||
| $array_defs[ $base ]['item_schema'][ $key ] = array( | ||
| 'label' => $label, | ||
| 'type' => self::infer_array_item_type( $key, $item_data_type ), | ||
| 'data_type' => $item_data_type, | ||
| ); | ||
| } | ||
|
|
||
| continue; | ||
| } | ||
|
|
||
| if ( isset( $parameters['repeat'] ) ) { | ||
| $repeat_base = sanitize_key( $parameters['repeat'] ); | ||
| if ( '' !== $repeat_base ) { | ||
| $repeat_hints[ $repeat_base ] = true; | ||
| } | ||
| } | ||
|
|
||
| $pending[] = array( | ||
| 'field' => $field, | ||
| 'placeholder' => $placeholder, | ||
| 'parameters' => $parameters, | ||
| 'label' => $label, | ||
| 'data_type' => $data_type, | ||
| 'index' => $index, | ||
| ); | ||
| } | ||
|
|
||
| $schema = array(); | ||
| $scalar_fields = array(); | ||
|
|
||
| foreach ( $pending as $entry ) { | ||
| $placeholder = $entry['placeholder']; | ||
| $parameters = $entry['parameters']; | ||
| $label = $entry['label']; | ||
| $data_type = $entry['data_type']; | ||
|
|
||
| $dot_match = self::detect_array_placeholder_without_index( $placeholder ); | ||
| if ( $dot_match && ( isset( $repeat_hints[ $dot_match['base'] ] ) || isset( $array_defs[ $dot_match['base'] ] ) ) ) { | ||
| $base = $dot_match['base']; | ||
| $key = $dot_match['key']; | ||
|
|
||
| if ( '' === $base || '' === $key ) { | ||
| continue; | ||
| } | ||
|
|
||
| if ( ! isset( $array_defs[ $base ] ) ) { | ||
| $array_defs[ $base ] = array( | ||
| 'slug' => $base, | ||
| 'label' => self::humanize_key( $base ), | ||
| 'type' => 'array', | ||
| 'placeholder' => $base, | ||
| 'data_type' => 'array', | ||
| 'item_schema' => array(), | ||
| '_order' => $order_counter++, | ||
| ); | ||
| $array_order[] = $base; | ||
| } | ||
|
|
||
| if ( ! isset( $array_defs[ $base ]['item_schema'][ $key ] ) ) { | ||
| $item_data_type = self::detect_data_type( $placeholder, $parameters ); | ||
| if ( '' === $label ) { | ||
| $label = self::humanize_key( $dot_match['raw_key'] ); | ||
| } | ||
| if ( '' === $item_data_type ) { | ||
| $item_data_type = 'text'; | ||
| } | ||
| $array_defs[ $base ]['item_schema'][ $key ] = array( | ||
| 'label' => ( '' !== $label ) ? $label : self::humanize_key( $dot_match['raw_key'] ), | ||
| 'type' => self::infer_array_item_type( $key, $item_data_type ), | ||
| 'data_type' => $item_data_type, | ||
| ); | ||
| } | ||
|
|
||
| continue; | ||
| } | ||
|
|
||
| $slug = isset( $entry['field']['slug'] ) ? sanitize_key( $entry['field']['slug'] ) : ''; | ||
| if ( '' === $slug ) { | ||
| $slug = sanitize_key( $placeholder ); | ||
| } | ||
|
|
||
| if ( '' === $slug ) { | ||
| continue; | ||
| } | ||
|
|
||
| $normalized_placeholder = ''; | ||
| if ( '' !== $placeholder ) { | ||
| $normalized_placeholder = preg_replace( '/[^A-Za-z0-9._:-]/', '', $placeholder ); | ||
| } | ||
| if ( '' === $label ) { | ||
| $source = '' !== $normalized_placeholder ? $normalized_placeholder : $slug; | ||
| $label = self::humanize_key( $source ); | ||
| } | ||
|
|
||
| if ( '' === $normalized_placeholder ) { | ||
| $normalized_placeholder = $slug; | ||
| } | ||
|
|
||
| if ( ! in_array( $data_type, array( 'text', 'number', 'boolean', 'date' ), true ) ) { | ||
| $data_type = 'text'; | ||
| } | ||
|
|
||
| if ( in_array( $slug, array( 'onshow', 'ondata', 'block', 'var' ), true ) ) { | ||
| continue; | ||
| } | ||
|
|
||
| $scalar_fields[] = array( | ||
| 'slug' => $slug, | ||
| 'label' => $label, | ||
| 'placeholder' => $normalized_placeholder, | ||
| 'data_type' => $data_type, | ||
| '_order' => $entry['index'], | ||
| ); | ||
| } | ||
|
|
||
| if ( ! empty( $array_defs ) ) { | ||
| uasort( | ||
| $array_defs, | ||
| static function ( $a, $b ) { | ||
| $order_a = isset( $a['_order'] ) ? intval( $a['_order'] ) : 0; | ||
| $order_b = isset( $b['_order'] ) ? intval( $b['_order'] ) : 0; | ||
| return $order_a <=> $order_b; | ||
| } | ||
| ); | ||
|
|
||
| foreach ( $array_defs as $base => $def ) { | ||
| unset( $def['_order'] ); | ||
| $schema[] = $def; | ||
| } | ||
| } | ||
|
|
||
| if ( ! empty( $scalar_fields ) ) { | ||
| usort( | ||
| $scalar_fields, | ||
| static function ( $a, $b ) { | ||
| $order_a = isset( $a['_order'] ) ? intval( $a['_order'] ) : 0; | ||
| $order_b = isset( $b['_order'] ) ? intval( $b['_order'] ) : 0; | ||
| return $order_a <=> $order_b; | ||
| } | ||
| ); | ||
|
|
||
| foreach ( $scalar_fields as $field ) { | ||
| unset( $field['_order'] ); | ||
| $field['type'] = 'textarea'; | ||
| $schema[] = $field; | ||
| } | ||
| } | ||
|
|
||
| return $schema; | ||
| } |
Check warning
Code scanning / PHPMD
Code Size Rules: NPathComplexity Warning
| 'type' => 'textarea', | ||
| 'data_type' => 'text', | ||
| ); | ||
| } | ||
|
|
||
| if ( ! current_user_can( 'edit_post', $post_id ) ) { | ||
| return; | ||
| return $schema; | ||
| } | ||
|
|
||
| /** | ||
| * Render an array field with repeatable items. | ||
| * | ||
| * @param string $slug Field slug. | ||
| * @param string $label Field label. | ||
| * @param array $item_schema Item schema definition. | ||
| * @param array $items Current values. | ||
| * @return void | ||
| */ | ||
| private function render_array_field( $slug, $label, $item_schema, $items ) { | ||
| $slug = sanitize_key( $slug ); | ||
| $label = sanitize_text_field( $label ); | ||
| $field_id = 'resolate-array-' . $slug; | ||
| $items = is_array( $items ) ? $items : array(); | ||
| $item_schema = is_array( $item_schema ) ? $item_schema : array(); | ||
|
|
||
| echo '<div class="resolate-array-field" data-array-field="' . esc_attr( $slug ) . '" style="margin-bottom:24px;">'; | ||
| echo '<div class="resolate-array-heading" style="display:flex;align-items:center;justify-content:space-between;margin-bottom:12px;gap:12px;">'; | ||
| echo '<span class="resolate-array-title" style="font-weight:600;font-size:15px;">' . esc_html( $label ) . '</span>'; | ||
| echo '<button type="button" class="button button-secondary resolate-array-add" data-array-target="' . esc_attr( $slug ) . '">' . esc_html__( 'Añadir elemento', 'resolate' ) . '</button>'; | ||
| echo '</div>'; | ||
|
|
||
| echo '<div class="resolate-array-items" id="' . esc_attr( $field_id ) . '" data-field="' . esc_attr( $slug ) . '">'; | ||
| foreach ( $items as $index => $values ) { | ||
| $values = is_array( $values ) ? $values : array(); | ||
| $this->render_array_field_item( $slug, (string) $index, $item_schema, $values ); | ||
| } | ||
| echo '</div>'; | ||
|
|
||
| // Handle type selection (lock after set). | ||
| if ( isset( $_POST['resolate_type_nonce'] ) && wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['resolate_type_nonce'] ) ), 'resolate_type_nonce' ) ) { | ||
| $posted = isset( $_POST['resolate_doc_type'] ) ? intval( $_POST['resolate_doc_type'] ) : 0; | ||
| $assigned = wp_get_post_terms( $post_id, 'resolate_doc_type', array( 'fields' => 'ids' ) ); | ||
| $current = ( ! is_wp_error( $assigned ) && ! empty( $assigned ) ) ? intval( $assigned[0] ) : 0; | ||
| if ( $current <= 0 && $posted > 0 ) { | ||
| wp_set_post_terms( $post_id, array( $posted ), 'resolate_doc_type', false ); | ||
| echo '<template class="resolate-array-template" data-field="' . esc_attr( $slug ) . '">'; | ||
| $this->render_array_field_item( $slug, '__INDEX__', $item_schema, array(), true ); | ||
| echo '</template>'; | ||
| echo '</div>'; | ||
| } | ||
|
|
||
| /** | ||
| * Render a single repeatable array item row. | ||
| * | ||
| * @param string $slug Field slug. | ||
| * @param string $index Item index. | ||
| * @param array $item_schema Item schema definition. | ||
| * @param array $values Current values. | ||
| * @param bool $is_template Whether the row is a template placeholder. | ||
| * @return void |
Check warning
Code scanning / PHPMD
Code Size Rules: NPathComplexity Warning
| private function render_array_field_item( $slug, $index, $item_schema, $values, $is_template = false ) { | ||
| $slug = sanitize_key( $slug ); | ||
| $index_attr = (string) $index; | ||
| $item_schema = is_array( $item_schema ) ? $item_schema : array(); | ||
| $values = is_array( $values ) ? $values : array(); | ||
|
|
||
| echo '<div class="resolate-array-item" data-index="' . esc_attr( $index_attr ) . '" draggable="true" style="border:1px solid #e5e5e5;padding:16px;margin-bottom:12px;background:#fff;">'; | ||
| echo '<div class="resolate-array-item-toolbar" style="display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:12px;">'; | ||
| echo '<span class="resolate-array-handle" role="button" tabindex="0" aria-label="' . esc_attr__( 'Mover elemento', 'resolate' ) . '" style="cursor:move;user-select:none;">≡</span>'; | ||
| echo '<button type="button" class="button-link-delete resolate-array-remove">' . esc_html__( 'Eliminar', 'resolate' ) . '</button>'; | ||
| echo '</div>'; | ||
|
|
||
| foreach ( $item_schema as $key => $definition ) { | ||
| $item_key = sanitize_key( $key ); | ||
| if ( '' === $item_key ) { | ||
| continue; | ||
| } | ||
|
|
||
| $field_name = 'tpl_fields[' . $slug . '][' . $index_attr . '][' . $item_key . ']'; | ||
| $field_id = 'resolate-' . $slug . '-' . $item_key . '-' . $index_attr; | ||
| $label = isset( $definition['label'] ) ? sanitize_text_field( $definition['label'] ) : $this->humanize_unknown_field_label( $item_key ); | ||
| $type = isset( $definition['type'] ) ? sanitize_key( $definition['type'] ) : 'textarea'; | ||
| $value = isset( $values[ $item_key ] ) ? (string) $values[ $item_key ] : ''; | ||
|
|
||
| if ( ! in_array( $type, array( 'single', 'textarea', 'rich' ), true ) ) { | ||
| $type = 'textarea'; | ||
| } | ||
|
|
||
| echo '<div class="resolate-array-field-control" style="margin-bottom:12px;">'; | ||
| echo '<label for="' . esc_attr( $field_id ) . '" style="font-weight:600;display:block;margin-bottom:4px;">' . esc_html( $label ) . '</label>'; | ||
|
|
||
| if ( 'single' === $type ) { | ||
| echo '<input type="text" class="widefat" id="' . esc_attr( $field_id ) . '" name="' . esc_attr( $field_name ) . '" value="' . esc_attr( $value ) . '" />'; | ||
| } else { | ||
| $rows = ( 'rich' === $type ) ? 8 : 4; | ||
| echo '<textarea class="widefat" rows="' . esc_attr( (string) $rows ) . '" id="' . esc_attr( $field_id ) . '" name="' . esc_attr( $field_name ) . '">' . esc_textarea( $value ) . '</textarea>'; | ||
| } | ||
|
|
||
| echo '</div>'; | ||
| } | ||
|
|
||
| echo '</div>'; | ||
| } |
Check warning
Code scanning / PHPMD
Code Size Rules: CyclomaticComplexity Warning
| private function render_array_field_item( $slug, $index, $item_schema, $values, $is_template = false ) { | ||
| $slug = sanitize_key( $slug ); | ||
| $index_attr = (string) $index; | ||
| $item_schema = is_array( $item_schema ) ? $item_schema : array(); | ||
| $values = is_array( $values ) ? $values : array(); | ||
|
|
||
| echo '<div class="resolate-array-item" data-index="' . esc_attr( $index_attr ) . '" draggable="true" style="border:1px solid #e5e5e5;padding:16px;margin-bottom:12px;background:#fff;">'; | ||
| echo '<div class="resolate-array-item-toolbar" style="display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:12px;">'; | ||
| echo '<span class="resolate-array-handle" role="button" tabindex="0" aria-label="' . esc_attr__( 'Mover elemento', 'resolate' ) . '" style="cursor:move;user-select:none;">≡</span>'; | ||
| echo '<button type="button" class="button-link-delete resolate-array-remove">' . esc_html__( 'Eliminar', 'resolate' ) . '</button>'; | ||
| echo '</div>'; | ||
|
|
||
| foreach ( $item_schema as $key => $definition ) { | ||
| $item_key = sanitize_key( $key ); | ||
| if ( '' === $item_key ) { | ||
| continue; | ||
| } | ||
|
|
||
| $field_name = 'tpl_fields[' . $slug . '][' . $index_attr . '][' . $item_key . ']'; | ||
| $field_id = 'resolate-' . $slug . '-' . $item_key . '-' . $index_attr; | ||
| $label = isset( $definition['label'] ) ? sanitize_text_field( $definition['label'] ) : $this->humanize_unknown_field_label( $item_key ); | ||
| $type = isset( $definition['type'] ) ? sanitize_key( $definition['type'] ) : 'textarea'; | ||
| $value = isset( $values[ $item_key ] ) ? (string) $values[ $item_key ] : ''; | ||
|
|
||
| if ( ! in_array( $type, array( 'single', 'textarea', 'rich' ), true ) ) { | ||
| $type = 'textarea'; | ||
| } | ||
|
|
||
| echo '<div class="resolate-array-field-control" style="margin-bottom:12px;">'; | ||
| echo '<label for="' . esc_attr( $field_id ) . '" style="font-weight:600;display:block;margin-bottom:4px;">' . esc_html( $label ) . '</label>'; | ||
|
|
||
| if ( 'single' === $type ) { | ||
| echo '<input type="text" class="widefat" id="' . esc_attr( $field_id ) . '" name="' . esc_attr( $field_name ) . '" value="' . esc_attr( $value ) . '" />'; | ||
| } else { | ||
| $rows = ( 'rich' === $type ) ? 8 : 4; | ||
| echo '<textarea class="widefat" rows="' . esc_attr( (string) $rows ) . '" id="' . esc_attr( $field_id ) . '" name="' . esc_attr( $field_name ) . '">' . esc_textarea( $value ) . '</textarea>'; | ||
| } | ||
|
|
||
| echo '</div>'; | ||
| } | ||
|
|
||
| echo '</div>'; | ||
| } |
Check warning
Code scanning / PHPMD
Code Size Rules: NPathComplexity Warning
| @@ -874,9 +1004,32 @@ private function get_structured_field_values( $post_id ) { | |||
| if ( '' === $value ) { | |||
| continue; | |||
| } | |||
| $type = isset( $row['type'] ) ? sanitize_key( $row['type'] ) : 'textarea'; | |||
| $type = isset( $row['type'] ) ? sanitize_key( $row['type'] ) : 'textarea'; | |||
| if ( 'array' === $type ) { | |||
| $encoded = ''; | |||
| $stored = get_post_meta( $post_id, 'resolate_field_' . $slug, true ); | |||
| if ( is_string( $stored ) && '' !== $stored ) { | |||
| $encoded = (string) $stored; | |||
| } else { | |||
| $legacy = get_post_meta( $post_id, 'resolate_' . $slug, true ); | |||
| if ( empty( $legacy ) && 'annexes' === $slug ) { | |||
| $legacy = get_post_meta( $post_id, 'resolate_annexes', true ); | |||
| } | |||
| if ( is_array( $legacy ) && ! empty( $legacy ) ) { | |||
| $encoded = wp_json_encode( $legacy ); | |||
| } | |||
| } | |||
|
|
|||
| if ( '' !== $encoded ) { | |||
| $fallback[ $slug ] = array( | |||
| 'value' => $encoded, | |||
| 'type' => 'array', | |||
| ); | |||
| } | |||
| continue; | |||
| } | |||
| if ( ! in_array( $type, array( 'single', 'textarea', 'rich' ), true ) ) { | |||
| $type = 'textarea'; | |||
Check warning
Code scanning / PHPMD
Code Size Rules: CyclomaticComplexity Warning
| @@ -874,9 +1004,32 @@ private function get_structured_field_values( $post_id ) { | |||
| if ( '' === $value ) { | |||
| continue; | |||
| } | |||
| $type = isset( $row['type'] ) ? sanitize_key( $row['type'] ) : 'textarea'; | |||
| $type = isset( $row['type'] ) ? sanitize_key( $row['type'] ) : 'textarea'; | |||
| if ( 'array' === $type ) { | |||
| $encoded = ''; | |||
| $stored = get_post_meta( $post_id, 'resolate_field_' . $slug, true ); | |||
| if ( is_string( $stored ) && '' !== $stored ) { | |||
| $encoded = (string) $stored; | |||
| } else { | |||
| $legacy = get_post_meta( $post_id, 'resolate_' . $slug, true ); | |||
| if ( empty( $legacy ) && 'annexes' === $slug ) { | |||
| $legacy = get_post_meta( $post_id, 'resolate_annexes', true ); | |||
| } | |||
| if ( is_array( $legacy ) && ! empty( $legacy ) ) { | |||
| $encoded = wp_json_encode( $legacy ); | |||
| } | |||
| } | |||
|
|
|||
| if ( '' !== $encoded ) { | |||
| $fallback[ $slug ] = array( | |||
| 'value' => $encoded, | |||
| 'type' => 'array', | |||
| ); | |||
| } | |||
| continue; | |||
| } | |||
| if ( ! in_array( $type, array( 'single', 'textarea', 'rich' ), true ) ) { | |||
| $type = 'textarea'; | |||
Check warning
Code scanning / PHPMD
Code Size Rules: NPathComplexity Warning
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting
| } | ||
|
|
||
| /** | ||
| * It should expose array schema definitions when reading the term configuration. | ||
| */ | ||
| public function test_get_term_schema_returns_array_definition() { | ||
| $term = wp_insert_term( 'Tipo Esquema', 'resolate_doc_type' ); | ||
| $term_id = intval( $term['term_id'] ); | ||
| $schema = $this->get_annex_schema(); | ||
| update_term_meta( $term_id, 'schema', $schema ); | ||
| update_term_meta( $term_id, 'resolate_type_fields', $schema ); | ||
|
|
||
| $result = Resolate_Documents::get_term_schema( $term_id ); | ||
|
|
||
| $this->assertNotEmpty( $result ); | ||
| $this->assertSame( 'annexes', $result[0]['slug'] ); | ||
| $this->assertSame( 'array', $result[0]['type'] ); | ||
| $this->assertSame( 'array', $result[0]['data_type'] ); | ||
| $this->assertArrayHasKey( 'item_schema', $result[0] ); | ||
| $this->assertArrayHasKey( 'number', $result[0]['item_schema'] ); | ||
| $this->assertSame( 'single', $result[0]['item_schema']['number']['type'] ); | ||
| $this->assertSame( 'Número', $result[0]['item_schema']['number']['label'] ); | ||
| } |
There was a problem hiding this comment.
Move schema test inside test class
The new test_get_term_schema_returns_array_definition() is declared after the closing brace of ResolateDocumentsArrayFieldsTest. Because it uses public function outside any class definition, the file will cause a PHP parse error as soon as the test suite is loaded, preventing any tests from running. Place this method back inside the class (before its final }) so the test file remains syntactically valid.
Useful? React with 👍 / 👎.
Summary
Testing
./vendor/bin/phpcs --standard=.phpcs.xml.dist includes/custom-post-types/class-resolate-documents.php tests/unit/includes/custom-post-types/ResolateDocumentsArrayFieldsTest.php(fails: ./vendor/bin/phpcs not found in container)https://chatgpt.com/codex/tasks/task_e_68ee30cf252083229149dd9f656091e1