Admin Sayfası Ekleme
Bir modüle admin panelde kendi sayfasını iki dosyayla açarsınız. HTML'i bir alan sınıfı döndürür; router.php içindeki tek satır onu kaydeder.
Genel Bakış
Kendi kayıtlarını yöneten ya da toplu iş çalıştıran bir modülün gerçek bir sayfaya ihtiyacı vardır. O sayfa çekirdeğe ait değildir: sınıfı bildirir, kaydedersiniz; rota, menü öğesi, yetki kontrolü, künye çizgisi ve tema kabuğu kendiliğinden gelir. Yeniyseniz: İlk Modülünüz.
Dağıtıcı coremio/controllers/admin/module-page.php dosyasıdır; slug rotaları oraya çözülür ve sonuç eklenti tema sarmalayıcısına gider.
Yapı
register(), rota kablolaması, menü kancası, örnek yardımcıları.
page_* ve op_* metotlarını çözer, URL'de görünmez.
Router::loadModuleRouters() router dosyasını rotalardan önce okur.
Adım Adım
Alan Sınıfını Bildirin
- Modül dizininizde
AdminArea.phpoluşturun, ad alanıWISECP\Modules\{Tip}\{Ad}olsun. \ModuleAdminAreasınıfını genişletin ve statikmanifest()metodundan manifest'i döndürün.- Sayfa yalnız bir koşulda anlamlıysa
available()metodunu ezin; menü öğesini de kapılar.
Kaydedin
- Yanına
router.phpoluşturun: sınıf dosyasını include edip\ModuleAdminArea::register(AdminArea::class)çağırın. - Oraya başka bir şey yazılmaz. Kayıt üç rota ekler, manifest'i saklar ve menü öğesini kancalar.
Sayfa Metodu Yazın
page_home()alanın kökünü yanıtlar; her metot URL'in kalan parçalarını dizi olarak alır.- HTML'i heredoc ile kurun; metinlerinizi
$this->langiçinden okuyun. - Kendinize verdiğiniz bağlantıları
$this->link()ile kurun, elle yazılmış bir yolla değil.
Operation Yazın
- Metodu
op_{ad}olarak adlandırın; yalnız bu önek çağrılabilir. - Alan URL'ine bir
operationparametresiyle POST edin; fırlatılan exception standart JSON hatasına dönüşür. $operation->demo()ile başlayın, girdiyiFilter::init()ile okuyun,$operation->output()ile bitirin.
Alan Ayarlarını Saklayın
- Kendi dosyanız değil,
settings(),setting()vesave_settings(). save_settings()mevcut değerlerle birleştirir, yani kısmi bir kayıt gerisini korur, sonraaction:module.area_settings_savedkancasını çalıştırır.
Referans
Taban Sınıf API'si
class ModuleAdminArea
{
public string $module_type = ''; // ad alanından çözülür
public string $module_name = ''; // ad alanından çözülür
public array $lang = []; // {module}/lang/{selected}.php, en.php'ye düşerek
public function __construct();
// Ezeceğiniz metot budur. Varsayılanı olan diğer her şey isteğe bağlıdır.
public static function manifest(): array;
public static function register(string $areaClass): void;
public static function get(string $type, string $name): ?array;
public function available(): bool; // varsayılan true
public function slug(): string;
public function link(array $params = []): string;
public function module_dir(): string; // dosya sistemi yolu, sonda ayraçla
public function module_url(): string; // modül dizininin genel URL'i
public function settings(): array;
public function setting(string $key, mixed $default = null): mixed;
public function save_settings(array $values): void;
protected function license_state_badge(string $slug): string;
}
Kabul edilen tipler: Servers, Payment, Registrars, Product, Addons, SMS, Mail, Authentication, Pipe, Imports ve Fraud. Diğerleri için register() sessizce hiçbir şey yapmaz.
Manifest Neyi Kabul Eder
menu.name yoksa menü etiketi.
Filter::route() yalnız a-zA-Z0-9, tire, alt çizgi ve noktayı korur.
['path' => ['PRODUCTS', 'GROUP_HOSTING_SERVER'], 'name' => 'Hetzner Cloud']. Yol ağaçta aşağı yürür; yazmazsanız öğe oluşmaz.
register() bunları ad alanından ve argümandan yazar. Siz atamayın.
URL Şeması ve Metot Çözümlemesi
// Üç rota, en özgülü başta. $target değeri module-page/{Type}/{Name} olur.
$router->add($slug . '-2', $slug . '/(?)/(?)', $target . '/(1)/(2)');
$router->add($slug . '-1', $slug . '/(?)', $target . '/(1)');
$router->add($slug, $slug, $target);
// link() metodunun rota anahtarını parametre sayısından türetmesinin sebebi budur:
// $this->link() -> /{admin}/{slug}
// $this->link(['configuration']) -> /{admin}/{slug}/configuration
// $this->link(['logs', 'archive']) -> /{admin}/{slug}/logs/archive
| İstek | Çağrılan metot | Argüman |
|---|---|---|
GET /{admin}/{slug} | page_home() | boş dizi |
GET /{admin}/{slug}/configuration | page_configuration() | ['configuration'] |
GET /{admin}/{slug}/logs/archive | page_logs_archive() / page_logs() | ['logs', 'archive'] |
POST + operation=sync_prices | op_sync_prices(Operation $operation) | Operation |
| eşleşen metot yok | 404 sayfası | hiçbir şey görünmez |
Bir parçadaki tire, nokta ve virgül alt çizgiye dönüşür, yani /{slug}/price-list adresi page_price_list() metoduna çözülür. Operation adı da aynı dönüşümden geçer.
Sayfa Metodu Ne Döndürebilir
Dize sayfa gövdesi olur. Dizi, aşağıdaki her anahtarı sarmalayıcıya geçirir; yazmadığınız anahtar gösterilen varsayılana düşer.
| Anahtar | Tip | Etkisi |
|---|---|---|
content | string | Sayfa gövdesi. Boşsa yerine No module area content is available. uyarısı görünür. |
page_title | string | Manifest başlığını ezer. |
page_title_buttons | array | Buton tanımı listesi; düz HTML işe yaramaz. |
page_title_after | string | Başlık ve butonlardan sonra serbest HTML. |
page_title_logo | string | Bir görsel adresi, ya da < içeriyorsa ham HTML. |
content_layout | string | Varsayılan panel; plain paneli kaldırır. |
content_data_class | string | İçerik sarmalayıcısına ek sınıf (plain yerleşimi). |
breadcrumbs | array | Pano + alan başlığı izinin sonuna eklenir. |
plugins | array | Sarmalayıcı varsayılanlarıyla birleşir. |
page_styles, page_scripts, modals | string | Head, footer ve modal alanı. |
Oturumdaki Yönetici
public static function LoginData($type = 'member', $isRemembered = false, $recheck = false);
$adminId = (int) (\UserManager::LoginData('admin')['id'] ?? 0); // panel dışında 0 (CLI, cron)
// Bir atama alanı için operatör seçici.
$staff = \Admin::list(); // [id => ['id' => 1, 'full_name' => 'Jane Doe'], ...]
Örnek
Hayali bir Acme modülü için iki dosyalık alan.
<?php
namespace WISECP\Modules\Servers\Acme;
use AdminFormBuilder;
use Exception;
use Filter;
use Operation;
use User;
use UserManager;
use WDB;
class AdminArea extends \ModuleAdminArea
{
public static function manifest(): array
{
return [
'title' => 'Acme',
// 'slug' => 'acme', // varsayılan: strtolower(modül adı)
'privileges' => ['PRODUCTS_OPERATION'],
'menu' => ['path' => ['PRODUCTS', 'GROUP_HOSTING_SERVER'], 'name' => 'Acme'],
];
}
/** Acme sunucusu yokken menü öğesini gizler ve sayfayı 404 yapar. */
public function available(): bool
{
static $available = null;
if ($available === null)
$available = (bool) WDB::select('id')->from('servers')
->where('type', '=', 'Acme', '&&')
->where('status', '=', 'active')
->build();
return $available;
}
public function page_home(array $params): array
{
$L = $this->lang;
$conf = $this->settings();
$form = new AdminFormBuilder('acmeAreaForm', $this->link(), ['disableStickySubmit' => true]);
$form->addHidden('operation', 'save_settings');
$form->addAmount('profit_rate', $L['profit-rate'] ?? '', (string) ($conf['profit_rate'] ?? 25));
$form->addSwitch('auto_sync', $L['auto-sync'] ?? '', $L['auto-sync-desc'] ?? '', '1', (int) ($conf['auto_sync'] ?? 0) === 1);
return [
'content' => $form->render(),
'page_title' => $L['area-title'] ?? 'Acme',
'page_title_after' => $this->license_state_badge('acme'),
];
}
public function op_save_settings(Operation $operation): bool
{
$operation->demo();
$values = [
'profit_rate' => (float) Filter::init('POST/profit_rate', 'amount'),
'auto_sync' => (int) Filter::init('POST/auto_sync', 'rnumbers') === 1,
];
if ($values['profit_rate'] < 0)
throw new Exception($this->lang['err-negative-rate'] ?? 'The profit rate cannot be negative.');
$this->save_settings($values);
$adminId = (int) (UserManager::LoginData('admin')['id'] ?? 0);
User::addAction($adminId, 'update', 'acme-settings-updated', ['changes' => $values]);
return $operation->output([
'status' => 'successful',
'redirect' => 'reload',
]);
}
}
<?php
namespace WISECP\Modules\Servers\Acme;
include_once __DIR__ . DS . 'AdminArea.php';
\ModuleAdminArea::register(AdminArea::class);
Okuma tarafı dağıtıcıdır; metodunuza ulaşılıp ulaşılmayacağına o karar verir:
$area_info = ModuleAdminArea::get($type, $name);
if (!$area_info) return $this->page_404();
if (($area_info['privileges'] ?? []) && !Admin::isPrivilege($area_info['privileges'])) return 'Access Denied';
$area = new $area_info['class']();
if (!$area->available()) return $this->page_404();
if ($operation = Filter::init('REQUEST/operation')) return $this->area_operation($area, $operation);
$page = Filter::route($this->params[2] ?? '') ?: 'home';
$subpage = Filter::route($this->params[3] ?? '');
$filter_method = fn ($param) => str_replace(['-', '.', ','], '_', $param);
$method = $filter_method('page_' . $page); // page_logs
$method2 = $filter_method($method . '_' . $subpage); // page_logs_archive
$page_params = array_slice($this->params, 2);
if ($subpage && method_exists($area, $method2)) $result = $area->$method2($page_params);
elseif (method_exists($area, $method)) $result = $area->$method($page_params);
else return $this->page_404();
if (!is_array($result)) $result = ['content' => (string) $result];
Tuzaklar
Tanımsız bir property ya da unutulmuş bir import hiç çıktı üretmez ve Exception üzerine kurulu try/catch bunu yakalamaz, çünkü Error bir Exception değildir. En sık vaka Admin::$data['id']'dir: öyle bir statik property yoktur, operatörü UserManager::LoginData('admin') ile okuyun.
Gerçek controller dosyaları dağıtımda kazanır, yani services olarak kaydedilen alana ulaşılamaz ve o anahtarın bağlantıları ezilir. Çekirdeğin kullanmadığı bir slug seçin.
Menü kancası ağaçta aşağı yürür ve bir adım yoksa döner. Ebeveyn dal yetkilerle budanmışsa öğeniz eklenmez, sayfa URL ile erişilebilir kalır.
Alan dağıtıcısı metodunuzu aramadan önce lisansı denetler ve JSON hatasıyla yanıtlar. Sayfalar view katmanında etkilenir.
Configurations tablosundaki satır anahtarı, dizinin yazdığı hâliyle modül adıdır; dizini yeniden adlandırmak ayarları öksüz bırakır. Taşımayı settings() ezerek yapın.
İlgili Makaleler
Geri bildiriminiz için teşekkürler!
Yukarıda bulamadığınız her şey için destek ekibimiz her zaman yanınızda.