# Metadados de recursos

Configurações de nível superior, campos do sistema, regras de recursos e exemplos.

## O arquivo .meta

Define a estrutura e o comportamento de um recurso. Todos os campos precisam ser configurados explicitamente.

```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"
  }
}
```

## Configurações de nível superior do .meta

| Chave | Descrição |
|---|---|
| `sort` | Ordenação padrão (`field`, `flags`: `string`/`numeric`, `order`: `asc`/`desc`) |
| `validate` | Regras de validação (por exemplo `natural-short-text`) |
| `languages` | Idiomas ativados para este recurso |
| `ai_prompts` | Instruções por campo para tradução com IA |
| `name` | Objeto com os rótulos opcionais `singular` e `plural`, usados por `[#resource-name#]`, pelos títulos do admin e pelos recursos na barra do Nimbly. Sem rótulos, o nome é derivado do id do recurso. Exemplo: `{"singular": "Member", "plural": "Members"}`. |
| `nimblybar` | Booleano que controla se o recurso aparece no menu Resources da barra do Nimbly. Recursos comuns aparecem por padrão; `users` e `roles` ficam ocultos por padrão. Use `true` para incluir ou `false` para ocultar explicitamente. O usuário atual ainda precisa de `view-<resource>`. |
| `actions` | Objeto com as chaves opcionais `view_url` e `url`. `view_url` substitui a ação genérica View do admin (padrão `/nb-admin/{resource}/{uuid}?view=1`); `url` acrescenta a ação separada "View on site". Ambas aceitam substituições `[#record.<field>#]`. Exemplo: `{"view_url": "/review/?id=[#record.uuid#]", "url": "[#base-url#]/articles/[#record.title_slug#]"}`. |
| `events` | Declarações de eventos do ciclo de vida do recurso. Chaves suportadas: `create`, `update`, `delete`. Os valores são listas de eventos por nome ou entradas de fila `job:<type>`. |
| `splitdir` | Booleano. Com `true`, os registros são guardados numa árvore de subpastas de dois níveis pelo início do UUID, para o desempenho do sistema de arquivos em escala (> ~10.000 registros). |
| `index` | Lista de nomes de campos a indexar. Cria caminhos de busca rápidos para esses campos. |
| `upsert` | Booleano. Com `true`, `data_update()` (e portanto `PUT /api/v1/{resource}/{uuid}`) cria um registro que falta em vez de falhar. Use só em recursos no estilo chave-valor em que "o registro talvez ainda não exista" é o normal (como o `.config` do core, usado para configurações de página), não em recursos de conteúdo comuns, em que alterar um registro inexistente deve continuar sendo um erro. |
| `self_edit` | Só `users`. Campos separados por vírgula que um usuário pode salvar no próprio registro por `PUT /api/v1/users/{uuid}`. Padrão: `name`. Quem pode editar usuários (`edit-users`, `manage-users`, admin) salva todos os campos. |
| `hidden` | Campos separados por vírgula que a API nunca devolve: nem numa lista, nem num registro, nem na resposta a uma criação ou alteração, nem numa exportação. Eles continuam sendo guardados e salvos normalmente; leia-os em PHP com `data_read()`. Campos listados em `encrypt` são sempre ocultos. Em `users`, o core oculta `password`, `salt`, `api`, `password_reset_token` e `change_email_token`, diga o `.meta` o que disser. |
| `keep_shortcodes` | Booleano. Por padrão, uma tag de shortcode num valor gravado num registro é guardada como texto (`[#` e `#]` viram entidades HTML), seja quem for que grave: a API, uma importação, um agente, um handler de formulário ou `data_create()` / `data_update()` numa biblioteca. Ela então aparece como foi digitada e nunca é executada quando o valor é exibido dentro de outro shortcode. Valores que um registro já tem e o próprio `.meta` não são tocados. Use `true` só para um recurso que nenhum template exibe e que precisa guardar o texto exatamente como foi escrito; o core faz isso para a fila de jobs e os registros do próprio agente. |
| `history` | Booleano. Se as alterações nos registros do recurso são guardadas. Padrão: ligado para recursos comuns, desligado para `users` e para recursos ocultos, exceto `.content`, `.config` e `.navigation`. |
| `sitemap` | Declaração para o sitemap. `url` é um texto ou um modelo de URL por idioma; o `published` opcional indica um campo de publicação e o `each` opcional indica um campo de lista a expandir. Na geração ficam disponíveis `record`, `language` e `sitemap_item`. |

## Campos do sistema (gerenciados automaticamente)

Todo registro tem automaticamente: `uuid`, `_created`, `_modified`, `_created_by`, `_modified_by`. Nunca os defina no `.meta`.

## Exemplos de .meta prontos para produção

### Exemplo A: parceiros (lista com ordenação)

```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"
  }
}
```

### Exemplo B: artigos (conteúdo ordenado por data)

```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"
  }
}
```

### Exemplo C: colunas da visão geral do admin

Use `admin_columns` para controlar a ordem das colunas da tabela do admin. Ela pode incluir as colunas de sistema `_modified` e `_created`.

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