From 0a844aa1747193036961990e693804979f439019 Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Mon, 4 Nov 2019 15:32:55 +0100 Subject: [PATCH] Open links from event URL and in event description in external window And add rel='noopener noreferrer' on them Closes #282 and #283 Signed-off-by: Thomas Citharel --- CHANGELOG.md | 9 ++-- config/config.exs | 4 +- js/src/i18n/en_US.json | 3 +- js/src/i18n/fr_FR.json | 41 ++++++++++--------- js/src/views/Event/Event.vue | 7 +++- .../service/formatter/formatter_test.exs | 25 ++++++----- 6 files changed, 49 insertions(+), 40 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b1316356..39a7c56a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,13 +31,14 @@ In order to move participant stats to the event table for existing events, you n - Upgraded frontend and backend dependencies ### Changed -- Improve Docker setup and docs -- Handle error message difference between user not found and user not confirmed -- Upgrade vue-cli to v4, change the way server params injection is made +- Move participant stats to event table **(read special instructions above)** - Limit length (20 characters) and number (10) of tags allowed - Added some backend changes and validation for field length +- Handle error message difference between user not found and user not confirmed +- Make external links (from URL field and description) open in a new tab with `noopener` +- Improve Docker setup and docs +- Upgrade vue-cli to v4, change the way server params injection is made - Improve some production ipv6 configuration -- Move participant stats to event table **(read special instructions above)** ### Fixed - Fix event URL validation and check if hostname is correct before showing it diff --git a/config/config.exs b/config/config.exs index f117bea4..c4ad01e5 100644 --- a/config/config.exs +++ b/config/config.exs @@ -106,9 +106,7 @@ config :auto_linker, # TODO: Set to :no_scheme when it works properly validate_tld: true, class: false, - strip_prefix: false, - new_window: false, - rel: false + strip_prefix: false ] config :phoenix, :format_encoders, json: Jason, "activity-json": Jason diff --git a/js/src/i18n/en_US.json b/js/src/i18n/en_US.json index e1fa94eb..a9e7dc33 100644 --- a/js/src/i18n/en_US.json +++ b/js/src/i18n/en_US.json @@ -278,6 +278,7 @@ "Users": "Users", "View event page": "View event page", "View everything": "View everything", + "View page on {hostname} (in a new window)": "View page on {hostname} (in a new window)", "Visible everywhere on the web (public)": "Visible everywhere on the web (public)", "Waiting for organization team approval.": "Waiting for organization team approval.", "Waiting list": "Waiting list", @@ -325,4 +326,4 @@ "{count} requests waiting": "{count} requests waiting", "{license} guarantees {respect} of the people who will use it. Since {source}, anyone can audit it, which guarantees its transparency.": "{license} guarantees {respect} of the people who will use it. Since {source}, anyone can audit it, which guarantees its transparency.", "© The Mobilizon Contributors {date} - Made with Elixir, Phoenix, VueJS & with some love and some weeks": "© The Mobilizon Contributors {date} - Made with Elixir, Phoenix, VueJS & with some love and some weeks" -} \ No newline at end of file +} diff --git a/js/src/i18n/fr_FR.json b/js/src/i18n/fr_FR.json index 7ae20c60..d9666fa5 100644 --- a/js/src/i18n/fr_FR.json +++ b/js/src/i18n/fr_FR.json @@ -3,14 +3,14 @@ "A user-friendly, emancipatory and ethical tool for gathering, organising, and mobilising.": "Un outil convivial, émancipateur et éthique pour se rassembler, s'organiser et se mobiliser.", "A validation email was sent to {email}": "Un email de validation a été envoyé à {email}", "Abandon edition": "Abandonner l'édition", - "About": "À propos", "About Mobilizon": "À propos de Mobilizon", "About this event": "À propos de cet événement", "About this instance": "À propos de cette instance", - "Add": "Ajouter", + "About": "À propos", "Add an address": "Ajouter une adresse", "Add some tags": "Ajouter des tags", "Add to my calendar": "Ajouter à mon agenda", + "Add": "Ajouter", "Additional comments": "Commentaires additionnels", "Administration": "Administration", "All data will be deleted every 48 hours, so please don't use this for anything real.": "Toutes les données seront effacées toutes les 48 heures, donc n'utilisez pas ce site à des fins autres que de démonstration.", @@ -25,28 +25,27 @@ "Avatar": "Avatar", "Before you can login, you need to click on the link inside it to validate your account": "Avant que vous puissiez vous enregistrer, vous devez cliquer sur le lien à l'intérieur pour valider votre compte", "By {name}": "Par {name}", - "Cancel": "Annuler", "Cancel creation": "Annuler la création", "Cancel edition": "Annuler l'édition", "Cancel my participation request…": "Annuler ma demande de participation…", "Cancel my participation…": "Annuler ma participation…", + "Cancel": "Annuler", "Cancelled: Won't happen": "Annulé: N'aura pas lieu", "Category": "Catégorie", - "Change": "Modifier", "Change my identity…": "Changer mon identité…", "Change my password": "Modifier mon mot de passe", "Change password": "Modifier mot de passe", + "Change": "Modifier", "Clear": "Effacer", "Click to select": "Cliquez pour sélectionner", "Click to upload": "Cliquez pour uploader", "Close comments for all (except for admins)": "Fermer les commentaires à tout le monde (excepté les administrateurs)", - "Comments": "Commentaires", "Comments on the event page": "Commentaires sur la page de l'événement", + "Comments": "Commentaires", "Confirm my particpation": "Confirmer ma participation", "Confirmed: Will happen": "Confirmé : aura lieu", "Continue editing": "Continuer l'édition", "Country": "Pays", - "Create": "Créer", "Create a new event": "Créer un nouvel événement", "Create a new group": "Créer un nouveau groupe", "Create a new identity": "Créer une nouvelle identité", @@ -57,16 +56,17 @@ "Create my profile": "Créer mon profil", "Create token": "Créer un jeton", "Create, edit or delete events": "Créer, modifier ou supprimer des événements", + "Create": "Créer", "Creator": "Créateur", "Current identity has been changed to {identityName} in order to manage this event.": "L'identité actuelle a été changée à {identityName} pour pouvoir gérer cet événement.", "Date and time settings": "Paramètres de date et d'heure", "Date parameters": "Paramètres de date", - "Delete": "Supprimer", "Delete event": "Supprimer un événement", "Delete this identity": "Supprimer cette identité", "Delete your identity": "Supprimer votre identité", "Delete {eventTitle}": "Supprimer {eventTitle}", "Delete {preferredUsername}": "Supprimer {preferredUsername}", + "Delete": "Supprimer", "Description": "Description", "Didn't receive the instructions ?": "Vous n'avez pas reçu les instructions ?", "Display name": "Nom affiché", @@ -84,7 +84,6 @@ "Error while communicating with the server.": "Erreur de communication avec le serveur.", "Error while saving report.": "Erreur lors de l'enregistrement du signalement.", "Error while validating account": "Erreur lors de la validation du compte", - "Event": "Événement", "Event already passed": "Événement déjà passé", "Event cancelled": "Événement annulé", "Event creation": "Création d'événement", @@ -95,6 +94,7 @@ "Event to be confirmed": "Événement à confirmer", "Event {eventTitle} deleted": "Événement {eventTitle} supprimé", "Event {eventTitle} reported": "Événement {eventTitle} signalé", + "Event": "Événement", "Events": "Événements", "Exclude": "Exclure", "Explore": "Explorer", @@ -105,8 +105,8 @@ "For instance: London, Taekwondo, Architecture…": "Par exemple: Lyon, Taekwondo, Architecture…", "Forgot your password ?": "Mot de passe oublié ?", "From a birthday party with friends and family to a march for climate change, right now, our gatherings are trapped inside the tech giants’ platforms. How can we organize, how can we click “Attend,” without providing private data to Facebook or locking ourselves up inside MeetUp?": "De l’anniversaire entre ami·e·s à une marche pour le climat, aujourd’hui, les bonnes raisons de se rassembler sont captées par les géants du web. Comment s’organiser, comment cliquer sur « je participe » sans livrer des données intimes à Facebook ou s’enfermer dans MeetUp ?", - "From the {startDate} at {startTime} to the {endDate}": "Du {startDate} à {startTime} jusqu'au {endDate}", "From the {startDate} at {startTime} to the {endDate} at {endTime}": "Du {startDate} à {startTime} au {endDate} à {endTime}", + "From the {startDate} at {startTime} to the {endDate}": "Du {startDate} à {startTime} jusqu'au {endDate}", "From the {startDate} to the {endDate}": "Du {startDate} au {endDate}", "Gather ⋅ Organize ⋅ Mobilize": "Rassembler ⋅ Organiser ⋅ Mobiliser", "General information": "Informations générales", @@ -131,8 +131,8 @@ "Join {instance}, a Mobilizon instance": "Rejoignez {instance}, une instance Mobilizon", "Last published event": "Dernier événement publié", "Last week": "La semaine dernière", - "Learn more": "En apprendre plus", "Learn more about Mobilizon": "En apprendre plus à propos de Mobilizon", + "Learn more": "En apprendre plus", "Leave event": "Annuler ma participation à l'événement", "Leaving event \"{title}\"": "Annuler ma participation à l'événement", "Let's create a new common": "Créons un nouveau Common", @@ -142,8 +142,8 @@ "Locality": "Commune", "Log in": "Se connecter", "Log out": "Se déconnecter", - "Login": "Se connecter", "Login on Mobilizon!": "Se connecter sur Mobilizon !", + "Login": "Se connecter", "Manage participations": "Gérer les participations", "Members": "Membres", "Mobilizon is a free/libre software that will allow communities to create their own spaces to publish events in order to better emancipate themselves from tech giants.": "Mobilizon est un logiciel libre qui permettra à des communautés de créer leurs propres espaces de publication d’événements, afin de mieux s’émanciper des géants du web.", @@ -165,15 +165,15 @@ "Number of places": "Nombre de places", "OK": "OK", "Old password": "Ancien mot de passe", - "On {date}": "Le {date}", "On {date} ending at {endTime}": "Le {date}, se terminant à {endTime}", "On {date} from {startTime} to {endTime}": "Le {date} de {startTime} à {endTime}", "On {date} starting at {startTime}": "Le {date} à partir de {startTime}", + "On {date}": "Le {date}", "One person is going": "Personne n'y va | Une personne y va | {approved} personnes y vont", "Only accessible through link and search (private)": "Uniquement accessibles par lien et la recherche (privé)", "Opened reports": "Signalements ouverts", - "Organized": "Organisés", "Organized by {name}": "Organisé par {name}", + "Organized": "Organisés", "Organizer": "Organisateur", "Otherwise this identity will just be removed from the group administrators.": "Sinon cette identité sera juste supprimée des administrateurs du groupe.", "Page limited to my group (asks for auth)": "Accès limité à mon groupe (demande authentification)", @@ -184,10 +184,10 @@ "Participate": "Participer", "Participation approval": "Validation des participations", "Participation requested!": "Participation demandée !", - "Password": "Mot de passe", "Password (confirmation)": "Mot de passe (confirmation)", "Password change": "Changement de mot de passe", "Password reset": "Réinitialisation du mot de passe", + "Password": "Mot de passe", "Past events": "Événements passés", "Pick an identity": "Choisissez une identité", "Please check your spam folder if you didn't receive the email.": "Merci de vérifier votre dossier des indésirables si vous n'avez pas reçu l'email.", @@ -209,23 +209,23 @@ "RSS/Atom Feed": "Flux RSS/Atom", "Read Framasoft’s statement of intent on the Framablog": "Lire la note d’intention de Framasoft sur le Framablog", "Region": "Région", - "Register": "S'inscrire", "Register an account on Mobilizon!": "S'inscrire sur Mobilizon !", "Register for an event by choosing one of your identities": "S'inscrire à un événement en choisissant une de vos identités", + "Register": "S'inscrire", "Registration is currently closed.": "Les inscriptions sont actuellement fermées.", "Reject": "Rejetter", - "Rejected": "Rejetés", "Rejected participations": "Participations rejetées", - "Report": "Signaler", + "Rejected": "Rejetés", "Report this event": "Signaler cet événement", + "Report": "Signaler", "Requests": "Requêtes", "Resend confirmation email": "Envoyer à nouveau l'email de confirmation", "Reset my password": "Réinitialiser mon mot de passe", - "Save": "Enregistrer", "Save draft": "Enregistrer le brouillon", - "Search": "Rechercher", + "Save": "Enregistrer", "Search events, groups, etc.": "Rechercher des événements, des groupes, etc.", "Search results: \"{search}\"": "Résultats de recherche: « {search} »", + "Search": "Rechercher", "Searching…": "Recherche en cours…", "Send me an email to reset my password": "Envoyez-moi un email pour réinitialiser mon mot de passe", "Send me the confirmation email once again": "Envoyez-moi l'email de confirmation encore une fois", @@ -246,8 +246,8 @@ "The draft event has been updated": "L'événement brouillon a été mis à jour", "The event has been created as a draft": "L'événement a été créé en tant que brouillon", "The event has been published": "L'événement a été publié", - "The event has been updated": "L'événement a été mis à jour", "The event has been updated and published": "L'événement a été mis à jour et publié", + "The event has been updated": "L'événement a été mis à jour", "The event organizer didn't add any description.": "L'organisateur de l'événement n'a pas ajouté de description.", "The event title will be ellipsed.": "Le titre de l'événement sera ellipsé.", "The page you're looking for doesn't exist.": "La page que vous recherchez n'existe pas.", @@ -278,6 +278,7 @@ "Users": "Utilisateurs", "View event page": "Voir la page de l'événement", "View everything": "Voir tout", + "View page on {hostname} (in a new window)": "Voir la page sur {hostname} (dans une nouvelle fenêtre)", "Visible everywhere on the web (public)": "Visible partout sur le web (public)", "Waiting for organization team approval.": "En attente d'approbation par l'organisation.", "Waiting list": "Liste d'attente", diff --git a/js/src/views/Event/Event.vue b/js/src/views/Event/Event.vue index 7819e9d4..62e66c9c 100644 --- a/js/src/views/Event/Event.vue +++ b/js/src/views/Event/Event.vue @@ -134,7 +134,12 @@ import {ParticipantRole} from "@/types/event.model"; - {{ urlToHostname(event.onlineAddress) }} + {{ urlToHostname(event.onlineAddress) }}
diff --git a/test/mobilizon/service/formatter/formatter_test.exs b/test/mobilizon/service/formatter/formatter_test.exs index cc3dcfa4..47588c44 100644 --- a/test/mobilizon/service/formatter/formatter_test.exs +++ b/test/mobilizon/service/formatter/formatter_test.exs @@ -33,21 +33,21 @@ defmodule Mobilizon.Service.FormatterTest do text = "Hey, check out https://www.youtube.com/watch?v=8Zg1-TufF%20zY?x=1&y=2#blabla ." expected = - "Hey, check out https://www.youtube.com/watch?v=8Zg1-TufF%20zY?x=1&y=2#blabla ." + "Hey, check out https://www.youtube.com/watch?v=8Zg1-TufF%20zY?x=1&y=2#blabla ." assert {^expected, [], []} = Formatter.linkify(text) text = "https://mastodon.social/@lambadalambda" expected = - "https://mastodon.social/@lambadalambda" + "https://mastodon.social/@lambadalambda" assert {^expected, [], []} = Formatter.linkify(text) text = "https://mastodon.social:4000/@lambadalambda" expected = - "https://mastodon.social:4000/@lambadalambda" + "https://mastodon.social:4000/@lambadalambda" assert {^expected, [], []} = Formatter.linkify(text) @@ -57,55 +57,58 @@ defmodule Mobilizon.Service.FormatterTest do assert {^expected, [], []} = Formatter.linkify(text) text = "http://www.cs.vu.nl/~ast/intel/" - expected = "http://www.cs.vu.nl/~ast/intel/" + + expected = + "http://www.cs.vu.nl/~ast/intel/" assert {^expected, [], []} = Formatter.linkify(text) text = "https://forum.zdoom.org/viewtopic.php?f=44&t=57087" expected = - "https://forum.zdoom.org/viewtopic.php?f=44&t=57087" + "https://forum.zdoom.org/viewtopic.php?f=44&t=57087" assert {^expected, [], []} = Formatter.linkify(text) text = "https://en.wikipedia.org/wiki/Sophia_(Gnosticism)#Mythos_of_the_soul" expected = - "https://en.wikipedia.org/wiki/Sophia_(Gnosticism)#Mythos_of_the_soul" + "https://en.wikipedia.org/wiki/Sophia_(Gnosticism)#Mythos_of_the_soul" assert {^expected, [], []} = Formatter.linkify(text) text = "https://www.google.co.jp/search?q=Nasim+Aghdam" expected = - "https://www.google.co.jp/search?q=Nasim+Aghdam" + "https://www.google.co.jp/search?q=Nasim+Aghdam" assert {^expected, [], []} = Formatter.linkify(text) text = "https://en.wikipedia.org/wiki/Duff's_device" expected = - "https://en.wikipedia.org/wiki/Duff's_device" + "https://en.wikipedia.org/wiki/Duff's_device" assert {^expected, [], []} = Formatter.linkify(text) text = "https://pleroma.com https://pleroma.com/sucks" expected = - "https://pleroma.com https://pleroma.com/sucks" + "https://pleroma.com https://pleroma.com/sucks" assert {^expected, [], []} = Formatter.linkify(text) text = "xmpp:contact@hacktivis.me" - expected = "xmpp:contact@hacktivis.me" + expected = + "xmpp:contact@hacktivis.me" assert {^expected, [], []} = Formatter.linkify(text) text = "magnet:?xt=urn:btih:7ec9d298e91d6e4394d1379caf073c77ff3e3136&tr=udp%3A%2F%2Fopentor.org%3A2710&tr=udp%3A%2F%2Ftracker.blackunicorn.xyz%3A6969&tr=udp%3A%2F%2Ftracker.ccc.de%3A80&tr=udp%3A%2F%2Ftracker.coppersurfer.tk%3A6969&tr=udp%3A%2F%2Ftracker.leechers-paradise.org%3A6969&tr=udp%3A%2F%2Ftracker.openbittorrent.com%3A80&tr=wss%3A%2F%2Ftracker.btorrent.xyz&tr=wss%3A%2F%2Ftracker.fastcast.nz&tr=wss%3A%2F%2Ftracker.openwebtorrent.com" - expected = "#{text}" + expected = "#{text}" assert {^expected, [], []} = Formatter.linkify(text) end