عقد واحد لامتدادات المواقع.
الهوية والإصدارات والثقة
لكل إضافة معرّف بصيغة namespace/name وإصدار حزمة دلالي مستقل وإصدار واجهة برمجية هو "1" تحديدًا. مساحة الأسماء eyivo محجوزة. النوع system أو standard، والمصدر system أو verified أو third_party. في هذا الإصدار تُقبل فقط الحزم ذات المصدر system المعتمدة من المنصة. لا يمكن للحزم منح نفسها حالة إلزامية أو قدرات جديدة.
التثبيت والإعدادات
يمتلك master المخصص سجلات التثبيت. تعمل عمليات التثبيت والتفعيل والتعطيل وإلغاء التثبيت والترقية تحت قفل للموقع وضمن معاملة قاعدة بيانات. تُثبَّت الاعتماديات أولًا، وتُرفض الحزم المفقودة والحلقات والتعارضات والرجوع إلى إصدارات أقدم. لا يمكن تعطيل الاعتماديات النشطة أو الحزم التي تفرضها المنصة أو إزالتها. تتلقى خطافات الترقية previous_version ويمكنها ترحيل إعداداتها ضمن مساحة أسمائها عبر الواجهة البرمجية. تستخدم الإعدادات مخططات قيم مفردة/كائنات معلنة وقيمًا افتراضية، وتُرفض المفاتيح غير المعروفة. يجب أن تعيد معالجات دورة الحياة مصفوفة وألا تعتمد على آثار جانبية خارجية.
إعدادات بنطاق واضح.
لكل موقع ولكل إضافة إعدادات خاصة في قاعدة بيانات master المخصصة. تقرأ لوحة التطبيق وتحفظ عبر Internal API الموثقة عبر WireGuard، ولا تحتفظ ببيانات اعتماد قواعد بيانات master. تستخدم الخدمات المشتركة على plugins.eyivo.com قاعدة بيانات منفصلة لحالة الخدمة.
الحقول المعلنة
50 كحد أقصى
تتشارك حاويات الكائنات والخصائص المتداخلة الإجمالي نفسه.
الإعدادات الكاملة
32 كيلوبايت كحد أقصى
32,768 بايت من JSON مضغوط بترميز UTF-8، بما في ذلك المفاتيح والقيم والقيم الافتراضية.
عمق التداخل
3 مستويات كحد أقصى
تبدأ حقول المستوى الأعلى من المستوى الأول.
يتحقق المضيف من المخططات عند تسجيل الحزمة ويفحص الإعدادات الكاملة عند كل عملية كتابة. تُرفض المفاتيح غير المعروفة. لا تحل عناصر تحكم اللوحة والتحقق في المتصفح محل الفرض من جهة الخادم.
تدعم المخططات السلاسل النصية والأعداد الصحيحة والقيم المنطقية والكائنات ذات الخصائص الصريحة. المصفوفات والخرائط العشوائية ليست أنواع إعدادات. يُقاس طول السلاسل بالبايت. تبدأ حقول المستوى الأعلى من المستوى الأول، وتُحتسب حاويات الكائنات ضمن إجمالي الحقول وعمق التداخل معًا.
تحمل عمليات الحفظ رقم مراجعة إضافات الموقع. تؤدي المراجعة القديمة إلى تعارض بدل الكتابة فوق إعدادات أحدث. أعد التحميل وراجع القيم الحالية قبل إعادة المحاولة. أي تغيير على أي إضافة في الموقع يغيّر هذه المراجعة، بما في ذلك التعطيل وإعادة التفعيل وإلغاء التثبيت وإعادة التثبيت.
عرض حدود مخطط الإعدادات وقواعد الترميز
{
"fields": 50,
"depth": 3,
"bytes": 32768,
"counting": "Every declared property counts, including object containers. Top-level fields are level one. JSON bytes include recursively merged defaults, keys and values, encoded compactly with UTF-8 characters and slashes unescaped.",
"types": [
"string",
"integer",
"boolean",
"object"
],
"policy": "Objects require explicit properties; unknown keys and arrays are rejected. Strings default to 4096 bytes and support min/max up to 32768 bytes. Integer min/max and scalar enum choices are supported. constructor, prototype and __proto__ are reserved keys."
}
صفحاتك. مكوّنات Eyivo.
تمتلك الإضافة تعريفات صفحاتها وتخطيطها ومحتواها المترجم ونماذجها والإجراءات المعلنة. يعرض التطبيق المركزي التعريفات بمكوّنات موثوقة داخل لوحة المستخدم الحالية. يؤدي النقر على إضافة إلى فتح صفحة الدخول المعلنة لها، مثل الإعدادات أو صندوق الطلبات الواردة.
-
أعلن الصفحة
سجّل تعريفات الصفحات وروابط الإعدادات في الحزمة. تشير روابط القائمة الاختيارية إلى صفحات تملكها الإضافة نفسها.
-
اعرضها في التطبيق
يتحقق التطبيق من صلاحية الوصول إلى الموقع ويتلقى تعريفات JSON موثّقة وبيانات من master المخصص. لا يُنقل أي كود PHP أو Blade خاص بالإضافة إلى التطبيق ولا يُنفَّذ فيه.
-
نفّذ على master
تمر الإجراءات المصرح بها عبر Internal API إلى معالجات مقيّدة. يفرض master حالة الإضافة والقدرات والمخططات وحدود الإعدادات.
مجموعة إضافات بمستوى واحد
تظهر الروابط المؤهلة مباشرة تحت الإضافات للموقع المحدد. القوائم الفرعية المتداخلة غير مدعومة. تبقى المجموعة مخفية عندما لا توجد روابط ظاهرة. يزيل التعطيل أو إلغاء التثبيت الروابط ويمنع الوصول المباشر إلى الصفحات والإجراءات، بما في ذلك الطلبات من علامات تبويب مفتوحة مسبقًا. تعيد إعادة التفعيل الروابط المؤهلة.
تفتح الحزم التي لا تحتوي على إعلان لوحة والحزم المعطّلة عرضًا معلوماتيًا من قائمة الإضافات. لا تتيح الحزم المعطّلة صفحات اللوحة أو الإجراءات المسجلة لها. يوفر التحقق البشري صفحة حزمة للقراءة فقط، وتبقى إعداداته الأمنية الافتراضية تحت تحكم المنصة.
عضوية الموقع والإجراءات المعلنة
تعلن الصفحات والإجراءات المخصصة إذن العرض أو الإدارة. يمكن لأعضاء الموقع الحاليين عرض الصفحات المؤهلة وتشغيل إجراءات العرض للقراءة فقط، بينما تتطلب الإدارة دور مالك الموقع. يستمد التطبيق الجهة الموثوقة من العضوية المسجّل دخولها. تتطلب إجراءات حفظ الإعدادات وإعادة تعيينها المدمجة إذن الإدارة، وتستخدم المعالجات المخصصة مخططات معاملات واستجابات معلنة. تسمح كل صفحة بمعرّفات الإجراءات المدرجة فيها فقط.
الإعلانات لا تمنح صلاحيات
تحميل الصفحة للقراءة فقط. لا يمكن لصفحة أو قائمة منح PHP غير مقيّد أو وصول إلى قاعدة البيانات أو مسارات تطبيق عشوائية أو نصوص برمجية داخل اللوحة الموثقة. تستمر الحزم الحالية التي لا تحتوي على إعلان لوحة في العمل. يبقى تثبيت حزم الجهات الخارجية معطّلًا.
المكوّنات المدعومة
ركّب الصفحة باستخدام هذه المكوّنات الموثوقة. ترتبط الحقول بالإعدادات المعلنة أو بمدخلات الإجراءات، وتعرض المعاينات نصًا عاديًا، والجداول للقراءة فقط. لا يدعم HTML الخام أو النصوص البرمجية العشوائية أو التعابير أو العرض الشرطي.
stackgridcardtextalertfieldbuttonpreviewtablesubmission-listsubmission-detail
عرض عقد Panel API ذي الإصدارات وحدوده
{
"api": "2",
"execution": "Plugins own JSON page definitions and restricted master-side handlers. App receives validated localized JSON only; plugin PHP, Blade, JavaScript, CSS, raw HTML and handler paths are not transferred or executed in the panel.",
"capabilities": [
"panel.render",
"panel.action",
"site.settings.read",
"site.settings.write"
],
"components": [
"stack",
"grid",
"card",
"text",
"alert",
"field",
"button",
"preview",
"table",
"submission-list",
"submission-detail"
],
"controls": [
"text",
"textarea",
"number",
"checkbox",
"select"
],
"action_types": [
"configuration.save",
"configuration.reset",
"handler"
],
"permissions": {
"view": [
"owner",
"viewer"
],
"manage": [
"owner"
]
},
"limits": {
"pages": 10,
"menu_links": 10,
"menu_depth": 1,
"actions": 20,
"action_input_bytes": 32768,
"components_per_page": 100,
"component_depth": 8,
"page_file_bytes": 65536,
"response_bytes": 262144,
"table_columns": 10,
"table_rows": 100
},
"configuration": "Configuration.save receives a complete configuration object as input; configuration.reset accepts empty input. Both require manage permission and site.settings.write. All panel actions require panel.action. Custom handlers declare view (read-only even for owners) or manage permission; their input/response use declared bounded schemas. Page loaders are read-only.",
"concurrency": "Every action requires the page revision. The master compares site.plugin_revision under the existing site row lock; stale saves return HTTP 409. Deactivation or uninstall blocks pages/actions and removes derived menus.",
"localization": "Definitions use locale maps with a required en fallback. The master localizes text before sending JSON to app. Field labels and input guidance are required.",
"supported_versions": [
"1",
"2"
],
"submissions": {
"version": "2",
"declaration": "submission-list declares form_type and detail_page; submission-detail declares the same form_type and list_page. Every registered form type requires mutually linked viewable pages and a flat sidebar menu entry to its list.",
"execution": "The host resolves current site/plugin ownership and supplies paginated summaries or a stored historical detail snapshot. App constructs all navigation and action URLs. Package PHP never reaches app.",
"contexts": "Page loaders receive authenticated actor and validated query/record context. Plugin operations cannot override the current site or plugin. Creating records requires an out-of-band host-issued FormProcessingContext unavailable to interpreted package code.",
"state": "Reading, archive and workflow state are independent. Record mutations require the current record_revision; configuration still uses plugin_revision. Viewing never marks a submission read. Viewers may read; existing owner/manage permissions govern mutations."
}
}
النماذج وسجل الطلبات
توفر Forms API 1 سبعة معالجات معتمدة. تتحكم القوالب في التصميم، والإضافات في المخطط والتحقق وسير العمل. تتولى المنصة الحفظ والتحقق البشري والإشعارات.
| إضافة النظام | نوع النموذج |
|---|---|
eyivo/contact | تواصل contact |
eyivo/quote-request | طلب عرض سعر quote_request |
eyivo/product-inquiry | استفسار عن منتج product_inquiry |
eyivo/appointment-request | طلب موعد appointment |
eyivo/course-application | طلب الالتحاق بدورة course_application |
eyivo/reservation-request | طلب حجز reservation |
eyivo/property-inquiry | استفسار عقاري property_inquiry |
نموذج واحد في مواضع متعددة
ينشئ الإعداد UUID افتراضيًا ثابتًا لكل موقع وإضافة ونوع. إعادة استخدام الموضع تحفظ الهوية، وإنشاء نموذج مسمّى يمنحه UUID آخر. تغيير القالب وإعادة التثبيت يحفظان السجل. تستخدم القوالب أسماء ربط قابلة للنقل ويختار العميل نموذجًا محفوظًا دون إدخال UUID.
{
"plugins": ["eyivo/contact", "eyivo/quote-request"],
"forms": {
"contact": {"plugin":"eyivo/contact","type":"contact","schema_version":1,"instance":"default"},
"quote": {"plugin":"eyivo/quote-request","type":"quote_request","schema_version":1,"instance":"default"}
}
}
استخدم forms.contact في التواصل وcomponent.form في أقسام النماذج. استبدل form_preset بـ form_binding. اعرض الحقول والإرشادات وaction وCSRF وform_context الموقّع وverification_html المقدّمة. تُقبل تغييرات العرض المعلنة والحقول الإضافية المسموحة فقط؛ لا يجوز إضعاف التحقق الإلزامي.
forms.contact · component.form · form_binding · form.fields · form.action · form.csrf_token · form.context · form.verification_html
عرضان لسجل واحد
يعرض الصندوق المشترك وصفحات قائمة وتفاصيل الإضافة السجلات نفسها. تتطلب حزم النماذج Panel API 2 ومكوّني submission-list وsubmission-detail ورابط قائمة بمستوى واحد. يبقى Panel API 1 مدعومًا. العرض لا يضع علامة مقروء؛ حالات القراءة والأرشفة والعمل منفصلة والتعديل يستخدم record_revision. تعطيل الإضافة يخفي صفحاتها؛ يبقى التاريخ في الصندوق المشترك.
{"components":[{"type":"submission-list","form_type":"contact","detail_page":"submission"}]}
{"components":[{"type":"submission-detail","form_type":"contact","list_page":"submissions"}]}
البيانات والأذونات
تُحفظ السجلات في قاعدة محتوى الموقع وتشارك حد الحزمة max_db_bytes مع المحتوى الآخر. لا توجد حصة إضافية للعدد أو مدة الاحتفاظ. تبقى الإعدادات في قاعدة master بحدود 50 حقلًا و32 كيلوبايت وثلاثة مستويات. الأعضاء يشاهدون والتعديل يتطلب صلاحية المالك أو الإدارة. لا وصول مباشر للإضافات إلى القاعدة ولا نقل PHP أو Blade إلى التطبيق.
حفظ موثّق وإشعارات
تتحقق المنصة من السياق والإجابات والتحقق البشري ثم تحفظ السجل والاستخدام ومهمة الإشعار معًا. يمكن استعادة المحاولة والبيانات نفسها خلال 24 ساعة دون تكرار. يعمل SMTP لاحقًا وقد يعيد المحاولة؛ فشل البريد لا يحذف السجل. إذا لم يُحدّد مستلم للنموذج يُستخدم بريد الشركة؛ وإذا لم يكن أي عنوان صالحًا يُحفظ الطلب دون بريد.
تستخدم جميع القوالب المرفقة هذا العقد. تعيد طلبات contact POST القديمة الحالة 410 وتبقى صفحات التواصل GET متاحة. طلب الموعد أو الحجز لا يؤكد حجزًا. معالجات الأطراف الأخرى ورفع الملفات والوجهات الخارجية العشوائية غير مشمولة.
POST /__eyivo/forms/{UUID}/submissionsGET /__eyivo/forms/{UUID}/challenge
القوالب ووضع النماذج
تعلن القوالب الاعتماديات المعتمدة وأسماء النماذج المستعارة القابلة للنقل في theme.json. يحدد الربط plugin وtype وschema_version وinstance، ويحلّه الإعداد إلى معرّف UUID دائم لنموذج الموقع. تستخدم قوالب التواصل forms.contact، وتستخدم مكوّنات جمع العملاء المحتملين component.form مع form_binding. اعرض الحقول المقدّمة وaction ورمز CSRF وform_context الموقّع وverification_html. استخدم فقط تغييرات العرض المعلنة أو الحقول الإضافية المسموح بها، ولا يمكن إضعاف تحقق الإضافة الإلزامي. تستخدم جميع القوالب المرفقة هذا العقد. الإجراءات الخارجية وطلب POST القديم للتواصل ليست مسارات إرسال مدعومة.
{"component_type":"eyivo:lead_form","data":{"form_binding":"contact"}}
Forms API 1
{
"api": "1",
"approval": "Only configured, approved Eyivo system form packages may declare forms; they require Human Verification and mandatory Panel API 2 submission pages.",
"schema": "forms.types maps stable identifiers to schema_version, localized label, fields, extensions, workflow and a restricted processor file. Fields declare scalar type, compatible control, required flag, localized label and guidance; strings require explicit max. No PHP, SQL, validation expressions or unknown fields are accepted.",
"field_types": [
"string",
"integer",
"boolean"
],
"controls": [
"text",
"textarea",
"email",
"tel",
"url",
"date",
"datetime-local",
"select",
"number",
"checkbox"
],
"presentation_extensions": [
"label",
"placeholder",
"help",
"order"
],
"storage": "The host owns site content storage. Plugin packages receive no credentials, database connection, SQL or unrestricted internal services. Site ownership is injected; schema snapshots remain readable without installed plugin code.",
"quotas": "Submission data consumes the existing package database allowance. Configuration limits of 50 fields / 32768 bytes / three levels do not cap form fields or accumulated submissions. Existing runtime, request and pagination bounds are technical transport protections.",
"identity": "Each site/plugin/type has a retained default form UUID created during authorized setup. Themes resolve portable aliases; rendering does not create instances. Additional named instances receive their own UUID. Theme changes and plugin reinstall preserve history.",
"transport": {
"encoding": "Escaped JSON byte counts match the restricted plugin interpreter; these technical ceilings do not limit accumulated form or submission counts.",
"schema_bytes": 49152,
"schema_accounting": "Definition plus one additional workflow copy, reserving the repeated workflow information on detail responses.",
"answers_bytes": 49152,
"source_bytes": 4096,
"processor_context_bytes": 114688,
"maximum_api_page": 25,
"instances_summary": [
"id",
"plugin_id",
"form_type",
"name",
"schema_version_id",
"record_revision",
"is_default"
]
}
}
مثال على ملف تعريف الإضافة
{
"id": "eyivo/human-verification",
"name": "Human Verification",
"version": "1.1.0",
"api": "1",
"type": "system",
"origin": "system",
"required": true,
"capabilities": [
"form.inject",
"form.validate",
"frontend.assets",
"public.endpoint",
"verification.challenge",
"verification.verify",
"panel.render"
],
"panel": {
"api": "1",
"entry": "settings",
"pages": {
"settings": {
"title": {
"en": "Human Verification",
"id": "Verifikasi Manusia",
"tr": "\u0130nsan Do\u011frulama",
"de": "Menschliche Verifizierung",
"es": "Verificaci\u00f3n humana",
"fr": "V\u00e9rification humaine",
"pt": "Verifica\u00e7\u00e3o humana",
"it": "Verifica umana",
"ar": "\u0627\u0644\u062a\u062d\u0642\u0642 \u0627\u0644\u0628\u0634\u0631\u064a"
},
"permission": "view",
"view": "panel/settings.json",
"actions": []
}
},
"menu": [],
"actions": []
},
"dependencies": {
"plugins": []
},
"conflicts": [],
"configuration": [],
"defaults": [],
"hooks": {
"form.before_submit": "render.php",
"form.validate": "validate.php"
},
"render_hook": "form.before_submit",
"required_validators": [
"contact"
],
"lifecycle": [],
"assets": [
{
"path": "assets/verification.css",
"type": "style"
},
{
"path": "assets/verification.js",
"type": "script"
}
],
"endpoints": {
"challenge": {
"method": "GET",
"handler": "challenge.php",
"parameters": {
"form_id": {
"type": "string",
"required": true,
"enum": [
"contact"
]
},
"locale": {
"type": "string",
"max": 10
}
},
"response": {
"token": {
"type": "string",
"max": 4096
},
"image": {
"type": "string",
"max": 65536
},
"expires_in": {
"type": "integer"
}
}
}
}
}
إضافة النظام · eyivo/contact · Forms API 1 / Panel API 2
{
"id": "eyivo/contact",
"name": "Contact",
"version": "1.0.0",
"api": "1",
"type": "system",
"origin": "system",
"required": false,
"capabilities": [
"forms.submissions.create",
"forms.submissions.read",
"forms.submissions.manage",
"forms.instances.read",
"forms.instances.manage",
"panel.render",
"panel.action"
],
"dependencies": {
"plugins": [
"eyivo/human-verification"
]
},
"conflicts": [],
"configuration": [],
"defaults": [],
"hooks": [],
"required_validators": [],
"lifecycle": [],
"assets": [],
"endpoints": [],
"forms": {
"api": "1",
"types": {
"contact": {
"schema_version": 1,
"label": {
"en": "Contact",
"id": "Kontak",
"tr": "İletişim",
"de": "Kontakt",
"es": "Contacto",
"fr": "Contact",
"pt": "Contato",
"it": "Contatti",
"ar": "التواصل",
"ru": "Контакт",
"nl": "Contact",
"pl": "Kontakt",
"ja": "お問い合わせ",
"ko": "문의",
"zh": "联系"
},
"fields": {
"name": {
"type": "string",
"control": "text",
"required": true,
"label": {
"en": "Name",
"id": "Nama",
"tr": "Ad",
"de": "Name",
"es": "Nombre",
"fr": "Nom",
"pt": "Nome",
"it": "Nome",
"ar": "الاسم",
"ru": "Имя",
"nl": "Naam",
"pl": "Imię i nazwisko",
"ja": "氏名",
"ko": "이름",
"zh": "姓名"
},
"max": 255,
"placeholder": {
"en": "Your full name",
"id": "Nama lengkap Anda",
"tr": "Adınız ve soyadınız",
"de": "Ihr vollständiger Name",
"es": "Tu nombre completo",
"fr": "Votre nom complet",
"pt": "Seu nome completo",
"it": "Il tuo nome completo",
"ar": "اسمك الكامل",
"ru": "Ваше полное имя",
"nl": "Uw volledige naam",
"pl": "Twoje imię i nazwisko",
"ja": "氏名を入力してください",
"ko": "성명을 입력하세요",
"zh": "请输入您的姓名"
},
"autocomplete": "name"
},
"email": {
"type": "string",
"control": "email",
"required": true,
"label": {
"en": "Email",
"id": "Email",
"tr": "E-posta",
"de": "E-Mail",
"es": "Correo electrónico",
"fr": "E-mail",
"pt": "E-mail",
"it": "Email",
"ar": "البريد الإلكتروني",
"ru": "Электронная почта",
"nl": "E-mail",
"pl": "E-mail",
"ja": "メールアドレス",
"ko": "이메일",
"zh": "电子邮箱"
},
"max": 254,
"placeholder": {
"en": "Your email address",
"id": "Alamat email Anda",
"tr": "E-posta adresiniz",
"de": "Ihre E-Mail-Adresse",
"es": "Tu dirección de correo",
"fr": "Votre adresse e-mail",
"pt": "Seu endereço de e-mail",
"it": "Il tuo indirizzo email",
"ar": "عنوان بريدك الإلكتروني",
"ru": "Ваш адрес электронной почты",
"nl": "Uw e-mailadres",
"pl": "Twój adres e-mail",
"ja": "メールアドレスを入力してください",
"ko": "이메일 주소를 입력하세요",
"zh": "请输入您的电子邮箱地址"
},
"autocomplete": "email"
},
"phone": {
"type": "string",
"control": "tel",
"required": false,
"label": {
"en": "Phone",
"id": "Telepon",
"tr": "Telefon",
"de": "Telefon",
"es": "Teléfono",
"fr": "Téléphone",
"pt": "Telefone",
"it": "Telefono",
"ar": "الهاتف",
"ru": "Телефон",
"nl": "Telefoon",
"pl": "Telefon",
"ja": "電話番号",
"ko": "전화번호",
"zh": "电话"
},
"max": 50,
"placeholder": {
"en": "Country code and phone number",
"id": "Kode negara dan nomor telepon",
"tr": "Ülke kodu ve telefon numarası",
"de": "Ländervorwahl und Telefonnummer",
"es": "Código de país y teléfono",
"fr": "Indicatif du pays et numéro",
"pt": "Código do país e telefone",
"it": "Prefisso internazionale e numero",
"ar": "رمز الدولة ورقم الهاتف",
"ru": "Код страны и номер телефона",
"nl": "Landcode en telefoonnummer",
"pl": "Numer telefonu z kodem kraju",
"ja": "国番号と電話番号を入力してください",
"ko": "국가 코드와 전화번호를 입력하세요",
"zh": "请输入国家代码和电话号码"
},
"autocomplete": "tel"
},
"message": {
"type": "string",
"control": "textarea",
"required": true,
"label": {
"en": "Message",
"id": "Pesan",
"tr": "Mesaj",
"de": "Nachricht",
"es": "Mensaje",
"fr": "Message",
"pt": "Mensagem",
"it": "Messaggio",
"ar": "الرسالة",
"ru": "Сообщение",
"nl": "Bericht",
"pl": "Wiadomość",
"ja": "メッセージ",
"ko": "메시지",
"zh": "留言"
},
"max": 20000,
"placeholder": {
"en": "Describe your question or request",
"id": "Jelaskan pertanyaan atau permintaan Anda",
"tr": "Sorunuzu veya talebinizi açıklayın",
"de": "Beschreiben Sie Ihre Frage oder Anfrage",
"es": "Describe tu pregunta o solicitud",
"fr": "Décrivez votre question ou demande",
"pt": "Descreva sua dúvida ou solicitação",
"it": "Descrivi la tua domanda o richiesta",
"ar": "صف سؤالك أو طلبك",
"ru": "Опишите ваш вопрос или запрос",
"nl": "Beschrijf uw vraag of verzoek",
"pl": "Opisz swoje pytanie lub prośbę",
"ja": "ご質問やご要望を入力してください",
"ko": "질문이나 요청 내용을 설명하세요",
"zh": "请描述您的问题或需求"
}
}
},
"extensions": {
"presentation": [
"label",
"placeholder",
"help",
"order"
],
"field_types": [
"string",
"integer",
"boolean"
]
},
"workflow": {
"initial": "received",
"states": {
"received": {
"en": "Received",
"id": "Diterima",
"tr": "Alındı",
"de": "Eingegangen",
"es": "Recibido",
"fr": "Reçu",
"pt": "Recebido",
"it": "Ricevuto",
"ar": "تم الاستلام"
},
"in_progress": {
"en": "In progress",
"id": "Sedang diproses",
"tr": "İşlemde",
"de": "In Bearbeitung",
"es": "En curso",
"fr": "En cours",
"pt": "Em andamento",
"it": "In corso",
"ar": "قيد المعالجة"
},
"completed": {
"en": "Completed",
"id": "Selesai",
"tr": "Tamamlandı",
"de": "Abgeschlossen",
"es": "Completado",
"fr": "Terminé",
"pt": "Concluído",
"it": "Completato",
"ar": "مكتمل"
}
},
"transitions": {
"start": {
"from": [
"received"
],
"to": "in_progress",
"label": {
"en": "Start processing",
"id": "Mulai proses",
"tr": "İşleme al",
"de": "Bearbeitung starten",
"es": "Iniciar gestión",
"fr": "Commencer le traitement",
"pt": "Iniciar atendimento",
"it": "Avvia elaborazione",
"ar": "بدء المعالجة"
}
},
"complete": {
"from": [
"received",
"in_progress"
],
"to": "completed",
"label": {
"en": "Complete",
"id": "Selesaikan",
"tr": "Tamamla",
"de": "Abschließen",
"es": "Completar",
"fr": "Terminer",
"pt": "Concluir",
"it": "Completa",
"ar": "إكمال"
}
},
"reopen": {
"from": [
"completed"
],
"to": "received",
"label": {
"en": "Reopen",
"id": "Buka kembali",
"tr": "Yeniden aç",
"de": "Erneut öffnen",
"es": "Reabrir",
"fr": "Rouvrir",
"pt": "Reabrir",
"it": "Riapri",
"ar": "إعادة الفتح"
}
}
}
},
"processor": "submit.php"
}
}
},
"panel": {
"api": "2",
"entry": "submissions",
"pages": {
"submissions": {
"title": {
"en": "Contact",
"id": "Kontak",
"tr": "İletişim",
"de": "Kontakt",
"es": "Contacto",
"fr": "Contact",
"pt": "Contato",
"it": "Contatti",
"ar": "التواصل"
},
"permission": "view",
"view": "panel/submissions.json",
"actions": []
},
"submission": {
"title": {
"en": "Submission",
"id": "Kiriman",
"tr": "Form kaydı",
"de": "Einsendung",
"es": "Solicitud",
"fr": "Demande",
"pt": "Solicitação",
"it": "Richiesta",
"ar": "الطلب"
},
"permission": "view",
"view": "panel/submission.json",
"actions": []
}
},
"menu": [
{
"page": "submissions",
"label": {
"en": "Contact",
"id": "Kontak",
"tr": "İletişim",
"de": "Kontakt",
"es": "Contacto",
"fr": "Contact",
"pt": "Contato",
"it": "Contatti",
"ar": "التواصل"
},
"icon": "list",
"order": 100
}
],
"actions": []
}
}
القدرات المعلنة
أعلن القدرات التي تحتاجها الحزمة. يتحقق المضيف منها قبل السماح بكل عملية.
frontend.renderfrontend.assetsform.injectform.validatepublic.endpointsite.settings.readsite.settings.writeverification.challengeverification.verifypanel.renderpanel.actionforms.submissions.createforms.submissions.readforms.submissions.manageforms.instances.readforms.instances.manage
الخطافات وأحداث دورة الحياة
خطافات الواجهة الأمامية
page.headpage.body.startpage.body.endform.startform.before_fieldsform.after_fieldsform.before_submitform.after_submitform.end
خطافات الواجهة الخلفية
form.before_validateform.validateform.after_validateform.before_processform.after_process
أحداث دورة الحياة
installenabledisableuninstallupgrade
التحقق وترتيب المعالجة
يتحقق محرك النماذج المشترك من المستأجر وسياق الوضع الموقّع والإضافة/المخطط الحالي والإجابات المعلنة قبل التحقق البشري. ويربط استرداد الإثبات بالمحاولة وبالحمولة القياسية. بعد إعادة فحص الحالة والحصة، يحفظ المعالج المقيّد المسؤول عبر واجهة المضيف البرمجية في معاملة محتوى واحدة مع إيصال المحاولة وتغيير الاستخدام ومهمة الإشعار. تسترد المحاولات المتطابقة الإيصال دون حفظ جديد. يتم إرسال البريد الإلكتروني بعد ذلك ويمكن إعادة محاولته، ولا يؤدي فشل البريد أبدًا إلى حذف طلب محفوظ.
بيئة تشغيل PHP مقيّدة
eyivo-php-subset-1
السياق والنتائج
تتلقى المعالجات $context كبيانات متوافقة مع JSON: site_id وsite_public_id وplugin_id وapi وconfiguration وlocale، إضافة إلى page/form_id/form_type/action/method/fields/validation_state عند الاقتضاء. يوفر المضيف هوية الموقع دائمًا. تعيد خطافات العرض حقل html، وتعيد خطافات التحقق حقلًا منطقيًا valid. يجب أن يعيد كل مدقق إلزامي القيمة true صراحة. تعيد خطافات الواجهة الخلفية الأخرى مصفوفة. الحقول المرسلة محدودة ولا تحتوي إلا على حقول النموذج العامة والإثبات. لا تُكشف أبدًا كائنات إطار العمل أو الأسرار أو رموز المستخدمين أو اتصالات قاعدة البيانات.
جسر الواجهة البرمجية والأذونات
يتيح $pluginApi العمليات get وpost وput وpatch وdelete مع معرّف عملية حرفي ومصفوفة معاملات. تُفحص كل عملية مقابل ملف التعريف من حيث الطريقة والقدرة ومخطط الطلب ومخطط الاستجابة وقيد الإضافة الاختياري. تُرفض عناوين URL غير المتاحة. لا يمكن تجاوز نطاق الموقع أو الإضافة. تتشارك عمليات الإعدادات خدمة التطبيق نفسها مع إدارة الإعدادات عبر Internal API الموثقة. تقتصر الكتابة على الإعدادات على معالجات دورة الحياة المصرح بها وإجراءات الإدارة في اللوحة التي يصرّح بها المالك. لا يمكن لمحمّلات الصفحات وإجراءات العرض ونقاط النهاية العامة وخطافات العرض تغيير إعدادات التثبيت.
عرض الصياغة المدعومة وحدود التنفيذ
{
"execution": "Interpreted PHP subset. Package code is never included, evaluated, or autoloaded by PHP.",
"statements": [
"local assignment",
"return array",
"if / else with braces"
],
"expressions": [
"single-quoted strings",
"integers",
"booleans",
"null",
"arrays",
"array lookup",
"concatenation",
"strict equality",
"strict inequality",
"boolean operators",
"null coalescing"
],
"functions": [
"trim",
"strtolower",
"strlen",
"count",
"htmlspecialchars"
],
"forbidden": [
"all undeclared functions",
"superglobals",
"objects and classes",
"dynamic calls",
"references",
"loops",
"includes",
"eval",
"filesystem access",
"network access",
"database access",
"environment access",
"configuration manipulation",
"deserialization"
],
"source_bytes": 32768,
"tokens": 4096,
"syntax_depth": 32,
"evaluation_steps": 4096,
"api_calls_per_handler": 16,
"handler_wall_seconds": 10,
"binary_expression_chain": 128,
"forms_per_page": 32,
"data_bytes": 131072
}
الملفات ونقاط النهاية العامة
تسجّل الحزم ملفات نصوص برمجية وأنماط وصور محلية. تُحمَّل النصوص البرمجية والأنماط مرة واحدة عند عرض الإضافة، ولا تُقدَّم إلا الملفات المسجلة. يستخدم HTML المضمّن للإضافة قائمة مسموحة للعناصر والسمات. تسمح سياسة أمان المحتوى للقوالب المخصصة ببصمات النصوص البرمجية المسجلة مع Subresource Integrity. تستخدم نقاط النهاية /__eyivo/plugins/{namespace}/{name}/{endpoint} وطرقًا صريحة وحقول إدخال معلنة. يبقى تحديد المستأجر وحدود المعدل وحماية CSRF للطرق غير الآمنة تحت تحكم المضيف. لا تضيف نقاط نهاية الإضافات أبدًا مسارات تطبيق عشوائية.
التحقق البشري
إضافة eyivo/human-verification إلزامية في كل موقع. تستخدم الخدمة المشتركة على plugins.eyivo.com/verification اختبارات PNG مشفرة من ستة أرقام تنتهي صلاحيتها بعد 180 ثانية. تُفرض هوية العميل الموثوق والخدمات الممنوحة وعناوين IP المصدر. تربط معرّفات UUID للنماذج المسجلة ومعرّفات المحاولات كل اختبار، ويربط التحقق أيضًا الحمولة القياسية. يمكن للمحاولات المقبولة المتطابقة استرداد نتيجتها لمدة 24 ساعة، لذا احتفظ بالمفاتيح السابقة طوال هذه المدة مع هامش للساعة والشبكة. تفشل الإثباتات التي انتهت صلاحيتها للتو. تخزن قاعدة بيانات الإضافات المشتركة حالة إعادة الإرسال وحدود المعدل والإيصالات، ولا تخزن أبدًا إجابات النماذج أو سجل صندوق الوارد. لا تحصل الحزم على أي وصول إلى قاعدة البيانات.
إمكانية الوصول وسلوك المتصفح
يحتوي عنصر التحكم على تسميات مترجمة وإرشادات للإدخال وإعلانات للحالة وزر لتحديث الصورة. يجب تفعيل JavaScript. لا يستطيع الزوار الذين لا يمكنهم إدراك الصورة إكمال هذا الوضع الأولي المعتمد على الصورة فقط، والاختبار البديل الميسّر ليس جزءًا من الإصدار 1. تتجاوز الصفحات التي تحتوي على نماذج مدعومة ذاكرة HTML المؤقتة المشتركة. تحتفظ معاينات القوالب بسياسة عدم تشغيل النصوص البرمجية ولا يمكنها إرسال اختبارات فعّالة.
عمليات الواجهة البرمجية المتاحة
استخدم معرّفات العمليات هذه عبر $pluginApi. يأتي كل إدخال أدناه من ملف الإتاحة نفسه المستخدم لفرض أذونات وقت التشغيل.
site.settings.read
Read the current plugin's namespaced site configuration.
site.settings.read
$pluginApi->get('site.settings.read', []);
عرض المعاملات والاستجابة والقيود
{
"parameters": [],
"response": {
"configuration": {
"type": "object"
}
},
"restricted_to_plugins": null,
"deprecated": false
}
site.settings.update
Replace the current plugin's configuration using its declared schema.
site.settings.write
$pluginApi->put('site.settings.update', ['configuration' => []]);
عرض المعاملات والاستجابة والقيود
{
"parameters": {
"configuration": {
"type": "object",
"required": true
}
},
"response": {
"configuration": {
"type": "object"
}
},
"restricted_to_plugins": null,
"deprecated": false
}
form.verification.widget
Render a localized verification control for a supported form.
form.inject
$pluginApi->get('form.verification.widget', []);
عرض المعاملات والاستجابة والقيود
{
"parameters": [],
"response": {
"html": {
"type": "string",
"max": 16384
}
},
"restricted_to_plugins": [
"eyivo/human-verification"
],
"deprecated": false
}
form.verification.challenge
Request a numeric image challenge bound to the current site and form.
verification.challenge
$pluginApi->get('form.verification.challenge', []);
عرض المعاملات والاستجابة والقيود
{
"parameters": [],
"response": {
"token": {
"type": "string",
"max": 4096
},
"image": {
"type": "string",
"max": 65536
},
"expires_in": {
"type": "integer"
}
},
"restricted_to_plugins": [
"eyivo/human-verification"
],
"deprecated": false
}
form.verification.verify
Consume a proof once. Rejection never allows form processing.
verification.verify
$pluginApi->post('form.verification.verify', ['token' => $context['fields']['verification_token'], 'answer' => $context['fields']['verification_answer']]);
عرض المعاملات والاستجابة والقيود
{
"parameters": {
"token": {
"type": "string",
"required": true,
"max": 4096
},
"answer": {
"type": "string",
"required": true,
"max": 12
}
},
"response": {
"valid": {
"type": "boolean"
}
},
"restricted_to_plugins": [
"eyivo/human-verification"
],
"deprecated": false
}
forms.submissions.create
Persist only the host-validated and verified attempt supplied out-of-band to the owning processor; accepts no answers or ownership override.
forms.submissions.create
$pluginApi->post('forms.submissions.create', []);
عرض المعاملات والاستجابة والقيود
{
"parameters": [],
"response": {
"result": {
"type": "object"
}
},
"restricted_to_plugins": null,
"deprecated": false
}
forms.submissions.list
Read paginated submission summaries for the current site and plugin in an authenticated panel context.
forms.submissions.read
$pluginApi->get('forms.submissions.list', []);
عرض المعاملات والاستجابة والقيود
{
"parameters": {
"form_type": {
"type": "string",
"max": 64
},
"form_id": {
"type": "string",
"max": 36
},
"cursor": {
"type": "string",
"max": 1024
},
"per_page": {
"type": "integer",
"min": 1,
"max": 25
},
"read": {
"type": "boolean"
},
"archived": {
"type": "boolean"
},
"workflow_state": {
"type": "string",
"max": 64
},
"from": {
"type": "string",
"max": 10
},
"to": {
"type": "string",
"max": 10
}
},
"response": {
"result": {
"type": "object"
}
},
"restricted_to_plugins": null,
"deprecated": false
}
forms.submissions.read
Read a historical submission snapshot owned by the current site and plugin.
forms.submissions.read
$pluginApi->get('forms.submissions.read', []);
عرض المعاملات والاستجابة والقيود
{
"parameters": {
"submission_id": {
"type": "string",
"max": 36,
"required": true
}
},
"response": {
"result": {
"type": "object"
}
},
"restricted_to_plugins": null,
"deprecated": false
}
forms.submissions.update
Apply authorized reading/archive changes or a declared workflow transition using the current record revision.
forms.submissions.manage
$pluginApi->patch('forms.submissions.update', []);
عرض المعاملات والاستجابة والقيود
{
"parameters": {
"submission_id": {
"type": "string",
"max": 36,
"required": true
},
"record_revision": {
"type": "integer",
"min": 1,
"required": true
},
"read": {
"type": "boolean"
},
"archived": {
"type": "boolean"
},
"transition": {
"type": "string",
"max": 64
}
},
"response": {
"result": {
"type": "object"
}
},
"restricted_to_plugins": null,
"deprecated": false
}
forms.submissions.delete
Explicitly delete an owned submission, cancel pending notifications and release its stored byte charge; preserve technical replay protection.
forms.submissions.manage
$pluginApi->delete('forms.submissions.delete', []);
عرض المعاملات والاستجابة والقيود
{
"parameters": {
"submission_id": {
"type": "string",
"max": 36,
"required": true
},
"record_revision": {
"type": "integer",
"min": 1,
"required": true
}
},
"response": {
"result": {
"type": "object"
}
},
"restricted_to_plugins": null,
"deprecated": false
}
forms.instances.list
Read cursor-paginated compact form summaries belonging to the current site and plugin; settings are not included.
forms.instances.read
$pluginApi->get('forms.instances.list', []);
عرض المعاملات والاستجابة والقيود
{
"parameters": {
"form_type": {
"type": "string",
"max": 64
},
"cursor": {
"type": "string",
"max": 1024
},
"per_page": {
"type": "integer",
"min": 1,
"max": 25
},
"q": {
"type": "string",
"max": 160
}
},
"response": {
"result": {
"type": "object"
}
},
"restricted_to_plugins": null,
"deprecated": false
}
forms.instances.create
Create a separate named form instance with an automatically generated UUID.
forms.instances.manage
$pluginApi->post('forms.instances.create', []);
عرض المعاملات والاستجابة والقيود
{
"parameters": {
"form_type": {
"type": "string",
"max": 64,
"required": true
},
"name": {
"type": "string",
"max": 160,
"required": true
}
},
"response": {
"result": {
"type": "object"
}
},
"restricted_to_plugins": null,
"deprecated": false
}
forms.instances.update
Update a current form instance name or schema-compatible settings under owner/manage authorization and record revision.
forms.instances.manage
$pluginApi->patch('forms.instances.update', []);
عرض المعاملات والاستجابة والقيود
{
"parameters": {
"form_id": {
"type": "string",
"max": 36,
"required": true
},
"record_revision": {
"type": "integer",
"min": 1,
"required": true
},
"name": {
"type": "string",
"max": 160
},
"settings": {
"type": "object"
}
},
"response": {
"result": {
"type": "object"
}
},
"restricted_to_plugins": null,
"deprecated": false
}
التوافق والنشر
يدعم Plugin API 1 كلًا من Forms API 1 وPanel API 1/2. تتطلب حزم النماذج صفحات القوائم والتفاصيل وإعلانات القوائم في Panel API 2. تُرفض العقود غير المدعومة والصياغة غير الآمنة. انشر خدمة إيصالات التحقق أولًا، ثم ترحيلات master والمحتوى ومواءمة الإضافات وترحيل البيانات القديمة واحتساب الاستخدام، ثم انشر بعد ذلك عارض التطبيق المتوافق ومرجع Apex. احتفظ بالسجل والمفاتيح القديمة أثناء التراجع. تستخدم جميع القوالب المرفقة نماذج مسجلة، وتعيد طلبات POST القديمة للتواصل الرمز 410. يوفر ملف الإتاحة هذا المرجع وأذونات وقت التشغيل.