# Mask Extension: mask.json Rehberi

Mask extension'ı, kod yazmadan özel içerik elemanları ve sayfa alanları oluşturmanızı sağlar.

---

## Kurulum

```bash
ddev composer require mask/mask
ddev exec vendor/bin/typo3 extension:setup
ddev exec vendor/bin/typo3 cache:flush
```

---

## Mask Nedir?

Mask, backend arayüzünden veya JSON yapılandırması ile özel içerik elemanları (content elements) ve sayfa alanları (page fields) tanımlamanızı sağlar.

**Avantajları:**
- Kod yazmadan özel içerik elemanı oluşturma
- Backend UI ile kolay yapılandırma
- Otomatik TCA, SQL ve Fluid template oluşturma
- İhraç/ithalat desteği (mask.json)

---

## mask.json Yapısı

Mask v9'da JSON yapısı tamamen değişmiştir. Özet farklılıklar:

| Eski Yapı (v7/v8) | Yeni Yapı (v9) |
|---|---|
| `{ "tt_content": { "mask_card": { "elements": {...} } } }` | `{ "version": "9.0.0", "tables": { "tt_content": { "elements": {}, "tca": {} } } }` |
| Element içinde alan tanımları | Alan tanımları `tca` bölümünde ayrı |
| Her alanda `config.type`, `label`, `order` | Element'te `columns`/`labels`/`descriptions` array, TCA'da `type` (mask tipi) |

`packages/testproject/Configuration/Mask/mask.json`:

```json
{
    "version": "9.0.0",
    "tables": {
        "tt_content": {
            "elements": {
                "mask_card": {
                    "key": "mask_card",
                    "label": "Card Element",
                    "description": "Card with title, text and image",
                    "shortLabel": "Card",
                    "icon": "content-card",
                    "color": "#0078D4",
                    "columns": [
                        "tx_mask_card_title",
                        "tx_mask_card_text",
                        "tx_mask_card_image",
                        "tx_mask_card_link"
                    ],
                    "labels": ["Card Title", "Card Text", "Card Image", "Card Link"],
                    "descriptions": ["Main heading of the card", "Description text of the card", "", ""],
                    "columnsOverride": {},
                    "sorting": 0
                },
                "mask_hero": {
                    "key": "mask_hero",
                    "label": "Hero Banner",
                    "description": "Full-width hero section",
                    "shortLabel": "Hero",
                    "icon": "content-header",
                    "color": "#E74C3C",
                    "columns": [
                        "tx_mask_hero_title",
                        "tx_mask_hero_subtitle",
                        "tx_mask_hero_bg_image",
                        "tx_mask_hero_button_text",
                        "tx_mask_hero_button_link"
                    ],
                    "labels": ["Hero Title", "Subtitle", "Background Image", "Button Text", "Button Link"],
                    "descriptions": ["", "", "", "", ""],
                    "columnsOverride": {},
                    "sorting": 1
                }
            },
            "tca": {
                "tx_mask_card_title": {
                    "config": { "type": "input", "max": 255, "eval": "trim" },
                    "key": "card_title",
                    "fullKey": "tx_mask_card_title",
                    "type": "string"
                },
                "tx_mask_card_text": {
                    "config": { "enableRichtext": true, "rows": 5 },
                    "key": "card_text",
                    "fullKey": "tx_mask_card_text",
                    "type": "richtext"
                },
                "tx_mask_card_image": {
                    "config": { "maxitems": 1 },
                    "key": "card_image",
                    "fullKey": "tx_mask_card_image",
                    "type": "file"
                },
                "tx_mask_card_link": {
                    "config": {},
                    "key": "card_link",
                    "fullKey": "tx_mask_card_link",
                    "type": "link"
                },
                "tx_mask_hero_title": {
                    "config": { "type": "input", "max": 255, "eval": "trim" },
                    "key": "hero_title",
                    "fullKey": "tx_mask_hero_title",
                    "type": "string"
                },
                "tx_mask_hero_subtitle": {
                    "config": { "type": "input", "max": 255, "eval": "trim" },
                    "key": "hero_subtitle",
                    "fullKey": "tx_mask_hero_subtitle",
                    "type": "string"
                },
                "tx_mask_hero_bg_image": {
                    "config": { "maxitems": 1 },
                    "key": "hero_bg_image",
                    "fullKey": "tx_mask_hero_bg_image",
                    "type": "file"
                },
                "tx_mask_hero_button_text": {
                    "config": { "type": "input", "max": 100, "eval": "trim" },
                    "key": "hero_button_text",
                    "fullKey": "tx_mask_hero_button_text",
                    "type": "string"
                },
                "tx_mask_hero_button_link": {
                    "config": {},
                    "key": "hero_button_link",
                    "fullKey": "tx_mask_hero_button_link",
                    "type": "link"
                }
            }
        }
    }
}
```

> **v9 Önemli Kurallar:**
> - `elements` içinde her elemanın `columns`, `labels`, `descriptions` array'leri birebir sırayla eşleşmeli
> - `tca` bölümündeki `type` değeri Mask iç tipidir: `string`, `richtext`, `text`, `file`, `link`, `integer`, `float`, `select`, `check`
> - `fullKey` = tam alan adı (`tx_mask_*`), `key` = kısa ad (prefix'siz)

---

## Fluid Template

Template dosyası adı **element key ile birebir eşleşmeli** (`mask_card.html`, `mask_hero.html`).

### mask_card.html

`Resources/Private/Templates/Mask/mask_card.html`:

```html
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers"
      data-namespace-typo3-fluid="true">

<div class="mask-card bg-white rounded-lg shadow-md overflow-hidden">
    <f:if condition="{data.tx_mask_card_image}">
        <div class="card-image">
            <f:for each="{data.tx_mask_card_image}" as="image">
                <f:image image="{image}" maxWidth="800" class="w-full h-52 object-cover"
                         alt="{image.properties.alternative}" />
            </f:for>
        </div>
    </f:if>

    <div class="card-body p-6">
        <f:if condition="{data.tx_mask_card_title}">
            <h3 class="text-xl font-bold mb-3">{data.tx_mask_card_title}</h3>
        </f:if>

        <f:if condition="{data.tx_mask_card_text}">
            <div class="text-gray-600 mb-4">
                <f:format.html>{data.tx_mask_card_text}</f:format.html>
            </div>
        </f:if>

        <f:if condition="{data.tx_mask_card_link}">
            <f:link.typolink parameter="{data.tx_mask_card_link}"
                             class="inline-block mt-2 text-primary font-semibold hover:underline">
                Read More →
            </f:link.typolink>
        </f:if>
    </div>
</div>

</html>
```

### mask_hero.html

`Resources/Private/Templates/Mask/mask_hero.html`:

```html
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers"
      data-namespace-typo3-fluid="true">

<section class="mask-hero relative w-full min-h-[480px] flex items-center overflow-hidden">

    <f:if condition="{data.tx_mask_hero_bg_image}">
        <div class="absolute inset-0 z-0">
            <f:for each="{data.tx_mask_hero_bg_image}" as="bgImage">
                <f:image image="{bgImage}" maxWidth="1920" class="w-full h-full object-cover" alt="" />
            </f:for>
            <div class="absolute inset-0 bg-black/50"></div>
        </div>
    </f:if>

    <div class="relative z-10 bido-container px-5 py-16 text-white">
        <f:if condition="{data.tx_mask_hero_title}">
            <h1 class="text-4xl md:text-5xl font-bold mb-4 leading-tight">
                {data.tx_mask_hero_title}
            </h1>
        </f:if>

        <f:if condition="{data.tx_mask_hero_subtitle}">
            <p class="text-lg md:text-xl mb-8 text-white/80">
                {data.tx_mask_hero_subtitle}
            </p>
        </f:if>

        <f:if condition="{data.tx_mask_hero_button_link}">
            <f:link.typolink parameter="{data.tx_mask_hero_button_link}"
                             class="inline-block px-8 py-3 bg-primary text-white font-semibold rounded-lg hover:bg-primary/90 transition">
                <f:if condition="{data.tx_mask_hero_button_text}">
                    <f:then>{data.tx_mask_hero_button_text}</f:then>
                    <f:else>Learn More</f:else>
                </f:if>
            </f:link.typolink>
        </f:if>
    </div>

</section>

</html>
```

---

## Alan Türleri (Mask v9 type değerleri)

| Mask `type` | Açıklama | TCA karşılığı |
|-------------|----------|---------------|
| `string` | Tek satırlık metin | `input` |
| `richtext` | Zengin metin editörü | `text` + `enableRichtext: true` |
| `text` | Düz çok satırlık metin | `text` |
| `integer` | Tam sayı | `number` (integer) |
| `float` | Ondalık sayı | `number` (decimal) |
| `file` | Dosya / resim | `file` |
| `link` | Bağlantı (sayfa/URL/e-posta) | `link` |
| `select` | Açılır liste | `select` |
| `check` | Onay kutusu | `check` |
| `radio` | Radyo butonlar | `radio` |
| `inline` | Tekrarlayan kayıtlar (IRRE) | `inline` |
| `date` | Tarih seçici | `datetime` (date only) |
| `datetime` | Tarih + saat | `datetime` |
| `color` | HEX renk seçici | `color` |

---

## Mask Ayarları

`config/system/additional.php`:

```php
$GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['mask'] = array_merge(
    $GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['mask'] ?? [],
    [
        'loader_identifier' => 'json',
        'json'     => 'EXT:testproject/Configuration/Mask/mask.json',
        'frontend' => 'EXT:testproject/Resources/Private/Templates/Mask/',
        'layouts'  => 'EXT:testproject/Resources/Private/Layouts/Mask/',
        'partials' => 'EXT:testproject/Resources/Private/Partials/Mask/',
        'backend'  => 'EXT:testproject/Resources/Private/Templates/Mask/Backend/',
    ]
);
```

> **v9 Uyarıları:**
> - Eski `'content'` anahtarı v9'da **`'frontend'`** olarak değişti — yanlış key "Missing directories or templates" hatası verir
> - Template dosya adı element key ile **birebir eşleşmeli**: `mask_card.html` ✅, `Card.html` ❌

---

## Kaynaklar

- [Mask Extension](https://docs.typo3.org/p/mask/mask/main/en-us/)
- [Mask GitHub](https://github.com/Gernott/mask)

            "description": "Başlık, metin ve resimli kart",
            "shortLabel": "Kart",
            "icon": "content-card",
            "color": "#0078D4",
            "elements": {
                "tx_mask_card_title": {
                    "config": {
                        "type": "input",
                        "eval": "trim,required",
                        "max": 255
                    },
                    "key": "card_title",
                    "label": "Kart Başlığı",
                    "description": "Kartın ana başlığı",
                    "order": 1
                },
                "tx_mask_card_text": {
                    "config": {
                        "type": "text",
                        "enableRichtext": true,
                        "rows": 5
                    },
                    "key": "card_text",
                    "label": "Kart Metni",
                    "description": "Kartın açıklama metni",
                    "order": 2
                },
                "tx_mask_card_image": {
                    "config": {
                        "type": "file",
                        "maxitems": 1,
                        "allowed": "common-image-types"
                    },
                    "key": "card_image",
                    "label": "Kart Görseli",
                    "order": 3
                },
                "tx_mask_card_link": {
                    "config": {
                        "type": "link"
                    },
                    "key": "card_link",
                    "label": "Kart Bağlantısı",
                    "order": 4
                }
            }
        },
        "my_hero": {
            "label": "Hero Banner",
            "description": "Tam genişlikte hero alanı",
            "shortLabel": "Hero",
            "icon": "content-header",
            "color": "#E74C3C",
            "elements": {
                "tx_mask_hero_title": {
                    "config": {
                        "type": "input",
                        "eval": "trim,required",
                        "max": 255
                    },
                    "key": "hero_title",
                    "label": "Hero Başlık",
                    "order": 1
                },
                "tx_mask_hero_subtitle": {
                    "config": {
                        "type": "input",
                        "eval": "trim",
                        "max": 255
                    },
                    "key": "hero_subtitle",
                    "label": "Alt Başlık",
                    "order": 2
                },
                "tx_mask_hero_bg_image": {
                    "config": {
                        "type": "file",
                        "maxitems": 1,
                        "allowed": "common-image-types"
                    },
                    "key": "hero_bg_image",
                    "label": "Arka Plan Görseli",
                    "order": 3
                },
                "tx_mask_hero_button_text": {
                    "config": {
                        "type": "input",
                        "eval": "trim",
                        "max": 100
                    },
                    "key": "hero_button_text",
                    "label": "Buton Metni",
                    "order": 4
                },
                "tx_mask_hero_button_link": {
                    "config": {
                        "type": "link"
                    },
                    "key": "hero_button_link",
                    "label": "Buton Bağlantısı",
                    "order": 5
                }
            }
        }
    }
}
```

---

## Fluid Template

### Kart Elemanı

`Resources/Private/Templates/Mask/Card.html`:

```html
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers"
      data-namespace-typo3-fluid="true">

<div class="mask-card bg-white rounded-lg shadow-md overflow-hidden">
    <f:if condition="{data.tx_mask_card_image}">
        <div class="card-image">
            <f:for each="{data.tx_mask_card_image}" as="image">
                <f:image image="{image}" maxWidth="600" class="w-full h-48 object-cover" />
            </f:for>
        </div>
    </f:if>

    <div class="card-body p-6">
        <h3 class="text-xl font-bold mb-2">{data.tx_mask_card_title}</h3>

        <f:if condition="{data.tx_mask_card_text}">
            <div class="text-gray-600 mb-4">
                <f:format.html>{data.tx_mask_card_text}</f:format.html>
            </div>
        </f:if>

        <f:if condition="{data.tx_mask_card_link}">
            <f:link.typolink parameter="{data.tx_mask_card_link}" class="text-primary hover:underline">
                Devamını Oku →
            </f:link.typolink>
        </f:if>
    </div>
</div>

</html>
```

---

## Alan Türleri

| Tür | Açıklama | Yapılandırma |
|-----|----------|--------------|
| `input` | Tek satırlık metin | `eval: trim,required` |
| `text` | Çok satırlık metin | `enableRichtext: true` |
| `file` | Dosya/resim | `maxitems, allowed` |
| `link` | Bağlantı | Sayfa, URL, e-posta, telefon |
| `select` | Açılır liste | `items` ile seçenekler |
| `check` | Onay kutusu | Evet/Hayır |
| `radio` | Radyo butonları | `items` ile seçenekler |
| `inline` | Tekrarlayan alanlar | İç içe kayıtlar |
| `date` | Tarih seçici | `dbType: date` |
| `datetime` | Tarih-saat | `dbType: datetime` |
| `number` | Sayı | `range: {lower, upper}` |
| `color` | Renk seçici | HEX renk |

---

## Mask Ayarları

`config/system/additional.php`:

```php
$GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['mask'] = [
    'json' => 'EXT:my_site_package/Configuration/Mask/mask.json',
    'content' => 'EXT:my_site_package/Resources/Private/Templates/Mask/',
    'layouts' => 'EXT:my_site_package/Resources/Private/Layouts/Mask/',
    'partials' => 'EXT:my_site_package/Resources/Private/Partials/Mask/',
    'preview' => 'EXT:my_site_package/Resources/Private/Templates/Mask/Backend/',
];
```

---

## Kaynaklar

- [Mask Extension](https://docs.typo3.org/p/mask/mask/main/en-us/)
- [Mask GitHub](https://github.com/Gernott/mask)
