# Resource-metadata

Instellingen op hoofdniveau, systeemvelden, resource-regels en voorbeelden.

## Het .meta-bestand

Beschrijft de structuur en het gedrag van een resource. Alle velden moeten uitdrukkelijk worden ingesteld.

```json
{
  "fields": {
    "title": {
      "name": "Title",
      "type": "text",
      "required": true
    },
    "title_slug": {
      "name": "URL slug",
      "type": "slug",
      "source": "title"
    },
    "published": {
      "name": "Published",
      "type": "boolean"
    },
    "body": {
      "name": "Body",
      "type": "html",
      "buttons": "h2,h3,h4,bold,italic,orderedlist,unorderedlist,quote,anchor",
      "media": true,
      "media_sizes": "sm-90,md-70,lg-60,xl-50,xxl-40",
      "admin_col": false
    },
    "sort_order": {
      "name": "Sort order",
      "type": "text",
      "admin_col": false
    }
  },
  "sort": {
    "field": "sort_order",
    "flags": "numeric",
    "order": "asc"
  }
}
```

## Instellingen op het hoogste niveau van .meta

| Sleutel | Beschrijving |
|---|---|
| `sort` | Standaardsortering (`field`, `flags`: `string`/`numeric`, `order`: `asc`/`desc`) |
| `validate` | Validatieregels (bijvoorbeeld `natural-short-text`) |
| `languages` | Talen die voor deze resource aan staan |
| `ai_prompts` | Instructies per veld voor AI-vertaling |
| `name` | Object met optionele labels `singular` en `plural`, gebruikt door `[#resource-name#]`, koppen in de admin en de resources in de Nimbly-balk. Zonder labels wordt een naam van de id van de resource afgeleid. Voorbeeld: `{"singular": "Member", "plural": "Members"}`. |
| `nimblybar` | Boolean die bepaalt of de resource in het menu Resources van de Nimbly-balk staat. Gewone resources zijn standaard zichtbaar; `users` en `roles` standaard verborgen. Zet `true` om hem op te nemen of `false` om hem uitdrukkelijk te verbergen. De huidige gebruiker heeft nog steeds `view-<resource>` nodig. |
| `actions` | Object met optionele sleutels `view_url` en `url`. `view_url` vervangt de algemene actie View in de admin (standaard `/nb-admin/{resource}/{uuid}?view=1`); `url` voegt de aparte actie "View on site" toe. Beide ondersteunen vervanging met `[#record.<field>#]`. Voorbeeld: `{"view_url": "/review/?id=[#record.uuid#]", "url": "[#base-url#]/articles/[#record.title_slug#]"}`. |
| `events` | Events in de levensloop van de resource. Ondersteunde sleutels: `create`, `update`, `delete`. Waarden zijn lijsten met events op naam of wachtrijregels `job:<type>`. |
| `splitdir` | Boolean. Bij `true` worden records in een boom van twee niveaus submappen opgeslagen op basis van het begin van de UUID, voor de snelheid van het bestandssysteem op schaal (> ~10.000 records). |
| `index` | Lijst met veldnamen die geïndexeerd worden. Maakt snelle opzoekpaden voor die velden. |
| `upsert` | Boolean. Bij `true` maakt `data_update()` (en dus `PUT /api/v1/{resource}/{uuid}`) een ontbrekend record aan in plaats van te mislukken. Alleen bedoeld voor resources in sleutel-waardestijl waar "het record bestaat misschien nog niet" normaal is (zoals `.config` van core, voor pagina-instellingen), niet voor gewone contentresources, waar het wijzigen van een niet-bestaand record een fout moet blijven. |
| `self_edit` | Alleen `users`. Velden, gescheiden door komma's, die een gebruiker via `PUT /api/v1/users/{uuid}` in zijn eigen record mag opslaan. Standaard: `name`. Wie gebruikers mag bewerken (`edit-users`, `manage-users`, admin) slaat elk veld op. |
| `hidden` | Velden, gescheiden door komma's, die de API nooit teruggeeft: niet in een lijst, niet in één record, niet in het antwoord op aanmaken of wijzigen, en niet in een export. Ze worden gewoon bewaard en opgeslagen; lees ze in PHP met `data_read()`. Velden in `encrypt` zijn altijd verborgen. Bij `users` verbergt core `password`, `salt`, `api`, `password_reset_token` en `change_email_token`, wat de `.meta` ook zegt. |
| `keep_shortcodes` | Boolean. Standaard wordt een shortcode-tag in een waarde die naar een record wordt geschreven, als tekst opgeslagen (`[#` en `#]` worden HTML-entiteiten), wie hem ook schrijft: de API, een import, een agent, een formulierhandler of `data_create()` / `data_update()` in een library. Hij wordt dan getoond zoals getypt en nooit uitgevoerd als de waarde binnen een andere shortcode wordt getoond. Waarden die een record al heeft en de `.meta` zelf blijven onaangeroerd. Zet `true` alleen voor een resource die geen template toont en die zijn tekst precies moet bewaren zoals geschreven; core doet dat voor de jobwachtrij en de eigen records van de agent. |
| `history` | Boolean. Of wijzigingen aan de records van de resource worden bewaard. Standaard: aan voor gewone resources, uit voor `users` en voor verborgen resources behalve `.content`, `.config` en `.navigation`. |
| `sitemap` | Declaratie voor de sitemap. `url` is een tekst of een URL-sjabloon per taal; het optionele `published` noemt een publicatieveld en het optionele `each` noemt één lijstveld dat wordt uitgevouwen. Bij het maken zijn `record`, `language` en `sitemap_item` beschikbaar. |

## Systeemvelden (automatisch beheerd)

Elk record heeft automatisch: `uuid`, `_created`, `_modified`, `_created_by`, `_modified_by`. Definieer deze nooit in `.meta`.

## Voorbeelden van .meta voor productie

### Voorbeeld A: partners (lijst met sortering)

```json
{
  "fields": {
    "title": {
      "name": "Partner name",
      "type": "text",
      "required": true
    },
    "published": {
      "name": "Published",
      "type": "boolean"
    },
    "logo": {
      "name": "Logo",
      "type": "image",
      "admin_col": false
    },
    "website": {
      "name": "Website",
      "type": "url",
      "admin_col": false
    },
    "sort_order": {
      "name": "Sort order",
      "type": "text",
      "admin_col": false
    }
  },
  "sort": {
    "field": "sort_order",
    "flags": "numeric",
    "order": "asc"
  }
}
```

### Voorbeeld B: artikelen (content gesorteerd op datum)

```json
{
  "fields": {
    "title": {
      "name": "Title",
      "type": "text",
      "required": true
    },
    "published": {
      "name": "Published",
      "type": "boolean"
    },
    "featured": {
      "name": "Featured",
      "type": "boolean"
    },
    "date": {
      "name": "Date",
      "type": "date"
    },
    "main_img": {
      "name": "Image",
      "type": "image",
      "admin_col": false
    },
    "intro": {
      "name": "Intro",
      "type": "html",
      "buttons": "bold,italic",
      "admin_col": false
    },
    "main_text": {
      "name": "Main text",
      "type": "html",
      "buttons": "h2,h3,h4,bold,italic,orderedlist,unorderedlist,quote,anchor",
      "media": true,
      "media_sizes": "sm-90,md-70,lg-60,xl-50,xxl-40",
      "admin_col": false
    }
  },
  "sort": {
    "field": "date",
    "flags": "string",
    "order": "desc"
  }
}
```

### Voorbeeld C: kolommen in het overzicht van de admin

Gebruik `admin_columns` om de volgorde van de kolommen in de admintabel te bepalen. Daar kunnen de ingebouwde systeemkolommen `_modified` en `_created` bij zitten.

```json
{
  "admin_columns": ["email", "_modified", "_created"],
  "sort": {
    "field": "_modified",
    "flags": "numeric",
    "order": "desc"
  }
}
```
