array_keys( $repository->all_labels() ); $filtered_order = array_values( array_intersect( $global_order, $requested_ids ) ); $items = $repository->get_by_ids( $filtered_order ); $result = []; foreach ( $requested_ids as $id ) { $result[ $id ] = $items[ $id ] ?? null; } return Response_Builder::make( (object) $result ) ->set_meta( [ 'order' => $filtered_order ] ) ->build(); } private function get_usage() { $classes_usage = ( new Applied_Global_Classes_Usage() )->get_detailed_usage(); return Response_Builder::make( (object) $classes_usage )->build(); } private function put( \WP_REST_Request $request ) { $context = $request->get_param( 'context' ); $is_preview = Global_Classes_Repository::CONTEXT_PREVIEW === $context; $changes = $request->get_param( 'changes' ) ?? []; $added_ids = $changes['added'] ?? []; $deleted_ids = $changes['deleted'] ?? []; $order = $request->get_param( 'order' ) ?? []; $repository = $this->get_repository()->set_preview( $is_preview ); $all_label_by_id = $repository->all_labels(); $existing_label_list = $this->global_classes_existing_label_list( $all_label_by_id, $deleted_ids ); $total_count = count( $all_label_by_id ) - count( $deleted_ids ) + count( $added_ids ); $parser = Global_Classes_Parser::make(); $items_result = $parser->parse_items( $request->get_param( 'items' ) ?? [] ); if ( ! $items_result->is_valid() ) { return Error_Builder::make( 'invalid_items' ) ->set_status( 400 ) ->set_message( 'Invalid items: ' . $items_result->errors()->to_string() ) ->build(); } $touched_items = $items_result->unwrap(); if ( $total_count > self::MAX_ITEMS ) { return Error_Builder::make( 'global_classes_limit_exceeded' ) ->set_status( 400 ) ->set_meta( [ 'current_count' => $total_count, 'max_allowed' => self::MAX_ITEMS, ] ) ->set_message( sprintf( /* translators: %d: Maximum allowed items. */ __( 'Global classes limit exceeded. Maximum allowed: %d', 'elementor' ), self::MAX_ITEMS ) ) ->build(); } $duplicated_labels = Global_Classes_Parser::check_for_duplicate_labels( $all_label_by_id, $deleted_ids, $touched_items, $added_ids ); $duplicate_validation_result = null; if ( ! empty( $duplicated_labels ) ) { $modified_labels = $this->handle_duplicates( $duplicated_labels, $existing_label_list ); $duplicate_validation_result = $modified_labels; foreach ( $modified_labels as $item_id => $labels ) { $touched_items[ $item_id ]['label'] = $labels['modified']; } } $final_item_ids = array_keys( $this->merge_touched_with_existing_labels( $all_label_by_id, $touched_items, $deleted_ids ) ); $final_item_ids_set = array_flip( $final_item_ids ); $order_set = array_flip( $order ); $order = array_values( array_filter( $order, fn( $id ) => isset( $final_item_ids_set[ $id ] ) ) ); $missing_from_order = array_values( array_filter( $final_item_ids, fn( $id ) => ! isset( $order_set[ $id ] ) ) ); $order = array_merge( $order, $missing_from_order ); $order_result = $parser->parse_order( $order, $final_item_ids ); if ( ! $order_result->is_valid() ) { return Error_Builder::make( 'invalid_order' ) ->set_status( 400 ) ->set_message( 'Invalid order: ' . $order_result->errors()->to_string() ) ->build(); } $repository->apply_changes( $touched_items, [ 'added' => $added_ids, 'deleted' => $changes['deleted'] ?? [], 'modified' => $changes['modified'] ?? [], 'order' => isset( $changes['order'] ) && $changes['order'], // boolean indicating if the order has changed ], $order_result->unwrap() ); if ( $duplicate_validation_result ) { return Response_Builder::make( [ 'code' => 'DUPLICATED_LABEL', 'modifiedLabels' => $duplicate_validation_result, ] )->build(); } return Response_Builder::make()->no_content()->build(); } private function global_classes_existing_label_list( array $label_by_id, array $deleted_ids ): array { $labels = []; foreach ( $label_by_id as $id => $label ) { if ( in_array( $id, $deleted_ids, true ) ) { continue; } $labels[] = $label; } return $labels; } private function merge_touched_with_existing_labels( array $label_by_id, array $touched_items, array $deleted_ids ): array { $final = []; foreach ( $label_by_id as $id => $label ) { if ( in_array( $id, $deleted_ids, true ) ) { continue; } if ( isset( $touched_items[ $id ] ) ) { $final[ $id ] = $touched_items[ $id ]; } else { $final[ $id ] = [ 'id' => $id, 'label' => $label, 'type' => 'class', 'variants' => [], ]; } } foreach ( $touched_items as $id => $item ) { if ( ! isset( $final[ $id ] ) ) { $final[ $id ] = $item; } } return $final; } private function route_wrapper( callable $cb ) { try { $response = $cb(); } catch ( \Exception $e ) { return Error_Builder::make( 'unexpected_error' ) ->set_message( __( 'Something went wrong', 'elementor' ) ) ->build(); } return $response; } private function handle_duplicates( array $duplicate_labels, array $existing_labels ) { $modified_labels = []; foreach ( $duplicate_labels as $duplicate_label ) { $item_id = $duplicate_label['item_id']; $original_label = $duplicate_label['label']; $modified_label = Global_Classes_Labels::generate_unique_label( $original_label, $existing_labels ); $modified_labels[ $item_id ] = [ 'original' => $original_label, 'modified' => $modified_label, ]; } return $modified_labels; } }