Commit realizado el 12:13:52 08-04-2024
This commit is contained in:
@@ -0,0 +1,165 @@
|
||||
<?php
|
||||
/**
|
||||
* Code Snippets library.
|
||||
*
|
||||
* Registers post types to be used in the "Code Snippets" library.
|
||||
*
|
||||
* @link https://github.com/elegantthemes/Divi/issues/26232
|
||||
*
|
||||
* @package Divi
|
||||
* @subpackage Builder
|
||||
* @since 4.19.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Core class used to implement "Code Snippets" library.
|
||||
*
|
||||
* Register post types & taxonomies to be used in "Code Snippets" library.
|
||||
*/
|
||||
class ET_Builder_Code_Snippets_Library {
|
||||
|
||||
/**
|
||||
* Instance of `ET_Builder_Code_Snippets_Library`.
|
||||
*
|
||||
* @var ET_Builder_Code_Snippets_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_code_snippet` taxonomy.
|
||||
*
|
||||
* @var ET_Builder_Post_Taxonomy_LayoutCategory
|
||||
*/
|
||||
public $code_snippet_categories;
|
||||
|
||||
/**
|
||||
* ET_Builder_Post_Taxonomy_LayoutTag instance.
|
||||
*
|
||||
* Shall be used for querying `et_code_snippet` taxonomy .
|
||||
*
|
||||
* @var ET_Builder_Post_Taxonomy_LayoutTag
|
||||
*/
|
||||
public $code_snippet_tags;
|
||||
|
||||
/**
|
||||
* ET_Builder_Post_Taxonomy_CodeSnippetType instance.
|
||||
*
|
||||
* Shall be used for querying `et_code_snippet` taxonomy .
|
||||
*
|
||||
* @var ET_Builder_Post_Taxonomy_CodeSnippetType
|
||||
*/
|
||||
public $code_snippet_types;
|
||||
|
||||
/**
|
||||
* ET_Builder_Post_Type_TBItem instance.
|
||||
*
|
||||
* Shall be used for querying `et_tb_item` posts .
|
||||
*
|
||||
* @var ET_Builder_Post_Type_TBItem
|
||||
*/
|
||||
public $code_snippets;
|
||||
|
||||
/**
|
||||
* ET_Builder_Post_Taxonomy_LayoutCategory instance.
|
||||
*
|
||||
* Shall be used for querying `et_tb_layout_category` taxonomy .
|
||||
*
|
||||
* @var ET_Builder_Post_Taxonomy_LayoutCategory
|
||||
*/
|
||||
public $code_snippets_categories;
|
||||
|
||||
/**
|
||||
* ET_Builder_Post_Taxonomy_LayoutTag instance.
|
||||
*
|
||||
* Shall be used for querying `et_tb_layout_tag` taxonomy .
|
||||
*
|
||||
* @var ET_Builder_Post_Taxonomy_LayoutTag
|
||||
*/
|
||||
public $code_snippets_tags;
|
||||
|
||||
/**
|
||||
* ET_Builder_Post_Taxonomy_CodeSnippetType instance.
|
||||
*
|
||||
* Shall be used for querying `et_tb_layout_type` taxonomy .
|
||||
*
|
||||
* @var ET_Builder_Post_Taxonomy_CodeSnippetType
|
||||
*/
|
||||
public $code_snippets_type;
|
||||
|
||||
/**
|
||||
* 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 Builder Library's custom post type and its taxonomies.
|
||||
*/
|
||||
protected function _register_cpt_and_taxonomies() {
|
||||
$files = [
|
||||
ET_CODE_SNIPPETS_DIR . 'post/type/CodeSnippet.php',
|
||||
ET_CODE_SNIPPETS_DIR . 'post/taxonomy/CodeSnippetType.php',
|
||||
];
|
||||
|
||||
if ( ! $files ) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ( $files as $file ) {
|
||||
require_once $file;
|
||||
}
|
||||
|
||||
$this->code_snippets = ET_Builder_Post_Type_Code_Snippet::instance();
|
||||
$this->code_snippets_categories = ET_Builder_Post_Taxonomy_LayoutCategory::instance();
|
||||
$this->code_snippets_tags = ET_Builder_Post_Taxonomy_LayoutTag::instance();
|
||||
$this->code_snippets_type = ET_Builder_Post_Taxonomy_CodeSnippetType::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_TBItem::register_all( 'builder' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ET_Builder_TBItem_Library instance.
|
||||
*
|
||||
* @return ET_Builder_TBItem_Library
|
||||
*/
|
||||
public static function instance() {
|
||||
if ( ! self::$_instance ) {
|
||||
self::$_instance = new self();
|
||||
}
|
||||
|
||||
return self::$_instance;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ET_Builder_Code_Snippets_Library::instance();
|
333
wp-content/themes/Divi/core/code-snippets/api.php
Normal file
333
wp-content/themes/Divi/core/code-snippets/api.php
Normal file
@@ -0,0 +1,333 @@
|
||||
<?php
|
||||
/**
|
||||
* Code Snippets Library API.
|
||||
*
|
||||
* @package Divi
|
||||
*/
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves Code Snippets Library items.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function et_code_snippets_library_get_items() {
|
||||
et_core_security_check( 'edit_posts', 'et_code_snippets_library_get_items', 'nonce' );
|
||||
|
||||
$code_snippet_type = '';
|
||||
if ( isset( $_POST['et_code_snippet_type'] ) && is_string( $_POST['et_code_snippet_type'] ) ) {
|
||||
$code_snippet_type = sanitize_text_field( $_POST['et_code_snippet_type'] );
|
||||
}
|
||||
|
||||
$is_code_snippet_type_valid = in_array(
|
||||
$code_snippet_type,
|
||||
[
|
||||
'et_code_snippet_css',
|
||||
'et_code_snippet_css_no_selector',
|
||||
'et_code_snippet_html_js',
|
||||
],
|
||||
true
|
||||
);
|
||||
|
||||
if ( ! $code_snippet_type || ! $is_code_snippet_type_valid ) {
|
||||
wp_send_json_error( 'Error: Wrong item type provided.' );
|
||||
}
|
||||
|
||||
$item_library_local = et_pb_code_snippets_library_local();
|
||||
$data = $item_library_local->get_library_items( $code_snippet_type );
|
||||
|
||||
wp_send_json_success( $data );
|
||||
}
|
||||
|
||||
add_action( 'wp_ajax_et_code_snippets_library_get_items', 'et_code_snippets_library_get_items' );
|
||||
|
||||
/**
|
||||
* Retrieves Code Snippets Library item content.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function et_code_snippets_library_get_item_content() {
|
||||
et_core_security_check( 'edit_posts', 'et_code_snippets_library_get_item_content', 'nonce' );
|
||||
|
||||
$post_id = isset( $_POST['et_code_snippet_id'] ) ? (int) sanitize_text_field( $_POST['et_code_snippet_id'] ) : 0;
|
||||
$return_format = isset( $_POST['et_code_snippet_format'] ) ? (string) sanitize_text_field( $_POST['et_code_snippet_format'] ) : 'raw';
|
||||
$snippet_type = isset( $_POST['et_code_snippet_type'] ) ? (string) sanitize_text_field( $_POST['et_code_snippet_type'] ) : 'et_code_snippet_html_js';
|
||||
$post_type = get_post_type( $post_id );
|
||||
|
||||
if ( ! current_user_can( 'edit_post', $post_id ) || ET_CODE_SNIPPET_POST_TYPE !== $post_type ) {
|
||||
wp_send_json_error( 'You do not have permission.' );
|
||||
}
|
||||
|
||||
$post = get_post( $post_id );
|
||||
|
||||
$post_content = $post->post_content;
|
||||
$exported = array();
|
||||
|
||||
if ( 'exported' === $return_format ) {
|
||||
$exported = array(
|
||||
'context' => 'et_code_snippet',
|
||||
'data' => $post_content,
|
||||
'snippet_type' => $snippet_type,
|
||||
);
|
||||
}
|
||||
|
||||
wp_send_json_success(
|
||||
array(
|
||||
'snippet' => $post_content,
|
||||
'exported' => $exported,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
add_action( 'wp_ajax_et_code_snippets_library_get_item_content', 'et_code_snippets_library_get_item_content' );
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Save Code Snippets Library item content.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function et_code_snippets_library_save_item_content() {
|
||||
et_core_security_check( 'edit_posts', 'et_code_snippets_library_save_item_content', 'nonce' );
|
||||
|
||||
$post_id = isset( $_POST['et_code_snippet_id'] ) ? absint( $_POST['et_code_snippet_id'] ) : 0;
|
||||
$post_type = get_post_type( $post_id );
|
||||
|
||||
if ( ! current_user_can( 'edit_post', $post_id ) || ET_CODE_SNIPPET_POST_TYPE !== $post_type ) {
|
||||
wp_send_json_error( 'You do not have permission.' );
|
||||
}
|
||||
|
||||
// phpcs:disable ET.Sniffs.ValidatedSanitizedInput -- $_POST is an array, it's value sanitization is done at the time of saving by wp_update_post.
|
||||
$result = wp_update_post(
|
||||
[
|
||||
'ID' => $post_id,
|
||||
'post_content' => $_POST['et_code_snippet_content'],
|
||||
]
|
||||
);
|
||||
// phpcs:enable
|
||||
|
||||
wp_send_json_success(
|
||||
array(
|
||||
'result' => $result,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
add_action( 'wp_ajax_et_code_snippets_library_save_item_content', 'et_code_snippets_library_save_item_content' );
|
||||
|
||||
/**
|
||||
* Update Code Snippets Library item.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function et_code_snippets_library_update_item() {
|
||||
et_core_security_check( 'edit_posts', 'et_code_snippets_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.
|
||||
|
||||
$item_library_local = et_pb_code_snippets_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_code_snippets_library_update_item', 'et_code_snippets_library_update_item' );
|
||||
|
||||
/**
|
||||
* Prepare Library Categories or Tags List.
|
||||
*
|
||||
* @param string $taxonomy Name of the taxonomy.
|
||||
*
|
||||
* @return array Clean Categories/Tags array.
|
||||
**/
|
||||
function et_get_clean_library_terms( $taxonomy = 'layout_category' ) {
|
||||
$raw_terms_array = apply_filters( 'et_pb_new_layout_cats_array', get_terms( $taxonomy, array( 'hide_empty' => false ) ) );
|
||||
$clean_terms_array = array();
|
||||
|
||||
if ( is_array( $raw_terms_array ) && ! empty( $raw_terms_array ) ) {
|
||||
foreach ( $raw_terms_array as $term ) {
|
||||
$clean_terms_array[] = array(
|
||||
'name' => et_core_intentionally_unescaped( html_entity_decode( $term->name ), 'react_jsx' ),
|
||||
'id' => $term->term_id,
|
||||
'slug' => $term->slug,
|
||||
'count' => $term->count,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $clean_terms_array;
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX Callback: Remove the Library layout after it was moved to the Cloud.
|
||||
*
|
||||
* @since 4.17.0
|
||||
*
|
||||
* @global $_POST['payload'] Array with the layout data to remove.
|
||||
*
|
||||
* @return void|string JSON encoded in case of empty payload
|
||||
*/
|
||||
function et_code_snippets_toggle_cloud_status() {
|
||||
et_core_security_check( 'edit_posts', 'et_code_snippets_library_toggle_item_location', 'nonce' );
|
||||
|
||||
$post_id = isset( $_POST['et_code_snippet_id'] ) ? (int) sanitize_text_field( $_POST['et_code_snippet_id'] ) : 0;
|
||||
|
||||
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_CODE_SNIPPET_POST_TYPE !== $post_type ) {
|
||||
wp_send_json_error( 'You do not have permission.' );
|
||||
}
|
||||
|
||||
wp_delete_post( $post_id, true );
|
||||
|
||||
wp_send_json_success(
|
||||
array(
|
||||
'localLibraryTerms' => [
|
||||
'layout_category' => et_get_clean_library_terms(),
|
||||
'layout_tag' => et_get_clean_library_terms( 'layout_tag' ),
|
||||
],
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
add_action( 'wp_ajax_et_code_snippets_toggle_cloud_status', 'et_code_snippets_toggle_cloud_status' );
|
||||
|
||||
/**
|
||||
* Export Code Snippets Library item.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function et_code_snippets_library_export_item() {
|
||||
et_core_security_check( et_core_portability_cap( 'et_code_snippets' ), 'et_code_snippets_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.
|
||||
$direct_export = isset( $_POST['directExport'] ) ? $_POST['directExport'] : ''; // phpcs:ignore ET.Sniffs.ValidatedSanitizedInput -- $_POST['directExport'] is an array, it's value sanitization is done at the time of accessing value.
|
||||
|
||||
$response = et_code_snippets_library_export_item_data( $post_id, $cloud_content, $direct_export );
|
||||
|
||||
if ( ! $response ) {
|
||||
wp_send_json_error( 'Error: Wrong data provided.' );
|
||||
}
|
||||
|
||||
wp_send_json_success( $response );
|
||||
}
|
||||
|
||||
add_action( 'wp_ajax_et_code_snippets_library_export_item', 'et_code_snippets_library_export_item' );
|
||||
|
||||
/**
|
||||
* Download exported Code Snippets Library item.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function et_code_snippets_library_export_item_download() {
|
||||
et_core_security_check( et_core_portability_cap( 'et_code_snippets' ), 'et_code_snippets_library_export_item', 'nonce', '_GET' );
|
||||
|
||||
$id = ! empty( $_GET['id'] ) ? absint( $_GET['id'] ) : 0;
|
||||
$file_name = empty( $_GET['fileName'] ) ? 'code-snippet' : 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_code_snippet_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_code_snippets_library_export_item_download', 'et_code_snippets_library_export_item_download' );
|
||||
|
||||
/**
|
||||
* Import Code Snippets Library item.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function et_code_snippets_library_import_item() {
|
||||
et_core_security_check( et_core_portability_cap( 'et_code_snippets' ), 'et_code_snippets_library_import_item', 'nonce' );
|
||||
|
||||
$response = et_code_snippets_library_import_item_data();
|
||||
|
||||
if ( ! $response ) {
|
||||
wp_send_json_error( 'Not a valid file.' );
|
||||
}
|
||||
|
||||
wp_send_json_success( $response );
|
||||
}
|
||||
|
||||
add_action( 'wp_ajax_et_code_snippets_library_import_item', 'et_code_snippets_library_import_item' );
|
||||
|
||||
|
||||
/**
|
||||
* Update Local Library Tags and Categories.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function et_code_snippets_library_update_terms() {
|
||||
et_core_security_check( 'edit_posts', 'et_code_snippets_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_code_snippets_library_local();
|
||||
$response = $item_library_local->perform_terms_update( $payload );
|
||||
|
||||
wp_send_json_success( $response );
|
||||
}
|
||||
|
||||
add_action( 'wp_ajax_et_code_snippets_library_update_terms', 'et_code_snippets_library_update_terms' );
|
||||
|
||||
/**
|
||||
* Ajax :: Save code snippets to the local library.
|
||||
*/
|
||||
function et_code_snippets_library_save() {
|
||||
et_core_security_check( 'edit_posts', 'et_code_snippets_save_to_local_library' );
|
||||
|
||||
$post_id = et_save_item_to_local_library( $_POST );
|
||||
|
||||
if ( is_wp_error( $post_id ) ) {
|
||||
wp_send_json_error();
|
||||
}
|
||||
|
||||
wp_send_json_success();
|
||||
}
|
||||
|
||||
add_action( 'wp_ajax_et_code_snippets_library_save', 'et_code_snippets_library_save' );
|
||||
|
||||
/**
|
||||
* Ajax :: Get Cloud token.
|
||||
*/
|
||||
function et_code_snippets_library_get_token() {
|
||||
et_core_security_check( 'edit_posts', 'et_code_snippets_library_get_token', 'nonce' );
|
||||
|
||||
$access_token = get_transient( 'et_cloud_access_token' );
|
||||
|
||||
wp_send_json_success(
|
||||
array(
|
||||
'accessToken' => $access_token,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
add_action( 'wp_ajax_et_code_snippets_library_get_token', 'et_code_snippets_library_get_token' );
|
103
wp-content/themes/Divi/core/code-snippets/app/boot.js
Normal file
103
wp-content/themes/Divi/core/code-snippets/app/boot.js
Normal file
@@ -0,0 +1,103 @@
|
||||
// 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';
|
||||
import {
|
||||
assign,
|
||||
clone,
|
||||
set,
|
||||
get,
|
||||
} from 'lodash';
|
||||
|
||||
// Internal Dependencies
|
||||
import store from './store/';
|
||||
import CodeSnippetsApp from './components/App';
|
||||
import { STATE_IDLE } from '@code-snippets/lib/constants';
|
||||
|
||||
|
||||
const initialState = {
|
||||
content: '',
|
||||
context: 'code_css',
|
||||
importError: false,
|
||||
items: [],
|
||||
showLibrary: false,
|
||||
showPortability: false,
|
||||
importState: STATE_IDLE,
|
||||
};
|
||||
|
||||
$(window).on('et_code_snippets_container_ready', (event, preferences, container = document) => {
|
||||
let devtools = null;
|
||||
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
devtools = Devtools({
|
||||
host: '127.0.0.1:4045',
|
||||
reconnect: false,
|
||||
bigComponentsWarning: 15,
|
||||
});
|
||||
}
|
||||
|
||||
const context = preferences.context;
|
||||
const state = assign(clone(initialState), {});
|
||||
const modalType = preferences.modalType;
|
||||
|
||||
if ('' !== context) {
|
||||
set(state, 'context', context);
|
||||
}
|
||||
|
||||
const content = get(preferences, 'content', '');
|
||||
if ('' !== content) {
|
||||
set(state, 'content', content);
|
||||
}
|
||||
|
||||
const selectedContent = get(preferences, 'selectedContent', '');
|
||||
set(state, 'selectedContent', selectedContent);
|
||||
|
||||
const sidebarLabel = get(preferences, 'sidebarLabel', '');
|
||||
set(state, 'sidebarLabel', sidebarLabel);
|
||||
|
||||
const app = App(store(state), {
|
||||
devtools,
|
||||
returnSequencePromise: true,
|
||||
});
|
||||
|
||||
const containerId = preferences.containerId;
|
||||
|
||||
const domNode = container.getElementById(containerId);
|
||||
if ('' === modalType) {
|
||||
unmountComponentAtNode(domNode);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
render(
|
||||
<Container app={app}>
|
||||
<CodeSnippetsApp
|
||||
modalType={modalType}
|
||||
codeMirrorId={preferences.codeMirrorId}
|
||||
insertCodeCallback={preferences.insertCodeCallback}
|
||||
container={container}
|
||||
/>
|
||||
</Container>,
|
||||
domNode
|
||||
);
|
||||
|
||||
// Disable main body scrolling.
|
||||
$(container).find('body.et-admin-page').addClass('et-code-snippets-open');
|
||||
|
||||
// Properly unmount app on close.
|
||||
$(window).on('et_code_snippets_library_close', () => {
|
||||
const appContainer = container.getElementById(containerId);
|
||||
|
||||
setTimeout(() => {
|
||||
if (appContainer) {
|
||||
unmountComponentAtNode(appContainer);
|
||||
appContainer.remove();
|
||||
}
|
||||
|
||||
$('body.et-admin-page').removeClass('et-code-snippets-open');
|
||||
});
|
||||
});
|
||||
});
|
@@ -0,0 +1,27 @@
|
||||
// External dependencies.
|
||||
import {
|
||||
every,
|
||||
get,
|
||||
isArray,
|
||||
isEmpty,
|
||||
} from 'lodash';
|
||||
|
||||
// Internal dependencies.
|
||||
import config from './config';
|
||||
|
||||
|
||||
const isAllowedActionPure = (capabilities, action, restrictByDefault = false) => {
|
||||
if (isEmpty(action)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const defaultValue = restrictByDefault ? 'off' : 'on';
|
||||
|
||||
if (isArray(action)) {
|
||||
return every(action, action => 'on' === get(capabilities, action, defaultValue));
|
||||
}
|
||||
|
||||
return 'on' === get(capabilities, action, defaultValue);
|
||||
};
|
||||
|
||||
export const isAllowedAction = (...args) => isAllowedActionPure(config.capabilities, ...args);
|
@@ -0,0 +1,32 @@
|
||||
// Set global variable to detect new library item creation.
|
||||
window.CodeSnippetItemsLoaded = {};
|
||||
|
||||
export const setCodeSnippetItemsLoaded = (context, flag) => {
|
||||
window.CodeSnippetItemsLoaded = {
|
||||
...CodeSnippetItemsLoaded,
|
||||
[context] : flag,
|
||||
};
|
||||
};
|
||||
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
export const getItemTypeByContext = context => {
|
||||
let type;
|
||||
|
||||
switch (context) {
|
||||
case 'code_html':
|
||||
type = 'et_code_snippet_html_js';
|
||||
break;
|
||||
case 'code_css_no_selector':
|
||||
type = 'et_code_snippet_css_no_selector';
|
||||
break;
|
||||
default:
|
||||
type = 'et_code_snippet_css';
|
||||
break;
|
||||
}
|
||||
|
||||
return type;
|
||||
};
|
||||
|
||||
export const setCodeSnippetsLibraryToken = (token) => {
|
||||
window.globalCloudToken = token;
|
||||
};
|
@@ -0,0 +1 @@
|
||||
export default window.et_code_snippets_data;
|
@@ -0,0 +1,10 @@
|
||||
export const MODAL_TYPE_SAVE_ITEM = 'save_item';
|
||||
export const MODAL_TYPE_EDIT_ITEM = 'edit_item';
|
||||
export const STATE_LOADING = 'loading';
|
||||
export const STATE_ERROR = 'error';
|
||||
export const STATE_SUCCESS = 'success';
|
||||
export const STATE_IDLE = 'idle';
|
||||
|
||||
// Capabilities.
|
||||
export const CAP_PORTABILITY = 'portability';
|
||||
export const CAP_SNIPPET_PORTABILITY = 'et_code_snippets_portability';
|
20
wp-content/themes/Divi/core/code-snippets/app/lib/i18n.js
Normal file
20
wp-content/themes/Divi/core/code-snippets/app/lib/i18n.js
Normal file
@@ -0,0 +1,20 @@
|
||||
// External dependencies.
|
||||
import { get } from 'lodash';
|
||||
|
||||
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(window.et_code_snippets_data.i18n, [path, key], ...args);
|
41
wp-content/themes/Divi/core/code-snippets/app/lib/request.js
Normal file
41
wp-content/themes/Divi/core/code-snippets/app/lib/request.js
Normal file
@@ -0,0 +1,41 @@
|
||||
// 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);
|
||||
|
||||
export const download = url => {
|
||||
const deferred = $.ajax({
|
||||
type: 'GET',
|
||||
url,
|
||||
});
|
||||
|
||||
return Promise.resolve(deferred.promise())
|
||||
.then(response => {
|
||||
if (false === response.success) {
|
||||
return Promise.reject(response || {});
|
||||
}
|
||||
return Promise.resolve(response);
|
||||
});
|
||||
};
|
@@ -0,0 +1,20 @@
|
||||
function insertCodeIntoField({ get, props: { snippet, codeMirrorId, isAppend, skipInsert } }) {
|
||||
if (skipInsert) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
const codeMirrorInstance = jQuery(`#${codeMirrorId}`).next('.CodeMirror')[0].CodeMirror;
|
||||
|
||||
// Insert code into specified codeMirror field.
|
||||
// Append or replace depending on user preferences.
|
||||
if (isAppend) {
|
||||
snippet = codeMirrorInstance.getValue() ? '\n' + snippet : snippet;
|
||||
return Promise.resolve(codeMirrorInstance.replaceRange(snippet, {line: codeMirrorInstance.lastLine()}));
|
||||
} else {
|
||||
return Promise.resolve(codeMirrorInstance.setValue(snippet));
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
insertCodeIntoField,
|
||||
};
|
@@ -0,0 +1,15 @@
|
||||
// External dependencies.
|
||||
import { state } from 'cerebral';
|
||||
|
||||
// Internal dependencies.
|
||||
import { MODAL_TYPE_EDIT_ITEM } from '@code-snippets/lib/constants';
|
||||
|
||||
|
||||
const isEditItemModalOpen = get => {
|
||||
const openModal = get(state`openModal`);
|
||||
return openModal === MODAL_TYPE_EDIT_ITEM;
|
||||
};
|
||||
|
||||
export {
|
||||
isEditItemModalOpen,
|
||||
};
|
@@ -0,0 +1,11 @@
|
||||
import { state } from 'cerebral';
|
||||
|
||||
|
||||
const openModal = type => ({ store }) => store.set(state`openModal`, type);
|
||||
|
||||
const closeModal = () => ({ store }) => store.set(state`openModal`, null);
|
||||
|
||||
export {
|
||||
openModal,
|
||||
closeModal,
|
||||
};
|
@@ -0,0 +1,23 @@
|
||||
import edit from '@code-snippets/store/edit/module';
|
||||
import * as providers from './providers';
|
||||
import * as sequences from './sequences';
|
||||
import * as computed from './computed';
|
||||
|
||||
export default initialState => (
|
||||
{
|
||||
state: {
|
||||
...initialState,
|
||||
openModal: null,
|
||||
itemsLoadedAndCached: false,
|
||||
computed: {
|
||||
...computed,
|
||||
},
|
||||
cloudToken: null,
|
||||
},
|
||||
providers,
|
||||
sequences,
|
||||
modules: {
|
||||
edit,
|
||||
},
|
||||
}
|
||||
);
|
@@ -0,0 +1,155 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
import { post } from '../../lib/request';
|
||||
import config from '../../lib/config';
|
||||
|
||||
|
||||
export const codeSnippetsLibApi = {
|
||||
/**
|
||||
* Gets the Code Snippet library items.
|
||||
*
|
||||
* @param {string} type One of `et_code_snippet` types.
|
||||
* @returns {Array} Resolved value from promise. Array of objects.
|
||||
*/
|
||||
getItems(type) {
|
||||
/* eslint-disable key-spacing */
|
||||
return post({
|
||||
action : 'et_code_snippets_library_get_items',
|
||||
nonce : config.nonces.et_code_snippets_library_get_items,
|
||||
et_code_snippet_type: type,
|
||||
});
|
||||
/* eslint-enable */
|
||||
},
|
||||
|
||||
getItemContent(id, snippetType, contentFormat = 'raw') {
|
||||
/* eslint-disable key-spacing */
|
||||
return post({
|
||||
action : 'et_code_snippets_library_get_item_content',
|
||||
nonce : config.nonces.et_code_snippets_library_get_item_content,
|
||||
et_code_snippet_id : id,
|
||||
et_code_snippet_type : snippetType,
|
||||
et_code_snippet_format : contentFormat,
|
||||
});
|
||||
/* eslint-enable */
|
||||
},
|
||||
|
||||
saveItemContent(id, snippetContent) {
|
||||
/* eslint-disable key-spacing */
|
||||
return post({
|
||||
action : 'et_code_snippets_library_save_item_content',
|
||||
nonce : config.nonces.et_code_snippets_library_save_item_content,
|
||||
et_code_snippet_id : id,
|
||||
et_code_snippet_content: snippetContent,
|
||||
});
|
||||
/* eslint-enable */
|
||||
},
|
||||
|
||||
removeLocalItem(id) {
|
||||
/* eslint-disable key-spacing */
|
||||
return post({
|
||||
action : 'et_code_snippets_toggle_cloud_status',
|
||||
nonce : config.nonces.et_code_snippets_library_toggle_item_location,
|
||||
et_code_snippet_id : id,
|
||||
});
|
||||
/* eslint-enable */
|
||||
},
|
||||
|
||||
/**
|
||||
* Update the Code Snippet library item.
|
||||
*
|
||||
* @param {object} payload Updated item details.
|
||||
* @returns {Array} Resolved value from promise. Array of objects.
|
||||
*/
|
||||
updateItem(payload) {
|
||||
/* eslint-disable key-spacing */
|
||||
return post({
|
||||
action : 'et_code_snippets_library_update_item',
|
||||
nonce : config.nonces.et_code_snippets_library_update_item,
|
||||
payload,
|
||||
});
|
||||
/* eslint-enable */
|
||||
},
|
||||
|
||||
/**
|
||||
* Export Code Snippet library item.
|
||||
*
|
||||
* @param {int} id Item ID.
|
||||
* @param {array} cloudContent Cloud content.
|
||||
* @param {obj} directExport Snippet type and Content.
|
||||
* @returns {Array} Resolved value from promise. Array of objects.
|
||||
*/
|
||||
exportItem(id, cloudContent, directExport) {
|
||||
/* eslint-disable key-spacing */
|
||||
return post({
|
||||
action : 'et_code_snippets_library_export_item',
|
||||
nonce : config.nonces.et_code_snippets_library_export_item,
|
||||
id,
|
||||
cloudContent,
|
||||
directExport,
|
||||
});
|
||||
/* eslint-enable */
|
||||
},
|
||||
|
||||
/**
|
||||
* Download Code Snippet library item.
|
||||
*
|
||||
* @param {int} id Item ID.
|
||||
* @param {string} fileName Item name.
|
||||
* @returns {string} URI string.
|
||||
*/
|
||||
downloadExportFile(id, fileName) {
|
||||
/* eslint-disable key-spacing */
|
||||
const args = {
|
||||
action : 'et_code_snippets_library_export_item_download',
|
||||
nonce : config.nonces.et_code_snippets_library_export_item,
|
||||
fileName,
|
||||
id,
|
||||
};
|
||||
|
||||
return `${config.api}?${jQuery.param(args)}`;
|
||||
/* eslint-enable */
|
||||
},
|
||||
|
||||
/**
|
||||
* Import Code Snippet library item.
|
||||
*
|
||||
* @param {Blob} file File.
|
||||
* @returns {Array} Response.
|
||||
*/
|
||||
importItem(fileData) {
|
||||
const fileContent = JSON.parse(fileData.content);
|
||||
|
||||
return post({
|
||||
action : 'et_code_snippets_library_import_item',
|
||||
nonce : config.nonces.et_code_snippets_library_import_item,
|
||||
fileContent: JSON.stringify(fileContent.data),
|
||||
fileData,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Update Local Tags/Categories and return updated list.
|
||||
*
|
||||
* @param {Blob} file File.
|
||||
* @returns {Array} Response.
|
||||
*/
|
||||
updateFilters(payload) {
|
||||
return post({
|
||||
action : 'et_code_snippets_library_update_terms',
|
||||
nonce : config.nonces.et_code_snippets_library_update_terms,
|
||||
payload,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieve Cloud Token.
|
||||
*
|
||||
* @returns {Array} Response with cloud token.
|
||||
*/
|
||||
getCloudToken() {
|
||||
return post({
|
||||
action : 'et_code_snippets_library_get_token',
|
||||
nonce : config.nonces.et_code_snippets_library_get_token,
|
||||
});
|
||||
}
|
||||
};
|
||||
/* eslint-enable */
|
@@ -0,0 +1,361 @@
|
||||
// External dependencies.
|
||||
import {
|
||||
props,
|
||||
sequence,
|
||||
state,
|
||||
} from 'cerebral';
|
||||
import { set } from 'cerebral/factories';
|
||||
import { isEmpty } from 'lodash';
|
||||
|
||||
// Internal dependencies.
|
||||
import { getItemTypeByContext, getContextByItemType } from '@common-ui/lib/local-library';
|
||||
import { updateTokens, doApiRequest } from '@cloud/app/lib/api';
|
||||
import { handleCloudError } from '@cloud/app/components/app/actions/shared-actions';
|
||||
import { insertCodeIntoField } from './actions';
|
||||
import { setCodeSnippetItemsLoaded, setCodeSnippetsLibraryToken } from '../../lib/code-snippets-library';
|
||||
import { STATE_IDLE, STATE_LOADING, STATE_SUCCESS } from '@code-snippets/lib/constants';
|
||||
|
||||
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
const setLibraryContext = sequence('Set Code Snippets library context', [
|
||||
set(state`context`, props`context`),
|
||||
set(state`itemsLoadedAndCached`, false),
|
||||
]);
|
||||
|
||||
const setCloudToken = sequence('Set cloudToken', [
|
||||
({ get }) => {
|
||||
setCodeSnippetsLibraryToken(get(props`cloudToken`));
|
||||
},
|
||||
]);
|
||||
|
||||
const cacheCloudToken = sequence('Retrieve saved Cloud Access token and save to state', [
|
||||
({ codeSnippetsLibApi, path }) => {
|
||||
return codeSnippetsLibApi.getCloudToken()
|
||||
.then(cloudTokenData => {
|
||||
return path.success({cloudToken: cloudTokenData.accessToken});
|
||||
})
|
||||
.catch(() => path.error());
|
||||
},
|
||||
{
|
||||
success: [
|
||||
setCloudToken
|
||||
],
|
||||
error: [],
|
||||
},
|
||||
]);
|
||||
|
||||
const loadItems = sequence('Load Code Snippets library items', [
|
||||
({ codeSnippetsLibApi, get, path }) => {
|
||||
const context = get(state`context`);
|
||||
const type = getItemTypeByContext(context);
|
||||
|
||||
// Exit if no context provided.
|
||||
if ('' === context) {
|
||||
return path.error();
|
||||
}
|
||||
|
||||
return codeSnippetsLibApi.getItems(type)
|
||||
.then(response => path.success({
|
||||
items: response,
|
||||
}))
|
||||
.catch(() => path.error());
|
||||
},
|
||||
{
|
||||
success: [
|
||||
set(state`items`, props`items`),
|
||||
|
||||
set(state`itemsLoadedAndCached`, true),
|
||||
|
||||
({get}) => { setCodeSnippetItemsLoaded(get(state`context`), true); },
|
||||
],
|
||||
error: [],
|
||||
},
|
||||
]);
|
||||
|
||||
const insertSnippet = sequence('Insert Code Snippet into a field', [
|
||||
({ codeSnippetsLibApi, get, path }) => {
|
||||
const id = get(props`snippetId`);
|
||||
const snippetContent = get(props`snippetContent`);
|
||||
const context = get(state`context`);
|
||||
const type = getItemTypeByContext(context);
|
||||
|
||||
if (false !== snippetContent) {
|
||||
return path.success({ snippet: snippetContent });
|
||||
}
|
||||
|
||||
return codeSnippetsLibApi.getItemContent(id, type)
|
||||
.then(response => path.success({ snippet: response.snippet }))
|
||||
.catch(() => path.error());
|
||||
},
|
||||
{
|
||||
success: [
|
||||
set(state`snippetCode`, props`snippet`),
|
||||
set(state`snippetCodeAppend`, props`isAppend`),
|
||||
insertCodeIntoField,
|
||||
set(state`showLibrary`, false),
|
||||
set(state`context`, ''),
|
||||
() => { jQuery(window).trigger('et_code_snippets_library_close'); },
|
||||
],
|
||||
error: [],
|
||||
},
|
||||
]);
|
||||
|
||||
const getExportedItem = sequence('Get the exported Code Snippet content', [
|
||||
({ codeSnippetsLibApi, path, props: { id, itemType } }) => {
|
||||
|
||||
return codeSnippetsLibApi.getItemContent(id, itemType, 'exported')
|
||||
.then((response => path.success(response)))
|
||||
.catch(() => path.error());
|
||||
},
|
||||
{
|
||||
success: [Promise.resolve(props`response`)],
|
||||
error: [],
|
||||
},
|
||||
]);
|
||||
|
||||
const toggleLibraryItemLocation = sequence('Remove local item from WPDB', [
|
||||
({ codeSnippetsLibApi, path, props: { id } }) => {
|
||||
|
||||
return codeSnippetsLibApi.removeLocalItem(id)
|
||||
.then((response => path.success(response)))
|
||||
.catch(() => path.error());
|
||||
},
|
||||
{
|
||||
success: [Promise.resolve(props`response`)],
|
||||
error: [],
|
||||
},
|
||||
]);
|
||||
|
||||
const updateLocalFilters = sequence('Update Local Filters', [
|
||||
({ codeSnippetsLibApi, path, props: { payload } }) => {
|
||||
|
||||
return codeSnippetsLibApi.updateFilters(payload)
|
||||
.then((response => path.success(response)))
|
||||
.catch(() => path.error());
|
||||
},
|
||||
{
|
||||
success: [Promise.resolve(props`response`)],
|
||||
error: [],
|
||||
},
|
||||
]);
|
||||
|
||||
const updateItem = sequence('Update Code Snippets library item', [
|
||||
({ codeSnippetsLibApi, get, path }) => {
|
||||
const payload = get(props`payload`);
|
||||
|
||||
return codeSnippetsLibApi.updateItem(payload)
|
||||
.then(response => path.success({
|
||||
updatedItem: {
|
||||
success: true,
|
||||
data: response,
|
||||
},
|
||||
}))
|
||||
.catch(() => path.error());
|
||||
},
|
||||
{
|
||||
success: [Promise.resolve(props`updatedItem`)],
|
||||
error: []
|
||||
},
|
||||
]);
|
||||
|
||||
const setShowLibrary = sequence('Set Code Snippets show library', [
|
||||
set(state`showLibrary`, props`toggle`),
|
||||
]);
|
||||
|
||||
const resetSnippetCode = sequence('Reset the saved code value', [
|
||||
set(state`snippetCode`, ''),
|
||||
]);
|
||||
|
||||
const setShowSaveModal = sequence('Toggle Code Snippets save modal', [
|
||||
set(state`showSave`, props`toggle`),
|
||||
]);
|
||||
|
||||
const openPortablity = sequence('Open Code Snippets portability modal', [
|
||||
set(state`showPortability`, true),
|
||||
]);
|
||||
|
||||
const closePortability = sequence('Close Code Snippets portability modal', [
|
||||
set(state`showPortability`, false),
|
||||
set(state`importError`, false),
|
||||
]);
|
||||
|
||||
const exportSnippet = sequence('Export code snippet', [
|
||||
({ codeSnippetsLibApi, path, props: { id, cloudContent, directExport } }) => {
|
||||
return codeSnippetsLibApi.exportItem(id, cloudContent, directExport)
|
||||
.then(() => path.success())
|
||||
.catch(() => path.error());
|
||||
},
|
||||
{
|
||||
success: [
|
||||
({ codeSnippetsLibApi, props: { id, fileName } }) => {
|
||||
const downloadURI = codeSnippetsLibApi.downloadExportFile(id, fileName);
|
||||
|
||||
window.location.assign(downloadURI);
|
||||
|
||||
window.ETCloudApp.emitSignal({
|
||||
signal: 'finishDownload',
|
||||
data: {},
|
||||
});
|
||||
},
|
||||
|
||||
({ props: { directExport } }) => {
|
||||
if (!isEmpty(directExport)) {
|
||||
jQuery(window).trigger('et_code_snippets_library_close');
|
||||
}
|
||||
},
|
||||
|
||||
closePortability,
|
||||
],
|
||||
error: []
|
||||
},
|
||||
]);
|
||||
|
||||
const importSnippetToCloud = sequence('Import code snippet to cloud', [
|
||||
({ get, store, props: { importFile } }) => updateTokens().then(refreshResponse => {
|
||||
const accessToken = refreshResponse['accessToken'];
|
||||
const callback = (newItem) => {
|
||||
if (newItem.error) {
|
||||
handleCloudError(newItem, get, store, null);
|
||||
return;
|
||||
}
|
||||
|
||||
store.set(state`showPortability`, false);
|
||||
store.set(state`importError`, false),
|
||||
store.set(state`importState`, STATE_SUCCESS),
|
||||
|
||||
// Trigger Cloud Items Refresh in the cloud snippet library modal.
|
||||
window.ETCloudApp.emitSignal({
|
||||
signal: 'refreshCloudItems',
|
||||
data: {},
|
||||
});
|
||||
};
|
||||
|
||||
if (!isEmpty(accessToken)) {
|
||||
store.set(state`cloudToken`, accessToken);
|
||||
|
||||
const newCloudItem = {
|
||||
title : importFile.title,
|
||||
content: importFile.content,
|
||||
status : 'publish',
|
||||
meta : {},
|
||||
};
|
||||
|
||||
const resource = getContextByItemType(importFile.type);
|
||||
const providedBaseUrl = window.ETCloudApp.getActiveFolderEndpoint();
|
||||
doApiRequest({ type: 'post', resource, accessToken, providedBaseUrl }, newCloudItem).then(callback);
|
||||
} else {
|
||||
set(state`importState`, STATE_IDLE),
|
||||
store.set(state`cloudStatus`, { error: 'auth_error' });
|
||||
}
|
||||
}),
|
||||
]);
|
||||
|
||||
const importSnippetToLocal = sequence('Import code snippet to local', [
|
||||
({ codeSnippetsLibApi, path, props: { importFile } }) => {
|
||||
return codeSnippetsLibApi.importItem(importFile)
|
||||
.then(() => path.success())
|
||||
.catch(() => path.error());
|
||||
},
|
||||
{
|
||||
success: [
|
||||
closePortability,
|
||||
set(state`importState`, STATE_SUCCESS),
|
||||
() => { jQuery(window).trigger('et_cloud_refresh_local_items'); },
|
||||
],
|
||||
error: [
|
||||
set(state`importState`, STATE_IDLE),
|
||||
set(state`importError`, true),
|
||||
]
|
||||
},
|
||||
]);
|
||||
|
||||
const decideSnippetImport = sequence('Decide import code snippet', [
|
||||
set(state`importState`, STATE_LOADING),
|
||||
|
||||
({ path, props: { importToCloud } }) => {
|
||||
if (importToCloud) {
|
||||
return path.cloud();
|
||||
} else {
|
||||
return path.local();
|
||||
}
|
||||
},
|
||||
{
|
||||
cloud: [
|
||||
importSnippetToCloud,
|
||||
],
|
||||
local: [
|
||||
importSnippetToLocal,
|
||||
],
|
||||
}
|
||||
]);
|
||||
|
||||
const importSnippet = sequence('Import code snippet', [
|
||||
({ path, props: { importFile } }) => {
|
||||
if (importFile) return path.yes();
|
||||
},
|
||||
{
|
||||
yes: [
|
||||
decideSnippetImport,
|
||||
],
|
||||
}
|
||||
]);
|
||||
|
||||
const downloadSnippetContent = sequence('Download Snippet Content', [
|
||||
() => window.ETCloudApp.setCodeSnippetPreviewState({ codeSnippetPreviewState: STATE_LOADING }),
|
||||
// eslint-disable-next-line no-shadow
|
||||
({ codeSnippetsLibApi, get, path, props: { snippetId, snippetContent, needImageRefresh, item_location = '' } }) => {
|
||||
// When a local item is downloaded, snippetId shall be available.
|
||||
if (! snippetContent && 'cloud' !== item_location ) {
|
||||
const context = get(state`context`);
|
||||
const type = getItemTypeByContext(context);
|
||||
|
||||
return codeSnippetsLibApi.getItemContent(snippetId, type)
|
||||
.then(response => path.success({ snippet: response.snippet, itemId: snippetId }))
|
||||
.catch(() => path.error());
|
||||
}
|
||||
|
||||
// When a Cloud item is downloaded, snippetContent shall be available.
|
||||
const snippet = snippetContent;
|
||||
return path.success({ snippet, itemId: snippetId, needImageRefresh });
|
||||
},
|
||||
{
|
||||
success: [
|
||||
({ props: { snippet, itemId, needImageRefresh } }) => {
|
||||
window.ETCloudApp.emitSignal({
|
||||
signal: 'renderCodeSnippetPreview',
|
||||
data: { snippet, itemId, needImageRefresh },
|
||||
});
|
||||
},
|
||||
],
|
||||
error: [
|
||||
|
||||
],
|
||||
},
|
||||
() => window.ETCloudApp.setCodeSnippetPreviewState({ codeSnippetPreviewState: '' }),
|
||||
]);
|
||||
|
||||
const closeModal = sequence('Close open modal', [
|
||||
set(state`openModal`, null),
|
||||
]);
|
||||
|
||||
export {
|
||||
closePortability,
|
||||
downloadSnippetContent,
|
||||
exportSnippet,
|
||||
getExportedItem,
|
||||
importSnippet,
|
||||
insertSnippet,
|
||||
loadItems,
|
||||
openPortablity,
|
||||
resetSnippetCode,
|
||||
setLibraryContext,
|
||||
setShowLibrary,
|
||||
setShowSaveModal,
|
||||
toggleLibraryItemLocation,
|
||||
updateItem,
|
||||
updateLocalFilters,
|
||||
closeModal,
|
||||
setCloudToken,
|
||||
cacheCloudToken,
|
||||
};
|
||||
/* eslint-enable */
|
@@ -0,0 +1,37 @@
|
||||
// External dependencies.
|
||||
import {
|
||||
state,
|
||||
} from 'cerebral';
|
||||
import { noop } from 'lodash';
|
||||
|
||||
// Internal dependencies.
|
||||
import { saveToCloudPure } from '@cloud/app/lib/api';
|
||||
|
||||
|
||||
const saveToCloud = ({ get, props, path }) => {
|
||||
const itemId = get(state`edit.item.id`);
|
||||
const context = get(state`context`);
|
||||
let snippetContent = get(state`edit.content`);
|
||||
|
||||
snippetContent = {
|
||||
...snippetContent,
|
||||
data: props.content,
|
||||
};
|
||||
|
||||
|
||||
return saveToCloudPure(context, { content: JSON.stringify(snippetContent) }, [], noop, itemId)
|
||||
.then(() => path.success())
|
||||
.catch(() => path.error());
|
||||
};
|
||||
|
||||
const saveToLocal = ({ codeSnippetsLibApi, path, props: { content }, get }) => {
|
||||
const itemId = get(state`edit.item.id`);
|
||||
return codeSnippetsLibApi.saveItemContent(itemId, content)
|
||||
.then(response => path.success({ snippet: response.snippet }))
|
||||
.catch(() => path.error());
|
||||
};
|
||||
|
||||
export {
|
||||
saveToCloud,
|
||||
saveToLocal,
|
||||
};
|
@@ -0,0 +1,15 @@
|
||||
// Internal dependencies.
|
||||
import * as sequences from './sequences';
|
||||
|
||||
|
||||
export default {
|
||||
state: {
|
||||
item: null,
|
||||
snippet: '',
|
||||
saveState: null,
|
||||
progress: 0,
|
||||
content: {},
|
||||
context: '',
|
||||
},
|
||||
sequences,
|
||||
};
|
@@ -0,0 +1,133 @@
|
||||
// External dependencies.
|
||||
import {
|
||||
props,
|
||||
sequence,
|
||||
state,
|
||||
} from 'cerebral';
|
||||
import {
|
||||
set,
|
||||
wait,
|
||||
when,
|
||||
} from 'cerebral/factories';
|
||||
|
||||
// Internal dependencies.
|
||||
import {
|
||||
MODAL_TYPE_EDIT_ITEM,
|
||||
STATE_ERROR,
|
||||
STATE_LOADING,
|
||||
STATE_SUCCESS,
|
||||
} from '@code-snippets/lib/constants';
|
||||
import {
|
||||
closeModal,
|
||||
openModal,
|
||||
} from '@code-snippets/store/code-snippets-library/factories';
|
||||
import { downloadSnippetContent } from '@code-snippets/store/code-snippets-library/sequences';
|
||||
import {
|
||||
saveToCloud,
|
||||
saveToLocal,
|
||||
} from './actions';
|
||||
|
||||
|
||||
const openLocalItemEditor = sequence('Open local item editor', [
|
||||
set(state`edit.progress`, 10),
|
||||
({ codeSnippetsLibApi, path, props: { item } }) => codeSnippetsLibApi.getItemContent(item.id, item.type)
|
||||
.then(response => path.success({ snippet: response.snippet }))
|
||||
.catch(() => path.error()),
|
||||
{
|
||||
success: [
|
||||
set(state`edit.progress`, 90),
|
||||
wait(500),
|
||||
set(state`edit.progress`, 100),
|
||||
wait(200),
|
||||
({ store, props: { snippet } }) => {
|
||||
store.set(state`edit.snippet`, snippet);
|
||||
},
|
||||
set(state`edit.progress`, 0),
|
||||
],
|
||||
error: [
|
||||
set(state`edit.progress`, 0),
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
const openCloudItemEditor = sequence('Open cloud item editor', [
|
||||
set(state`edit.snippet`, props`content.data`),
|
||||
set(state`edit.content`, props`content`),
|
||||
]);
|
||||
|
||||
const openEditModal = sequence('Open modal to edit code snippet', [
|
||||
openModal(MODAL_TYPE_EDIT_ITEM),
|
||||
set(state`edit.item`, props`item`),
|
||||
set(state`edit.context`, props`context`),
|
||||
when(props`item.item_location`, item_location => 'cloud' === item_location),
|
||||
{
|
||||
true: [
|
||||
openCloudItemEditor,
|
||||
],
|
||||
false: [
|
||||
openLocalItemEditor,
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
const closeEditModal = sequence('Close snippet editor modal', [
|
||||
closeModal(),
|
||||
set(state`edit.saveState`, null),
|
||||
set(state`edit.item`, null),
|
||||
set(state`edit.snippet`, ''),
|
||||
]);
|
||||
|
||||
|
||||
const saveEditedItemSuccess = sequence('Edited item Saved', [
|
||||
() => ({needImageRefresh: true}),
|
||||
downloadSnippetContent,
|
||||
set(state`edit.saveState`, STATE_SUCCESS),
|
||||
wait(500),
|
||||
closeEditModal,
|
||||
]);
|
||||
|
||||
const saveEditedItemError = sequence('Error while saving edited item', [
|
||||
set(state`edit.saveState`, STATE_ERROR),
|
||||
wait(500),
|
||||
closeEditModal,
|
||||
]);
|
||||
|
||||
const saveEditedContent = sequence('Save editted content', [
|
||||
set(state`edit.saveState`, STATE_LOADING),
|
||||
when(state`edit.item.item_location`, item_location => 'cloud' === item_location),
|
||||
{
|
||||
true: [
|
||||
saveToCloud,
|
||||
{
|
||||
success: [
|
||||
({ get, props: { content } }) => ({ snippetId: get(state`edit.item.id`), snippetContent: content }),
|
||||
({ get }) => ({needImageRefresh: true, item_location: get(state`edit.item.item_location`)}),
|
||||
saveEditedItemSuccess,
|
||||
],
|
||||
error: [
|
||||
saveEditedItemError,
|
||||
],
|
||||
},
|
||||
],
|
||||
false: [
|
||||
saveToLocal,
|
||||
{
|
||||
success: [
|
||||
({ get }) => ({ snippetId: get(state`edit.item.id`) }),
|
||||
saveEditedItemSuccess,
|
||||
],
|
||||
error: [
|
||||
saveEditedItemError,
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
]);
|
||||
|
||||
|
||||
export {
|
||||
openEditModal,
|
||||
closeEditModal,
|
||||
saveEditedContent,
|
||||
};
|
@@ -0,0 +1,5 @@
|
||||
// Internal dependencies.
|
||||
import codeSnippetsLibrary from './code-snippets-library/module';
|
||||
|
||||
|
||||
export default codeSnippetsLibrary;
|
138
wp-content/themes/Divi/core/code-snippets/code-snippets-app.php
Normal file
138
wp-content/themes/Divi/core/code-snippets/code-snippets-app.php
Normal file
@@ -0,0 +1,138 @@
|
||||
<?php
|
||||
/**
|
||||
* Code Snippets App helpers.
|
||||
*
|
||||
* @since 4.19.0
|
||||
*
|
||||
* @package Divi
|
||||
*/
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* ET_Code_Snippets_App class.
|
||||
*
|
||||
* Nonces and i18n strings needed for Code Snippets Library Cloud app.
|
||||
*
|
||||
* @package Divi
|
||||
*/
|
||||
class ET_Code_Snippets_App {
|
||||
/**
|
||||
* Class instance.
|
||||
*
|
||||
* @var ET_Code_Snippets_App
|
||||
*/
|
||||
private static $_instance;
|
||||
|
||||
/**
|
||||
* Get the class instance.
|
||||
*
|
||||
* @since 4.19.0
|
||||
*
|
||||
* @return ET_Code_Snippets_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();
|
||||
|
||||
// phpcs:disable WordPress.Arrays.MultipleStatementAlignment.DoubleArrowNotAligned, WordPress.Arrays.MultipleStatementAlignment.LongIndexSpaceBeforeDoubleArrow -- Aligned manually.
|
||||
return [
|
||||
'i18n' => [ 'library' => require ET_CORE_PATH . '/i18n/library.php' ],
|
||||
'api' => admin_url( 'admin-ajax.php' ),
|
||||
'capabilities' => isset( $role_capabilities[ $user_role ] ) ? $role_capabilities[ $user_role ] : array(),
|
||||
'nonces' => [
|
||||
'et_code_snippets_library_get_items' => wp_create_nonce( 'et_code_snippets_library_get_items' ),
|
||||
'et_code_snippets_library_get_item_content' => wp_create_nonce( 'et_code_snippets_library_get_item_content' ),
|
||||
'et_code_snippets_library_update_item' => wp_create_nonce( 'et_code_snippets_library_update_item' ),
|
||||
'et_code_snippets_library_toggle_item_location' => wp_create_nonce( 'et_code_snippets_library_toggle_item_location' ),
|
||||
'et_code_snippets_library_export_item' => wp_create_nonce( 'et_code_snippets_library_export_item' ),
|
||||
'et_code_snippets_library_import_item' => wp_create_nonce( 'et_code_snippets_library_import_item' ),
|
||||
'et_code_snippets_library_save_item_content' => wp_create_nonce( 'et_code_snippets_library_save_item_content' ),
|
||||
'et_code_snippets_library_update_terms' => wp_create_nonce( 'et_code_snippets_library_update_terms' ),
|
||||
'et_code_snippets_library_get_token' => wp_create_nonce( 'et_code_snippets_library_get_token' ),
|
||||
'et_code_snippets_save_to_local_library' => wp_create_nonce( 'et_code_snippets_save_to_local_library' ),
|
||||
],
|
||||
];
|
||||
// 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.
|
||||
*
|
||||
* @since 4.19.0
|
||||
*
|
||||
* @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.
|
||||
$CORE_VERSION = defined( 'ET_CORE_VERSION' ) ? ET_CORE_VERSION : '';
|
||||
$ET_DEBUG = defined( 'ET_DEBUG' ) && ET_DEBUG;
|
||||
$DEBUG = $ET_DEBUG;
|
||||
|
||||
$home_url = wp_parse_url( get_site_url() );
|
||||
$build_dir_uri = ET_CORE_URL . 'build';
|
||||
$common_scripts = ET_COMMON_URL . '/scripts';
|
||||
$cache_buster = $DEBUG ? mt_rand() / mt_getrandmax() : $CORE_VERSION; // phpcs:ignore WordPress.WP.AlternativeFunctions.rand_mt_rand -- mt_rand() should do for cache busting.
|
||||
$asset_path = ET_CORE_PATH . 'build/et-core-app.bundle.js';
|
||||
|
||||
if ( file_exists( $asset_path ) ) {
|
||||
wp_enqueue_style( 'et-code-snippets-styles', "{$build_dir_uri}/et-core-app.bundle.css", [], (string) $cache_buster );
|
||||
}
|
||||
|
||||
wp_enqueue_script( 'es6-promise', "{$common_scripts}/es6-promise.auto.min.js", [], '4.2.2', true );
|
||||
|
||||
$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']}:31499/et-core-app.bundle.js" : "{$build_dir_uri}/et-core-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-code-snippets-app',
|
||||
$BUNDLE_URI,
|
||||
$BUNDLE_DEPS,
|
||||
(string) $cache_buster,
|
||||
true
|
||||
);
|
||||
|
||||
wp_localize_script(
|
||||
'et-code-snippets-app',
|
||||
'et_code_snippets_data',
|
||||
self::get_cloud_helpers()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ET_Code_Snippets_App::instance();
|
||||
// phpcs:enable
|
@@ -0,0 +1,230 @@
|
||||
<?php
|
||||
/**
|
||||
* Local Library API.
|
||||
*
|
||||
* @since 4.21.0
|
||||
*
|
||||
* @package Divi
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* ET_Code_Snippets_Library_Local utility class.
|
||||
*
|
||||
* Item can be a layout, a template, a theme option, a code snippet, etc.
|
||||
*
|
||||
* @since 4.21.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
class ET_Code_Snippets_Library_Local extends ET_Item_Library_Local {
|
||||
/**
|
||||
* Gets the class instance.
|
||||
*
|
||||
* @since 4.21.0
|
||||
*
|
||||
* @return ET_Code_Snippets_Library_Local
|
||||
*/
|
||||
public static function instance() {
|
||||
if ( ! self::$_instance ) {
|
||||
self::$_instance = new self();
|
||||
}
|
||||
|
||||
return self::$_instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->post_type = ET_CODE_SNIPPET_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();
|
||||
$code_snippet_items = ET_Builder_Post_Type_Code_Snippet::instance();
|
||||
$code_snippet_tags = ET_Builder_Post_Taxonomy_LayoutTag::instance();
|
||||
$code_snippet_categories = ET_Builder_Post_Taxonomy_LayoutCategory::instance();
|
||||
$code_snippet_types = ET_Builder_Post_Taxonomy_CodeSnippetType::instance();
|
||||
|
||||
$item_categories = [];
|
||||
$item_tags = [];
|
||||
$items = [];
|
||||
$index = 0;
|
||||
|
||||
$query_posts = $code_snippet_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;
|
||||
$types = wp_get_post_terms( $item->id, $code_snippet_types->name );
|
||||
|
||||
if ( ! $types ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$item->type = $types[0]->slug;
|
||||
|
||||
if ( $item_type !== $item->type ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$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' );
|
||||
}
|
||||
|
||||
$item->slug = $post->post_name;
|
||||
$item->url = esc_url( wp_make_link_relative( get_permalink( $post ) ) );
|
||||
|
||||
$item->short_name = '';
|
||||
$item->is_default = get_post_meta( $item->id, '_et_default', true );
|
||||
$item->description = '';
|
||||
$item->is_favorite = $code_snippet_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,
|
||||
$code_snippet_categories->name,
|
||||
'category'
|
||||
);
|
||||
|
||||
$this->process_item_taxonomy(
|
||||
$post,
|
||||
$item,
|
||||
$index,
|
||||
$item_tags,
|
||||
$code_snippet_tags->name,
|
||||
'tag'
|
||||
);
|
||||
|
||||
$items[] = $item;
|
||||
|
||||
$index++;
|
||||
}
|
||||
|
||||
return [
|
||||
'categories' => $this->get_processed_terms( $code_snippet_categories->name ),
|
||||
'tags' => $this->get_processed_terms( $code_snippet_tags->name ),
|
||||
'items' => $items,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs item exceptional updates.
|
||||
*
|
||||
* @param array $payload Payload.
|
||||
* @param array $updated_data Updated data.
|
||||
*
|
||||
* @since 4.21.0
|
||||
*
|
||||
* @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();
|
||||
$code_snippets_type = ET_Builder_Post_Taxonomy_CodeSnippetType::instance();
|
||||
|
||||
if ( in_array( $update_type, [ 'duplicate', 'duplicate_and_delete' ], true ) ) {
|
||||
if ( isset( $update_details['content'] ) ) {
|
||||
$content = isset( $update_details['content']['data'] ) ? $update_details['content']['data'] : '';
|
||||
$snippet_type = isset( $update_details['content']['snippet_type'] ) ? sanitize_text_field( $update_details['content']['snippet_type'] ) : 'et_code_snippet_html_js';
|
||||
} else {
|
||||
$content = get_the_content( null, false, $item_id );
|
||||
$snippet_type = wp_get_post_terms( $item_id, $code_snippets_type->name, array( 'fields' => 'names' ) );
|
||||
$snippet_type = is_wp_error( $snippet_type ) || '' === $snippet_type ? 'et_code_snippet_html_js' : sanitize_text_field( $snippet_type[0] );
|
||||
}
|
||||
|
||||
$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'],
|
||||
$code_snippets_type->name => $snippet_type,
|
||||
),
|
||||
);
|
||||
|
||||
$updated_data['newItem'] = wp_insert_post( $new_item );
|
||||
}
|
||||
|
||||
$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,319 @@
|
||||
<?php
|
||||
/**
|
||||
* Code Snippets Library API.
|
||||
*
|
||||
* @package Divi
|
||||
*/
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the terms list and processes it into desired format.
|
||||
*
|
||||
* @since 4.19.0
|
||||
*
|
||||
* @param string $tax_name Term Name.
|
||||
*
|
||||
* @return array $terms_by_id
|
||||
*/
|
||||
function et_code_snippets_library_get_processed_terms( $tax_name ) {
|
||||
$terms = get_terms( $tax_name, [ 'hide_empty' => false ] );
|
||||
$terms_by_id = [];
|
||||
|
||||
if ( is_wp_error( $terms ) || empty( $terms ) ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
foreach ( $terms as $term ) {
|
||||
$term_id = $term->term_id;
|
||||
|
||||
$terms_by_id[ $term_id ]['id'] = $term_id;
|
||||
$terms_by_id[ $term_id ]['name'] = $term->name;
|
||||
$terms_by_id[ $term_id ]['slug'] = $term->slug;
|
||||
$terms_by_id[ $term_id ]['count'] = $term->count;
|
||||
}
|
||||
|
||||
return $terms_by_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes item taxonomies for inclusion in the theme builder library UI items data.
|
||||
*
|
||||
* @since 4.19.0
|
||||
*
|
||||
* @param WP_POST $post Unprocessed item.
|
||||
* @param object $item Currently processing item.
|
||||
* @param int $index The item's index position.
|
||||
* @param array[] $item_terms Processed items.
|
||||
* @param string $taxonomy_name Item name.
|
||||
* @param string $type Item type.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function et_code_snippets_library_process_item_taxonomy( $post, $item, $index, &$item_terms, $taxonomy_name, $type ) {
|
||||
$terms = wp_get_post_terms( $post->ID, $taxonomy_name );
|
||||
|
||||
if ( ! $terms ) {
|
||||
if ( 'category' === $type ) {
|
||||
$item->category_slug = 'uncategorized';
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ( $terms as $term ) {
|
||||
$term_name = et_core_intentionally_unescaped( $term->name, 'react_jsx' );
|
||||
|
||||
if ( ! isset( $item_terms[ $term->term_id ] ) ) {
|
||||
$item_terms[ $term->term_id ] = array(
|
||||
'id' => $term->term_id,
|
||||
'name' => $term_name,
|
||||
'slug' => $term->slug,
|
||||
'items' => array(),
|
||||
);
|
||||
}
|
||||
|
||||
$item_terms[ $term->term_id ]['items'][] = $index;
|
||||
|
||||
if ( 'category' === $type ) {
|
||||
$item->categories[] = $term_name;
|
||||
} else {
|
||||
$item->tags[] = $term_name;
|
||||
}
|
||||
|
||||
$item->{$type . '_ids'}[] = $term->term_id;
|
||||
|
||||
if ( ! isset( $item->{$type . '_slug'} ) ) {
|
||||
$item->{$type . '_slug'} = $term->slug;
|
||||
}
|
||||
|
||||
$id = get_post_meta( $post->ID, "_primary_{$taxonomy_name}", true );
|
||||
|
||||
if ( $id ) {
|
||||
// $id is a string, $term->term_id is an int.
|
||||
if ( $id === $term->term_id ) {
|
||||
// This is the primary term (used in the item URL).
|
||||
$item->{$type . '_slug'} = $term->slug;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize txonomies.
|
||||
*
|
||||
* @since 4.19.0
|
||||
*
|
||||
* @param array $taxonomies Array of id for categories and tags.
|
||||
*
|
||||
* @return array Sanitized value.
|
||||
*/
|
||||
function et_code_snippets_library_sanitize_taxonomies( $taxonomies ) {
|
||||
if ( empty( $taxonomies ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
return array_unique(
|
||||
array_map( 'intval', $taxonomies )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all terms of an item and merge any newly passed IDs with the list.
|
||||
*
|
||||
* @since 4.19.0
|
||||
*
|
||||
* @param string $new_terms_list List of new terms.
|
||||
* @param array $taxonomies Taxonomies.
|
||||
* @param string $taxonomy_name Taxonomy name.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function et_code_snippets_library_create_and_get_all_item_terms( $new_terms_list, $taxonomies, $taxonomy_name ) {
|
||||
$new_names_array = explode( ',', $new_terms_list );
|
||||
|
||||
foreach ( $new_names_array as $new_name ) {
|
||||
if ( '' !== $new_name ) {
|
||||
$new_term = wp_insert_term( $new_name, $taxonomy_name );
|
||||
|
||||
if ( ! is_wp_error( $new_term ) ) {
|
||||
$taxonomies[] = $new_term['term_id'];
|
||||
} elseif (
|
||||
! empty( $new_term->error_data ) &&
|
||||
! empty( $new_term->error_data['term_exists'] )
|
||||
) {
|
||||
$taxonomies[] = $new_term->error_data['term_exists'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $taxonomies;
|
||||
}
|
||||
|
||||
/**
|
||||
* Export the Code Snippets library item from cloud.
|
||||
*
|
||||
* @since 4.19.0
|
||||
*
|
||||
* @param array $cloud_content Optional cloud content.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function et_code_snippets_library_export_cloud_item( $cloud_content ) {
|
||||
if ( ! current_user_can( 'edit_posts' ) ) {
|
||||
wp_die();
|
||||
}
|
||||
|
||||
$content = array( 'context' => 'et_code_snippet' );
|
||||
$content['snippet_type'] = $cloud_content['snippet_type'];
|
||||
$content['data'] = $cloud_content['data'];
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Export the Code Snippets library item local item.
|
||||
*
|
||||
* @since 4.19.0
|
||||
*
|
||||
* @param int $post_id Item ID.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function et_code_snippets_library_export_local_item( $post_id ) {
|
||||
if ( ! current_user_can( 'edit_post', $post_id ) ) {
|
||||
wp_die();
|
||||
}
|
||||
|
||||
$snippet = get_post( $post_id );
|
||||
$snippet_type = current( wp_get_post_terms( $post_id, 'et_code_snippet_type' ) );
|
||||
|
||||
$content = array( 'context' => 'et_code_snippet' );
|
||||
$content['snippet_type'] = $snippet_type->slug;
|
||||
$content['data'] = $snippet->post_content;
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Export the Code Snippets library item directly.
|
||||
*
|
||||
* @since 4.19.0
|
||||
* @param array $direct_export Contain snippet-type and content.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function et_code_snippets_library_export_directly( $direct_export ) {
|
||||
if ( ! current_user_can( 'edit_posts' ) ) {
|
||||
wp_die();
|
||||
}
|
||||
|
||||
if ( ! trim( $direct_export['content'] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$content = array( 'context' => 'et_code_snippet' );
|
||||
$content['snippet_type'] = $direct_export['snippet_type'];
|
||||
$content['data'] = stripslashes_deep( $direct_export['content'] );
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Export the Code Snippets library item.
|
||||
*
|
||||
* @since 4.19.0
|
||||
*
|
||||
* @param int $id Item ID.
|
||||
* @param array $cloud_content Optional cloud content.
|
||||
* @param array $direct_export Contain snippet-type and content.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function et_code_snippets_library_export_item_data( $id, $cloud_content, $direct_export ) {
|
||||
if ( ! current_user_can( 'edit_posts' ) ) {
|
||||
wp_die();
|
||||
}
|
||||
|
||||
if ( empty( $id ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$id = absint( $id );
|
||||
|
||||
if ( empty( $direct_export ) ) {
|
||||
$export_content = empty( $cloud_content ) ?
|
||||
et_code_snippets_library_export_local_item( $id )
|
||||
:
|
||||
et_code_snippets_library_export_cloud_item( $cloud_content );
|
||||
} else {
|
||||
$export_content = et_code_snippets_library_export_directly( $direct_export );
|
||||
}
|
||||
|
||||
if ( empty( $export_content ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$transient = 'et_code_snippet_export_' . get_current_user_id() . '_' . $id;
|
||||
set_transient( $transient, $export_content, 60 * 60 * 24 );
|
||||
|
||||
return $export_content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Import the Code Snippets library item.
|
||||
*
|
||||
* @since 4.19.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function et_code_snippets_library_import_item_data() {
|
||||
if ( ! current_user_can( 'edit_posts' ) || ! isset( $_POST['fileData'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce verified in `et_builder_security_check` before calling this function.
|
||||
return false;
|
||||
}
|
||||
|
||||
// phpcs:disable ET.Sniffs.ValidatedSanitizedInput.InputNotSanitized -- wp_insert_post function does sanitization.
|
||||
$file_data = $_POST['fileData']; // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce verified in `et_builder_security_check` before calling this function.
|
||||
$file_name = sanitize_text_field( $file_data['title'] );
|
||||
$file_content = isset( $_POST['fileContent'] ) ? json_decode( stripslashes( $_POST['fileContent'] ), true ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce verified in `et_builder_security_check` before calling this function.
|
||||
// phpcs:enable
|
||||
|
||||
if ( ! isset( $file_data['type'] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$snippet = array(
|
||||
'item_name' => $file_name,
|
||||
'item_type' => $file_data['type'],
|
||||
'content' => $file_content,
|
||||
);
|
||||
|
||||
return et_code_snippets_save_to_local_library( $snippet );
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a code snippet to local library.
|
||||
*
|
||||
* @param array $item Item data.
|
||||
*/
|
||||
function et_code_snippets_save_to_local_library( $item ) {
|
||||
$_ = et_();
|
||||
$item_name = sanitize_text_field( $_->array_get( $item, 'item_name', '' ) );
|
||||
$content = $_->array_get( $item, 'content', '' );
|
||||
|
||||
$post_id = wp_insert_post(
|
||||
array(
|
||||
'post_type' => ET_CODE_SNIPPET_POST_TYPE,
|
||||
'post_status' => 'publish',
|
||||
'post_title' => $item_name,
|
||||
'post_content' => $content,
|
||||
)
|
||||
);
|
||||
|
||||
et_local_library_set_item_taxonomy( $post_id, $item );
|
||||
|
||||
return $post_id;
|
||||
}
|
42
wp-content/themes/Divi/core/code-snippets/code-snippets.php
Normal file
42
wp-content/themes/Divi/core/code-snippets/code-snippets.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
/**
|
||||
* Code snippets quick feature entry file.
|
||||
*
|
||||
* Divi Cloud Code Snippets
|
||||
*
|
||||
* @link https://github.com/elegantthemes/Divi/issues/26232
|
||||
*
|
||||
* @package Divi
|
||||
* @subpackage Core
|
||||
* @since 4.19.0
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ET_CODE_SNIPPETS_DIR' ) ) {
|
||||
define( 'ET_CODE_SNIPPETS_DIR', ET_CORE_PATH . 'code-snippets/' );
|
||||
}
|
||||
|
||||
require_once trailingslashit( ET_CODE_SNIPPETS_DIR ) . 'constants.php';
|
||||
require_once trailingslashit( ET_CODE_SNIPPETS_DIR ) . 'code-snippets-library.php';
|
||||
require_once trailingslashit( ET_CODE_SNIPPETS_DIR ) . 'api.php';
|
||||
|
||||
if ( ! function_exists( 'et_init_code_snippets_library' ) ) :
|
||||
/**
|
||||
* Init Code Snippets Library.
|
||||
*
|
||||
* Class `ET_Builder_Post_Taxonomy_LayoutCategory` must be initalized
|
||||
* before `ET_Builder_Code_Snippets_Library` because of the internal dependency.
|
||||
*
|
||||
* Since `ET_Builder_Post_Taxonomy_LayoutCategory is initialized using
|
||||
* `add_action( 'init', 'et_setup_builder', 0 );`,
|
||||
*
|
||||
* We initialize `ET_Builder_Code_Snippets_Library` using
|
||||
* `add_action( 'init', 'et_init_code_snippets_library', 10 );`
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function et_init_code_snippets_library() {
|
||||
require_once trailingslashit( ET_CODE_SNIPPETS_DIR ) . 'CodeSnippetsLibrary.php';
|
||||
}
|
||||
endif;
|
||||
|
||||
add_action( 'init', 'et_init_code_snippets_library' );
|
20
wp-content/themes/Divi/core/code-snippets/constants.php
Normal file
20
wp-content/themes/Divi/core/code-snippets/constants.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
/**
|
||||
* "Code Snippets" quick feature constants file.
|
||||
*
|
||||
* Divi Cloud Code Snippet constants.
|
||||
*
|
||||
* @link https://github.com/elegantthemes/Divi/issues/26232
|
||||
*
|
||||
* @package Divi
|
||||
* @subpackage Cloud
|
||||
* @since 4.19.0
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ET_CODE_SNIPPET_POST_TYPE' ) ) {
|
||||
define( 'ET_CODE_SNIPPET_POST_TYPE', 'et_code_snippet' );
|
||||
}
|
||||
|
||||
if ( ! defined( 'ET_CODE_SNIPPET_TAXONOMY_TYPE' ) ) {
|
||||
define( 'ET_CODE_SNIPPET_TAXONOMY_TYPE', 'et_code_snippet_type' );
|
||||
}
|
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
/**
|
||||
* Init ET_CODE_SNIPPET_TAXONOMY_TYPE taxonomy.
|
||||
*
|
||||
* @since 4.19.0
|
||||
*
|
||||
* @package Divi
|
||||
* @subpackage Builder
|
||||
* @since 4.19.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Register ET_CODE_SNIPPET_TAXONOMY_TYPE taxonomy.
|
||||
*/
|
||||
class ET_Builder_Post_Taxonomy_CodeSnippetType extends ET_Core_Post_Taxonomy {
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_owner = 'builder';
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $name = 'et_code_snippet_type';
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function _get_args() {
|
||||
return array(
|
||||
'hierarchical' => false,
|
||||
'show_ui' => false,
|
||||
'show_admin_column' => true,
|
||||
'query_var' => true,
|
||||
'show_in_nav_menus' => false,
|
||||
'publicly_queryable' => true,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function _get_labels() {
|
||||
return array(
|
||||
'name' => esc_html__( 'Type', 'et_builder' ),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the class instance.
|
||||
*
|
||||
* @param string $type See {@see self::$wp_type} for accepted values. Default is 'taxonomy'.
|
||||
* @param string $name The name/slug of the post object. Default is {@see self::$name}.
|
||||
*
|
||||
* @return ET_Builder_Post_Taxonomy_CodeSnippet_Type
|
||||
*/
|
||||
public static function instance( $type = 'taxonomy', $name = 'et_code_snippet_type' ) {
|
||||
$instance = parent::instance( $type, $name );
|
||||
if ( ! $instance ) {
|
||||
$instance = new self();
|
||||
}
|
||||
|
||||
return $instance;
|
||||
}
|
||||
}
|
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
/**
|
||||
* Register ET_CODE_SNIPPET_POST_TYPE taxonomy.
|
||||
*
|
||||
* @since 4.19.0
|
||||
*
|
||||
* @package Divi
|
||||
* @subpackage Cloud
|
||||
* @since 4.19.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class to handle `et_tb_item` post type.
|
||||
*
|
||||
* Registers TB Item.
|
||||
*/
|
||||
class ET_Builder_Post_Type_Code_Snippet extends ET_Core_Post_Type {
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_owner = 'builder';
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $name = ET_CODE_SNIPPET_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',
|
||||
'et_code_snippet_type',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function _get_labels() {
|
||||
return array(
|
||||
'add_new' => esc_html_x( 'Add New', 'Layout', 'et_builder' ),
|
||||
'add_new_item' => esc_html__( 'Add New Code Snippet', 'et_builder' ),
|
||||
'all_items' => esc_html__( 'All Code Snippets', 'et_builder' ),
|
||||
'edit_item' => esc_html__( 'Edit Code Snippet', 'et_builder' ),
|
||||
'name' => esc_html__( 'Code Snippets', 'et_builder' ),
|
||||
'new_item' => esc_html__( 'New Code Snippet', '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 Code Snippets', 'et_builder' ),
|
||||
'singular_name' => esc_html__( 'Code Snippet', 'et_builder' ),
|
||||
'view_item' => esc_html__( 'View Code Snippet', '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_CODE_SNIPPET_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 );
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user