# Arquivo (Drive) — Resumo Executivo

> **Versão analisada:** `v5` (Laravel 5.5 / PHP 7.2). O módulo **não existe
> como módulo autonome na v6**: a v6 mantém a tabela `archives` e o
> `ArchiveController`, mas **removeu** `ArchiveCategory`, as rotas
> `/cms/drive/*` e toda a interface de categorização.
> **Slug do dossiê:** `arquivo` (não há `resources.slug` correspondente; na v5
> o recurso equivalente é `midias` / "Drive" — `resources.id = 44`; na v6 é
> `drive` / "Drive" — `resources.id = 34`, ligado ao `modulos.id = 30`).
> **Natureza:** CMS (painel administrativo) + Portal (público, leitura)
> **Estado:** apenas v5 (parcialmente — ver `08-GAPS-E-DIVIDAS.md`)
>
> **Dossiê correlato:** `../drive/` documenta o mesmo conjunto de código do
> ponto de vista da **v6** (que mantém `archives` e o `ArchiveController`, mas
> perdeu `ArchiveCategory` e as rotas `/cms/drive/*`). Este dossiê (`arquivo`)
> cobre o **lado v5** e detalha justamente o que foi perdido na v6 — a
> categorização de arquivos e as três rotas do Drive de categorias.

---

## 1. Objetivo

O módulo de **arquivo** é a camada central de armazenamento de arquivos
(mídias, PDFs, planilhas, certidões) do portal municipal. Ele não é um
"módulo de negócio": é a **infraestrutura de arquivos** compartilhada por
quase todos os demais módulos — notícias, páginas estáticas, agenda de
eventos, licitações, contratos, obras, emendas parlamentares, respostas de
ouvidoria, diário oficial, requerimento de diárias e o manual do sistema.

Do ponto de vista do operador do CMS, o módulo aparece como o **"Drive"**
(menu lateral, `v5/app/Providers/MenuServiceProvider.php:283-285`): uma
galeria de mídias com upload, busca por nome, filtro por categoria,
ordenação por arraste (drag-and-drop), cópia de link, download, preview e
exclusão. Do ponto de vista do cidadão, o módulo é invisível: ele apenas
materializa o bloco "Arquivos para Download" e a galeria de fotos que
aparecem dentro das páginas do portal.

## 2. Efeito / Impacto

| Faixa | Efeito |
|---|---|
| Cidadão | Baixa documentos e fotos sem sair da página de conteúdo. Não existe "área de arquivos" pública: o cidadão só vê arquivos vinculados a uma página específica. |
| Prefeitura (CMS) | Centraliza o acervo de arquivos do município (imagens de destaque, galerias, PDFs de editais/contratos, certidões). Habilita a reutilização de mídias via galeria do Drive. |
| Transparência e legalidade | O `path` do arquivo é a evidência do que foi publicado. Exclusão de arquivo (soft delete) **não** apaga o binário do storage — o documento continua acessível por URL. |
| Operação técnica | Tabela única, sem escopo por módulo, compartilhada por ~13 módulos. Regressões em `archives` derrubam galerias, editais, contratos e o Diário Oficial simultaneamente. |

## 3. Comportamento

- **Upload pelo Drive (`/cms/galeria`)** — aceita um arquivo por requisição.
  Se `table` = `content`, `calendar_event` ou `static_pages`, o arquivo é
  gravado em `storage/app/public/{dir}` e vinculado via a coluna polimórfica
  correspondente (`contents_id`, `calendar_events_id`, `static_pages_id`).
  Sem `table`, cai no "bucket" padrão `/content/midias`.
- **Categorização (v5 apenas)** — cada mídia do Drive recebe uma
  `archive_categories.category_id`. As categorias são criadas por um modal
  SweetAlert e listadas no seletor do upload. Este é o único diferencial
  relevante da v5 que **não existe na v6**.
- **Ordenação** — a coluna `order` (nullable, `integer`) define a posição na
  galeria. Duas mecânicas coexistem: `order`/`reorder` (rotas `/order`,
  escopo `contents_id`) para o `sortable-galerias.js`, e
  `updateOrder` (rota `/cms/drive/atualizar-ordem`, troca direta de valores
  via `DB::table`) para o `sortable` do jQuery UI do Drive.
- **Exclusão** — `SoftDeletes`: a linha recebe `deleted_at`, mas o arquivo
  físico permanece em `storage/app/public/`.
- **Compressão automática** — `FileTrait::compress()` gera, para imagens,
  três variantes (`_min`, `_med`, `_larg`) com Intervention Image, e o
  portal as consome via `getMinFile()`/`getLargFile()`
  (`v5/app/Helpers/functions.php:909-940`).
- **Destaque vs. galeria** — a coluna booleana `highlight` distingue a
  "imagem de destaque" (`highlight = true`) dos itens de galeria
  (`highlight IS NULL`). A coluna `view_pdf` marca o arquivo que o portal
  embute em `<iframe>`.
- **Ordem de leitura (v5)** — `getArchivesFromGalleryOrDriver` ordena por
  `order DESC, id DESC`; na v6 a ordenação por `order` foi removida.

## 4. Como o Usuário Interage

**Operador (CMS)**

| Tela / ação | Caminho |
|---|---|
| Abrir o Drive | `/cms/conteudos/cadastrar/midias` (menu lateral "Drive") |
| Enviar arquivo | botão "Upload" → cropper (imagens) → modal "Informações do arquivo" (nome + categoria) → `POST /cms/galeria` |
| Listar / buscar | galeria paginada por infinite scroll (lotes de 50), busca por nome **ou** categoria (`filterImages`) |
| Reordenar | arrastar item → `POST /cms/drive/atualizar-ordem` (`dragged_id`, `target_id`) |
| Criar categoria | botão "Adicionar Categoria" (visível só se `configurations.advanced_drive_settings_enabled != 0`) → `POST /cms/drive/cadastrar/categoria` |
| Copiar link / Baixar / Visualizar / Excluir | botões em cada item da galeria |

**Cidadão (portal)**

- Bloco "Arquivos para Download" (`v5/resources/views/portal/layouts/default/pages/elements/files-list.blade.php`)
  e variante com PDF embutido (`v5/resources/views/portal/components/view_pdf/files-list.blade.php`).
- Galeria de fotos com lightgallery (`v5/resources/views/portal/components/galerias/index.blade.php`).
- Arquivos de obras (`v5/resources/views/portal/components/obras/arquivos.blade.php`).
- Arquivos de respostas de ouvidoria (`v5/resources/views/portal/layouts/modelo1/informacoes-prefeitura/arquivo/index.blade.php`).

## 5. Detalhes e Particularidades Técnicas

- **Camadas:** `routes/basepro.php` → `ArchiveController` (herda
  `Prefecture\Controller`, que aplica `check.route.permission`) →
  `ArchiveRepository` (`v5/app/Repositories/Eloquent/Prefecture/Archive/ArchiveRepository.php`)
  → `App\Prefecture\Archive` → tabela `archives`.
- **Não há Form Request** para o módulo: a validação do upload é
  inexistente no controller (só o `accept` do plugin JS e a checagem de
  `isValid()`).
- **Colunas polimórficas** — `contents_id`, `static_pages_id`,
  `calendar_events_id`, `biddings_id`, `requerimento_id`, `highlight_id`,
  `reference_id` (texto!) coexistem na mesma tabela. Nenhuma FK garante
  que apenas uma esteja preenchida.
- **`reference_id` é `text`** — usado como FK lógica por
  `PaginasSecretaria` (`v5/app/Http/Model/Prefecture/PaginasSecretaria.php:32-39`).
- **Bug crítico de segurança (verificado em execução):** as rotas
  `/cms/galeria*` e `/cms/drive/*` **não passam por `check.route.permission`**
  (o `ArchiveController` sobrescreve o construtor e não chama
  `parent::__construct()`, que é quem registra o middleware), e o grupo
  `cms` de `v5/routes/basepro.php:37` só aplica `before => csrf`. Na
  prática, `GET /cms/drive/categorias` responde **200 sem autenticação** e
  `POST /cms/drive/cadastrar/categoria` **cria categoria sem autenticação**
  (verificado: a linha criada foi removida em seguida para não sujar a base).
- **`path` nullable e inconsistente** — alguns registros gravam
  `storage/...` (prefixo do symlink público), outros gravam
  `storage/content/midias/...`, outros gravam URL absoluta
  (`url("storage/answer/...")`) e outros gravam `docs/{arquivo}`.
- **Armadilha `where('extension', '!=', null)`** — em `getArchivesFromGalleryOrDriver`
  o Laravel 5.5 traduz `!= null` para `IS NOT NULL` (comportamento correto
  nesta versão), mas o filtro é redundante e confuso.
- **Dependências:** `intervention/image ^2.7` (compressão), `blueimp/jquery-file-upload`
  (upload múltiplo no Drive), `sweetalert2` + `cropperjs` (front), `cartalyst/sentinel`
  (permissões), `spatie/laravel-activitylog` (log de cadastro de arquivo).

## 6. Estrutura de Dados

| Objeto | Tipo | Observação |
|---|---|---|
| `archives` | tabela | 26 colunas; PK `id`; soft delete; 8 FKs de saída |
| `archive_categories` | tabela | `name` + `slug` único; soft delete |
| `work_archives` | pivot | `work_id` + `archive_id` + `type` (`gallery`/`file`) |
| `biddings_has_archives` | pivot | `biddings_id` + `archives_id` |
| `answer_archives` | pivot | `answer_id` + `archive_id` |
| `contract_archives` | pivot | `contract_id` + `archive_id` |
| `ombudsman_answer_archives` | pivot | `ombudsman_answer_id` + `archive_id` |
| `emendas_has_archives` | pivot | `emenda_id` + `archive_id` (sem PK, sem timestamps) |
| `types_sizes` | lookup | `type`/`size` — define o tamanho de exibição da imagem |
| `highlights` | 1-N | `archives.highlight_id` (FK, cascade) |

Detalhe completo em `02-MODELAGEM.md`.

## 7. Como Testar

Cenários mínimos para validar a reconstrução:

1. Upload sem `table` → arquivo em `storage/app/public/content/midias/`,
   linha em `archives` com `path` iniciado por `storage/content/midias/`.
2. Upload com `table=static_pages` → `static_pages_id` preenchido.
3. Upload com `category` inválido → FK `archives_category_id_foreign` deve
   rejeitar (hoje o `category_id` não é validado no controller).
4. Reordenação por drag-and-drop → `order` trocado entre os dois itens e
   **apenas** dentro do mesmo escopo.
5. Exclusão → `deleted_at` preenchido, linha ausente das listagens, arquivo
   ainda acessível por `/storage/...`.
6. Listagem por categoria → só os arquivos da categoria pedida.
7. **Negativo (segurança):** `GET /cms/drive/categorias` e
   `POST /cms/drive/cadastrar/categoria` **sem sessão** devem retornar
   401/302 para login. Na v5 retornam 200 e criam dados.

Detalhe em `07-TESTES.md`.

## 8. Migração para Laravel Moderno

- `archives` vira um **modelo único** (`App\Models\Archive`) com
  `HasMany`/pivots explícitos e uma **coluna polimórfica canônica**
  (`attachable_type` + `attachable_id`) no lugar das 7 colunas
  `*_id` — decisão que precisa ser validada com o negócio.
- `archive_categories` é recriada **com a mesma semântica da v5**
  (`belongsTo` opcional via `category_id`), porque a v6 a eliminou.
- Upload passa por `Storage::disk('public')` com `UploadedFile` validado
  (`max:`, `mimes:`, `image` condicional), `FormRequest` e `Policy`.
- Ordenação: um único endpoint `PUT /api/archives/order`, transacional,
  escopado por `attachable_type`/`attachable_id`.
- `order` deixa de ser `nullable` e passa a ser `unsignedInteger` com índice
  composto; valores contíguos reatribuídos em transação.
- Exclusão passa a apagar o binário (job de limpeza para órfãos).
- `SoftDeletes` mantido, mas com filtro global por módulo (o model `Archive`
  v7 deve expor `scopeForModule()`).

## 9. Resumo Geral

O "módulo de arquivo" da v5 é, na prática, uma **tabela central de
arquivos** (`archives`) com 26 colunas, 8 chaves estrangeiras e colunas
polimórficas que apontam para conteúdos, páginas estáticas, agenda de
eventos, licitações, requerimentos de diárias, destaques e secretarias. Ao
redor dela existem **sete tabelas pivot** que ligam o mesmo arquivo a
obras, licitações, respostas, contratos, emendas parlamentares e respostas
de ouvidoria. O `ArchiveController` é a única porta de entrada: upload,
listagem, reordenação e exclusão. Ao lado dele, o `ArchiveCategoryController`
e a tabela `archive_categories` implementam a **categorização de mídias do
Drive** — a funcionalidade que a v6 eliminou por completo, junto com as
rotas `/cms/drive/*`, o botão "Adicionar Categoria" e o filtro por
categoria. A v6 também removeu a ordenação por `order` na listagem,
trocou a reordenação por `updateArchivesOrder` (POST `/cms/galeria/update-order`),
adicionou edição de título/descrição (PUT `/cms/galeria/update-title/{id}`)
e o campo `tv_camara_id`. Para a v7, o dossiê recomenda reconstruir o
Drive **com** as categorias da v5, **com** ordenação transacional e
**com** autorização por `Policy` — corrigindo o bug de segurança em que as
rotas do Drive respondem sem autenticação.

---

## Índice do dossiê

| Arquivo | Conteúdo |
|---|---|
| `00-RESUMO.md` | Este documento |
| `01-ARQUITETURA.md` | Camadas, classes e o diagrama de fluxo |
| `02-MODELAGEM.md` | Estrutura de dados detalhada (26 colunas + pivots) |
| `03-ROTAS-E-CONTROLADORES.md` | Rotas do CMS e do portal, `ArchiveController`, `ArchiveCategoryController` |
| `04-VIEWS-E-UX.md` | Telas do CMS e do portal, o que o usuário e o operador veem, assets |
| `05-REGRAS-E-PORTABILIDADE.md` | Regras, armadilhas e acoplamentos |
| `06-MIGRACAO-MODERNA.md` | Plano de reconstrução: decisões, schema, modelos, Form Requests, Policies |
| `07-TESTES.md` | Estratégia e casos de teste |
| `08-GAPS-E-DIVIDAS.md` | Pendências, riscos e comparativo v5 vs v6 (inclui "Regressão v5 -> v6") |
| `09-PERMISSOES-E-COMPARATIVO-V6.md` | Repositório, validação, API Resources, permissões e comparativo de rotas |
| `10-ENDPOINTS-VIEWS-E-OPERACAO.md` | Endpoints REST/JSON, API Resources, views Blade, fila/cache, riscos e sequenciamento |
| `11-JS-DO-DRIVE.md` | JavaScript do Drive: upload com cropper e categoria, scroll infinito, busca, reordenação |
