diff --git a/admin/abstract-api.php b/admin/abstract-api.php index baf46fa4b7..70c2bffe7f 100644 --- a/admin/abstract-api.php +++ b/admin/abstract-api.php @@ -34,6 +34,10 @@ protected function verifyNonce( $action ) { $this->error( 400, "Invalid editor version. Please refresh the page and try again" ); } + $this->checkNonce( $action ); + } + + protected function checkNonce( $action ) { if ( ! wp_verify_nonce( $this->getRequestNonce(), $action ) ) { Brizy_Logger::instance()->error( 'Invalid request nonce', $_REQUEST ); $this->error( 400, "Bad request" ); diff --git a/editor/accounts/abstract-account.php b/editor/accounts/abstract-account.php index 992022aa80..5a5db0cc1c 100644 --- a/editor/accounts/abstract-account.php +++ b/editor/accounts/abstract-account.php @@ -6,6 +6,7 @@ abstract class Brizy_Editor_Accounts_AbstractAccount extends Brizy_Admin_Seriali const INTEGRATIONS_GROUP = 'form-integration'; const RECAPTCHA_GROUP = 'recaptcha'; const SOCIAL_GROUP = 'social'; + const ADOBE_GROUP = 'adobe'; use Brizy_Editor_Forms_DynamicPropsAware; @@ -140,6 +141,8 @@ static public function createFromSerializedData( $data ) { return new Brizy_Editor_Accounts_SocialAccount( $data ); case self::RECAPTCHA_GROUP: return new Brizy_Editor_Accounts_RecaptchaAccount( $data ); + case self::ADOBE_GROUP: + return new Brizy_Editor_Accounts_AdobeAccount( $data ); } throw new Exception( 'Invalid account group.' ); diff --git a/editor/accounts/adobe-account.php b/editor/accounts/adobe-account.php new file mode 100644 index 0000000000..4fe0c526dd --- /dev/null +++ b/editor/accounts/adobe-account.php @@ -0,0 +1,56 @@ +group = Brizy_Editor_Accounts_AbstractAccount::ADOBE_GROUP; + + return Brizy_Editor_Accounts_AbstractAccount::createFromSerializedData( get_object_vars( $json_obj ) ); + } + + throw new Exception( 'Invalid json provided.' ); + } + + public function getKey() { + return ! empty( $this->data['key'] ) ? $this->data['key'] : null; + } +} diff --git a/editor/api.php b/editor/api.php index 841c5287f2..b495ee18fa 100755 --- a/editor/api.php +++ b/editor/api.php @@ -36,6 +36,7 @@ class Brizy_Editor_API extends Brizy_Admin_AbstractApi { const AJAX_GET_TERMS = '_get_terms'; const AJAX_GET_TERMS_BY = '_get_terms_by'; const AJAX_GET_POST_TAXONOMIES = '_get_post_taxonomies'; + const AJAX_GET_ADOBE_FONTS = '_get_adobe_fonts'; const AJAX_GET_DYNAMIC_CONTENT = '_get_dynamic_content'; @@ -69,35 +70,36 @@ protected function initializeApiActions() { return; } - $p = 'wp_ajax_' . Brizy_Editor::prefix(); - add_action( $p . self::AJAX_REMOVE_LOCK, array( $this, 'removeProjectLock' ) ); - add_action( $p . self::AJAX_HEARTBEAT, array( $this, 'heartbeat' ) ); - add_action( $p . self::AJAX_TAKE_OVER, array( $this, 'takeOver' ) ); - add_action( $p . self::AJAX_GET, array( $this, 'get_item' ) ); - add_action( $p . self::AJAX_GET_POST_INFO, array( $this, 'get_post_info' ) ); - add_action( $p . self::AJAX_UPDATE, array( $this, 'update_item' ) ); - add_action( $p . self::AJAX_GET_PROJECT, array( $this, 'get_project' ) ); - add_action( $p . self::AJAX_SET_PROJECT, array( $this, 'set_project' ) ); - add_action( $p . self::AJAX_LOCK_PROJECT, array( $this, 'lock_project' ) ); - add_action( $p . self::AJAX_SIDEBARS, array( $this, 'get_sidebars' ) ); - add_action( $p . self::AJAX_SHORTCODE_CONTENT, array( $this, 'shortcode_content' ) ); - add_action( $p . self::AJAX_PLACEHOLDER_CONTENT, array( $this, 'placeholder_content' ) ); - add_action( $p . self::AJAX_PLACEHOLDERS_CONTENT, array( $this, 'placeholders_content' ) ); - add_action( $p . self::AJAX_GET_POST_OBJECTS, array( $this, 'get_post_objects' ) ); - add_action( $p . self::AJAX_SEARCH_POST, array( $this, 'search_post' ) ); - add_action( $p . self::AJAX_GET_MENU_LIST, array( $this, 'get_menu_list' ) ); - add_action( $p . self::AJAX_GET_TERMS, array( $this, 'get_terms' ) ); - add_action( $p . self::AJAX_GET_USERS, array( $this, 'get_users' ) ); - add_action( $p . self::AJAX_GET_TERMS_BY, array( $this, 'get_terms_by' ) ); - add_action( $p . self::AJAX_MEDIA_METAKEY, array( $this, 'get_media_key' ) ); - add_action( $p . self::AJAX_CREATE_ATTACHMENT_UID, array( $this, 'get_attachment_key' ) ); - add_action( $p . self::AJAX_SET_FEATURED_IMAGE, array( $this, 'set_featured_image' ) ); - add_action( $p . self::AJAX_SET_IMAGE_FOCAL_PT, array( $this, 'set_featured_image_focal_point' ) ); - add_action( $p . self::AJAX_TIMESTAMP, array( $this, 'timestamp' ) ); - add_action( $p . self::AJAX_SET_TEMPLATE_TYPE, array( $this, 'setTemplateType' ) ); - add_action( $p . self::AJAX_GET_POST_TAXONOMIES, array( $this, 'addPostTaxonomies' ) ); - add_action( $p . self::AJAX_GET_DYNAMIC_CONTENT, array( $this, 'addDynamicContent' ) ); - add_action( $p . 'nopriv_' . Brizy_Editor::prefix( self::AJAX_TIMESTAMP ), array( $this, 'timestamp' ) ); + $p = 'wp_ajax_' . Brizy_Editor::prefix(); + add_action($p . self::AJAX_REMOVE_LOCK, array($this, 'removeProjectLock')); + add_action($p . self::AJAX_HEARTBEAT, array($this, 'heartbeat')); + add_action($p . self::AJAX_TAKE_OVER, array($this, 'takeOver')); + add_action($p . self::AJAX_GET, array($this, 'get_item')); + add_action($p . self::AJAX_GET_POST_INFO, array($this, 'get_post_info')); + add_action($p . self::AJAX_UPDATE, array($this, 'update_item')); + add_action($p . self::AJAX_GET_PROJECT, array($this, 'get_project')); + add_action($p . self::AJAX_SET_PROJECT, array($this, 'set_project')); + add_action($p . self::AJAX_LOCK_PROJECT, array($this, 'lock_project')); + add_action($p . self::AJAX_SIDEBARS, array($this, 'get_sidebars')); + add_action($p . self::AJAX_SHORTCODE_CONTENT, array($this, 'shortcode_content')); + add_action($p . self::AJAX_PLACEHOLDER_CONTENT, array($this, 'placeholder_content')); + add_action($p . self::AJAX_PLACEHOLDERS_CONTENT, array($this, 'placeholders_content')); + add_action($p . self::AJAX_GET_POST_OBJECTS, array($this, 'get_post_objects')); + add_action($p . self::AJAX_SEARCH_POST, array($this, 'search_post')); + add_action($p . self::AJAX_GET_MENU_LIST, array($this, 'get_menu_list')); + add_action($p . self::AJAX_GET_TERMS, array($this, 'get_terms')); + add_action($p . self::AJAX_GET_USERS, array($this, 'get_users')); + add_action($p . self::AJAX_GET_TERMS_BY, array($this, 'get_terms_by')); + add_action($p . self::AJAX_MEDIA_METAKEY, array($this, 'get_media_key')); + add_action($p . self::AJAX_CREATE_ATTACHMENT_UID, array($this, 'get_attachment_key')); + add_action($p . self::AJAX_SET_FEATURED_IMAGE, array($this, 'set_featured_image')); + add_action($p . self::AJAX_SET_IMAGE_FOCAL_PT, array($this, 'set_featured_image_focal_point')); + add_action($p . self::AJAX_TIMESTAMP, array($this, 'timestamp')); + add_action($p . self::AJAX_SET_TEMPLATE_TYPE, array($this, 'setTemplateType')); + add_action($p . self::AJAX_GET_POST_TAXONOMIES, array($this, 'addPostTaxonomies')); + add_action($p . self::AJAX_GET_DYNAMIC_CONTENT, array($this, 'addDynamicContent')); + add_action($p . self::AJAX_GET_ADOBE_FONTS, array($this, 'getAdobeFonts')); + add_action($p . 'nopriv_' . Brizy_Editor::prefix(self::AJAX_TIMESTAMP), array($this, 'timestamp')); } @@ -1104,4 +1106,29 @@ private function createMediaKey( $postId, $attachmentId ) { return $uid; } + + public function getAdobeFonts() { + $this->checkNonce( self::nonce ); + + $manager = new Brizy_Editor_Accounts_ServiceAccountManager( Brizy_Editor_Project::get() ); + $accounts = $manager->getAccountsByGroup( Brizy_Editor_Accounts_AbstractAccount::ADOBE_GROUP ); + + if ( ! isset( $accounts[0] ) ) { + $this->error( 400, 'No adobe account found.' ); + } + + $adobeKey = $accounts[0]->getKey(); + + if ( ! $adobeKey ) { + $this->error( 400, 'No adobe key found.' ); + } + + $response = wp_remote_get( "https://typekit.com/api/v1/json/kits/$adobeKey/published", [ 'timeout' => 20 ] ); + + if ( is_wp_error( $response ) || 200 != wp_remote_retrieve_response_code( $response ) ) { + $this->error( 400, 'An error occurred creating the request to adobe.' ); + } + + $this->success( json_decode( wp_remote_retrieve_body( $response ), true ) ); + } } diff --git a/public/editor-client/src/api/index.ts b/public/editor-client/src/api/index.ts index 52a5c67e12..2b5c505011 100644 --- a/public/editor-client/src/api/index.ts +++ b/public/editor-client/src/api/index.ts @@ -907,6 +907,67 @@ export const updateBlockScreenshot = async ({ //#endregion +//#region Screenshots +interface AddAccount { + group: string; + key: string; +} + +export const getAdobeFont = async () => { + const config = getConfig(); + + if (!config) { + throw new Error(t("Invalid __BRZ_PLUGIN_ENV__")); + } + + const { editorVersion, url: _url, hash, actions } = config; + + const url = makeUrl(_url, { + hash, + action: actions.adobeFontsUrl, + version: editorVersion + }); + + const r = await request(url, { + method: "GET" + }); + + if (r.ok) { + const d = await r.json(); + + if (d) { + return d.data; + } + } else { + throw new Error(t("Failed to get adobe fonts")); + } +}; + +export const addAdobeAccount = async (body: AddAccount) => { + const config = getConfig(); + + if (!config) { + throw new Error(t("Invalid __BRZ_PLUGIN_ENV__")); + } + const { url: _url, hash, editorVersion, actions } = config; + + const url = makeUrl(_url, { + hash, + action: actions.addAccount, + version: editorVersion + }); + + return request(url, { + method: "POST", + headers: { + "Content-Type": "application/json; charset=utf-8" + }, + body: JSON.stringify(body) + }).then((res) => res); +}; + +//#endregion + //#region Dynamic Content export const getPlaceholders = (extraData: { diff --git a/public/editor-client/src/config.ts b/public/editor-client/src/config.ts index d12a38c37a..e3ea7cb6c0 100644 --- a/public/editor-client/src/config.ts +++ b/public/editor-client/src/config.ts @@ -2,7 +2,7 @@ import { readIconUrl } from "@/types/Icon"; import { Arr, Bool, Obj, Str } from "@brizy/readers"; import { match, mPipe, optional, parseStrict } from "fp-utilities"; import { CollectionType } from "./types/Collections"; -import { PLUGIN_ENV } from "./types/global"; +import { ImagePatterns, PLUGIN_ENV } from "./types/global"; import { pipe } from "./utils/fp/pipe"; import { onNullish } from "./utils/onNullish"; import { throwOnNullish } from "./utils/throwOnNullish"; @@ -41,6 +41,8 @@ interface Actions { createBlockScreenshot: string; updateBlockScreenshot: string; + adobeFontsUrl: string; + addAccount: string; getDynamicContentPlaceholders: string; @@ -144,17 +146,23 @@ const apiReader = parseStrict({ Obj.readKey("imagePatterns"), Obj.read, parseStrict({ - full: pipe( - mPipe(Obj.readKey("full"), Str.read), - throwOnNullish("Invalid API: ImagePatterns full pattern") + full: optional( + pipe( + mPipe(Obj.readKey("full"), Str.read), + throwOnNullish("Invalid API: ImagePatterns full pattern") + ) ), - original: pipe( - mPipe(Obj.readKey("original"), Str.read), - throwOnNullish("Invalid API: ImagePatterns original pattern") + original: optional( + pipe( + mPipe(Obj.readKey("original"), Str.read), + throwOnNullish("Invalid API: ImagePatterns original pattern") + ) ), - split: pipe( - mPipe(Obj.readKey("split"), Str.read), - throwOnNullish("Invalid API: ImagePatterns split pattern") + split: optional( + pipe( + mPipe(Obj.readKey("split"), Str.read), + throwOnNullish("Invalid API: ImagePatterns split pattern") + ) ) }) ), @@ -247,6 +255,14 @@ const actionsReader = parseStrict({ mPipe(Obj.readKey("updateBlockScreenshot"), Str.read), throwOnNullish("Invalid actions: updateBlockScreenshot") ), + adobeFontsUrl: pipe( + mPipe(Obj.readKey("adobeFontsUrl"), Str.read), + throwOnNullish("Invalid actions: adobeFontsUrl") + ), + addAccount: pipe( + mPipe(Obj.readKey("addAccount"), Str.read), + throwOnNullish("Invalid actions: addAccount") + ), getDynamicContentPlaceholders: pipe( mPipe(Obj.readKey("getDynamicContentPlaceholders"), Str.read), throwOnNullish("Invalid actions: getDynamicContentPlaceholders") diff --git a/public/editor-client/src/fonts/index.ts b/public/editor-client/src/fonts/index.ts index dc7c2ebf10..2ad24b1988 100644 --- a/public/editor-client/src/fonts/index.ts +++ b/public/editor-client/src/fonts/index.ts @@ -1,6 +1,48 @@ +import { addAdobeAccount, getAdobeFont } from "../api"; +import { AdobeFonts } from "../types/AdobeFonts"; +import { t } from "../utils/i18n"; +import { Fonts, KitData } from "./types"; import { FontsData, getUploadedFonts } from "../api"; import { Response } from "../types/Response"; +const convertDataToLocal = (mockTypeKitData: KitData): Fonts => { + const families = mockTypeKitData.kit.families.map((family) => ({ + id: family.id, + family: family.name, + category: family.slug, + kind: "webfonts#webfont", + subsets: [family.css_names[0]], + variants: family.variations + })); + + return { + kit: { + id: mockTypeKitData.kit.id, + families + } + }; +}; + +export const adobeFont = (): AdobeFonts => { + return { + async get(res, rej) { + try { + const r = await getAdobeFont(); + res(convertDataToLocal(r)); + } catch (e) { + rej(t("Failed to get AdobeFont")); + } + }, + async add(res, rej, extra) { + try { + const r = await addAdobeAccount(extra); + res(r); + } catch (e) { + rej(t("Failed to add Adobe account")); + } + } + }; +}; export const uploadedFonts = { async get(res: Response>, rej: Response) { try { diff --git a/public/editor-client/src/fonts/types.ts b/public/editor-client/src/fonts/types.ts new file mode 100644 index 0000000000..91288c94b1 --- /dev/null +++ b/public/editor-client/src/fonts/types.ts @@ -0,0 +1,30 @@ +interface Family { + id: string; + name: string; + slug: string; + css_names: string[]; + variations: string[]; +} + +export interface KitData { + kit: { + id: string; + families: Family[]; + }; +} + +interface Font { + id: string; + family: string; + category: string; + kind: string; + subsets: string[]; + variants: string[]; +} + +export interface Fonts { + kit: { + id: string; + families: Font[]; + }; +} diff --git a/public/editor-client/src/index.ts b/public/editor-client/src/index.ts index 9eb4f44fab..8bc72a2802 100644 --- a/public/editor-client/src/index.ts +++ b/public/editor-client/src/index.ts @@ -16,6 +16,7 @@ import { } from "./defaultTemplates"; import { placeholderData, placeholders } from "./dynamicContent"; import { handler as posts } from "./Elements/Posts"; +import { adobeFont } from "./fonts"; import { uploadedFonts } from "./fonts"; import { globalBlocks } from "./globalBlocks/blocks"; import { globalPopups } from "./globalBlocks/popups"; @@ -70,6 +71,9 @@ const api = { loadCollectionTypes }, screenshots: screenshots(), + fonts: { + adobeFont: adobeFont() + }, heartBeat: heartBeat(config) }; diff --git a/public/editor-client/src/types/AdobeFonts.ts b/public/editor-client/src/types/AdobeFonts.ts new file mode 100644 index 0000000000..224ed98f4d --- /dev/null +++ b/public/editor-client/src/types/AdobeFonts.ts @@ -0,0 +1,10 @@ +import { Response } from "./Response"; + +export interface AdobeFonts { + get?: (res: Response, rej: Response) => void; + add?: ( + res: Response, + rej: Response, + extra: { group: string; key: string } + ) => void; +}