JSON API של טפסים וטבלאות

איך הטפסים והטבלאות של פאנל הניהול מדברים עם השרת: פרוטוקול הטבלה, שמירת טופס, כותרות הגילוי של AI, נקודות קצה של פאנל מותאם, CSRF ושגיאות.

⏱ 7 דק' קריאה 1347 מילים ערוך דף זה ב-GitHub

ממשק הניהול אינו 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-ErrorJSON מקודד ב-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_managerapi?action=... (קריאה וכתיבה, עם CSRF לכתיבה), download
db_managerapi?action=... (טבלאות, מבנה, נתונים)
panel_registrylist, register, exists (רישום פאנלים מקוד, ראו בניית פאנל חדש)
Adminsנקודות סשנים (session_id ב-POST, התשובה ok)
clear_cache, storage, wizzo_update, cron_managerנקודות פנימיות לממשק שלהם (ראו פאנלים מובנים)
הערה

כל אלה נקודות פנימיות של ממשק הניהול ואין להן התחייבות לתאימות לאחור. כשצריך ממשק יציב לשימוש חיצוני, כתבו נקודה משלכם או כלי MCP (כלי MCP מותאמים).

ראו גם#

מצאתם טעות או חוסר? תקנו את הדף או פתחו Issue בריפו. התיעוד נכתב מתוך הקוד של ליבה 5.0.115.