OW Agenda — التوثيق
حجز المواعيد عبر الإنترنت وأجندة العمل اليومي، داخل WordPress، بلا اشتراك. المؤلف: OptionWeb — Julien Daniel صفحة الإضافة: https://optionweb.dev/ar/addons/ow-agenda/ الترخيص: GPL-2.0-or-later الإصدار الذي يغطيه هذا المستند: 1.3.1
جدول المحتويات
- نظرة عامة
- التثبيت
- بداية سريعة
- مسار الحجز
- التحقق المزدوج بالبريد الإلكتروني
- مصادقة الطلبات
- صفحة الأجندة
- دليل العملاء
- دعوات الجلسات
- رسائل البريد الإلكتروني وملف التقويم (ICS)
- التذكيرات والمهمة المجدولة
- مكافحة الرسائل المزعجة وحدود المعدل
- GDPR
- الشورت-كود
- REST API
- مرجع الإعدادات
- البيانات المخزَّنة
- التدويل
- إلغاء التثبيت
- استكشاف الأخطاء
- الأسئلة الشائعة
- سجل التغييرات
نظرة عامة
يضيف OW Agenda إلى WordPress نظامًا كاملًا للمواعيد: صفحة عمومية يختار فيها الزائر يومه ثم ساعته، وصفحة «الأجندة» المحمية بتسجيل الدخول، المصمَّمة لتُستعمل من الهاتف، وفيها تديرون يوم عملكم.
كل شيء يبقى داخل الموقع. لا خدمة خارجية، ولا API طرف ثالث، ولا اشتراك: الخانات
الزمنية يحسبها خادمكم، والمواعيد تعيش في جداول MySQL خاصتكم، والرسائل تنطلق عبر
wp_mail().
ما يأتي مع الإضافة (كل شيء مجاني، GPL-2):
- حجز عبر الإنترنت: اختيار اليوم، ثم اختيار الخانة، ثم نموذج قصير، وخانة موافقة GDPR
- تأكيد مزدوج بالبريد الإلكتروني: الطلب لا يصير حقيقيًا إلا بعد النقر على رابط مصادقة — البوتات لا تبلغ الأجندة أبدًا
- مصادقة يدوية (موصى بها) أو تأكيد تلقائي، مع زرَّي تأكيد / رفض مباشرة داخل رسالة الإشعار (روابط موقَّعة بـ HMAC، دون تسجيل دخول)
- صفحة «الأجندة» المصمَّمة للهاتف أولًا: عرض اليوم، والطلبات المعلَّقة، والمواعيد الهاتفية المُدخَلة يدويًا، وحجب الخانات، وإعادة الجدولة، وحالتا تم / تخلَّف عن الحضور
- دليل العملاء: بحث، وإنشاء، وتحرير، وحذف؛ يتغذى تلقائيًا من المواعيد المؤكَّدة
- دعوات الجلسات: رابط خاص يتيح لعميل قائم اختيار تاريخ جلسته، ضمن خدمة ومدة تحدّدونهما
- رسائل HTML أنيقة مع مرفق ICS (إضافة إلى تقويم الهاتف بحركة واحدة)، وتذكير تلقائي في اليوم السابق، وإلغاء عبر الإنترنت من طرف العميل
- إخفاء تلقائي لهوية المواعيد المنتهية، وحذف الطلبات التي لم تُصادَق قط
- REST API كاملة تحت النطاق
owag/v1
ما لا تفعله الإضافة، عن قصد:
- لا دفع عبر الإنترنت: يأخذ OW Agenda الموعد، ولا يحصّل عربونًا (استعملوا WooCommerce إن كانت تلك حاجتكم)
- أجندة واحدة: لا إدارة لعدة ممارسين ولا لعدة موارد؛ الإضافة تصف جدولًا واحدًا، هو جدول الموقع
- لا مزامنة ثنائية الاتجاه مع Google Agenda أو Outlook: تقويم العميل يُحدَّث بملف ICS (اتجاه واحد)، وارتباطاتكم الشخصية تُدخَل على شكل حجب للخانات
- لا كتالوج خدمات أمام الزائر: الحجز العمومي يخصّ خدمة واحدة (عنوانها ومدتها قابلان للضبط)؛ أما الخدمات الأخرى فتمرّ عبر دعوات الجلسات
- لا مواعيد متكررة، ولا اجتماعات مرئية، ولا غرفة انتظار
هذه القائمة خيار معماري، لا خارطة طريق مخفية: أجندة بسيطة لا تخطئ الخانة أبدًا خير من آلة معقدة لا يجرؤ أحد على لمسها.
التثبيت
من ملف .zip
- نزّلوا
ow-agenda-1.3.1.zipمن https://optionweb.dev/ar/addons/ow-agenda/ - الإضافات → إضافة جديدة → رفع إضافة
- اختاروا الملف، ثم انقروا تثبيت، ثم تفعيل
عبر FTP
فُكّوا ضغط الأرشيف وضعوا مجلد ow-agenda في /wp-content/plugins/، ثم فعّلوا الإضافة
من الإضافات.
المتطلبات
- WordPress 6.3 أو أحدث
- PHP 7.4 أو أحدث (يُوصى بـ 8.1+)
- MySQL 5.7+ / MariaDB 10.2+ — الدالتان
GET_LOCK()/RELEASE_LOCK()تُستعملان لسلسلة الحجوزات المتزامنة - إرسال بريد إلكتروني يعمل فعلًا. هذه هي التبعية الحقيقية الوحيدة للإضافة: المسار كله (المصادقة، والتأكيد، والتذكير، والإلغاء) يمرّ عبر البريد. على استضافة مشتركة، ثبّتوا إضافة SMTP قبل إطلاق الأجندة.
ما الذي يُثبَّت
عند التفعيل، ينشئ OW Agenda ثلاثة جداول:
| الجدول | المحتوى |
|---|---|
{prefix}_owag_appointments | كل أحداث الأجندة: الاستشارات القادمة من الويب، والجلسات الناتجة عن دعوة، والمواعيد المُدخَلة يدويًا، وحجب الخانات |
{prefix}_owag_invites | الروابط الخاصة «اختر تاريخ جلستك» |
{prefix}_owag_customers | دليل العملاء |
…وينشئ، في المرة الأولى فقط، صفحتين منشورتين:
- «حجز موعد» — تحتوي على الشورت-كود
[ow_agenda] - «الأجندة» — تحتوي على الشورت-كود
[ow_agenda_admin]
معرّفاهما محفوظان في الخيارين owag_page_booking وowag_page_admin؛ أما العَلَم
owag_pages_created فيضمن أن التعطيل ثم إعادة التفعيل لا يُنشئان نسخًا مكرَّرة. يمكنكم
إعادة تسمية هاتين الصفحتين، ونقلهما إلى قائمة، وتغيير رابطهما الدائم: الإضافة تتبعهما
بمعرّفهما.
يضيف التفعيل كذلك:
- الصلاحية
owag_manageإلى دور المسؤول — هي، لاmanage_options، من تفتح الأجندة ومسارات REST الإدارية؛ - الإعدادات الافتراضية في الخيار
owag_settings؛ - المهمة المجدولة الساعية
owag_hourly_event(أول تنفيذ بعد 5 دقائق من التفعيل).
ويُولَّد سرّ HMAC (owag_admin_secret، 64 حرفًا، غير محمَّل تلقائيًا) عند أول إشعار
للمسؤول: هو الذي يوقّع زرَّي تأكيد / رفض في الرسائل.
منح الوصول لشخص آخر
صفحة الأجندة مرئية لكل مستخدم مسجَّل الدخول يملك owag_manage. ولفتح الأجندة أمام
مساعِدة دون تسليمها مفاتيح الموقع:
// À exécuter une fois (mu-plugin, ou plugin de gestion des rôles).
$role = get_role( 'editor' );
if ( $role ) { $role->add_cap( 'owag_manage' ); }
بداية سريعة
بعد التفعيل، توجّهوا إلى OW Agenda (القائمة الرئيسية في لوحة الإدارة). شاشة الإعدادات تسع صفحة واحدة؛ خمس دقائق تكفي.
1. ساعات العمل
لكل يوم من أيام الأسبوع، أدخلوا مجالًا (09:00 → 18:00)، وإن كنتم تتوقفون ظهرًا،
مجالًا ثانيًا (14:00 → 18:30). اليوم المتروك فارغًا يومٌ مغلق. افتراضيًا: من
الاثنين إلى الجمعة، 09:00–18:00؛ والسبت والأحد مغلقان.
تُفسَّر الساعات بالمنطقة الزمنية للموقع (الإعدادات → عام → المنطقة الزمنية). تحقّقوا منها قبل فتح الحجز: كل التواريخ تُخزَّن بالتوقيت المحلي.
2. الخدمة القابلة للحجز عبر الإنترنت
- عنوان الاستشارة — ما يراه الزائر عنوانًا للأداة («استشارة»، «موعد أول»، «تقييم»…)
- مدة الاستشارة — 60 دقيقة افتراضيًا. هذه المدة هي التي تُحجَز في الأجندة.
- دقة الخانات — الفارق بين ساعتين معروضتين (30 دقيقة افتراضيًا). بمدة 60 ودقة 30، تعرضون 9:00 و9:30 و10:00… وأول موعد يُؤخذ عند 9:30 يحذف 9:00 و10:00 من القائمة.
- الفاصل بين موعدين — دقائق تنفُّس تُضاف على جانبَي كل خانة مرشَّحة (0 افتراضيًا).
3. المهل
- المهلة الدنيا للحجز — 24 ساعة افتراضيًا: لا يستطيع زائر أن يحجز لبعد ساعة.
- أفق الحجز — 60 يومًا افتراضيًا: وبعده لا يُعرض شيء.
- الإلغاء عبر الإنترنت من طرف العميل — حتى 24 ساعة قبل الموعد؛ وبعد هذه المهلة، تدعوه صفحة الإلغاء إلى الاتصال هاتفيًا.
4. المصادقة والرسائل
- تأكيد الطلبات: مصادقة يدوية (موصى بها) أو تأكيد تلقائي.
- التحقق المزدوج بالبريد: اتركوه مفعَّلًا. إنه أفضل حماية للإضافة ضد البوتات.
- إشعارات المسؤول تُرسَل إلى: عنوانكم. افتراضيًا، بريد إدارة الموقع.
احفظوا، ثم افتحوا صفحة «حجز موعد»: تعرض الأداة الأيام الأربعة عشر القادمة وخاناتكم الشاغرة. أنتم على الهواء.
5. الأجندة على الهاتف
افتحوا صفحة «الأجندة» على هاتفكم، وسجّلوا الدخول، ثم استعملوا «إضافة إلى الشاشة الرئيسية». تنشر الإضافة بيان تطبيق ويب: عندها تُفتح الصفحة بملء الشاشة، بلا شريط متصفح، كما لو كانت تطبيقًا.
مسار الحجز
من جهة الزائر، صفحة الحجز مكوّن مستقل (JavaScript بلا إطار عمل، أقل من 10 كيلوبايت)
يتحدث إلى النقاط العمومية owag/v1/public/*.
الخطوة 1 — اليوم
عند التحميل، تطلب الأداة التوفّر على 14 يومًا ابتداءً من اليوم، وتعرض شريطًا من الأيام القابلة للنقر. اليوم الذي لا خانة شاغرة فيه يظهر رماديًا وغير قابل للنقر. وزر «عرض التواريخ التالية» يحمّل الأيام الأربعة عشر التالية، وبقدر ما يلزم حتى الأفق المضبوط.
تواريخ الأداة تُبنى بـالتوقيت المحلي للمتصفح، لا بـ UTC أبدًا. هذا تصحيح جاء في الإصدار 1.3.0: سابقًا، كان الزائر الواقع شرق UTC والفاتح للصفحة بُعيد منتصف الليل يرى القائمة تبدأ من اليوم السابق فيخسر يومًا كاملًا من التوفّر.
الخطوة 2 — الساعة
النقر على يوم يستدعي /public/slots ويعرض ساعات البدء الشاغرة. تُعرض الخانة عندما
تتحقق كل هذه الشروط:
- أن تقع ضمن مجال عمل في اليوم المعني؛
- أن يسع الموعد كاملًا (البداية + المدة) قبل الإغلاق؛
- أن يبدأ بعد الآن + المهلة الدنيا؛
- أن يقع اليوم داخل أفق الحجز؛
- ألّا تتقاطع مع أي حدث نشط، بما في ذلك الفاصل — و«الحدث النشط» يشمل المواعيد المؤكَّدة، والطلبات المعلَّقة، وحجب الخانات، والمواعيد الهاتفية المُدخَلة يدويًا، والطلبات التي لم يُتحقَّق منها بعدُ ولا تزال تحتجز خانتها.
وهذه هي النقطة المهمة: كل ما يشغل الأجندة يغلق الخانة على الإنترنت. فالموعد المأخوذ عبر الهاتف والمدوَّن في الأجندة يختفي فورًا من الخانات المعروضة على الموقع.
الخطوة 3 — النموذج
أربعة حقول، ثلاثة منها إلزامية: الاسم (حرفان على الأقل)، والهاتف (8 أرقام على الأقل)، والبريد الإلكتروني (مُتحقَّق منه)، ورسالة حرة. وخانة موافقة GDPR، غير مؤشَّرة افتراضيًا، إلزامية.
ويحمل النموذج فضلًا عن ذلك حقلين غير مرئيين للزائر — فخّ للبوتات وطابع زمني لفتح الصفحة — موصوفين في قسم مكافحة الرسائل المزعجة.
الخطوة 4 — الجواب
بحسب إعداداتكم، ثلاث نتائج ممكنة:
| الإعداد | الحالة المُنشأة | الرسالة المعروضة |
|---|---|---|
| التحقق المزدوج بالبريد مفعَّل (افتراضي) | unverified | «خطوة أخيرة: رسالة أُرسلت إليك للتوّ…» |
| التحقق معطَّل + مصادقة يدوية | pending | «تم استلام طلبك!» |
| التحقق معطَّل + تأكيد تلقائي | confirmed | «موعدك مؤكَّد!» |
وإن أُخذت الخانة بين لحظة عرض القائمة ولحظة إرسال النموذج، يتلقى الزائر رسالة صريحة
(owag_slot_taken، HTTP 409) وتُعاد قائمة الأيام تلقائيًا. ولا يمكن لزائرَين الحصول على
الخانة نفسها: فالمقطع الحرج مُسلسَل بـقفل MySQL مُسمّى، خاص بقاعدة بياناتكم وببادئة
جداولكم، وهو ما يصمد حتى على استضافة مشتركة.
التحقق المزدوج بالبريد الإلكتروني
هذه هي الميزة التي تميّز OW Agenda عن نموذج اتصال متنكّر.
عندما يكون التحقق المزدوج بالبريد مفعَّلًا (الإعداد email_verify، مفعَّل
افتراضيًا):
- يُسجَّل الطلب بالحالة
unverified. ولا يُشعَر المسؤول إطلاقًا. ولا يظهر الموعد كطلب يحتاج معالجة. - يتلقى الزائر رسالة «خطوة أخيرة…» تحمل زر مصادقة.
- تُحتجَز الخانة 45 دقيقة. وبعد هذه المهلة بلا مصادقة، تعود متاحة للجميع — فلا يستطيع بوت تجميد يوم عملكم.
- النقر على الزر يفتح صفحة مستقلة (
?owag_verify=<uid>) تعرض ملخص الموعد وزرًا يقول «أؤكّد طلبي». والمصادقة لا تنطلق إلا بالنقر: فبرامج مكافحة الفيروسات وماسحات الروابط التي تزور عناوين الرسائل لا تصادق على شيء. - عندئذ يصير الطلب
pending(مصادقة يدوية) أوconfirmed(تأكيد تلقائي)، ويتلقى العميل الرسالة المقابلة، وعند تلك اللحظة فقط ينطلق إشعار المسؤول. - أما الطلبات التي لم تُصادَق قط فتُحذف بعد 48 ساعة بواسطة المهمة الساعية. ولا تترك أي أثر ولا أي بيانات شخصية.
وإن كانت الخانة قد أُخذت من غيره بين الإرسال والنقر (أي بعد انقضاء نافذة الـ45 دقيقة)، أعادت المصادقة خطأً واضحًا وأُلغي الطلب — فلا موعد وهمي على خانة مشغولة أبدًا.
النتيجة العملية: عنوان بريد غير صالح لا ينتج أي موعد. لم تعودوا تضيّعون وقتكم في معاودة الاتصال بأرقام وهمية. والثمن نقرة إضافية على العميل؛ وتُظهر التجربة أن نسبة التخلي أدنى بكثير من نسبة المواعيد الزائفة التي يتفاداها.
أما الحجوزات الناتجة عن دعوة جلسة فتتخطى هذه الخطوة: فالعميل قد أثبت أصلًا أنه يقرأ بريده حين نقر على الرابط الخاص الذي وصله.
مصادقة الطلبات
المصادقة اليدوية (موصى بها)
الإعداد الافتراضي: auto_confirm = 0. يصل الطلب المُتحقَّق منه بالحالة pending. ولا
شيء يُوعَد به العميل ما دمتم لم تفعلوا شيئًا — لكن الخانة محجوزة مسبقًا، فلا أحد يسبقكم
إليها.
تتلقون رسالة «للتأكيد: <العميل> — <التاريخ>» تحتوي الملخص الكامل (الخدمة، والتاريخ، والاسم، والهاتف، والبريد، ورسالة العميل) وزرّين:
- تأكيد الموعد — بلون سماوي
- رفض — بإطار أحمر
أزرار الرسالة: روابط موقَّعة بـ HMAC
هذان الزران لا يطلبان أي تسجيل دخول. إنهما يشيران إلى موقعكم بثلاث معطيات:
owag_admin_action (confirm أو refuse)، وuid (المعرّف العشوائي للموعد)، وsig،
وهي توقيع HMAC-SHA256 لـ uid|action محسوب بالسرّ الخاص بالموقع
(owag_admin_secret).
- يُتحقَّق من التوقيع قبل عرض أدنى تفصيل: فالرابط المتلاعَب به لا يعرض سوى «رابط غير صالح».
- تستعمل المقارنة
hash_equals()— فلا تسريب للمعلومات عبر زمن الاستجابة. - تعرض الصفحة ملخصًا وزرًا؛ والإجراء لا يُنفَّذ إلا بالنقر. فماسح الروابط لا يؤكّد شيئًا نيابة عنكم.
- والطلب المعالَج سابقًا يعيد «سبقت معالجته (الحالة: …)» بدل رسالة خطأ.
التأكيد يرسل تلقائيًا رسالة التأكيد إلى العميل، مع ملف التقويم. والرفض يرسل رسالة الإلغاء. وفي الحالتين، تكون الأجندة محدَّثة قبل أن تعيدوا هاتفكم إلى جيبكم.
تبقى هذه الروابط صالحة ما دام الموعد معلَّقًا. وإن حذفتم الخيار
owag_admin_secret(استعادة جزئية لقاعدة البيانات، أو تنظيف يدوي)، توقفت كل الروابط المُرسَلة سابقًا عن العمل: إذ يُولَّد سرّ جديد ولا تعود التواقيع القديمة مطابقة. عندئذ مرّوا عبر صفحة الأجندة.
التأكيد التلقائي
الإعداد auto_confirm = 1. تُؤكَّد الخانة الشاغرة فورًا: فيتلقى العميل تأكيده مع ملف
التقويم، وتتلقون أنتم إشعار «موعد جديد». يُستحسن قصر هذا الوضع على الأنشطة التي تكون
فيها كل خانة مفتوحة خانةً مقبولة.
ويتركّب الإعدادان مع التحقق المزدوج: فحتى في التأكيد التلقائي، يمرّ الطلب أولًا بحالة
unverified إن كان email_verify مفعَّلًا.
صفحة الأجندة
صفحة «الأجندة» ([ow_agenda_admin]) هي أداة كل يوم. وهي مصمَّمة للهاتف أولًا:
أزرار عريضة، وعمود واحد، ولا جدول يحتاج تمريرًا أفقيًا، وتعمل بالطبع بالجودة نفسها على
شاشة حاسوب.
وهي محمية: الزائر غير المسجَّل يرى رابط تسجيل دخول، والمستخدم المسجَّل بلا صلاحية
owag_manage يرى «الوصول مقصور على الفريق». والصفحة noindex,nofollow: فلن تجدوها في
Google أبدًا.
تبويب الأجندة — عرض اليوم
يوم العمل الجاري، حدثًا بحدث، مع الساعة، والمدة، والعنوان، والحالة، والمصدر (الموقع، دعوة، إدخال يدوي). تنقّل من يوم إلى آخر، وزر اليوم. وعلى كل موعد:
- التفاصيل — بيانات اتصال العميل، والرسالة التي تركها عند الحجز، وكتلة ملاحظات داخلية (لا يراها العميل أبدًا) تُحفَظ دون إرسال أي رسالة
- تأكيد ✓ — لطلب لا يزال معلَّقًا؛ فتنطلق رسالة التأكيد
- تم / تخلَّف عن الحضور (no-show) — حالتا نهاية الموعد
- نقل — تاريخ جديد، وساعة جديدة، ومدة قابلة للتعديل. تسألكم الإضافة إن كان ينبغي إخطار العميل بالبريد؛ فإن قلتم نعم، تلقّى تأكيدًا محدَّثًا وملف تقويم يصحّح الحدث الموجود أصلًا على هاتفه
- إلغاء الموعد — مع خيار إخطار العميل أو عدمه، هنا أيضًا
- حذف الحجب — لخانة محجوبة
وحين يُحدث إجراءٌ تقاطعًا (نقل إلى خانة مشغولة)، تنبّه الإضافة وتطلب التأكيد: ويمكنكم الفرض. إنه جدولكم، وأنتم أدرى بما تفعلون — فالحاجز مانع أمام الزوار وحدهم.
إضافة موعد أو حجب
زر «إضافة موعد» يفتح بطاقة واحدة تخدم استعمالين:
- موعد — العميل اتصل بكم. الاسم، والهاتف، والبريد (اختياري)، والخدمة، والتاريخ، والساعة، والمدة، والملاحظات الداخلية. وإن أدخلتم عنوان بريد وأشّرتم على الخيار، تلقّى العميل التأكيد مع ملف التقويم، كما لو كان قد حجز عبر الإنترنت. والموعد يغذّي كذلك دليل العملاء.
- حجب (إجازة، استراحة…) — سبب، وتاريخ، وساعة، ومدة. لا رسالة، ولا بطاقة عميل، لكن الخانة تختفي فورًا من الحجز عبر الإنترنت. هكذا تُوضع إجازة أسبوع، أو دورة تدريبية، أو خانة الظهيرة التي لا تريدون أن تُحجَز.
تبويب «معلَّق»
قائمة الطلبات المُتحقَّق منها التي تنتظر قراركم، في كل الأيام مجتمعة (المواعيد المنقضية مستثناة). زران في كل سطر: تأكيد ورفض. إنها أول شاشة تُفتح صباحًا.
حيلة: الأجندة كتطبيق
على صفحة الأجندة، تعلن الإضافة بيان تطبيق ويب (?owag_manifest=1) باسم الموقع،
وأيقونته، ولون سمته. وعلى Android كما على iOS، تقترح قائمة المتصفح حينها «إضافة إلى
الشاشة الرئيسية»: فتحصلون على أيقونة، وعلى إقلاع بملء الشاشة، ولا تعودون تكتبون
الرابط أبدًا.
دليل العملاء
التبويب الثالث في صفحة الأجندة دليل خفيف، بسيط عن قصد: اسم، وهاتف، وبريد إلكتروني، وملاحظات حرة.
- يمتلئ من تلقاء نفسه. فما إن يُصادَق على موعد (حجز عبر الإنترنت مُتحقَّق منه، أو جلسة بدعوة، أو إدخال يدوي)، حتى يُقارَن العميل ببطاقة قائمة عبر بريده، ثم عبر هاتفه. فإن لم يكن موجودًا أُنشئت البطاقة؛ وإن كان موجودًا فلا تُملأ إلا الحقول الفارغة — فالإضافة لا تدهس أبدًا ما أدخلتموه بأيديكم.
- بحث بالاسم أو الهاتف أو البريد الإلكتروني (200 بطاقة معروضة كحد أقصى).
- إنشاء وتحرير يدويان، مع حقل ملاحظات للتاريخ الطبي، والتفضيلات، والاحتياطات الواجبة.
- اختصاران في كل بطاقة: موعد جديد (يملأ مسبقًا بطاقة الإدخال اليدوي) ودعوة لجلسة (تملأ الدعوة مسبقًا).
- حذف بطاقة: المواعيد المنقضية تبقى في الأجندة، ولا تختفي إلا بطاقة الدليل.
هذا الدليل ليس CRM ولا يدّعي ذلك. إنه يجيب عن سؤال واحد: «من هي، للتذكير، سيدة الساعة الثانية؟»
دعوات الجلسات
حالة الاستعمال: عميل قائم يستحق جلسة — متابعة، أو تصحيحًا، أو خدمة بيعت في مكان آخر — وتريدون أن يختار تاريخه دون المرور بالحجز العمومي، ضمن خدمة ومدة تحدّدونهما.
من تبويب الدعوات:
- أدخلوا اسم العميل، وعنوان الجلسة (إلزاميان)، ومدتها (من 15 إلى 720 دقيقة)، وإن شئتم بريده وهاتفه.
- اختاروا صلاحية الرابط: 60 يومًا افتراضيًا، من 1 إلى 365.
- أشّروا عند الاقتضاء على «إرسال الرابط إلى العميل بالبريد» — فيتلقى العميل رسالة «اختر تاريخ جلستك» تحمل العنوان، والمدة، وتاريخ الانتهاء، وزرًا.
- وإلا، فانسخوا الرابط وأرسلوه عبر SMS أو WhatsApp، أو بزر المشاركة الأصلي في الهاتف.
يشير الرابط إلى صفحة الحجز عندكم مع رمز خاص (?owag_invite=<token>، 32 حرفًا
ست عشريًا مأخوذة من مولّد تشفيري). وعندها تعرض الأداة عنوان الجلسة ومدتها، لا
الاستشارة القياسية. ويملأ العميل النموذج القصير نفسه.
القواعد التي تحكم رابط الدعوة:
- استعمال واحد. فما إن تُحجَز الجلسة، حتى يجيب الرابط «سبق استعمال هذا الرابط» (HTTP 410). ولا يمكن لفتحتين متزامنتين بالرمز نفسه أن تنشئا جلستين: إذ يجري استهلاك الرمز تحت القفل نفسه الذي يحمي حجز الخانة.
- مؤرَّخ. بعد انتهاء الصلاحية، يدعو الرابط إلى معاودة الاتصال بكم (HTTP 410).
- بلا تأكيد مزدوج. فالعميل قد تلقى هذا الرابط عبر قناة تتحكمون فيها: وتُنشأ الجلسة
مباشرة بحالة
confirmed، مع تأكيد وملف تقويم. - يُعاد تدويره عند الإلغاء. فإن أُلغيت الجلسة (منكم أو من العميل)، تُحرَّر الدعوة: فيعود الرابط نشطًا وتعود الدعوة إلى قائمة الدعوات المفتوحة. هذا تصحيح جاء في الإصدار 1.3.0 — إذ كان الإلغاء سابقًا يقتل الرابط نهائيًا فيلزم إنشاء غيره.
- قابل للإبطال. حذف دعوة من القائمة يعطّل الرابط فورًا.
رسائل البريد الإلكتروني وملف التقويم (ICS)
الرسائل المعامَلاتية
كل الرسائل بصيغة HTML، على تخطيط جدولي متوافق مع Gmail وOutlook: بطاقة بيضاء على خلفية
فاتحة، واسم الموقع في الترويسة، وإطار ملخّص بحاشية سماوية، وأزرار على شكل أقراص.
وتُرسَل عبر wp_mail()، فهي تحترم إذن إعداد SMTP عندكم وفلاتر WordPress المعتادة.
| الرسالة | المستلِم | المُطلِق |
|---|---|---|
| مصادقة الطلب | العميل | حجز عبر الويب حين يكون التحقق المزدوج مفعَّلًا |
| تم استلام الطلب | العميل | طلب مُصادَق عليه، ينتظر قراركم |
| الموعد مؤكَّد | العميل | تأكيد (يدوي، أو تلقائي، أو إدخال إداري، أو نقل) — مع مرفق ICS |
| الموعد أُلغي | العميل | إلغاء من العميل، أو رفض أو إلغاء من المسؤول — مع مرفق ICS إلغاء |
| تذكير | العميل | تلقائيًا، بين 12 و36 ساعة قبل الموعد |
| اختر تاريخ جلستك | العميل | إنشاء دعوة مع إرسال بالبريد |
| للتأكيد: … | المسؤول | طلب جديد مُتحقَّق منه في الانتظار — مع زرَّي تأكيد / رفض |
| موعد جديد: … | المسؤول | موعد جديد مؤكَّد (تأكيد تلقائي، أو دعوة) |
| إلغاء من العميل: … | المسؤول | العميل ألغى بنفسه من رسالته |
عنوان إشعار المسؤول هو الإعداد notify_email (وافتراضيًا بريد إدارة الموقع). ورسائل
العميل لا تنطلق إلا إذا كان العنوان صالحًا، ولا تنطلق أبدًا في حالة حجب خانة.
ورسالتا التأكيد والإلغاء تذكّران العميل بأنه يستطيع الإلغاء عبر الإنترنت، مع عدد ساعات الإشعار المضبوطة فعليًا.
مرفق ICS
ملف .ics المرفق بالتأكيدات هو ما يحوّل رسالة بريد إلى موعد حاضر فعلًا في هاتف العميل:
حركة واحدة على المرفق، فيصير الحدث في تقويمه، بتذكيره الخاص.
الملف مطابق لـ RFC 5545 ويحتوي ما يلزم كي تعالج برامج التقويم (Google، وOutlook، وApple) التحديثات معالجة صحيحة:
METHOD:REQUESTعند الإنشاء وعند كل تعديل للتوقيت، وMETHOD:CANCELعند الإلغاء؛UIDثابت مشتق من معرّف الموعد ومن نطاق الموقع — وهو الذي يتيح للتعديل أن يستبدل الحدث القائم بدل إنشاء حدث ثانٍ؛SEQUENCEتصاعدي مشتق من تاريخ آخر تعديل، مردودًا إلى لحظة مطلقة: فالانتقال إلى التوقيت الشتوي لا يمكنه أن يُرجع رقم التعديل إلى الوراء (والإلغاء يضيف 1، ليبقى أعلى من تعديل وقع في الثانية نفسها)؛ORGANIZER(عنوان الإشعار عندكم) وATTENDEE(العميل)؛DTSTART/DTENDبتوقيت UTC، وSTATUS:CONFIRMEDأوSTATUS:CANCELLED؛- طيّ الأسطر عند 75 بايت دون قطع أي محرف UTF-8 أبدًا.
ومن ثمّ فإن نقل موعد مؤكَّد يرسل ملفًا جديدًا يصحّح الحدث الموجود عند العميل؛ والإلغاء يرسل ملفًا يحذفه. هذا تصحيح جاء في الإصدار 1.3.0: إذ كان التوقيت القديم يبقى سابقًا في تقويم العميل.
ويُكتب الملف في المجلد المؤقت للخادم — لا في wp-content/uploads/ أبدًا، فهو مُقدَّم
للعموم — عبر واجهة ملفات WordPress، ويُمحى فور انتهاء الإرسال، بما في ذلك عند فشله.
إنه يحمل اسم العميل وبريده: فلا شأن له بالبقاء على القرص ثانية واحدة زيادة عن اللزوم.
التذكيرات والمهمة المجدولة
مهمة مجدولة واحدة، owag_hourly_event، تُنفَّذ كل ساعة. وهي تقوم بثلاثة أشياء.
1. تذكير اليوم السابق
ينطلق التذكير حين يبدأ الموعد بعد 12 إلى 36 ساعة. وليست هذه نافذة ساعية ثابتة، وذلك عن قصد: فـ WP-Cron لا يُطلَق إلا عند زيارة صفحة. وموقع بلا حركة مساءً كان يخسر نهائيًا تذكيرات اليوم التالي. أما بنافذة منزلقة، فينطلق التذكير عند أول تنفيذ متاح ويستدرك تأخره.
الشروط: أن يكون الموعد مؤكَّدًا، وأن يحمل عنوان بريد، وألّا يكون حجبًا، وألّا يكون التذكير قد أُرسل من قبل.
ويُحجَز كل تذكير بشكل ذرّي قبل الإرسال (عبر UPDATE مشروط على العَلَم
reminder_sent): فتنفيذان متداخلان لـ cron لا يمكنهما إرسال الرسالة نفسها مرتين. وإن
فشل الإرسال، رُدّ الحجز وأُعيدت محاولة التذكير في التنفيذ التالي. وقفل قصير (10 دقائق)
يمنع فضلًا عن ذلك أن يجتاز مروران متزامنان القائمة نفسها.
ونقل موعد يعيد العَلَم إلى الصفر: فيتلقى العميل تذكيرًا للتوقيت الجديد كما ينبغي.
2. حذف الطلبات غير المُصادَق عليها
تُحذف الطلبات التي بقيت unverified أكثر من 48 ساعة. (أما خانتها فقد رُدّت إلى
قائمة المتاح بعد 45 دقيقة.)
3. إخفاء الهوية وفق GDPR
انظروا قسم GDPR.
شبكة الأمان
قد يختفي حدث WP-Cron: تحسين لقاعدة البيانات، أو استعادة جزئية، أو إضافة تنظيف، أو
تصفير للخيار cron. ويتحقق OW Agenda عند كل تحميل من أن المهمة مجدولة فعلًا، ويعيد
جدولتها إن لم تكن كذلك. فالتذكيرات، والحذف، والاحتفاظ لا يمكن أن تتوقف في صمت.
استعمال cron نظامي حقيقي
يعتمد WP-Cron على حركة الزوار. وعلى موقع قليل الزيارة، فضّلوا cron نظاميًا:
// wp-config.php
define( 'DISABLE_WP_CRON', true );
# crontab -e — toutes les 15 minutes
*/15 * * * * curl -s https://example.com/wp-cron.php?doing_wp_cron > /dev/null
مكافحة الرسائل المزعجة وحدود المعدل
الحجز عمومي بطبيعته: لا حساب، ولا تسجيل دخول، ولا nonce ممكن. لذلك تعمل خمس حمايات مستقلة قبل ذلك.
1. فخّ البوتات (honeypot)
يحتوي النموذج على حقل نصّي خارج الشاشة، غير مرئي لإنسان ومغرٍ لبوت. ويجب أن يكون الحقل موجودًا وفارغًا. فالحقل المملوء — أو الغائب، وهو ما يفضح إرسالًا مباشرًا إلى الـ API — يعيد رفضًا جافًا (HTTP 403)، بلا تفصيل قابل للاستغلال.
2. زمن التعبئة
تنقل الصفحة الطابع الزمني لعرضها. والإرسال الوارد بعد أقل من 4 ثوانٍ من التحميل يُرفض. وهنا أيضًا، الطابع الزمني الغائب أو المشوَّه رفضٌ: ففي الإصدار 1.3.0، صار هذان الحقلان إلزاميين — إذ كان إغفالهما يعطّل الفحص ببساطة تامة.
3. الحصة الساعية لكل عنوان
5 حجوزات في الساعة لكل نطاق. ففي IPv4، النطاق هو العنوان؛ أما في IPv6، فهو البادئة /64 — إذ يملك المهاجم فيها 2⁶⁴ عنوانًا، وعدّ العنوان الكامل لن يحدّ شيئًا.
ولا يُزاد العدّاد إلا بعد التحقق من صحة النموذج: فالزائر الذي يخطئ في بريده ثلاث مرات متتالية لم يعد يُحظر ساعة كاملة بلا سبب.
4. السقف اليومي للطلبات غير المُتحقَّق منها
يمكن لـ 3 طلبات في انتظار المصادقة بالبريد على الأكثر أن تحتجز خانة في اليوم الواحد. فبلا هذا السقف، كانت بضع إرساليات تكفي لتجميد كل خانات تاريخ ما. وبعده، يتلقى الزائر دعوة إلى إعادة المحاولة لاحقًا أو إلى اختيار يوم آخر (HTTP 429).
5. قفل الحجز
يجري حجز الخانة واستهلاك الدعوة داخل مقطع حرج مُسلسَل بـقفل MySQL مُسمّى، اسمه مشتق من قاعدة بياناتكم ومن بادئة جداولكم (فموقعان على الاستضافة المشتركة نفسها لا يعطّل أحدهما الآخر). وإن لم يُحصَّل القفل خلال 5 ثوانٍ، أعاد الطلب HTTP 503 صريحًا بدل موعد مزدوج.
وهذا القفل يحلّ، منذ الإصدار 1.3.0، محلّ معاملة كانت تقوم على SELECT مجرَّد: فتحت
REPEATABLE READ، لا تضع هذه القراءة أي قفل صفّي، وكان بإمكان زائرين متزامنين حجز
التوقيت نفسه.
وأما التحقق المزدوج
فهو الطبقة الأنجع: إذ حتى لو تخطّى بوتٌ كل ما سبق، فإنه لا يبلغ أجندتكم ولا صندوق بريدكم أبدًا، لأن إشعار المسؤول لا يُرسَل إلا بعد النقر على رابط وصل بالبريد. انظروا التحقق المزدوج بالبريد الإلكتروني.
GDPR
أجندة مواعيد تعالج بيانات صحية محتملة، وبيانات اتصال، وسجلات تاريخية. لذلك جاءت الإعدادات الافتراضية متحفّظة عن قصد.
الموافقة
خانة الموافقة إلزامية وغير مؤشَّرة افتراضيًا في النموذج العمومي: «أوافق على استعمال بيانات اتصالي لإدارة موعدي.» والحجز بلا موافقة يُرفض على الخادم، لا في المتصفح فحسب.
التقليل من البيانات
- لا يُخزَّن أي عنوان IP في قاعدة البيانات. فالحصة الساعية تستعمل عدّادًا مؤقتًا (transient) مفتاحه بصمة للنطاق الشبكي، ويُمحى بعد ساعة.
- لا user agent، ولا أداة تتبّع، ولا كوكيز تضعها الإضافة.
- لا تُرسَل أي بيانات إلى خدمة طرف ثالث: لا CDN، ولا نداء خارجي، ولا قياس عن بُعد.
- الطلبات التي لم تُصادَق قط تُحذف بعد 48 ساعة — فلا تترك وراءها أي بيانات.
- المواعيد الملغاة لم تعد تُعاد من واجهة الأجندة: فالبيانات الشخصية غير الضرورية لا تتنقل.
إخفاء الهوية التلقائي
الإعداد retention_months، 24 شهرًا افتراضيًا. كل ساعة، تُخفى هوية المواعيد
المنتهية منذ مدة أطول من ذلك، في مكانها: فيُستبدل بالاسم «مجهول الهوية»، ويُفرَّغ
الهاتف والبريد، وتُمحى رسالة العميل والملاحظات الداخلية. ويبقى السطر، وهو ما يحفظ
التاريخ الإحصائي للنشاط (كم موعدًا، ومتى، ولأي خدمة) دون الاحتفاظ بأدنى بيانات شخصية.
ضعوا 0 لتعطيل إخفاء الهوية التلقائي — عن معرفة بالعواقب.
للملاحظة: يستهدف إخفاء الهوية المواعيد الحاملة لعنوان بريد إلكتروني. أما موعد أُدخل يدويًا بلا بريد (اسم وهاتف فقط) فلا يبلغه الحذف التلقائي؛ احذفوه يدويًا إن كانت سياسة الاحتفاظ عندكم تقتضي ذلك.
حق الوصول وحق المحو
لا تربط الإضافة مصدِّرات/ماحيات WordPress الأصلية (فهذه تشتغل على حسابات المستخدمين، والعملاء لا حسابات لهم). ولمعالجة طلب:
- الوصول — ابحثوا عن الشخص في دليل العملاء (بالاسم أو الهاتف أو البريد) وتصفّحوا
مواعيده في الأجندة. ويستطيع مسؤولو قواعد البيانات كذلك الاستعلام مباشرة من
{prefix}_owag_appointmentsو{prefix}_owag_customersعلى العمودcustomer_email/email. - المحو — احذفوا البطاقة من الدليل، ثم ألغوا المواعيد المعنية أو اتركوها تُخفى
هويتها. والحذف الفوري يجري في قاعدة البيانات على
customer_email.
-- Effacement manuel des données personnelles d'une personne (adaptez le préfixe).
UPDATE wp_owag_appointments
SET customer_name = 'Anonymisé', customer_phone = '', customer_email = '',
customer_msg = NULL, admin_notes = NULL
WHERE customer_email = 'personne@example.com';
DELETE FROM wp_owag_customers WHERE email = 'personne@example.com';
سجل أنشطة المعالجة
لسجلكم: الغاية إدارة المواعيد؛ والأساس القانوني الموافقة (الحجز عبر الإنترنت) أو تنفيذ عقد / تدابير سابقة للتعاقد (موعد مأخوذ عبر الهاتف)؛ والفئات الهوية، وبيانات الاتصال، ومحتوى الرسالة، والملاحظات الداخلية؛ والمدة 24 شهرًا افتراضيًا، ثم إخفاء الهوية؛ والمستلمون لا معالج فرعي خارج مستضيفكم وخدمة إرسال بريدكم.
الشورت-كود
[ow_agenda] — حجز الزائر
يُوضَع في صفحة عمومية. لا خصائص له: فالخدمة والمدة والساعات تأتي من الإعدادات. ويحمّل الشورت-كود ورقة أنماطه وسكربته (أقل من 12 كيلوبايت إجمالًا، بلا تبعيات) ويعرض حاوية تمتلئ بعد أول نداء إلى الـ API.
[ow_agenda]
وهذا الشورت-كود هو الذي يفسّر معطى الرابط ?owag_invite=<token>: فالصفحة نفسها تخدم
الاستشارة القياسية والجلسات بالدعوة.
[ow_agenda_admin] — صفحة الإدارة
يُوضَع في صفحة مخصَّصة. لا خصائص له. ويدير الشورت-كود الوصول بنفسه:
- زائر غير مسجَّل → رابط تسجيل دخول يعيده إلى الصفحة؛
- مسجَّل بلا صلاحية
owag_manage→ رسالة وصول مقيَّد؛ - مسجَّل بالصلاحية → الواجهة الكاملة (الأجندة، والمعلَّق، والعملاء، والدعوات).
[ow_agenda_admin]
وتُنشأ الصفحتان تلقائيًا عند التفعيل بهذين الشورت-كودين. وإن كنتم قد حذفتموهما، فيكفي
إنشاء صفحة ولصق الشورت-كود فيها — على أن تحدّثوا حينئذ الخيار owag_page_admin (أو
owag_page_booking) كي تشير روابط الرسائل والبيان إلى المكان الصحيح.
الصفحات المستقلة
أربعة معطيات في الرابط، تُلتقَط في أي صفحة من الموقع، تنتج صفحات مستقلة تُقدَّم خارج
القالب (عرض بأدنى حدّ، noindex,nofollow):
| الرابط | الدور |
|---|---|
?owag_verify=<uid> | مصادقة الطلب من طرف العميل (تأكيد مزدوج) |
?owag_cancel=<uid> | إلغاء عبر الإنترنت من طرف العميل |
?owag_admin_action=confirm|refuse&uid=…&sig=… | تأكيد / رفض من رسالة المسؤول |
?owag_manifest=1 | بيان تطبيق صفحة الأجندة |
وهذه الصفحات لا تنفّذ أي إجراء عند التحميل: بل تعرض ملخصًا وزرًا. فلا بوت، ولا مضاد فيروسات بريدي، ولا prefetch من متصفح، يستطيع تأكيد موعد أو إلغاءه نيابة عن إنسان.
REST API
كل النقاط تعيش تحت النطاق owag/v1، في جذر REST المعتاد
(https://example.com/wp-json/owag/v1/…).
النقاط العمومية
بلا استيثاق: فهذه مسارات أداة الحجز والروابط الواصلة بالبريد. وأمنها قائم على رموز عشوائية (32 حرفًا ست عشريًا من مولّد تشفيري)، وعلى تواقيع HMAC، وعلى الحمايات الموصوفة في مكافحة الرسائل المزعجة.
| الطريقة | المسار | المعطيات | الدور |
|---|---|---|---|
GET | /public/days | from (Y-m-d، افتراضيًا: اليوم)، token (دعوة، اختياري) | تعيد label وduration وdays: 14 يومًا ابتداءً من from، كل يوم مع عدد خاناته الشاغرة |
GET | /public/slots | date (Y-m-d، مطلوب)، token | تعيد slots: ساعات البدء الشاغرة (HH:MM) |
POST | /public/book | date، time، name، phone، email، message، consent، website (فخّ)، ts (طابع زمني)، token | تنشئ الطلب؛ وتعيد status والرسالة المطلوب عرضها |
POST | /public/verify | uid | تصادق على طلب unverified |
POST | /public/cancel | uid | تلغي موعدًا pending أو confirmed، ضمن حدود مهلة الإشعار |
POST | /public/admin-action | uid، action (confirm|refuse)، sig | تؤكّد أو ترفض من رسالة المسؤول (توقيع HMAC) |
مثال على حجز:
curl -X POST https://example.com/wp-json/owag/v1/public/book \
-H 'Content-Type: application/json' \
-d '{
"date": "2026-09-15",
"time": "14:30",
"name": "Marie Dupont",
"phone": "0470 12 34 56",
"email": "marie@example.com",
"message": "Première visite",
"consent": 1,
"website": "",
"ts": 1757942400
}'
{
"ok": true,
"status": "unverified",
"message": "Un dernier geste : un e-mail vient de vous être envoyé…"
}
نقاط الإدارة
جميعها تتطلب الصلاحية owag_manage وnonce REST صالحًا من WordPress
(الترويسة X-WP-Nonce).
| الطريقة | المسار | المعطيات | الدور |
|---|---|---|---|
GET | /admin/agenda | from، to (Y-m-d) | events (المجال المطلوب، بلا الملغاة) وpending (كل الطلبات المعلَّقة القادمة) |
POST | /admin/appointment | kind (manual|block|consult|seance)، date، time، duration (5–720 دقيقة)، label، name، phone، email، notes، force، send_email | تنشئ موعدًا يدويًا أو حجبًا |
POST | /admin/appointment/{id} | status (pending|confirmed|cancelled|done|noshow)، name، phone، email، label، notes، date، time، duration، force، silent | تحدّث، وتنقل، وتغيّر الحالة. silent=1 يمنع كل إرسال بريد |
GET | /admin/customers | q (بحث) | الدليل (200 بطاقة كحد أقصى) |
POST | /admin/customers | name (مطلوب)، phone، email، notes | تنشئ بطاقة |
POST | /admin/customers/{id} | المعطيات نفسها | تحدّث بطاقة |
DELETE | /admin/customers/{id} | — | تحذف بطاقة |
GET | /admin/invites | — | الدعوات المفتوحة (لم تُستعمل قط أو حُرِّرت، وغير منتهية) |
POST | /admin/invites | name (مطلوب)، label (مطلوب)، duration (15–720)، days_valid (1–365، افتراضيًا 60)، email، phone، send_email | تنشئ دعوة؛ وتعيد url وemail_sent |
DELETE | /admin/invites/{id} | — | تحذف دعوة |
GET | /admin/settings | — | الإعدادات السارية |
POST | /admin/settings | جسم JSON (انظروا مرجع الإعدادات) | تحفظ الإعدادات |
وكل مسار يعلن ضبط وصوله صراحةً؛ وتُفحَص الصلاحية قبل تنفيذ أدنى معالجة.
رموز الأخطاء
الأخطاء هي WP_Error قياسية، برمز مقروء وحالة HTTP.
| الرمز | HTTP | المعنى |
|---|---|---|
owag_spam | 403 | فخّ البوتات مملوء، أو الطابع الزمني غائب / حديث جدًا |
owag_rate | 429 | بلغت الحصة الساعية حدّها لهذا العنوان |
owag_day_full | 429 | طلبات كثيرة تنتظر المصادقة في هذا التاريخ |
owag_busy | 503 | لم يُحصَّل قفل الحجز — أعيدوا المحاولة بعد لحظة |
owag_slot_taken | 409 | أُخذت الخانة في هذه الأثناء |
owag_overlap | 409 | كُشف تقاطع (إداري) — أعيدوا الإرسال مع force |
owag_invite_invalid | 404 | رمز دعوة مجهول |
owag_invite_used | 410 | الدعوة استُهلكت من قبل |
owag_invite_expired | 410 | الدعوة منتهية الصلاحية |
owag_too_late | 403 | إلغاء عبر الإنترنت خارج مهلة الإشعار |
owag_bad_sig | 403 | توقيع HMAC غير صالح |
owag_not_found | 404 | الموعد أو البطاقة غير موجودة |
owag_name، owag_phone، owag_email، owag_consent | 400 | حقل غير صالح أو موافقة ناقصة |
owag_bad_input، owag_bad_date | 400 | تاريخ أو ساعة أو إدخال غير صالح |
owag_noop | 400 | طلب تحديث فارغ |
owag_insert | 500 | فشل الكتابة في قاعدة البيانات |
خطافات المطورين
في الإصدار 1.3.1، لا يعرض OW Agenda أي فلتر ولا أي action عمومي: فلا واجهة توسعة
تحتاج توثيقًا، ولا شيء مما سبق ينبغي تخمينه. والخطاف الوحيد القابل للاستعمال هو الحدث
المجدول owag_hourly_event، الذي يمكنكم أن تربطوا به صيانتكم الخاصة:
add_action( 'owag_hourly_event', function () {
// Votre traitement horaire, exécuté juste après celui du plugin.
} );
أما فيما عدا ذلك، فـ REST API هي نقطة التكامل: إنها تعرض تمامًا ما تفعله الواجهة، لا أكثر ولا أقل.
مرجع الإعدادات
كل الإعدادات تسع خيارًا واحدًا، owag_settings. القراءة والكتابة بلغة PHP:
$s = owag_settings(); // réglages effectifs (défauts + enregistrés)
$duree = (int) $s['consult_duration'];
update_option( 'owag_settings', array_merge( owag_settings(), [
'consult_duration' => 45,
'buffer' => 10,
] ) );
القيم الافتراضية
[
// Horaires : clé = 1 (lundi) … 7 (dimanche), valeur = liste de plages [début, fin]
'hours' => [
'1' => [ [ '09:00', '18:00' ] ],
'2' => [ [ '09:00', '18:00' ] ],
'3' => [ [ '09:00', '18:00' ] ],
'4' => [ [ '09:00', '18:00' ] ],
'5' => [ [ '09:00', '18:00' ] ],
'6' => [], // samedi fermé
'7' => [], // dimanche fermé
],
'slot_step' => 30, // granularité des créneaux proposés (minutes)
'consult_duration' => 60, // durée d'une consultation (minutes)
'buffer' => 0, // battement autour de chaque rendez-vous (minutes)
'min_notice_hours' => 24, // délai minimum avant réservation
'max_advance_days' => 60, // horizon maximum de réservation
'auto_confirm' => 0, // 0 = validation manuelle, 1 = confirmation automatique
'email_verify' => 1, // double vérification par e-mail
'cancel_notice_hours' => 24, // préavis d'annulation en ligne par le client
'notify_email' => get_option( 'admin_email' ),
'retention_months' => 24, // anonymisation des RDV passés (0 = jamais)
'consult_label' => 'Consultation',
]
التفصيل
| الإعداد | النوع | حدود الواجهة | الأثر |
|---|---|---|---|
hours | مصفوفة | مجال أو مجالان في اليوم | مجالان يتيحان توقف الظهيرة. ولا يُعتمد المجال إلا إذا كانت البداية والنهاية صالحتين وكانت البداية < النهاية. واليوم الفارغ = مغلق |
slot_step | عدد صحيح | 5 → 120 دقيقة | تباعد الساعات المعروضة. وكلما دقّ، طالت القائمة |
consult_duration | عدد صحيح | 15 → 480 دقيقة | المدة المحجوزة في الأجندة لاستشارة قادمة من الويب |
buffer | عدد صحيح | 0 → 120 دقيقة | فاصل يُطبَّق على جانبَي الخانة المرشَّحة |
min_notice_hours | عدد صحيح | 0 → 168 ساعة | لا يُعرض شيء قبل الآن + هذه المهلة |
max_advance_days | عدد صحيح | 7 → 365 يومًا | الأفق الذي لا تُعرض بعده أي خانة |
auto_confirm | 0 / 1 | — | مصادقة يدوية أو تأكيد فوري |
email_verify | 0 / 1 | — | تأكيد مزدوج بالبريد الإلكتروني (يُترك مفعَّلًا) |
cancel_notice_hours | عدد صحيح | 0 → 168 ساعة | بعده، يُرفض الإلغاء عبر الإنترنت برسالة تدعو إلى الاتصال هاتفيًا |
notify_email | بريد إلكتروني | — | مستلِم إشعارات المسؤول؛ ويُستعمل أيضًا كـ ORGANIZER في ملفات ICS |
retention_months | عدد صحيح | 0 → 120 شهرًا | إخفاء هوية المواعيد المنتهية؛ و0 يعطّله |
consult_label | نص | — | العنوان المعروض على أداة الحجز |
وثمة ثابتان غير قابلين للضبط يستحقان أن يُعرفا، لأنهما يحكمان سلوك التأكيد المزدوج: خانة الطلب غير المُتحقَّق منه تُحتجَز 45 دقيقة، ويُحذف الطلب بعد 48 ساعة.
ويقبل الحفظ عبر REST API المفاتيح نفسها بصيغة JSON؛ والأعداد الصحيحة تُردّ إلى صفر كحدّ أدنى، ولا يُقبل عنوان الإشعار إلا إن كان صالحًا، ويُعاد التحقق من كل مجال ساعات على الخادم.
البيانات المخزَّنة
ثلاثة جداول، كلها ببادئة تثبيتكم. والتواريخ من نوع DATETIME بـالتوقيت المحلي
للموقع، لا بـ UTC أبدًا — وهذا ما يجعل الاستعلامات مقروءة ومقارنات التوقيت بديهية.
{prefix}_owag_appointments
| العمود | النوع | ملاحظات |
|---|---|---|
id | BIGINT | المفتاح الأساسي |
uid | CHAR(32) | رمز عمومي فريد (ست عشري، من مولّد تشفيري). يُستعمل مفتاحًا لروابط المصادقة والإلغاء وإجراء المسؤول، وكـ UID في ICS |
kind | VARCHAR(16) | consult، seance، manual، block |
service_label | VARCHAR(120) | العنوان المعروض |
customer_name، customer_phone، customer_email | VARCHAR | بيانات الاتصال |
customer_msg | TEXT | الرسالة التي تركها العميل |
admin_notes | TEXT | ملاحظات داخلية، لا تُرسَل إلى العميل أبدًا |
start_dt، end_dt | DATETIME | حدّا الخانة (مفهرسة على start_dt) |
status | VARCHAR(16) | unverified، pending، confirmed، cancelled، done، noshow |
source | VARCHAR(16) | web، invite، admin |
invite_id | BIGINT | الدعوة الأصل، عند الاقتضاء |
reminder_sent | TINYINT | عَلَم التذكير (يُردّ إلى 0 عند النقل) |
created_at، updated_at | DATETIME | updated_at يغذّي رقم تعديل ICS |
{prefix}_owag_invites
id، وtoken (CHAR(32)، فريد)، وبيانات اتصال العميل، وservice_label،
وduration_min، وexpires_at، وappointment_id (يُملأ عند استهلاك الرابط، ويُردّ إلى
NULL إن أُلغي الموعد)، وcreated_at.
{prefix}_owag_customers
id، وname، وphone، وemail، وnotes، وcreated_at، وupdated_at. مع فهرس على
name وemail.
وخيار واحد يحمل الإعدادات (owag_settings)؛ أما البقية فتقنية: owag_admin_secret
(سرّ HMAC)، وowag_db_version، وowag_pages_created، وowag_page_booking،
وowag_page_admin.
التدويل
الواجهة كلها قابلة للترجمة: إعدادات الإدارة، وأداة الحجز، وصفحة الأجندة، والصفحات
المستقلة، ورسائل الـ API، وقوالب البريد الإلكتروني. اللغة المصدر هي الإنجليزية؛
وتعلن الإضافة نطاق الترجمة ow-agenda والمسار /languages.
وتُسلَّم الحزمة مع القالب languages/ow-agenda.pot. كما تُقدَّم ترجمة فرنسية كاملة،
أُنتجت انطلاقًا من الصياغة الأصلية للواجهة.
تثبيت ترجمة يدويًا
صرّفوا ملف .po وضعوا ملف .mo في مجلد ترجمات WordPress:
msgfmt ow-agenda-fr_FR.po -o ow-agenda-fr_FR.mo
# puis : wp-content/languages/plugins/ow-agenda-fr_FR.mo
ويحمّل WordPress الكتالوج عند أول سلسلة مترجَمة؛ فلا حاجة إلى أي نداء يدوي.
مطبّان أمام المترجمين
- السلاسل الموجَّهة إلى JavaScript تستعمل علامات مرقَّمة (
%1$s،%2$s) ولا تستعمل%sمجرَّدًا أبدًا: فالمنسّق الصغير المدمج في السكربتات لا يفهم سواها. والعلامة غير المرقَّمة ستُعرض حرفيًا. - سلسلتان هما صيغتا تاريخ في PHP، لا جملتان:
l, F j, Y \a\t g:i aوl j F Y \a\t H:i. ترجموهما بوصفهما صيغتين (بإعادة ترتيب الرموز، وتهريب الأحرف الحرفية بشرطة مائلة عكسية). والفرنسية تستعملl j F Y \à H\hi.
إلغاء التثبيت
تعطيل الإضافة لا يفعل سوى شيء واحد: حذف المهمة المجدولة الساعية. أما بياناتكم وإعداداتكم وصفحاتكم فتبقى سليمة، وكل شيء يعود عند إعادة التفعيل.
حذف الإضافة من WordPress ينفّذ إلغاء التثبيت الكامل:
- حذف الجداول الثلاثة (
owag_appointments،owag_invites،owag_customers)؛ - حذف الخيارات
owag_settings، وowag_admin_secret، وowag_db_version، وowag_pages_created، وowag_page_booking، وowag_page_admin؛ - حذف المهمة المجدولة؛
- سحب الصلاحية
owag_manageمن دور المسؤول.
أما الصفحتان المُنشأتان عند التفعيل فلا تُحذفان — فقد تكونان أُعيدت صياغتهما، ووُضعتا في قائمة، وفُهرستا. ولكم أن تنقلوهما إلى سلة المهملات إن لم تعودوا تريدونهما.
حذف الجداول نهائي. فصدّروا قاعدة بياناتكم قبله إن كان تاريخ المواعيد ذا قيمة عندكم.
استكشاف الأخطاء
لا تظهر أي خانة في صفحة الحجز
راجعوا القائمة بالترتيب:
- هل ساعات اليوم مُدخلة؟ فاليوم الفارغ يوم مغلق.
- المهلة الدنيا (24 ساعة افتراضيًا) تخفي حتمًا اليوم وجزءًا من الغد.
- هل تسع المدة داخل المجال؟ فبمجال 09:00–12:00 ومدة 240 دقيقة، لا يمكن أن توجد أي خانة.
- الأفق: الأيام الواقعة بعد
max_advance_daysلا تُعرض أبدًا. - الفاصل: فاصل واسع على جدول مزدحم قد يغلق كل شيء.
- حجب ربما يغطي اليوم كله. افتحوا صفحة الأجندة على اليوم المعني.
- المنطقة الزمنية للموقع (الإعدادات → عام): منطقة مضبوطة خطأً تزيح كل الحسابات.
الرسائل لا تصل
السبب في الغالب الأعم هو قابلية التسليم، لا الإضافة.
- تأكّدوا من وجود الموعد فعلًا في صفحة الأجندة: فإن كان فيها، فقد أدّت الإضافة عملها.
- ثبّتوا إضافة SMTP (مثل Fluent SMTP أو WP Mail SMTP). فدالة
mail()في PHP ترفضها الغالبية الساحقة من خوادم الاستقبال. - تحقّقوا من SPF وDKIM وDMARC لنطاقكم.
- اختبروا العنوان في OW Agenda → إشعارات المسؤول تُرسَل إلى: فالعنوان غير الصالح يعطّل الإشعارات في صمت.
تذكير اليوم السابق لم ينطلق
يعتمد التذكير على WP-Cron، الذي يعتمد على حركة الزوار. وعلى موقع قليل الزيارة، انتقلوا إلى cron نظامي حقيقي (انظروا التذكيرات والمهمة المجدولة). وتأكّدوا كذلك من أن الموعد مؤكَّد، وأنه يحمل عنوان بريد، وأنه ليس حجبًا.
صفحة الأجندة تعرض «الوصول مقصور على الفريق»
المستخدم المسجَّل لا يملك الصلاحية owag_manage. وهي تُمنح للمسؤولين عند التفعيل؛ أما
للأدوار الأخرى، فانظروا التثبيت.
زر «تأكيد» في الرسالة يعرض «رابط غير صالح»
ثلاثة أسباب ممكنة: الرابط بُتر من طرف برنامج البريد (جرّبوا نسخ الرابط كاملًا)، أو الموعد سبقت معالجته (والرسالة تبيّن ذلك)، أو أُعيد توليد سرّ HMAC للموقع. وفي هذه الحالة الأخيرة، مرّوا عبر صفحة الأجندة: فالروابط القديمة لم تعد قابلة للمصادقة، بحكم التصميم.
موعدان يجتمعان على الخانة نفسها
منذ الإصدار 1.3.0، هذا مستحيل عبر الحجز على الإنترنت. في المقابل، يستطيع المسؤول فرض
تقاطع من صفحة الأجندة (والإضافة تنبّه وتطلب التأكيد): فتأكّدوا أولًا من أن الأمر ليس
فرضًا متعمَّدًا. وتأكّدوا كذلك من أن قاعدة بياناتكم تدعم GET_LOCK() فعلًا — فاستضافة
غريبة الأطوار كانت ستعيد حينها أخطاء 503 عند الحجز، لا نسخًا مكرَّرة.
صفحتا «الحجز» و«الأجندة» لم تُنشآ
لا تُنشآن إلا مرة واحدة، عند أول تفعيل على الإطلاق. وإن كنتم قد حذفتموهما، فأنشئوا
صفحتين بالشورت-كودين [ow_agenda] و[ow_agenda_admin]، ثم وجّهوا إليهما الخيارين
owag_page_booking وowag_page_admin كي تبقى روابط الرسائل صحيحة:
update_option( 'owag_page_booking', 42 ); // ID de votre page de réservation
update_option( 'owag_page_admin', 43 ); // ID de votre page Agenda
ملف التقويم غير مرفق
يُكتب الملف في المجلد المؤقت للخادم عبر واجهة ملفات WordPress. وإن لم يكن هذا المجلد قابلًا للكتابة (حصة، أو صلاحيات، أو استضافة مقفلة)، انطلقت الرسالة بلا مرفق بدل أن تفشل. تحقّقوا من صلاحيات الكتابة ومن مساحة القرص.
الساعات مزاحة بساعة واحدة
الإعدادات → عام → المنطقة الزمنية. اختاروا مدينة (Europe/Brussels) بدل إزاحة UTC
ثابتة: فالمنطقة المُسمّاة وحدها تدير الانتقال إلى التوقيت الصيفي.
الأسئلة الشائعة
هل OW Agenda مجاني فعلًا؟
نعم. GPL-2.0-or-later، ويُنزَّل بصيغة .zip من
https://optionweb.dev/ar/addons/ow-agenda/. لا نسخة Pro، ولا مفتاح ترخيص، ولا ميزة
مقفلة، ولا حساب يُنشأ، ولا حصة مواعيد.
هل تأخذ الخانات على الإنترنت المواعيد المأخوذة عبر الهاتف بالحسبان؟ نعم. فكل حدث في الأجندة — حجز من الويب، أو موعد مُدخل يدويًا، أو جلسة بدعوة، أو حجب — يجعل مجاله الزمني غير متاح للحجز عبر الإنترنت.
هل على العميل إنشاء حساب؟ لا. لا حساب ولا كلمة مرور. فكل إجراءات العميل (المصادقة، والإلغاء) تمرّ عبر روابط خاصة تصله بالبريد الإلكتروني.
هل يمكن إدارة عدة ممارسين أو عدة قاعات؟ لا. يصف OW Agenda جدولًا واحدًا. ولأجندتين مستقلتين، يلزم موقعان (أو موقعان في شبكة multisite).
هل يتزامن مع Google Agenda؟ بالمعنى الذي يهمّ العميل: نعم. فكل تأكيد يحمل ملف تقويم قياسيًا يضيف الحدث — ويحدّثه، ويحذفه — في Google Agenda أو Outlook أو تقويم iPhone. في المقابل، لا تقرأ الإضافة تقاويمكم الخارجية: فارتباطاتكم الشخصية تُدخَل على شكل حجب للخانات.
هل يمكن تحصيل عربون عند الحجز؟ لا، فهذا خارج النطاق الذي تتبنّاه الإضافة.
كم خدمة يمكن عرضها على الإنترنت؟ خدمة واحدة، تختارون عنوانها ومدتها. أما الخدمات الأخرى فتمرّ عبر دعوات الجلسات، التي تترك لكم تحديد العنوان والمدة حالة بحالة. وهذا خيار متعمَّد: فقائمة خدمات على صفحة عمومية تضاعف الطلبات سيئة التوجيه.
هل يعمل على multisite؟ نعم. لكل موقع في الشبكة جداوله الخاصة، وإعداداته الخاصة، وأجندته الخاصة.
هل تبطّئ الإضافة الموقع؟ لا. فلا يُحمَّل أي سكربت خارج الصفحتين المعنيتين؛ وأداة الحجز تزن أقل من 12 كيلوبايت، بلا jQuery ولا إطار عمل. والعمل الدوري كله يسع مهمة ساعية واحدة.
ماذا يحدث إذا لم يصادق عميل على بريده أبدًا؟ تعود خانته متاحة بعد 45 دقيقة ويُحذف طلبه بعد 48 ساعة. ولن تسمعوا عنه شيئًا أبدًا.
أين أجد الدعم؟
- بوابة الدعم: https://optionweb.dev/ar/addons/support/
- البريد الإلكتروني: support@optionweb.dev
- التوثيق: https://optionweb.dev/ar/addons/ow-agenda/
سجل التغييرات
1.3.1
الامتثال
- الصفحات المستقلة للإجراء والتحقق والإلغاء لم تعد تطبع CSS وJavaScript خاصتها في
السطر: فكلاهما صار ملفين مسجَّلين ومُقدَّمين عبر واجهة enqueue في WordPress. ولونا
الزر المتغيّران يمران عبر خصائص CSS مخصَّصة، والسكربت يُهيّأ عبر
wp_localize_script(). - كتالوجات الترجمة لم تعد مدمجة: فلا يُسلَّم سوى
languages/ow-agenda.pot، والفرنسية تُوزَّع عبر حزم لغات WordPress.org التي يغذّيها translate.wordpress.org. - عاد الحد الأدنى لإصدار WordPress إلى 6.3: فلم يعد في الحزمة شيء يعتمد على اكتشاف الترجمات المدمجة الذي أُدخل في 6.7.
REST API
- صار كل مسار إداري يعلن
permission_callbackخاصته صراحةً بدل مشاركتها عبرarray_merge(). وكان فحص الصلاحية مطبَّقًا أصلًا؛ لكنه صار الآن مرئيًا للتحليل الساكن.
بلا أي تغيير في السلوك.
1.3.0
التدويل
- الإضافة صارت قابلة للترجمة بالكامل (النطاق
ow-agenda، والإنجليزية لغةً مصدرًا): الإعدادات، والصفحات العمومية، ورسائل الـ API، ورسائل البريد، وواجهة JavaScript. - تسليم القالب
languages/ow-agenda.potوترجمة فرنسية كاملة.
الأمن
- المعرّفات العمومية للمواعيد ورموز الدعوات صارت تُولَّد بـمولّد تشفيري
(
random_bytes()). وهذه الرموز هي الاستيثاق الوحيد لروابط الإلغاء والمصادقة والدعوة. - فخّ البوتات والطابع الزمني صارا إلزاميين: إذ كان إغفالهما يعطّل فحوص مكافحة الرسائل المزعجة.
- الحصة الساعية صارت مجمَّعة حسب بادئة /64 في IPv6، وسقفٌ يومي يحدّ الطلبات غير المُتحقَّق منها — فلم يعد بوسع زائر واحد احتجاز كل خانات يوم كامل.
- ولا تُخصم الحصة إلا بعد التحقق من صحة النموذج: فالخطأ المطبعي لم يعد يحظر زائرًا شرعيًا لمدة ساعة.
الموثوقية
- الحجوزات المتزامنة على الخانة نفسها صارت مُسلسَلة بـقفل MySQL مُسمّى. فالمعاملة السابقة لم تكن تضع أي قفل صفّي: وكان بإمكان زائرين حجز التوقيت نفسه.
- تذكير اليوم السابق صار يستعمل نافذة منزلقة من 12 إلى 36 ساعة ويستدرك تأخره؛ وكل إرسال يُحجَز بشكل ذرّي، فلم يعد بوسع تنفيذين لـ cron إرسال التذكير نفسه مرتين.
- المهمة الساعية صارت تعيد جدولة نفسها تلقائيًا إن اختفت: فالتذكيرات والحذف وإخفاء الهوية لم يعد بوسعها التوقف في صمت.
- نقل موعد صار يحدّث تقويم العميل (
SEQUENCE، وMETHOD:REQUEST، والمنظّم والمشارك)؛ والإلغاء يرسلMETHOD:CANCELيحذف الحدث. - تواريخ الأداة صارت تُحسب بـالتوقيت المحلي لا بـ UTC: فلا يوم ضائع بعد اليوم على الزوار شرق UTC.
- إلغاء موعد ناتج عن دعوة صار يحرّر الدعوة، فتعود قابلة للاستعمال.
- المواعيد الملغاة والطلبات غير المُصادَق عليها المنتهية لم تعد تُعاد إلى الأجندة: بيانات شخصية أقل في الاستجابات، ولا مواعيد وهمية بعد اليوم.
1.2.1
- امتثال Plugin Check: تهريب المخرجات، وتنظيف المدخلات، وnonce على الإعدادات، وملف readme.
- إلغاء التثبيت صار يحذف كذلك جدول دليل العملاء وسرّ HMAC.
1.2.0
- رسائل HTML (تخطيط على شكل بطاقة، بلون تمييز الموقع) لكل الإشعارات.
- زرّا تأكيد / رفض في رسالة المسؤول (روابط موقَّعة بـ HMAC).
- دليل العملاء: بحث، وإنشاء، وتحرير، وحذف، واختصارات نحو موعد جديد ونحو دعوة.
1.1.0
- تحقق مزدوج بالبريد الإلكتروني ضد البوتات (احتجاز الخانة 45 دقيقة، وحذف بعد 48 ساعة).
1.0.0
- الإصدار الأول: حجز عبر الإنترنت، وأجندة إدارة مصمَّمة للهاتف أولًا، ودعوات جلسات، ورسائل مع ملف تقويم، وتذكير اليوم السابق.
تم البناء بواسطة OptionWeb — Julien Daniel، Châtelet، بلجيكا.