Merge branch 'master' into dev

This commit is contained in:
les 2021-07-28 10:38:58 +02:00
commit 5415db1fdb
No known key found for this signature in database
GPG key ID: 352918250B012177
45 changed files with 1482 additions and 1046 deletions

View file

@ -1,5 +1,34 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
### UNRELEASED
- fix/improve debian install docs
- fix ics export, use new ics package
- use slug url everywhere (rss feed, embedded list)
- use i18n in event confirmation email
- remove lot of deps warning and remove some unused dependencies
- fix show_recurrent in embedded list
- remove old to-ico dep, use png favicon instead
### 1.0.4 (alpha)
- shows a generic img for events without it
### 1.0.3 (alpha)
- 12 hour clock selection, #119
- improve media management
- add alt-text to featured image, fix #106
- add focalPoint support, fix #116
- improve a11y
- improve node v16 compatibility
- fix #122 ? (downgrade prettier)
### 1.0.2 (alpha)
- improve oauth flow UI
- [WordPress plugin](https://wordpress.org/plugins/wpgancio/)
- fix h-event import
- improve error logging (add stack trace to exception)
- choose start date for recurreing events (#120)
- fix user delete from admin
### 1.0.1 (alpha) ### 1.0.1 (alpha)
- fix AP resource removal - fix AP resource removal

View file

@ -8,6 +8,7 @@
:locale='$i18n.locale' :locale='$i18n.locale'
:attributes='attributes' :attributes='attributes'
transition='fade' transition='fade'
aria-label='Calendar'
is-expanded is-expanded
is-inline is-inline
@dayclick='click') @dayclick='click')

View file

@ -11,8 +11,8 @@
v-card-text(v-show='!!message') {{ message }} v-card-text(v-show='!!message') {{ message }}
v-card-actions v-card-actions
v-spacer v-spacer
v-btn(color='error' @click='cancel') {{$t('common.cancel')}} v-btn(text color='error' @click='cancel') {{$t('common.cancel')}}
v-btn(color='primary' @click='agree') {{$t('common.ok')}} v-btn(text color='primary' @click='agree') {{$t('common.ok')}}
</template> </template>
<script> <script>

View file

@ -1,7 +1,7 @@
<template lang="pug"> <template lang="pug">
v-card.h-event.event.d-flex v-card.h-event.event.d-flex
nuxt-link(:to='`/event/${event.slug || event.id}`') nuxt-link(:to='`/event/${event.slug || event.id}`')
v-img.u-featured.img(:src="`/media/thumb/${event.image_path || 'logo.svg' }`") v-img.u-featured.img(aspect-ratio='1.7778' :src='thumbnail' :position='thumbnailPosition' :alt='event.media && event.media.length ? event.media[0].name : ""')
v-icon.float-right.mr-1(v-if='event.parentId' color='success') mdi-repeat v-icon.float-right.mr-1(v-if='event.parentId' color='success') mdi-repeat
.title.p-name {{event.title}} .title.p-name {{event.title}}
@ -22,12 +22,12 @@
v-list(dense) v-list(dense)
v-list-item-group v-list-item-group
v-list-item(v-clipboard:success="() => $root.$message('common.copied', { color: 'success' })" v-list-item(v-clipboard:success="() => $root.$message('common.copied', { color: 'success' })"
v-clipboard:copy='`${settings.baseurl}/event/${event.id}`') v-clipboard:copy='`${settings.baseurl}/event/${event.slug || event.id}`')
v-list-item-icon v-list-item-icon
v-icon mdi-content-copy v-icon mdi-content-copy
v-list-item-content v-list-item-content
v-list-item-title {{$t('common.copy_link')}} v-list-item-title {{$t('common.copy_link')}}
v-list-item(:href='`/api/event/${event.id}.ics`') v-list-item(:href='`/api/event/${event.slug || event.id}.ics`')
v-list-item-icon v-list-item-icon
v-icon mdi-calendar-export v-icon mdi-calendar-export
v-list-item-content v-list-item-content
@ -52,6 +52,22 @@ export default {
}, },
computed: { computed: {
...mapState(['settings']), ...mapState(['settings']),
thumbnail () {
let path
if (this.event.media && this.event.media.length) {
path = '/media/thumb/' + this.event.media[0].url
} else {
path = '/noimg.svg'
}
return path
},
thumbnailPosition () {
if (this.event.media && this.event.media.length && this.event.media[0].focalpoint) {
const focalpoint = this.event.media[0].focalpoint
return `${(focalpoint[0] + 1) * 50}% ${(focalpoint[1] + 1) * 50}%`
}
return 'center center'
},
is_mine () { is_mine () {
if (!this.$auth.user) { if (!this.$auth.user) {
return false return false

View file

@ -1,5 +1,5 @@
<template lang="pug"> <template lang="pug">
v-footer(color='secondary') v-footer(color='secondary' aria-label='Footer')
v-dialog(v-model='showFollowMe' destroy-on-close max-width='700px') v-dialog(v-model='showFollowMe' destroy-on-close max-width='700px')
FollowMe(@close='showFollowMe=false' is-dialog) FollowMe(@close='showFollowMe=false' is-dialog)
@ -20,7 +20,7 @@
:href='instance.url' :href='instance.url'
two-line) two-line)
v-list-item-avatar v-list-item-avatar
v-img(:src='`${instance.url}/favicon.ico`') v-img(:src='`${instance.url}/logo.png`')
v-list-item-content v-list-item-content
v-list-item-title {{instance.name}} v-list-item-title {{instance.name}}
v-list-item-subtitle {{instance.label}} v-list-item-subtitle {{instance.label}}

View file

@ -12,7 +12,6 @@ div#list
v-list-item-subtitle <v-icon small color='success' v-if='event.parentId'>mdi-repeat</v-icon> {{event|when}} v-list-item-subtitle <v-icon small color='success' v-if='event.parentId'>mdi-repeat</v-icon> {{event|when}}
span.primary--text.ml-1 @{{event.place.name}} span.primary--text.ml-1 @{{event.place.name}}
v-list-item-title(v-text='event.title') v-list-item-title(v-text='event.title')
//- a.text-body-1(:href='`/event/${event.id}`' target='_blank') {{event.title}}
</template> </template>
<script> <script>

View file

@ -14,23 +14,23 @@
v-tooltip(bottom) {{$t('common.add_event')}} v-tooltip(bottom) {{$t('common.add_event')}}
template(v-slot:activator='{ on }') template(v-slot:activator='{ on }')
v-btn(v-if='could_add' icon nuxt to='/add' v-on='on') v-btn(v-if='could_add' icon nuxt to='/add' v-on='on' :aria-label='$t("common.add_event")')
v-icon(large color='primary') mdi-plus v-icon(large color='primary') mdi-plus
v-tooltip(bottom) {{$t('common.share')}} v-tooltip(bottom) {{$t('common.share')}}
template(v-slot:activator='{ on }') template(v-slot:activator='{ on }')
v-btn(icon nuxt to='/export' v-on='on') v-btn(icon nuxt to='/export' v-on='on' :aria-label='$t("common.share")')
v-icon mdi-share-variant v-icon mdi-share-variant
v-tooltip(v-if='!$auth.loggedIn' bottom) {{$t('common.login')}} v-tooltip(v-if='!$auth.loggedIn' bottom) {{$t('common.login')}}
template(v-slot:activator='{ on }') template(v-slot:activator='{ on }')
v-btn(icon nuxt to='/login' v-on='on') v-btn(icon nuxt to='/login' v-on='on' :aria-label='$t("common.login")')
v-icon mdi-login v-icon mdi-login
v-menu(v-else v-menu(v-else
offset-y bottom open-on-hover transition="slide-y-transition") offset-y bottom open-on-hover transition="slide-y-transition")
template(v-slot:activator="{ on, attrs }") template(v-slot:activator="{ on, attrs }")
v-btn(icon v-bind='attrs' v-on='on') v-btn(icon v-bind='attrs' v-on='on' aria-label='Menu')
v-icon mdi-dots-vertical v-icon mdi-dots-vertical
v-list v-list
v-list-item(nuxt to='/settings') v-list-item(nuxt to='/settings')
@ -51,7 +51,7 @@
v-list-item-content v-list-item-content
v-list-item-title {{$t('common.logout')}} v-list-item-title {{$t('common.logout')}}
v-btn(icon v-clipboard:copy='feedLink' v-clipboard:success='copyLink') v-btn(icon v-clipboard:copy='feedLink' v-clipboard:success='copyLink' aria-label='RSS')
v-icon(color='orange') mdi-rss v-icon(color='orange') mdi-rss
</template> </template>

View file

@ -9,8 +9,8 @@
accept='image/*') accept='image/*')
template(slot='append-outer') template(slot='append-outer')
v-btn(color='warning' text @click='resetLogo') <v-icon>mdi-restore</v-icon> {{$t('common.reset')}} v-btn(color='warning' text @click='resetLogo') <v-icon>mdi-restore</v-icon> {{$t('common.reset')}}
v-img(:src='`${settings.baseurl}/favicon.ico?${logoKey}`' v-img(:src='`${settings.baseurl}/logo.png?${logoKey}`'
max-width="100px" max-height="80px" contain) max-width="60px" max-height="60px" contain)
//- v-switch.mt-5(v-model='is_dark' //- v-switch.mt-5(v-model='is_dark'
//- inset //- inset

View file

@ -8,6 +8,35 @@ nav_order: 10
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
### UNRELEASED
- fix/improve debian install docs
- fix ics export, use new ics package
- use slug url everywhere (rss feed, embedded list)
- use i18n in event confirmation email
- remove lot of deps warning and remove some unused dependencies
- fix show_recurrent in embedded list
- remove old to-ico dep, use png favicon instead
### 1.0.4 (alpha)
- shows a generic img for events without it
### 1.0.3 (alpha)
- 12 hour clock selection, #119
- improve media management
- add alt-text to featured image, fix #106
- add focalPoint support, fix #116
- improve a11y
- improve node v16 compatibility
- fix #122 ? (downgrade prettier)
### 1.0.2 (alpha)
- improve oauth flow UI
- [WordPress plugin](https://wordpress.org/plugins/wpgancio/)
- fix h-event import
- improve error logging (add stack trace to exception)
- choose start date for recurreing events (#120)
- fix user delete from admin
### 1.0.1 (alpha) ### 1.0.1 (alpha)
- fix AP resource removal - fix AP resource removal

View file

@ -1,6 +1,7 @@
FROM node:buster FROM node:buster
RUN yarn global add --silent https://gancio.org/latest.tgz 2> /dev/null RUN yarn global remove gancio
RUN yarn cache clean gancio
RUN yarn global add --latest --silent https://gancio.org/latest.tgz 2> /dev/null
ADD entrypoint.sh / ADD entrypoint.sh /
RUN chmod 755 /entrypoint.sh RUN chmod 755 /entrypoint.sh
ENTRYPOINT [ "/bin/sh", "/entrypoint.sh" ] ENTRYPOINT [ "/bin/sh", "/entrypoint.sh" ]

View file

@ -7,24 +7,24 @@ parent: Install
## Debian installation ## Debian installation
1. Install Node.js & yarn (**from root**) 1. Install dependencies
```bash ```bash
curl -sL https://deb.nodesource.com/setup_16.x | bash - sudo apt install curl gcc g++ make libpq-dev
apt-get install -y nodejs ```
curl -sL https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list
apt-get update && apt-get install yarn 1. Install Node.js & yarn
```bash
curl -sL https://deb.nodesource.com/setup_16.x | sudo bash -
sudo apt-get install -y nodejs
sudo npm install -g yarn
``` ```
<small>[source](https://github.com/nodesource/distributions/blob/master/README.md)</small> <small>[source](https://github.com/nodesource/distributions/blob/master/README.md)</small>
1. Install Gancio
```bash
yarn global add --silent {{site.url}}/latest.tgz 2> /dev/null
```
1. Setup with postgreSQL __(optional as you can choose sqlite)__ 1. Setup with postgreSQL __(optional as you can choose sqlite)__
```bash ```bash
apt-get install postgresql sudo apt-get install postgresql
# Create the database # Create the database
su postgres -c psql su postgres -c psql
postgres=# create database gancio; postgres=# create database gancio;
@ -34,18 +34,22 @@ postgres=# grant all privileges on database gancio to gancio;
1. Create a user to run gancio from 1. Create a user to run gancio from
```bash ```bash
adduser gancio sudo adduser gancio
su gancio su - gancio
```
1. Install Gancio
```bash
yarn global add --silent {{site.url}}/latest.tgz 2> /dev/null
``` ```
1. Launch interactive setup 1. Launch interactive setup
```bash ```bash
gancio setup --config config.json $(yarn global bin)/gancio setup --config config.json
``` ```
1. Start 1. Start
```bash ```bash
gancio start --config config.json $(yarn global bin)/gancio start --config config.json
``` ```
1. Point your web browser to [http://localhost:13120](http://localhost:13120) or where you selected during setup. 1. Point your web browser to [http://localhost:13120](http://localhost:13120) or where you selected during setup.
@ -63,7 +67,14 @@ sudo pm2 startup -u gancio
``` ```
## Upgrade ## Upgrade
> warning "Backup your data"
> Backup your data is generally a good thing to do and this is especially true before upgrading.
> Don't be lazy and [backup]({% link install/backup.md %}) your data!
```bash ```bash
sudo yarn global add --silent {{site.url}}/latest.tgz 2> /dev/null yarn global remove gancio
yarn cache clean
yarn global add --silent {{site.url}}/latest.tgz 2> /dev/null
sudo service pm2 restart sudo service pm2 restart
``` ```

View file

@ -8,7 +8,7 @@ nav_order: 7
- [gancio.cisti.org](https://gancio.cisti.org) (Turin, Italy) - [gancio.cisti.org](https://gancio.cisti.org) (Turin, Italy)
- [lapunta.org](https://lapunta.org) (Florence, Italy) - [lapunta.org](https://lapunta.org) (Florence, Italy)
- [chesefa.org](https://chesefa.org) (Naples, Italy) - [termine.161.social](https://termine.161.social) (Germany)
<small>Do you want your instance to appear here? [Write us]({% link contact.md %}).</small> <small>Do you want your instance to appear here? [Write us]({% link contact.md %}).</small>

View file

@ -18,5 +18,8 @@
"admin_register": { "admin_register": {
"subject": "New registration", "subject": "New registration",
"content": "{{user.email}} has requested registration on {{config.title}}: <br/><pre>{{user.description}}</pre><br/> Confirm it <a href='{{config.baseurl}}/admin'>here</a>." "content": "{{user.email}} has requested registration on {{config.title}}: <br/><pre>{{user.description}}</pre><br/> Confirm it <a href='{{config.baseurl}}/admin'>here</a>."
} },
"event_confirm": {
"content": "You can confirm this event at <a href='{{url}}'>this page</a>"
}
} }

View file

@ -18,5 +18,8 @@
"admin_register": { "admin_register": {
"subject": "Nuova registrazione", "subject": "Nuova registrazione",
"content": "{{user.email}} si è registratǝ a {{config.title}} scrivendo:<br/><pre>{{user.description}}</pre><br/> Puoi confermarlo <a href='{{config.baseurl}}/admin'>qui</a>." "content": "{{user.email}} si è registratǝ a {{config.title}} scrivendo:<br/><pre>{{user.description}}</pre><br/> Puoi confermarlo <a href='{{config.baseurl}}/admin'>qui</a>."
},
"event_confirm": {
"content": "Puoi confermare questo evento premendo il tasto conferma in <a href='{{url}}'>questa pagina</a>"
} }
} }

View file

@ -129,7 +129,7 @@
"where_description": "Dov'è il gancio? Se il posto non è presente potrai crearlo.", "where_description": "Dov'è il gancio? Se il posto non è presente potrai crearlo.",
"confirmed": "Evento confermato", "confirmed": "Evento confermato",
"not_found": "Evento non trovato", "not_found": "Evento non trovato",
"remove_confirmation": "Sei sicuro/a di voler eliminare questo evento?", "remove_confirmation": "Vuoi eliminare questo evento?",
"remove_recurrent_confirmation": "Sei sicura di voler eliminare questo evento ricorrente?\nGli eventi passati verranno mantenuti ma non ne verranno creati altri.", "remove_recurrent_confirmation": "Sei sicura di voler eliminare questo evento ricorrente?\nGli eventi passati verranno mantenuti ma non ne verranno creati altri.",
"recurrent": "Ricorrente", "recurrent": "Ricorrente",
"edit_recurrent": "Modifica evento ricorrente:", "edit_recurrent": "Modifica evento ricorrente:",
@ -159,7 +159,10 @@
"import_ICS": "Importa da ICS", "import_ICS": "Importa da ICS",
"import_URL": "Importa da URL (ics o h-event)", "import_URL": "Importa da URL (ics o h-event)",
"ics": "ICS", "ics": "ICS",
"import_description": "Puoi importare eventi da altre piattaforme e da altre istanze attraverso i formati standard (ics e h-event)" "import_description": "Puoi importare eventi da altre piattaforme e da altre istanze attraverso i formati standard (ics e h-event)",
"alt_text_description": "Descrizione per utenti con disabilità visive",
"choose_focal_point": "Scegli il punto centrale cliccando",
"remove_media_confirmation": "Confermi l'eliminazione dell'immagine?"
}, },
"admin": { "admin": {
"place_description": "Nel caso in cui un luogo sia errato o cambi indirizzo, puoi modificarlo.<br/>Considera che tutti gli eventi associati a questo luogo cambieranno indirizzo (anche quelli passati).", "place_description": "Nel caso in cui un luogo sia errato o cambi indirizzo, puoi modificarlo.<br/>Considera che tutti gli eventi associati a questo luogo cambieranno indirizzo (anche quelli passati).",

252
locales/nb_NO.json Normal file
View file

@ -0,0 +1,252 @@
{
"oauth": {
"scopes": {
"event:write": "Legg til og rediger dine hendelser"
},
"authorization_request": "Programmet <code>{app}</code> ber om følgende autorisering på <code>{instance_name}</code>:",
"redirected_to": "Etter bekreftelse vil du bli videresendt til <code>{url}</code>"
},
"confirm": {
"title": "Brukerbekreftelse",
"valid": "Kontoen din er bekreftet, du kan nå <a href=\"/login\">logge inn</a>",
"not_valid": "Noe gikk galt."
},
"error": {
"email_taken": "Denne e-postadressen er allerede i bruk",
"nick_taken": "Dette kallenavnet er allerede i bruk"
},
"settings": {
"password_updated": "Passord endret",
"change_password": "Endre passord",
"remove_account": "Ved å trykke på følgende knapp vil din brukerkonto slettes. Hendelser du har offentliggjort vil ikke bli det.",
"danger_section": "Farlig del",
"remove_account_confirm": "Du er i ferd med å slette kontoen din for godt",
"update_confirm": "Ønsker du å lagre endringen?"
},
"auth": {
"not_confirmed": "Ikke bekreftet enda…",
"fail": "Kunne ikke logge inn. Er du sikker på at passordet stemmer?"
},
"admin": {
"new_announcement": "Ny kunngjøring",
"edit_place": "Rediger sted",
"delete_footer_link_confirm": "Er du sikker på at du vil fjerne denne lenken?",
"add_link": "Legg til lenke",
"is_dark": "Mørk drakt",
"instance_locale": "Forvalgt språk",
"announcement_remove_ok": "Kunngjøring fjernet",
"delete_announcement_confirm": "Er du sikker på at du vil fjerne denne kunngjøringen?",
"user_block_confirm": "Er du sikker på at du vil blokkere denne brukeren?",
"favicon": "Logo",
"resources": "Ressurser",
"filter_users": "Filtrer brukere",
"filter_instances": "Filtrer instanser",
"block_user": "Blokker bruker",
"delete_resource_confirm": "Er du sikker på at du vil slette denne ressursen?",
"delete_resource": "Slett ressurs",
"hide_resource": "Skjul ressurs",
"show_resource": "Vis ressurs",
"instance_name": "Instansens navn",
"unblock": "Opphev blokkering",
"block": "Blokker",
"enable_resources": "Skru på ressurser",
"select_instance_timezone": "Tidssone",
"user_create_ok": "Bruker opprettet",
"user_remove_ok": "Bruker fjernet",
"delete_user_confirm": "Er du sikker på at du vil fjerne denne brukeren?",
"remove_admin": "Fjern administrator",
"delete_user": "Fjern",
"place_description": "Hvis du har valgt feil sted eller adresse, kan du endre det. <br/>Alle nåværende og foregående hendelser tilknyttet dette stedet vil endre adresse.",
"footer_links": "Bunntekst-lenker",
"trusted_instances_help": "Liste over vennlige instanser vises i toppteksten",
"instance_place": "Indiker sted for denne instansen",
"description_description": "Vises i toppteksten ved siden av tittelen",
"title_description": "Det brukes som overskrift på siden, i emnet av e-posten for eksportering til RSS- og ICS-informasjonsstrømmer.",
"instance_locale_description": "Foretrukket brukerspråk for sider. Noen meldinger vises påsamme bruk for alle (for eksempel ved publisering via ActivityPub, eller ved forsendelse av noen e-poster). I sådant fall vil språket ovenfor bli brukt.",
"user_add_help": "En e-post med instruks om bekreftelse av abonnementet og valg av passord vil bli sendt til den nye brukeren",
"instance_timezone_description": "Gancio er designet for å samle hendelser fra et gitt sted, som en by. Alle hendelser på dette stedet vil bli vist i tidssonen valgt for det.",
"announcement_description": "I denne delen kan du smette inn kunngjøringer som forblir på hjemmesiden",
"hide_boost_bookmark_help": "Skjuler de små ikonene som viser antall framhevelser og bokmerker som kommer fra fediverset",
"hide_boost_bookmark": "Skjuler framhevelser/bokmerker",
"event_confirm_description": "Du kan bekrefte hendelser som oppføres av anonyme brukere her",
"enable_trusted_instances": "Skru på vennlige instanser",
"instance_name_help": "ActivityPub-konto å følge",
"user_blocked": "Brukeren {user} blokkert",
"enable_resources_help": "Tillat tillegg av ressurser til hendelsen fra fediverset",
"enable_federation_help": "Det vil bli mulig å følge denne instansen fra fediverset",
"allow_recurrent_event": "Tillat gjentagende hendelser",
"allow_anon_event": "Tillat anonyme hendelser (må bekreftes)?",
"allow_registration_description": "Tillat selv-registrering?",
"enable_federation": "Skru på føderasjon",
"federation": "Føderasjon/ActivityPub",
"delete_trusted_instance_confirm": "Ønsker du virkelig å slette dette elementet fra venneinstansmenyen?",
"instance_place_help": "Etikett å vise i andres instanser",
"add_trusted_instance": "Legg til en vennlig instans",
"recurrent_event_visible": "Vis gjentagende hendelser som forvalg"
},
"event": {
"interact_with_me": "Følg meg",
"from": "Fra",
"due": "til",
"each_month": "Hver måed",
"each_2w": "Annenhver uke",
"each_week": "Hver uke",
"recurrent_1w_days": "Hver {days}",
"normal_description": "Velg dagen.",
"normal": "Normal",
"multidate": "Flere dager",
"recurrent_description": "Velg hyppighet og velg dagene",
"only_future": "kun kommende hendelser",
"show_past": "også i fortid",
"show_recurrent": "gjentagende hendelser",
"recurrent": "Gjentagende",
"remove_confirmation": "Er du sikker på at du vil fjerne denne hendelsen?",
"not_found": "Fant ikke hendelse",
"confirmed": "Hendelse bekreftet",
"tag_description": "Etikett",
"description_description": "Beskrivelse",
"what_description": "Tittel",
"same_day": "på samme dag",
"anon": "Anon",
"follow_me_description": "Én av måtene å holde deg oppdatert på hendelser som publiseres her på {title}\ner å følge kontoen <u>{account}</u> fra fediverset, for eksempel via Gab, og også legge til ressurser til en hendelser derfra.<br/><br/>\nHvis du aldri har hørt om Gab eller fediverset anbefales <a href='https://www.savjee.be/videos/simply-explained/mastodon-and-fediverse-explained/'>denne artikkelen</a>.<br/><br/> Skriv inn din instans nedenfor (f.eks. social.librem.one)",
"anon_description": "Du kan legge til en hendelse uten å registrere deg eller logge inn, og den vil bli lagt ut etter at den er bekreftet å være passende. Det vil ikke være mulig å endre den.<br/><br/>\nDu kan istedenfor <a href='/login'>logge inn</a>, eller <a href='/register'>registrere deg</a>. Ellers kan du forsette for å få et svar så snart som mulig. ",
"remove_recurrent_confirmation": "Er du sikker på at du ønsker å fjerne denne gjentagende hendelsen?\nHendelser i fortiden vil forbli, men ingen videre hendelser vil bli opprettet.",
"recurrent_2m_ordinal": "|Den {n} {days} i måneden annenhver|Den {n} {days} i måneden annenhver",
"where_description": "Hvor finner hendelsen sted? Hvis ingensteds hen, skriv det og <b>trykk ⏎</b>. ",
"recurrent_1m_ordinal": "På {n} {days} i hver måned",
"recurrent_2m_days": "|På {days} i hver måned annenhver|{days} i hver måned annenhver",
"recurrent_1m_days": "|På {days} i hver måned|{days} i hver måned",
"recurrent_2w_days": "En {days} annenhver",
"multidate_description": "Er det en festival? Velg når den starter og slutter",
"media_description": "Du kan legge til et flygeblad (valgfritt)",
"interact_with_me_at": "Snakk til meg i fediverset på",
"image_too_big": "Bildet kan ikke være større enn 4 MB",
"added_anon": "Hendelse lagt til, men ikke bekreftet enda.",
"added": "Hendelse lagt til"
},
"register": {
"first_user": "Administrator opprettet",
"complete": "Registrering må bekreftes.",
"error": "Feil: ",
"description": "Sosiale bevegelser bør organisere og finansiere seg selv.<br/>\n<br/>Før du kan publisere, <strong>må kontoen godkjennes</strong>, ha i minnet <strong> at bak denne siden er det mennesker, så skriv to linjer om hvilke hendelser du ønsker å publisere."
},
"recover": {
"not_valid_code": "Noe gikk galt"
},
"login": {
"ok": "Innlogget",
"forgot_password": "Glemt passordet?",
"not_registered": "Ikke registrert?",
"description": "Ved å logge inn kan du publisere nye hendelser.",
"insert_email": "Skriv inn din e-postadresse",
"error": "Kunne ikke logge inn. Sjekk din data.",
"check_email": "Sjekk din e-postinnboks og søppelpost."
},
"common": {
"reset": "Tilbakestill",
"theme": "Drakt",
"tags": "Etiketter",
"place": "Sted",
"url": "Nettadresse",
"announcements": "Kunngjøringer",
"delete": "Fjern",
"skip": "Hopp over",
"fediverse": "Fediverset",
"start": "Start",
"pause": "Pause",
"event": "Hendelse",
"filter": "Filter",
"title": "Tittel",
"user": "Bruker",
"moderation": "Moderering",
"follow": "Følg",
"follow_me_title": "Følg oppdateringer fra fediverset",
"feed_url_copied": "Informasjonskanalsnettadresse kopiert, lim den inn i din RSS-leser",
"feed": "Informasjonskanal-RSS",
"embed_help": "Kopiering av følgende kode til nettsiden din vil vises som her",
"embed_title": "Bygg inn denne hendelsen på nettsiden din",
"embed": "Innebygg",
"copied": "Kopiert",
"instances": "Instanser",
"add_to_calendar": "Legg til i kalender",
"send_via_mail": "Send e-post",
"copy_link": "Kopier lenke",
"set_password": "Sett passord",
"displayname": "Visningsnavn",
"activate_user": "Bekreftet",
"resources": "Ressurser",
"password_updated": "Passord endret.",
"me": "Du",
"disable": "Skru av",
"enable": "Skru på",
"cancel": "Avbryt",
"ok": "OK",
"new_user": "Ny bruker",
"new_password": "Nytt passord",
"recover_password": "Gjenopprett passord",
"copy": "Kopier",
"logout_ok": "Utlogget",
"add": "Legg til",
"related": "Relatert",
"edit_event": "Rediger hendelse",
"name": "Navn",
"share": "Del",
"logout": "Logg ut",
"preview": "Forhåndsvis",
"save": "Lagre",
"activate": "Aktiver",
"remove_admin": "Fjern administrator",
"deactivate": "Skru av",
"actions": "Handlinger",
"settings": "Valg",
"places": "Steder",
"events": "Handelser",
"admin": "Administrator",
"users": "Brukere",
"confirm": "Bekreft",
"info": "Info",
"edit": "Rediger",
"search": "Søk",
"hide": "Skjul",
"remove": "Fjern",
"description": "Beskrivelse",
"register": "Registrer",
"password": "Passord",
"email": "E-post",
"login": "Logg inn",
"media": "Media",
"what": "Hva",
"when": "Når",
"address": "Adresse",
"where": "Hvor",
"send": "Send",
"export": "Eksporter",
"next": "Neste",
"add_event": "Legg til hendelse",
"associate": "Tilknytt",
"authorize": "Autoriser",
"federation": "Føderasjon",
"n_resources": "ingen ressurs|én ressurs|{n} ressurser"
},
"export": {
"feed_description": "For å følge oppdateringer fra en datamaskin eller smarttelefon uten å trenge å åpne denne siden, kan du bruke en RSS-leser. </p>\n\n<p> Med en RSS-informasjonskanal kan du bruke et egnet program for å motta oppdateringer fra sider som interesserer deg. Det er en bra måte å følge mange sider raskt, uten å måtte opprette en konto eller annet plunder. </p>\n\n<li> Hvis du har Android, anbefales <a href=\"https://f-droid.org/en/packages/net.frju.flym/\">Flym</a> or Feeder </li>\n<li> For iPhone/iPad kan du bruke <a href=\"https://itunes.apple.com/ua/app/feeds4u/id1038456442?mt=8\"> Feed4U </a> </li>\n<li> For skrivebord/bærbar anbefales Feedbro, installert på <a href=\"https://addons.mozilla.org/en-GB/firefox/addon/feedbroreader/\"> Firefox </a> eller <a href=\"https://chrome.google.com/webstore/detail/feedbro/mefgmmbdailogpfhfblcnnjfmnpnmdfa\">Chrome </a>.</li>\n<br/>\nÅ legge denne lenken til i din RSS-leser vil holde deg oppdatert.",
"intro": "Ulikt usosiale plattformer som gjør det de kan for å beholde brukere og data om dem, tror vi at den infoen, som folk, må ha sin frihet. Du kan holde deg oppdatert om hendelsene du ønsker, uten å nødvendigvis gå gjennom denne siden.",
"list_description": "Hvis du har en nettside og ønsker å vise en liste over hendelser, bruk følgende kode",
"ical_description": "Datamaskiner og smarttelefoner er vanligvis utstyrt med et kalenderprogram som kan importere kalendere.",
"email_description": "Du kan få hendelser som interesserer deg tilsendt per e-post.",
"insert_your_address": "Skriv inn din e-postadresse"
},
"about": "\n <p><a href='https://gancio.org'>Gancio</a> er en delt agenda for lokale gemenskaper.</p>\n ",
"validators": {
"email": "Skriv inn en gyldig e-postadresse",
"required": "{fieldName} kreves"
},
"ordinal": {
"-1": "siste",
"5": "femte",
"4": "fjerde",
"3": "tredje",
"2": "andre",
"1": "første"
}
}

View file

@ -11,7 +11,7 @@ module.exports = {
{ charset: 'utf-8' }, { charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' } { name: 'viewport', content: 'width=device-width, initial-scale=1' }
], ],
link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }] link: [{ rel: 'icon', type: 'image/png', href: '/logo.png' }]
}, },
dev: (process.env.NODE_ENV !== 'production'), dev: (process.env.NODE_ENV !== 'production'),

View file

@ -1,6 +1,6 @@
{ {
"name": "gancio", "name": "gancio",
"version": "1.0.1-alpha", "version": "1.0.5-alpha",
"description": "A shared agenda for local communities", "description": "A shared agenda for local communities",
"author": "lesion", "author": "lesion",
"scripts": { "scripts": {
@ -32,34 +32,27 @@
"dependencies": { "dependencies": {
"@nuxtjs/auth": "^4.9.1", "@nuxtjs/auth": "^4.9.1",
"@nuxtjs/axios": "^5.13.5", "@nuxtjs/axios": "^5.13.5",
"@popperjs/core": "2.9.2",
"accept-language": "^3.0.18", "accept-language": "^3.0.18",
"axios": "^0.21.1", "axios": "^0.21.1",
"bcryptjs": "^2.4.3", "bcryptjs": "^2.4.3",
"body-parser": "^1.18.3", "body-parser": "^1.18.3",
"bufferutil": "^4.0.1",
"config": "^3.3.6", "config": "^3.3.6",
"consola": "^2.15.3", "consola": "^2.15.3",
"cookie-parser": "^1.4.5", "cookie-parser": "^1.4.5",
"core-js": "3.14.0", "core-js": "3.14.0",
"cors": "^2.8.5", "cors": "^2.8.5",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"date-fns": "^2.21.3",
"dayjs": "^1.10.5", "dayjs": "^1.10.5",
"dompurify": "^2.2.9", "dompurify": "^2.2.9",
"email-templates": "^8.0.7", "email-templates": "^8.0.7",
"express": "^4.17.1", "express": "^4.17.1",
"express-oauth-server": "^2.0.0", "express-oauth-server": "lesion/express-oauth-server#master",
"express-prom-bundle": "^6.3.4",
"fs": "^0.0.1-security",
"global": "^4.4.0",
"http-signature": "^1.3.5", "http-signature": "^1.3.5",
"ical.js": "^1.4.0", "ical.js": "^1.4.0",
"ics": "^2.29.0", "ics": "lesion/ics#dist",
"inquirer": "^8.1.1", "inquirer": "^8.1.1",
"jsdom": "^16.6.0", "jsdom": "^16.6.0",
"jsonwebtoken": "^8.5.1", "jsonwebtoken": "^8.5.1",
"less": "^4.1.1",
"linkifyjs": "3.0.0-beta.3", "linkifyjs": "3.0.0-beta.3",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"microformat-node": "^2.0.1", "microformat-node": "^2.0.1",
@ -68,23 +61,17 @@
"nuxt": "^2.15.7", "nuxt": "^2.15.7",
"nuxt-express-module": "^0.0.11", "nuxt-express-module": "^0.0.11",
"pg": "^8.6.0", "pg": "^8.6.0",
"pg-native": "3.0.0",
"prom-client": "^13.1.0",
"sequelize": "^6.6.4", "sequelize": "^6.6.4",
"sequelize-cli": "^6.2.0",
"sequelize-slugify": "^1.5.0", "sequelize-slugify": "^1.5.0",
"sharp": "^0.28.2", "sharp": "^0.28.2",
"sqlite3": "^5.0.2", "sqlite3": "mapbox/node-sqlite3#593c9d4",
"tiptap": "^1.32.0", "tiptap": "^1.32.0",
"tiptap-extensions": "^1.35.0", "tiptap-extensions": "^1.35.0",
"to-ico": "^1.1.5", "umzug": "^2.3.0",
"url": "^0.11.0", "v-calendar": "2.3.1",
"utf-8-validate": "^5.0.5",
"v-calendar": "2.3.0",
"vue": "^2.6.14", "vue": "^2.6.14",
"vue-clipboard2": "^0.3.1", "vue-clipboard2": "^0.3.1",
"vue-i18n": "^8.24.5", "vue-i18n": "^8.24.5",
"vue-server-renderer": "^2.6.14",
"vue-template-compiler": "^2.6.14", "vue-template-compiler": "^2.6.14",
"winston": "^3.3.3", "winston": "^3.3.3",
"winston-daily-rotate-file": "^4.5.5", "winston-daily-rotate-file": "^4.5.5",
@ -106,15 +93,15 @@
"eslint-plugin-nuxt": "^2.0.0", "eslint-plugin-nuxt": "^2.0.0",
"eslint-plugin-prettier": "^3.4.0", "eslint-plugin-prettier": "^3.4.0",
"eslint-plugin-promise": "^5.1.0", "eslint-plugin-promise": "^5.1.0",
"eslint-plugin-standard": "^5.0.0",
"eslint-plugin-vue": "^7.10.0", "eslint-plugin-vue": "^7.10.0",
"fibers": "^5.0.0", "less": "^4.1.1",
"less-loader": "7", "less-loader": "7",
"prettier": "^2.3.2", "prettier": "^2.3.0",
"pug": "^3.0.2", "pug": "^3.0.2",
"pug-plain-loader": "^1.1.0", "pug-plain-loader": "^1.1.0",
"sass": "^1.32.12", "sass": "^1.32.12",
"sass-loader": "10", "sass-loader": "10",
"sequelize-cli": "^6.2.0",
"typescript": "^4.3.5", "typescript": "^4.3.5",
"vue-cli-plugin-vuetify": "~2.4.0", "vue-cli-plugin-vuetify": "~2.4.0",
"vuetify": "^2.5.6", "vuetify": "^2.5.6",
@ -133,7 +120,8 @@
"@nuxtjs/vuetify/**/sass": "1.32.12", "@nuxtjs/vuetify/**/sass": "1.32.12",
"postcss": "7.0.36", "postcss": "7.0.36",
"glob-parent": "5.1.2", "glob-parent": "5.1.2",
"core-js": "3.14.0" "core-js": "3.14.0",
"chokidar": "3.5.2"
}, },
"bin": { "bin": {
"gancio": "server/cli.js" "gancio": "server/cli.js"

View file

@ -7,8 +7,10 @@ v-col(cols=12)
v-btn(v-if='settings.allow_recurrent_event' value='recurrent' label="recurrent") {{$t('event.recurrent')}} v-btn(v-if='settings.allow_recurrent_event' value='recurrent' label="recurrent") {{$t('event.recurrent')}}
p {{$t(`event.${type}_description`)}} p {{$t(`event.${type}_description`)}}
v-btn-toggle.v-col-6.flex-column.flex-sm-row(v-if='type === "recurrent"' color='primary' :value='value.recurrent.frequency' @change='fq => change("frequency", fq)') v-btn-toggle.v-col-6.flex-column.flex-sm-row(v-if='type === "recurrent"' color='primary' :value='value.recurrent.frequency' @change='fq => change("frequency", fq)')
v-btn(v-for='f in frequencies' :key='f.value' :value='f.value') {{f.text}} v-btn(v-for='f in frequencies' :key='f.value' :value='f.value') {{f.text}}
client-only client-only
.datePicker.mt-3 .datePicker.mt-3
v-input(:value='fromDate' v-input(:value='fromDate'
@ -43,11 +45,6 @@ v-col(cols=12)
:value='dueHour' clearable :value='dueHour' clearable
:items='hourList' @change='hr => change("dueHour", hr)') :items='hourList' @change='hr => change("dueHour", hr)')
//- div.col-md-12(v-if='isRecurrent')
//- p(v-if='value.recurrent.frequency !== "1m" && value.recurrent.frequency !== "2m"') 🡲 {{whenPatterns}}
//- v-btn-toggle(v-else dense group v-model='value.recurrent.type' color='primary')
//- v-btn(text link v-for='whenPattern in whenPatterns' :value='whenPattern.key' :key='whenPatterns.key') {{whenPattern.label}}
List(v-if='type==="normal" && todayEvents.length' :events='todayEvents' :title='$t("event.same_day")') List(v-if='type==="normal" && todayEvents.length' :events='todayEvents' :title='$t("event.same_day")')
</template> </template>
@ -61,17 +58,13 @@ export default {
name: 'DateInput', name: 'DateInput',
components: { List }, components: { List },
props: { props: {
value: { type: Object, default: () => ({ from: null, due: null, recurrent: null }) } value: { type: Object, default: () => ({ from: null, due: null, recurrent: null }) },
event: { type: Object, default: () => null }
}, },
data () { data () {
return { return {
type: 'normal', type: 'normal',
time: { start: null, end: null },
fromDateMenu: null,
dueDateMenu: null,
date: null,
page: null, page: null,
frequency: '',
events: [], events: [],
frequencies: [ frequencies: [
{ value: '1w', text: this.$t('event.each_week') }, { value: '1w', text: this.$t('event.each_week') },
@ -85,7 +78,7 @@ export default {
todayEvents () { todayEvents () {
const start = dayjs(this.value.from).startOf('day').unix() const start = dayjs(this.value.from).startOf('day').unix()
const end = dayjs(this.value.from).endOf('day').unix() const end = dayjs(this.value.from).endOf('day').unix()
const events = this.events.filter(e => e.start_datetime >= start && e.start_datetime <= end) const events = this.events.filter(e => (this.event.id && e.id !== this.event.id) && e.start_datetime >= start && e.start_datetime <= end)
return events return events
}, },
attributes () { attributes () {
@ -106,16 +99,15 @@ export default {
}, },
hourList () { hourList () {
const hourList = [] const hourList = []
const pad = '00' const leftPad = h => ('00' + h).slice(-2)
for (let h = 0; h < 24; h++) { for (let h = 0; h < 24; h++) {
hourList.push(`${(pad + h).slice(-pad.length)}:00`) const textHour = leftPad(h < 13 ? h : h - 12)
hourList.push(`${(pad + h).slice(-pad.length)}:30`) hourList.push({ text: textHour + ':00 ' + (h <= 12 ? 'AM' : 'PM'), value: leftPad(h) + ':00' })
hourList.push({ text: textHour + ':30 ' + (h <= 12 ? 'AM' : 'PM'), value: leftPad(h) + ':30' })
} }
return hourList return hourList
}, },
isRecurrent () {
return !!this.value.recurrent
},
whenPatterns () { whenPatterns () {
if (!this.value.from) { return } if (!this.value.from) { return }
const date = dayjs(this.value.from) const date = dayjs(this.value.from)
@ -181,7 +173,7 @@ export default {
if (what === 'type') { if (what === 'type') {
if (typeof value === 'undefined') { this.type = 'normal' } if (typeof value === 'undefined') { this.type = 'normal' }
if (value === 'recurrent') { if (value === 'recurrent') {
this.$emit('input', { ...this.value, recurrent: {}, multidate: false }) this.$emit('input', { ...this.value, recurrent: { frequency: '1w' }, multidate: false })
} else if (value === 'multidate') { } else if (value === 'multidate') {
this.$emit('input', { ...this.value, recurrent: null, multidate: true }) this.$emit('input', { ...this.value, recurrent: null, multidate: true })
} else { } else {

115
pages/add/MediaInput.vue Normal file
View file

@ -0,0 +1,115 @@
<template lang="pug">
span
v-dialog(v-model='openMediaDetails' max-width='1000px')
v-card
v-card-title {{$t('common.media')}}
v-card-text
v-row
v-col(:span='4' :cols='4')
p {{$t('event.choose_focal_point')}}
v-img(v-if='mediaPreview'
:src='mediaPreview'
aspect-ratio='1.7778'
:position="position")
v-textarea.mt-4(type='text'
label='Alternative text'
persistent-hint
@input='v => name=v'
:value='value.name' filled
:hint='$t("event.alt_text_description")')
br
v-card-actions.justify-space-between
v-btn(text @click='openMediaDetails=false' color='warning') Cancel
v-btn(text color='primary' @click='save') Save
v-col(:span='8' :cols='8')
v-img.cursorPointer(
v-if='mediaPreview' :src='mediaPreview'
@click='selectFocal')
h3.mb-3.font-weight-regular(v-if='mediaPreview') {{$t('common.media')}}
v-card-actions(v-if='mediaPreview')
v-spacer
v-btn(text color='primary' @click='openMediaDetails = true') {{$t('common.edit')}}
v-btn(text color='error' @click='remove') {{$t('common.remove')}}
div(v-if='mediaPreview')
v-img.col-12.col-sm-2.ml-3(:src='mediaPreview' aspect-ratio='1.7778' :position='savedPosition')
span.float-right {{event.media[0].name}}
v-file-input(
v-else
:label="$t('common.media')"
:hint="$t('event.media_description')"
prepend-icon="mdi-camera"
:value='value.image'
@change="v => $emit('input', { image: v, focalpoint: [0, 0] })"
persistent-hint
accept='image/*')
</template>
<script>
export default {
name: 'MediaInput',
props: {
value: { type: Object, default: () => ({ image: null }) },
event: { type: Object, default: () => {} }
},
data () {
return {
openMediaDetails: false,
name: this.value.name || '',
focalpoint: this.value.focalpoint || [0, 0]
}
},
computed: {
mediaPreview () {
if (!this.value.url && !this.value.image) {
return false
}
const url = this.value.image ? URL.createObjectURL(this.value.image) : /^https?:\/\//.test(this.value.url) ? this.value.url : `/media/thumb/${this.value.url}`
return url
},
savedPosition () {
const focalpoint = this.value.focalpoint || [0, 0]
return `${(focalpoint[0] + 1) * 50}% ${(focalpoint[1] + 1) * 50}%`
},
position () {
const focalpoint = this.focalpoint || [0, 0]
return `${(focalpoint[0] + 1) * 50}% ${(focalpoint[1] + 1) * 50}%`
}
},
methods: {
save () {
this.$emit('input', { url: this.value.url, image: this.value.image, name: this.name || '', focalpoint: [...this.focalpoint] })
this.openMediaDetails = false
},
async remove () {
const ret = await this.$root.$confirm('event.remove_media_confirmation')
if (!ret) { return }
this.$emit('remove')
},
selectFocal (ev) {
const boundingClientRect = ev.target.getBoundingClientRect()
// get relative coordinate
let x = Math.ceil(ev.clientX - boundingClientRect.left)
let y = Math.ceil(ev.clientY - boundingClientRect.top)
// snap to border
x = x < 20 ? 0 : x > boundingClientRect.width - 20 ? boundingClientRect.width : x
y = y < 20 ? 0 : y > boundingClientRect.height - 20 ? boundingClientRect.height : y
// map to real image coordinate
const posY = -1 + (y / boundingClientRect.height) * 2
const posX = -1 + (x / boundingClientRect.width) * 2
this.focalpoint = [posX, posY]
}
}
}
</script>
<style>
.cursorPointer {
cursor: crosshair;
}
</style>

View file

@ -33,7 +33,7 @@
WhereInput(ref='where' v-model='event.place') WhereInput(ref='where' v-model='event.place')
//- When //- When
DateInput(v-model='date') DateInput(v-model='date' :event='event')
//- Description //- Description
v-col.px-0(cols='12') v-col.px-0(cols='12')
@ -45,14 +45,7 @@
//- MEDIA / FLYER / POSTER //- MEDIA / FLYER / POSTER
v-col(cols=12 md=6) v-col(cols=12 md=6)
v-file-input( MediaInput(v-model='event.media[0]' :event='event' @remove='event.media=[]')
:label="$t('common.media')"
:hint="$t('event.media_description')"
prepend-icon="mdi-camera"
v-model='event.image'
persistent-hint
accept='image/*')
v-img.col-12.col-sm-2.ml-3(v-if='mediaPreview' :src='mediaPreview')
//- tags //- tags
v-col(cols=12 md=6) v-col(cols=12 md=6)
@ -66,7 +59,7 @@
v-card-actions v-card-actions
v-spacer v-spacer
v-btn(@click='done' :loading='loading' :disabled='!valid || loading' v-btn(@click='done' :loading='loading' :disabled='!valid || loading'
color='primary') {{edit?$t('common.edit'):$t('common.send')}} color='primary') {{edit?$t('common.save'):$t('common.send')}}
</template> </template>
<script> <script>
@ -77,16 +70,17 @@ import List from '@/components/List'
import ImportDialog from './ImportDialog' import ImportDialog from './ImportDialog'
import DateInput from './DateInput' import DateInput from './DateInput'
import WhereInput from './WhereInput' import WhereInput from './WhereInput'
import MediaInput from './MediaInput'
export default { export default {
name: 'NewEvent', name: 'NewEvent',
components: { List, Editor, ImportDialog, WhereInput, DateInput }, components: { List, Editor, ImportDialog, MediaInput, WhereInput, DateInput },
validate ({ store }) { validate ({ store }) {
return (store.state.auth.loggedIn || store.state.settings.allow_anon_event) return (store.state.auth.loggedIn || store.state.settings.allow_anon_event)
}, },
async asyncData ({ params, $axios, error, store }) { async asyncData ({ params, $axios, error, store }) {
if (params.edit) { if (params.edit) {
const data = { event: { place: {} } } const data = { event: { place: {}, media: [] } }
data.id = params.edit data.id = params.edit
data.edit = true data.edit = true
let event let event
@ -112,7 +106,7 @@ export default {
data.event.description = event.description data.event.description = event.description
data.event.id = event.id data.event.id = event.id
data.event.tags = event.tags data.event.tags = event.tags
data.event.image_path = event.image_path data.event.media = event.media || []
return data return data
} }
return {} return {}
@ -128,7 +122,7 @@ export default {
title: '', title: '',
description: '', description: '',
tags: [], tags: [],
image: null media: []
}, },
page: { month, year }, page: { month, year },
fileList: [], fileList: [],
@ -136,7 +130,6 @@ export default {
date: { from: 0, due: 0, recurrent: null }, date: { from: 0, due: 0, recurrent: null },
edit: false, edit: false,
loading: false, loading: false,
mediaUrl: '',
disableAddress: false disableAddress: false
} }
}, },
@ -145,16 +138,7 @@ export default {
title: `${this.settings.title} - ${this.$t('common.add_event')}` title: `${this.settings.title} - ${this.$t('common.add_event')}`
} }
}, },
computed: { computed: mapState(['tags', 'places', 'settings']),
...mapState(['tags', 'places', 'settings']),
mediaPreview () {
if (!this.event.image && !this.event.image_path) {
return false
}
const url = this.event.image ? URL.createObjectURL(this.event.image) : `/media/thumb/${this.event.image_path}`
return url
}
},
methods: { methods: {
...mapActions(['updateMeta']), ...mapActions(['updateMeta']),
eventImported (event) { eventImported (event) {
@ -170,9 +154,6 @@ export default {
} }
this.openImportDialog = false this.openImportDialog = false
}, },
cleanFile () {
this.event.image = {}
},
async done () { async done () {
if (!this.$refs.form.validate()) { if (!this.$refs.form.validate()) {
this.$nextTick(() => { this.$nextTick(() => {
@ -187,9 +168,13 @@ export default {
formData.append('recurrent', JSON.stringify(this.date.recurrent)) formData.append('recurrent', JSON.stringify(this.date.recurrent))
if (this.event.image) { if (this.event.media.length) {
formData.append('image', this.event.image) formData.append('image', this.event.media[0].image)
formData.append('image_url', this.event.media[0].url)
formData.append('image_name', this.event.media[0].name)
formData.append('image_focalpoint', this.event.media[0].focalpoint)
} }
formData.append('title', this.event.title) formData.append('title', this.event.title)
formData.append('place_name', this.event.place.name) formData.append('place_name', this.event.place.name)
formData.append('place_address', this.event.place.address) formData.append('place_address', this.event.place.address)

View file

@ -1,8 +1,8 @@
<template lang="pug"> <template lang="pug">
nuxt-link.embed_event(:to='`/event/${event.slug || event.id}`' target='_blank' :class='{ withImg: event.image_path }') nuxt-link.embed_event(:to='`/event/${event.slug || event.id}`' target='_blank' :class='{ withImg: event.media }')
//- image //- image
img.float-left(:src='`/media/thumb/${event.image_path || "logo.png"}`') img.float-left(:src='event | mediaURL("thumb")')
.event-info .event-info
//- title //- title
.date {{event|when}}<br/> .date {{event|when}}<br/>
@ -37,7 +37,7 @@ export default {
.embed_event { .embed_event {
display: flex; display: flex;
transition: margin .1s; transition: margin .1s;
background: url('/favicon.ico') no-repeat right 5px bottom 5px; background: url('/logo.png') no-repeat right 5px bottom 5px;
background-size: 32px; background-size: 32px;
background-color: #1f1f1f; background-color: #1f1f1f;
text-decoration: none; text-decoration: none;

View file

@ -11,10 +11,12 @@ export default {
const title = query.title const title = query.title
const tags = query.tags const tags = query.tags
const places = query.places const places = query.places
const show_recurrent = !!query.show_recurrent
let params = [] let params = []
if (places) { params.push(`places=${places}`) } if (places) { params.push(`places=${places}`) }
if (tags) { params.push(`tags=${tags}`) } if (tags) { params.push(`tags=${tags}`) }
if (show_recurrent) { params.push('show_recurrent=1') }
params = params.length ? `?${params.join('&')}` : '' params = params.length ? `?${params.join('&')}` : ''
const events = await $axios.$get(`/export/json${params}`) const events = await $axios.$get(`/export/json${params}`)

View file

@ -11,13 +11,14 @@ v-container#event.pa-0.pa-sm-2
v-row v-row
v-col.col-12.col-lg-8 v-col.col-12.col-lg-8
//- fake image to use u-featured in h-event microformat //- fake image to use u-featured in h-event microformat
img.u-featured(v-show='false' v-if='event.image_path' :src='`${settings.baseurl}${imgPath}`') img.u-featured(v-show='false' v-if='hasMedia' :src='event | mediaURL')
v-img.main_image.mb-3( v-img.main_image.mb-3(
contain contain
:src='imgPath' :alt='event | mediaURL("alt")'
:lazy-src='thumbImgPath' :src='event | mediaURL'
v-if='event.image_path') :lazy-src='event | mediaURL("thumb")'
.p-description.text-body-1.pa-3.grey.darken-4.rounded(v-else v-html='event.description') v-if='hasMedia')
.p-description.text-body-1.pa-3.grey.darken-4.rounded(v-if='!hasMedia && event.description' v-html='event.description')
v-col.col-12.col-lg-4 v-col.col-12.col-lg-4
v-card v-card
@ -34,8 +35,8 @@ v-container#event.pa-0.pa-sm-2
.text-h6.p-location .text-h6.p-location
v-icon mdi-map-marker v-icon mdi-map-marker
b.vcard.ml-2 {{event.place.name}} b.vcard.ml-2 {{event.place && event.place.name}}
.text-subtitle-1.adr {{event.place.address}} .text-subtitle-1.adr {{event.place && event.place.address}}
//- tags, hashtags //- tags, hashtags
v-card-text(v-if='event.tags.length') v-card-text(v-if='event.tags.length')
@ -60,11 +61,11 @@ v-container#event.pa-0.pa-sm-2
:href='`/api/event/${event.slug || event.id}.ics`') :href='`/api/event/${event.slug || event.id}.ics`')
v-icon mdi-calendar-export v-icon mdi-calendar-export
.p-description.text-body-1.pa-3.grey.darken-4.rounded(v-if='event.image_path && event.description' v-html='event.description') .p-description.text-body-1.pa-3.grey.darken-4.rounded(v-if='hasMedia && event.description' v-html='event.description')
//- resources from fediverse //- resources from fediverse
#resources.mt-1(v-if='settings.enable_federation') #resources.mt-1(v-if='settings.enable_federation')
//- div.float-right(v-if='!settings.hide_boosts') //- div.float-right(v-if='settings.hide_boosts')
//- small.mr-3 🔖 {{event.likes.length}} //- small.mr-3 🔖 {{event.likes.length}}
//- small {{event.boost.length}}<br/> //- small {{event.boost.length}}<br/>
@ -170,8 +171,8 @@ export default {
const place_feed = { const place_feed = {
rel: 'alternate', rel: 'alternate',
type: 'application/rss+xml', type: 'application/rss+xml',
title: `${this.settings.title} events @${this.event.place.name}`, title: `${this.settings.title} events @${this.event.place && this.event.place.name}`,
href: this.settings.baseurl + `/feed/rss?places=${this.event.place.id}` href: this.settings.baseurl + `/feed/rss?places=${this.event.place && this.event.place.id}`
} }
return { return {
@ -197,7 +198,7 @@ export default {
{ property: 'og:type', content: 'event' }, { property: 'og:type', content: 'event' },
{ {
property: 'og:image', property: 'og:image',
content: this.thumbImgPath content: this.$options.filters.mediaURL(this.event)
}, },
{ property: 'og:site_name', content: this.settings.title }, { property: 'og:site_name', content: this.settings.title },
{ {
@ -213,7 +214,7 @@ export default {
{ property: 'twitter:title', content: this.event.title }, { property: 'twitter:title', content: this.event.title },
{ {
property: 'twitter:image', property: 'twitter:image',
content: this.thumbImgPath content: this.$options.filters.mediaURL(this.event, 'thumb')
}, },
{ {
property: 'twitter:description', property: 'twitter:description',
@ -221,7 +222,7 @@ export default {
} }
], ],
link: [ link: [
{ rel: 'image_src', href: this.thumbImgPath }, { rel: 'image_src', href: this.$options.filters.mediaURL(this.event, 'thumb') },
{ {
rel: 'alternate', rel: 'alternate',
type: 'application/rss+xml', type: 'application/rss+xml',
@ -235,18 +236,15 @@ export default {
}, },
computed: { computed: {
...mapState(['settings']), ...mapState(['settings']),
hasMedia () {
return this.event.media && this.event.media.length
},
plainDescription () { plainDescription () {
return htmlToText.fromString(this.event.description.replace('\n', '').slice(0, 1000)) return htmlToText.fromString(this.event.description.replace('\n', '').slice(0, 1000))
}, },
currentAttachmentLabel () { currentAttachmentLabel () {
return get(this.selectedResource, `data.attachment[${this.currentAttachment}].name`, '') return get(this.selectedResource, `data.attachment[${this.currentAttachment}].name`, '')
}, },
imgPath () {
return '/media/' + this.event.image_path
},
thumbImgPath () {
return this.settings.baseurl + '/media/thumb/' + this.event.image_path
},
is_mine () { is_mine () {
if (!this.$auth.user) { if (!this.$auth.user) {
return false return false

View file

@ -184,7 +184,7 @@ export default {
// Message({ message: this.$t('email_notification_activated'), showClose: true, type: 'success' }) // Message({ message: this.$t('email_notification_activated'), showClose: true, type: 'success' })
}, },
imgPath (event) { imgPath (event) {
return event.image_path && event.image_path return event.media && event.media[0].url
} }
} }
} }

View file

@ -65,7 +65,7 @@ export default {
{ hid: 'og-description', name: 'og:description', content: this.settings.description }, { hid: 'og-description', name: 'og:description', content: this.settings.description },
{ hid: 'og-title', property: 'og:title', content: this.settings.title }, { hid: 'og-title', property: 'og:title', content: this.settings.title },
{ hid: 'og-url', property: 'og:url', content: this.settings.baseurl }, { hid: 'og-url', property: 'og:url', content: this.settings.baseurl },
{ property: 'og:image', content: this.settings.baseurl + '/favicon.ico' } { property: 'og:image', content: this.settings.baseurl + '/logo.png' }
], ],
link: [ link: [
{ rel: 'alternate', type: 'application/rss+xml', title: this.settings.title, href: this.settings.baseurl + '/feed/rss' } { rel: 'alternate', type: 'application/rss+xml', title: this.settings.title, href: this.settings.baseurl + '/feed/rss' }

View file

@ -2,7 +2,7 @@
v-row.mt-5(align='center' justify='center') v-row.mt-5(align='center' justify='center')
v-col(cols='12' md="6" lg="5" xl="4") v-col(cols='12' md="6" lg="5" xl="4")
v-card v-card
v-card-title <nuxt-link to='/'><img src='/favicon.ico'/></nuxt-link> {{$t('common.set_password')}} v-card-title <nuxt-link to='/'><v-img src='/logo.png' max-width="40px" max-height="40px" contain class='mr-4'/></nuxt-link> {{$t('common.set_password')}}
template(v-if='valid') template(v-if='valid')
v-card-text(v-if='valid') v-card-text(v-if='valid')
v-form(v-if='valid') v-form(v-if='valid')

View file

@ -33,6 +33,18 @@ export default ({ app, store }) => {
// shown in mobile homepage // shown in mobile homepage
Vue.filter('day', value => dayjs.unix(value).locale(store.state.locale).format('dddd, D MMM')) Vue.filter('day', value => dayjs.unix(value).locale(store.state.locale).format('dddd, D MMM'))
Vue.filter('mediaURL', (event, type) => {
if (event.media && event.media.length) {
if (type === 'alt') {
return event.media[0].name
} else {
return store.state.settings.baseurl + '/media/' + (type === 'thumb' ? 'thumb/' : '') + event.media[0].url
}
} else if (type !== 'alt') {
return store.state.settings.baseurl + '/media/' + (type === 'thumb' ? 'thumb/' : '') + 'logo.svg'
}
return ''
})
Vue.filter('from', timestamp => dayjs.unix(timestamp).fromNow()) Vue.filter('from', timestamp => dayjs.unix(timestamp).fromNow())

View file

@ -17,7 +17,7 @@ const announceController = {
announcement: req.body.announcement, announcement: req.body.announcement,
visible: true visible: true
} }
log.info('Create announcement: "%s" ', req.body.title) log.info('Create announcement: ' + req.body.title)
const announce = await Announcement.create(announcementDetail) const announce = await Announcement.create(announcementDetail)
res.json(announce) res.json(announce)
}, },

View file

@ -289,10 +289,23 @@ const eventController = {
is_visible: !!req.user is_visible: !!req.user
} }
if (req.file) { if (req.file || body.image_url) {
eventDetails.image_path = req.file.filename let url
} else if (body.image_url) { if (req.file) {
eventDetails.image_path = await helpers.getImageFromURL(body.image_url) url = req.file.filename
} else {
url = await helpers.getImageFromURL(body.image_url)
}
let focalpoint = body.image_focalpoint ? body.image_focalpoint.split(',') : ['0', '0']
focalpoint = [parseFloat(focalpoint[0]).toFixed(2), parseFloat(focalpoint[1]).toFixed(2)]
eventDetails.media = [{
url,
name: body.image_name || '',
focalpoint: [parseFloat(focalpoint[0]), parseFloat(focalpoint[1])]
}]
} else {
eventDetails.media = []
} }
const event = await Event.create(eventDetails) const event = await Event.create(eventDetails)
@ -343,28 +356,29 @@ const eventController = {
if (req.err) { if (req.err) {
return res.status(400).json(req.err.toString()) return res.status(400).json(req.err.toString())
} }
const body = req.body
const event = await Event.findByPk(body.id)
if (!event) { return res.sendStatus(404) }
if (!req.user.is_admin && event.userId !== req.user.id) {
return res.sendStatus(403)
}
const recurrent = body.recurrent ? JSON.parse(body.recurrent) : null try {
const eventDetails = { const body = req.body
title: body.title, const event = await Event.findByPk(body.id)
// remove html tags if (!event) { return res.sendStatus(404) }
description: helpers.sanitizeHTML(linkifyHtml(body.description, { target: '_blank' })), if (!req.user.is_admin && event.userId !== req.user.id) {
multidate: body.multidate, return res.sendStatus(403)
start_datetime: body.start_datetime, }
end_datetime: body.end_datetime,
recurrent
}
if (req.file) { const recurrent = body.recurrent ? JSON.parse(body.recurrent) : null
if (event.image_path && !event.recurrent) { const eventDetails = {
const old_path = path.resolve(config.upload_path, event.image_path) title: body.title,
const old_thumb_path = path.resolve(config.upload_path, 'thumb', event.image_path) // remove html tags
description: helpers.sanitizeHTML(linkifyHtml(body.description, { target: '_blank' })),
multidate: body.multidate,
start_datetime: body.start_datetime,
end_datetime: body.end_datetime,
recurrent
}
if ((req.file || /^https?:\/\//.test(body.image_url)) && !event.recurrent && event.media && event.media.length) {
const old_path = path.resolve(config.upload_path, event.media[0].url)
const old_thumb_path = path.resolve(config.upload_path, 'thumb', event.media[0].url)
try { try {
fs.unlinkSync(old_path) fs.unlinkSync(old_path)
fs.unlinkSync(old_thumb_path) fs.unlinkSync(old_thumb_path)
@ -372,34 +386,55 @@ const eventController = {
log.info(e.toString()) log.info(e.toString())
} }
} }
eventDetails.image_path = req.file.filename let url
} else if (body.image_url) { if (req.file) {
eventDetails.image_path = await helpers.getImageFromURL(body.image_url) url = req.file.filename
} } else if (body.image_url) {
if (/^https?:\/\//.test(body.image_url)) {
url = await helpers.getImageFromURL(body.image_url)
} else {
url = body.image_url
}
}
await event.update(eventDetails) if (url && !event.recurrent) {
const [place] = await Place.findOrCreate({ const focalpoint = body.image_focalpoint ? body.image_focalpoint.split(',') : ['0', '0']
where: { name: body.place_name }, eventDetails.media = [{
defaults: { address: body.place_address } url,
}) name: body.image_name || '',
focalpoint: [parseFloat(focalpoint[0].slice(0, 6)), parseFloat(focalpoint[1].slice(0, 6))]
}]
} else {
eventDetails.media = []
}
await event.setPlace(place) await event.update(eventDetails)
await event.setTags([]) const [place] = await Place.findOrCreate({
if (body.tags) { where: { name: body.place_name },
await Tag.bulkCreate(body.tags.map(t => ({ tag: t })), { ignoreDuplicates: true }) defaults: { address: body.place_address }
const tags = await Tag.findAll({ where: { tag: { [Op.in]: body.tags } } }) })
await event.addTags(tags)
}
const newEvent = await Event.findByPk(event.id, { include: [Tag, Place] })
res.json(newEvent)
// create recurrent instances of event if needed await event.setPlace(place)
// without waiting for the task manager await event.setTags([])
if (event.recurrent) { if (body.tags) {
eventController._createRecurrent() await Tag.bulkCreate(body.tags.map(t => ({ tag: t })), { ignoreDuplicates: true })
} else { const tags = await Tag.findAll({ where: { tag: { [Op.in]: body.tags } } })
const notifier = require('../../notifier') await event.addTags(tags)
notifier.notifyEvent('Update', event.id) }
const newEvent = await Event.findByPk(event.id, { include: [Tag, Place] })
res.json(newEvent)
// create recurrent instances of event if needed
// without waiting for the task manager
if (event.recurrent) {
eventController._createRecurrent()
} else {
const notifier = require('../../notifier')
notifier.notifyEvent('Update', event.id)
}
} catch (e) {
log.error('[EVENT UPDATE]', e)
res.sendStatus(400)
} }
}, },
@ -407,9 +442,9 @@ const eventController = {
const event = await Event.findByPk(req.params.id) const event = await Event.findByPk(req.params.id)
// check if event is mine (or user is admin) // check if event is mine (or user is admin)
if (event && (req.user.is_admin || req.user.id === event.userId)) { if (event && (req.user.is_admin || req.user.id === event.userId)) {
if (event.image_path && !event.recurrent) { if (event.media && event.media.length && !event.recurrent) {
const old_path = path.join(config.upload_path, event.image_path) const old_path = path.join(config.upload_path, event.media[0].url)
const old_thumb_path = path.join(config.upload_path, 'thumb', event.image_path) const old_thumb_path = path.join(config.upload_path, 'thumb', event.media[0].url)
try { try {
fs.unlinkSync(old_thumb_path) fs.unlinkSync(old_thumb_path)
fs.unlinkSync(old_path) fs.unlinkSync(old_path)
@ -419,7 +454,12 @@ const eventController = {
} }
const notifier = require('../../notifier') const notifier = require('../../notifier')
await notifier.notifyEvent('Delete', event.id) await notifier.notifyEvent('Delete', event.id)
log.debug('[EVENT REMOVED]', event.title)
// unassociate child events
if (event.recurrent) {
await Event.update({ parentId: null }, { where: { parentId: event.id } })
}
log.debug('[EVENT REMOVED] ' + event.title)
await event.destroy() await event.destroy()
res.sendStatus(200) res.sendStatus(200)
} else { } else {
@ -504,6 +544,7 @@ const eventController = {
/** /**
* Ensure we have the next instance of a recurrent event * Ensure we have the next instance of a recurrent event
* TODO: create a future instance if the next one is skipped
*/ */
_createRecurrentOccurrence (e) { _createRecurrentOccurrence (e) {
log.debug(`Create recurrent event [${e.id}] ${e.title}"`) log.debug(`Create recurrent event [${e.id}] ${e.title}"`)
@ -511,7 +552,7 @@ const eventController = {
parentId: e.id, parentId: e.id,
title: e.title, title: e.title,
description: e.description, description: e.description,
image_path: e.image_path, media: e.media,
is_visible: true, is_visible: true,
userId: e.userId, userId: e.userId,
placeId: e.placeId placeId: e.placeId

View file

@ -12,6 +12,7 @@ const exportController = {
const type = req.params.type const type = req.params.type
const tags = req.query.tags const tags = req.query.tags
const places = req.query.places const places = req.query.places
const show_recurrent = !!req.query.show_recurrent
const where = {} const where = {}
const yesterday = moment().subtract('1', 'day').unix() const yesterday = moment().subtract('1', 'day').unix()
@ -25,9 +26,13 @@ const exportController = {
where.placeId = places.split(',') where.placeId = places.split(',')
} }
if (!show_recurrent) {
where.parentId = null
}
const events = await Event.findAll({ const events = await Event.findAll({
order: ['start_datetime'], order: ['start_datetime'],
attributes: { exclude: ['is_visible', 'recurrent', 'createdAt', 'updatedAt', 'likes', 'boost', 'slug', 'userId', 'placeId'] }, attributes: { exclude: ['is_visible', 'recurrent', 'createdAt', 'likes', 'boost', 'userId', 'placeId'] },
where: { where: {
is_visible: true, is_visible: true,
recurrent: { [Op.eq]: null }, recurrent: { [Op.eq]: null },
@ -62,8 +67,8 @@ const exportController = {
const eventsMap = events.map(e => { const eventsMap = events.map(e => {
const tmpStart = moment.unix(e.start_datetime) const tmpStart = moment.unix(e.start_datetime)
const tmpEnd = moment.unix(e.end_datetime) const tmpEnd = moment.unix(e.end_datetime)
const start = tmpStart.utc(true).format('YYYY-M-D-H-m').split('-') const start = tmpStart.utc(true).format('YYYY-M-D-H-m').split('-').map(Number)
const end = tmpEnd.utc(true).format('YYYY-M-D-H-m').split('-') const end = tmpEnd.utc(true).format('YYYY-M-D-H-m').split('-').map(Number)
return { return {
start, start,
// startOutputType: 'utc', // startOutputType: 'utc',
@ -77,8 +82,12 @@ const exportController = {
} }
}) })
res.type('text/calendar; charset=UTF-8') res.type('text/calendar; charset=UTF-8')
const ret = ics.createEvents(eventsMap) ics.createEvents(eventsMap, (err, value) => {
res.send(ret.value) if (err) {
return res.status(401).send(err)
}
return res.send(value)
})
} }
} }

View file

@ -6,10 +6,7 @@ const fs = require('fs')
const pkg = require('../../../package.json') const pkg = require('../../../package.json')
const crypto = require('crypto') const crypto = require('crypto')
const util = require('util') const util = require('util')
const toIco = require('to-ico')
const generateKeyPair = util.promisify(crypto.generateKeyPair) const generateKeyPair = util.promisify(crypto.generateKeyPair)
const readFile = util.promisify(fs.readFile)
const writeFile = util.promisify(fs.writeFile)
const sharp = require('sharp') const sharp = require('sharp')
const log = require('../../log') const log = require('../../log')
@ -124,16 +121,13 @@ const settingsController = {
const baseImgPath = path.resolve(config.upload_path, 'logo') const baseImgPath = path.resolve(config.upload_path, 'logo')
// convert and resize to png // convert and resize to png
sharp(uploadedPath) return sharp(uploadedPath)
.resize(400) .resize(400)
.png({ quality: 90 }) .png({ quality: 90 })
.toFile(baseImgPath + '.png', async (err, info) => { .toFile(baseImgPath + '.png', (err, info) => {
if (err) { if (err) {
log.error('[LOGO]', err) log.error('[LOGO] ' + err)
} }
const image = await readFile(baseImgPath + '.png')
const favicon = await toIco([image], { sizes: [64], resize: true })
writeFile(baseImgPath + '.ico', favicon)
settingsController.set('logo', baseImgPath) settingsController.set('logo', baseImgPath)
res.sendStatus(200) res.sendStatus(200)
}) })

View file

@ -44,6 +44,7 @@ Event.init({
index: true index: true
}, },
image_path: DataTypes.STRING, image_path: DataTypes.STRING,
media: DataTypes.JSON,
is_visible: DataTypes.BOOLEAN, is_visible: DataTypes.BOOLEAN,
recurrent: DataTypes.JSON, recurrent: DataTypes.JSON,
likes: { type: DataTypes.JSON, defaultValue: [] }, likes: { type: DataTypes.JSON, defaultValue: [] },
@ -85,15 +86,18 @@ Event.prototype.toAPNote = function (username, locale, to = []) {
${tags && tags.map(t => `#${t}`)} ${tags && tags.map(t => `#${t}`)}
` `
// const attachment = [] const attachment = []
if (this.media && this.media.length) {
attachment.push({
type: 'Document',
mediaType: 'image/jpeg',
url: `${config.baseurl}/media/${this.media[0].url}`,
name: this.media[0].name || '',
blurHash: null,
focalPoint: this.media[0].focalPoint || [0, 0]
})
}
// if (this.image_path) { // if (this.image_path) {
// attachment.push({
// type: 'Document',
// mediaType: 'image/jpeg',
// url: `${config.baseurl}/media/${this.image_path}`,
// name: null,
// blurHash: null
// })
// } // }
return { return {

View file

@ -34,7 +34,7 @@ oauth.use((req, res) => res.sendStatus(404))
oauth.use((err, req, res, next) => { oauth.use((err, req, res, next) => {
const error_msg = err.toString() const error_msg = err.toString()
log.error('[OAUTH USE]', error_msg) log.error('[OAUTH USE] ' + error_msg)
res.status(500).send(error_msg) res.status(500).send(error_msg)
}) })

View file

@ -322,7 +322,7 @@ async function setup (options) {
process.exit(0) process.exit(0)
} }
consola.info(`📅 ${pkg.name} - v${pkg.version} - ${pkg.description}`) consola.info(`📅 ${pkg.name} - v${pkg.version} - ${pkg.description} (nodejs: ${process.version})`)
require('yargs') require('yargs')
.usage('Usage $0 <command> [options]') .usage('Usage $0 <command> [options]')

View file

@ -1,21 +1,22 @@
extends ../layout.pug extends ../layout.pug
block content block content
h3 #{event.title} h3 #{event.title}
p 📍 Dove: #{event.place.name} - #{event.place.address} p 📍 #{event.place.name} - #{event.place.address}
p ⏰ Quando: #{datetime(event.start_datetime)} p ⏰ #{datetime(event.start_datetime)}
br br
if event.image_path if event.media && event.media.length
<center> <center>
<img style="height: 89vh; margin: 0 auto;" src="#{config.baseurl}/media/#{event.image_path}" /> <img style="height: 89vh; margin: 0 auto;" alt="#{event.media[0].name}" src="#{config.baseurl}/media/#{event.media[0].url}" />
</center> </center>
p !{event.description} if event.description
p !{event.description}
each tag in event.tags each tag in event.tags
span ##{tag.tag} span ##{tag.tag}
br br
<a href="#{config.baseurl}/event/#{event.id}">#{config.baseurl}/event/#{event.id}</a> <a href="#{config.baseurl}/event/#{event.slug || event.id}">#{config.baseurl}/event/#{event.slug || event.id}</a>
hr hr
if to_confirm if to_confirm
p Puoi confermare questo evento premendo il tasto conferma a <a href="#{config.baseurl}/event/#{event.id}">questa pagina</a> p !{t('event_confirm.content', {config, url: `${config.baseurl}/event/${event.slug || event.id}`})}
else //- else
p Puoi eliminare queste notifiche <a href="#{config.baseurl}/del_notification/#{notification.remove_code}">qui</a> //- p Puoi eliminare queste notifiche <a href="#{config.baseurl}/del_notification/#{notification.remove_code}">qui</a>

View file

@ -10,7 +10,7 @@ module.exports = {
if (typeof body.object !== 'string') { return } if (typeof body.object !== 'string') { return }
const username = body.object.replace(`${config.baseurl}/federation/u/`, '') const username = body.object.replace(`${config.baseurl}/federation/u/`, '')
if (username !== req.settings.instance_name) { if (username !== req.settings.instance_name) {
log.warn(`Following the wrong user: ${username} instead of ${req.settings.instance_name}`) log.warn(`Following the wrong user: ${username} instead of ${req.settings.instance_name} (could be a wrong config.baseurl)`)
return res.status(404).send('User not found') return res.status(404).send('User not found')
} }

View file

@ -97,7 +97,8 @@ const Helpers = {
'https://www.w3.org/ns/activitystreams', 'https://www.w3.org/ns/activitystreams',
'https://w3id.org/security/v1', 'https://w3id.org/security/v1',
{ {
Hashtag: 'as:Hashtag' Hashtag: 'as:Hashtag',
focalPoint: { '@container': '@list', '@id': 'toot:focalPoint' }
}] }]
await Helpers.signAndSend(JSON.stringify(body), sharedInbox) await Helpers.signAndSend(JSON.stringify(body), sharedInbox)
} }

View file

@ -132,6 +132,7 @@ module.exports = {
} }
const events = data.items.map(e => { const events = data.items.map(e => {
const props = e.properties const props = e.properties
const media = get(props, 'featured[0]')
return { return {
title: get(props, 'name[0]', ''), title: get(props, 'name[0]', ''),
description: get(props, 'description[0]', ''), description: get(props, 'description[0]', ''),
@ -142,7 +143,7 @@ module.exports = {
start_datetime: dayjs(get(props, 'start[0]', '')).unix(), start_datetime: dayjs(get(props, 'start[0]', '')).unix(),
end_datetime: dayjs(get(props, 'end[0]', '')).unix(), end_datetime: dayjs(get(props, 'end[0]', '')).unix(),
tags: get(props, 'category', []), tags: get(props, 'category', []),
image_path: get(props, 'featured[0]') media: media ? [{ name: get(props, 'name[0]', ''), url: get(props, 'featured[0]'), focalpoint: [0, 0] }] : []
} }
}) })
return res.json(events) return res.json(events)

View file

@ -0,0 +1,25 @@
module.exports = {
up: async (queryInterface, Sequelize) => {
return Promise.all(
[
await queryInterface.addColumn('events', 'media', { type: Sequelize.JSON }),
await queryInterface.sequelize.query("UPDATE events set media=JSON('[{ \"url\": \"' || image_path || '\" }]')")
])
/**
* Add altering commands here.
*
* Example:
* await queryInterface.createTable('users', { id: Sequelize.INTEGER });
*/
},
down: async (queryInterface, Sequelize) => {
/**
* Add reverting commands here.
*
* Example:
* await queryInterface.dropTable('users');
*/
}
}

View file

@ -43,7 +43,7 @@ const notifier = {
// insert notifications // insert notifications
const notifications = await eventController.getNotifications(event, action) const notifications = await eventController.getNotifications(event, action)
await event.addNotifications(notifications) await event.addNotifications(notifications)
const event_notifications = await event.getNotifications() const event_notifications = await event.getNotifications({ through: { where: { status: 'new' } } })
const promises = event_notifications.map(async notification => { const promises = event_notifications.map(async notification => {
try { try {

View file

@ -33,6 +33,10 @@ app.use('/media/', express.static(config.upload_path))
// initialize instance settings / authentication / locale // initialize instance settings / authentication / locale
app.use(helpers.initSettings) app.use(helpers.initSettings)
app.use('/noimg.svg', (req, res, next) => {
return express.static('./static/noimg.svg')
})
// serve favicon and static content // serve favicon and static content
app.use('/logo.png', (req, res, next) => { app.use('/logo.png', (req, res, next) => {
const logoPath = req.settings.logo || './static/gancio' const logoPath = req.settings.logo || './static/gancio'
@ -51,11 +55,11 @@ app.get('/feed/:type', cors(), exportController.export)
app.use('/.well-known', webfinger) app.use('/.well-known', webfinger)
app.use('/federation', federation) app.use('/federation', federation)
// api!
app.use(cookieParser()) app.use(cookieParser())
// fill req.user if request is authenticated // fill req.user if request is authenticated
app.use(auth.fillUser) app.use(auth.fillUser)
// api!
app.use('/api', api) app.use('/api', api)
app.use('/oauth', oauth) app.use('/oauth', oauth)

274
static/noimg.svg Normal file
View file

@ -0,0 +1,274 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="1280.000000pt" height="640.000000pt" viewBox="0 0 1280.000000 640.000000"
preserveAspectRatio="xMidYMid meet">
<metadata>
Created by potrace 1.15, written by Peter Selinger 2001-2017
</metadata>
<g transform="translate(0.000000,640.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M4410 6320 c0 -12 26 -30 124 -88 106 -62 118 -81 125 -185 3 -46 11
-90 17 -98 6 -8 16 -41 22 -73 16 -81 50 -151 120 -249 84 -116 90 -131 112
-279 26 -176 37 -229 63 -312 24 -79 59 -120 164 -189 35 -23 63 -45 63 -51 0
-18 -174 3 -280 34 -121 35 -230 60 -266 60 -16 0 -43 -17 -74 -46 l-50 -47
-104 17 c-160 25 -212 21 -387 -24 -20 -6 -44 -19 -53 -29 -17 -18 -17 -22 3
-66 28 -64 27 -74 -16 -92 -29 -12 -56 -14 -118 -9 -135 11 -145 16 -271 137
-62 60 -120 109 -126 109 -7 0 -35 -11 -62 -25 -64 -32 -119 -32 -232 1 -106
30 -212 85 -311 161 -72 55 -180 100 -256 106 -28 2 -32 -1 -30 -21 2 -12 15
-40 30 -63 17 -26 28 -58 31 -93 4 -47 2 -54 -21 -69 -19 -12 -26 -27 -30 -64
-2 -26 -17 -81 -32 -120 -27 -72 -28 -73 -84 -94 -77 -29 -164 -33 -381 -16
-143 11 -203 21 -266 41 -96 31 -174 43 -174 28 0 -16 138 -140 215 -192 39
-26 75 -58 80 -71 14 -33 89 -87 133 -95 21 -4 74 -1 122 7 53 9 160 14 285
14 l200 0 85 -42 c71 -36 95 -43 144 -43 79 0 99 14 252 176 70 74 144 144
165 155 28 14 59 19 116 19 93 0 143 -12 203 -50 25 -15 65 -31 90 -35 66 -10
91 -29 245 -187 132 -134 148 -147 256 -205 134 -73 167 -85 307 -118 56 -14
151 -43 210 -66 59 -22 155 -58 214 -79 59 -22 124 -48 145 -58 21 -11 93 -32
161 -47 199 -43 319 -88 376 -141 22 -21 25 -29 15 -34 -8 -5 -113 -13 -234
-20 -345 -18 -425 -33 -425 -82 0 -20 3 -20 73 -14 170 15 326 26 382 25 l60
0 27 -44 c33 -55 57 -65 156 -65 l77 0 84 -113 c101 -136 137 -203 127 -241
-5 -21 -12 -27 -29 -24 -16 2 -31 22 -58 77 -20 41 -44 80 -54 87 -11 8 -40
14 -66 14 -69 0 -92 12 -128 68 -18 28 -39 53 -47 56 -32 12 -92 5 -199 -24
-123 -33 -154 -35 -253 -18 -63 11 -72 10 -113 -10 -24 -12 -56 -33 -71 -47
-52 -48 -32 -130 56 -239 26 -32 46 -59 44 -61 -2 -1 -54 -17 -116 -35 -86
-25 -121 -40 -145 -64 -18 -17 -52 -43 -76 -59 -56 -36 -253 -233 -410 -410
-67 -76 -139 -148 -160 -161 -21 -14 -81 -62 -133 -108 -53 -46 -108 -90 -122
-98 -25 -13 -29 -12 -52 11 -13 14 -27 41 -30 60 -6 32 -1 39 73 114 98 99
153 168 199 251 56 100 86 126 189 163 50 18 126 41 170 51 112 25 115 27 115
65 0 36 19 57 88 95 54 29 63 47 29 56 -46 11 -132 -15 -206 -63 -40 -25 -86
-49 -105 -53 -36 -8 -96 -2 -96 10 0 4 25 27 57 50 31 24 92 76 136 117 44 40
91 76 105 80 34 8 97 -13 136 -47 36 -32 66 -39 66 -16 0 23 20 30 103 36 42
3 78 9 80 15 1 5 -32 19 -74 30 -101 28 -189 65 -189 78 0 6 38 61 84 121 99
130 140 211 157 309 14 83 6 258 -13 299 -16 35 -45 36 -58 3 -13 -33 -26 -32
-25 3 0 37 -26 35 -37 -3 -11 -36 -4 -125 18 -222 l16 -73 -109 -114 c-59 -63
-137 -138 -172 -166 -36 -28 -78 -67 -95 -86 -26 -29 -41 -36 -94 -45 -71 -13
-145 -5 -167 17 -8 9 -15 34 -15 57 -1 54 -29 116 -69 150 -32 26 -36 27 -162
27 -71 0 -129 -2 -129 -5 0 -3 6 -14 13 -26 10 -17 9 -22 -7 -35 -11 -8 -27
-14 -37 -14 -22 0 -41 -24 -33 -44 4 -11 -2 -19 -20 -26 -31 -11 -33 -32 -7
-61 10 -12 23 -25 27 -31 5 -5 23 -20 39 -33 l30 -23 -28 -11 c-16 -6 -66 -11
-112 -11 l-84 0 -28 42 c-47 69 -73 83 -169 87 -61 2 -95 -1 -124 -13 -24 -10
-70 -16 -113 -16 -95 0 -133 -12 -179 -57 l-38 -37 0 54 c-1 73 -25 123 -94
192 -66 65 -108 88 -164 88 -46 0 -121 -30 -169 -66 -51 -38 -65 -32 -142 60
-117 140 -232 256 -260 263 -56 13 -92 44 -394 339 -172 168 -320 310 -330
315 -38 21 2 -27 178 -216 260 -279 389 -407 490 -489 50 -41 150 -137 224
-215 73 -78 147 -149 163 -157 18 -9 27 -19 21 -25 -5 -5 -65 -13 -133 -18
-95 -7 -142 -15 -194 -35 -38 -15 -74 -30 -80 -35 -19 -15 -118 -41 -157 -41
-43 0 -40 -3 -464 480 -143 162 -319 360 -390 440 -72 80 -261 294 -420 475
-159 182 -370 422 -469 534 l-181 203 -6 -288 c-3 -159 -7 -475 -8 -701 -1
-388 0 -413 17 -413 9 0 96 11 192 25 96 14 184 25 195 25 11 0 51 -29 89 -64
l68 -64 -21 -38 c-13 -23 -35 -107 -55 -206 -32 -162 -34 -168 -77 -223 -66
-85 -122 -160 -144 -197 -27 -44 -67 -69 -168 -103 l-86 -29 -6 -81 c-9 -101
-5 -120 21 -119 25 0 163 82 251 149 48 35 84 75 125 135 113 166 244 467 236
542 -3 36 -28 51 -28 18 0 -42 -25 -14 -34 38 -23 132 61 90 260 -130 64 -71
122 -128 129 -128 8 0 30 9 49 21 19 11 68 25 108 30 40 6 103 19 139 30 89
26 107 24 165 -12 85 -55 414 -392 414 -426 0 -18 -47 -35 -73 -27 -27 9 -37
-11 -37 -79 0 -44 4 -59 17 -64 19 -8 90 7 155 32 l46 19 63 -59 c35 -32 76
-66 92 -75 l29 -18 -54 -12 c-29 -6 -82 -14 -118 -17 -59 -5 -70 -2 -124 26
-38 21 -72 31 -101 31 -55 0 -104 17 -137 48 l-27 25 24 23 c23 21 25 30 25
119 0 63 -5 105 -14 123 -9 18 -47 46 -109 80 -52 28 -100 52 -106 52 -6 0
-34 -11 -61 -25 -27 -14 -59 -25 -70 -25 -12 0 -30 -5 -41 -10 -25 -14 -25
-71 0 -126 15 -34 27 -44 62 -56 23 -8 44 -19 46 -25 4 -11 -51 -23 -111 -23
-22 0 -47 -6 -55 -14 -9 -8 -63 -26 -121 -41 -125 -30 -201 -67 -292 -138 -65
-51 -106 -74 -238 -136 -83 -39 -175 -111 -197 -156 -23 -43 -151 -230 -216
-314 -27 -35 -58 -83 -69 -107 -22 -51 -34 -131 -24 -168 6 -24 9 -25 29 -15
12 7 47 31 78 55 31 24 69 49 83 56 15 6 66 64 115 127 216 287 210 280 249
291 70 19 252 34 264 22 15 -15 -7 -41 -52 -59 -42 -18 -86 -58 -107 -96 -8
-16 -49 -68 -92 -115 -197 -222 -321 -412 -352 -537 -6 -28 -36 -99 -65 -160
-32 -66 -60 -140 -69 -185 -13 -63 -21 -79 -48 -102 l-32 -27 -3 -159 -3 -160
108 5 c77 3 121 1 154 -10 32 -10 52 -11 64 -4 9 5 35 47 56 92 62 135 131
196 183 162 18 -12 26 -31 35 -79 15 -85 58 -168 99 -192 38 -21 105 -25 239
-12 l92 9 7 -28 c4 -16 1 -92 -6 -169 -9 -105 -9 -152 -1 -181 6 -22 11 -72
11 -112 0 -59 3 -73 18 -77 30 -9 337 -26 362 -19 43 10 61 35 132 180 73 151
107 193 159 193 16 0 29 -2 29 -4 0 -2 -12 -27 -26 -54 -33 -65 -45 -205 -20
-237 15 -19 17 -18 78 50 104 114 261 263 297 281 33 16 132 17 147 1 4 -3
-11 -28 -33 -54 -48 -58 -73 -104 -73 -135 0 -13 -15 -48 -34 -77 -44 -67 -52
-99 -31 -120 21 -21 327 -27 637 -12 309 15 634 29 958 41 151 5 285 13 298
16 43 12 28 29 -80 93 -51 30 -78 67 -78 107 0 33 64 170 96 206 18 19 81 24
117 9 16 -6 16 -9 -8 -31 -15 -14 -33 -36 -40 -50 -18 -35 -27 -126 -18 -185
12 -72 31 -71 108 10 l65 66 14 -33 c8 -18 20 -48 27 -67 13 -35 87 -91 120
-91 9 0 33 14 54 30 21 17 47 30 59 30 49 1 217 103 199 121 -5 5 -38 13 -73
18 -36 5 -83 16 -105 25 -43 18 -95 63 -95 81 0 11 41 55 51 55 3 0 29 -18 59
-40 68 -51 101 -57 176 -31 70 23 219 36 284 24 44 -8 46 -10 62 -62 10 -29
22 -58 27 -65 5 -6 17 -45 25 -86 9 -40 27 -91 41 -112 44 -67 85 -43 105 62
17 89 43 95 53 11 8 -64 31 -109 63 -121 28 -11 322 -14 348 -4 18 7 22 59 6
69 -21 13 -8 44 51 120 33 44 65 81 69 83 14 5 22 -75 18 -178 l-3 -95 85 -7
c113 -8 535 -10 594 -2 25 4 46 11 46 15 0 5 -48 36 -106 70 -131 77 -137 87
-101 174 15 36 27 74 27 84 0 19 184 181 207 181 22 0 36 -40 29 -84 -4 -22
-27 -81 -52 -131 -56 -112 -64 -155 -36 -183 12 -12 44 -29 73 -39 29 -10 74
-38 102 -63 44 -40 56 -45 94 -44 27 1 51 8 61 18 16 16 13 21 -55 84 l-73 68
0 57 c0 66 29 149 89 258 70 129 91 144 69 50 -17 -77 -7 -81 99 -33 109 49
151 58 177 39 17 -12 19 -26 18 -112 -1 -53 -7 -126 -13 -161 -10 -59 -17 -71
-74 -133 -65 -71 -84 -106 -67 -123 6 -6 58 -8 126 -6 158 4 158 4 167 175 9
155 31 224 89 277 39 35 90 66 109 66 6 0 16 -20 22 -45 9 -36 17 -46 38 -51
74 -16 176 -46 199 -58 15 -8 26 -23 27 -37 9 -136 19 -228 27 -249 13 -35 48
-70 70 -70 16 0 18 9 18 70 0 63 3 73 29 102 39 44 50 32 71 -74 l16 -83 74
-8 c41 -4 145 -6 230 -5 l155 3 0 59 0 60 80 69 c216 187 429 351 447 344 14
-5 109 -251 133 -344 8 -31 15 -83 15 -115 0 -54 2 -59 26 -68 34 -13 319 -13
367 1 38 10 38 11 21 31 -10 11 -56 41 -103 66 -91 51 -141 100 -160 159 -16
50 -34 359 -26 450 6 67 9 75 38 95 18 12 82 70 143 128 126 120 227 200 254
200 22 0 63 -53 58 -76 -2 -9 -38 -35 -81 -58 -83 -44 -247 -160 -247 -174 0
-5 17 -12 38 -16 64 -10 331 -7 399 6 114 21 114 22 -114 -257 -91 -111 -113
-143 -113 -169 0 -56 29 -290 40 -329 14 -48 45 -79 73 -75 26 4 35 32 36 114
2 113 66 265 161 384 29 36 57 75 63 88 7 15 18 22 31 20 18 -3 32 -34 85
-188 63 -180 65 -188 65 -289 1 -102 2 -104 29 -125 27 -20 41 -21 212 -21
192 0 213 4 201 42 -7 23 -74 63 -105 63 -30 0 -88 31 -135 72 l-40 34 -8 265
c-5 145 -7 288 -6 318 l3 54 61 23 c56 21 63 21 90 8 16 -8 36 -22 44 -31 13
-13 13 -16 -3 -26 -133 -84 -142 -97 -68 -97 58 0 91 -23 91 -65 0 -29 19 -32
99 -13 50 11 52 11 90 -20 47 -38 50 -38 126 -12 70 24 165 37 165 23 0 -14
-93 -144 -123 -172 -14 -13 -47 -37 -74 -55 -57 -37 -72 -71 -54 -121 8 -23
10 -55 5 -95 -10 -89 27 -172 87 -193 30 -10 41 10 50 92 10 91 36 175 69 220
22 32 146 99 189 103 23 3 27 -3 41 -58 9 -34 27 -85 39 -113 12 -29 31 -82
42 -119 12 -37 26 -73 32 -81 19 -22 81 -7 129 32 73 60 207 113 228 91 4 -5
4 -36 -1 -69 -18 -109 -43 -101 348 -107 199 -3 342 -2 348 4 6 5 -26 43 -88
103 l-98 95 -2 182 c-3 239 1 251 103 348 47 45 90 97 107 130 45 86 194 310
202 305 5 -2 59 -78 121 -167 63 -90 117 -167 122 -173 4 -5 8 535 8 1200 l-1
1210 -69 48 c-39 26 -70 53 -70 61 0 7 18 21 40 31 22 10 40 22 40 27 0 22
-220 -3 -287 -32 -26 -11 -48 -30 -58 -49 -18 -35 -91 -75 -295 -161 -151 -64
-296 -135 -306 -151 -5 -9 5 -11 37 -7 46 6 162 53 401 164 79 36 155 69 171
71 15 3 57 2 94 -2 77 -8 82 -13 187 -156 104 -141 107 -154 42 -163 -82 -13
-183 -125 -212 -235 -8 -33 -28 -89 -44 -124 -34 -76 -56 -159 -65 -250 l-7
-67 -50 0 c-27 0 -58 5 -68 10 -24 13 -99 140 -116 196 -25 83 25 167 119 202
61 23 84 50 95 112 18 99 -23 192 -93 211 -62 17 -169 8 -197 -18 -14 -12 -48
-25 -87 -32 -83 -14 -93 -24 -58 -57 15 -13 27 -29 27 -34 0 -5 -17 -18 -37
-29 l-38 -19 40 -17 c32 -15 36 -19 20 -22 -36 -8 -99 -31 -187 -67 -48 -20
-94 -36 -102 -36 -11 0 -16 18 -21 68 -10 94 -42 265 -65 342 -13 43 -30 75
-50 95 -16 16 -30 36 -30 46 0 9 -7 22 -15 29 -9 7 -15 29 -15 51 0 48 -6 45
250 137 251 91 364 138 385 162 41 45 -22 31 -343 -75 -368 -121 -541 -173
-592 -177 -32 -3 -35 -1 -42 32 -35 163 -48 236 -48 268 0 21 -6 46 -12 55
-18 25 -130 69 -143 56 -5 -5 -15 -40 -20 -77 -10 -62 -12 -67 -34 -64 -29 4
-27 1 55 -100 69 -84 87 -129 82 -198 l-3 -44 -72 -31 c-40 -16 -77 -35 -84
-42 -20 -20 1 -33 52 -33 27 0 49 -3 49 -7 0 -3 -12 -22 -27 -42 -68 -90 -143
-167 -222 -225 -47 -35 -99 -78 -116 -95 -16 -17 -50 -38 -75 -45 -25 -8 -65
-23 -90 -35 -90 -40 -234 -49 -247 -14 -8 22 7 32 78 51 76 20 76 21 71 174
l-3 99 -85 39 c-46 21 -84 42 -84 45 0 4 8 19 18 33 37 53 37 54 1 50 -32 -3
-34 -1 -37 30 -2 22 5 48 23 78 14 24 24 48 21 53 -12 19 -48 19 -68 -1 -16
-16 -22 -18 -29 -7 -5 8 -9 18 -9 22 0 24 -19 -9 -25 -43 -7 -40 -18 -51 -29
-27 -3 6 -4 -35 -1 -93 3 -58 2 -110 -3 -116 -21 -32 -88 -45 -98 -19 -4 8
-14 15 -24 15 -14 0 -22 -13 -32 -47 -12 -45 -20 -52 -103 -108 -50 -33 -102
-72 -117 -87 -29 -30 -99 -53 -134 -44 -31 8 -30 13 5 67 59 88 142 179 195
213 71 45 419 427 403 442 -11 11 -57 -30 -202 -182 -82 -87 -172 -172 -200
-190 -57 -36 -128 -109 -243 -252 -44 -53 -87 -103 -96 -111 -23 -19 -62 -5
-84 31 -15 25 -15 30 1 63 31 65 14 129 -49 184 -62 55 -178 48 -205 -12 -7
-15 -23 -27 -38 -30 -16 -4 -32 -16 -39 -31 -11 -25 -36 -35 -46 -18 -2 4 13
55 35 112 50 132 69 220 69 320 0 96 -17 124 -75 124 -30 0 -44 -7 -67 -33
-29 -33 -30 -36 -22 -95 4 -34 8 -77 7 -97 -1 -40 -50 -145 -82 -173 -20 -18
-22 -18 -39 -3 -36 33 -151 16 -191 -27 l-23 -25 21 -62 c11 -35 21 -87 21
-116 0 -53 -1 -54 -37 -69 -42 -18 -143 -31 -143 -19 0 5 8 17 17 27 26 29 5
40 -59 30 l-55 -8 -10 87 c-11 93 -29 134 -66 148 -12 5 -48 19 -80 31 -52 20
-60 21 -75 7 -9 -8 -39 -17 -66 -20 -27 -2 -52 -11 -57 -18 -15 -24 -28 -81
-35 -161 l-7 -77 38 -18 c61 -29 75 -103 19 -103 -14 0 -37 -9 -51 -20 -14
-11 -36 -20 -50 -20 -16 0 -51 -26 -112 -85 l-88 -85 -11 61 c-22 119 -110
274 -179 316 -48 29 -106 30 -133 3 -29 -29 -25 -51 16 -91 20 -18 43 -47 51
-64 21 -40 43 -122 43 -162 0 -28 -4 -33 -23 -33 -28 0 -93 44 -159 108 -50
48 -58 71 -33 92 8 7 15 29 15 49 1 20 7 47 15 61 8 14 14 34 15 46 0 21 -102
179 -148 226 -45 49 -122 71 -244 70 -87 0 -109 2 -121 16 -21 25 -67 114 -67
132 0 8 16 46 35 84 23 47 32 73 25 80 -5 5 -10 20 -10 33 0 16 -8 27 -25 33
-14 5 -25 15 -25 21 0 7 -9 5 -25 -5 -24 -16 -25 -15 -31 9 -13 51 -30 24 -36
-55 -2 -22 -8 -35 -17 -35 -19 0 -131 162 -131 190 0 13 7 42 15 66 23 64 76
277 145 584 34 149 72 314 86 367 28 113 29 118 10 118 -10 0 -41 -109 -111
-397 -152 -627 -151 -623 -201 -623 -40 0 -9 -44 -504 724 -113 174 -225 339
-250 365 -41 43 -52 48 -112 58 -84 14 -126 34 -207 100 l-66 53 -81 0 c-48 0
-87 5 -97 13 -13 10 -17 36 -21 142 -3 93 -10 153 -26 210 -35 125 -35 136 1
274 28 108 31 133 21 155 -6 14 -47 49 -94 79 -55 36 -110 84 -162 141 -77 85
-80 88 -115 82 -118 -19 -115 -19 -179 13 -65 33 -82 37 -82 21z m1270 -1941
c33 -49 64 -81 181 -188 31 -28 83 -88 115 -133 124 -171 169 -222 230 -257
106 -62 124 -79 124 -114 0 -18 -11 -84 -24 -147 -51 -241 -57 -260 -82 -260
-9 0 -48 11 -86 25 -38 14 -88 25 -112 25 -31 0 -48 6 -63 23 -37 41 -167 261
-229 387 -34 69 -100 192 -148 274 -90 158 -103 187 -131 304 -21 88 -21 88
75 97 60 6 97 10 110 14 3 0 21 -22 40 -50z m799 -829 c61 -99 63 -132 16
-212 -93 -155 -174 -241 -189 -202 -8 21 80 454 99 484 11 18 31 -1 74 -70z
m4875 -205 c-22 -34 -13 -99 18 -132 13 -14 41 -34 62 -45 l39 -20 -7 -196
c-4 -108 -8 -198 -10 -199 -9 -9 -125 -19 -145 -12 -14 4 -50 17 -79 27 l-54
19 30 34 c47 55 55 77 54 163 -1 102 -13 136 -47 136 -14 0 -28 7 -32 16 -10
27 14 154 33 175 18 20 106 58 137 58 16 1 16 -1 1 -24z m-4666 -106 c18 -30
37 -71 43 -92 5 -21 23 -59 40 -84 16 -26 29 -52 29 -57 0 -42 24 -86 60 -111
67 -47 45 -69 -43 -44 -26 7 -42 19 -49 37 -18 41 -79 121 -108 139 -17 12
-43 16 -82 15 -32 -2 -58 0 -58 3 0 10 69 174 91 217 11 22 25 38 32 35 7 -2
27 -28 45 -58z m-1183 -219 c3 -5 -12 -21 -34 -34 -54 -34 -56 -42 -27 -77 48
-57 21 -124 -49 -121 -73 3 -97 59 -51 121 13 18 30 47 37 64 7 18 17 38 22
45 11 14 93 16 102 2z m4492 -74 l28 -24 -44 -41 c-24 -22 -64 -48 -90 -58
-25 -9 -72 -36 -104 -60 -94 -68 -174 -114 -212 -120 -53 -9 -46 20 13 48 82
39 164 100 256 191 49 48 98 88 107 88 10 0 31 -11 46 -24z m-7484 -34 c29
-13 47 -27 45 -34 -3 -7 -68 -38 -146 -69 -78 -32 -156 -69 -173 -84 -24 -19
-39 -25 -58 -20 -37 8 -151 129 -151 159 0 23 7 26 93 45 136 30 134 30 242
27 77 -2 111 -8 148 -24z m4116 -17 c55 -63 54 -131 -1 -78 -26 24 -68 91 -68
108 0 18 43 -1 69 -30z m4281 -5 c17 -31 4 -55 -50 -96 -58 -44 -88 -84 -165
-224 -32 -58 -75 -127 -96 -155 -50 -66 -97 -167 -104 -224 -11 -84 -14 -90
-51 -106 -66 -27 -188 -127 -345 -282 -85 -84 -163 -153 -171 -153 -20 0 -74
53 -83 81 -5 15 2 32 22 57 39 49 55 81 87 180 15 45 29 82 31 82 2 0 16 -5
30 -12 19 -9 28 -9 37 0 7 7 25 12 40 12 32 0 68 31 68 58 0 10 -11 29 -25 42
-14 13 -25 30 -25 37 0 8 25 68 55 134 30 66 58 131 61 145 7 29 32 44 72 44
41 0 136 34 196 71 78 48 150 107 274 223 62 58 117 106 122 106 5 0 14 -9 20
-20z m-4401 -243 c10 -27 -1 -66 -35 -124 -37 -64 -48 -46 -35 61 6 48 13 91
16 96 9 15 45 -7 54 -33z m1991 -98 c0 -6 -13 -27 -30 -46 -16 -18 -30 -42
-30 -53 0 -37 -108 -135 -128 -116 -17 18 56 175 96 207 27 20 92 26 92 8z
m860 -16 c0 -4 -11 -18 -25 -33 -27 -28 -85 -161 -85 -195 0 -27 34 -58 140
-129 47 -30 95 -65 108 -75 l23 -20 -39 -53 c-56 -76 -207 -215 -290 -268 -85
-54 -96 -70 -47 -70 29 0 35 -4 35 -21 0 -22 -69 -99 -89 -99 -13 0 -31 34
-71 136 -18 44 -44 94 -58 113 -44 55 -72 95 -72 103 0 4 11 15 25 24 22 15
33 15 84 4 50 -10 71 -10 142 5 98 19 159 21 188 5 27 -14 41 -6 41 24 0 12
11 32 25 44 16 14 25 32 25 51 0 35 -22 41 -110 28 -142 -19 -209 -20 -235 -3
-15 10 -25 26 -25 39 1 12 42 96 93 187 85 152 96 167 137 188 44 22 80 29 80
15z m-2195 -16 c46 -27 125 -92 125 -102 0 -6 -13 -21 -28 -32 -35 -26 -147
-83 -163 -83 -16 0 -1 178 17 208 16 25 21 26 49 9z m-3615 -62 c-40 -42 -108
-90 -116 -83 -9 10 28 88 49 103 12 8 39 15 61 15 l40 0 -34 -35z m8126 4 c8
-14 -44 -69 -66 -69 -38 0 -54 51 -22 69 23 14 79 14 88 0z m-390 -18 c18 -11
47 -47 68 -85 20 -36 46 -72 57 -80 26 -18 15 -43 -36 -77 -25 -17 -45 -44
-66 -89 -60 -132 -59 -130 -190 -212 -162 -101 -172 -105 -204 -84 -14 9 -25
26 -25 39 0 24 4 33 78 161 28 49 63 126 78 170 48 145 100 232 157 260 41 20
46 20 83 -3z m-4971 -52 c10 -15 -1 -23 -20 -15 -9 3 -13 10 -10 16 8 13 22
13 30 -1z m3269 -24 c6 -16 -9 -65 -20 -65 -13 0 -64 35 -64 44 0 29 73 48 84
21z m-7732 -61 l27 -6 -39 -47 c-43 -51 -109 -171 -140 -255 -12 -30 -32 -71
-45 -91 -14 -19 -40 -73 -59 -118 -60 -144 -127 -275 -143 -282 -27 -10 -47
21 -128 195 -90 191 -107 237 -100 259 3 9 28 33 54 53 57 43 134 139 168 210
13 27 35 61 50 75 l25 26 152 -7 c83 -4 163 -9 178 -12z m5906 -46 c3 -41 0
-49 -22 -63 -42 -28 -84 -25 -103 7 -16 26 -16 28 2 49 25 27 85 59 105 56 10
-1 16 -17 18 -49z m-2548 26 c0 -28 -29 -79 -76 -134 -24 -28 -44 -55 -44 -60
0 -4 -12 -11 -27 -14 -14 -4 -45 -20 -68 -36 -22 -15 -50 -30 -60 -32 -17 -2
-20 3 -19 32 1 53 45 113 109 152 31 18 79 50 108 70 57 41 77 46 77 22z
m-2525 -4 c3 -5 -6 -20 -20 -33 -15 -12 -45 -53 -68 -91 -23 -38 -46 -69 -52
-70 -12 -1 -19 66 -10 111 9 50 58 93 105 93 21 0 42 -4 45 -10z m4711 -22
c-4 -18 -11 -118 -16 -223 l-9 -190 -138 -135 c-156 -153 -260 -243 -276 -237
-6 2 -31 63 -55 137 l-45 133 18 51 c37 104 73 165 108 180 41 17 129 84 243
183 171 150 181 156 170 101z m4980 -175 c-4 -69 -12 -135 -17 -145 -12 -23
-32 -23 -105 2 -32 12 -68 18 -80 16 -28 -7 -114 -84 -114 -102 0 -22 33 -17
70 11 37 27 80 32 142 15 75 -21 61 -43 -66 -103 -114 -55 -149 -84 -187 -158
-33 -67 -58 -76 -86 -32 -26 40 -23 191 6 303 36 136 41 149 67 163 13 7 32
28 42 47 10 21 32 42 54 53 37 18 198 54 252 56 l29 1 -7 -127z m-6592 45 c21
-30 20 -54 -3 -87 -11 -15 -31 -51 -45 -81 -14 -30 -45 -83 -68 -116 -24 -34
-51 -76 -60 -93 -21 -38 -49 -40 -77 -5 -27 35 -27 89 0 134 52 84 210 270
230 270 4 0 15 -10 23 -22z m1823 -65 c38 -55 34 -70 -32 -106 -52 -29 -52
-30 -35 69 19 102 21 104 67 37z m630 -28 c-3 -41 -16 -107 -29 -147 l-23 -73
-3 59 c-4 80 29 236 50 236 7 0 9 -23 5 -75z m-983 -492 c-9 -16 -48 -62 -84
-104 -72 -81 -110 -152 -161 -299 -27 -78 -34 -90 -44 -76 -18 24 -26 143 -15
216 8 51 16 70 38 88 15 13 65 56 111 95 100 86 130 107 155 107 18 0 18 -1 0
-27z m1361 -727 c27 -19 14 -44 -70 -134 -74 -79 -108 -102 -154 -102 -41 0
-52 -19 -96 -169 -57 -195 -64 -202 -159 -162 l-45 20 31 93 c32 93 45 112
118 167 20 15 51 62 85 130 30 58 61 115 69 125 30 38 183 60 221 32z m-6972
-135 c48 -8 48 -21 3 -36 -20 -7 -53 -30 -74 -50 -20 -21 -57 -45 -81 -54 -65
-24 -79 -49 -96 -180 -13 -99 -32 -161 -51 -161 -14 0 -62 31 -83 54 -28 30
-32 1 33 276 20 85 42 158 49 163 12 8 232 0 300 -12z m1508 -1 c59 -32 66
-190 15 -325 -15 -38 -32 -85 -37 -102 -6 -18 -17 -33 -25 -33 -23 0 -66 25
-93 54 l-25 26 19 73 c10 39 27 86 36 102 10 17 25 61 34 100 14 64 37 115 51
115 3 0 14 -5 25 -10z m8272 -68 c9 -10 20 -33 23 -50 4 -18 8 -32 10 -32 3 0
35 15 72 34 88 45 170 68 224 64 39 -3 43 -5 40 -28 -5 -43 -110 -210 -140
-221 -44 -17 -233 -7 -265 14 -26 17 -27 20 -27 121 0 56 3 106 7 109 12 13
40 7 56 -11z m-5368 -138 c12 -13 15 -29 11 -68 -3 -28 -8 -81 -12 -119 -9
-100 -42 -108 -133 -31 -29 24 -31 30 -31 96 0 66 2 72 34 104 27 27 42 34 74
34 24 0 47 -7 57 -16z m-2465 -58 c0 -50 -14 -67 -29 -37 -14 26 -14 54 1 69
21 21 28 13 28 -32z m-781 -13 c-4 -28 -3 -71 3 -103 12 -63 6 -105 -17 -114
-17 -6 -122 27 -139 44 -6 6 -7 19 0 35 9 25 142 185 154 185 4 0 3 -21 -1
-47z m-360 -28 l42 -45 -27 -48 c-53 -93 -52 -92 -93 -92 -42 0 -91 38 -91 70
0 10 19 42 43 70 23 29 48 61 56 71 7 11 17 19 21 19 4 0 26 -20 49 -45z
m3021 15 c11 -11 20 -27 20 -35 0 -30 -29 -85 -45 -85 -38 0 -90 97 -70 129
11 19 73 13 95 -9z m-2080 -32 c37 -62 16 -137 -34 -122 -30 9 -66 44 -66 65
0 19 56 89 71 89 5 0 18 -15 29 -32z m8564 6 c12 -4 16 -20 16 -54 0 -104 -25
-144 -76 -121 -20 9 -24 18 -24 56 0 90 34 138 84 119z"/>
<path d="M178 2610 c-9 -6 -26 -39 -38 -74 -23 -71 -38 -88 -92 -105 -42 -14
-48 -25 -48 -94 0 -98 24 -99 127 -3 66 61 100 125 110 205 4 36 1 51 -12 64
-19 19 -24 20 -47 7z"/>
<path d="M12766 250 c-20 -51 -21 -153 -2 -169 12 -10 17 -8 25 8 13 24 15
201 2 201 -5 0 -16 -18 -25 -40z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 19 KiB

View file

@ -8,15 +8,15 @@ rss(version='2.0' xmlns:atom="http://www.w3.org/2005/Atom")
each event in events each event in events
item item
title [#{moment.unix(event.start_datetime).format("YY-MM-DD")}] #{event.title} @#{event.place.name} title [#{moment.unix(event.start_datetime).format("YY-MM-DD")}] #{event.title} @#{event.place.name}
link #{settings.baseurl}/event/#{event.id} link #{settings.baseurl}/event/#{event.slug || event.id}
description description
| <![CDATA[ | <![CDATA[
| <h4>#{event.title}</h4> | <h4>#{event.title}</h4>
| <strong>#{event.place.name} - #{event.place.address}</strong> | <strong>#{event.place.name} - #{event.place.address}</strong>
| <small>(#{moment.unix(event.start_datetime).format("dddd, D MMMM HH:mm")})</small><br/> | <small>(#{moment.unix(event.start_datetime).format("dddd, D MMMM HH:mm")})</small><br/>
if (event.image_path) if (event.media && event.media.length)
| <img src="#{settings.baseurl}/media/#{event.image_path}"/> | <img alt="#{event.media[0].name || ''}" src="#{settings.baseurl}/media/#{event.media[0].url}"/>
| <pre>!{event.description}</pre> | <pre>!{event.description}</pre>
| ]]> | ]]>
pubDate= new Date(event.updatedAt).toUTCString() pubDate= new Date(event.updatedAt).toUTCString()
guid(isPermaLink='false') #{settings.baseurl}/event/#{event.id} guid(isPermaLink='false') #{settings.baseurl}/event/#{event.slug || event.id}

1267
yarn.lock

File diff suppressed because it is too large Load diff