PluginProbe
Ultimate Store Kit – Store Builder Addons for Elementor, WooCommerce Store Builder, EDD Store Builder / 3.1.4
Ultimate Store Kit – Store Builder Addons for Elementor, WooCommerce Store Builder, EDD Store Builder v3.1.4
3.1.4 3.0.8 3.0.9 3.1.0 3.1.2 3.1.3 3.0.7 3.0.5 3.0.4 3.0.3 3.0.2 trunk 1.5.0 1.5.1 1.5.2 1.6.1 1.6.2 1.6.3 1.6.4 2.0.0 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 All 93 releases
← All changes | includes/builder/builder-cpt.php +278 -173 2.0.43.1.4 View file →
@@ -3,9 +3,9 @@
3 3 namespace UltimateStoreKit\Includes\Builder;
4 4
5 5 use UltimateStoreKit\Base\Singleton;
6 6
7 -if ( ! defined( 'ABSPATH' ) ) {
7 +if (! defined('ABSPATH')) {
8 8 exit;
9 9 }
10 10
11 11 class Builder_Cpt {
@@ -14,26 +14,31 @@
14 14
15 15 public function init_hooks() {
16 16 $builderCpt = Meta::POST_TYPE;
17 17
18 - add_action( 'init', [ $this, 'registered_post_type' ] );
19 - add_action( 'admin_footer', [ $this, 'add_modal_html' ], 1 );
20 - add_action( 'delete_post', [ $this, 'trashed_or_delete_post' ], 10, 2 );
21 - add_action( 'trashed_post', [ $this, 'trashed_or_delete_post' ], 10, 1 );
18 + add_action('init', [$this, 'registered_post_type']);
19 + add_action('admin_footer', [$this, 'add_modal_html'], 1);
20 + add_action('delete_post', [$this, 'trashed_or_delete_post'], 10, 2);
21 + add_action('trashed_post', [$this, 'trashed_or_delete_post'], 10, 1);
22 22
23 - add_action( 'wp_ajax_ultimate_store_kit_builder_create_template', [ $this, 'create_builder_template' ] );
24 - add_action( 'wp_ajax_ultimate_store_kit_builder_get_edit_template', [ $this, 'get_builder_template_action' ] );
25 - add_filter( "manage_{$builderCpt}_posts_columns", [ $this, 'set_post_columns' ] );
26 - add_action( "manage_{$builderCpt}_posts_custom_column", [ $this, 'set_custom_column_value' ], 10, 2 );
27 - add_filter( 'post_row_actions', [ $this, 'post_row_actions_filter' ], 20, 2 );
23 + add_action('wp_ajax_ultimate_store_kit_builder_create_template', [$this, 'create_builder_template']);
24 + add_action('wp_ajax_ultimate_store_kit_builder_get_edit_template', [$this, 'get_builder_template_action']);
25 + add_filter("manage_{$builderCpt}_posts_columns", [$this, 'set_post_columns']);
26 + add_action("manage_{$builderCpt}_posts_custom_column", [$this, 'set_custom_column_value'], 10, 2);
27 + add_filter('post_row_actions', [$this, 'post_row_actions_filter'], 20, 2);
28 28
29 - if ( is_admin() ) {
30 - add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_scripts' ], 1 );
31 - add_action( 'admin_menu', [ $this, 'add_admin_menu' ], 202 );
32 - add_action( 'restrict_manage_posts', [ $this, 'add_filter' ] );
33 - add_filter( 'parse_query', [ $this, 'parse_query_filter' ] );
29 + // Simple WPML fix
30 + if (function_exists('icl_object_id')) {
31 + add_filter('elementor/editor/before_enqueue_scripts', [$this, 'fix_wpml_elementor_data'], 1);
34 32 }
35 33
34 + if (is_admin()) {
35 + add_action('admin_enqueue_scripts', [$this, 'enqueue_scripts'], 1);
36 + add_action('admin_menu', [$this, 'add_admin_menu'], 202);
37 + add_action('restrict_manage_posts', [$this, 'add_filter']);
38 + add_filter('parse_query', [$this, 'parse_query_filter']);
39 + }
40 +
36 41 // $this->resetTemplateCache();
37 42 }
38 43
39 44 public function resetTemplateCache() {
@@ -40,70 +45,93 @@
40 45 global $wpdb;
41 46
42 47 $postType = Meta::POST_TYPE;
43 48
44 - $query = $wpdb->get_results( "SELECT {$wpdb->posts}.ID,{$wpdb->posts}.post_type, {$wpdb->posts}.post_status, {$wpdb->postmeta}.meta_value as template_type
45 -FROM $wpdb->posts
46 - LEFT JOIN $wpdb->postmeta
47 - ON {$wpdb->postmeta}.post_id = {$wpdb->posts}.ID
48 -WHERE 1=1
49 -AND {$wpdb->posts}.post_type ='{$postType}'
50 -AND {$wpdb->postmeta}.meta_key ='_ultimate_store_kit_template_type'
51 -ORDER BY {$wpdb->posts}.post_date DESC" );
49 + // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Rebuilds the template cache; this is the call that populates it.
50 + $query = $wpdb->get_results($wpdb->prepare(
51 + "SELECT {$wpdb->posts}.ID, {$wpdb->posts}.post_type, {$wpdb->posts}.post_status, {$wpdb->postmeta}.meta_value as template_type
52 + FROM {$wpdb->posts}
53 + LEFT JOIN {$wpdb->postmeta}
54 + ON {$wpdb->postmeta}.post_id = {$wpdb->posts}.ID
55 + WHERE 1=1
56 + AND {$wpdb->posts}.post_type = %s
57 + AND {$wpdb->postmeta}.meta_key = %s
58 + ORDER BY {$wpdb->posts}.post_date DESC",
59 + $postType,
60 + '_ultimate_store_kit_template_type'
61 + ));
62 + // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
52 63
53 - foreach ( $query as $q ) {
54 - if ( ! $q->template_type ) {
64 + foreach ($query as $q) {
65 + if (! $q->template_type) {
55 66 continue;
56 67 }
57 68
58 - $optionKey = Meta::TEMPLATE_ID . $q->template_type;
59 -
60 - if ( $q->post_status == 'publish' ) {
61 - update_option( $optionKey, $q->ID );
69 + if ($q->post_status == 'publish') {
70 + Meta::update_template_option($q->template_type, $q->ID);
62 71 } else {
63 - delete_option( $optionKey, $q->ID );
72 + Meta::delete_template_option($q->template_type);
64 73 }
65 74 }
66 75 }
67 76
68 - public function trashed_or_delete_post( $postId ) {
69 - if ( get_post_type( $postId ) != Meta::POST_TYPE ) {
77 + public function trashed_or_delete_post($postId) {
78 + if (get_post_type($postId) != Meta::POST_TYPE) {
70 79 return;
71 80 }
72 81
73 - if ( $template = get_post_meta( $postId, Meta::TEMPLATE_TYPE, true ) ) {
74 - delete_option( Meta::TEMPLATE_ID . $template );
82 + if ($template = get_post_meta($postId, Meta::TEMPLATE_TYPE, true)) {
83 + Meta::delete_template_option($template);
75 84 }
76 85 }
77 86
78 - public function post_row_actions_filter( $actions, $post ) {
87 + public function post_row_actions_filter($actions, $post) {
79 88
80 89 global $typenow;
81 90
82 - if ( $typenow !== Meta::POST_TYPE ) {
91 + if ($typenow !== Meta::POST_TYPE) {
83 92 return $actions;
84 93 }
85 94
86 - if ( isset( $actions['edit_with_elementor'] ) ) {
87 - unset( $actions['edit_with_elementor'] );
95 + if (isset($actions['edit_with_elementor'])) {
96 + unset($actions['edit_with_elementor']);
88 97 }
89 98
90 - if ( get_post_meta( $post->ID, Meta::EDIT_WITH, true ) == 'gutenberg' ) {
99 + if (get_post_meta($post->ID, Meta::EDIT_WITH, true) == 'gutenberg') {
91 100 $actions['usk_edit_with_gutenberg'] = sprintf(
92 101 '<a href="%1$s">%2$s</a>',
93 - add_query_arg( [ 'post' => $post->ID, 'action' => 'edit' ], admin_url( 'post.php' ) ),
94 - esc_html__( 'Edit with Gutenberg', 'ultimate-store-kit' )
102 + add_query_arg(['post' => $post->ID, 'action' => 'edit'], admin_url('post.php')),
103 + esc_html__('Edit with Gutenberg', 'ultimate-store-kit')
95 104 );
96 -
97 105 }
98 106
99 - if ( get_post_meta( $post->ID, Meta::EDIT_WITH, true ) == 'elementor' ) {
107 + if (get_post_meta($post->ID, Meta::EDIT_WITH, true) == 'elementor') {
100 108 $actions['usk_edit_with_elementor'] = sprintf(
101 109 '<a href="%1$s">%2$s</a>',
102 - add_query_arg( [ 'post' => $post->ID, 'action' => 'elementor', 'usk-template' => 1 ], admin_url( 'post.php' ) ),
103 - esc_html__( 'Edit with Elementor', 'ultimate-store-kit' )
110 + add_query_arg(
111 + [
112 + 'post' => $post->ID,
113 + 'action' => 'elementor',
114 + 'usk-template' => 1
115 + ],
116 + admin_url('post.php')
117 + ),
118 + esc_html__('Edit with Elementor', 'ultimate-store-kit')
104 119 );
105 -
120 + } else {
121 + // Always offer Elementor edit option regardless of stored preference
122 + $actions['usk_edit_with_elementor'] = sprintf(
123 + '<a href="%1$s">%2$s</a>',
124 + add_query_arg(
125 + [
126 + 'post' => $post->ID,
127 + 'action' => 'elementor',
128 + 'usk-template' => 1
129 + ],
130 + admin_url('post.php')
131 + ),
132 + esc_html__('Edit with Elementor', 'ultimate-store-kit')
133 + );
106 134 }
107 135
108 136 $editActionLink = sprintf(
109 137 '<a href="%1$s" data-id="%2$s" >%3$s</a>',
@@ -108,23 +136,26 @@
108 136 $editActionLink = sprintf(
109 137 '<a href="%1$s" data-id="%2$s" >%3$s</a>',
110 138 'javascript:void(0)',
111 139 $post->ID,
112 - esc_html__( 'Edit', 'ultimate-store-kit' )
140 + esc_html__('Edit', 'ultimate-store-kit')
113 141 );
114 142
115 - if ( isset( $actions['edit'] ) && apply_filters( 'ultimate_store_kit_remove_action_edit_link', __return_true() ) ) {
116 - unset( $actions['edit'] );
143 + if (isset($actions['edit']) && apply_filters('ultimate_store_kit_remove_action_edit_link', __return_true())) {
144 + unset($actions['edit']);
117 145 }
118 146
119 - return array_slice( $actions, 0, 1, true ) + [ 'usk-edit-action' => $editActionLink ] + array_slice( $actions, 1, null, true );
147 + return array_slice($actions, 0, 1, true) + ['usk-edit-action' => $editActionLink] + array_slice($actions, 1, null, true);
120 148 }
121 149
122 - public function set_post_columns( $columns ) {
123 - return array_slice( $columns, 0, 2, true ) + [ 'template_type' => 'Type', 'is_enabled' => 'Status' ] + array_slice( $columns, 2, null, true );
150 + public function set_post_columns($columns) {
151 + return array_slice($columns, 0, 2, true) + [
152 + 'template_type' => esc_html__('Type', 'ultimate-store-kit'),
153 + 'is_enabled' => esc_html__('Status', 'ultimate-store-kit')
154 + ] + array_slice($columns, 2, null, true);
124 155 }
125 156
126 - public function set_custom_column_value( $column, $post_id ) {
157 + public function set_custom_column_value($column, $post_id) {
127 158 $templateType = get_post_meta(
128 159 $post_id,
129 160 Meta::TEMPLATE_TYPE,
130 161 true
@@ -129,16 +160,20 @@
129 160 Meta::TEMPLATE_TYPE,
130 161 true
131 162 );
132 163
133 - switch ( $column ) {
164 + switch ($column) {
134 165 case 'template_type':
135 - $postType = Builder_Template_Helper::getTemplatePostTypeByIndex( $templateType );
136 - $postTypeLabel = isset( $postType->name ) ? ' <strong>-- ' . ucwords( $postType->name ) . '</strong>' : '';
137 - echo Builder_Template_Helper::getTemplateByIndex( $templateType ) . $postTypeLabel;
166 + $postType = Builder_Template_Helper::getTemplatePostTypeByIndex($templateType);
167 + echo esc_html(Builder_Template_Helper::getTemplateByIndex($templateType));
168 + if (isset($postType->name)) {
169 + echo ' <strong>-- ' . esc_html(ucwords($postType->name)) . '</strong>';
170 + }
138 171 break;
139 172 case 'is_enabled':
140 - echo ( Builder_Template_Helper::getTemplateId( $templateType ) == $post_id ? 'Active' : 'Inactive' );
173 + echo Builder_Template_Helper::getTemplateId($templateType) == $post_id
174 + ? esc_html__('Active', 'ultimate-store-kit')
175 + : esc_html__('Inactive', 'ultimate-store-kit');
141 176 break;
142 177 }
143 178 }
144 179
@@ -144,123 +179,153 @@
144 179
145 180 public function add_filter() {
146 181 global $typenow;
147 182
148 - if ( $typenow !== Meta::POST_TYPE ) {
183 + if ($typenow !== Meta::POST_TYPE) {
149 184 return;
150 185 }
151 186
152 - $selected = isset( $_GET['type'] ) ? sanitize_key( $_GET['type'] ) : '';
153 - ?>
187 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list-table filter; the value only re-selects the current dropdown option.
188 + $selected = isset($_GET['type']) ? sanitize_text_field(wp_unslash($_GET['type'])) : '';
189 +?>
154 190 <select name="type" id="type">
155 191 <option value="all" <?php
156 - selected( 'all', $selected ); ?>><?php
157 - esc_html_e(
158 - 'Template Type ',
159 - 'ultimate-store-kit'
160 - ); ?></option>
192 + selected('all', $selected); ?>><?php
193 + esc_html_e(
194 + 'Template Type ',
195 + 'ultimate-store-kit'
196 + ); ?></option>
161 197 <?php
162 198 $templates = Builder_Template_Helper::templateForSelectDropdown();
163 199 // It is single
164 - if ( count( $templates ) == 1 ) {
165 - $templateKey = array_key_last( $templates );
166 - $template = $templates[ $templateKey ];
167 - foreach ( $template as $key => $item ) :
200 + if (count($templates) == 1) {
201 + $templateKey = array_key_last($templates);
202 + $template = $templates[$templateKey];
203 + foreach ($template as $key => $item) :
168 204 $selectValue = "{$templateKey}_$key";
169 - ?>
205 + ?>
170 206 <option value="<?php
171 - echo esc_attr( $selectValue ) ?>"><?php
172 - echo wp_kses_post( $item ) ?></option>
207 + echo esc_attr($selectValue) ?>"><?php
208 + echo wp_kses_post($item) ?></option>
173 209 <?php
174 210 endforeach;
175 211 }
176 212
177 - if ( count( $templates ) > 1 ) {
178 - foreach ( $templates as $keys => $items ) :
179 - $label = ucwords( str_replace(
180 - [ '-', '_' ],
181 - [ ' ' ],
213 + if (count($templates) > 1) {
214 + foreach ($templates as $keys => $items) :
215 + $label = ucwords(str_replace(
216 + ['-', '_'],
217 + [' '],
182 218 $keys
183 - ) );
184 - if ( is_array( $items ) ) {
185 - ?>
219 + ));
220 + if (is_array($items)) {
221 + ?>
186 222 <optgroup label="<?php
187 - echo esc_attr( $label ) ?>"><?php
188 - foreach ( $items as $key => $item ) :
189 - $itemValue = "{$keys}_$key"
190 - ?>
223 + echo esc_attr($label) ?>"><?php
224 + foreach ($items as $key => $item) :
225 + $itemValue = "{$keys}_$key"
226 + ?>
191 227 <option value="<?php
192 - echo esc_attr( $itemValue ) ?>" <?php
193 - selected( $key, $selected ); ?>><?php
194 - echo wp_kses_post( $item ) ?></option>
195 - <?php
196 - endforeach;
197 - ?>
228 + echo esc_attr($itemValue) ?>" <?php
229 + selected($key, $selected); ?>><?php
230 + echo wp_kses_post($item) ?></option>
231 + <?php
232 + endforeach;
233 + ?>
198 234 </optgroup>
199 - <?php
235 + <?php
200 236 }
201 237 endforeach;
202 238 }
203 239 ?>
204 240 </select>
205 - <?php
241 +<?php
206 242 }
207 243
208 - public function parse_query_filter( $query ) {
244 + public function parse_query_filter($query) {
209 245 global $pagenow, $typenow;
210 246
211 - if ( $typenow !== Meta::POST_TYPE ) {
247 + if ($typenow !== Meta::POST_TYPE) {
212 248 return;
213 249 }
214 250
251 + // phpcs:disable WordPress.Security.NonceVerification.Recommended, WordPress.DB.SlowDBQuery -- Admin list-table filter; the value only narrows the query that is already running.
215 252 if (
216 253 'edit.php' == $pagenow
217 - && isset( $_GET['type'] )
254 + && isset($_GET['type'])
218 255 && $_GET['type'] != ''
219 256 && $_GET['type'] != 'all'
220 257 ) {
221 - $query->query_vars['meta_key'] = Meta::TEMPLATE_TYPE;
222 - $query->query_vars['meta_value'] = sanitize_key( $_GET['type'] );
223 - $query->query_vars['meta_compare'] = '=';
258 + $requested_type = sanitize_text_field(wp_unslash($_GET['type']));
259 +
260 + if (Builder_Template_Helper::getTemplateByIndex($requested_type)) {
261 + $query->query_vars['meta_key'] = Meta::TEMPLATE_TYPE;
262 + $query->query_vars['meta_value'] = $requested_type;
263 + $query->query_vars['meta_compare'] = '=';
264 + }
224 265 }
266 + // phpcs:enable WordPress.Security.NonceVerification.Recommended, WordPress.DB.SlowDBQuery
225 267 }
226 268
227 269
228 270 public function create_builder_template() {
229 - parse_str( $_POST['data'], $data );
230 271
231 - $templateId = isset( $data['template_id'] ) ? trim( $data['template_id'] ) : '';
232 - $name = isset( $data['template_name'] ) ? trim( $data['template_name'] ) : '';
233 - $type = isset( $data['template_type'] ) ? trim( $data['template_type'] ) : '';
234 - $editWith = isset( $data['edit_with'] ) ? trim( $data['edit_with'] ) : 'elementor'; //gutenberg
235 - $isEnabled = ( isset( $data['template_status'] ) && $data['template_status'] ) == 1 ? 1 : 0;
272 + if (! current_user_can('manage_options')) {
273 + wp_send_json_error(['success' => false, 'errors_arr' => ['permission' => 'Permission denied']], 403);
274 + }
236 275
276 + $data = [];
277 + // The form arrives as one serialized string, so it is unslashed before parsing
278 + // and every field is sanitized individually below — parse_str itself does not
279 + // sanitize anything.
280 + if (isset($_POST['data'])) {
281 + parse_str(wp_unslash($_POST['data']), $data); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Serialized form string; every parsed field is sanitized individually below.
282 + }
283 +
284 + $nonce = isset($data['nonce']) ? sanitize_text_field($data['nonce']) : '';
285 +
286 + if (! wp_verify_nonce($nonce, 'usk-builder')) {
287 + wp_send_json_error(['success' => false, 'errors_arr' => ['nonce' => 'Invalid nonce']], 403);
288 + }
289 +
290 + $templateId = isset($data['template_id']) ? absint($data['template_id']) : 0;
291 + $name = isset($data['template_name']) ? sanitize_text_field($data['template_name']) : '';
292 + $type = isset($data['template_type']) ? sanitize_text_field($data['template_type']) : '';
293 + $editWith = isset($data['edit_with']) ? sanitize_key($data['edit_with']) : 'elementor'; //gutenberg
294 + $isEnabled = (isset($data['template_status']) && $data['template_status']) == 1 ? 1 : 0;
295 +
296 + // Only these two editors are ever handled below, and the value ends up in a
297 + // redirect URL, so anything else falls back to the default.
298 + if (! in_array($editWith, ['elementor', 'gutenberg'], true)) {
299 + $editWith = 'elementor';
300 + }
301 +
237 302 $errors = [];
238 303
239 - if ( empty( $name ) ) {
304 + if (empty($name)) {
240 305 $errors['template_name'] = 'Field is required';
241 306 }
242 307
243 - if ( empty( $type ) ) {
308 + if (empty($type)) {
244 309 $errors['template_type'] = 'Field is required';
245 310 } else {
246 - if ( ! Builder_Template_Helper::getTemplateByIndex( $type ) ) {
311 + if (! Builder_Template_Helper::getTemplateByIndex($type)) {
247 312 $errors['template_type'] = 'Invalid section';
248 313 }
249 314 }
250 315
251 316
252 - if ( count( $errors ) > 0 ) {
253 - wp_send_json_error( [ 'success' => false, 'errors_arr' => $errors ], 422 );
317 + if (count($errors) > 0) {
318 + wp_send_json_error(['success' => false, 'errors_arr' => $errors], 422);
254 319 }
255 320
256 - $page_data = [
321 + $page_data = [
257 322 'post_status' => 'publish',
258 323 'post_type' => Meta::POST_TYPE,
259 324 'post_author' => get_current_user_id(),
260 325 'post_title' => $name,
261 326 'comment_status' => 'closed',
262 - 'meta_input' => [
327 + 'meta_input' => [
263 328 Meta::EDIT_WITH => $editWith,
264 329 Meta::TEMPLATE_TYPE => $type,
265 330 ],
266 331 ];
@@ -268,86 +333,104 @@
268 333 global $wp_rewrite;
269 334 $wp_rewrite->flush_rules();
270 335 $wp_rewrite->init();
271 336
272 - if ( $templateId ) {
337 + if ($templateId) {
273 338 $page_data['ID'] = $templateId;
274 339 }
275 340
276 - $post_id = wp_insert_post( $page_data );
341 + $post_id = wp_insert_post($page_data);
277 342
278 - $enabledTemplate = strtolower( Meta::TEMPLATE_ID . $type );
279 - if ( $isEnabled == 1 ) {
280 - update_option( $enabledTemplate, $post_id );
343 + if ($isEnabled == 1) {
344 + Meta::update_template_option($type, $post_id);
281 345 } else {
282 - if ( get_option( $enabledTemplate ) == $post_id ) {
283 - delete_option( $enabledTemplate );
346 + if (Meta::get_template_option($type) == $post_id) {
347 + Meta::delete_template_option($type);
284 348 }
285 349 }
286 350
287 351
288 - if ( $editWith == 'elementor' ) {
289 - if ( ! get_post_meta( $post_id, '_elementor_data' ) ) {
290 - update_post_meta( $post_id, '_elementor_data', [] );
352 + if ($editWith == 'elementor') {
353 + if (! get_post_meta($post_id, '_elementor_data')) {
354 + update_post_meta($post_id, '_elementor_data', []);
291 355 }
292 356
293 - if ( ! $templateId ) {
294 - update_post_meta( $post_id, '_elementor_data', [] );
357 + if (! $templateId) {
358 + update_post_meta($post_id, '_elementor_data', []);
295 359 }
296 - update_post_meta( $post_id, '_wp_page_template', 'elementor_header_footer' );
297 - update_post_meta( $post_id, '_elementor_edit_mode', 'builder' );
298 - update_post_meta( $post_id, '_elementor_version', '3.7.1' );
360 + update_post_meta($post_id, '_wp_page_template', 'elementor_header_footer');
361 + update_post_meta($post_id, '_elementor_edit_mode', 'builder');
362 + update_post_meta($post_id, '_elementor_version', '3.7.1');
299 363 }
300 364
301 - if ( $templateId ) {
365 + if ($templateId) {
302 366 $url = add_query_arg(
303 - [ 'post_type' => Meta::POST_TYPE ],
304 - admin_url( 'edit.php' )
367 + ['post_type' => Meta::POST_TYPE],
368 + admin_url('edit.php')
305 369 );
306 370 } else {
307 - $url = add_query_arg( [
371 + $url = add_query_arg([
308 372 'post' => $post_id,
309 373 'action' => $editWith,
310 - ], admin_url( 'post.php' ) );
374 + ], admin_url('post.php'));
311 375 }
312 376
313 - wp_send_json_success( [ 'success' => true, 'redirect' => $url ] );
377 + wp_send_json_success(['success' => true, 'redirect' => $url]);
314 378 }
315 379
316 380 public function get_builder_template_action() {
317 - if ( isset( $_REQUEST['template_id'] ) && ! empty( $_REQUEST['template_id'] ) ) {
318 - $templateId = $_REQUEST['template_id'];
319 - $templateData = get_post( $templateId );
320 381
321 - if ( $templateData ) {
322 - $meta = get_post_meta( $templateData->ID );
382 + if (! current_user_can('manage_options')) {
383 + wp_send_json_error(['success' => false, 'errors_arr' => ['permission' => 'Permission denied']], 403);
384 + }
323 385
386 + $nonce = isset($_REQUEST['nonce']) ? sanitize_text_field(wp_unslash($_REQUEST['nonce'])) : '';
324 387
325 - $templateType = isset( $meta[ Meta::TEMPLATE_TYPE ][0] ) ? $meta[ Meta::TEMPLATE_TYPE ][0] : '';
326 - $enabledTemplate = strtolower( Meta::TEMPLATE_ID . $templateType );
327 - $enabledTemplate = get_option( $enabledTemplate );
388 + if (! wp_verify_nonce($nonce, 'ultimate_store_kit_builder_nonce')) {
389 + wp_send_json_error(['success' => false, 'errors_arr' => ['nonce' => 'Invalid nonce']], 403);
390 + }
328 391
329 - wp_send_json_success( [
392 + if (isset($_REQUEST['template_id']) && ! empty($_REQUEST['template_id'])) {
393 + $templateId = absint($_REQUEST['template_id']);
394 + $templateData = get_post($templateId);
395 +
396 + if ($templateData && $templateData->post_type === Meta::POST_TYPE) {
397 + $meta = get_post_meta($templateData->ID);
398 +
399 +
400 + $templateType = isset($meta[Meta::TEMPLATE_TYPE][0]) ? $meta[Meta::TEMPLATE_TYPE][0] : '';
401 + $enabledTemplate = Meta::get_template_option($templateType);
402 +
403 + wp_send_json_success([
330 404 'id' => $templateData->ID,
331 405 'name' => $templateData->post_title,
332 406 'type' => $templateType,
333 - 'status' => is_numeric( $enabledTemplate ) ? 1 : 0,
334 - ] );
407 + 'status' => is_numeric($enabledTemplate) ? 1 : 0,
408 + ]);
335 409 }
336 410 }
411 +
412 + wp_send_json_error(['success' => false, 'errors_arr' => ['template' => 'Template not found']], 404);
337 413 }
338 414
339 - public function add_modal_html( $hook_suffix ) {
415 + public function add_modal_html($hook_suffix) {
340 416 require_once 'modal/modal.php';
341 417 }
342 418
343 - public function enqueue_scripts( $hook_suffix ) {
344 - if ( in_array( $hook_suffix, [ 'edit.php', 'post-new.php' ] ) ) {
419 + public function enqueue_scripts($hook_suffix) {
420 + if (in_array($hook_suffix, ['edit.php', 'post-new.php'])) {
345 421 $screen = get_current_screen();
346 422
347 - if ( is_object( $screen ) && Meta::POST_TYPE == $screen->post_type ) {
348 - wp_enqueue_style( 'ultimate-store-kit-builder', BDTUSK_ADM_ASSETS_URL . 'css/usk-ultimate-builder.css', [], BDTUSK_VER );
349 - wp_enqueue_script( 'ultimate-store-kit-builder', BDTUSK_ADM_ASSETS_URL . 'js/ultimate-builder.min.js', [ 'jquery' ], BDTUSK_VER );
423 + if (is_object($screen) && Meta::POST_TYPE == $screen->post_type) {
424 + wp_enqueue_style('ultimate-store-kit-builder', BDTUSK_ASSETS_URL . 'admin/others/css/ultimate-builder.css', [], BDTUSK_VER);
425 + wp_enqueue_script('ultimate-store-kit-builder', BDTUSK_ASSETS_URL . 'admin/others/js/ultimate-builder.js', ['jquery', 'wp-i18n'], BDTUSK_VER, true);
426 + wp_set_script_translations('ultimate-store-kit-builder', 'ultimate-store-kit');
427 +
428 + wp_localize_script('ultimate-store-kit-builder', 'UltimateStoreKitConfigBuilder', [
429 + 'ajaxurl' => admin_url('admin-ajax.php'),
430 + 'nonce' => wp_create_nonce('ultimate_store_kit_builder_nonce'),
431 + 'resturl' => rest_url('usk/v1/'),
432 + ]);
350 433 }
351 434 }
352 435 }
353 436
@@ -353,37 +436,37 @@
353 436
354 437
355 438 public function add_admin_menu() {
356 439 add_submenu_page(
357 - 'ultimate_store_kit_options',
358 - esc_html__( 'Template Builder', 'ultimate-store-kit' ),
359 - esc_html__( 'Template Builder', 'ultimate-store-kit' ),
360 - 'edit_pages',
440 + 'ultimate-store-kit',
441 + esc_html__('Template Builder', 'ultimate-store-kit'),
442 + esc_html__('Template Builder', 'ultimate-store-kit'),
443 + 'manage_options',
361 444 'edit.php?post_type=' . Meta::POST_TYPE
362 445 );
363 446 }
364 447
365 448 public function registered_post_type() {
366 - $labels = [
367 - 'name' => _x( 'Template Items', 'post type general name', 'ultimate-store-kit' ),
368 - 'singular_name' => _x( 'Template Item', 'post type singular name', 'ultimate-store-kit' ),
369 - 'menu_name' => _x( 'Template Manager', 'admin menu', 'ultimate-store-kit' ),
370 - 'name_admin_bar' => _x( 'Template Manager', 'add new on admin bar', 'ultimate-store-kit' ),
371 - 'add_new' => _x( 'Add New', 'template_manager', 'ultimate-store-kit' ),
372 - 'add_new_item' => __( 'Add New Template', 'ultimate-store-kit' ),
373 - 'new_item' => __( 'New Template', 'ultimate-store-kit' ),
374 - 'edit_item' => __( 'Edit Template', 'ultimate-store-kit' ),
375 - 'view_item' => __( 'View Template', 'ultimate-store-kit' ),
376 - 'all_items' => __( 'All Templates', 'ultimate-store-kit' ),
377 - 'search_items' => __( 'Search Templates', 'ultimate-store-kit' ),
378 - 'parent_item_colon' => __( 'Parent Template:', 'ultimate-store-kit' ),
379 - 'not_found' => __( 'No Template found.', 'ultimate-store-kit' ),
380 - 'not_found_in_trash' => __( 'No Template found in Trash.', 'ultimate-store-kit' ),
449 + $labels = [
450 + 'name' => _x('Template Items', 'post type general name', 'ultimate-store-kit'),
451 + 'singular_name' => _x('Template Item', 'post type singular name', 'ultimate-store-kit'),
452 + 'menu_name' => _x('Template Manager', 'admin menu', 'ultimate-store-kit'),
453 + 'name_admin_bar' => _x('Template Manager', 'add new on admin bar', 'ultimate-store-kit'),
454 + 'add_new' => _x('Add New', 'template_manager', 'ultimate-store-kit'),
455 + 'add_new_item' => __('Add New Template', 'ultimate-store-kit'),
456 + 'new_item' => __('New Template', 'ultimate-store-kit'),
457 + 'edit_item' => __('Edit Template', 'ultimate-store-kit'),
458 + 'view_item' => __('View Template', 'ultimate-store-kit'),
459 + 'all_items' => __('All Templates', 'ultimate-store-kit'),
460 + 'search_items' => __('Search Templates', 'ultimate-store-kit'),
461 + 'parent_item_colon' => __('Parent Template:', 'ultimate-store-kit'),
462 + 'not_found' => __('No Template found.', 'ultimate-store-kit'),
463 + 'not_found_in_trash' => __('No Template found in Trash.', 'ultimate-store-kit'),
381 464 ];
382 465
383 - $args = [
466 + $args = [
384 467 'labels' => $labels,
385 - 'description' => __( 'Description.', 'ultimate-store-kit' ),
468 + 'description' => __('Description.', 'ultimate-store-kit'),
386 469 'taxonomies' => [],
387 470 'hierarchical' => false,
388 471 'public' => true,
389 472 'show_in_menu' => false,
@@ -391,9 +474,9 @@
391 474 'show_in_admin_bar' => true,
392 475 'menu_position' => null,
393 476 'menu_icon' => null,
394 477 'publicly_queryable' => true,
395 - 'supports' => [ 'title', 'editor', 'elementor' ],
478 + 'supports' => ['title', 'editor', 'elementor'],
396 479 'exclude_from_search' => true,
397 480 'has_archive' => false,
398 481 'query_var' => true,
399 482 'can_export' => true,
@@ -403,9 +486,31 @@
403 486 // 'rest_base' => $this->getPostType(),
404 487 // 'register_meta_box_cb' => [ $this, 'register_meta_box_cb' ],
405 488 ];
406 489
407 - register_post_type( Meta::POST_TYPE, $args );
490 + register_post_type(Meta::POST_TYPE, $args);
491 +
492 + // Fix WPML integration with Elementor
493 + if (function_exists('icl_object_id')) {
494 + add_filter('wpml_pb_elementor_get_data', [$this, 'fix_wpml_elementor_data'], 10, 2);
495 + }
496 + }
497 +
498 + /**
499 + * Simple fix for WPML and Elementor integration
500 + */
501 + public function fix_wpml_elementor_data() {
502 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reads the post being edited so WPML/Elementor metadata can be normalised.
503 + $post_id = isset($_REQUEST['post']) ? absint($_REQUEST['post']) : 0;
504 +
505 + if ($post_id && get_post_type($post_id) === Meta::POST_TYPE) {
506 + $meta_data = get_post_meta($post_id, '_elementor_data', true);
507 +
508 + // If metadata exists but is in array format, convert it to JSON string
509 + if (is_array($meta_data)) {
510 + update_post_meta($post_id, '_elementor_data', wp_json_encode($meta_data));
511 + }
512 + }
408 513 }
409 514 }
410 515
411 516 Builder_Cpt::instance()->init_hooks();