🇬🇧 English Version: Read the original article in English here.
حفظ الثيمات على مستوى المؤسسة في Oracle APEX: قاعدة البيانات، الملفات الشخصية، والوميض الصفري 🏨🔐
الجزء السادس (مسك الختام) من السلسلة التقنية الشاملة: الربط مع قاعدة البيانات والمزامنة الفورية في Oracle APEX.
⚡ الخلاصة في كلمتين (TL;DR)
تخيل نزلت في فندق خمس نجوم عالمي في طوكيو، وموظف الاستقبال سلمك كارت غرفة ذكي لونه أسود في دهبي. أول ما حطيت الكارت على باب جناحك، الجناح الذكي حس بملفك الشخصي فوراً: الستاير فتحت لوحدها، والإضاءة خفتت لدرجة كهرمانية دافية، والتكييف اتظبط على 20 درجة مئوية، وموسيقى الجاز اشتغلت بهدوء في الخلفية!
بعدها بأسبوعين، سافرت لندن ونزلت في نفس سلسلة الفنادق. أول ما مررت نفس الكارت على باب غرفتك في لندن.. في نفس اللحظة الغرفة اتظبطت بنفس درجات الإضاءة والتكييف والمزيكا اللي بتفضلها قبل ما تحط شنطتك على الأرض! مكنتش محتاج تظبط أي زرار من أول وجديد.
التجربة المتناسقة دي هي بالضبط اللي بيستناها مستخدمين أنظمة الشركات والمؤسسات. التخزين المحلي في المتصفح
localStorageممتاز لجهاز واحد، بس موظف الشركة بيتنقل بين كمبيوتر المكتب، واللابتوب في الاجتماعات، والتابلت في المخازن. وعايز ثيمه المفضل يفضل متسجل في هويته المؤسسية (:APP_USER) في كل حتة.في مسك الختام لسلسلتنا APEX Theme Factory، هنربط واجهات المستخدم الحديثة مع هندسة قاعدة بيانات أوراكل العميقة. هنتعلم إزاي نخزن تفضيلات المستخدمين عبر حزمة
APEX_UTIL.SET_PREFERENCE، ونعمل مزامنة سريعة في الخلفية بتقنية Ajax، ونحقق المعجزة: حقن الثيم من السيرفر بدون أي وميض أبيض (Zero-FOUC Hydration) باستخدام عناصر التطبيق (Application Items). 🚀
📦 مستودع كود المشروع: كود المحرك، وحزم الثيمات، وسكربتات التثبيت متاحة بالكامل على github.com/ash2osh/APEX_THEME_FACTORY.
🌐 واقع بيئات الشركات: ليه التخزين المحلي لوحده ميكفيش؟
في الجزأين الثالث والخامس من السلسلة اعتمدنا على localStorage في المتصفح. للتشغيل على جهاز واحد، الحل ده مثالي وسريع جداً: صفر استهلاك لقاعدة البيانات، وصفر زمن تأخير شبكة، وسرعة تشغيل في أقل من 2 ميلي ثانية.
لكن في بيئات العمل الحقيقية للشركات:
- التنقل بين الأجهزة: الممرض في المستشفى بيسجل دخول على شاشة الاستقبال في عنبر أ، وبعدها بنصف ساعة بيسجل دخول على تابلت في عنبر ب. كود المتصفح المحلي ملوش أي وسيلة ينقل التفضيل بين جهازين منفصلين.
- بيئات الأجهزة الافتراضية (Citrix / VDI): بنوك كتير ومؤسسات حكومية بتشغل أنظمة VDI بتمسح ذاكرة التخزين المؤقت للمتصفح (Cache) بالكامل مع كل تسجيل خروج يومي للموظف.
- التحكم والامتثال للمسؤولين: فرق الدعم الفني وتكنولوجيا المعلومات مش بيعرفوا يفحصوا أو يفرضوا ثيمات مريحة لمتحدي الإعاقة البصرية لو التفضيلات محبوسة جوه هارد ديسك المستخدم.
عشان نعمل نظام ثيمات مؤسسي بجد، لازم نربط تفضيل الثيم بملف المستخدم جوه قاعدة بيانات أوراكل، مع الحفاظ على سرعة الرسم اللحظية.
🗄️ المعمارية الهندسية: الجسر المؤسسي في 4 مراحل
المخطط ده بيوضح إزاي Theme Factory بيربط شاشات المتصفح مع سطور قاعدة بيانات أوراكل:
┌────────────────────────────────────────────────────────┐
│ 1. تسجيل دخول المستخدم (:APP_USER) │
│ إجراء ما بعد المصادقة يقرا التفضيل ويحطه في │
│ عنصر التطبيق العام: &F_USER_THEME. │
└───────────────────────────┬────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────┐
│ 2. الحقن المسبق من السيرفر (Server-Side Hydration) │
│ كود التمهيد في Page 0 يستلم &F_USER_THEME!ATTR. │
│ الصفحة بتوصل للمتصفح وهي جاهزة بالثيم المظلم فورا! │
└───────────────────────────┬────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────┐
│ 3. تفاعل المستخدم (اختيار ثيم جديد من الشاشة) │
│ استدعاء window.ApexThemeFactory.use('carbon-volt') │
│ تحديث التخزين المحلي للسرعة الفورية على الجهاز │
└───────────────────────────┬────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────┐
│ 4. مزامنة الخلفية غير المتزامنة (Ajax Callback) │
│ إرسال طلب صامت لإجراء PL/SQL في الصفحة رقم 0 │
│ حفظ الاختيار في الداتابيز عبر APEX_UTIL.SET_PREFERENCE│
└────────────────────────────────────────────────────────┘
🛠️ الخطوة 1: تخزين التفضيل في قاعدة بيانات أوراكل
أوراكل أبيكس بتقدّم محرك تفضيلات جاهز وفائق الأداء ومفهرس داخلياً من خلال حزمة APEX_UTIL. المحرك ده بيعزل التفضيلات تلقائياً لكل تطبيق ولكل مستخدم:
-- تجربة حفظ وقراءة تفضيل الثيم في ورشة العمل كالتالي:
BEGIN
-- حفظ تفضيل الثيم للمستخدم المسجل حالياً
apex_util.set_preference(
p_preference => 'USER_THEME',
p_value => 'carbon-volt',
p_user => :APP_USER
);
-- قراءة التفضيل المخزن
dbms_output.put_line(
'الثيم المخزن في الداتابيز: ' || apex_util.get_preference('USER_THEME', :APP_USER)
);
END;
/
(ملحوظة: لو نظام شركتك بيعتمد على جداول مخصصة لحسابات المستخدمين مع شاشات رقابة وتدقيق، تقدر ببساطة تستبدل حزمة apex_util بأمر تحديث صريح: UPDATE app_user_profiles SET theme = ...).
⚡ الخطوة 2: إجراء الـ Ajax الصامت في الصفحة 0
لما المستخدم يغير الثيم من شاشة الإعدادات، مش عايزين نعمل إعادة إرسال كاملة للصفحة (Full Page Submit) تلخبط موضع التمرير أو تقفل حقول مفتوحة. إحنا بنبعت طلب Ajax خفيف في الخلفية.
خطوات الإعداد جوه Page Designer كالتالي:
- افتح الصفحة رقم 0 (Global Page).
- تحت قسم Processing، اضغط كليك يمين على Ajax Callback واختار Create Process.
- اضبط الإجراء كالتالي:
- الاسم:
SET_USER_THEME_AJAX - كود الـ PL/SQL:
- الاسم:
DECLARE
l_theme VARCHAR2(100) := apex_application.g_x01;
BEGIN
-- التحقق من القائمة المعتمدة لمنع أي حقن برمجي غير مصرح به
IF l_theme IN ('carbon-volt', 'citrus-pop', 'cobalt-press',
'estate-slate', 'estate-slate-dark', 'linen',
'solarized-dark', 'velvet-signal', 'iris') THEN
-- حفظ التفضيل الرسمي في قاعدة بيانات أوراكل أبيكس
apex_util.set_preference(
p_preference => 'USER_THEME',
p_value => l_theme,
p_user => :APP_USER
);
-- تحديث عنصر الجلسة الحالي عشان يفضل متطابق
:F_USER_THEME := l_theme;
-- إرجاع رسالة نجاح بصيغة JSON
apex_json.open_object;
apex_json.write('status', 'SUCCESS');
apex_json.write('theme', l_theme);
apex_json.close_object;
ELSE
apex_json.open_object;
apex_json.write('status', 'ERROR');
apex_json.write('message', 'اسم الثيم غير مصرح به.');
apex_json.close_object;
END IF;
EXCEPTION
WHEN OTHERS THEN
apex_json.open_object;
apex_json.write('status', 'ERROR');
apex_json.write('message', SQLERRM);
apex_json.close_object;
END;
كود الربط في ملفات الـ JavaScript كالتالي:
دلوقتي بنربط أداة تبديل الثيمات بحيث أول ما المستخدم ينادي دالة use، نبعت طلب الـ Ajax الصامت ده:
// مزامنة اختيار الثيم مع قاعدة البيانات بدون مقاطعة المستخدم
function persistThemeToDatabase(themeName) {
if (typeof apex === "undefined" || !apex.server) return;
apex.server.process(
"SET_USER_THEME_AJAX",
{
x01: themeName
},
{
dataType: "json",
success: function (data) {
if (data.status === "SUCCESS") {
console.log("[Theme Factory] تم حفظ التفضيل بنجاح في قاعدة البيانات:", data.theme);
}
},
error: function (jqXHR, textStatus, errorThrown) {
console.warn("[Theme Factory] تعذر الاتصال بالسيرفر للحفظ:", errorThrown);
}
}
);
}
🔑 الخطوة 3: تجهيز الجلسة لحظة تسجيل الدخول
لما المستخدم يفتح التطبيق من جهاز كمبيوتر جديد خالص، بيكون التخزين المحلي في المتصفح فاضي تماماً. إزاي نضمن إنه ميرجعش يشوف الشاشة البيضاء في أول لقطة؟
بنقرا تفضيله من الداتابيز ونحطه في عنصر تطبيق عام (Application Item) أثناء عملية تسجيل الدخول:
- في المكونات المشتركة (Shared Components)، أنشئ عنصر تطبيق جديد:
- الاسم:
F_USER_THEME - النطاق: Application
- الاسم:
- في مخططات المصادقة (Authentication Schemes)، افتح المخطط النشط:
- في خانة Post-Authentication Procedure Name، ضيف الكود التالي:
PROCEDURE post_auth_load_theme IS
l_pref VARCHAR2(100);
BEGIN
-- قراءة التفضيل المسجل للمستخدم في قاعدة البيانات
l_pref := apex_util.get_preference('USER_THEME', :APP_USER);
-- لو المستخدم ملوش تفضيل سابق بنخليه يفتح على ثيم Linen الافتراضي
:F_USER_THEME := coalesce(l_pref, 'linen');
END post_auth_load_theme;
🌟 اللمسة السحرية: حقن السيرفر الخالي من الوميض تماماً
ودلوقتي مع الضربة القاضية اللي بتجمع السلسلة المكونة من 6 أجزاء في سيمفونية واحدة متكاملة.
بناخد كود التمهيد في الصفحة 0 اللي شرحناه في الجزء الثالث، ونخليه يقرا عنصر التطبيق مباشرة عبر علامات استبدال أبيكس:
<!-- APEX_THEME_FACTORY_MANAGED:BEGIN:BOOTSTRAP -->
<script>
window.APEX_THEME_FACTORY_CONFIG = {
appId: 102,
/* السيرفر بيعوض بقيمة الداتابيز مباشرة جوه ملف الـ HTML الأول! */
defaultTheme: "&F_USER_THEME!ATTR.",
switcherEnabled: true,
themes: __THEMES_JSON__
};
(function (doc, config) {
"use strict";
var root = doc.documentElement;
var key = "apex.themeFactory." + config.appId;
// 1. استخدام الثيم المعتمد من الداتابيز واللي واصل في أول بايت من السيرفر
var selected = config.defaultTheme || "linen";
// 2. استخدام التخزين المحلي لو المستخدم عدل حاجة أوفلاين
try {
var stored = window.localStorage.getItem(key);
if (stored) { selected = stored; }
} catch (e) {}
// 3. تركيب كلاس الثيم على <html> في أقل من 2 ميلي ثانية قبل الرسم
if (selected && selected !== "iris") {
root.classList.add("app-theme-" + selected);
}
root.dataset.appThemeCurrent = selected;
}(document, window.APEX_THEME_FACTORY_CONFIG));
</script>
<!-- APEX_THEME_FACTORY_MANAGED:END:BOOTSTRAP -->
ليه الحركة دي بتمثل قمة النضج الهندسي؟
- صفر وميض في أول زيارة من أي جهاز: لما المستخدم يفتح التطبيق من جهاز آيباد جديد، حزمة الـ HTML اللي بتتبعت من سيرفر ORDS واصلة ومكتوب جواها مسبقاً
defaultTheme: "carbon-volt". المتصفح بيلحق يركب الكلاس على<html>قبل ما محرك التفسير يوصل حتى لوسم الـ<body>! - استقرار فوري بدون إنترنت: لو الاتصال قطع أو الموظف شغال في بيئة معزولة، التخزين المحلي بيكمل المسيرة بسلاسة تامة.
- صفر تكلفة صيانة مستقبلية: كل الشغل محطوط بأمان في الصفحة 0 وملفات الـ CSS. شاشاتك وتقاريرك وجداولك وبرمجياتك الأساسية بتفضل نظيفة وسليمة 100%!
🏁 ختام السلسلة الشاملة وملخص الرحلة
على مدار المقالات الستة دي، سافرنا سوا من عشوائية التنسيقات التقليدية الملحومة، لحد ما بنينا محرك تصميم عالمي يليق بكبرى الشركات:
- الجزء 1: بدلة الحرباء — الهروب من قيود Theme Roller وبناء حزم ثيمات مستقلة فوق Universal Theme 42.
- الجزء 2: قلعة العزل الحصينة — معمارية التوكنز ثلاثية الطبقات، حماية الحركة بأعلى أسبقية تنسيق، وإنقاذ أيقونات Font APEX.
- الجزء 3: سر الوميض الصفري — القضاء التام على وميض الشاشات المزعج في أقل من 2 ميلي ثانية وتغطية النوافذ المنبثقة.
- الجزء 4: ماكينة الحزم المحمولة — تجميع خطوط WOFF2 بدون اعتمادات خارجية، والترقيع الذري لملفات APEXLang مع أمان التراجع.
- الجزء 5: بناء محول الثيمات — اعتراض قائمة Navigation Bar، والتحكم في روابط الـ Hash، وتصميم أزرار Alpine.js العصرية.
- الجزء 6: حفظ الثيمات على مستوى المؤسسة — ربط التفضيلات بحساب المستخدم في الداتابيز عبر
APEX_UTILوالحقن السحابي الخالي من الوميض.
دلوقتي معاك الكتالوج الهندسي الكامل لبناء، ونشر، وصيانة أنظمة تصميم استثنائية في أوراكل أبيكس تدوم لسنين بدون تراكم أي ديون برمجية. انطلق وابنِ برمجيات مبهرة تسعد مستخدميك! 🚀
💡 ابدأ مع APEX Theme Factory الآن: تقدر تعمل Clone للمستودع، وتجرب الثيمات الجاهزة (
carbon-volt،iris-dark، وغيرها)، وتستخدم أداة التثبيت الآلية: 🔗 https://github.com/ash2osh/APEX_THEME_FACTORY