تطوير الإضافات
تضيف إضافات كليجا مزايا جديدة إلى السكربت دون المساس بملفات النواة. تعيش كل إضافة في مجلد خاص بها داخل plugins/، وتُعرّف نفسها في ملف init.php واحد، ثم تربط دوالها بـ خطّافات تستدعيها النواة أثناء تنفيذ الصفحة.
ولأن الإضافات لا تعدّل النواة، فإن ترقية كليجا لا تمحو تعديلاتك. يحمّل النظام الإضافات المثبّتة والمفعّلة فقط، لذا يمكن تعطيل أي إضافة معطوبة من لوحة التحكم بدل حذفها يدوياً.
includes/plugins.php، ومدير الإضافات في includes/adm/j_plugins.php. يتطلب كليجا 4 الإصدار PHP 8.2، وقد تغيّرت فيه طبقة قاعدة البيانات، لذا تحتاج الإضافة المكتوبة لكليجا 3 إلى بعض التعديلات. راجع ترقية إضافة من كليجا 3.كيف يعمل النظام
يبدأ الكائن المفرد Plugins مع أول خطّاف تستدعيه النواة، في وقت مبكر من includes/common.php أثناء تحميل المخزون المؤقت. يقرأ الباني جدول plugins ويحتفظ بالصفوف التي تكون فيها plg_disabled = 0، ثم يضمّن ملف init.php لكل مجلد مطابق داخل plugins/.
يملأ كل ملف init.php مصفوفة $kleeja_plugin ببيانات الإضافة ودوال دورة حياتها ودوال خطّافاتها. يسجّل المحمّل بعدها دوال الخطّافات في خريطة عامة مرتّبة حسب أولوية الإضافة.
تستدعي ملفات النواة بعد ذلك runHook('hook_name', get_defined_vars()) عند نقاط ثابتة. تستقبل كل دالة مسجّلة على ذلك الخطّاف متغيرات المستدعي، ويُدمج ما تُرجعه في نطاق المستدعي.
بنية المجلد
الإضافة مجلد يطابق اسمه المفتاح المستخدم داخل init.php. يقرأ كليجا اسم المجلد من القرص ثم يبحث عنه في المصفوفة، لذا يجب أن يتطابق الاثنان.
plugins/
└── my_plugin/
├── init.php # مطلوب — تعريف الإضافة بالكامل
├── icon.png # اختياري — تظهر في قائمة الإضافات وفي صفحة المساعدة
├── index.html # اختياري — يمنع عرض محتويات المجلد
├── language/ # اختياري — نصوص المساعدة، ملف لكل لغة
└── templates/ # اختياري — قوالب .html الخاصة بك
plugins/my_plugin/init.php المصفوفة $kleeja_plugin['my_plugin']، وإلا تجاهل المحمّل الإضافة. استخدم الحروف الصغيرة والأرقام والنقاط والشرطات السفلية، وبطول ثلاثة محارف على الأقل.ملف init.php
يبدأ كل ملف init.php بحارس. يُعرَّف الثابت IN_PLUGINS_SYSTEM في includes/plugins.php، لذا يتوقف الملف عن العمل إذا طلبه أحدهم مباشرة من المتصفح.
<?php
// منع التشغيل المباشر
if (! defined('IN_PLUGINS_SYSTEM')) {
exit();
}
$kleeja_plugin['my_plugin']['information'] = [ /* ... */ ];
$kleeja_plugin['my_plugin']['first_run']['ar'] = 'شكراً لتثبيت الإضافة!';
$kleeja_plugin['my_plugin']['install'] = function ($plg_id) { /* ... */ };
$kleeja_plugin['my_plugin']['update'] = function ($old_version, $new_version) { /* ... */ };
$kleeja_plugin['my_plugin']['uninstall'] = function ($plg_id) { /* ... */ };
$kleeja_plugin['my_plugin']['functions'] = [ /* اسم الخطّاف => دالة */ ];
information وinstall وupdate وuninstall وfunctions حتى لو كانت إحدى الدوال فارغة، فالمفتاح first_run وحده اختياري. يقرأ المحمّل ومدير الإضافات المفاتيح الأخرى مباشرة، والمفتاح الناقص يسبّب تحذيراً من PHP. أما الإضافة التي لا تعرّف دالة uninstall فلا يمكن إزالتها من لوحة التحكم إطلاقاً، لأن المدير يعيدك إلى قائمة الإضافات.معلومات الإضافة
تصف مصفوفة information الإضافة للوحة التحكم، وتتحكم بفحص التوافق وترتيب التحميل.
| المفتاح | النوع | الغرض |
|---|---|---|
plugin_title | array | الاسم المعروض لكل لغة، مثل ['en' => 'My Plugin', 'ar' => 'إضافتي']. يرجع إلى en ثم إلى أول عنصر. |
plugin_developer | string | اسم المطوّر، ويُخزَّن في plg_author. |
plugin_version | string | إصدار الإضافة. تعتمد عليه دالة التحديث وفحص التحديثات في المتجر، فارفعه مع كل إصدار. |
plugin_description | array | وصف مختصر لكل لغة. يظهر في قائمة الإضافات مقتطعاً عند 100 محرف. |
plugin_kleeja_version_min | string | أدنى إصدار مدعوم من كليجا. يفشل التثبيت دونه. اتركه فارغاً أو 0 لإلغاء الحد. |
plugin_kleeja_version_max | string | أعلى إصدار مدعوم من كليجا. يفشل التثبيت فوقه. اتركه فارغاً أو 0 لإلغاء الحد. |
plugin_priority | int | ترتيب التحميل. الرقم الأعلى يعمل أولاً، و0 هو الوضع الطبيعي. |
settings_page | string | اختياري. سلسلة الاستعلام لصفحة إعدادات الإضافة، تُضاف بعد admin/?. تُظهر زر إعدادات بجانب الإضافة ورابطاً في صفحة المساعدة. |
$kleeja_plugin['my_plugin']['information'] = [
'plugin_title' => [
'en' => 'My Plugin',
'ar' => 'إضافتي',
],
'plugin_developer' => 'اسمك',
'plugin_version' => '1.0',
'plugin_description' => [
'en' => 'Does something useful',
'ar' => 'تقوم بشيء مفيد',
],
'plugin_kleeja_version_min' => '4.0.0',
'plugin_kleeja_version_max' => '4.9',
'plugin_priority' => 0,
'settings_page' => 'cp=options&smt=my_plugin',
];
version_compare() مقابل KLEEJA_VERSION. اجعل plugin_kleeja_version_max واسعاً، فالحد الأعلى الضيّق يمنع التثبيت على إصدارات كليجا الأحدث حتى لو كانت الإضافة تعمل عليها. لا يمكن تثبيت إضافة لكليجا 3 حدّها الأعلى 3.9 على كليجا 4.0.0، والحد الأعلى 4.9 يتوقف عند 4.9.1.اكتب settings_page بالمحرف & العادي كما في المثال أعلاه. تُرمّز صفحة المساعدة الرابط بنفسها، لذا يتحول & فيها إلى رابط معطوب. ويُتجاهَل الرابط الكامل الذي يبدأ بـ http:// أو https://.
رسالة التشغيل الأول
يحمل المفتاح first_run رسالة HTML تظهر فور نجاح التثبيت. يختار كليجا النص المطابق للغة الموقع ويرجع إلى en عند عدم وجوده.
$kleeja_plugin['my_plugin']['first_run']['ar'] = '
شكراً لتثبيت هذه الإضافة. للإبلاغ عن الأخطاء: <br>
info@example.com
';
عند غياب first_run تعيدك لوحة التحكم إلى قائمة الإضافات بعد ثانيتين بدل انتظار نقرة منك.
دالة التثبيت
تعمل دالة install مرة واحدة، بعد إدراج صف الإضافة في جدول plugins. تستقبل الدالة رقم plg_id الجديد، ومرّره إلى add_config_r() وadd_olang() كي يربط كليجا إعداداتك وعباراتك اللغوية بالإضافة.
$kleeja_plugin['my_plugin']['install'] = function ($plg_id) {
add_config_r([
'my_plugin_enabled' => [
'value' => '1',
'html' => configField('my_plugin_enabled', 'yesno'),
'plg_id' => $plg_id,
'type' => 'my_plugin',
'order' => '1',
],
'my_plugin_api_key' => [
'value' => '',
'html' => configField('my_plugin_api_key'),
'plg_id' => $plg_id,
'type' => 'my_plugin',
'order' => '2',
],
]);
add_olang([
'CONFIG_KLJ_MENUS_MY_PLUGIN' => 'إعدادات إضافتي',
'MY_PLUGIN_ENABLED' => 'تفعيل الإضافة',
'MY_PLUGIN_API_KEY' => 'مفتاح الواجهة البرمجية',
], 'ar', $plg_id);
};
$SQL->query() في الاستعلامات المهمة، واختبر دالة التثبيت على MySQL وSQLite معاً قبل النشر.دالة التحديث
يقارن المحمّل الإصدار المثبّت المخزَّن في plg_ver بقيمة plugin_version في init.php مع كل طلب. فإذا أعلن الملف إصداراً أحدث، استدعى كليجا دالة update ثم كتب الإصدار الجديد في قاعدة البيانات.
تعمل هذه الدالة أثناء تحميل الإضافات، قبل أن يحمّل كليجا المستخدم الحالي والمجموعات. لذا استخدم فيها $SQL->build() مباشرة:
- تقرأ الدوال
add_config()وupdate_config()وdelete_config()وget_config()مجموعات المستخدم الحالي، فتوقف الطلب بخطأ قاتل. - تشغّل
delete_cache()وkleeja_unlink()وغيرها من الدوال المساعدة التي تستدعي خطّافاً محمّلَ إضافات ثانياً. احذف ملفات المخزون المؤقت بالدالةunlink()بدلاً منها.
$kleeja_plugin['my_plugin']['update'] = function ($old_version, $new_version) {
global $SQL, $dbprefix;
$result = $SQL->build([
'SELECT' => 'plg_id',
'FROM' => "{$dbprefix}plugins",
'WHERE' => 'plg_name = :name',
'BIND' => ['name' => 'my_plugin'],
]);
$plg_id = (int) ($SQL->fetch($result)['plg_id'] ?? 0);
$SQL->freeresult($result);
$result = $SQL->build([
'SELECT' => 'name',
'FROM' => "{$dbprefix}config",
'WHERE' => 'name = :name',
'BIND' => ['name' => 'my_plugin_timeout'],
]);
$has_timeout = $SQL->fetch($result) !== false;
$SQL->freeresult($result);
if (version_compare($old_version, '1.1', '<') && ! $has_timeout) {
$SQL->build([
'INSERT' => '`name`, `value`, `option`, `display_order`, `type`, `plg_id`',
'INTO' => "{$dbprefix}config",
'VALUES' => ':name, :value, :option, :order, :type, :plg_id',
'BIND' => [
'name' => 'my_plugin_timeout',
'value' => '30',
'option' => configField('my_plugin_timeout'),
'order' => 3,
'type' => 'my_plugin',
'plg_id' => $plg_id,
],
]);
}
if (version_compare($old_version, '2.0', '<')) {
$SQL->build([
'UPDATE' => "{$dbprefix}config",
'SET' => 'value = :value',
'WHERE' => 'name = :name',
'BIND' => ['value' => '', 'name' => 'my_plugin_api_key'],
]);
}
// الإعدادات مخزّنة مؤقتاً، ولا يمكن تشغيل delete_cache() هنا
$cache_file = PATH . 'cache/data_config.php';
if (file_exists($cache_file)) {
if (function_exists('opcache_invalidate')) {
@opcache_invalidate($cache_file, true);
}
@unlink($cache_file);
}
};
احرس كل ترحيل بفحص version_compare() على $old_version. فقد يقفز المستخدمون عدة إصدارات دفعة واحدة، ويجب أن تتعامل الدالة مع كل مسار من أي إصدار قديم إلى الإصدار الحالي.
اجعل كل خطوة آمنة إذا نُفّذت مرتين. لا تُخفى أخطاء SQL أثناء التحديث، فيعرض الاستعلام الفاشل صفحة خطأ، ولا يحفظ كليجا الإصدار الجديد إلا بعد اكتمال الدالة بأكملها. لذا فإن الخطوة التي تفشل، كإدراج إعداد موجود أصلاً، توقف كل صفحات الموقع حتى تُصلَح الإضافة.
$kleeja_plugin متغير محلي داخل المحمّل وليس متغيراً عاماً. لذا تحصل الدالة التي تحتاج مصفوفة information الخاصة بالإضافة عليها عبر function (...) use ($kleeja_plugin). ولا يمسح التحديث القوالب المترجَمة أيضاً، لذا تحذف الإضافة التي تغيّر الشيفرة عبر style_parse_func ملفات cache/tpl_*.php داخل دالة التحديث.دالة الإزالة
تعمل دالة uninstall قبل حذف صف الإضافة. احذف فيها كل ما أنشأته دالة التثبيت: الإعدادات والعبارات اللغوية وأي جداول خاصة.
$kleeja_plugin['my_plugin']['uninstall'] = function ($plg_id) {
delete_config([
'my_plugin_enabled',
'my_plugin_api_key',
]);
// القيمة 0 لـ plg_id تحذف عبارات كل الإضافات
if ($plg_id > 0) {
delete_olang(null, null, $plg_id);
}
};
null كمعامل أول لـ delete_olang() يحذف كل عبارات اللغة المحددة، واللغة null تعني كل اللغات. ومع تمرير $plg_id تُحذف عبارات الإضافة وحدها. يمرّر مدير الإضافات القيمة 0 حين لا يكون للإضافة صف، والاستدعاء delete_olang(null, 'en', 0) يحذف العبارات الإنجليزية لكل الإضافات المثبّتة، لذا تحقق دائماً من $plg_id > 0 أولاً.الخطّافات
الخطّافات هي المدخل الوحيد لنظام الإضافات إلى منطق النواة. يوفّر كليجا أكثر من 200 خطّاف موزّعة على index.php وgo.php وdo.php وucp.php وadmin/index.php وملفات includes/.
كيف تستدعي النواة الخطّاف
يبدو كل موضع استدعاء في النواة هكذا. يستقبل الخطّاف كل متغير في النطاق عند تلك النقطة، وتكتب extract() القيم المُرجَعة فوقها.
extract(runHook('begin_download_page', get_defined_vars()));
الدالة runHook() اختصار لـ Plugins::getInstance()->run(). استخدمت نواة كليجا 3 أيضاً صيغة أطول هي is_array($plugin_run_result = ...)، ولم تعد نواة كليجا 4 تستخدمها.
كتابة دالة خطّاف
سجّل دوال الخطّافات في مصفوفة functions مفهرسةً باسم الخطّاف. تأخذ كل دالة مصفوفة $args واحدة، وتُرجع مصفوفة من المتغيرات المطلوب كتابتها، أو لا تُرجع شيئاً إن كانت للمراقبة فقط.
$kleeja_plugin['my_plugin']['functions'] = [
// تغيير متغير في نطاق المستدعي
'Saaheader_links_func' => function ($args) {
$extra = $args['extra'] . '<meta name="generator" content="Kleeja">';
return compact('extra');
},
// مراقبة فقط — بلا قيمة مُرجَعة
'ok_added_users_register' => function ($args) {
my_plugin_log('new user: ' . kleeja_html_decode($args['name']));
},
];
$args، واستدعِ المتغيرات العامة صراحةً عبر global $config; عند الحاجة إليها.القيمة المُرجَعة مهمة:
| القيمة المُرجَعة | الأثر |
|---|---|
compact('var') أو ['var' => $value] | تستبدل $var في نطاق المستدعي. |
[] أو null أو بلا return | تترك المستدعي دون تغيير. |
| مفتاح جديد غير موجود لدى المستدعي | ينشئ ذلك المتغير في نطاق المستدعي. |
الأولوية والتسلسل
يتحكم plugin_priority بالترتيب. يرتّب المحمّل الخطّافات من الأولوية الأعلى إلى الأدنى، فتعمل الإضافة ذات الأولوية 10 قبل الإضافة ذات الأولوية 0.
تتسلسل الخطّافات: تدمج run() القيمة المُرجَعة من كل دالة في $args قبل استدعاء الدالة التالية. لذا ترى الإضافة المتأخرة القيم التي أنتجتها الإضافة السابقة، لا القيم الأصلية.
لا تستدعي النواة extract() إلا بعد تنفيذ آخر دالة في الخطّاف. لذا ترى الإضافة المتأخرة التي تقرأ قيمة عبر global $x القيمة القديمة، فالدالة التي تغيّر متغيراً عاماً من أجل الإضافات الأخرى تضبط المتغير العام وتُرجعه أيضاً.
أسماء الخطّافات الديناميكية
تُبنى بعض أسماء الخطّافات من متغير الطلب، ما يتيح للإضافة أن تستولي على اسم صفحة غير موجود في النواة. تبني لوحة التحكم ثلاثة منها من قيمة cp المطلوبة:
extract(runHook("require_admin_page_begin_{$go_to}", get_defined_vars()));
extract(runHook("require_admin_page_end_{$go_to}", get_defined_vars()));
extract(runHook("not_exists_{$go_to}", get_defined_vars()));
مرجع الخطّافات
مجموعة مختارة من أكثر الخطّافات فائدة. للحصول على القائمة الكاملة ابحث في الشيفرة بالأمر grep -rn "runHook(" --include="*.php" .، وطابق معها كل خطّاف في الإضافة القديمة: فالخطّاف الذي لم تعد النواة تستدعيه يفشل بصمت.
| الخطّاف | الموضع | متى يعمل |
|---|---|---|
boot_common | includes/common.php | بعد جهوزية الإعدادات وقاعدة البيانات ومحرك القوالب والمستخدم الحالي. لم تُحمَّل اللغة بعد. |
end_common | includes/common.php | بعد انتهاء الإقلاع، وقبل تنفيذ سكربت الصفحة. |
Saaheader_links_func | includes/functions_display.php | عند بناء قوائم الترويسة. أضف وسوم الميتا عبر $extra، وعناصر القائمة عبر $top_menu أو $side_menu. |
Saaheader_func | includes/functions_display.php | بعد توليد HTML الترويسة وقبل طباعته. |
Saafooter_func | includes/functions_display.php | بعد إسناد متغيرات التذييل وقبل توليده. |
print_Saafooter_func | includes/functions_display.php | بعد توليد HTML التذييل وقبل طباعته. |
begin_index_page | index.php | عند بدء صفحة الرفع. استبدل أداة الرفع بتغيير $uploadingMethodClass. |
end_index_page | index.php | عند انتهاء صفحة الرفع. |
begin_go_page | go.php | عند بدء أي طلب على go.php. |
default_go_page | go.php | حين لا تطابق قيمة go أي صفحة في النواة. |
begin_download_page | do.php | عند بدء طلب تحميل. |
down_go_page | do.php | قبل عرض صفحة التحميل مباشرة. |
begin_usrcp_page | ucp.php | عند بدء أي طلب على لوحة المستخدم. |
login_after_submit | ucp.php | بعد إرسال نموذج تسجيل الدخول. |
ok_added_users_register | ucp.php | بعد إدراج صف مستخدم جديد. |
begin_admin_page | admin/index.php | عند بدء لوحة التحكم، بعد اكتشاف الامتدادات. أضف الصفحات إلى $adm_extensions والأيقونات إلى $ext_icons. |
end_admin_page | admin/index.php | حين يصبح كل شيء جاهزاً للعرض. اضبط $extra_admin_header_code أو $extra_admin_footer_code. |
admin_help_guides | includes/adm/s_help.php | عند بناء صفحة المساعدة لأدلّتها. أضف دليلك إلى $help_guides. |
kleeja_begin_send_mail_func | includes/functions.php | عند جهوزية رسالة بريد. أرسلها بنفسك واضبط $sending_mail_handled و$mail_sent. |
kleeja_send_mail | includes/functions.php | قبل أن يرسل كليجا رسالة بريد عبر mail(). |
kleeja_mail_template_func | includes/functions.php | عند توليد HTML رسالة البريد. غيّر $template_name و$template_path لاستخدام قالبك الخاص. |
kleeja_fetch_file_start | includes/FetchFile.php | عند بدء جلب ملف بعيد. |
style_parse_func | includes/style.php | عند ترجمة قالب. عدّل $html لحقن شيفرة. |
style_parse_func_step_2 | includes/style.php | بعد ترجمة القالب إلى PHP. أضف أزواج regex => replacement إلى $rep لوسومك الخاصة. |
delete_cache_func | includes/functions.php | عند مسح عنصر من المخزون المؤقت. |
قاعدة البيانات
يتصل كليجا 4 بقواعد MySQL أو MariaDB أو SQLite عبر PDO. المتغير العام $SQL كائن من الصنف KleejaDatabase المعرّف في includes/pdo.php. اكتب الاستعلام على شكل مصفوفة ومرّره إلى $SQL->build()، وضع كل قيمة في BIND مع عنصر نائب :name مكانها في الاستعلام.
global $SQL, $dbprefix;
$result = $SQL->build([
'SELECT' => 'f.id, f.name, f.size',
'FROM' => "{$dbprefix}files f",
'WHERE' => 'f.user = :user AND f.type IN (:types)',
'ORDER BY' => 'f.id DESC',
'LIMIT' => '10',
'BIND' => ['user' => $user_id, 'types' => ['jpg', 'png']],
]);
while ($row = $SQL->fetch($result)) {
// ...
}
$SQL->freeresult($result);
المفاتيح هي كلمات SQL نفسها: SELECT وFROM وJOINS وWHERE وGROUP BY وHAVING وORDER BY وLIMIT، وINSERT مع INTO وVALUES، وUPDATE مع SET، وDELETE. تتوسّع القيمة المصفوفة في BIND إلى قائمة لـ IN، والمصفوفة الفارغة لا تطابق أي صف. ويمكن أن يتكرر العنصر النائب أكثر من مرة، وتُسقَط القيمة التي لا يوجد عنصرها النائب في الاستعلام.
| الدالة | الغرض |
|---|---|
build(array $query) | تنفيذ مصفوفة استعلام. تُرجع PDOStatement، أو false عند الخطأ. |
query(string $sql, array $params = []) | تنفيذ SQL خام بالعناصر النائبة نفسها، مثل CREATE TABLE. |
fetch($result) | الصف التالي كمصفوفة ترابطية، أو false. |
freeresult($result) | تحرير النتيجة. استدعِها قبل أن يغيّر استعلام آخر الجدول نفسه على SQLite. |
num_rows($result) | عدد صفوف استعلام SELECT. |
insert_id() | معرّف آخر صف مُدرَج. |
affected() | عدد الصفوف التي غيّرها آخر استعلام. |
get_error() | رمز آخر خطأ ورسالته. |
$SQL->escape() و$SQL->real_escape() موجودتين من أجل الإضافات القديمة، لكنهما مهملتان. اربط كل قيمة بدل بناء نصوص SQL، ورمّز النص لـ HTML عبر kleeja_html_encode().يخزّن كليجا النصوص مُرمَّزة بـ HTML، وتطبعها القوالب كما هي. رمّز القيمة مرة واحدة عند إدخالها إلى قاعدة البيانات عبر kleeja_html_encode()، واطبع النص المخزَّن عبر kleeja_html_display(). تفكّ هذه الدالة كل مستويات الترميز ثم ترمّز مرة واحدة، لذا يظهر النص بشكل صحيح حتى لو رُمِّز مرتين عند حفظه. أما النص الخام، كعنوان رسالة بريد، فاستخدم له kleeja_html_decode().
جداول خاصة
اكتب استعلامات CREATE TABLE بصيغة MySQL، وأضف البادئة {$dbprefix} إلى اسم الجدول. على SQLite، يحوّل كليجا الاستعلام عبر التعابير النمطية الموجودة في KleejaDatabase::SQLITE_TYPES.
int في الاستعلام بـ INTEGER، حتى داخل الأسماء، فيصبح العمود المسمّى point بالاسم poINTEGER. ويعطب أيضاً أسطر UNIQUE KEY name (column). أبعد int عن أسماء جداولك وأعمدتك، وضع UNIQUE على العمود نفسه، وأنشئ الجدول على قاعدتَي البيانات كلتيهما قبل النشر.إضافة إعدادات إلى لوحة التحكم
يخزّن كليجا الإعدادات في جدول config. يظهر الإعداد الذي يملك قيمة option في لوحة التحكم ← الخيارات، مُجمَّعاً في تبويب حسب قيمة type.
تسجيل الإعدادات
استخدم add_config_r() داخل دالة التثبيت. اضبط type على معرّف إضافتك كي تحصل الإعدادات على تبويب خاص بها، ومرّر plg_id كي تختفي من قائمة التبويبات عند تعطيل الإضافة.
add_config_r([
'my_plugin_mode' => [
'value' => 'fast',
'html' => configField('my_plugin_mode', 'select', [
'سريع' => 'fast',
'دقيق' => 'accurate',
]),
'plg_id' => $plg_id,
'type' => 'my_plugin',
'order' => '1',
],
]);
تولّد الدالة configField() من includes/functions_display.php شيفرة الحقل بصيغة قوالب كليجا:
| النوع | ما يولّده |
|---|---|
text | حقل نصي مرتبط بـ {con.name}، وهو النوع الافتراضي. |
yesno | زرّا اختيار يستخدمان العبارتين YES وNO. |
select | قائمة منسدلة تُبنى من مصفوفة $select_options بصيغة ['العنوان' => 'القيمة']. |
يمكنك أيضاً كتابة html بنفسك. تضيف صفحة الإعدادات class="form-control" إلى كل <input> و<select>، لذا لا تضف هذا الصنف بنفسك.
تسمية تبويب الإعدادات
يقرأ تبويب الإعدادات عنوانه من مفتاح اللغة CONFIG_KLJ_MENUS_<TYPE> بحروف كبيرة. أضف تلك العبارة في دالة التثبيت، وأضف عنوان كل إعداد باسمه <NAME> بحروف كبيرة.
add_olang([
'CONFIG_KLJ_MENUS_MY_PLUGIN' => 'إعدادات إضافتي',
'MY_PLUGIN_MODE' => 'وضع المعالجة',
], 'ar', $plg_id);
يظهر التبويب حتى بدون CONFIG_KLJ_MENUS_MY_PLUGIN، لكنه يحمل عنوان خيارات أخرى.
قراءة الإعدادات أثناء التشغيل
تُخزَّن الإعدادات مؤقتاً وتُتاح عبر المصفوفة العامة $config:
$kleeja_plugin['my_plugin']['functions'] = [
'end_index_page' => function ($args) {
global $config;
if ($config['my_plugin_mode'] === 'fast') {
// ...
}
},
];
تُخزَّن الإعدادات النصية مُرمَّزة بـ HTML. اطبعها عبر kleeja_html_display()، وفكّ ترميزها عبر kleeja_html_decode() قبل استخدامها كنص خام.
العبارات اللغوية
تعيش عبارات الإضافات في جدول lang وتُتاح عبر المصفوفة العامة $olang إلى جانب $lang الخاصة بالنواة. تقرؤها القوالب بالصيغة {olang.KEY}.
| الدالة | التوقيع | الغرض |
|---|---|---|
add_olang() | add_olang(array $words, string $lang, int $plg_id) | إدراج عبارات للغة واحدة. |
update_olang() | update_olang(string $name, string $value, string $lang) | تعديل عبارة واحدة. |
delete_olang() | delete_olang($words, $lang, int $plg_id) | حذف العبارات. مرّر null مكان العبارات مع plg_id لحذف كل عبارات الإضافة. |
استدعِ add_olang() مرة لكل لغة تدعمها إضافتك:
add_olang(['MY_PLUGIN_TITLE' => 'My Plugin'], 'en', $plg_id);
add_olang(['MY_PLUGIN_TITLE' => 'إضافتي'], 'ar', $plg_id);
trans لـ 255 محرفاً على MySQL. والعبارة الأطول تُفشل التثبيت برسالة "Data too long" على MySQL وحده، لأن SQLite لا يفحص الطول. اجعل كل عبارة قصيرة، وضع النصوص الطويلة، كأدلة المساعدة، في ملفات اللغة الخاصة بالإضافة.يأتي كليجا بالإنجليزية والعربية، لذا أضف كل عبارة باللغتين، واجعل كل صفحة تضيفها تعمل في الاتجاهين.
القوالب والستايلات
يعرض كليجا الصفحات عبر الكائن $tpl. تترجم الدالة display($template_name, $style_path) القالب إلى مجلد cache/ وتُرجع الـ HTML، حيث $style_path مسار مطلق يتجاوز الستايل النشط.
تستطيع الإضافة أن تشحن قوالبها الخاصة وتوجّه كليجا إليها. ولاستبدال قالب لوحة التحكم، تجاوز متغيرات مسار اللوحة من الخطّاف end_common:
$kleeja_plugin['my_admin_theme']['functions'] = [
'end_common' => function ($args) {
if (! defined('IN_ADMIN')) {
return;
}
global $config;
$args['STYLE_PATH_ADMIN_ABS'] = PATH . 'plugins/my_admin_theme/theme/';
$args['DEFAULT_PATH_ADMIN_ABS'] = PATH . 'plugins/my_admin_theme/theme/';
$args['DEFAULT_PATH_ADMIN'] = $config['siteurl'] . 'plugins/my_admin_theme/theme/';
$args['STYLE_PATH_ADMIN'] = $config['siteurl'] . 'plugins/my_admin_theme/theme/';
return $args;
},
];
المتغيرات المنتهية بـ _ABS مسارات على نظام الملفات تُستخدم لتحديد موضع ملفات القوالب، أما البقية فروابط تُستخدم داخل القوالب لتحميل ملفات CSS وJavaScript والصور. ويجب أن يضم القالب الجديد كل ملفات admin_*.html، لأن كليجا لم يعد يستخدم القالب المدمج admin/Damask/.
style_parse_func وعدّل $html قبل الترجمة، أو الخطّاف Saaheader_func وعدّل $header بعد التوليد. ولإضافة وسم خاص بك، استخدم الخطّاف style_parse_func_step_2 وأضف زوج regex => replacement إلى $rep. يفحص كليجا الناتج المترجَم، ويعرض خطأ قالب حين ينتج استبدالك شيفرة PHP غير صالحة.يستخدم قالب لوحة التحكم والستايل bootstrap إطار Bootstrap 5.3 وأيقونات Font Awesome من includes/static_shared_files/. اكتب شيفرة صفحاتك الإدارية لـ Bootstrap 5.3 كي تنسجم مع بقية لوحة التحكم.
إضافة صفحات جديدة
صفحة في الواجهة
يستدعي go.php الخطّاف default_go_page حين لا يطابق المعامل go أي صفحة في النواة. اضبط $no_request على false للاستيلاء على الطلب، ثم اختر القالب عبر $stylee و$styleePath، وعنوان الصفحة عبر $titlee.
$kleeja_plugin['my_plugin']['functions'] = [
'default_go_page' => function ($args) {
if (g('go') !== 'mypage') {
return;
}
global $tpl;
$tpl->assign('my_message', 'مرحباً من إضافتي');
$no_request = false;
$stylee = 'my_page';
$styleePath = PATH . 'plugins/my_plugin/templates/';
$titlee = 'صفحتي';
return compact('no_request', 'stylee', 'styleePath', 'titlee');
},
];
ضع القالب في plugins/my_plugin/templates/my_page.html. عندها يقدّم كليجا الصفحة على العنوان go.php?go=mypage، وعلى go-mypage.html حين تكون الروابط الصديقة مفعّلة.
لكل رابط صيغتان، واحدة لكل قيمة من قيم $config['mod_writer']. ابنِ الروابط عبر kleeja_get_link()، أو فرّع حسب mod_writer كما تفعل النواة. ولمنح الصفحة رابطاً خاصاً بها، أضف قاعدة إعادة كتابة من دالة التثبيت عبر add_to_serve_rules()، واحذفها في دالة الإزالة عبر remove_from_serve_rules():
add_to_serve_rules("'^my-page\\.html$' => ['file' => 'go.php', 'args' => 'go=mypage'],", 'my_plugin');
تُكتب القواعد في الملف plugins_rules.php، الذي يقرؤه serve.php مع كل رابط صديق.
remove_from_serve_rules() ملف plugins_rules.php فارغاً حين يكون الملف مفقوداً، فتفشل بعدها كل الروابط الصديقة. تحقق من وجود الملف وقابليته للكتابة قبل استدعائها.صفحة في لوحة التحكم
تبني لوحة التحكم قائمتها من ملفات PHP الموجودة في includes/adm/، وتعيد أي قيمة أخرى لـ cp إلى الصفحة الرئيسة للوحة. أضف اسم صفحتك إلى $adm_extensions في الخطّاف begin_admin_page. وحين لا يكون للصفحة المطلوبة ملف في includes/adm/، يستدعي كليجا الخطّاف not_exists_{$go_to} ويضمّن المسار الذي تسنده إلى $include_alternative.
$kleeja_plugin['my_plugin']['functions'] = [
'begin_admin_page' => function ($args) {
$adm_extensions = $args['adm_extensions'];
$ext_icons = $args['ext_icons'];
$adm_extensions[] = 'my_plugin';
$ext_icons['my_plugin'] = 'puzzle-piece';
return compact('adm_extensions', 'ext_icons');
},
'not_exists_my_plugin' => function ($args) {
$include_alternative = __DIR__ . '/admin_page.php';
return compact('include_alternative');
},
];
يضبط ملفك المضمَّن المتغيرين $stylee و$styleePath تماماً كأي صفحة إدارية في النواة. تصل إلى الصفحة عبر admin/?cp=my_plugin. تعرض القائمة العبارة R_MY_PLUGIN من $lang أو $olang، والأيقونة اسم من Font Awesome بدون البادئة fa-.
<?php
// يمنع الفتح المباشر
if (! defined('IN_ADMIN')) {
exit();
}
$H_FORM_KEYS = kleeja_add_form_key('my_plugin');
if (ip('submit') && ! kleeja_check_form_key('my_plugin')) {
kleeja_admin_err($lang['INVALID_FORM_KEY']);
}
$stylee = 'admin_my_plugin';
$styleePath = __DIR__ . '/templates/';
احمِ كل نموذج عبر kleeja_add_form_key() وkleeja_check_form_key()، وكل رابط يغيّر البيانات عبر صيغتيهما المنتهيتين بـ _get. وتحقق من الصلاحيات عبر user_can()، أو عبر $userinfo['founder'] للإجراءات المقصورة على المؤسسين.
settings_page في مصفوفة المعلومات على cp=options&smt=my_plugin. عندها تعرض قائمة الإضافات زر إعدادات ينقلك مباشرة إلى تبويب إعداداتك.دليل المساعدة
في لوحة التحكم صفحة مساعدة على العنوان admin/?cp=s_help، ويفتح زر المساعدة في كل صفحة الدليلَ الخاص بها. تضيف الإضافة دليلها عبر الخطّاف admin_help_guides، مفهرساً باسم الإضافة، فيعرض كليجا أيقونة الإضافة بجانبه. أما الإضافة المثبّتة التي لا دليل لها فتحصل على مدخل يضم وصفها ورابطاً إلى إعداداتها.
'admin_help_guides' => function ($args) {
$help_guides = $args['help_guides'];
$help_guides['my_plugin'] = [
'title' => ['en' => 'My Plugin', 'ar' => 'إضافتي'],
'intro' => ['en' => 'What the plugin does, in a sentence or two.', 'ar' => 'ما تفعله الإضافة في جملة أو جملتين.'],
// يفتح زر المساعدة في ?cp=my_plugin هذا الدليل
'page' => 'my_plugin',
'link' => './?cp=my_plugin',
'sections' => [
['type' => 'steps', 'items' => ['en' => ['Open the settings.', 'Save.'], 'ar' => ['افتح الإعدادات.', 'احفظ.']]],
['type' => 'faq', 'items' => [['q' => 'سؤال؟', 'a' => 'الجواب.']]],
],
];
return compact('help_guides');
},
قيمة type للقسم هي features أو steps أو tips أو warnings أو faq أو text أو code. وعناصره إما قائمة من العناصر المترجمة، أو قائمة لكل لغة. احفظ نصوص الدليل في ملفات اللغة الخاصة بالإضافة، مثل language/help_en.php وlanguage/help_ar.php، بدل جدول lang. وفي صفحة المساعدة نفسها دليل لمطوّري الإضافات فيه مثال أطول.
إدارة الإضافات
يدير المؤسسون الإضافات من لوحة التحكم ← الإضافات. في الصفحة أربعة تبويبات: الإضافات المثبّتة، والمجلدات الموجودة على الخادم دون تثبيت، والإضافات المتاحة في متجر كليجا، والإضافات المثبّتة التي لها إصدار أحدث في المتجر.
| الإجراء | الأثر |
|---|---|
| التثبيت | يدرج صف plugins، ويشغّل install، ويعرض رسالة first_run. |
| التعطيل | يضبط plg_disabled = 1. تبقى الإضافة مثبّتة لكنها لا تُحمَّل، ويختفي تبويب إعداداتها. |
| التفعيل | يصفّر plg_disabled. |
| الإزالة | يشغّل uninstall ويحذف صف plugins. تبقى الملفات على الخادم. |
| حذف المجلد | يحذف مجلد الإضافة من plugins/. |
| الرفع | يفكّ ضغط ملف .zip مرفوع داخل plugins/. |
| التنزيل | يجلب الإضافة من المتجر ويفكّ ضغطها، مع تراجع تلقائي عند الفشل. ويحدّث أيضاً الإضافة المثبّتة. |
تستطيع الشيفرة التي تحتاج إلى إدارة الإضافات خارج صفحة الإدارة استدعاء الدوال الساكنة في الصنف Plugins. لا تتحقق هذه الدوال من صلاحيات المستخدم الحالي، وتبدأ خطّافات الإضافة المفعّلة حديثاً بالعمل من الطلب التالي.
| الدالة | الأثر |
|---|---|
Plugins::installFromStore($name) | تنزّل الإضافة وتثبّتها وتفعّلها في استدعاء واحد، متخطيةً الخطوات المنجزة سابقاً. |
Plugins::download($name) | تنزّل الإضافة من المتجر إلى plugins/، أو تحدّث ملفاتها، دون تثبيتها. |
Plugins::install($name) | تثبّت إضافة موجودة أصلاً في plugins/. |
Plugins::enable($name) وPlugins::disable($name) | تفعّل إضافة مثبّتة أو تعطّلها. |
Plugins::lastError() | تشرح سبب إرجاع آخر استدعاء من هذه الاستدعاءات القيمة false. |
التحزيم والتوزيع
وزّع الإضافة كملف .zip يحتوي مجلداً واحداً في جذره. يرفعه المديرون من لوحة التحكم ← الإضافات، ويفكّ كليجا ضغطه مباشرة داخل plugins/.
zip -r my_plugin.zip my_plugin -x "*.git*"
يجوز أن يحمل المجلد الجذري رقم إصدار، كما في الأرشيفات التي يبنيها GitHub للإصدارات. يعيد كليجا تسمية my-plugin-1.0/ إلى my_plugin بعد فكّ الضغط، محوّلاً الشرطات إلى شرطات سفلية. أضف ملف icon.png إلى مجلد الإضافة للحصول على أيقونة مخصصة في قائمة الإضافات، وملف index.html لمنع عرض محتويات المجلد على الخوادم التي تسمح بذلك.
يقرأ كليجا أيضاً فهرساً عاماً، فتُثبَّت الإضافات المدرجة فيه بنقرة واحدة. يقرأ كليجا 4 الملف kleeja-4-catalog.json، ولكل إضافة فيه مدخل مثل هذا:
{
"type": "plugin",
"name": "my_plugin",
"icon": "https://raw.githubusercontent.com/you/my_plugin/main/icon.png",
"title": { "en": "My Plugin", "ar": "إضافتي" },
"description": { "en": "Does something useful", "ar": "تقوم بشيء مفيد" },
"developer": "اسمك",
"website": "https://github.com/you/my_plugin",
"kleeja_version": { "min": "4.0.0", "max": "4.9" },
"file": {
"version": "1.0",
"url": "https://github.com/you/my_plugin/archive/1.0.zip"
}
}
يُخفي المتجر الإضافات التي لا يشمل نطاق kleeja_version فيها إصدار كليجا العامل. ويحل الكائن الاختياري preview، بالصيغة نفسها التي لـ file، محل file على المواقع التي تعمل في وضع التطوير، فتستطيع اختبار الإصدار قبل نشره.
تنقيح الأخطاء
أضف define('DEV_STAGE', true); إلى ملف config.php لتفعيل أدوات التنقيح. عندها تُترجَم القوالب من جديد مع كل طلب، وتُسجَّل كل أخطاء PHP وتُعرض، وتصبح الدالة dd() متاحة، وتكتب kleeja_log() في الملف cache/kleeja_log.log.
يعرض تذييل الصفحة زمن التوليد وعدد الاستعلامات، ويحصل المديرون على رابط Debug Info يفتح الصفحة من جديد مع ?debug. تعرض لوحة التنقيح:
- كل استعلامات الصفحة، مع زمن كل منها وقيمه المربوطة،
- كل خطّاف له دوال، مع الإضافات التي سجّلتها بترتيب تنفيذها،
- استهلاك الذاكرة وإصدار PHP ومشغّل قاعدة البيانات.
قائمة الخطّافات أسرع وسيلة للتأكد من صحة كتابة أسماء خطّافاتك. ولاختبار عناصر المتجر غير المعلَّمة كمتوافقة مع إصدار كليجا لديك، أضف أيضاً define('IGNORE_STORE_COMPATIBILITY', true);.
التعافي من إضافة معطوبة
حين تعطّل إضافةٌ الموقعَ إلى حد تعذّر الوصول إلى لوحة التحكم، أوقف النظام بأكمله من config.php:
define('STOP_PLUGINS', true);
يعود المحمّل فوراً دون تضمين أي إضافة، ويعرض التذييل Hook System: Disabled. احذف السطر بعد تعطيل الإضافة المسببة للمشكلة أو حذفها.
init.php مع كل طلب دون استثناء. اقصره على التعريفات فقط، ولا تنفّذ فيه استعلامات أو طلبات شبكة أو فحص ملفات في المستوى الأعلى، وإلا أبطأت كل صفحة في الموقع.مثال كامل
إضافة بسيطة تضيف إعداداً واحداً وتلحق ملاحظة مخصصة بتذييل الموقع.
<?php
// منع التشغيل المباشر
if (! defined('IN_PLUGINS_SYSTEM')) {
exit();
}
$kleeja_plugin['footer_note']['information'] = [
'plugin_title' => [
'en' => 'Footer Note',
'ar' => 'ملاحظة التذييل',
],
'plugin_developer' => 'اسمك',
'plugin_version' => '1.0',
'plugin_description' => [
'en' => 'Appends a custom note to the site footer',
'ar' => 'تضيف ملاحظة مخصصة إلى تذييل الموقع',
],
'plugin_kleeja_version_min' => '4.0.0',
'plugin_kleeja_version_max' => '4.9',
'plugin_priority' => 0,
'settings_page' => 'cp=options&smt=footer_note',
];
$kleeja_plugin['footer_note']['first_run']['ar'] = 'اضبط نص الملاحظة من: لوحة التحكم ← الخيارات ← ملاحظة التذييل.';
$kleeja_plugin['footer_note']['install'] = function ($plg_id) {
add_config_r([
'footer_note_text' => [
'value' => '',
'html' => configField('footer_note_text'),
'plg_id' => $plg_id,
'type' => 'footer_note',
'order' => '1',
],
]);
add_olang([
'CONFIG_KLJ_MENUS_FOOTER_NOTE' => 'ملاحظة التذييل',
'FOOTER_NOTE_TEXT' => 'نص الملاحظة',
], 'ar', $plg_id);
add_olang([
'CONFIG_KLJ_MENUS_FOOTER_NOTE' => 'Footer note',
'FOOTER_NOTE_TEXT' => 'Note text',
], 'en', $plg_id);
};
$kleeja_plugin['footer_note']['update'] = function ($old_version, $new_version) {
// لا ترحيلات حتى الآن
};
$kleeja_plugin['footer_note']['uninstall'] = function ($plg_id) {
delete_config(['footer_note_text']);
if ($plg_id > 0) {
delete_olang(null, null, $plg_id);
}
};
$kleeja_plugin['footer_note']['functions'] = [
'print_Saafooter_func' => function ($args) {
global $config;
if (empty($config['footer_note_text'])) {
return;
}
// يُخزَّن الإعداد مُرمَّزاً بـ HTML، وتُرمّزه kleeja_html_display() مرة واحدة بالضبط
$note = '<p class="footer-note">' . kleeja_html_display($config['footer_note_text']) . '</p>';
$footer = str_replace('</body>', $note . '</body>', $args['footer']);
return compact('footer');
},
];
ترقية إضافة من كليجا 3
يعمل معظم نظام الإضافات كما كان في كليجا 3. راجع هذه النقاط عند نقل إضافة:
- PHP 8.2. يتطلب كليجا 4 الإصدار PHP 8.2، لذا يجب إصلاح الشيفرة التي تعتمد على مزايا أُزيلت من PHP أو على الأنواع المتساهلة.
- حدود الإصدار. ارفع
plugin_kleeja_version_max، فالحد الأعلى3.9يمنع التثبيت على كليجا 4. - الاستعلامات. أصبحت طبقة قاعدة البيانات مبنية على PDO. انقل القيم من نصوص SQL إلى
BIND، واستبدل$SQL->escape()و$SQL->real_escape(). راجع قاعدة البيانات. - دالة التحديث. استبدل فيها دوال الإعدادات المساعدة و
delete_cache()باستعلامات مباشرة. راجع دالة التحديث. - الخطّافات. طابق اسم كل خطّاف مع النواة، فالدالة المربوطة بخطّاف لم يعد كليجا يستدعيه لا تعمل أبداً، ولا شيء ينبّهك إلى ذلك.
- قالب لوحة التحكم. تستخدم لوحة التحكم القالب
Damaskالمبني على Bootstrap 5.3، لذا تحتاج القوالب الإدارية المكتوبة لقالب Bootstrap 4 القديم إلى شيفرة جديدة. - ترميز النصوص. اطبع النص المخزَّن عبر
kleeja_html_display()كي لا يظهر النص المرمَّز مرتين بكيانات مثل&quot;. - المساعدة. انقل نصوص المساعدة إلى صفحة المساعدة عبر الخطّاف
admin_help_guides. راجع دليل المساعدة.
المرجع البرمجي
الدوال المساعدة المتاحة للإضافات، وجميعها معرّفة في includes/functions.php ما لم يُذكر خلاف ذلك.
| الدالة | التوقيع |
|---|---|
add_config() | add_config(string $name, string $value, int $order = 0, string $html = '', string $type = '0', int $plg_id = 0, bool $dynamic = false): bool |
add_config_r() | add_config_r(array $configs): bool |
update_config() | update_config(string $name, string $value, bool $escape = true, int $group = 0): bool |
delete_config() | delete_config(string|array $name): bool |
get_config() | get_config(string $name): ?string، تقرأ القيمة من قاعدة البيانات لا من المخزون المؤقت |
add_olang() | add_olang(array $words = [], string $lang = 'en', int $plg_id = 0): void |
update_olang() | update_olang(string $name, string $value, string $lang = 'en'): bool |
delete_olang() | delete_olang(string|array|null $words = '', string|array|null $lang = 'en', int $plg_id = 0): bool |
delete_cache() | delete_cache(string|array $name, bool $all = false): bool |
g() وp() | g(string $name, string $type = 'str', string $default = ''): string|int، تقرأ من $_GET أو $_POST مُرمَّزة بـ HTML |
ig() وip() | ig(string $name): bool، تتحقق من وجود قيمة في $_GET أو $_POST |
user_can() | user_can(string $acl_name, int $group_id = 0): bool |
kleeja_html_encode() | kleeja_html_encode(?string $text): string |
kleeja_html_decode() | kleeja_html_decode(?string $text): string |
kleeja_html_display() | kleeja_html_display(?string $text): string |
add_to_serve_rules() | add_to_serve_rules(string|array $rules, string $unique_id = ''): bool |
remove_from_serve_rules() | remove_from_serve_rules(string $unique_id): bool |
send_mail() | send_mail(string $to, string|array $body, string $subject, string $fromAddress, string $fromName, string $bcc = ''): bool |
kleeja_get_link() | kleeja_get_link(string $pid, array $extra = []): string — في includes/functions_display.php |
configField() | configField(string $name, string $type = 'text', array $select_options = []): string — في includes/functions_display.php |
runHook() | runHook(string $hookName, array $definedVariables): array — في includes/plugins.php |
الثوابت
| الثابت | المعنى |
|---|---|
IN_PLUGINS_SYSTEM | يُعرَّف أثناء تحميل الإضافات. احرس به كل ملف init.php. |
STOP_PLUGINS | يُعرَّف في config.php لتعطيل نظام الإضافات بالكامل. |
KLEEJA_PLUGINS_FOLDER | اسم مجلد الإضافات، وهو plugins افتراضياً. |
KLEEJA_VERSION | إصدار كليجا العامل، ويُستخدم في فحص التوافق. |
PATH | المسار المطلق لجذر كليجا على نظام الملفات، وينتهي بشرطة مائلة. |
DEV_STAGE | يفعّل مخرجات التنقيح ويوقف تخزين القوالب مؤقتاً. |
IGNORE_STORE_COMPATIBILITY | يُعرَّف في config.php لعرض عناصر المتجر وتنزيلها أياً كان نطاق إصدار كليجا الخاص بها. |
SQL_NO_ERRORS | يُعرَّف أثناء تثبيت الإضافة. تُرجع الاستعلامات الفاشلة false بدل عرض صفحة خطأ. |
IN_ADMIN | يُعرَّف أثناء عرض لوحة التحكم. |
ACP_STYLE_NAME | مجلد قالب لوحة التحكم، وهو Damask. |