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 <tcit@tcit.fr>
This commit is contained in:
Thomas Citharel 2019-11-04 15:32:55 +01:00
parent fb25c7c07f
commit 0a844aa174
No known key found for this signature in database
GPG Key ID: A061B9DDE0CA0773
6 changed files with 49 additions and 40 deletions

View File

@ -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

View File

@ -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

View File

@ -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"
}
}

View File

@ -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 <b>trapped inside the tech giants platforms</b>. How can we organize, how can we click “Attend,” without <b>providing private data</b> to Facebook or <b>locking ourselves up</b> inside MeetUp?": "De lanniversaire entre ami·e·s à une marche pour le climat, aujourdhui, les bonnes raisons de se rassembler sont <b>captées par les géants du web</b>. Comment sorganiser, comment cliquer sur «je participe» sans <b>livrer des données intimes</b> à Facebook ou<b> senfermer</b> 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 <b>their own spaces</b> to publish events in order to better emancipate themselves from tech giants.": "Mobilizon est un logiciel libre qui permettra à des communautés de <b>créer leurs propres espaces</b> 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 Framasofts statement of intent on the Framablog": "Lire la note dintention 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",

View File

@ -134,7 +134,12 @@ import {ParticipantRole} from "@/types/event.model";
</div>
<span class="online-address" v-if="event.onlineAddress && urlToHostname(event.onlineAddress)">
<b-icon icon="link"></b-icon>
<a :href="event.onlineAddress">{{ urlToHostname(event.onlineAddress) }}</a>
<a
target="_blank"
rel="noopener noreferrer"
:href="event.onlineAddress"
:title="$t('View page on {hostname} (in a new window)', {hostname: urlToHostname(event.onlineAddress) })"
>{{ urlToHostname(event.onlineAddress) }}</a>
</span>
<div class="organizer">
<span>

View File

@ -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 <a href=\"https://www.youtube.com/watch?v=8Zg1-TufF%20zY?x=1&y=2#blabla\">https://www.youtube.com/watch?v=8Zg1-TufF%20zY?x=1&y=2#blabla</a> ."
"Hey, check out <a href=\"https://www.youtube.com/watch?v=8Zg1-TufF%20zY?x=1&y=2#blabla\" target=\"_blank\" rel=\"noopener noreferrer\">https://www.youtube.com/watch?v=8Zg1-TufF%20zY?x=1&y=2#blabla</a> ."
assert {^expected, [], []} = Formatter.linkify(text)
text = "https://mastodon.social/@lambadalambda"
expected =
"<a href=\"https://mastodon.social/@lambadalambda\">https://mastodon.social/@lambadalambda</a>"
"<a href=\"https://mastodon.social/@lambadalambda\" target=\"_blank\" rel=\"noopener noreferrer\">https://mastodon.social/@lambadalambda</a>"
assert {^expected, [], []} = Formatter.linkify(text)
text = "https://mastodon.social:4000/@lambadalambda"
expected =
"<a href=\"https://mastodon.social:4000/@lambadalambda\">https://mastodon.social:4000/@lambadalambda</a>"
"<a href=\"https://mastodon.social:4000/@lambadalambda\" target=\"_blank\" rel=\"noopener noreferrer\">https://mastodon.social:4000/@lambadalambda</a>"
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 = "<a href=\"http://www.cs.vu.nl/~ast/intel/\">http://www.cs.vu.nl/~ast/intel/</a>"
expected =
"<a href=\"http://www.cs.vu.nl/~ast/intel/\" target=\"_blank\" rel=\"noopener noreferrer\">http://www.cs.vu.nl/~ast/intel/</a>"
assert {^expected, [], []} = Formatter.linkify(text)
text = "https://forum.zdoom.org/viewtopic.php?f=44&t=57087"
expected =
"<a href=\"https://forum.zdoom.org/viewtopic.php?f=44&t=57087\">https://forum.zdoom.org/viewtopic.php?f=44&t=57087</a>"
"<a href=\"https://forum.zdoom.org/viewtopic.php?f=44&t=57087\" target=\"_blank\" rel=\"noopener noreferrer\">https://forum.zdoom.org/viewtopic.php?f=44&t=57087</a>"
assert {^expected, [], []} = Formatter.linkify(text)
text = "https://en.wikipedia.org/wiki/Sophia_(Gnosticism)#Mythos_of_the_soul"
expected =
"<a href=\"https://en.wikipedia.org/wiki/Sophia_(Gnosticism)#Mythos_of_the_soul\">https://en.wikipedia.org/wiki/Sophia_(Gnosticism)#Mythos_of_the_soul</a>"
"<a href=\"https://en.wikipedia.org/wiki/Sophia_(Gnosticism)#Mythos_of_the_soul\" target=\"_blank\" rel=\"noopener noreferrer\">https://en.wikipedia.org/wiki/Sophia_(Gnosticism)#Mythos_of_the_soul</a>"
assert {^expected, [], []} = Formatter.linkify(text)
text = "https://www.google.co.jp/search?q=Nasim+Aghdam"
expected =
"<a href=\"https://www.google.co.jp/search?q=Nasim+Aghdam\">https://www.google.co.jp/search?q=Nasim+Aghdam</a>"
"<a href=\"https://www.google.co.jp/search?q=Nasim+Aghdam\" target=\"_blank\" rel=\"noopener noreferrer\">https://www.google.co.jp/search?q=Nasim+Aghdam</a>"
assert {^expected, [], []} = Formatter.linkify(text)
text = "https://en.wikipedia.org/wiki/Duff's_device"
expected =
"<a href=\"https://en.wikipedia.org/wiki/Duff's_device\">https://en.wikipedia.org/wiki/Duff's_device</a>"
"<a href=\"https://en.wikipedia.org/wiki/Duff's_device\" target=\"_blank\" rel=\"noopener noreferrer\">https://en.wikipedia.org/wiki/Duff's_device</a>"
assert {^expected, [], []} = Formatter.linkify(text)
text = "https://pleroma.com https://pleroma.com/sucks"
expected =
"<a href=\"https://pleroma.com\">https://pleroma.com</a> <a href=\"https://pleroma.com/sucks\">https://pleroma.com/sucks</a>"
"<a href=\"https://pleroma.com\" target=\"_blank\" rel=\"noopener noreferrer\">https://pleroma.com</a> <a href=\"https://pleroma.com/sucks\" target=\"_blank\" rel=\"noopener noreferrer\">https://pleroma.com/sucks</a>"
assert {^expected, [], []} = Formatter.linkify(text)
text = "xmpp:contact@hacktivis.me"
expected = "<a href=\"xmpp:contact@hacktivis.me\">xmpp:contact@hacktivis.me</a>"
expected =
"<a href=\"xmpp:contact@hacktivis.me\" target=\"_blank\" rel=\"noopener noreferrer\">xmpp:contact@hacktivis.me</a>"
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 = "<a href=\"#{text}\">#{text}</a>"
expected = "<a href=\"#{text}\" target=\"_blank\" rel=\"noopener noreferrer\">#{text}</a>"
assert {^expected, [], []} = Formatter.linkify(text)
end