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 - Upgraded frontend and backend dependencies
### Changed ### Changed
- Improve Docker setup and docs - Move participant stats to event table **(read special instructions above)**
- 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
- Limit length (20 characters) and number (10) of tags allowed - Limit length (20 characters) and number (10) of tags allowed
- Added some backend changes and validation for field length - 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 - Improve some production ipv6 configuration
- Move participant stats to event table **(read special instructions above)**
### Fixed ### Fixed
- Fix event URL validation and check if hostname is correct before showing it - 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 # TODO: Set to :no_scheme when it works properly
validate_tld: true, validate_tld: true,
class: false, class: false,
strip_prefix: false, strip_prefix: false
new_window: false,
rel: false
] ]
config :phoenix, :format_encoders, json: Jason, "activity-json": Jason config :phoenix, :format_encoders, json: Jason, "activity-json": Jason

View File

@ -278,6 +278,7 @@
"Users": "Users", "Users": "Users",
"View event page": "View event page", "View event page": "View event page",
"View everything": "View everything", "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)", "Visible everywhere on the web (public)": "Visible everywhere on the web (public)",
"Waiting for organization team approval.": "Waiting for organization team approval.", "Waiting for organization team approval.": "Waiting for organization team approval.",
"Waiting list": "Waiting list", "Waiting list": "Waiting list",
@ -325,4 +326,4 @@
"{count} requests waiting": "{count} requests waiting", "{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.", "{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" "© 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 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}", "A validation email was sent to {email}": "Un email de validation a été envoyé à {email}",
"Abandon edition": "Abandonner l'édition", "Abandon edition": "Abandonner l'édition",
"About": "À propos",
"About Mobilizon": "À propos de Mobilizon", "About Mobilizon": "À propos de Mobilizon",
"About this event": "À propos de cet événement", "About this event": "À propos de cet événement",
"About this instance": "À propos de cette instance", "About this instance": "À propos de cette instance",
"Add": "Ajouter", "About": "À propos",
"Add an address": "Ajouter une adresse", "Add an address": "Ajouter une adresse",
"Add some tags": "Ajouter des tags", "Add some tags": "Ajouter des tags",
"Add to my calendar": "Ajouter à mon agenda", "Add to my calendar": "Ajouter à mon agenda",
"Add": "Ajouter",
"Additional comments": "Commentaires additionnels", "Additional comments": "Commentaires additionnels",
"Administration": "Administration", "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.", "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", "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", "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}", "By {name}": "Par {name}",
"Cancel": "Annuler",
"Cancel creation": "Annuler la création", "Cancel creation": "Annuler la création",
"Cancel edition": "Annuler l'édition", "Cancel edition": "Annuler l'édition",
"Cancel my participation request…": "Annuler ma demande de participation…", "Cancel my participation request…": "Annuler ma demande de participation…",
"Cancel my participation…": "Annuler ma participation…", "Cancel my participation…": "Annuler ma participation…",
"Cancel": "Annuler",
"Cancelled: Won't happen": "Annulé: N'aura pas lieu", "Cancelled: Won't happen": "Annulé: N'aura pas lieu",
"Category": "Catégorie", "Category": "Catégorie",
"Change": "Modifier",
"Change my identity…": "Changer mon identité…", "Change my identity…": "Changer mon identité…",
"Change my password": "Modifier mon mot de passe", "Change my password": "Modifier mon mot de passe",
"Change password": "Modifier mot de passe", "Change password": "Modifier mot de passe",
"Change": "Modifier",
"Clear": "Effacer", "Clear": "Effacer",
"Click to select": "Cliquez pour sélectionner", "Click to select": "Cliquez pour sélectionner",
"Click to upload": "Cliquez pour uploader", "Click to upload": "Cliquez pour uploader",
"Close comments for all (except for admins)": "Fermer les commentaires à tout le monde (excepté les administrateurs)", "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 on the event page": "Commentaires sur la page de l'événement",
"Comments": "Commentaires",
"Confirm my particpation": "Confirmer ma participation", "Confirm my particpation": "Confirmer ma participation",
"Confirmed: Will happen": "Confirmé : aura lieu", "Confirmed: Will happen": "Confirmé : aura lieu",
"Continue editing": "Continuer l'édition", "Continue editing": "Continuer l'édition",
"Country": "Pays", "Country": "Pays",
"Create": "Créer",
"Create a new event": "Créer un nouvel événement", "Create a new event": "Créer un nouvel événement",
"Create a new group": "Créer un nouveau groupe", "Create a new group": "Créer un nouveau groupe",
"Create a new identity": "Créer une nouvelle identité", "Create a new identity": "Créer une nouvelle identité",
@ -57,16 +56,17 @@
"Create my profile": "Créer mon profil", "Create my profile": "Créer mon profil",
"Create token": "Créer un jeton", "Create token": "Créer un jeton",
"Create, edit or delete events": "Créer, modifier ou supprimer des événements", "Create, edit or delete events": "Créer, modifier ou supprimer des événements",
"Create": "Créer",
"Creator": "Créateur", "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.", "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 and time settings": "Paramètres de date et d'heure",
"Date parameters": "Paramètres de date", "Date parameters": "Paramètres de date",
"Delete": "Supprimer",
"Delete event": "Supprimer un événement", "Delete event": "Supprimer un événement",
"Delete this identity": "Supprimer cette identité", "Delete this identity": "Supprimer cette identité",
"Delete your identity": "Supprimer votre identité", "Delete your identity": "Supprimer votre identité",
"Delete {eventTitle}": "Supprimer {eventTitle}", "Delete {eventTitle}": "Supprimer {eventTitle}",
"Delete {preferredUsername}": "Supprimer {preferredUsername}", "Delete {preferredUsername}": "Supprimer {preferredUsername}",
"Delete": "Supprimer",
"Description": "Description", "Description": "Description",
"Didn't receive the instructions ?": "Vous n'avez pas reçu les instructions ?", "Didn't receive the instructions ?": "Vous n'avez pas reçu les instructions ?",
"Display name": "Nom affiché", "Display name": "Nom affiché",
@ -84,7 +84,6 @@
"Error while communicating with the server.": "Erreur de communication avec le serveur.", "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 saving report.": "Erreur lors de l'enregistrement du signalement.",
"Error while validating account": "Erreur lors de la validation du compte", "Error while validating account": "Erreur lors de la validation du compte",
"Event": "Événement",
"Event already passed": "Événement déjà passé", "Event already passed": "Événement déjà passé",
"Event cancelled": "Événement annulé", "Event cancelled": "Événement annulé",
"Event creation": "Création d'événement", "Event creation": "Création d'événement",
@ -95,6 +94,7 @@
"Event to be confirmed": "Événement à confirmer", "Event to be confirmed": "Événement à confirmer",
"Event {eventTitle} deleted": "Événement {eventTitle} supprimé", "Event {eventTitle} deleted": "Événement {eventTitle} supprimé",
"Event {eventTitle} reported": "Événement {eventTitle} signalé", "Event {eventTitle} reported": "Événement {eventTitle} signalé",
"Event": "Événement",
"Events": "Événements", "Events": "Événements",
"Exclude": "Exclure", "Exclude": "Exclure",
"Explore": "Explorer", "Explore": "Explorer",
@ -105,8 +105,8 @@
"For instance: London, Taekwondo, Architecture…": "Par exemple: Lyon, Taekwondo, Architecture…", "For instance: London, Taekwondo, Architecture…": "Par exemple: Lyon, Taekwondo, Architecture…",
"Forgot your password ?": "Mot de passe oublié ?", "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 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} 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}", "From the {startDate} to the {endDate}": "Du {startDate} au {endDate}",
"Gather ⋅ Organize ⋅ Mobilize": "Rassembler ⋅ Organiser ⋅ Mobiliser", "Gather ⋅ Organize ⋅ Mobilize": "Rassembler ⋅ Organiser ⋅ Mobiliser",
"General information": "Informations générales", "General information": "Informations générales",
@ -131,8 +131,8 @@
"Join {instance}, a Mobilizon instance": "Rejoignez {instance}, une instance Mobilizon", "Join {instance}, a Mobilizon instance": "Rejoignez {instance}, une instance Mobilizon",
"Last published event": "Dernier événement publié", "Last published event": "Dernier événement publié",
"Last week": "La semaine dernière", "Last week": "La semaine dernière",
"Learn more": "En apprendre plus",
"Learn more about Mobilizon": "En apprendre plus à propos de Mobilizon", "Learn more about Mobilizon": "En apprendre plus à propos de Mobilizon",
"Learn more": "En apprendre plus",
"Leave event": "Annuler ma participation à l'événement", "Leave event": "Annuler ma participation à l'événement",
"Leaving event \"{title}\"": "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", "Let's create a new common": "Créons un nouveau Common",
@ -142,8 +142,8 @@
"Locality": "Commune", "Locality": "Commune",
"Log in": "Se connecter", "Log in": "Se connecter",
"Log out": "Se déconnecter", "Log out": "Se déconnecter",
"Login": "Se connecter",
"Login on Mobilizon!": "Se connecter sur Mobilizon !", "Login on Mobilizon!": "Se connecter sur Mobilizon !",
"Login": "Se connecter",
"Manage participations": "Gérer les participations", "Manage participations": "Gérer les participations",
"Members": "Membres", "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.", "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", "Number of places": "Nombre de places",
"OK": "OK", "OK": "OK",
"Old password": "Ancien mot de passe", "Old password": "Ancien mot de passe",
"On {date}": "Le {date}",
"On {date} ending at {endTime}": "Le {date}, se terminant à {endTime}", "On {date} ending at {endTime}": "Le {date}, se terminant à {endTime}",
"On {date} from {startTime} to {endTime}": "Le {date} de {startTime} à {endTime}", "On {date} from {startTime} to {endTime}": "Le {date} de {startTime} à {endTime}",
"On {date} starting at {startTime}": "Le {date} à partir de {startTime}", "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", "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é)", "Only accessible through link and search (private)": "Uniquement accessibles par lien et la recherche (privé)",
"Opened reports": "Signalements ouverts", "Opened reports": "Signalements ouverts",
"Organized": "Organisés",
"Organized by {name}": "Organisé par {name}", "Organized by {name}": "Organisé par {name}",
"Organized": "Organisés",
"Organizer": "Organisateur", "Organizer": "Organisateur",
"Otherwise this identity will just be removed from the group administrators.": "Sinon cette identité sera juste supprimée des administrateurs du groupe.", "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)", "Page limited to my group (asks for auth)": "Accès limité à mon groupe (demande authentification)",
@ -184,10 +184,10 @@
"Participate": "Participer", "Participate": "Participer",
"Participation approval": "Validation des participations", "Participation approval": "Validation des participations",
"Participation requested!": "Participation demandée !", "Participation requested!": "Participation demandée !",
"Password": "Mot de passe",
"Password (confirmation)": "Mot de passe (confirmation)", "Password (confirmation)": "Mot de passe (confirmation)",
"Password change": "Changement de mot de passe", "Password change": "Changement de mot de passe",
"Password reset": "Réinitialisation du mot de passe", "Password reset": "Réinitialisation du mot de passe",
"Password": "Mot de passe",
"Past events": "Événements passés", "Past events": "Événements passés",
"Pick an identity": "Choisissez une identité", "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.", "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", "RSS/Atom Feed": "Flux RSS/Atom",
"Read Framasofts statement of intent on the Framablog": "Lire la note dintention de Framasoft sur le Framablog", "Read Framasofts statement of intent on the Framablog": "Lire la note dintention de Framasoft sur le Framablog",
"Region": "Région", "Region": "Région",
"Register": "S'inscrire",
"Register an account on Mobilizon!": "S'inscrire sur Mobilizon !", "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 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.", "Registration is currently closed.": "Les inscriptions sont actuellement fermées.",
"Reject": "Rejetter", "Reject": "Rejetter",
"Rejected": "Rejetés",
"Rejected participations": "Participations rejetées", "Rejected participations": "Participations rejetées",
"Report": "Signaler", "Rejected": "Rejetés",
"Report this event": "Signaler cet événement", "Report this event": "Signaler cet événement",
"Report": "Signaler",
"Requests": "Requêtes", "Requests": "Requêtes",
"Resend confirmation email": "Envoyer à nouveau l'email de confirmation", "Resend confirmation email": "Envoyer à nouveau l'email de confirmation",
"Reset my password": "Réinitialiser mon mot de passe", "Reset my password": "Réinitialiser mon mot de passe",
"Save": "Enregistrer",
"Save draft": "Enregistrer le brouillon", "Save draft": "Enregistrer le brouillon",
"Search": "Rechercher", "Save": "Enregistrer",
"Search events, groups, etc.": "Rechercher des événements, des groupes, etc.", "Search events, groups, etc.": "Rechercher des événements, des groupes, etc.",
"Search results: \"{search}\"": "Résultats de recherche: « {search} »", "Search results: \"{search}\"": "Résultats de recherche: « {search} »",
"Search": "Rechercher",
"Searching…": "Recherche en cours…", "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 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", "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 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 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 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 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 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 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.", "The page you're looking for doesn't exist.": "La page que vous recherchez n'existe pas.",
@ -278,6 +278,7 @@
"Users": "Utilisateurs", "Users": "Utilisateurs",
"View event page": "Voir la page de l'événement", "View event page": "Voir la page de l'événement",
"View everything": "Voir tout", "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)", "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 for organization team approval.": "En attente d'approbation par l'organisation.",
"Waiting list": "Liste d'attente", "Waiting list": "Liste d'attente",

View File

@ -134,7 +134,12 @@ import {ParticipantRole} from "@/types/event.model";
</div> </div>
<span class="online-address" v-if="event.onlineAddress && urlToHostname(event.onlineAddress)"> <span class="online-address" v-if="event.onlineAddress && urlToHostname(event.onlineAddress)">
<b-icon icon="link"></b-icon> <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> </span>
<div class="organizer"> <div class="organizer">
<span> <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 ." text = "Hey, check out https://www.youtube.com/watch?v=8Zg1-TufF%20zY?x=1&y=2#blabla ."
expected = 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) assert {^expected, [], []} = Formatter.linkify(text)
text = "https://mastodon.social/@lambadalambda" text = "https://mastodon.social/@lambadalambda"
expected = 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) assert {^expected, [], []} = Formatter.linkify(text)
text = "https://mastodon.social:4000/@lambadalambda" text = "https://mastodon.social:4000/@lambadalambda"
expected = 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) assert {^expected, [], []} = Formatter.linkify(text)
@ -57,55 +57,58 @@ defmodule Mobilizon.Service.FormatterTest do
assert {^expected, [], []} = Formatter.linkify(text) assert {^expected, [], []} = Formatter.linkify(text)
text = "http://www.cs.vu.nl/~ast/intel/" 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) assert {^expected, [], []} = Formatter.linkify(text)
text = "https://forum.zdoom.org/viewtopic.php?f=44&t=57087" text = "https://forum.zdoom.org/viewtopic.php?f=44&t=57087"
expected = 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) assert {^expected, [], []} = Formatter.linkify(text)
text = "https://en.wikipedia.org/wiki/Sophia_(Gnosticism)#Mythos_of_the_soul" text = "https://en.wikipedia.org/wiki/Sophia_(Gnosticism)#Mythos_of_the_soul"
expected = 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) assert {^expected, [], []} = Formatter.linkify(text)
text = "https://www.google.co.jp/search?q=Nasim+Aghdam" text = "https://www.google.co.jp/search?q=Nasim+Aghdam"
expected = 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) assert {^expected, [], []} = Formatter.linkify(text)
text = "https://en.wikipedia.org/wiki/Duff's_device" text = "https://en.wikipedia.org/wiki/Duff's_device"
expected = 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) assert {^expected, [], []} = Formatter.linkify(text)
text = "https://pleroma.com https://pleroma.com/sucks" text = "https://pleroma.com https://pleroma.com/sucks"
expected = 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) assert {^expected, [], []} = Formatter.linkify(text)
text = "xmpp:contact@hacktivis.me" 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) assert {^expected, [], []} = Formatter.linkify(text)
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" "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) assert {^expected, [], []} = Formatter.linkify(text)
end end