فهرست منبع

Replace TinyMCE source editor with Flask HTML editor.

Kailash Nadh 3 سال پیش
والد
کامیت
93c7c8727c

+ 1 - 1
frontend/src/components/CampaignPreview.vue

@@ -22,7 +22,7 @@
           ></iframe>
         </section>
         <footer class="modal-card-foot has-text-right">
-          <b-button @click="close">Close</b-button>
+          <b-button @click="close">{{ $t('globals.buttons.close') }}</b-button>
         </footer>
       </div>
     </b-modal>

+ 57 - 10
frontend/src/components/Editor.vue

@@ -36,12 +36,32 @@
     </div>
 
     <!-- wsywig //-->
-    <tiny-mce
-      v-model="form.body"
-      v-if="isRichtextReady && form.format === 'richtext'"
-      :disabled="disabled"
-      :init="richtextConf"
-    />
+    <template v-if="isRichtextReady && form.format === 'richtext'">
+      <tiny-mce
+        v-model="form.body"
+        :disabled="disabled"
+        :init="richtextConf"
+      />
+
+      <b-modal scroll="keep" :width="1200"
+        :aria-modal="true" :active.sync="isRichtextSourceVisible">
+        <div>
+          <section expanded class="modal-card-body preview">
+            <html-editor v-model="richTextSourceBody" />
+          </section>
+          <footer class="modal-card-foot has-text-right">
+            <b-button @click="onFormatRichtextHTML">{{ $t('campaigns.formatHTML') }}</b-button>
+            <b-button @click="() => { this.isRichtextSourceVisible = false; }">
+              {{ $t('globals.buttons.close') }}
+            </b-button>
+            <b-button @click="onSaveRichTextSource" class="is-primary">
+              {{ $t('globals.buttons.save') }}
+            </b-button>
+          </footer>
+        </div>
+      </b-modal>
+
+    </template>
 
     <!-- raw html editor //-->
     <html-editor v-if="form.format === 'html'" v-model="form.body" />
@@ -83,7 +103,6 @@ import 'tinymce/skins/ui/oxide/skin.css';
 import 'tinymce/plugins/autoresize';
 import 'tinymce/plugins/autolink';
 import 'tinymce/plugins/charmap';
-import 'tinymce/plugins/code';
 import 'tinymce/plugins/colorpicker';
 import 'tinymce/plugins/contextmenu';
 import 'tinymce/plugins/emoticons';
@@ -148,8 +167,10 @@ export default {
       isEditorFullscreen: false,
       isReady: false,
       isRichtextReady: false,
+      isRichtextSourceVisible: false,
       richtextConf: {},
       isTrackLink: false,
+      richTextSourceBody: '',
       form: {
         body: '',
         format: this.contentType,
@@ -180,13 +201,20 @@ export default {
           editor.on('init', () => {
             this.onEditorDialogOpen(editor);
           });
+
+          // Custom HTML editor.
+          editor.ui.registry.addButton('html', {
+            icon: 'sourcecode',
+            tooltip: 'Source code',
+            onAction: this.onRichtextViewSource,
+          });
         },
 
         min_height: 500,
         entity_encoding: 'raw',
         convert_urls: true,
         plugins: [
-          'autoresize', 'autolink', 'charmap', 'code', 'emoticons', 'fullscreen', 'help',
+          'autoresize', 'autolink', 'charmap', 'emoticons', 'fullscreen', 'help',
           'hr', 'image', 'imagetools', 'link', 'lists', 'paste', 'searchreplace',
           'table', 'visualblocks', 'visualchars', 'wordcount',
         ],
@@ -194,7 +222,7 @@ export default {
                   bold italic underline strikethrough forecolor backcolor subscript superscript |
                   alignleft aligncenter alignright alignjustify |
                   bullist numlist table image | outdent indent | link hr removeformat |
-                  code fullscreen help`,
+                  html fullscreen help`,
         fontsize_formats: '10px 11px 12px 14px 15px 16px 18px 24px 36px',
         skin: false,
         content_css: false,
@@ -242,6 +270,23 @@ export default {
       return u;
     },
 
+
+    onRichtextViewSource() {
+      this.richTextSourceBody = this.form.body;
+      this.isRichtextSourceVisible = true;
+    },
+
+    onFormatRichtextHTML() {
+      this.richTextSourceBody = this.beautifyHTML(this.richTextSourceBody);
+    },
+
+    onSaveRichTextSource() {
+      this.form.body = this.richTextSourceBody;
+      window.tinymce.editors[0].setContent(this.form.body);
+      this.richTextSourceBody = '';
+      this.isRichtextSourceVisible = false;
+    },
+
     onEditorDialogOpen(editor) {
       const ed = editor;
       const oldEd = ed.windowManager.open;
@@ -384,7 +429,9 @@ export default {
         // Preserve line breaks when converting HTML to plaintext.
         const d = document.createElement('div');
         d.innerHTML = this.beautifyHTML(this.form.body);
-        this.form.body = this.trimLines(d.innerText.trim(), true);
+        this.$nextTick(() => {
+          this.form.body = this.trimLines(d.innerText.trim(), true);
+        });
       } else if ((from === 'richtext' || from === 'html') && to === 'markdown') {
         // richtext, html => markdown
         this.form.body = turndown.turndown(this.form.body).replace(/\n\n+/ig, '\n\n');

+ 12 - 2
frontend/src/components/HTMLEditor.vue

@@ -14,6 +14,7 @@ export default {
 
   data() {
     return {
+      data: '',
       flask: null,
     };
   },
@@ -43,8 +44,9 @@ export default {
         readonly: this.disabled,
       });
 
-      this.flask.onUpdate((b) => {
-        this.$emit('input', b);
+      this.flask.onUpdate((v) => {
+        this.data = v;
+        this.$emit('input', v);
       });
 
       // Set the initial value.
@@ -55,5 +57,13 @@ export default {
   mounted() {
     this.initHTMLEditor(this.$props.value || '');
   },
+
+  watch: {
+    value(newVal) {
+      if (newVal !== this.data) {
+        this.flask.updateCode(newVal);
+      }
+    },
+  },
 };
 </script>

+ 1 - 1
frontend/src/views/TemplateForm.vue

@@ -18,7 +18,7 @@
 
             <b-field v-if="form.body !== null"
               :label="$t('templates.rawHTML')" label-position="on-border">
-              <html-editor v-model="form.body" name="body" required />
+              <html-editor v-model="form.body" name="body" />
             </b-field>
 
             <p class="is-size-7">

+ 1 - 0
i18n/cs-cz.json

@@ -31,6 +31,7 @@
     "campaigns.fieldInvalidName": "Neplatná délka jména.",
     "campaigns.fieldInvalidSendAt": "Naplánované datum by mělo být v budoucnosti.",
     "campaigns.fieldInvalidSubject": "Neplatná délka předmětu.",
+    "campaigns.formatHTML": "Format HTML",
     "campaigns.fromAddress": "Z adresy",
     "campaigns.fromAddressPlaceholder": "Vaše jméno <noreply@yoursite.com>",
     "campaigns.invalid": "Neplatná kampaň",

+ 1 - 0
i18n/de.json

@@ -31,6 +31,7 @@
     "campaigns.fieldInvalidName": "Ungültige Länge für `name`.",
     "campaigns.fieldInvalidSendAt": "Das Datum muss in der Zukunft liegen.",
     "campaigns.fieldInvalidSubject": "Ungültige Länge für `subject`.",
+    "campaigns.formatHTML": "Format HTML",
     "campaigns.fromAddress": "Absender",
     "campaigns.fromAddressPlaceholder": "Dein Name <noreply@deineseite.de>",
     "campaigns.invalid": "Ungültige Kampagne",

+ 1 - 0
i18n/en.json

@@ -31,6 +31,7 @@
     "campaigns.fieldInvalidName": "Invalid length for name.",
     "campaigns.fieldInvalidSendAt": "Scheduled date should be in the future.",
     "campaigns.fieldInvalidSubject": "Invalid length for subject.",
+    "campaigns.formatHTML": "Format HTML",
     "campaigns.fromAddress": "From address",
     "campaigns.fromAddressPlaceholder": "Your Name <noreply@yoursite.com>",
     "campaigns.invalid": "Invalid campaign",

+ 1 - 0
i18n/es.json

@@ -31,6 +31,7 @@
     "campaigns.fieldInvalidName": "Largo de nombre inválido",
     "campaigns.fieldInvalidSendAt": "La hora agendada debe ser en el futuro.",
     "campaigns.fieldInvalidSubject": "Largo de asunto inválido",
+    "campaigns.formatHTML": "Format HTML",
     "campaigns.fromAddress": "Dirección origen",
     "campaigns.fromAddressPlaceholder": "Su Nombre <noresponder@susitio.com>",
     "campaigns.invalid": "Campaña inválida",

+ 1 - 0
i18n/fr.json

@@ -31,6 +31,7 @@
     "campaigns.fieldInvalidName": "Longueur du nom invalide.",
     "campaigns.fieldInvalidSendAt": "La date planifiée doit être future.",
     "campaigns.fieldInvalidSubject": "Longueur d'objet non valide.",
+    "campaigns.formatHTML": "Format HTML",
     "campaigns.fromAddress": "Adresse d'envoi",
     "campaigns.fromAddressPlaceholder": "Nom à afficher <noreply@votresite.com>",
     "campaigns.invalid": "Campagne non valide",

+ 1 - 0
i18n/it.json

@@ -31,6 +31,7 @@
     "campaigns.fieldInvalidName": "Lunghezza del nome non valida.",
     "campaigns.fieldInvalidSendAt": "La data programmata deve essere futura.",
     "campaigns.fieldInvalidSubject": "Lunghezza dell'oggetto non valida.",
+    "campaigns.formatHTML": "Format HTML",
     "campaigns.fromAddress": "Mittente",
     "campaigns.fromAddressPlaceholder": "Tuo nome <noreply@tuosito.com>",
     "campaigns.invalid": "Campagna non valida",

+ 1 - 0
i18n/ml.json

@@ -31,6 +31,7 @@
     "campaigns.fieldInvalidName": "`name` ന്റെ ദൈർഘ്യം അസാധുവാണ്.",
     "campaigns.fieldInvalidSendAt": "`send_at` ഭാവിയിലുള്ള തിയതിയായിരിക്കണം.",
     "campaigns.fieldInvalidSubject": "`subject` ന്റെ ദൈർഘ്യം അസാധുവാണ്.",
+    "campaigns.formatHTML": "Format HTML",
     "campaigns.fromAddress": "പ്രേക്ഷകൻ",
     "campaigns.fromAddressPlaceholder": "നിങ്ങളുടെ പേര് <noreply@yoursite.com>",
     "campaigns.invalid": "ക്യാമ്പേയ്ൻ അസാധുവാണ്",

+ 1 - 0
i18n/pl.json

@@ -31,6 +31,7 @@
     "campaigns.fieldInvalidName": "Nieprawidłowa długość dla nazwy,",
     "campaigns.fieldInvalidSendAt": "Zaplanowana data powinna być w przyszłości,",
     "campaigns.fieldInvalidSubject": "Nieprawidłowa długość tytułu",
+    "campaigns.formatHTML": "Format HTML",
     "campaigns.fromAddress": "Adres od",
     "campaigns.fromAddressPlaceholder": "Twoja Nazwa <noreply@yoursite.com>",
     "campaigns.invalid": "Nieprawidłowa kampania",

+ 1 - 0
i18n/pt-BR.json

@@ -31,6 +31,7 @@
     "campaigns.fieldInvalidName": "Quantidade de caracteres inválida para o nome.",
     "campaigns.fieldInvalidSendAt": "A data agendada deve ser no futuro.",
     "campaigns.fieldInvalidSubject": "Quantidade de caracteres inválida para o assunto.",
+    "campaigns.formatHTML": "Format HTML",
     "campaigns.fromAddress": "Endereço do remetente",
     "campaigns.fromAddressPlaceholder": "Seu Nome <noreply@yoursite.com>",
     "campaigns.invalid": "Campanha inválida",

+ 1 - 0
i18n/pt.json

@@ -31,6 +31,7 @@
     "campaigns.fieldInvalidName": "Tamanho de nome inválido.",
     "campaigns.fieldInvalidSendAt": "Data agendada deve ser no futuro.",
     "campaigns.fieldInvalidSubject": "Tamanho de corpo inválido.",
+    "campaigns.formatHTML": "Format HTML",
     "campaigns.fromAddress": "Endereço do Remetente",
     "campaigns.fromAddressPlaceholder": "O Teu Nome <noreply@oteusite.com>",
     "campaigns.invalid": "Campanha inválida",

+ 1 - 0
i18n/ro.json

@@ -31,6 +31,7 @@
     "campaigns.fieldInvalidName": "Lungime nevalidă pentru nume",
     "campaigns.fieldInvalidSendAt": "Data programată ar trebui să fie în viitor.",
     "campaigns.fieldInvalidSubject": "Lungime nevalida pentru subiect.",
+    "campaigns.formatHTML": "Format HTML",
     "campaigns.fromAddress": "De la adresa",
     "campaigns.fromAddressPlaceholder": "Numele tau <noreply@yoursite.com>",
     "campaigns.invalid": "Campanie nevalidă",

+ 1 - 0
i18n/ru.json

@@ -31,6 +31,7 @@
     "campaigns.fieldInvalidName": "Неверная длина имени.",
     "campaigns.fieldInvalidSendAt": "Запланированная дата должна быть позже текущей.",
     "campaigns.fieldInvalidSubject": "Неверная длина темы.",
+    "campaigns.formatHTML": "Format HTML",
     "campaigns.fromAddress": "Адрес отправителя",
     "campaigns.fromAddressPlaceholder": "Ваше имя <noreply@yoursite.com>",
     "campaigns.invalid": "Неверная компания",

+ 1 - 0
i18n/tr.json

@@ -31,6 +31,7 @@
     "campaigns.fieldInvalidName": "İsim uzunluğu yanlış.",
     "campaigns.fieldInvalidSendAt": "Tanımlanan tarih gelecekte olmalı.",
     "campaigns.fieldInvalidSubject": "Konu uzunluğu yanlış verilmiş.",
+    "campaigns.formatHTML": "Format HTML",
     "campaigns.fromAddress": "Gelen adres",
     "campaigns.fromAddressPlaceholder": "isminiz <cevap-verme@siteniz.com>",
     "campaigns.invalid": "Yanlış tanımlı kapmanya",