Commit realizado el 12:13:52 08-04-2024
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
/**
|
||||
* Theme Options library.
|
||||
*
|
||||
* Registers post types to be used in the "Theme Options" library.
|
||||
*
|
||||
* @package Divi
|
||||
* @subpackage Cloud
|
||||
* @since ??
|
||||
*/
|
||||
|
||||
/**
|
||||
* Core class used to implement "Theme Options" library.
|
||||
*
|
||||
* Register post types & taxonomies to be used in "Theme Options" library.
|
||||
*/
|
||||
class ET_Builder_Theme_Options_Library {
|
||||
|
||||
/**
|
||||
* Instance of `ET_Builder_Theme_Options_Library`.
|
||||
*
|
||||
* @var ET_Builder_Theme_Options_Library
|
||||
*/
|
||||
private static $_instance;
|
||||
|
||||
/**
|
||||
* Instance of`ET_Core_Data_Utils`.
|
||||
*
|
||||
* @var ET_Core_Data_Utils
|
||||
*/
|
||||
protected static $_;
|
||||
|
||||
/**
|
||||
* List of i18n strings.
|
||||
*
|
||||
* @var mixed[]
|
||||
*/
|
||||
protected static $_i18n;
|
||||
|
||||
/**
|
||||
* ET_Builder_Post_Taxonomy_LayoutCategory instance.
|
||||
*
|
||||
* Shall be used for querying `et_theme_options` taxonomy.
|
||||
*
|
||||
* @var ET_Builder_Post_Taxonomy_LayoutCategory
|
||||
*/
|
||||
public $theme_options_categories;
|
||||
|
||||
/**
|
||||
* ET_Builder_Post_Taxonomy_LayoutTag instance.
|
||||
*
|
||||
* Shall be used for querying `et_theme_options` taxonomy .
|
||||
*
|
||||
* @var ET_Builder_Post_Taxonomy_LayoutTag
|
||||
*/
|
||||
public $theme_options_tags;
|
||||
|
||||
/**
|
||||
* ET_Builder_Post_Type_TBItem instance.
|
||||
*
|
||||
* Shall be used for querying `et_tb_item` posts .
|
||||
*
|
||||
* @var ET_Builder_Post_Type_TBItem
|
||||
*/
|
||||
public $theme_options;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->_instance_check();
|
||||
$this->_register_cpt_and_taxonomies();
|
||||
}
|
||||
|
||||
/**
|
||||
* Dies if an instance already exists.
|
||||
*/
|
||||
protected function _instance_check() {
|
||||
if ( self::$_instance ) {
|
||||
et_error( 'Multiple instances are not allowed!' );
|
||||
wp_die();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the Theme Options Library's custom post type and its taxonomies.
|
||||
*/
|
||||
protected function _register_cpt_and_taxonomies() {
|
||||
$files = [
|
||||
ET_THEME_OPTIONS_DIR . 'post/type/ThemeOptions.php',
|
||||
];
|
||||
|
||||
if ( ! $files ) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ( $files as $file ) {
|
||||
require_once $file;
|
||||
}
|
||||
|
||||
$this->theme_options = ET_Post_Type_Theme_Options::instance();
|
||||
$this->theme_options_categories = ET_Builder_Post_Taxonomy_LayoutCategory::instance();
|
||||
$this->theme_options_tags = ET_Builder_Post_Taxonomy_LayoutTag::instance();
|
||||
|
||||
// We manually call register_all() now to ensure the CPT and taxonomies are registered
|
||||
// at exactly the same point during the request that they were in prior releases.
|
||||
ET_Builder_Post_Type_Layout::register_all( 'builder' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ET_Builder_Theme_Options_Library instance.
|
||||
*
|
||||
* @return ET_Builder_Theme_Options_Library
|
||||
*/
|
||||
public static function instance() {
|
||||
if ( ! self::$_instance ) {
|
||||
self::$_instance = new self();
|
||||
}
|
||||
|
||||
return self::$_instance;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ET_Builder_Theme_Options_Library::instance();
|
294
wp-content/themes/Divi/epanel/theme-options-library/api.php
Normal file
294
wp-content/themes/Divi/epanel/theme-options-library/api.php
Normal file
@@ -0,0 +1,294 @@
|
||||
<?php
|
||||
/**
|
||||
* Theme Options Library API.
|
||||
*
|
||||
* @package Divi
|
||||
*/
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves Theme Options Library items.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function et_theme_options_library_get_items() {
|
||||
et_core_security_check( et_core_portability_cap( 'epanel' ), 'et_theme_options_library_get_items', 'nonce' );
|
||||
|
||||
$context = isset( $_POST['context'] )
|
||||
? sanitize_text_field( $_POST['context'] )
|
||||
: '';
|
||||
|
||||
if ( '' === $context ) {
|
||||
wp_send_json_error( 'missing_context' );
|
||||
}
|
||||
|
||||
$item_library_local = et_pb_theme_options_library_local();
|
||||
$data = $item_library_local->get_library_items( $context );
|
||||
|
||||
wp_send_json_success( $data );
|
||||
}
|
||||
|
||||
add_action( 'wp_ajax_et_theme_options_library_get_items', 'et_theme_options_library_get_items' );
|
||||
|
||||
/**
|
||||
* Update Terms.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function et_theme_options_library_update_terms() {
|
||||
et_core_security_check( 'manage_categories', 'et_theme_options_library_update_terms', 'nonce' );
|
||||
|
||||
$payload = isset( $_POST['payload'] ) ? (array) $_POST['payload'] : array(); // phpcs:ignore ET.Sniffs.ValidatedSanitizedInput -- $_POST['payload'] is an array, it's value sanitization is done at the time of accessing value.
|
||||
|
||||
if ( empty( $payload ) ) {
|
||||
wp_send_json_error( 'Payload is empty.' );
|
||||
}
|
||||
|
||||
$item_library_local = et_pb_theme_options_library_local();
|
||||
$response = $item_library_local->perform_terms_update( $payload );
|
||||
|
||||
wp_send_json_success( $response );
|
||||
}
|
||||
|
||||
add_action( 'wp_ajax_et_theme_options_library_update_terms', 'et_theme_options_library_update_terms' );
|
||||
|
||||
/**
|
||||
* Export the Theme Options library item.
|
||||
* This function only retrieves the data.
|
||||
* All the permissions checks should be performed at the top level function which calls this one.
|
||||
*
|
||||
* @since 4.19.0
|
||||
*
|
||||
* @param int $id Item ID.
|
||||
* @param array $cloud_content Optional cloud content.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function et_theme_options_library_export_item_data( $id, $cloud_content ) {
|
||||
if ( empty( $cloud_content ) ) {
|
||||
if ( empty( $id ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$id = absint( $id );
|
||||
$post = get_post( $id );
|
||||
$export_content = $post->post_content;
|
||||
$export_content = json_decode( $export_content );
|
||||
} else {
|
||||
$export_content = $cloud_content;
|
||||
}
|
||||
|
||||
if ( empty( $export_content ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$transient = 'et_theme_options_export_' . get_current_user_id() . '_' . $id;
|
||||
set_transient( $transient, $export_content, 60 * 60 * 24 );
|
||||
|
||||
return $export_content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Export Theme options Library item.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function et_theme_options_library_export_item() {
|
||||
et_core_security_check( et_core_portability_cap( 'epanel' ), 'et_theme_options_library_export_item', 'nonce' );
|
||||
|
||||
$post_id = isset( $_POST['id'] ) ? absint( $_POST['id'] ) : 0;
|
||||
$cloud_content = isset( $_POST['cloudContent'] ) ? $_POST['cloudContent'] : ''; // phpcs:ignore ET.Sniffs.ValidatedSanitizedInput -- $_POST['cloudContent'] is an array, it's value sanitization is done at the time of accessing value.
|
||||
|
||||
$post_type = get_post_type( $post_id );
|
||||
|
||||
// When exporting cloud content this check doesn't make sense as we already have data.
|
||||
if ( empty( $cloud_content ) && ( ! current_user_can( 'edit_post', $post_id ) || ET_THEME_OPTIONS_POST_TYPE !== $post_type ) ) {
|
||||
wp_send_json_error( 'You do not have permission.' );
|
||||
}
|
||||
|
||||
$response = et_theme_options_library_export_item_data( $post_id, $cloud_content );
|
||||
|
||||
if ( ! $response ) {
|
||||
wp_send_json_error( 'Error: Wrong data provided.' );
|
||||
}
|
||||
|
||||
wp_send_json_success( $response );
|
||||
}
|
||||
|
||||
add_action( 'wp_ajax_et_theme_options_library_export_item', 'et_theme_options_library_export_item' );
|
||||
|
||||
/**
|
||||
* Download exported Theme options Library item.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function et_theme_options_library_export_item_download() {
|
||||
et_core_security_check( et_core_portability_cap( 'epanel' ), 'et_theme_options_library_export_item', 'nonce', '_GET' );
|
||||
|
||||
$id = ! empty( $_GET['id'] ) ? absint( $_GET['id'] ) : 0;
|
||||
$file_name = empty( $_GET['fileName'] ) ? 'Theme Options' : sanitize_file_name( $_GET['fileName'] );
|
||||
|
||||
header( 'Content-Description: File Transfer' );
|
||||
header( 'Content-Disposition: attachment; filename="' . $file_name . '.json"' );
|
||||
header( 'Content-Type: application/json' );
|
||||
header( 'Pragma: no-cache' );
|
||||
|
||||
$transient = 'et_theme_options_export_' . get_current_user_id() . '_' . $id;
|
||||
$export_content = get_transient( $transient );
|
||||
|
||||
delete_transient( $transient );
|
||||
|
||||
echo wp_json_encode( $export_content );
|
||||
|
||||
wp_die();
|
||||
}
|
||||
|
||||
add_action( 'wp_ajax_et_theme_options_library_export_item_download', 'et_theme_options_library_export_item_download' );
|
||||
|
||||
/**
|
||||
* Update theme options Library item.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function et_theme_options_library_update_item() {
|
||||
et_core_security_check( et_core_portability_cap( 'epanel' ), 'et_theme_options_library_update_item', 'nonce' );
|
||||
|
||||
$payload = isset( $_POST['payload'] ) ? $_POST['payload'] : array(); // phpcs:ignore ET.Sniffs.ValidatedSanitizedInput -- $_POST['payload'] is an array, it's value sanitization is done at the time of accessing value.
|
||||
|
||||
if ( empty( $payload ) ) {
|
||||
wp_send_json_error( 'Payload is empty.' );
|
||||
}
|
||||
|
||||
$item_library_local = et_pb_theme_options_library_local();
|
||||
$response = $item_library_local->perform_item_update( $payload );
|
||||
|
||||
if ( ! $response ) {
|
||||
wp_send_json_error( 'Error: Wrong data provided.' );
|
||||
}
|
||||
|
||||
wp_send_json_success( $response );
|
||||
}
|
||||
|
||||
add_action( 'wp_ajax_et_theme_options_library_update_item', 'et_theme_options_library_update_item' );
|
||||
|
||||
/**
|
||||
* Get cloud access token.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function et_theme_options_library_get_token() {
|
||||
et_core_security_check( et_core_portability_cap( 'epanel' ), 'et_theme_options_library_get_token', 'nonce' );
|
||||
|
||||
wp_send_json_success(
|
||||
array( 'accessToken' => get_transient( 'et_cloud_access_token' ) )
|
||||
);
|
||||
}
|
||||
|
||||
add_action( 'wp_ajax_et_theme_options_library_get_token', 'et_theme_options_library_get_token' );
|
||||
|
||||
/**
|
||||
* Get Theme options Library item.
|
||||
*
|
||||
* @since ??
|
||||
* Retrieves theme options library item content.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function et_theme_options_library_get_item_content() {
|
||||
et_core_security_check( et_core_portability_cap( 'epanel' ), 'et_theme_options_library_get_item_content', 'nonce' );
|
||||
|
||||
$id = isset( $_POST['et_theme_option_id'] ) ? (int) sanitize_text_field( $_POST['et_theme_option_id'] ) : 0;
|
||||
|
||||
if ( empty( $id ) ) {
|
||||
wp_send_json_error();
|
||||
}
|
||||
|
||||
$result = array();
|
||||
$post = get_post( $id );
|
||||
|
||||
$post_type = ET_THEME_OPTIONS_POST_TYPE;
|
||||
|
||||
if ( $post_type !== $post->post_type ) {
|
||||
wp_die();
|
||||
}
|
||||
|
||||
$result = [];
|
||||
|
||||
$result['exported'] = json_decode( $post->post_content );
|
||||
|
||||
$response = wp_json_encode(
|
||||
array(
|
||||
'success' => true,
|
||||
'data' => $result,
|
||||
)
|
||||
);
|
||||
|
||||
// Charset has to be explicitly mentioned when it is other than UTF-8.
|
||||
header( 'Content-Type: application/json; charset=' . esc_attr( get_option( 'blog_charset' ) ) );
|
||||
|
||||
die( et_core_intentionally_unescaped( $response, 'html' ) );
|
||||
}
|
||||
|
||||
add_action( 'wp_ajax_et_theme_options_library_get_item_content', 'et_theme_options_library_get_item_content' );
|
||||
|
||||
/**
|
||||
* AJAX Callback: Remove the Library layout after it was moved to the Cloud.
|
||||
*
|
||||
* @since ??
|
||||
*
|
||||
* @global $_POST['payload'] Array with the layout data to remove.
|
||||
*
|
||||
* @return void|string JSON encoded in case of empty payload
|
||||
*/
|
||||
function et_theme_options_toggle_cloud_status() {
|
||||
et_core_security_check( et_core_portability_cap( 'epanel' ), 'et_theme_options_library_toggle_item_location', 'nonce' );
|
||||
|
||||
$post_id = isset( $_POST['et_theme_option_id'] ) ? (int) sanitize_text_field( $_POST['et_theme_option_id'] ) : 0;
|
||||
$post_type = get_post_type( $post_id );
|
||||
|
||||
if ( empty( $post_id ) ) {
|
||||
wp_send_json_error( 'No post ID' );
|
||||
}
|
||||
|
||||
$post_type = get_post_type( $post_id );
|
||||
|
||||
if ( ! current_user_can( 'edit_post', $post_id ) || ET_THEME_OPTIONS_POST_TYPE !== $post_type ) {
|
||||
wp_send_json_error( 'You do not have permission.' );
|
||||
}
|
||||
|
||||
wp_delete_post( $post_id, true );
|
||||
|
||||
$item_library_local = et_pb_theme_options_library_local();
|
||||
|
||||
wp_send_json_success(
|
||||
array(
|
||||
'localLibraryTerms' => [
|
||||
'layout_category' => $item_library_local->get_formatted_library_terms(),
|
||||
'layout_tag' => $item_library_local->get_formatted_library_terms( 'layout_tag' ),
|
||||
],
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
add_action( 'wp_ajax_et_theme_options_toggle_cloud_status', 'et_theme_options_toggle_cloud_status' );
|
||||
|
||||
/**
|
||||
* Delete temporary options library
|
||||
*/
|
||||
function et_theme_options_delete_temp_options() {
|
||||
et_core_security_check( et_core_portability_cap( 'epanel' ), 'et_theme_options_delete_temp_options' );
|
||||
|
||||
$deleted = delete_option( 'et_divi_' . get_current_user_id() );
|
||||
|
||||
if ( $deleted ) {
|
||||
return wp_send_json_success();
|
||||
}
|
||||
|
||||
return wp_send_json_error();
|
||||
}
|
||||
|
||||
add_action( 'wp_ajax_et_theme_options_delete_temp_options', 'et_theme_options_delete_temp_options' );
|
@@ -0,0 +1,76 @@
|
||||
// External Dependencies
|
||||
import React from 'react';
|
||||
import { render, unmountComponentAtNode } from 'react-dom';
|
||||
import $ from 'jquery';
|
||||
import App from 'cerebral';
|
||||
import Devtools from 'cerebral/devtools';
|
||||
import { Container } from '@cerebral/react';
|
||||
|
||||
|
||||
// Internal Dependencies
|
||||
import store from './store/index';
|
||||
import ThemeOptionsApp from './components/App';
|
||||
|
||||
const initialState = {
|
||||
content: '',
|
||||
context: 'theme-options',
|
||||
items: [],
|
||||
showLibrary: false,
|
||||
showPortability: false,
|
||||
showSave: false,
|
||||
};
|
||||
|
||||
const unMountCommonLibraryApp = () => {
|
||||
const container = document.getElementById('et-theme-options-container');
|
||||
if (container) {
|
||||
unmountComponentAtNode(container);
|
||||
container.remove();
|
||||
}
|
||||
}
|
||||
|
||||
// Note: Hyphen is used to stay consistent w/ the Cloud context.
|
||||
$(window).on(`et_theme-options_container_ready`, (event, preferences) => {
|
||||
let devtools = null;
|
||||
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
devtools = Devtools({
|
||||
host: '127.0.0.1:22722',
|
||||
reconnect: false,
|
||||
bigComponentsWarning: 15,
|
||||
});
|
||||
}
|
||||
|
||||
const modalType = preferences?.modalType || '';
|
||||
|
||||
const state = {
|
||||
...initialState,
|
||||
modalType
|
||||
};
|
||||
|
||||
state.sidebarLabel = preferences?.sidebarLabel || '';
|
||||
state.builtFor = preferences?.builtFor ?? 'Divi';
|
||||
|
||||
const app = App(store(state), {
|
||||
devtools,
|
||||
returnSequencePromise: true,
|
||||
});
|
||||
|
||||
const {
|
||||
containerId = 'et-theme-options-container',
|
||||
containerClass = 'et-theme-options-container'
|
||||
} = preferences;
|
||||
|
||||
$(document.body).first().append(`<div id=${containerId} class=${containerClass}></div>`);
|
||||
|
||||
render(
|
||||
<Container app={app}>
|
||||
<ThemeOptionsApp />
|
||||
</Container>,
|
||||
document.getElementById(containerId)
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
$(window).on('et_theme-options_container_close', () => {
|
||||
unMountCommonLibraryApp();
|
||||
});
|
@@ -0,0 +1 @@
|
||||
export default window.et_theme_options_data;
|
@@ -0,0 +1,4 @@
|
||||
// Portability states.
|
||||
export const PORTABILITY_STATE_DEFAULT = 'default';
|
||||
export const PORTABILITY_STATE_EXPORT_THEME_OPTIONS = 'export';
|
||||
export const PORTABILITY_STATE_IMPORT_THEME_OPTIONS = 'import';
|
@@ -0,0 +1,23 @@
|
||||
// External dependencies.
|
||||
import { get } from 'lodash';
|
||||
|
||||
// Internal dependencies.
|
||||
import config from './config';
|
||||
|
||||
|
||||
const i18n = (context, key, ...args) => {
|
||||
const value = get(context, key, '');
|
||||
|
||||
if ('production' !== process.env.NODE_ENV && '' === value) {
|
||||
console.error('Failed to find i18n string:', key);
|
||||
}
|
||||
|
||||
if (args.length > 0) {
|
||||
const sprintf = get(window, 'wp.i18n.sprintf');
|
||||
return sprintf(value, ...args);
|
||||
}
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
export default (path, key, ...args) => i18n(config.i18n, [path, key], ...args);
|
@@ -0,0 +1,26 @@
|
||||
// External dependencies.
|
||||
import $ from 'jquery';
|
||||
|
||||
// Internal dependencies.
|
||||
import config from './config';
|
||||
|
||||
|
||||
export const request = (method, data, options = {}) => {
|
||||
const deferred = $.ajax({
|
||||
type: method,
|
||||
url: config.api,
|
||||
dataType: 'json',
|
||||
data,
|
||||
...options,
|
||||
});
|
||||
|
||||
return Promise.resolve(deferred.promise())
|
||||
.then(response => {
|
||||
if (false === response.success) {
|
||||
return Promise.reject(response.data || {});
|
||||
}
|
||||
return Promise.resolve(response.data);
|
||||
});
|
||||
};
|
||||
|
||||
export const post = (data, options = {}) => request('POST', data, options);
|
@@ -0,0 +1,12 @@
|
||||
// Set global variable to detect new library item creation.
|
||||
window.themeOptionsLibraryItemsLoaded = {};
|
||||
|
||||
export const setThemeOptionsLibraryItemsLoaded = (context, flag) => {
|
||||
window.themeOptionsLibraryItemsLoaded = {
|
||||
[context] : flag,
|
||||
};
|
||||
};
|
||||
|
||||
export const setThemeOptionsLibraryToken = (token) => {
|
||||
window.globalCloudToken = token;
|
||||
};
|
@@ -0,0 +1,81 @@
|
||||
import {
|
||||
noop,
|
||||
trim,
|
||||
set,
|
||||
} from 'lodash';
|
||||
|
||||
import config from '@common-ui/lib/config';
|
||||
import { post } from '@common-ui/lib/request';
|
||||
import { saveToCloudPure } from '@cloud/app/lib/api';
|
||||
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
const saveThemeOptionsToLocal = (item, content) => {
|
||||
const {
|
||||
item_name,
|
||||
selected_cats,
|
||||
new_category_name,
|
||||
new_tag_name,
|
||||
builtFor,
|
||||
} = item;
|
||||
|
||||
const {
|
||||
nonces,
|
||||
post_types,
|
||||
} = config;
|
||||
|
||||
return post({
|
||||
action: 'et_library_save_item',
|
||||
et_library_save_item_nonce: nonces.et_library_save_item,
|
||||
post_type: post_types.et_theme_options,
|
||||
item_name,
|
||||
selected_cats,
|
||||
new_category_name,
|
||||
new_tag_name,
|
||||
content,
|
||||
builtFor,
|
||||
});
|
||||
};
|
||||
|
||||
// eslint-disable-next-line arrow-parens
|
||||
const sanitizeCommaSeparatedTaxNames = (taxName) => {
|
||||
const categoryName = 'string' === typeof taxName && taxName ? taxName : '';
|
||||
|
||||
return categoryName.split(',').map(newCategory => trim(newCategory));
|
||||
};
|
||||
|
||||
const saveThemeOptionsToCloud = async (obj, content) => {
|
||||
const {
|
||||
builtFor,
|
||||
item_name,
|
||||
new_category_name,
|
||||
new_tag_name,
|
||||
providedBaseUrl,
|
||||
selected_cats,
|
||||
selected_tags,
|
||||
} = obj;
|
||||
|
||||
const newCategories = sanitizeCommaSeparatedTaxNames(new_category_name);
|
||||
const newTags = sanitizeCommaSeparatedTaxNames(new_tag_name);
|
||||
const termsData = {
|
||||
tags: [...selected_tags, ...newTags],
|
||||
categories: [...selected_cats, ...newCategories],
|
||||
};
|
||||
|
||||
const newCloudItem = {
|
||||
title: item_name,
|
||||
content,
|
||||
status: 'publish',
|
||||
};
|
||||
|
||||
if (builtFor) {
|
||||
set(newCloudItem, 'meta._built_for', builtFor);
|
||||
}
|
||||
|
||||
return saveToCloudPure('theme-options', newCloudItem, termsData, noop, 0, providedBaseUrl);
|
||||
};
|
||||
/* eslint-enable */
|
||||
|
||||
export {
|
||||
saveThemeOptionsToCloud,
|
||||
saveThemeOptionsToLocal,
|
||||
};
|
@@ -0,0 +1,5 @@
|
||||
// Internal dependencies.
|
||||
import themeOptionsLibrary from './theme-options-library/module';
|
||||
|
||||
|
||||
export default themeOptionsLibrary;
|
@@ -0,0 +1,12 @@
|
||||
async function importThemeOptions({ themeOptionsLibApi, props: { item } }) {
|
||||
const exportedContent = item;
|
||||
exportedContent.context = 'epanel';
|
||||
|
||||
const file = new File([JSON.stringify(exportedContent)], 'theme_option.json', { type: 'application/json' });
|
||||
await themeOptionsLibApi.importContent(file);
|
||||
window.location = window.location.href.replace(/reset\=true\&|\&reset\=true/, '');
|
||||
};
|
||||
|
||||
export default {
|
||||
importThemeOptions,
|
||||
};
|
@@ -0,0 +1,24 @@
|
||||
import { state } from 'cerebral';
|
||||
import * as sequences from './sequences';
|
||||
import themeOptionsLibApi from './providers';
|
||||
import { PORTABILITY_STATE_DEFAULT } from '../../lib/constants';
|
||||
|
||||
|
||||
export default initialState => (
|
||||
{
|
||||
state: {
|
||||
...initialState,
|
||||
showSaveModal: get => 'save' === get(state`modalType`),
|
||||
showLibraryModal: get => 'add' === get(state`modalType`),
|
||||
itemsLoadedAndCached: false,
|
||||
portability: {
|
||||
state: PORTABILITY_STATE_DEFAULT,
|
||||
export: {},
|
||||
},
|
||||
},
|
||||
providers: {
|
||||
themeOptionsLibApi,
|
||||
},
|
||||
sequences,
|
||||
}
|
||||
);
|
@@ -0,0 +1,225 @@
|
||||
// Internal dependencies.
|
||||
import { noop } from 'lodash';
|
||||
|
||||
// Internal dependencies.
|
||||
import { post } from '../../lib/request';
|
||||
import config from '../../lib/config';
|
||||
import { saveToCloudPure } from '@cloud/app/lib/api';
|
||||
|
||||
export default {
|
||||
/**
|
||||
* Gets the Theme Options library items.
|
||||
*
|
||||
* @param {string} context Context (a.k.a Item type).
|
||||
*
|
||||
* @returns {Array} Resolved value from promise. Array of objects.
|
||||
*/
|
||||
getItems(context) {
|
||||
/* eslint-disable key-spacing */
|
||||
return post({
|
||||
action: 'et_theme_options_library_get_items',
|
||||
context,
|
||||
nonce: config.nonces.et_theme_options_library_get_items,
|
||||
});
|
||||
/* eslint-enable key-spacing */
|
||||
},
|
||||
|
||||
getItemsContent(item) {
|
||||
return post({
|
||||
action: 'et_theme_options_library_get_item_content',
|
||||
et_theme_option_id: item,
|
||||
nonce: config.nonces.et_theme_options_library_get_item_content,
|
||||
});
|
||||
},
|
||||
|
||||
importContent(file) {
|
||||
const formData = new FormData();
|
||||
formData.append('action', 'et_core_portability_import');
|
||||
formData.append('file', file, 'theme-options.json');
|
||||
formData.append('context', 'epanel');
|
||||
formData.append('nonce', config.nonces.et_core_portability_import);
|
||||
return post(formData, {
|
||||
contentType: false,
|
||||
processData: false,
|
||||
});
|
||||
},
|
||||
|
||||
exportItem(id, cloudContent) {
|
||||
return post({
|
||||
action: 'et_theme_options_library_export_item',
|
||||
nonce: config.nonces.et_theme_options_library_export_item,
|
||||
id,
|
||||
cloudContent,
|
||||
});
|
||||
},
|
||||
|
||||
downloadExportFile(id, fileName) {
|
||||
const args = {
|
||||
action: 'et_theme_options_library_export_item_download',
|
||||
nonce: config.nonces.et_theme_options_library_export_item,
|
||||
fileName,
|
||||
id,
|
||||
};
|
||||
|
||||
return `${config.api}?${jQuery.param(args)}`;
|
||||
},
|
||||
|
||||
/**
|
||||
* Update Local Tags/Categories and return updated list.
|
||||
*
|
||||
* @param {object} payload Payload.
|
||||
* @returns {Array} Response.
|
||||
*/
|
||||
updateFilters(payload) {
|
||||
return post({
|
||||
action: 'et_theme_options_library_update_terms',
|
||||
nonce: config.nonces.et_theme_options_library_update_terms,
|
||||
payload,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Update the theme options library item.
|
||||
*
|
||||
* @param {object} payload Updated item details.
|
||||
* @returns {Array} Resolved value from promise. Array of objects.
|
||||
*/
|
||||
updateItem(payload) {
|
||||
return post({
|
||||
action : 'et_theme_options_library_update_item',
|
||||
nonce : config.nonces.et_theme_options_library_update_item,
|
||||
payload,
|
||||
});
|
||||
},
|
||||
|
||||
/*
|
||||
* Gets the Theme Options library item content.
|
||||
*
|
||||
* @returns {Array} Resolved value from promise. Array of objects.
|
||||
*/
|
||||
getItemContent(id) {
|
||||
return post({
|
||||
action: 'et_theme_options_library_get_item_content',
|
||||
nonce: config.nonces.et_theme_options_library_get_item_content,
|
||||
et_theme_option_id: id,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieve Cloud Token.
|
||||
*
|
||||
* @returns {Array} Response with cloud token.
|
||||
*/
|
||||
getCloudToken() {
|
||||
return post({
|
||||
action: 'et_theme_options_library_get_token',
|
||||
nonce : config.nonces.et_theme_options_library_get_token,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove local item.
|
||||
*
|
||||
* @param {int} id
|
||||
* @returns {Array} Response with cloud token.
|
||||
*/
|
||||
export() {
|
||||
return post({
|
||||
action: 'et_core_portability_export',
|
||||
nonce: config.nonces.et_core_portability_export,
|
||||
context: 'epanel_temp',
|
||||
content: false,
|
||||
selection: false,
|
||||
timestamp: 0,
|
||||
page: 1,
|
||||
});
|
||||
},
|
||||
|
||||
saveTempOptions() {
|
||||
let opsForm = jQuery('#main_options_form').formSerialize();
|
||||
const nonce = `&_ajax_nonce=${config.nonces.et_core_save_theme_options}`;
|
||||
opsForm += `${nonce}&action=save_epanel_temp`;
|
||||
return post(opsForm);
|
||||
},
|
||||
|
||||
download(timestamp) {
|
||||
let downloadURL = config.epanel_save_url;
|
||||
const query = {
|
||||
timestamp,
|
||||
name: '',
|
||||
};
|
||||
|
||||
Object.entries(query).forEach(([key, value]) => {
|
||||
if (value) {
|
||||
downloadURL = `${downloadURL}&${key}=${value}`;
|
||||
}
|
||||
});
|
||||
|
||||
return fetch(downloadURL);
|
||||
},
|
||||
|
||||
saveThemeOptionsToLocal(item, content) {
|
||||
const {
|
||||
item_name,
|
||||
selected_cats,
|
||||
selected_tags,
|
||||
new_category_name,
|
||||
new_tag_name,
|
||||
} = item;
|
||||
|
||||
return post({
|
||||
action: 'et_library_save_item',
|
||||
et_library_save_item_nonce: config.nonces.et_library_save_item,
|
||||
post_type: config.post_types.et_theme_options,
|
||||
item_name,
|
||||
selected_cats,
|
||||
selected_tags,
|
||||
new_category_name,
|
||||
new_tag_name,
|
||||
content,
|
||||
});
|
||||
},
|
||||
|
||||
saveThemeOptionsToCloud(item, content) {
|
||||
const {
|
||||
new_category_name,
|
||||
new_tag_name,
|
||||
selected_tags,
|
||||
selected_cats,
|
||||
item_name,
|
||||
providedBaseUrl,
|
||||
} = item;
|
||||
|
||||
const newCategories = new_category_name.split(',').map(newCategory => newCategory.trim());
|
||||
const newTags = new_tag_name.split(',').map(newTag => newTag.trim());
|
||||
const termsData = {
|
||||
tags: [...selected_tags, ...newTags],
|
||||
categories: [...selected_cats, ...newCategories],
|
||||
};
|
||||
|
||||
const newCloudItem = {
|
||||
title: item_name,
|
||||
content,
|
||||
status: 'publish',
|
||||
};
|
||||
|
||||
return saveToCloudPure('theme-options', newCloudItem, termsData, noop, 0, providedBaseUrl);
|
||||
},
|
||||
|
||||
deleteTempOptions() {
|
||||
return post({
|
||||
action: 'et_theme_options_delete_temp_options',
|
||||
et_theme_options_delete_temp_options_nonce: config.nonces.et_theme_options_delete_temp_options,
|
||||
});
|
||||
},
|
||||
|
||||
removeLocalItem(id) {
|
||||
/* eslint-disable key-spacing */
|
||||
return post({
|
||||
action : 'et_theme_options_toggle_cloud_status',
|
||||
nonce : config.nonces.et_theme_options_library_toggle_item_location,
|
||||
et_theme_option_id : id,
|
||||
});
|
||||
/* eslint-enable */
|
||||
},
|
||||
};
|
@@ -0,0 +1,246 @@
|
||||
// External dependencies.
|
||||
import { props, sequence, state } from 'cerebral';
|
||||
import { set, when } from 'cerebral/factories';
|
||||
import { get as lodashGet } from 'lodash';
|
||||
|
||||
// Internal dependencies.
|
||||
import { setThemeOptionsLibraryItemsLoaded, setThemeOptionsLibraryToken } from '../../lib/theme-options-library';
|
||||
import actions from './actions';
|
||||
import {
|
||||
PORTABILITY_STATE_EXPORT_THEME_OPTIONS,
|
||||
PORTABILITY_STATE_IMPORT_THEME_OPTIONS,
|
||||
} from '../../lib/constants';
|
||||
|
||||
|
||||
const closePortability = sequence('Close Theme Options portability modal', [
|
||||
set(state`showPortability`, false),
|
||||
set(state`importError`, false),
|
||||
]);
|
||||
|
||||
const closeThemeOptionApp = sequence('Close theme options library app', [
|
||||
set(state`modalType`, null),
|
||||
]);
|
||||
|
||||
const loadItems = sequence('Load theme options library items', [
|
||||
/* eslint-disable arrow-body-style, arrow-parens */
|
||||
({ get, themeOptionsLibApi, path }) => {
|
||||
const context = get(state`context`);
|
||||
|
||||
return themeOptionsLibApi
|
||||
.getItems(context)
|
||||
.then(response => path.success({
|
||||
items: response,
|
||||
}))
|
||||
.catch(() => path.error());
|
||||
},
|
||||
{
|
||||
success: [
|
||||
set(state`items`, props`items`),
|
||||
set(state`itemsLoadedAndCached`, true),
|
||||
|
||||
({ get }) => {
|
||||
setThemeOptionsLibraryItemsLoaded(get(state`context`), true);
|
||||
},
|
||||
],
|
||||
error: [],
|
||||
},
|
||||
/* eslint-enable */
|
||||
]);
|
||||
|
||||
const updateLocalFilters = sequence('Update Local Filters', [
|
||||
({ themeOptionsLibApi, path, props: { payload } }) => themeOptionsLibApi
|
||||
.updateFilters(payload)
|
||||
.then((response => path.success(response)))
|
||||
.catch(() => path.error()),
|
||||
{
|
||||
success: [Promise.resolve(props`response`)],
|
||||
error: [],
|
||||
},
|
||||
]);
|
||||
|
||||
const getExportedItem = sequence('Get the exported theme option content', [
|
||||
({ themeOptionsLibApi, path, props: { id } }) => themeOptionsLibApi
|
||||
.getItemContent(id)
|
||||
.then(response => path.success(response))
|
||||
.catch(() => path.error()),
|
||||
{
|
||||
success: [Promise.resolve(props`response`)],
|
||||
error: [],
|
||||
},
|
||||
]);
|
||||
|
||||
const updateItem = sequence('Update theme options library item', [
|
||||
({ themeOptionsLibApi, get, path }) => {
|
||||
const payload = get(props`payload`);
|
||||
return themeOptionsLibApi.updateItem(payload)
|
||||
.then(response => path.success({
|
||||
updatedItem: {
|
||||
success: true,
|
||||
data: response,
|
||||
},
|
||||
}))
|
||||
.catch(() => path.error());
|
||||
},
|
||||
{
|
||||
success: [Promise.resolve(props`updatedItem`)],
|
||||
error: [],
|
||||
},
|
||||
]);
|
||||
|
||||
const setCloudToken = sequence('Set cloudToken', [
|
||||
({ get }) => {
|
||||
setThemeOptionsLibraryToken(get(props`cloudToken`));
|
||||
},
|
||||
]);
|
||||
|
||||
const cacheCloudToken = sequence('Retrieve saved Cloud Access token and save to state', [
|
||||
({ themeOptionsLibApi, path }) => {
|
||||
|
||||
return themeOptionsLibApi.getCloudToken()
|
||||
.then(cloudTokenData => {
|
||||
return path.success({cloudToken: cloudTokenData.accessToken});
|
||||
})
|
||||
.catch(() => path.error());
|
||||
},
|
||||
{
|
||||
success: [
|
||||
setCloudToken,
|
||||
],
|
||||
error: [],
|
||||
},
|
||||
]);
|
||||
|
||||
const setLibraryContext = sequence('Set Theme Options library context', [
|
||||
set(state`context`, props`context`),
|
||||
]);
|
||||
|
||||
const useThemeOptions = sequence('Insert theme options into a field', [
|
||||
when(props`item`, item => isNaN(parseInt(item))),
|
||||
{
|
||||
true: [
|
||||
({ props: contextProps }) => ({ item: JSON.parse(contextProps.item) }),
|
||||
actions.importThemeOptions,
|
||||
],
|
||||
false: [
|
||||
async ({ props: { item }, themeOptionsLibApi }) => {
|
||||
const data = await themeOptionsLibApi.getItemsContent(item);
|
||||
const { exported } = data;
|
||||
return { item: exported };
|
||||
},
|
||||
actions.importThemeOptions,
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
const openPortablity = sequence('Open theme options library modal', [
|
||||
({ store, props: { data } }) => {
|
||||
const portabilityState = lodashGet(data, 'action');
|
||||
|
||||
if (PORTABILITY_STATE_EXPORT_THEME_OPTIONS === portabilityState) {
|
||||
const itemLocation = lodashGet(data, 'item.item_location');
|
||||
const exportItemId = lodashGet(data, 'item.id');
|
||||
store.set(state`portability.export.id`, exportItemId);
|
||||
|
||||
if ('cloud' === itemLocation) {
|
||||
const exportItemContent = lodashGet(data, 'content');
|
||||
store.set(state`portability.export.content`, exportItemContent);
|
||||
store.set(state`portability.export.item_location`, itemLocation);
|
||||
}
|
||||
}
|
||||
|
||||
if ([PORTABILITY_STATE_IMPORT_THEME_OPTIONS, PORTABILITY_STATE_EXPORT_THEME_OPTIONS].includes(portabilityState)) {
|
||||
store.set(state`portability.state`, portabilityState);
|
||||
} else {
|
||||
store.set(state`portability.state`, PORTABILITY_STATE_IMPORT_THEME_OPTIONS);
|
||||
}
|
||||
},
|
||||
|
||||
set(state`showPortability`, true),
|
||||
]);
|
||||
|
||||
const setShowLibrary = sequence('Set theme options library', [
|
||||
set(state`showLibrary`, props`toggle`),
|
||||
]);
|
||||
|
||||
const exportThemeOptions = sequence('Export theme option', [
|
||||
({ themeOptionsLibApi, path, props: { id, cloudContent } }) => themeOptionsLibApi.exportItem(id, cloudContent)
|
||||
.then(() => path.success())
|
||||
.catch(() => path.error()),
|
||||
{
|
||||
success: [
|
||||
({ themeOptionsLibApi, props: { id, fileName } }) => {
|
||||
const downloadURI = themeOptionsLibApi.downloadExportFile(id, fileName);
|
||||
|
||||
window.location.assign(downloadURI);
|
||||
|
||||
window.ETCloudApp.emitSignal({
|
||||
signal: 'finishDownload',
|
||||
data: {},
|
||||
});
|
||||
},
|
||||
closePortability,
|
||||
],
|
||||
error: [],
|
||||
},
|
||||
]);
|
||||
|
||||
const saveThemeOptions = sequence('Save theme options', [
|
||||
async ({ themeOptionsLibApi }) => {
|
||||
await themeOptionsLibApi.saveTempOptions();
|
||||
const exportRestData = await themeOptionsLibApi.export();
|
||||
return { timestamp: exportRestData.timestamp };
|
||||
},
|
||||
async ({ themeOptionsLibApi, props: contextProps }) => {
|
||||
const response = await themeOptionsLibApi.download(contextProps.timestamp);
|
||||
const exportedContent = await response.json();
|
||||
return { content: JSON.stringify(exportedContent) };
|
||||
},
|
||||
when(props`item.cloud`, cloud => 'on' === cloud),
|
||||
{
|
||||
true: [
|
||||
({ themeOptionsLibApi, props: contextProps }) => {
|
||||
const { item, content } = contextProps;
|
||||
return themeOptionsLibApi.saveThemeOptionsToCloud(item, content);
|
||||
},
|
||||
],
|
||||
false: [
|
||||
({ themeOptionsLibApi, props: contextProps }) => {
|
||||
const { item, content } = contextProps;
|
||||
return themeOptionsLibApi.saveThemeOptionsToLocal(item, content);
|
||||
},
|
||||
],
|
||||
},
|
||||
({ themeOptionsLibApi }) => themeOptionsLibApi.deleteTempOptions(),
|
||||
]);
|
||||
|
||||
const toggleLibraryItemLocation = sequence('Remove local item from WPDB', [
|
||||
/* eslint-disable-next-line arrow-body-style */
|
||||
({ themeOptionsLibApi, path, props: { id } }) => {
|
||||
return themeOptionsLibApi.removeLocalItem(id)
|
||||
.then((response => path.success(response)))
|
||||
.catch(() => path.error());
|
||||
},
|
||||
{
|
||||
success: [Promise.resolve(props`response`)],
|
||||
error: [],
|
||||
},
|
||||
/* eslint-enable arrow-body-style */
|
||||
]);
|
||||
|
||||
export {
|
||||
closePortability,
|
||||
closeThemeOptionApp,
|
||||
exportThemeOptions,
|
||||
cacheCloudToken,
|
||||
getExportedItem,
|
||||
loadItems,
|
||||
openPortablity,
|
||||
setCloudToken,
|
||||
setLibraryContext,
|
||||
setShowLibrary,
|
||||
toggleLibraryItemLocation,
|
||||
updateItem,
|
||||
updateLocalFilters,
|
||||
useThemeOptions,
|
||||
saveThemeOptions,
|
||||
};
|
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
/**
|
||||
* "Theme Options Library" quick feature constants file.
|
||||
*
|
||||
* Divi Cloud Theme Options Library constants.
|
||||
*
|
||||
* @link https://elegantthemes.slack.com/archives/C03073D7S04/p1676610967464779?thread_ts=1676602536.658269&cid=C03073D7S04
|
||||
*
|
||||
* @package Divi
|
||||
* @subpackage Cloud
|
||||
* @since ??
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ET_THEME_OPTIONS_POST_TYPE' ) ) {
|
||||
define( 'ET_THEME_OPTIONS_POST_TYPE', 'et_theme_options' );
|
||||
}
|
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
/**
|
||||
* Register ET_THEME_OPTIONS_POST_TYPE.
|
||||
*
|
||||
* @since ??
|
||||
*
|
||||
* @package Divi
|
||||
* @subpackage Cloud
|
||||
* @since ??
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class to handle `et_theme_options` post type.
|
||||
*
|
||||
* Registers TO Item.
|
||||
*/
|
||||
class ET_Post_Type_Theme_Options extends ET_Core_Post_Type {
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_owner = 'builder';
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $name = ET_THEME_OPTIONS_POST_TYPE;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function _get_args() {
|
||||
return array(
|
||||
'can_export' => true,
|
||||
'capability_type' => 'post',
|
||||
'has_archive' => false,
|
||||
'hierarchical' => false,
|
||||
'map_meta_cap' => true,
|
||||
'public' => false,
|
||||
'publicly_queryable' => false,
|
||||
'query_var' => false,
|
||||
'show_in_menu' => false,
|
||||
'show_ui' => false,
|
||||
'supports' => array(
|
||||
'editor',
|
||||
'excerpt',
|
||||
'revisions',
|
||||
'thumbnail',
|
||||
'title',
|
||||
),
|
||||
'taxonomies' => array(
|
||||
'layout_category',
|
||||
'layout_tag',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function _get_labels() {
|
||||
return array(
|
||||
'add_new' => esc_html_x( 'Add New', 'Layout', 'et_builder' ),
|
||||
'add_new_item' => esc_html__( 'Add New Theme Options', 'et_builder' ),
|
||||
'all_items' => esc_html__( 'All Theme Options', 'et_builder' ),
|
||||
'edit_item' => esc_html__( 'Edit Theme Options', 'et_builder' ),
|
||||
'name' => esc_html__( 'Theme Options', 'et_builder' ),
|
||||
'new_item' => esc_html__( 'New Theme Options', 'et_builder' ),
|
||||
'not_found' => esc_html__( 'Nothing found', 'et_builder' ),
|
||||
'not_found_in_trash' => esc_html__( 'Nothing found in Trash', 'et_builder' ),
|
||||
'parent_item_colon' => '',
|
||||
'search_items' => esc_html__( 'Search Theme Options', 'et_builder' ),
|
||||
'singular_name' => esc_html__( 'Theme Options', 'et_builder' ),
|
||||
'view_item' => esc_html__( 'View Theme Options', 'et_builder' ),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the class instance.
|
||||
*
|
||||
* @param string $type See {@see self::$wp_type} for accepted values. Default is 'cpt'.
|
||||
* @param string $name The name/slug of the post object. Default is {@see self::$name}.
|
||||
*
|
||||
* @return self|null
|
||||
*/
|
||||
public static function instance( $type = 'cpt', $name = ET_THEME_OPTIONS_POST_TYPE ) {
|
||||
$instance = parent::instance( $type, $name );
|
||||
if ( ! $instance ) {
|
||||
$instance = new self();
|
||||
}
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns TRUE when a layout is Favorite.
|
||||
*
|
||||
* @param string $post_id Post ID.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_favorite( $post_id ) {
|
||||
return 'favorite' === get_post_meta( $post_id, 'favorite_status', true );
|
||||
}
|
||||
}
|
@@ -0,0 +1,149 @@
|
||||
<?php
|
||||
/**
|
||||
* Theme Options Library App helpers.
|
||||
*
|
||||
* @since ??
|
||||
*
|
||||
* @package Divi
|
||||
*/
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* ET_Theme_Options_Library_App class.
|
||||
*
|
||||
* Nonces and i18n strings needed for Theme Options Library Cloud app.
|
||||
*
|
||||
* @package Divi
|
||||
*/
|
||||
class ET_Theme_Options_Library_App {
|
||||
/**
|
||||
* Class instance.
|
||||
*
|
||||
* @var ET_Theme_Options_Library_App
|
||||
*/
|
||||
private static $_instance;
|
||||
|
||||
/**
|
||||
* Get the class instance.
|
||||
*
|
||||
* @return ET_Theme_Options_Library_App
|
||||
*/
|
||||
public static function instance() {
|
||||
if ( ! self::$_instance ) {
|
||||
self::$_instance = new self();
|
||||
}
|
||||
|
||||
return self::$_instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Cloud Helpers.
|
||||
*
|
||||
* @return array Helpers.
|
||||
*/
|
||||
public static function get_cloud_helpers() {
|
||||
$role_capabilities = et_pb_get_role_settings();
|
||||
$user_role = et_pb_get_current_user_role();
|
||||
$args = array(
|
||||
'et_core_portability' => true,
|
||||
'context' => 'epanel',
|
||||
'name' => 'save',
|
||||
'nonce' => wp_create_nonce( 'et_core_portability_export' ),
|
||||
);
|
||||
|
||||
$epanel_save_url = add_query_arg( $args, admin_url() );
|
||||
|
||||
// phpcs:disable WordPress.Arrays.MultipleStatementAlignment.DoubleArrowNotAligned, WordPress.Arrays.MultipleStatementAlignment.LongIndexSpaceBeforeDoubleArrow -- Aligned manually.
|
||||
return [
|
||||
'i18n' => [
|
||||
'library' => require ET_CORE_PATH . '/i18n/library.php',
|
||||
'epanel' => require ET_EPANEL_DIR . '/i18n/epanel.php',
|
||||
],
|
||||
'api' => admin_url( 'admin-ajax.php' ),
|
||||
'capabilities' => isset( $role_capabilities[ $user_role ] ) ? $role_capabilities[ $user_role ] : array(),
|
||||
'epanel_save_url' => $epanel_save_url,
|
||||
'post_types' => [
|
||||
'et_theme_options' => ET_THEME_OPTIONS_POST_TYPE,
|
||||
],
|
||||
'nonces' => [
|
||||
'et_theme_options_library_get_items' => wp_create_nonce( 'et_theme_options_library_get_items' ),
|
||||
'et_theme_options_library_update_terms' => wp_create_nonce( 'et_theme_options_library_update_terms' ),
|
||||
'et_theme_options_library_get_item_content' => wp_create_nonce( 'et_theme_options_library_get_item_content' ),
|
||||
'et_theme_options_library_import_item_content' => wp_create_nonce( 'et_theme_options_library_import_item_content' ),
|
||||
'et_core_portability_import' => wp_create_nonce( 'et_core_portability_import' ),
|
||||
'et_theme_options_library_update_item' => wp_create_nonce( 'et_theme_options_library_update_item' ),
|
||||
'et_theme_options_library_export_item' => wp_create_nonce( 'et_theme_options_library_export_item' ),
|
||||
'et_theme_options_library_get_token' => wp_create_nonce( 'et_theme_options_library_get_token' ),
|
||||
'et_core_save_theme_options' => wp_create_nonce( 'et_core_save_theme_options' ),
|
||||
'et_core_portability_export' => wp_create_nonce( 'et_core_portability_export' ),
|
||||
'et_library_save_item' => wp_create_nonce( 'et_library_save_item' ),
|
||||
'et_theme_options_delete_temp_options' => wp_create_nonce( 'et_theme_options_delete_temp_options' ),
|
||||
'et_theme_options_library_toggle_item_location' => wp_create_nonce( 'et_theme_options_library_toggle_item_location' ),
|
||||
],
|
||||
];
|
||||
// phpcs:enable
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the Cloud App scripts.
|
||||
*
|
||||
* @param string $enqueue_prod_scripts Flag to force Production scripts.
|
||||
* @param bool $skip_react_loading Flag to skip react loading.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function load_js( $enqueue_prod_scripts = true, $skip_react_loading = false ) {
|
||||
// phpcs:disable ET.Sniffs.ValidVariableName.VariableNotSnakeCase -- Following the pattern found in /cloud.
|
||||
$EPANEL_VERSION = et_get_theme_version();
|
||||
$ET_DEBUG = defined( 'ET_DEBUG' ) && ET_DEBUG;
|
||||
$DEBUG = $ET_DEBUG;
|
||||
|
||||
$home_url = wp_parse_url( get_site_url() );
|
||||
$build_dir_uri = ET_EPANEL_URI . '/build';
|
||||
$cache_buster = $DEBUG ? mt_rand() / mt_getrandmax() : $EPANEL_VERSION; // phpcs:ignore WordPress.WP.AlternativeFunctions.rand_mt_rand -- mt_rand() should do for cache busting.
|
||||
$asset_path = ET_EPANEL_DIR . '/build/et-theme-options-library-app.bundle.js';
|
||||
|
||||
if ( file_exists( $asset_path ) ) {
|
||||
wp_enqueue_style( 'et-theme-options-library-styles', "{$build_dir_uri}/et-theme-options-library-app.bundle.css", [], (string) $cache_buster );
|
||||
}
|
||||
|
||||
$BUNDLE_DEPS = [
|
||||
'jquery',
|
||||
'react',
|
||||
'react-dom',
|
||||
'es6-promise',
|
||||
];
|
||||
|
||||
if ( $DEBUG || $enqueue_prod_scripts || file_exists( $asset_path ) ) {
|
||||
$BUNDLE_URI = ! file_exists( $asset_path ) ? "{$home_url['scheme']}://{$home_url['host']}:31599/et-theme-options-library-app.bundle.js" : "{$build_dir_uri}/et-theme-options-library-app.bundle.js";
|
||||
|
||||
// Skip the React loading if we already have React ( Gutenberg editor for example ) to avoid conflicts.
|
||||
if ( ! $skip_react_loading ) {
|
||||
if ( function_exists( 'et_fb_enqueue_react' ) ) {
|
||||
et_fb_enqueue_react();
|
||||
}
|
||||
}
|
||||
|
||||
wp_enqueue_script(
|
||||
'et-theme-options-library-app',
|
||||
$BUNDLE_URI,
|
||||
$BUNDLE_DEPS,
|
||||
(string) $cache_buster,
|
||||
true
|
||||
);
|
||||
|
||||
wp_localize_script(
|
||||
'et-theme-options-library-app',
|
||||
'et_theme_options_data',
|
||||
self::get_cloud_helpers()
|
||||
);
|
||||
}
|
||||
// phpcs:enable
|
||||
}
|
||||
}
|
||||
|
||||
ET_Theme_Options_Library_App::instance();
|
@@ -0,0 +1,222 @@
|
||||
<?php
|
||||
/**
|
||||
* Local Library API.
|
||||
*
|
||||
* @since ??
|
||||
*
|
||||
* @package Divi
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* ET_Theme_Options_Library_Local utility class.
|
||||
*
|
||||
* Item can be a layout, a template, a theme option, a code snippet, etc.
|
||||
*
|
||||
* @since ??
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
class ET_Theme_Options_Library_Local extends ET_Item_Library_Local {
|
||||
/**
|
||||
* Gets the class instance.
|
||||
*
|
||||
* @since ??
|
||||
*
|
||||
* @return ET_Item_Library_Local
|
||||
*/
|
||||
public static function instance() {
|
||||
if ( ! self::$_instance ) {
|
||||
self::$_instance = new self();
|
||||
}
|
||||
|
||||
return self::$_instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->post_type = ET_THEME_OPTIONS_POST_TYPE;
|
||||
|
||||
$this->exceptional_processes = array(
|
||||
'duplicate',
|
||||
'duplicate_and_delete',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the library items.
|
||||
*
|
||||
* @param string $item_type Item type.
|
||||
* @return array
|
||||
*/
|
||||
public function get_library_items( $item_type ) {
|
||||
$_ = ET_Core_Data_Utils::instance();
|
||||
$theme_options_items = ET_Post_Type_Theme_Options::instance();
|
||||
$theme_options_tags = ET_Builder_Post_Taxonomy_LayoutTag::instance();
|
||||
$theme_options_categories = ET_Builder_Post_Taxonomy_LayoutCategory::instance();
|
||||
|
||||
$item_categories = [];
|
||||
$item_tags = [];
|
||||
$items = [];
|
||||
$index = 0;
|
||||
|
||||
$query_posts = $theme_options_items
|
||||
->query()
|
||||
->run(
|
||||
array(
|
||||
'post_status' => array( 'publish', 'trash' ),
|
||||
'orderby' => 'name',
|
||||
'fields' => 'ids',
|
||||
)
|
||||
);
|
||||
|
||||
$post_ids = is_array( $query_posts ) ? $query_posts : array( $query_posts );
|
||||
|
||||
foreach ( $post_ids as $post_id ) {
|
||||
$item = new stdClass();
|
||||
$post = get_post( $post_id );
|
||||
|
||||
$item->id = $post->ID;
|
||||
$item->index = $index;
|
||||
$item->date = $post->post_date;
|
||||
|
||||
$title = html_entity_decode( $post->post_title );
|
||||
|
||||
// check if current user can edit library item.
|
||||
$can_edit_post = current_user_can( 'edit_post', $item->id );
|
||||
|
||||
if ( $title ) {
|
||||
// Remove periods since we use dot notation to retrieve translation.
|
||||
$title = str_replace( '.', '', $title );
|
||||
|
||||
$item->name = et_core_intentionally_unescaped( $title, 'react_jsx' );
|
||||
}
|
||||
|
||||
$built_for = get_post_meta( $item->id, '_built_for', true );
|
||||
|
||||
$item->slug = $post->post_name;
|
||||
$item->url = esc_url( wp_make_link_relative( get_permalink( $post ) ) );
|
||||
|
||||
$item->short_name = '';
|
||||
$item->builtFor = $built_for && '' !== $built_for ? $built_for : 'Divi'; // phpcs:ignore ET.Sniffs.ValidVariableName.UsedPropertyNotSnakeCase -- This is valid format for the property in the Cloud App.
|
||||
$item->description = '';
|
||||
$item->is_favorite = $theme_options_items->is_favorite( $item->id );
|
||||
$item->isTrash = 'trash' === $post->post_status; // phpcs:ignore ET.Sniffs.ValidVariableName.UsedPropertyNotSnakeCase -- This is valid format for the property in the Cloud App.
|
||||
$item->isReadOnly = ! $can_edit_post; // phpcs:ignore ET.Sniffs.ValidVariableName.UsedPropertyNotSnakeCase -- This is valid format for the property in the Cloud App.
|
||||
$item->categories = array();
|
||||
$item->category_ids = array();
|
||||
$item->tags = array();
|
||||
$item->tag_ids = array();
|
||||
|
||||
$this->process_item_taxonomy(
|
||||
$post,
|
||||
$item,
|
||||
$index,
|
||||
$item_categories,
|
||||
$theme_options_categories->name,
|
||||
'category'
|
||||
);
|
||||
|
||||
$this->process_item_taxonomy(
|
||||
$post,
|
||||
$item,
|
||||
$index,
|
||||
$item_tags,
|
||||
$theme_options_tags->name,
|
||||
'tag'
|
||||
);
|
||||
|
||||
$items[] = $item;
|
||||
|
||||
$index++;
|
||||
}
|
||||
|
||||
return [
|
||||
'categories' => $this->get_processed_terms( $theme_options_categories->name ),
|
||||
'tags' => $this->get_processed_terms( $theme_options_tags->name ),
|
||||
'items' => $items,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs item exceptional updates.
|
||||
*
|
||||
* @param array $payload Payload.
|
||||
* @param array $updated_data Updated data.
|
||||
*
|
||||
* @since ??
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function _perform_item_exceptional_updates( $payload, $updated_data ) {
|
||||
if ( empty( $payload['item_id'] ) || empty( $payload['update_details'] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$update_details = $payload['update_details'];
|
||||
|
||||
if ( empty( $update_details['updateType'] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$item_id = absint( $payload['item_id'] );
|
||||
$update_type = sanitize_text_field( $update_details['updateType'] );
|
||||
$item_name = isset( $update_details['itemName'] ) ? sanitize_text_field( $update_details['itemName'] ) : '';
|
||||
$et_builder_categories = ET_Builder_Post_Taxonomy_LayoutCategory::instance();
|
||||
$et_builder_tags = ET_Builder_Post_Taxonomy_LayoutTag::instance();
|
||||
|
||||
switch ( $update_type ) {
|
||||
case 'duplicate':
|
||||
case 'duplicate_and_delete':
|
||||
if ( isset( $update_details['content'] ) ) {
|
||||
$content = $update_details['content'];
|
||||
} else {
|
||||
$content = get_the_content( null, false, $item_id );
|
||||
}
|
||||
|
||||
if ( is_array( $content ) ) {
|
||||
$content = wp_json_encode( $content );
|
||||
}
|
||||
|
||||
$new_item = array(
|
||||
'post_title' => $item_name,
|
||||
'post_content' => $content,
|
||||
'post_status' => 'publish',
|
||||
'post_type' => $this->post_type,
|
||||
'tax_input' => array(
|
||||
$et_builder_categories->name => $updated_data['categories'],
|
||||
$et_builder_tags->name => $updated_data['tags'],
|
||||
),
|
||||
);
|
||||
|
||||
$updated_data['newItem'] = wp_insert_post( $new_item );
|
||||
break;
|
||||
}
|
||||
|
||||
$updated_data['updateType'] = $update_type;
|
||||
|
||||
return $updated_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the library item.
|
||||
*
|
||||
* @param array $payload Payload.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function perform_item_update( $payload ) {
|
||||
$updated_data = $this->_perform_item_common_updates( $payload );
|
||||
|
||||
if ( ! empty( $this->exceptional_processes ) ) {
|
||||
$updated_data = $this->_perform_item_exceptional_updates( $payload, $updated_data );
|
||||
}
|
||||
|
||||
return $updated_data;
|
||||
}
|
||||
}
|
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
/**
|
||||
* Theme options quick feature entry file.
|
||||
*
|
||||
* Divi Cloud Theme Options Library.
|
||||
*
|
||||
* @package Divi
|
||||
* @subpackage Epanel
|
||||
* @since ??
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ET_THEME_OPTIONS_DIR' ) ) {
|
||||
define( 'ET_THEME_OPTIONS_DIR', get_template_directory() . '/epanel/theme-options-library/' );
|
||||
}
|
||||
|
||||
require_once trailingslashit( ET_THEME_OPTIONS_DIR ) . 'constants.php';
|
||||
require_once trailingslashit( ET_THEME_OPTIONS_DIR ) . 'api.php';
|
||||
|
||||
if ( ! function_exists( 'et_init_theme_options_library' ) ) :
|
||||
/**
|
||||
* Init Theme Options Library.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function et_init_theme_options_library() {
|
||||
require_once trailingslashit( ET_THEME_OPTIONS_DIR ) . 'ThemeOptionsLibrary.php';
|
||||
}
|
||||
endif;
|
||||
|
||||
add_action( 'init', 'et_init_theme_options_library' );
|
Reference in New Issue
Block a user