@@ -14,6 +15,7 @@
\ No newline at end of file
diff --git a/js/src/components/Utils/VerticalDivider.vue b/js/src/components/Utils/VerticalDivider.vue
new file mode 100644
index 000000000..235814a40
--- /dev/null
+++ b/js/src/components/Utils/VerticalDivider.vue
@@ -0,0 +1,22 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/js/src/graphql/admin.ts b/js/src/graphql/admin.ts
index 1a7a5bfef..904e7a87e 100644
--- a/js/src/graphql/admin.ts
+++ b/js/src/graphql/admin.ts
@@ -103,3 +103,45 @@ export const REJECT_RELAY = gql`
}
${RELAY_FRAGMENT}
`;
+
+export const ADMIN_SETTINGS_FRAGMENT = gql`
+ fragment adminSettingsFragment on AdminSettings {
+ instanceName,
+ instanceDescription,
+ instanceTerms,
+ instanceTermsType,
+ instanceTermsUrl
+ registrationsOpen
+ }
+`;
+
+export const ADMIN_SETTINGS = gql`
+ query {
+ adminSettings {
+ ...adminSettingsFragment
+ }
+ }
+ ${ADMIN_SETTINGS_FRAGMENT}
+`;
+
+export const SAVE_ADMIN_SETTINGS = gql`
+ mutation SaveAdminSettings(
+ $instanceName: String,
+ $instanceDescription: String,
+ $instanceTerms: String,
+ $instanceTermsType: InstanceTermsType,
+ $instanceTermsUrl: String,
+ $registrationsOpen: Boolean) {
+ saveAdminSettings(
+ instanceName: $instanceName,
+ instanceDescription: $instanceDescription,
+ instanceTerms: $instanceTerms,
+ instanceTermsType: $instanceTermsType,
+ instanceTermsUrl: $instanceTermsUrl,
+ registrationsOpen: $registrationsOpen
+ ) {
+ ...adminSettingsFragment
+ }
+ }
+ ${ADMIN_SETTINGS_FRAGMENT}
+`;
diff --git a/js/src/graphql/config.ts b/js/src/graphql/config.ts
index a128eba38..fdad484ee 100644
--- a/js/src/graphql/config.ts
+++ b/js/src/graphql/config.ts
@@ -9,6 +9,33 @@ query {
registrationsWhitelist,
demoMode,
countryCode,
+ anonymous {
+ participation {
+ allowed,
+ validation {
+ email {
+ enabled,
+ confirmationRequired
+ },
+ captcha {
+ enabled
+ }
+ }
+ }
+ eventCreation {
+ allowed,
+ validation {
+ email {
+ enabled,
+ confirmationRequired
+ },
+ captcha {
+ enabled
+ }
+ }
+ }
+ actorId
+ },
location {
latitude,
longitude,
@@ -27,3 +54,15 @@ query {
}
}
`;
+
+export const TERMS = gql`
+query Terms($locale: String) {
+ config {
+ terms(locale: $locale) {
+ type,
+ url,
+ bodyHtml
+ }
+ }
+}
+`;
diff --git a/js/src/graphql/event.ts b/js/src/graphql/event.ts
index 9f192d563..b229ad325 100644
--- a/js/src/graphql/event.ts
+++ b/js/src/graphql/event.ts
@@ -14,7 +14,11 @@ const participantQuery = `
domain
},
event {
- id
+ id,
+ uuid
+ },
+ metadata {
+ cancellationToken
}
`;
@@ -41,6 +45,7 @@ const optionsQuery = `
maximumAttendeeCapacity,
remainingAttendeeCapacity,
showRemainingAttendeeCapacity,
+ anonymousParticipation,
showStartTime,
showEndTime,
offers {
@@ -366,10 +371,11 @@ export const EDIT_EVENT = gql`
`;
export const JOIN_EVENT = gql`
- mutation JoinEvent($eventId: ID!, $actorId: ID!) {
+ mutation JoinEvent($eventId: ID!, $actorId: ID!, $email: String) {
joinEvent(
eventId: $eventId,
- actorId: $actorId
+ actorId: $actorId,
+ email: $email
) {
${participantQuery}
}
@@ -377,10 +383,11 @@ export const JOIN_EVENT = gql`
`;
export const LEAVE_EVENT = gql`
- mutation LeaveEvent($eventId: ID!, $actorId: ID!) {
+ mutation LeaveEvent($eventId: ID!, $actorId: ID!, $token: String) {
leaveEvent(
eventId: $eventId,
- actorId: $actorId
+ actorId: $actorId,
+ token: $token
) {
actor {
id
@@ -389,6 +396,20 @@ export const LEAVE_EVENT = gql`
}
`;
+export const CONFIRM_PARTICIPATION = gql`
+ mutation ConfirmParticipation($token: String!) {
+ confirmParticipation(confirmationToken: $token) {
+ actor {
+ id,
+ },
+ event {
+ uuid
+ },
+ role
+ }
+ }
+`;
+
export const UPDATE_PARTICIPANT = gql`
mutation AcceptParticipant($id: ID!, $moderatorActorId: ID!, $role: ParticipantRoleEnum!) {
updateParticipation(id: $id, moderatorActorId: $moderatorActorId, role: $role) {
diff --git a/js/src/i18n/en_US.json b/js/src/i18n/en_US.json
index c8c14d7eb..9b5a5d646 100644
--- a/js/src/i18n/en_US.json
+++ b/js/src/i18n/en_US.json
@@ -15,10 +15,16 @@
"Add to my calendar": "Add to my calendar",
"Add": "Add",
"Additional comments": "Additional comments",
+ "Admin settings successfully saved.": "Admin settings successfully saved.",
+ "Admin settings": "Admin settings",
"Administration": "Administration",
"All the places have already been taken": "All the places have been taken|One place is still available|{places} places are still available",
"Allow all comments": "Allow all comments",
+ "Allow registrations": "Allow registrations",
"An error has occurred.": "An error has occurred.",
+ "And no anonymous participations|And one anonymous participation|And {count} anonymous participations": "And no anonymous participations|And one anonymous participation|And {count} anonymous participations",
+ "Anonymous participants will be asked to confirm their participation through e-mail.": "Anonymous participants will be asked to confirm their participation through e-mail.",
+ "Anonymous participations": "Anonymous participations",
"Approve": "Approve",
"Are you sure you want to
delete this comment? This action cannot be undone.": "Are you sure you want to
delete this comment? This action cannot be undone.",
"Are you sure you want to
delete this event? This action cannot be undone. You may want to engage the conversation with the event creator or edit its event instead.": "Are you sure you want to
delete this event? This action cannot be undone. You may want to engage the conversation with the event creator or edit its event instead.",
@@ -27,8 +33,10 @@
"Are you sure you want to cancel your participation at event \"{title}\"?": "Are you sure you want to cancel your participation at event \"{title}\"?",
"Are you sure you want to delete this event? This action cannot be reverted.": "Are you sure you want to delete this event? This action cannot be reverted.",
"Avatar": "Avatar",
+ "Back to previous page": "Back to previous page",
"Before you can login, you need to click on the link inside it to validate your account": "Before you can login, you need to click on the link inside it to validate your account",
"By {name}": "By {name}",
+ "Cancel anonymous participation": "Cancel anonymous participation",
"Cancel creation": "Cancel creation",
"Cancel edition": "Cancel edition",
"Cancel my participation request…": "Cancel my participation request…",
@@ -67,10 +75,15 @@
"Create": "Create",
"Creator": "Creator",
"Current identity has been changed to {identityName} in order to manage this event.": "Current identity has been changed to {identityName} in order to manage this event.",
+ "Custom URL": "Custom URL",
+ "Custom text": "Custom text",
+ "Custom": "Custom",
"Dashboard": "Dashboard",
"Date and time settings": "Date and time settings",
"Date parameters": "Date parameters",
"Date": "Date",
+ "Default Mobilizon.org terms": "Default Mobilizon.org terms",
+ "Default": "Default",
"Delete Comment": "Delete Comment",
"Delete Event": "Delete Event",
"Delete event": "Delete event",
@@ -90,14 +103,18 @@
"Drafts": "Drafts",
"Edit": "Edit",
"Eg: Stockholm, Dance, Chess…": "Eg: Stockholm, Dance, Chess…",
+ "Either on the {instance} instance or on another instance.": "Either on the {instance} instance or on another instance.",
"Either the account is already validated, either the validation token is incorrect.": "Either the account is already validated, either the validation token is incorrect.",
+ "Either the participation has already been validated, either the validation token is incorrect.": "Either the participation has already been validated, either the validation token is incorrect.",
"Email": "Email",
"Ends on…": "Ends on…",
"Enjoy discovering Mobilizon!": "Enjoy discovering Mobilizon!",
"Enter the link URL": "Enter the link URL",
+ "Enter your own terms. HTML tags allowed. Mobilizon.org's terms are provided as template.": "Enter your own terms. HTML tags allowed. Mobilizon.org's terms are provided as template.",
"Error while communicating with the server.": "Error while communicating with the server.",
"Error while saving report.": "Error while saving report.",
"Error while validating account": "Error while validating account",
+ "Error while validating participation": "Error while validating participation",
"Event already passed": "Event already passed",
"Event cancelled": "Event cancelled",
"Event creation": "Event creation",
@@ -113,6 +130,7 @@
"Ex: test.mobilizon.org": "Ex: test.mobilizon.org",
"Exclude": "Exclude",
"Explore": "Explore",
+ "Failed to save admin settings": "Failed to save admin settings",
"Featured events": "Featured events",
"Features": "Features",
"Find an address": "Find an address",
@@ -128,6 +146,7 @@
"Gather ⋅ Organize ⋅ Mobilize": "Gather ⋅ Organize ⋅ Mobilize",
"General information": "General information",
"Getting location": "Getting location",
+ "Go": "Go",
"Going as {name}": "Going as {name}",
"Group List": "Group List",
"Group full name": "Group full name",
@@ -137,7 +156,11 @@
"Headline picture": "Headline picture",
"Hide replies": "Hide replies",
"I create an identity": "I create an identity",
+ "I don't have a Mobilizon account": "I don't have a Mobilizon account",
+ "I have a Mobilizon account": "I have a Mobilizon account",
+ "I have an account on another Mobilizon instance.": "I have an account on another Mobilizon instance.",
"I participate": "I participate",
+ "I want to allow people to participate without an account.": "I want to allow people to participate without an account.",
"I want to approve every participation request": "I want to approve every participation request",
"Identity {displayName} created": "Identity {displayName} created",
"Identity {displayName} deleted": "Identity {displayName} deleted",
@@ -147,6 +170,11 @@
"Impossible to login, your email or password seems incorrect.": "Impossible to login, your email or password seems incorrect.",
"In the meantime, please consider that the software is not (yet) finished. More information {onBlog}.": "In the meantime, please consider that the software is not (yet) finished. More information {onBlog}.",
"Installing Mobilizon will allow communities to free themselves from the services of tech giants by creating
their own event platform .": "Installing Mobilizon will allow communities to free themselves from the services of tech giants by creating
their own event platform .",
+ "Instance Description": "Instance Description",
+ "Instance Name": "Instance Name",
+ "Instance Terms Source": "Instance Terms Source",
+ "Instance Terms URL": "Instance Terms URL",
+ "Instance Terms": "Instance Terms",
"Instances": "Instances",
"Join {instance}, a Mobilizon instance": "Join {instance}, a Mobilizon instance",
"Last published event": "Last published event",
@@ -163,10 +191,12 @@
"Log in": "Log in",
"Log out": "Log out",
"Login on Mobilizon!": "Login on Mobilizon!",
+ "Login on {instance}": "Login on {instance}",
"Login": "Login",
"Manage participations": "Manage participations",
"Mark as resolved": "Mark as resolved",
"Members": "Members",
+ "Mobilizon is a federated network. You can interact with this event from a different server.": "Mobilizon is a federated network. You can interact with this event from a different server.",
"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 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 is under development, we will add new features to this site during regular updates, until the release of
version 1 of the software in the first half of 2020 .": "Mobilizon is under development, we will add new features to this site during regular updates, until the release of
version 1 of the software in the first half of 2020 .",
"Mobilizon’s licence": "Mobilizon’s licence",
@@ -207,15 +237,18 @@
"Only alphanumeric characters and underscores are supported.": "Only alphanumeric characters and underscores are supported.",
"Open": "Open",
"Opened reports": "Opened reports",
+ "Or": "Or",
"Organized by {name}": "Organized by {name}",
"Organized": "Organized",
"Organizer": "Organizer",
+ "Other software may also support this.": "Other software may also support this.",
"Otherwise this identity will just be removed from the group administrators.": "Otherwise this identity will just be removed from the group administrators.",
"Page limited to my group (asks for auth)": "Page limited to my group (asks for auth)",
"Page not found": "Page not found",
"Participant already was rejected.": "Participant already was rejected.",
"Participant has already been approved as participant.": "Participant has already been approved as participant.",
"Participants": "Participants",
+ "Participate using your email address": "Participate using your email address",
"Participate": "Participate",
"Participation approval": "Participation approval",
"Participation requested!": "Participation requested!",
@@ -234,6 +267,7 @@
"Post a comment": "Post a comment",
"Post a reply": "Post a reply",
"Postal Code": "Postal Code",
+ "Privacy Policy": "Privacy Policy",
"Private event": "Private event",
"Private feeds": "Private feeds",
"Public RSS/Atom Feed": "Public RSS/Atom Feed",
@@ -245,10 +279,13 @@
"Published events": "Published events",
"RSS/Atom Feed": "RSS/Atom Feed",
"Read Framasoft’s statement of intent on the Framablog": "Read Framasoft’s statement of intent on the Framablog",
+ "Redirecting to event…": "Redirecting to event…",
"Region": "Region",
"Register an account on Mobilizon!": "Register an account on Mobilizon!",
"Register for an event by choosing one of your identities": "Register for an event by choosing one of your identities",
"Register": "Register",
+ "Registration is allowed, anyone can register.": "Registration is allowed, anyone can register.",
+ "Registration is closed.": "Registration is closed.",
"Registration is currently closed.": "Registration is currently closed.",
"Registrations are restricted by whitelisting.": "Registrations are restricted by whitelisting.",
"Reject": "Reject",
@@ -269,15 +306,19 @@
"Resend confirmation email": "Resend confirmation email",
"Reset my password": "Reset my password",
"Resolved": "Resolved",
+ "Resource provided is not an URL": "Resource provided is not an URL",
"Save draft": "Save draft",
"Save": "Save",
"Search events, groups, etc.": "Search events, groups, etc.",
"Search results: \"{search}\"": "Search results: \"{search}\"",
"Search": "Search",
"Searching…": "Searching…",
+ "Send email": "Send email",
"Send me an email to reset my password": "Send me an email to reset my password",
"Send me the confirmation email once again": "Send me the confirmation email once again",
"Send the report": "Send the report",
+ "Set an URL to a page with your own terms.": "Set an URL to a page with your own terms.",
+ "Settings": "Settings",
"Share this event": "Share this event",
"Show map": "Show map",
"Show remaining number of places": "Show remaining number of places",
@@ -289,6 +330,8 @@
"Status": "Status",
"Street": "Street",
"Tentative: Will be confirmed later": "Tentative: Will be confirmed later",
+ "Terms": "Terms",
+ "The actual number of participants may differ, as this event is hosted on another instance.": "The actual number of participants may differ, as this event is hosted on another instance.",
"The content came from another server. Transfer an anonymous copy of the report?": "The content came from another server. Transfer an anonymous copy of the report ?",
"The current identity doesn't have any permission on this event. You should probably change it.": "The current identity doesn't have any permission on this event. You should probably change it.",
"The draft event has been updated": "The draft event has been updated",
@@ -302,8 +345,12 @@
"The password was successfully changed": "The password was successfully changed",
"The report will be sent to the moderators of your instance. You can explain why you report this content below.": "The report will be sent to the moderators of your instance. You can explain why you report this content below.",
"The user account you're trying to login as has not been confirmed yet. Check your email inbox and eventually your spam folder.": "The user account you're trying to login as has not been confirmed yet. Check your email inbox and eventually your spam folder.",
+ "The {default_terms} will be used. They will be translated in the user's language.": "The {default_terms} will be used. They will be translated in the user's language.",
"There are {participants} participants.": "There are {participants} participants.",
"These events may interest you": "These events may interest you",
+ "This Mobilizon instance and this event organizer allows anonymous participations, but requires validation through email confirmation.": "This Mobilizon instance and this event organizer allows anonymous participations, but requires validation through email confirmation.",
+ "This email is already registered as participant for this event": "This email is already registered as participant for this event",
+ "This information is saved only on your computer. Click for details": "This information is saved only on your computer. Click for details",
"This installation (called “instance“) can easily {interconnect}, thanks to {protocol}.": "This installation (called “instance“) can easily {interconnect}, thanks to {protocol}.",
"This instance isn't opened to registrations, but you can register on other instances.": "This instance isn't opened to registrations, but you can register on other instances.",
"This is a demonstration site to test the beta version of Mobilizon.": "This is a demonstration site to test the beta version of Mobilizon.",
@@ -315,6 +362,7 @@
"To confirm, type your identity username \"{preferredUsername}\"": "To confirm, type your identity username \"{preferredUsername}\"",
"Transfer to {outsideDomain}": "Transfer to {outsideDomain}",
"Type": "Type",
+ "URL": "URL",
"Unfortunately, this instance isn't opened to registrations": "Unfortunately, this instance isn't opened to registrations",
"Unfortunately, your participation request was rejected by the organizers.": "Unfortunately, your participation request was rejected by the organizers.",
"Unknown actor": "Unknown actor",
@@ -337,6 +385,7 @@
"Warning": "Warning",
"We just sent an email to {email}": "We just sent an email to {email}",
"We want to develop a
digital common , that everyone can make their own, which respects
privacy and activism by design .": "We want to develop a
digital common , that everyone can make their own, which respects
privacy and activism by design .",
+ "We will redirect you to your instance in order to interact with this event": "We will redirect you to your instance in order to interact with this event",
"We won’t change the world from Facebook. The tool we dream of, surveillance capitalism corporations won’t develop it, as they couldn’t profit from it. This is an opportunity to build something better, by taking another approach.": "We won’t change the world from Facebook. The tool we dream of, surveillance capitalism corporations won’t develop it, as they couldn’t profit from it. This is an opportunity to build something better, by taking another approach.",
"Website / URL": "Website / URL",
"Welcome back {username}!": "Welcome back {username}!",
@@ -348,7 +397,8 @@
"Write something…": "Write something…",
"You and one other person are going to this event": "You're the only one going to this event | You and one other person are going to this event | You and {approved} persons are going to this event.",
"You are already a participant of this event.": "You are already a participant of this event.",
- "You are already logged-in.": "You are already logged-in.",
+ "You are participating in this event anonymously but didn't confirm participation": "You are participating in this event anonymously but didn't confirm participation",
+ "You are participating in this event anonymously": "You are participating in this event anonymously",
"You can add tags by hitting the Enter key or by adding a comma": "You can add tags by hitting the Enter key or by adding a comma",
"You can try another search term or drag and drop the marker on the map": "You can try another search term or drag and drop the marker on the map",
"You can't remove your last identity.": "You can't remove your last identity.",
@@ -360,25 +410,33 @@
"You have one event tomorrow.": "You have no events tomorrow | You have one event tomorrow. | You have {count} events tomorrow",
"You may also ask to {resend_confirmation_email}.": "You may also ask to {resend_confirmation_email}.",
"You need to login.": "You need to login.",
+ "You will be redirected to the original instance": "You will be redirected to the original instance",
+ "You wish to participate to the following event": "You wish to participate to the following event",
"Your account has been validated": "Your account has been validated",
"Your account is being validated": "Your account is being validated",
"Your account is nearly ready, {username}": "Your account is nearly ready, {username}",
"Your email is not whitelisted, you can't register.": "Your email is not whitelisted, you can't register.",
+ "Your email will only be used to confirm that you're a real person and send you eventual updates for this event. It will NOT be transmitted to other instances or to the event organizer.": "Your email will only be used to confirm that you're a real person and send you eventual updates for this event. It will NOT be transmitted to other instances or to the event organizer.",
+ "Your federated identity": "Your federated identity",
"Your local administrator resumed its policy:": "Your local administrator resumed its policy:",
"Your participation has been confirmed": "Your participation has been confirmed",
"Your participation has been rejected": "Your participation has been rejected",
"Your participation has been requested": "Your participation has been requested",
+ "Your participation has been validated": "Your participation has been validated",
+ "Your participation is being validated": "Your participation is being validated",
"Your participation status has been changed": "Your participation status has been changed",
"[This comment has been deleted]": "[This comment has been deleted]",
"[deleted]": "[deleted]",
"a decentralised federation protocol": "a decentralised federation protocol",
"as {identity}": "as {identity}",
+ "default Mobilizon terms": "default Mobilizon terms",
"e.g. 10 Rue Jangot": "e.g. 10 Rue Jangot",
"firstDayOfWeek": "0",
"iCal Feed": "iCal Feed",
"interconnect with others like it": "interconnect with others like it",
"its source code is public": "its source code is public",
"on our blog": "on our blog",
+ "profile@instance": "profile@instance",
"resend confirmation email": "resend confirmation email",
"respect of the fundamental freedoms": "respect of the fundamental freedoms",
"with another identity…": "with another identity…",
@@ -388,4 +446,4 @@
"{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 OpenStreetMap Contributors": "© The OpenStreetMap Contributors"
-}
\ No newline at end of file
+}
diff --git a/js/src/i18n/fr_FR.json b/js/src/i18n/fr_FR.json
index 1cf0b2d7f..7106a57cf 100644
--- a/js/src/i18n/fr_FR.json
+++ b/js/src/i18n/fr_FR.json
@@ -3,22 +3,28 @@
"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 la modification",
- "About": "À propos",
"About Mobilizon": "À propos de Mobilizon",
"About this event": "À propos de cet évènement",
"About this instance": "À propos de cette instance",
+ "About": "À propos",
"Accepted": "Accepté",
- "Add": "Ajouter",
"Add a note": "Ajouter une note",
"Add an address": "Ajouter une adresse",
"Add an instance": "Ajouter une instance",
"Add some tags": "Ajouter des tags",
"Add to my calendar": "Ajouter à mon agenda",
+ "Add": "Ajouter",
"Additional comments": "Commentaires additionnels",
+ "Admin settings successfully saved.": "Les paramètres administrateur ont bien été sauvegardés",
+ "Admin settings": "Paramètres administrateur",
"Administration": "Administration",
"All the places have already been taken": "Toutes les places ont été prises|Une place est encore disponible|{places} places sont encore disponibles",
"Allow all comments": "Autoriser tous les commentaires",
+ "Allow registrations": "Autoriser les inscriptions",
"An error has occurred.": "Une erreur est survenue.",
+ "And no anonymous participations|And one anonymous participation|And {count} anonymous participations": "Et aucune participation anonyme|Et une participation anonyme|Et {count} participations anonymes",
+ "Anonymous participants will be asked to confirm their participation through e-mail.": "Les participants anonymes devront confirmer leur participation par email.",
+ "Anonymous participations": "Participations anonymes",
"Approve": "Approuver",
"Are you sure you want to
delete this comment? This action cannot be undone.": "Êtes-vous certain⋅e de vouloir
supprimer ce commentaire ? Cette action ne peut pas être annulée.",
"Are you sure you want to
delete this event? This action cannot be undone. You may want to engage the conversation with the event creator or edit its event instead.": "Êtes-vous certain⋅e de vouloir
supprimer cet évènement ? Cette action n'est pas réversible. Vous voulez peut-être engager la conversation avec le créateur de l'évènement ou bien modifier son évènement à la place.",
@@ -27,35 +33,36 @@
"Are you sure you want to cancel your participation at event \"{title}\"?": "Êtes-vous certain⋅e de vouloir annuler votre participation à l'évènement « {title} » ?",
"Are you sure you want to delete this event? This action cannot be reverted.": "Êtes-vous certain⋅e de vouloir supprimer cet évènement ? Cette action ne peut être annulée.",
"Avatar": "Avatar",
+ "Back to previous page": "Retour à la page précédente",
"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 anonymous participation": "Annuler ma participation anonyme",
"Cancel creation": "Annuler la création",
"Cancel edition": "Annuler la modification",
"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": "Fermé",
"Close comments for all (except for admins)": "Fermer les commentaires à tout le monde (excepté les administrateurs)",
+ "Close": "Fermé",
"Closed": "Fermé",
"Comment deleted": "Commentaire supprimé",
"Comment from @{username} reported": "Commentaire de @{username} signalé",
- "Comments": "Commentaires",
"Comments have been closed.": "Les commentaires sont fermés.",
"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 la modification",
"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é",
@@ -66,13 +73,18 @@
"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.",
+ "Custom URL": "URL personnalisée",
+ "Custom text": "Texte personnalisé",
+ "Custom": "Custom",
"Dashboard": "Tableau de bord",
- "Date": "Date",
"Date and time settings": "Paramètres de date et d'heure",
"Date parameters": "Paramètres de date",
- "Delete": "Supprimer",
+ "Date": "Date",
+ "Default Mobilizon.org terms": "Conditions d'utilisation par défaut de Mobilizon.org",
+ "Default": "Default",
"Delete Comment": "Supprimer le commentaire",
"Delete Event": "Supprimer l'évènement",
"Delete event": "Supprimer un évènement",
@@ -80,6 +92,7 @@
"Delete your identity": "Supprimer votre identité",
"Delete {eventTitle}": "Supprimer {eventTitle}",
"Delete {preferredUsername}": "Supprimer {preferredUsername}",
+ "Delete": "Supprimer",
"Deleting comment": "Suppression du commentaire en cours",
"Deleting event": "Suppression de l'évènement",
"Description": "Description",
@@ -91,15 +104,18 @@
"Drafts": "Brouillons",
"Edit": "Modifier",
"Eg: Stockholm, Dance, Chess…": "Par exemple : Lyon, Danse, Bridge…",
+ "Either on the {instance} instance or on another instance.": "Sur l'instance {instance} ou bien sur une autre instance.",
"Either the account is already validated, either the validation token is incorrect.": "Soit le compte est déjà validé, soit le jeton de validation est incorrect.",
+ "Either the participation has already been validated, either the validation token is incorrect.": "Either the participation has already been validated, either the validation token is incorrect.",
"Email": "Email",
"Ends on…": "Se termine le…",
"Enjoy discovering Mobilizon!": "Amusez-vous bien en découvrant Mobilizon !",
"Enter the link URL": "Entrez l'URL du lien",
+ "Enter your own terms. HTML tags allowed. Mobilizon.org's terms are provided as template.": "Entrez vos propres conditions d'utilisations. Les balises HTML sont autorisées. Les conditions d'utilisation par défaut de Mobilizon.org sont fournies comme modèle.",
"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",
+ "Error while validating participation": "Error lors de la validation de la participation",
"Event already passed": "Événement déjà passé",
"Event cancelled": "Événement annulé",
"Event creation": "Création d'évènement",
@@ -110,10 +126,12 @@
"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",
"Ex: test.mobilizon.org": "Ex : test.mobilizon.org",
"Exclude": "Exclure",
"Explore": "Explorer",
+ "Failed to save admin settings": "Échec de la sauvegarde des paramètres administrateur",
"Featured events": "Événements à la une",
"Features": "Fonctionnalités",
"Find an address": "Trouver une adresse",
@@ -123,12 +141,13 @@
"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",
"Getting location": "Récupération de la position",
+ "Go": "Allons-y",
"Going as {name}": "En tant que {name}",
"Group List": "Liste de groupes",
"Group full name": "Nom complet du groupe",
@@ -138,7 +157,11 @@
"Headline picture": "Image à la une",
"Hide replies": "Masquer les réponses",
"I create an identity": "Je crée une identité",
+ "I don't have a Mobilizon account": "Je n'ai pas de compte Mobilizon",
+ "I have a Mobilizon account": "J'ai un compte Mobilizon",
+ "I have an account on another Mobilizon instance.": "J'ai un compte sur une autre instance Mobilizon.",
"I participate": "Je participe",
+ "I want to allow people to participate without an account.": "Je veux permettre aux gens de participer sans avoir un compte.",
"I want to approve every participation request": "Je veux approuver chaque demande de participation",
"Identity {displayName} created": "Identité {displayName} créée",
"Identity {displayName} deleted": "Identité {displayName} supprimée",
@@ -148,12 +171,17 @@
"Impossible to login, your email or password seems incorrect.": "Impossible de se connecter, votre email ou bien votre mot de passe semble incorrect.",
"In the meantime, please consider that the software is not (yet) finished. More information {onBlog}.": "D'ici là, veuillez considérer que le logiciel n'est pas (encore) fini. Plus d'informations {onBlog}.",
"Installing Mobilizon will allow communities to free themselves from the services of tech giants by creating
their own event platform .": "Installer Mobilizon permettra à des collectifs de s’émanciper des outils des géants du web en créant
leur propre plateforme d’évènements .",
+ "Instance Description": "Description de l'instance ",
+ "Instance Name": "Nom de l'instance",
+ "Instance Terms Source": "Source des conditions d'utilisation de l'instance",
+ "Instance Terms URL": "URL des conditions générales de l'instance",
+ "Instance Terms": "Conditions générales de l'instance",
"Instances": "Instances",
"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",
@@ -163,11 +191,13 @@
"Locality": "Commune",
"Log in": "Se connecter",
"Log out": "Se déconnecter",
- "Login": "Se connecter",
"Login on Mobilizon!": "Se connecter sur Mobilizon !",
+ "Login on {instance}": "Se connecter sur {instance}",
+ "Login": "Se connecter",
"Manage participations": "Gérer les participations",
"Mark as resolved": "Marquer comme résolu",
"Members": "Membres",
+ "Mobilizon is a federated network. You can interact with this event from a different server.": "Mobilizon est un réseau fédéré. Vous pouvez interagir avec cet événement depuis un serveur différent.",
"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.",
"Mobilizon is under development, we will add new features to this site during regular updates, until the release of
version 1 of the software in the first half of 2020 .": "Mobilizon est en cours de développement, nous ajouterons de nouvelles fonctionnalités à ce site lors de mises à jour régulières, jusqu'à la publication de
la version 1 du logiciel au premier semestre 2020 .",
"Mobilizon’s licence": "La licence de Mobilizon",
@@ -199,31 +229,34 @@
"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é)",
"Only alphanumeric characters and underscores are supported.": "Seuls les caractères alphanumériques et les tirets bas sont acceptés.",
"Open": "Ouvert",
"Opened reports": "Signalements ouverts",
- "Organized": "Organisés",
+ "Or": "Ou",
"Organized by {name}": "Organisé par {name}",
+ "Organized": "Organisés",
"Organizer": "Organisateur",
+ "Other software may also support this.": "D'autres logiciels peuvent également supporter cette fonctionnalité.",
"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 not found": "Page non trouvée",
"Participant already was rejected.": "Le participant a déjà été refusé.",
"Participant has already been approved as participant.": "Le participant a déjà été approuvé en tant que participant.",
"Participants": "Participants",
+ "Participate using your email address": "Participer en utilisant votre adresse email",
"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",
"Pending": "En attente",
"Pick an identity": "Choisissez une identité",
@@ -236,6 +269,7 @@
"Post a comment": "Ajouter un commentaire",
"Post a reply": "Envoyer une réponse",
"Postal Code": "Code postal",
+ "Privacy Policy": "Politique de confidentialité",
"Private event": "Événement privé",
"Private feeds": "Flux privés",
"Public RSS/Atom Feed": "Flux RSS/Atom public",
@@ -247,39 +281,46 @@
"Published events": "Événements publiés",
"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",
+ "Redirecting to event…": "Redirection vers l'événement…",
"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 allowed, anyone can register.": "Les inscriptions sont autorisées, n'importe qui peut s'inscrire.",
+ "Registration is closed.": "Les inscriptions sont fermées.",
"Registration is currently closed.": "Les inscriptions sont actuellement fermées.",
"Registrations are restricted by whitelisting.": "Les inscriptions sont restreintes par liste blanche.",
"Reject": "Rejetter",
- "Rejected": "Rejetés",
"Rejected participations": "Participations rejetées",
+ "Rejected": "Rejetés",
"Reopen": "Réouvrir",
"Reply": "Répondre",
- "Report": "Signalement",
"Report this comment": "Signaler ce commentaire",
"Report this event": "Signaler cet évènement",
- "Reported": "Signalée",
- "Reported by": "Signalée par",
+ "Report": "Signalement",
"Reported by someone on {domain}": "Signalé par quelqu'un depuis {domain}",
"Reported by {reporter}": "Signalé par {reporter}",
+ "Reported by": "Signalée par",
"Reported identity": "Identité signalée",
+ "Reported": "Signalée",
"Reports": "Signalements",
"Requests": "Requêtes",
"Resend confirmation email": "Envoyer à nouveau l'email de confirmation",
"Reset my password": "Réinitialiser mon mot de passe",
"Resolved": "Résolu",
- "Save": "Enregistrer",
+ "Resource provided is not an URL": "La ressource fournie n'est pas une URL",
"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 email": "Envoyer un email",
"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 the report": "Envoyer le signalement",
+ "Set an URL to a page with your own terms.": "Entrez une URL vers une page web avec vos propres conditions d'utilisation.",
+ "Settings": "Paramètres",
"Share this event": "Partager l'évènement",
"Show map": "Afficher la carte",
"Show remaining number of places": "Afficher le nombre de places restantes",
@@ -291,21 +332,27 @@
"Status": "Statut",
"Street": "Rue",
"Tentative: Will be confirmed later": "Provisoire : sera confirmé plus tard",
+ "Terms": "Conditions d'utilisation",
+ "The actual number of participants may differ, as this event is hosted on another instance.": "Le nombre réel de participants peut être différent, car cet événement provient d'une autre instance.",
"The content came from another server. Transfer an anonymous copy of the report?": "Le contenu provient d'une autre instance. Transférer une copie anonyme du signalement ?",
"The current identity doesn't have any permission on this event. You should probably change it.": "L'identité actuelle n'a pas de permissions sur cet évènement. Vous devriez probablement en changer.",
"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.",
"The password was successfully changed": "Le mot de passe a été changé avec succès",
"The report will be sent to the moderators of your instance. You can explain why you report this content below.": "Le signalement sera envoyé aux modérateur⋅ices de votre instance. Vous pouvez expliquer pourquoi vous signalez ce contenu ci-dessous.",
"The user account you're trying to login as has not been confirmed yet. Check your email inbox and eventually your spam folder.": "Le compte utilisateur avec lequel vous essayez de vous connectez n'a pas été confirmé. Vérifiez la boite de réception de votre adresse email et éventuellement le dossier des messages indésirables.",
+ "The {default_terms} will be used. They will be translated in the user's language.": "Les {default_terms} seront utilisées. Elles seront traduites dans la langue de l'utilisateur⋅ice.",
"There are {participants} participants.": "Il n'y a qu'un⋅e participant⋅e. | Il y a {participants} participants.",
"These events may interest you": "Ces évènements peuvent vous intéresser",
+ "This Mobilizon instance and this event organizer allows anonymous participations, but requires validation through email confirmation.": "Cette instance Mobilizon et l'organisateur⋅ice de l'événement autorise les participations anonymes, mais requiert une validation à travers une confirmation par email.",
+ "This email is already registered as participant for this event": "Cet email est déjà enregistré comme participant pour cet événement",
+ "This information is saved only on your computer. Click for details": "Cette information est sauvegardée uniquement sur votre appareil. Cliquez pour plus de details",
"This installation (called “instance“) can easily {interconnect}, thanks to {protocol}.": "Cette installation (appelée “instance“) peut facilement {interconnect}, grâce à {protocol}.",
"This instance isn't opened to registrations, but you can register on other instances.": "Cette instance n'autorise pas les inscriptions, mais vous pouvez vous enregistrer sur d'autres instances.",
"This is a demonstration site to test the beta version of Mobilizon.": "Ceci est un site de démonstration permettant de tester la version bêta de Mobilizon.",
@@ -317,11 +364,12 @@
"To confirm, type your identity username \"{preferredUsername}\"": "Pour confirmer, entrez le nom de l’identité « {preferredUsername} »",
"Transfer to {outsideDomain}": "Transférer à {outsideDomain}",
"Type": "Type",
+ "URL": "URL",
"Unfortunately, this instance isn't opened to registrations": "Malheureusement, cette instance n'est pas ouverte aux inscriptions",
"Unfortunately, your participation request was rejected by the organizers.": "Malheureusement, votre demande de participation a été refusée par les organisateur⋅ices.",
- "Unknown": "Inconnu",
"Unknown actor": "Acteur inconnu",
"Unknown error.": "Erreur inconnue.",
+ "Unknown": "Inconnu",
"Unsaved changes": "Modifications non enregistrées",
"Upcoming": "À venir",
"Update event {name}": "Mettre à jour l'évènement {name}",
@@ -339,6 +387,7 @@
"Warning": "Attention",
"We just sent an email to {email}": "Nous venons d'envoyer un email à {email}",
"We want to develop a
digital common , that everyone can make their own, which respects
privacy and activism by design .": "Nous voulons développer un
commun numérique , que tout le monde pourra s’approprier, conçu dans
le respect de la vie privée et de l’action militante .",
+ "We will redirect you to your instance in order to interact with this event": "Nous vous redirigerons vers votre instance pour interagir avec cet événement",
"We won’t change the world from Facebook. The tool we dream of, surveillance capitalism corporations won’t develop it, as they couldn’t profit from it. This is an opportunity to build something better, by taking another approach.": "On ne changera pas le monde depuis Facebook. L’outil dont nous rêvons, les entreprises du capitalisme de surveillance sont incapables de le produire, car elles ne sauraient pas en tirer profit. C’est l’occasion de faire mieux qu’elles, en faisant autrement.",
"Website / URL": "Site web / URL",
"Welcome back {username}!": "Bon retour {username} !",
@@ -351,6 +400,8 @@
"You and one other person are going to this event": "Vous êtes le ou la seule à vous rendre à cet évènement | Vous et une autre personne vous rendez à cet évènement | Vous et {approved} autres personnes vous rendez à cet évènement.",
"You are already a participant of this event.": "Vous participez déjà à cet évènement.",
"You are already logged-in.": "Vous êtes déjà connecté.",
+ "You are participating in this event anonymously but didn't confirm participation": "Vous participez à cet événement anonymement mais vous n'avez pas confirmé votre participation",
+ "You are participating in this event anonymously": "Vous participez à cet événement anonymement",
"You can add tags by hitting the Enter key or by adding a comma": "Vous pouvez ajouter des tags en appuyant sur la touche Entrée ou bien en ajoutant une virgule",
"You can try another search term or drag and drop the marker on the map": "Vous pouvez essayer avec d'autres termes de recherche ou bien glisser et déposer le marqueur sur la carte",
"You can't remove your last identity.": "Vous ne pouvez pas supprimer votre dernière identité.",
@@ -362,25 +413,34 @@
"You have one event tomorrow.": "Vous n'avez pas d'évènement demain | Vous avez un évènement demain. | Vous avez {count} évènements demain",
"You may also ask to {resend_confirmation_email}.": "Vous pouvez aussi demander à {resend_confirmation_email}.",
"You need to login.": "Vous devez vous connecter.",
+ "You will be redirected to the original instance": "Vous allez être redirigé⋅e vers l'instance d'origine",
+ "You wish to participate to the following event": "Vous souhaitez participer à l'événement suivant",
"Your account has been validated": "Votre compte a été validé",
"Your account is being validated": "Votre compte est en cours de validation",
"Your account is nearly ready, {username}": "Votre compte est presque prêt, {username}",
"Your email is not whitelisted, you can't register.": "Votre email n'est pas sur la liste blanche, vous ne pouvez pas vous inscrire.",
+ "Your email will only be used to confirm that you're a real person and send you eventual updates for this event. It will NOT be transmitted to other instances or to the event organizer.": "Votre email sera uniquement utilisé pour confirmer que vous êtes bien une personne réelle et vous envoyer des éventuelles mises à jour pour cet événement. Il ne sera PAS transmis à d'autres instances ou à l'organisateur de l'événement.",
+ "Your federated identity profile@instance": "Votre identité fédérée profil@instance",
+ "Your federated identity": "Votre identité fédérée",
"Your local administrator resumed its policy:": "Votre administrateur local a résumé sa politique ainsi :",
"Your participation has been confirmed": "Votre participation a été confirmée",
"Your participation has been rejected": "Votre participation a été rejettée",
"Your participation has been requested": "Votre participation a été demandée",
+ "Your participation has been validated": "Votre participation a été validée",
+ "Your participation is being validated": "Votre participation est en cours de validation",
"Your participation status has been changed": "Le statut de votre participation a été mis à jour",
"[This comment has been deleted]": "[Ce commentaire a été supprimé]",
"[deleted]": "[supprimé]",
"a decentralised federation protocol": "un protocole de fédération décentralisée",
"as {identity}": "en tant que {identity}",
+ "default Mobilizon terms": "conditions d'utilisation par défaut de Mobilizon.org",
"e.g. 10 Rue Jangot": "par exemple : 10 Rue Jangot",
"firstDayOfWeek": "1",
"iCal Feed": "Flux iCal",
"interconnect with others like it": "s’interconnecter simplement avec d’autres",
"its source code is public": "son code source est public",
"on our blog": "sur notre blog",
+ "profile@instance": "profil@instance",
"resend confirmation email": "réenvoyer l'email de confirmation",
"respect of the fundamental freedoms": "le respect des libertés fondamentales",
"with another identity…": "avec une autre identité…",
diff --git a/js/src/mixins/event.ts b/js/src/mixins/event.ts
index 9d77e3d84..2092b5d45 100644
--- a/js/src/mixins/event.ts
+++ b/js/src/mixins/event.ts
@@ -1,14 +1,82 @@
import { mixins } from 'vue-class-component';
import { Component, Vue } from 'vue-property-decorator';
-import { IEvent, IParticipant } from '@/types/event.model';
-import { DELETE_EVENT } from '@/graphql/event';
+import { IEvent, IParticipant, ParticipantRole } from '@/types/event.model';
+import { DELETE_EVENT, EVENT_PERSON_PARTICIPATION, FETCH_EVENT, LEAVE_EVENT } from '@/graphql/event';
import { RouteName } from '@/router';
-import { IPerson } from '@/types/actor';
+import { IActor, IPerson } from '@/types/actor';
@Component
export default class EventMixin extends mixins(Vue) {
- async openDeleteEventModal(event: IEvent, currentActor: IPerson) {
+ protected async leaveEvent(
+ event: IEvent,
+ actorId: number,
+ token: String|null = null,
+ anonymousParticipationConfirmed: boolean|null = null,
+ ) {
+ try {
+ const { data } = await this.$apollo.mutate<{ leaveEvent: IParticipant }>({
+ mutation: LEAVE_EVENT,
+ variables: {
+ eventId: event.id,
+ actorId,
+ token,
+ },
+ update: (store, { data }) => {
+ if (data == null) return;
+ let participation;
+
+ if (!token) {
+ const participationCachedData = store.readQuery<{ person: IPerson }>({
+ query: EVENT_PERSON_PARTICIPATION,
+ variables: { eventId: event.id, actorId },
+ });
+ if (participationCachedData == null) return;
+ const { person } = participationCachedData;
+ if (person === null) {
+ console.error('Cannot update participation cache, because of null value.');
+ return;
+ }
+ participation = person.participations[0];
+ person.participations = [];
+ store.writeQuery({
+ query: EVENT_PERSON_PARTICIPATION,
+ variables: { eventId: event.id, actorId },
+ data: { person },
+ });
+ }
+
+ const eventCachedData = store.readQuery<{ event: IEvent }>({ query: FETCH_EVENT, variables: { uuid: event.uuid } });
+ if (eventCachedData == null) return;
+ const { event: eventCached } = eventCachedData;
+ if (eventCached === null) {
+ console.error('Cannot update event cache, because of null value.');
+ return;
+ }
+ if (participation && participation.role === ParticipantRole.NOT_APPROVED) {
+ eventCached.participantStats.notApproved = eventCached.participantStats.notApproved - 1;
+ } else if (anonymousParticipationConfirmed === false) {
+ eventCached.participantStats.notConfirmed = eventCached.participantStats.notApproved - 1;
+ } else {
+ eventCached.participantStats.going = eventCached.participantStats.going - 1;
+ eventCached.participantStats.participant = eventCached.participantStats.participant - 1;
+ }
+ store.writeQuery({ query: FETCH_EVENT, variables: { uuid: event.uuid }, data: { event: eventCached } });
+ },
+ });
+ if (data) {
+ this.participationCancelledMessage();
+ }
+ } catch (error) {
+ console.error(error);
+ }
+ }
+
+ private participationCancelledMessage() {
+ this.$notifier.success(this.$t('You have cancelled your participation') as string);
+ }
+
+ protected async openDeleteEventModal(event: IEvent, currentActor: IPerson) {
const participantsLength = event.participantStats.participant;
const prefix = participantsLength
? this.$tc('There are {participants} participants.', event.participantStats.participant, {
diff --git a/js/src/router/admin.ts b/js/src/router/admin.ts
index 3195e8e0c..7e3dd0851 100644
--- a/js/src/router/admin.ts
+++ b/js/src/router/admin.ts
@@ -3,10 +3,12 @@ import Dashboard from '@/views/Admin/Dashboard.vue';
import Follows from '@/views/Admin/Follows.vue';
import Followings from '@/components/Admin/Followings.vue';
import Followers from '@/components/Admin/Followers.vue';
+import Settings from '@/views/Admin/Settings.vue';
export enum AdminRouteName {
DASHBOARD = 'Dashboard',
RELAYS = 'Relays',
+ ADMIN_SETTINGS = 'ADMIN_SETTINGS',
RELAY_FOLLOWINGS = 'Followings',
RELAY_FOLLOWERS = 'Followers',
}
@@ -19,6 +21,13 @@ export const adminRoutes: RouteConfig[] = [
props: true,
meta: { requiredAuth: true },
},
+ {
+ path: '/admin/settings',
+ name: AdminRouteName.ADMIN_SETTINGS,
+ component: Settings,
+ props: true,
+ meta: { requiredAuth: true },
+ },
{
path: '/admin/relays',
name: AdminRouteName.RELAYS,
diff --git a/js/src/router/event.ts b/js/src/router/event.ts
index 944580201..02f711f9c 100644
--- a/js/src/router/event.ts
+++ b/js/src/router/event.ts
@@ -1,7 +1,10 @@
import EventList from '@/views/Event/EventList.vue';
import Location from '@/views/Location.vue';
import { RouteConfig } from 'vue-router';
-import { RouteName } from '@/router/index';
+import ParticipationWithAccount from '@/components/Participation/ParticipationWithAccount.vue';
+import UnloggedParticipation from '@/components/Participation/UnloggedParticipation.vue';
+import ParticipationWithoutAccount from '@/components/Participation/ParticipationWithoutAccount.vue';
+import ConfirmParticipation from '@/components/Participation/ConfirmParticipation.vue';
// tslint:disable:space-in-parens
const participations = () => import(/* webpackChunkName: "participations" */ '@/views/Event/Participants.vue');
@@ -19,6 +22,10 @@ export enum EventRouteName {
EDIT_EVENT = 'EditEvent',
PARTICIPATIONS = 'Participations',
EVENT = 'Event',
+ EVENT_PARTICIPATE_WITH_ACCOUNT = 'EVENT_PARTICIPATE_WITH_ACCOUNT',
+ EVENT_PARTICIPATE_WITHOUT_ACCOUNT = 'EVENT_PARTICIPATE_WITHOUT_ACCOUNT',
+ EVENT_PARTICIPATE_LOGGED_OUT = 'EVENT_PARTICIPATE_LOGGED_OUT',
+ EVENT_PARTICIPATE_CONFIRM = 'EVENT_PARTICIPATE_CONFIRM',
LOCATION = 'Location',
TAG = 'Tag',
}
@@ -75,6 +82,30 @@ export const eventRoutes: RouteConfig[] = [
props: true,
meta: { requiredAuth: false },
},
+ {
+ path: '/events/:uuid/participate',
+ name: EventRouteName.EVENT_PARTICIPATE_LOGGED_OUT,
+ component: UnloggedParticipation,
+ props: true,
+ },
+ {
+ path: '/events/:uuid/participate/with-account',
+ name: EventRouteName.EVENT_PARTICIPATE_WITH_ACCOUNT,
+ component: ParticipationWithAccount,
+ props: true,
+ },
+ {
+ path: '/events/:uuid/participate/without-account',
+ name: EventRouteName.EVENT_PARTICIPATE_WITHOUT_ACCOUNT,
+ component: ParticipationWithoutAccount,
+ props: true,
+ },
+ {
+ path: '/participation/email/confirm/:token',
+ name: EventRouteName.EVENT_PARTICIPATE_CONFIRM,
+ component: ConfirmParticipation,
+ props: true,
+ },
{
path: '/tag/:tag',
name: EventRouteName.TAG,
diff --git a/js/src/router/index.ts b/js/src/router/index.ts
index 807aea98a..fa4d4066c 100644
--- a/js/src/router/index.ts
+++ b/js/src/router/index.ts
@@ -19,6 +19,8 @@ enum GlobalRouteName {
ABOUT = 'About',
PAGE_NOT_FOUND = 'PageNotFound',
SEARCH = 'Search',
+ TERMS = 'TERMS',
+ INTERACT = 'INTERACT',
}
function scrollBehavior(to, from, savedPosition) {
@@ -79,6 +81,18 @@ const router = new Router({
component: () => import(/* webpackChunkName: "about" */ '@/views/About.vue'),
meta: { requiredAuth: false },
},
+ {
+ path: '/terms',
+ name: RouteName.TERMS,
+ component: () => import(/* webpackChunkName: "cookies" */ '@/views/Terms.vue'),
+ meta: { requiredAuth: false },
+ },
+ {
+ path: '/interact',
+ name: RouteName.INTERACT,
+ component: () => import(/* webpackChunkName: "cookies" */ '@/views/Interact.vue'),
+ meta: { requiredAuth: false },
+ },
{
path: '/404',
name: RouteName.PAGE_NOT_FOUND,
diff --git a/js/src/services/AnonymousParticipationStorage.ts b/js/src/services/AnonymousParticipationStorage.ts
new file mode 100644
index 000000000..418fde660
--- /dev/null
+++ b/js/src/services/AnonymousParticipationStorage.ts
@@ -0,0 +1,128 @@
+import { IEvent } from '@/types/event.model';
+
+const ANONYMOUS_PARTICIPATIONS_LOCALSTORAGE_KEY = 'ANONYMOUS_PARTICIPATIONS';
+
+interface IAnonymousParticipation {
+ token: String;
+ expiration: Date;
+ confirmed: boolean;
+}
+
+class AnonymousParticipationNotFoundError extends Error {
+ constructor(message?: string) {
+ super(message);
+ Object.setPrototypeOf(this, new.target.prototype);
+ this.name = AnonymousParticipationNotFoundError.name;
+ }
+}
+
+/**
+ * Fetch existing anonymous participations saved inside this browser
+ */
+function getLocalAnonymousParticipations(): Map
{
+ return jsonToMap(localStorage.getItem(ANONYMOUS_PARTICIPATIONS_LOCALSTORAGE_KEY) || mapToJson(new Map()));
+}
+
+function mapToJson(map): string {
+ return JSON.stringify([...map]);
+}
+function jsonToMap(jsonStr): Map {
+ return new Map(JSON.parse(jsonStr));
+}
+
+/**
+ * Purge participations which expiration has been reached
+ * @param participations Map
+ */
+function purgeOldParticipations(participations: Map): Map {
+ for (const [hashedUUID, { expiration }] of participations) {
+ if (expiration < new Date()) {
+ participations.delete(hashedUUID);
+ }
+ }
+ return participations;
+}
+
+/**
+ * Insert a participation in the list of anonymous participations
+ * @param hashedUUID
+ * @param participation
+ */
+function insertLocalAnonymousParticipation(hashedUUID: String, participation: IAnonymousParticipation) {
+ const participations = purgeOldParticipations(getLocalAnonymousParticipations());
+ participations.set(hashedUUID, participation);
+ localStorage.setItem(ANONYMOUS_PARTICIPATIONS_LOCALSTORAGE_KEY, mapToJson(participations));
+}
+
+function buildExpiration(event: IEvent): Date {
+ const expiration = event.endsOn || event.beginsOn;
+ expiration.setMonth(expiration.getMonth() + 3);
+ expiration.setDate(1);
+ return expiration;
+}
+
+async function addLocalUnconfirmedAnonymousParticipation(event: IEvent, cancellationToken: string) {
+ /**
+ * We hash the event UUID so that we can't know which events an anonymous user goes by looking up it's localstorage
+ */
+ const hashedUUID = await digestMessage(event.uuid);
+
+ /**
+ * We round expiration to first day of next 3 months so that it's difficult to find event from date
+ */
+ const expiration = buildExpiration(event);
+ insertLocalAnonymousParticipation(hashedUUID, { token: cancellationToken, expiration, confirmed: false });
+}
+
+async function confirmLocalAnonymousParticipation(uuid: String) {
+ const participations = purgeOldParticipations(getLocalAnonymousParticipations());
+ const hashedUUID = await digestMessage(uuid);
+ const participation = participations.get(hashedUUID);
+ if (participation) {
+ participation.confirmed = true;
+ participations.set(hashedUUID, participation);
+ localStorage.setItem(ANONYMOUS_PARTICIPATIONS_LOCALSTORAGE_KEY, mapToJson(participations));
+ }
+}
+
+async function isParticipatingInThisEvent(eventUUID: String): Promise {
+ const participation = await getParticipation(eventUUID);
+ return participation !== undefined && participation.confirmed;
+}
+
+async function getParticipation(eventUUID: String): Promise {
+ const hashedUUID = await digestMessage(eventUUID);
+ const participation = purgeOldParticipations(getLocalAnonymousParticipations()).get(hashedUUID);
+ if (participation) {
+ return participation;
+ }
+ throw new AnonymousParticipationNotFoundError('Participation not found');
+}
+
+async function getLeaveTokenForParticipation(eventUUID: String): Promise {
+ return (await getParticipation(eventUUID)).token;
+}
+
+async function removeAnonymousParticipation(eventUUID: String): Promise {
+ const hashedUUID = await digestMessage(eventUUID);
+ const participations = purgeOldParticipations(getLocalAnonymousParticipations());
+ participations.delete(hashedUUID);
+ localStorage.setItem(ANONYMOUS_PARTICIPATIONS_LOCALSTORAGE_KEY, mapToJson(participations));
+}
+
+async function digestMessage(message): Promise {
+ const encoder = new TextEncoder();
+ const data = encoder.encode(message);
+ const hashBuffer = await crypto.subtle.digest('SHA-256', data);
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
+ return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
+}
+
+export {
+ addLocalUnconfirmedAnonymousParticipation,
+ confirmLocalAnonymousParticipation,
+ getLeaveTokenForParticipation,
+ isParticipatingInThisEvent,
+ removeAnonymousParticipation,
+ AnonymousParticipationNotFoundError,
+};
diff --git a/js/src/types/admin.model.ts b/js/src/types/admin.model.ts
index 226b2fb59..e175db63f 100644
--- a/js/src/types/admin.model.ts
+++ b/js/src/types/admin.model.ts
@@ -7,3 +7,18 @@ export interface IDashboard {
numberOfComments: number;
numberOfReports: number;
}
+
+export enum InstanceTermsType {
+ DEFAULT = 'DEFAULT',
+ URL = 'URL',
+ CUSTOM = 'CUSTOM',
+}
+
+export interface IAdminSettings {
+ instanceName: string;
+ instanceDescription: string;
+ instanceTerms: string;
+ instanceTermsType: InstanceTermsType;
+ instanceTermsUrl: string|null;
+ registrationsOpen: boolean;
+}
diff --git a/js/src/types/config.model.ts b/js/src/types/config.model.ts
index e99a38526..4b5d55ca4 100644
--- a/js/src/types/config.model.ts
+++ b/js/src/types/config.model.ts
@@ -1,3 +1,5 @@
+import { InstanceTermsType } from '@/types/admin.model';
+
export interface IConfig {
name: string;
description: string;
@@ -11,6 +13,33 @@ export interface IConfig {
longitude: number;
accuracyRadius: number;
};
+ anonymous: {
+ participation: {
+ allowed: boolean;
+ validation: {
+ email: {
+ enabled: boolean;
+ confirmationRequired: boolean;
+ },
+ captcha: {
+ enabled: boolean;
+ },
+ }
+ }
+ eventCreation: {
+ allowed: boolean;
+ validation: {
+ email: {
+ enabled: boolean;
+ confirmationRequired: boolean;
+ },
+ captcha: {
+ enabled: boolean,
+ },
+ }
+ }
+ actorId,
+ };
maps: {
tiles: {
endpoint: string;
@@ -21,4 +50,9 @@ export interface IConfig {
provider: string;
autocomplete: boolean;
};
+ terms: {
+ bodyHtml: string;
+ type: InstanceTermsType;
+ url: string;
+ };
}
diff --git a/js/src/types/event.model.ts b/js/src/types/event.model.ts
index 7ee8238af..c84e7e61b 100644
--- a/js/src/types/event.model.ts
+++ b/js/src/types/event.model.ts
@@ -31,6 +31,7 @@ export enum EventVisibilityJoinOptions {
export enum ParticipantRole {
NOT_APPROVED = 'NOT_APPROVED',
+ NOT_CONFIRMED = 'NOT_CONFIRMED',
REJECTED = 'REJECTED',
PARTICIPANT = 'PARTICIPANT',
MODERATOR = 'MODERATOR',
@@ -58,6 +59,7 @@ export interface IParticipant {
role: ParticipantRole;
actor: IActor;
event: IEvent;
+ metadata: { cancellationToken?: string };
}
export class Participant implements IParticipant {
@@ -65,6 +67,7 @@ export class Participant implements IParticipant {
event!: IEvent;
actor!: IActor;
role: ParticipantRole = ParticipantRole.NOT_APPROVED;
+ metadata = {};
constructor(hash?: IParticipant) {
if (!hash) return;
@@ -73,6 +76,7 @@ export class Participant implements IParticipant {
this.event = new EventModel(hash.event);
this.actor = new Actor(hash.actor);
this.role = hash.role;
+ this.metadata = hash.metadata;
}
}
@@ -96,6 +100,7 @@ export enum CommentModeration {
export interface IEventParticipantStats {
notApproved: number;
+ notConfirmed: number;
rejected: number;
participant: number;
creator: number;
@@ -146,6 +151,7 @@ export interface IEventOptions {
maximumAttendeeCapacity: number;
remainingAttendeeCapacity: number;
showRemainingAttendeeCapacity: boolean;
+ anonymousParticipation: boolean;
offers: IOffer[];
participationConditions: IParticipationCondition[];
attendees: string[];
@@ -160,6 +166,7 @@ export class EventOptions implements IEventOptions {
maximumAttendeeCapacity = 0;
remainingAttendeeCapacity = 0;
showRemainingAttendeeCapacity = false;
+ anonymousParticipation = false;
offers: IOffer[] = [];
participationConditions: IParticipationCondition[] = [];
attendees: string[] = [];
@@ -197,7 +204,7 @@ export class EventModel implements IEvent {
publishAt = new Date();
- participantStats = { notApproved: 0, rejected: 0, participant: 0, moderator: 0, administrator: 0, creator: 0, going: 0 };
+ participantStats = { notApproved: 0, notConfirmed: 0, rejected: 0, participant: 0, moderator: 0, administrator: 0, creator: 0, going: 0 };
participants: IParticipant[] = [];
relatedEvents: IEvent[] = [];
diff --git a/js/src/variables.scss b/js/src/variables.scss
index 6493dc980..0a3d71bcb 100644
--- a/js/src/variables.scss
+++ b/js/src/variables.scss
@@ -32,3 +32,11 @@ $navbar-height: 4rem;
// Footer
$footer-padding: 3rem 1.5rem 4rem;
$footer-background-color: $primary;
+
+$body-background-color: #f8f7fa;
+$fullhd-enabled: false;
+$hero-body-padding-medium: 6rem 1.5rem;
+
+main > .container {
+ background: $white;
+}
\ No newline at end of file
diff --git a/js/src/views/Account/MyAccount.vue b/js/src/views/Account/MyAccount.vue
index b2e9d3c28..80db19f39 100644
--- a/js/src/views/Account/MyAccount.vue
+++ b/js/src/views/Account/MyAccount.vue
@@ -1,5 +1,5 @@
-
+
diff --git a/js/src/views/Account/Register.vue b/js/src/views/Account/Register.vue
index 468d9c262..fc8b10d4e 100644
--- a/js/src/views/Account/Register.vue
+++ b/js/src/views/Account/Register.vue
@@ -1,5 +1,5 @@
-
+
diff --git a/js/src/views/Admin/Dashboard.vue b/js/src/views/Admin/Dashboard.vue
index 3c1e84fcb..57c6228a7 100644
--- a/js/src/views/Admin/Dashboard.vue
+++ b/js/src/views/Admin/Dashboard.vue
@@ -1,5 +1,5 @@
-
+
{{ $t('Administration') }}
@@ -45,6 +45,13 @@
+
+
+
+ {{ $t('Settings') }}
+
+
+
diff --git a/js/src/views/Admin/Follows.vue b/js/src/views/Admin/Follows.vue
index fc1aa000a..9c04e0b13 100644
--- a/js/src/views/Admin/Follows.vue
+++ b/js/src/views/Admin/Follows.vue
@@ -1,5 +1,5 @@
-
+
\ No newline at end of file
diff --git a/js/src/views/Event/Edit.vue b/js/src/views/Event/Edit.vue
index 8e8ddccc4..458fc2f6a 100644
--- a/js/src/views/Event/Edit.vue
+++ b/js/src/views/Event/Edit.vue
@@ -8,117 +8,127 @@
{{ $t('Update event {name}', { name: event.title }) }}
-
+
+
+
+ {{ $t('Tentative: Will be confirmed later') }}
+
+
+
+ {{ $t('Confirmed: Will happen') }}
+
+
+
+ {{ $t("Cancelled: Won't happen") }}
+
+
+