Direkt zum Inhalt
Bild
CKEditor 5 Custom Plugin OpenSense Labs

Erstellen und Integrieren: CKEditor 5 Plugin in Drupal 11

AI-Translated

Drupal

Ein vollständiger Satz kostenloser Plugins, die CKEditor 5 für Drupal erweitern und von CKSource betreut werden.

Das CKEditor 5 Plugin Pack ermöglicht Ihnen den kostenlosen Zugriff auf einige Premium-Funktionen des CKEditor 5 Premium Features Moduls. Dieses Modul ist jedoch für die Nutzung erforderlich. Für diese Funktionen benötigen Sie keine kostenpflichtige Lizenz.

Dieser Blog bietet eine umfassende Anleitung zur Erstellung eines benutzerdefinierten CKEditor 5 Plugins in Drupal. Wir werden ein Callout-Plugin entwickeln, das es Benutzern ermöglicht, gestylte Inhaltsblöcke mit anpassbaren Hintergrundfarben einzufügen. Es deckt alles ab, von der Projekteinrichtung bis zu den Implementierungsdetails, einschließlich Schema-Definitionen, Model-View-Architektur und Drupal-Integration.

Wenn Sie maßgeschneiderte Dienstleistungen für Ihre Drupal-Website suchen, schauen Sie sich unbedingt unser Angebot an.

Entdecken Sie hier unsere Dienstleistungen

Lassen Sie uns den Prozess starten!

CKEditor 5: Warum benutzerdefinierte Plugins verwenden?

Benutzerdefinierte Plugins in CKEditor 5 erweitern oder ändern die Funktionen des Editors über die Kern- und offiziellen Plugins hinaus. Dies ermöglicht Entwicklern, den Editor an spezifische Anforderungen anzupassen, wie z. B. das Hinzufügen spezieller Funktionen, die Verbindung mit externen Diensten oder die Änderung der Benutzeroberfläche.

Hier ist eine detailliertere Erklärung:

  1. Funktionserweiterung: Wenn die aktuellen Plugins die von Ihnen benötigte präzise Funktion nicht bieten, können Sie mit einem benutzerdefinierten Plugin diese selbst erstellen.
  2. Integration mit externen Diensten: Sie können Plugins entwickeln, um sich mit Drittanbieterdiensten zu verbinden, z. B. zum Einbetten von Videos oder Social-Media-Inhalten.
  3. Benutzerdefinierte Benutzeroberfläche: Plugins können die Benutzeroberfläche des Editors ändern, indem sie benutzerdefinierte Schaltflächen, Symbolleisten oder sogar neue Editortypen hinzufügen.
  4. Semantischer Wert: Plugins können Inhalten eine semantische Bedeutung verleihen, z. B. Anmerkungen oder Funktionen für Barrierefreiheit.
  5. Spezifische Anwendungsfälle: Benutzerdefinierte Plugins sind entscheidend für Situationen, in denen der Standard-Editor die spezifischen Anforderungen eines Projekts nicht erfüllt.
  6. Flexibilität und Anpassungsfähigkeit: Das Plugin-basierte Design von CKEditor 5 bietet große Flexibilität und Anpassungsfähigkeit, wodurch Entwickler hochgradig maßgeschneiderte Bearbeitungserlebnisse schaffen können.

Beispiel:

Stellen Sie sich vor, Sie benötigen eine Funktion, um bestimmte Textpassagen mit einer bestimmten Farbe hervorzuheben. Das vorhandene Hervorhebungs-Plugin bietet möglicherweise nicht die gewünschten genauen Farboptionen. In diesem Szenario können Sie ein benutzerdefiniertes Plugin entwickeln, das die Hervorhebungsfunktion erweitert, um Ihre bevorzugten Farboptionen einzuschließen.

Zusammenfassend lässt sich sagen, dass benutzerdefinierte Plugins unerlässlich sind, um die vollen Möglichkeiten von CKEditor 5 auszuschöpfen und ihn an verschiedene und einzigartige Bearbeitungsanforderungen anzupassen.

CKEditor 5: Projektstruktur für benutzerdefinierte Plugins

Zur Erstellung eines benutzerdefinierten Plugins für CKEditor 5 in einer Drupal 11-Umgebung. Dieses Plugin führt einen wiederverwendbaren, gestylten Block (ähnlich einer Callout-Box) im Inhaltseditor ein, der es Inhaltserstellern ermöglicht, wichtige Nachrichten einfach über eine modale Oberfläche und visuelle Formatierung hervorzuheben.

Plugin-Konzept

Der Blog beschreibt den Prozess der Entwicklung eines "Callout"-Block-Plugins, das:

  • Eine Schaltfläche zur CKEditor 5-Symbolleiste hinzufügt.
  • Ein benutzerdefiniertes modales Formular für Benutzereingaben (wie Titel und Nachricht) öffnet.
  • Einen visuell gestylten Block (wie eine farbige Box) in den Inhalt einfügt.
  • Es ermöglicht, den Block durch Anklicken erneut zu bearbeiten.
  • Den Inhalt semantisch speichert, sodass er korrekt gespeichert, angezeigt und geändert werden kann.
your_module/ 
├── js/ 
│   ├── ckeditor5_plugins/ 
│   │   └── callout/ 
│   │       ├── src/ 
│   │       │   ├── index.js        # Plugin entry point 
│   │       │   ├── callout.js      # Main plugin class 
│   │       │   ├── calloutui.js    # UI implementation 
│   │       │   └── calloutediting.js # Editing logic 
│   │       │   └── insertcalloutcommand.js # Insert command 
│   │       └── icons/ 
│   │           └── callout.svg     # Toolbar icon 
│   └── build/ 
│       └── callout.js              # Built plugin file 
├── css/ 
│   ├── callout.css                 # Callout styles 
│   └── callout_inputbox.css        # Modal styles 
├── your_module.info.yml            # Module info 
├── your_module.libraries.yml       # Libraries definition 
├── your_module.ckeditor5.yml       # Plugin configuration 
└── webpack.config.js               # Build configuration 

1. Einstiegspunkt (index.js) 

Die Datei index.js fungiert als zentrale Anlaufstelle oder Startpunkt für das benutzerdefinierte CKEditor 5 Plugin. Stellen Sie es sich als den Ort vor, an dem alles zusammengeführt und dem Editor zur Verfügung gestellt wird.

Hier ist, was sie typischerweise tut:

Lädt und registriert Untermodule

CKEditor-Plugins folgen oft einem modularen Design. Die eigentliche Funktionalität und Benutzeroberfläche des Plugins sind normalerweise in kleinere Teile aufgeteilt:

  1. CalloutEditing: Verwaltet, wie sich der Plugin-Inhalt im Datenmodell des Editors verhält (Schema, Konvertierung, Befehle usw.)
  2. CalloutUI: Verwaltet die Symbolleistenschaltfläche oder die Benutzeroberfläche, die ein Benutzer anklickt, um die Funktion zu aktivieren

Der Einstiegspunkt importiert diese Unter-Plugins und stellt sicher, dass sie registriert werden, wenn der Editor Ihr benutzerdefiniertes Plugin lädt. Dies hilft, die Codebasis sauber und nach Verantwortlichkeiten getrennt zu halten.

Registriert Befehle

In CKEditor 5 wird jede wichtige Aktion (wie das Einfügen einer Tabelle, eines Bildes oder eines benutzerdefinierten Blocks wie eines Callouts) über einen Befehl abgewickelt. Diese Befehle können durch Schaltflächen oder programmatisch ausgelöst werden.

In index.js registrieren Sie also auch den Befehl insertCallout, der ausgeführt wird, wenn ein Benutzer die Symbolleistenschaltfläche anklickt, um eine Callout-Box im Editor hinzuzufügen.

Teilt CKEditor mit, was dieses Plugin ist

Schließlich definiert diese Datei die eigentliche Plugin-Klasse (normalerweise Callout genannt) und verwendet die Eigenschaft requires, um CKEditor mitzuteilen, welche anderen Plugins für die Funktion erforderlich sind (d.h. die UI- und Bearbeitungsmodule). CKEditor bündelt sie dann bei der Initialisierung.

import Callout from './callout'; 
import CalloutUI from './calloutui'; 
import CalloutEditing from './calloutediting'; 
 
// Export the plugin for CKEditor 5 
export default { 
  Callout, 
  CalloutUI, 
  CalloutEditing 
}; 

2. Haupt-Plugin-Klasse (callout.js) 

Diese Datei dient als Kern-Plugin-Definition. Die Datei callout.js stellt die offizielle Definition Ihres benutzerdefinierten Plugins dar. Wenn index.js die Startrampe ist, dann ist callout.js der Bauplan, der CKEditor mitteilt:

„Hey, das ist das Callout-Plugin, und das braucht es, um zu funktionieren.“

Lassen Sie es uns aufschlüsseln:

Fungiert als Plugin-Container

Diese Datei enthält die Haupt-Plugin-Klasse, die typischerweise Callout genannt wird. Es ist eine Klasse, die die Basisklasse Plugin von CKEditor erweitert. Dies teilt CKEditor mit, dass es sich um ein richtiges Plugin handelt.

Kombiniert Logik und Benutzeroberfläche

Innerhalb der Klasse gibt es eine erforderliche statische Eigenschaft. Hier listet das Plugin Abhängigkeiten auf, insbesondere die beiden Kernbestandteile:

  1. CalloutEditing: Definiert, wie sich das Plugin verhält, welches Schema es verwendet, wie Daten gespeichert/konvertiert werden usw.
  2. CalloutUI: Verwaltet, wie das Plugin in der Editor-Benutzeroberfläche erscheint (Symbolleistenschaltflächen, Dropdowns, Symbole)

Durch die Aggregation beider stellt das Plugin sicher, dass sowohl die Funktionalität (Bearbeitung) als auch die Benutzererfahrung (Benutzeroberfläche) CKEditor bei der Initialisierung des Plugins zur Verfügung stehen.

Plugin-Initialisierung

Obwohl die Hauptlogik in CalloutEditing und CalloutUI liegt, fungiert die Datei callout.js als zentrales Register, das sie miteinander verbindet. Sie hilft CKEditor, das Plugin als eine einzige Einheit zu erkennen, sodass es bei der Konfiguration des Editors einfach hinzugefügt werden kann.

import { Plugin } from 'ckeditor5/src/core'; 
import CalloutEditing from './calloutediting'; 
import CalloutUI from './calloutui'; 
 
export default class Callout extends Plugin { 
  // Define required plugins 
  static get requires() { 
    return [CalloutEditing, CalloutUI]; 
  } 
 
  // Plugin name used in configuration 
  static get pluginName() { 
    return 'Callout'; 
  } 
} 

3. UI-Implementierung (calloutui.js)

Die Datei calloutui.js ist der Ort, an dem die visuelle Seite des Plugins zum Leben erwacht. Sie konzentriert sich ausschließlich darauf, wie Benutzer mit dem Plugin über die CKEditor-Symbolleiste und alle UI-Elemente wie Schaltflächen oder Modals interagieren.

Lassen Sie es uns aufschlüsseln:

Einrichtung der Symbolleistenschaltfläche

Diese Datei ist für die Erstellung und Registrierung der Symbolleistenschaltfläche verantwortlich, die in CKEditor erscheint und die Benutzer anklicken, um eine Callout-Box einzufügen.

Sie gibt der Schaltfläche eine Beschriftung, einen Tooltip und ein Symbol (das benutzerdefiniert oder aus dem CKEditor-Symbolset stammen kann).

Sie verbindet die Schaltfläche mit einem bestimmten Befehl, in diesem Fall insertCallout. Wenn ein Benutzer die Schaltfläche anklickt, wird dieser Befehl ausgeführt, um den Callout einzufügen.

Benutzerdefinierte UI-Komponenten

Neben der Schaltfläche kann diese Datei auch benutzerdefinierte Interaktionen wie ein modales Fenster oder ein Dropdown definieren.
In diesem Fall verfügt das Plugin über ein Modal, das es Benutzern ermöglicht, die Hintergrundfarbe für den Callout auszuwählen.

Wenn die Symbolleistenschaltfläche angeklickt wird, erscheint eine Benutzeroberfläche, in der der Benutzer aus vordefinierten Hintergrundfarben (wie Info, Warnung, Erfolg usw.) auswählen kann.

Sobald ein Benutzer die Farbe ausgewählt hat, wird sie auf den eingefügten Callout-Block angewendet.

Dies macht das Plugin flexibler und benutzerfreundlicher, indem es Benutzern ermöglicht, das Aussehen des Callouts visuell zu konfigurieren, ohne Code schreiben zu müssen.

Reibungslose Integration

Alles, was in dieser Datei erstellt wird – Schaltflächen, Modals, Event-Handler – wird in das UI-Framework von CKEditor integriert. Das bedeutet, dass sich das Plugin nativ in den Editor einfügt und ein konsistentes Erscheinungsbild bietet.

import { Plugin } from 'ckeditor5/src/core'; 
import { ButtonView } from 'ckeditor5/src/ui'; 
import icon from '../../../../icons/callout.svg'; 
import '../../../../css/callout_inputbox.css'; 
import { DomEventObserver } from 'ckeditor5/src/engine.js'; 
 
/** 
 * Custom observer to handle double-click events. 
 */ 
class DoubleClickObserver extends DomEventObserver { 
  constructor(view) { 
    super(view); 
    this.domEventType = 'dblclick'; 
  } 
 
  onDomEvent(domEvent) { 
    this.fire(domEvent.type, domEvent); 
  } 
} 
 
/** 
 * CalloutUI Plugin for CKEditor 5 
 * Adds a button to the toolbar for inserting/editing callouts with customizable background colors. 
 */ 
export default class CalloutUI extends Plugin { 
  init() { 
    const editor = this.editor; 
 
    // Register the callout button in the component factory 
    editor.ui.componentFactory.add('callout', (locale) => { 
      const command = editor.commands.get('insertCallout'); 
      const buttonView = new ButtonView(locale); 
 
      // Configure the toolbar button 
      buttonView.set({ 
        label: editor.t('Callout'), 
        icon, 
        tooltip: true, 
      }); 
 
      // Bind button state to command 
      buttonView.bind('isOn', 'isEnabled').to(command, 'value', 'isEnabled'); 
 
      // Open modal on button click 
      this.listenTo(buttonView, 'execute', () => this._openCalloutModal(editor)); 
 
      return buttonView; 
    }); 
 
    // Add double-click event listener for editing existing callouts 
    this._addClickListener(editor); 
  } 
 
  /** 
   * Opens a modal popup for editing or inserting callout properties. 
   * @param {Object} editor - CKEditor instance. 
   * @param {Object} existingData - Optional data for editing an existing callout. 
   */ 
  _openCalloutModal(editor, existingData = {}) { 
    const modal = document.createElement('div'); 
    modal.classList.add('ck-modal'); 
    modal.innerHTML = ` 
      <div class="dialog-box"> 
        <div class="ck-modal-popup"> 
          <div class="header"> 
            <div>Edit Callout</div> 
            <div id="closeButton">x</div> 
          </div> 
          <div class="form_input"> 
            <label for="backgroundColorSelect">Background Color:</label> 
            <select id="backgroundColorSelect" style="width: 100%; margin-bottom: 10px;"> 
              <option value="callout-blue" ${existingData.class === 'callout-blue' ? 'selected' : ''}>Blue</option> 
              <option value="callout-grey" ${existingData.class === 'callout-grey' ? 'selected' : ''}>Grey</option> 
              <option value="callout-black" ${existingData.class === 'callout-black' ? 'selected' : ''}>Black</option> 
            </select> 
          </div> 
          <div class="quote_buttons"> 
            <button id="okButton">OK</button> 
            <button id="cancelButton">Cancel</button> 
          </div> 
        </div> 
      </div> 
    `; 
 
    document.body.appendChild(modal); 
 
    // Close modal on clicking 'x' button 
    modal.querySelector('#closeButton').addEventListener('click', () => modal.remove()); 
 
    // Close modal on clicking 'Cancel' button 
    modal.querySelector('#cancelButton').addEventListener('click', () => modal.remove()); 
 
    // Save selected background color and insert callout on clicking 'OK' button 
    modal.querySelector('#okButton').addEventListener('click', () => { 
      try { 
        const backgroundColor = modal.querySelector('#backgroundColorSelect').value; 
        editor.execute('insertCallout', { backgroundColor }); 
        modal.remove(); 
      } catch (err) { 
        console.error('Error executing insertCallout command:', err); 
      } 
    }); 
  } 
 
  /** 
   * Adds a double-click listener to the editor for opening the callout modal on existing callouts. 
   * @param {Object} editor - CKEditor instance. 
   */ 
  _addClickListener(editor) { 
    const view = editor.editing.view; 
    const viewDocument = view.document; 
 
    // Register observer for double-click event 
    view.addObserver(DoubleClickObserver); 
 
    // Listen for double-click event on callout elements 
    editor.listenTo(viewDocument, 'dblclick', (event, data) => { 
      const domTarget = data.domTarget; 
      const calloutElement = domTarget.closest('.callout'); 
 
      if (calloutElement) { 
        const modelElement = this._getModelElementFromDom(editor, calloutElement); 
        if (modelElement) { 
          const existingClass = modelElement.getAttribute('class'); 
          this._openCalloutModal(editor, { class: existingClass }); 
        } 
      } 
    }); 
  } 
 
  /** 
   * Retrieves the model element corresponding to a given DOM element. 
   * @param {Object} editor - CKEditor instance. 
   * @param {HTMLElement} domElement - DOM element representing a callout. 
   * @returns {Object|null} - The model element, or null if not found. 
   */ 
  _getModelElementFromDom(editor, domElement) { 
    const mapper = editor.editing.mapper; 
    const domConverter = editor.editing.view.domConverter; 
    const viewElement = domConverter.domToView(domElement); 
    return mapper.toModelElement(viewElement); 
  } 
} 

4. Bearbeitungslogik (calloutediting.js)

Die Datei calloutediting.js ist das funktionale Rückgrat Ihres Plugins. Sie definiert, wie sich der Inhalt intern in CKEditor 5 unter Verwendung seiner Model-View-Controller (MVC)-inspirierten Architektur verhält:

  1. Modell: Interne Datenstruktur, die den Inhalt darstellt. 
  2. Bearbeitungsansicht: WYSIWYG-Oberfläche, mit der Benutzer interagieren. 
  3. Datenansicht: Saubere HTML-Ausgabe zum Speichern. 
  4. Konverter: Brücke zwischen Modell und Ansichten. 

Verwaltet das Datenmodell-Schema und die Konvertierungspipeline zwischen dem Modell und der Editor-Ansicht.

import { Plugin } from 'ckeditor5/src/core'; 
import { toWidget, toWidgetEditable } from 'ckeditor5/src/widget'; 
import { Widget } from 'ckeditor5/src/widget'; 
import InsertCalloutCommand from './insertcalloutcommand'; 
 
/** 
 * This plugin handles the editing functionality for Callout elements in CKEditor. 
 */ 
export default class CalloutEditing extends Plugin { 
  /** 
   * Specifies the required dependencies for this plugin. 
   */ 
  static get requires() { 
    return [Widget]; 
  } 
 
  /** 
   * Initializes the plugin by defining the schema, converters, and adding the command. 
   */ 
  init() { 
    this._defineSchema(); 
    this._defineConverters(); 
    this.editor.commands.add('insertCallout', new InsertCalloutCommand(this.editor)); 
  } 
 
  /** 
   * Defines the schema for Callout elements and their allowed attributes and content. 
   */ 
  _defineSchema() { 
    const schema = this.editor.model.schema; 
 
    /** 
     * **isObject: true**: It behaves like an independent unit (can't be split across lines). 
     * **allowWhere: '$block'**: Can be inserted where block-level elements (e.g., paragraphs) are allowed. 
     * **allowAttributes: ['class']**: Allows setting a CSS class (e.g., callout-blue). 
     */ 
    schema.register('callout', { 
      isObject: true, 
      allowWhere: '$block', 
      allowAttributes: ['class'], 
    }); 
 
    /** 
     * **calloutDescription**: A child element inside callout, containing text. 
     * **isLimit: true**: Prevents nested callouts inside descriptions. 
     */ 
    schema.register('calloutDescription', { 
      isLimit: true, 
      allowIn: 'callout', 
      allowContentOf: '$root', 
    }); 
 
    schema.register('calloutParagraph', { 
      allowWhere: '$block', 
      allowIn: 'calloutDescription', 
      allowContentOf: '$block', 
    }); 
 
    // Prevents nested callout elements inside a calloutDescription. 
    schema.addChildCheck((context, childDefinition) => { 
      if (context.endsWith('calloutDescription') && childDefinition.name === 'callout') { 
        return false; 
      } 
    }); 
  } 
 
  /** 
   * Defines the data and editing converters for callout-related elements. 
   */ 
  _defineConverters() { 
    const conversion = this.editor.conversion; 
    console.log(conversion, "conversion"); 
 
    // Converter for callout element Upcasting (HTML → Editor Model) 
    // **<div class="callout">** and maps it to a CKEditor callout element. 
    conversion.for('upcast').elementToElement({ 
      model: (viewElement, { writer }) => { 
        const cssClass = viewElement.getAttribute('class') || 'callout-blue'; 
        return writer.createElement('callout', { 
          class: cssClass, 
        }); 
      }, 
      view: { 
        name: 'div', 
        classes: 'callout' 
      }, 
    }); 
 
    // Attribute converter for callout class 
    conversion.for('upcast').attributeToAttribute({ 
      view: { 
        name: 'div', 
        key: 'class', 
        value: /callout-[a-z]+/ 
      }, 
      model: { 
        key: 'class', 
        value: viewElement => { 
          const classes = viewElement.getAttribute('class').split(' '); 
          return classes.find(cls => cls.startsWith('callout-')) || 'callout-blue'; 
        } 
      } 
    }); 
 
    conversion.for('downcast').attributeToAttribute({ 
      model: { 
        name: 'callout', 
        key: 'class' 
      }, 
      view: modelAttributeValue => ({ 
        key: 'class', 
        value: `callout ${modelAttributeValue}` 
      }) 
    }); 
 
    // Downcasting (Editor Model → HTML) 
    // Writes CKEditor callout elements back as <div class="callout callout-blue"> in the final HTML. 
    conversion.for('dataDowncast').elementToElement({ 
      model: 'callout', 
      view: (modelElement, { writer }) => { 
        const cssClass = modelElement.getAttribute('class') || 'callout-blue'; 
        return writer.createContainerElement('div', { class: `callout ${cssClass}` }); 
      } 
    }); 
 
    conversion.for('editingDowncast').elementToElement({ 
      model: 'callout', 
      view: (modelElement, { writer }) => { 
        const cssClass = modelElement.getAttribute('class') || 'callout-blue'; 
        const div = writer.createContainerElement('div', { class: `callout ${cssClass}` }); 
        return toWidget(div, writer, { label: 'Editable callout' }); 
      } 
    }); 
 
    // Converter for calloutDescription element (upcast, downcast, editing) 
    conversion.for('upcast').elementToElement({ 
      model: 'calloutDescription', 
      view: { 
        name: 'div', 
        classes: 'callout-description' 
      }, 
    }); 
 
    conversion.for('dataDowncast').elementToElement({ 
      model: 'calloutDescription', 
      view: { 
        name: 'div', 
        classes: 'callout-description' 
      }, 
    }); 
 
    // Wraps the callout in a CKEditor "widget" for better user interaction. 
    conversion.for('editingDowncast').elementToElement({ 
      model: 'calloutDescription', 
      view: (modelElement, { writer }) => { 
        const div = writer.createEditableElement('div', { class: 'callout-description' }); 
        return toWidgetEditable(div, writer, { label: 'Callout description editable area' }); 
      }, 
    }); 
 
    // Converter for calloutParagraph element (upcast, downcast, editing) 
    conversion.for('upcast').elementToElement({ 
      model: 'calloutParagraph', 
      view: 'p', 
    }); 
 
    conversion.for('dataDowncast').elementToElement({ 
      model: 'calloutParagraph', 
      view: 'p', 
    }); 
 
    conversion.for('editingDowncast').elementToElement({ 
      model: 'calloutParagraph', 
      view: (modelElement, { writer }) => { 
        return writer.createEditableElement('p'); 
      }, 
    }); 
  } 
} 

5. Einfüge-Befehl (insertcalloutcommand.js)

Die Datei insertcalloutcommand.js definiert die Kernlogik, die die Callout-Funktion in CKEditor 5 antreibt.

Sie bestimmt, was passiert, wenn Benutzer einen Callout einfügen oder ändern, und stellt sicher, dass die korrekte Struktur und die richtigen Attribute zum Modell hinzugefügt werden.

Obwohl sie durch die Benutzeroberfläche ausgelöst wird, funktioniert diese Datei auf der Logikebene und ist unerlässlich, um das Plugin interaktiv und benutzergesteuert zu gestalten.

import { Command } from 'ckeditor5/src/core'; 
 
export default class InsertCalloutCommand extends Command { 
  execute(options = {}) { 
    try { 
      const { model } = this.editor; 
      model.change((writer) => { 
        // Get the first position in the selection 
        const firstPosition = model.document.selection.getFirstPosition(); 
 
        // Find the closest callout element ancestor 
        const calloutElement = firstPosition.findAncestor('callout'); 
 
        if (calloutElement) { 
          writer.setAttribute('class', options.backgroundColor, calloutElement); 
        } else { 
          const callout = createCallout(writer, options.backgroundColor || 'callout-blue'); 
          model.insertContent(callout); 
        } 
      }); 
    } catch (err) { 
      throw err; 
    } 
  } 
 
  refresh() { 
    const { model } = this.editor; 
    const { selection } = model.document; 
    const allowedIn = model.schema.findAllowedParent(selection.getFirstPosition(), 'callout'); 
    this.isEnabled = true; 
  } 
} 
 
function createCallout(writer, calloutClass) { 
  const callout = writer.createElement('callout', { 
    class: calloutClass 
  }); 
 
  const calloutDescription = writer.createElement('calloutDescription'); 
  writer.append(calloutDescription, callout); 
 
  const paragraph = writer.createElement('paragraph'); 
  writer.appendText('Insert callout content here...', paragraph); 
  writer.append(paragraph, calloutDescription); 
 
  return callout; 
}

Drupal-Konfiguration 

your_module.libraries.yml 

Definiert die JavaScript- und CSS-Bibliotheken für das Modul.

callout: 
  js: 
    js/build/callout.js: { preprocess: false, minified: true } 
  dependencies: 
    - core/ckeditor5 
  css: 
    theme: 
      css/callout.css: {} 

your_module.ckeditor5.yml 

Entscheidend für die Integration des Plugins in CKEditor 5 in Drupal. Es weist Drupal an, wie das Plugin geladen werden soll.

your_module_callout: 
  ckeditor5: 
    plugins: 
      - callout.Callout 
  drupal: 
    label: Callout 
    library: your_module/callout 
  elements: 
    - <div class="callout"> 
    - <div class="callout-blue"> 
    - <div class="callout-grey"> 
    - <div class="callout-black"> 

Webpack-Konfiguration 

Webpack ist unerlässlich für die Erstellung des CKEditor 5 Plugins. Schlüsselelemente: 

  1. DllReferencePlugin: Verknüpft das Plugin mit dem Kern-DLL-Bundle von CKEditor. 
  2. Modulregeln: Handhabt SVG-Symbole und CSS-Verarbeitung. 
  3. Ausgabekonfiguration: Erstellt das Modul im UMD-Format für Drupal.
const path = require('path'); 
const fs = require('fs'); 
const webpack = require('webpack'); 
const { styles, builds } = require('@ckeditor/ckeditor5-dev-utils'); 
const TerserPlugin = require('terser-webpack-plugin'); 
 
function getDirectories(srcpath) { 
  return fs 
    .readdirSync(srcpath) 
    .filter((item) => fs.statSync(path.join(srcpath, item)).isDirectory()); 
} 
 
module.exports = []; 
 
// Loop through every subdirectory in src, each a different plugin, and build 
// each one in ./build. 
getDirectories('./js/ckeditor5_plugins').forEach((dir) => { 
  const bc = { 
    mode: 'production', 
    optimization: { 
      minimize: true, 
      minimizer: [ 
        new TerserPlugin({ 
          terserOptions: { 
            format: { 
              comments: false, 
            }, 
          }, 
          test: /\.js(\?.*)?$/i, 
          extractComments: false, 
        }), 
      ], 
      moduleIds: 'named', 
    }, 
    entry: { 
      path: path.resolve( 
        __dirname, 
        'js/ckeditor5_plugins', 
        dir, 
        'src/index.js', 
      ), 
    }, 
    output: { 
      path: path.resolve(__dirname, './js/build'), 
      filename: `${dir}.js`, 
      library: ['CKEditor5', dir], 
      libraryTarget: 'umd', 
      libraryExport: 'default', 
    }, 
    plugins: [ 
      // It is possible to require the ckeditor5-dll.manifest.json used in 
      // core/node_modules rather than having to install CKEditor 5 here. 
      // However, that requires knowing the location of that file relative to 
      // where your module code is located. 
      new webpack.DllReferencePlugin({ 
        manifest: require('./node_modules/ckeditor5/build/ckeditor5-dll.manifest.json'), 
        scope: 'ckeditor5/src', 
        name: 'CKEditor5.dll', 
      }), 
    ], 
    module: { 
      rules: [ 
        { test: /\.svg$/, use: 'raw-loader' }, 
        { 
          test: /\.css$/, 
          use: ['style-loader', 'css-loader'], // Process and inject CSS files 
        }, 
      ], 
    }, 
  }; 
 
  module.exports.push(bc); 
});

Package.json 

{ 
  "name": "callout", 
  "main": "index.js", 
  "scripts": { 
    "dll:build": "webpack --config webpack.config.js" 
  }, 
  "devDependencies": { 
    "@ckeditor/ckeditor5-dev-utils": "43.0.1", 
    "ckeditor5": "44.0.0", 
    "css-loader": "^7.1.2", 
    "prettier": "^2.8.8", 
    "raw-loader": "^4.0.2", 
    "style-loader": "^4.0.0", 
    "terser-webpack-plugin": "^3.0.2", 
    "webpack": "^5.88.2", 
    "webpack-cli": "^4.4.0" 
  }, 
  "dependencies": { 
    "v18": "^1.0.2" 
  } 
}

CKEditor 5: Testen Ihres Plugins

  • Führen Sie npm install aus, um Abhängigkeiten zu installieren. 

  • Erstellen Sie das Plugin mit npm run build. 

  • Leeren Sie den Drupal-Cache mit drush cr. 

  • Konfigurieren Sie ein Textformat, um Ihr Plugin in der Drupal-Admin-Oberfläche zu verwenden. 

  • Testen Sie das Plugin in einem Inhaltseditor. 

CKEditor 5: Visuelle Ausgabe

So sieht das Plugin in CKEditor 5 aus:

  1.   Symbolleistenschaltfläche: 

Callout CKEditor 5 OpenSense Labs

  1. Modaler Dialog

Modaler Dialog CKEditor 5 OpenSense Labs

3. Gerenderter Callout: 

Gerenderter Callout CKEditor 5 OpenSense Labs

CKEditor 5: Häufige Probleme und Lösungen

  1. Plugin wird nicht angezeigt: Überprüfen Sie, ob der Plugin-Name in ckeditor5.yml exakt übereinstimmt. 
  2. Styling-Probleme: Stellen Sie sicher, dass CSS ordnungsgemäß über libraries.yml geladen wird, und überprüfen Sie die Browserkonsole auf Fehler. 
  3. Konvertierungsprobleme: Überprüfen Sie die Schema-Definitionen und Konverter-Mappings.

Siehe auch:

1. Drupal SDC: Vorteile von Single Directory Components

2. Drupal Debug: Effektive Techniken und Tools

3. Drupal SDC vs. Storybook: Was ist der Unterschied?

4. Starshot: Drupals neue CMS-Initiative

Wichtige Erkenntnisse:

1. Das Callout-Plugin wurde entwickelt, um CKEditor 5 in Drupal 11 zu erweitern und Redakteuren das Hinzufügen von gestylten Inhaltsboxen im WYSIWYG-Editor zu ermöglichen.

2. Es umfasst wesentliche Elemente wie eine Symbolleistenschaltfläche, ein benutzerfreundliches Popup zur Anpassung und Funktionen zum Einfügen und Rendern.

3. Das Plugin hält sich an die Model-View-Architektur von CKEditor, um eine saubere Struktur, Bearbeitung und Ausgabe zu gewährleisten.

4. Ein spezifischer Befehl verwaltet die interaktiven Aktionen, wenn Benutzer Callouts einfügen oder ändern.

5. Perfekt in Drupal 11 integriert, verbessert es das redaktionelle Erlebnis, ohne benutzerdefiniertes HTML zu erfordern.

Abonnieren

Ready to start your digital transformation journey with us?

Verwandte Blogs

Gin Admin Theme: Claro im Drupal CMS ersetzen

Gin Admin Theme Drupal CMS Will Replace Claro OpenSense Labs

Drupals Standard-Admin-Theme, Claro, ist einer der Faktoren, die einen visuellen Vergleich zwischen Drupal 7 und Drupal 10…

Zurück von der DrupalCon Atlanta 2025: Ein Meilenstein für OpenSense Labs

Zurück von der DrupalCon Atlanta 2025: Ein Meilenstein für OpenSense Labs

„Fit. Schnell. Für die Ewigkeit gebaut.“ Das war nicht nur ein Slogan, sondern die Denkweise, mit der wir zur DrupalCon…

Erklärbare KI-Tools: SHAPs Stärke in der KI

Explainable AI tools Explainable AI And SHAP OpenSense Labs

Wissen Sie, was erklärbare KI-Tools sind? Erklärbare KI-Tools sind Programme, die zeigen, wie eine KI ihre Entscheidungen…