Browse Source

Feat(OnlyOffice): Botão editar em Matéria e Doc Acessório + refatora URLs

- Adiciona botão "Editar com OnlyOffice" na detail de Matéria Legislativa
  para administradores, operadores de matéria e protocolo
- Adiciona botão OnlyOffice na listagem e detail de Documento Acessório
- Refatora URLs do OnlyOffice usando SAPL_INTERNAL_URL para comunicação
  entre containers Docker (evita URLs hardcoded)
- Centraliza lógica de URL em build_onlyoffice_url() e
  get_onlyoffice_browser_url() no utils.py
pull/3858/head
rangelbruno 7 months ago
parent
commit
172e2df176
  1. 2
      .gitignore
  2. 1
      docker/Dockerfile.dev
  3. 9
      sapl/base/onlyoffice_template_views.py
  4. 28
      sapl/materia/onlyoffice_materia_views.py
  5. 28
      sapl/materia/onlyoffice_views.py
  6. 33
      sapl/materia/views.py
  7. 9
      sapl/norma/onlyoffice_views.py
  8. 9
      sapl/protocoloadm/onlyoffice_views.py
  9. 4
      sapl/settings.py
  10. 2
      sapl/templates/materia/documentoacessorio_detail.html
  11. 2
      sapl/templates/materia/materialegislativa_detail.html
  12. 28
      sapl/utils.py

2
.gitignore

@ -124,3 +124,5 @@ NOTAS.txt
# Database dumps
*.sql
dump_*.sql
DADOS

1
docker/Dockerfile.dev

@ -14,6 +14,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
COPY ./requirements ./requirements/
RUN pip install --upgrade pip && \
pip install "setuptools<71" && \
pip install -r ./requirements/dev-requirements.txt
EXPOSE 8000

9
sapl/base/onlyoffice_template_views.py

@ -16,6 +16,7 @@ from django.views.decorators.http import require_http_methods
from django.contrib import messages
from sapl.base.models import DocumentTemplate
from sapl.utils import build_onlyoffice_url, get_onlyoffice_browser_url
logger = logging.getLogger(__name__)
@ -40,10 +41,12 @@ def template_onlyoffice_config(request, pk):
template = get_object_or_404(DocumentTemplate, pk=pk)
# URLs para o OnlyOffice acessar (dentro da rede Docker)
download_url = request.build_absolute_uri(
download_url = build_onlyoffice_url(
request,
reverse('sapl.base:template_onlyoffice_download', kwargs={'pk': pk})
)
callback_url = request.build_absolute_uri(
callback_url = build_onlyoffice_url(
request,
reverse('sapl.base:template_onlyoffice_callback', kwargs={'pk': pk})
)
@ -226,7 +229,7 @@ def template_onlyoffice_editor(request, pk):
template = get_object_or_404(DocumentTemplate, pk=pk)
# URL do OnlyOffice acessível pelo navegador do usuário
onlyoffice_url = settings.ONLYOFFICE_URL
onlyoffice_url = get_onlyoffice_browser_url(request)
context = {
'documento': template,

28
sapl/materia/onlyoffice_materia_views.py

@ -16,6 +16,7 @@ from django.views.decorators.http import require_http_methods
from django.contrib import messages
from sapl.materia.models import MateriaLegislativa, DocumentoAcessorio
from sapl.utils import build_onlyoffice_url, get_onlyoffice_browser_url
logger = logging.getLogger(__name__)
@ -46,10 +47,12 @@ def materia_onlyoffice_config(request, pk):
can_edit = request.user.has_perm('materia.change_materialegislativa')
# URLs para o OnlyOffice acessar (dentro da rede Docker)
download_url = request.build_absolute_uri(
download_url = build_onlyoffice_url(
request,
reverse('sapl.materia:materia_onlyoffice_download', kwargs={'pk': pk})
)
callback_url = request.build_absolute_uri(
callback_url = build_onlyoffice_url(
request,
reverse('sapl.materia:materia_onlyoffice_callback', kwargs={'pk': pk})
)
@ -244,12 +247,14 @@ def materia_onlyoffice_editor(request, pk):
materia = get_object_or_404(MateriaLegislativa, pk=pk)
# Verifica se o usuário tem permissão
if not request.user.has_perm('materia.change_materialegislativa'):
# (administrador, operador de matéria, protocolo)
if not (request.user.is_superuser or
request.user.has_perm('materia.change_materialegislativa')):
messages.error(request, 'Você não tem permissão para editar esta matéria.')
return redirect('sapl.materia:materialegislativa_detail', pk=pk)
# URL do OnlyOffice acessível pelo navegador do usuário
onlyoffice_url = settings.ONLYOFFICE_URL
onlyoffice_url = get_onlyoffice_browser_url(request)
context = {
'documento': materia,
@ -280,10 +285,12 @@ def docacessorio_onlyoffice_config(request, pk):
can_edit = request.user.has_perm('materia.change_documentoacessorio')
# URLs para o OnlyOffice acessar (dentro da rede Docker)
download_url = request.build_absolute_uri(
download_url = build_onlyoffice_url(
request,
reverse('sapl.materia:docacessorio_onlyoffice_download', kwargs={'pk': pk})
)
callback_url = request.build_absolute_uri(
callback_url = build_onlyoffice_url(
request,
reverse('sapl.materia:docacessorio_onlyoffice_callback', kwargs={'pk': pk})
)
@ -482,12 +489,14 @@ def docacessorio_onlyoffice_editor(request, pk):
documento = get_object_or_404(DocumentoAcessorio, pk=pk)
# Verifica se o usuário tem permissão
if not request.user.has_perm('materia.change_documentoacessorio'):
# (administrador, operador de matéria, protocolo)
if not (request.user.is_superuser or
request.user.has_perm('materia.change_documentoacessorio')):
messages.error(request, 'Você não tem permissão para editar este documento.')
return redirect('sapl.materia:documentoacessorio_detail', pk=documento.materia.pk, zpk=pk)
# URL do OnlyOffice acessível pelo navegador do usuário
onlyoffice_url = settings.ONLYOFFICE_URL
onlyoffice_url = get_onlyoffice_browser_url(request)
context = {
'documento': documento,
@ -546,7 +555,8 @@ def materia_gerar_pdf_assinatura(request, pk):
return redirect('sapl.materia:materialegislativa_detail', pk=pk)
# URL do documento para o OnlyOffice (dentro da rede Docker)
download_url = request.build_absolute_uri(
download_url = build_onlyoffice_url(
request,
reverse('sapl.materia:materia_onlyoffice_download', kwargs={'pk': pk})
)

28
sapl/materia/onlyoffice_views.py

@ -15,7 +15,7 @@ from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_http_methods
from sapl.materia.models import Proposicao
from sapl.utils import get_base_url
from sapl.utils import get_base_url, build_onlyoffice_url, get_onlyoffice_browser_url
logger = logging.getLogger(__name__)
@ -44,14 +44,14 @@ def onlyoffice_config(request, pk):
proposicao.autor.operadores.filter(id=request.user.id).exists()
)
base_url = get_base_url(request)
# URLs para o OnlyOffice acessar (dentro da rede Docker)
# OnlyOffice precisa acessar o container SGVP pelo nome do serviço
download_url = request.build_absolute_uri(
# Usa SAPL_INTERNAL_URL quando configurada para comunicação container-a-container
download_url = build_onlyoffice_url(
request,
reverse('sapl.materia:onlyoffice_download', kwargs={'pk': pk})
)
callback_url = request.build_absolute_uri(
callback_url = build_onlyoffice_url(
request,
reverse('sapl.materia:onlyoffice_callback', kwargs={'pk': pk})
)
@ -309,13 +309,7 @@ def onlyoffice_editor(request, pk):
return redirect('sapl.materia:proposicao_detail', pk=pk)
# URL do OnlyOffice acessível pelo navegador do usuário
# Se ONLYOFFICE_URL contém 'onlyoffice' (nome do container), substitui pelo host da requisição
onlyoffice_url = settings.ONLYOFFICE_URL
if 'onlyoffice:' in onlyoffice_url or 'onlyoffice/' in onlyoffice_url:
# É a URL interna do Docker, precisa usar a URL externa
protocol = 'https' if request.is_secure() else 'http'
host = request.get_host().split(':')[0] # Remove porta se existir
onlyoffice_url = f"{protocol}://{host}:8001"
onlyoffice_url = get_onlyoffice_browser_url(request)
context = {
'proposicao': proposicao,
@ -344,10 +338,12 @@ def onlyoffice_confirmar_config(request, pk):
return JsonResponse({"error": "Proposição não está em confirmação"}, status=400)
# URLs para o OnlyOffice acessar o documento
download_url = request.build_absolute_uri(
download_url = build_onlyoffice_url(
request,
reverse('sapl.materia:onlyoffice_download', kwargs={'pk': pk})
)
callback_url = request.build_absolute_uri(
callback_url = build_onlyoffice_url(
request,
reverse('sapl.materia:onlyoffice_callback', kwargs={'pk': pk})
)
@ -437,7 +433,7 @@ def onlyoffice_confirmar_editor(request, pk, hash):
return redirect('sapl.materia:proposicao-confirmar', hash=hash, pk=pk)
# URL do OnlyOffice acessível pelo navegador do usuário
onlyoffice_url = settings.ONLYOFFICE_URL
onlyoffice_url = get_onlyoffice_browser_url(request)
context = {
'proposicao': proposicao,

33
sapl/materia/views.py

@ -1727,6 +1727,30 @@ class DocumentoAcessorioCrud(MasterDetailCrud):
context = super(UpdateView, self).get_context_data(**kwargs)
return context
class DetailView(MasterDetailCrud.DetailView):
template_name = "materia/documentoacessorio_detail.html"
class ListView(MasterDetailCrud.ListView):
def hook_arquivo(self, obj, default, url):
u = self.request.user
can_edit = u.is_authenticated and (
u.is_superuser or
u.has_perm('materia.change_documentoacessorio')
)
html = default
if can_edit:
editor_url = reverse(
'sapl.materia:docacessorio_onlyoffice_editor',
kwargs={'pk': obj.pk}
)
html += (
f'&nbsp;<a class="btn btn-sm btn-primary" '
f'href="{editor_url}" title="Editar com OnlyOffice">'
f'<i class="fa fa-file-word-o"></i></a>'
)
return html, url
class AutoriaCrud(MasterDetailCrud):
model = Autoria
@ -2060,6 +2084,15 @@ class MateriaLegislativaCrud(Crud):
break
context['is_autor'] = is_autor
# Verifica se o usuário tem permissão de edição da matéria
# (administrador, operador de matéria, protocolo)
context['can_edit_materia'] = (
self.request.user.is_authenticated and (
self.request.user.is_superuser or
self.request.user.has_perm('materia.change_materialegislativa')
)
)
return context
class ListView(Crud.ListView, RedirectView):

9
sapl/norma/onlyoffice_views.py

@ -16,6 +16,7 @@ from django.views.decorators.http import require_http_methods
from django.contrib import messages
from sapl.norma.models import NormaJuridica
from sapl.utils import build_onlyoffice_url, get_onlyoffice_browser_url
logger = logging.getLogger(__name__)
@ -42,10 +43,12 @@ def norma_onlyoffice_config(request, pk):
can_edit = request.user.has_perm('norma.change_normajuridica')
# URLs para o OnlyOffice acessar (dentro da rede Docker)
download_url = request.build_absolute_uri(
download_url = build_onlyoffice_url(
request,
reverse('sapl.norma:norma_onlyoffice_download', kwargs={'pk': pk})
)
callback_url = request.build_absolute_uri(
callback_url = build_onlyoffice_url(
request,
reverse('sapl.norma:norma_onlyoffice_callback', kwargs={'pk': pk})
)
@ -245,7 +248,7 @@ def norma_onlyoffice_editor(request, pk):
return redirect('sapl.norma:normajuridica_detail', pk=pk)
# URL do OnlyOffice acessível pelo navegador do usuário
onlyoffice_url = settings.ONLYOFFICE_URL
onlyoffice_url = get_onlyoffice_browser_url(request)
context = {
'documento': norma,

9
sapl/protocoloadm/onlyoffice_views.py

@ -16,6 +16,7 @@ from django.views.decorators.http import require_http_methods
from django.contrib import messages
from sapl.protocoloadm.models import DocumentoAdministrativo
from sapl.utils import build_onlyoffice_url, get_onlyoffice_browser_url
logger = logging.getLogger(__name__)
@ -42,10 +43,12 @@ def docadm_onlyoffice_config(request, pk):
can_edit = request.user.has_perm('protocoloadm.change_documentoadministrativo')
# URLs para o OnlyOffice acessar (dentro da rede Docker)
download_url = request.build_absolute_uri(
download_url = build_onlyoffice_url(
request,
reverse('sapl.protocoloadm:docadm_onlyoffice_download', kwargs={'pk': pk})
)
callback_url = request.build_absolute_uri(
callback_url = build_onlyoffice_url(
request,
reverse('sapl.protocoloadm:docadm_onlyoffice_callback', kwargs={'pk': pk})
)
@ -246,7 +249,7 @@ def docadm_onlyoffice_editor(request, pk):
return redirect('sapl.protocoloadm:documentoadministrativo_detail', pk=pk)
# URL do OnlyOffice acessível pelo navegador do usuário
onlyoffice_url = settings.ONLYOFFICE_URL
onlyoffice_url = get_onlyoffice_browser_url(request)
context = {
'documento': documento,

4
sapl/settings.py

@ -308,6 +308,10 @@ EMAIL_RUNNING = None
ONLYOFFICE_URL = config('ONLYOFFICE_URL', default=config('ONLYOFFICE_SERVER', default='https://onlyoffice.legisinc.com.br'))
ONLYOFFICE_JWT_SECRET = config('ONLYOFFICE_JWT_SECRET', default='')
ONLYOFFICE_JWT_ENABLED = config('ONLYOFFICE_JWT_ENABLED', cast=bool, default=False)
# URL interna para o OnlyOffice acessar o SAPL (ex: http://sapl-dev:8000)
# Quando não configurada, usa a URL do request (funciona quando OnlyOffice
# consegue acessar o SAPL pela mesma URL que o navegador)
SAPL_INTERNAL_URL = config('SAPL_INTERNAL_URL', default='')
# Integração externa de matérias da sessão
SESSAO_MATERIAS_API_URL = config('SESSAO_MATERIAS_API_URL', default='')

2
sapl/templates/materia/documentoacessorio_detail.html

@ -3,7 +3,7 @@
{% block sub_actions %}
{{ block.super }}
{% if perms.materia.change_documentoacessorio %}
{% if perms.materia.change_documentoacessorio or user.is_superuser %}
<div class="actions btn-group btn-group-sm" role="group">
<a class="btn btn-primary" href="{% url 'sapl.materia:docacessorio_onlyoffice_editor' object.pk %}">
<i class="fa fa-file-word-o"></i> {% trans "Editar com OnlyOffice" %}

2
sapl/templates/materia/materialegislativa_detail.html

@ -8,7 +8,7 @@
<a href="{% url 'sapl.materia:acompanhar_materia' object.id %}" class="btn btn-outline-primary">Acompanhar Matéria</a>
</div>
{% endif %}
{% if is_autor and not object.numero_protocolo %}
{% if is_autor and not object.numero_protocolo or can_edit_materia %}
<div class="actions btn-group btn-group-sm" role="group">
<a class="btn btn-primary" href="{% url 'sapl.materia:materia_onlyoffice_editor' object.pk %}">
<i class="fa fa-file-word-o"></i> {% trans "Editar com OnlyOffice" %}

28
sapl/utils.py

@ -427,6 +427,34 @@ def get_base_url(request):
return "{0}://{1}".format(protocol, current_domain)
def build_onlyoffice_url(request, path):
"""
Constrói URL absoluta para o OnlyOffice acessar o SAPL.
Usa SAPL_INTERNAL_URL quando configurada (ambiente Docker),
caso contrário usa a URL do request.
"""
from django.conf import settings
internal_url = getattr(settings, 'SAPL_INTERNAL_URL', '')
if internal_url:
return internal_url.rstrip('/') + path
return request.build_absolute_uri(path)
def get_onlyoffice_browser_url(request):
"""
Retorna a URL do OnlyOffice acessível pelo navegador do usuário.
Se ONLYOFFICE_URL é um nome de container Docker (ex: http://onlyoffice:80),
substitui pelo host da requisição com porta 8001.
"""
from django.conf import settings
onlyoffice_url = settings.ONLYOFFICE_URL
if 'onlyoffice:' in onlyoffice_url or 'onlyoffice/' in onlyoffice_url:
protocol = 'https' if request.is_secure() else 'http'
host = request.get_host().split(':')[0]
onlyoffice_url = f"{protocol}://{host}:8001"
return onlyoffice_url
def create_barcode(value, width=170, height=50, dpi=72):
"""
creates a base64 encoded barcode PNG image

Loading…
Cancel
Save