# Sunucu Modülü Araçları

https://dev.wisecp.com/tr/sunucu-modulu-araclari

Araçlar, müşterinin hizmetin içinde gördüğü sayfalardır: dosya yöneticisi, veritabanları, DNS, zamanlanmış görevler. Etrafındaki her şeyi taban sınıf üstlenir; siz iki metot ve bir şablon verirsiniz.

## Genel Bakış

`ServerModule`, gruplanmış ve sıralanmış bir araç tanımı kataloğu taşır; hepsi kapalı gelir. Birini açmak, onu desteklenir ilan etmek ve iki soruyu yanıtlamak demektir: sayfa neyi gösteriyor ve buton ne yapıyor.

Tarayıcı ile o iki yanıt arasındaki her şey taban sınıfa aittir.

- **get_tool_data()**: Okuma yolu: tanımı doğrular, `tool_data()` metodunuzu çağırır, satırları normalleştirir, filtre kancasını çalıştırır, istek başına önbelleğe alır.
- **handle_tool_action()**: Yazma yolu: her alanı temizler ve doğrular, yeteneği denetler, `tool_action()` metodunuzu çağırır, sırları maskeleyerek hizmet geçmişine yazar.
- **create_tool_table()**: Listeleme tablosunu bildirilmiş kolonlardan ve satır geri çağrısından kurar.
- **has_client_management()**: Müşterinin Yönetim sekmesi alıp almayacağı: etkin bir araç ya da müşteri alanında kalan bir kart varken true.
- **yetenek**: Aracın sunduğu tek fiil: izin kapısı ve arayüz işareti.

## Ön Koşullar

- Çalışan bir sunucu modülü ([Sunucu Modülü Yazma](https://dev.wisecp.com/tr/sunucu-modulu-yazma)). Araçlar ancak `create()` hesap kimliğini sakladıktan sonra açılır.
- Ana varlığın canlı sağlayıcıda hem oluşturmayı hem silmeyi desteklediğini doğrulayın.
- Sağlayıcının ücretlendirdiği bir şey oluşturuluyorsa önce kotayı planlayın.

## Yapı

Altı katman, üçü sizin.

| Katman | Nerede | Ne tutar |
| --- | --- | --- |
| Katalog | `coremio/classes/ServerModule.php` | Tanım, filtreler, kurallar, kolonlar, satır geri çağrıları |
| Aracı açma | `config.php` ya da `configure_features()` | Desteklenen araçlar ve yetenekleri |
| Okuma | Modülünüz, `tool_data()` | Araç başına bir dal, sonra bir fetch metodu |
| Yazma | Modülünüz, `tool_action()` | Araç başına bir dal, sonra bir aksiyon yönlendiricisi |
| Arayüz | `templates/system/module/service/hosting/tools` | Araç adı başına paylaşılan şablon |
| Metin | `coremio/locale/en/cm/system/module.php` | Paylaşılan etiketler ve mesajlar |

## Adım Adım

### Aracı Açın

Adı yapılandırmada bildirmek aracı varsayılan yetenekleriyle açar.

```php
// config.php: bildirimsel yarı.
return [
    // ... modül yapılandırmasının geri kalanı
    'supported' => [
        'tools' => ['file-manager', 'ftp-accounts', 'databases', 'cron-jobs'],

        // Araç başına seçenek ezmeleri; set_tool() metodunun yazacağı anahtarların aynısı.
        'tool_options' => [
            'file-manager' => ['allow_upload_overwrite' => true, 'allow_chmod_recursive' => true],
        ],
    ],
];
```

```php
public function configure_features(): void
{
    // Yapılandırma listesiyle aynı etki; karar canlı duruma bağlı olduğunda işe yarar.
    $this->support_tools(['cron-jobs' => ['list', 'create', 'delete']]);

    // Sağlayıcıda hesap başına kota ve zamanlanmış görevlerde e-posta alanı yok,
    // bu yüzden karşılayamadığımız yetenekleri ve besledikleri kolonu kaldırın.
    $this->remove_tool_capability('ftp-accounts', ['quota']);
    $this->remove_tool_column('ftp-accounts', ['quota']);

    $this->set_tool_order('databases', 5);
}
```

### Okuma Yolu

`tool_data()` tek bir dağıtıcıdır; araca özgü işi özel fetch metotlarında tutun.

1. `['items' => [...]]` döndürün; her satır kolon adıyla anahtarlanır, yanına şablonun okuduğu ek anahtarları koyun.

### Yazma Yolu

`tool_action()` aynı biçimdedir; aksiyon fiiline göre eşleştiren bir yönlendiriciye dağıtır.

1. Sağlayıcı reddederse fırlatın; mesaj müşteriye kırmızı uyarı olarak ulaşır.
2. `[]` döndürün ya da arayüz bir değer bekliyorsa bir yük döndürün.

### Kolonlar ve Metin

Kolon başlıkları, etiketler ve başarı mesajları paylaşılan dil dosyasından gelir. Şablon paylaşılır, yani bir alan adı sözleşmedir. Sağlayıcıya özgü dizeler kendi modül dil dosyanızda durur.

Etkinlik günlüğü her işlemi `activity_action_label()` ile adlandırır: taban sınıf yaygın sanal sunucu işlemleri için `system/module/al-action/{kod}` anahtarına bakar, bulamazsa kodu okunur biçimde gösterir. Kodun kendisi süzgeç değeri olarak kalır. Sağlayıcınız kendi işlem adlarını kullanıyorsa metodu ezip etiketleri modül dil dosyanızdan döndürün.

## Referans

### Araç Tanımı

```php
$this->tools['ftp-accounts'] = [
    'name'         => 'FTP Accounts',
    // Grup adları config.php'de bildirilen modül tipine bağlıdır:
    //   hosting: files, databases, domains, email, software, security
    //   server:  power, system, network, storage
    'group'        => 'files',
    'order'        => 20,               // grup içindeki konum
    'icon'         => 'bi bi-hdd-network',   // bir Bootstrap simgesi ya da uzak bir görsel için 'img:<url>'
    'page'         => 'ftp-accounts',   // tools/ altındaki şablon dosyasının adı
    'type'         => 'page-loader',    // 'page-loader' bir sayfa basar, 'action' tek bir düğmedir
    'supported'    => false,            // modülünüz bunu açar
    'capabilities' => ['list', 'create', 'edit', 'delete', 'quota', 'directory'],
];
```

- **supported**: `false` gelir. Desteklenmeyen bir araca gelen istek, kodunuz çalışmadan reddedilir.
- **capabilities**: Hem izin listesi hem arayüz işareti; buton görünmeden önce denetlenir.
- **type**: `'page-loader'`, `page` ile adlandırılan şablonu yükler; `'action'` araç ızgarasındaki tek butondur, isteğe bağlı `'confirm' => true` ile.
- **options**: Şablonun okuduğu araç başına anahtarlar. Yapılandırmadan ya da çalışma anında ayarlanır.
- **yetenek takma adları**: `get_content` ve `save_content` `edit` sayılır; `update_email` ise `email` sayılır.

### Modül İmzaları

```php
// Taban sınıfın çağırdığı iki metot. $params temizlenmiş sorgu parametrelerini,
// $data ise temizlenmiş POST gövdesini taşır.
public function tool_data(string $tool, string $action = 'index', array $params = []): array;
public function tool_action(string $tool, string $action, array $data = []): array;

// İsteğe bağlı: kataloğu bu modüle göre düzenler. Sunucu kaydı bağlandığında
// (yapıcıdan, set_server üzerinden) ve bir kez de set_service içinden koşar.
public function configure_features(): void;

// İsteğe bağlı: yalnız parola sıfırlama aracının parolayı panele ürettirmesi gerektiğinde.
protected function panel_generated_password(): string;
```

### Taban Sınıf Yardımcıları

```php
public function support_tools(array $tools): static;
public function add_tool(string $key, array $config): static;
public function set_tool(string $key, array $config): static;
public function remove_tool(string $key): static;
public function set_tool_order(string $key, int $order): static;

public function add_tool_capability(string $key, array|string $capabilities): static;
public function remove_tool_capability(string $key, array|string $capabilities): static;

public function add_tool_column(string $tool, string $column, array $config): static;
public function remove_tool_column(string $tool, array|string $columns): static;

public function add_tool_group(string $key, array $config): static;
public function set_tool_group_order(string $key, int $order): static;

public function get_tool(string $key): ?array;
public function get_tools(): array;
public function get_effective_tools(): array;
public function get_disabled_features(): array;
public function has_client_management(): bool;
```

> **support_tools() argümanını iki türlü okur**
> 
> Düz bir liste elemanı aracı varsayılan yetenekleriyle açar. Bir diziye işaret eden anahtar ise o listeyi geçtiğinizle **değiştirir**.

### Doğrulama Katmanları

Üçü de taban sınıfta bildirilir ve aksiyon metodunuzdan önce çalışır.

```php
// 1. Temizleme. Listede olmayan her alan 'hclear' filtresine düşer.
protected function get_tool_action_data_filters(): array;
// Bu araç için ürünle gelen kural, birebir. FTP kullanıcı adının e-posta gibi
// filtrelendiğine dikkat edin: paneller user@domain biçimini kabul eder.
// 'ftp-accounts' => ['username' => 'email', 'password' => null,
//                    'directory' => 'path', 'quota' => 'numeric']
// Filtreler: hclear, numeric, route, identifier, domain, subdomain, hostname,
//            email, email_list, ip, url, path, json_filenames, null (dokunmadan geçer)

// 2. Aksiyon başına zorunlu alanlar. Kırpma sonrası boş dize fırlatır.
protected function get_tool_action_rules(): array;
// 'ftp-accounts' => ['create' => ['username', 'password'],
//                    'edit' => ['username'], 'delete' => ['username']]

// 3. Biçim denetimleri; zorunluluk denetiminden sonra koşar (işlemin zorunlu alanı
//    olmasa da), boş değerlerde atlanır.
protected function get_tool_action_field_validations(): array;
// 'mx-entry' => ['create' => ['domain' => ['domain'], 'priority' => ['numeric']]]
// Kurallar: url, email, email_list, email_local, ip, ipv4, ipv6, ip_or_wildcard,
//           domain, dns_label, dns_name, numeric, cron_field, enum:a,b,c, min:N, max:N
// Tanınmayan kural adı atlanır ve hata kütüğüne uyarı olarak yazılır.
```

> **Parola alanı null filtresiyle bildirilmelidir**
> 
> Diğer her filtre parolayı güçlü kılan karakterleri siler: hesap, müşterinin yazmadığı bir sırla açılır.

### tool_action() Dönüşleri

- **[]**: Sıradan başarı: taban sınıf önce `action-success-{tool}-{action}`, sonra `action-success-{action}`, sonra üretilmiş bir etiket arar.
- **['status' => 'successful', 'message' => '...']**: Kendi metninizle başarı, olduğu gibi döner.
- **['stream' => ...]**: Dosya indirme; taban sınıf yanıtı akıtır ve istek orada biter.
- **['redirect' => ...]**: Ayrılmış: aynı sekmede anında tam sayfa yönlendirme.
- **fırlatma**: Başarısızlık. Mesaj müşteriye ulaşır, bu yüzden çevrilmiş yazın.

### Şablon Değişkenleri

```php
/** @var ServerModule $module      canlı örnek, yani $module->service erişilebilir */
/** @var string       $tool        slug */
/** @var array        $tool_config tanım; yetenekler ve seçenekler dahil */
/** @var string       $tool_label  çevrilmiş araç adı */
/** @var string       $action      sayfa bir alt aksiyonla açılmadıysa 'index' */
/** @var array        $data        fetch metodunuzun döndürdüğünün aynısı */
/** @var mixed        $table       hazırlanmış listeleme tablosu, aracın kolonu yoksa null */
/** @var array        $tables      alt tablolar, kendi slug'larıyla anahtarlanır */
/** @var bool         $admin_view  admin panelde true, müşteri panelinde false */
/** @var string|null  $error       fetch başarısız olduğunda dolar */
```

| Tarayıcı fonksiyonu | Amacı |
| --- | --- |
| `request_tool_action(tool, action, data, options)` | Tek bir aksiyonu gönderir, buton göstergesi ve bildirimle |
| `reload_module_content(tool)` | Araç sayfasını değişiklikten sonra yeniden yükler |
| `open_modal(id, {title, body, footer})` | Bir pencere kurar ve açar |
| `confirmDeleteModal({message, description, buttonText, onConfirm})` | Standart silme onayı |
| `watchRequired(selector)` | Zorunlu alanlar dolunca gönderi etkinleştirir |
| `passwordInput(id, placeholder, options)` | Üret, göster ve kopyala butonlu parola alanı |

## Örnek

Baştan sona tek bir araç: okuma, yönlendirici ve taban sınıf.

```php
public function tool_data(string $tool, string $action = 'index', array $params = []): array
{
    $username = $this->options['config']['user'] ?? '';
    $domain   = $this->options['domain'] ?? '';

    return match ($tool) {
        'ftp-accounts' => $this->fetch_ftp_accounts($username, $domain),
        'databases'    => $this->fetch_databases($username),
        default        => [],
    };
}

private function fetch_ftp_accounts(string $username, string $domain): array
{
    $response = $this->api->call('ftp/list', ['username' => $username]);

    $items = [];
    foreach ($response['data'] ?? [] as $row)
        $items[] = [
            'username'  => $row['user'] ?? '',
            'directory' => $row['dir'] ?? '/',
            'quota'     => (int) ($row['quota'] ?? 0),
        ];

    // 'items' tabloyu besler; geri kalan her şeyi şablon okur.
    return ['items' => $items, 'domain' => $domain];
}

public function tool_action(string $tool, string $action, array $data = []): array
{
    $username = $this->options['config']['user'] ?? '';

    return match ($tool) {
        'ftp-accounts' => $this->action_ftp_accounts($action, $data, $username),
        default        => throw new Exception($this->lang['err-tool-unknown']),
    };
}

private function action_ftp_accounts(string $action, array $data, string $username): array
{
    return match ($action) {
        'create' => $this->ftp_create($data, $username),
        'delete' => $this->ftp_delete($data, $username),
        default  => throw new Exception($this->lang['err-action-unknown']),
    };
}

private function ftp_create(array $data, string $username): array
{
    $this->api->call('ftp/create', [
        'account'   => $username,

        // Bildirilen filtrelerle zaten temizlendi ve varlığı zaten denetlendi.
        'user'      => $data['username'] ?? '',
        'password'  => $data['password'] ?? '',
        'directory' => $data['directory'] ?? '/',
    ], 'POST');

    // Boş dizi: çevrilmiş başarı mesajını taban sınıf yazar.
    return [];
}
```

```php
// ServerModule::handle_tool_action, önemli olan sıraya indirgenmiş hali.
$tool   = Filter::init("REQUEST/tool", "route");
$action = Filter::init("REQUEST/action", "route") ?: 'index';
$data   = !empty($_POST) ? $_POST : $_GET;
unset($data['operation'], $data['method'], $data['tool'], $data['action']);

$data = $this->sanitize_tool_action_data($tool, $data);

$tool_config = $this->get_tool($tool);
if (!$tool_config) throw new \Exception('Tool not found');
if (empty($tool_config['supported'])) throw new \Exception('Tool not supported');

$this->check_tool_capability($tool_config, $action);
$this->validate_tool_action($tool, $action, $data);

$result = $this->tool_action($tool, $action, $data);

// Aksiyon hizmet geçmişine ulaşmadan önce sırlar maskelenir.
foreach ($data as $k => $v)
    if (is_string($v) && $v !== '' && preg_match('/pass(word)?|secret|token/i', (string) $k))
        $data[$k] = '***';

if (empty($result)) return $this->tool_action_success_response($tool, $action);

return $result;
```

```javascript
var TOOL = 'ftp-accounts';

window.ftpCreateSubmit = function (btn) {
    request_tool_action(TOOL, 'create', {
        username:  document.getElementById('ftpUser').value,
        password:  document.getElementById('ftpPass').value,
        directory: document.getElementById('ftpDir').value,
    }, {
        button: btn,
        buttonLoader: creating_loader,
        successToast: true,
        afterDone: function () {
            close_modal(document.querySelector('.modal.show'));
            reload_module_content(TOOL);
        },
    });
};
```

## Tuzaklar

> **Uygulanmamış bir yetenek, hata fırlatan bir butondur**
> 
> Arayüz her varsayılan yetenek için bir denetim gösterir. Yönlendiricinizin karşılamadığını kaldırın, yoksa müşteri Düzenle'ye tıklar ve "Action not available" alır.

> **Ücretli kaynak kota kapısı ister**
> 
> Açık bir oluştur butonu merakı sizin faturanıza dönüştürür. Kaynağı bir eklentiye bağlayın ve hak tükendiğinde aksiyonu reddedin.

> **Bir form alanına asla action, operation, method ya da tool adını vermeyin**
> 
> Tarayıcı yardımcısı gövdeyi araç aksiyonuyla kurar, sonra verinizi üzerine birleştirir. `action` adlı bir alan fiili ezer ve dağıtıcı hata fırlatır. Ön ek verin (`task_action`) ve adı şablonda, zorunlu alan kurallarında ve aksiyon metodunuzda değiştirin.

> **redirect anında sayfadan çıkarır**
> 
> Yardımcı o anahtarı aynı sekmede, geri çağrınız bitmeden işler. Bir pencerede ya da yeni sekmede açmak istediğiniz URL için `console_url` gibi nötr bir ad kullanın.

> **Yalnız listeleyen araç yayınlanmaz**
> 
> Sağlayıcı aracın ana varlığını hem oluşturamıyor hem silemiyorsa araç yayınlanmaz. Yapılandırmadan çıkarın ve ölü metotları silin.

> **Aracı olmayan modülde Yönetim sekmesi açılmaz**
> 
> Müşterinin Yönetim sekmesi yalnız `has_client_management()` true iken açılır: en az bir etkin araç ya da müşteri alanında kalan bir kart. Göstergeler, hesap bilgisi, kaynaklar ve bağlantılar Genel Bakış sekmesinde durduğu için sayılmaz. Çekirdek bu soruyu operatörün kısıtlamaları uygulanmış hâlde sorar; Tercihler'de her aracı gizlemek de sekmeyi kaldırır. Yalnız tek tıkla giriş sunan bir panelin başlatıcısı Hızlı İşlemler'de kalır.

## İlgili Makaleler

- [Sunucu Modülü Yazma](https://dev.wisecp.com/tr/sunucu-modulu-yazma)
- [Arayüz Bileşenleri](https://dev.wisecp.com/tr/arayuz-bilesenleri)
- [Kullanıcı Girdisini Filtreleme](https://dev.wisecp.com/tr/kullanici-girdisini-filtreleme)
- [Müşteri Paneli Köprüsü](https://dev.wisecp.com/tr/musteri-paneli-koprusu)
- [Çeviriler ve Dil Dosyaları](https://dev.wisecp.com/tr/ceviriler-ve-dil-dosyalari)
- [Ürün Modülü Müşteri Yönetimi](https://dev.wisecp.com/tr/urun-modulu-musteri-yonetimi)
