Fix settings menu
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
parent
dd806896d1
commit
6797075461
|
@ -17,6 +17,10 @@ a {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nav.breadcrumb ul li a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
input.input {
|
input.input {
|
||||||
border-color: $input-border-color !important;
|
border-color: $input-border-color !important;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,24 +1,25 @@
|
||||||
<template>
|
<template>
|
||||||
<li class="setting-menu-item" :class="{ active: isActive }">
|
<li class="setting-menu-item" :class="{ active: isActive }">
|
||||||
<router-link v-if="menuItem.to" :to="menuItem.to">
|
<router-link v-if="to" :to="to">
|
||||||
<span>{{ menuItem.title }}</span>
|
<span>{{ title }}</span>
|
||||||
</router-link>
|
</router-link>
|
||||||
<span v-else>{{ menuItem.title }}</span>
|
<span v-else>{{ title }}</span>
|
||||||
</li>
|
</li>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Component, Prop, Vue } from "vue-property-decorator";
|
import { Component, Prop, Vue } from "vue-property-decorator";
|
||||||
import { ISettingMenuSection } from "@/types/setting-menu.model";
|
import { Route } from "vue-router";
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
export default class SettingMenuItem extends Vue {
|
export default class SettingMenuItem extends Vue {
|
||||||
@Prop({ required: true, type: Object }) menuItem!: ISettingMenuSection;
|
@Prop({ required: false, type: String }) title!: string;
|
||||||
|
@Prop({ required: true, type: Object }) to!: Route;
|
||||||
|
|
||||||
get isActive() {
|
get isActive() {
|
||||||
if (!this.menuItem.to) return false;
|
if (!this.to) return false;
|
||||||
if (this.menuItem.to.name === this.$route.name) {
|
if (this.to.name === this.$route.name) {
|
||||||
if (this.menuItem.to.params) {
|
if (this.to.params) {
|
||||||
return this.menuItem.to.params.identityName === this.$route.params.identityName;
|
return this.to.params.identityName === this.$route.params.identityName;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,27 +1,35 @@
|
||||||
<template>
|
<template>
|
||||||
<li :class="{ active: sectionActive }">
|
<li :class="{ active: sectionActive }">
|
||||||
<router-link v-if="menuSection.to" :to="menuSection.to">{{ menuSection.title }}</router-link>
|
<router-link v-if="to" :to="to">{{ title }}</router-link>
|
||||||
<b v-else>{{ menuSection.title }}</b>
|
<b v-else>{{ title }}</b>
|
||||||
<ul>
|
<ul>
|
||||||
<setting-menu-item :menu-item="item" v-for="item in menuSection.items" :key="item.title" />
|
<slot></slot>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Component, Prop, Vue } from "vue-property-decorator";
|
import { Component, Prop, Vue } from "vue-property-decorator";
|
||||||
import { ISettingMenuSection } from "@/types/setting-menu.model";
|
|
||||||
import SettingMenuItem from "@/components/Settings/SettingMenuItem.vue";
|
import SettingMenuItem from "@/components/Settings/SettingMenuItem.vue";
|
||||||
|
import { Route } from "vue-router";
|
||||||
@Component({
|
@Component({
|
||||||
components: { SettingMenuItem },
|
components: { SettingMenuItem },
|
||||||
})
|
})
|
||||||
export default class SettingMenuSection extends Vue {
|
export default class SettingMenuSection extends Vue {
|
||||||
@Prop({ required: true, type: Object }) menuSection!: ISettingMenuSection;
|
@Prop({ required: false, type: String }) title!: string;
|
||||||
|
@Prop({ required: true, type: Object }) to!: Route;
|
||||||
|
|
||||||
get sectionActive(): boolean | undefined {
|
get sectionActive() {
|
||||||
return (
|
if (this.$slots.default) {
|
||||||
this.menuSection.items &&
|
return this.$slots.default.some(
|
||||||
this.menuSection.items.some(({ to }) => to && to.name === this.$route.name)
|
({
|
||||||
);
|
componentOptions: {
|
||||||
|
// @ts-ignore
|
||||||
|
propsData: { to },
|
||||||
|
},
|
||||||
|
}) => to && to.name === this.$route.name
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,27 +1,88 @@
|
||||||
<template>
|
<template>
|
||||||
<aside>
|
<aside>
|
||||||
<ul>
|
<ul>
|
||||||
|
<SettingMenuSection :title="$t('Account')" :to="{ name: RouteName.ACCOUNT_SETTINGS }">
|
||||||
|
<SettingMenuItem
|
||||||
|
:title="this.$t('General')"
|
||||||
|
:to="{ name: RouteName.ACCOUNT_SETTINGS_GENERAL }"
|
||||||
|
/>
|
||||||
|
<SettingMenuItem :title="$t('Preferences')" :to="{ name: RouteName.PREFERENCES }" />
|
||||||
|
<SettingMenuItem
|
||||||
|
:title="this.$t('Email notifications')"
|
||||||
|
:to="{ name: RouteName.NOTIFICATIONS }"
|
||||||
|
/>
|
||||||
|
</SettingMenuSection>
|
||||||
|
<SettingMenuSection :title="$t('Profiles')" :to="{ name: RouteName.IDENTITIES }">
|
||||||
|
<SettingMenuItem
|
||||||
|
v-for="profile in identities"
|
||||||
|
:key="profile.preferredUsername"
|
||||||
|
:title="profile.preferredUsername"
|
||||||
|
:to="{
|
||||||
|
name: RouteName.UPDATE_IDENTITY,
|
||||||
|
params: { identityName: profile.preferredUsername },
|
||||||
|
}"
|
||||||
|
/>
|
||||||
|
<SettingMenuItem :title="$t('New profile')" :to="{ name: RouteName.CREATE_IDENTITY }" />
|
||||||
|
</SettingMenuSection>
|
||||||
<SettingMenuSection
|
<SettingMenuSection
|
||||||
v-for="section in menuValue"
|
v-if="
|
||||||
:key="section.title"
|
[ICurrentUserRole.MODERATOR, ICurrentUserRole.ADMINISTRATOR].includes(
|
||||||
:menu-section="section"
|
this.currentUser.role
|
||||||
/>
|
)
|
||||||
|
"
|
||||||
|
:title="$t('Moderation')"
|
||||||
|
:to="{ name: RouteName.MODERATION }"
|
||||||
|
>
|
||||||
|
<SettingMenuItem :title="$t('Reports')" :to="{ name: RouteName.REPORTS }" />
|
||||||
|
<SettingMenuItem :title="$t('Moderation log')" :to="{ name: RouteName.REPORT_LOGS }" />
|
||||||
|
<SettingMenuItem :title="$t('Users')" :to="{ name: RouteName.USERS }" />
|
||||||
|
<SettingMenuItem :title="$t('Profiles')" :to="{ name: RouteName.PROFILES }" />
|
||||||
|
</SettingMenuSection>
|
||||||
|
<SettingMenuSection
|
||||||
|
v-if="this.currentUser.role == ICurrentUserRole.ADMINISTRATOR"
|
||||||
|
:title="$t('Admin')"
|
||||||
|
:to="{ name: RouteName.ADMIN }"
|
||||||
|
>
|
||||||
|
<SettingMenuItem :title="$t('Dashboard')" :to="{ name: RouteName.ADMIN_DASHBOARD }" />
|
||||||
|
<SettingMenuItem
|
||||||
|
:title="$t('Instance settings')"
|
||||||
|
:to="{ name: RouteName.ADMIN_SETTINGS }"
|
||||||
|
/>
|
||||||
|
<SettingMenuItem :title="$t('Federation')" :to="{ name: RouteName.RELAYS }" />
|
||||||
|
</SettingMenuSection>
|
||||||
</ul>
|
</ul>
|
||||||
</aside>
|
</aside>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Component, Prop, Vue } from "vue-property-decorator";
|
import { Component, Prop, Vue } from "vue-property-decorator";
|
||||||
import SettingMenuSection from "@/components/Settings/SettingMenuSection.vue";
|
import SettingMenuSection from "./SettingMenuSection.vue";
|
||||||
import { ISettingMenuSection } from "@/types/setting-menu.model";
|
import SettingMenuItem from "./SettingMenuItem.vue";
|
||||||
|
import { IDENTITIES } from "../../graphql/actor";
|
||||||
|
import { IPerson, Person } from "../../types/actor";
|
||||||
|
import { CURRENT_USER_CLIENT } from "../../graphql/user";
|
||||||
|
import { ICurrentUser, ICurrentUserRole } from "../../types/current-user.model";
|
||||||
|
|
||||||
|
import RouteName from "../../router/name";
|
||||||
@Component({
|
@Component({
|
||||||
components: { SettingMenuSection },
|
components: { SettingMenuSection, SettingMenuItem },
|
||||||
|
apollo: {
|
||||||
|
identities: {
|
||||||
|
query: IDENTITIES,
|
||||||
|
update: (data) => data.identities.map((identity: IPerson) => new Person(identity)),
|
||||||
|
},
|
||||||
|
currentUser: CURRENT_USER_CLIENT,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
export default class SettingsMenu extends Vue {
|
export default class SettingsMenu extends Vue {
|
||||||
@Prop({ required: true, type: Array }) menu!: ISettingMenuSection[];
|
profiles = [];
|
||||||
|
|
||||||
get menuValue() {
|
currentUser!: ICurrentUser;
|
||||||
return this.menu;
|
|
||||||
}
|
identities!: IPerson[];
|
||||||
|
|
||||||
|
ICurrentUserRole = ICurrentUserRole;
|
||||||
|
|
||||||
|
RouteName = RouteName;
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
|
@ -106,6 +106,7 @@ export const USER_SETTINGS_FRAGMENT = gql`
|
||||||
export const USER_SETTINGS = gql`
|
export const USER_SETTINGS = gql`
|
||||||
query UserSetting {
|
query UserSetting {
|
||||||
loggedUser {
|
loggedUser {
|
||||||
|
id
|
||||||
locale
|
locale
|
||||||
settings {
|
settings {
|
||||||
...UserSettingFragment
|
...UserSettingFragment
|
||||||
|
|
|
@ -695,5 +695,6 @@
|
||||||
"contact uninformed": "contact uninformed",
|
"contact uninformed": "contact uninformed",
|
||||||
"Can be an email or a link, or just plain text.": "Can be an email or a link, or just plain text.",
|
"Can be an email or a link, or just plain text.": "Can be an email or a link, or just plain text.",
|
||||||
"No profiles found": "No profiles found",
|
"No profiles found": "No profiles found",
|
||||||
"URL copied to clipboard": "URL copied to clipboard"
|
"URL copied to clipboard": "URL copied to clipboard",
|
||||||
|
"Report #{reportNumber}": "Report #{reportNumber}"
|
||||||
}
|
}
|
||||||
|
|
|
@ -695,5 +695,6 @@
|
||||||
"A place for your code of conduct, rules or guidelines. You can use HTML tags.": "Une section appropriée pour votre code de conduite, règles ou lignes directrices. Vous pouvez utiliser des balises HTML.",
|
"A place for your code of conduct, rules or guidelines. You can use HTML tags.": "Une section appropriée pour votre code de conduite, règles ou lignes directrices. Vous pouvez utiliser des balises HTML.",
|
||||||
"contact uninformed": "contact non renseigné",
|
"contact uninformed": "contact non renseigné",
|
||||||
"Can be an email or a link, or just plain text.": "Peut être une adresse email ou bien un lien, ou alors du simple texte brut.",
|
"Can be an email or a link, or just plain text.": "Peut être une adresse email ou bien un lien, ou alors du simple texte brut.",
|
||||||
"URL copied to clipboard": "URL copiée dans le presse-papiers"
|
"URL copied to clipboard": "URL copiée dans le presse-papiers",
|
||||||
|
"Report #{reportNumber}": "Signalement #{reportNumber}"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
import { Route } from "vue-router";
|
|
||||||
|
|
||||||
export interface ISettingMenuSection {
|
|
||||||
title: string;
|
|
||||||
to: Route;
|
|
||||||
items?: ISettingMenuSection[];
|
|
||||||
parents?: ISettingMenuSection[];
|
|
||||||
}
|
|
|
@ -1,66 +1,91 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="root" v-if="identity">
|
<div>
|
||||||
<h1 class="title">
|
<nav class="breadcrumb" aria-label="breadcrumbs">
|
||||||
<span v-if="isUpdate">{{ identity.displayName() }}</span>
|
<ul>
|
||||||
<span v-else>{{ $t("I create an identity") }}</span>
|
<li>
|
||||||
</h1>
|
<router-link :to="{ name: RouteName.IDENTITIES }">{{ $t("Profiles") }}</router-link>
|
||||||
|
</li>
|
||||||
|
<li class="is-active" v-if="isUpdate && identity">
|
||||||
|
<router-link
|
||||||
|
:to="{
|
||||||
|
name: RouteName.UPDATE_IDENTITY,
|
||||||
|
params: { identityName: identity.preferredUsername },
|
||||||
|
}"
|
||||||
|
>{{ identity.name }}</router-link
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
<li class="is-active" v-else>
|
||||||
|
<router-link :to="{ name: RouteName.CREATE_IDENTITY }">{{
|
||||||
|
$t("New profile")
|
||||||
|
}}</router-link>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
<div class="root" v-if="identity">
|
||||||
|
<h1 class="title">
|
||||||
|
<span v-if="isUpdate">{{ identity.displayName() }}</span>
|
||||||
|
<span v-else>{{ $t("I create an identity") }}</span>
|
||||||
|
</h1>
|
||||||
|
|
||||||
<picture-upload v-model="avatarFile" class="picture-upload" />
|
<picture-upload v-model="avatarFile" class="picture-upload" />
|
||||||
|
|
||||||
<b-field horizontal :label="$t('Display name')">
|
<b-field horizontal :label="$t('Display name')">
|
||||||
<b-input
|
|
||||||
aria-required="true"
|
|
||||||
required
|
|
||||||
v-model="identity.name"
|
|
||||||
@input="autoUpdateUsername($event)"
|
|
||||||
/>
|
|
||||||
</b-field>
|
|
||||||
|
|
||||||
<b-field
|
|
||||||
horizontal
|
|
||||||
custom-class="username-field"
|
|
||||||
expanded
|
|
||||||
:label="$t('Username')"
|
|
||||||
:message="message"
|
|
||||||
>
|
|
||||||
<b-field expanded>
|
|
||||||
<b-input
|
<b-input
|
||||||
aria-required="true"
|
aria-required="true"
|
||||||
required
|
required
|
||||||
v-model="identity.preferredUsername"
|
v-model="identity.name"
|
||||||
:disabled="isUpdate"
|
@input="autoUpdateUsername($event)"
|
||||||
:use-html5-validation="!isUpdate"
|
|
||||||
pattern="[a-z0-9_]+"
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<p class="control">
|
|
||||||
<span class="button is-static">@{{ getInstanceHost }}</span>
|
|
||||||
</p>
|
|
||||||
</b-field>
|
</b-field>
|
||||||
</b-field>
|
|
||||||
|
|
||||||
<b-field horizontal :label="$t('Description')">
|
<b-field
|
||||||
<b-input type="textarea" aria-required="false" v-model="identity.summary" />
|
horizontal
|
||||||
</b-field>
|
custom-class="username-field"
|
||||||
|
expanded
|
||||||
|
:label="$t('Username')"
|
||||||
|
:message="message"
|
||||||
|
>
|
||||||
|
<b-field expanded>
|
||||||
|
<b-input
|
||||||
|
aria-required="true"
|
||||||
|
required
|
||||||
|
v-model="identity.preferredUsername"
|
||||||
|
:disabled="isUpdate"
|
||||||
|
:use-html5-validation="!isUpdate"
|
||||||
|
pattern="[a-z0-9_]+"
|
||||||
|
/>
|
||||||
|
|
||||||
<b-notification
|
<p class="control">
|
||||||
type="is-danger"
|
<span class="button is-static">@{{ getInstanceHost }}</span>
|
||||||
has-icon
|
</p>
|
||||||
aria-close-label="Close notification"
|
</b-field>
|
||||||
role="alert"
|
</b-field>
|
||||||
:key="error"
|
|
||||||
v-for="error in errors"
|
|
||||||
>{{ error }}</b-notification
|
|
||||||
>
|
|
||||||
|
|
||||||
<b-field class="submit">
|
<b-field horizontal :label="$t('Description')">
|
||||||
<div class="control">
|
<b-input type="textarea" aria-required="false" v-model="identity.summary" />
|
||||||
<button type="button" class="button is-primary" @click="submit()">{{ $t("Save") }}</button>
|
</b-field>
|
||||||
|
|
||||||
|
<b-notification
|
||||||
|
type="is-danger"
|
||||||
|
has-icon
|
||||||
|
aria-close-label="Close notification"
|
||||||
|
role="alert"
|
||||||
|
:key="error"
|
||||||
|
v-for="error in errors"
|
||||||
|
>{{ error }}</b-notification
|
||||||
|
>
|
||||||
|
|
||||||
|
<b-field class="submit">
|
||||||
|
<div class="control">
|
||||||
|
<button type="button" class="button is-primary" @click="submit()">
|
||||||
|
{{ $t("Save") }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</b-field>
|
||||||
|
|
||||||
|
<div class="delete-identity" v-if="isUpdate">
|
||||||
|
<span @click="openDeleteIdentityConfirmation()">{{ $t("Delete this identity") }}</span>
|
||||||
</div>
|
</div>
|
||||||
</b-field>
|
|
||||||
|
|
||||||
<div class="delete-identity" v-if="isUpdate">
|
|
||||||
<span @click="openDeleteIdentityConfirmation()">{{ $t("Delete this identity") }}</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -148,6 +173,8 @@ export default class EditIdentity extends mixins(identityEditionMixin) {
|
||||||
|
|
||||||
private currentActor: IPerson | null = null;
|
private currentActor: IPerson | null = null;
|
||||||
|
|
||||||
|
RouteName = RouteName;
|
||||||
|
|
||||||
get message() {
|
get message() {
|
||||||
if (this.isUpdate) return null;
|
if (this.isUpdate) return null;
|
||||||
return this.$t("Only alphanumeric characters and underscores are supported.");
|
return this.$t("Only alphanumeric characters and underscores are supported.");
|
||||||
|
|
|
@ -1,53 +1,65 @@
|
||||||
<template>
|
<template>
|
||||||
<section>
|
<div>
|
||||||
<h1 class="title">{{ $t("Administration") }}</h1>
|
<nav class="breadcrumb" aria-label="breadcrumbs">
|
||||||
<div class="tile is-ancestor" v-if="dashboard">
|
<ul>
|
||||||
<div class="tile is-vertical">
|
<li>
|
||||||
<div class="tile">
|
<router-link :to="{ name: RouteName.ADMIN }">{{ $t("Admin") }}</router-link>
|
||||||
<div class="tile is-parent is-vertical is-6">
|
</li>
|
||||||
<article class="tile is-child box">
|
<li class="is-active">
|
||||||
<p class="dashboard-number">{{ dashboard.numberOfEvents }}</p>
|
<router-link :to="{ name: RouteName.ADMIN_DASHBOARD }">{{ $t("Dashboard") }}</router-link>
|
||||||
<p>{{ $t("Published events") }}</p>
|
</li>
|
||||||
</article>
|
</ul>
|
||||||
<article class="tile is-child box">
|
</nav>
|
||||||
<p class="dashboard-number">{{ dashboard.numberOfComments }}</p>
|
<section>
|
||||||
<p>{{ $t("Comments") }}</p>
|
<h1 class="title">{{ $t("Administration") }}</h1>
|
||||||
</article>
|
<div class="tile is-ancestor" v-if="dashboard">
|
||||||
|
<div class="tile is-vertical">
|
||||||
|
<div class="tile">
|
||||||
|
<div class="tile is-parent is-vertical is-6">
|
||||||
|
<article class="tile is-child box">
|
||||||
|
<p class="dashboard-number">{{ dashboard.numberOfEvents }}</p>
|
||||||
|
<p>{{ $t("Published events") }}</p>
|
||||||
|
</article>
|
||||||
|
<article class="tile is-child box">
|
||||||
|
<p class="dashboard-number">{{ dashboard.numberOfComments }}</p>
|
||||||
|
<p>{{ $t("Comments") }}</p>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
<div class="tile is-parent is-vertical">
|
||||||
|
<article class="tile is-child box">
|
||||||
|
<router-link :to="{ name: RouteName.USERS }">
|
||||||
|
<p class="dashboard-number">{{ dashboard.numberOfUsers }}</p>
|
||||||
|
<p>{{ $t("Users") }}</p>
|
||||||
|
</router-link>
|
||||||
|
</article>
|
||||||
|
<article class="tile is-child box">
|
||||||
|
<router-link :to="{ name: RouteName.REPORTS }">
|
||||||
|
<p class="dashboard-number">{{ dashboard.numberOfReports }}</p>
|
||||||
|
<p>{{ $t("Opened reports") }}</p>
|
||||||
|
</router-link>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="tile is-parent is-vertical">
|
<div class="tile is-parent" v-if="dashboard.lastPublicEventPublished">
|
||||||
<article class="tile is-child box">
|
<router-link
|
||||||
<router-link :to="{ name: RouteName.USERS }">
|
:to="{
|
||||||
<p class="dashboard-number">{{ dashboard.numberOfUsers }}</p>
|
name: RouteName.EVENT,
|
||||||
<p>{{ $t("Users") }}</p>
|
params: { uuid: dashboard.lastPublicEventPublished.uuid },
|
||||||
</router-link>
|
}"
|
||||||
</article>
|
>
|
||||||
<article class="tile is-child box">
|
<article class="tile is-child box">
|
||||||
<router-link :to="{ name: RouteName.REPORTS }">
|
<p class="dashboard-number">{{ $t("Last published event") }}</p>
|
||||||
<p class="dashboard-number">{{ dashboard.numberOfReports }}</p>
|
<p class="subtitle">{{ dashboard.lastPublicEventPublished.title }}</p>
|
||||||
<p>{{ $t("Opened reports") }}</p>
|
<figure class="image is-4by3" v-if="dashboard.lastPublicEventPublished.picture">
|
||||||
</router-link>
|
<img :src="dashboard.lastPublicEventPublished.picture.url" />
|
||||||
</article>
|
</figure>
|
||||||
|
</article>
|
||||||
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="tile is-parent" v-if="dashboard.lastPublicEventPublished">
|
|
||||||
<router-link
|
|
||||||
:to="{
|
|
||||||
name: RouteName.EVENT,
|
|
||||||
params: { uuid: dashboard.lastPublicEventPublished.uuid },
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<article class="tile is-child box">
|
|
||||||
<p class="dashboard-number">{{ $t("Last published event") }}</p>
|
|
||||||
<p class="subtitle">{{ dashboard.lastPublicEventPublished.title }}</p>
|
|
||||||
<figure class="image is-4by3" v-if="dashboard.lastPublicEventPublished.picture">
|
|
||||||
<img :src="dashboard.lastPublicEventPublished.picture.url" />
|
|
||||||
</figure>
|
|
||||||
</article>
|
|
||||||
</router-link>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</section>
|
||||||
</section>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Component, Vue } from "vue-property-decorator";
|
import { Component, Vue } from "vue-property-decorator";
|
||||||
|
|
|
@ -1,40 +1,60 @@
|
||||||
<template>
|
<template>
|
||||||
<section>
|
<div>
|
||||||
<h1 class="title">{{ $t("Instances") }}</h1>
|
<nav class="breadcrumb" aria-label="breadcrumbs">
|
||||||
<div class="tabs is-boxed">
|
|
||||||
<ul>
|
<ul>
|
||||||
<router-link
|
<li>
|
||||||
tag="li"
|
<router-link :to="{ name: RouteName.ADMIN }">{{ $t("Admin") }}</router-link>
|
||||||
active-class="is-active"
|
</li>
|
||||||
:to="{ name: RouteName.RELAY_FOLLOWINGS }"
|
<li>
|
||||||
exact
|
<router-link :to="{ name: RouteName.RELAYS }">{{ $t("Federation") }}</router-link>
|
||||||
>
|
</li>
|
||||||
<a>
|
<li class="is-active" v-if="$route.name == RouteName.RELAY_FOLLOWINGS">
|
||||||
<b-icon icon="inbox-arrow-down"></b-icon>
|
<router-link :to="{ name: RouteName.RELAY_FOLLOWINGS }">{{
|
||||||
<span>
|
$t("Followings")
|
||||||
{{ $t("Followings") }}
|
}}</router-link>
|
||||||
<b-tag rounded>{{ relayFollowings.total }}</b-tag>
|
</li>
|
||||||
</span>
|
<li class="is-active" v-if="$route.name == RouteName.RELAY_FOLLOWERS">
|
||||||
</a>
|
<router-link :to="{ name: RouteName.RELAY_FOLLOWERS }">{{ $t("Followers") }}</router-link>
|
||||||
</router-link>
|
</li>
|
||||||
<router-link
|
|
||||||
tag="li"
|
|
||||||
active-class="is-active"
|
|
||||||
:to="{ name: RouteName.RELAY_FOLLOWERS }"
|
|
||||||
exact
|
|
||||||
>
|
|
||||||
<a>
|
|
||||||
<b-icon icon="inbox-arrow-up"></b-icon>
|
|
||||||
<span>
|
|
||||||
{{ $t("Followers") }}
|
|
||||||
<b-tag rounded>{{ relayFollowers.total }}</b-tag>
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
</router-link>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</nav>
|
||||||
<router-view></router-view>
|
<section>
|
||||||
</section>
|
<h1 class="title">{{ $t("Instances") }}</h1>
|
||||||
|
<div class="tabs is-boxed">
|
||||||
|
<ul>
|
||||||
|
<router-link
|
||||||
|
tag="li"
|
||||||
|
active-class="is-active"
|
||||||
|
:to="{ name: RouteName.RELAY_FOLLOWINGS }"
|
||||||
|
exact
|
||||||
|
>
|
||||||
|
<a>
|
||||||
|
<b-icon icon="inbox-arrow-down"></b-icon>
|
||||||
|
<span>
|
||||||
|
{{ $t("Followings") }}
|
||||||
|
<b-tag rounded>{{ relayFollowings.total }}</b-tag>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</router-link>
|
||||||
|
<router-link
|
||||||
|
tag="li"
|
||||||
|
active-class="is-active"
|
||||||
|
:to="{ name: RouteName.RELAY_FOLLOWERS }"
|
||||||
|
exact
|
||||||
|
>
|
||||||
|
<a>
|
||||||
|
<b-icon icon="inbox-arrow-up"></b-icon>
|
||||||
|
<span>
|
||||||
|
{{ $t("Followers") }}
|
||||||
|
<b-tag rounded>{{ relayFollowers.total }}</b-tag>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</router-link>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<router-view></router-view>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
|
|
@ -1,69 +1,81 @@
|
||||||
<template>
|
<template>
|
||||||
<div v-if="persons">
|
<div>
|
||||||
<b-switch v-model="local">{{ $t("Local") }}</b-switch>
|
<nav class="breadcrumb" aria-label="breadcrumbs">
|
||||||
<b-switch v-model="suspended">{{ $t("Suspended") }}</b-switch>
|
<ul>
|
||||||
<b-table
|
<li>
|
||||||
:data="persons.elements"
|
<router-link :to="{ name: RouteName.MODERATION }">{{ $t("Moderation") }}</router-link>
|
||||||
:loading="$apollo.queries.persons.loading"
|
</li>
|
||||||
paginated
|
<li class="is-active">
|
||||||
backend-pagination
|
<router-link :to="{ name: RouteName.PROFILES }">{{ $t("Profiles") }}</router-link>
|
||||||
backend-filtering
|
</li>
|
||||||
:total="persons.total"
|
</ul>
|
||||||
:per-page="PROFILES_PER_PAGE"
|
</nav>
|
||||||
@page-change="onPageChange"
|
<div v-if="persons">
|
||||||
@filters-change="onFiltersChange"
|
<b-switch v-model="local">{{ $t("Local") }}</b-switch>
|
||||||
>
|
<b-switch v-model="suspended">{{ $t("Suspended") }}</b-switch>
|
||||||
<template slot-scope="props">
|
<b-table
|
||||||
<b-table-column field="preferredUsername" :label="$t('Username')" searchable>
|
:data="persons.elements"
|
||||||
<template slot="searchable" slot-scope="props">
|
:loading="$apollo.queries.persons.loading"
|
||||||
<b-input
|
paginated
|
||||||
v-model="props.filters.preferredUsername"
|
backend-pagination
|
||||||
placeholder="Search..."
|
backend-filtering
|
||||||
icon="magnify"
|
:total="persons.total"
|
||||||
size="is-small"
|
:per-page="PROFILES_PER_PAGE"
|
||||||
/>
|
@page-change="onPageChange"
|
||||||
</template>
|
@filters-change="onFiltersChange"
|
||||||
<router-link
|
>
|
||||||
class="profile"
|
<template slot-scope="props">
|
||||||
:to="{ name: RouteName.ADMIN_PROFILE, params: { id: props.row.id } }"
|
<b-table-column field="preferredUsername" :label="$t('Username')" searchable>
|
||||||
>
|
<template slot="searchable" slot-scope="props">
|
||||||
<article class="media">
|
<b-input
|
||||||
<figure class="media-left" v-if="props.row.avatar">
|
v-model="props.filters.preferredUsername"
|
||||||
<p class="image is-48x48">
|
placeholder="Search..."
|
||||||
<img :src="props.row.avatar.url" />
|
icon="magnify"
|
||||||
</p>
|
size="is-small"
|
||||||
</figure>
|
/>
|
||||||
<div class="media-content">
|
</template>
|
||||||
<div class="content">
|
<router-link
|
||||||
<strong v-if="props.row.name">{{ props.row.name }}</strong
|
class="profile"
|
||||||
><br v-if="props.row.name" />
|
:to="{ name: RouteName.ADMIN_PROFILE, params: { id: props.row.id } }"
|
||||||
<small>@{{ props.row.preferredUsername }}</small>
|
>
|
||||||
|
<article class="media">
|
||||||
|
<figure class="media-left" v-if="props.row.avatar">
|
||||||
|
<p class="image is-48x48">
|
||||||
|
<img :src="props.row.avatar.url" />
|
||||||
|
</p>
|
||||||
|
</figure>
|
||||||
|
<div class="media-content">
|
||||||
|
<div class="content">
|
||||||
|
<strong v-if="props.row.name">{{ props.row.name }}</strong
|
||||||
|
><br v-if="props.row.name" />
|
||||||
|
<small>@{{ props.row.preferredUsername }}</small>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</article>
|
||||||
</article>
|
</router-link>
|
||||||
</router-link>
|
</b-table-column>
|
||||||
</b-table-column>
|
|
||||||
|
|
||||||
<b-table-column field="domain" :label="$t('Domain')" searchable>
|
<b-table-column field="domain" :label="$t('Domain')" searchable>
|
||||||
<template slot="searchable" slot-scope="props">
|
<template slot="searchable" slot-scope="props">
|
||||||
<b-input
|
<b-input
|
||||||
v-model="props.filters.domain"
|
v-model="props.filters.domain"
|
||||||
placeholder="Search..."
|
placeholder="Search..."
|
||||||
icon="magnify"
|
icon="magnify"
|
||||||
size="is-small"
|
size="is-small"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
{{ props.row.domain }}
|
{{ props.row.domain }}
|
||||||
</b-table-column>
|
</b-table-column>
|
||||||
</template>
|
</template>
|
||||||
<template slot="empty">
|
<template slot="empty">
|
||||||
<section class="section">
|
<section class="section">
|
||||||
<div class="content has-text-grey has-text-centered">
|
<div class="content has-text-grey has-text-centered">
|
||||||
<p>{{ $t("No profile matches the filters") }}</p>
|
<p>{{ $t("No profile matches the filters") }}</p>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
</b-table>
|
</b-table>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
|
|
@ -1,241 +1,259 @@
|
||||||
<template>
|
<template>
|
||||||
<section v-if="adminSettings">
|
<div>
|
||||||
<form @submit.prevent="updateSettings">
|
<nav class="breadcrumb" aria-label="breadcrumbs">
|
||||||
<b-field :label="$t('Instance Name')">
|
<ul>
|
||||||
<b-input v-model="adminSettings.instanceName" />
|
<li>
|
||||||
</b-field>
|
<router-link :to="{ name: RouteName.ADMIN }">{{ $t("Admin") }}</router-link>
|
||||||
<div class="field">
|
</li>
|
||||||
<label class="label has-help">{{ $t("Instance Short Description") }}</label>
|
<li class="is-active">
|
||||||
<small>
|
<router-link :to="{ name: RouteName.ADMIN_SETTINGS }">{{
|
||||||
{{
|
$t("Instance settings")
|
||||||
$t(
|
}}</router-link>
|
||||||
"Displayed on homepage and meta tags. Describe what Mobilizon is and what makes this instance special in a single paragraph."
|
</li>
|
||||||
)
|
</ul>
|
||||||
}}
|
</nav>
|
||||||
</small>
|
<section v-if="adminSettings">
|
||||||
<b-input type="textarea" v-model="adminSettings.instanceDescription" rows="2" />
|
<form @submit.prevent="updateSettings">
|
||||||
</div>
|
<b-field :label="$t('Instance Name')">
|
||||||
<div class="field">
|
<b-input v-model="adminSettings.instanceName" />
|
||||||
<label class="label has-help">{{ $t("Contact") }}</label>
|
</b-field>
|
||||||
<small>
|
<div class="field">
|
||||||
{{ $t("Can be an email or a link, or just plain text.") }}
|
<label class="label has-help">{{ $t("Instance Short Description") }}</label>
|
||||||
</small>
|
<small>
|
||||||
<b-input v-model="adminSettings.contact" />
|
{{
|
||||||
</div>
|
$t(
|
||||||
<b-field :label="$t('Allow registrations')">
|
"Displayed on homepage and meta tags. Describe what Mobilizon is and what makes this instance special in a single paragraph."
|
||||||
<b-switch v-model="adminSettings.registrationsOpen">
|
)
|
||||||
<p class="content" v-if="adminSettings.registrationsOpen">
|
}}
|
||||||
{{ $t("Registration is allowed, anyone can register.") }}
|
</small>
|
||||||
</p>
|
<b-input type="textarea" v-model="adminSettings.instanceDescription" rows="2" />
|
||||||
<p class="content" v-else>{{ $t("Registration is closed.") }}</p>
|
|
||||||
</b-switch>
|
|
||||||
</b-field>
|
|
||||||
<div class="field">
|
|
||||||
<label class="label has-help">{{ $t("Instance Long Description") }}</label>
|
|
||||||
<small>
|
|
||||||
{{
|
|
||||||
$t(
|
|
||||||
"A place to explain who you are and the things that set your instance apart. You can use HTML tags."
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</small>
|
|
||||||
<b-input type="textarea" v-model="adminSettings.instanceLongDescription" rows="4" />
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<label class="label has-help">{{ $t("Instance Rules") }}</label>
|
|
||||||
<small>
|
|
||||||
{{ $t("A place for your code of conduct, rules or guidelines. You can use HTML tags.") }}
|
|
||||||
</small>
|
|
||||||
<b-input type="textarea" v-model="adminSettings.instanceRules" />
|
|
||||||
</div>
|
|
||||||
<b-field :label="$t('Instance Terms Source')">
|
|
||||||
<div class="columns">
|
|
||||||
<div class="column is-one-third-desktop">
|
|
||||||
<b-field>
|
|
||||||
<b-radio
|
|
||||||
v-model="adminSettings.instanceTermsType"
|
|
||||||
name="instanceTermsType"
|
|
||||||
:native-value="InstanceTermsType.DEFAULT"
|
|
||||||
>{{ $t("Default Mobilizon terms") }}</b-radio
|
|
||||||
>
|
|
||||||
</b-field>
|
|
||||||
<b-field>
|
|
||||||
<b-radio
|
|
||||||
v-model="adminSettings.instanceTermsType"
|
|
||||||
name="instanceTermsType"
|
|
||||||
:native-value="InstanceTermsType.URL"
|
|
||||||
>{{ $t("Custom URL") }}</b-radio
|
|
||||||
>
|
|
||||||
</b-field>
|
|
||||||
<b-field>
|
|
||||||
<b-radio
|
|
||||||
v-model="adminSettings.instanceTermsType"
|
|
||||||
name="instanceTermsType"
|
|
||||||
:native-value="InstanceTermsType.CUSTOM"
|
|
||||||
>{{ $t("Custom text") }}</b-radio
|
|
||||||
>
|
|
||||||
</b-field>
|
|
||||||
</div>
|
|
||||||
<div class="column">
|
|
||||||
<div
|
|
||||||
class="notification"
|
|
||||||
v-if="adminSettings.instanceTermsType === InstanceTermsType.DEFAULT"
|
|
||||||
>
|
|
||||||
<b>{{ $t("Default") }}</b>
|
|
||||||
<i18n
|
|
||||||
tag="p"
|
|
||||||
class="content"
|
|
||||||
path="The {default_terms} will be used. They will be translated in the user's language."
|
|
||||||
>
|
|
||||||
<a
|
|
||||||
slot="default_terms"
|
|
||||||
href="https://mobilizon.org/terms"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener"
|
|
||||||
>{{ $t("default Mobilizon terms") }}</a
|
|
||||||
>
|
|
||||||
</i18n>
|
|
||||||
<b>{{
|
|
||||||
$t(
|
|
||||||
"NOTE! The default terms have not been checked over by a lawyer and thus are unlikely to provide full legal protection for all situations for an instance admin using them. They are also not specific to all countries and jurisdictions. If you are unsure, please check with a lawyer."
|
|
||||||
)
|
|
||||||
}}</b>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="notification"
|
|
||||||
v-if="adminSettings.instanceTermsType === InstanceTermsType.URL"
|
|
||||||
>
|
|
||||||
<b>{{ $t("URL") }}</b>
|
|
||||||
<p class="content">{{ $t("Set an URL to a page with your own terms.") }}</p>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="notification"
|
|
||||||
v-if="adminSettings.instanceTermsType === InstanceTermsType.CUSTOM"
|
|
||||||
>
|
|
||||||
<b>{{ $t("Custom") }}</b>
|
|
||||||
<i18n
|
|
||||||
tag="p"
|
|
||||||
class="content"
|
|
||||||
path="Enter your own terms. HTML tags allowed. The {mobilizon_terms} are provided as template."
|
|
||||||
>
|
|
||||||
<a
|
|
||||||
slot="mobilizon_terms"
|
|
||||||
href="https://mobilizon.org/terms"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener"
|
|
||||||
>
|
|
||||||
{{ $t("default Mobilizon terms") }}</a
|
|
||||||
>
|
|
||||||
</i18n>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</b-field>
|
<div class="field">
|
||||||
<b-field
|
<label class="label has-help">{{ $t("Contact") }}</label>
|
||||||
:label="$t('Instance Terms URL')"
|
<small>
|
||||||
v-if="adminSettings.instanceTermsType === InstanceTermsType.URL"
|
{{ $t("Can be an email or a link, or just plain text.") }}
|
||||||
>
|
</small>
|
||||||
<b-input type="URL" v-model="adminSettings.instanceTermsUrl" />
|
<b-input v-model="adminSettings.contact" />
|
||||||
</b-field>
|
|
||||||
<b-field
|
|
||||||
:label="$t('Instance Terms')"
|
|
||||||
v-if="adminSettings.instanceTermsType === InstanceTermsType.CUSTOM"
|
|
||||||
>
|
|
||||||
<b-input type="textarea" v-model="adminSettings.instancePrivacyPolicy" />
|
|
||||||
</b-field>
|
|
||||||
<b-field :label="$t('Instance Privacy Policy Source')">
|
|
||||||
<div class="columns">
|
|
||||||
<div class="column is-one-third-desktop">
|
|
||||||
<b-field>
|
|
||||||
<b-radio
|
|
||||||
v-model="adminSettings.instancePrivacyPolicyType"
|
|
||||||
name="instancePrivacyType"
|
|
||||||
:native-value="InstancePrivacyType.DEFAULT"
|
|
||||||
>{{ $t("Default Mobilizon privacy policy") }}</b-radio
|
|
||||||
>
|
|
||||||
</b-field>
|
|
||||||
<b-field>
|
|
||||||
<b-radio
|
|
||||||
v-model="adminSettings.instancePrivacyPolicyType"
|
|
||||||
name="instancePrivacyType"
|
|
||||||
:native-value="InstancePrivacyType.URL"
|
|
||||||
>{{ $t("Custom URL") }}</b-radio
|
|
||||||
>
|
|
||||||
</b-field>
|
|
||||||
<b-field>
|
|
||||||
<b-radio
|
|
||||||
v-model="adminSettings.instancePrivacyPolicyType"
|
|
||||||
name="instancePrivacyType"
|
|
||||||
:native-value="InstancePrivacyType.CUSTOM"
|
|
||||||
>{{ $t("Custom text") }}</b-radio
|
|
||||||
>
|
|
||||||
</b-field>
|
|
||||||
</div>
|
|
||||||
<div class="column">
|
|
||||||
<div
|
|
||||||
class="notification"
|
|
||||||
v-if="adminSettings.instancePrivacyPolicyType === InstancePrivacyType.DEFAULT"
|
|
||||||
>
|
|
||||||
<b>{{ $t("Default") }}</b>
|
|
||||||
<i18n
|
|
||||||
tag="p"
|
|
||||||
class="content"
|
|
||||||
path="The {default_privacy_policy} will be used. They will be translated in the user's language."
|
|
||||||
>
|
|
||||||
<a
|
|
||||||
slot="default_privacy_policy"
|
|
||||||
href="https://mobilizon.org/terms"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener"
|
|
||||||
>{{ $t("default Mobilizon privacy policy") }}</a
|
|
||||||
>
|
|
||||||
</i18n>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="notification"
|
|
||||||
v-if="adminSettings.instancePrivacyPolicyType === InstancePrivacyType.URL"
|
|
||||||
>
|
|
||||||
<b>{{ $t("URL") }}</b>
|
|
||||||
<p class="content">{{ $t("Set an URL to a page with your own privacy policy.") }}</p>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="notification"
|
|
||||||
v-if="adminSettings.instancePrivacyPolicyType === InstancePrivacyType.CUSTOM"
|
|
||||||
>
|
|
||||||
<b>{{ $t("Custom") }}</b>
|
|
||||||
<i18n
|
|
||||||
tag="p"
|
|
||||||
class="content"
|
|
||||||
path="Enter your own privacy policy. HTML tags allowed. The {mobilizon_privacy_policy} is provided as template."
|
|
||||||
>
|
|
||||||
<a
|
|
||||||
slot="mobilizon_privacy_policy"
|
|
||||||
href="https://mobilizon.org/privacy"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener"
|
|
||||||
>
|
|
||||||
{{ $t("default Mobilizon privacy policy") }}</a
|
|
||||||
>
|
|
||||||
</i18n>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</b-field>
|
<b-field :label="$t('Allow registrations')">
|
||||||
<b-field
|
<b-switch v-model="adminSettings.registrationsOpen">
|
||||||
:label="$t('Instance Privacy Policy URL')"
|
<p class="content" v-if="adminSettings.registrationsOpen">
|
||||||
v-if="adminSettings.instancePrivacyPolicyType === InstancePrivacyType.URL"
|
{{ $t("Registration is allowed, anyone can register.") }}
|
||||||
>
|
</p>
|
||||||
<b-input type="URL" v-model="adminSettings.instancePrivacyPolicyUrl" />
|
<p class="content" v-else>{{ $t("Registration is closed.") }}</p>
|
||||||
</b-field>
|
</b-switch>
|
||||||
<b-field
|
</b-field>
|
||||||
:label="$t('Instance Privacy Policy')"
|
<div class="field">
|
||||||
v-if="adminSettings.instancePrivacyPolicyType === InstancePrivacyType.CUSTOM"
|
<label class="label has-help">{{ $t("Instance Long Description") }}</label>
|
||||||
>
|
<small>
|
||||||
<b-input type="textarea" v-model="adminSettings.instancePrivacyPolicy" />
|
{{
|
||||||
</b-field>
|
$t(
|
||||||
<b-button native-type="submit" type="is-primary">{{ $t("Save") }}</b-button>
|
"A place to explain who you are and the things that set your instance apart. You can use HTML tags."
|
||||||
</form>
|
)
|
||||||
</section>
|
}}
|
||||||
|
</small>
|
||||||
|
<b-input type="textarea" v-model="adminSettings.instanceLongDescription" rows="4" />
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label class="label has-help">{{ $t("Instance Rules") }}</label>
|
||||||
|
<small>
|
||||||
|
{{
|
||||||
|
$t("A place for your code of conduct, rules or guidelines. You can use HTML tags.")
|
||||||
|
}}
|
||||||
|
</small>
|
||||||
|
<b-input type="textarea" v-model="adminSettings.instanceRules" />
|
||||||
|
</div>
|
||||||
|
<b-field :label="$t('Instance Terms Source')">
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column is-one-third-desktop">
|
||||||
|
<b-field>
|
||||||
|
<b-radio
|
||||||
|
v-model="adminSettings.instanceTermsType"
|
||||||
|
name="instanceTermsType"
|
||||||
|
:native-value="InstanceTermsType.DEFAULT"
|
||||||
|
>{{ $t("Default Mobilizon terms") }}</b-radio
|
||||||
|
>
|
||||||
|
</b-field>
|
||||||
|
<b-field>
|
||||||
|
<b-radio
|
||||||
|
v-model="adminSettings.instanceTermsType"
|
||||||
|
name="instanceTermsType"
|
||||||
|
:native-value="InstanceTermsType.URL"
|
||||||
|
>{{ $t("Custom URL") }}</b-radio
|
||||||
|
>
|
||||||
|
</b-field>
|
||||||
|
<b-field>
|
||||||
|
<b-radio
|
||||||
|
v-model="adminSettings.instanceTermsType"
|
||||||
|
name="instanceTermsType"
|
||||||
|
:native-value="InstanceTermsType.CUSTOM"
|
||||||
|
>{{ $t("Custom text") }}</b-radio
|
||||||
|
>
|
||||||
|
</b-field>
|
||||||
|
</div>
|
||||||
|
<div class="column">
|
||||||
|
<div
|
||||||
|
class="notification"
|
||||||
|
v-if="adminSettings.instanceTermsType === InstanceTermsType.DEFAULT"
|
||||||
|
>
|
||||||
|
<b>{{ $t("Default") }}</b>
|
||||||
|
<i18n
|
||||||
|
tag="p"
|
||||||
|
class="content"
|
||||||
|
path="The {default_terms} will be used. They will be translated in the user's language."
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
slot="default_terms"
|
||||||
|
href="https://mobilizon.org/terms"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener"
|
||||||
|
>{{ $t("default Mobilizon terms") }}</a
|
||||||
|
>
|
||||||
|
</i18n>
|
||||||
|
<b>{{
|
||||||
|
$t(
|
||||||
|
"NOTE! The default terms have not been checked over by a lawyer and thus are unlikely to provide full legal protection for all situations for an instance admin using them. They are also not specific to all countries and jurisdictions. If you are unsure, please check with a lawyer."
|
||||||
|
)
|
||||||
|
}}</b>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="notification"
|
||||||
|
v-if="adminSettings.instanceTermsType === InstanceTermsType.URL"
|
||||||
|
>
|
||||||
|
<b>{{ $t("URL") }}</b>
|
||||||
|
<p class="content">{{ $t("Set an URL to a page with your own terms.") }}</p>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="notification"
|
||||||
|
v-if="adminSettings.instanceTermsType === InstanceTermsType.CUSTOM"
|
||||||
|
>
|
||||||
|
<b>{{ $t("Custom") }}</b>
|
||||||
|
<i18n
|
||||||
|
tag="p"
|
||||||
|
class="content"
|
||||||
|
path="Enter your own terms. HTML tags allowed. The {mobilizon_terms} are provided as template."
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
slot="mobilizon_terms"
|
||||||
|
href="https://mobilizon.org/terms"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener"
|
||||||
|
>
|
||||||
|
{{ $t("default Mobilizon terms") }}</a
|
||||||
|
>
|
||||||
|
</i18n>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</b-field>
|
||||||
|
<b-field
|
||||||
|
:label="$t('Instance Terms URL')"
|
||||||
|
v-if="adminSettings.instanceTermsType === InstanceTermsType.URL"
|
||||||
|
>
|
||||||
|
<b-input type="URL" v-model="adminSettings.instanceTermsUrl" />
|
||||||
|
</b-field>
|
||||||
|
<b-field
|
||||||
|
:label="$t('Instance Terms')"
|
||||||
|
v-if="adminSettings.instanceTermsType === InstanceTermsType.CUSTOM"
|
||||||
|
>
|
||||||
|
<b-input type="textarea" v-model="adminSettings.instancePrivacyPolicy" />
|
||||||
|
</b-field>
|
||||||
|
<b-field :label="$t('Instance Privacy Policy Source')">
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column is-one-third-desktop">
|
||||||
|
<b-field>
|
||||||
|
<b-radio
|
||||||
|
v-model="adminSettings.instancePrivacyPolicyType"
|
||||||
|
name="instancePrivacyType"
|
||||||
|
:native-value="InstancePrivacyType.DEFAULT"
|
||||||
|
>{{ $t("Default Mobilizon privacy policy") }}</b-radio
|
||||||
|
>
|
||||||
|
</b-field>
|
||||||
|
<b-field>
|
||||||
|
<b-radio
|
||||||
|
v-model="adminSettings.instancePrivacyPolicyType"
|
||||||
|
name="instancePrivacyType"
|
||||||
|
:native-value="InstancePrivacyType.URL"
|
||||||
|
>{{ $t("Custom URL") }}</b-radio
|
||||||
|
>
|
||||||
|
</b-field>
|
||||||
|
<b-field>
|
||||||
|
<b-radio
|
||||||
|
v-model="adminSettings.instancePrivacyPolicyType"
|
||||||
|
name="instancePrivacyType"
|
||||||
|
:native-value="InstancePrivacyType.CUSTOM"
|
||||||
|
>{{ $t("Custom text") }}</b-radio
|
||||||
|
>
|
||||||
|
</b-field>
|
||||||
|
</div>
|
||||||
|
<div class="column">
|
||||||
|
<div
|
||||||
|
class="notification"
|
||||||
|
v-if="adminSettings.instancePrivacyPolicyType === InstancePrivacyType.DEFAULT"
|
||||||
|
>
|
||||||
|
<b>{{ $t("Default") }}</b>
|
||||||
|
<i18n
|
||||||
|
tag="p"
|
||||||
|
class="content"
|
||||||
|
path="The {default_privacy_policy} will be used. They will be translated in the user's language."
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
slot="default_privacy_policy"
|
||||||
|
href="https://mobilizon.org/terms"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener"
|
||||||
|
>{{ $t("default Mobilizon privacy policy") }}</a
|
||||||
|
>
|
||||||
|
</i18n>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="notification"
|
||||||
|
v-if="adminSettings.instancePrivacyPolicyType === InstancePrivacyType.URL"
|
||||||
|
>
|
||||||
|
<b>{{ $t("URL") }}</b>
|
||||||
|
<p class="content">
|
||||||
|
{{ $t("Set an URL to a page with your own privacy policy.") }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="notification"
|
||||||
|
v-if="adminSettings.instancePrivacyPolicyType === InstancePrivacyType.CUSTOM"
|
||||||
|
>
|
||||||
|
<b>{{ $t("Custom") }}</b>
|
||||||
|
<i18n
|
||||||
|
tag="p"
|
||||||
|
class="content"
|
||||||
|
path="Enter your own privacy policy. HTML tags allowed. The {mobilizon_privacy_policy} is provided as template."
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
slot="mobilizon_privacy_policy"
|
||||||
|
href="https://mobilizon.org/privacy"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener"
|
||||||
|
>
|
||||||
|
{{ $t("default Mobilizon privacy policy") }}</a
|
||||||
|
>
|
||||||
|
</i18n>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</b-field>
|
||||||
|
<b-field
|
||||||
|
:label="$t('Instance Privacy Policy URL')"
|
||||||
|
v-if="adminSettings.instancePrivacyPolicyType === InstancePrivacyType.URL"
|
||||||
|
>
|
||||||
|
<b-input type="URL" v-model="adminSettings.instancePrivacyPolicyUrl" />
|
||||||
|
</b-field>
|
||||||
|
<b-field
|
||||||
|
:label="$t('Instance Privacy Policy')"
|
||||||
|
v-if="adminSettings.instancePrivacyPolicyType === InstancePrivacyType.CUSTOM"
|
||||||
|
>
|
||||||
|
<b-input type="textarea" v-model="adminSettings.instancePrivacyPolicy" />
|
||||||
|
</b-field>
|
||||||
|
<b-button native-type="submit" type="is-primary">{{ $t("Save") }}</b-button>
|
||||||
|
</form>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Component, Vue } from "vue-property-decorator";
|
import { Component, Vue } from "vue-property-decorator";
|
||||||
|
|
|
@ -1,77 +1,89 @@
|
||||||
<template>
|
<template>
|
||||||
<div v-if="users">
|
<div>
|
||||||
<b-table
|
<nav class="breadcrumb" aria-label="breadcrumbs">
|
||||||
:data="users.elements"
|
<ul>
|
||||||
:loading="$apollo.queries.users.loading"
|
<li>
|
||||||
paginated
|
<router-link :to="{ name: RouteName.MODERATION }">{{ $t("Moderation") }}</router-link>
|
||||||
backend-pagination
|
</li>
|
||||||
backend-filtering
|
<li class="is-active">
|
||||||
detailed
|
<router-link :to="{ name: RouteName.USERS }">{{ $t("Users") }}</router-link>
|
||||||
:show-detail-icon="true"
|
</li>
|
||||||
:total="users.total"
|
</ul>
|
||||||
:per-page="USERS_PER_PAGE"
|
</nav>
|
||||||
:has-detailed-visible="(row => row.actors.length > 0)"
|
<div v-if="users">
|
||||||
@page-change="onPageChange"
|
<b-table
|
||||||
@filters-change="onFiltersChange"
|
:data="users.elements"
|
||||||
>
|
:loading="$apollo.queries.users.loading"
|
||||||
<template slot-scope="props">
|
paginated
|
||||||
<b-table-column field="id" width="40" numeric>
|
backend-pagination
|
||||||
{{ props.row.id }}
|
backend-filtering
|
||||||
</b-table-column>
|
detailed
|
||||||
<b-table-column field="email" :label="$t('Email')" searchable>
|
:show-detail-icon="true"
|
||||||
<template slot="searchable" slot-scope="props">
|
:total="users.total"
|
||||||
<b-input
|
:per-page="USERS_PER_PAGE"
|
||||||
v-model="props.filters.email"
|
:has-detailed-visible="(row => row.actors.length > 0)"
|
||||||
placeholder="Search..."
|
@page-change="onPageChange"
|
||||||
icon="magnify"
|
@filters-change="onFiltersChange"
|
||||||
size="is-small"
|
>
|
||||||
/>
|
<template slot-scope="props">
|
||||||
</template>
|
<b-table-column field="id" width="40" numeric>
|
||||||
<router-link
|
{{ props.row.id }}
|
||||||
class="user-profile"
|
</b-table-column>
|
||||||
:to="{ name: RouteName.ADMIN_USER_PROFILE, params: { id: props.row.id } }"
|
<b-table-column field="email" :label="$t('Email')" searchable>
|
||||||
:class="{ disabled: props.row.disabled }"
|
<template slot="searchable" slot-scope="props">
|
||||||
>
|
<b-input
|
||||||
{{ props.row.email }}
|
v-model="props.filters.email"
|
||||||
</router-link>
|
placeholder="Search..."
|
||||||
</b-table-column>
|
icon="magnify"
|
||||||
<b-table-column field="confirmedAt" :label="$t('Confirmed at')" :centered="true">
|
size="is-small"
|
||||||
<template v-if="props.row.confirmedAt">
|
/>
|
||||||
{{ props.row.confirmedAt | formatDateTimeString }}
|
</template>
|
||||||
</template>
|
<router-link
|
||||||
<template v-else>
|
class="user-profile"
|
||||||
{{ $t("Not confirmed") }}
|
:to="{ name: RouteName.ADMIN_USER_PROFILE, params: { id: props.row.id } }"
|
||||||
</template>
|
:class="{ disabled: props.row.disabled }"
|
||||||
</b-table-column>
|
>
|
||||||
<b-table-column field="locale" :label="$t('Language')" :centered="true">
|
{{ props.row.email }}
|
||||||
{{ props.row.locale }}
|
</router-link>
|
||||||
</b-table-column>
|
</b-table-column>
|
||||||
</template>
|
<b-table-column field="confirmedAt" :label="$t('Confirmed at')" :centered="true">
|
||||||
|
<template v-if="props.row.confirmedAt">
|
||||||
|
{{ props.row.confirmedAt | formatDateTimeString }}
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
{{ $t("Not confirmed") }}
|
||||||
|
</template>
|
||||||
|
</b-table-column>
|
||||||
|
<b-table-column field="locale" :label="$t('Language')" :centered="true">
|
||||||
|
{{ props.row.locale }}
|
||||||
|
</b-table-column>
|
||||||
|
</template>
|
||||||
|
|
||||||
<template slot="detail" slot-scope="props">
|
<template slot="detail" slot-scope="props">
|
||||||
<router-link
|
<router-link
|
||||||
class="profile"
|
class="profile"
|
||||||
v-for="actor in props.row.actors"
|
v-for="actor in props.row.actors"
|
||||||
:key="actor.id"
|
:key="actor.id"
|
||||||
:to="{ name: RouteName.ADMIN_PROFILE, params: { id: actor.id } }"
|
:to="{ name: RouteName.ADMIN_PROFILE, params: { id: actor.id } }"
|
||||||
>
|
>
|
||||||
<article class="media">
|
<article class="media">
|
||||||
<figure class="media-left">
|
<figure class="media-left">
|
||||||
<p class="image is-64x64">
|
<p class="image is-64x64">
|
||||||
<img :src="actor.avatar.url" />
|
<img :src="actor.avatar.url" />
|
||||||
</p>
|
</p>
|
||||||
</figure>
|
</figure>
|
||||||
<div class="media-content">
|
<div class="media-content">
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<strong v-if="actor.name">{{ actor.name }}</strong>
|
<strong v-if="actor.name">{{ actor.name }}</strong>
|
||||||
<small>@{{ actor.preferredUsername }}</small>
|
<small>@{{ actor.preferredUsername }}</small>
|
||||||
<p>{{ actor.summary }}</p>
|
<p>{{ actor.summary }}</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</article>
|
||||||
</article>
|
</router-link>
|
||||||
</router-link>
|
</template>
|
||||||
</template>
|
</b-table>
|
||||||
</b-table>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
|
|
@ -1,20 +1,37 @@
|
||||||
<template>
|
<template>
|
||||||
<section class="container section" v-if="group">
|
<div>
|
||||||
<form @submit.prevent="inviteMember">
|
<nav class="breadcrumb" aria-label="breadcrumbs">
|
||||||
<b-field :label="$t('Invite a new member')" custom-class="add-relay" horizontal>
|
<ul>
|
||||||
<b-field grouped expanded size="is-large">
|
<li>
|
||||||
<p class="control">
|
<router-link :to="{ name: RouteName.GROUP }">{{ group.name }}</router-link>
|
||||||
<b-input v-model="newMemberUsername" :placeholder="$t('Ex: someone@mobilizon.org')" />
|
</li>
|
||||||
</p>
|
<li>
|
||||||
<p class="control">
|
<router-link :to="{ name: RouteName.GROUP_SETTINGS }">{{ $t("Settings") }}</router-link>
|
||||||
<b-button type="is-primary" native-type="submit">{{ $t("Invite member") }}</b-button>
|
</li>
|
||||||
</p>
|
<li class="is-active">
|
||||||
|
<router-link :to="{ name: RouteName.GROUP_MEMBERS_SETTINGS }">{{
|
||||||
|
$t("Members")
|
||||||
|
}}</router-link>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
<section class="container section" v-if="group">
|
||||||
|
<form @submit.prevent="inviteMember">
|
||||||
|
<b-field :label="$t('Invite a new member')" custom-class="add-relay" horizontal>
|
||||||
|
<b-field grouped expanded size="is-large">
|
||||||
|
<p class="control">
|
||||||
|
<b-input v-model="newMemberUsername" :placeholder="$t('Ex: someone@mobilizon.org')" />
|
||||||
|
</p>
|
||||||
|
<p class="control">
|
||||||
|
<b-button type="is-primary" native-type="submit">{{ $t("Invite member") }}</b-button>
|
||||||
|
</p>
|
||||||
|
</b-field>
|
||||||
</b-field>
|
</b-field>
|
||||||
</b-field>
|
</form>
|
||||||
</form>
|
<h1>{{ $t("Group Members") }} ({{ group.members.total }})</h1>
|
||||||
<h1>{{ $t("Group Members") }} ({{ group.members.total }})</h1>
|
<pre>{{ group.members }}</pre>
|
||||||
<pre>{{ group.members }}</pre>
|
</section>
|
||||||
</section>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
|
|
@ -2,19 +2,21 @@
|
||||||
<aside class="section container">
|
<aside class="section container">
|
||||||
<h1 class="title">{{ $t("Settings") }}</h1>
|
<h1 class="title">{{ $t("Settings") }}</h1>
|
||||||
<div class="columns">
|
<div class="columns">
|
||||||
<SettingsMenu class="column is-one-quarter-desktop" :menu="menu" />
|
<aside class="column is-one-quarter-desktop">
|
||||||
|
<ul>
|
||||||
|
<SettingMenuSection :title="$t('Settings')" :to="{ name: RouteName.GROUP_SETTINGS }">
|
||||||
|
<SettingMenuItem
|
||||||
|
:title="this.$t('Public')"
|
||||||
|
:to="{ name: RouteName.GROUP_PUBLIC_SETTINGS }"
|
||||||
|
/>
|
||||||
|
<SettingMenuItem
|
||||||
|
:title="this.$t('Members')"
|
||||||
|
:to="{ name: RouteName.GROUP_MEMBERS_SETTINGS }"
|
||||||
|
/>
|
||||||
|
</SettingMenuSection>
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<nav class="breadcrumb" aria-label="breadcrumbs">
|
|
||||||
<ul>
|
|
||||||
<li
|
|
||||||
v-for="route in routes.get($route.name)"
|
|
||||||
:class="{ 'is-active': route.to.name === $route.name }"
|
|
||||||
:key="route.title"
|
|
||||||
>
|
|
||||||
<router-link :to="{ name: route.to.name }">{{ route.title }}</router-link>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
<router-view />
|
<router-view />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -22,70 +24,20 @@
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Component, Vue, Watch } from "vue-property-decorator";
|
import { Component, Vue, Watch } from "vue-property-decorator";
|
||||||
import SettingsMenu from "@/components/Settings/SettingsMenu.vue";
|
|
||||||
import { ISettingMenuSection } from "@/types/setting-menu.model";
|
|
||||||
import { Route } from "vue-router";
|
import { Route } from "vue-router";
|
||||||
import { IGroup, IPerson } from "@/types/actor";
|
import { IGroup, IPerson } from "@/types/actor";
|
||||||
import { FETCH_GROUP } from "@/graphql/actor";
|
import { FETCH_GROUP } from "@/graphql/actor";
|
||||||
import RouteName from "../../router/name";
|
import RouteName from "../../router/name";
|
||||||
|
import SettingMenuSection from "../../components/Settings/SettingMenuSection.vue";
|
||||||
|
import SettingMenuItem from "../../components/Settings/SettingMenuItem.vue";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
components: { SettingsMenu },
|
components: { SettingMenuSection, SettingMenuItem },
|
||||||
apollo: {
|
|
||||||
group: {
|
|
||||||
query: FETCH_GROUP,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
export default class Settings extends Vue {
|
export default class Settings extends Vue {
|
||||||
RouteName = RouteName;
|
RouteName = RouteName;
|
||||||
|
|
||||||
menu: ISettingMenuSection[] = [];
|
|
||||||
|
|
||||||
group!: IGroup[];
|
group!: IGroup[];
|
||||||
|
|
||||||
mounted() {
|
|
||||||
this.menu = [
|
|
||||||
{
|
|
||||||
title: this.$t("Settings") as string,
|
|
||||||
to: { name: RouteName.GROUP_SETTINGS } as Route,
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
title: this.$t("Public") as string,
|
|
||||||
to: { name: RouteName.GROUP_PUBLIC_SETTINGS } as Route,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: this.$t("Members") as string,
|
|
||||||
to: { name: RouteName.GROUP_MEMBERS_SETTINGS } as Route,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
get routes(): Map<string, Route[]> {
|
|
||||||
return this.getPath(this.menu);
|
|
||||||
}
|
|
||||||
|
|
||||||
getPath(object: ISettingMenuSection[]) {
|
|
||||||
function iter(menu: ISettingMenuSection[] | ISettingMenuSection, acc: ISettingMenuSection[]) {
|
|
||||||
if (Array.isArray(menu)) {
|
|
||||||
return menu.forEach((item: ISettingMenuSection) => {
|
|
||||||
iter(item, acc.concat(item));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (menu.items && menu.items.length > 0) {
|
|
||||||
return menu.items.forEach((item: ISettingMenuSection) => {
|
|
||||||
iter(item, acc.concat(item));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
result.set(menu.to.name, acc);
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = new Map();
|
|
||||||
iter(object, []);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -1,146 +1,160 @@
|
||||||
<template>
|
<template>
|
||||||
<section>
|
<div>
|
||||||
<ul v-if="actionLogs.length > 0">
|
<nav class="breadcrumb" aria-label="breadcrumbs">
|
||||||
<li v-for="log in actionLogs" :key="log.id">
|
<ul>
|
||||||
<div class="box">
|
<li>
|
||||||
<img class="image" :src="log.actor.avatar.url" v-if="log.actor.avatar" />
|
<router-link :to="{ name: RouteName.MODERATION }">{{ $t("Moderation") }}</router-link>
|
||||||
<i18n
|
</li>
|
||||||
v-if="log.action === ActionLogAction.REPORT_UPDATE_CLOSED"
|
<li class="is-active">
|
||||||
tag="span"
|
<router-link :to="{ name: RouteName.REPORT_LOGS }">{{
|
||||||
path="{moderator} closed {report}"
|
$t("Moderation log")
|
||||||
>
|
}}</router-link>
|
||||||
<router-link
|
</li>
|
||||||
slot="moderator"
|
</ul>
|
||||||
:to="{ name: RouteName.ADMIN_PROFILE, params: { id: log.actor.id } }"
|
</nav>
|
||||||
>@{{ log.actor.preferredUsername }}</router-link
|
<section>
|
||||||
|
<ul v-if="actionLogs.length > 0">
|
||||||
|
<li v-for="log in actionLogs" :key="log.id">
|
||||||
|
<div class="box">
|
||||||
|
<img class="image" :src="log.actor.avatar.url" v-if="log.actor.avatar" />
|
||||||
|
<i18n
|
||||||
|
v-if="log.action === ActionLogAction.REPORT_UPDATE_CLOSED"
|
||||||
|
tag="span"
|
||||||
|
path="{moderator} closed {report}"
|
||||||
>
|
>
|
||||||
<router-link
|
<router-link
|
||||||
:to="{ name: RouteName.REPORT, params: { reportId: log.object.id } }"
|
slot="moderator"
|
||||||
slot="report"
|
:to="{ name: RouteName.ADMIN_PROFILE, params: { id: log.actor.id } }"
|
||||||
>{{ $t("report #{report_number}", { report_number: log.object.id }) }}
|
>@{{ log.actor.preferredUsername }}</router-link
|
||||||
</router-link>
|
>
|
||||||
</i18n>
|
<router-link
|
||||||
<i18n
|
:to="{ name: RouteName.REPORT, params: { reportId: log.object.id } }"
|
||||||
v-else-if="log.action === ActionLogAction.REPORT_UPDATE_OPENED"
|
slot="report"
|
||||||
tag="span"
|
>{{ $t("report #{report_number}", { report_number: log.object.id }) }}
|
||||||
path="{moderator} reopened {report}"
|
</router-link>
|
||||||
>
|
</i18n>
|
||||||
<router-link
|
<i18n
|
||||||
slot="moderator"
|
v-else-if="log.action === ActionLogAction.REPORT_UPDATE_OPENED"
|
||||||
:to="{ name: RouteName.ADMIN_PROFILE, params: { id: log.actor.id } }"
|
tag="span"
|
||||||
>@{{ log.actor.preferredUsername }}</router-link
|
path="{moderator} reopened {report}"
|
||||||
>
|
>
|
||||||
<router-link
|
<router-link
|
||||||
:to="{ name: RouteName.REPORT, params: { reportId: log.object.id } }"
|
slot="moderator"
|
||||||
slot="report"
|
:to="{ name: RouteName.ADMIN_PROFILE, params: { id: log.actor.id } }"
|
||||||
>{{ $t("report #{report_number}", { report_number: log.object.id }) }}
|
>@{{ log.actor.preferredUsername }}</router-link
|
||||||
</router-link>
|
>
|
||||||
</i18n>
|
<router-link
|
||||||
<i18n
|
:to="{ name: RouteName.REPORT, params: { reportId: log.object.id } }"
|
||||||
v-else-if="log.action === ActionLogAction.REPORT_UPDATE_RESOLVED"
|
slot="report"
|
||||||
tag="span"
|
>{{ $t("report #{report_number}", { report_number: log.object.id }) }}
|
||||||
path="{moderator} marked {report} as resolved"
|
</router-link>
|
||||||
>
|
</i18n>
|
||||||
<router-link
|
<i18n
|
||||||
slot="moderator"
|
v-else-if="log.action === ActionLogAction.REPORT_UPDATE_RESOLVED"
|
||||||
:to="{ name: RouteName.ADMIN_PROFILE, params: { id: log.actor.id } }"
|
tag="span"
|
||||||
>@{{ log.actor.preferredUsername }}</router-link
|
path="{moderator} marked {report} as resolved"
|
||||||
>
|
>
|
||||||
<router-link
|
<router-link
|
||||||
:to="{ name: RouteName.REPORT, params: { reportId: log.object.id } }"
|
slot="moderator"
|
||||||
slot="report"
|
:to="{ name: RouteName.ADMIN_PROFILE, params: { id: log.actor.id } }"
|
||||||
>{{ $t("report #{report_number}", { report_number: log.object.id }) }}
|
>@{{ log.actor.preferredUsername }}</router-link
|
||||||
</router-link>
|
>
|
||||||
</i18n>
|
<router-link
|
||||||
<i18n
|
:to="{ name: RouteName.REPORT, params: { reportId: log.object.id } }"
|
||||||
v-else-if="log.action === ActionLogAction.NOTE_CREATION"
|
slot="report"
|
||||||
tag="span"
|
>{{ $t("report #{report_number}", { report_number: log.object.id }) }}
|
||||||
path="{moderator} added a note on {report}"
|
</router-link>
|
||||||
>
|
</i18n>
|
||||||
<router-link
|
<i18n
|
||||||
slot="moderator"
|
v-else-if="log.action === ActionLogAction.NOTE_CREATION"
|
||||||
:to="{ name: RouteName.ADMIN_PROFILE, params: { id: log.actor.id } }"
|
tag="span"
|
||||||
>@{{ log.actor.preferredUsername }}</router-link
|
path="{moderator} added a note on {report}"
|
||||||
>
|
>
|
||||||
<router-link
|
<router-link
|
||||||
v-if="log.object.report"
|
slot="moderator"
|
||||||
:to="{ name: RouteName.REPORT, params: { reportId: log.object.report.id } }"
|
:to="{ name: RouteName.ADMIN_PROFILE, params: { id: log.actor.id } }"
|
||||||
slot="report"
|
>@{{ log.actor.preferredUsername }}</router-link
|
||||||
>{{ $t("report #{report_number}", { report_number: log.object.report.id }) }}
|
>
|
||||||
</router-link>
|
<router-link
|
||||||
<span v-else slot="report">{{ $t("a non-existent report") }}</span>
|
v-if="log.object.report"
|
||||||
</i18n>
|
:to="{ name: RouteName.REPORT, params: { reportId: log.object.report.id } }"
|
||||||
<i18n
|
slot="report"
|
||||||
v-else-if="log.action === ActionLogAction.EVENT_DELETION"
|
>{{ $t("report #{report_number}", { report_number: log.object.report.id }) }}
|
||||||
tag="span"
|
</router-link>
|
||||||
path='{moderator} deleted an event named "{title}"'
|
<span v-else slot="report">{{ $t("a non-existent report") }}</span>
|
||||||
>
|
</i18n>
|
||||||
<router-link
|
<i18n
|
||||||
slot="moderator"
|
v-else-if="log.action === ActionLogAction.EVENT_DELETION"
|
||||||
:to="{ name: RouteName.ADMIN_PROFILE, params: { id: log.actor.id } }"
|
tag="span"
|
||||||
>@{{ log.actor.preferredUsername }}</router-link
|
path='{moderator} deleted an event named "{title}"'
|
||||||
>
|
>
|
||||||
<b slot="title">{{ log.object.title }}</b>
|
<router-link
|
||||||
</i18n>
|
slot="moderator"
|
||||||
<i18n
|
:to="{ name: RouteName.ADMIN_PROFILE, params: { id: log.actor.id } }"
|
||||||
v-else-if="log.action === ActionLogAction.ACTOR_SUSPENSION"
|
>@{{ log.actor.preferredUsername }}</router-link
|
||||||
tag="span"
|
>
|
||||||
path="{moderator} suspended profile {profile}"
|
<b slot="title">{{ log.object.title }}</b>
|
||||||
>
|
</i18n>
|
||||||
<router-link
|
<i18n
|
||||||
slot="moderator"
|
v-else-if="log.action === ActionLogAction.ACTOR_SUSPENSION"
|
||||||
:to="{ name: RouteName.ADMIN_PROFILE, params: { id: log.actor.id } }"
|
tag="span"
|
||||||
>@{{ log.actor.preferredUsername }}</router-link
|
path="{moderator} suspended profile {profile}"
|
||||||
>
|
>
|
||||||
<router-link
|
<router-link
|
||||||
slot="profile"
|
slot="moderator"
|
||||||
:to="{ name: RouteName.ADMIN_PROFILE, params: { id: log.object.id } }"
|
:to="{ name: RouteName.ADMIN_PROFILE, params: { id: log.actor.id } }"
|
||||||
>{{ displayNameAndUsername(log.object) }}
|
>@{{ log.actor.preferredUsername }}</router-link
|
||||||
</router-link>
|
>
|
||||||
</i18n>
|
<router-link
|
||||||
<i18n
|
slot="profile"
|
||||||
v-else-if="log.action === ActionLogAction.ACTOR_UNSUSPENSION"
|
:to="{ name: RouteName.ADMIN_PROFILE, params: { id: log.object.id } }"
|
||||||
tag="span"
|
>{{ displayNameAndUsername(log.object) }}
|
||||||
path="{moderator} has unsuspended profile {profile}"
|
</router-link>
|
||||||
>
|
</i18n>
|
||||||
<router-link
|
<i18n
|
||||||
slot="moderator"
|
v-else-if="log.action === ActionLogAction.ACTOR_UNSUSPENSION"
|
||||||
:to="{ name: RouteName.ADMIN_PROFILE, params: { id: log.actor.id } }"
|
tag="span"
|
||||||
>@{{ log.actor.preferredUsername }}</router-link
|
path="{moderator} has unsuspended profile {profile}"
|
||||||
>
|
>
|
||||||
<router-link
|
<router-link
|
||||||
slot="profile"
|
slot="moderator"
|
||||||
:to="{ name: RouteName.ADMIN_PROFILE, params: { id: log.object.id } }"
|
:to="{ name: RouteName.ADMIN_PROFILE, params: { id: log.actor.id } }"
|
||||||
>{{ displayNameAndUsername(log.object) }}
|
>@{{ log.actor.preferredUsername }}</router-link
|
||||||
</router-link>
|
>
|
||||||
</i18n>
|
<router-link
|
||||||
<i18n
|
slot="profile"
|
||||||
v-else-if="log.action === ActionLogAction.USER_DELETION"
|
:to="{ name: RouteName.ADMIN_PROFILE, params: { id: log.object.id } }"
|
||||||
tag="span"
|
>{{ displayNameAndUsername(log.object) }}
|
||||||
path="{moderator} has deleted user {user}"
|
</router-link>
|
||||||
>
|
</i18n>
|
||||||
<router-link
|
<i18n
|
||||||
slot="moderator"
|
v-else-if="log.action === ActionLogAction.USER_DELETION"
|
||||||
:to="{ name: RouteName.ADMIN_PROFILE, params: { id: log.actor.id } }"
|
tag="span"
|
||||||
>@{{ log.actor.preferredUsername }}</router-link
|
path="{moderator} has deleted user {user}"
|
||||||
>
|
>
|
||||||
<router-link
|
<router-link
|
||||||
v-if="log.object.confirmedAt"
|
slot="moderator"
|
||||||
slot="user"
|
:to="{ name: RouteName.ADMIN_PROFILE, params: { id: log.actor.id } }"
|
||||||
:to="{ name: RouteName.ADMIN_USER_PROFILE, params: { id: log.object.id } }"
|
>@{{ log.actor.preferredUsername }}</router-link
|
||||||
>{{ log.object.email }}
|
>
|
||||||
</router-link>
|
<router-link
|
||||||
<b v-else slot="user">{{ log.object.email }}</b>
|
v-if="log.object.confirmedAt"
|
||||||
</i18n>
|
slot="user"
|
||||||
<br />
|
:to="{ name: RouteName.ADMIN_USER_PROFILE, params: { id: log.object.id } }"
|
||||||
<small>{{ log.insertedAt | formatDateTimeString }}</small>
|
>{{ log.object.email }}
|
||||||
</div>
|
</router-link>
|
||||||
</li>
|
<b v-else slot="user">{{ log.object.email }}</b>
|
||||||
</ul>
|
</i18n>
|
||||||
<div v-else>
|
<br />
|
||||||
<b-message type="is-info">{{ $t("No moderation logs yet") }}</b-message>
|
<small>{{ log.insertedAt | formatDateTimeString }}</small>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</li>
|
||||||
|
</ul>
|
||||||
|
<div v-else>
|
||||||
|
<b-message type="is-info">{{ $t("No moderation logs yet") }}</b-message>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Component, Vue } from "vue-property-decorator";
|
import { Component, Vue } from "vue-property-decorator";
|
||||||
|
|
|
@ -1,202 +1,219 @@
|
||||||
<template>
|
<template>
|
||||||
<section>
|
<div>
|
||||||
<b-message title="Error" type="is-danger" v-for="error in errors" :key="error">
|
<nav class="breadcrumb" aria-label="breadcrumbs" v-if="report">
|
||||||
{{ error }}
|
<ul>
|
||||||
</b-message>
|
<li>
|
||||||
<div class="container" v-if="report">
|
<router-link :to="{ name: RouteName.MODERATION }">{{ $t("Moderation") }}</router-link>
|
||||||
<div class="buttons">
|
</li>
|
||||||
<b-button
|
<li>
|
||||||
v-if="report.status !== ReportStatusEnum.RESOLVED"
|
<router-link :to="{ name: RouteName.REPORTS }">{{ $t("Reports") }}</router-link>
|
||||||
@click="updateReport(ReportStatusEnum.RESOLVED)"
|
</li>
|
||||||
type="is-primary"
|
<li class="is-active">
|
||||||
>{{ $t("Mark as resolved") }}</b-button
|
<router-link :to="{ name: RouteName.REPORT, params: { id: report.id } }">{{
|
||||||
>
|
$t("Report #{reportNumber}", { reportNumber: report.id })
|
||||||
<b-button
|
}}</router-link>
|
||||||
v-if="report.status !== ReportStatusEnum.OPEN"
|
</li>
|
||||||
@click="updateReport(ReportStatusEnum.OPEN)"
|
</ul>
|
||||||
type="is-success"
|
</nav>
|
||||||
>{{ $t("Reopen") }}</b-button
|
<section>
|
||||||
>
|
<b-message title="Error" type="is-danger" v-for="error in errors" :key="error">
|
||||||
<b-button
|
{{ error }}
|
||||||
v-if="report.status !== ReportStatusEnum.CLOSED"
|
</b-message>
|
||||||
@click="updateReport(ReportStatusEnum.CLOSED)"
|
<div class="container" v-if="report">
|
||||||
type="is-danger"
|
<div class="buttons">
|
||||||
>{{ $t("Close") }}</b-button
|
<b-button
|
||||||
>
|
v-if="report.status !== ReportStatusEnum.RESOLVED"
|
||||||
</div>
|
@click="updateReport(ReportStatusEnum.RESOLVED)"
|
||||||
<div class="table-container">
|
type="is-primary"
|
||||||
<table class="table is-striped is-fullwidth">
|
>{{ $t("Mark as resolved") }}</b-button
|
||||||
<tbody>
|
>
|
||||||
<tr>
|
<b-button
|
||||||
<td>{{ $t("Reported identity") }}</td>
|
v-if="report.status !== ReportStatusEnum.OPEN"
|
||||||
<td>
|
@click="updateReport(ReportStatusEnum.OPEN)"
|
||||||
<router-link
|
type="is-success"
|
||||||
:to="{
|
>{{ $t("Reopen") }}</b-button
|
||||||
name: RouteName.ADMIN_PROFILE,
|
>
|
||||||
params: { id: report.reported.id },
|
<b-button
|
||||||
}"
|
v-if="report.status !== ReportStatusEnum.CLOSED"
|
||||||
>
|
@click="updateReport(ReportStatusEnum.CLOSED)"
|
||||||
<img
|
type="is-danger"
|
||||||
v-if="report.reported.avatar"
|
>{{ $t("Close") }}</b-button
|
||||||
class="image"
|
>
|
||||||
:src="report.reported.avatar.url"
|
</div>
|
||||||
alt=""
|
<div class="table-container">
|
||||||
/>
|
<table class="table is-striped is-fullwidth">
|
||||||
@{{ report.reported.preferredUsername }}
|
<tbody>
|
||||||
</router-link>
|
<tr>
|
||||||
</td>
|
<td>{{ $t("Reported identity") }}</td>
|
||||||
</tr>
|
<td>
|
||||||
<tr>
|
<router-link
|
||||||
<td>{{ $t("Reported by") }}</td>
|
:to="{
|
||||||
<td v-if="report.reporter.type === ActorType.APPLICATION">
|
name: RouteName.ADMIN_PROFILE,
|
||||||
{{ report.reporter.domain }}
|
params: { id: report.reported.id },
|
||||||
</td>
|
}"
|
||||||
<td v-else>
|
>
|
||||||
<router-link
|
<img
|
||||||
:to="{
|
v-if="report.reported.avatar"
|
||||||
name: RouteName.ADMIN_PROFILE,
|
class="image"
|
||||||
params: { id: report.reporter.id },
|
:src="report.reported.avatar.url"
|
||||||
}"
|
alt=""
|
||||||
>
|
/>
|
||||||
<img
|
@{{ report.reported.preferredUsername }}
|
||||||
v-if="report.reporter.avatar"
|
</router-link>
|
||||||
class="image"
|
</td>
|
||||||
:src="report.reporter.avatar.url"
|
</tr>
|
||||||
alt=""
|
<tr>
|
||||||
/>
|
<td>{{ $t("Reported by") }}</td>
|
||||||
@{{ report.reporter.preferredUsername }}
|
<td v-if="report.reporter.type === ActorType.APPLICATION">
|
||||||
</router-link>
|
{{ report.reporter.domain }}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
<td v-else>
|
||||||
<tr>
|
<router-link
|
||||||
<td>{{ $t("Reported") }}</td>
|
:to="{
|
||||||
<td>{{ report.insertedAt | formatDateTimeString }}</td>
|
name: RouteName.ADMIN_PROFILE,
|
||||||
</tr>
|
params: { id: report.reporter.id },
|
||||||
<tr v-if="report.updatedAt !== report.insertedAt">
|
}"
|
||||||
<td>{{ $t("Updated") }}</td>
|
>
|
||||||
<td>{{ report.updatedAt | formatDateTimeString }}</td>
|
<img
|
||||||
</tr>
|
v-if="report.reporter.avatar"
|
||||||
<tr>
|
class="image"
|
||||||
<td>{{ $t("Status") }}</td>
|
:src="report.reporter.avatar.url"
|
||||||
<td>
|
alt=""
|
||||||
<span v-if="report.status === ReportStatusEnum.OPEN">{{ $t("Open") }}</span>
|
/>
|
||||||
<span v-else-if="report.status === ReportStatusEnum.CLOSED">
|
@{{ report.reporter.preferredUsername }}
|
||||||
{{ $t("Closed") }}
|
</router-link>
|
||||||
</span>
|
</td>
|
||||||
<span v-else-if="report.status === ReportStatusEnum.RESOLVED">
|
</tr>
|
||||||
{{ $t("Resolved") }}
|
<tr>
|
||||||
</span>
|
<td>{{ $t("Reported") }}</td>
|
||||||
<span v-else>{{ $t("Unknown") }}</span>
|
<td>{{ report.insertedAt | formatDateTimeString }}</td>
|
||||||
</td>
|
</tr>
|
||||||
</tr>
|
<tr v-if="report.updatedAt !== report.insertedAt">
|
||||||
<tr v-if="report.event && report.comments.length > 0">
|
<td>{{ $t("Updated") }}</td>
|
||||||
<td>{{ $t("Event") }}</td>
|
<td>{{ report.updatedAt | formatDateTimeString }}</td>
|
||||||
<td>
|
</tr>
|
||||||
<router-link :to="{ name: RouteName.EVENT, params: { uuid: report.event.uuid } }">
|
<tr>
|
||||||
{{ report.event.title }}
|
<td>{{ $t("Status") }}</td>
|
||||||
</router-link>
|
<td>
|
||||||
<span class="is-pulled-right">
|
<span v-if="report.status === ReportStatusEnum.OPEN">{{ $t("Open") }}</span>
|
||||||
<!-- <b-button-->
|
<span v-else-if="report.status === ReportStatusEnum.CLOSED">
|
||||||
<!-- tag="router-link"-->
|
{{ $t("Closed") }}
|
||||||
<!-- type="is-primary"-->
|
</span>
|
||||||
<!-- :to="{ name: RouteName.EDIT_EVENT, params: {eventId: report.event.uuid } }"-->
|
<span v-else-if="report.status === ReportStatusEnum.RESOLVED">
|
||||||
<!-- icon-left="pencil"-->
|
{{ $t("Resolved") }}
|
||||||
<!-- size="is-small">{{ $t('Edit') }}</b-button>-->
|
</span>
|
||||||
|
<span v-else>{{ $t("Unknown") }}</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr v-if="report.event && report.comments.length > 0">
|
||||||
|
<td>{{ $t("Event") }}</td>
|
||||||
|
<td>
|
||||||
|
<router-link :to="{ name: RouteName.EVENT, params: { uuid: report.event.uuid } }">
|
||||||
|
{{ report.event.title }}
|
||||||
|
</router-link>
|
||||||
|
<span class="is-pulled-right">
|
||||||
|
<!-- <b-button-->
|
||||||
|
<!-- tag="router-link"-->
|
||||||
|
<!-- type="is-primary"-->
|
||||||
|
<!-- :to="{ name: RouteName.EDIT_EVENT, params: {eventId: report.event.uuid } }"-->
|
||||||
|
<!-- icon-left="pencil"-->
|
||||||
|
<!-- size="is-small">{{ $t('Edit') }}</b-button>-->
|
||||||
|
<b-button
|
||||||
|
type="is-danger"
|
||||||
|
@click="confirmEventDelete()"
|
||||||
|
icon-left="delete"
|
||||||
|
size="is-small"
|
||||||
|
>{{ $t("Delete") }}</b-button
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box report-content">
|
||||||
|
<p v-if="report.content" v-html="nl2br(report.content)" />
|
||||||
|
<p v-else>{{ $t("No comment") }}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box" v-if="report.event && report.comments.length === 0">
|
||||||
|
<router-link :to="{ name: RouteName.EVENT, params: { uuid: report.event.uuid } }">
|
||||||
|
<h3 class="title">{{ report.event.title }}</h3>
|
||||||
|
<p v-html="report.event.description" />
|
||||||
|
</router-link>
|
||||||
|
<!-- <b-button-->
|
||||||
|
<!-- tag="router-link"-->
|
||||||
|
<!-- type="is-primary"-->
|
||||||
|
<!-- :to="{ name: RouteName.EDIT_EVENT, params: {eventId: report.event.uuid } }"-->
|
||||||
|
<!-- icon-left="pencil"-->
|
||||||
|
<!-- size="is-small">{{ $t('Edit') }}</b-button>-->
|
||||||
|
<b-button
|
||||||
|
type="is-danger"
|
||||||
|
@click="confirmEventDelete()"
|
||||||
|
icon-left="delete"
|
||||||
|
size="is-small"
|
||||||
|
>{{ $t("Delete") }}</b-button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ul v-for="comment in report.comments" v-if="report.comments.length > 0" :key="comment.id">
|
||||||
|
<li>
|
||||||
|
<div class="box" v-if="comment">
|
||||||
|
<article class="media">
|
||||||
|
<div class="media-left">
|
||||||
|
<figure class="image is-48x48" v-if="comment.actor && comment.actor.avatar">
|
||||||
|
<img :src="comment.actor.avatar.url" alt="Image" />
|
||||||
|
</figure>
|
||||||
|
<b-icon class="media-left" v-else size="is-large" icon="account-circle" />
|
||||||
|
</div>
|
||||||
|
<div class="media-content">
|
||||||
|
<div class="content">
|
||||||
|
<span v-if="comment.actor">
|
||||||
|
<strong>{{ comment.actor.name }}</strong>
|
||||||
|
<small>@{{ comment.actor.preferredUsername }}</small>
|
||||||
|
</span>
|
||||||
|
<span v-else>{{ $t("Unknown actor") }}</span>
|
||||||
|
<br />
|
||||||
|
<p v-html="comment.text" />
|
||||||
|
</div>
|
||||||
<b-button
|
<b-button
|
||||||
type="is-danger"
|
type="is-danger"
|
||||||
@click="confirmEventDelete()"
|
@click="confirmCommentDelete(comment)"
|
||||||
icon-left="delete"
|
icon-left="delete"
|
||||||
size="is-small"
|
size="is-small"
|
||||||
>{{ $t("Delete") }}</b-button
|
>{{ $t("Delete") }}</b-button
|
||||||
>
|
>
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="box report-content">
|
|
||||||
<p v-if="report.content" v-html="nl2br(report.content)" />
|
|
||||||
<p v-else>{{ $t("No comment") }}</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="box" v-if="report.event && report.comments.length === 0">
|
|
||||||
<router-link :to="{ name: RouteName.EVENT, params: { uuid: report.event.uuid } }">
|
|
||||||
<h3 class="title">{{ report.event.title }}</h3>
|
|
||||||
<p v-html="report.event.description" />
|
|
||||||
</router-link>
|
|
||||||
<!-- <b-button-->
|
|
||||||
<!-- tag="router-link"-->
|
|
||||||
<!-- type="is-primary"-->
|
|
||||||
<!-- :to="{ name: RouteName.EDIT_EVENT, params: {eventId: report.event.uuid } }"-->
|
|
||||||
<!-- icon-left="pencil"-->
|
|
||||||
<!-- size="is-small">{{ $t('Edit') }}</b-button>-->
|
|
||||||
<b-button
|
|
||||||
type="is-danger"
|
|
||||||
@click="confirmEventDelete()"
|
|
||||||
icon-left="delete"
|
|
||||||
size="is-small"
|
|
||||||
>{{ $t("Delete") }}</b-button
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ul v-for="comment in report.comments" v-if="report.comments.length > 0" :key="comment.id">
|
|
||||||
<li>
|
|
||||||
<div class="box" v-if="comment">
|
|
||||||
<article class="media">
|
|
||||||
<div class="media-left">
|
|
||||||
<figure class="image is-48x48" v-if="comment.actor && comment.actor.avatar">
|
|
||||||
<img :src="comment.actor.avatar.url" alt="Image" />
|
|
||||||
</figure>
|
|
||||||
<b-icon class="media-left" v-else size="is-large" icon="account-circle" />
|
|
||||||
</div>
|
|
||||||
<div class="media-content">
|
|
||||||
<div class="content">
|
|
||||||
<span v-if="comment.actor">
|
|
||||||
<strong>{{ comment.actor.name }}</strong>
|
|
||||||
<small>@{{ comment.actor.preferredUsername }}</small>
|
|
||||||
</span>
|
|
||||||
<span v-else>{{ $t("Unknown actor") }}</span>
|
|
||||||
<br />
|
|
||||||
<p v-html="comment.text" />
|
|
||||||
</div>
|
</div>
|
||||||
<b-button
|
</article>
|
||||||
type="is-danger"
|
</div>
|
||||||
@click="confirmCommentDelete(comment)"
|
</li>
|
||||||
icon-left="delete"
|
</ul>
|
||||||
size="is-small"
|
|
||||||
>{{ $t("Delete") }}</b-button
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<h2 class="title" v-if="report.notes.length > 0">{{ $t("Notes") }}</h2>
|
<h2 class="title" v-if="report.notes.length > 0">{{ $t("Notes") }}</h2>
|
||||||
<div class="box note" v-for="note in report.notes" :id="`note-${note.id}`" :key="note.id">
|
<div class="box note" v-for="note in report.notes" :id="`note-${note.id}`" :key="note.id">
|
||||||
<p>{{ note.content }}</p>
|
<p>{{ note.content }}</p>
|
||||||
<router-link :to="{ name: RouteName.ADMIN_PROFILE, params: { id: note.moderator.id } }">
|
<router-link :to="{ name: RouteName.ADMIN_PROFILE, params: { id: note.moderator.id } }">
|
||||||
<img alt class="image" :src="note.moderator.avatar.url" v-if="note.moderator.avatar" />
|
<img alt class="image" :src="note.moderator.avatar.url" v-if="note.moderator.avatar" />
|
||||||
@{{ note.moderator.preferredUsername }}
|
@{{ note.moderator.preferredUsername }}
|
||||||
</router-link>
|
</router-link>
|
||||||
<br />
|
<br />
|
||||||
<small>
|
<small>
|
||||||
<a :href="`#note-${note.id}`" v-if="note.insertedAt">
|
<a :href="`#note-${note.id}`" v-if="note.insertedAt">
|
||||||
{{ note.insertedAt | formatDateTimeString }}
|
{{ note.insertedAt | formatDateTimeString }}
|
||||||
</a>
|
</a>
|
||||||
</small>
|
</small>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form @submit="addNote()">
|
||||||
|
<b-field :label="$t('New note')" label-for="newNoteInput">
|
||||||
|
<b-input type="textarea" v-model="noteContent" id="newNoteInput"></b-input>
|
||||||
|
</b-field>
|
||||||
|
<b-button type="submit" @click="addNote">{{ $t("Add a note") }}</b-button>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
</section>
|
||||||
<form @submit="addNote()">
|
</div>
|
||||||
<b-field :label="$t('New note')" label-for="newNoteInput">
|
|
||||||
<b-input type="textarea" v-model="noteContent" id="newNoteInput"></b-input>
|
|
||||||
</b-field>
|
|
||||||
<b-button type="submit" @click="addNote">{{ $t("Add a note") }}</b-button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Component, Prop, Vue } from "vue-property-decorator";
|
import { Component, Prop, Vue } from "vue-property-decorator";
|
||||||
|
|
|
@ -1,35 +1,47 @@
|
||||||
<template>
|
<template>
|
||||||
<section>
|
<div>
|
||||||
<b-field>
|
<nav class="breadcrumb" aria-label="breadcrumbs">
|
||||||
<b-radio-button v-model="filterReports" :native-value="ReportStatusEnum.OPEN">{{
|
<ul>
|
||||||
$t("Open")
|
<li>
|
||||||
}}</b-radio-button>
|
<router-link :to="{ name: RouteName.MODERATION }">{{ $t("Moderation") }}</router-link>
|
||||||
<b-radio-button v-model="filterReports" :native-value="ReportStatusEnum.RESOLVED">{{
|
</li>
|
||||||
$t("Resolved")
|
<li class="is-active">
|
||||||
}}</b-radio-button>
|
<router-link :to="{ name: RouteName.REPORTS }">{{ $t("Reports") }}</router-link>
|
||||||
<b-radio-button v-model="filterReports" :native-value="ReportStatusEnum.CLOSED">{{
|
</li>
|
||||||
$t("Closed")
|
</ul>
|
||||||
}}</b-radio-button>
|
</nav>
|
||||||
</b-field>
|
<section>
|
||||||
<ul v-if="reports.length > 0">
|
<b-field>
|
||||||
<li v-for="report in reports" :key="report.id">
|
<b-radio-button v-model="filterReports" :native-value="ReportStatusEnum.OPEN">{{
|
||||||
<router-link :to="{ name: RouteName.REPORT, params: { reportId: report.id } }">
|
$t("Open")
|
||||||
<report-card :report="report" />
|
}}</b-radio-button>
|
||||||
</router-link>
|
<b-radio-button v-model="filterReports" :native-value="ReportStatusEnum.RESOLVED">{{
|
||||||
</li>
|
$t("Resolved")
|
||||||
</ul>
|
}}</b-radio-button>
|
||||||
<div v-else>
|
<b-radio-button v-model="filterReports" :native-value="ReportStatusEnum.CLOSED">{{
|
||||||
<b-message v-if="filterReports === ReportStatusEnum.OPEN" type="is-info">
|
$t("Closed")
|
||||||
{{ $t("No open reports yet") }}
|
}}</b-radio-button>
|
||||||
</b-message>
|
</b-field>
|
||||||
<b-message v-if="filterReports === ReportStatusEnum.RESOLVED" type="is-info">
|
<ul v-if="reports.length > 0">
|
||||||
{{ $t("No resolved reports yet") }}
|
<li v-for="report in reports" :key="report.id">
|
||||||
</b-message>
|
<router-link :to="{ name: RouteName.REPORT, params: { reportId: report.id } }">
|
||||||
<b-message v-if="filterReports === ReportStatusEnum.CLOSED" type="is-info">
|
<report-card :report="report" />
|
||||||
{{ $t("No closed reports yet") }}
|
</router-link>
|
||||||
</b-message>
|
</li>
|
||||||
</div>
|
</ul>
|
||||||
</section>
|
<div v-else>
|
||||||
|
<b-message v-if="filterReports === ReportStatusEnum.OPEN" type="is-info">
|
||||||
|
{{ $t("No open reports yet") }}
|
||||||
|
</b-message>
|
||||||
|
<b-message v-if="filterReports === ReportStatusEnum.RESOLVED" type="is-info">
|
||||||
|
{{ $t("No resolved reports yet") }}
|
||||||
|
</b-message>
|
||||||
|
<b-message v-if="filterReports === ReportStatusEnum.CLOSED" type="is-info">
|
||||||
|
{{ $t("No closed reports yet") }}
|
||||||
|
</b-message>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Component, Prop, Vue, Watch } from "vue-property-decorator";
|
import { Component, Prop, Vue, Watch } from "vue-property-decorator";
|
||||||
|
|
|
@ -2,19 +2,8 @@
|
||||||
<div class="section container">
|
<div class="section container">
|
||||||
<h1 class="title">{{ $t("Settings") }}</h1>
|
<h1 class="title">{{ $t("Settings") }}</h1>
|
||||||
<div class="columns">
|
<div class="columns">
|
||||||
<SettingsMenu class="column is-one-quarter-desktop" :menu="menu" />
|
<SettingsMenu class="column is-one-quarter-desktop" />
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<nav class="breadcrumb" aria-label="breadcrumbs">
|
|
||||||
<ul>
|
|
||||||
<li
|
|
||||||
v-for="route in routes.get($route.name)"
|
|
||||||
:class="{ 'is-active': route.to.name === $route.name }"
|
|
||||||
:key="route.to.name"
|
|
||||||
>
|
|
||||||
<router-link :to="{ name: route.to.name }">{{ route.title }}</router-link>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
<router-view />
|
<router-view />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -25,7 +14,6 @@ import { Component, Vue, Watch } from "vue-property-decorator";
|
||||||
import { Route } from "vue-router";
|
import { Route } from "vue-router";
|
||||||
import SettingsMenu from "../components/Settings/SettingsMenu.vue";
|
import SettingsMenu from "../components/Settings/SettingsMenu.vue";
|
||||||
import RouteName from "../router/name";
|
import RouteName from "../router/name";
|
||||||
import { ISettingMenuSection } from "../types/setting-menu.model";
|
|
||||||
import { IPerson, Person } from "../types/actor";
|
import { IPerson, Person } from "../types/actor";
|
||||||
import { IDENTITIES } from "../graphql/actor";
|
import { IDENTITIES } from "../graphql/actor";
|
||||||
import { CURRENT_USER_CLIENT } from "../graphql/user";
|
import { CURRENT_USER_CLIENT } from "../graphql/user";
|
||||||
|
@ -44,148 +32,9 @@ import { ICurrentUser, ICurrentUserRole } from "../types/current-user.model";
|
||||||
export default class Settings extends Vue {
|
export default class Settings extends Vue {
|
||||||
RouteName = RouteName;
|
RouteName = RouteName;
|
||||||
|
|
||||||
menu: ISettingMenuSection[] = [];
|
|
||||||
|
|
||||||
identities!: IPerson[];
|
identities!: IPerson[];
|
||||||
|
|
||||||
newIdentity!: ISettingMenuSection;
|
|
||||||
|
|
||||||
currentUser!: ICurrentUser;
|
currentUser!: ICurrentUser;
|
||||||
|
|
||||||
mounted() {
|
|
||||||
this.newIdentity = {
|
|
||||||
title: this.$t("New profile") as string,
|
|
||||||
to: { name: RouteName.CREATE_IDENTITY } as Route,
|
|
||||||
};
|
|
||||||
this.menu = [
|
|
||||||
{
|
|
||||||
title: this.$t("Account") as string,
|
|
||||||
to: { name: RouteName.ACCOUNT_SETTINGS } as Route,
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
title: this.$t("General") as string,
|
|
||||||
to: { name: RouteName.ACCOUNT_SETTINGS_GENERAL } as Route,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: this.$t("Preferences") as string,
|
|
||||||
to: { name: RouteName.PREFERENCES } as Route,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: this.$t("Email notifications") as string,
|
|
||||||
to: { name: RouteName.NOTIFICATIONS } as Route,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: this.$t("Profiles") as string,
|
|
||||||
to: { name: RouteName.IDENTITIES } as Route,
|
|
||||||
items: [this.newIdentity],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
if (
|
|
||||||
[ICurrentUserRole.MODERATOR, ICurrentUserRole.ADMINISTRATOR].includes(this.currentUser.role)
|
|
||||||
) {
|
|
||||||
this.menu.push({
|
|
||||||
title: this.$t("Moderation") as string,
|
|
||||||
to: { name: RouteName.MODERATION } as Route,
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
title: this.$t("Reports") as string,
|
|
||||||
to: { name: RouteName.REPORTS } as Route,
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
title: this.$t("Report") as string,
|
|
||||||
to: { name: RouteName.REPORT } as Route,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: this.$t("Moderation log") as string,
|
|
||||||
to: { name: RouteName.REPORT_LOGS } as Route,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: this.$t("Users") as string,
|
|
||||||
to: { name: RouteName.USERS } as Route,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: this.$t("Profiles") as string,
|
|
||||||
to: { name: RouteName.PROFILES } as Route,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (this.currentUser.role === ICurrentUserRole.ADMINISTRATOR) {
|
|
||||||
this.menu.push({
|
|
||||||
title: this.$t("Admin") as string,
|
|
||||||
to: { name: RouteName.ADMIN } as Route,
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
title: this.$t("Dashboard") as string,
|
|
||||||
to: { name: RouteName.ADMIN_DASHBOARD } as Route,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: this.$t("Instance settings") as string,
|
|
||||||
to: { name: RouteName.ADMIN_SETTINGS } as Route,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: this.$t("Federation") as string,
|
|
||||||
to: { name: RouteName.RELAYS } as Route,
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
title: this.$t("Followings") as string,
|
|
||||||
to: { name: RouteName.RELAY_FOLLOWINGS } as Route,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: this.$t("Followers") as string,
|
|
||||||
to: { name: RouteName.RELAY_FOLLOWERS } as Route,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Watch("identities")
|
|
||||||
updateIdentities(identities: IPerson[]) {
|
|
||||||
if (!identities) return;
|
|
||||||
if (!this.menu[1].items) return;
|
|
||||||
this.menu[1].items = [];
|
|
||||||
this.menu[1].items.push(
|
|
||||||
...identities.map((identity: IPerson) => ({
|
|
||||||
to: ({
|
|
||||||
name: RouteName.UPDATE_IDENTITY,
|
|
||||||
params: { identityName: identity.preferredUsername },
|
|
||||||
} as unknown) as Route,
|
|
||||||
title: `@${identity.preferredUsername}`,
|
|
||||||
}))
|
|
||||||
);
|
|
||||||
this.menu[1].items.push(this.newIdentity);
|
|
||||||
}
|
|
||||||
|
|
||||||
get routes(): Map<string, Route[]> {
|
|
||||||
return this.getPath(this.menu);
|
|
||||||
}
|
|
||||||
|
|
||||||
getPath(object: ISettingMenuSection[]) {
|
|
||||||
function iter(menu: ISettingMenuSection[] | ISettingMenuSection, acc: ISettingMenuSection[]) {
|
|
||||||
if (Array.isArray(menu)) {
|
|
||||||
return menu.forEach((item: ISettingMenuSection) => {
|
|
||||||
iter(item, acc.concat(item));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (menu.items && menu.items.length > 0) {
|
|
||||||
return menu.items.forEach((item: ISettingMenuSection) => {
|
|
||||||
iter(item, acc.concat(item));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
result.set(menu.to.name, acc);
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = new Map();
|
|
||||||
iter(object, []);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -1,145 +1,159 @@
|
||||||
<template>
|
<template>
|
||||||
<section>
|
<div>
|
||||||
<div class="setting-title">
|
<nav class="breadcrumb" aria-label="breadcrumbs">
|
||||||
<h2>{{ $t("Email") }}</h2>
|
<ul>
|
||||||
</div>
|
<li>
|
||||||
<i18n
|
<router-link :to="{ name: RouteName.ACCOUNT_SETTINGS }">{{ $t("Account") }}</router-link>
|
||||||
tag="p"
|
</li>
|
||||||
class="content"
|
<li class="is-active">
|
||||||
v-if="loggedUser"
|
<router-link :to="{ name: RouteName.ACCOUNT_SETTINGS_GENERAL }">{{
|
||||||
path="Your current email is {email}. You use it to log in."
|
$t("General")
|
||||||
>
|
}}</router-link>
|
||||||
<b slot="email">{{ loggedUser.email }}</b>
|
</li>
|
||||||
</i18n>
|
</ul>
|
||||||
<b-notification
|
</nav>
|
||||||
type="is-danger"
|
<section>
|
||||||
has-icon
|
<div class="setting-title">
|
||||||
aria-close-label="Close notification"
|
<h2>{{ $t("Email") }}</h2>
|
||||||
role="alert"
|
</div>
|
||||||
:key="error"
|
<i18n
|
||||||
v-for="error in changeEmailErrors"
|
tag="p"
|
||||||
>{{ error }}</b-notification
|
class="content"
|
||||||
>
|
v-if="loggedUser"
|
||||||
<form @submit.prevent="resetEmailAction" ref="emailForm" class="form">
|
path="Your current email is {email}. You use it to log in."
|
||||||
<b-field :label="$t('New email')">
|
|
||||||
<b-input aria-required="true" required type="email" v-model="newEmail" />
|
|
||||||
</b-field>
|
|
||||||
<p class="help">{{ $t("You'll receive a confirmation email.") }}</p>
|
|
||||||
<b-field :label="$t('Password')">
|
|
||||||
<b-input
|
|
||||||
aria-required="true"
|
|
||||||
required
|
|
||||||
type="password"
|
|
||||||
password-reveal
|
|
||||||
minlength="6"
|
|
||||||
v-model="passwordForEmailChange"
|
|
||||||
/>
|
|
||||||
</b-field>
|
|
||||||
<button
|
|
||||||
class="button is-primary"
|
|
||||||
:disabled="!($refs.emailForm && $refs.emailForm.checkValidity())"
|
|
||||||
>
|
>
|
||||||
{{ $t("Change my email") }}
|
<b slot="email">{{ loggedUser.email }}</b>
|
||||||
</button>
|
</i18n>
|
||||||
</form>
|
<b-notification
|
||||||
<div class="setting-title">
|
type="is-danger"
|
||||||
<h2>{{ $t("Password") }}</h2>
|
has-icon
|
||||||
</div>
|
aria-close-label="Close notification"
|
||||||
<b-notification
|
role="alert"
|
||||||
type="is-danger"
|
:key="error"
|
||||||
has-icon
|
v-for="error in changeEmailErrors"
|
||||||
aria-close-label="Close notification"
|
>{{ error }}</b-notification
|
||||||
role="alert"
|
|
||||||
:key="error"
|
|
||||||
v-for="error in changePasswordErrors"
|
|
||||||
>{{ error }}</b-notification
|
|
||||||
>
|
|
||||||
<form @submit.prevent="resetPasswordAction" ref="passwordForm" class="form">
|
|
||||||
<b-field :label="$t('Old password')">
|
|
||||||
<b-input
|
|
||||||
aria-required="true"
|
|
||||||
required
|
|
||||||
type="password"
|
|
||||||
password-reveal
|
|
||||||
minlength="6"
|
|
||||||
v-model="oldPassword"
|
|
||||||
/>
|
|
||||||
</b-field>
|
|
||||||
<b-field :label="$t('New password')">
|
|
||||||
<b-input
|
|
||||||
aria-required="true"
|
|
||||||
required
|
|
||||||
type="password"
|
|
||||||
password-reveal
|
|
||||||
minlength="6"
|
|
||||||
v-model="newPassword"
|
|
||||||
/>
|
|
||||||
</b-field>
|
|
||||||
<button
|
|
||||||
class="button is-primary"
|
|
||||||
:disabled="!($refs.passwordForm && $refs.passwordForm.checkValidity())"
|
|
||||||
>
|
>
|
||||||
{{ $t("Change my password") }}
|
<form @submit.prevent="resetEmailAction" ref="emailForm" class="form">
|
||||||
</button>
|
<b-field :label="$t('New email')">
|
||||||
</form>
|
<b-input aria-required="true" required type="email" v-model="newEmail" />
|
||||||
<div class="setting-title">
|
</b-field>
|
||||||
<h2>{{ $t("Delete account") }}</h2>
|
<p class="help">{{ $t("You'll receive a confirmation email.") }}</p>
|
||||||
</div>
|
<b-field :label="$t('Password')">
|
||||||
<p class="content">{{ $t("Deleting my account will delete all of my identities.") }}</p>
|
<b-input
|
||||||
<b-button @click="openDeleteAccountModal" type="is-danger">
|
aria-required="true"
|
||||||
{{ $t("Delete my account") }}
|
required
|
||||||
</b-button>
|
type="password"
|
||||||
|
password-reveal
|
||||||
|
minlength="6"
|
||||||
|
v-model="passwordForEmailChange"
|
||||||
|
/>
|
||||||
|
</b-field>
|
||||||
|
<button
|
||||||
|
class="button is-primary"
|
||||||
|
:disabled="!($refs.emailForm && $refs.emailForm.checkValidity())"
|
||||||
|
>
|
||||||
|
{{ $t("Change my email") }}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
<div class="setting-title">
|
||||||
|
<h2>{{ $t("Password") }}</h2>
|
||||||
|
</div>
|
||||||
|
<b-notification
|
||||||
|
type="is-danger"
|
||||||
|
has-icon
|
||||||
|
aria-close-label="Close notification"
|
||||||
|
role="alert"
|
||||||
|
:key="error"
|
||||||
|
v-for="error in changePasswordErrors"
|
||||||
|
>{{ error }}</b-notification
|
||||||
|
>
|
||||||
|
<form @submit.prevent="resetPasswordAction" ref="passwordForm" class="form">
|
||||||
|
<b-field :label="$t('Old password')">
|
||||||
|
<b-input
|
||||||
|
aria-required="true"
|
||||||
|
required
|
||||||
|
type="password"
|
||||||
|
password-reveal
|
||||||
|
minlength="6"
|
||||||
|
v-model="oldPassword"
|
||||||
|
/>
|
||||||
|
</b-field>
|
||||||
|
<b-field :label="$t('New password')">
|
||||||
|
<b-input
|
||||||
|
aria-required="true"
|
||||||
|
required
|
||||||
|
type="password"
|
||||||
|
password-reveal
|
||||||
|
minlength="6"
|
||||||
|
v-model="newPassword"
|
||||||
|
/>
|
||||||
|
</b-field>
|
||||||
|
<button
|
||||||
|
class="button is-primary"
|
||||||
|
:disabled="!($refs.passwordForm && $refs.passwordForm.checkValidity())"
|
||||||
|
>
|
||||||
|
{{ $t("Change my password") }}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
<div class="setting-title">
|
||||||
|
<h2>{{ $t("Delete account") }}</h2>
|
||||||
|
</div>
|
||||||
|
<p class="content">{{ $t("Deleting my account will delete all of my identities.") }}</p>
|
||||||
|
<b-button @click="openDeleteAccountModal" type="is-danger">
|
||||||
|
{{ $t("Delete my account") }}
|
||||||
|
</b-button>
|
||||||
|
|
||||||
<b-modal
|
<b-modal
|
||||||
:active.sync="isDeleteAccountModalActive"
|
:active.sync="isDeleteAccountModalActive"
|
||||||
has-modal-card
|
has-modal-card
|
||||||
full-screen
|
full-screen
|
||||||
:can-cancel="false"
|
:can-cancel="false"
|
||||||
>
|
>
|
||||||
<section class="hero is-primary is-fullheight">
|
<section class="hero is-primary is-fullheight">
|
||||||
<div class="hero-body has-text-centered">
|
<div class="hero-body has-text-centered">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="columns">
|
<div class="columns">
|
||||||
<div class="column is-one-third-desktop is-offset-one-third-desktop">
|
<div class="column is-one-third-desktop is-offset-one-third-desktop">
|
||||||
<h1 class="title">{{ $t("Deleting your Mobilizon account") }}</h1>
|
<h1 class="title">{{ $t("Deleting your Mobilizon account") }}</h1>
|
||||||
<p class="content">
|
<p class="content">
|
||||||
{{
|
{{
|
||||||
$t(
|
$t(
|
||||||
"Are you really sure you want to delete your whole account? You'll lose everything. Identities, settings, events created, messages and participations will be gone forever."
|
"Are you really sure you want to delete your whole account? You'll lose everything. Identities, settings, events created, messages and participations will be gone forever."
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
<br />
|
<br />
|
||||||
<b>{{ $t("There will be no way to recover your data.") }}</b>
|
<b>{{ $t("There will be no way to recover your data.") }}</b>
|
||||||
</p>
|
</p>
|
||||||
<p class="content">
|
<p class="content">
|
||||||
{{ $t("Please enter your password to confirm this action.") }}
|
{{ $t("Please enter your password to confirm this action.") }}
|
||||||
</p>
|
</p>
|
||||||
<form @submit.prevent="deleteAccount">
|
<form @submit.prevent="deleteAccount">
|
||||||
<b-field>
|
<b-field>
|
||||||
<b-input
|
<b-input
|
||||||
type="password"
|
type="password"
|
||||||
v-model="passwordForAccountDeletion"
|
v-model="passwordForAccountDeletion"
|
||||||
password-reveal
|
password-reveal
|
||||||
icon="lock"
|
icon="lock"
|
||||||
:placeholder="$t('Password')"
|
:placeholder="$t('Password')"
|
||||||
/>
|
/>
|
||||||
</b-field>
|
</b-field>
|
||||||
<b-button native-type="submit" type="is-danger" size="is-large">
|
<b-button native-type="submit" type="is-danger" size="is-large">
|
||||||
{{ $t("Delete everything") }}
|
{{ $t("Delete everything") }}
|
||||||
</b-button>
|
</b-button>
|
||||||
</form>
|
</form>
|
||||||
<div class="cancel-button">
|
<div class="cancel-button">
|
||||||
<b-button type="is-light" @click="isDeleteAccountModalActive = false">
|
<b-button type="is-light" @click="isDeleteAccountModalActive = false">
|
||||||
{{ $t("Cancel") }}
|
{{ $t("Cancel") }}
|
||||||
</b-button>
|
</b-button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</section>
|
||||||
</section>
|
</b-modal>
|
||||||
</b-modal>
|
</section>
|
||||||
</section>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
|
|
@ -1,5 +1,17 @@
|
||||||
<template>
|
<template>
|
||||||
<div v-if="loggedUser">
|
<div v-if="loggedUser">
|
||||||
|
<nav class="breadcrumb" aria-label="breadcrumbs">
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<router-link :to="{ name: RouteName.ACCOUNT_SETTINGS }">{{ $t("Account") }}</router-link>
|
||||||
|
</li>
|
||||||
|
<li class="is-active">
|
||||||
|
<router-link :to="{ name: RouteName.NOTIFICATIONS }">{{
|
||||||
|
$t("Email notifications")
|
||||||
|
}}</router-link>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
<section>
|
<section>
|
||||||
<div class="setting-title">
|
<div class="setting-title">
|
||||||
<h2>{{ $t("Participation notifications") }}</h2>
|
<h2>{{ $t("Participation notifications") }}</h2>
|
||||||
|
|
|
@ -1,38 +1,50 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<b-field :label="$t('Language')">
|
<nav class="breadcrumb" aria-label="breadcrumbs">
|
||||||
<b-select
|
<ul>
|
||||||
:loading="!config || !loggedUser"
|
<li>
|
||||||
v-model="$i18n.locale"
|
<router-link :to="{ name: RouteName.ACCOUNT_SETTINGS }">{{ $t("Account") }}</router-link>
|
||||||
:placeholder="$t('Select a language')"
|
</li>
|
||||||
>
|
<li class="is-active">
|
||||||
<option v-for="(language, lang) in languages" :value="lang" :key="lang">
|
<router-link :to="{ name: RouteName.PREFERENCES }">{{ $t("Preferences") }}</router-link>
|
||||||
{{ language }}
|
</li>
|
||||||
</option>
|
</ul>
|
||||||
</b-select>
|
</nav>
|
||||||
</b-field>
|
<div>
|
||||||
<b-field :label="$t('Timezone')">
|
<b-field :label="$t('Language')">
|
||||||
<b-select
|
<b-select
|
||||||
:placeholder="$t('Select a timezone')"
|
:loading="!config || !loggedUser"
|
||||||
:loading="!config || !loggedUser"
|
v-model="$i18n.locale"
|
||||||
v-model="selectedTimezone"
|
:placeholder="$t('Select a language')"
|
||||||
>
|
>
|
||||||
<optgroup :label="group" v-for="(groupTimezones, group) in timezones" :key="group">
|
<option v-for="(language, lang) in languages" :value="lang" :key="lang">
|
||||||
<option
|
{{ language }}
|
||||||
v-for="timezone in groupTimezones"
|
|
||||||
:value="`${group}/${timezone}`"
|
|
||||||
:key="timezone"
|
|
||||||
>
|
|
||||||
{{ sanitize(timezone) }}
|
|
||||||
</option>
|
</option>
|
||||||
</optgroup>
|
</b-select>
|
||||||
</b-select>
|
</b-field>
|
||||||
</b-field>
|
<b-field :label="$t('Timezone')">
|
||||||
<em>{{
|
<b-select
|
||||||
$t("Timezone detected as {timezone}.", {
|
:placeholder="$t('Select a timezone')"
|
||||||
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
:loading="!config || !loggedUser"
|
||||||
})
|
v-model="selectedTimezone"
|
||||||
}}</em>
|
>
|
||||||
|
<optgroup :label="group" v-for="(groupTimezones, group) in timezones" :key="group">
|
||||||
|
<option
|
||||||
|
v-for="timezone in groupTimezones"
|
||||||
|
:value="`${group}/${timezone}`"
|
||||||
|
:key="timezone"
|
||||||
|
>
|
||||||
|
{{ sanitize(timezone) }}
|
||||||
|
</option>
|
||||||
|
</optgroup>
|
||||||
|
</b-select>
|
||||||
|
</b-field>
|
||||||
|
<em>{{
|
||||||
|
$t("Timezone detected as {timezone}.", {
|
||||||
|
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
||||||
|
})
|
||||||
|
}}</em>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
@ -42,6 +54,7 @@ import { USER_SETTINGS, SET_USER_SETTINGS, UPDATE_USER_LOCALE } from "../../grap
|
||||||
import { IConfig } from "../../types/config.model";
|
import { IConfig } from "../../types/config.model";
|
||||||
import { ICurrentUser } from "../../types/current-user.model";
|
import { ICurrentUser } from "../../types/current-user.model";
|
||||||
import langs from "../../i18n/langs.json";
|
import langs from "../../i18n/langs.json";
|
||||||
|
import RouteName from "../../router/name";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
apollo: {
|
apollo: {
|
||||||
|
@ -58,6 +71,8 @@ export default class Preferences extends Vue {
|
||||||
|
|
||||||
locale: string | null = null;
|
locale: string | null = null;
|
||||||
|
|
||||||
|
RouteName = RouteName;
|
||||||
|
|
||||||
@Watch("loggedUser")
|
@Watch("loggedUser")
|
||||||
setSavedTimezone(loggedUser: ICurrentUser) {
|
setSavedTimezone(loggedUser: ICurrentUser) {
|
||||||
if (loggedUser && loggedUser.settings.timezone) {
|
if (loggedUser && loggedUser.settings.timezone) {
|
||||||
|
|
Loading…
Reference in a new issue