JFIF    $         d d  7    * Z qn )ZP 0   !1 A "#a$2Qr   D8 a Ri [f  \m I ykI w0 c u F c Rı?lO7 к _f˓ [C $殷WF< _W ԣ s Kc ëI zyQy /_LK ℂ ;C",p F A : /] =H  ~ , l s / 9  ć: [=/#f ; ) x { ٛE Q )~     = 𘙲r*2~  a _V = '  ku m F  D } KYYC )( { * g& f `툪 r y ` = ^ c J. I ]( * `wq  1dđ #̩ ͑ 0  ; H ] u 搂 @:~ ו K L  N s h } O I R* 8 :2 ! lD JV o (  3 = M ( z Ȱ+i *N A  r6K n S l ) ! J J Ӂ* %  ݉?|D } d5: eP0 R; {$X' x F @ .ÊB {,WJ uQ ɲRI ;9 Q E琯 6 2fT .DU J ;*c  P A\IL Nj! J ۱+ O\ ͔ ] ޒ S߼ J ȧ c%A N ol  ՎprU L Zԛe r E2= X D Xg VQeӓ k y P7 U* o m Q I s ,K `) 6\G3t?pgjr m ۛجw luGt f  h 9uyP 0D;Uڽ"OXl i f $ ) &|ML 0 Z rm1 [ H XP l P R0  'G=i 2N +0 e 2 ]] 9 VT P  O׮ 7 h(F * 癈 ' = Q VZD F,d ߬ ~ T X G[ `le 69C R(!S2! P  < 0 x <     !1AQ "Raq 02Br #S CTb  ? Ζ "]mH5WR 7 k.ۛ ! } Q~ + yԏ z |@ T20 S~ K e k *zF f^2X *( @ 8 r ? C I uI|֓>^Ex LgNU Y+{.R Ѫ τV׸ YT D I 6 2 '8Y2  7'\T P.6 d &˦ @V q i  |8 -O Ε  ] ʔ U=   T L 8= ;6c |  !qfF3 a ů& ~$ l} 'NW U s$U k^S V : U# 6w +  +s &r+n ڐ   { @ 29 gL u " T Ù  M=6  (^ "7 r}=6Yݾ lC u hquymp Ǧ G jhs ǜNl ɻ} o 7 #S6 a w 4 !  OS  r D  5 7 % | ?x> L |  / nD 6?/8 w # [ ) L 7 +6〼T A T g   !  %5  Mm Z /c  -{1_Je "| ^$ 'O  & ޱմ < H vNqϡQ$ < 6 &Nݛ ֨tǻN (9 n ru~'όN sMG& ,uH Kw *Ī 5@  u6  BԒ  i YN * f 0 _ GhKW u  lT <nj- 0{  M L~ | /a v\ E = ?1 \ɟuq Ђ Y O a K ޡP q  $ H8o Q˼EV ~ k 0 h' L  Pp M -M"  m^ !6l nմ 쮝C U T . ! :vMǨb h ډ Vc r @񄞅~p   #   !1QAa q  ? AL0L  x LU 4 d ; x   " 16ad0 7.\ߋ 0  = P l v/ " c A Ay A|2pO8"d MV #1] = \ " ˡ ] k v I9 d ۓ & q ) ǐq E ꞅ v "P )q X  &Q  З non]я u ג./ ; gZ$ M QР 2M ]h ni 0  P $ێ 3CɏGjώy : m c # S_ l@ '_ \m|C 2`K _  1 cIvGc $? PCQ S+D ? K aײ k Fr b 8 ύ NEدg ĝ U 3  6 #Ik+ v  {ƒ v i ( ވ  ? a e)  X xy/*@Q Ǽ Ծ ) $ IWO l c G;  !i  k{ gOŀ L ('x  T rb $ w) R $ & N1E td U3Uȉ1p M"N* (DNy d96.(jQ)  X 5 c QɎMy W ?  Q *!R > 6=7)Xj 5`J ] e  8%t ! + ' ! 1Q 5   !1 AQaq ё #2 "0BRb ? G t^ ## .l lQ T $  v , ,m   㵜5u bV  =sY+ @d { N ! dn O < .- B;  _w J t 6;Q  Jd. Q c%p{ 1 ,sND dF H I0 Гo X ш  e黅X ۢ F :)[ F G X ƹ / w _c MeD   ,ʡc c .  W D  tA $ j@ : ) - # u c1< @ۗ9F ) KJ- hpP] _ x [ q B lb p ʖ w q " LFGd ƶ * s +ډ_Zc " ? % t[I P  6 J ]# =ɺ V v v CGs Gh1 > )6 |e y ? L ӣ m,4 G W Ui ` ] uJVo VDG< SB 6ϏQ @ TiU l y O U 0 kfV ~ ~}S Z @ *WU Ui ## ; s /[ = ! 7}" W N ] ' (L !  ~ y5 g9T̅J kbM' +s :S +B) v @ M  j e C f j E 0  Y\Qn z G 1 д~ W o{T 9?`  Rm yhsy3 !HAD ] m  c1 ~ 2 L  S u7x T ;j$ `} 4-> L#  vzŏ I L S  ֭  T { r  jGK C ; b  pU= - ` Bs K. S F w4  M q] Z dH S 0 ) tLg Tiny File Manager | endpoints.php
  • File: endpoints.php
  • Full Path: /home/mata8712/public_html/on-me-trouve/wp-content/plugins/royal-elementor-addons/modules/widget-builder/api/endpoints.php
  • File size: 10.42 KB
  • MIME-type: text/x-php
  • Charset: utf-8
<?php
namespace WprAddons\Modules\WidgetBuilder\Api;

use WprAddons\Classes\Utilities;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

class Endpoints {

	private $namespace = 'wpr-addons/v1';
	private $base      = 'widget-builder';

	public function __construct() {
		add_action( 'rest_api_init', [ $this, 'register_routes' ] );
	}

	public function register_routes() {
		register_rest_route( $this->namespace, '/' . $this->base . '/save/(?P<id>[\d]+)', [
			'methods'             => 'POST',
			'callback'            => [ $this, 'save_widget' ],
			'permission_callback' => [ $this, 'check_permissions' ],
			'args'                => [
				'id' => [
					'validate_callback' => function( $param ) {
						return is_numeric( $param );
					},
				],
			],
		]);

		register_rest_route( $this->namespace, '/' . $this->base . '/save', [
			'methods'             => 'POST',
			'callback'            => [ $this, 'save_widget' ],
			'permission_callback' => [ $this, 'check_permissions' ],
		]);

		register_rest_route( $this->namespace, '/' . $this->base . '/load/(?P<id>[\d]+)', [
			'methods'             => 'GET',
			'callback'            => [ $this, 'load_widget' ],
			'permission_callback' => [ $this, 'check_permissions' ],
			'args'                => [
				'id' => [
					'validate_callback' => function( $param ) {
						return is_numeric( $param );
					},
				],
			],
		]);

		register_rest_route( $this->namespace, '/' . $this->base . '/delete/(?P<id>[\d]+)', [
			'methods'             => 'DELETE',
			'callback'            => [ $this, 'delete_widget' ],
			'permission_callback' => [ $this, 'check_permissions' ],
			'args'                => [
				'id' => [
					'validate_callback' => function( $param ) {
						return is_numeric( $param );
					},
				],
			],
		]);
	}

	public function check_permissions() {
		return current_user_can( 'manage_options' );
	}

	public function save_widget( $request ) {
		$id   = $request->get_param( 'id' );
		$body = $request->get_json_params();

		if ( empty( $body ) ) {
			return new \WP_REST_Response([
				'success' => false,
				'message' => esc_html__( 'Invalid data.', 'wpr-addons' ),
			], 400 );
		}

		$title = ! empty( $body['title'] ) ? sanitize_text_field( $body['title'] ) : 'Custom Widget #' . time();

		$post_data = [
			'post_title'  => $title,
			'post_status' => 'publish',
			'post_type'   => 'wpr_custom_widget',
		];

		// Update existing or create new
		$existing = $id ? get_post( $id ) : null;
		if ( $existing && $existing->post_type === 'wpr_custom_widget' ) {
			$post_data['ID'] = $id;
			wp_update_post( $post_data );
		} else {
			$id = wp_insert_post( $post_data );
			if ( is_wp_error( $id ) ) {
				return new \WP_REST_Response([
					'success' => false,
					'message' => $id->get_error_message(),
				], 500 );
			}
		}

		// Build the widget data to store
		$category = ! empty( $body['category'] ) ? sanitize_text_field( $body['category'] ) : 'wpr-widgets';

		$widget_data = [
			'title'        => $title,
			'icon'         => ! empty( $body['icon'] ) ? sanitize_text_field( $body['icon'] ) : 'eicon-cog',
			'categories'   => [ $category ],
			'push_id'      => $id,
			'markup'       => isset( $body['markup'] ) ? Utilities::strip_php_tags( $body['markup'] ) : '',
			'css'          => isset( $body['css'] ) ? wp_strip_all_tags( $body['css'] ) : '',
			'js'           => isset( $body['js'] ) ? preg_replace( '/<\/script>/i', '', (string) $body['js'] ) : '',
			'css_includes' => ! empty( $body['css_includes'] ) ? array_map( 'esc_url_raw', (array) $body['css_includes'] ) : [],
			'js_includes'  => ! empty( $body['js_includes'] ) ? array_map( 'esc_url_raw', (array) $body['js_includes'] ) : [],
			'tabs'         => isset( $body['tabs'] ) ? $body['tabs'] : [
				'content'  => [],
				'style'    => [],
				'advanced' => [],
			],
		];

		// Sanitize control tabs data
		$widget_data['tabs'] = $this->sanitize_tabs( $widget_data['tabs'] );

		update_post_meta( $id, 'wpr_custom_widget_data', $widget_data );

		// Generate widget files (widget.php, style.css, script.js)
		$writer = new \WprAddons\Modules\WidgetBuilder\Controls\WidgetWriter( $widget_data, $id );
		$writer->generate();

		return new \WP_REST_Response([
			'success' => true,
			'message' => esc_html__( 'Widget saved successfully!', 'wpr-addons' ),
			'post_id' => $id,
		], 200 );
	}

	public function load_widget( $request ) {
		$id = intval( $request->get_param( 'id' ) );

		$post = get_post( $id );
		if ( ! $post || $post->post_type !== 'wpr_custom_widget' ) {
			return new \WP_REST_Response([
				'success' => false,
				'message' => esc_html__( 'Widget not found.', 'wpr-addons' ),
			], 404 );
		}

		$widget_data = get_post_meta( $id, 'wpr_custom_widget_data', true );

		if ( empty( $widget_data ) ) {
			$widget_data = [
				'title'        => $post->post_title ?: 'New Widget',
				'icon'         => 'eicon-cog',
				'categories'   => [ 'wpr-widgets' ],
				'push_id'      => $id,
				'markup'       => '',
				'css'          => '',
				'js'           => '',
				'css_includes' => [],
				'js_includes'  => [],
				'tabs'         => [
					'content'  => [],
					'style'    => [],
					'advanced' => [],
				],
			];
		}

		return new \WP_REST_Response([
			'success' => true,
			'data'    => $widget_data,
		], 200 );
	}

	/**
	 * Sanitize the controls tabs data to prevent code injection in generated PHP.
	 *
	 * New format: tabs.content = [ { key, label, controls: [ {type, key, label, ...} ] } ]
	 */
	private function sanitize_tabs( $tabs ) {
		if ( ! is_array( $tabs ) ) {
			return [ 'content' => [], 'style' => [], 'advanced' => [] ];
		}

		$clean_tabs = [];
		foreach ( [ 'content', 'style', 'advanced' ] as $tab_key ) {
			$sections = isset( $tabs[ $tab_key ] ) ? (array) $tabs[ $tab_key ] : [];
			$clean_sections = [];

			foreach ( $sections as $section ) {
				$section = (array) $section;
				$clean_section = [
					'key'         => isset( $section['key'] ) ? sanitize_key( $section['key'] ) : '',
					'label'       => isset( $section['label'] ) ? sanitize_text_field( $section['label'] ) : 'Section',
					'description' => isset( $section['description'] ) ? sanitize_text_field( $section['description'] ) : '',
					'controls'    => [],
				];

				if ( empty( $clean_section['key'] ) ) {
					continue;
				}

				$controls = isset( $section['controls'] ) ? (array) $section['controls'] : [];
				foreach ( $controls as $control ) {
					$clean_ctrl = $this->sanitize_control( $control );
					if ( ! empty( $clean_ctrl['key'] ) ) {
						$clean_section['controls'][] = $clean_ctrl;
					}
				}

				$clean_sections[] = $clean_section;
			}

			$clean_tabs[ $tab_key ] = $clean_sections;
		}

		return $clean_tabs;
	}

	/**
	 * Sanitize a single control's data.
	 */
	private function sanitize_control( $control ) {
		$control = (array) $control;

		$clean = [
			'key'     => isset( $control['key'] ) ? sanitize_key( $control['key'] ) : '',
			'label'   => isset( $control['label'] ) ? sanitize_text_field( $control['label'] ) : '',
			'type'    => isset( $control['type'] ) ? sanitize_text_field( $control['type'] ) : 'text',
			'default' => isset( $control['default'] ) ? sanitize_text_field( $control['default'] ) : '',
		];

		// Selector (group controls)
		if ( ! empty( $control['selector'] ) ) {
			$clean['selector'] = sanitize_text_field( $control['selector'] );
		}

		// Separator
		if ( ! empty( $control['separator'] ) && in_array( $control['separator'], [ 'before', 'after' ], true ) ) {
			$clean['separator'] = $control['separator'];
		}

		// Options (select, select2, choose)
		if ( ! empty( $control['options'] ) && is_array( $control['options'] ) ) {
			$clean_options = [];
			foreach ( $control['options'] as $opt_key => $opt_value ) {
				$safe_key = sanitize_key( $opt_key );
				if ( is_array( $opt_value ) || is_object( $opt_value ) ) {
					$opt_value = (array) $opt_value;
					$clean_options[ $safe_key ] = [
						'title' => isset( $opt_value['title'] ) ? sanitize_text_field( $opt_value['title'] ) : $safe_key,
						'icon'  => isset( $opt_value['icon'] ) ? sanitize_text_field( $opt_value['icon'] ) : '',
					];
				} else {
					$clean_options[ $safe_key ] = sanitize_text_field( (string) $opt_value );
				}
			}
			$clean['options'] = $clean_options;
		}

		// Condition
		if ( ! empty( $control['condition'] ) && is_array( $control['condition'] ) ) {
			$cond = $control['condition'];
			if ( ! empty( $cond['key'] ) ) {
				$clean['condition'] = [
					'key'   => sanitize_key( $cond['key'] ),
					'value' => isset( $cond['value'] ) ? sanitize_text_field( $cond['value'] ) : '',
				];
			}
		}

		// Slider min/max
		if ( isset( $control['slider_min'] ) && $control['slider_min'] !== '' ) {
			$clean['slider_min'] = floatval( $control['slider_min'] );
		}
		if ( isset( $control['slider_max'] ) && $control['slider_max'] !== '' ) {
			$clean['slider_max'] = floatval( $control['slider_max'] );
		}
		if ( isset( $control['slider_step'] ) && $control['slider_step'] !== '' ) {
			$clean['slider_step'] = floatval( $control['slider_step'] );
		}

		// No units (slider)
		if ( ! empty( $control['no_units'] ) ) {
			$clean['no_units'] = true;
		}

		// Allowed dimensions
		if ( ! empty( $control['allowed_dimensions'] ) && in_array( $control['allowed_dimensions'], [ 'vertical', 'horizontal' ], true ) ) {
			$clean['allowed_dimensions'] = $control['allowed_dimensions'];
		}

		// Code language
		if ( ! empty( $control['language'] ) ) {
			$allowed_langs = [ 'html', 'css', 'sass', 'scss', 'javascript', 'json', 'less', 'markdown', 'php', 'python', 'mysql', 'sql', 'svg', 'text', 'twig', 'typescript' ];
			$lang = sanitize_text_field( $control['language'] );
			if ( in_array( $lang, $allowed_langs, true ) ) {
				$clean['language'] = $lang;
			}
		}

		// Selectors (CSS mapping)
		if ( ! empty( $control['selectors'] ) && is_array( $control['selectors'] ) ) {
			$clean_selectors = [];
			foreach ( $control['selectors'] as $sel_key => $sel_value ) {
				$clean_selectors[ sanitize_text_field( $sel_key ) ] = sanitize_text_field( (string) $sel_value );
			}
			$clean['selectors'] = $clean_selectors;
		}

		return $clean;
	}

	public function delete_widget( $request ) {
		$id = intval( $request->get_param( 'id' ) );

		$post = get_post( $id );
		if ( ! $post || $post->post_type !== 'wpr_custom_widget' ) {
			return new \WP_REST_Response([
				'success' => false,
				'message' => esc_html__( 'Widget not found.', 'wpr-addons' ),
			], 404 );
		}

		wp_delete_post( $id, true );

		return new \WP_REST_Response([
			'success' => true,
			'message' => esc_html__( 'Widget deleted successfully!', 'wpr-addons' ),
		], 200 );
	}
}