diff --git a/client/src/login.js b/client/src/login.js index 2f21270..ae79763 100644 --- a/client/src/login.js +++ b/client/src/login.js @@ -25,7 +25,13 @@ auth.onAuthStateChanged((user) => { return; } - user.getIdToken(params.get('token_error') === '1').then(sendTokenToServer); + user.getIdToken(params.get('token_error') === '1') + .then( + sendTokenToServer, + () => { // on error we sign out the user and let him login again + signOut(auth); + }, + ); } }); diff --git a/client/src/reset-password.js b/client/src/reset-password.js index 4d0cc57..4f1a502 100644 --- a/client/src/reset-password.js +++ b/client/src/reset-password.js @@ -1,9 +1,7 @@ import { auth } from './auth' -import { sendPasswordResetEmail } from 'firebase/auth'; +import { sendPasswordResetEmail, signOut } from 'firebase/auth'; const form = document.getElementById('reset-password-form'); -const url = new URL(window.nextUrl); -const params = new URLSearchParams(url.search); const sendButton = document.getElementById('send-email'); const emailSentCheckbox = document.getElementById('email_sent_checkbox'); @@ -16,6 +14,8 @@ form.onsubmit = (event) => { const data = new FormData(form); const email = data.get("email"); + const url = new URL(window.nextUrl); + const params = new URLSearchParams(); params.append("email", email); url.search = params; diff --git a/server/cp/__init__.py b/server/cp/__init__.py index 17a6a97..3b85720 100644 --- a/server/cp/__init__.py +++ b/server/cp/__init__.py @@ -9,5 +9,9 @@ # AUTHORS and LICENSE files distributed with this source code, or # at https://www.sourcefabric.org/superdesk/license +import logging + HEADLINE2 = "headline_extended" CORRECTION = "correction" + +logging.basicConfig(level=logging.INFO) diff --git a/server/cp/auth.py b/server/cp/auth.py index 6e1d9ef..490fbed 100644 --- a/server/cp/auth.py +++ b/server/cp/auth.py @@ -28,7 +28,7 @@ def token(): try: claims = google.oauth2.id_token.verify_firebase_token( token, - audience="cp-identity", + audience="cp-identity-dev" if "cp-dev" in flask.request.base_url else "cp-identity", request=firebase_request_adapter, ) except ValueError as err: @@ -50,7 +50,11 @@ def logout(): @blueprint.route("/cp_reset_password_done") def reset_password_confirmation(): - return flask.render_template("cp_reset_password_confirmation.html") + flask.flash( + gettext("A reset password token has been sent to your email address."), + "success", + ) + return flask.redirect(flask.url_for("auth.login")) @blueprint.route("/cp_reset_password", methods=["GET", "POST"]) diff --git a/server/cp/cem.py b/server/cp/cem.py index 3d2b1c1..b0e63cf 100644 --- a/server/cp/cem.py +++ b/server/cp/cem.py @@ -1,11 +1,15 @@ +import logging import requests + +from typing import Literal from flask import current_app as app +logger = logging.getLogger(__name__) session = requests.Session() -def send_notification(_type, user): +def send_notification(_type, user, id_key: Literal["_id", "email"] = "_id"): url = app.config.get("CEM_URL", "") apikey = app.config.get("CEM_APIKEY", "") if not url or not apikey: @@ -13,14 +17,20 @@ def send_notification(_type, user): headers = {"x-api-key": apikey} payload = { "type": _type, - "object_id": str(user["_id"]), + "object_id": str(user[id_key]), "platform": app.config.get("CEM_PLATFORM"), } if user.get("company"): payload["company"] = str(user["company"]) - session.patch( - url, - timeout=5, - json=payload, - headers=headers, - ) + try: + session.patch( + url, + json=payload, + headers=headers, + timeout=int(app.config.get("CEM_TIMEOUT", 10)), + verify=bool(app.config.get("CEM_VERIFY_TLS", True)), + ) + except requests.exceptions.RequestException as err: + logger.error(err) + return + logger.info("Notification sent to CEM") diff --git a/server/cp/commands/__init__.py b/server/cp/commands/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/server/cp/commands/fix_language.py b/server/cp/commands/fix_language.py new file mode 100644 index 0000000..25c01be --- /dev/null +++ b/server/cp/commands/fix_language.py @@ -0,0 +1,23 @@ + +import time + +from superdesk import get_resource_service +from newsroom.commands.manager import manager + + +@manager.command +def fix_language(resource="items", limit=50, sleep_secs=2): + service = get_resource_service(resource) + + source = {"query": {"terms": {"language": ["en-CA", "en-US", "fr-CA"]}}, "size": 100} + + for i in range(int(limit)): + items = service.search(source) + if not items.count(): + break + for item in items: + updates = {"language": item["language"].split("-")[0]} + service.system_update(item["_id"], updates, item) + print(".", end="", flush=True) + time.sleep(int(sleep_secs)) + print(".") diff --git a/server/cp/signals.py b/server/cp/signals.py index 7cf2196..e59a79f 100644 --- a/server/cp/signals.py +++ b/server/cp/signals.py @@ -37,7 +37,7 @@ def copy_correction_to_body_html(item): def on_user_created(sender, user, **kwargs): if user_auth_is_gip(user): - send_notification("new user", user) + send_notification("new", user, id_key="email") def on_user_updated(sender, user, updates=None, **kwargs): diff --git a/server/data/email_templates.json b/server/data/email_templates.json index 4a9ecb6..968ddf6 100644 --- a/server/data/email_templates.json +++ b/server/data/email_templates.json @@ -72,20 +72,20 @@ } }, { "_id": "agenda_updated_email", - "init_version": 4, + "init_version": 5, "subject": { - "default": "{{ agenda.name or agenda.headline or agenda.slugline | safe }} -{{ ' Coverage' if coverage_modified else '' }} updated", + "default": "{{ (agenda.name or agenda.headline or agenda.slugline) | safe }} -{{ ' Coverage' if coverage_modified else '' }} updated", "translations": { - "fr_ca": "{{ agenda.name or agenda.headline or agenda.slugline | safe }} - {% if coverage_modified %}Couverture mise à jour{% else %}Événement mis à jour{% endif %}" + "fr_ca": "{{ (agenda.name or agenda.headline or agenda.slugline) | safe }} - {% if coverage_modified %}Couverture mise à jour{% else %}Événement mis à jour{% endif %}" } } }, { "_id": "coverage_request_email", - "init_version": 2, + "init_version": 3, "subject": { - "default": "Coverage inquiry: {{ item.name or item.slugline | safe }}", + "default": "Coverage inquiry: {{ (item.name or item.slugline) | safe }}", "translations": { - "fr_ca": "Requête de couverture: {{ item.name or item.slugline | safe }}" + "fr_ca": "Requête de couverture: {{ (item.name or item.slugline) | safe }}" } } }, { diff --git a/server/manage.py b/server/manage.py index a6ccc73..ad34d7b 100644 --- a/server/manage.py +++ b/server/manage.py @@ -1,6 +1,8 @@ from newsroom.commands import * # noqa from newsroom.commands.manager import manager +from cp.commands.fix_language import fix_language # noqa + if __name__ == "__main__": manager.run() diff --git a/server/requirements.txt b/server/requirements.txt index f3b988b..3094a40 100644 --- a/server/requirements.txt +++ b/server/requirements.txt @@ -130,7 +130,6 @@ flask==1.1.2 # flask-script # flask-webpack # flask-wtf - # newsroom-core # raven # sentry-sdk # superdesk-core diff --git a/server/settings.py b/server/settings.py index 734cbaf..b734b25 100644 --- a/server/settings.py +++ b/server/settings.py @@ -1,6 +1,7 @@ import os import pathlib from flask_babel import lazy_gettext +from superdesk.default_settings import strtobool from newsroom.web.default_settings import ( env, CLIENT_CONFIG, @@ -251,7 +252,7 @@ # saml auth SAML_LABEL = env("SAML_LABEL", "SSO") -SAML_COMPANY = env("SAML_COMPANY", "CP") +SAML_COMPANY = env("SAML_COMPANY", "The Canadian Press") SAML_BASE_PATH = pathlib.Path(env("SAML_PATH", SERVER_PATH.joinpath("saml"))) SAML_PATH_MAP = { "localhost": "localhost", @@ -272,8 +273,10 @@ CEM_URL = os.environ.get("CEM_URL", "") CEM_APIKEY = os.environ.get("CEM_APIKEY", "") CEM_PLATFORM = os.environ.get("CEM_PLATFORM", "NewsPro") - -DEFAULT_ALLOW_COMPANIES_TO_MANAGE_PRODUCTS = True +CEM_VERIFY_TLS = strtobool(os.environ.get("CEM_VERIFY_TLS", "off")) +CEM_TIMEOUT = int(os.environ.get("CEM_TIMEOUT") or 10) AGENDA_SECTION = lazy_gettext("Calendar") SAVED_SECTION = lazy_gettext("Bookmarks") + +DEFAULT_ALLOW_COMPANIES_TO_MANAGE_PRODUCTS = True diff --git a/server/tests/test_signals.py b/server/tests/test_signals.py index 0ed9d10..6d3cc3c 100644 --- a/server/tests/test_signals.py +++ b/server/tests/test_signals.py @@ -65,9 +65,8 @@ def test_cem_notification_on_user_changes(app): ), matchers.json_params_matcher( { - "object_id": str(user["_id"]), - "company": str(company_id), - "type": "new user", + "object_id": str(user["email"]), + "type": "new", "platform": "Test", } ), diff --git a/server/theme/copy_agenda_item.fr_ca.txt b/server/theme/copy_agenda_item.fr_ca.txt new file mode 100644 index 0000000..350f3b7 --- /dev/null +++ b/server/theme/copy_agenda_item.fr_ca.txt @@ -0,0 +1,52 @@ +{%- if item.name %}{{ item.name }} +{% endif %} +{%- if item.dates %}Dates: {{ item.dates.start | datetime_short }} - {{ item.dates.end | datetime_short }} +{% endif %} +{%- if location %}Endroit: {{ location }} +{% endif %} +{%- if item.ednote %}Note de la rédaction: {{ item.ednote }} +{% endif %} +{%- if item.definition_short %}Description: {{ item.definition_short | plain_text }} +{% endif -%} +{%- if item.invitation_details %}Détails de l'invitation: {{ item.invitation_details | plain_text }} +{% endif -%} +{%- if item.registration_details %}Détails de l'inscription: {{ item.registration_details | plain_text }} +{% endif -%} +{%- if contacts | length is gt 0 %} + +Contacts +{%- for contact in contacts %} +Nom: {{ contact.name }} +{% if contact.organisation %}Organisation: {{ contact.organisation }} +{% endif %} +{%- if contact.email %}Courriel: {{ contact.email }} +{% endif %} +{%- if contact.phone %}Téléphone: {{ contact.phone }} +{% endif %} +{%- if contact.mobile %}Mobile: {{ contact.mobile }} +{% endif %} +{%- endfor %} +{%- endif %} +{%- if calendars %} + +Calendriers: {{ calendars }} +{% endif -%} +{%- if item.planning_items | length is gt 0 -%} +{%- for plan in item.planning_items %} + +Élément de couverture +{%- if plan.description_text %} +Description: {{ plan.description_text | plain_text }} +{% endif -%} +{%- for coverage in plan.coverages %} +{% if coverage.planning.g2_content_type %}Type de couverture: {{ coverage.planning.g2_content_type }} +{% endif %} +{%- if coverage.planning.scheduled %}Programmé: {{ coverage.planning.scheduled | datetime_short }} +{% endif %} +{%- if coverage.workflow_status %}Statut: {{ coverage.workflow_status }} +{% endif -%} +{%- if coverage.planning.description_text %}Description: {{ coverage.planning.description_text }} +{% endif -%} +{%- endfor -%} +{%- endfor -%} +{%- endif -%} \ No newline at end of file diff --git a/server/theme/copy_wire_item.fr_ca.txt b/server/theme/copy_wire_item.fr_ca.txt new file mode 100644 index 0000000..a6d56cb --- /dev/null +++ b/server/theme/copy_wire_item.fr_ca.txt @@ -0,0 +1,8 @@ +{{ item.versioncreated | datetime_short }} +{% if item.slugline %}{{ get_slugline(item, True) }}{% endif %} +{% if item.headline %}{{ item.headline }}{% endif %} +{% if item.byline %}Par: {{ item.byline }}{% endif %} +{% if item.located %}Endroit: {{ item.located }}{% endif %} +{% if item.source %}Source: {{ item.source }}{% endif %} + +{% if item.body_text %}{{ item.body_text }}{% elif item.body_html %}{{ item.body_html | plain_text }}{% endif %} \ No newline at end of file diff --git a/server/theme/request_token.html b/server/theme/request_token.html new file mode 100644 index 0000000..0b2b4ce --- /dev/null +++ b/server/theme/request_token.html @@ -0,0 +1,51 @@ +{% extends "layout_wire.html" %} + +{% block contentMain %} + +