# Outras Configurações — Rotas e Controladores

> Módulo `outras-configuracoes`. Todas as rotas vivem em `v6/routes/basepro.php`,
> no grupo `Route::group(['prefix' => 'cms', 'before' => 'csrf'], ...)` (linhas 35-513).

---

## 1. Tabela de rotas

| # | Método | URI | Nome da rota | Controller@ação | Middleware | Papel no módulo |
|---|---|---|---|---|---|---|
| 1 | GET | `/cms/configuracoes` | `configuracoes.index` | `Prefecture\ConfigurationController@index` | `check.logged` | Listagem (tela alvo do módulo) |
| 2 | GET | `/cms/configuracoes/create` | `configuracoes.create` | `Prefecture\ConfigurationController@create` | `check.logged` | Formulário de cadastro (super-admin) |
| 3 | POST | `/cms/configuracoes` | `configuracoes.store` | `Prefecture\ConfigurationController@store` | `check.logged` | Grava a configuração |
| 4 | GET | `/cms/configuracoes/{id}` | `configuracoes.show` | `Prefecture\ConfigurationController@show` | `check.logged` | Não usado pelo fluxo do módulo |
| 5 | GET | `/cms/configuracoes/{id}/edit` | `configuracoes.edit` | `Prefecture\ConfigurationController@edit` | `check.logged` | Formulário de edição (super-admin) |
| 6 | PUT/PATCH | `/cms/configuracoes/{id}` | `configuracoes.update` | `Prefecture\ConfigurationController@update` | `check.logged` | Atualiza a configuração |
| 7 | DELETE | `/cms/configuracoes/{id}` | `configuracoes.destroy` | `Prefecture\ConfigurationController@destroy` | `check.logged` | Soft delete, resposta JSON |
| 8 | GET | `/cms/configuracoes/editar/{slug}` | `edit.configuracoes.by.category` | `Prefecture\ConfigurationController@editByCategory` | `check.logged` | Entrada do menu "Outras Configurações" (`slug = outros`) |
| 9 | POST | `/cms/update/configuration` | `update.configurations.by.category` | `Prefecture\ConfigurationController@updateByCategory` | `csrf` (sem `check.logged`) | Gravação por categoria; **exige `configuracoes.edit`** |
| 10 | GET | `/cms/datatable/configuracoes` | `datatable.configuracoes` | `Prefecture\DatatableController@get_configuracoes` | `csrf` (grupo `datatable`) | Alimenta a DataTable da listagem |
| 11 | POST | `/cms/ajax/remove-arquivo-configuracao` | `remove.arquivo.configuracao` | `Prefecture\ConfigurationController@removeArquivo` | grupo `ajax` | Zera o `value` de uma configuração de arquivo |
| 12 | GET | `/cms/modulos` | `modulos.index` | `Admin\ModulosController@index` | `check.logged` | Liga/desliga o módulo |
| 13 | POST | `/cms/modulos` | `modulos.update` | `Admin\ModulosController@update` | `check.logged` | Persiste o status do módulo |

Referências de origem:

```php
// v6/routes/basepro.php:175
Route::resource('configuracoes', 'Prefecture\ConfigurationController')->middleware('check.logged');

// v6/routes/basepro.php:241
Route::get('configuracoes/editar/{slug}', ['uses' => 'Prefecture\ConfigurationController@editByCategory'])
    ->name('edit.configuracoes.by.category')->middleware('check.logged');

// v6/routes/basepro.php:246
Route::post('update/configuration', 'Prefecture\ConfigurationController@updateByCategory')
    ->name('update.configurations.by.category');

// v6/routes/basepro.php:451 (grupo 'datatable', prefixo 'datatable')
Route::get('/configuracoes', 'Prefecture\DatatableController@get_configuracoes');
```

> Não há rota pública (portal) nem rota de API associada a este módulo.

## 2. Controller principal

`v6/app/Http/Controllers/Prefecture/ConfigurationController.php` (856 linhas) —
métodos relevantes ao módulo:

| Método | Linhas | Comportamento |
|---|---|---|
| `index()` | 57-70 | Guarda `has_access(['outros.index'])`; carrega `getConfiguration('outros')`; renderiza `prefecture.configurations.index` |
| `create()` | 76-100 | Exige `Sentinel::check()` **e** `Sentinel::inRole('super-admin')`; carrega `CategoryConfiguration::all()->pluck('name','id')` |
| `store()` | 102-148 | `DB::transaction`; grava a lista fixa de campos; upload para `configuracoes/`; log de atividade |
| `edit()` | 150-175 | Exige `super-admin`; `Configuration::findOrFail($id)` |
| `update()` | 177-224 | `str_slug` na `key`; upload `img_{YmdHi}{4}.ext`; `Storage::delete("/".$fileOld)`; `$request->all()` na atualização |
| `destroy()` | 527-546 | Soft delete; JSON `{success}` 200 ou `{erro}` 500 |
| `removeArquivo()` | 548-556 | Zera `value` da configuração indicada |
| `editByCategory()` | 423-511 | `switch ($slug)`; `case 'outros'` → `$this->index()` |
| `updateByCategory()` | 226-421 | Grava todas as categorias num único endpoint; guarda `has_access(['configuracoes.edit'])` |
| `configurationLog()` | 513-521 | Grava log de atividade |

Trecho real que define o comportamento do módulo (`editByCategory`, casos
`'outros'`, `'barra-privacidade'` e `default`):

```php
public function editByCategory(string $slug)
{
    $reference = $acess = $slug;

    if ($slug == 'configuracoes')
        $reference = "site";
    if ($slug == 'temas')
        $acess = 'tema-do-portal';

    $configurations = $this->getConfiguration($reference);

    $category = CategoryConfiguration::where('slug', $reference)->first();
    $states = DB::table('states')->get();

    if (!has_access(["$acess.edit"])) {
        flash('Você não tem permissão para acessar este conteúdo')->warning();
        return redirect()->route('dashboard.index');
    }

    switch ($slug) {
        case 'outros':
            $this->configurationLog('Listou outras configurações');
            return $this->index();
        case 'barra-privacidade':
            $title = 'LGPD';
            $this->configurationLog('Acessou edição de barra de privacidade');
            return view("prefecture.configurations.{$slug}.edit", compact("configurations", 'title', 'category'));
        default:
            $this->configurationLog('Listou configuração');
            return $this->index();
    }
}
```

Trecho real do `index()`:

```php
public function index()
{
    if (!has_access(['outros.index'])) {
        flash('Você não tem permissão para acessar')->warning();
        return redirect()->route('dashboard.index');
    }

    $title = 'Configurações';
    $configurations = $this->getConfiguration('outros');

    $this->configurationLog('Listou configurações gerais');
    return view('prefecture.configurations.index', compact('configurations', 'title'));
}
```

## 3. Controller da DataTable

`v6/app/Http/Controllers/Prefecture/DatatableController.php`:

```php
public function get_configuracoes()
{
    $configuracoes = DB::table("configuracoes")->get();
    return DataTables::of($this->struct_views($configuracoes, 'configuracoes'))->make(true);
}
```

`struct_views()` (linha 504) injeta as permissões por linha:

```php
public function struct_views($data, $permission)
{
    for ($i = 0; $i < count($data); $i++) {
        $data[$i]->edit = has_access("$permission.edit");

        if ($permission === 'diario-oficial') {
        } else {
            $data[$i]->delete = has_access("$permission.delete");
        }

        $data[$i]->active = has_access("$permission.restore");
        $data[$i]->inactivate = has_access("$permission.inactivate");
    }

    return $data;
}
```

Observação: a listagem usa o prefixo de permissão **`configuracoes`** (não
`outros`), porque o `permission` passado é a string `'configuracoes'`. O botão
de excluir usa `configuracoes.delete` e o de editar `configuracoes.edit` —
**não** `outros.edit`. Isso é coerente com o `resource` de rotas
`configuracoes.*`, mas diverge do menu, que exige `outros.index`/`outros.edit`.

## 4. Form Request / validação

`v6/app/Http/Requests/ConfigurationRequest.php`:

| Rota | Regras |
|---|---|
| `configuracoes.store` | `category_configurations_id` required, `type` required, `title` required\|max:190, `key` required\|max:190, `value` required |
| `configuracoes.update` | Mesmas regras, mas `value` só é required quando `type != 'file'`; `codigo_tempo` → `required\|numeric` |
| `update.configurations.by.category` | Regras **geradas dinamicamente**: uma query ao banco por campo enviado (`Configuration::where('key',$keys[$i])->first()`), combinando `required` (se `notnull`), `max` e `min`; as 6 chaves de GA/Dashboard recebem regra vazia (`''`) e são validadas à mão no controller |

Trecho real (regras dinâmicas):

```php
for ( $i = 0; $i < $size; $i++) {
    $config = Configuration::where('key', $keys[$i])->first();
    if (!empty($config)) {
        if ($config->notnull == true) {
            $required = 'required';
            if ($config->max_input != ''){
                $max = '|max:'.$config->max_input."|";
            } else {
                $max = '';
            }
            if ($config->max_input != ''){
                $min = '|min:'.$config->min_input.'';
            } else {
                $min = '';
            }

        } else {
            $required = '';
            $max = '';
            $min = '';
        }
    }

    $rules += [ $keys[$i] => $required.$max.$min];
}
```

> Observação: a regra `min` só é aplicada quando `max_input` não está vazio
> (condição duplicada no legado) — bug sutil herdado.

Mensagens de erro:

```php
$messages = [
    'category_configurations_id.required' => 'Não foi possível recuperar a categoria da configuração.',
    'type.required' => 'Não foi possível recuperar o tipo de configuração.',
    'title.required' => 'O campo título é obrigatório.',
    'title.max' => 'O campo título excedeu o limite de caracteres.',
    'key.max' => 'O campo chave excedeu o limite de caracteres.',
    'key.required' => 'O campo chave é obrigatório.',
    'value.required' => 'O campo valor é obrigatório.',
    'codigo_tempo.numeric' => 'O campo código tempo deve ser um número.',
];
```

## 5. Autorização

| Recurso | Permissão / papel | Onde é verificado |
|---|---|---|
| Visibilidade do menu | `has_access(['outros.index'])` **e** `$modulos['outras-configuracoes']` | `v6/resources/views/vendor/adminlte/page.blade.php:254` |
| Abrir a tela (leitura) | `has_access(['outros.edit'])` | `editByCategory()` |
| Abrir a listagem | `has_access(['outros.index'])` | `index()` |
| Cadastrar | `Sentinel::inRole('super-admin')` | `create()` |
| Editar | `Sentinel::inRole('super-admin')` | `edit()` |
| Gravar por categoria | `has_access(['configuracoes.edit'])` | `updateByCategory()` |
| Botão editar na tabela | `has_access('configuracoes.edit')` | `struct_views()` |
| Botão excluir na tabela | `has_access('configuracoes.delete')` | `struct_views()` |

Implementação de `has_access()` (`v6/app/Library/Helper/appHelpers.php:22`):

```php
function has_access($access = null)
{
    if (!Sentinel::check()) {
        return false;
    }

    if (!isset($access)) {
        return true;
    }

    if (Sentinel::inRole('super-admin')) {
        return true;
    }

    if (is_array($access)) {
        return Sentinel::check()->hasAnyAccess($access);
    }

    return Sentinel::check()->hasAccess([$access]);
}
```

O middleware `check.route.permission`
(`v6/app/Http/Middleware/CheckRoutePermission.php`) mapeia verbos
(`edit`→`update`, `create`→`store`, `delete`→`destroy`), mas **não é aplicado**
às rotas deste módulo: a checagem é feita dentro do controller.

## 6. Formato de saída

| Endpoint | Tipo | Formato |
|---|---|---|
| `GET /cms/datatable/configuracoes` | JSON (Yajra DataTables) | `{data: [...], recordsTotal, recordsFiltered, draw}`; cada linha com `value`, `titulo`, `categoria`, `id`, `tipo`, `edit`, `delete`, `active`, `inactivate` |
| `DELETE /cms/configuracoes/{id}` | JSON | 200 `{"success": "Configuração deletada com sucesso."}` ou 500 `{"erro": "..."}` |
| `POST /cms/ajax/remove-arquivo-configuracao` | sem retorno | apenas zera `value` |
| Demais | HTML (redirect + flash) | — |

Não há API Resource / Presenter no legado: o JSON é montado manualmente pelo
Yajra sobre a view SQL.
