�PNG  IHDR22?�� pHYs  �� OiCCPPhotoshop ICC profilexڝSgTS�=���BK���KoR RB���&*! J�!��Q�EEȠ�����Q, � ��!���������{�kּ������>���� �H3Q5� �B�������.@� $p�d!s�#�~<<+"��x� �M��0���B�\���t�8K�@z�B�@F���&S�`�cb�P-`'������{[�!�� e�Dh;��V�EX0fK�9�-0IWfH���� � 0Q��){`�##x��F�W<�+��*x��<�$9E�[-qWW.(�I+6aa�@.�y�2�4���������x����6��_-��"bb���ϫp@�t~��,/��;�m��%�h^ �u��f�@����W�p�~<�5�j>{�-�]c�K'Xt����o��(�h���w��?�G�%�fI�q^D$.Tʳ?�D��*�A��,���� �`6�B$��BB d�r`)��B(�Ͱ*`/�@4�Qh��p.�U�=p�a��(�� A�a!ڈb�X#����!�H�$ ɈQ"K�5H1R�T UH�=r9�\F��;�2����G1���Q=� �C��7�F� �dt1�����r�=�6��Ыhڏ>C�0��3�l0.��B�8, �c˱"� ���V����cϱw�E� 6wB aAHXLXN�H� $4� 7 �Q�'"��K�&���b21�XH,#��/{�C�7$�C2'��I��T��F�nR#�,��4H#���dk�9�, +ȅ����3��!�[ �b@q��S�(R�jJ��4�e�2AU��Rݨ�T5�ZB���R�Q��4u�9̓IK�����hh�i��t�ݕN��W���G���w ��Ljg(�gw��L�Ӌ�T071���oUX*�*|�� �J�&�*/T����ު U�U�T��^S}�FU3S� Ԗ�U��P�SSg�;���g�oT?�~Y��Y�L�OC�Q��_�� c�x,!k ��u�5�&���|v*�����=���9C3J3W�R�f?�q��tN �(���~���)�)�4L�1e\k����X�H�Q�G�6������E�Y��A�J'\'Gg����S�Sݧ �M=:��.�k���Dw�n��^��Lo��y���}/�T�m���G X� $� �<�5qo</���QC]�@C�a�a�ᄑ��<��F�F�i�\�$�m�mƣ&&!&KM�M�RM��)�;L;L���͢�֙5�=1�2��כ߷`ZxZ,����eI��Z�Yn�Z9Y�XUZ]�F���%ֻ�����N�N���gð�ɶ�����ۮ�m�}agbg�Ů��}�}��= ���Z~s�r:V:ޚΜ�?}����/gX���3��)�i�S��Ggg�s�󈋉K��.�>.���Ƚ�Jt�q]�z���������ۯ�6�i�ܟ�4�)�Y3s���C�Q��? ��0k߬~OCO�g��#/c/�W�װ��w��a�>�>r��>�<7�2�Y_�7��ȷ�O�o�_��C#�d�z����%g��A�[��z|!��?:�e����A���AA�������!h�쐭!��Α�i�P~���a�a��~ '���W�?�p�X�1�5w��Cs�D�D�Dޛg1O9�-J5*>�.j<�7�4�?�.fY��X�XIlK9.*�6nl������� �{�/�]py�����.,:�@L�N8��A*��%�w%� y��g"/�6ш�C\*N�H*Mz�쑼5y$�3�,幄'���L Lݛ:��v m2=:�1����qB�!M��g�g�fvˬe����n��/��k���Y- �B��TZ(�*�geWf�͉�9���+��̳�ې7�����ᒶ��KW-X潬j9�������(�x��oʿ�ܔ���Ĺd�f�f���-�[����n �ڴ �V����E�/��(ۻ��C���<��e����;?T�T�T�T6��ݵa��n��{��4���[���>ɾ�UUM�f�e�I���?�������m]�Nmq����#�׹���=TR��+�G�����w- 6 U����#pDy��� �� :�v�{���vg/jB��F�S��[b[�O�>����z�G��499�?r����C�d�&����ˮ/~�����јѡ�򗓿m|������������x31^�V���w�w��O�| (�h���SЧ��������c3-� cHRMz%������u0�`:�o�_�F5IDATx��ytUս�?�;���fN$$��@B�!�k��� -*�gE���j�O�kQ�ʠ`U�B_ �AÐ0%�Ȕ�@r3ޛ;߳��$�@��v����^�s����������$��ߢ !�W��5�̱'-٘������4�>�Rƫ$�G��0U�H}�_ _� #��k�MJ/=�9Du *ڲ�k�PQ;0��j|�*�$�^컂脛y���>�z×� B��� ��(�:$�ʔ_�6��C!B����MVx��a��ζyY����j��ƋY2�9�F�{r��r�딟F��Q��hY6��� x��[l��RC��������nt@��;P��3��� F�w��7��:�P���ɋ'���mj���Bƪ�$�Q�L��y��߷g��\���z����v5�p�v$�v櫻�`6~K �Y�X����,�:��"�o�e0II"(��,������l_�C���/4�+f�E�!q��:����u����c��樼.UE�@� �]K�W���$-�h]�JG���Dz)�`��.�A/ ���mᏣc�3jK� �!�>���$�Q�!�_�? \�C2(E �M$Z I{�U���s�ǣ���=��@Բe ޻��W�����@ �w����}�9v,)��.}*|"�s��di���,���G�]W�AC�������|<����[��ߕ������K�*�G nߴ!��‹��J�!��6(L�a)��Am��������;adf��:�xm��nh�;֦I��(���o�C~ډa?�=��s����h��E������xp����)���_���t��W�����´;8���`dz=(��@@�+,�;�`�fhl��XȌ�3,uI�~z(J����� �ZX�A����H'�À�㜦�82|��a�n�z2��I���� ���EY�h�ε<�0�q�{��C��v�˅��?"@��χ�x�WS+��N�~�KQ�J�8ل�� _.W��� !TU{�����,��"�� �;�������HI S� ��))�o�1�H�D� ���� (� ;����"Ҕ��,���� �F=���O��&�Ph>�`���y��߲ &S��P�<��'��1s�1cĂ�N����2)����@�u��tv��<�+�-&�����h��[!FSG���' � z z0��<����8d&�����E0�,��<��"!P�b4��� �{S�����X����.�囤���T��hEF��X�q0"-<����h���]�Py*����F⩍��@v�˂�Y08�� .q��a��s����(!<̫1�3�L�H��7sYW�`��qtu�\7��?�@�Y�� %Ka�x�GZ�*W�P��y�����N0r9��Z�c�%���q,��k�����!!,��>���IB��� �x1�Xu�Y���v-���ֺ��N������JP��a�2`�e5��w��u;�6�i���̃��(K���ťa��(l51��JL��O'%���]���ٖ뿉!��{*�p+f ���i8��D�ȑ���ܩ�M��V��ӟz��x�7Z�1i}�^͜���6e����x��)u�d�ˏ�V�eG�\�X1�����=n�U'�~Y����L�\,�(d!x ,�:�TW8� M`r���@��r+v�Q0��>^;˴�VbF��('����� �*O��i��j�Ӆ����^�S3&� ���{������.ی�� N;��NU%d0�Ce\r"�C�b��&t7N�7��� �ȪD�[%l�۪�$��'m?�A9tI�9>��NVL ��� 6v~M�����ъ�B���0����9$&=ώm��W�0���L���&��E���f�f��'+�9��hv��7,���M��6�D�&�^� �����8 D֮�����o��k^z��O��hP�8q�5d*��YVq3�{��������#LX4�?��jP=3�~�؅c�ݰ�̱�ұ��8̲��2�G�����j��έ�9��w7���U�X�w�85C��?!I&���o&����L���I#�^�eV�,�^t5�k� �xs˛\��U�8��."���{���ǵ�l4��ݼ�;��r"���G�/~���gJV�N糦��ZZ^k+NN�D��I$�=OE�z����|*�*{��%k���G��>�`9��b;�s���8,�qs��c��.b��q�%�>r��n�R�Ƨ�%�,�A��le#�8���}; 21�0�b�&�QX�f���q�00����$>�h�N�'�a3��A���/�r ��L5�8�GԲ'�����w;��c#�q1���h$*��V^�k��QĽ�b:i������� ����H F#�_���,��]g����Ը����`RUU���� ��"�qD���>-��ϤI�~��3�̴�����NZ[[555�eeek7�m����YF#[��Đ��R��i��L�H�r ��@��J-�Pŝ��_|�-��6y��ks�V�U~��6���w.����(��0A� 4��f��� /���C�xŊ�ٳ����z=6�����rg���mKo��o�/����a�g���?'9%9��P��斓��~���L{�� �H��{AW��B��$����v�1b�&Mb�ر��ر#�0`�`xMBp! )Z�Nmo�mZ�g�}�eذaWTVV ���'99��C5��&�&+5�bT>|8��PUUETT����&���t��~TE%��'������b�tEGG;RSS�S�N�v��ѯ��t����u1@�ޚ"f��͈���r'L�Ph��m��ƌ�74o��`�x�q�P���NRSS�X,�����l&&&��MFFA!-��`��ҷ�n��Yuu�����x<I�LB%����KԒ50�!�3�bz�����ʸKL���;---�l6[��`��������Ȏ;�����egg4��!D�T��jWujyZ��������K����%����p�^���X,6��sV=�ܖ����6�3�X ���4ҿ��A2�&����_k�;d�� �VfOIEND�B`� 0x0x
PHP 8.1.34
Preview: components-rest-api.php Size: 21.37 KB
/home2/astrociv/public_html/wp-content/plugins/elementor/modules/components/components-rest-api.php
<?php

namespace Elementor\Modules\Components;

use Elementor\Core\Base\Document;
use Elementor\Core\Utils\Api\Error_Builder;
use Elementor\Core\Utils\Api\Response_Builder;
use Elementor\Core\Utils\Collection;
use Elementor\Modules\Components\Documents\Component;
use Elementor\Modules\Components\OverridableProps\Component_Overridable_Props_Parser;

if ( ! defined( 'ABSPATH' ) ) {
	exit; // Exit if accessed directly.
}

class Components_REST_API {
	const API_NAMESPACE = 'elementor/v1';
	const API_BASE = 'components';
	const LOCK_DOCUMENT_TYPE_NAME = 'components';
	const STYLES_ROUTE = 'styles';
	const MAX_COMPONENTS = 100;

	private $repository = null;
	public function register_hooks() {
		add_action( 'rest_api_init', fn() => $this->register_routes() );
	}

	private function get_repository() {
		if ( ! $this->repository ) {
			$this->repository = new Components_Repository();
		}

		return $this->repository;
	}

	/**
	 * @return Component_Lock_Manager instance
	 */
	private function get_component_lock_manager() {
		return Component_Lock_Manager::get_instance();
	}

	private function register_routes() {
		register_rest_route( self::API_NAMESPACE, '/' . self::API_BASE, [
			[
				'methods' => 'GET',
				'callback' => fn() => $this->route_wrapper( fn() => $this->get_components() ),
				'permission_callback' => fn() => current_user_can( 'edit_posts' ),
			],
		] );

		register_rest_route( self::API_NAMESPACE, '/' . self::API_BASE . '/' . self::STYLES_ROUTE, [
			[
				'methods' => 'GET',
				'callback' => fn() => $this->route_wrapper( fn() => $this->get_styles() ),
				'permission_callback' => fn() => current_user_can( 'edit_posts' ),
			],
		] );

		register_rest_route( self::API_NAMESPACE, '/' . self::API_BASE, [
			[
				'methods' => 'POST',
				'callback' => fn( $request ) => $this->route_wrapper( fn() => $this->create_components( $request ) ),
				'permission_callback' => fn() => current_user_can( 'manage_options' ),
				'args' => [
					'status' => [
						'type' => 'string',
						'enum' => [ Document::STATUS_PUBLISH, Document::STATUS_DRAFT, Document::STATUS_AUTOSAVE ],
						'required' => true,
					],
					'items' => [
						'type' => 'array',
						'required' => true,
						'items' => [
							'type' => 'object',
							'properties' => [
								'uid' => [
									'type' => 'string',
									'required' => true,
								],
								'title' => [
									'type' => 'string',
									'required' => true,
									'minLength' => 2,
									'maxLength' => 200,
								],
								'elements' => [
									'type' => 'array',
									'required' => true,
									'items' => [
										'type' => 'object',
									],
								],
								'settings' => [
									'type' => 'object',
									'required' => false,
								],
							],
						],
					],
				],
			],
		] );

		register_rest_route( self::API_NAMESPACE, '/' . self::API_BASE . '/create-validate', [
			[
				'methods' => 'POST',
				'callback' => fn( $request ) => $this->route_wrapper( fn() => $this->create_validate_components( $request ) ),
				'permission_callback' => fn() => current_user_can( 'manage_options' ),
				'args' => [
					'items' => [
						'type' => 'array',
						'required' => true,
						'items' => [
							'type' => 'object',
							'properties' => [
								'uid' => [
									'type' => 'string',
									'required' => true,
								],
								'title' => [
									'type' => 'string',
									'required' => true,
									'minLength' => 2,
									'maxLength' => 200,
								],
								'elements' => [
									'type' => 'array',
									'required' => true,
									'items' => [
										'type' => 'object',
									],
								],
								'settings' => [
									'type' => 'object',
									'required' => false,
								],
							],
						],
					],
				],
			],
		] );

		register_rest_route( self::API_NAMESPACE, '/' . self::API_BASE . '/overridable-props', [
			[
				'methods' => 'GET',
				'callback' => fn( $request ) => $this->route_wrapper( fn() => $this->get_overridable_props( $request ) ),
				'permission_callback' => fn() => current_user_can( 'edit_posts' ),
				'args' => [
					'componentIds' => [
						'type' => 'array',
						'items' => [
							'type' => 'integer',
						],
						'required' => true,
						'description' => 'The component IDs to get overridable props for',
					],
				],
			],
		] );

		register_rest_route( self::API_NAMESPACE, '/' . self::API_BASE . '/status', [
			[
				'methods' => 'PUT',
				'callback' => fn( $request ) => $this->route_wrapper( fn() => $this->update_statuses( $request ) ),
				'permission_callback' => fn() => current_user_can( 'manage_options' ),
				'args' => [
					'status' => [
						'type' => 'string',
						'required' => true,
						'enum' => [ Document::STATUS_PUBLISH ],
					],
					'ids' => [
						'type' => 'array',
						'required' => true,
						'items' => [
							'type' => 'number',
							'required' => true,
						],
					],
				],
			],
		] );

		register_rest_route( self::API_NAMESPACE, '/' . self::API_BASE . '/lock', [
			[
				'methods' => 'POST',
				'callback' => fn( $request ) => $this->route_wrapper( fn() => $this->lock_component( $request ) ),
				'permission_callback' => fn() => current_user_can( 'manage_options' ),
				'args' => [
					'componentId' => [
						'type' => 'number',
						'required' => true,
						'description' => 'The component ID to unlock',
					],
				],
			],
		] );

		register_rest_route( self::API_NAMESPACE, '/' . self::API_BASE . '/unlock', [
			[
				'methods' => 'POST',
				'callback' => fn( $request ) => $this->route_wrapper( fn() => $this->unlock_component( $request ) ),
				'permission_callback' => fn() => current_user_can( 'manage_options' ),
				'args' => [
					'componentId' => [
						'type' => 'number',
						'required' => true,
						'description' => 'The component ID to unlock',
					],
				],
			],
		] );

		register_rest_route( self::API_NAMESPACE, '/' . self::API_BASE . '/lock-status', [
			[
				'methods' => 'GET',
				'callback' => fn( $request ) => $this->route_wrapper( fn() => $this->get_lock_status( $request ) ),
				'permission_callback' => fn() => current_user_can( 'manage_options' ),
				'args' => [
					'componentId' => [
						'type' => 'string',
						'required' => true,
						'description' => 'The component ID to check lock status',
					],
				],
			],
		] );

		register_rest_route( self::API_NAMESPACE, '/' . self::API_BASE . '/archive', [
			[
				'methods' => 'POST',
				'callback' => fn( $request ) => $this->route_wrapper( fn() => $this->archive_components( $request ) ),
				'permission_callback' => fn() => current_user_can( 'manage_options' ),
				'args' => [
					'componentIds' => [
						'type' => 'array',
						'items' => [
							'type' => 'number',
							'required' => true,
						],
						'required' => true,
						'description' => 'The component IDs to archive',
					],
					'status' => [
						'type' => 'string',
						'enum' => [ Document::STATUS_PUBLISH, Document::STATUS_DRAFT, Document::STATUS_AUTOSAVE ],
						'required' => true,
					],
				],
			],
		] );

		register_rest_route( self::API_NAMESPACE, '/' . self::API_BASE . '/update-titles', [
			[
				'methods' => 'POST',
				'callback' => fn( $request ) => $this->route_wrapper( fn() => $this->update_components_title( $request ) ),
				'permission_callback' => fn() => current_user_can( 'manage_options' ),
				'args' => [
					'components' => [
						'type' => 'array',
						'required' => true,
						'items' => [
							'type' => 'object',
							'properties' => [
								'componentId' => [
									'type' => 'number',
									'required' => true,
									'description' => 'The component ID to update title',
								],
								'title' => [
									'type' => 'string',
									'required' => true,
									'description' => 'The new title for the component',
								],
							],
						],
					],
					'status' => [
						'type' => 'string',
						'enum' => [ Document::STATUS_PUBLISH, Document::STATUS_DRAFT, Document::STATUS_AUTOSAVE ],
						'required' => true,
					],
				],
			],
		] );
	}

	private function get_components() {
		$components = $this->get_repository()->all();

		$components_list = array_values( $components
			->map( fn( $component ) => [
				'id' => $component['id'],
				'name' => $component['title'],
				'uid' => $component['uid'],
				'isArchived' => $component['is_archived'] ?? false,
			] )
		->all() );

		return Response_Builder::make( $components_list )->build();
	}

	private function get_styles() {
		$components = $this->get_repository()->all();

		$styles = [];
		$components->each( function( $component ) use ( &$styles ) {
			$styles[ $component['id'] ] = $component['styles'];
		} );

		return Response_Builder::make( $styles )->build();
	}

	private function get_overridable_props( \WP_REST_Request $request ) {
		$component_ids = $request->get_param( 'componentIds' );

		$data = [];
		$errors = [];

		foreach ( $component_ids as $component_id ) {
			$component_id = (int) $component_id;

			/** @var Component $document */
			$document = $this->get_repository()->get( $component_id );

			if ( ! $document ) {
				$errors[ $component_id ] = 'component_not_found';
				continue;
			}

			// This is a fix for the case where overridable props in element settings where migrated
			// but the overridable props metadata were not aligned with the new origin values.
			// In version 4.0.1, we fixed this by running the align_overridable_props_with_elements method after the migration.
			$document_version = $document->get_elementor_version();
			$overridable_props_migration_fix_version = '4.0.1';
			$should_align_overridable_props = version_compare( $document_version, $overridable_props_migration_fix_version, '<=' );
			if ( $should_align_overridable_props ) {
				$document->align_overridable_props_with_elements();
			}

			$overridable = $document->get_json_meta( Component::OVERRIDABLE_PROPS_META_KEY );

			$data[ $component_id ] = empty( $overridable ) ? null : $overridable;
		}

		return Response_Builder::make( $data )
			->set_meta( [ 'errors' => $errors ] )
			->build();
	}

	private function create_components( \WP_REST_Request $request ) {
		if ( ! Components_Access_Controller::can_create() ) {
			return $this->get_insufficient_permissions_error( 'create' );
		}

		$save_status = $request->get_param( 'status' );

		$items = Collection::make( $request->get_param( 'items' ) );
		$components = $this->get_repository()->all();

		$result = Save_Components_Validator::make( $components )->validate( $items );

		if ( ! $result['success'] ) {
			return Error_Builder::make( 'components_validation_failed' )
				->set_status( 422 )
				->set_message( 'Validation failed: ' . implode( ', ', $result['messages'] ) )
				->build();
		}

		$circular_result = Circular_Dependency_Validator::make()->validate_new_components( $items );

		if ( ! $circular_result['success'] ) {
			return Error_Builder::make( 'circular_dependency_detected' )
				->set_status( 422 )
				->set_message( __( "Can't add this component - components that contain each other can't be nested.", 'elementor' ) )
				->set_meta( [ 'caused_by' => $circular_result['messages'] ] )
				->build();
		}

		$non_atomic_result = Non_Atomic_Widget_Validator::make()->validate_items( $items );

		if ( ! $non_atomic_result['success'] ) {
			return Error_Builder::make( Non_Atomic_Widget_Validator::ERROR_CODE )
				->set_status( 422 )
				->set_message( __( 'Components require atomic elements only. Remove widgets to create this component.', 'elementor' ) )
				->set_meta( [ 'non_atomic_elements' => $non_atomic_result['non_atomic_elements'] ] )
				->build();
		}

		$validation_errors = [];

		$created = $items->map_with_keys( function ( $item ) use ( $save_status, &$validation_errors ) {
			$title = sanitize_text_field( $item['title'] );
			$content = $item['elements'];
			$uid = $item['uid'];

			try {
				$settings = isset( $item['settings'] ) ? $this->parse_settings( $item['settings'] ) : [];

				$status = Document::STATUS_AUTOSAVE === $save_status
					? Document::STATUS_DRAFT
					: $save_status;

				$component_id = $this->get_repository()->create( $title, $content, $status, $uid, $settings );

				return [ $uid => $component_id ];
			} catch ( \Exception $e ) {
				$validation_errors[ $uid ] = $e->getMessage();
				return [ $uid => null ];
			}
		} );

		if ( ! empty( $validation_errors ) ) {
			return Error_Builder::make( 'settings_validation_failed' )
				->set_status( 422 )
				->set_message( 'Settings validation failed: ' . json_encode( $validation_errors ) )
				->build();
		}

		return Response_Builder::make( $created->all() )
			->set_status( 201 )
			->build();
	}

	private function update_statuses( \WP_REST_Request $request ) {
		if ( ! Components_Access_Controller::can_publish() ) {
			return $this->get_insufficient_permissions_error( 'publish' );
		}

		$result = Collection::make( $request->get_param( 'ids' ) )
			->reduce(
				function ( $result, int $component_id ) {
					$component = $this->get_repository()->get( $component_id );

					if ( ! $component ) {
						$result['failed'][] = $component_id;
						return $result;
					}

					$publish_result = $this->get_repository()->publish_component( $component );

					$result[ $publish_result ? 'success' : 'failed' ][] = $component_id;

					return $result;
				},
				[
					'success' => [],
					'failed' => [],
				]
			);

		return Response_Builder::make( $result )->build();
	}

	private function lock_component( \WP_REST_Request $request ) {
		if ( ! Components_Access_Controller::can_lock() ) {
			return $this->get_insufficient_permissions_error( 'lock' );
		}

		$component_id = $request->get_param( 'componentId' );
		try {
			$success = $this->get_component_lock_manager()->lock( $component_id );
		} catch ( \Exception $e ) {
			error_log( 'Components REST API lock_component error: ' . $e->getMessage() );
			return Error_Builder::make( 'lock_failed' )
				->set_status( 500 )
				->set_message( __( 'Failed to lock component', 'elementor' ) )
				->build();
		}

		if ( ! $success ) {
			return Error_Builder::make( 'lock_failed' )
				->set_status( 500 )
				->set_message( __( 'Failed to lock component', 'elementor' ) )
				->build();
		}

		return Response_Builder::make( [ 'locked' => $success ] )->build();
	}

	private function unlock_component( \WP_REST_Request $request ) {
		if ( ! Components_Access_Controller::can_lock() ) {
			return $this->get_insufficient_permissions_error( 'unlock' );
		}

		$component_id = $request->get_param( 'componentId' );
		try {
			$success = $this->get_component_lock_manager()->unlock( $component_id );
		} catch ( \Exception $e ) {
			error_log( 'Components REST API unlock_component error: ' . $e->getMessage() );
			return Error_Builder::make( 'unlock_failed' )
				->set_status( 500 )
				->set_message( __( 'Failed to unlock component', 'elementor' ) )
				->build();
		}

		if ( ! $success ) {
			return Error_Builder::make( 'unlock_failed' )
				->set_status( 500 )
				->set_message( __( 'Failed to unlock component', 'elementor' ) )
				->build();
		}
		return Response_Builder::make( [ 'unlocked' => $success ] )->build();
	}

	private function get_lock_status( \WP_REST_Request $request ) {
		if ( ! Components_Access_Controller::can_lock() ) {
			return $this->get_insufficient_permissions_error( 'lock_status' );
		}

		$component_id = (int) $request->get_param( 'componentId' );
		try {
			$lock_manager = $this->get_component_lock_manager();
			if ( $lock_manager->is_lock_expired( $component_id ) ) {
				$lock_manager->unlock( $component_id );
			}

			$lock_data = $lock_manager->get_lock_data( $component_id );
			$current_user_id = get_current_user_id();

			// if current  user is the lock user, return true
			if ( $lock_data['locked_by'] && $lock_data['locked_by'] === $current_user_id ) {
				return Response_Builder::make( [
					'is_current_user_allow_to_edit' => true,
					'locked_by' => get_user_by( 'id', $lock_data['locked_by'] )->display_name,
				] )->build();
			}

			// if the user is not the lock user, return false
			if ( $lock_data['locked_by'] && $lock_data['locked_by'] !== $current_user_id ) {
				return Response_Builder::make( [
					'is_current_user_allow_to_edit' => false,
					'locked_by' => get_user_by( 'id', $lock_data['locked_by'] )->display_name,
				] )->build();
			}

			// if the component is not locked, return true
			if ( ! $lock_data['locked_by'] ) {
				return Response_Builder::make( [
					'is_current_user_allow_to_edit' => true,
					'locked_by' => null,
				] )->build();
			}
		} catch ( \Exception $e ) {
			error_log( 'Components REST API get_lock_status error: ' . $e->getMessage() );
			return Error_Builder::make( 'get_lock_status_failed' )
				->set_status( 500 )
				->set_message( __( 'Failed to get lock status', 'elementor' ) )
				->build();
		}
	}

	private function archive_components( \WP_REST_Request $request ) {
		if ( ! Components_Access_Controller::can_delete() ) {
			return $this->get_insufficient_permissions_error( 'delete' );
		}

		$component_ids = $request->get_param( 'componentIds' );
		$status = $request->get_param( 'status' );

		try {
			$result = $this->get_repository()->archive( $component_ids, $status );
		} catch ( \Exception $e ) {
			error_log( 'Components REST API archive_components error: ' . $e->getMessage() );
			return Error_Builder::make( 'archive_failed' )
				->set_meta( [ 'error' => $e->getMessage() ] )
				->set_status( 500 )
				->set_message( __( 'Failed to archive components', 'elementor' ) )
				->build();
		}
		return Response_Builder::make( $result )->build();
	}

	private function update_components_title( \WP_REST_Request $request ) {
		if ( ! Components_Access_Controller::can_rename() ) {
			return $this->get_insufficient_permissions_error( 'rename' );
		}

		$failed_ids = [];
		$success_ids = [];
		$components = $request->get_param( 'components' );
		$status = $request->get_param( 'status' );

		foreach ( $components as $component ) {
			$is_success = $this->get_repository()->update_title( $component['componentId'], $component['title'], $status );

			if ( ! $is_success ) {
				$failed_ids[] = $component['componentId'];
				continue;
			}
			$success_ids[] = $component['componentId'];

		}
		return Response_Builder::make( [
			'failedIds' => $failed_ids,
			'successIds' => $success_ids,
		] )->build();
	}

	private function create_validate_components( \WP_REST_Request $request ) {
		if ( ! Components_Access_Controller::can_create() ) {
			return $this->get_insufficient_permissions_error( 'create' );
		}

		$items = Collection::make( $request->get_param( 'items' ) );
		$components = $this->get_repository()->all();

		$result = Save_Components_Validator::make( $components )->validate( $items );

		if ( ! $result['success'] ) {
			return Error_Builder::make( 'components_validation_failed' )
				->set_status( 422 )
				->set_message( 'Validation failed: ' . implode( ', ', $result['messages'] ) )
				->build();
		}

		$circular_result = Circular_Dependency_Validator::make()->validate_new_components( $items );

		if ( ! $circular_result['success'] ) {
			return Error_Builder::make( 'circular_dependency_detected' )
				->set_status( 422 )
				->set_message( __( "Can't add this component - components that contain each other can't be nested.", 'elementor' ) )
				->set_meta( [ 'caused_by' => $circular_result['messages'] ] )
				->build();
		}

		$non_atomic_result = Non_Atomic_Widget_Validator::make()->validate_items( $items );

		if ( ! $non_atomic_result['success'] ) {
			return Error_Builder::make( Non_Atomic_Widget_Validator::ERROR_CODE )
				->set_status( 422 )
				->set_message( __( 'Components require atomic elements only. Remove widgets to create this component.', 'elementor' ) )
				->set_meta( [ 'non_atomic_elements' => $non_atomic_result['non_atomic_elements'] ] )
				->build();
		}

		$validation_errors = $items->map_with_keys( function ( $item ) {
			try {
				if ( isset( $item['settings'] ) ) {
					$this->parse_settings( $item['settings'] );
				}
			} catch ( \Exception $e ) {
				return [ $item['uid'] => $e->getMessage() ];
			}

			return [ $item['uid'] => null ];
		} )
		->filter( fn( $value ) => null !== $value );

		if ( ! $validation_errors->is_empty() ) {
			return Error_Builder::make( 'settings_validation_failed' )
				->set_status( 422 )
				->set_message( 'Settings validation failed: ' . json_encode( $validation_errors->all() ) )
				->build();
		}

		return Response_Builder::make()
			->set_status( 200 )
			->build();
	}

	private function parse_settings( array $settings ): array {
		$result = [];

		if ( empty( $settings ) ) {
			return $result;
		}

		if ( isset( $settings['overridable_props'] ) ) {
			$parser = Component_Overridable_Props_Parser::make();
			$overridable_props_result = $parser->parse( $settings['overridable_props'] );

			if ( ! $overridable_props_result->is_valid() ) {
				throw new \Exception(
					esc_html( 'Validation failed for overridable_props: ' . $overridable_props_result->errors()->to_string() )
				);
			}

			$result['overridable_props'] = $overridable_props_result->unwrap();
		}

		return $result;
	}

	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 get_insufficient_permissions_error( string $action ) {
		return Error_Builder::make( 'insufficient_permissions' )
			->set_status( 403 )
			->set_message( __( 'You do not have permission to perform this action.', 'elementor' ) )
			->set_meta( [
				'action' => $action,
				'tier' => Components_Access_Controller::get_access_tier(),
			] )
			->build();
	}
}

Directory Contents

Dirs: 7 × Files: 10
Name Size Perms Modified Actions
documents DIR
- drwxr-xr-x 2026-09-08 12:13:51
Edit Download
- drwxr-xr-x 2026-09-08 12:13:51
Edit Download
- drwxr-xr-x 2026-09-08 12:13:51
Edit Download
styles DIR
- drwxr-xr-x 2026-09-08 12:13:51
Edit Download
- drwxr-xr-x 2026-09-08 12:13:51
Edit Download
utils DIR
- drwxr-xr-x 2026-09-08 12:13:51
Edit Download
widgets DIR
- drwxr-xr-x 2026-09-08 12:13:51
Edit Download
4.52 KB lrwxr-xr-x 2026-09-08 12:13:51
Edit Download
3.61 KB lrwxr-xr-x 2026-09-08 12:13:51
Edit Download
1.39 KB lrwxr-xr-x 2026-09-08 12:13:51
Edit Download
5.76 KB lrwxr-xr-x 2026-09-08 12:13:51
Edit Download
21.37 KB lrwxr-xr-x 2026-09-08 12:13:51
Edit Download
4.50 KB lrwxr-xr-x 2026-09-08 12:13:51
Edit Download
6.82 KB lrwxr-xr-x 2026-09-08 12:13:51
Edit Download
2.59 KB lrwxr-xr-x 2026-09-08 12:13:51
Edit Download
927 B lrwxr-xr-x 2026-09-08 12:13:51
Edit Download
2.35 KB lrwxr-xr-x 2026-09-08 12:13:51
Edit Download
If ZipArchive is unavailable, a .tar will be created (no compression).
© 2026 0xNothings — Secure File Manager. All rights reserved. Built with ❤️ & Tailwind x Dark UI