# Notícias — Arquitetura

## 1. Visão geral

"Notícias" não é um módulo com controller próprio no CMS: é um **caso de uso
do módulo genérico de conteúdos**. Uma única tabela (`contents`) e um único
controller (`ContentController`) servem a 20+ tipos de conteúdo; o slug da
categoria determina a view Blade, o conjunto de regras de validação e a
subcategoria onde o registro será gravado.

```
        CMS  (/cms/...)                      PORTAL  (/)                 API (/api/v1)
 ┌──────────────────────────┐        ┌────────────────────────┐   ┌──────────────────────┐
 │ GET /cms/conteudos/      │        │ GET /noticias/{cat?}   │   │ GET /api/v1/noticias  │
 │      noticias            │        │ GET /noticia/{slug}    │   │                      │
 │   ContentController@     │        │ GET /noticias/categoria│   │ Swagger\V1\Portal\    │
 │   genericIndex           │        │ GET /noticias-ajax/... │   │ NewController@index  │
 └───────────┬──────────────┘        └───────────┬────────────┘   └──────────┬───────────┘
             │                                   │                           │
             ▼                                   ▼                           ▼
   prefecture.contents.noticias.*        portal.layouts.<modelo>.noticias.*   NewResource
             │                                   │                           │
             └───────────────┬───────────────────┘                           │
                             ▼                                               ▼
                  App\Prefecture\Content  ──belongsTo──▶  App\Prefecture\CategoryContent
                             │
                             ├──hasMany──▶ App\Prefecture\Archive   (destaque + galeria)
                             └──hasOne───▶ App\Prefecture\Link      (vídeo único)
```

## 2. Classes envolvidas

| Classe | Caminho | Papel |
|---|---|---|
| `Content` | `v6/app/Http/Model/Prefecture/Content.php` | Modelo Eloquent da tabela `contents`; `SoftDeletes`; `PresentableTrait`; define os scopes usados pelo portal |
| `CategoryContent` | `v6/app/Http/Model/Prefecture/CategoryContent.php` | Categorias auto-referenciadas; `getAllDescendants()`; scopes `byCategoryNews`, `byCategoryHasContent` |
| `Archive` | `v6/app/Http/Model/Prefecture/Archive.php` | Arquivos do conteúdo (destaque, galeria, anexos); `scopeByHighLight` |
| `Link` | `v6/app/Http/Model/Prefecture/Link.php` | Link único por conteúdo (vídeo/YouTube) — relação `linksUnique()` |
| `ContentController` | `v6/app/Http/Controllers/Prefecture/ContentController.php` | CRUD genérico do CMS dirigido por slug; upload de arquivos; destaque; status; ordem; log de auditoria |
| `CategoryContentController` | `v6/app/Http/Controllers/Prefecture/CategoryContentController.php` | CRUD das categorias (pai/filho) e reordenação |
| `DatatableController` | `v6/app/Http/Controllers/Prefecture/DatatableController.php` | Alimenta a datatable do CMS a partir da view SQL `noticias` |
| `NewsController` | `v6/app/Http/Controllers/Portal/NewsController.php` | Listagem, exibição e AJAX do portal |
| `NewsTrait` | `v6/app/Http/Traits/NewsTrait.php` | Regras de consulta compartilhadas (`getContentNews`, `getContentNewsAjax`, `getCategoriesNews`, `getContentsByParentCategory`) |
| `NewController` | `v6/app/Http/Controllers/Swagger/V1/Portal/NewController.php` | API de dados abertos de notícias |
| `ContentRequest` | `v6/app/Http/Requests/ContentRequest.php` | Validação por slug de categoria (`switch ($slug)`) |
| `ContentPresenter` | `v6/app/Http/Presenters/ContentPresenter.php` | Formatação de data, miniatura de destaque, thumbnail de YouTube/Vimeo |
| `IContentRepository` | `v6/app/Repositories/Contracts/IContentRepository.php` | **Interface vazia** — contrato previsto, não usado |
| `ContentRepositoryDefaultImpl` | `v6/app/Repositories/Eloquent/Prefecture/Content/ContentRepositoryDefaultImpl.php` | Implementação mínima (`structureUpInsert()` retorna `[]`) |
| `ArchiveController` | `v6/app/Http/Controllers/Prefecture/ArchiveController.php` | Rotas `galeria.*` usadas pelo CMS para galeria/drive |

## 3. Fluxo da requisição — CMS (criar/editar)

1. `GET /cms/conteudos/editar/noticias/{id}` -> rota `editar-conteudo`
   (`v6/routes/basepro.php:237`) -> `ContentController@genericEdit`.
2. `genericEdit` -> `genericGenerate('noticias', $id)`
   (`v6/app/Http/Controllers/Prefecture/ContentController.php:158-161`).
3. Em `genericGenerate`, o caso `noticias` monta o payload extra
   (`ContentController.php:203-211`):

   ```php
   if ($slug === 'noticias')
       return $this->returnData('Notícias', $slug, null, [
           'categoryContentSelect' => $this->getItemsToCategoryContentsWithParams(1, ['name', 'id']),
           'secretarias' => $this->getContentByCategoryWith('secretarias', ['title as name', 'slug as valor']),
           'orgaos' => $this->getContentByCategoryWith('orgaos', ['title as name', 'id as valor'])
       ], $id);
   ```

4. `returnData` carrega `content`, `gallery`, `archives`, `category` e
   `destaque`, e devolve a view `prefecture.contents.noticias.edit`.
5. `POST /cms/conteudo` (rota `conteudo.store`) -> `ContentController@store`
   (`ContentController.php:616`), que:
   - valida via `ContentRequest` (caso `noticias`);
   - normaliza `publication_date` (`str_replace('/', '-')` + `strtotime`);
   - gera a slug com `slugfy($request->get('title'))` e desambigua com
     `-<max(id)+1>` em caso de colisão;
   - grava `Content::create($contentData)` dentro de `DB::transaction`;
   - move imagens do Summernote de `summernote-files/temporarios` para
     `summernote-files/content/$slug/$id`;
   - cria `Link` (vídeo) e `Archive` (destaque e galeria);
   - registra auditoria com `activity()`.

## 4. Fluxo da requisição — Portal (listagem)

1. `GET /noticias` -> rota `noticia.index` (`v6/routes/web.php:106`),
   middleware `check.site.manutention` (grupo de `v6/routes/web.php:7`).
2. `NewsController@index` (`v6/app/Http/Controllers/Portal/NewsController.php:32`)
   lê a configuração `noticias`:
   - se `configurations.value == 0` -> modo **agrupado**: lista as categorias
     filhas com conteúdo (`getCategoriesNews(0)`) e, para cada uma, carrega
     `quantidade_itens` matérias (`getContentNews`).
   - caso contrario -> modo simples, com `aviso_noticias` controlando o aviso.
3. `NewsTrait::getContentNews` (`v6/app/Http/Traits/NewsTrait.php:30`):

   ```php
   public function getContentNews($slugCategory = null, $limit = null, $paginate = 12)
   {
       $date = date('Y-m-d H:i:s');
       return $this->getMainContent()
           ->byCategoryContentNews($slugCategory)
           ->where("publication_date", "<=", $date)
           ->byOrder('publication_date')
           ->byStatus()
           ->limit($limit)
           ->paginate($paginate);
   }
   ```

4. `scopeByCategoryContentNews` (`Content.php:212-224`) restringe a
   `category_contents.category_contents_id = 1` (filhas de Notícias) e,
   opcionalmente, a uma categoria específica.
5. A view `portal.layouts.<modelo>.noticias.index` recebe `contents`,
   `categoriesNews`, `layoutToBeDisplayed`.

## 5. Fluxo da requisição — Portal (matéria)

1. `GET /noticia/{slug}` -> `NewsController@showContent`
   (`v6/app/Http/Controllers/Portal/NewsController.php:63`).
2. `NewsTrait::showContentNew` filtra por `publication_date <= now`,
   `status = true` e `slug`.
3. Arquivos: `Archive::where('contents_id', ...)->whereNull('highlight')`;
   PDF inline: `Archive::where('view_pdf', true)->first()`.
4. `updateClicks()` (`NewsController.php:136-141`) incrementa `clicks`.
5. Vídeo: `embedVideo($contentNew->linksUnique->link)`.
6. View: `portal.layouts.<modelo>.noticias.show`.

## 6. Dependências externas

| Pacote | Uso no módulo |
|---|---|
| `laracasts/presenter` | `PresentableTrait` + `ContentPresenter` (datas, miniaturas) |
| `yajra/laravel-datatables` | `DatatableController::get_noticias` |
| `intervention/image` | `Image::make()` em `pathUpdate` e compressão de destaques |
| `maatwebsite/excel` | Exportações (usadas por Publicações, mesmo `ExportsContentTrait`) |
| `barryvdh/laravel-dompdf` | `PDF::loadView('exports.contents')` |
| `spatie/laravel-activitylog` | Trilha `activity()` em todas as escritas |
| `cartalyst/sentinel` | Identidade do operador (`Sentinel::getUser()`) |
| `zizaco/entrust` (via `has_access`) | Permissões `noticias.create`, `noticias.edit`, `noticias.delete` |

## 7. Middleware

| Camada | Middleware | Origem |
|---|---|---|
| Portal | `check.site.manutention` | `v6/routes/web.php:7` (grupo que envolve todas as rotas públicas) |
| CMS | `check.logged` + `check.route.permission` | grupo `/cms` em `v6/routes/basepro.php` e `ContentController::__construct` (`ContentController.php:60`) |
| API | `api` | `v6/app/Providers/RouteServiceProvider.php:66-72` |

## 8. Pontos de extensão

- **Nova subcategoria de notícias**: basta inserir em `category_contents` com
  `category_contents_id = 1` (o `CategoryContentTableSeeder` faz isso).
- **Novo layout de listagem**: campo `layout` (1..3) em `category_contents`;
  validado em `NewsTrait::getLayoutContentActived()`.
- **Novo tipo de conteúdo** (não-notícia): novo slug em `category_contents`,
  novo `case` em `ContentRequest::rules()`, nova pasta
  `v6/resources/views/prefecture/contents/<slug>/` e um `if` em
  `ContentController::genericIndex/genericGenerate`.
