Mapas Culturais 7.8
Release Notes

Mapas Culturais 7.8

De v7.7.0 a v7.8.0 — lançado em 2 de julho de 2026.
525 arquivos alterados, 49.228 inserções, 6.433 remoções, 30 versões patch (v7.7.46–v7.7.64) consolidadas.

1. Contexto

Para quem não conhece o Mapas Culturais

O Mapas Culturais é uma plataforma open-source de mapeamento e gestão cultural, usada por municípios, estados e órgãos federais no Brasil. Ela permite que gestores públicos criem editais e oportunidades culturais (cursos, prêmios, fomentos), recebam inscrições de artistas e produtores, avaliem essas inscrições por meio de comissões, e acompanhem os projetos aprovados.

Para quem já conhece a base de código, o ciclo principal é: um agente cria uma Oportunidade com múltiplas Fases (inscrição, seleção, recurso, prestação de contas). Inscrições (registrations) fluem pelas fases conforme são selecionadas. Cada fase pode ter uma Configuração de Método de Avaliação (técnica, qualificação documental, contínua), com critérios e seções. Avaliadores são distribuídos por comissões, e suas notas geram um resultado consolidado.

O que existia até v7.7

Até a versão 7.7, o ciclo de vida de uma oportunidade terminava essencialmente na publicação do resultado final. Havia uma fase de prestação de informações (reporting), mas sem estrutura para acompanhar modificações no projeto aprovado. O plano de metas era básico — apenas título, descrição e entregas com poucos campos. O sistema de avaliação funcionava, mas sofria com problemas de integridade quando critérios eram editados após o início das avaliações, e a distribuição de avaliadores não respeitava quotas por comissão.

2. A essência desta release

Se você precisasse resumir a v7.8 em uma frase: ela transforma o Mapas Culturais de um sistema de seleção em um sistema de gestão de projetos culturais. As três grandes apostas são:

  • Vida após a aprovação. A nova fase de execução permite que proponentes aprovados solicitem alterações (data, orçamento, local) e que uma comissão avalie cada pedido — tudo registrado e visível na ficha da inscrição.
  • Profundidade no planejamento. O plano de metas ganhou dezenas de campos configuráveis (equipe, orçamento, acessibilidade, comunicação, comunidades, meio ambiente) e contrapartes de monitoramento para registrar o que foi efetivamente executado.
  • Robustez nas avaliações. Bônus de pontuação com modo fixo e teto, validação de integridade entre seções e critérios, distribuição por quotas, e controle granular de exclusão de critérios com avaliações em andamento.
1
Módulo novo
30+
Campos novos no workplan
40+
Testes novos
12
Correções de bugs

3. Fase de Execução

Novo

O problema

Um projeto cultural é aprovado. O proponente recebe o fomento e começa a executar. Mas projetos culturais são vivos: a data do evento muda, o orçamento precisa de ajustes, o local não está mais disponível. Até a v7.7, não havia mecanismo estruturado para registrar e avaliar essas alterações. O gestor não tinha visibilidade, e o proponente não tinha canal formal.

A solução

A fase de execução é uma nova fase no ciclo de vida da oportunidade, inserida após a publicação do resultado final e antes da fase de prestação de informações. O proponente aprovado pode abrir pedidos de alteração, cada um avaliado por uma comissão de avaliação independente. Os pedidos ficam registrados no histórico da inscrição e não bloqueiam as fases seguintes.

Inscrição Submissão de propostas Seleção Avaliação técnica / documental Recurso Pedido de revisão Execução Pedidos de alteração (data, orçamento, local) avaliados por comissão NOVO Prestação de Informações Relatório final Resultado Final publicado aqui
A fase de execução se posiciona entre o resultado final e a prestação de informações. Apenas proponentes aprovados (status = selecionado) na última fase podem criar pedidos. Cada pedido é uma inscrição independente vinculada à inscrição original pelo mesmo número.

Como funciona na prática

O gestor cria a fase via modal simples, definindo datas de inscrição e avaliação (que devem ser posteriores à data de publicação do resultado final). A fase usa método de avaliação simples e o formulário é montado livremente, sem categorias fixas.

O proponente aprovado abre um pedido de alteração. O sistema cria uma nova inscrição na fase de execução, vinculada à inscrição original pelo campo number e por previousPhaseRegistrationId. Avaliadores da comissão avaliam o pedido dentro da janela de datas.

Decisão de design

A fase de execução é excluída do sincronismo automático de inscrições entre fases. Isso é intencional: os pedidos de alteração são independentes e não devem bloquear ou afetar as fases seguintes de prestação de informações. Os métodos enqueueRegistrationSync, syncRegistrations e importPreviousPhaseRegistrations retornam false para fases de execução.

Os pedidos aparecem em três lugares: na ficha da inscrição original, na linha do tempo de fases, e na lista do gestor — todos identificados com o número da inscrição original e a ordem de criação. Apenas uma fase de execução é permitida por oportunidade (validado por hasExecutionPhase).

4. Plano de Metas e Monitoramento

Expandido

De formulário simples a sistema de gestão

O plano de metas era, essencialmente, um conjunto de metas com entregas contendo poucos campos (título, descrição, período, valor). Na v7.8, ele se torna um sistema completo de planejamento e acompanhamento, com dezenas de campos configuráveis cobrindo todas as dimensões de um projeto cultural.

O modelo de dados: planned → executed

Cada campo de entrega (delivery) agora existe em dois tempos: planejado (preenchido na inscrição) e executado (preenchido no monitoramento). Por exemplo, totalBudget registra o orçamento previsto, enquanto executedTotalBudget registra quanto foi efetivamente gasto. Essa dualidade se repete para todos os novos campos.

Configuração da Oportunidade inform + require por campo Fase de Inscrição Campos planejados (Delivery) totalBudget paidStaffByRole communicationChannels accessibilityMeasures communityCoauthorsDetail environmentalPractices monthInitial / monthEnd ... e mais 20+ campos Fase de Monitoramento Campos executados (Delivery) executedTotalBudget executedPaidStaffByRole executedCommunicationChannels executedAccessibilityMeasures executedCommunityCoauthors executedEnvironmentalPractices executedMonthInitial / End ... espelho de cada campo aprovação
Cada campo de entrega existe em dois tempos: o planejamento (inscrição) e a execução (monitoramento). O gestor configura visibilidade e obrigatoriedade independentes para cada lado.

Categorias de campos novos

Categoria Exemplos de campos Tipo
Orçamento e produção totalBudget, unitPrice, commercialUnits currency, number
Equipe paidStaffByRole, teamCompositionGender, teamCompositionRace JSON array/object
Alcance geográfico numberOfCities, numberOfNeighborhoods number
Comunicação hasPressStrategy, communicationChannels boolean, multiselect
Acessibilidade hasAccessibilityPlan, expectedAccessibilityMeasures boolean, multiselect (~30 opções)
Comunidades e inclusão hasCommunityCoauthors, hasTransInclusionStrategy boolean + text detail
Meio ambiente hasEnvironmentalPractices boolean + text
Inovação e documentação hasInnovationAction, documentationTypes boolean, multiselect

Configuração pelo gestor

Cada campo é controlado por um par de flags na oportunidade: workplan_deliveryInform<Field> (exibir) e workplan_deliveryRequire<Field> (obrigatório). O mesmo padrão se repete para monitoramento: workplan_monitoringInform<Field> e workplan_monitoringRequire<Field>. Isso permite ao gestor configurar exatamente quais dimensões são relevantes para cada edital, sem forçar todos os campos em todas as oportunidades.

Validação movida

A responsabilidade de validação do plano de metas foi transferida do módulo ProjectMonitoring para OpportunityWorkplan. Agora, toda a lógica de validação — tanto para a fase de inscrição quanto para a de monitoramento — vive em um único lugar. Os erros são reportados de forma estruturada via workplanProxy, aninhados como errors.workplanProxy.deliveries[id][field][].

Exportação em planilha

Dois novos hooks (SpreadsheetJob.getHeader:after e SpreadsheetJob.getBatch:after) adicionam colunas do plano de metas à exportação de inscrições. Aproximadamente 25 colunas por inscrição, incluindo duração do projeto, segmento cultural, detalhes de metas e todos os campos de entrega. Múltiplas metas/entregas são separadas por pipe (|), booleanos renderizados como "Sim"/"Não", e campos JSON são achatados.

5. Overhaul do sistema de avaliação

As mudanças no sistema de avaliação são as mais densas desta release. Elas atacam quatro problemas que afetavam editais em produção: a rigidez do bônus de pontuação, a fragilidade na edição de critérios, a distribuição desigual de avaliadores, e a propagação incorreta entre fases.

Melhoria

Bônus de pontuação: agora com modo fixo

O sistema de bônus por políticas afirmativas (cotas) suportava apenas bônus percentuais. Agora suporta dois modos:

  • Percentual (legado): adiciona uma porcentagem da nota técnica bruta como bônus. Exemplo: nota 80 × 10% = +8 pontos.
  • Fixo (novo): adiciona um número fixo de pontos. Exemplo: +5 pontos independente da nota.

Ambos suportam um teto configurável (roof) que limita o bônus máximo aplicado. Se as configurações de bônus forem alteradas após resultados publicados, o sistema bloqueia a modificação para não-administradores e recalcula automaticamente todas as avaliações já enviadas.

Modo Percentual Nota técnica bruta 80 Bônus (10% por cota) +8 Subtotal 88 Teto: 6 pontos 8 > 6 → limita Resultado final 86 Modo Fixo Nota técnica bruta 80 Bônus (+5 fixo por cota) +5 Subtotal 85 Teto: 8 pontos 5 ≤ 8 → mantém Resultado final 85
Comparação dos dois modos de bônus com o mesmo edital: nota bruta 80. No modo percentual, o bônus de 8 pontos é limitado pelo teto de 6. No modo fixo, os 5 pontos ficam dentro do teto de 8. O teto protege contra bonificações desproporcionais em ambos os modos.
Melhoria

Integridade de critérios e seções

Até a v7.7, era possível criar critérios órfãos (referenciando seções deletadas) ou com campos obrigatórios vazios — especialmente porque a interface de configuração faz auto-save a cada alteração, salvando estados intermediários enquanto o gestor ainda está digitando.

Na v7.8, três mecanismos trabalham juntos:

  • Sanitização de rascunho (sanitizeCriteriaSectionsDraft()): roda antes de cada save(), removendo seções sem nome, critérios sem campos obrigatórios, e critérios órfãos. Isso lida com os estados intermediários do auto-save.
  • Validação de integridade (validateCriteriaSectionsIntegrity()): roda como regra de validação da entidade. Verifica se todos os critérios referenciam seções válidas e têm campos obrigatórios preenchidos. Para qualificação documental: cada seção deve ter pelo menos um critério. Para avaliação técnica: seções vazias são removidas automaticamente.
  • Controle de exclusão (validateCriteriaDeletion()): impede que não-administradores removam critérios ou seções quando existem avaliações iniciadas. Administradores podem excluir, e removeDeletedCriteriaFromEvaluations() limpa as notas/respostas correspondentes das avaliações existentes e reconsolida o resultado.
Melhoria

Distribuição por quotas

O algoritmo de distribuição de inscrições para avaliadores foi reescrito com lógica de quotas por comissão. O fluxo é:

  1. Avaliadores com maxRegistrations configurado recebem até esse limite.
  2. O restante das inscrições é dividido igualmente entre avaliadores sem limite.
  3. Restos da divisão inteira são distribuídos aleatoriamente (via shuffle).
  4. A ordenação considera a capacidade restante da quota de cada avaliador, não apenas a contagem total.

A query SQL foi corrigida para eliminar linhas duplicadas (havia um LEFT JOIN redundante em registration_evaluation). Avaliações já realizadas agora são preservadas durante a redistribuição, evitando que um avaliador que já completou sua avaliação seja desatribuído.

Melhorias na fase de recurso

  • Configuração para exibir ou ocultar o detalhamento da avaliação anterior na fase de recurso.
  • Configuração para habilitar/desabilitar o sincronismo automático de inscrições quando o recurso é deferido.
  • Datas de recurso visíveis no step vertical de fases.
  • Detalhamento da avaliação da fase anterior exibido para avaliadores da fase de recurso.

Números sequenciais nos avaliadores

Avaliadores das comissões agora recebem números sequenciais, visíveis na interface, na tabela e na planilha de avaliações. Atualização automática é aplicada em editais já existentes via db update. Busca de avaliadores agora suporta ID do agente no formato #ID e e-mail do usuário.

6. API, performance e segurança

Queries multi-fase paginadas

O método apiFindRegistrations() foi reescrito com uma otimização significativa para oportunidades multi-fase. Quando a query usa @limit, a fase alvo é consultada primeiro com paginação, e as fases anteriores são consultadas apenas pelo campo number (sem carregar a entidade completa). Isso evita o carregamento N+1 de inscrições de fases anteriores em oportunidades grandes.

As quotas de inscrição coletiva agora são aplicadas apenas à fase alvo, não às fases anteriores — que são consultadas como lookups suplementares.

Remoção atômica de avaliador

Novo endpoint POST_deleteEvaluationAndRemoveValuer() permite remover um avaliador de uma inscrição específica e deletar sua avaliação submetida em uma única operação. Requer permissão modifyValuers. O fluxo: adiciona o avaliador à valuersExcludeList, remove da valuersIncludeList, atualiza a coluna JSONB valuers via SQL raw, e deleta a entidade RegistrationEvaluation (com lookup multi-estratégia).

Segurança de upload de arquivos

Hardening

A blocklist de MIME types foi expandida para incluir php3php8, phtml, phar, x-python, x-perl, x-sh, svg+xml, xml, yaml, sql, ini, log, entre outros. Uma nova whitelist regex-based (app.default_allowed_mime_types) valida tipos permitidos quando o grupo de arquivos não define a sua própria. A blocklist de extensões (app.not_allowed_extensions) bloqueia .php, .exe, .sh, .py, .svg, .xml, .env, .sql, etc.

Performance: modelos de oportunidade

O loop N+1 em findOpportunitiesModels foi substituído por uma query SQL única. Flushes ao banco ao gerar oportunidades a partir de modelos foram reduzidos. A listagem de modelos agora identifica corretamente os oficiais pelo selo verificado.

Importação assíncrona de campos

Novo job ImportFields para importação de campos de formulário em background, com templates de e-mail de sucesso (import_fields_success) e erro (import_fields_error). A importação via TXT agora preserva corretamente os filtros por categoria, tipo de proponente e faixa.

Outras melhorias

  • Header de proxy configurável (PROXY_HEADER): identifica o IP do visitante em ambientes com proxy (Cloudflare, etc.).
  • Filtro por grupo de arquivos na API query ($registeredFileGroups).
  • Fluxo LGPD de solicitação de exclusão de conta com modais de confirmação e e-mail ao responsável.
  • Avatar obrigatório por tipo de entidade, configurável via variáveis de ambiente.
  • Validação de idade: menores de 18 anos não podem enviar mensagens de contato.
  • Otimização de cache Buildx nos workflows de CI, develop e release candidate.

7. Tour pelo código

O grande diff de App.php

O arquivo mais alterado em volume é src/core/App.php — 4.910 linhas de diff. Mas a maior parte é cosmética: uma passagem de formatação automatizada que converteu aspas simples para duplas, braces de K&R para Allman, adicionou trailing commas em chamadas multi-linha, e traduziu docblocks de inglês para português. Não houve mudança estrutural — nenhum método foi adicionado ou removido.

Três bug fixes reais estavam escondidos na formatação:

  • Ponto-e-vírgula duplo: new $class($id);;new $class($id);
  • Case com ponto-e-vírgula em vez de dois-pontos: case 'k';case "k": — um bug latente que causava fallthrough silencioso no getMaxUploadSize().
  • Anotações @property-read@property: sinaliza que certas propriedades podem agora ser escritas via magic setter.

Novos módulos e arquivos

Arquivo Linhas Propósito
OpportunityExecution/Module.php 647 Fase de execução — pedidos de alteração e avaliação
EvaluationConsolidationTest.php 776 Testes do novo bônus e reconsolidação
PhaseRegistrationSyncTest.php 989 Testes de propagação entre fases e recurso
EvaluationMethodTechnicalTest.php 3.237 Cobertura extensiva da avaliação técnica
OpportunityPhasesGettersTest.php 1.163 Testes dos getters de fase (main, upstream, downstream)
PointRewardBonusTest.php 880 Cobertura dos modos fixo e percentual com teto

Arquivos mais modificados (existentes)

Arquivo Δ linhas Área de mudança
OpportunityWorkplan/Module.php +1.958 Novos campos, validação unificada, exportação
ProjectMonitoring/Module.php +612 Campos executados, persistência, serialização
OpportunityPhases/Module.php +604 Appeal phase, sync, getters, date validation
EvaluationMethod.php +601 Quotas, redistribution, consolidation
EvaluationMethodTechnical/Module.php +532 Point reward, criteria sanitization
ApiQuery.php +587 PHPDoc, file group filtering
Controllers/Opportunity.php +598 Paginated phases, import job, docblocks

Trecho: normalização do bônus de pontuação

// Converte configurações legadas (array com fieldPercent)
// para o formato canônico com type e rules[].bonusValue
public function normalizePointRewardConfig(array $config): array
{
    if (isset($config['type'])) {
        return $config; // já está no formato novo
    }

    // Legado: array de regras com fieldPercent
    $rules = [];
    foreach ($config as $rule) {
        $rules[] = [
            'field' => $rule['field'],
            'bonusValue' => $rule['fieldPercent'],
        ];
    }

    return [
        'type' => 'percentage',
        'rules' => $rules,
    ];
}

Trecho: sanitização de rascunho de critérios

// Roda antes de cada save() para limpar estados
// intermediários do auto-save da UI
protected function sanitizeCriteriaSectionsDraft(
    array &$sections,
    array &$criteria
): void {
    // Remove critérios órfãos (sectionId inexistente)
    $sectionIds = array_column($sections, 'id');
    $criteria = array_filter($criteria,
        fn($c) => in_array(
            $c['sectionId'] ?? null, $sectionIds
        )
    );

    // Remove seções sem nome (auto-save intermediário)
    $sections = array_filter($sections,
        fn($s) => !empty(trim($s['name'] ?? ''))
    );
}

8. Resumo

A v7.8.0 é uma release de expansão funcional significativa. O Mapas Culturais passa a cobrir o ciclo completo de um projeto cultural — da inscrição ao acompanhamento pós-aprovação — e ganha profundidade em três dimensões críticas: planejamento de projetos (workplan), robustez de avaliações, e segurança de uploads.

Se você está migrando de v7.7

As atualizações automáticas de banco (_dbUpdates()) cuidam de normalizar bônus de pontuação antigos e preencher números sequenciais de avaliadores em editais existentes. A flag de envio de e-mails de recurso está agora desligada por padrão — se seu edital usa e-mails de recurso, habilite-a explicitamente. O PROXY_HEADER deve ser configurado se você usa Cloudflare ou proxy similar.

Destaques por persona

  • Gestores: fase de execução para acompanhar alterações de projetos aprovados; plano de metas com dezenas de campos novos; tutorial passo a passo; validações configuráveis por campo.
  • Avaliadores: bônus de pontuação com modo fixo e teto; números sequenciais na comissão; busca por ID e e-mail; exclusão controlada de critérios.
  • Proponentes: pedidos de alteração estruturados; foto de perfil obrigatória configurável; duplicação de campos e anexos no formulário.
  • DevOps: cache Buildx otimizado; header de proxy configurável; tolerância a tabelas ausentes na inicialização; hardening de MIME types.