ממשק הניהול אינו REST API נפרד. כל פאנל הוא כתובת ({admin}/<panel>/<method>), והטפסים והטבלאות שבו מתקשרים עם אותה כתובת בבקשות AJAX. הדף מתאר את הפרוטוקולים האלה: מה הדפדפן שולח ומה חוזר, כדי שתוכלו לכתוב לקוח משלכם, לבדוק פאנל מסקריפט, או לכתוב נקודות JSON בפאנל מותאם.
{admin} הוא CONFIG::$admin_url, ו-system/ הוא תיקיית הליבה (api/core בריפו). כל הבקשות דורשות מנהל מחובר (העוגייה admin_session, ראו התחברות).
הפרוטוקולים של panel_table ושל AJAXForm אינם מוגנים ב-CSRF: ההגנה היחידה היא עוגיית הסשן. בפאנל מותאם שמשנה מידע כתבו בדיקת CSRF בעצמכם (ראו "נקודות JSON בפאנל מותאם").
מצבי תגובה: pmode#
פרמטר pmode ב-query string קובע כמה מהעמוד חוזר:
pmode | מה חוזר |
|---|---|
| (ריק) | עמוד מלא עם מעטפת האדמין |
inner | רק תוכן הפאנל (כך adminMovePage טוען פאנל לתוך #mainframe) |
empg | תוכן חלקי בלי מעטפת: HTML של טופס, או JSON כשהמתודה מדפיסה JSON |
כותרות התגובה שהמעטפת מוסיפה: breadcrumbs, add_action, scripts ו-scripts_ver. כותרות הפרוטוקול של ה-AI מתוארות בהמשך.
פרוטוקול הטבלה (panel_table)#
הטבלה היא רכיב Vue בצד הלקוח. הוא שולח לכתובת הפאנל עצמה (REQUEST_URI של העמוד) בקשת POST עם גוף JSON. השרת (panel_table::get_html) מזהה את הבקשה לפי tbl_action ו-tblID שמתאים לטבלה, עונה JSON ועוצר.
גוף הבקשה#
{
"tblID": "articles",
"tbl_action": "get_lines",
"pgnm": 1,
"ordBy": "id",
"ordDir": "DESC",
"q": "חדשות",
"filters": { "cat_id": "3" }
}
| שדה | משמעות |
|---|---|
tblID | מזהה הטבלה ($tbl->tblID). בקשה שלא תואמת אותו מתעלמת מהפרוטוקול |
tbl_action | הפעולה (טבלה למטה) |
pgnm | מספר העמוד (מ-1) |
ordBy, ordDir | מיון לפי שדה וכיוון |
q | טקסט חיפוש |
filters | {שדה: ערך}; --all-- או ריק מתעלמים |
id | מזהה שורה (לפעולות על שורה) |
token | אסימון הפעולה, כפי שהשרת הדפיס אותו בשורה |
checkgroup | מערך מזהים לפעולה קבוצתית |
editable_field_name, editable_value | עריכה בתוך הטבלה |
limit | מגבלת שורות לייצוא |
order_ids | סדר חדש של מזהים (גרירה) |
ערכי tbl_action#
| ערך | מה קורה |
|---|---|
get_lines, search | מחזיר שורות |
order | שומר סדר חדש (order_ids), עונה {"success": true/false} |
xls | מייצא את השורות לקובץ |
DELETE | מוחק את השורה id דרך $tbl->delete, בכפוף ל-has_perms("delete"), ואז מחזיר שורות |
group_action | מריץ פעולה קבוצתית (token, checkgroup) ומחזיר שורות |
editable_field | משנה ערך של עמודה שהוגדרה editable |
| כל ערך אחר | נשלח ל-do_actions: מחפש בפעולות השורה (actions) את זו שה-token שלה תואם ל-token שבבקשה |
התגובה#
{
"more_data": { "alert": "נשמר" },
"lines": [ { "id": 1, "title": "...", "...": "..." } ],
"paging": { "...": "..." },
"order_manager": false,
"action_result": null
}
more_data.alertהוא מה שפונקציית הפעולה החזירה (מוצג כהודעה).action_resultהוא פסק הדין שלdo_actions:true(בוצע),false(נדחה) אוnull(קריאה בלבד). כשהשורה לא שייכת לאוכלוסיית הפאנל חוזרrow_not_in_list.- אם לטבלה מוגדר
additional_content, התוצאה שלו מצורפת כ-additional_content. - ב-
xlsובפעולות שלא מחזירות שורות מתקבל JSON אחר (או קובץ).
גילוי הטבלה#
מנהל מחובר שמוסיף את הכותרת AI-Table-Discovery לבקשה מקבל במקום שורות את תיאור הטבלה (output_table_json): tblID, title, הטבלה הראשית (table, בלי CRM_), base_query, רשימת columns (title, field, type, editable, select_options), limit, default_order, order_manager, btn_add_url, has_delete ו-group_actions. כך שרת ה-MCP של האתר לומד איך לקרוא ולערוך כל פאנל.
curl -s "https://example.com{admin}/articles" \
-H "Cookie: admin_session=<SESSION>" \
-H "AI-Table-Discovery: 1"
ordBy ו-ordDir נכנסים ל-ORDER BY ללא סינון, ופעולת xls אינה בודקת הרשאת ייצוא בצד השרת. אל תסתמכו על הסתרת הכפתור כהגנה, ובטבלה שחושפת מידע רגיש הגדירו הרשאה על הפאנל עצמו.פרוטוקול הטופס (AJAXForm)#
שמירה#
הטופס נשלח ב-$.ajax מסוג POST לכתובת הטופס (action) עם pmode=empg, כ-application/x-www-form-urlencoded. השרת מזהה שהטופס נשלח לפי השדה המוסתר isFormSent_<formName>:
curl -s -X POST "https://example.com{admin}/articles/insert/5?pmode=empg" \
-H "Cookie: admin_session=<SESSION>" \
--data-urlencode "isFormSent_frmArticles=1" \
--data-urlencode "title=כותרת חדשה" \
--data-urlencode "status=1"
{admin}/articles/insertיוצרת,{admin}/articles/insert/5מעדכנת את השורה 5.- תשובה מוצלחת היא הטקסט שהוגדר ב-
msgAddedStr, ואם הוגדרsuccessFuncהוא מודפס כ-<script>. כשל מחזירmsgNotAddedStrואת הטופס עם השגיאות. ReloadAfterSubmit = trueגורם ללקוח להחליף את ה-HTML של הטופס בתשובה (כך מוצגות שגיאות ולוח מעודכן).- פעולת כפתור (
general_buttonsעםfunc) נשלחת כ-form_action=<token>. השרת מריץ את הפונקציה, מדפיס את תוצאתה ועוצר.
גילוי ושמירה תכנותית: כותרות AI#
| כותרת | משמעות |
|---|---|
AI-Form-Discovery | בבקשת GET של מנהל מחובר: מחזיר את מבנה הטופס כ-JSON במקום HTML |
AI-Form-Insert | בשמירה: השרת מנרמל ערכים לפי סוג השדה, ושדה שלא נשלח נשאר כפי שהיה (בדפדפן כל שדה נשלח תמיד) |
תשובת הגילוי:
{
"table": "articles",
"form_name": "frmArticles",
"fields": [
{ "name": "title", "title": "כותרת", "type": "Text", "must": true, "table": "conf", "group": "", "placeholder": "" },
{ "name": "cat_id", "title": "קטגוריה", "type": "SelectDB", "is_select_field": true, "select_options": [] }
],
"actions": [ { "title": "שמירה ופרסום", "saves_form": true, "token": "..." } ]
}
לשדות קבצים מתווספים is_file_field, file_types, is_image_field, storage_object_type, dir ו-is_gallery. לשדות בחירה: is_select_field ו-select_options. לשדות מתג: is_boolean_field. שדה מורכב (כמו SEO) מציג sub_fields שנכתבים בשמותיהם.
כותרות התגובה של שמירה עם AI-Form-Insert:
| כותרת | משמעות |
|---|---|
AI-New-ID | המזהה של השורה שנוצרה |
AI-Form-Error | JSON מקודד ב-rawurlencode של שגיאות לפי שם שדה |
אחרי שמירה מוצלחת של טופס שמוגדר לו table, השרת גם מפעיל (אסינכרונית) סוכן AI שמחובר לאירוע form_save:<table> אם הוגדר כזה. פירוט ב-סוכני AI.
נקודות JSON בפאנל מותאם#
מתודה ציבורית בפאנל היא כתובת, ולכן כדי לחשוף API קובעים pmode, כותרת תוכן, ומחזירים מחרוזת JSON:
public function api()
{
$_GET["pmode"] = "empg";
header("Content-Type: application/json; charset=utf-8");
$data = json_decode(file_get_contents("php://input"), true);
if (!is_array($data)) $data = $_POST;
$action = $_GET["action"] ?? $data["action"] ?? "";
// פעולות כתיבה: בדיקת CSRF
if (in_array($action, ["save", "delete"], true)) {
$token = $data["csrf"] ?? $_SERVER["HTTP_X_WZ_CSRF"] ?? "";
if (!ADMIN::verify_csrf_token($token))
return json_encode(["success" => false, "code" => "csrf", "error" => "פג תוקף הטוקן"]);
}
switch ($action) {
case "capabilities":
return json_encode(["success" => true, "data" => ["csrf" => ADMIN::generate_csrf_cookie()]]);
// ...
}
return json_encode(["success" => false, "error" => "פעולה לא מוכרת"]);
}
הדפוס הזה (כפי שהוא ב-system/admin/file_manager.php):
- אסימון ה-CSRF הוא עוגיית
admin_csrf(תוקף של שעה).ADMIN::generate_csrf_cookie()מחזירה את הקיים או יוצרת חדש, ו-ADMIN::verify_csrf_token($token)משווה בזמן קבוע. - הלקוח שולח את האסימון בגוף (
csrf), ב-$_POST["csrf"]או בכותרתX-WZ-CSRF. - כשל אימות עונה
{"success": false, "code": "csrf", "error": "..."}. הלקוח שולף אסימון חדש ומנסה שוב פעם אחת, כדי שטאב שנשאר פתוח מעל שעה לא יאבד עבודה. - צורת התשובה המקובלת:
{"success": true, "data": ...}או{"success": false, "error": "..."}. - בדיקת הרשאה ספציפית (
ADMIN::has_perms("delete")) נעשית בכל פעולה, כי ההרשאה לפאנל היא רק שער הכניסה.
פרטים על בניית לקוח Vue מעל נקודה כזו ב-פאנלים עם Vue.
נקודות JSON שהליבה כבר מספקת#
| פאנל | נקודות |
|---|---|
file_manager | api?action=... (קריאה וכתיבה, עם CSRF לכתיבה), download |
db_manager | api?action=... (טבלאות, מבנה, נתונים) |
panel_registry | list, register, exists (רישום פאנלים מקוד, ראו בניית פאנל חדש) |
Admins | נקודות סשנים (session_id ב-POST, התשובה ok) |
clear_cache, storage, wizzo_update, cron_manager | נקודות פנימיות לממשק שלהם (ראו פאנלים מובנים) |
כל אלה נקודות פנימיות של ממשק הניהול ואין להן התחייבות לתאימות לאחור. כשצריך ממשק יציב לשימוש חיצוני, כתבו נקודה משלכם או כלי MCP (כלי MCP מותאמים).