12 KiB
Configuração do Docker
docker-compose-dev.yml
O OnlyOffice é executado como um container Docker separado. Adicione ao arquivo docker/docker-compose-dev.yml:
version: '3.7'
services:
sapl-dev:
container_name: sapl-dev
image: sapl:dev
build:
context: ../
dockerfile: ./docker/Dockerfile.dev
command: python3 manage.py runserver 0:8000
volumes:
- ..:/sapl-dev
ports:
- "8000:8000"
environment:
SECRET_KEY: '$dkhxm-$zvxdox$g2-&w^1i!_z1juq0xwox6e3#gy6w_88!3t^'
DEBUG: 'True'
DATABASE_URL: postgresql://sapl:sapl@host.docker.internal:5432/sapl
TZ: America/Sao_Paulo
ONLYOFFICE_URL: 'http://onlyoffice:80'
depends_on:
- onlyoffice
onlyoffice:
container_name: onlyoffice-documentserver
image: onlyoffice/documentserver:latest
ports:
- "8001:80"
environment:
- JWT_ENABLED=false
- JWT_SECRET=your-secret-key-change-this
volumes:
- onlyoffice_data:/var/www/onlyoffice/Data
- onlyoffice_log:/var/log/onlyoffice
- onlyoffice_fonts:/usr/share/fonts/truetype/custom
restart: unless-stopped
volumes:
onlyoffice_data:
onlyoffice_log:
onlyoffice_fonts:
Portas Utilizadas
| Serviço | Porta Interna | Porta Externa | Descrição |
|---|---|---|---|
| SAPL | 8000 | 8000 | Aplicação Django |
| OnlyOffice | 80 | 8001 | Document Server |
Comunicação entre Containers
- Navegador → OnlyOffice:
http://localhost:8001(porta externa) - OnlyOffice → SAPL:
http://sapl-dev:8000(nome do container na rede Docker) - SAPL → OnlyOffice:
http://onlyoffice:80(nome do container na rede Docker)
Configuração do Django
settings.py
Adicione as seguintes configurações ao sapl/settings.py:
# OnlyOffice Document Server Configuration
ONLYOFFICE_URL = config('ONLYOFFICE_URL', default='http://localhost:8001')
ONLYOFFICE_JWT_SECRET = config('ONLYOFFICE_JWT_SECRET', default='')
ONLYOFFICE_JWT_ENABLED = config('ONLYOFFICE_JWT_ENABLED', cast=bool, default=False)
Variáveis de Ambiente
| Variável | Descrição | Valor Padrão |
|---|---|---|
ONLYOFFICE_URL |
URL do OnlyOffice Document Server | http://localhost:8001 |
ONLYOFFICE_JWT_SECRET |
Chave secreta para autenticação JWT | (vazio) |
ONLYOFFICE_JWT_ENABLED |
Habilitar autenticação JWT | False |
Tipos de Documentos Suportados
A integração OnlyOffice está disponível para os seguintes tipos de documentos:
| Tipo de Documento | Modelo Django | Campo de Arquivo | App |
|---|---|---|---|
| Proposição | Proposicao |
texto_original |
materia |
| Matéria Legislativa | MateriaLegislativa |
texto_original |
materia |
| Documento Acessório | DocumentoAcessorio |
arquivo |
materia |
| Documento Administrativo | DocumentoAdministrativo |
texto_integral |
protocoloadm |
| Norma Jurídica | NormaJuridica |
texto_integral |
norma |
Arquitetura da Implementação
Estrutura de Views
Cada módulo possui 4 endpoints para integração com OnlyOffice:
- editor: Renderiza a página com o editor OnlyOffice
- config: Retorna configuração JSON para inicializar o editor
- download: Permite que o OnlyOffice baixe o documento
- callback: Recebe notificações de salvamento do OnlyOffice
URLs Implementadas
Proposição (já existia)
/proposicao/<pk>/onlyoffice/editor
/proposicao/<pk>/onlyoffice/config
/proposicao/<pk>/onlyoffice/download
/proposicao/<pk>/onlyoffice/callback
Matéria Legislativa
/materia/<pk>/onlyoffice/editor
/materia/<pk>/onlyoffice/config
/materia/<pk>/onlyoffice/download
/materia/<pk>/onlyoffice/callback
Documento Acessório
/materia/documentoacessorio/<pk>/onlyoffice/editor
/materia/documentoacessorio/<pk>/onlyoffice/config
/materia/documentoacessorio/<pk>/onlyoffice/download
/materia/documentoacessorio/<pk>/onlyoffice/callback
Documento Administrativo
/docadm/<pk>/onlyoffice/editor
/docadm/<pk>/onlyoffice/config
/docadm/<pk>/onlyoffice/download
/docadm/<pk>/onlyoffice/callback
Norma Jurídica
/norma/<pk>/onlyoffice/editor
/norma/<pk>/onlyoffice/config
/norma/<pk>/onlyoffice/download
/norma/<pk>/onlyoffice/callback
Arquivos Criados/Modificados
Arquivos Criados
Views OnlyOffice
| Arquivo | Descrição |
|---|---|
sapl/protocoloadm/onlyoffice_views.py |
Views para Documento Administrativo |
sapl/norma/onlyoffice_views.py |
Views para Norma Jurídica |
sapl/materia/onlyoffice_materia_views.py |
Views para Matéria Legislativa e Documento Acessório |
Templates
| Arquivo | Descrição |
|---|---|
sapl/templates/onlyoffice/onlyoffice_editor.html |
Template genérico do editor |
sapl/templates/materia/documentoacessorio_detail.html |
Detail com botão OnlyOffice |
Arquivos Modificados
URLs
| Arquivo | Modificação |
|---|---|
sapl/protocoloadm/urls.py |
Adicionados 4 endpoints OnlyOffice |
sapl/norma/urls.py |
Adicionados 4 endpoints OnlyOffice |
sapl/materia/urls.py |
Adicionados 8 endpoints OnlyOffice (matéria + doc acessório) |
Templates (botão OnlyOffice)
| Arquivo | Modificação |
|---|---|
sapl/templates/protocoloadm/documentoadministrativo_detail.html |
Botão "Editar com OnlyOffice" |
sapl/templates/norma/normajuridica_detail.html |
Botão "Editar com OnlyOffice" |
sapl/templates/materia/materialegislativa_detail.html |
Botão "Editar com OnlyOffice" |
Formulário de Proposição
| Arquivo | Modificação |
|---|---|
sapl/materia/forms.py |
Opção "Criar com OnlyOffice" sempre visível |
sapl/materia/views.py |
Redirecionamento para OnlyOffice após salvar |
sapl/templates/materia/proposicao_form.html |
Layout melhorado |
Fluxo de Funcionamento
1. Abertura do Editor
Usuário clica em "Editar com OnlyOffice"
↓
GET /documento/<pk>/onlyoffice/editor
↓
Renderiza template com iframe do OnlyOffice
↓
JavaScript busca configuração via AJAX
↓
GET /documento/<pk>/onlyoffice/config
↓
Retorna JSON com URLs e configurações
↓
OnlyOffice inicializa e busca documento
↓
GET /documento/<pk>/onlyoffice/download
↓
Retorna arquivo .docx (ou documento em branco)
2. Salvamento do Documento
Usuário edita documento no OnlyOffice
↓
OnlyOffice faz autosave/forcesave
↓
POST /documento/<pk>/onlyoffice/callback
↓
Body: { "status": 2, "url": "http://..." }
↓
SAPL baixa documento da URL fornecida
↓
Salva no campo de arquivo do modelo
↓
Retorna { "error": 0 }
3. Status do Callback
| Status | Significado | Ação |
|---|---|---|
| 1 | Documento sendo editado | Nenhuma |
| 2 | Documento pronto para salvar | Baixar e salvar |
| 4 | Documento fechado sem alterações | Nenhuma |
| 6 | Documento salvo (forcesave) | Baixar e salvar |
Problemas Conhecidos e Soluções
1. OnlyOffice não consegue baixar o documento
Sintoma: Editor carrega mas mostra erro ao abrir documento.
Causa: OnlyOffice não consegue acessar a URL de download do SAPL.
Solução: As URLs precisam ser convertidas para usar o nome do container Docker:
# No código das views:
host = request.get_host()
download_url = download_url.replace(f'http://{host}', 'http://sapl-dev:8000')
download_url = download_url.replace(f'https://{host}', 'http://sapl-dev:8000')
2. Callback não salva o documento
Sintoma: Edições são perdidas ao fechar o editor.
Causa: SAPL não consegue baixar o documento da URL fornecida pelo OnlyOffice.
Solução: Converter a URL do callback para acessar via rede Docker:
# Na função de callback:
if 'localhost:8001' in download_url:
download_url = download_url.replace('localhost:8001', 'onlyoffice:80')
3. Erro de JWT
Sintoma: OnlyOffice rejeita requisições com erro de token.
Causa: JWT está habilitado no OnlyOffice mas não configurado no SAPL.
Solução:
- Opção 1: Desabilitar JWT no OnlyOffice (
JWT_ENABLED=false) - Opção 2: Configurar mesmo segredo em ambos:
# docker-compose.yml environment: - JWT_ENABLED=true - JWT_SECRET=sua-chave-secreta# settings.py ou .env ONLYOFFICE_JWT_ENABLED=True ONLYOFFICE_JWT_SECRET=sua-chave-secreta
4. Opção OnlyOffice não aparece no formulário de Proposição
Sintoma: Ao criar proposição, não aparece a opção "Criar com OnlyOffice".
Causa: A opção estava condicionada à configuração texto_articulado_proposicao.
Solução: Modificado sapl/materia/forms.py para sempre mostrar as opções de tipo de texto:
# Sempre incluir tipo_texto para permitir escolher entre Arquivo Digital e OnlyOffice
if 'tipo_texto' not in self._meta.fields:
self._meta.fields.append('tipo_texto')
# Ajustar choices baseado na configuração
if not self.texto_articulado_proposicao:
self.fields['tipo_texto'].choices = [
('D', _('Arquivo Digital')),
('O', _('Criar com OnlyOffice'))
]
5. Container OnlyOffice não inicia
Sintoma: Container fica reiniciando ou não responde.
Causa: OnlyOffice requer recursos significativos (mínimo 2GB RAM).
Solução:
- Verificar logs:
docker logs onlyoffice-documentserver - Aumentar recursos do Docker Desktop
- Aguardar inicialização completa (~2-3 minutos na primeira vez)
6. Documento em branco não é criado
Sintoma: Erro ao abrir editor para documento sem arquivo.
Causa: Biblioteca python-docx não instalada.
Solução: Adicionar ao requirements.txt:
python-docx
Melhorias no Formulário de Proposição
Alterações Visuais
O template sapl/templates/materia/proposicao_form.html foi completamente reformulado para melhorar a experiência do usuário.
1. Botão "Novo Tipo"
Antes: Colado ao select, sem espaçamento adequado.
Depois: Posicionado ao lado do label com flexbox:
#div_id_tipo .tipo-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 8px;
}
2. Opções de Tipo de Texto
Antes: Radio buttons simples, difíceis de clicar.
Depois: Cards visuais com hover e seleção destacada:
#div_id_tipo_texto .form-check label {
padding: 14px 20px;
background: white;
border: 2px solid #dee2e6;
border-radius: 8px;
cursor: pointer;
transition: all 0.2s ease;
}
#div_id_tipo_texto .form-check label.checked {
border-color: #007bff;
background: #e7f1ff;
box-shadow: 0 0 0 1px #007bff;
}
3. Layout dos Campos
Antes: Campos divididos em colunas (5 + 7).
Depois: Campos ocupam largura total (12 colunas).
# forms.py
fields.append(to_column((InlineRadios('tipo_texto'), 12)))
fields.append(to_column(('texto_original', 12)))
4. Botões do Formulário
Antes: Desalinhados.
Depois: Alinhados com flexbox e separados visualmente:
.form-group.row.justify-content-between {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20px 0;
margin-top: 20px;
border-top: 1px solid #e9ecef;
}
5. Feedback de Busca de Matéria
Antes: Apenas texto.
Depois: Alertas coloridos com ícones:
if (data.pagination.total_entries === 1) {
$(".ementa_materia")
.html('<strong><i class="fa fa-check-circle text-success"></i> Matéria encontrada:</strong> ' + data.results[0].ementa)
.addClass('alert-info');
} else {
$(".ementa_materia")
.html('<i class="fa fa-exclamation-triangle"></i> <em>Matéria não encontrada</em>')
.addClass('alert-warning');
}
Testando a Integração
1. Iniciar os containers
cd docker
docker-compose -f docker-compose-dev.yml up -d
2. Verificar status
# SAPL
curl http://localhost:8000
# OnlyOffice (aguardar ~2 min na primeira vez)
curl http://localhost:8001/healthcheck
3. Testar no navegador
- Acessar
http://localhost:8000 - Fazer login
- Navegar até um documento (Proposição, Matéria, Norma, etc.)
- Clicar em "Editar com OnlyOffice"
- Editar o documento
- Fechar e verificar se foi salvo
4. Verificar logs
# Logs do SAPL
docker logs -f sapl-dev
# Logs do OnlyOffice
docker logs -f onlyoffice-documentserver