המדריך מניח שדרישות המערכת מתקיימות. בסוף נקבל אתר שעונה ב-/system/core_ping, פאנל ניהול שאפשר להיכנס אליו, ו-cron שרץ. את העמוד הראשון של האתר עצמו בונים בהעמוד הראשון שלך.
הליבה חיה ב-api/core/ בריפו ונפרסת לאתר אל system/core/. קבצי הדפדפן של הליבה ב-api/js/ נפרסים אל system/js/. בעמוד הזה system/ תמיד תיקייה בשורש האתר.
התצורה והקוד כאן נגזרים מקריאת הקוד של הליבה (גרסה 5.0.115) ומאתר חי. הם לא נבדקו בהתקנה נקייה מקצה לקצה, ולכן אחרי כל שלב יש בדיקה קצרה.
סדר הפעולות#
- יוצרים מסד נתונים ומשתמש.
- מעתיקים את הליבה ל-
system/coreומריציםcomposer install. - כותבים
system/config.php. - כותבים
index.phpו-.htaccess. - מקימים את הטבלאות ושורות הפתיחה.
- מוסיפים ערכת נושא מינימלית.
- נכנסים לניהול.
- מתזמנים cron ובודקים.
1. מסד נתונים#
CREATE DATABASE mysite CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
CREATE USER 'mysite'@'localhost' IDENTIFIED BY 'CHOOSE_A_STRONG_PASSWORD';
GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, ALTER, INDEX, DROP ON mysite.* TO 'mysite'@'localhost';
CREATE ו-ALTER
הליבה יוצרת לבד טבלאות ועמודות חסרות (למשל CRM_adminPanel_sessions, ועדכון המבנה ב-wizzo_update). בלי ההרשאות האלה הכשלון שקט: שורה ב-error_log ואין שגיאה על המסך.2. הליבה והחבילות#
מהריפו (או מעותק שלו):
mkdir -p /var/www/site/system
cp -r api/core /var/www/site/system/core
cp -r api/js /var/www/site/system/js
cd /var/www/site/system/core
composer install --no-dev
הריפו לא כולל vendor/ ולא composer.lock. בלי vendor/autoload.php הליבה נופלת ב-require ברגע שהיא צריכה אחת מהחבילות (למשל PAGE::is_mobile() כש-allow_cache דלוק). הליבה שמורידים בעדכון ליבה מהשרת הראשי כוללת vendor/ מוכן. פרטים: חבילות Composer.
בדיקה: ls /var/www/site/system/core/core.php /var/www/site/system/core/vendor/autoload.php /var/www/site/system/js/system.js מציג את שלושת הקבצים.
3. system/config.php#
הקובץ מגדיר את המחלקה CONFIG_USER. הליבה מגדירה class CONFIG extends CONFIG_USER, ולכן הקובץ חייב להיטען לפני core.php. מאפיין סטטי שהליבה קוראת ישירות וחסר כאן גורם ל-Error: Access to undeclared static property, ולכן הרשימה שלמטה היא המינימום, לא המלצה.
<?php
class CONFIG_USER
{
// DB
static $db_type = "PDO";
static $db_host = "localhost";
static $db_port = 3306;
static $db_dbname = "mysite";
static $db_user = "mysite";
static $db_password = "CHOOSE_A_STRONG_PASSWORD";
static $db_prefix = "CRM"; // חייב להישאר CRM
// paths (יחסיים לשורש האתר; index.php הופך את core_path למוחלט)
static $core_path = "system/core";
static $models_folder = "application/models";
static $cache_folder = "cache";
// routing
static $admin_url = "wizzocms"; // https://example.com/wizzocms
static $default_module = "home"; // דף הבית: application/controllers/home.php
// behaviour
static $allow_cache = true;
static $use_smarty = true;
static $minify_html = true;
static $ignore_domain_check = false;
static $storage_remote = ""; // "" = מקומי, "ftp", "s3"
// mail
static $mail_is_smtp = false;
static $mail_queue = false;
}
| מאפיין | חשוב לדעת |
|---|---|
$db_prefix | "CRM" בלבד. הקידומת CRM_ כתובה ישירות בעשרות שאילתות |
$core_path | נכתב יחסי ("system/core"). index.php ממיר אותו לנתיב מוחלט |
$admin_url | slug פשוט ללא /, מוזרק לביטוי רגולרי ב-ROUTER. אפשר לבחור שם אחר משלכם |
$default_module | נקרא רק לדף הבית. נדרשת גם שורה ב-CRM_modules (שלב 5) |
$allow_cache | true מפעיל מטמון עמודים מלא. בפיתוח אפשר false |
$ignore_domain_check | false: דומיין שאינו ב-CRM_platforms מקבל die("URL ERROR"). ב-CLI הליבה מדליקה אותו לבד |
מפתחות אופציונליים (environment, mask, show_wizzo_credits, set_base_domain, SMTP, FTP ועוד) וההסבר המלא: קונפיגורציה.
system/config.php מכיל את סיסמת המסד
אסור שיהיה נגיש ב-HTTP. ב-Apache הליבה מנסה לכתוב system/.htaccess שחוסם אותו (ראו שלב 6), ואין להסתמך על כך בלבד: ודאו ש-https://example.com/system/config.php אינו מחזיר את הקובץ. ב-nginx חסמו אותו ידנית.4. index.php ו-.htaccess#
index.php#
<?php
// בפרודקשן לא מציגים שגיאות על המסך (הליבה כותבת ל-error_log).
error_reporting(E_ALL);
ini_set('display_errors', 0);
ob_start("ob_gzhandler");
require_once("system/config.php");
// הופך את CONFIG_USER::$core_path היחסי לנתיב מוחלט
$ex = explode("../", CONFIG_USER::$core_path);
$realPath = dirname(__FILE__);
if (CONFIG_USER::$core_path != "system/core") for ($i = 0; $i < (count($ex)); $i++) $realPath = dirname($realPath);
CONFIG_USER::$core_path = $realPath . "/" . $ex[(count($ex) - 1)];
// הליבה
require_once(CONFIG_USER::$core_path . "/core.php");
הקובץ הוא העתק מופשט של index.php באתר ייחוס חי, בלי שורת display_errors שמציגה שגיאות. CONFIG::preinit() קובע את שורש האתר (CONFIG::$base_path) לפי הקובץ הראשי שהופעל, ולכן ההפעלה חייבת להתחיל ב-index.php שבשורש.
עם $core_path = "system/core" הלולאה לא רצה והכול פשוט. אם מציבים ערך עם ../ (ליבה משותפת לכמה אתרים), הלולאה עולה count($ex) רמות: אחת יותר ממספר ה-../. לדוגמה "../shared/system/core" באתר שנמצא ב-/var/www/site מתפרש ל-/var/shared/system/core. כך הקוד נקרא (לא נבדק בהרצה). בדקו את הנתיב המוחלט שנוצר לפני שמסתמכים עליו.
.htaccess#
ההעתק הבא מבוסס על ה-.htaccess של אתר הייחוס. השמטתי כותרות CORS, הגדרות PHP של cPanel וחוקי Expires ו-DEFLATE (אופציונליים, לביצועים).
Options -Indexes
AddDefaultCharset utf-8
RewriteEngine on
ErrorDocument 404 /404
# www -> ללא www, ו-http -> https
RewriteCond %{HTTP_HOST} ^www\.(.*)$ [NC]
RewriteRule ^(.*)$ https://%1/$1 [R=301,L]
RewriteCond %{HTTPS} off
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
# JS ו-CSS שכבר נדחסו למטמון נשלחים ישירות מהדיסק
RewriteCond %{REQUEST_METHOD} !^POST$ [NC]
RewriteCond %{DOCUMENT_ROOT}/cache/%{SERVER_NAME}%{REQUEST_URI}%{QUERY_STRING}.js -f
RewriteRule ^(.*)$ cache/%{SERVER_NAME}%{REQUEST_URI}%{QUERY_STRING}.js [L]
# SCSS: קובץ מקומפל מהמטמון, ואם אין, קומפילציה ב-PHP
RewriteCond %{REQUEST_METHOD} !^POST$ [NC]
RewriteCond %{DOCUMENT_ROOT}/cache/%{SERVER_NAME}/scss/%{REQUEST_URI}%{QUERY_STRING}.css -f
RewriteRule ^(.*)$ cache/%{SERVER_NAME}/scss%{REQUEST_URI}%{QUERY_STRING}.css [L]
RewriteCond %{REQUEST_METHOD} !^POST$ [NC]
RewriteCond %{DOCUMENT_ROOT}/cache/%{SERVER_NAME}/scss/%{REQUEST_URI}%{QUERY_STRING}.css !-f
RewriteRule ^(.+)\.scss\.css$ index.php?%{QUERY_STRING}
RewriteCond %{REQUEST_METHOD} !^POST$ [NC]
RewriteCond %{DOCUMENT_ROOT}/cache/%{SERVER_NAME}%{REQUEST_URI}%{QUERY_STRING}.css -f
RewriteRule ^(.*)$ cache/%{SERVER_NAME}%{REQUEST_URI}%{QUERY_STRING}.css [L]
# קבצי minify שעדיין לא נוצרו: PHP מייצר אותם
RewriteRule ^minify\/(.+)\.(js|css)$ index.php?%{QUERY_STRING} [L]
# אין גישה לקבצי מטמון הנתונים
RewriteCond %{REQUEST_URI} ^/cache/items/ [NC]
RewriteRule \.chc$ - [R=404,L]
# front controller: כל מה שאינו קובץ אמיתי או תיקייה סטטית
RewriteCond $1 !^(\.well-known)
RewriteCond $1 !^(firebase-messaging-sw\.js)
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond $1 !^(index\.php|cache|application/views|application/views_mobile|system/js|system/libraries|system/css|media|themes)
RewriteRule ^(.*)$ /index.php?%{QUERY_STRING} [L]
שני כללי ה-https מתחילים להפנות מיד. בסביבת פיתוח בלי תעודה הסירו אותם (וגם את כלל ה-www).
בקשות אל /system/<name> מגיעות ל-index.php כי system/ אינה ברשימת הפטורים. system/js כן ברשימה, ולכן קבצי ה-JS מוגשים ישירות. התיקייה system/ מקבלת system/.htaccess מהליבה, שחוסם את config.php, את market_settings.json ומפתחות. הוא בכוונה בלי RewriteEngine, כדי שנתיבי /system/<name> ימשיכו לעבוד.
nginx (התאמה)#
הקטע נגזר ידנית מכללי ה-Apache שלמעלה. הוא משמיט את קיצור ההגשה של קבצי מטמון JS ו-CSS (PHP מגיש אותם), ולכן נכון אך פחות מהיר. שנו fastcgi_pass לנתיב של PHP-FPM אצלכם ובדקו לפני עלייה לאוויר.
server {
server_name example.com;
root /var/www/site;
index index.php;
# קבצים נסתרים, למעט .well-known
location ^~ /.well-known/ { allow all; }
location ~ /\. { deny all; }
# קבצים רגישים של המערכת (ב-Apache נחסמים ב-system/.htaccess)
location ~ ^/system/(config\.php|market_settings\.json.*|system_tags\.json|.*_keys?\.json)$ { return 404; }
location ~ ^/cache/items/.*\.chc$ { return 404; }
# SCSS ו-minify מגיעים ל-PHP
location ~ \.scss\.css$ { rewrite ^ /index.php last; }
location ~ ^/minify/.+\.(js|css)$ { rewrite ^ /index.php last; }
# נכסים סטטיים: קובץ אמיתי או 404
location ~ ^/(cache|application/views|application/views_mobile|system/js|system/libraries|system/css|media|themes)/ {
try_files $uri =404;
}
# front controller
location / {
try_files $uri /index.php?$query_string;
}
location = /index.php {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root/index.php;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}
# אין להריץ שום PHP אחר ישירות
location ~ \.php$ { return 404; }
}
5. טבלאות ושורות פתיחה#
אין אשף התקנה. את הטבלאות ושורות הפתיחה מריצים ידנית. ה-DDL המלא של כל הטבלאות נמצא בSQL להקמה, והמפה שלהן בסכמת הטבלאות. כאן רק מה שנדרש כדי שבקשה ראשונה וכניסה לניהול יעבדו, לפי הטבלאות שהקוד קורא בכל בקשה (CRM_platforms, CRM_params, CRM_modules, CRM_seoUrl, CRM_redirections, CRM_langs_words, CRM_scripts, CRM_minify_*) ובניהול (CRM_adminPanel_*).
SET NAMES utf8mb4;
SET sql_mode = '';
CREATE TABLE `CRM_platforms` (
`id` INT NOT NULL AUTO_INCREMENT,
`domain` VARCHAR(255) NOT NULL,
`allowed_domains` TEXT NULL,
`name` VARCHAR(100) NOT NULL DEFAULT '',
`direction` VARCHAR(3) NOT NULL DEFAULT 'rtl',
`ord` INT NOT NULL DEFAULT 0,
`seo_title` VARCHAR(255) NOT NULL DEFAULT '',
`seo_keywords` TEXT NULL,
`seo_description` TEXT NULL,
`deftheme` VARCHAR(50) NOT NULL DEFAULT 'default',
PRIMARY KEY (`id`), UNIQUE KEY `uq_domain` (`domain`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE `CRM_params` (
`id` INT NOT NULL AUTO_INCREMENT,
`sysName` VARCHAR(100) NOT NULL,
`value` TEXT NULL,
`description` TEXT NULL,
`perm_developer` TINYINT(1) NOT NULL DEFAULT 0,
PRIMARY KEY (`id`), UNIQUE KEY `uq_sysname` (`sysName`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE `CRM_modules` (
`id` INT NOT NULL AUTO_INCREMENT,
`moduleName` VARCHAR(100) NOT NULL,
`active` TINYINT(1) NOT NULL DEFAULT 1,
PRIMARY KEY (`id`), UNIQUE KEY `uq_module` (`moduleName`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE `CRM_seoUrl` (
`id` INT NOT NULL AUTO_INCREMENT,
`sysname` VARCHAR(500) NOT NULL,
`pageurl` VARCHAR(500) NOT NULL,
`regularexp` TINYINT(1) NOT NULL DEFAULT 0,
`ord` INT NOT NULL DEFAULT 0,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE `CRM_redirections` (
`id` INT NOT NULL AUTO_INCREMENT,
`url_from` VARCHAR(500) NOT NULL,
`url_to` VARCHAR(500) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE `CRM_langs_words` (
`id` INT NOT NULL AUTO_INCREMENT,
`sysName` VARCHAR(100) NOT NULL,
`group_id` INT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE `CRM_langs_words_content` (
`id` INT NOT NULL AUTO_INCREMENT,
`parentId` INT NOT NULL,
`langId` INT NOT NULL,
`value` TEXT NULL,
PRIMARY KEY (`id`), UNIQUE KEY `uq_parent_lang` (`parentId`,`langId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE `CRM_scripts` (
`id` INT NOT NULL AUTO_INCREMENT,
`name` VARCHAR(255) NOT NULL DEFAULT '',
`is_active` TINYINT(1) NOT NULL DEFAULT 1,
`area` VARCHAR(20) NOT NULL DEFAULT 'head',
`script` TEXT NULL,
`ord` INT NOT NULL DEFAULT 0,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE `CRM_minify_groups` (
`id` INT NOT NULL AUTO_INCREMENT,
`name` VARCHAR(100) NOT NULL DEFAULT '',
`type` VARCHAR(3) NOT NULL DEFAULT 'js',
`area` VARCHAR(20) NOT NULL DEFAULT 'head',
`async` TINYINT(1) NOT NULL DEFAULT 0,
`theme` VARCHAR(50) NOT NULL DEFAULT '',
`ord` INT NOT NULL DEFAULT 0,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE `CRM_minify_files` (
`id` INT NOT NULL AUTO_INCREMENT,
`url` VARCHAR(500) NOT NULL,
`group_id` INT NOT NULL,
`ord` INT NOT NULL DEFAULT 0,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE `CRM_adminPanel_admins_groups` (
`id` INT NOT NULL AUTO_INCREMENT,
`name` VARCHAR(100) NOT NULL,
`perms` MEDIUMTEXT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE `CRM_adminPanel_admins` (
`id` INT NOT NULL AUTO_INCREMENT,
`uname` VARCHAR(100) NOT NULL,
`password` VARCHAR(255) NOT NULL DEFAULT '',
`name` VARCHAR(100) NOT NULL DEFAULT '',
`email` VARCHAR(255) NOT NULL DEFAULT '',
`phone` VARCHAR(30) NOT NULL DEFAULT '',
`group_id` INT NOT NULL DEFAULT 0,
`allow_ip` TEXT NULL,
`skip_otp` TINYINT(1) NOT NULL DEFAULT 0,
`auth_provider` VARCHAR(30) NOT NULL DEFAULT 'local',
`external_id` VARCHAR(255) NULL,
`developer_token` TEXT NULL,
`last_login` DATETIME NULL,
`phone_last_code` VARCHAR(255) NOT NULL DEFAULT '',
`phone_last_code_date` VARCHAR(40) NOT NULL DEFAULT '',
PRIMARY KEY (`id`), UNIQUE KEY `uq_uname` (`uname`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE `CRM_adminPanel_admins_bad_attempts` (
`id` INT NOT NULL AUTO_INCREMENT,
`uname` VARCHAR(100) NOT NULL DEFAULT '',
`password` VARCHAR(255) NOT NULL DEFAULT '',
`ip` VARCHAR(45) NOT NULL DEFAULT '',
`date_created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE `CRM_adminPanel_panels_categories` (
`id` INT NOT NULL AUTO_INCREMENT,
`name` VARCHAR(100) NOT NULL,
`ord` INT NOT NULL DEFAULT 0,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE `CRM_adminPanel_panels` (
`id` INT NOT NULL AUTO_INCREMENT,
`category` INT NOT NULL DEFAULT 0,
`panel_name` VARCHAR(100) NOT NULL,
`text` VARCHAR(255) NOT NULL DEFAULT '',
`icon` TEXT NULL,
`ord` INT NOT NULL DEFAULT 0,
`perm_developer` TINYINT(1) NOT NULL DEFAULT 0,
PRIMARY KEY (`id`), UNIQUE KEY `uq_panel` (`panel_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE `CRM_adminPanel_panels_options` (
`id` INT NOT NULL AUTO_INCREMENT,
`panelId` INT NOT NULL,
`filename` VARCHAR(100) NOT NULL,
`name` VARCHAR(255) NOT NULL DEFAULT '',
`ord` INT NOT NULL DEFAULT 0,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CRM_adminPanel_sessions לא ברשימה: ADMIN::ensure_sessions_table() יוצרת אותה בכניסה הראשונה (וגם מוסיפה ל-CRM_adminPanel_admins את auth_provider ו-external_id אם חסרות). טבלאות נוספות (אחסון, Cron, SEO, AI) מגיעות עם wizzo_update או מה-DDL המלא.
ה-DDL לעיל כולל את העמודות שהקוד קורא או כותב. סוגי העמודות, האורכים והאינדקסים הם בחירה סבירה. הטבלאות האמיתיות נוצרות ב-wizzo_update לפי check_version/db_structure של השרת הראשי, ושם הסוגים הם ש"נכונים". ההשוואה ביניהם מומלצת אחרי הרצת wizzo_update בפעם הראשונה.
שורות פתיחה#
-- פלטפורמה (= שפה 1). בלי שורה כזו CONFIG::init נופל על TypeError.
-- domain חייב להיות בדיוק ה-SERVER_NAME שהשרת מדווח. ב-allowed_domains: |domain||domain2|
INSERT INTO CRM_platforms (id, domain, allowed_domains, name, direction, ord, seo_title, deftheme)
VALUES (1, 'example.com', '', 'עברית', 'rtl', 1, 'האתר שלי', 'default');
-- פרמטרים שהליבה קוראת
INSERT INTO CRM_params (sysName, value, description, perm_developer) VALUES
('attaches_version', '1', 'cache-buster לנכסים', 1),
('log_sql', '0', '1 = רישום שאילתות ל-sqllog.txt', 1),
('cron_runner_token', REPLACE(UUID(),'-',''), 'טוקן שנדרש ב-/system/cron_runner (tk)', 1);
-- קבוצת מנהלים עם הרשאות מלאות ('-1' = הכול)
INSERT INTO CRM_adminPanel_admins_groups (id, name, perms) VALUES (1, 'Admins', '-1');
-- המנהל הראשון: uname באותיות קטנות, password הוא hash של bcrypt, skip_otp=1 מדלג על קוד במייל/SMS
INSERT INTO CRM_adminPanel_admins (uname, password, name, email, group_id, skip_otp)
VALUES ('admin', 'PASTE_BCRYPT_HASH_HERE', 'Admin', 'admin@example.com', 1, 1);
-- תפריט הניהול
INSERT INTO CRM_adminPanel_panels_categories (id, name, ord) VALUES (1, 'הגדרות', 1);
INSERT INTO CRM_adminPanel_panels (category, panel_name, text, ord, perm_developer) VALUES
(1, 'wizzo_update', 'עדכון גרסה', 1, 0),
(1, 'Params', 'הגדרות מערכת', 2, 0),
(1, 'clear_cache', 'ניקוי מטמון', 3, 0),
(1, 'cron_manager', 'תזמונים', 4, 0),
(1, 'Modules', 'מודולים', 5, 0);
-- מודול דף הבית (הקובץ application/controllers/home.php יתווסף ב"העמוד הראשון שלך")
INSERT INTO CRM_modules (moduleName, active) VALUES ('home', 1);
ליצירת hash לסיסמה (הפקודה מדפיסה מחרוזת שמתחילה ב-$2y$, ו-libraries/bcrypt.php מאמת אותה דרך crypt()):
php -r 'echo password_hash("CHOOSE_A_PASSWORD", PASSWORD_BCRYPT), PHP_EOL;'
skip_otp ופאנלי מפתח
בלי skip_otp = 1 הכניסה שולחת קוד במייל או ב-SMS דרך שירותי Wizzo, ובאתר חדש הם עשויים לא להיות זמינים. הפאנלים עם perm_developer = 1 נראים רק למנהל שיש לו developer_token (מונפק דרך WIZZO ID), ולכן מנהל שנוצר ידנית לא רואה אותם. בדוגמה wizzo_update ו-Modules נרשמו עם perm_developer = 0 כדי שיהיו נגישים. פרטים: התחברות, סשנים ו-WIZZO ID והרשאות וקבוצות.CRM_platforms נקראת פעם אחת למטמון לתקופה של 30 יום (cache/platforms.chc.gz, או .chc כשהדחיסה כבויה), ו-CRM_params גם כן. אם שניתם שורה אחרי הבקשה הראשונה, מחקו את קובץ המטמון או נקו אותו בפאנל clear_cache (ניקוי ותחזוקה). אחרת השינוי לא יופיע. אותו דבר ב-CRM_modules (7 ימים).6. תיקיות כתיבה#
cd /var/www/site
mkdir -p cache media system application/controllers application/models application/includes application/admin themes/default
chown -R www-data:www-data cache media system
chmod -R u+rwX cache media system
החליפו www-data במשתמש ה-PHP אצלכם (אצל cPanel זה משתמש האתר). הליבה כותבת לבד את system/.htaccess (בלוק שחוסם config.php, market_settings.json* ומפתחות, ב-market_service::protect_settings_dir), ולכן system/ חייבת להיות ניתנת לכתיבה. לא אימתנו באיזו בקשה בדיוק הוא נכתב לראשונה באתר ללא שוק מחובר, לכן בדקו שהקובץ קיים. הרשימה המלאה: דרישות מערכת.
7. ערכת נושא מינימלית#
בקשה רגילה (לא בקשת ניהול ולא בקשת /system/... שמבקשת pmode=empg) טוענת את themes/<deftheme>/index.php. בלי הקובץ הזה זו שגיאה קטלנית של require. ערכת הניהול מגיעה מהליבה ולא צריכה ערכה באתר.
<?php
// themes/default/index.php
class TEMPLATE_default extends bgl_theme
{
public function index()
{
$this->vars["site_name"] = CONFIG::$platform_data["name"];
}
}
{* themes/default/index.tpl *}
<main>{$content nofilter}</main>
שם המחלקה הוא TEMPLATE_ ואחריו הערך של deftheme, ולכן deftheme יכול להכיל רק אותיות, ספרות וקו תחתון. עיצוב מלא, נכסים ו-SCSS: ערכות נושא ותבניות Smarty.
8. כניסה ראשונה לניהול#
- גלשו אל
https://example.com/wizzocms(הערך שלCONFIG_USER::$admin_url). - הזינו את שם המשתמש והסיסמה מהשלב הקודם.
- בכניסה מוצלחת מתקבל עוגיית
admin_session(תקפה 7 ימים) ונפתח הפאנלhome.
עוד פרטים על הכניסה:
- בקשת הכניסה (
POST) נבדקת מולHTTP_REFERER: המארח שלו חייב להיותsite_urlאו אחד מ-allowed_domains, אחרת השרת עונהerror. - הטופס נושא
csrf_token(double-submit cookie), ובקשה עם טוקן שגוי מקבלתcsrf_error. - אחרי 5 ניסיונות כושלים למשתמש (או 15 לכתובת IP) בעשר דקות הכניסה נחסמת זמנית.
- עכשיו אפשר להריץ
wizzo_updateכדי שהאתר ימלא את שאר הטבלאות והפאנלים מהשרת הראשי (דורש יציאת HTTPS). ראו עדכון ליבה.
9. Cron#
הוסיפו את השורה ל-crontab של משתמש האתר (crontab -e), כשה-tk הוא הערך של cron_runner_token מ-CRM_params:
* * * * * cd /var/www/site && /usr/bin/php index.php url=system/cron_runner tk=<TOKEN> >/dev/null 2>&1
SELECT value FROM CRM_params WHERE sysName = 'cron_runner_token';
הפאנל cron_manager (אם רשום) מציע שורה מוכנה. הוא מייצר את הטוקן אם חסר, אבל השורה שהוא מציע לא כוללת cd, ולכן הוסיפו אותו (הסבר בדרישות מערכת). מי שרוצה שהפאנל יתקין את ה-crontab בעצמו צריך ש-exec יהיה פעיל. לפרטים: מנהל משימות (Cron).
10. בדיקות מהירות#
| בדיקה | תוצאה צפויה |
|---|---|
curl -s https://example.com/system/core_ping | JSON מהצורה {"ok":true,"version":"5.0.115","db":true,"php":"8.3.x"} |
https://example.com/wizzocms | טופס כניסה, ואחרי כניסה ה-home של הניהול |
https://example.com/ | דף הבית מהקונטרולר home, אחרי העמוד הראשון שלך. לפני כן התגובה היא 404 |
ls cache | קיימת תיקייה לפי שם הדומיין עם .chc.gz |
crontab -l ושורת הלוגים של cron_manager | משימה ראשונה רצה בתוך דקה |
כשמשהו לא עובד#
| סימפטום | סיבה סבירה | מה עושים |
|---|---|---|
| 503 | חיבור PDO נכשל (MISC::service_unavailable) | בדקו db_host, db_dbname, משתמש וסיסמה. הסיבה נכתבת ל-error_log |
TypeError ב-CONFIG::init, או מסך לבן | CRM_platforms ריקה או לא קיימת | הוסיפו שורה (שלב 5) ונקו את cache/platforms.chc.gz (או .chc) |
URL ERROR | SERVER_NAME לא תואם ל-domain ולא מופיע ב-allowed_domains | תקנו את השורה, או הוסיפו את הדומיין ל-allowed_domains בפורמט ` |
Access to undeclared static property | חסר מאפיין ב-CONFIG_USER | השוו לרשימה בשלב 3 |
שגיאה קטלנית על vendor/autoload.php | לא הורץ composer install | הריצו אותו ב-system/core |
שגיאה קטלנית על themes/default/index.php | חסרה ערכת נושא | שלב 7 |
404 על כל כתובת למעט / | mod_rewrite או AllowOverride כבויים, או שאין .htaccess | בדקו את שרת הרשת |
| 404 על דף הבית | חסרה שורה פעילה ב-CRM_modules או חסר application/controllers/home.php | הוסיפו, ונקו את מטמון modules_lang |
כניסה לניהול מחזירה error | חסר Referer מתאים | היכנסו דרך הדומיין עצמו ולא דרך כלי שלא שולח אותו |
פאנל מפנה ל-/404 | אין לו שורה ב-CRM_adminPanel_panels, או שהמנהל חסר הרשאה | רשמו את הפאנל. ראו בניית פאנל |
| קומפילציית תבנית נכשלת ב-cron | cron רץ מתיקייה אחרת | הוסיפו cd ל-crontab |
מה הלאה#
- מבנה התיקיות של אתר: מה בכל תיקייה ואיך מסדרים את
application/. - קונפיגורציה: כל מפתחות
CONFIG_USER. - העמוד הראשון שלך: קונטרולר, תבנית וכתובת.
- הקשחה: חסימת קבצים רגישים וחלוקת הרשאות לפני עלייה לאוויר.