From ca8e118327125c814b7ff6709cf946f26f745d1f Mon Sep 17 00:00:00 2001 From: "Wiessfelt, Katie" Date: Thu, 14 Dec 2023 18:46:21 -0600 Subject: [PATCH 01/12] button doesn't show on tinymce --- castle/cms/browser/configure.zcml | 8 + .../cms/browser/controlpanel/configure.zcml | 83 ++++---- castle/cms/browser/controlpanel/openai.py | 25 +++ castle/cms/browser/openai.py | 91 +++++++++ castle/cms/profiles/3011/controlpanel.xml | 17 ++ .../profiles/3011/registry/controlpanel.xml | 12 ++ castle/cms/profiles/3011/registry/mosaic.xml | 62 ++++++ castle/cms/profiles/default/controlpanel.xml | 12 ++ .../default/registry/controlpanel.xml | 1 + .../cms/profiles/default/registry/mosaic.xml | 19 ++ castle/cms/registry.py | 1 - castle/cms/static/svg/tinymce/openai.svg | 44 ++++ .../cms/static/svg/tinymce/spinner-solid.svg | 1 + castle/cms/static/tinymce-openai.js | 189 ++++++++++++++++++ castle/cms/upgrades.zcml | 22 ++ castle/cms/upgrades/__init__.py | 43 ++++ 16 files changed, 591 insertions(+), 39 deletions(-) create mode 100644 castle/cms/browser/controlpanel/openai.py create mode 100644 castle/cms/browser/openai.py create mode 100644 castle/cms/profiles/3011/controlpanel.xml create mode 100644 castle/cms/profiles/3011/registry/controlpanel.xml create mode 100644 castle/cms/profiles/3011/registry/mosaic.xml create mode 100644 castle/cms/static/svg/tinymce/openai.svg create mode 100644 castle/cms/static/svg/tinymce/spinner-solid.svg create mode 100644 castle/cms/static/tinymce-openai.js diff --git a/castle/cms/browser/configure.zcml b/castle/cms/browser/configure.zcml index 6471f3bb4..37f41bd86 100644 --- a/castle/cms/browser/configure.zcml +++ b/castle/cms/browser/configure.zcml @@ -412,4 +412,12 @@ layer="..interfaces.ICastleLayer" /> + + diff --git a/castle/cms/browser/controlpanel/configure.zcml b/castle/cms/browser/controlpanel/configure.zcml index ea17f9308..863ed6a6f 100644 --- a/castle/cms/browser/controlpanel/configure.zcml +++ b/castle/cms/browser/controlpanel/configure.zcml @@ -1,8 +1,9 @@ - - + + /> + /> + /> + /> + /> + /> + /> + /> + /> + /> + /> - + /> + /> + /> + /> + /> + /> + /> + /> + /> + /> - + + /> + /> + /> + diff --git a/castle/cms/browser/controlpanel/openai.py b/castle/cms/browser/controlpanel/openai.py new file mode 100644 index 000000000..0a8237ffa --- /dev/null +++ b/castle/cms/browser/controlpanel/openai.py @@ -0,0 +1,25 @@ +from plone.app.registry.browser.controlpanel import ( + RegistryEditForm, + ControlPanelFormWrapper, +) +from plone.supermodel import model + +import zope.schema as schema + +class IOpenAISettings(model.Schema): + openai_api_key = schema.TextLine( + title=u'OpenAI API Key', + default=None, + required=False, + ) + +class OpenAISettingsControlPanelForm(RegistryEditForm): + schema_prefix = 'fbigov.theme' + schema = IOpenAISettings + id = 'OpenAISettingsControlPanel' + label = u'OpenAI Settings' + description = 'Settings to communicate with OpenAI API' + + +class OpenAISettingsControlPanel(ControlPanelFormWrapper): + form = OpenAISettingsControlPanelForm diff --git a/castle/cms/browser/openai.py b/castle/cms/browser/openai.py new file mode 100644 index 000000000..c2cbf6e9d --- /dev/null +++ b/castle/cms/browser/openai.py @@ -0,0 +1,91 @@ +from Products.Five import BrowserView +from plone.protect import ( + PostOnly, + protect, +) +from plone import api +import requests +import random +import json + +class OpenAI(BrowserView): + + @protect(PostOnly) + def __call__(self, REQUEST=None): + data = self.request.form.get("data", {}) + return json.dumps(self.openai_api_request(data)) + + def openai_api_request(self, data): + response = requests.post( + url="https://api.openai.com/v1/chat/completions", + data=json.dumps({ + "model": "gpt-3.5-turbo", + "messages": [{ + "role": "user", + "content": data, + }], + "temperature": 0.2, + "max_tokens": 500, + }), + headers={ + "Content-Type": "application/json", + "Authorization": "Bearer {}".format(self.api_key) + }, + ) + return self.handle_response(response) + + def handle_response(self, response, num_retries=1, delay=1, max_retries=10): + if not response.ok: + try: + response_json = response.json() + except: # noqa + return self.error_response() + error = response_json.get("error", {}) + code = error.get("code", {}) + if response.status_code == 429 and code != "insufficient_quota": # nosec + if num_retries <= max_retries: + self.exponential_backoff(data=self.request.data, num_retries=num_retries, delay=delay) + try: + received_data = response.json() + except: # noqa + return self.error_response() + + status_code = response.status_code + success = 200 <= status_code < 300 + + if success: + status = "success" + message = received_data.get('choices')[0].get('message').get('content') + else: # openai api error + status = response_json.get("error", {}).get("code", {}) + message = received_data.get("error", {}).get("message", {}) + + return_data = { + "status": status, + "message": message, + } + + response = self.request.response + response.setStatus(status_code) + response.setHeader('Content-Type', 'application/json') + + return return_data + + def error_response(self): # unforseen error + response = self.request.response + response.setStatus(response.status_code) + response.setHeader('Content-Type', 'application/json') + return { + "status": "error", + "message": "unforseen error", + } + + @property + def api_key(self): + key = api.portal.get_registry_record("fbigov.theme.openai_api_key", default="default_value") + return key + + def exponential_backoff(self, data, num_retries, delay, exponential_base=2): + num_retries = num_retries + 1 + delay *= exponential_base * (2 * random.random()) # nosec + self.openai_api_request(data=data, num_retries=num_retries, delay=delay) diff --git a/castle/cms/profiles/3011/controlpanel.xml b/castle/cms/profiles/3011/controlpanel.xml new file mode 100644 index 000000000..db1129355 --- /dev/null +++ b/castle/cms/profiles/3011/controlpanel.xml @@ -0,0 +1,17 @@ + + + + + Manage portal + + + diff --git a/castle/cms/profiles/3011/registry/controlpanel.xml b/castle/cms/profiles/3011/registry/controlpanel.xml new file mode 100644 index 000000000..f21fca56c --- /dev/null +++ b/castle/cms/profiles/3011/registry/controlpanel.xml @@ -0,0 +1,12 @@ + + + + + + openai|++plone++castle/tinymce-openai.js + + + + diff --git a/castle/cms/profiles/3011/registry/mosaic.xml b/castle/cms/profiles/3011/registry/mosaic.xml new file mode 100644 index 000000000..4163e4eca --- /dev/null +++ b/castle/cms/profiles/3011/registry/mosaic.xml @@ -0,0 +1,62 @@ + + + + + + openai + + + + + toolbar-openai + styles + OpenAI + code + false + false + 1000 + + + + + toolbar-openai + + + + + + toolbar-openai + + + + + + toolbar-openai + + + + + + toolbar-openai + + + + + + toolbar-openai + + + + + + toolbar-openai + + + + diff --git a/castle/cms/profiles/default/controlpanel.xml b/castle/cms/profiles/default/controlpanel.xml index 8ab18ec6b..1959e1795 100644 --- a/castle/cms/profiles/default/controlpanel.xml +++ b/castle/cms/profiles/default/controlpanel.xml @@ -125,4 +125,16 @@ i18n:attributes="title"> Plone Site Setup: Users and Groups + + + Manage portal + diff --git a/castle/cms/profiles/default/registry/controlpanel.xml b/castle/cms/profiles/default/registry/controlpanel.xml index 59030030d..ddf7ef8c5 100644 --- a/castle/cms/profiles/default/registry/controlpanel.xml +++ b/castle/cms/profiles/default/registry/controlpanel.xml @@ -43,6 +43,7 @@ interface="Products.CMFPlone.interfaces.controlpanel.ITinyMCESchema" field="custom_plugins"> mce-table-buttons|++plone++castle/tinymce-table.js + openai|++plone++castle/tinymce-openai.js diff --git a/castle/cms/profiles/default/registry/mosaic.xml b/castle/cms/profiles/default/registry/mosaic.xml index 624659f39..38a14e481 100644 --- a/castle/cms/profiles/default/registry/mosaic.xml +++ b/castle/cms/profiles/default/registry/mosaic.xml @@ -107,6 +107,7 @@ tile-remove-format grid-row-dark grid-row-remove-format + openai @@ -563,6 +564,17 @@ 1000 + + toolbar-openai + actions + OpenAI + openai + false + false + 1050 + + @@ -571,6 +583,7 @@ toolbar-code toolbar-indent toolbar-outdent + toolbar-openai @@ -582,6 +595,7 @@ toolbar-code toolbar-indent toolbar-outdent + toolbar-openai @@ -593,6 +607,7 @@ toolbar-code toolbar-indent toolbar-outdent + toolbar-openai @@ -604,6 +619,7 @@ toolbar-code toolbar-indent toolbar-outdent + toolbar-openai @@ -614,8 +630,10 @@ toolbar-code toolbar-indent toolbar-outdent + toolbar-openai + toolbar-removeformat @@ -623,6 +641,7 @@ toolbar-code toolbar-indent toolbar-outdent + toolbar-openai diff --git a/castle/cms/registry.py b/castle/cms/registry.py index 1b0b143ef..3533b4a4e 100644 --- a/castle/cms/registry.py +++ b/castle/cms/registry.py @@ -35,7 +35,6 @@ def parseRegistry(self): result = super(CastleMosaicRegistry, self).parseRegistry() else: result = super(CastleMosaicRegistry, self).parseRegistry() - mng = get_tile_manager() for tile in mng.get_tiles(): if tile.get('hidden'): diff --git a/castle/cms/static/svg/tinymce/openai.svg b/castle/cms/static/svg/tinymce/openai.svg new file mode 100644 index 000000000..dc18881cf --- /dev/null +++ b/castle/cms/static/svg/tinymce/openai.svg @@ -0,0 +1,44 @@ + + + + + + + + diff --git a/castle/cms/static/svg/tinymce/spinner-solid.svg b/castle/cms/static/svg/tinymce/spinner-solid.svg new file mode 100644 index 000000000..f3b4afbd2 --- /dev/null +++ b/castle/cms/static/svg/tinymce/spinner-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/castle/cms/static/tinymce-openai.js b/castle/cms/static/tinymce-openai.js new file mode 100644 index 000000000..1dcaaf32c --- /dev/null +++ b/castle/cms/static/tinymce-openai.js @@ -0,0 +1,189 @@ +/** + * plugin.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/*global tinymce:true */ +define(["tinymce"], function(tinymce) { + tinymce.PluginManager.add("openai", function (editor) { + "use strict"; + function showDialog() { + editor.windowManager.open({ + title: "AI Assistant", + body: [ + { + type: "textbox", + name: "request", + placeholder: "Ask the AI to edit or generate...", + }, + ], + height: 60, + width: 600, + onsubmit: async function(e) { + const { portalUrl } = document.querySelector( "body" ).dataset; + + function dimScreen() { + let element = document.querySelector("#dimmer") + if (element === null){ + const dimmer = document.createElement("div"); + dimmer.id = "dimmer" + dimmer.style.display = "block" + dimmer.style.backgroundColor = "black" + dimmer.style.position = "fixed"; + dimmer.style.width = "100%"; + dimmer.style.height = "100%"; + dimmer.style.zIndex = 1000; + dimmer.style.top = "0px"; + dimmer.style.left = "0px"; + dimmer.style.opacity = .5; /* in FireFox */ + document.body.appendChild(dimmer) + } + else { + element.style.display = "block" + } + } + + function hideDimmer() { + const element = document.querySelector("#dimmer") + element.style.display = "none" + } + + function showSpinner() { + dimScreen() + let element = document.querySelector("#spinner") + if (element === null){ + const spinnerImg = document.createElement("img"); + spinnerImg.src = "++plone++castle/svg/tinymce/spinner-solid.svg" + spinnerImg.id = "spinner" + spinnerImg.classList = "text-center" + spinnerImg.style.position = "fixed"; + spinnerImg.style.top = "50%"; + spinnerImg.style.left = "50%"; + spinnerImg.style.height = "100px"; + spinnerImg.style.width = "100px"; + spinnerImg.style.transform = "translate(-50%, -50%)"; + spinnerImg.style.zIndex = "70000" + spinnerImg.style.display = "block" + document.body.appendChild(spinnerImg) + spin() + } + else { + element.style.display = "block" + } + } + + function hideSpinner() { + const spinnerElm = document.querySelector("#spinner") + spinnerElm.style.display = "none" + hideDimmer() + } + + function spin() { + const loadingSpinning = [ + { transform: "rotate(0)" }, + { transform: "rotate(360deg)" }, + ]; + + const loadingTiming = { + duration: 2000, + iterations: Infinity, + }; + + const loading = document.querySelector("#spinner"); + loading.animate(loadingSpinning, loadingTiming); + } + + function delay(time) { + return new Promise(resolve => setTimeout(resolve, time)); + } + + function checkFormExists() { + if (document.querySelector("#openai-form") == null) { + const form = document.createElement("form"); + form.id = "openai-form" + const requestInput = document.createElement("input"); + form.method = "POST"; + + requestInput.name="data"; + requestInput.id="openai-request-input" + form.appendChild(requestInput); + + form.style.display = "none" + document.body.appendChild(form); + } + } + + async function openAiRequest() { + checkFormExists() + const input = document.querySelector("#openai-request-input") + input.value = e.data.request + const form = document.querySelector("#openai-form") + + const request = await fetch(`${portalUrl}/@@openai-request`, { + method: "POST", + body: new URLSearchParams(new FormData(form)) + }).then(async response => { + const jsonResponse = await response.json() + if (!response.ok) { + if (response.status === 401) { + hideSpinner() + await delay(100); + alert("Invalid authentication or api key. Please contact your administrator.") + + } + else if (response.status === 429) { + if (jsonResponse.error.type === "insufficient_quota") { + hideSpinner() + await delay(100); + alert("You used up your monthly requests. Please message your administrator to load more.") + } + else { + hideSpinner() + await delay(100); + alert("Too many requests have been submited to OpenAI. Please try again later.") + } + } + else if (response.status >= 500) { + hideSpinner() + await delay(100); + alert("There has been an error connecting to OpenAI. Please try again later.") + } + else { + hideSpinner() + await delay(100); + alert("There has been an unexpected error. Please contact your administrator.") + } + } + else { + hideSpinner() + return await jsonResponse; + } + }) + .catch(async (error) => { + hideSpinner() + await delay(100); + alert("There has been an unexpected error. Please contact your administrator.") + }); + return request + } + + showSpinner() + const requestJson = openAiRequest() + const answer = await requestJson + editor.insertContent(answer.message); + } + }); + } + + editor.addButton("openai", { + icon: "openai", + tooltip: "OpenAI", + onclick: showDialog, + }); + }); +}) diff --git a/castle/cms/upgrades.zcml b/castle/cms/upgrades.zcml index 84c624091..60c5fb42b 100644 --- a/castle/cms/upgrades.zcml +++ b/castle/cms/upgrades.zcml @@ -165,4 +165,26 @@ handler=".upgrades.upgrade_3010" profile="castle.cms:default" /> + + + + + + diff --git a/castle/cms/upgrades/__init__.py b/castle/cms/upgrades/__init__.py index 05f6c4f19..f7bbc565f 100644 --- a/castle/cms/upgrades/__init__.py +++ b/castle/cms/upgrades/__init__.py @@ -1,10 +1,14 @@ from castle.cms.interfaces import IAPISettings, ISecuritySchema +from castle.cms.browser.controlpanel.openai import IOpenAISettings from Products.CMFPlone.resources.browser.cook import cookWhenChangingSettings from Products.CMFCore.utils import getToolByName import importlib import plone.api as api +from castle.cms.tiles.dynamic import get_tile_manager +from plone.app.mosaic.registry import MosaicRegistry + PROFILE_ID = 'profile-castle.cms:default' @@ -82,3 +86,42 @@ def upgrade_3008(site, logger=None): upgrade_3009 = default_upgrade_factory('3009') upgrade_3010 = default_upgrade_factory('3010') + +def upgrade_3011_interface_registry(setup_tool, logger=None): + registry = api.portal.get_tool('portal_registry') + registry.registerInterface( + IOpenAISettings, + prefix='castle', + ) +def upgrade_3011_register_profile(setup_tool, logger=None): + # registry = api.portal.get_tool('portal_registry') + # reg = MosaicRegistry(registry) + # result = reg.parseRegistry + # mng = get_tile_manager() + # for tile in mng.get_tiles(): + # if tile.get('hidden'): + # continue + # key = 'castle_cms_dynamic_{}'.format(tile['id']) + # category = tile.get('category') or 'advanced' + # category_id = category.replace(' ', '_').lower() + # if category_id not in result['plone']['app']['mosaic']['tiles_categories']: + # result['plone']['app']['mosaic']['tiles_categories'][category_id] = { + # 'label': category, + # 'name': category_id, + # 'weight': 100 + # } + # result['plone']['app']['mosaic']['app_tiles'][key] = { + # 'category': category_id, + # 'default_value': None, + # 'favorite': False, + # 'label': tile['title'], + # 'name': tile['name'], + # 'tile_type_id': u'castle.cms.dynamic', + # 'read_only': False, + # 'rich_text': False, + # 'settings': True, + # 'tile_type': u'app', + # 'weight': tile['weight'] + # } + # import pdb; pdb.set_trace() + default_upgrade_factory('3011') From 8df927be0dcd2949e477b82447b57813bfd8c9ce Mon Sep 17 00:00:00 2001 From: "Wiessfelt, Katie" Date: Fri, 15 Dec 2023 11:23:41 -0600 Subject: [PATCH 02/12] fixes --- castle/cms/profiles/3011/registry/mosaic.xml | 2 +- castle/cms/profiles/default/registry/mosaic.xml | 2 +- castle/cms/upgrades.zcml | 16 +++++----------- 3 files changed, 7 insertions(+), 13 deletions(-) diff --git a/castle/cms/profiles/3011/registry/mosaic.xml b/castle/cms/profiles/3011/registry/mosaic.xml index 4163e4eca..ae2a76427 100644 --- a/castle/cms/profiles/3011/registry/mosaic.xml +++ b/castle/cms/profiles/3011/registry/mosaic.xml @@ -13,7 +13,7 @@ toolbar-openai styles OpenAI - code + openai false false 1000 diff --git a/castle/cms/profiles/default/registry/mosaic.xml b/castle/cms/profiles/default/registry/mosaic.xml index 38a14e481..00b04814a 100644 --- a/castle/cms/profiles/default/registry/mosaic.xml +++ b/castle/cms/profiles/default/registry/mosaic.xml @@ -572,7 +572,7 @@ openai false false - 1050 + 1000 - - - - + /> From 24aff1b66c1b8fabff21ce999e7d2bb1663e195f Mon Sep 17 00:00:00 2001 From: "Wiessfelt, Katie" Date: Mon, 18 Dec 2023 16:17:52 -0600 Subject: [PATCH 03/12] condense upgrade step --- castle/cms/upgrades/__init__.py | 37 +++------------------------------ 1 file changed, 3 insertions(+), 34 deletions(-) diff --git a/castle/cms/upgrades/__init__.py b/castle/cms/upgrades/__init__.py index f7bbc565f..bb7c37726 100644 --- a/castle/cms/upgrades/__init__.py +++ b/castle/cms/upgrades/__init__.py @@ -86,42 +86,11 @@ def upgrade_3008(site, logger=None): upgrade_3009 = default_upgrade_factory('3009') upgrade_3010 = default_upgrade_factory('3010') - -def upgrade_3011_interface_registry(setup_tool, logger=None): + +def upgrade_3011(setup_tool, logger=None): registry = api.portal.get_tool('portal_registry') registry.registerInterface( IOpenAISettings, prefix='castle', ) -def upgrade_3011_register_profile(setup_tool, logger=None): - # registry = api.portal.get_tool('portal_registry') - # reg = MosaicRegistry(registry) - # result = reg.parseRegistry - # mng = get_tile_manager() - # for tile in mng.get_tiles(): - # if tile.get('hidden'): - # continue - # key = 'castle_cms_dynamic_{}'.format(tile['id']) - # category = tile.get('category') or 'advanced' - # category_id = category.replace(' ', '_').lower() - # if category_id not in result['plone']['app']['mosaic']['tiles_categories']: - # result['plone']['app']['mosaic']['tiles_categories'][category_id] = { - # 'label': category, - # 'name': category_id, - # 'weight': 100 - # } - # result['plone']['app']['mosaic']['app_tiles'][key] = { - # 'category': category_id, - # 'default_value': None, - # 'favorite': False, - # 'label': tile['title'], - # 'name': tile['name'], - # 'tile_type_id': u'castle.cms.dynamic', - # 'read_only': False, - # 'rich_text': False, - # 'settings': True, - # 'tile_type': u'app', - # 'weight': tile['weight'] - # } - # import pdb; pdb.set_trace() - default_upgrade_factory('3011') + return default_upgrade_factory('3011') From f7aeb62c898fb4e2a5d17087c8e1949a5e09bd84 Mon Sep 17 00:00:00 2001 From: "Wiessfelt, Katie" Date: Mon, 18 Dec 2023 16:18:19 -0600 Subject: [PATCH 04/12] change where openai.js is saved --- castle/cms/profiles/3011/registry/controlpanel.xml | 2 +- castle/cms/profiles/default/registry/controlpanel.xml | 2 +- .../{tinymce-openai.js => patterns/tinymce/js/openai.js} | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) rename castle/cms/static/{tinymce-openai.js => patterns/tinymce/js/openai.js} (98%) diff --git a/castle/cms/profiles/3011/registry/controlpanel.xml b/castle/cms/profiles/3011/registry/controlpanel.xml index f21fca56c..18792f051 100644 --- a/castle/cms/profiles/3011/registry/controlpanel.xml +++ b/castle/cms/profiles/3011/registry/controlpanel.xml @@ -5,7 +5,7 @@ - openai|++plone++castle/tinymce-openai.js + openai|++plone++castle/patterns/tinymce/js/openai.js diff --git a/castle/cms/profiles/default/registry/controlpanel.xml b/castle/cms/profiles/default/registry/controlpanel.xml index ddf7ef8c5..6ccec888a 100644 --- a/castle/cms/profiles/default/registry/controlpanel.xml +++ b/castle/cms/profiles/default/registry/controlpanel.xml @@ -43,7 +43,7 @@ interface="Products.CMFPlone.interfaces.controlpanel.ITinyMCESchema" field="custom_plugins"> mce-table-buttons|++plone++castle/tinymce-table.js - openai|++plone++castle/tinymce-openai.js + openai|++plone++castle/patterns/tinymce/js/openai.js diff --git a/castle/cms/static/tinymce-openai.js b/castle/cms/static/patterns/tinymce/js/openai.js similarity index 98% rename from castle/cms/static/tinymce-openai.js rename to castle/cms/static/patterns/tinymce/js/openai.js index 1dcaaf32c..82331e0ec 100644 --- a/castle/cms/static/tinymce-openai.js +++ b/castle/cms/static/patterns/tinymce/js/openai.js @@ -9,7 +9,8 @@ */ /*global tinymce:true */ -define(["tinymce"], function(tinymce) { +define(["jquery", "tinymce"], function($, tinymce) { + // $.mosaic.actionManager.actions tinymce.PluginManager.add("openai", function (editor) { "use strict"; function showDialog() { From 1bb4ef5aba83ae85340298f279e9a3f962b54246 Mon Sep 17 00:00:00 2001 From: "Wiessfelt, Katie" Date: Mon, 18 Dec 2023 16:18:32 -0600 Subject: [PATCH 05/12] import openai.js --- castle/cms/static/patterns/tinymce/pattern.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/castle/cms/static/patterns/tinymce/pattern.js b/castle/cms/static/patterns/tinymce/pattern.js index f41e97593..9613ed976 100644 --- a/castle/cms/static/patterns/tinymce/pattern.js +++ b/castle/cms/static/patterns/tinymce/pattern.js @@ -18,6 +18,7 @@ define([ 'text!mockup-patterns-tinymce-url/templates/selection.xml', 'mockup-utils', 'mockup-patterns-tinymce-url/js/links', + 'mockup-patterns-tinymce-url/js/openai', 'mockup-i18n', 'translate', 'tinymce-modern-theme', @@ -124,12 +125,12 @@ define([ theme: 'modern', plugins: ['advlist', 'autolink', 'lists', 'charmap', 'print', 'preview', 'anchor', 'searchreplace', 'visualblocks', 'code', 'fullscreen', 'insertdatetime', 'media', 'table', 'contextmenu', - 'paste', 'plonelink', 'ploneimage'], + 'paste', 'plonelink', 'ploneimage', 'openai'], menubar: 'edit table format tools view insert', toolbar: 'undo redo | styleselect | bold italic | ' + 'alignleft aligncenter alignright alignjustify | ' + 'bullist numlist outdent indent | ' + - 'unlink plonelink ploneimage', + 'unlink plonelink ploneimage | openai', //'autoresize_max_height': 900, 'height': 400, // stick here because it's easier to config without From 15e2c705b6902445e4aad0d4c7988424e2daa4fc Mon Sep 17 00:00:00 2001 From: "Wiessfelt, Katie" Date: Mon, 18 Dec 2023 19:33:19 -0600 Subject: [PATCH 06/12] assign value if `this === undefined` --- castle/cms/static/plone-compiled.js | 3 ++- castle/cms/static/plone-compiled.min.js | 2 +- castle/cms/static/plone-compiled.min.js.map | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/castle/cms/static/plone-compiled.js b/castle/cms/static/plone-compiled.js index d1645da67..3161e70dc 100644 --- a/castle/cms/static/plone-compiled.js +++ b/castle/cms/static/plone-compiled.js @@ -5404,7 +5404,8 @@ define('mockup-i18n',[ 'use strict'; var I18N = function() { - var self = this; + var self = this || {}; + self.baseUrl = $('body').attr('data-i18ncatalogurl'); if (!self.baseUrl) { diff --git a/castle/cms/static/plone-compiled.min.js b/castle/cms/static/plone-compiled.min.js index 111774ae3..7c634e543 100644 --- a/castle/cms/static/plone-compiled.min.js +++ b/castle/cms/static/plone-compiled.min.js @@ -1,2 +1,2 @@ -!function(){function t(l,u){return function(t){var e=arguments.length;if(!(e<2||null==t))for(var n=1;n":">",'"':""","'":"'","`":"`"}),d=g.invert(a),N=(g.escape=P(a),g.unescape=P(d),g.result=function(t,e,n){e=null==t?void 0:t[e];return g.isFunction(e=void 0===e?n:e)?e.call(t):e},0),A=(g.uniqueId=function(t){var e=++N+"";return t?t+e:e},g.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g},/(.)^/),H={"'":"'","\\":"\\","\r":"r","\n":"n","\u2028":"u2028","\u2029":"u2029"},R=/\\|'|\r|\n|\u2028|\u2029/g;g.template=function(a,t,e){t=g.defaults({},t=!t&&e?e:t,g.templateSettings);var e=RegExp([(t.escape||A).source,(t.interpolate||A).source,(t.evaluate||A).source].join("|")+"|$","g"),s=0,r="__p+='";a.replace(e,function(t,e,n,i,o){return r+=a.slice(s,o).replace(R,O),s=o+t.length,e?r+="'+\n((__t=("+e+"))==null?'':_.escape(__t))+\n'":n?r+="'+\n((__t=("+n+"))==null?'':__t)+\n'":i&&(r+="';\n"+i+"\n__p+='"),t}),r+="';\n",r="var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};\n"+(r=t.variable?r:"with(obj||{}){\n"+r+"}\n")+"return __p;\n";try{var n=new Function(t.variable||"obj","_",r)}catch(t){throw t.source=r,t}function i(t){return n.call(this,t,g)}e=t.variable||"obj";return i.source="function("+e+"){\n"+r+"}",i},g.chain=function(t){t=g(t);return t._chain=!0,t};g.mixin=function(n){g.each(g.functions(n),function(t){var e=g[t]=n[t];g.prototype[t]=function(){var t=[this._wrapped];return r.apply(t,arguments),Y(this,e.apply(g,t))}})},g.mixin(g),g.each(["pop","push","reverse","shift","sort","splice","unshift"],function(e){var n=o[e];g.prototype[e]=function(){var t=this._wrapped;return n.apply(t,arguments),"shift"!==e&&"splice"!==e||0!==t.length||delete t[0],Y(this,t)}}),g.each(["concat","join","slice"],function(t){var e=o[t];g.prototype[t]=function(){return Y(this,e.apply(this._wrapped,arguments))}}),g.prototype.value=function(){return this._wrapped},g.prototype.valueOf=g.prototype.toJSON=g.prototype.value,g.prototype.toString=function(){return""+this._wrapped},"function"==typeof define&&define.amd&&define("underscore",[],function(){return g})}.call(this),function(){Function.prototype.bind||(Function.prototype.bind=function(t){if("function"!=typeof this)throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");function e(){return i.apply(this instanceof o&&t?this:t,n.concat(Array.prototype.slice.call(arguments)))}var n=Array.prototype.slice.call(arguments,1),i=this,o=function(){};return o.prototype=this.prototype,e.prototype=new o,e});var n,i={DEBUG:10,INFO:20,WARN:30,ERROR:40,FATAL:50};function t(){}function e(){}function a(t,e){this._loggers={},this.name=t||"",this._parent=e||null,e||(this._enabled=!0,this._level=i.WARN)}function o(t){n=t}t.prototype={output:function(t,e,n){void 0!==window.console&&void 0!==console.log&&(t&&n.unshift(t+":"),t=n.join(" "),console.info,e<=i.DEBUG?(t="[DEBUG] "+t,console.log(t)):e<=i.INFO?console.info(t):e<=i.WARN?console.warn(t):console.error(t))}},e.prototype={output:function(t,e,n){t&&n.unshift(t+":"),(e<=i.DEBUG?(n.unshift("[DEBUG]"),console.log):e<=i.INFO?console.info:e<=i.WARN?console.warn:console.error).apply(console,n)}},a.prototype={getLogger:function(t){for(var e=t.split("."),n=this,i=this.name?[this.name]:[];e.length;){var o=e.shift();i.push(o),o in n._loggers||(n._loggers[o]=new a(i.join("."),n)),n=n._loggers[o]}return n},_getFlag:function(t){var e=this;for(t="_"+t;null!==e;){if(void 0!==e[t])return e[t];e=e._parent}return null},setEnabled:function(t){this._enabled=!!t},isEnabled:function(){this._getFlag("enabled")},setLevel:function(t){"number"==typeof t?this._level=t:"string"==typeof t&&(t=t.toUpperCase())in i&&(this._level=i[t])},getLevel:function(){return this._getFlag("level")},log:function(t,e){!e.length||!this._getFlag("enabled")||t>>0;if("function"!=typeof t)throw new TypeError(t+" is not a function");for(1n||t.top>e)},removeWildcardClass:function(t,e){var o;-1===e.indexOf("*")?t.removeClass(e):(o=(o=e.replace(/[\-\[\]{}()+?.,\\\^$|#\s]/g,"\\$&")).replace(/[*]/g,".*"),o=new RegExp("^"+o+"$"),t.filter("[class]").each(function(){for(var t=a(this),e=t.attr("class").split(/\s+/),n=[],i=0;i