پلاگین‌های ویرایشگر

این قسمت توضیح می‌دهد چگونه یک پلاگین ویرایشگر توسعه بدهیم.

پلاگین ویرایشگر به این صورت کار می‌کند: پلاگین یک ارائه‌دهنده ویرایشگر (Editor provider) ثبت می‌کند، در رویداد onEditorSetup، سپس سیستم بسته به تنظیمات انتخاب شده در پیکربندی کلی و نحوه پیکربندی فیلد ویرایشگر در فرم، از آن استفاده می‌کند.

رویدادهای این گروه:

onEditorSetup

این رویداد یک بار در زمان اجرا فعال می‌شود، وقتی که سیستم نیاز دارد فیلد ویرایشگر را رندر کند.

امضای رویداد:

 
function onEditorSetup(Joomla\CMS\Event\Editor\EditorSetupEvent $event){…}

ویژگی‌های رویداد:

 
* @var Joomla\CMS\Editor\EditorsRegistry $subject
 */
$subject = $event->getEditorsRegistry();
 

ایجاد پلاگین ویرایشگر

پلاگین شامل سه جزء اصلی است:

1. کلاس ارائه‌دهنده ویرایشگر، که منطق رندر کردن ورودی و بارگذاری پلاگین‌های دکمه‌های ویرایشگر (XTD) را فراهم می‌کند.

2. کد جاوااسکریپت ویرایشگر برای یکپارچگی سمت مشتری.

3. پلاگین در گروه ویرایشگرها که ارائه‌دهنده را ثبت می‌کند تا سیستم از وجود آن آگاه باشد.

مثال زیر فرض می‌کند شما از قبل نحوه ساخت پلاگین جوملا را می‌دانید. 

بیایید یک ویرایشگر ساده بسازیم.

در این مثال، ما یک عنصر <textarea> را به عنوان ویرایشگر نمایش می‌دهیم.

بخش Backend

ابتدا یک پلاگین در مسیر plugins/editors/example/ ایجاد کنید و نام آن را example بگذارید، فرض کنید namespace آن JoomlaExample\Plugin\Editors\Example باشد.

سپس یک ارائه‌دهنده بسازید که در مسیر plugins/editors/example/src/Provider/ExampleEditorProvider.php قرار می‌گیرد.

 
namespace JoomlaExample\Plugin\Editors\Example\Provider;

use Joomla\CMS\Application\CMSApplicationInterface;
use Joomla\CMS\Editor\AbstractEditorProvider;
use Joomla\Event\DispatcherInterface;
use Joomla\Registry\Registry;

/**
 * ارائه‌دهنده برای ویرایشگر Example
 */
final class ExampleEditorProvider extends AbstractEditorProvider
{
    /**
     * شیء رجیستری که پارامترهای پلاگین را نگه می‌دارد
     *
     * @var    Registry
     */
    protected $params;

    /**
     * شیء برنامه کاربردی
     *
     * @var    CMSApplicationInterface
     */
    protected $application;

    /**
     * سازنده کلاس
     *
     * @param   Registry                 $params
     * @param   CMSApplicationInterface  $application
     * @param   DispatcherInterface      $dispatcher
     */
    public function __construct(Registry $params, CMSApplicationInterface $application, DispatcherInterface $dispatcher)
    {
        $this->params      = $params;
        $this->application = $application;

        $this->setDispatcher($dispatcher);
    }

    /**
     * نام ویرایشگر را به صورت رشته CMD برمی‌گرداند.
     *
     * @return string
     */
    public function getName(): string
    {
        return 'example';
    }

    /**
     * دریافت کد HTML ویرایشگر
     *
     * @param   string  $name        نام ورودی
     * @param   string  $content     محتوای فیلد
     * @param   array   $attributes  آرایه انجمنی ویژگی‌های ویرایشگر
     * @param   array   $params      آرایه انجمنی پارامترهای ویرایشگر
     *
     * @return  string  کد HTML ویرایشگر
     */
    public function display(string $name, string $content = '', array $attributes = [], array $params = [])
    {
        // بررسی ویژگی‌ها و پارامترهای ویرایشگر
        $col     = $attributes['col'] ?? '';
        $row     = $attributes['row'] ?? '';
        $id      = $attributes['id'] ?? '';
        $buttons = $params['buttons'] ?? true;
        $asset   = $params['asset'] ?? 0;
        $author  = $params['author'] ?? 0;

        // رندر کردن کد ویرایشگر
        return '<joomla-editor-example>'
          . '<textarea name="' . $name . '" id="' . $id . '" cols="' . $col . '" rows="' . $row . '">' . $content . '</textarea>'
          . $this->displayButtons($buttons, ['asset' => $asset, 'author' => $author, 'editorId' => $id])
          . '</joomla-editor-example>';
    }
}

نکته: ما از کلاس AbstractEditorProvider به عنوان پایه استفاده می‌کنیم که قبلاً منطق بارگذاری و رندر کردن پلاگین‌های دکمه‌های ویرایشگر (XTD) را دارد.

در اینجا متد `display()` کد HTML ویرایشگر را تولید می‌کند. مهم است که دکمه‌های ویرایشگر (XTD) داخل همان کانتینر ویرایشگر باشند. ما از عنصر سفارشی `<joomla-editor-example>` استفاده می‌کنیم که یکپارچگی سمت کلاینت را ساده‌تر می‌کند، ولی می‌توانستیم از یک <div> معمولی هم استفاده کنیم.

ثبت ارائه‌دهنده در سیستم با پلاگین و رویداد:

 
namespace JoomlaExample\Plugin\Editors\Example\Extension;

use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\Event\SubscriberInterface;
use JoomlaExample\Plugin\Editors\Example\Provider\ExampleEditorProvider;

final class ExampleEditor extends CMSPlugin implements SubscriberInterface
{
    /**
     * آرایه‌ای از رویدادهایی که این پلاگین گوش می‌دهد.
     *
     * @return  array
     */
    public static function getSubscribedEvents(): array
    {
        return [
            'onEditorSetup' => 'onEditorSetup',
        ];
    }

    /**
     * ثبت نمونه ویرایشگر
     *
     * @param EditorSetupEvent $event
     *
     * @return void
     *
     * @since   __DEPLOY_VERSION__
     */
    public function onEditorSetup(EditorSetupEvent $event)
    {
        $event->getEditorsRegistry()->add(new ExampleEditorProvider($this->params, $this->getApplication(), $this->getDispatcher()));
    }
}

در این مرحله، پس از نصب و فعال‌سازی پلاگین، ویرایشگر باید در تنظیمات کلی قابل انتخاب باشد و `<textarea>` ما را رندر کند. با این حال، هنوز بخش یکپارچگی سمت کلاینت را کامل نکرده‌ایم که در مرحله بعدی انجام خواهد شد.

بخش فرانت‌اند

این بخش اجازه می‌دهد سایر افزونه‌های جوملا با ویرایشگر و محتوای آن تعامل داشته باشند. یک فایل جاوااسکریپت در مسیر `media/plg_editors_example/js/editor-example.js` ایجاد کنید و متد `display()` ارائه‌دهنده ویرایشگر را برای بارگذاری این فایل اصلاح کنید.

 
public function display(string $name, string $content = '', array $attributes = [], array $params = [])
{
    // بارگذاری فایل‌های دارایی ویرایشگر (Asset files)
    $this->application->getDocument()->getWebAssetManager()
        ->registerAndUseScript(
            'plg_editors_example.editor-example',
            'plg_editors_example/editor-example.js',
            [],
            ['type' => 'module'],
            ['editors']
        );

    // بررسی ویژگی‌ها و پارامترهای ویرایشگر
    $col     = $attributes['col'] ?? '';
    $row     = $attributes['row'] ?? '';
    $id      = $attributes['id'] ?? '';
    $buttons = $params['buttons'] ?? true;
    $asset   = $params['asset'] ?? 0;
    $author  = $params['author'] ?? 0;

    // رندر کردن کد ویرایشگر
    return '<joomla-editor-example>'
      . '<textarea name="' . $name . '" id="' . $id . '" cols="' . $col . '" rows="' . $row . '">' . $content . '</textarea>'
      . $this->displayButtons($buttons, ['asset' => $asset, 'author' => $author, 'editorId' => $id])
      . '</joomla-editor-example>';
}

 اسکریپت باید نمونه ویرایشگر را ثبت کند و متدهای پایه‌ای برای «تنظیم/دریافت» مقدار، جایگزینی بخش انتخاب‌شده و غیره را فراهم کند.

 
// وارد کردن مؤلفه‌های لازم
import { JoomlaEditor, JoomlaEditorDecorator } from 'editor-api';

/**
 * دکوراتور EditorExample که متدهای لازم هر نمونه ویرایشگر را پیاده‌سازی می‌کند.
 * جوملا از این برای به اشتراک‌گذاری بین افزونه‌ها استفاده می‌کند، تا با نمونه اصلی ویرایشگر تعامل داشته باشند.
 */
class EditorExampleDecorator extends JoomlaEditorDecorator {

  getValue() {
    return this.instance.input.value;
  }

  setValue(value) {
    this.instance.input.value = value;
    return this;
  }

  getSelection() {
    const input = this.instance.input;

    if (input.selectionStart || input.selectionStart === 0) {
        return input.value.substring(input.selectionStart, input.selectionEnd);
    }
    return input.value;
  }

  replaceSelection(value) {
    const input = this.instance.input;
    if (input.selectionStart || input.selectionStart === 0) {
        input.value = input.value.substring(0, input.selectionStart)
            + value
            + input.value.substring(input.selectionEnd, input.value.length);
    } else {
        input.value += value;
    }
    return this;
  }

  disable(enable) {
    this.instance.input.disabled = !enable;
    this.instance.input.readOnly = !enable;
    return this;
  }
}

/**
 * مقداردهی اولیه ویرایشگر
 */
class JoomlaEditorExample extends HTMLElement {
    // زمانی که عنصر به DOM متصل می‌شود
    connectedCallback() {
        // گرفتن اولین فرزند <textarea> که ورودی است
        this.input = this.firstElementChild;

        // ثبت دکوراتور در Joomla.Editor
        const jEditor = new EditorExampleDecorator(this, 'example', this.input.id);
        JoomlaEditor.register(jEditor);

        // شناسایی زمان تعامل کاربر با ویرایشگر
        // اسکریپت باید به جوملا اطلاع دهد که ویرایشگر یا یکی از دکمه‌های ویرایشگر (XTD) فعال شده‌اند
        if (!this.interactionCallback) {
            this.interactionCallback = () => {
                JoomlaEditor.setActive(this.input.id);
            };
        }
        this.addEventListener('click', this.interactionCallback);
    }

    // زمانی که عنصر از DOM حذف می‌شود
    disconnectedCallback() {
        // لغو ثبت ویرایشگر و حذف همه رویدادها
        JoomlaEditor.unregister(this.input.id);
        this.removeEventListener('click', this.interactionCallback);
    }
}

customElements.define('joomla-editor-example', JoomlaEditorExample);

و تمام شد 🎉 حالا ما یک ویرایشگر جدید و کاملاً یکپارچه داریم.