قالب‌ها (Templates)

چگونه یک قالب بسازیم 

پشتیبانی از طرح رنگ (Color Scheme) 

یا همان «حالت تاریک» (dark mode)

قالب جوملا APIای را برای پشتیبانی از طرح رنگ روشن و تاریک ارائه می‌دهد، که می‌تواند از طریق سوئیچ سفارشی یا به صورت خودکار با استفاده از CSS media queries تغییر کند.

وقتی رنگ قالب تغییر می‌کند (چه به صورت خودکار و چه با سوئیچ سفارشی)، قالب باید مقدار مناسب را به ویژگی `data-color-scheme` عنصر `<html>` اختصاص دهد تا به افزونه‌ها (مثلاً پلاگین‌های ویرایشگر) کمک کند تم صحیح (تاریک یا روشن) را بارگذاری کنند. علاوه بر این، رویداد `joomla:color-scheme-change` باید ارسال (trigger) شود.

ویژگی‌های داده‌ای (Document attributes)

ویژگی‌های `data-` زیر باید در عنصر `<html>` قالب‌هایی که قابلیت تغییر طرح رنگ دارند استفاده شود:

- data-color-scheme-os

  وقتی قالب طبق تنظیمات سیستم عامل عمل می‌کند و طرح رنگ را به صورت خودکار با CSS media query تغییر می‌دهد، این ویژگی باید ست شود.

- data-color-scheme="light" 

  وقتی قالب از طرح رنگ روشن استفاده می‌کند، چه توسط تغییر خودکار یا سوئیچ سفارشی.

- data-color-scheme="dark" 

  وقتی قالب از طرح رنگ تاریک استفاده می‌کند، چه توسط تغییر خودکار یا سوئیچ سفارشی.

رویداد جاوااسکریپت (JavaScript event)

زمانی که طرح رنگ تغییر می‌کند، قالب باید یک رویداد سفارشی `joomla:color-scheme-change` به منظور اطلاع‌رسانی به افزونه‌ها ارسال کند. نمونه کد:

 
document.documentElement.dataset.colorScheme = 'dark';
document.dispatchEvent(new CustomEvent('joomla:color-scheme-change', { bubbles: true }));

 

صفحات خطای سفارشی (Custom Error Pages)

از نسخه Joomla 5.0 قالب پیش‌فرض Cassiopeia قابلیت سفارشی‌سازی صفحات خطای 404 و 403 را دارد.

دو موقعیت جدید ماژول به نام‌های error-403 و error-404 اضافه شده‌اند که محتوا را به فایل `error.php` قالب هنگام مواجهه با خطاهای 403 (ممنوع) و 404 (یافت نشد) اضافه می‌کنند تا پیام پیش‌فرض جایگزین شود.

اگر ماژولی برای این موقعیت‌ها وجود نداشته باشد، متن پیش‌فرض خطا نمایش داده می‌شود و اگر حالت اشکال‌زدایی (debug) فعال باشد، اطلاعات اشکال‌زدایی زیر ماژول نمایش داده می‌شود.

صفحه خطای 404 پیش‌فرض

اسکرین‌شات صفحه خطای 404 پیش‌فرض

اسکرین‌شات صفحه خطای 404 پیش‌فرض

صفحات خطای 404 سفارشی نمونه

صفحه خطای 404 سفارشی با تصاویر و دکمه‌ها

صفحه خطای 404 سفارشی با تصاویر و دکمه‌ها

صفحه خطای 404 سفارشی خنده‌دار و جالب

صفحه خطای 404 سفارشی خنده‌دار و جالب

افزودن صفحه خطای سفارشی به قالب خود

این قابلیت را می‌توانید در فایل `error.php` قالب خود اضافه کنید. کافی است کد زیر را برای دریافت کد خطا و نمایش هر ماژولی که در موقعیت ماژول مربوطه قرار دارد، اضافه کنید:

 
<?php $errorCode = $this->error->getCode(); ?>

<?php if ($this->countModules('error-' . $errorCode)) : ?>
    <div class="container">
        <jdoc:include type="modules" name="error-<?php echo $errorCode; ?>" style="none" />
    </div>
<?php else : ?>

<!-- این حداقل کد مورد نیاز است اما احتمالاً می‌خواهید آن را بیشتر سفارشی کنید. -->
 

پشتیبانی از زبان‌های راست به چپ (RTL)

زبان‌هایی مانند فارسی، عربی و برخی زبان‌های دیگر، زبان‌های راست به چپ (RTL) هستند، یعنی رابط کاربری معمولاً به صورت آینه‌ای نمایش داده می‌شود.

مدیریت جوملا حالت چپ به راست (LTR)

اسکرین‌شات مدیریت جوملا به زبان عربی که نوار کناری در سمت راست صفحه دیده می‌شود

اسکرین‌شات مدیریت جوملا به زبان انگلیسی که نوار کناری در سمت چپ صفحه دیده می‌شود.

مدیریت جوملا حالت راست به چپ (RTL)

اسکرین‌شات مدیریت جوملا به زبان عربی که نوار کناری در سمت راست صفحه دیده می‌شود

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

پشتیبانی قالب

هر دو قالب پیش‌فرض سایت، Cassiopeia و قالب پیش‌فرض مدیریت، Atum، به صورت کامل از زبان‌های راست به چپ پشتیبانی می‌کنند. زمانی که جوملا به زبان RTL تنظیم شود، نسخه متفاوتی از فایل CSS قالب استفاده می‌شود که به نام `template-rtl.min.css` شناخته می‌شود.

چه کاری باید انجام دهید؟

احتمالاً برای پشتیبانی از RTL در کامپوننت خود نیازی به کاری ندارید، مگر اینکه بخواهید فایل CSS اختصاصی خود را اضافه کنید. اگر تا حد امکان از خصوصیات CSS منطقی (Logical CSS Properties) استفاده کنید، ممکن است نیازی به استایل CSS خاص برای RTL نداشته باشید.

خصوصیات منطقی CSS (Logical CSS Properties)

به جای استفاده از ویژگی‌هایی مثل `margin-left` و سپس مجبور شدن برای RTL یک فایل CSS مجزا با `margin-right` بسازید، می‌توانید از ویژگی‌های منطقی استفاده کنید، مانند `margin-inline-start`.

اطلاعات بیشتر، شامل یک راهنمای تصویری مفید، در این مقاله عالی از Adrian Roselli درباره CSS Logical Properties قابل دسترس است. (https://adrianroselli.com/2019/11/css-logical-properties.html)

خصوصیات مطلق CSS (Absolute CSS Properties)

گاهی اوقات امکان استفاده از خصوصیات منطقی برای همه موارد وجود ندارد و یا ممکن است بخواهید از کوتاه‌نویسی CSS مانند `margin: 8px 10px 16px 4px` استفاده کنید به جای اینکه خصوصیات منطقی کامل بنویسید.

در این موارد می‌توانید از ابزار مشابه جوملا استفاده کنید تا CSS شما را به صورت خودکار تبدیل کند. RTLCSS یک پلاگین postcss است که تمام کارهای سخت را انجام می‌دهد و اطمینان می‌دهد که یک فایل CSS مناسب RTL از روی فایل اصلی LTR شما ساخته شود.

اطلاعات بیشتر

- RTLCSS — چارچوبی برای تبدیل CSS از چپ به راست (LTR) به راست به چپ (RTL)  (https://rtlcss.com)

- RTL Styling 101 — یک راهنمای جامع برای استایل‌دهی به CSS به صورت RTL (https://rtlstyling.com)

templateDetails.xml

فایل templateDetails.xml، یک فایل اصلی‌ است که دستورالعمل‌های نصب، موقعیت‌های ماژول و فرم تنظیمات سبک (style form) قالب را تعریف می‌کند. بخش‌های آن به صورت زیر تعریف می‌شوند:

 
<?xml version="1.0" encoding="UTF-8"?>
<extension type="template" client="site">
<name><!-- نام قالب اینجا قرار می‌گیرد --></name>
<version><!-- نسخه قالب اینجا قرار می‌گیرد --></version>
<creationDate><!-- تاریخ ساخت قالب اینجا قرار می‌گیرد --></creationDate>
<author><!-- نام نویسنده قالب اینجا قرار می‌گیرد --></author>
<authorEmail><!-- ایمیل نویسنده قالب اینجا قرار می‌گیرد --></authorEmail>
<copyright><!-- کپی‌رایت قالب اینجا قرار می‌گیرد --></copyright>
<description><!-- توضیح قالب اینجا قرار می‌گیرد --></description>
  <namespace><!-- نام فضای نام قالب، معمولاً CompanyNamespace\Templates\TemplateName اینجا قرار می‌گیرد --></namespace>
<inheritable>1</inheritable>
<files><!-- فایل‌ها/پوشه‌های موجود در فولدر قالب اینجا وارد می‌شوند --></files>
<media destination="templates/site/cassiopeia" folder="media"><!-- پوشه‌های دارایی‌های استاتیک (static assets) اینجا وارد می‌شوند --></media>
<positions><!-- موقعیت‌های ماژول اینجا تعریف می‌شوند --></positions>
<languages folder="language"><!-- زبان‌ها اینجا تعریف می‌شوند --></languages>
<config>
<fields name="params">
<fieldset name="advanced"><!-- تعریف فیلد‌ها اینجا قرار می‌گیرد --></fieldset>
</fields>
</config>
</extension>

از نسخه ۵.۱ به بعد، در بخش config می‌توان تب تخصیص منو (Menu Assignment) را از طریق قسمت config فایل templateDetails.xml بازنویسی (override) کرد. به همین منظور، نمونه‌ای از ورودی که محتوای تب Menu Assignment را با یک فیلد به نام menus بازنویسی می‌کند، به صورت زیر است:

 
<fields name="assigned">
  <fieldset name="assigned" addfieldprefix="YourNamespace\Templates\Field">
    <field name="assigned" type="menus" />
  </fieldset>
</fields>
 

نمونه کدی برای فیلد menus به صورت زیر خواهد بود:

 
<?php
namespace YourNamespace\Templates\Field;

use Joomla\CMS\Factory;
use Joomla\CMS\Form\Field\TextField;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Layout\LayoutHelper;
use Joomla\Component\Menus\Administrator\Helper\MenusHelper;

// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects


class MenusField extends TextField
{
    protected $type = 'Menus';

    // تنظیم و راه‌اندازی فیلد فرم
    public function setup(\SimpleXMLElement $element, $value, $group = null)
    {
        // اطمینان از اینکه عنصر XML یک عنصر معتبر فیلد فرم است
        if ((string) $element->getName() !== 'field'){
            return false;
        }

        // بازنشانی مقدار ورودی و برچسب
        $this->input = null;
        $this->label = null;

        // تنظیم شیء عنصر XML
        $this->element = $element;

        // تنظیم گروه فیلد
        $this->group = $group;

        // لیست ویژگی‌های مجاز برای فیلد
        $attributes = [
            'multiple', 'name', 'id', 'hint', 'class', 'description', 'labelclass', 'onchange', 'onclick',
            'validate', 'pattern', 'validationtext', 'default', 'required', 'disabled', 'readonly', 'autofocus',
            'hidden', 'autocomplete', 'spellcheck', 'translateHint', 'translateLabel', 'translate_label',
            'translateDescription', 'translate_description', 'size', 'showon'
        ];

        // تنظیم مقدار پیش‌فرض فیلد
        $this->default = isset($element['value']) ? (string) $element['value'] : $this->default;

        // تنظیم مقدار فعلی فیلد
        $this->value = $value;

        // شناسایی داده‌های مختلفی که به صورت data-* هستند
        foreach ($this->element->attributes() as $key => $value) {
            if (strpos($key, 'data-') === 0) {
                // جفت کلید و مقدار داده‌ها
                $this->dataAttributes[$key] = $value;
            }
        }

        // تنظیم ویژگی‌های مشخص شده بر اساس عناصر XML
        foreach ($attributes as $attributeName) {
            $this->__set($attributeName, $element[$attributeName]);
        }

        // تنظیم وضعیت قابل مشاهده بودن (مخفی بودن)
        $this->hidden = ($this->hidden || strtolower((string) $this->element['type']) === 'hidden');

        // تنظیم کلاس والد، اگر تعیین شده باشد
        $this->parentclass = isset($this->element['parentclass']) ? (string) $this->element['parentclass'] : $this->parentclass;

        // اگر فیلد ضروری باشد، کلاس 'required' به آن اضافه می‌شود
        if ($this->required) {
            $this->class = trim($this->class . ' required');
        }

        // پنهان کردن برچسب
        $this->hiddenLabel = true;
        $this->hidden = true;

        return true;
    }

    // تولید ورودی HTML فیلد
    public function getInput() {
        // مقداردهی اولیه داده‌های مرتبط
        $menuTypes = MenusHelper::getMenuLinks();  // دریافت لینک‌های منو
        $user      = $this->getCurrentUser();
        $app       = Factory::getApplication();
        $currentId = $app->input->getInt('id');

        /** @var Joomla\CMS\WebAsset\WebAssetManager $wa */
        $wa = Factory::getApplication()->getDocument()->getWebAssetManager();
        $wa->useScript('com_templates.admin-template-toggle-assignment');

        // ساختار HTML برای نمایش انتخاب منو
        $html = '<label id="jform_menuselect-lbl" for="jform_menuselect">'. Text::_('JGLOBAL_MENU_SELECTION') . '</label>'
        .  '<div class="btn-toolbar">'
        .    '<button class="btn btn-sm btn-secondary jform-rightbtn" type="button" onclick="Joomla.toggleAll()">'
        .    '<span class="icon-square" aria-hidden="true"></span> ' . Text::_('JGLOBAL_SELECTION_INVERT_ALL') . '</button>'
        .  '</div>'
        .  '<div id="menu-assignment" class="menu-assignment">'
        .    '<ul class="menu-links">';

        // برای هر نوع منو لیستی از گزینه‌ها ساخته می‌شود
        foreach ($menuTypes as &$type) {
            $html .= '<li>'
            . '<div class="menu-links-block">'
            .   '<button class="btn btn-sm btn-secondary jform-rightbtn mb-2" type="button" onclick=\'Joomla.toggleMenutype("' . $type->menutype . '")\'>'
            .   '<span class="icon-square" aria-hidden="true"></span> ' . Text::_('JGLOBAL_SELECTION_INVERT') . '</button>'
            .   '<h5>' . $type->title ?: $type->menutype . '</h5>';

            // لیست لینک‌های منو به صورت چک‌باکس
            foreach ($type->links as $link) {
                $html .= '<label class="checkbox small" for="link' . (int) $link->value . '" >'
                . '<input type="checkbox" name="jform[assigned][]" value="'
                . (int) $link->value . '" id="link' . (int) $link->value . '"'
                . (($link->template_style_id == $currentId) ? ' checked="checked"' : '')
                . (($link->checked_out && $link->checked_out != $user->id) ? ' disabled="disabled"' : ' class="form-check-input chk-menulink menutype-' . $type->menutype . '"')
                . '/>';

                // افزودن پیشوند درختی برای نمایش سطح لینک در منو
                $html .= LayoutHelper::render('joomla.html.treeprefix', ['level' => $link->level]) . $link->text;
                $html .= '</label>';
            }

            $html .= '</div></li>';
        }

        $html .= '</ul></div>';

        return $html;
    }
}