diff --git a/opac/tests/test_main_errors.py b/opac/tests/test_main_errors.py
index 0844b8273..1154cb324 100644
--- a/opac/tests/test_main_errors.py
+++ b/opac/tests/test_main_errors.py
@@ -95,6 +95,8 @@ def test_forbidden_json(self):
self.assertEqual(expected_msg, json_msg)
def test_page_not_found(self):
+ # When PREVIOUS_WEBSITE_URI is not set, should render 404 template
+ current_app.config["PREVIOUS_WEBSITE_URI"] = ""
response = self.client.get("/page_not_found")
self.assert_404(response)
self.assertEqual("text/html; charset=utf-8", response.content_type)
@@ -104,6 +106,8 @@ def test_page_not_found(self):
self.assertEqual(expected_msg, context_msg)
def test_page_not_found_json(self):
+ # JSON requests should not redirect
+ current_app.config["PREVIOUS_WEBSITE_URI"] = "https://old.scielo.br"
response = self.client.get(
"/page_not_found", headers={"Accept": "application/json"}
)
@@ -114,6 +118,24 @@ def test_page_not_found_json(self):
json_msg = response.json["error"]
expected_msg = "
%s
" % ERROR_MSG
self.assertEqual(expected_msg, json_msg)
+
+ def test_page_not_found_with_redirect_to_classic_site(self):
+ # When PREVIOUS_WEBSITE_URI is set, should redirect to classic site
+ classic_site_url = "https://old.scielo.br"
+ current_app.config["PREVIOUS_WEBSITE_URI"] = classic_site_url
+ response = self.client.get("/page_not_found", follow_redirects=False)
+ self.assertEqual(302, response.status_code)
+ expected_redirect_url = classic_site_url + "/page_not_found"
+ self.assertEqual(expected_redirect_url, response.location)
+
+ def test_page_not_found_redirect_preserves_query_string(self):
+ # When redirecting, should preserve query string
+ classic_site_url = "https://old.scielo.br"
+ current_app.config["PREVIOUS_WEBSITE_URI"] = classic_site_url
+ response = self.client.get("/page_not_found?param=value&test=123", follow_redirects=False)
+ self.assertEqual(302, response.status_code)
+ expected_redirect_url = classic_site_url + "/page_not_found?param=value&test=123"
+ self.assertEqual(expected_redirect_url, response.location)
def test_internal_server_error(self):
current_app.config["DEBUG"] = False
diff --git a/opac/webapp/config/default.py b/opac/webapp/config/default.py
index def61ddf4..0e9006e97 100644
--- a/opac/webapp/config/default.py
+++ b/opac/webapp/config/default.py
@@ -197,6 +197,11 @@
- OPAC_SITE_LICENSE_NAME: Nome da licença (default: "Creative Common - by 4.0")
- OPAC_SITE_LICENSE_URL: URL da licença (default: https://creativecommons.org/licenses/by-nc/4.0/)
- OPAC_SITE_LICENSE_IMG_URL: Imagem da licença (default: https://licensebuttons.net/l/by/4.0/88x31.png)
+
+ - Migration & Legacy Site
+ - PREVIOUS_WEBSITE_URI: URL do site anterior/clássico (ex: 'https://old.scielo.br').
+ Quando configurado, se um recurso não for encontrado (404),
+ o usuário será redirecionado para a mesma URL no site clássico.
"""
PROJECT_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
@@ -640,6 +645,7 @@
APM_VERIFY_SERVER_CERT = os.environ.get("OPAC_APM_APM_VERIFY_SERVER_CERT", "True") == "True"
# Caso queira apresentar na home do website que o atual tem versão anterior
+# e redirecionar automaticamente para o site clássico quando um recurso não for encontrado (404)
PREVIOUS_WEBSITE_URI = os.environ.get("PREVIOUS_WEBSITE_URI", "")
# Caso queira apresentar na home do website qualquer mensagem de texto
diff --git a/opac/webapp/main/errors.py b/opac/webapp/main/errors.py
index fdae533a0..ebce9c300 100644
--- a/opac/webapp/main/errors.py
+++ b/opac/webapp/main/errors.py
@@ -1,7 +1,9 @@
# coding: utf-8
-from flask import jsonify, render_template, request
-
+from flask import current_app, jsonify, redirect, render_template, request
+import re
from . import main
+from .helper import build_classic_website_uri
+from webapp import controllers
@main.app_errorhandler(400)
@@ -39,10 +41,93 @@ def page_not_found(e):
response = jsonify({"error": e})
response.status_code = 404
return response
+
+ # Try to redirect to the classic site if configured
+ classic_site_url = current_app.config.get("PREVIOUS_WEBSITE_URI", "")
+ if classic_site_url:
+ # Try to build a specific classic URL based on the resource type
+ classic_url = _build_classic_url_for_resource(request.path)
+
+ # If we couldn't build a specific URL, fallback to the simple redirect
+ if not classic_url:
+ classic_url = classic_site_url.rstrip("/") + request.full_path.rstrip("?")
+
+ return redirect(classic_url, code=302)
+
+ # If no classic site is configured, show the 404 page
context = {"message": e}
return render_template("errors/404.html", **context), 404
+def _build_classic_url_for_resource(path):
+ """
+ Tenta construir uma URL específica para o site clássico com base no tipo de recurso.
+
+ Args:
+ path: o caminho da requisição
+
+ Returns:
+ String com a URL completa para o site clássico ou None se não puder ser construída
+ """
+ # Pattern para journal: /j// ou /j/
+ journal_pattern = r'^/j/([^/]+)/?$'
+ match = re.match(journal_pattern, path)
+ if match:
+ url_seg = match.group(1)
+ journal = controllers.get_journal_by_url_seg(url_seg)
+ if journal:
+ return build_classic_website_uri('journal', journal)
+
+ # Pattern para issue: /j//i// ou /j//i/
+ issue_pattern = r'^/j/([^/]+)/i/([^/]+)/?$'
+ match = re.match(issue_pattern, path)
+ if match:
+ url_seg = match.group(1)
+ url_seg_issue = match.group(2)
+ issue = controllers.get_issue_by_url_seg(url_seg, url_seg_issue)
+ if issue:
+ return build_classic_website_uri('issue', issue)
+
+ # Pattern para article: /j//a// ou /j//a///
+ article_pattern = r'^/j/([^/]+)/a/([^/]+)(?:/[^/]+)?/?$'
+ match = re.match(article_pattern, path)
+ if match:
+ url_seg = match.group(1)
+ article_pid_v3 = match.group(2)
+ # Tenta obter o artigo pelo aid (v3 PID)
+ try:
+ from opac_schema.v1.models import Article
+ article = Article.objects(aid=article_pid_v3).first()
+ if article:
+ return build_classic_website_uri('article', article)
+ except (AttributeError, ImportError) as e:
+ current_app.logger.debug(f"Error loading article for classic URL: {e}")
+
+ # Pattern para PDF: /pdf///.pdf
+ pdf_pattern = r'^/pdf/([^/]+)/([^/]+)/([^/]+)\.pdf$'
+ match = re.match(pdf_pattern, path)
+ if match:
+ journal_acron = match.group(1)
+ issue_info = match.group(2)
+ pdf_filename = match.group(3) + ".pdf"
+ # Tenta obter o artigo pelo PDF filename
+ try:
+ article = controllers.get_article_by_pdf_filename(
+ journal_acron, issue_info, pdf_filename
+ )
+ # Se não encontrou, tenta material suplementar
+ if not article:
+ article = controllers.get_article_by_suppl_material_filename(
+ journal_acron, issue_info, pdf_filename
+ )
+ if article:
+ return build_classic_website_uri('pdf', article)
+ except (AttributeError, ImportError) as e:
+ current_app.logger.debug(f"Error loading article PDF for classic URL: {e}")
+
+ return None
+
+
@main.app_errorhandler(500)
def internal_server_error(e):
if (
diff --git a/opac/webapp/main/helper.py b/opac/webapp/main/helper.py
index 4d97886cb..d4f60596e 100644
--- a/opac/webapp/main/helper.py
+++ b/opac/webapp/main/helper.py
@@ -1,8 +1,10 @@
import datetime
from functools import wraps
+from urllib.parse import urlencode
import jwt
-from flask import jsonify, request
+from flask import jsonify, request, session
+from flask_babelex import get_locale
from webapp import controllers
from werkzeug.security import check_password_hash
from flask import current_app
@@ -68,3 +70,64 @@ def auth():
),
401,
)
+
+
+def build_classic_website_uri(resource_type, resource=None, **kwargs):
+ """
+ Constrói a URI completa para o site clássico com base no tipo de recurso.
+
+ Args:
+ resource_type: tipo do recurso ('journal', 'issue', 'article', 'pdf')
+ resource: objeto do recurso (Journal, Issue, ou Article)
+ **kwargs: parâmetros adicionais (ex: lang)
+
+ Returns:
+ String com a URI completa para o site clássico ou None se não puder ser construída
+ """
+ classic_site_url = current_app.config.get("PREVIOUS_WEBSITE_URI", "")
+ if not classic_site_url:
+ return None
+
+ # Obter idioma da sessão ou usar o padrão
+ lang = kwargs.get('lang')
+ if not lang:
+ lang = session.get("lang", str(get_locale()))
+ if lang and len(lang) > 2:
+ lang = lang[:2] # Converter pt_BR para pt
+
+ base_url = classic_site_url.rstrip("/")
+
+ try:
+ if resource_type == 'journal' and resource:
+ # Usa print_issn ou electronic_issn
+ issn = getattr(resource, 'print_issn', None) or getattr(resource, 'electronic_issn', None)
+ if issn:
+ params = {'script': 'sci_serial', 'pid': issn, 'lng': lang, 'nrm': 'iso'}
+ return f"{base_url}/scielo.php?{urlencode(params)}"
+
+ elif resource_type == 'issue' and resource:
+ # Usa o PID do issue
+ pid = getattr(resource, 'pid', None)
+ if pid:
+ params = {'script': 'sci_issuetoc', 'pid': pid, 'lng': lang, 'nrm': 'iso'}
+ return f"{base_url}/scielo.php?{urlencode(params)}"
+
+ elif resource_type == 'article' and resource:
+ # Usa o PID v2 do artigo
+ pid = getattr(resource, 'pid', None)
+ if pid:
+ params = {'script': 'sci_arttext', 'pid': pid, 'lng': lang, 'nrm': 'iso'}
+ return f"{base_url}/scielo.php?{urlencode(params)}"
+
+ elif resource_type == 'pdf' and resource:
+ # Para PDF, usa o PID v2 do artigo com script sci_pdf
+ pid = getattr(resource, 'pid', None)
+ if pid:
+ params = {'script': 'sci_pdf', 'pid': pid, 'lng': lang, 'nrm': 'iso'}
+ return f"{base_url}/scielo.php?{urlencode(params)}"
+
+ except (AttributeError, TypeError) as e:
+ # Se houver erro ao acessar atributos do recurso, retorna None
+ current_app.logger.debug(f"Error building classic URL for {resource_type}: {e}")
+
+ return None
diff --git a/src/opac-schema b/src/opac-schema
new file mode 160000
index 000000000..2bb30ecb4
--- /dev/null
+++ b/src/opac-schema
@@ -0,0 +1 @@
+Subproject commit 2bb30ecb425eaa282be0492f9eaacd64fd6b3909
diff --git a/src/packtools b/src/packtools
new file mode 160000
index 000000000..e3d3641af
--- /dev/null
+++ b/src/packtools
@@ -0,0 +1 @@
+Subproject commit e3d3641af12e6cc47435969a1b94e502e6ebfc2a
diff --git a/src/scieloh5m5 b/src/scieloh5m5
new file mode 160000
index 000000000..ad942d31e
--- /dev/null
+++ b/src/scieloh5m5
@@ -0,0 +1 @@
+Subproject commit ad942d31e19148cb6a84900b90b44e76c848401b