mirror of
https://gitlab.com/PronounsPage/PronounsPage.git
synced 2025-09-26 14:32:04 -04:00
Merge branch 'main' into pl-pronouns-new
This commit is contained in:
commit
c6cf5600e0
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<span>
|
||||
<span v-if="range" class="badge bg-primary">{{ event.getRange(year) }}</span>
|
||||
<Flag v-if="event.flag" name="" :alt="$t('flags_alt.' + event.flag.replace(/'/g, '*').replace(/ /g, '_')) || ''" :img="`/flags/${event.flag}.png`"/>
|
||||
<Flag v-if="event.flag" termkey="" name="" :alt="$t('flags_alt.' + event.flag.replace(/'/g, '*').replace(/ /g, '_')) || ''" :img="`/flags/${event.flag}.png`"/>
|
||||
<Icon v-else v="arrow-circle-right"/>
|
||||
<T v-if="$te(`calendar.events.${eventName}`)" :params="{param: eventParam}">calendar.events.{{eventName}}</T>
|
||||
<LinkedText v-else :text="eventName"/>
|
||||
|
108
components/CustomFlagsWidget.vue
Normal file
108
components/CustomFlagsWidget.vue
Normal file
@ -0,0 +1,108 @@
|
||||
<template>
|
||||
<div class="form-group">
|
||||
<draggable tag="ul" v-model="images" handle=".handle" ghostClass="ghost" @end="$emit('input', images)" class="list-unstyled">
|
||||
<li v-for="(image, i) in images" class="mb-4">
|
||||
<div class="input-group mb-1">
|
||||
<button class="btn btn-light border handle" type="button" :aria-label="$t('table.sort')">
|
||||
<Icon v="bars"/>
|
||||
</button>
|
||||
<div class="d-flex flex-grow-1 flex-column">
|
||||
<div class="d-flex">
|
||||
<ImageThumb :id="images[i].value" smallSize="flag" bigSize="flag" size="2.4em"/>
|
||||
<input v-model="images[i].name" type="text" class="form-control"
|
||||
@keyup="update" @change="update"
|
||||
:placeholder="$t('profile.flagsCustomForm.label')"
|
||||
required :maxlength="maxLength"/>
|
||||
</div>
|
||||
<div>
|
||||
<input v-model="images[i].description" type="text" class="form-control form-control-sm"
|
||||
@keyup="update" @change="update"
|
||||
:placeholder="$t('profile.flagsCustomForm.description')"
|
||||
maxlength="512"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<input v-model="images[i].alt" type="text" class="form-control form-control-sm"
|
||||
@keyup="update" @change="update"
|
||||
:placeholder="$t('profile.flagsCustomForm.alt')"
|
||||
maxlength="512"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<input v-model="images[i].link" type="url" class="form-control form-control-sm"
|
||||
@keyup="update" @change="update"
|
||||
:placeholder="$t('profile.flagsCustomForm.link')"
|
||||
/>
|
||||
<p v-if="images[i].link && !isValidLink(images[i].link)" class="small text-danger m-1">
|
||||
<Icon v="exclamation-triangle"/>
|
||||
<span class="ml-1">{{$t('crud.validation.invalidLink')}}</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<button class="btn btn-outline-danger" type="button" @click.prevent="removeFile(image.value)" :aria-label="$t('crud.remove')">
|
||||
<Icon v="times"/>
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
<li slot="footer">
|
||||
<ImageUploader v-if="maxitems === null || value.length < maxitems" multiple :name="name" form @uploaded="addFiles" :sizes="sizes"/>
|
||||
<div v-if="images.length" class="alert alert-info small mt-3 mb-0">
|
||||
<p>
|
||||
<Icon v="info-circle"/>
|
||||
<T>profile.flagsCustomForm.altExample</T><T>quotation.colon</T>
|
||||
</p>
|
||||
<p class="simple">
|
||||
<Flag img="/flags/Progress Pride_.png" name="" :alt="$t('flags_alt.Progress_Pride_')"/>
|
||||
<T>flags_alt.Progress_Pride_</T>
|
||||
</p>
|
||||
</div>
|
||||
</li>
|
||||
<li v-if="maxitems && value.length > maxitems" class="alert alert-danger">
|
||||
<Icon v="exclamation-triangle"/>
|
||||
<T :params="{maxlength: maxitems}" class="ml-1">crud.validation.listMaxLength</T>
|
||||
</li>
|
||||
</draggable>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import draggable from 'vuedraggable'
|
||||
import {curry, isValidLink} from "../src/helpers";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
draggable,
|
||||
},
|
||||
props: {
|
||||
value: {},
|
||||
name: {'default': 'images'},
|
||||
maxLength: {'default': 24},
|
||||
sizes: {'default': 'all'},
|
||||
maxitems: { 'default': null },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
images: this.value,
|
||||
curry,
|
||||
isValidLink,
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value() {
|
||||
this.images = this.value;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
addFiles(fileIds) {
|
||||
this.$emit('input', [...this.images, ...fileIds.map(id => { return {value: id, name: '', description: '', link: '', alt: ''}})]);
|
||||
},
|
||||
async removeFile(id) {
|
||||
await this.$confirm(this.$t('crud.removeConfirm'), 'danger');
|
||||
this.$emit('input', this.images.filter(i => i.value !== id));
|
||||
},
|
||||
update() {
|
||||
this.$emit('input', this.images);
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
@ -1,17 +1,17 @@
|
||||
<template>
|
||||
<span class="flag-wrapper">
|
||||
<a v-if="link" :href="`/${config.nouns.route}/${config.terminology.route}#${link.toLowerCase()}`" :title="alt">
|
||||
<img v-if="missing === false" :src="img" :alt="name" class="flag-mini rounded" @error="missing = true"/>
|
||||
<a v-if="link" :href="link.startsWith('http') ? link : `/${config.nouns.route}/${config.terminology.route}#${link.toLowerCase()}`" target="_blank" rel="noopener" :title="alt">
|
||||
<img v-if="missing === false" :src="img" :alt="alt" class="flag-mini rounded" @error="missing = true"/>
|
||||
<LocaleLink locale="en" v-else link="/blog/missing-flags" class="text-danger"><Icon v="exclamation-circle"/></LocaleLink>
|
||||
<Twemoji><Spelling escape :text="name"/><sup v-if="custom" class="text-muted"><small><Icon v="user"/></small></sup><sup v-if="asterisk" class="text-muted"><small>*</small></sup></Twemoji>
|
||||
</a>
|
||||
<span v-else :title="alt">
|
||||
<img v-if="missing === false" :src="img" :alt="name" class="flag-mini rounded" @error="missing = true"/>
|
||||
<img v-if="missing === false" :src="img" :alt="alt" class="flag-mini rounded" @error="missing = true"/>
|
||||
<LocaleLink locale="en" v-else link="/blog/missing-flags" class="text-danger"><Icon v="exclamation-circle"/></LocaleLink>
|
||||
<Twemoji><Spelling escape :text="name"/><sup v-if="custom" class="text-muted"><small><Icon v="user"/></small></sup><sup v-if="asterisk" class="text-muted"><small>*</small></sup></Twemoji>
|
||||
</span>
|
||||
<span class="flag-preview bg-white rouded p-2 border">
|
||||
<img v-if="missing === false" :src="img" :alt="name" class="rounded" @error="missing = true"/>
|
||||
<img v-if="missing === false" :src="img" :alt="alt" class="rounded" @error="missing = true"/>
|
||||
<LocaleLink locale="en" v-else link="/blog/missing-flags" class="text-danger"><Icon v="exclamation-circle"/></LocaleLink>
|
||||
<span v-if="asterisk" class="alert alert-warning small d-block-force mt-2 mb-0 p-2">
|
||||
*
|
||||
@ -21,6 +21,8 @@
|
||||
<Icon v="user"/>
|
||||
<T>profile.flagsCustomWarning</T>
|
||||
</span>
|
||||
<span v-if="description" class="d-block-force alert p-2"><strong>{{name}}</strong> — {{description}}</span>
|
||||
<span v-if="alt" class="d-block-force alert p-2 small mb-0 text-muted"><T>crud.alt</T><T>quotation.colon</T> {{alt}}</span>
|
||||
</span>
|
||||
</span>
|
||||
</template>
|
||||
@ -28,9 +30,12 @@
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
termkey: { required: true },
|
||||
name: { required: true },
|
||||
alt: { required: true },
|
||||
img: { required: true },
|
||||
description: { },
|
||||
customlink: { },
|
||||
terms: { },
|
||||
custom: { type: Boolean },
|
||||
asterisk: { type: Boolean },
|
||||
@ -42,6 +47,10 @@
|
||||
},
|
||||
computed: {
|
||||
link() {
|
||||
if (this.customlink) {
|
||||
return this.customlink;
|
||||
}
|
||||
|
||||
if (!this.config.terminology.enabled || !(this.config.terminology.published || this.$isGranted('terms'))) {
|
||||
return null;
|
||||
}
|
||||
@ -50,25 +59,25 @@
|
||||
|
||||
for (let term of this.terms || []) {
|
||||
// exact match
|
||||
if (term.key && term.key.toLowerCase() === this.alt.toLowerCase()) {
|
||||
if (term.key && term.key.toLowerCase() === this.termkey.toLowerCase()) {
|
||||
return term.key;
|
||||
}
|
||||
if (term.term.toLowerCase() === this.name.toLowerCase()) {
|
||||
return this.name;
|
||||
}
|
||||
if (term.original.toLowerCase() === this.alt.toLowerCase()) {
|
||||
return this.alt;
|
||||
if (term.original.toLowerCase() === this.termkey.toLowerCase()) {
|
||||
return this.termkey;
|
||||
}
|
||||
|
||||
// fallback
|
||||
if (term.key && term.key.toLowerCase().includes(this.alt.toLowerCase())) {
|
||||
if (term.key && term.key.toLowerCase().includes(this.termkey.toLowerCase())) {
|
||||
fallback = term.key;
|
||||
}
|
||||
if (term.term.toLowerCase().includes(this.name.toLowerCase())) {
|
||||
fallback = this.name;
|
||||
}
|
||||
if (term.original.toLowerCase().includes(this.alt.toLowerCase())) {
|
||||
fallback = this.alt;
|
||||
if (term.original.toLowerCase().includes(this.termkey.toLowerCase())) {
|
||||
fallback = this.termkey;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12,13 +12,15 @@
|
||||
inverse: { type: Boolean }
|
||||
},
|
||||
data() {
|
||||
let values = Array.isArray(this.v) ? this.v : [this.v];
|
||||
values = values.filter(x => !!x);
|
||||
return this.buildQueue(this.v);
|
||||
},
|
||||
watch: {
|
||||
v(v) {
|
||||
const {value, fallback} = this.buildQueue(v);
|
||||
|
||||
return {
|
||||
value: values.shift(),
|
||||
fallbacks: values,
|
||||
};
|
||||
this.value = value;
|
||||
this.fallback = fallback;
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
valueParts() {
|
||||
@ -44,6 +46,19 @@
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
buildQueue(v) {
|
||||
let values = Array.isArray(v) ? v : [v];
|
||||
values = values.filter(x => !!x);
|
||||
|
||||
if (!values.length) {
|
||||
values = ['spacer'];
|
||||
}
|
||||
|
||||
return {
|
||||
value: values.shift(),
|
||||
fallbacks: values,
|
||||
};
|
||||
},
|
||||
fallBack() {
|
||||
if (!this.fallbacks.length) { return; }
|
||||
|
||||
|
@ -41,19 +41,24 @@
|
||||
<ClientOnly>
|
||||
<ul class="list-inline">
|
||||
<li v-for="flag in profile.flags" v-if="allFlags[flag]" class="list-inline-item p-1">
|
||||
<Flag :name="flag.startsWith('-') ? allFlags[flag] : $translateForPronoun(allFlags[flag], mainPronoun)"
|
||||
:alt="$t('flags_alt.' + flag.replace(/'/g, '*').replace(/ /g, '_')) || allFlags[flag]"
|
||||
<Flag :termkey="allFlags[flag]"
|
||||
:name="flag.startsWith('-') ? allFlags[flag] : $translateForPronoun(allFlags[flag], mainPronoun)"
|
||||
:alt="$t('flags_alt.' + flag.replace(/'/g, '*').replace(/ /g, '_'))"
|
||||
:img="`/flags/${flag}.png`"
|
||||
:terms="terms || []"
|
||||
:asterisk="flagsAsterisk.includes(flag)"
|
||||
/>
|
||||
</li>
|
||||
<li v-for="{value: flag, name} in profile.customFlags" class="list-inline-item p-1">
|
||||
<Flag :name="name"
|
||||
:alt="name"
|
||||
<li v-for="{value: flag, name, description, alt, link} in profile.customFlags" class="list-inline-item p-1">
|
||||
<Flag :termkey="name"
|
||||
:name="name"
|
||||
:alt="alt || ''"
|
||||
:img="buildImageUrl(flag, 'flag')"
|
||||
:terms="terms|| []"
|
||||
custom/>
|
||||
custom
|
||||
:description="description"
|
||||
:customlink="link"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</ClientOnly>
|
||||
|
@ -664,6 +664,12 @@ profile:
|
||||
flagsCustom: 'Upload custom flags'
|
||||
flagsCustomWarning: 'This flag has been uploaded by a user. The team of pronouns.page is not responsible for it.'
|
||||
flagsAsterisk: 'This is not a queer identity, but we include it for people who are queer in other ways (eg. straight trans people).'
|
||||
flagsCustomForm:
|
||||
label: 'Label name'
|
||||
description: '(optional) A short description of the label'
|
||||
link: '(optional) Link to more info'
|
||||
alt: '(optional) Alternative text describing the appearance of the flag (see below)'
|
||||
altExample: 'An example of how the alternative text might be phrased'
|
||||
links: 'Links'
|
||||
linksRecommended: 'We recommend linking to'
|
||||
verifiedLinks:
|
||||
@ -787,6 +793,8 @@ crud:
|
||||
validation:
|
||||
genericForm: 'The submitted form is invalid. Correct the issues and send it again.'
|
||||
listMaxLength: 'The maximum number of elements in this list is %maxlength%.'
|
||||
invalidLink: 'Invalid URL format'
|
||||
alt: 'Alt text'
|
||||
|
||||
footer:
|
||||
license: >
|
||||
@ -1173,6 +1181,130 @@ flags:
|
||||
Two_Spirit: 'Two Spirit'
|
||||
Xenogender: 'Xenogender'
|
||||
|
||||
flags_alt:
|
||||
-de-Gay: 'A rectangular flag with six equal-width horizontal stripes: red, orange, yellow, green, blue and purple'
|
||||
-Drag: 'A rectangular flag with three equal-width vertical stripes: light purple, white and blue; emblem on the white field: a pink crown with three pointy ends.'
|
||||
-en-Genderdoe: ''
|
||||
-pl-Dukaizmy: 'A rectangular flag with four equal-width horizontal stripes: yellow, white, purple, black; emblem: a white ghost with black eyes, open mouth, and a speech bubble saying “łu!”'
|
||||
-pl-Gay: 'A rectangular flag with six equal-width horizontal stripes: red, orange, yellow, green, blue and purple'
|
||||
-pl-Rodzaj_neutralny: 'A rectangular flag with four equal-width horizontal stripes: yellow, white, purple, black; emblem: a black moose holding a crowbar.'
|
||||
-pl-Rodzaj_nijaki: 'A rectangular flag with four equal-width horizontal stripes: yellow, white, purple, black; emblem: a black moose holding a crowbar.'
|
||||
_black-ribbon: 'A black ribbon'
|
||||
_Butch: 'A rectangular flag with seven equal-width horizontal stripes, forming a gradient from dark grey blue on top, to white in the middle, to purple on the bottom'
|
||||
_hrc: 'A square logo with red background and a pink equality sign “=”'
|
||||
_law: 'A paragraph symbol: “§”'
|
||||
_mspec_lesbians: 'A rectangular flag with five equal-width horizontal stripes: light pink, magenta, purple, blue, teal'
|
||||
_red-ribbon: 'A red ribbon'
|
||||
_sex-work: 'An open red umbrella'
|
||||
_Unlabelled: 'A rectangular flag with four equal-width horizontal stripes: light green, white, light blue, light orange'
|
||||
_yellow-ribbon: 'A yellow ribbon'
|
||||
_zaimki: 'Logo of pronouns.page: two stylised letters “P”, first one rotated by 180°'
|
||||
Abroromantic: 'A rectangular flag with five equal-width horizontal stripes: tealish green, peppermint green, white, pink, and pinkish red; a heart shape in the middle that makes colours on its inside more vibrant and on the outside – subdued'
|
||||
Abrosexual: 'A rectangular flag with five equal-width horizontal stripes: tealish green, peppermint green, white, pink, and pinkish red'
|
||||
Achillean: ''
|
||||
Agender: 'A rectangular flag with seven equal-width horizontal stripes: black, grey, white, lime green, white, grey, black'
|
||||
Alloromantic_Asexual: ''
|
||||
Ambiamorous: ''
|
||||
Ambiamorous_: ''
|
||||
Anarcha-Queer: 'A rectangular flag split along the positive diagonal; upper-left part pink, bottom-right part black'
|
||||
Androgyne: ''
|
||||
Androsexual: ''
|
||||
Aporagender: ''
|
||||
Archaeopronouns: ''
|
||||
Aroace: 'A rectangular flag with five equal-width horizontal stripes: orange, yellow, white, cyan, dark blue'
|
||||
Aromantic: 'A rectangular flag with five equal-width horizontal stripes: green, light green, white, grey, black'
|
||||
Aromantic_Allosexual: ''
|
||||
Asexual: 'A rectangular flag with four equal-width horizontal stripes: black, grey, white, purple'
|
||||
Autigender: ''
|
||||
Bear: 'A rectangular flag with seven equal-width horizontal stripes: brown, orange, yellow, bright yellow, white, grey, black; emblem in upper hoist quarter: a black footprint of a bear.'
|
||||
Bicurious: ''
|
||||
Bigender: ''
|
||||
Bigender_: ''
|
||||
Biromantic: 'A rectangular flag with the following horizontal stripes: a wide pink, a narrow purple, and a wide blue; a heart shape in the middle that makes colours on its inside more vibrant and on the outside – subdued'
|
||||
Bisexual: 'A rectangular flag with the following horizontal stripes: a wide pink, a narrow purple, and a wide blue'
|
||||
Butch: 'A rectangular flag with seven equal-width horizontal stripes, forming a gradient from dark grey blue on top, to white in the middle, to purple on the bottom'
|
||||
Ceteroromantic: ''
|
||||
Ceterosexual: ''
|
||||
Cis_Ally: 'A rectangular flag with five equal-width horizontal stripes: blue, pink, white, pink, blue; overlaid a black upwards-facing chevron'
|
||||
Demiboy: 'A rectangular flag with seven equal-width horizontal stripes: dark grey, light grey, cyan, white, cyan, light grey, dark grey'
|
||||
Demigender: ''
|
||||
Demigirl: 'A rectangular flag with seven equal-width horizontal stripes: dark grey, light grey, pink, white, pink, light grey, dark grey'
|
||||
Demiromantic: ''
|
||||
Demisexual: ''
|
||||
Diamoric: ''
|
||||
Enbian: ''
|
||||
Fa*afafine: ''
|
||||
Femme: ''
|
||||
Gay: 'A rectangular flag with six equal-width horizontal stripes: red, orange, yellow, green, blue and purple'
|
||||
Gay_: 'A rectangular flag with seven equal-width horizontal stripes: dark green, greenish cyan, teal, white, cyan, purply blue, purple'
|
||||
Gender_Questioning: ''
|
||||
Genderfae: ''
|
||||
Genderfaun: ''
|
||||
Genderfluid: 'A rectangular flag with five equal-width horizontal stripes: pink, white, purple, black, blue'
|
||||
Genderflux: ''
|
||||
Genderqueer: ''
|
||||
Greyaromantic: ''
|
||||
Greyasexual: ''
|
||||
Gynesexual: ''
|
||||
Heteroflexible: 'A rectangular flag with three equal-width horizontal stripes: blue, white and pink; an extra vertical stripe through the middle that’s coloured like a rainbow flag: red, orange, yellow, green, blue, purple'
|
||||
Heteroromantic: 'A rectangular flag with three equal-width horizontal stripes: blue, white and pink; a heart shape in the middle that makes colours on its inside more vibrant and on the outside – subdued'
|
||||
Heterosexual: 'A rectangular flag with three equal-width horizontal stripes: blue, white and pink'
|
||||
Hijra: 'A rectangular flag with following horizontal stripes, from the top: a wide pink, narrow white, crimson and white, wide light blue'
|
||||
Homoflexible: 'A rectangular flag with six equal-width horizontal stripes: red, orange, yellow, green, blue and purple; an extra vertical stripe through the middle that’s coloured like a heterosexual flag: blue, white and pink'
|
||||
Homoromantic: 'A rectangular flag with six equal-width horizontal stripes: red, orange, yellow, green, blue and purple; a heart shape in the middle that makes colours on its inside more vibrant and on the outside – subdued'
|
||||
Intersex: 'A rectangular flag with a yellow background and a purple circle in the centre'
|
||||
Leather_Pride: 'A rectangular flag with nine equal-width horizontal stripes: black, blue, black, blue, white,blue, black, blue, black; emblem in upper hoist quarter: a red heart symbol at an angle'
|
||||
Lesbian: 'A rectangular flag with five equal-width horizontal stripes: orangy red, orange, white, light magenta, dark magenta'
|
||||
Lesbian_: 'A rectangular flag with a purple background and black labret in the middle'
|
||||
Lesbian__: 'A rectangular flag with six equal-width horizontal stripes: red, orange, yellow, green, blue and purple'
|
||||
Lesbian___: 'A rectangular flag with four equal-width horizontal stripes: purple, pink, yellow, green'
|
||||
Lesbiromantic: 'A rectangular flag with five equal-width horizontal stripes: orangy red, orange, white, light magenta, dark magenta; a heart shape in the middle that makes colours on its inside more vibrant and on the outside – subdued'
|
||||
LGBTQ: 'A rectangular flag with six equal-width horizontal stripes: red, orange, yellow, green, blue and purple'
|
||||
Maverique: ''
|
||||
Monoamorous: ''
|
||||
Monogamous: ''
|
||||
Muxe: ''
|
||||
Nebularomantic: ''
|
||||
Neopronouns: ''
|
||||
Neopronouns_: ''
|
||||
Neutrois: ''
|
||||
Nonbinary: 'A rectangular flag with four equal-width horizontal stripes: yellow, white, purple, black'
|
||||
Omniromantic: ''
|
||||
Omnisexual: ''
|
||||
Oriented_Aroace: ''
|
||||
Pangender: ''
|
||||
Panromantic: ''
|
||||
Pansexual: ''
|
||||
Polyamorous: 'A rectangular flag with a background split in two in the shape of a wave: top part crimson, bottom a lighter crimson-pink; emblem in the middle: an ivory white heart composed of the infinity symbol “∞” and a downwards-pointing chevron'
|
||||
Polyamorous_: 'A rectangular flag with three equal-width horizontal stripes: blue, red, black; emblem in the middle: a yellow Green letter pi “π”'
|
||||
Polyamorous__: 'A rectangular flag with four equal-width horizontal stripes: light green, darker green, light blue and dark blue; emblem: a white heart intertwined with the infinity symbol “∞”'
|
||||
Polyamorous___: 'A rectangular flag with three equal-width horizontal stripes: blue, red, black; emblem in the middle: a yellow heart intertwined with the infinity symbol “∞”'
|
||||
Polyromantic: ''
|
||||
Polysexual: ''
|
||||
Pomoromantic: ''
|
||||
Pomosexual: ''
|
||||
Progress_Pride: 'A rectangular flag with six equal-width horizontal stripes: red, orange, yellow, green, blue and purple; a chevron (triangle) on the left side composed of stripes of the following colours (from the left): white, pink, light blue, brown, black'
|
||||
Progress_Pride_: 'A rectangular flag with six equal-width horizontal stripes: red, orange, yellow, green, blue and purple; a chevron (triangle) on the left side composed of stripes of the following colours (from the left): yellow with a purple circle, white, pink, light blue, brown, black'
|
||||
Queer: 'A rectangular flag with six equal-width horizontal stripes: red, orange, yellow, green, blue and purple'
|
||||
Queer_: 'A rectangular flag with ivory white background and two downwards-pointing chevrons: light pink and washed-out purple'
|
||||
Queerian: ''
|
||||
Queerplatonic: ''
|
||||
Quoiromantic: ''
|
||||
Sapphic: ''
|
||||
Sapphic_: ''
|
||||
Sexuality_Questioning: ''
|
||||
Straight_Ally: 'A rectangular flag with six equal-width horizontal stripes: red, orange, yellow, green, blue and purple; overlaid a black upwards-facing chevron'
|
||||
Toric: ''
|
||||
Transfeminine: 'A rectangular flag with seven equal-width horizontal stripes creating a gradient from blue on top, through pink in the middle, to blue on the bottom'
|
||||
Transgender: 'A rectangular flag with five equal-width horizontal stripes: blue, pink, white, pink, blue'
|
||||
Transmasculine: 'A rectangular flag with seven equal-width horizontal stripes creating a gradient from pink on top, through blue in the middle, to pink on the bottom'
|
||||
Transneutral: 'A rectangular flag with seven equal-width horizontal stripes creating a gradient from blue on top, through yellow in the middle, to pink on the bottom'
|
||||
Trigender: ''
|
||||
Trixic: ''
|
||||
Two_Spirit: ''
|
||||
Two_Spirit_: ''
|
||||
Xenogender: ''
|
||||
|
||||
calendar:
|
||||
header: 'Calendar'
|
||||
headerLong: 'Queer Calendar'
|
||||
|
@ -805,6 +805,12 @@ profile:
|
||||
flagsCustom: 'Upload custom flags'
|
||||
flagsCustomWarning: 'This flag has been uploaded by a user. The team of pronouns.page is not responsible for it.'
|
||||
flagsAsterisk: 'This is not a queer identity, but we include it for people who are queer in other ways (eg. straight trans people).'
|
||||
flagsCustomForm:
|
||||
label: 'Label name'
|
||||
description: '(optional) A short description of the label'
|
||||
link: '(optional) Link to more info'
|
||||
alt: '(optional) Alternative text describing the appearance of the flag (see below)'
|
||||
altExample: 'An example of how the alternative text might be phrased'
|
||||
links: 'Links'
|
||||
linksRecommended: 'We recommend linking to'
|
||||
verifiedLinks:
|
||||
@ -932,6 +938,7 @@ crud:
|
||||
validation:
|
||||
genericForm: 'The submitted form is invalid. Correct the issues and send it again.'
|
||||
listMaxLength: 'The maximum number of elements in this list is %maxlength% (more info {/blog/length-validation=in this blog post}).'
|
||||
invalidLink: 'Invalid URL format'
|
||||
|
||||
footer:
|
||||
license: >
|
||||
@ -1314,130 +1321,6 @@ flags:
|
||||
Two_Spirit: 'Two Spirit'
|
||||
Xenogender: 'Xenogender'
|
||||
|
||||
flags_alt:
|
||||
-de-Gay: ''
|
||||
-Drag: ''
|
||||
-en-Genderdoe: ''
|
||||
-pl-Dukaizmy: ''
|
||||
-pl-Gay: ''
|
||||
-pl-Rodzaj_neutralny: ''
|
||||
-pl-Rodzaj_nijaki: ''
|
||||
_black-ribbon: ''
|
||||
_Butch: ''
|
||||
_hrc: ''
|
||||
_law: ''
|
||||
_mspec_lesbians: ''
|
||||
_red-ribbon: ''
|
||||
_sex-work: ''
|
||||
_Unlabelled: ''
|
||||
_yellow-ribbon: ''
|
||||
_zaimki: ''
|
||||
Abroromantic: ''
|
||||
Abrosexual: ''
|
||||
Achillean: ''
|
||||
Agender: ''
|
||||
Alloromantic_Asexual: ''
|
||||
Ambiamorous: ''
|
||||
Ambiamorous_: ''
|
||||
Anarcha-Queer: ''
|
||||
Androgyne: ''
|
||||
Androsexual: ''
|
||||
Aporagender: ''
|
||||
Archaeopronouns: ''
|
||||
Aroace: ''
|
||||
Aromantic: ''
|
||||
Aromantic_Allosexual: ''
|
||||
Asexual: ''
|
||||
Autigender: ''
|
||||
Bear: ''
|
||||
Bicurious: ''
|
||||
Bigender: ''
|
||||
Bigender_: ''
|
||||
Biromantic: ''
|
||||
Bisexual: ''
|
||||
Butch: ''
|
||||
Ceteroromantic: ''
|
||||
Ceterosexual: ''
|
||||
Cis_Ally: ''
|
||||
Demiboy: ''
|
||||
Demigender: ''
|
||||
Demigirl: ''
|
||||
Demiromantic: ''
|
||||
Demisexual: ''
|
||||
Diamoric: ''
|
||||
Enbian: ''
|
||||
Fa*afafine: ''
|
||||
Femme: ''
|
||||
Gay: 'Rectangular flag with six equal-width horizontal stripes: red, orange, yellow, green, blue and purple'
|
||||
Gay_: ''
|
||||
Gender_Questioning: ''
|
||||
Genderfae: ''
|
||||
Genderfaun: ''
|
||||
Genderfluid: ''
|
||||
Genderflux: ''
|
||||
Genderqueer: ''
|
||||
Greyaromantic: ''
|
||||
Greyasexual: ''
|
||||
Gynesexual: ''
|
||||
Heteroflexible: ''
|
||||
Heteroromantic: ''
|
||||
Heterosexual: ''
|
||||
Hijra: ''
|
||||
Homoflexible: ''
|
||||
Homoromantic: ''
|
||||
Intersex: ''
|
||||
Leather_Pride: ''
|
||||
Lesbian: ''
|
||||
Lesbian_: ''
|
||||
Lesbian__: ''
|
||||
Lesbian___: ''
|
||||
Lesbiromantic: ''
|
||||
LGBTQ: 'Rectangular flag with six equal-width horizontal stripes: red, orange, yellow, green, blue and purple; a chevron (triangle) on the left side composed of stripes of the following colours (from the left): white, pink, light blue, brown, black'
|
||||
Maverique: ''
|
||||
Monoamorous: ''
|
||||
Monogamous: ''
|
||||
Muxe: ''
|
||||
Nebularomantic: ''
|
||||
Neopronouns: ''
|
||||
Neopronouns_: ''
|
||||
Neutrois: ''
|
||||
Nonbinary: 'Rectangular flag with four equal-width horizontal stripes: yellow, white, purple, black'
|
||||
Omniromantic: ''
|
||||
Omnisexual: ''
|
||||
Oriented_Aroace: ''
|
||||
Pangender: ''
|
||||
Panromantic: ''
|
||||
Pansexual: ''
|
||||
Polyamorous: ''
|
||||
Polyamorous_: ''
|
||||
Polyamorous__: ''
|
||||
Polyamorous___: ''
|
||||
Polyromantic: ''
|
||||
Polysexual: ''
|
||||
Pomoromantic: ''
|
||||
Pomosexual: ''
|
||||
Progress_Pride: 'Rectangular flag with six equal-width horizontal stripes: red, orange, yellow, green, blue and purple; a chevron (triangle) on the left side composed of stripes of the following colours (from the left): white, pink, light blue, brown, black'
|
||||
Progress_Pride_: 'Rectangular flag with six equal-width horizontal stripes: red, orange, yellow, green, blue and purple; a chevron (triangle) on the left side composed of stripes of the following colours (from the left): yellow with a purple circle, white, pink, light blue, brown, black'
|
||||
Queer: 'Rectangular flag with six equal-width horizontal stripes: red, orange, yellow, green, blue and purple; a chevron (triangle) on the left side composed of stripes of the following colours (from the left): white, pink, light blue, brown, black'
|
||||
Queer_: 'Rectangular flag with ivory white background and two downwards-pointing chevrons: light pink and washed-out purple'
|
||||
Queerian: ''
|
||||
Queerplatonic: ''
|
||||
Quoiromantic: ''
|
||||
Sapphic: ''
|
||||
Sapphic_: ''
|
||||
Sexuality_Questioning: ''
|
||||
Straight_Ally: ''
|
||||
Toric: ''
|
||||
Transfeminine: ''
|
||||
Transgender: ''
|
||||
Transmasculine: ''
|
||||
Transneutral: ''
|
||||
Trigender: ''
|
||||
Trixic: ''
|
||||
Two_Spirit: ''
|
||||
Two_Spirit_: ''
|
||||
Xenogender: ''
|
||||
|
||||
calendar:
|
||||
header: 'Calendar'
|
||||
headerLong: 'Queer Calendar'
|
||||
|
@ -107,4 +107,11 @@ module.exports = [
|
||||
'profile.sensitive.hide',
|
||||
'profile.sensitive.email.subject',
|
||||
'profile.sensitive.email.content',
|
||||
'crud.validation.invalidLink',
|
||||
'profile.flagsCustomForm.label',
|
||||
'profile.flagsCustomForm.description',
|
||||
'profile.flagsCustomForm.link',
|
||||
'profile.flagsCustomForm.alt',
|
||||
'profile.flagsCustomForm.altExample',
|
||||
'crud.alt',
|
||||
];
|
||||
|
@ -6,7 +6,7 @@ home:
|
||||
link: '홈페이지'
|
||||
header: '대명사'
|
||||
headerLong: '대명사 목록'
|
||||
welcome: 'pronouns.page로 환영합니다!'
|
||||
welcome: 'pronouns.page 로 환영합니다!'
|
||||
intro: >
|
||||
We're creating a source of information about nonbinary and gender neutral language.
|
||||
why: '대명사는 왜 그렇게 중요한가요?'
|
||||
@ -731,8 +731,8 @@ confirm:
|
||||
yes: '예, 확신합니다'
|
||||
no: '아니요, 취소해요'
|
||||
ok: '네'
|
||||
save: 'Save'
|
||||
dismiss: 'Dismiss'
|
||||
save: '변경사항 저장 을'
|
||||
dismiss: '변경사항 제거'
|
||||
|
||||
terms:
|
||||
header: '서비스 약관'
|
||||
@ -790,17 +790,17 @@ terms:
|
||||
queerphobia: 'queerphobia'
|
||||
exclusionism: 'queer exclusionism'
|
||||
sexism: 'sexism'
|
||||
misogyny: 'misogyny'
|
||||
harassment: 'harassment'
|
||||
impersonation: 'impersonation'
|
||||
misogyny: '여성 혐오'
|
||||
harassment: '괴롭힘'
|
||||
impersonation: '사칭'
|
||||
selfHarm: 'encouraging self-harm and/or suicide' # TODO
|
||||
childPornography: 'child pornography'
|
||||
unlawfulConduct: 'unlawful conduct'
|
||||
misinformation: 'misinformation'
|
||||
misinformation: '오보'
|
||||
doxxing: 'sharing of someone else''s personal data'
|
||||
spam: 'spam'
|
||||
trolling: 'trolling'
|
||||
advertisement: 'advertisement'
|
||||
spam: '스팸'
|
||||
trolling: '트롤링'
|
||||
advertisement: '광고'
|
||||
copyright: 'copyright or trademark violations'
|
||||
# TODO
|
||||
violationsStrict: >
|
||||
@ -908,7 +908,7 @@ captcha:
|
||||
|
||||
mode:
|
||||
light: '라이트 모드'
|
||||
automatic: 'Automatic'
|
||||
automatic: '자동적 인'
|
||||
dark: '다크 모드'
|
||||
reducedColours: '감소된 색깔'
|
||||
|
||||
|
@ -1876,7 +1876,7 @@ census:
|
||||
- ['liczba mnoga, rodzaj męskoosobowy', '„byliśmy zmęczeni”']
|
||||
- ['liczba mnoga, rodzaj niemęskoosobowy', '„byłyśmy zmęczone”']
|
||||
- ['liczba mnoga, rodzaj neutralny', '„byłośmy zmęczone”']
|
||||
- ['liczba mnoga, rodzaj postpłciowy', '„byłuśmy zmęczone”']
|
||||
- ['liczba mnoga, rodzaj postpłciowy', '„byłuśmy zmęczony”']
|
||||
- ['unikanie form nacechowanych płciowo', '„dopadło mnie zmęczenie”']
|
||||
aggregates:
|
||||
binarne:
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 114 KiB After Width: | Height: | Size: 113 KiB |
@ -1371,6 +1371,12 @@ profile:
|
||||
flagsCustom: 'Dodaj inne flagi'
|
||||
flagsCustomWarning: 'Ta flaga została wgrana przez osobę użytkującą. Ekipa zaimki.pl nie jest za nią odpowiedzialna.'
|
||||
flagsAsterisk: 'To nie jest queerowa tożsamość, ale uwględniamy ją tutaj przez wzgląd na osoby, które są queerowe na inne sposoby (np. hetero osoby trans).'
|
||||
flagsCustomForm:
|
||||
label: 'Nazwa etykietki'
|
||||
description: '(nieobowiązkowe) Krótki opis etykietki'
|
||||
link: '(nieobowiązkowe) Link do strony z informacjami o etykietce'
|
||||
alt: '(nieobowiązkowe) Opis obrazka dla czytników ekranowych'
|
||||
altExample: 'Przykładowy opis obrazka dla czytników ekranowych'
|
||||
links: 'Linki'
|
||||
linksRecommended: 'Polecamy dodać link do'
|
||||
verifiedLinks:
|
||||
@ -1563,6 +1569,8 @@ crud:
|
||||
validation:
|
||||
genericForm: 'Wysłany formularz jest niepoprawny. Napraw błędy i spróbuj ponownie.'
|
||||
listMaxLength: 'Maksymalna liczba elementów na tej liście to %maxlength%.'
|
||||
invalidLink: 'Niepoprawny format URL'
|
||||
alt: 'Opis obrazka'
|
||||
|
||||
footer:
|
||||
license: >
|
||||
|
@ -219,7 +219,7 @@ export default {
|
||||
LOCALE: config.locale,
|
||||
LOCALES: locales,
|
||||
FLAGS: buildFlags(),
|
||||
FLAGS_ASTERISK: ['Heteroromantic', 'Heterosexual', 'Monoamorous', 'Monogamous'],
|
||||
FLAGS_ASTERISK: ['Heteroromantic', 'Heterosexual', 'Monoamorous', 'Monogamous', 'Cis Ally', 'Straight Ally'],
|
||||
BUCKET: `https://${process.env.AWS_S3_BUCKET}.s3-${process.env.AWS_REGION}.amazonaws.com`,
|
||||
CLOUDFRONT: process.env.CLOUDFRONT,
|
||||
STATS_FILE: process.env.STATS_FILE,
|
||||
|
@ -94,6 +94,7 @@
|
||||
icon: 'id-card',
|
||||
endpoints: {
|
||||
profile_get: ['GET', '/api/profile/get/{username}?version=2', undefined, ['Note that the <code>birthday</code> field will only be available when querying your own account; otherwise only the calucaled <code>age</code> might be available (if the person has filled out their birthday)']],
|
||||
profile_get_by_id: ['GET', '/api/profile/get-id/{id}?version=2', undefined, ['Note that the <code>birthday</code> field will only be available when querying your own account; otherwise only the calucaled <code>age</code> might be available (if the person has filled out their birthday)']],
|
||||
},
|
||||
}, {
|
||||
enabled: this.config.calendar.enabled,
|
||||
|
@ -158,7 +158,7 @@
|
||||
<T>profile.flagsCustom</T>
|
||||
</summary>
|
||||
<div class="border-top">
|
||||
<ImageWidgetRich v-model="customFlags" sizes="flag" :maxitems="128"/>
|
||||
<CustomFlagsWidget v-model="customFlags" sizes="flag" :maxitems="128"/>
|
||||
</div>
|
||||
</details>
|
||||
<PropagateCheckbox field="customFlags" :before="beforeChanges.customFlags" :after="customFlags" v-if="otherProfiles > 0" @change="propagateChanged"/>
|
||||
@ -170,8 +170,16 @@
|
||||
<T>profile.links</T>
|
||||
</template>
|
||||
<template v-slot:links>
|
||||
<ListInput v-model="links" v-slot="s" :maxitems="128">
|
||||
<input v-model="s.val" type="url" class="form-control" @keyup="s.update(s.val)" @paste="$nextTick(() => s.update(s.val))" @change="s.update(s.val)" required/>
|
||||
<ListInput v-model="links" :maxitems="128">
|
||||
<template v-slot="s">
|
||||
<input v-model="s.val" type="url" class="form-control" @keyup="s.update(s.val)" @paste="$nextTick(() => s.update(s.val))" @change="s.update(s.val)" required/>
|
||||
</template>
|
||||
<template v-slot:validation="s">
|
||||
<p v-if="s.val && !isValidLink(s.val)" class="small text-danger">
|
||||
<Icon v="exclamation-triangle"/>
|
||||
<span class="ml-1">{{$t('crud.validation.invalidLink')}}</span>
|
||||
</p>
|
||||
</template>
|
||||
</ListInput>
|
||||
<PropagateCheckbox field="links" :before="beforeChanges.links" :after="links" v-if="otherProfiles > 0" @change="propagateChanged"/>
|
||||
<p class="small text-muted mb-0">
|
||||
@ -278,7 +286,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {head, buildList, buildDict} from "../src/helpers";
|
||||
import {head, buildList, buildDict, isValidLink} from "../src/helpers";
|
||||
import { pronouns } from "~/src/data";
|
||||
import { buildPronoun } from "../src/buildPronoun";
|
||||
import config from '../data/config.suml';
|
||||
@ -411,6 +419,7 @@
|
||||
propagate: [],
|
||||
flagsAsterisk: process.env.FLAGS_ASTERISK,
|
||||
defaultOpinions: opinionsToForm(opinions),
|
||||
isValidLink,
|
||||
};
|
||||
},
|
||||
async asyncData({ app, store }) {
|
||||
|
@ -60,6 +60,13 @@ const isTroll = (answers, writins) => {
|
||||
return false; // no free-text provided
|
||||
}
|
||||
|
||||
const boolToInt = (value) => {
|
||||
if (value === null) { return null; }
|
||||
if (value === true) { return 1; }
|
||||
if (value === false) { return 0; }
|
||||
throw `Invalid value ${value}`
|
||||
}
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.get('/census/finished', handleErrorAsync(async (req, res) => {
|
||||
@ -80,8 +87,8 @@ router.post('/census/submit', handleErrorAsync(async (req, res) => {
|
||||
${req.body.answers},
|
||||
${req.body.writins},
|
||||
${await hasFinished(req)},
|
||||
${isRelevant(answers) ? 1 : 0},
|
||||
${isTroll(answers, writins) ? 1 : 0}
|
||||
${boolToInt(isRelevant(answers))},
|
||||
${boolToInt(isTroll(answers, writins))}
|
||||
)`);
|
||||
|
||||
return res.json(id);
|
||||
|
@ -3,7 +3,7 @@ import SQL from 'sql-template-strings';
|
||||
import md5 from "js-md5";
|
||||
import {ulid} from "ulid";
|
||||
import avatar from "../avatar";
|
||||
import {handleErrorAsync, now} from "../../src/helpers";
|
||||
import {handleErrorAsync, now, isValidLink} from "../../src/helpers";
|
||||
import { caches } from "../../src/cache";
|
||||
import fs from 'fs';
|
||||
import { minBirthdate, maxBirthdate, formatDate, parseDate } from '../../src/birthdate';
|
||||
@ -228,9 +228,36 @@ const fetchCircles = async(db, profileId, userId) => {
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.get('/profile/get/:username', handleErrorAsync(async (req, res) => {
|
||||
const fetchProfilesRoute = async (req, res, user) => {
|
||||
const isSelf = req.user && req.user.username === req.params.username;
|
||||
const isAdmin = req.isGranted('users');
|
||||
|
||||
if (!user || (user.bannedReason !== null && !isAdmin && !isSelf)) {
|
||||
return res.json({
|
||||
profiles: {},
|
||||
});
|
||||
}
|
||||
|
||||
user.emailHash = md5(user.email);
|
||||
delete user.email;
|
||||
user.avatar = await avatar(req.db, user);
|
||||
|
||||
user.bannedTerms = user.bannedTerms ? user.bannedTerms.split(',') : [];
|
||||
|
||||
let profiles = await fetchProfiles(req.db, user.username, isSelf);
|
||||
if (req.query.version !== '2') {
|
||||
for (let [locale, profile] of Object.entries(profiles)) {
|
||||
profiles[locale] = downgradeToV1(profile);
|
||||
}
|
||||
}
|
||||
|
||||
return res.json({
|
||||
...user,
|
||||
profiles,
|
||||
});
|
||||
}
|
||||
|
||||
router.get('/profile/get/:username', handleErrorAsync(async (req, res) => {
|
||||
const user = await req.db.get(SQL`
|
||||
SELECT
|
||||
users.id,
|
||||
@ -245,29 +272,25 @@ router.get('/profile/get/:username', handleErrorAsync(async (req, res) => {
|
||||
WHERE users.usernameNorm = ${normalise(req.params.username)}
|
||||
`);
|
||||
|
||||
if (!user || (user.bannedReason !== null && !isAdmin && !isSelf)) {
|
||||
return res.json({
|
||||
profiles: {},
|
||||
});
|
||||
}
|
||||
return await fetchProfilesRoute(req, res, user);
|
||||
}));
|
||||
|
||||
user.emailHash = md5(user.email);
|
||||
delete user.email;
|
||||
user.avatar = await avatar(req.db, user);
|
||||
router.get('/profile/get-id/:id', handleErrorAsync(async (req, res) => {
|
||||
const user = await req.db.get(SQL`
|
||||
SELECT
|
||||
users.id,
|
||||
users.username,
|
||||
users.email,
|
||||
users.avatarSource,
|
||||
users.bannedReason,
|
||||
users.bannedTerms,
|
||||
users.bannedBy,
|
||||
users.roles != '' AS team
|
||||
FROM users
|
||||
WHERE users.id = ${req.params.id}
|
||||
`);
|
||||
|
||||
user.bannedTerms = user.bannedTerms ? user.bannedTerms.split(',') : [];
|
||||
|
||||
let profiles = await fetchProfiles(req.db, req.params.username, isSelf);
|
||||
if (req.query.version !== '2') {
|
||||
for (let [locale, profile] of Object.entries(profiles)) {
|
||||
profiles[locale] = downgradeToV1(profile);
|
||||
}
|
||||
}
|
||||
|
||||
return res.json({
|
||||
...user,
|
||||
profiles,
|
||||
});
|
||||
return await fetchProfilesRoute(req, res, user);
|
||||
}));
|
||||
|
||||
router.get('/profile/versions/:username', handleErrorAsync(async (req, res) => {
|
||||
@ -369,7 +392,16 @@ router.post('/profile/save', handleErrorAsync(async (req, res) => {
|
||||
const pronouns = req.body.pronouns.map(p => { return {...p, value: p.value.substring(0, 192)}});
|
||||
const description = req.body.description.substring(0, 256);
|
||||
const birthday = cleanupBirthday(req.body.birthday || null);
|
||||
const links = req.body.links.filter(x => !!x);
|
||||
const links = req.body.links.filter(x => !!x && isValidLink(x));
|
||||
const customFlags = req.body.customFlags.filter(x => x.name && (!x.link || isValidLink(x.link))).map(x => {
|
||||
return {
|
||||
value: x.value,
|
||||
name: x.name.substring(0, 24),
|
||||
description: x.description ? x.description.substring(0, 512) : null,
|
||||
alt: x.alt ? x.alt.substring(0, 512) : null,
|
||||
link: x.link ? normaliseUrl(x.link) : null,
|
||||
}
|
||||
});
|
||||
const words = req.body.words.map(c => { return {...c, values: c.values.map(p => { return {...p, value: p.value.substring(0, 32)}})}});
|
||||
const sensitive = req.body.sensitive.filter(x => !!x).map(x => x.substring(0, 64));
|
||||
const timezone = req.body.timezone ? {
|
||||
@ -393,7 +425,7 @@ router.post('/profile/save', handleErrorAsync(async (req, res) => {
|
||||
timezone = ${JSON.stringify(timezone)},
|
||||
links = ${JSON.stringify(links)},
|
||||
flags = ${JSON.stringify(req.body.flags)},
|
||||
customFlags = ${JSON.stringify(req.body.customFlags)},
|
||||
customFlags = ${JSON.stringify(customFlags)},
|
||||
words = ${JSON.stringify(words)},
|
||||
sensitive = ${JSON.stringify(sensitive)},
|
||||
teamName = ${req.isGranted() ? req.body.teamName || null : ''},
|
||||
@ -418,7 +450,7 @@ router.post('/profile/save', handleErrorAsync(async (req, res) => {
|
||||
${JSON.stringify(timezone)},
|
||||
${JSON.stringify(links)},
|
||||
${JSON.stringify(req.body.flags)},
|
||||
${JSON.stringify(req.body.customFlags)},
|
||||
${JSON.stringify(customFlags)},
|
||||
${JSON.stringify(words)},
|
||||
${JSON.stringify(sensitive)},
|
||||
1,
|
||||
|
@ -2,22 +2,39 @@ require('../src/dotenv')();
|
||||
|
||||
const dbConnection = require('./db');
|
||||
const SQL = require('sql-template-strings');
|
||||
const {normaliseUrl} = require('../src/links');
|
||||
const Suml = require('suml');
|
||||
const fs = require("fs");
|
||||
|
||||
const loadSuml = name => new Suml().parse(fs.readFileSync(`${__dirname}/../data/${name}.suml`).toString());
|
||||
const config = loadSuml('config');
|
||||
|
||||
const isTroll = (answers, writins) => {
|
||||
if (Object.values(writins).filter(x => !!x).length) {
|
||||
return null; // unknown, send to moderation
|
||||
}
|
||||
|
||||
for (let i in config.census.questions) {
|
||||
if (config.census.questions[i].type === 'textarea' && answers[i.toString()]) {
|
||||
return null; // unknown, send to moderation
|
||||
}
|
||||
}
|
||||
|
||||
return false; // no free-text provided
|
||||
}
|
||||
|
||||
const boolToInt = (value) => {
|
||||
if (value === null) { return null; }
|
||||
if (value === true) { return 1; }
|
||||
if (value === false) { return 0; }
|
||||
throw `Invalid value ${value}`
|
||||
}
|
||||
|
||||
(async () => {
|
||||
const db = await dbConnection();
|
||||
|
||||
const profiles = await db.all(SQL`SELECT links FROM profiles`);
|
||||
let i = 0;
|
||||
for (let {links} of profiles) {
|
||||
if (i % 1000 === 0) {
|
||||
console.log(`${i}/${profiles.length}`);
|
||||
}
|
||||
i++;
|
||||
for (let url of JSON.parse(links)) {
|
||||
url = normaliseUrl(url);
|
||||
if (!url) { continue; }
|
||||
await db.get(SQL`INSERT INTO links (url) VALUES (${url}) ON CONFLICT (url) DO UPDATE SET expiresAt = null`);
|
||||
}
|
||||
const responses = await db.all(SQL`SELECT * FROM census WHERE edition = 2023 and locale='pl'`);
|
||||
for (let response of responses) {
|
||||
const troll = isTroll(JSON.parse(response.answers), JSON.parse(response.writins));
|
||||
await db.get(SQL`UPDATE census SET troll = ${boolToInt(troll)} WHERE id = ${response.id}`);
|
||||
}
|
||||
})();
|
||||
|
@ -300,3 +300,13 @@ export const findAdmins = async (db, locale, area) => {
|
||||
const admins = await db.all(`SELECT username, email, roles, adminNotifications FROM users WHERE roles != ''`);
|
||||
return admins.filter(admin => isGranted(admin, locale, area));
|
||||
};
|
||||
|
||||
export const isValidLink = (url) => {
|
||||
try {
|
||||
url = new URL(url);
|
||||
return ['http:', 'https:', 'mailto:'].includes(url.protocol);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user