@ -197,6 +197,20 @@ def materia_onlyoffice_callback(request, pk):
# Status 2 ou 6 significa que o documento foi salvo
if status in [ 2 , 6 ] and download_url :
logger . info ( f " URL original recebida: { download_url } " )
# Substitui o host da URL pelo ONLYOFFICE_URL configurado
# O OnlyOffice retorna URLs com seu próprio hostname que pode não ser acessível
import re
from urllib . parse import urlparse
onlyoffice_parsed = urlparse ( settings . ONLYOFFICE_URL )
onlyoffice_base = f " { onlyoffice_parsed . scheme } :// { onlyoffice_parsed . netloc } "
download_url = re . sub (
r ' https?://[^/]+ ' ,
onlyoffice_base ,
download_url
)
logger . info ( f " URL substituída para: { download_url } " )
materia = get_object_or_404 ( MateriaLegislativa , pk = pk )
@ -204,6 +218,7 @@ def materia_onlyoffice_callback(request, pk):
import requests
try :
response = requests . get ( download_url , timeout = 30 )
logger . info ( f " Download response: status= { response . status_code } , size= { len ( response . content ) } " )
except Exception as e :
logger . error ( f " Erro ao fazer requisição de download: { e } " )
return JsonResponse ( { " error " : 1 } )
@ -215,7 +230,9 @@ def materia_onlyoffice_callback(request, pk):
# Remove arquivo antigo se existir
if materia . texto_original :
old_file = materia . texto_original . name
materia . texto_original . delete ( save = False )
logger . info ( f " Arquivo antigo removido: { old_file } " )
try :
materia . texto_original . save (
@ -239,6 +256,56 @@ def materia_onlyoffice_callback(request, pk):
return JsonResponse ( { " error " : 1 } )
@login_required
@require_http_methods ( [ " GET " ] )
def materia_check_doc ( request , pk ) :
"""
Verifica se a matéria já possui documento salvo .
Usado pelo frontend para polling após forcesave do OnlyOffice .
"""
materia = get_object_or_404 ( MateriaLegislativa , pk = pk )
has_document = bool ( materia . texto_original )
return JsonResponse ( { " has_document " : has_document } )
@login_required
@require_http_methods ( [ " POST " ] )
def materia_forcesave ( request , pk ) :
"""
Força o salvamento do documento via OnlyOffice Command Service .
"""
get_object_or_404 ( MateriaLegislativa , pk = pk )
body = json . loads ( request . body . decode ( ' utf-8 ' ) )
doc_key = body . get ( ' key ' )
if not doc_key :
return JsonResponse ( { " error " : " key é obrigatório " } , status = 400 )
command_url = f " { settings . ONLYOFFICE_URL } /coauthoring/CommandService.ashx "
payload = {
" c " : " forcesave " ,
" key " : doc_key
}
if settings . ONLYOFFICE_JWT_ENABLED and settings . ONLYOFFICE_JWT_SECRET :
import jwt
token = jwt . encode ( payload , settings . ONLYOFFICE_JWT_SECRET , algorithm = ' HS256 ' )
payload [ ' token ' ] = token
try :
import requests as http_requests
logger . info ( f " Forcesave para matéria { pk } : key= { doc_key } " )
resp = http_requests . post ( command_url , json = payload , timeout = 10 )
result = resp . json ( )
logger . info ( f " Forcesave response para matéria { pk } : { result } " )
return JsonResponse ( result )
except Exception as e :
logger . error ( f " Erro ao chamar forcesave para matéria { pk } : { e } " )
return JsonResponse ( { " error " : str ( e ) } , status = 500 )
@login_required
def materia_onlyoffice_editor ( request , pk ) :
"""
@ -264,6 +331,8 @@ def materia_onlyoffice_editor(request, pk):
' onlyoffice_url ' : onlyoffice_url ,
' config_url ' : reverse ( ' sapl.materia:materia_onlyoffice_config ' , kwargs = { ' pk ' : pk } ) ,
' voltar_url ' : reverse ( ' sapl.materia:materialegislativa_detail ' , kwargs = { ' pk ' : pk } ) ,
' check_url ' : reverse ( ' sapl.materia:materia_check_doc ' , kwargs = { ' pk ' : pk } ) ,
' forcesave_url ' : reverse ( ' sapl.materia:materia_forcesave ' , kwargs = { ' pk ' : pk } ) ,
}
return render ( request , ' onlyoffice/onlyoffice_editor.html ' , context )
@ -439,6 +508,20 @@ def docacessorio_onlyoffice_callback(request, pk):
# Status 2 ou 6 significa que o documento foi salvo
if status in [ 2 , 6 ] and download_url :
logger . info ( f " URL original recebida: { download_url } " )
# Substitui o host da URL pelo ONLYOFFICE_URL configurado
# O OnlyOffice retorna URLs com seu próprio hostname que pode não ser acessível
import re
from urllib . parse import urlparse
onlyoffice_parsed = urlparse ( settings . ONLYOFFICE_URL )
onlyoffice_base = f " { onlyoffice_parsed . scheme } :// { onlyoffice_parsed . netloc } "
download_url = re . sub (
r ' https?://[^/]+ ' ,
onlyoffice_base ,
download_url
)
logger . info ( f " URL substituída para: { download_url } " )
documento = get_object_or_404 ( DocumentoAcessorio , pk = pk )
@ -446,6 +529,7 @@ def docacessorio_onlyoffice_callback(request, pk):
import requests
try :
response = requests . get ( download_url , timeout = 30 )
logger . info ( f " Download response: status= { response . status_code } , size= { len ( response . content ) } " )
except Exception as e :
logger . error ( f " Erro ao fazer requisição de download: { e } " )
return JsonResponse ( { " error " : 1 } )
@ -457,7 +541,9 @@ def docacessorio_onlyoffice_callback(request, pk):
# Remove arquivo antigo se existir
if documento . arquivo :
old_file = documento . arquivo . name
documento . arquivo . delete ( save = False )
logger . info ( f " Arquivo antigo removido: { old_file } " )
try :
documento . arquivo . save (
@ -481,6 +567,56 @@ def docacessorio_onlyoffice_callback(request, pk):
return JsonResponse ( { " error " : 1 } )
@login_required
@require_http_methods ( [ " GET " ] )
def docacessorio_check_doc ( request , pk ) :
"""
Verifica se o documento acessório já possui arquivo salvo .
Usado pelo frontend para polling após forcesave do OnlyOffice .
"""
documento = get_object_or_404 ( DocumentoAcessorio , pk = pk )
has_document = bool ( documento . arquivo )
return JsonResponse ( { " has_document " : has_document } )
@login_required
@require_http_methods ( [ " POST " ] )
def docacessorio_forcesave ( request , pk ) :
"""
Força o salvamento do documento via OnlyOffice Command Service .
"""
get_object_or_404 ( DocumentoAcessorio , pk = pk )
body = json . loads ( request . body . decode ( ' utf-8 ' ) )
doc_key = body . get ( ' key ' )
if not doc_key :
return JsonResponse ( { " error " : " key é obrigatório " } , status = 400 )
command_url = f " { settings . ONLYOFFICE_URL } /coauthoring/CommandService.ashx "
payload = {
" c " : " forcesave " ,
" key " : doc_key
}
if settings . ONLYOFFICE_JWT_ENABLED and settings . ONLYOFFICE_JWT_SECRET :
import jwt
token = jwt . encode ( payload , settings . ONLYOFFICE_JWT_SECRET , algorithm = ' HS256 ' )
payload [ ' token ' ] = token
try :
import requests as http_requests
logger . info ( f " Forcesave para documento acessório { pk } : key= { doc_key } " )
resp = http_requests . post ( command_url , json = payload , timeout = 10 )
result = resp . json ( )
logger . info ( f " Forcesave response para documento acessório { pk } : { result } " )
return JsonResponse ( result )
except Exception as e :
logger . error ( f " Erro ao chamar forcesave para documento acessório { pk } : { e } " )
return JsonResponse ( { " error " : str ( e ) } , status = 500 )
@login_required
def docacessorio_onlyoffice_editor ( request , pk ) :
"""
@ -506,6 +642,8 @@ def docacessorio_onlyoffice_editor(request, pk):
' onlyoffice_url ' : onlyoffice_url ,
' config_url ' : reverse ( ' sapl.materia:docacessorio_onlyoffice_config ' , kwargs = { ' pk ' : pk } ) ,
' voltar_url ' : reverse ( ' sapl.materia:documentoacessorio_detail ' , kwargs = { ' pk ' : documento . materia . pk , ' zpk ' : pk } ) ,
' check_url ' : reverse ( ' sapl.materia:docacessorio_check_doc ' , kwargs = { ' pk ' : pk } ) ,
' forcesave_url ' : reverse ( ' sapl.materia:docacessorio_forcesave ' , kwargs = { ' pk ' : pk } ) ,
}
return render ( request , ' onlyoffice/onlyoffice_editor.html ' , context )