resetTemplateCache();
}
public function resetTemplateCache() {
global $wpdb;
$postType = Meta::POST_TYPE;
// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Rebuilds the template cache; this is the call that populates it.
$query = $wpdb->get_results($wpdb->prepare(
"SELECT {$wpdb->posts}.ID, {$wpdb->posts}.post_type, {$wpdb->posts}.post_status, {$wpdb->postmeta}.meta_value as template_type
FROM {$wpdb->posts}
LEFT JOIN {$wpdb->postmeta}
ON {$wpdb->postmeta}.post_id = {$wpdb->posts}.ID
WHERE 1=1
AND {$wpdb->posts}.post_type = %s
AND {$wpdb->postmeta}.meta_key = %s
ORDER BY {$wpdb->posts}.post_date DESC",
$postType,
'_ultimate_store_kit_template_type'
));
// phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
foreach ($query as $q) {
if (! $q->template_type) {
continue;
}
if ($q->post_status == 'publish') {
Meta::update_template_option($q->template_type, $q->ID);
} else {
Meta::delete_template_option($q->template_type);
}
}
}
public function trashed_or_delete_post($postId) {
if (get_post_type($postId) != Meta::POST_TYPE) {
return;
}
if ($template = get_post_meta($postId, Meta::TEMPLATE_TYPE, true)) {
Meta::delete_template_option($template);
}
}
public function post_row_actions_filter($actions, $post) {
global $typenow;
if ($typenow !== Meta::POST_TYPE) {
return $actions;
}
if (isset($actions['edit_with_elementor'])) {
unset($actions['edit_with_elementor']);
}
if (get_post_meta($post->ID, Meta::EDIT_WITH, true) == 'gutenberg') {
$actions['usk_edit_with_gutenberg'] = sprintf(
'%2$s',
add_query_arg(['post' => $post->ID, 'action' => 'edit'], admin_url('post.php')),
esc_html__('Edit with Gutenberg', 'ultimate-store-kit')
);
}
if (get_post_meta($post->ID, Meta::EDIT_WITH, true) == 'elementor') {
$actions['usk_edit_with_elementor'] = sprintf(
'%2$s',
add_query_arg(
[
'post' => $post->ID,
'action' => 'elementor',
'usk-template' => 1
],
admin_url('post.php')
),
esc_html__('Edit with Elementor', 'ultimate-store-kit')
);
} else {
// Always offer Elementor edit option regardless of stored preference
$actions['usk_edit_with_elementor'] = sprintf(
'%2$s',
add_query_arg(
[
'post' => $post->ID,
'action' => 'elementor',
'usk-template' => 1
],
admin_url('post.php')
),
esc_html__('Edit with Elementor', 'ultimate-store-kit')
);
}
$editActionLink = sprintf(
'%3$s',
'javascript:void(0)',
$post->ID,
esc_html__('Edit', 'ultimate-store-kit')
);
if (isset($actions['edit']) && apply_filters('ultimate_store_kit_remove_action_edit_link', __return_true())) {
unset($actions['edit']);
}
return array_slice($actions, 0, 1, true) + ['usk-edit-action' => $editActionLink] + array_slice($actions, 1, null, true);
}
public function set_post_columns($columns) {
return array_slice($columns, 0, 2, true) + [
'template_type' => esc_html__('Type', 'ultimate-store-kit'),
'is_enabled' => esc_html__('Status', 'ultimate-store-kit')
] + array_slice($columns, 2, null, true);
}
public function set_custom_column_value($column, $post_id) {
$templateType = get_post_meta(
$post_id,
Meta::TEMPLATE_TYPE,
true
);
switch ($column) {
case 'template_type':
$postType = Builder_Template_Helper::getTemplatePostTypeByIndex($templateType);
echo esc_html(Builder_Template_Helper::getTemplateByIndex($templateType));
if (isset($postType->name)) {
echo ' -- ' . esc_html(ucwords($postType->name)) . '';
}
break;
case 'is_enabled':
echo Builder_Template_Helper::getTemplateId($templateType) == $post_id
? esc_html__('Active', 'ultimate-store-kit')
: esc_html__('Inactive', 'ultimate-store-kit');
break;
}
}
public function add_filter() {
global $typenow;
if ($typenow !== Meta::POST_TYPE) {
return;
}
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list-table filter; the value only re-selects the current dropdown option.
$selected = isset($_GET['type']) ? sanitize_text_field(wp_unslash($_GET['type'])) : '';
?>
query_vars['meta_key'] = Meta::TEMPLATE_TYPE;
$query->query_vars['meta_value'] = $requested_type;
$query->query_vars['meta_compare'] = '=';
}
}
// phpcs:enable WordPress.Security.NonceVerification.Recommended, WordPress.DB.SlowDBQuery
}
public function create_builder_template() {
if (! current_user_can('manage_options')) {
wp_send_json_error(['success' => false, 'errors_arr' => ['permission' => 'Permission denied']], 403);
}
$data = [];
// The form arrives as one serialized string, so it is unslashed before parsing
// and every field is sanitized individually below — parse_str itself does not
// sanitize anything.
if (isset($_POST['data'])) {
parse_str(wp_unslash($_POST['data']), $data); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Serialized form string; every parsed field is sanitized individually below.
}
$nonce = isset($data['nonce']) ? sanitize_text_field($data['nonce']) : '';
if (! wp_verify_nonce($nonce, 'usk-builder')) {
wp_send_json_error(['success' => false, 'errors_arr' => ['nonce' => 'Invalid nonce']], 403);
}
$templateId = isset($data['template_id']) ? absint($data['template_id']) : 0;
$name = isset($data['template_name']) ? sanitize_text_field($data['template_name']) : '';
$type = isset($data['template_type']) ? sanitize_text_field($data['template_type']) : '';
$editWith = isset($data['edit_with']) ? sanitize_key($data['edit_with']) : 'elementor'; //gutenberg
$isEnabled = (isset($data['template_status']) && $data['template_status']) == 1 ? 1 : 0;
// Only these two editors are ever handled below, and the value ends up in a
// redirect URL, so anything else falls back to the default.
if (! in_array($editWith, ['elementor', 'gutenberg'], true)) {
$editWith = 'elementor';
}
$errors = [];
if (empty($name)) {
$errors['template_name'] = 'Field is required';
}
if (empty($type)) {
$errors['template_type'] = 'Field is required';
} else {
if (! Builder_Template_Helper::getTemplateByIndex($type)) {
$errors['template_type'] = 'Invalid section';
}
}
if (count($errors) > 0) {
wp_send_json_error(['success' => false, 'errors_arr' => $errors], 422);
}
$page_data = [
'post_status' => 'publish',
'post_type' => Meta::POST_TYPE,
'post_author' => get_current_user_id(),
'post_title' => $name,
'comment_status' => 'closed',
'meta_input' => [
Meta::EDIT_WITH => $editWith,
Meta::TEMPLATE_TYPE => $type,
],
];
global $wp_rewrite;
$wp_rewrite->flush_rules();
$wp_rewrite->init();
if ($templateId) {
$page_data['ID'] = $templateId;
}
$post_id = wp_insert_post($page_data);
if ($isEnabled == 1) {
Meta::update_template_option($type, $post_id);
} else {
if (Meta::get_template_option($type) == $post_id) {
Meta::delete_template_option($type);
}
}
if ($editWith == 'elementor') {
if (! get_post_meta($post_id, '_elementor_data')) {
update_post_meta($post_id, '_elementor_data', []);
}
if (! $templateId) {
update_post_meta($post_id, '_elementor_data', []);
}
update_post_meta($post_id, '_wp_page_template', 'elementor_header_footer');
update_post_meta($post_id, '_elementor_edit_mode', 'builder');
update_post_meta($post_id, '_elementor_version', '3.7.1');
}
if ($templateId) {
$url = add_query_arg(
['post_type' => Meta::POST_TYPE],
admin_url('edit.php')
);
} else {
$url = add_query_arg([
'post' => $post_id,
'action' => $editWith,
], admin_url('post.php'));
}
wp_send_json_success(['success' => true, 'redirect' => $url]);
}
public function get_builder_template_action() {
if (! current_user_can('manage_options')) {
wp_send_json_error(['success' => false, 'errors_arr' => ['permission' => 'Permission denied']], 403);
}
$nonce = isset($_REQUEST['nonce']) ? sanitize_text_field(wp_unslash($_REQUEST['nonce'])) : '';
if (! wp_verify_nonce($nonce, 'ultimate_store_kit_builder_nonce')) {
wp_send_json_error(['success' => false, 'errors_arr' => ['nonce' => 'Invalid nonce']], 403);
}
if (isset($_REQUEST['template_id']) && ! empty($_REQUEST['template_id'])) {
$templateId = absint($_REQUEST['template_id']);
$templateData = get_post($templateId);
if ($templateData && $templateData->post_type === Meta::POST_TYPE) {
$meta = get_post_meta($templateData->ID);
$templateType = isset($meta[Meta::TEMPLATE_TYPE][0]) ? $meta[Meta::TEMPLATE_TYPE][0] : '';
$enabledTemplate = Meta::get_template_option($templateType);
wp_send_json_success([
'id' => $templateData->ID,
'name' => $templateData->post_title,
'type' => $templateType,
'status' => is_numeric($enabledTemplate) ? 1 : 0,
]);
}
}
wp_send_json_error(['success' => false, 'errors_arr' => ['template' => 'Template not found']], 404);
}
public function add_modal_html($hook_suffix) {
require_once 'modal/modal.php';
}
public function enqueue_scripts($hook_suffix) {
if (in_array($hook_suffix, ['edit.php', 'post-new.php'])) {
$screen = get_current_screen();
if (is_object($screen) && Meta::POST_TYPE == $screen->post_type) {
wp_enqueue_style('ultimate-store-kit-builder', BDTUSK_ASSETS_URL . 'admin/others/css/ultimate-builder.css', [], BDTUSK_VER);
wp_enqueue_script('ultimate-store-kit-builder', BDTUSK_ASSETS_URL . 'admin/others/js/ultimate-builder.js', ['jquery', 'wp-i18n'], BDTUSK_VER, true);
wp_set_script_translations('ultimate-store-kit-builder', 'ultimate-store-kit');
wp_localize_script('ultimate-store-kit-builder', 'UltimateStoreKitConfigBuilder', [
'ajaxurl' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('ultimate_store_kit_builder_nonce'),
'resturl' => rest_url('usk/v1/'),
]);
}
}
}
public function add_admin_menu() {
add_submenu_page(
'ultimate-store-kit',
esc_html__('Template Builder', 'ultimate-store-kit'),
esc_html__('Template Builder', 'ultimate-store-kit'),
'manage_options',
'edit.php?post_type=' . Meta::POST_TYPE
);
}
public function registered_post_type() {
$labels = [
'name' => _x('Template Items', 'post type general name', 'ultimate-store-kit'),
'singular_name' => _x('Template Item', 'post type singular name', 'ultimate-store-kit'),
'menu_name' => _x('Template Manager', 'admin menu', 'ultimate-store-kit'),
'name_admin_bar' => _x('Template Manager', 'add new on admin bar', 'ultimate-store-kit'),
'add_new' => _x('Add New', 'template_manager', 'ultimate-store-kit'),
'add_new_item' => __('Add New Template', 'ultimate-store-kit'),
'new_item' => __('New Template', 'ultimate-store-kit'),
'edit_item' => __('Edit Template', 'ultimate-store-kit'),
'view_item' => __('View Template', 'ultimate-store-kit'),
'all_items' => __('All Templates', 'ultimate-store-kit'),
'search_items' => __('Search Templates', 'ultimate-store-kit'),
'parent_item_colon' => __('Parent Template:', 'ultimate-store-kit'),
'not_found' => __('No Template found.', 'ultimate-store-kit'),
'not_found_in_trash' => __('No Template found in Trash.', 'ultimate-store-kit'),
];
$args = [
'labels' => $labels,
'description' => __('Description.', 'ultimate-store-kit'),
'taxonomies' => [],
'hierarchical' => false,
'public' => true,
'show_in_menu' => false,
'show_ui' => true,
'show_in_admin_bar' => true,
'menu_position' => null,
'menu_icon' => null,
'publicly_queryable' => true,
'supports' => ['title', 'editor', 'elementor'],
'exclude_from_search' => true,
'has_archive' => false,
'query_var' => true,
'can_export' => true,
'rewrite' => false,
'show_in_nav_menus' => false,
'capability_type' => 'post',
// 'rest_base' => $this->getPostType(),
// 'register_meta_box_cb' => [ $this, 'register_meta_box_cb' ],
];
register_post_type(Meta::POST_TYPE, $args);
// Fix WPML integration with Elementor
if (function_exists('icl_object_id')) {
add_filter('wpml_pb_elementor_get_data', [$this, 'fix_wpml_elementor_data'], 10, 2);
}
}
/**
* Simple fix for WPML and Elementor integration
*/
public function fix_wpml_elementor_data() {
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reads the post being edited so WPML/Elementor metadata can be normalised.
$post_id = isset($_REQUEST['post']) ? absint($_REQUEST['post']) : 0;
if ($post_id && get_post_type($post_id) === Meta::POST_TYPE) {
$meta_data = get_post_meta($post_id, '_elementor_data', true);
// If metadata exists but is in array format, convert it to JSON string
if (is_array($meta_data)) {
update_post_meta($post_id, '_elementor_data', wp_json_encode($meta_data));
}
}
}
}
Builder_Cpt::instance()->init_hooks();