Merge branch 'master' into gh
This commit is contained in:
commit
5dddfbd29e
126 changed files with 6962 additions and 2739 deletions
9
.gitignore
vendored
9
.gitignore
vendored
|
@ -1,13 +1,14 @@
|
||||||
# Created by .ignore support plugin (hsz.mobi)
|
# Created by .ignore support plugin (hsz.mobi)
|
||||||
|
|
||||||
### Gancio dev configuration
|
### Gancio dev configuration
|
||||||
*.sqlite
|
gancio.sqlite
|
||||||
|
db.sqlite
|
||||||
releases
|
releases
|
||||||
wp-plugin/wpgancio
|
wp-plugin/wpgancio
|
||||||
config/development.json
|
config/development.json
|
||||||
gancio_config.json
|
/gancio_config.json
|
||||||
config.json
|
/config.json
|
||||||
db.sqlite
|
/assets/config.json
|
||||||
thumb
|
thumb
|
||||||
docs/_site
|
docs/_site
|
||||||
.vscode
|
.vscode
|
||||||
|
|
68
CHANGELOG
68
CHANGELOG
|
@ -1,11 +1,77 @@
|
||||||
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
|
||||||
|
- add CLI support to manage accounts (list / modify / add accounts)
|
||||||
|
### 1.4.3 - 10 mar '22
|
||||||
|
- fix [#140](https://framagit.org/les/gancio/-/issues/140) - Invalid date
|
||||||
|
- fix [#141](https://framagit.org/les/gancio/-/issues/141) - Cannot change logo
|
||||||
|
- fix same day events
|
||||||
|
- add missing icons in admin
|
||||||
|
- prepare multisite settings
|
||||||
|
- improve initialization
|
||||||
|
- start unit testing API (it's never too late)
|
||||||
|
|
||||||
|
### 1.4.1 - 4 mar '22
|
||||||
|
- add gl/galego locale, thanks @xosem
|
||||||
|
- fix import redirect loop
|
||||||
|
- add missing icons (close, repeat, arrows ...)
|
||||||
|
- turn rss icon into a real link to improve a11y
|
||||||
|
- force seconds to 0 for each events, fix recurring events starting date issue
|
||||||
|
- fix next/prev selection on same datetime events
|
||||||
|
- improve moderation UI (add author and event link + format creation date)
|
||||||
|
- refactoring resource UI from fedi
|
||||||
|
|
||||||
|
### 1.4.0 - 9 feb '22
|
||||||
|
- improve Cumulative Layout Shift
|
||||||
|
- remove filename as default media label to avoid leak metadata
|
||||||
|
- add endData to microdata
|
||||||
|
- security fix with filtering settings, avoid sharing SMTP pass with front-end
|
||||||
|
- fix broken SMTP
|
||||||
|
- remove global materialicons / vuetify css, use threeshake and @nuxt/vuetify (really improve lighthouse score)
|
||||||
|
- new Dockerfile using node:17.4-slim as base img (from 1.5Gb to ~800Mb)
|
||||||
|
- add XSS and path traversal mitigation
|
||||||
|
- improve a11y
|
||||||
|
- update deps
|
||||||
|
|
||||||
|
### 1.3.3 - 1 feb '22
|
||||||
|
- security fix, avoid sharing smtp pass with front-end
|
||||||
|
|
||||||
|
### 1.3.2 - 1 feb '22
|
||||||
|
- fix webcomponent for event without img
|
||||||
|
|
||||||
|
### 1.3.1 - 1 feb '22
|
||||||
|
- inherits tags in recurring events [#138](https://framagit.org/les/gancio/-/issues/138)
|
||||||
|
- you can now skip an occurrence of a recurring event
|
||||||
|
- fix `show_recurrent` event in webcomponent and API
|
||||||
|
- add new webcomponent `sidebar` attribute and a [`fullwith` layout](https://gancio.org/usage/embed#embed-event-lists)
|
||||||
|
|
||||||
|
### 1.3.0 - 26 gen '22
|
||||||
|
- add mariadb support
|
||||||
|
- add [microdata](https://developer.mozilla.org/en-US/docs/Web/HTML/Microdata) support
|
||||||
|
- support db setup via environment variables (used in updated `docker-compose.yml` files)
|
||||||
|
- improve rss feed:
|
||||||
|
- fix validation
|
||||||
|
- add enclusure media for featured image
|
||||||
|
- add categories
|
||||||
|
- fix typo in export code
|
||||||
|
- add theme attribute to gancio-events webcomponent (dark/light)
|
||||||
|
- increase thumbs quality
|
||||||
|
- improve logo for light theme
|
||||||
|
- improve [wordpress plugin](https://wordpress.org/plugins/wpgancio/)
|
||||||
|
- add \[gancio-event\] and \[gancio-events\] shortcode
|
||||||
|
- allow gancio-events / gancio-event tags in editor
|
||||||
|
- automatically enqueue webcomponent script
|
||||||
|
- tags/places filters are now inclusive not exclusive
|
||||||
|
- fix image undefined alternative text
|
||||||
|
- update documentation, dependencies, translations
|
||||||
|
|
||||||
### 1.2.2 - 7 dic '21
|
### 1.2.2 - 7 dic '21
|
||||||
- shiny new gancio-event\[s\] webcomponents => [docs](https://gancio.org/usage/embed)
|
- shiny new gancio-event\[s\] webcomponents => [docs](https://gancio.org/usage/embed)
|
||||||
- new backend plugin system
|
- new backend plugin system => [docs](https://gancio.org/dev/plugins)
|
||||||
- improve media focal point selection
|
- improve media focal point selection
|
||||||
- improve non-js experience (load img, use native lazy loading)
|
- improve non-js experience (load img, use native lazy loading)
|
||||||
- improve user_confirm / recover code flow
|
- improve user_confirm / recover code flow
|
||||||
|
- permit admins to choose user password (usefull on instance without SMTP configuration)
|
||||||
- fix task manager exception
|
- fix task manager exception
|
||||||
- fix db initialization when a custom setup is used, #131
|
- fix db initialization when a custom setup is used, #131
|
||||||
- remove vue-clipboard2 dependency due to [this](https://github.com/euvl/v-clipboard/issues/18) bug and using a [native with fallback mixin instead](./assets/clipboard.js)
|
- remove vue-clipboard2 dependency due to [this](https://github.com/euvl/v-clipboard/issues/18) bug and using a [native with fallback mixin instead](./assets/clipboard.js)
|
||||||
|
|
|
@ -1,11 +1,7 @@
|
||||||
- change version in package.json
|
- change version in package.json
|
||||||
- add changes to CHANGELOG / changelog.md
|
- add changes to CHANGELOG / changelog.md
|
||||||
- yarn build
|
./release.sh
|
||||||
- yarn pack
|
|
||||||
- yarn publish
|
|
||||||
- yarn doc
|
|
||||||
- git add .
|
- git add .
|
||||||
- git ci -m 'v...'
|
- git ci -m 'v...'
|
||||||
- git tag ...
|
- git tag ...
|
||||||
- git push --tags
|
- git push --tags
|
||||||
-
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ export default {
|
||||||
document.execCommand('copy')
|
document.execCommand('copy')
|
||||||
document.body.removeChild(el)
|
document.body.removeChild(el)
|
||||||
}
|
}
|
||||||
this.$root.$message(msg)
|
this.$root.$message(msg, { color: 'success'})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,61 +0,0 @@
|
||||||
|
|
||||||
.editor {
|
|
||||||
position: relative;
|
|
||||||
overflow-y: auto;
|
|
||||||
padding-top: 1.7em;
|
|
||||||
scrollbar-width: thin;
|
|
||||||
|
|
||||||
&.with-border {
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
border-radius: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
padding: 0px 5px 0px 5px;
|
|
||||||
flex: 1;
|
|
||||||
scrollbar-width: thin;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.menububble {
|
|
||||||
position: absolute;
|
|
||||||
display: flex;
|
|
||||||
overflow: hidden;
|
|
||||||
opacity: 0;
|
|
||||||
z-index: 1;
|
|
||||||
background: #dddddd;
|
|
||||||
transform: translateX(-50%);
|
|
||||||
border-radius: 3px;
|
|
||||||
padding: 0.07rem;
|
|
||||||
transition: opacity 0.2s, visibility 0.2s, left .2s, bottom .2s;
|
|
||||||
visibility: hidden;
|
|
||||||
|
|
||||||
&.is-active {
|
|
||||||
opacity: 1;
|
|
||||||
visibility: visible;
|
|
||||||
}
|
|
||||||
input {
|
|
||||||
padding: 0;
|
|
||||||
margin: 1px;
|
|
||||||
display: block;
|
|
||||||
border: 0;
|
|
||||||
color: #444;
|
|
||||||
font-size: .8em;
|
|
||||||
border-radius: 3px;
|
|
||||||
line-height: 100%;
|
|
||||||
transition: width .2s;
|
|
||||||
padding-left: 5px;
|
|
||||||
flex-grow: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fa-icon {
|
|
||||||
width: auto;
|
|
||||||
font-size: 10px;
|
|
||||||
height: 1.4em; /* or any other relative font sizes */
|
|
||||||
/* You would have to include the following two lines to make this work in Safari */
|
|
||||||
// max-width: 100%;
|
|
||||||
max-height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,56 +0,0 @@
|
||||||
// .event {
|
|
||||||
// width: 320px;
|
|
||||||
// max-width: 450px;
|
|
||||||
// flex-grow: 1;
|
|
||||||
// margin: .2em;
|
|
||||||
// background-color: #202020;
|
|
||||||
// overflow: hidden;
|
|
||||||
|
|
||||||
// a:hover {
|
|
||||||
// text-decoration: none;
|
|
||||||
// .title {
|
|
||||||
// border-bottom: 1px solid #888;
|
|
||||||
// color: white;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// .title {
|
|
||||||
// margin-left: 1rem;
|
|
||||||
// margin-top: 1rem;
|
|
||||||
// margin-right: 1rem;
|
|
||||||
// border-bottom: 1px solid #333;
|
|
||||||
// transition: border-color .5s;
|
|
||||||
// font-size: 1.2em;
|
|
||||||
// max-height: 3em;
|
|
||||||
// overflow: hidden;
|
|
||||||
// color: white;
|
|
||||||
// font-weight: bold;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// .card-footer {
|
|
||||||
// max-height: 4.5em;
|
|
||||||
// overflow: hidden;
|
|
||||||
// padding: .25rem 0.5rem;
|
|
||||||
// line-height: 1.8rem;
|
|
||||||
// min-height: 2.2rem;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// .card-body {
|
|
||||||
// overflow: hidden;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// .description {
|
|
||||||
// color: #999;
|
|
||||||
// font-size: 0.8em;
|
|
||||||
// overflow: hidden;
|
|
||||||
// max-height: 100%;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// .el-image { width: 100% }
|
|
||||||
// img {
|
|
||||||
// width: 100%;
|
|
||||||
// max-height: 250px;
|
|
||||||
// object-fit: cover;
|
|
||||||
// object-position: top;
|
|
||||||
// }
|
|
||||||
// }
|
|
|
@ -30,6 +30,10 @@ li {
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#calh {
|
||||||
|
height: 292px;
|
||||||
|
}
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
max-width: 1400px;
|
max-width: 1400px;
|
||||||
}
|
}
|
||||||
|
@ -91,7 +95,6 @@ li {
|
||||||
.place {
|
.place {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
span {
|
span {
|
||||||
display: block;
|
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
|
8
assets/variables.scss
Normal file
8
assets/variables.scss
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
// assets/variables.scss
|
||||||
|
|
||||||
|
// Variables you want to modify
|
||||||
|
// $btn-border-radius: 0px;
|
||||||
|
|
||||||
|
// If you need to extend Vuetify SASS lists
|
||||||
|
// $material-light: ( cards: blue );
|
||||||
|
@import '~vuetify/src/styles/styles.sass';
|
|
@ -1,15 +1,16 @@
|
||||||
<template lang="pug">
|
<template lang="pug">
|
||||||
nuxt-link(:to='`/announcement/${announcement.id}`')
|
nuxt-link(:to='`/announcement/${announcement.id}`')
|
||||||
v-alert.mb-1(border='left' type='info' color="primary" show-icon) {{announcement.title}}
|
v-alert.mb-1(border='left' type='info' color="primary" :icon='mdiInformation') {{announcement.title}}
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import { mapState } from 'vuex'
|
import { mdiInformation } from '@mdi/js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
data () {
|
||||||
|
return { mdiInformation }
|
||||||
|
},
|
||||||
props: {
|
props: {
|
||||||
announcement: { type: Object, default: () => ({}) }
|
announcement: { type: Object, default: () => ({}) }
|
||||||
},
|
}
|
||||||
computed: mapState(['announcements'])
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,17 +1,18 @@
|
||||||
<template lang="pug">
|
<template lang="pug">
|
||||||
#calendar
|
#calendar
|
||||||
vc-calendar(
|
vc-date-picker(
|
||||||
|
v-model='selectedDate'
|
||||||
title-position='left'
|
title-position='left'
|
||||||
:is-dark="settings['theme.is_dark']"
|
:is-dark="settings['theme.is_dark']"
|
||||||
:columns="$screens({ sm: 2 }, 1)"
|
:columns="$screens({ sm: 2 }, 1)"
|
||||||
|
@input='click'
|
||||||
@update:from-page='updatePage'
|
@update:from-page='updatePage'
|
||||||
:locale='$i18n.locale'
|
:locale='$i18n.locale'
|
||||||
:attributes='attributes'
|
:attributes='attributes'
|
||||||
transition='fade'
|
transition='fade'
|
||||||
aria-label='Calendar'
|
aria-label='Calendar'
|
||||||
is-expanded
|
is-expanded
|
||||||
is-inline
|
is-inline)
|
||||||
@dayclick='click')
|
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
|
@ -28,6 +29,7 @@ export default {
|
||||||
const month = dayjs().month() + 1
|
const month = dayjs().month() + 1
|
||||||
const year = dayjs().year()
|
const year = dayjs().year()
|
||||||
return {
|
return {
|
||||||
|
selectedDate: null,
|
||||||
page: { month, year }
|
page: { month, year }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -7,64 +7,67 @@
|
||||||
v-btn(icon text tabindex='-1'
|
v-btn(icon text tabindex='-1'
|
||||||
:class="{ primary: isActive.bold() }"
|
:class="{ primary: isActive.bold() }"
|
||||||
@click="commands.bold")
|
@click="commands.bold")
|
||||||
v-icon mdi-format-bold
|
v-icon(v-text='mdiFormatBold')
|
||||||
|
|
||||||
v-btn(icon text tabindex='-1'
|
v-btn(icon text tabindex='-1'
|
||||||
:class="{ primary: isActive.underline() }"
|
:class="{ primary: isActive.underline() }"
|
||||||
@click="commands.underline")
|
@click="commands.underline")
|
||||||
v-icon mdi-format-underline
|
v-icon(v-text='mdiFormatUnderline')
|
||||||
|
|
||||||
v-btn(icon text tabindex='-1'
|
v-btn(icon text tabindex='-1'
|
||||||
:class="{ primary: isActive.strike() }"
|
:class="{ primary: isActive.strike() }"
|
||||||
@click="commands.strike")
|
@click="commands.strike")
|
||||||
v-icon mdi-format-strikethrough-variant
|
v-icon(v-text='mdiFormatStrikethroughVariant')
|
||||||
|
|
||||||
v-btn(icon text tabindex='-1'
|
v-btn(icon text tabindex='-1'
|
||||||
:class="{ primary: isActive.italic() }"
|
:class="{ primary: isActive.italic() }"
|
||||||
@click="commands.italic")
|
@click="commands.italic")
|
||||||
v-icon mdi-format-italic
|
v-icon(v-text='mdiFormatItalic')
|
||||||
|
|
||||||
v-btn(icon text tabindex='-1'
|
v-btn(icon text tabindex='-1'
|
||||||
:class="{ primary: isActive.heading({level: 1}) }"
|
:class="{ primary: isActive.heading({level: 1}) }"
|
||||||
@click="commands.heading({level: 1})")
|
@click="commands.heading({level: 1})")
|
||||||
v-icon mdi-format-header-1
|
v-icon(v-text='mdiFormatHeader1')
|
||||||
|
|
||||||
v-btn(icon text tabindex='-1'
|
v-btn(icon text tabindex='-1'
|
||||||
:class="{ primary: isActive.heading({level: 2}) }"
|
:class="{ primary: isActive.heading({level: 2}) }"
|
||||||
@click="commands.heading({level: 2})")
|
@click="commands.heading({level: 2})")
|
||||||
v-icon mdi-format-header-2
|
v-icon(v-text='mdiFormatHeader2')
|
||||||
|
|
||||||
v-btn(icon text tabindex='-1'
|
v-btn(icon text tabindex='-1'
|
||||||
:class="{ primary: isActive.heading({level: 3}) }"
|
:class="{ primary: isActive.heading({level: 3}) }"
|
||||||
@click="commands.heading({level: 3})")
|
@click="commands.heading({level: 3})")
|
||||||
v-icon mdi-format-header-3
|
v-icon(v-text='mdiFormatHeader3')
|
||||||
|
|
||||||
v-btn(icon text tabindex='-1'
|
v-btn(icon text tabindex='-1'
|
||||||
:class="{ primary: isActive.code() }"
|
:class="{ primary: isActive.code() }"
|
||||||
@click="commands.code")
|
@click="commands.code")
|
||||||
v-icon mdi-code-tags
|
v-icon(v-text='mdiCodeTags')
|
||||||
|
|
||||||
v-btn(icon text tabindex='-1'
|
v-btn(icon text tabindex='-1'
|
||||||
:class="{ primary: isActive.blockquote() }"
|
:class="{ primary: isActive.blockquote() }"
|
||||||
@click="commands.blockquote")
|
@click="commands.blockquote")
|
||||||
v-icon mdi-format-quote-open
|
v-icon(v-text='mdiFormatQuoteOpen')
|
||||||
|
|
||||||
v-btn(icon text tabindex='-1'
|
v-btn(icon text tabindex='-1'
|
||||||
:class="{ primary: isActive.bullet_list() }"
|
:class="{ primary: isActive.bullet_list() }"
|
||||||
@click="commands.bullet_list")
|
@click="commands.bullet_list")
|
||||||
v-icon mdi-format-list-bulleted
|
v-icon(v-text='mdiFormatListBulleted')
|
||||||
|
|
||||||
v-btn(icon text tabindex='-1' :class='{ primary: isActive.link() }'
|
v-btn(icon text tabindex='-1' :class='{ primary: isActive.link() }'
|
||||||
@click='commands.link({href: getMarkAttrs("link") && getMarkAttrs("link").href ? "" : "https://"}); $refs.link.focus();')
|
@click='commands.link({href: getMarkAttrs("link") && getMarkAttrs("link").href ? "" : "https://"}); $refs.link.focus();')
|
||||||
v-icon mdi-link
|
v-icon(v-text='mdiLink')
|
||||||
v-text-field.pt-0.ml-1(v-show='isActive.link()' ref='link' @focus='focus' @blur='blur' hide-details
|
v-text-field.pt-0.ml-1(v-show='isActive.link()' ref='link' @focus='focus' @blur='blur' hide-details
|
||||||
:value='isActive.link() && getMarkAttrs("link") && getMarkAttrs("link").href || ""'
|
:value='isActive.link() && getMarkAttrs("link") && getMarkAttrs("link").href || ""'
|
||||||
@keypress.enter='commands.link({ href: $event.target.value}); editor.focus()')
|
@keypress.enter='commands.link({ href: $event.target.value}); editor.focus()')
|
||||||
|
|
||||||
editor-content.content(:editor='editor' spellcheck='false' :style="{ 'max-height': maxHeight }")
|
editor-content.content(:editor='editor' spellcheck='false' :style="{ 'max-height': maxHeight }" :aria-label='label' :label='label')
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import debounce from 'lodash/debounce'
|
import debounce from 'lodash/debounce'
|
||||||
|
import { mdiLink, mdiFormatListBulleted, mdiFormatQuoteOpen, mdiCodeTags,
|
||||||
|
mdiFormatHeader1, mdiFormatHeader2, mdiFormatHeader3, mdiFormatItalic,
|
||||||
|
mdiFormatStrikethroughVariant, mdiFormatBold, mdiFormatUnderline } from '@mdi/js'
|
||||||
import { Editor, EditorContent, EditorMenuBar, EditorMenuBubble } from 'tiptap'
|
import { Editor, EditorContent, EditorMenuBar, EditorMenuBubble } from 'tiptap'
|
||||||
import {
|
import {
|
||||||
Blockquote,
|
Blockquote,
|
||||||
|
@ -97,6 +100,9 @@ export default {
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
|
mdiLink, mdiFormatListBulleted, mdiFormatQuoteOpen, mdiCodeTags,
|
||||||
|
mdiFormatHeader1, mdiFormatHeader2, mdiFormatHeader3, mdiFormatItalic,
|
||||||
|
mdiFormatStrikethroughVariant, mdiFormatBold, mdiFormatUnderline,
|
||||||
options: [],
|
options: [],
|
||||||
linkActive: false,
|
linkActive: false,
|
||||||
editor: null,
|
editor: null,
|
||||||
|
|
|
@ -1,54 +1,66 @@
|
||||||
<template lang="pug">
|
<template lang="pug">
|
||||||
v-card.h-event.event.d-flex
|
v-card.h-event.event.d-flex(itemscope itemtype="https://schema.org/Event")
|
||||||
nuxt-link(:to='`/event/${event.slug || event.id}`')
|
nuxt-link(:to='`/event/${event.slug || event.id}`' itemprop="url")
|
||||||
img.img.u-featured(:src='thumbnail' :alt='alt' loading='lazy' :style="{ 'object-position': thumbnailPosition }")
|
img.img.u-featured(:src='thumbnail' :alt='alt' :loading='this.lazy?"lazy":"eager"' itemprop="image" :style="{ 'object-position': thumbnailPosition }")
|
||||||
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' v-text='mdiRepeat')
|
||||||
.title.p-name {{event.title}}
|
.title.p-name(itemprop="name") {{event.title}}
|
||||||
|
|
||||||
v-card-text.body.pt-0.pb-0
|
v-card-text.body.pt-0.pb-0
|
||||||
time.dt-start.subtitle-1(:datetime='event.start_datetime|unixFormat("YYYY-MM-DD HH:mm")') <v-icon>mdi-calendar</v-icon> {{ event|when }}
|
time.dt-start.subtitle-1(:datetime='event.start_datetime|unixFormat("YYYY-MM-DD HH:mm")' itemprop="startDate" :content="event.start_datetime|unixFormat('YYYY-MM-DDTHH:mm')") <v-icon v-text='mdiCalendar'></v-icon> {{ event|when }}
|
||||||
.d-none.dt-end {{event.end_datetime|unixFormat('YYYY-MM-DD HH:mm')}}
|
.d-none.dt-end(itemprop="endDate" :content="event.end_datetime|unixFormat('YYYY-MM-DDTHH:mm')") {{event.end_datetime|unixFormat('YYYY-MM-DD HH:mm')}}
|
||||||
a.place.d-block.p-location.pl-0(text color='primary' @click="$emit('placeclick', event.place.id)") <v-icon>mdi-map-marker</v-icon> {{event.place.name}}
|
a.place.d-block.p-location.pl-0(text color='primary' @click="$emit('placeclick', event.place.id)" itemprop="location" :content="event.place.name") <v-icon v-text='mdiMapMarker'></v-icon> {{event.place.name}}
|
||||||
|
.d-none(itemprop='location.address') {{event.place.address}}
|
||||||
|
|
||||||
v-card-actions.pt-0.actions.justify-space-between
|
v-card-actions.pt-0.actions.justify-space-between
|
||||||
.tags
|
.tags
|
||||||
v-chip.ml-1.mt-1(v-for='tag in event.tags.slice(0,6)' small
|
v-chip.ml-1.mt-1(v-for='tag in event.tags.slice(0,6)' small
|
||||||
:key='tag' outlined color='primary' @click="$emit('tagclick', tag)") {{tag}}
|
:key='tag' outlined color='primary' @click="$emit('tagclick', tag)") {{tag}}
|
||||||
|
|
||||||
|
client-only
|
||||||
v-menu(offset-y)
|
v-menu(offset-y)
|
||||||
template(v-slot:activator="{on}")
|
template(v-slot:activator="{on}")
|
||||||
v-btn.align-self-end(icon v-on='on' color='primary' alt='more')
|
v-btn.align-self-end(icon v-on='on' color='primary' title='more' aria-label='more')
|
||||||
v-icon mdi-dots-vertical
|
v-icon(v-text='mdiDotsVertical')
|
||||||
v-list(dense)
|
v-list(dense)
|
||||||
v-list-item-group
|
v-list-item-group
|
||||||
v-list-item(@click='clipboard(`${settings.baseurl}/event/${event.slug || event.id}`)')
|
v-list-item(@click='clipboard(`${settings.baseurl}/event/${event.slug || event.id}`)')
|
||||||
v-list-item-icon
|
v-list-item-icon
|
||||||
v-icon mdi-content-copy
|
v-icon(v-text='mdiContentCopy')
|
||||||
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.slug || 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(v-text='mdiCalendarExport')
|
||||||
v-list-item-content
|
v-list-item-content
|
||||||
v-list-item-title {{$t('common.add_to_calendar')}}
|
v-list-item-title {{$t('common.add_to_calendar')}}
|
||||||
v-list-item(v-if='is_mine' :to='`/add/${event.id}`')
|
v-list-item(v-if='is_mine' :to='`/add/${event.id}`')
|
||||||
v-list-item-icon
|
v-list-item-icon
|
||||||
v-icon mdi-pencil
|
v-icon(v-text='mdiPencil')
|
||||||
v-list-item-content
|
v-list-item-content
|
||||||
v-list-item-title {{$t('common.edit')}}
|
v-list-item-title {{$t('common.edit')}}
|
||||||
v-list-item(v-if='is_mine' @click='remove(false)')
|
v-list-item(v-if='is_mine' @click='remove(false)')
|
||||||
v-list-item-icon
|
v-list-item-icon
|
||||||
v-icon(color='error') mdi-delete-forever
|
v-icon(color='error' v-text='mdiDeleteForever')
|
||||||
v-list-item-content
|
v-list-item-content
|
||||||
v-list-item-title {{$t('common.remove')}}
|
v-list-item-title {{$t('common.remove')}}
|
||||||
|
template(#placeholder)
|
||||||
|
v-btn.align-self-end(icon color='primary' aria-label='more')
|
||||||
|
v-icon(v-text='mdiDotsVertical')
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import { mapState } from 'vuex'
|
import { mapState } from 'vuex'
|
||||||
import clipboard from '../assets/clipboard'
|
import clipboard from '../assets/clipboard'
|
||||||
|
import { mdiRepeat, mdiPencil, mdiDotsVertical, mdiContentCopy,
|
||||||
|
mdiCalendarExport, mdiDeleteForever, mdiCalendar, mdiMapMarker } from '@mdi/js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
data () {
|
||||||
|
return { mdiRepeat, mdiPencil, mdiDotsVertical, mdiContentCopy, mdiCalendarExport,
|
||||||
|
mdiDeleteForever, mdiMapMarker, mdiCalendar }
|
||||||
|
},
|
||||||
props: {
|
props: {
|
||||||
event: { type: Object, default: () => ({}) }
|
event: { type: Object, default: () => ({}) },
|
||||||
|
lazy: Boolean
|
||||||
},
|
},
|
||||||
mixins: [clipboard],
|
mixins: [clipboard],
|
||||||
computed: {
|
computed: {
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
v-dialog(v-model='showFollowMe' destroy-on-close max-width='700px' :fullscreen='$vuetify.breakpoint.xsOnly')
|
v-dialog(v-model='showFollowMe' destroy-on-close max-width='700px' :fullscreen='$vuetify.breakpoint.xsOnly')
|
||||||
FollowMe(@close='showFollowMe=false' is-dialog)
|
FollowMe(@close='showFollowMe=false' is-dialog)
|
||||||
|
|
||||||
v-btn(color='primary' text href='https://gancio.org' target='_blank') Gancio <small>{{settings.version}}</small>
|
v-btn(color='primary' text href='https://gancio.org' target='_blank' rel="noopener") Gancio <small>{{settings.version}}</small>
|
||||||
v-btn.ml-1(v-for='link in footerLinks'
|
v-btn.ml-1(v-for='link in footerLinks'
|
||||||
:key='link.label' color='primary' text
|
:key='link.label' color='primary' text
|
||||||
:href='link.href' :to='link.to' :target="link.href && '_blank'") {{link.label}}
|
:href='link.href' :to='link.to' :target="link.href && '_blank'") {{link.label}}
|
||||||
|
|
|
@ -9,14 +9,18 @@ div#list
|
||||||
v-for='event in computedEvents'
|
v-for='event in computedEvents'
|
||||||
:key='`${event.id}_${event.start_datetime}`' small)
|
:key='`${event.id}_${event.start_datetime}`' small)
|
||||||
v-list-item-content
|
v-list-item-content
|
||||||
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' v-text='mdiRepeat'></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')
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
|
import { mdiRepeat } from '@mdi/js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'List',
|
name: 'List',
|
||||||
|
data () {
|
||||||
|
return { mdiRepeat }
|
||||||
|
},
|
||||||
props: {
|
props: {
|
||||||
title: {
|
title: {
|
||||||
type: String,
|
type: String,
|
||||||
|
|
|
@ -1,71 +1,73 @@
|
||||||
<template lang="pug">
|
<template lang="pug">
|
||||||
v-app-bar(app aria-label='Menu')
|
v-app-bar(app aria-label='Menu' height=64)
|
||||||
|
|
||||||
//- logo, title and description
|
//- logo, title and description
|
||||||
v-list-item(:to='$route.name==="index"?"/about":"/"')
|
v-list-item(:to='$route.name==="index"?"/about":"/"')
|
||||||
v-list-item-avatar(tile)
|
v-list-item-avatar(tile)
|
||||||
v-img(src='/logo.png')
|
v-img(src='/logo.png' alt='home')
|
||||||
v-list-item-content.d-none.d-sm-flex
|
v-list-item-content.d-none.d-sm-flex
|
||||||
v-list-item-title
|
v-list-item-title
|
||||||
h2 {{settings.title}}
|
h2 {{settings.title}}
|
||||||
v-list-item-subtitle {{settings.description}}
|
v-list-item-subtitle {{settings.description}}
|
||||||
|
|
||||||
v-spacer
|
v-spacer
|
||||||
|
v-btn(v-if='$auth.loggedIn || settings.allow_anon_event' icon nuxt to='/add' :aria-label='$t("common.add_event")' :title='$t("common.add_event")')
|
||||||
|
v-icon(large color='primary' v-text='mdiPlus')
|
||||||
|
|
||||||
v-tooltip(bottom) {{$t('common.add_event')}}
|
v-btn(icon nuxt to='/export' :title='$t("common.share")' :aria-label='$t("common.share")')
|
||||||
template(v-slot:activator='{ on }')
|
v-icon(v-text='mdiShareVariant')
|
||||||
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-tooltip(bottom) {{$t('common.share')}}
|
v-btn(v-if='!$auth.loggedIn' icon nuxt to='/login' :title='$t("common.login")' :aria-label='$t("common.login")')
|
||||||
template(v-slot:activator='{ on }')
|
v-icon(v-text='mdiLogin')
|
||||||
v-btn(icon nuxt to='/export' v-on='on' :aria-label='$t("common.share")')
|
|
||||||
v-icon mdi-share-variant
|
|
||||||
|
|
||||||
v-tooltip(v-if='!$auth.loggedIn' bottom) {{$t('common.login')}}
|
client-only
|
||||||
template(v-slot:activator='{ on }')
|
v-menu(v-if='$auth.loggedIn' offset-y)
|
||||||
v-btn(icon nuxt to='/login' v-on='on' :aria-label='$t("common.login")')
|
|
||||||
v-icon mdi-login
|
|
||||||
|
|
||||||
v-menu(v-else
|
|
||||||
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' aria-label='Menu')
|
v-btn(icon v-bind='attrs' v-on='on' title='Menu' aria-label='Menu')
|
||||||
v-icon mdi-dots-vertical
|
v-icon(v-text='mdiDotsVertical')
|
||||||
v-list
|
v-list
|
||||||
v-list-item(nuxt to='/settings')
|
v-list-item(nuxt to='/settings')
|
||||||
v-list-item-icon
|
v-list-item-icon
|
||||||
v-icon mdi-cog
|
v-icon(v-text='mdiCog')
|
||||||
v-list-item-content
|
v-list-item-content
|
||||||
v-list-item-title {{$t('common.settings')}}
|
v-list-item-title {{$t('common.settings')}}
|
||||||
|
|
||||||
v-list-item(v-if='$auth.user.is_admin' nuxt to='/admin')
|
v-list-item(v-if='$auth.user.is_admin' nuxt to='/admin')
|
||||||
v-list-item-icon
|
v-list-item-icon
|
||||||
v-icon mdi-account
|
v-icon(v-text='mdiAccount')
|
||||||
v-list-item-content
|
v-list-item-content
|
||||||
v-list-item-title {{$t('common.admin')}}
|
v-list-item-title {{$t('common.admin')}}
|
||||||
|
|
||||||
v-list-item(@click='logout')
|
v-list-item(@click='logout')
|
||||||
v-list-item-icon
|
v-list-item-icon
|
||||||
v-icon mdi-logout
|
v-icon(v-text='mdiLogout')
|
||||||
v-list-item-content
|
v-list-item-content
|
||||||
v-list-item-title {{$t('common.logout')}}
|
v-list-item-title {{$t('common.logout')}}
|
||||||
|
template(#placeholder)
|
||||||
|
v-btn(v-if='$auth.loggedIn' icon aria-label='Menu' title='Menu')
|
||||||
|
v-icon(v-text='mdiDotsVertical')
|
||||||
|
|
||||||
v-btn(icon @click='clipboard(feedLink, "common.feed_url_copied")' aria-label='RSS')
|
|
||||||
v-icon(color='orange') mdi-rss
|
v-btn(icon target='_blank' :href='feedLink' title='RSS' aria-label='RSS')
|
||||||
|
v-icon(color='orange' v-text='mdiRss')
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import { mapState } from 'vuex'
|
import { mapState } from 'vuex'
|
||||||
import clipboard from '../assets/clipboard'
|
import clipboard from '../assets/clipboard'
|
||||||
|
import { mdiPlus, mdiShareVariant, mdiLogin, mdiDotsVertical, mdiLogout, mdiAccount, mdiCog, mdiRss } from '@mdi/js'
|
||||||
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Nav',
|
name: 'Nav',
|
||||||
|
data () {
|
||||||
|
return { mdiPlus, mdiShareVariant, mdiLogout, mdiLogin, mdiDotsVertical, mdiAccount, mdiCog, mdiRss }
|
||||||
|
},
|
||||||
mixins: [clipboard],
|
mixins: [clipboard],
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(['filters', 'settings']),
|
...mapState(['filters', 'settings']),
|
||||||
feedLink () {
|
feedLink () {
|
||||||
const tags = this.filters.tags && this.filters.tags.join(',')
|
const tags = this.filters.tags && this.filters.tags.map(encodeURIComponent).join(',')
|
||||||
const places = this.filters.places && this.filters.places.join(',')
|
const places = this.filters.places && this.filters.places.join(',')
|
||||||
let query = ''
|
let query = ''
|
||||||
if (tags || places) {
|
if (tags || places) {
|
||||||
|
@ -80,32 +82,11 @@ export default {
|
||||||
|
|
||||||
return `${this.settings.baseurl}/feed/rss${query}`
|
return `${this.settings.baseurl}/feed/rss${query}`
|
||||||
},
|
},
|
||||||
could_add () {
|
|
||||||
return (this.$auth.loggedIn || this.settings.allow_anon_event)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
logout () {
|
logout () {
|
||||||
this.$root.$message('common.logout_ok')
|
this.$root.$message('common.logout_ok')
|
||||||
this.$auth.logout()
|
this.$auth.logout()
|
||||||
},
|
|
||||||
async createTrustedInstance () {
|
|
||||||
let url = this.instance_url
|
|
||||||
if (!url.match(/^https?:\/\//)) {
|
|
||||||
url = `https://${url}`
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const instance = await this.$axios.$get(`${url}/.well-known/nodeinfo/2.0`)
|
|
||||||
const trusted_instance = {
|
|
||||||
url,
|
|
||||||
name: instance.metadata.nodeName,
|
|
||||||
description: instance.metadata.nodeDescription,
|
|
||||||
place: instance.metadata.placeDescription
|
|
||||||
}
|
|
||||||
this.setSetting({ key: 'trusted_instances', value: this.settings.trusted_instances.concat(trusted_instance) })
|
|
||||||
} catch (e) {
|
|
||||||
this.$root.$message(e, { color: 'error' })
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,20 +21,22 @@
|
||||||
template(v-slot:selection="data")
|
template(v-slot:selection="data")
|
||||||
v-chip(v-bind="data.attrs"
|
v-chip(v-bind="data.attrs"
|
||||||
close
|
close
|
||||||
|
:close-icon='mdiCloseCircle'
|
||||||
@click:close='remove(data.item)'
|
@click:close='remove(data.item)'
|
||||||
:input-value="data.selected")
|
:input-value="data.selected")
|
||||||
v-avatar(left)
|
v-avatar(left)
|
||||||
v-icon {{data.item.type === 'place' ? 'mdi-map-marker' : 'mdi-tag' }}
|
v-icon(v-text="data.item.type === 'place' ? mdiMapMarker : mdiTag")
|
||||||
span {{ data.item.label }}
|
span {{ data.item.label }}
|
||||||
template(v-slot:item='{ item }')
|
template(v-slot:item='{ item }')
|
||||||
v-list-item-avatar
|
v-list-item-avatar
|
||||||
v-icon {{item.type === 'place' ? 'mdi-map-marker' : 'mdi-tag' }}
|
v-icon(v-text="item.type === 'place' ? mdiMapMarker : mdiTag")
|
||||||
v-list-item-content
|
v-list-item-content
|
||||||
v-list-item-title(v-text='item.label')
|
v-list-item-title(v-text='item.label')
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapState } from 'vuex'
|
import { mapState } from 'vuex'
|
||||||
|
import { mdiMapMarker, mdiTag, mdiCloseCircle } from '@mdi/js'
|
||||||
export default {
|
export default {
|
||||||
name: 'Search',
|
name: 'Search',
|
||||||
props: {
|
props: {
|
||||||
|
@ -43,6 +45,7 @@ export default {
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
|
mdiTag, mdiMapMarker, mdiCloseCircle,
|
||||||
tmpfilter: null,
|
tmpfilter: null,
|
||||||
search: ''
|
search: ''
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,17 +7,20 @@
|
||||||
:left="left"
|
:left="left"
|
||||||
:right="right"
|
:right="right"
|
||||||
:timeout="timeout")
|
:timeout="timeout")
|
||||||
v-icon.mr-3(color="white") {{icon}}
|
v-icon.mr-3(color="white" v-text='icon')
|
||||||
span {{ message }}
|
span {{ message }}
|
||||||
template(v-slot:action="{ }")
|
template(v-slot:action="{ }")
|
||||||
v-icon(size="16" @click="active = false") mdi-close-circle
|
v-icon(size="16" @click="active = false" v-text='mdiCloseCircle')
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { mdiAlert, mdiCloseCircle, mdiInformation } from '@mdi/js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
icon: 'md-alert',
|
mdiAlert, mdiAlert, mdiCloseCircle, mdiInformation,
|
||||||
|
icon: mdiInformation,
|
||||||
color: 'secondary',
|
color: 'secondary',
|
||||||
bottom: true,
|
bottom: true,
|
||||||
top: false,
|
top: false,
|
||||||
|
@ -33,7 +36,7 @@ export default {
|
||||||
this.active = true
|
this.active = true
|
||||||
this.message = this.$t(message, opts)
|
this.message = this.$t(message, opts)
|
||||||
this.color = opts.color || 'secondary'
|
this.color = opts.color || 'secondary'
|
||||||
this.icon = opts.icon || 'md-alert'
|
this.icon = opts.icon || (this.color === 'success' ? mdiInformation : mdiAlert)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,11 +17,11 @@
|
||||||
v-btn(@click='dialog=false' color='error') {{$t('common.cancel')}}
|
v-btn(@click='dialog=false' color='error') {{$t('common.cancel')}}
|
||||||
v-btn(@click='save' color='primary' :disabled='!valid || loading' :loading='loading') {{$t(`common.${editing?'save':'send'}`)}}
|
v-btn(@click='save' color='primary' :disabled='!valid || loading' :loading='loading') {{$t(`common.${editing?'save':'send'}`)}}
|
||||||
|
|
||||||
v-btn(@click='openDialog' text color='primary') <v-icon>mdi-plus</v-icon> {{$t('common.add')}}
|
v-btn(@click='openDialog' text color='primary') <v-icon v-text='mdiPlus'></v-icon> {{$t('common.add')}}
|
||||||
v-card-text
|
v-card-text
|
||||||
v-data-table(
|
v-data-table(
|
||||||
v-if='announcements.length'
|
v-if='announcements.length'
|
||||||
:hide-default-footer='announcements.length<10'
|
:footer-props='{ prevIcon: mdiChevronLeft, nextIcon: mdiChevronRight }'
|
||||||
:headers='headers'
|
:headers='headers'
|
||||||
:items='announcements')
|
:items='announcements')
|
||||||
template(v-slot:item.actions='{ item }')
|
template(v-slot:item.actions='{ item }')
|
||||||
|
@ -36,11 +36,13 @@ import { mapActions } from 'vuex'
|
||||||
import cloneDeep from 'lodash/cloneDeep'
|
import cloneDeep from 'lodash/cloneDeep'
|
||||||
import Editor from '../Editor'
|
import Editor from '../Editor'
|
||||||
import Announcement from '../Announcement'
|
import Announcement from '../Announcement'
|
||||||
|
import { mdiPlus, mdiChevronRight, mdiChevronLeft } from '@mdi/js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { Editor, Announcement },
|
components: { Editor, Announcement },
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
|
mdiPlus, mdiChevronRight, mdiChevronLeft,
|
||||||
valid: false,
|
valid: false,
|
||||||
dialog: false,
|
dialog: false,
|
||||||
editing: false,
|
editing: false,
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
v-card-text
|
v-card-text
|
||||||
v-data-table(
|
v-data-table(
|
||||||
:hide-default-footer='unconfirmedEvents.length<10'
|
:hide-default-footer='unconfirmedEvents.length<10'
|
||||||
|
:footer-props='{ prevIcon: mdiChevronLeft, nextIcon: mdiChevronRight }'
|
||||||
:items='unconfirmedEvents'
|
:items='unconfirmedEvents'
|
||||||
:headers='headers')
|
:headers='headers')
|
||||||
template(v-slot:item.actions='{ item }')
|
template(v-slot:item.actions='{ item }')
|
||||||
|
@ -16,6 +17,7 @@
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
|
import { mdiChevronLeft, mdiChevronRight } from '@mdi/js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
|
@ -23,6 +25,7 @@ export default {
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
|
mdiChevronLeft, mdiChevronRight,
|
||||||
valid: false,
|
valid: false,
|
||||||
dialog: false,
|
dialog: false,
|
||||||
editing: false,
|
editing: false,
|
||||||
|
@ -40,7 +43,7 @@ export default {
|
||||||
try {
|
try {
|
||||||
await this.$axios.$put(`/event/confirm/${event.id}`)
|
await this.$axios.$put(`/event/confirm/${event.id}`)
|
||||||
this.$emit('confirmed', event.id)
|
this.$emit('confirmed', event.id)
|
||||||
this.$root.$message('event.confirmed')
|
this.$root.$message('event.confirmed', { color: 'success' })
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
},
|
},
|
||||||
async remove (event) {
|
async remove (event) {
|
||||||
|
|
|
@ -56,25 +56,29 @@
|
||||||
v-btn(color='error' @click='dialogAddInstance=false') {{$t('common.cancel')}}
|
v-btn(color='error' @click='dialogAddInstance=false') {{$t('common.cancel')}}
|
||||||
v-btn(color='primary' :disabled='!valid || loading' :loading='loading' @click='createTrustedInstance') {{$t('common.ok')}}
|
v-btn(color='primary' :disabled='!valid || loading' :loading='loading' @click='createTrustedInstance') {{$t('common.ok')}}
|
||||||
|
|
||||||
v-btn.mt-4(@click='dialogAddInstance = true' color='primary' text) <v-icon>mdi-plus</v-icon> {{$t('admin.add_instance')}}
|
v-btn.mt-4(@click='dialogAddInstance = true' color='primary' text) <v-icon v-text='mdiPlus'></v-icon> {{$t('admin.add_instance')}}
|
||||||
v-data-table(
|
v-data-table(
|
||||||
v-if='settings.trusted_instances.length'
|
v-if='settings.trusted_instances.length'
|
||||||
:hide-default-footer='settings.trusted_instances.length<10'
|
:hide-default-footer='settings.trusted_instances.length<10'
|
||||||
|
:footer-props='{ prevIcon: mdiChevronLeft, nextIcon: mdiChevronRight }'
|
||||||
:headers='headers'
|
:headers='headers'
|
||||||
:items='settings.trusted_instances')
|
:items='settings.trusted_instances')
|
||||||
template(v-slot:item.actions="{item}")
|
template(v-slot:item.actions="{item}")
|
||||||
v-btn(icon @click='deleteInstance(item)' color='error')
|
v-btn(icon @click='deleteInstance(item)' color='error')
|
||||||
v-icon mdi-delete-forever
|
v-icon(v-text='mdiDeleteForever')
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import { mapActions, mapState } from 'vuex'
|
import { mapActions, mapState } from 'vuex'
|
||||||
|
import get from 'lodash/get'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
|
import { mdiDeleteForever, mdiPlus, mdiChevronLeft, mdiChevronRight } from '@mdi/js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Federation',
|
name: 'Federation',
|
||||||
data ({ $store, $options }) {
|
data ({ $store, $options }) {
|
||||||
return {
|
return {
|
||||||
|
mdiDeleteForever, mdiPlus, mdiChevronLeft, mdiChevronRight,
|
||||||
instance_url: '',
|
instance_url: '',
|
||||||
instance_name: $store.state.settings.instance_name,
|
instance_name: $store.state.settings.instance_name,
|
||||||
instance_place: $store.state.settings.instance_place,
|
instance_place: $store.state.settings.instance_place,
|
||||||
|
@ -127,8 +131,8 @@ export default {
|
||||||
key: 'trusted_instances',
|
key: 'trusted_instances',
|
||||||
value: this.settings.trusted_instances.concat({
|
value: this.settings.trusted_instances.concat({
|
||||||
url: this.instance_url,
|
url: this.instance_url,
|
||||||
name: instance.data.metadata.nodeName,
|
name: get(instance, 'data.metadata.nodeName', ''),
|
||||||
label: instance.data.metadata.nodeLabel
|
label: get(instance, 'data.metadata.nodeLabel', '')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
this.$refs.form.reset()
|
this.$refs.form.reset()
|
||||||
|
|
|
@ -10,10 +10,11 @@
|
||||||
:items-per-page='5'
|
:items-per-page='5'
|
||||||
:search='instancesFilter'
|
:search='instancesFilter'
|
||||||
:hide-default-footer='instances.length<5'
|
:hide-default-footer='instances.length<5'
|
||||||
|
:footer-props='{ prevIcon: mdiChevronLeft, nextIcon: mdiChevronRight }'
|
||||||
dense :headers='instancesHeader'
|
dense :headers='instancesHeader'
|
||||||
@click:row='instanceSelected')
|
@click:row='instanceSelected')
|
||||||
template(v-slot:item.blocked="{ item }")
|
template(v-slot:item.blocked="{ item }")
|
||||||
v-icon(@click='toggleBlock(item)') {{item.blocked ? 'mdi-checkbox-intermediate' : 'mdi-checkbox-blank-outline'}}
|
v-icon(@click='toggleBlock(item)' v-text='item.blocked ? mdiCheckboxIntermediate : mdiCheckboxBlankOutline')
|
||||||
|
|
||||||
v-col(:span='11')
|
v-col(:span='11')
|
||||||
span {{$t('common.users')}}
|
span {{$t('common.users')}}
|
||||||
|
@ -22,45 +23,53 @@
|
||||||
:items-per-page='5'
|
:items-per-page='5'
|
||||||
:search='usersFilter'
|
:search='usersFilter'
|
||||||
:hide-default-footer='users.length<5'
|
:hide-default-footer='users.length<5'
|
||||||
|
:footer-props='{ prevIcon: mdiChevronLeft, nextIcon: mdiChevronRight }'
|
||||||
dense :headers='usersHeader')
|
dense :headers='usersHeader')
|
||||||
template(v-slot:item.blocked="{ item }")
|
template(v-slot:item.blocked="{ item }")
|
||||||
v-icon(@click='toggleUserBlock(item)') {{item.blocked?'mdi-checkbox-intermediate':'mdi-checkbox-blank-outline'}}
|
v-icon(@click='toggleUserBlock(item)' v-text='item.blocked ? mdiCheckboxIntermediate : mdiCheckboxBlankOutline')
|
||||||
|
|
||||||
div
|
div
|
||||||
v-card-title {{$t('common.resources')}}
|
v-card-title {{$t('common.resources')}}
|
||||||
v-data-table(:items='resources' dense
|
v-data-table(:items='resources' dense
|
||||||
:headers='resourcesHeader'
|
:headers='resourcesHeader'
|
||||||
:hide-default-footer='resources.length<10'
|
:hide-default-footer='resources.length<10'
|
||||||
:items-per-page='10')
|
:items-per-page='10'
|
||||||
|
:footer-props='{ prevIcon: mdiChevronLeft, nextIcon: mdiChevronRight }')
|
||||||
template(v-slot:item.content='{ item }')
|
template(v-slot:item.content='{ item }')
|
||||||
span(v-html='item.data.content')
|
span(v-html='item.data.content')
|
||||||
|
template(v-slot:item.created='{ item }')
|
||||||
|
span {{item.created | dateFormat('lll')}}
|
||||||
template(v-slot:item.user='{ item }')
|
template(v-slot:item.user='{ item }')
|
||||||
span {{item.ap_user.preferredUsername}}
|
a(:href='item.ap_user.url || item.ap_user.ap_id' target='_blank') {{item.ap_user.preferredUsername}}
|
||||||
template(v-slot:item.event='{ item }')
|
template(v-slot:item.event='{ item }')
|
||||||
span {{item.event.title}}
|
nuxt-link(:to='`/event/${item.event.slug || item.event.id}`') {{item.event.title}}
|
||||||
template(v-slot:item.actions='{ item }')
|
template(v-slot:item.actions='{ item }')
|
||||||
v-menu(offset-y)
|
v-menu(offset-y)
|
||||||
template(v-slot:activator="{ on }")
|
template(v-slot:activator="{ on }")
|
||||||
v-btn.mr-2(v-on='on' color='primary' small icon)
|
v-btn.mr-2(v-on='on' color='primary' small icon)
|
||||||
v-icon mdi-dots-vertical
|
v-icon(v-text='mdiDotsVertical')
|
||||||
v-list
|
v-list
|
||||||
v-list-item(v-if='!item.hidden' @click='hideResource(item, true)')
|
v-list-item(v-if='!item.hidden' @click='hideResource(item, true)')
|
||||||
v-list-item-title <v-icon left>mdi-eye-off</v-icon> {{$t('admin.hide_resource')}}
|
v-list-item-title <v-icon left v-text='mdiEyeOff'></v-icon> {{$t('admin.hide_resource')}}
|
||||||
v-list-item(v-else @click='hideResource(item, false)')
|
v-list-item(v-else @click='hideResource(item, false)')
|
||||||
v-list-item-title <v-icon left>mdi-eye</v-icon> {{$t('admin.show_resource')}}
|
v-list-item-title <v-icon left v-text='mdiEye'></v-icon> {{$t('admin.show_resource')}}
|
||||||
v-list-item(@click='deleteResource(item)')
|
v-list-item(@click='deleteResource(item)')
|
||||||
v-list-item-title <v-icon left>mdi-delete</v-icon> {{$t('admin.delete_resource')}}
|
v-list-item-title <v-icon left v-text='mdiDelete'></v-icon> {{$t('admin.delete_resource')}}
|
||||||
//- v-list-item(@click='toggleUserBlock(item.ap_user)')
|
//- v-list-item(@click='toggleUserBlock(item.ap_user)')
|
||||||
//- v-list-item-title <v-icon left>mdi-lock</v-icon> {{$t('admin.block_user')}}
|
//- v-list-item-title <v-icon left>mdi-lock</v-icon> {{$t('admin.block_user')}}
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import { mapState, mapActions } from 'vuex'
|
import { mapState, mapActions } from 'vuex'
|
||||||
import get from 'lodash/get'
|
import get from 'lodash/get'
|
||||||
|
import { mdiDelete, mdiEye, mdiEyeOff, mdiDotsVertical, mdiCheckboxIntermediate,
|
||||||
|
mdiCheckboxBlankOutline, mdiChevronLeft, mdiChevronRight } from '@mdi/js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Moderation',
|
name: 'Moderation',
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
|
mdiDelete, mdiEye, mdiEyeOff, mdiDotsVertical, mdiCheckboxIntermediate,
|
||||||
|
mdiCheckboxBlankOutline, mdiChevronLeft, mdiChevronRight,
|
||||||
instances: [],
|
instances: [],
|
||||||
resources: [],
|
resources: [],
|
||||||
users: [],
|
users: [],
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
<template lang='pug'>
|
<template lang='pug'>
|
||||||
v-container
|
v-container
|
||||||
v-card-title {{$t('common.places')}}
|
v-card-title {{$t('common.places')}}
|
||||||
|
v-spacer
|
||||||
|
v-text-field(v-model='search'
|
||||||
|
:append-icon='mdiMagnify' outlined rounded
|
||||||
|
label='Search'
|
||||||
|
single-line hide-details)
|
||||||
v-card-subtitle(v-html="$t('admin.place_description')")
|
v-card-subtitle(v-html="$t('admin.place_description')")
|
||||||
|
|
||||||
v-dialog(v-model='dialog' width='600' :fullscreen='$vuetify.breakpoint.xsOnly')
|
v-dialog(v-model='dialog' width='600' :fullscreen='$vuetify.breakpoint.xsOnly')
|
||||||
|
@ -29,19 +34,26 @@
|
||||||
v-card-text
|
v-card-text
|
||||||
v-data-table(
|
v-data-table(
|
||||||
:headers='headers'
|
:headers='headers'
|
||||||
:items='places')
|
:items='places'
|
||||||
|
:hide-default-footer='places.length<5'
|
||||||
|
:footer-props='{ prevIcon: mdiChevronLeft, nextIcon: mdiChevronRight }'
|
||||||
|
:search='search')
|
||||||
template(v-slot:item.actions='{item}')
|
template(v-slot:item.actions='{item}')
|
||||||
v-btn(@click='editPlace(item)' color='primary' icon)
|
v-btn(@click='editPlace(item)' color='primary' icon)
|
||||||
v-icon mdi-pencil
|
v-icon(v-text='mdiPencil')
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import { mapState, mapActions } from 'vuex'
|
import { mapState, mapActions } from 'vuex'
|
||||||
|
import { mdiPencil, mdiChevronLeft, mdiChevronRight } from '@mdi/js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
|
mdiPencil, mdiChevronRight, mdiChevronLeft,
|
||||||
loading: false,
|
loading: false,
|
||||||
dialog: false,
|
dialog: false,
|
||||||
valid: false,
|
valid: false,
|
||||||
|
search: '',
|
||||||
place: { name: '', address: '', id: null },
|
place: { name: '', address: '', id: null },
|
||||||
headers: [
|
headers: [
|
||||||
{ value: 'name', text: 'Name' },
|
{ value: 'name', text: 'Name' },
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
v-card-title SMTP Email configuration
|
v-card-title SMTP Email configuration
|
||||||
v-card-text
|
v-card-text
|
||||||
p(v-html="$t('admin.smtp_description')")
|
p(v-html="$t('admin.smtp_description')")
|
||||||
|
|
||||||
v-form(v-model='isValid')
|
v-form(v-model='isValid')
|
||||||
v-text-field(v-model='admin_email'
|
v-text-field(v-model='admin_email'
|
||||||
@blur="save('admin_email', admin_email )"
|
@blur="save('admin_email', admin_email )"
|
||||||
|
@ -53,7 +52,7 @@ export default {
|
||||||
async testSMTP () {
|
async testSMTP () {
|
||||||
this.loading = true
|
this.loading = true
|
||||||
try {
|
try {
|
||||||
this.setSetting({ key: 'smtp', value: this.smtp })
|
await this.setSetting({ key: 'smtp', value: this.smtp })
|
||||||
await this.$axios.$post('/settings/smtp', { smtp: this.smtp })
|
await this.$axios.$post('/settings/smtp', { smtp: this.smtp })
|
||||||
this.$root.$message(this.$t('admin.smtp_test_success', { admin_email: this.admin_email }), { color: 'success' })
|
this.$root.$message(this.$t('admin.smtp_test_success', { admin_email: this.admin_email }), { color: 'success' })
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -68,7 +67,9 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
done () {
|
done () {
|
||||||
|
if (this.smtp.auth.pass) {
|
||||||
this.setSetting({ key: 'smtp', value: JSON.parse(JSON.stringify(this.smtp)) })
|
this.setSetting({ key: 'smtp', value: JSON.parse(JSON.stringify(this.smtp)) })
|
||||||
|
}
|
||||||
this.$emit('close')
|
this.$emit('close')
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -53,9 +53,9 @@
|
||||||
|
|
||||||
v-card-actions
|
v-card-actions
|
||||||
v-btn(text @click='showSMTP=true')
|
v-btn(text @click='showSMTP=true')
|
||||||
<v-icon v-if='showSMTPAlert' color='error'>mdi-alert</v-icon> {{$t('admin.show_smtp_setup')}}
|
<v-icon v-if='showSMTPAlert' color='error' v-text='mdiAlert'></v-icon> {{$t('admin.show_smtp_setup')}}
|
||||||
v-btn(text @click='$emit("complete")' color='primary' v-if='setup') {{$t('common.next')}}
|
v-btn(text @click='$emit("complete")' color='primary' v-if='setup') {{$t('common.next')}}
|
||||||
v-icon mdi-arrow-right
|
v-icon(v-text='mdiArrowRight')
|
||||||
|
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
@ -65,6 +65,7 @@ import { mapActions, mapState } from 'vuex'
|
||||||
import moment from 'dayjs'
|
import moment from 'dayjs'
|
||||||
import tzNames from './tz.json'
|
import tzNames from './tz.json'
|
||||||
import locales from '../../locales/esm'
|
import locales from '../../locales/esm'
|
||||||
|
import { mdiAlert, mdiArrowRight } from '@mdi/js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
|
@ -74,6 +75,7 @@ export default {
|
||||||
name: 'Settings',
|
name: 'Settings',
|
||||||
data ({ $store }) {
|
data ({ $store }) {
|
||||||
return {
|
return {
|
||||||
|
mdiAlert, mdiArrowRight,
|
||||||
title: $store.state.settings.title,
|
title: $store.state.settings.title,
|
||||||
description: $store.state.settings.description,
|
description: $store.state.settings.description,
|
||||||
locales: Object.keys(locales).map(locale => ({ value: locale, text: locales[locale] })),
|
locales: Object.keys(locales).map(locale => ({ value: locale, text: locales[locale] })),
|
||||||
|
@ -83,7 +85,7 @@ export default {
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(['settings']),
|
...mapState(['settings']),
|
||||||
showSMTPAlert () {
|
showSMTPAlert () {
|
||||||
return !this.setup && (!this.settings.admin_email || !this.settings.smtp || !this.settings.smtp.host || !this.settings.smtp.user)
|
return !this.setup && (!this.settings.admin_email || !this.settings.smtp || !this.settings.smtp.host || !this.settings.smtp.auth.user)
|
||||||
},
|
},
|
||||||
instance_locale: {
|
instance_locale: {
|
||||||
get () { return this.settings.instance_locale },
|
get () { return this.settings.instance_locale },
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
@change='uploadLogo'
|
@change='uploadLogo'
|
||||||
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 v-text='mdiRestore'></v-icon> {{$t('common.reset')}}
|
||||||
v-img(:src='`${settings.baseurl}/logo.png?${logoKey}`'
|
v-img(:src='`${settings.baseurl}/logo.png?${logoKey}`'
|
||||||
max-width="60px" max-height="60px" contain)
|
max-width="60px" max-height="60px" contain)
|
||||||
|
|
||||||
|
@ -50,8 +50,8 @@
|
||||||
|
|
||||||
v-card-title {{$t('admin.footer_links')}}
|
v-card-title {{$t('admin.footer_links')}}
|
||||||
v-card-text
|
v-card-text
|
||||||
v-btn(color='primary' text @click='openLinkModal') <v-icon>mdi-plus</v-icon> {{$t('admin.add_link')}}
|
v-btn(color='primary' text @click='openLinkModal') <v-icon v-text='mdiPlus'></v-icon> {{$t('admin.add_link')}}
|
||||||
v-btn(color='warning' text @click='reset') <v-icon>mdi-restore</v-icon> {{$t('common.reset')}}
|
v-btn(color='warning' text @click='reset') <v-icon v-text='mdiRestore'></v-icon> {{$t('common.reset')}}
|
||||||
v-card
|
v-card
|
||||||
v-list.mt-1(two-line subheader)
|
v-list.mt-1(two-line subheader)
|
||||||
v-list-item(v-for='link in settings.footerLinks'
|
v-list-item(v-for='link in settings.footerLinks'
|
||||||
|
@ -61,16 +61,18 @@
|
||||||
v-list-item-subtitle {{link.href}}
|
v-list-item-subtitle {{link.href}}
|
||||||
v-list-item-action
|
v-list-item-action
|
||||||
v-btn(icon color='error' @click.stop='removeFooterLink(link)')
|
v-btn(icon color='error' @click.stop='removeFooterLink(link)')
|
||||||
v-icon mdi-delete-forever
|
v-icon(v-text='mdiDeleteForever')
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import { mapActions, mapState } from 'vuex'
|
import { mapActions, mapState } from 'vuex'
|
||||||
|
import { mdiDeleteForever, mdiRestore, mdiPlus } from '@mdi/js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Theme',
|
name: 'Theme',
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
|
mdiDeleteForever, mdiRestore, mdiPlus,
|
||||||
valid: false,
|
valid: false,
|
||||||
logoKey: 0,
|
logoKey: 0,
|
||||||
link: { href: '', label: '' },
|
link: { href: '', label: '' },
|
||||||
|
|
|
@ -3,11 +3,11 @@
|
||||||
v-card-title {{$t('common.users')}}
|
v-card-title {{$t('common.users')}}
|
||||||
v-spacer
|
v-spacer
|
||||||
v-text-field(v-model='search'
|
v-text-field(v-model='search'
|
||||||
append-icon='mdi-magnify' outlined rounded
|
:append-icon='mdiMagnify' outlined rounded
|
||||||
label='Search'
|
label='Search'
|
||||||
single-line hide-details)
|
single-line hide-details)
|
||||||
|
|
||||||
v-btn(color='primary' text @click='newUserDialog = true') <v-icon>mdi-plus</v-icon> {{$t('common.new_user')}}
|
v-btn(color='primary' text @click='newUserDialog = true') <v-icon v-text='mdiPlus'></v-icon> {{$t('common.new_user')}}
|
||||||
|
|
||||||
//- ADD NEW USER
|
//- ADD NEW USER
|
||||||
v-dialog(v-model='newUserDialog' :fullscreen='$vuetify.breakpoint.xsOnly')
|
v-dialog(v-model='newUserDialog' :fullscreen='$vuetify.breakpoint.xsOnly')
|
||||||
|
@ -20,7 +20,7 @@
|
||||||
:label="$t('common.email')"
|
:label="$t('common.email')"
|
||||||
:rules="$validators.email")
|
:rules="$validators.email")
|
||||||
v-switch(v-model='new_user.is_admin' :label="$t('common.admin')" inset)
|
v-switch(v-model='new_user.is_admin' :label="$t('common.admin')" inset)
|
||||||
v-alert(type='info' :closable='false') {{$t('admin.user_add_help')}}
|
v-alert(type='info' :closable='false' :icon='mdiInformation') {{$t('admin.user_add_help')}}
|
||||||
v-card-actions
|
v-card-actions
|
||||||
v-spacer
|
v-spacer
|
||||||
v-btn(@click='newUserDialog=false' color='error') {{$t('common.cancel')}}
|
v-btn(@click='newUserDialog=false' color='error') {{$t('common.cancel')}}
|
||||||
|
@ -32,10 +32,11 @@
|
||||||
:headers='headers'
|
:headers='headers'
|
||||||
:items='users'
|
:items='users'
|
||||||
:hide-default-footer='users.length<5'
|
:hide-default-footer='users.length<5'
|
||||||
|
:footer-props='{ prevIcon: mdiChevronLeft, nextIcon: mdiChevronRight }'
|
||||||
:search='search')
|
:search='search')
|
||||||
template(v-slot:item.is_active='{item}')
|
template(v-slot:item.is_active='{item}')
|
||||||
v-icon(v-if='item.is_active' color='success') mdi-check
|
v-icon(v-if='item.is_active' color='success' v-text='mdiCheck')
|
||||||
v-icon(v-else color='warning') mdi-close
|
v-icon(v-else color='warning' v-text='mdiClose')
|
||||||
template(v-slot:item.actions='{item}')
|
template(v-slot:item.actions='{item}')
|
||||||
v-btn(v-if='item.recover_code' text small :to='`/user_confirm/${item.recover_code}`') {{$t('common.confirm')}}
|
v-btn(v-if='item.recover_code' text small :to='`/user_confirm/${item.recover_code}`') {{$t('common.confirm')}}
|
||||||
v-btn(text small @click='toggle(item)'
|
v-btn(text small @click='toggle(item)'
|
||||||
|
@ -49,6 +50,7 @@
|
||||||
<script>
|
<script>
|
||||||
import { mapState } from 'vuex'
|
import { mapState } from 'vuex'
|
||||||
import get from 'lodash/get'
|
import get from 'lodash/get'
|
||||||
|
import { mdiClose, mdiMagnify, mdiCheck, mdiPlus, mdiInformation, mdiChevronLeft, mdiChevronRight } from '@mdi/js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Users',
|
name: 'Users',
|
||||||
|
@ -57,6 +59,7 @@ export default {
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
|
mdiClose, mdiMagnify, mdiCheck, mdiPlus, mdiInformation, mdiChevronLeft, mdiChevronRight,
|
||||||
newUserDialog: false,
|
newUserDialog: false,
|
||||||
valid: false,
|
valid: false,
|
||||||
new_user: {
|
new_user: {
|
||||||
|
|
27
docs/.api.md
27
docs/.api.md
|
@ -1,10 +1,34 @@
|
||||||
|
|
||||||
|
## Get events
|
||||||
|
|
||||||
|
GET
|
||||||
|
{: .label .label-green}
|
||||||
|
|
||||||
|
**`/api/events`**
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**Params**
|
||||||
|
|
||||||
|
| start | `integer` | start timestamp (default: now) |
|
||||||
|
| end | `integer` | end timestamp (optional) |
|
||||||
|
| tags | `array` | List of tags |
|
||||||
|
| places | `array` | List of places |
|
||||||
|
| max | `integer` | Max events |
|
||||||
|
| show_recurrent | `boolean` | Show also recurrent events (default: as choosen in admin settings) |
|
||||||
|
|
||||||
|
|
||||||
|
***Example***
|
||||||
|
[https://demo.gancio.org/api/events](https://demo.gancio.org/api/events)
|
||||||
|
[usage example](https://framagit.org/les/gancio/-/blob/master/webcomponents/src/GancioEvents.svelte#L18-42)
|
||||||
|
---
|
||||||
|
|
||||||
## Add a new event
|
## Add a new event
|
||||||
|
|
||||||
POST
|
POST
|
||||||
{: .label .label-orange}
|
{: .label .label-orange}
|
||||||
|
|
||||||
**`/event`**
|
**`/api/event`**
|
||||||
|
|
||||||
> info "info"
|
> info "info"
|
||||||
> `Content-Type` has to be `multipart/form-data` to support image upload
|
> `Content-Type` has to be `multipart/form-data` to support image upload
|
||||||
|
@ -21,7 +45,6 @@ POST
|
||||||
| tags | `array` | List of tags |
|
| tags | `array` | List of tags |
|
||||||
| recurrent | `object` | Recurrent event details |
|
| recurrent | `object` | Recurrent event details |
|
||||||
| recurrent.frequency | `string` | could be `1w` or `2w` |
|
| recurrent.frequency | `string` | could be `1w` or `2w` |
|
||||||
| recurrent.type | `string` | not used |
|
|
||||||
| recurrent.days | `array` | array of days |
|
| recurrent.days | `array` | array of days |
|
||||||
| image | `image` | Image |
|
| image | `image` | Image |
|
||||||
|
|
||||||
|
|
|
@ -1,30 +1,30 @@
|
||||||
GEM
|
GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
activesupport (6.0.3.7)
|
activesupport (6.0.4.1)
|
||||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||||
i18n (>= 0.7, < 2)
|
i18n (>= 0.7, < 2)
|
||||||
minitest (~> 5.1)
|
minitest (~> 5.1)
|
||||||
tzinfo (~> 1.1)
|
tzinfo (~> 1.1)
|
||||||
zeitwerk (~> 2.2, >= 2.2.2)
|
zeitwerk (~> 2.2, >= 2.2.2)
|
||||||
addressable (2.7.0)
|
addressable (2.8.0)
|
||||||
public_suffix (>= 2.0.2, < 5.0)
|
public_suffix (>= 2.0.2, < 5.0)
|
||||||
colorator (1.1.0)
|
colorator (1.1.0)
|
||||||
concurrent-ruby (1.1.8)
|
concurrent-ruby (1.1.9)
|
||||||
em-websocket (0.5.2)
|
em-websocket (0.5.3)
|
||||||
eventmachine (>= 0.12.9)
|
eventmachine (>= 0.12.9)
|
||||||
http_parser.rb (~> 0.6.0)
|
http_parser.rb (~> 0)
|
||||||
eventmachine (1.2.7)
|
eventmachine (1.2.7)
|
||||||
ffi (1.15.1)
|
ffi (1.15.4)
|
||||||
forwardable-extended (2.6.0)
|
forwardable-extended (2.6.0)
|
||||||
gemoji (3.0.1)
|
gemoji (3.0.1)
|
||||||
html-pipeline (2.14.0)
|
html-pipeline (2.14.0)
|
||||||
activesupport (>= 2)
|
activesupport (>= 2)
|
||||||
nokogiri (>= 1.4)
|
nokogiri (>= 1.4)
|
||||||
http_parser.rb (0.6.0)
|
http_parser.rb (0.8.0)
|
||||||
i18n (1.8.10)
|
i18n (1.8.11)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
jekyll (4.2.0)
|
jekyll (4.2.1)
|
||||||
addressable (~> 2.4)
|
addressable (~> 2.4)
|
||||||
colorator (~> 1.0)
|
colorator (~> 1.0)
|
||||||
em-websocket (~> 0.5)
|
em-websocket (~> 0.5)
|
||||||
|
@ -62,28 +62,24 @@ GEM
|
||||||
kramdown-parser-gfm (1.1.0)
|
kramdown-parser-gfm (1.1.0)
|
||||||
kramdown (~> 2.0)
|
kramdown (~> 2.0)
|
||||||
liquid (4.0.3)
|
liquid (4.0.3)
|
||||||
listen (3.5.1)
|
listen (3.7.0)
|
||||||
rb-fsevent (~> 0.10, >= 0.10.3)
|
|
||||||
rb-inotify (~> 0.9, >= 0.9.10)
|
rb-inotify (~> 0.9, >= 0.9.10)
|
||||||
mercenary (0.4.0)
|
mercenary (0.4.0)
|
||||||
mini_magick (4.11.0)
|
mini_magick (4.11.0)
|
||||||
mini_portile2 (2.5.3)
|
|
||||||
minitest (5.14.4)
|
minitest (5.14.4)
|
||||||
nokogiri (1.11.7)
|
nokogiri (1.12.5-x86_64-linux)
|
||||||
mini_portile2 (~> 2.5.0)
|
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
pathutil (0.16.2)
|
pathutil (0.16.2)
|
||||||
forwardable-extended (~> 2.6)
|
forwardable-extended (~> 2.6)
|
||||||
premonition (4.0.1)
|
premonition (4.0.1)
|
||||||
jekyll (>= 3.7, < 5.0)
|
jekyll (>= 3.7, < 5.0)
|
||||||
public_suffix (4.0.6)
|
public_suffix (4.0.6)
|
||||||
racc (1.5.2)
|
racc (1.6.0)
|
||||||
rake (13.0.3)
|
rake (13.0.6)
|
||||||
rb-fsevent (0.11.0)
|
|
||||||
rb-inotify (0.10.1)
|
rb-inotify (0.10.1)
|
||||||
ffi (~> 1.0)
|
ffi (~> 1.0)
|
||||||
rexml (3.2.5)
|
rexml (3.2.5)
|
||||||
rouge (3.26.0)
|
rouge (3.26.1)
|
||||||
safe_yaml (1.0.5)
|
safe_yaml (1.0.5)
|
||||||
sassc (2.4.0)
|
sassc (2.4.0)
|
||||||
ffi (~> 1.9)
|
ffi (~> 1.9)
|
||||||
|
@ -92,14 +88,14 @@ GEM
|
||||||
thread_safe (0.3.6)
|
thread_safe (0.3.6)
|
||||||
tzinfo (1.2.9)
|
tzinfo (1.2.9)
|
||||||
thread_safe (~> 0.1)
|
thread_safe (~> 0.1)
|
||||||
tzinfo-data (1.2021.1)
|
tzinfo-data (1.2021.5)
|
||||||
tzinfo (>= 1.0.0)
|
tzinfo (>= 1.0.0)
|
||||||
unicode-display_width (1.7.0)
|
unicode-display_width (1.8.0)
|
||||||
wdm (0.1.1)
|
wdm (0.1.1)
|
||||||
zeitwerk (2.4.2)
|
zeitwerk (2.5.1)
|
||||||
|
|
||||||
PLATFORMS
|
PLATFORMS
|
||||||
ruby
|
x86_64-linux
|
||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
jekyll
|
jekyll
|
||||||
|
@ -114,4 +110,4 @@ DEPENDENCIES
|
||||||
wdm (~> 0.1.0)
|
wdm (~> 0.1.0)
|
||||||
|
|
||||||
BUNDLED WITH
|
BUNDLED WITH
|
||||||
2.1.4
|
2.2.27
|
||||||
|
|
1205
docs/assets/js/gancio-events.es.js
Normal file
1205
docs/assets/js/gancio-events.es.js
Normal file
File diff suppressed because it is too large
Load diff
|
@ -8,12 +8,77 @@ 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.
|
||||||
|
|
||||||
|
### 1.4.3 - 10 mar '22
|
||||||
|
- fix [#140](https://framagit.org/les/gancio/-/issues/140) - Invalid date
|
||||||
|
- fix [#141](https://framagit.org/les/gancio/-/issues/141) - Cannot change logo
|
||||||
|
- fix same day events
|
||||||
|
- add missing icons in admin
|
||||||
|
- prepare multisite settings
|
||||||
|
- improve initialization
|
||||||
|
- start unit testing API (it's never too late)
|
||||||
|
- make it clear that the admin password must be saved at the end of setup
|
||||||
|
|
||||||
|
### 1.4.1 - 4 mar '22
|
||||||
|
- add gl/galego locale, thanks @xosem
|
||||||
|
- fix import redirect loop
|
||||||
|
- add missing icons (close, repeat, arrows ...)
|
||||||
|
- turn rss icon into a real link to improve a11y
|
||||||
|
- force seconds to 0 for each events, fix recurring events starting date issue
|
||||||
|
- fix next/prev selection on same datetime events
|
||||||
|
- improve moderation UI (add author and event link + format creation date)
|
||||||
|
- refactoring resource UI from fedi
|
||||||
|
|
||||||
|
### 1.4.0 - 9 feb '22
|
||||||
|
- improve Cumulative Layout Shift
|
||||||
|
- remove filename as default media label to avoid leak metadata
|
||||||
|
- add endData to microdata
|
||||||
|
- security fix with filtering settings, avoid sharing SMTP pass with front-end
|
||||||
|
- fix broken SMTP
|
||||||
|
- remove global materialicons / vuetify css, use threeshake and @nuxt/vuetify (really improve lighthouse score)
|
||||||
|
- new Dockerfile using node:17.4-slim as base img (from 1.5Gb to ~800Mb)
|
||||||
|
- add XSS and path traversal mitigation
|
||||||
|
- improve a11y
|
||||||
|
- update deps
|
||||||
|
|
||||||
|
### 1.3.3 - 1 feb '22
|
||||||
|
- security fix, avoid sharing smtp pass with front-end
|
||||||
|
|
||||||
|
### 1.3.2 - 1 feb '22
|
||||||
|
- fix webcomponent for event without img
|
||||||
|
|
||||||
|
### 1.3.1 - 1 feb '22
|
||||||
|
- inherits tags in recurring events [#138](https://framagit.org/les/gancio/-/issues/138)
|
||||||
|
- you can now skip an occurrence of a recurring event
|
||||||
|
- fix `show_recurrent` event in webcomponent and API
|
||||||
|
- add new webcomponent `sidebar` attribute and a [`fullwith` layout](https://gancio.org/usage/embed#embed-event-lists)
|
||||||
|
|
||||||
|
### 1.3.0 - 26 gen '22
|
||||||
|
- add mariadb support
|
||||||
|
- add [microdata](https://developer.mozilla.org/en-US/docs/Web/HTML/Microdata) support
|
||||||
|
- support db setup via environment variables (used in updated `docker-compose.yml` files)
|
||||||
|
- improve rss feed:
|
||||||
|
- fix validation
|
||||||
|
- add enclusure media for featured image
|
||||||
|
- add categories
|
||||||
|
- fix typo in export code
|
||||||
|
- add theme attribute to gancio-events webcomponent (dark/light)
|
||||||
|
- increase thumbs quality
|
||||||
|
- improve logo for light theme
|
||||||
|
- improve [wordpress plugin](https://wordpress.org/plugins/wpgancio/)
|
||||||
|
- add \[gancio-event\] and \[gancio-events\] shortcode
|
||||||
|
- allow gancio-events / gancio-event tags in editor
|
||||||
|
- automatically enqueue webcomponent script
|
||||||
|
- tags/places filters are now inclusive not exclusive
|
||||||
|
- fix image undefined alternative text
|
||||||
|
- update documentation, dependencies, translations
|
||||||
|
|
||||||
### 1.2.2 - 7 dic '21
|
### 1.2.2 - 7 dic '21
|
||||||
- shiny new gancio-event\[s\] webcomponents => [docs](https://gancio.org/usage/embed)
|
- shiny new gancio-event\[s\] webcomponents => [docs](https://gancio.org/usage/embed)
|
||||||
- new backend plugin system
|
- new backend plugin system => [docs](https://gancio.org/dev/plugins)
|
||||||
- improve media focal point selection
|
- improve media focal point selection
|
||||||
- improve non-js experience (load img, use native lazy loading)
|
- improve non-js experience (load img, use native lazy loading)
|
||||||
- improve user_confirm / recover code flow
|
- improve user_confirm / recover code flow
|
||||||
|
- permit admins to choose user password (usefull on instance without SMTP configuration)
|
||||||
- fix task manager exception
|
- fix task manager exception
|
||||||
- fix db initialization when a custom setup is used, #131
|
- fix db initialization when a custom setup is used, #131
|
||||||
- remove vue-clipboard2 dependency due to [this](https://github.com/euvl/v-clipboard/issues/18) bug and using a [native with fallback mixin instead](./assets/clipboard.js)
|
- remove vue-clipboard2 dependency due to [this](https://github.com/euvl/v-clipboard/issues/18) bug and using a [native with fallback mixin instead](./assets/clipboard.js)
|
||||||
|
|
|
@ -10,15 +10,37 @@ nav_order: 8
|
||||||
1. TOC
|
1. TOC
|
||||||
{:toc}
|
{:toc}
|
||||||
|
|
||||||
|
## Get events
|
||||||
|
|
||||||
|
GET
|
||||||
|
{: .label .label-green}
|
||||||
|
|
||||||
|
**`/api/events`**
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**Params**
|
||||||
|
|
||||||
|
| start | `integer` | start timestamp (default: now) |
|
||||||
|
| end | `integer` | end timestamp (optional) |
|
||||||
|
| tags | `array` | List of tags |
|
||||||
|
| places | `array` | List of places |
|
||||||
|
| max | `integer` | Max events |
|
||||||
|
| show_recurrent | `boolean` | Show also recurrent events (default: as choosen in admin settings) |
|
||||||
|
|
||||||
|
|
||||||
|
***Example***
|
||||||
|
[https://demo.gancio.org/api/events](https://demo.gancio.org/api/events)
|
||||||
|
[usage example](https://framagit.org/les/gancio/-/blob/master/webcomponents/src/GancioEvents.svelte#L18-42)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Add a new event
|
## Add a new event
|
||||||
|
|
||||||
POST
|
POST
|
||||||
{: .label .label-orange}
|
{: .label .label-orange}
|
||||||
|
|
||||||
**`/event`**
|
**`/api/event`**
|
||||||
|
|
||||||
> info "info"
|
> info "info"
|
||||||
> `Content-Type` has to be `multipart/form-data` to support image upload
|
> `Content-Type` has to be `multipart/form-data` to support image upload
|
||||||
|
@ -35,7 +57,6 @@ POST
|
||||||
| tags | `array` | List of tags |
|
| tags | `array` | List of tags |
|
||||||
| recurrent | `object` | Recurrent event details |
|
| recurrent | `object` | Recurrent event details |
|
||||||
| recurrent.frequency | `string` | could be `1w` or `2w` |
|
| recurrent.frequency | `string` | could be `1w` or `2w` |
|
||||||
| recurrent.type | `string` | not used |
|
|
||||||
| recurrent.days | `array` | array of days |
|
| recurrent.days | `array` | array of days |
|
||||||
| image | `image` | Image |
|
| image | `image` | Image |
|
||||||
|
|
||||||
|
|
51
docs/dev/plugin.md
Normal file
51
docs/dev/plugin.md
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
---
|
||||||
|
layout: default
|
||||||
|
title: Plugins
|
||||||
|
permalink: /dev/plugins
|
||||||
|
nav_order: 2
|
||||||
|
parent: Hacking
|
||||||
|
---
|
||||||
|
|
||||||
|
## Plugins
|
||||||
|
Since **v.1.2.2** you can write your own plugin that react to event related action (create,update,delete).
|
||||||
|
|
||||||
|
> info "What this is useful for?"
|
||||||
|
> - Do you want to create a post in your wordpress website each time an event is published? [hint](http://wp-api.org/node-wpapi/using-the-client/#creating-posts)
|
||||||
|
> - Do you want to send a summary notification of daily events via mail?
|
||||||
|
> - Notify a telegram group or share via twitter?
|
||||||
|
>
|
||||||
|
> [**<u>Please share your plugins or your needs</u>**](/contacts)
|
||||||
|
|
||||||
|
Plugins should be inside `./plugins` directory, this is an example:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const plugin = {
|
||||||
|
gancio: null,
|
||||||
|
load (gancio) {
|
||||||
|
console.error('Plugin GancioPluginExample loaded!')
|
||||||
|
plugin.gancio = gancio
|
||||||
|
},
|
||||||
|
|
||||||
|
onEventCreate (event) {
|
||||||
|
const eventLink = `${plugin.gancio.settings.baseurl}/event/${event.slug}`
|
||||||
|
if (!event.is_visible) {
|
||||||
|
console.error(`Unconfirmed event created: ${event.title} / ${eventLink}`)
|
||||||
|
} else {
|
||||||
|
console.error(`Event created: ${event.title} / ${eventLink}`)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onEventUpdate (event) {
|
||||||
|
console.error(`Event "${event.title}" updated`)
|
||||||
|
},
|
||||||
|
|
||||||
|
onEventDelete (event) {
|
||||||
|
console.error(`Event "${event.title}" deleted`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = plugin
|
||||||
|
```
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
FROM node:buster
|
FROM node:17.4-slim
|
||||||
RUN yarn global remove gancio || true
|
RUN yarn global remove gancio || true
|
||||||
RUN yarn cache clean
|
RUN yarn cache clean
|
||||||
RUN yarn global add --latest --production --silent https://gancio.org/latest.tgz 2> /dev/null
|
RUN yarn global add --latest --production --silent https://gancio.org/latest.tgz
|
||||||
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" ]
|
||||||
|
|
20
docs/docker/docker-compose.yml
Normal file
20
docs/docker/docker-compose.yml
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
version: '3'
|
||||||
|
|
||||||
|
services:
|
||||||
|
gancio:
|
||||||
|
build: .
|
||||||
|
restart: always
|
||||||
|
image: node:17.4-slim
|
||||||
|
container_name: gancio
|
||||||
|
environment:
|
||||||
|
- PATH=$PATH:/home/node/.yarn/bin
|
||||||
|
- GANCIO_DATA=/home/node/data
|
||||||
|
- NODE_ENV=production
|
||||||
|
- GANCIO_DB_DIALECT=sqlite
|
||||||
|
- GANCIO_DB_STORAGE=./gancio.sqlite
|
||||||
|
entrypoint: /entrypoint.sh
|
||||||
|
command: gancio start --docker
|
||||||
|
volumes:
|
||||||
|
- ./data:/home/node/data
|
||||||
|
ports:
|
||||||
|
- "127.0.0.1:13120:13120"
|
37
docs/docker/mariadb/docker-compose.yml
Normal file
37
docs/docker/mariadb/docker-compose.yml
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
version: '3'
|
||||||
|
|
||||||
|
services:
|
||||||
|
db:
|
||||||
|
image: mariadb
|
||||||
|
container_name: mariadb
|
||||||
|
volumes:
|
||||||
|
- ./db:/var/lib/mysql
|
||||||
|
- /etc/localtime:/etc/localtime:ro
|
||||||
|
environment:
|
||||||
|
- MARIADB_USER=gancio
|
||||||
|
- MARIADB_DATABASE=gancio
|
||||||
|
- MARIADB_PASSWORD=gancio
|
||||||
|
- MARIADB_RANDOM_ROOT_PASSWORD=yes
|
||||||
|
restart: always
|
||||||
|
gancio:
|
||||||
|
build: .
|
||||||
|
restart: always
|
||||||
|
image: node:17.4-slim
|
||||||
|
container_name: gancio
|
||||||
|
environment:
|
||||||
|
- PATH=$PATH:/home/node/.yarn/bin
|
||||||
|
- GANCIO_DATA=/home/node/data
|
||||||
|
- NODE_ENV=production
|
||||||
|
- GANCIO_DB_DIALECT=mariadb
|
||||||
|
- GANCIO_DB_HOST=db
|
||||||
|
- GANCIO_DB_DATABASE=gancio
|
||||||
|
- GANCIO_DB_USERNAME=gancio
|
||||||
|
- GANCIO_DB_PASSWORD=gancio
|
||||||
|
command: gancio start --docker
|
||||||
|
entrypoint: /entrypoint.sh
|
||||||
|
volumes:
|
||||||
|
- ./data:/home/node/data
|
||||||
|
ports:
|
||||||
|
- "127.0.0.1:13120:13120"
|
||||||
|
depends_on:
|
||||||
|
- db
|
|
@ -18,12 +18,17 @@ services:
|
||||||
gancio:
|
gancio:
|
||||||
build: .
|
build: .
|
||||||
restart: always
|
restart: always
|
||||||
image: node:buster
|
image: node:17.4-slim
|
||||||
container_name: gancio
|
container_name: gancio
|
||||||
environment:
|
environment:
|
||||||
- PATH=$PATH:/home/node/.yarn/bin
|
- PATH=$PATH:/home/node/.yarn/bin
|
||||||
- GANCIO_DATA=/home/node/data
|
- GANCIO_DATA=/home/node/data
|
||||||
- NODE_ENV=production
|
- NODE_ENV=production
|
||||||
|
- GANCIO_DB_DIALECT=postgres
|
||||||
|
- GANCIO_DB_HOST=db
|
||||||
|
- GANCIO_DB_DATABASE=gancio
|
||||||
|
- GANCIO_DB_USERNAME=gancio
|
||||||
|
- GANCIO_DB_PASSWORD=gancio
|
||||||
command: gancio start --docker
|
command: gancio start --docker
|
||||||
entrypoint: /entrypoint.sh
|
entrypoint: /entrypoint.sh
|
||||||
volumes:
|
volumes:
|
||||||
|
|
|
@ -4,12 +4,14 @@ services:
|
||||||
gancio:
|
gancio:
|
||||||
build: .
|
build: .
|
||||||
restart: always
|
restart: always
|
||||||
image: node:buster
|
image: node:17.4-slim
|
||||||
container_name: gancio
|
container_name: gancio
|
||||||
environment:
|
environment:
|
||||||
- PATH=$PATH:/home/node/.yarn/bin
|
- PATH=$PATH:/home/node/.yarn/bin
|
||||||
- GANCIO_DATA=/home/node/data
|
- GANCIO_DATA=/home/node/data
|
||||||
- NODE_ENV=production
|
- NODE_ENV=production
|
||||||
|
- GANCIO_DB_DIALECT=sqlite
|
||||||
|
- GANCIO_DB_STORAGE=./gancio.sqlite
|
||||||
entrypoint: /entrypoint.sh
|
entrypoint: /entrypoint.sh
|
||||||
command: gancio start --docker
|
command: gancio start --docker
|
||||||
volumes:
|
volumes:
|
||||||
|
|
|
@ -1,33 +1,100 @@
|
||||||
---
|
---
|
||||||
layout: default
|
layout: default
|
||||||
title: Embed events
|
title: Embed events in webpages
|
||||||
permalink: /usage/embed
|
permalink: /usage/embed
|
||||||
nav_order: 1
|
nav_order: 1
|
||||||
parent: Usage
|
parent: Usage
|
||||||
---
|
---
|
||||||
|
|
||||||
## Embed event
|
|
||||||
|
|
||||||
|
## Embed a single event or a list of events in your webpage
|
||||||
|
{: .no_toc }
|
||||||
|
|
||||||
You can embed a list of filtered events or a specific event card in your webpage using a classic old-school `iframe` or a shiny new webcomponent.
|
You can embed a list of filtered events or a specific event card in your webpage using a classic old-school `iframe` or a shiny new webcomponent.
|
||||||
|
|
||||||
### Webcomponents
|
1. TOC
|
||||||
The webcomponent require a small js to be loaded in your page:
|
{:toc}
|
||||||
```javascript
|
## Webcomponents
|
||||||
|
[Webcomponents](https://www.webcomponents.org/introduction) usage requires a small (~5kB gzipped) js script to be loaded in your page (note that you should use your instance name):
|
||||||
|
```html
|
||||||
<script src='https://demo.gancio.org/gancio-events.es.js'></script>
|
<script src='https://demo.gancio.org/gancio-events.es.js'></script>
|
||||||
```
|
```
|
||||||
|
|
||||||
#### embed a single event
|
### Embed a single event
|
||||||
> you can copy the code in **event page > Embed > Copy**
|
To embed an event in webpages you use `<gancio-event>` custom element, you can copy the required code in **event's page > Embed > Copy**, should be like the following:
|
||||||
|
|
||||||
<script src='https://demo.gancio.org/gancio-events.es.js'></script>
|
```html
|
||||||
<gancio-event id=17 baseurl='https://demo.gancio.org'></gancio-event>
|
|
||||||
```javascript
|
|
||||||
<gancio-event id=17 baseurl='https://demo.gancio.org'></gancio-event>
|
<gancio-event id=17 baseurl='https://demo.gancio.org'></gancio-event>
|
||||||
```
|
```
|
||||||
|
|
||||||
#### embed event lists
|
<script src='/assets/js/gancio-events.es.js'></script>
|
||||||
> you can copy the code in **Export > List > Copy**
|
<gancio-event id=17 baseurl='https://demo.gancio.org'></gancio-event>
|
||||||
<gancio-events baseurl='https://demo.gancio.org'></gancio-events>
|
|
||||||
```javascript
|
|
||||||
<gancio-event baseurl='https://demo.gancio.org'></gancio-event>
|
### Embed event lists
|
||||||
|
You can also embed a list of events using `<gancio-events>` custom element, you can copy the required code in **Export > List > Copy**
|
||||||
|
|
||||||
|
|
||||||
|
```html
|
||||||
|
<gancio-events baseurl='https://gancio.cisti.org'>
|
||||||
|
<a href='https://gancio.cisti.org'>Gancio Events</a>
|
||||||
|
</gancio-events>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
<script>
|
||||||
|
var theme = 'light';
|
||||||
|
var sidebar = 'true';
|
||||||
|
function toggleDark() {
|
||||||
|
var items = document.getElementsByTagName('gancio-events');
|
||||||
|
theme = theme === 'dark' ? 'light' : 'dark';
|
||||||
|
items[0].setAttribute('theme', theme );
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleSidebar() {
|
||||||
|
var items = document.getElementsByTagName('gancio-events');
|
||||||
|
sidebar = sidebar === 'true' ? 'false' : 'true';
|
||||||
|
items[0].setAttribute('sidebar', sidebar );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function changeMax(value) {
|
||||||
|
var items = document.getElementsByTagName('gancio-events');
|
||||||
|
items[0].setAttribute('maxlength', value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function changeTitle (title) {
|
||||||
|
var items = document.getElementsByTagName('gancio-events');
|
||||||
|
items[0].setAttribute('title', title)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
label {
|
||||||
|
display: block;
|
||||||
|
margin: 0px;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<label><input type='checkbox' onchange="toggleDark()"/> Dark</label>
|
||||||
|
<label><input type='checkbox' checked onchange="toggleSidebar()"/> Sidebar</label>
|
||||||
|
<label>Max items <input value=4 type='number' label='Max items' onchange="changeMax(this.value)"/></label>
|
||||||
|
<label>Title <input value='Gancio' type='text' onkeyup="changeTitle(this.value)"/></label>
|
||||||
|
<gancio-events sidebar='true' title='Gancio' theme='light' maxlength=4 baseurl='https://gancio.cisti.org'><a href='https://gancio.cisti.org'>Gancio Events</a></gancio-events>
|
||||||
|
|
||||||
|
> info "Customize"
|
||||||
|
> Note that you can modify the title (or completely remove it using an empty `title` param),
|
||||||
|
> you can limit the list to a maximum number of events using the `maxlength` parameter and filter events by `tags` or `places` using that parameters (it's easier using **gancio** than to explain it here)
|
||||||
|
|
||||||
|
|
||||||
|
## IFrame
|
||||||
|
You can also use the old iframe method
|
||||||
|
<iframe src='https://demo.gancio.org/embed/17' style="width: 410px; border: none; height: 210px; overflow: hidden;"></iframe>
|
||||||
|
|
||||||
|
```html
|
||||||
|
<iframe src='https://demo.gancio.org/embed/17' style="width: 410px; border: none; height: 210px; overflow: hidden;"></iframe>
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Wordpress
|
||||||
|
To embed an event or a list of events into a [WordPress](https://wordpress.com) website you can use the [WPGancio](https://wordpress.org/plugins/wpgancio/) plugin, this allows you to use webcomponents and shortcodes and automatically includes the needed script in each page and post.
|
|
@ -18,22 +18,22 @@ A shared agenda for local communities.
|
||||||
## Some relevant key features:
|
## Some relevant key features:
|
||||||
|
|
||||||
- **Focus on content** not on people:
|
- **Focus on content** not on people:
|
||||||
nowhere on gancio appears the identity of who published the event, not even under a nickname, not even to administrators (except in the db). This is not an ego-friendly platform.
|
nowhere on gancio does the identity of who posted an event appear, not even under a nickname, not even to administrators (except in the db). This is not an ego-friendly platform.
|
||||||
|
|
||||||
- **Visitors first**. We do not want logged user to get more features than random visitor. We do not want users to register, except to publish events and even in this case you can publish an anonymous event.
|
- **Visitors first**. We do not want logged user to get more features than random visitor. We don't want users to register, except to post events and even then you can post an anonymous event.
|
||||||
|
|
||||||
- **Anonymous events**: optionally a visitor can create events without being registered (an administrator has to confirm them)
|
- **Anonymous events**: optionally a visitor can create events without being registered (an administrator must confirm them)
|
||||||
|
|
||||||
- **We are not interested in making hits** so we export events in many ways, via RSS feeds, via global or individual ics, incorporating lists of events or single event via iframe on other websites and via [AP]({% link federation.md %})
|
- **We don't care about making hits** so we export events in many ways: via RSS feeds, via global or individual ics, allowing you to embed list of events or single event via [iframe or webcomponent]({% link embed.md %}) on other websites, via [AP]({% link federation.md %}), [microdata](https://developer.mozilla.org/en-US/docs/Web/HTML/Microdata) and [microformat](https://developer.mozilla.org/en-US/docs/Web/HTML/microformats#h-event)
|
||||||
|
|
||||||
- Very easy UI
|
- Very easy UI
|
||||||
- Multidays events support (festival, conferences...)
|
- Multi-day events (festival, conferences...)
|
||||||
- Recurrent events support (each monday, each two monday, each monday and friday, each two saturday, etc.)
|
- Recurring events (each monday, each two monday, each monday and friday, each two saturday, etc.)
|
||||||
- Filters events for tags or places
|
- Filter events for tags or places
|
||||||
- RSS and ICS export (with filters)
|
- RSS and ICS export (with filters)
|
||||||
- embeddable iframe ([example](https://gancio.cisti.org/embed/list?title=Upcoming events))
|
- embed your events in your website with [webcomponents]({% link embed.md %}) or iframe ([example](https://gancio.cisti.org/embed/list?title=Upcoming events))
|
||||||
- boost / bookmark / comment events from the fediverse!
|
- boost / bookmark / comment events from the fediverse!
|
||||||
- Lot of configurations available (user registration open/close, enable federation, enable recurrent events)
|
- Lot of configurations available (dark/light theme, user registration open/close, enable federation, enable recurring events)
|
||||||
|
|
||||||
### License
|
### License
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ sudo -u postgres pg_dump -Fc gancio > gancio.dump
|
||||||
1. Archive database, configuration, custom user locales, logs, images and thumbnails
|
1. Archive database, configuration, custom user locales, logs, images and thumbnails
|
||||||
```bash
|
```bash
|
||||||
sudo tar -czf gancio-$(date +%Y-%m-%d-%H%M%S)-backup.tgz \
|
sudo tar -czf gancio-$(date +%Y-%m-%d-%H%M%S)-backup.tgz \
|
||||||
$(ls -d config.json uploads user_locale db.sqlite gancio.dump postgres data logs 2> /dev/null)
|
$(ls -d config.json uploads user_locale db.sqlite gancio.dump postgres data db logs 2> /dev/null)
|
||||||
```
|
```
|
||||||
|
|
||||||
> info "Automatic backup"
|
> info "Automatic backup"
|
||||||
|
|
|
@ -7,7 +7,7 @@ parent: Install
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
{: .no_toc }
|
{: .no_toc }
|
||||||
`gancio` configuration is done during installation process but you can change it editing the configuration file. Note that you can always re-run gancio with `--setup` flag to use the interactive setup.
|
`gancio` configuration is done during installation process but you can change it editing the configuration file.
|
||||||
The configuration file shoud be a `.json` or a `.js` file and could be specified using the `--config` flag.
|
The configuration file shoud be a `.json` or a `.js` file and could be specified using the `--config` flag.
|
||||||
|
|
||||||
- <small>eg. `gancio start --config ./config.json`</small>
|
- <small>eg. `gancio start --config ./config.json`</small>
|
||||||
|
@ -27,7 +27,7 @@ This probably support unix socket too
|
||||||
```
|
```
|
||||||
|
|
||||||
- ### Database
|
- ### Database
|
||||||
DB configuration, look [here](https://sequelize.org/master/class/lib/sequelize.js~Sequelize.html#instance-constructor-constructor) for options.
|
DB configuration, look [here](https://sequelize.org/v6/class/src/sequelize.js~Sequelize.html#instance-constructor-constructor) for options.
|
||||||
```json
|
```json
|
||||||
"db": {
|
"db": {
|
||||||
"dialect": "sqlite",
|
"dialect": "sqlite",
|
||||||
|
|
|
@ -21,8 +21,8 @@ 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>
|
||||||
|
|
||||||
|
#### Choose you database (sqlite, postgresql, mariadb, mysql)
|
||||||
1. Setup with postgreSQL __(optional as you can choose sqlite)__
|
1. Setup using postgreSQL __(optional)__
|
||||||
```bash
|
```bash
|
||||||
sudo apt-get install postgresql
|
sudo apt-get install postgresql
|
||||||
# Create the database
|
# Create the database
|
||||||
|
@ -32,13 +32,25 @@ postgres=# create user gancio with encrypted password 'gancio';
|
||||||
postgres=# grant all privileges on database gancio to gancio;
|
postgres=# grant all privileges on database gancio to gancio;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
1. Setup using MariaDB (__optional__)
|
||||||
|
```bash
|
||||||
|
sudo apt-get install mariadb
|
||||||
|
sudo mysql
|
||||||
|
MariaDB [(none)]> create database gancio;
|
||||||
|
Query OK, 1 row affected (0.001 sec)
|
||||||
|
MariaDB [(none)]> create user gancio identified by 'gancio';
|
||||||
|
Query OK, 0 rows affected (0.011 sec)
|
||||||
|
MariaDB [(none)]> grant all privileges on gancio.* to gancio;
|
||||||
|
Query OK, 0 rows affected (0.009 sec)
|
||||||
|
```
|
||||||
|
|
||||||
1. Create a user to run gancio from
|
1. Create a user to run gancio from
|
||||||
```bash
|
```bash
|
||||||
sudo adduser --group --system --shell /bin/false --home /opt/gancio gancio
|
sudo adduser --group --system --shell /bin/false --home /opt/gancio gancio
|
||||||
```
|
```
|
||||||
1. Install Gancio
|
1. Install Gancio
|
||||||
```bash
|
```bash
|
||||||
sudo yarn global add --silent {{site.url}}/latest.tgz 2> /dev/null
|
sudo yarn global add --silent {{site.url}}/latest.tgz
|
||||||
```
|
```
|
||||||
|
|
||||||
1. Setup systemd service and reload systemd
|
1. Setup systemd service and reload systemd
|
||||||
|
@ -66,6 +78,6 @@ sudo systemctl start gancio
|
||||||
```bash
|
```bash
|
||||||
yarn global remove gancio
|
yarn global remove gancio
|
||||||
yarn cache clean
|
yarn cache clean
|
||||||
yarn global add --silent {{site.url}}/latest.tgz 2> /dev/null
|
yarn global add --silent {{site.url}}/latest.tgz
|
||||||
sudo service gancio restart
|
sudo systemctl restart gancio
|
||||||
```
|
```
|
||||||
|
|
|
@ -11,76 +11,51 @@ nav_order: 2
|
||||||
1. TOC
|
1. TOC
|
||||||
{:toc}
|
{:toc}
|
||||||
|
|
||||||
## Initial setup
|
## Setup
|
||||||
|
|
||||||
|
|
||||||
- __You must have the following dependencies installed: Docker, Docker Compose and Nginx__
|
|
||||||
|
|
||||||
|
Make sure to have [Docker Engine](https://docs.docker.com/engine/install/),
|
||||||
|
[Docker Compose](https://docs.docker.com/compose/install/) and [nginx](https://nginx.org/en/docs/install.html) installed:
|
||||||
```bash
|
```bash
|
||||||
sudo apt install docker docker-compose nginx
|
sudo apt install docker docker-compose nginx
|
||||||
```
|
```
|
||||||
or
|
|
||||||
1. [Install docker](https://docs.docker.com/engine/install/)
|
|
||||||
1. [Install docker-compose](https://docs.docker.com/compose/install/)
|
|
||||||
1. [Install nginx](https://nginx.org/en/docs/install.html)
|
|
||||||
|
|
||||||
- __Create a directory where everything related to gancio is stored__
|
Create a directory where everything related to gancio is stored:
|
||||||
```bash
|
```bash
|
||||||
mkdir -p /opt/gancio
|
mkdir -p /opt/gancio
|
||||||
cd /opt/gancio
|
cd /opt/gancio
|
||||||
```
|
```
|
||||||
|
|
||||||
## Use sqlite
|
Download `Dockerfile` and `entrypoint.sh`:
|
||||||
<div class='code-example bg-grey-lt-100' markdown="1">
|
|
||||||
|
|
||||||
1. **Download docker-compose.yml and Dockerfile**
|
|
||||||
```bash
|
```bash
|
||||||
wget {{site.url}}{% link /docker/Dockerfile %}
|
wget {{site.url}}{% link /docker/Dockerfile %}
|
||||||
wget {{site.url}}{% link /docker/entrypoint.sh %}
|
wget {{site.url}}{% link /docker/entrypoint.sh %}
|
||||||
wget {{site.url}}{% link /docker/sqlite/docker-compose.yml %}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Download `docker-compose.yml` choosing your preferred database dialect between `sqlite`, `postgres` and `mariadb`:
|
||||||
1. Build docker image
|
|
||||||
```
|
|
||||||
docker-compose build
|
|
||||||
```
|
|
||||||
</div>
|
|
||||||
|
|
||||||
## Use postgreSQL
|
|
||||||
<div class='code-example bg-grey-lt-100' markdown="1">
|
|
||||||
|
|
||||||
1. **Download docker-compose.yml and Dockerfile**
|
|
||||||
```bash
|
```bash
|
||||||
wget {{site.url}}{% link /docker/Dockerfile %}
|
DB=sqlite
|
||||||
wget {{site.url}}{% link /docker/entrypoint.sh %}
|
wget {{site.url}}/docker/$DB/docker-compose.yml
|
||||||
wget {{site.url}}{% link /docker/postgres/docker-compose.yml %}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
1. Build docker image
|
Build docker image
|
||||||
```
|
```bash
|
||||||
docker-compose build
|
docker-compose build
|
||||||
```
|
```
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
## Start gancio
|
## Start gancio
|
||||||
|
|
||||||
1. Run your container
|
Start your container:
|
||||||
```bash
|
```bash
|
||||||
docker-compose up -d
|
docker-compose up -d
|
||||||
```
|
```
|
||||||
|
|
||||||
1. Look at logs
|
You can take a look at logs using:
|
||||||
```bash
|
```bash
|
||||||
tail -f data/logs/gancio.log
|
tail -f data/logs/gancio.log
|
||||||
```
|
```
|
||||||
|
|
||||||
1. [Setup nginx as a proxy]({% link install/nginx.md %})
|
You'll need to [setup nginx as a proxy]({% link install/nginx.md %}) then you can point your web browser to your domain :tada:
|
||||||
|
|
||||||
1. Point your web browser to your domain :tada:
|
|
||||||
|
|
||||||
1. Edit `data/config.json` and restart the container on your needs, see [Configuration]({% link install/configuration.md %}) for more details.
|
|
||||||
|
|
||||||
## Upgrade
|
## Upgrade
|
||||||
|
|
||||||
|
@ -89,20 +64,6 @@ tail -f data/logs/gancio.log
|
||||||
> Don't be lazy and [backup]({% link install/backup.md %}) your data!
|
> Don't be lazy and [backup]({% link install/backup.md %}) your data!
|
||||||
|
|
||||||
|
|
||||||
> error "Upgrade from a version < 1.0"
|
|
||||||
> Since v1.0 our docker setup is changed and a new container has to be built:
|
|
||||||
>
|
|
||||||
> 1. `cd /opt/gancio`
|
|
||||||
> 1. [Backup your data]({% link install/backup.md %})
|
|
||||||
> 1. Download new `Dockerfile` <br/> `wget {{site.url}}{% link /docker/Dockerfile %}`
|
|
||||||
> 1. Download new `entrypoint.sh` <br/> `wget {{site.url}}{% link /docker/entrypoint.sh %}`
|
|
||||||
> 1. Download new `docker-compose.yml` (substitute `sqlite` with `postgres` in case): <br/>`wget {{site.url}}{% link /docker/sqlite/docker-compose.yml %}`
|
|
||||||
> 1. Build the new container `docker-compose build`
|
|
||||||
> 1. Extract your backup into `./data` <br/>`mkdir data; tar xvzf gancio-<yourLastBackup>-backup.tgz -C data`
|
|
||||||
> 1. Stop your old container `docker-compose stop`
|
|
||||||
> 1. Start your new container `docker-compose up`
|
|
||||||
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd /opt/gancio
|
cd /opt/gancio
|
||||||
docker-compose up -d --no-deps --build
|
docker-compose up -d --no-deps --build
|
||||||
|
|
|
@ -9,7 +9,7 @@ has_toc: false
|
||||||
## Pre-requisites
|
## Pre-requisites
|
||||||
- a Linux machine with <strong>root access</strong> (a VPS with 500MB of RAM and a cpu should be enough but do not use docker on a small machine :stuck_out_tongue_winking_eye:)
|
- a Linux machine with <strong>root access</strong> (a VPS with 500MB of RAM and a cpu should be enough but do not use docker on a small machine :stuck_out_tongue_winking_eye:)
|
||||||
- a domain name or subdomain (eg. gancio.mydomain.org, subpath are not supported)
|
- a domain name or subdomain (eg. gancio.mydomain.org, subpath are not supported)
|
||||||
- an SMTP server to deliver emails
|
- optionally an SMTP server to deliver emails
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,10 @@ 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)
|
||||||
|
- [sapratza.in](https://sapratza.in/) (Sardinia, Italy)
|
||||||
- [termine.161.social](https://termine.161.social) (Germany)
|
- [termine.161.social](https://termine.161.social) (Germany)
|
||||||
|
- [ezkerraldea.euskaragendak.eus](https://ezkerraldea.euskaragendak.eus/)
|
||||||
|
- [lakelogaztetxea.net](https://lakelogaztetxea.net)
|
||||||
|
|
||||||
|
|
||||||
<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>
|
||||||
|
|
83
docs/public.key
Normal file
83
docs/public.key
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||||
|
|
||||||
|
mQINBFjwEUkBEADGkRYEgSGQHEi/YIKvJCuDDB8TYGT3Gzuz0BfgwWlSiupjzkTv
|
||||||
|
eJAZTX+rH1MCnlwV7QJ+Y7v2I/REA9acc+8IapvcUarBJsEwtdp9nIyjKnDR5eiF
|
||||||
|
UOkh4BxLsi541cm6UreSPOMGEEviTiv4CXYeIg+V+Q20Sp+2dy8vwBe3xSYW5aVg
|
||||||
|
c+d6IFeq40CtYyqheAPpJKQop/DKtRRklz3bIOiObXHIYZNzjCDSQyjbBAF1DapS
|
||||||
|
4Z6pqR4+6l5+ylXbWGI7Cg+JFmGl4jH1zI3lX5utWbqsIS/cDkLG+pYNwKDoP8o9
|
||||||
|
VY7SSQbr/n8BaPRNsLP6zw3xLok0+i8GGYUyeNN0Yua3IIB8Bu503+4kZTgl2J3G
|
||||||
|
sxf+zGS6cWA6u9ROo+plDN9fip7XW29hfVCjc7lsdsbvVGHLI2ZScF0gfXuI2AMf
|
||||||
|
kLeoI32ZMlPT6BSWmYO0fAoZyZgIG7u+DH59pEqM1mc4ChSzhSxtDDzsOenbAstH
|
||||||
|
xkJWB9Y/P77OfmwiNkUkCBb7uk/n092g/0xf8CqE82sj2P4LccBt0J1qi4wSyAZQ
|
||||||
|
Nfm2I5dVmW9A21AoLjuNoTLbB0EEcORyrYYcA13b3DIj0UcSlo/KsP0ZZe6Xu915
|
||||||
|
smUixqWJ8j9FPGAnIGY7FJEkHmnhXc/wj+qVxPFKgMfDvbdtIXIzT6eMFwARAQAB
|
||||||
|
tDJVbmRlcnNjb3JlIF9UTyogSGFja2xhYiA8dW5kZXJzY29yZUBhdXRpc3RpY2ku
|
||||||
|
b3JnPokCNwQTAQgAIQUCWPARSQIbAwULCQgHAwUVCgkICwUWAgMBAAIeAQIXgAAK
|
||||||
|
CRC7603TmsbMqQUSD/4p0WHK51l4SULlAGYVUCd8gM/OGfvA8FkzBJaPwvGAJTGL
|
||||||
|
Pj9U876R4MxBSxsI0UhajKDQBRlswpSOxTGknBVDJ9O1RPhXYgRPQt+piiiGIExT
|
||||||
|
n3be+lxw8DDj/t5MGQDXMbXR/UfVlO5X573/2XSmHVWB9RWdfL3XYGYusxPB1kLO
|
||||||
|
jSeC8ETcV/tka8kToPFW8Fshg8xVkBkzvXiwXrbdKwreDAo/Ukms+8MkI8Qlyhhh
|
||||||
|
vfiUb+GnGuEnHSUom7Sn2YMW5qkOKsVZ4Ca5DhqjbXZEDCA2VhnaS5TrRaBb0odk
|
||||||
|
B9rg399pI36cOAO9qtejchG88JbK6Tr7Dh56/Lr6T0mZBPUWk4nJLAWcF6U5k9kT
|
||||||
|
jVD8K9zsGL/4Zex0a7GCcS0bVmXPt8+G9n4VoFMP5rjVkE9Mi7ugBzMNzEDd2crT
|
||||||
|
GMjPpe77arfEaLfg/2mnDCmqz3osRqV0L+Ef3S4GNjmT0LNiEWbB7eGEOVf2CEBb
|
||||||
|
IHoexd74DF6Dz96tjaN7xeDgvm/hYLdioyCgf6cccZp4xdE2SVcEWOLYoG5B77qr
|
||||||
|
W4b+ssOotx7U0pEpn9A+sYno73tHfzNhoC1UXMcaVh7+nWJAQ0AhVkF6EHUXlb7J
|
||||||
|
p1SDmtUajBYeOEfWDfJZ/3p9Z4ARiE/CEo08O59xtJ5p8XNxBEU1TbpNADHdm4kC
|
||||||
|
HAQQAQgABgUCWPAVQQAKCRA1KRglCwEhd/ciD/9k4fxrgH4ZkEbjwZ4r7HrZ8A29
|
||||||
|
ZsP/ThAaSiLgoKCDjpes2hYir8jwp5QI3aEINseL4+Uzh9st6y3WthgH6aTTPUvP
|
||||||
|
QTjpLfre16YTTWS7ztfLMGUGvXpruXncF8rQUeRwjc/FK0/yzMFcY8iX9DpSSKSc
|
||||||
|
evNlfbDblzQhzo0ibEQ5t49HsaFXNgBlY74PrdFrAde8gI5PPaBWJcg94s1hsCx5
|
||||||
|
Epv7Vfl/wQI8mcnRBAfKfnqVl3AYoqwwCFFuxhdjEyNz35otWJgBfufd5jFVmY8a
|
||||||
|
dT5ZDieFabOdcp0qlGQaPkXO9cIMkbloIxMFiID8kArn4cEWx/hEHa6iHqcBhXgN
|
||||||
|
hZqiTONYlaRZGcWyRh+hFOzVfVdgy/Q7Mqo6VRqbuWWAdMJUrT29xuL7HP2M+Kd6
|
||||||
|
/Fk3C0cfupw7sjhcPlWq/rhvLM+rjfury6cCpGP5Ff22VZebddgGuOGOcY09Q+p+
|
||||||
|
nUaEcU2x/p/JWwyZC8j0cDTBEvzOHZDi2avs3rYjydkbi2syRn0IslbRnrKV45tw
|
||||||
|
amKm9vaI8CWXzvEaGfAUclwBcXw5wES/x2h0Mt6CR2Kt5zNnbieSXjWN/5YLQpQ1
|
||||||
|
YcHEsVdB1lv9jsFzls1xg9hFyLx7DuIsCrZSMQ1wiBWHkvmU9DCH9sCN4KFzI58Q
|
||||||
|
hexGgVf6e8ACRaUsoYkCMwQQAQgAHRYhBLSjt7RWdzvKZ9Uz1CZCVK0JvPrZBQJY
|
||||||
|
/SKHAAoJECZCVK0JvPrZVIUP/2+EB2MCFQRUq1jS7Ud7zQVVneCj1jpBoQqrQOa3
|
||||||
|
ntfIwr0hIJkAuNqsObkgVtS5GThW5w1x0d6MGvSD80n18ipKzsfp7XLU2FBUwU6D
|
||||||
|
nHgUuTNe092aIXPhln/qIRA7EieCSTyvAvE6P+d/VlhylY/R97TN13jhYz+3wBV0
|
||||||
|
IQ4fkfgU08N9MGJ/cBk8CQQAPM+3tDcWQCyefaOVX1JW93gROwcUeRSYO1qQVwUZ
|
||||||
|
iykTPDfWDccKSLM4sAz0uYxo5tJTVdt+1s7pmsAWDadZOxILcurz3zRqrpyjPcqU
|
||||||
|
+i4c1JBhWX1j0tYz6p5gs3GnhEmyrluALNmLEn6x9G671iDjmkyqK/WLefzyeo95
|
||||||
|
Yj3b2+EDq0wKwpL+yuiSvKEBHqbgmCwbQ8+D6Fp+hNEScgmt5efQ0iyQYDu/p4K7
|
||||||
|
0qOfVpzJYT+Sv+aVwXzclD/18W8rLfz+PV80Au87vseQW53BiuI4C5FqtFe1t3OY
|
||||||
|
kys2lVATeruwtlep+2Py1cyMgYyLjENc0h0sxmzhOid/KKCGWTeMB9SKX7rVI7Op
|
||||||
|
J9ErN58GeDyrhAiTur0F4cdvTmxZFGXaUvVoQCkWNo9iYngCeQ8TxCTGzVPRGMNv
|
||||||
|
hm4YYJAPw0P4H2+riAJFd0gqJ9B70bDaFep+/WnGOIBwpW3R7ElGzUpAGEWKSMTu
|
||||||
|
fgfSiQGzBBABCAAdFiEEmu4Bhq6CdRPO82CyQf9TSC2eROYFAlnRWtoACgkQQf9T
|
||||||
|
SC2eROYyOwv/WyC13Ae81ElGDefFyO8hu/d4xRx2wiWG4X6Dft6dyu5pv628l5Kd
|
||||||
|
ZUeowm/TdRL58yLpciJCn9NOGJ83Xwu3Fo5/1qhckuJyOcKuxaYoTahH8PQDtvCZ
|
||||||
|
Xb8lsvYGA7SOrp5jIihW72Bfz3Se5IibBUSyEKXE6Ro7qmR1drNIrTYldRo5YDMY
|
||||||
|
y4M00BEWcfi7z/KFWVdmwusxMz6/raW+sw9kPQbEsh7aTAORHqc0/vf7cdmIue+F
|
||||||
|
SU6GFcKojIkb/7AZlQzyfuaLogJ+80sBQVJDNSBYvmgNwH61BXAKs1divL4e/p52
|
||||||
|
eF7cJe1p66QrNHJXvBR4vxcS5novSVtO2exL0cAbF/e/f7+vBGViPzDi9fdKeyF0
|
||||||
|
KmW9KSncpiePQ1qkDvmHFfejV5bWa8hdrPc6X8PQ/AOPsNPw/8TBeXCTjg/nNZvC
|
||||||
|
pl9m12ISiIOhpURq1srpzw0JygxV+z+K/fkZuPo6QJFy9hSXUwZ1QzV8yLTyKz3i
|
||||||
|
aHrcwQRcK/jwuQINBFjwEUkBEAC1GrFLnjU4hKx3dEiEYQWgIAdKezZzLGYr7KgU
|
||||||
|
riNflGUEouWHg0OJoSD8rJ8IySeFKMUGHfPRci6i8FBgGGOUEBv4bwCvZeNSG5/S
|
||||||
|
mIiB5PDSSJhdqBGM3tGq72FWdRaLxDHyooGnjeMtwtSEGL7XmWtFd4daJL3GFnc0
|
||||||
|
arfCA0w+jBVGlUrVOy4VQ1hLgT6Yiu4mxdg3oun96fHaWkRh1YxGfWzlDx5963uS
|
||||||
|
LVUmZSatRcQHaNLEUeYYfFCUepKpz8bbQdPv0eF/A9mPn3zqZQypSdhDQPnTUCgv
|
||||||
|
omAEZhR4dghNXmXf/TmyBc+2oRXynv5HUfnVUr+ynjcz4k3HeYHjEst0Uue2Rz48
|
||||||
|
O9yP+VRSzOgl2HG8TfuD750UArk9WF06GFAnwWcv9AJalgLzQ1qJisurtyan0fkI
|
||||||
|
PWOgdQJwXgC7lGNZXE2xQMkcihWmDBdfK9YKpkyPhi4aSW8M///QP4WT4LFQe/nu
|
||||||
|
irhEQh8zuujA02HlzTbfdqlecpSkkMu2u+bgXHG/kC7wzMcCa+I7HyxKtXBd1MOC
|
||||||
|
+4c6PY0Am5QkwvLQVlAg7WWDW9kRpymN6xQc8hwlZChziXe3K2nv0rmM2+K1UVPB
|
||||||
|
Ub69wUHVUnXCCOAEfjiIFMloQa39+LLp2DamTrVaIkLDr21KXH90FXD+pydMlPBU
|
||||||
|
FkCXrQARAQABiQIfBBgBCAAJBQJY8BFJAhsMAAoJELvrTdOaxsypf7AP/0LMlW9v
|
||||||
|
WkTlFK8Q3QOmRadwwbrjwdPEBaoN2Ll4SwxQ2KBMBAZqyGnbV6f2JOvDep0S9Xwy
|
||||||
|
RvyUnTWlZb+Hz62xT/RJwhorRxBTEFAgkEbGK8jq5oSiixjOslKv8Qe1W6Qggh0+
|
||||||
|
dCbo2LVEDnlOmQlMpmf9k3fL8mDrOiYany/un1x7Fdtux6B7CtCIib4c84RrotuY
|
||||||
|
vFwS+bLKV2oHroHKqdt7iY5kZ4LBtrKuoo/2fYtIk+2jCmligOpsrI27zpS82SWp
|
||||||
|
wGspeOIukiNOtYA8kbjUxx4K7qGF5f35o+6wVOwPnWTjKtKm6BaltlUk7mgtXwYG
|
||||||
|
GRO44Nkw3sPE/9MAKhIJNm4Oq+2/av4ooE8dYXERUY0g5dR5PHX0O8lQBjDyGYoz
|
||||||
|
H/m1DDVoxOwkVbQP6alpie6XvM03mOWOklj20mlFAnyjLzdxw2m7vySOnXnvZq3s
|
||||||
|
SZUYTh0rsWmXt/ye1/E8aXtTqu9ZnuSAJh+8TLmGYppyjqr6PhENtZcOxUZyoKzc
|
||||||
|
A8ZrRzyuelHqiAWQBNDhOde+QYcBgYXaD1BQUO/9dWanUnP9Lbhu4zkxIC353m1e
|
||||||
|
xAYreSmvD5paqoL4jH5eJakdIasQTjyT8FJ/CI131fbxuVT9P2oaKt/7mjk0JKcz
|
||||||
|
/mmL8sP2wfO515ZBxq6+nuzOel+4IHFFqRFv
|
||||||
|
=XDDO
|
||||||
|
-----END PGP PUBLIC KEY BLOCK-----
|
|
@ -1,16 +1,19 @@
|
||||||
<template lang='pug'>
|
<template lang='pug'>
|
||||||
v-container.p-4.text-center
|
v-container.p-4.text-center
|
||||||
v-alert(v-if="error.statusCode === 404") ¯\_(ツ)_/¯ {{error.message}}
|
v-alert(v-if="error.statusCode === 404" type='error' :icon='mdiAlert') ¯\_(ツ)_/¯ {{error.message}}
|
||||||
v-alert(v-else type='error') <v-icon>mdi-warning</v-icon> An error occurred: {{error.message}}
|
v-alert(v-else type='error' :icon='mdiAlert') An error occurred: {{error.message}}
|
||||||
nuxt-link(to='/')
|
nuxt-link(to='/')
|
||||||
v-btn Back to home
|
v-btn Back to home
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapState } from 'vuex'
|
import { mapState } from 'vuex'
|
||||||
|
import { mdiAlert } from '@mdi/js'
|
||||||
export default {
|
export default {
|
||||||
props: { error: { type: Object, default: () => ({ }) } },
|
props: { error: { type: Object, default: () => ({ }) } },
|
||||||
|
data () {
|
||||||
|
return { mdiAlert }
|
||||||
|
},
|
||||||
head () {
|
head () {
|
||||||
return { title: `${this.settings.title} - Error` }
|
return { title: `${this.settings.title} - Error` }
|
||||||
},
|
},
|
||||||
|
|
|
@ -109,7 +109,7 @@
|
||||||
"list_description": "Si tens una web i vols encastar una llista d'activitats, pots fer servir el codi de sota"
|
"list_description": "Si tens una web i vols encastar una llista d'activitats, pots fer servir el codi de sota"
|
||||||
},
|
},
|
||||||
"register": {
|
"register": {
|
||||||
"description": "Els moviments socials necessitem organitzar-nos i auto-finançar-nos. <br/> Aquest és un regal per vosaltres, feu-lo servir només per usos no-comercials i òbviament per activitats antifeixistes, antisexistes, i antiracistes.\n <br/> Abans que puguis publicar, <strong> hem d'aprovar el teu compte </strong>, tingues en comtpe que <strong> darrere d'aquesta web hi ha persones </strong> de carn i ossos. Ens agradaria saber quin tipus d'activitats vols publicar, ens escrius dues línies explicant-ho?",
|
"description": "Els moviments socials necessitem organitzar-nos i auto-finançar-nos.\n<br/> Abans que puguis publicar, <strong> hem d'aprovar el teu compte </strong>, tingues en comtpe que <strong> darrere d'aquesta web hi ha persones </strong> de carn i ossos, així que escriviu dues línies per fer-nos saber quins esdeveniments voleu publicar.",
|
||||||
"error": "Error: ",
|
"error": "Error: ",
|
||||||
"complete": "El registre ha de ser confirmat.",
|
"complete": "El registre ha de ser confirmat.",
|
||||||
"first_user": "S'ha creat i activat un compte administrador"
|
"first_user": "S'ha creat i activat un compte administrador"
|
||||||
|
@ -124,7 +124,7 @@
|
||||||
"media_description": "Pots adjuntar un cartell (opcional)",
|
"media_description": "Pots adjuntar un cartell (opcional)",
|
||||||
"added": "S'ha afegit l'activitat",
|
"added": "S'ha afegit l'activitat",
|
||||||
"added_anon": "S'ha afegit l'activitat però encara ha de ser confirmada.",
|
"added_anon": "S'ha afegit l'activitat però encara ha de ser confirmada.",
|
||||||
"where_description": "On es farà? Si no apareix el lloc el pots crear.",
|
"where_description": "On es farà? Si no està posat, escriu-ho i <b>prem Enter</b>.",
|
||||||
"confirmed": "S'ha confirmat l'activitat",
|
"confirmed": "S'ha confirmat l'activitat",
|
||||||
"not_found": "No s'ha trobat l'activitat",
|
"not_found": "No s'ha trobat l'activitat",
|
||||||
"remove_confirmation": "Segur que vols esborrar l'activitat?",
|
"remove_confirmation": "Segur que vols esborrar l'activitat?",
|
||||||
|
@ -156,10 +156,10 @@
|
||||||
"ics": "ICS",
|
"ics": "ICS",
|
||||||
"import_ICS": "Importa des d'un ICS",
|
"import_ICS": "Importa des d'un ICS",
|
||||||
"import_URL": "Importa des d'una URL",
|
"import_URL": "Importa des d'una URL",
|
||||||
"saved": "S'ha desat l'activitat",
|
|
||||||
"import_description": "Pots importar activitats des d'altres instàncies o plataformes que facin servir formats estàndards (ics o h-event)",
|
|
||||||
"edit_recurrent": "Edita l'activitat periòdica:",
|
"edit_recurrent": "Edita l'activitat periòdica:",
|
||||||
"updated": "S'ha actualitzat l'activitat"
|
"updated": "S'ha actualitzat l'activitat",
|
||||||
|
"saved": "S'ha desat l'activitat",
|
||||||
|
"import_description": "Pots importar activitats des d'altres instàncies o plataformes que facin servir formats estàndards (ics o h-event)"
|
||||||
},
|
},
|
||||||
"admin": {
|
"admin": {
|
||||||
"place_description": "En el cas que un lloc és incorrecte o l'adreça ha de canviar, pots arreglar-ho.<br/>Tingues en compte que totes les activitats passades i futures associades amb aquest lloc també canviaran d'adreça.",
|
"place_description": "En el cas que un lloc és incorrecte o l'adreça ha de canviar, pots arreglar-ho.<br/>Tingues en compte que totes les activitats passades i futures associades amb aquest lloc també canviaran d'adreça.",
|
||||||
|
@ -221,11 +221,12 @@
|
||||||
"add_instance": "Afegeix una instància",
|
"add_instance": "Afegeix una instància",
|
||||||
"instance_block_confirm": "Segur que vols bloquejar la instància {instance}?",
|
"instance_block_confirm": "Segur que vols bloquejar la instància {instance}?",
|
||||||
"show_smtp_setup": "Configuració de correu",
|
"show_smtp_setup": "Configuració de correu",
|
||||||
|
"admin_email": "Correu d'admin",
|
||||||
|
"smtp_test_success": "S'ha enviat un correu de prova a {admin_email}, comprova que hagi arribat bé",
|
||||||
"smtp_hostname": "Amfitrió SMTP (hostname)",
|
"smtp_hostname": "Amfitrió SMTP (hostname)",
|
||||||
"smtp_description": "<ul><li>L'admin hauria de rebre un correu cada cop que es pengi alguna una activitat anònima (si estan activades).</li><li>L'admin hauria de rebre un correu per cada soŀlicitud de registre (si estan actives).</li><li>La usuària hauria de rebre un correu després de soŀlicitar registrar-se.</li><li>La usuària hauria de rebre un correu quan se li hagi confirmat el registre.</li><li>La usuària hauria de rebre un correu si l'admin la registra directament.</li><li>La usuària hauria de rebre un correu de restabliment de contrasenya si ho demana</li></ul>",
|
"smtp_description": "<ul><li>L'admin hauria de rebre un correu cada cop que es pengi alguna una activitat anònima (si estan activades).</li><li>L'admin hauria de rebre un correu per cada soŀlicitud de registre (si estan actives).</li><li>La usuària hauria de rebre un correu després de soŀlicitar registrar-se.</li><li>La usuària hauria de rebre un correu quan se li hagi confirmat el registre.</li><li>La usuària hauria de rebre un correu si l'admin la registra directament.</li><li>La usuària hauria de rebre un correu de restabliment de contrasenya si ho demana</li></ul>",
|
||||||
"smtp_test_success": "S'ha enviat un correu de prova a {admin_email}, comprova que hagi arribat bé",
|
|
||||||
"smtp_test_button": "Envia un correu de prova",
|
"smtp_test_button": "Envia un correu de prova",
|
||||||
"admin_email": "Correu d'admin"
|
"widget": "Giny"
|
||||||
},
|
},
|
||||||
"auth": {
|
"auth": {
|
||||||
"not_confirmed": "Encara no s'ha confirmat…",
|
"not_confirmed": "Encara no s'ha confirmat…",
|
||||||
|
@ -270,7 +271,6 @@
|
||||||
},
|
},
|
||||||
"setup": {
|
"setup": {
|
||||||
"completed": "S'ha completat la configuració inicial",
|
"completed": "S'ha completat la configuració inicial",
|
||||||
"check_db": "Comprova la BD",
|
|
||||||
"completed_description": "<p>Ara ja pots entrar amb aquesta usuària:<br/><br/>Nom: <b>{email}</b><br/>Contrasenya: <b>{password}<b/></p>",
|
"completed_description": "<p>Ara ja pots entrar amb aquesta usuària:<br/><br/>Nom: <b>{email}</b><br/>Contrasenya: <b>{password}<b/></p>",
|
||||||
"start": "Comença"
|
"start": "Comença"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"register": {
|
"register": {
|
||||||
"subject": "Hem rebut una soŀlicitud de registre",
|
"subject": "Hem rebut una soŀlicitud de registre",
|
||||||
"content": "Hem rebut una soŀlicitud de registre. Hi respondrem tan aviat com ens sigui possible."
|
"content": "Hem rebut una soŀlicitud de registre. Hi respondrem tan aviat com ens sigui possible.<br/>Salut."
|
||||||
},
|
},
|
||||||
"confirm": {
|
"confirm": {
|
||||||
"subject": "Ja pots publicar activitats",
|
"subject": "Ja pots publicar activitats",
|
||||||
|
@ -9,7 +9,7 @@
|
||||||
},
|
},
|
||||||
"user_confirm": {
|
"user_confirm": {
|
||||||
"subject": "Ja pots publicar activitats",
|
"subject": "Ja pots publicar activitats",
|
||||||
"content": "Hola, que creat un compte a <a href='{{config.baseurl}}'>{{config.title}}</a>? Si és així, <a href='{{config.baseurl}}/user_confirm/{{user.recover_code}}'>confirma-ho i tria una contrasenya</a>, si us plau."
|
"content": "Hola, que has creat un compte a <a href='{{config.baseurl}}'>{{config.title}}</a>? Si és així, <a href='{{config.baseurl}}/user_confirm/{{user.recover_code}}'>confirma-ho i tria una contrasenya</a>, si us plau."
|
||||||
},
|
},
|
||||||
"recover": {
|
"recover": {
|
||||||
"subject": "Recupera la contrasenya",
|
"subject": "Recupera la contrasenya",
|
||||||
|
@ -20,7 +20,7 @@
|
||||||
"content": "{{user.email}} ha soŀlicitat regsitrar-se a {{config.title}}: <br/><pre>{{user.description}}</pre><br/> Respon a la soŀlicitud <a href='{{config.baseurl}}/admin'>aquí</a>."
|
"content": "{{user.email}} ha soŀlicitat regsitrar-se a {{config.title}}: <br/><pre>{{user.description}}</pre><br/> Respon a la soŀlicitud <a href='{{config.baseurl}}/admin'>aquí</a>."
|
||||||
},
|
},
|
||||||
"event_confirm": {
|
"event_confirm": {
|
||||||
"content": "Pots acceptar aquesta activitat a <a href='{{url}}'>la pàgina de confirmació</a>"
|
"content": "Pots confirmar aquesta activitat a <a href='{{url}}'>la pàgina de confirmació</a>"
|
||||||
},
|
},
|
||||||
"test": {
|
"test": {
|
||||||
"subject": "La configuració SMTP funciona",
|
"subject": "La configuració SMTP funciona",
|
||||||
|
|
|
@ -18,5 +18,12 @@
|
||||||
"admin_register": {
|
"admin_register": {
|
||||||
"subject": "Izen-emate berria",
|
"subject": "Izen-emate berria",
|
||||||
"content": "{{user.email}}-(e)k {{config.title}}-n izena ematea eskatu du: <br/><pre>{{user.description}}</pre><br/> Baieztatu ezazu horrela dela <a href='{{config.baseurl}}/admin'>hemen</a>."
|
"content": "{{user.email}}-(e)k {{config.title}}-n izena ematea eskatu du: <br/><pre>{{user.description}}</pre><br/> Baieztatu ezazu horrela dela <a href='{{config.baseurl}}/admin'>hemen</a>."
|
||||||
|
},
|
||||||
|
"event_confirm": {
|
||||||
|
"content": "Ekitaldi hau <a href='{{url}}'>hemen</a> baieztatu dezakezu"
|
||||||
|
},
|
||||||
|
"test": {
|
||||||
|
"subject": "Zure SMTP konfigurazioa badabil",
|
||||||
|
"content": "Hau probako eposta bat da, mezu hau irakurtzen ari bazara konfigurazioa badabilela esan nahi du."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
29
locales/email/gl.json
Normal file
29
locales/email/gl.json
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
{
|
||||||
|
"register": {
|
||||||
|
"subject": "Recibida solicitude de rexistro",
|
||||||
|
"content": "Recibimos unha solicitude de rexistro. Confirmarémola o antes posible."
|
||||||
|
},
|
||||||
|
"recover": {
|
||||||
|
"subject": "Recuperación do contrasinal",
|
||||||
|
"content": "Ola, pediches recuperar o contrasinal en {{config.title}}. <a href='{{config.baseurl}}/recover/{{user.recover_code}}'>Preme aquí</a> para confirmalo."
|
||||||
|
},
|
||||||
|
"admin_register": {
|
||||||
|
"content": "{{user.email}} solicitou rexistrarse en {{config.title}}: <br/><pre>{{user.description}}</pre><br/> Confírmao <a href='{{config.baseurl}}/admin'>aquí</a>.",
|
||||||
|
"subject": "Nova conta"
|
||||||
|
},
|
||||||
|
"test": {
|
||||||
|
"subject": "A configuración SMTP é correcta",
|
||||||
|
"content": "Este é un email de proba, se estás lendo isto é que a configuración funciona."
|
||||||
|
},
|
||||||
|
"confirm": {
|
||||||
|
"subject": "Xa podes comezar a publicar eventos",
|
||||||
|
"content": "Ola, a túa conta en <a href='{{config.baseurl}}'>{{config.title}}</a> foi confirmada. Escríbenos a {{config.admin_email}} se precisas máis información."
|
||||||
|
},
|
||||||
|
"user_confirm": {
|
||||||
|
"content": "Ola, creouse a túa conta en <a href='{{config.baseurl}}'>{{config.title}}</a>. <a href='{{config.baseurl}}/user_confirm/{{user.recover_code}}'>Debes confirmala e elexir un contrasinal</a>.",
|
||||||
|
"subject": "Xa podes comezar a publicar eventos"
|
||||||
|
},
|
||||||
|
"event_confirm": {
|
||||||
|
"content": "Podes confirmar este evento <a href='{{url}}'>nesta páxina</a>"
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,5 +21,9 @@
|
||||||
},
|
},
|
||||||
"event_confirm": {
|
"event_confirm": {
|
||||||
"content": "Puoi confermare questo evento premendo il tasto conferma in <a href='{{url}}'>questa pagina</a>"
|
"content": "Puoi confermare questo evento premendo il tasto conferma in <a href='{{url}}'>questa pagina</a>"
|
||||||
|
},
|
||||||
|
"test": {
|
||||||
|
"subject": "La tua configurazione SMTP sta funzionando",
|
||||||
|
"content": "Questa è una email di test, se la stai leggendo significa che la tua configurazione sta funzionando."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -109,7 +109,7 @@
|
||||||
"list_description": "If you have a website and want to show a list of events, use the following code"
|
"list_description": "If you have a website and want to show a list of events, use the following code"
|
||||||
},
|
},
|
||||||
"register": {
|
"register": {
|
||||||
"description": "Social movements should organize and self-finance.<br/>\n<br/>Before you can publish, <strong> the account must be approved</strong>, consider that <strong> behind this site you will find real people, so write two lines to let us know what events you would like to publish.",
|
"description": "Social movements should organize and self-finance.<br/>\n<br/>Before you can publish, <strong> the account must be approved</strong>, consider that <strong> behind this site you will find real people</strong>, so write two lines to let us know what events you would like to publish.",
|
||||||
"error": "Error: ",
|
"error": "Error: ",
|
||||||
"complete": "Registration has to be confirmed.",
|
"complete": "Registration has to be confirmed.",
|
||||||
"first_user": "Administrator created"
|
"first_user": "Administrator created"
|
||||||
|
@ -225,8 +225,8 @@
|
||||||
"smtp_description": "<ul><li>Admin should receive an email when anon event is added (if enabled).</li><li>Admin should receive email of registration request (if enabled).</li><li>User should receive an email of registration request.</li><li>User should receive email of confirmed registration.</li><li>User should receive a confirmation email when subscribed directly by admin.</li><li>Users should receive email to restore password when they forgot it</li></ul>",
|
"smtp_description": "<ul><li>Admin should receive an email when anon event is added (if enabled).</li><li>Admin should receive email of registration request (if enabled).</li><li>User should receive an email of registration request.</li><li>User should receive email of confirmed registration.</li><li>User should receive a confirmation email when subscribed directly by admin.</li><li>Users should receive email to restore password when they forgot it</li></ul>",
|
||||||
"smtp_test_success": "A test email is sent to {admin_email}, please check your inbox",
|
"smtp_test_success": "A test email is sent to {admin_email}, please check your inbox",
|
||||||
"smtp_test_button": "Send a test email",
|
"smtp_test_button": "Send a test email",
|
||||||
"admin_email": "Admin e-mail"
|
"admin_email": "Admin e-mail",
|
||||||
|
"widget": "Widget"
|
||||||
},
|
},
|
||||||
"auth": {
|
"auth": {
|
||||||
"not_confirmed": "Not confirmed yet…",
|
"not_confirmed": "Not confirmed yet…",
|
||||||
|
@ -272,6 +272,7 @@
|
||||||
"setup": {
|
"setup": {
|
||||||
"completed": "Setup completed",
|
"completed": "Setup completed",
|
||||||
"completed_description": "<p>You can now login with the following user:<br/><br/>User: <b>{email}</b><br/>Password: <b>{password}<b/></p>",
|
"completed_description": "<p>You can now login with the following user:<br/><br/>User: <b>{email}</b><br/>Password: <b>{password}<b/></p>",
|
||||||
|
"copy_password_dialog": "Yes, you have to copy the password!",
|
||||||
"start": "Start"
|
"start": "Start"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
export default {
|
export default {
|
||||||
it: 'Italiano',
|
ca: 'Català',
|
||||||
|
de: 'Deutsch',
|
||||||
en: 'English',
|
en: 'English',
|
||||||
es: 'Español',
|
es: 'Español',
|
||||||
ca: 'Català',
|
|
||||||
pl: 'Polski',
|
|
||||||
eu: 'Euskara',
|
eu: 'Euskara',
|
||||||
nb: 'Norwegian Bokmål',
|
|
||||||
fr: 'Francais',
|
fr: 'Francais',
|
||||||
de: 'Deutsch'
|
gl: 'Galego',
|
||||||
|
it: 'Italiano',
|
||||||
|
nb: 'Norwegian Bokmål',
|
||||||
|
pl: 'Polski',
|
||||||
}
|
}
|
||||||
|
|
261
locales/eu.json
261
locales/eu.json
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"common": {
|
"common": {
|
||||||
"add_event": "Ekitaldia sortu",
|
"add_event": "Sortu ekitaldia",
|
||||||
"next": "Jarraitu",
|
"next": "Jarraitu",
|
||||||
"export": "Esportatu",
|
"export": "Esportatu",
|
||||||
"send": "Bidali",
|
"send": "Bidali",
|
||||||
|
@ -10,58 +10,58 @@
|
||||||
"what": "Zer",
|
"what": "Zer",
|
||||||
"media": "Media",
|
"media": "Media",
|
||||||
"login": "Saioa hasi",
|
"login": "Saioa hasi",
|
||||||
"email": "Email",
|
"email": "Eposta",
|
||||||
"password": "Pasahitza",
|
"password": "Pasahitza",
|
||||||
"register": "Izena eman",
|
"register": "Izena eman",
|
||||||
"description": "Deskribapena",
|
"description": "Deskribapena",
|
||||||
"remove": "Ezabatu",
|
"remove": "Ezabatu",
|
||||||
"hide": "Ezkutatu",
|
"hide": "Ezkutatu",
|
||||||
"search": "Bilatu",
|
"search": "Bilatu",
|
||||||
"edit": "Aldatu",
|
"edit": "Editatu",
|
||||||
"info": "Informazioa",
|
"info": "Informazioa",
|
||||||
"confirm": "Baieztatu",
|
"confirm": "Baieztatu",
|
||||||
"admin": "Kudeatu",
|
"admin": "Administratu",
|
||||||
"users": "Erabiltzaileak",
|
"users": "Erabiltzaileak",
|
||||||
"events": "Ekitaldiak",
|
"events": "Ekitaldiak",
|
||||||
"places": "Lekuak",
|
"places": "Lekuak",
|
||||||
"settings": "Ezarpenak",
|
"settings": "Aukerak",
|
||||||
"actions": "Ekintzak",
|
"actions": "Ekintzak",
|
||||||
"deactivate": "Desaktibatu",
|
"deactivate": "Desaktibatu",
|
||||||
"remove_admin": "Administratzailea kanporatu",
|
"remove_admin": "Kendu administratzaile baimena",
|
||||||
"activate": "Aktibatu",
|
"activate": "Aktibatu",
|
||||||
"save": "Gorde",
|
"save": "Gorde",
|
||||||
"preview": "Aurrebista",
|
"preview": "Aurrebista",
|
||||||
"logout": "Saioa amaitu",
|
"logout": "Itxi saioa",
|
||||||
"share": "Partekatu",
|
"share": "Partekatu",
|
||||||
"name": "Izena",
|
"name": "Izena",
|
||||||
"associate": "Elkartu",
|
"associate": "Elkartu",
|
||||||
"edit_event": "Ekitaldia aldatu",
|
"edit_event": "Editatu ekitaldia",
|
||||||
"related": "Erlazionatuta",
|
"related": "Erlazionatuta",
|
||||||
"add": "Gehitu",
|
"add": "Gehitu",
|
||||||
"logout_ok": "Saioa ondo itxi da",
|
"logout_ok": "Saioa ondo itxi da",
|
||||||
"copy": "Kopiatu",
|
"copy": "Kopiatu",
|
||||||
"recover_password": "Pasahitza berreskuratu",
|
"recover_password": "Berreskuratu pasahitza",
|
||||||
"new_password": "Pasahitz berria",
|
"new_password": "Pasahitz berria",
|
||||||
"new_user": "Erabiltzaile berria",
|
"new_user": "Erabiltzaile berria",
|
||||||
"ok": "Ados",
|
"ok": "Ados",
|
||||||
"cancel": "Ezeztatzea",
|
"cancel": "Utzi",
|
||||||
"enable": "Gaitu",
|
"enable": "Gaitu",
|
||||||
"disable": "Desgaitu",
|
"disable": "Desgaitu",
|
||||||
"me": "Zu",
|
"me": "Zu",
|
||||||
"password_updated": "Pasahitza eguneratuta.",
|
"password_updated": "Pasahitza aldatu da.",
|
||||||
"activate_user": "Egiaztatuta",
|
"activate_user": "Baieztatuta",
|
||||||
"displayname": "Erakutsitako izena",
|
"displayname": "Erakutsitako izena",
|
||||||
"federation": "Federazioa",
|
"federation": "Federazioa",
|
||||||
"set_password": "Pasahitza ezarri",
|
"set_password": "Ezarri pasahitza",
|
||||||
"copy_link": "Lotura kopiatu",
|
"copy_link": "Kopiatu esteka",
|
||||||
"send_via_mail": "Posta elektronikoa bidali",
|
"send_via_mail": "Bidali eposta",
|
||||||
"add_to_calendar": "Egutegira gehitu",
|
"add_to_calendar": "Gehitu egutegira",
|
||||||
"instances": "Instantziak",
|
"instances": "Instantziak",
|
||||||
"copied": "Kopiatuta",
|
"copied": "Kopiatuta",
|
||||||
"embed": "Txertatuta",
|
"embed": "Kapsulatu",
|
||||||
"embed_title": "Txertatu ekitaldi hau zure web-gunean",
|
"embed_title": "Kapsulatu ekitaldi hau zure webgunean",
|
||||||
"embed_help": "Hurrengo kodea zure web-orrian kopiatuz gero ekitaldia txertatuko da hemen ikusten den bezala",
|
"embed_help": "Kode hau zure webgunean kopiatu eta ekitaldia bertan honela ikusiko da",
|
||||||
"feed": "RSS Jarioa",
|
"feed": "RSS jarioa",
|
||||||
"feed_url_copied": "Erabili RSS jarioaren esteka zure RSS jario irakurgailuan",
|
"feed_url_copied": "Erabili RSS jarioaren esteka zure RSS jario irakurgailuan",
|
||||||
"follow_me_title": "Fedibertsoko eguneraketak jarraitu",
|
"follow_me_title": "Fedibertsoko eguneraketak jarraitu",
|
||||||
"follow": "Jarraitu",
|
"follow": "Jarraitu",
|
||||||
|
@ -78,65 +78,65 @@
|
||||||
"fediverse": "Fedibertsoa",
|
"fediverse": "Fedibertsoa",
|
||||||
"skip": "Saltatu",
|
"skip": "Saltatu",
|
||||||
"delete": "Ezabatu",
|
"delete": "Ezabatu",
|
||||||
"announcements": "Iragarkiak",
|
"announcements": "Iragarpenak",
|
||||||
"url": "URL esteka",
|
"url": "URLa",
|
||||||
"place": "Lekua",
|
"place": "Lekua",
|
||||||
"label": "Etiketa",
|
"label": "Izena",
|
||||||
"max_events": "Max zenbakidun gertaerak",
|
"max_events": "Gehienezko ekitaldi kopurua",
|
||||||
"import": "Inportatu",
|
"import": "Inportatu",
|
||||||
"reset": "Zeroan jarri",
|
"reset": "Berrezarri",
|
||||||
"theme": "Gai",
|
"theme": "Itxura",
|
||||||
"tags": "Tags"
|
"tags": "Etiketak"
|
||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"description": "Saioa hasiz gero, ekitaldi berriak sortu ahal izango dituzu.",
|
"description": "Saioa hasten baduzu ekitaldi berriak sortu ahal izango dituzu.",
|
||||||
"check_email": "Begiratu zure postontzi elektronikoan, baita mezu baztergarrietan.",
|
"check_email": "Begiratu zure epostaren sarrera-ontzia eta zabor karpeta.",
|
||||||
"not_registered": "Ez duzu izena eman?",
|
"not_registered": "Ez duzu izenik eman?",
|
||||||
"forgot_password": "Pasahitza ahaztu duzu?",
|
"forgot_password": "Pasahitza ahaztu duzu?",
|
||||||
"error": "Ezin da saioa hasi, egiaztatu zure datuok.",
|
"error": "Ezin izan da saioa hasi. Egiaztatu datuak.",
|
||||||
"insert_email": "Sartu zure helbide elektronikoa",
|
"insert_email": "Sartu zure eposta helbidea",
|
||||||
"ok": "Saioa hasi duzu"
|
"ok": "Saioa hasi duzu"
|
||||||
},
|
},
|
||||||
"recover": {
|
"recover": {
|
||||||
"not_valid_code": "Mmmmm zerbaitek huts egin du..."
|
"not_valid_code": "Zerbaitek huts egin du."
|
||||||
},
|
},
|
||||||
"export": {
|
"export": {
|
||||||
"intro": "Kapitalismoaren plataformek edozer egingo dute erabiltzaileak eta haien datuak gordetzeko. Guk aldiz, informazioak, pertsonen antzera askeak izan behar dutela sinesten dugu. Horretarako gogoko dituzun ekitaldietaz info eguneratuak jaso ditzakezu webgune honetatik pasatzeko beharrik gabe.",
|
"intro": "Kapitalismoaren plataformek edozer egingo dute erabiltzaileak eta haien datuak gordetzeko. Guk aldiz, informazioa, pertsonak bezala, askea izan behar duela uste dugu. Horregatik hemengo ekitaldien informazio eguneratua jaso dezakezu webgune honetatik pasatu beharrik ere izan gabe.",
|
||||||
"email_description": "Interesatzen zaizkizun ekitaldiak jaso ditzakezu posta elektronikoan.",
|
"email_description": "Zure intereseko ekitaldiak epostaz jaso ditzakezu.",
|
||||||
"insert_your_address": "Sartu zure helbide elektronikoa",
|
"insert_your_address": "Sartu zure eposta helbidea",
|
||||||
"feed_description": "Eguneraketak sakelekoan edo ordenagailuan jaso nahi badituzu webgune hau bisitatu gabe, RSS jarioa erabiltzea gomendatzen dizugu.</p>\n\n<p>RSS jarioarentzat aplikazio berezi bat erabiliko duzu gogoko dituzun weguneetatik berriak jasotzeko. Oso modu egokia da gune askotako berriak erraz eta azkar jasotzeko eta ez da konturik sortu behar! </p>\n\n<li>Android baldin badaukazu <a href=\"https://play.google.com/store/apps/details?id=net.frju.flym\">Flym</a> edo Feeder gomendatzen dizugu</li>\n<li>iPhone/iPad-erako eskuragarri daukazu <a href=\"https://itunes.apple.com/ua/app/feeds4u/id1038456442?mt=8\">Feed4U</a></li>\n<li>Ordenagailuaren kasuan Feedbro iradokitzen dugu, <a href=\"https://addons.mozilla.org/en-GB/firefox/addon/feedbroreader/\">Firefoxeko</a> edo <a href=\"https://chrome.google.com/webstore/detail/feedbro/mefgmmbdailogpfhfblcnnjfmnpnmdfa\">Chromeko</a> gehigarri gisa instalatzen da eta sistema gehienetan dabil.</li>\n<br/>\nHonako esteka jario irakurgailuan sartuta, eguneraketa guztiak jasoko dituzu.",
|
"feed_description": "Eguneraketak sakelekoan edo ordenagailuan jaso nahi badituzu webgune hau bisitatu gabe, RSS jarioa erabiltzea gomendatzen dizugu.</p>\n\n<p>RSS jarioarentzat aplikazio berezi bat erabiliko duzu gogoko dituzun weguneetatik berriak jasotzeko. Oso modu egokia da gune askotako berriak erraz eta azkar jasotzeko eta ez da konturik sortu behar! </p>\n\n<li>Android baldin badaukazu <a href=\"https://play.google.com/store/apps/details?id=net.frju.flym\">Flym</a> edo Feeder gomendatzen dizugu</li>\n<li>iPhone/iPad-erako eskuragarri daukazu <a href=\"https://itunes.apple.com/ua/app/feeds4u/id1038456442?mt=8\">Feed4U</a></li>\n<li>Ordenagailuaren kasuan Feedbro iradokitzen dugu, <a href=\"https://addons.mozilla.org/en-GB/firefox/addon/feedbroreader/\">Firefoxeko</a> edo <a href=\"https://chrome.google.com/webstore/detail/feedbro/mefgmmbdailogpfhfblcnnjfmnpnmdfa\">Chromeko</a> gehigarri gisa instalatzen da eta sistema gehienetan dabil.</li>\n<br/>\nHonako esteka jario irakurgailuan sartuta, eguneraketa guztiak jasoko dituzu.",
|
||||||
"ical_description": "Normalean ordenagailuak eta smartphoneak egutegiak inportatu eta kudeatzeko aplikazioekin etorri ohi dira.",
|
"ical_description": "Ordenagailuak eta telefonoak maiz egutegiak inportatzeko eta kudeatzeko aplikazioekin datoz.",
|
||||||
"list_description": "Webgune bat baduzu eta ekitaldien zerrenda erakutsi nahi baduzu, ondorengo kodea erabili dezakezu"
|
"list_description": "Webgune bat baduzu eta ekitaldien zerrenda erakutsi nahi baduzu, ondorengo kodea erabili"
|
||||||
},
|
},
|
||||||
"register": {
|
"register": {
|
||||||
"description": "Herri mugimenduek autoantolaketaren bidean diru-iturrien beharrak dauzkatela badakigu.<br/>Honako hauxe oparitxoa da, hortaz erabili ezazue ekitaldi ez-komertzialak iragartzeko, eta esan gabe doa, ekitaldi antifaxistak, antisexistak eta antiarriztetarako :) .\n<br/>Argitaratzen hasi baino lehen<strong> zure kontu berriak onarpena jaso beharko du </strong>beraz, <strong>webgune honen atzean hezur-haragizko pertsonak gaudela jakinda </strong>, (momenutz euskal 'AI'-rik ez daukagu baina adi, agertuko direla) idatzi iezaguzu lerro batzuk argitaratu nahi dituzun ekitaldiei buruz.",
|
"description": "Herri mugimenduek autoantolaketaren bidean diru-iturrien beharrak dauzkatela badakigu.<br/>Honako hauxe oparitxoa da, hortaz erabili ezazue ekitaldi ez-komertzialak iragartzeko, eta esan gabe doa, ekitaldi antifaxistak, antisexistak eta antiarriztetarako :) .\n<br/>Argitaratzen hasi baino lehen<strong> zure kontu berriak onarpena jaso beharko du </strong>beraz, <strong>webgune honen atzean hezur-haragizko pertsonak gaudela jakinda </strong>, (momenutz euskal 'AI'-rik ez daukagu baina adi, agertuko direla) idatzi iezaguzu lerro batzuk argitaratu nahi dituzun ekitaldiei buruz.",
|
||||||
"error": "Hutsa: ",
|
"error": "Errorea: ",
|
||||||
"complete": "Izen-ematea baieztatu behar dute.",
|
"complete": "Izen-ematea baieztatua izan behar da.",
|
||||||
"first_user": "Administratzailea sortu da"
|
"first_user": "Administratzailea sortu da"
|
||||||
},
|
},
|
||||||
"event": {
|
"event": {
|
||||||
"anon": "Ezezaguna",
|
"anon": "Anonimoa",
|
||||||
"anon_description": "Ekitaldia sortu dezakezu <a href='/login'>saioa hasi</a> edo <a href='/register'>izena eman</a> gabe,\nbaina kasu honetan norbaitek egiaztatu beharko du ekitaldia gune honetarako egokia dela eta itxaron beharko duzu. Gainera, behin egiaztatuta hura aldatzea ez da posiblea izango.<br/><br/>\nDena den, ahalik eta azkarren erantzuten saiatuko gara. ",
|
"anon_description": "Ekitaldi bat sor dezakezu saioa hasi edo izena eman gabe, baina norbaitek berau irakurri eta\ngune honetarako egokia dela baieztatu arte itxaron beharko duzu. Ez da posible izango ekitaldia aldatzerik.<br/><br/>\nHorren ordez <a href='/login'>saioa hasi</a> edo <a href='/register'>izena eman</a> dezakezu. Bestela, jarraitu aurrera eta ahalik azkarren erantzuten saiatuko gara. ",
|
||||||
"same_day": "egun berean",
|
"same_day": "egun berean",
|
||||||
"what_description": "Ekitaldiaren izena",
|
"what_description": "Izenburua",
|
||||||
"description_description": "Ekitaldiaren azalpena",
|
"description_description": "Deskripzioa",
|
||||||
"tag_description": "Etiketak (tag)...",
|
"tag_description": "Etiketa",
|
||||||
"media_description": "Eskuorria edo irudia gehitu dezakezu (aukerakoa)",
|
"media_description": "Irudi bat gehi dezakezu (hautazkoa)",
|
||||||
"added": "Ekitaldia sortu da",
|
"added": "Ekitaldia gehitu da",
|
||||||
"added_anon": "Ekitaldia sortu da, baina baieztatzear dago.",
|
"added_anon": "Ekitaldia gehitu da, baina baieztatzeko zain dago.",
|
||||||
"where_description": "Non da ekitaldia? Lekua ez bada zerrendan agertzen idatzi ezazu eta <b>enter sakatu</b>.",
|
"where_description": "Non da ekitaldia? Agertzen ez bada zuk sor dezakezu.",
|
||||||
"confirmed": "Ekitaldia egiaztatu da",
|
"confirmed": "Ekitaldia egiaztatu da",
|
||||||
"not_found": "Ezin da ekitaldia aurkitu",
|
"not_found": "Ezin da ekitaldia aurkitu",
|
||||||
"remove_confirmation": "Ziur zaude ekitaldi hau ezabatu nahi duzula?",
|
"remove_confirmation": "Ziur zaude ekitaldi hau ezabatu nahi duzula?",
|
||||||
"remove_recurrent_confirmation": "Ziur zaude ekitaldi errepikari hau ezabatu nahi duzula?\nIragan diren ekitaldiak mantenduko dira, baina ez da ekitaldi berririk sortuko.",
|
"remove_recurrent_confirmation": "Ziur zaude ekitaldi errepikari hau ezabatu nahi duzula?\nIragandako ekitaldiak mantenduko dira, baina ez da ekitaldi berririk sortuko.",
|
||||||
"recurrent": "Errepikaria",
|
"recurrent": "Errepikaria",
|
||||||
"show_recurrent": "Ekitaldi errepikariak",
|
"show_recurrent": "ekitaldi errepikariak",
|
||||||
"show_past": "Erakutsi iraganeko ekitaldiak",
|
"show_past": "iragandako ekitaldiak ere",
|
||||||
"recurrent_description": "Aukera ezazu maiztasuna eta hautatu egunak",
|
"recurrent_description": "Aukera maiztasuna eta hautatu egunak",
|
||||||
"multidate_description": "Egun bat baino gehiagoko jaialdia da? Aukeratu noiz hasten den eta noiz amaitzen den",
|
"multidate_description": "Jaialdi bat da? Aukeratu noiz hasten eta amaitzen den",
|
||||||
"multidate": "Egun gehiagotan",
|
"multidate": "Egun gehiagokoa",
|
||||||
"normal": "Egunekoa",
|
"normal": "Normala",
|
||||||
"normal_description": "Eguna aukeratu.",
|
"normal_description": "Aukeratu eguna.",
|
||||||
"recurrent_1w_days": "{days}(e)ro",
|
"recurrent_1w_days": "{days}(e)ro",
|
||||||
"recurrent_2w_days": "Bi {days}(e)z behin",
|
"recurrent_2w_days": "Bi {days}(e)z behin",
|
||||||
"recurrent_1m_days": "|Hilabetero {days}etan|Hilabetero {days}etan",
|
"recurrent_1m_days": "|Hilabetero {days}etan|Hilabetero {days}etan",
|
||||||
|
@ -148,118 +148,129 @@
|
||||||
"each_month": "Hilero",
|
"each_month": "Hilero",
|
||||||
"due": "Amaiera ordua",
|
"due": "Amaiera ordua",
|
||||||
"from": "Hasiera ordua",
|
"from": "Hasiera ordua",
|
||||||
"image_too_big": "Irudia handiegia omen da (4mb gehienez)",
|
"image_too_big": "Irudia ezin da 4 MB baino handiagoa izan",
|
||||||
"interact_with_me": "Elkar gaitezen fedibertsoan",
|
"interact_with_me": "Jarrai nazazu",
|
||||||
"follow_me_description": "{title}n argitaratutako ekitaldien berri izateko aukeren artean,\n fedibertsoko <u>{account}</u> kontuari jarraitzea daukazu. Horretarako Mastodon erabili dezakezu, eta bertatik baliabideak gehitu ekitaldi baten.<br/><br/>\n Mastodon eta Fedibertsoa zer diren ez badakizu <a href='https://es.wikipedia.org/wiki/Fediverso'>artikulu hau</a> irakurtzea iradokitzen dizugu.<br/><br/> Sartu zure instantzia behean (adibidez mastodon.eus edo mastodon.jalgi.eus)",
|
"follow_me_description": "{title}(e)n argitaratutako ekitaldien berri izateko aukeren artean,\nfedibertsoko <u>{account}</u> kontua jarraitzea daukazu. Horretarako, Mastodon erabil dezakezu eta bertatik ekitaldi bati baliabideak gehitu.<br/><br/>\nMastodon eta fedibertsoa zer diren ez badakizu <a href='https://eu.wikipedia.org/wiki/Fedibertso'>artikulu hau</a> irakurtzea gomendatzen dizugu.<br/><br/>Sartu zure instantzia behean (adibidez, mastodon.eus edo mastodon.jalgi.eus)",
|
||||||
"import_description": "Beste plataforma eta adibide batzuetako gertaerak formatu estandarren bidez inportatu ditzakezu (ics eta h-event)",
|
"import_description": "Beste plataforma eta instantzietatik inportatu ditzakezu ekitaldiak formatu estandarrak erabiliz (ics eta h-event)",
|
||||||
"ics": "ICS",
|
"ics": "ICS",
|
||||||
"import_ICS": "ICS-ko inportazioa",
|
"import_ICS": "ICS-tik inportatu",
|
||||||
"import_URL": "URL-ko inportazioa",
|
"import_URL": "URL-tik inportatu",
|
||||||
"interact_with_me_at": "Hitz egin nirekin fediversoan",
|
"interact_with_me_at": "Mintzatu nirekin fedibertsoko leku honetan:",
|
||||||
"only_future": "gertakizunak besterik ez",
|
"only_future": "datozen ekitaldiak bakarrik",
|
||||||
"edit_recurrent": "Gertaera errepikakorra:",
|
"edit_recurrent": "Editatu ekitaldi errepikaria:",
|
||||||
"updated": "Gertaera eguneratua",
|
"updated": "Ekitaldia eguneratu da",
|
||||||
"saved": "Gertaera salbatua"
|
"saved": "Ekitaldia gorde da"
|
||||||
},
|
},
|
||||||
"admin": {
|
"admin": {
|
||||||
"place_description": "Lekuaren zehaztapenak aldatu ditzakezu, bai gaizki idatzita dagoelako, bai helbidez aldatu delako.<br/> Ondorioz, leku horrekin lotutako ekitaldi guztiak helbidez aldatuko direla kontuan hartu behar da (baita iraganekoak ere!)",
|
"place_description": "Helbidea oker badago, alda dezakezu.<br/>Leku honekin lotutako iraganeko eta etorkizuneko ekitaldien helbidea aldatuko da.",
|
||||||
"event_confirm_description": "Erabiltzaile ezezagunek sortutako ekitaldiak hemen egiaztatu ditzakezu",
|
"event_confirm_description": "Erabiltzaile anonimoek sortutako ekitaldiak hemen baieztatu ditzakezu",
|
||||||
"delete_user": "Erabiltzailea ezabatu",
|
"delete_user": "Ezabatu",
|
||||||
"remove_admin": "Administratzailea ezabatu",
|
"remove_admin": "Kendu administratzaile baimena",
|
||||||
"delete_user_confirm": "Ziur zaude {user} ezabatu nahi duzula?",
|
"delete_user_confirm": "Ziur al zaude {user} ezabatu nahi duzula?",
|
||||||
"user_remove_ok": "Erabiltzailea ezabatu da",
|
"user_remove_ok": "Erabiltzailea ezabatu da",
|
||||||
"user_create_ok": "Erabiltzailea sortu da",
|
"user_create_ok": "Erabiltzailea sortu da",
|
||||||
"allow_registration_description": "Izen-emateak ahalbidetu nahi dituzu?",
|
"allow_registration_description": "Izen-emateak ahalbidetu nahi dituzu?",
|
||||||
"allow_anon_event": "Ezezagunek ekitaldiak sortzea ahalbidetu nahi duzu? (Beti ere baieztapenarekin)",
|
"allow_anon_event": "Anonimoek ekitaldiak sortzea ahalbidetu (baieztatu ondoren)?",
|
||||||
"allow_recurrent_event": "Ekitaldi errepikariak ahalbidetu",
|
"allow_recurrent_event": "Ekitaldi errepikariak ahalbidetu",
|
||||||
"recurrent_event_visible": "Erakutsi ekitaldi errepikariak modu lehenetsian",
|
"recurrent_event_visible": "Erakutsi ekitaldi errepikariak modu lehenetsian",
|
||||||
"federation": "Federazioa / ActivityPub",
|
"federation": "Federazioa / ActivityPub",
|
||||||
"enable_federation": "Federatzea gaitu",
|
"enable_federation": "Federazioa gaitu",
|
||||||
"enable_federation_help": "Instantzia hau fedibertsoan jarraitzea gaituko duzu",
|
"enable_federation_help": "Instantzia hau fedibertsotik jarraitzea posible izango da",
|
||||||
"select_instance_timezone": "Ordu-eremua",
|
"select_instance_timezone": "Ordu-eremua",
|
||||||
"instance_timezone_description": "Gancio hiri baten moduko lekuen ekitaldiak biltzeko diseinatuta dago. Leku honen ordu-eremua hautatuz gero ekitaldi gutziek ordu-eremu horrekiko adieraziko dira..",
|
"instance_timezone_description": "Gancio gune bateko ekitaldiak biltzeko diseinatuta dago, adibidez hiri batekoak. Hemengo ekitaldi guztiak aukeratzen duzun ordu-eremuan erakutsiko dira.",
|
||||||
"enable_resources": "Baliabideak gaitu",
|
"enable_resources": "Baliabideak gaitu",
|
||||||
"enable_resources_help": "Fedibertsotik ekitaldietan baliabideak gehitzea ahalbidetzen du",
|
"enable_resources_help": "Fedibertsotik ekitaldiei baliabideak gehitzea ahalbidetzen du",
|
||||||
"hide_boost_bookmark": "Bultzadak eta laster-markak ezkutatu",
|
"hide_boost_bookmark": "Bultzadak eta laster-markak ezkutatzen ditu",
|
||||||
"hide_boost_bookmark_help": "Fedibertsotik datozen bultzaden eta laster-marken ikonotxoak ezkutatzen ditu",
|
"hide_boost_bookmark_help": "Fedibertsotik datozen bultzada eta laster-marka kopuruaren ikonotxoak ezkutatzen ditu",
|
||||||
"block": "Blokeatu",
|
"block": "Blokeatu",
|
||||||
"unblock": "Desblokeatu",
|
"unblock": "Desblokeatu",
|
||||||
"user_add_help": "Mezu elektroniko bat bidaliko diogu erabiltzaile berriari harpidetza baieztatzeko eta pasahitz bat aukeratzeko argibideekin",
|
"user_add_help": "Eposta bat bidaliko diogu erabiltzaile berriari harpidetza baieztatzeko eta pasahitz bat aukeratzeko argibideekin",
|
||||||
"resources": "Baliabideak",
|
"resources": "Baliabideak",
|
||||||
"hide_resource": "Baliabidea ezkutatu",
|
"hide_resource": "Ezkutatu baliabidea",
|
||||||
"show_resource": "Baliabidea erakutsi",
|
"show_resource": "Erakutsi baliabidea",
|
||||||
"delete_resource": "Baliabidea ezabatu",
|
"delete_resource": "Ezabatu baliabidea",
|
||||||
"delete_resource_confirm": "Ziur zaude baliabide hau ezabatu nahi duzula?",
|
"delete_resource_confirm": "Ziur zaude baliabide hau ezabatu nahi duzula?",
|
||||||
"block_user": "Erabiltzailea blokeatu",
|
"block_user": "Blokeatu erabiltzailea",
|
||||||
"user_blocked": "{user} erabiltzailea blokeatuta dago",
|
"user_blocked": "{user} erabiltzailea blokeatuta dago",
|
||||||
"filter_instances": "Instantziak iragazi",
|
"filter_instances": "Iragazi instantziak",
|
||||||
"filter_users": "Erabiltzaileak iragazi",
|
"filter_users": "Iragazi erabiltzaileak",
|
||||||
"instance_name": "Instantziaren izena",
|
"instance_name": "Instantziaren izena",
|
||||||
"favicon": "Iruditxoa",
|
"favicon": "Logoa",
|
||||||
"user_block_confirm": "Ziur zaude {user} blokeatu nahi duzula?",
|
"user_block_confirm": "Ziur al zaude {user} blokeatu nahi duzula?",
|
||||||
"delete_announcement_confirm": "Ziur zaude iragarkia ezabatu nahi duzula?",
|
"delete_announcement_confirm": "Ziur al zaude iragarpena ezabatu nahi duzula?",
|
||||||
"announcement_remove_ok": "Iragarkia ezabatu da",
|
"announcement_remove_ok": "Iragarpena ezabatu da",
|
||||||
"announcement_description": "Atal honetan iragarkiak txertatu ditzakezu hasiera-orrian ager daitezen",
|
"announcement_description": "Atal honetan iragarpenak txertatu ditzakezu hasiera-orrialdean ager daitezen",
|
||||||
"instance_locale": "Instantziaren hizkuntza lehenetsia",
|
"instance_locale": "Hizkuntza lehenetsia",
|
||||||
"instance_locale_description": "Orriak erakusteko erabilitako hizkuntza erabiltzaileak nahiago duen hizkuntza da. Hala ere, kasu batzuetan mezuak modu berean erakutsi behar ditugu guztiontzat (adibidez ActivityPub-etik argitaratzen dugunean edo posta elektroniko batzuk bidaltzerakoan). Kasu hauetan goian hautatutako hizkuntza erabiliko dugu.",
|
"instance_locale_description": "Orrialdeak erabiltzailearen gogoko hizkuntzan bistaratzen dira. Batzuetan mezuak jende guztiari hizkuntza berean bistaratu beharra dago (adibidez, ActivityPub bidez argitaratzen dugunean edo eposta batzuetan). Kasu hauetan goian hautatutako hizkuntza erabiliko dugu.",
|
||||||
"instance_place": "Instantziaren kokalekua",
|
"instance_place": "Instantziaren kokalekua adierazten du",
|
||||||
"title_description": "Orriaren izenburuan, jario eta ics-en esportazioan eta mezu elektronikoen gaian erabiliko da.",
|
"title_description": "Orrialdearen izenburuan, ICS jarioetan eta RSS-ak esportatzeko epostaren gaian erabiltzen da.",
|
||||||
"description_description": "Orriburuan agertuko da, izenburuarekin batera",
|
"description_description": "Goiburuan agertzen da izenburutik gertu",
|
||||||
"instance_name_help": "Instantziaren kontua ActivityPub-en",
|
"instance_name_help": "Jarraitu beharreko ActivityPub kontua",
|
||||||
"enable_trusted_instances": "Kideko instantziak gaitu",
|
"enable_trusted_instances": "Gaitu instantzia adiskidetsuak",
|
||||||
"trusted_instances_help": "Kideko instantzien zerrenda orri-buruan agertuko dira",
|
"trusted_instances_help": "Kideak diren instantzien zerrenda goiburuan erakutsiko da",
|
||||||
"add_trusted_instance": "Gehitu kideko instantzia bat",
|
"add_trusted_instance": "Gehitu kide den instantzia bat",
|
||||||
"instance_place_help": "Beste instantzien zerrendetan agertuko den izena",
|
"instance_place_help": "Beste instantzietan agertuko den izena",
|
||||||
"delete_trusted_instance_confirm": "Ziur zaude kideko instantzia hau zerrendatik ezabatu nahi duzula?",
|
"delete_trusted_instance_confirm": "Ziur al zaude instantzia hau menuko zerrendatik kendu nahi duzula?",
|
||||||
"new_announcement": "Iragarpen berria",
|
"new_announcement": "Iragarpen berria",
|
||||||
"edit_place": "Leku ederrean",
|
"edit_place": "Editatu lekua",
|
||||||
"delete_footer_link_confirm": "Ziur lotura kenduko duzula?",
|
"delete_footer_link_confirm": "Ziur al zaude esteka hau kendu nahi duzula?",
|
||||||
"footer_links": "Oinezkoen konexioak",
|
"footer_links": "Oineko estekak",
|
||||||
"add_link": "Gehitu lotura",
|
"add_link": "Gehitu esteka",
|
||||||
"is_dark": "Gai iluna",
|
"is_dark": "Itxura iluna",
|
||||||
"instance_block_confirm": "Ziur al zaude blokearen adibidea {instance} nahi duzula?",
|
"instance_block_confirm": "Ziur al zaude {instance} instantzia blokeatu nahi duzula?",
|
||||||
"add_instance": "Gehitu adibidea",
|
"add_instance": "Gehitu instantzia",
|
||||||
"disable_user_confirm": "Ziur zaude {user} deskonektatu nahi duzula?"
|
"disable_user_confirm": "Ziur zaude {user} deskonektatu nahi duzula?",
|
||||||
|
"show_smtp_setup": "Eposta ezarpenak",
|
||||||
|
"smtp_test_button": "Bidali probako eposta bat",
|
||||||
|
"smtp_test_success": "Probako eposta bidali da {admin_email}-(e)ra, begiratu zure sarrera-ontzia",
|
||||||
|
"admin_email": "Administratzailearen eposta",
|
||||||
|
"smtp_hostname": "SMTP hostname",
|
||||||
|
"smtp_description": "<ul><li>Administratzaileak eposta bat jaso beharko luke anonimo batek ekitaldi bat gehitzen duenean (gaituta badago).</li><li>Administratzaileak eposta bat jaso beharko luke izena emateko eskari bakoitzeko (gaituta badago).</li><li>Erabiltzaileak eposta bat jaso beharko luke izena emateko eskariarekin.</li><li>Erabiltzaileak eposta bat jaso beharko luke izen ematea baieztatzean.</li><li>Erabiltzaileak eposta bat jaso beharko luke administratzaileak zuzenean izena emanez gero.</li><li>Erabiltzaileek eposta bat jaso beharko lukete pasahitza ahazten dutenean.</li></ul>"
|
||||||
},
|
},
|
||||||
"auth": {
|
"auth": {
|
||||||
"not_confirmed": "Oraindik baieztatu gabe dago…",
|
"not_confirmed": "Oraindik baieztatu gabe dago…",
|
||||||
"fail": "Saioa hasteak huts egin du! Ziur zaude datuok ondo daudela?"
|
"fail": "Ezin izan da saioa hasi. Ziur al zaude pasahitza zuzena dela?"
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"update_confirm": "Aldaketak gorde nahi duzu?",
|
"update_confirm": "Aldaketak gorde nahi duzu?",
|
||||||
"change_password": "Pasahitza aldatu",
|
"change_password": "Aldatu pasahitza",
|
||||||
"password_updated": "Pasahitza eguneratu da.",
|
"password_updated": "Pasahitza aldatu da.",
|
||||||
"danger_section": "Atal arriskutsua",
|
"danger_section": "Atal arriskutsua",
|
||||||
"remove_account": "Ondorengo botoia zapalduz gero zure erabiltzailea ezabatuko da. Argitaratutako ekitaldiak ordea, ez dira ezabatuko.",
|
"remove_account": "Ondorengo botoia sakatuz gero zure erabiltzailea ezabatuko da. Argitaratutako ekitaldiak ordea, ez dira ezabatuko.",
|
||||||
"remove_account_confirm": "Zure kontua behin betiko ezabatzear zaude"
|
"remove_account_confirm": "Zure kontua behin betiko ezabatzear zaude"
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"nick_taken": "Dagoeneko ezizen hau hartuta dago.",
|
"nick_taken": "Ezizen hau dagoeneko hartuta dago.",
|
||||||
"email_taken": "Dagoeneko posta elektroniko hau hartuta dago."
|
"email_taken": "Eposta hau dageneko hartuta dago."
|
||||||
},
|
},
|
||||||
"confirm": {
|
"confirm": {
|
||||||
"title": "Erabiltzaile-baieztapena",
|
"title": "Erabiltzaile-baieztapena",
|
||||||
"not_valid": "Mmmmm zerbaitek huts egiten du.",
|
"not_valid": "Zerbaitek huts egiten du.",
|
||||||
"valid": "Zure kontua baieztatua izan da, orain <a href=\"/login\">saioa hasi</a> dezakezu"
|
"valid": "Zure kontua baieztatua izan da, orain <a href=\"/login\">saioa hasi</a> dezakezu"
|
||||||
},
|
},
|
||||||
"ordinal": {
|
"ordinal": {
|
||||||
"1": "lehen",
|
"1": "lehen",
|
||||||
"2": "bigarren",
|
"2": "bigarren",
|
||||||
"3": "hirugarren",
|
"3": "hirugarren",
|
||||||
"4": "laugarrena",
|
"4": "laugarren",
|
||||||
"5": "bostgarrena",
|
"5": "bosgarren",
|
||||||
"-1": "azkena"
|
"-1": "azken"
|
||||||
},
|
},
|
||||||
"about": "\n <p><a href='https://gancio.org'>Gancio</a> Tokiko komunitateentzako agenda partekatua da.</p>\n ",
|
"about": "\n <p><a href='https://gancio.org'>Gancio</a> Tokiko komunitateentzako agenda partekatua da.</p>\n ",
|
||||||
"oauth": {
|
"oauth": {
|
||||||
"authorization_request": "<code>{app}</code> aplikazioak baimena eskatu du <code>{instance_name}</code>-n ondorengo lanak egiteko:",
|
"authorization_request": "<code>{app}</code> aplikazioak ondorengo baimena eskatu du <code>{instance_name}</code>(e)n:",
|
||||||
"redirected_to": "Baieztapenaren ondoren <code>{url}</code> helbidera berbideratua izango zara",
|
"redirected_to": "Baieztatu ondoren hona birbideratua izango zara: <code>{url}</code>",
|
||||||
"scopes": {
|
"scopes": {
|
||||||
"event:write": "Zure ekitaldiak sortu eta aldatu"
|
"event:write": "Sortu eta editatu zure ekitaldiak"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"validators": {
|
"validators": {
|
||||||
"email": "Sar ezazu posta elektroniko baliozko bat",
|
"email": "Sartu baliozko eposta bat",
|
||||||
"required": "{fieldName} beharrezkoa da"
|
"required": "{fieldName} beharrezkoa da"
|
||||||
|
},
|
||||||
|
"setup": {
|
||||||
|
"start": "Hasi",
|
||||||
|
"completed": "Instalazioa bukatu da",
|
||||||
|
"completed_description": "<p>Erabiltzaile honekin saioa has dezakezu orain:<br/><br/>Erabiltzailea: <b>{email}</b><br/>Pasahitza: <b>{password}<b/></p>"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -136,7 +136,7 @@
|
||||||
"saved": "Événement enregistré"
|
"saved": "Événement enregistré"
|
||||||
},
|
},
|
||||||
"register": {
|
"register": {
|
||||||
"description": "Les mouvements sociaux doivent s'organiser et s'autofinancer.<br/>\n<br/>Avant de pouvoir publier, <strong> le compte doit être approuvé</strong>, considérez que <strong> derrière ce site vous trouverez de vraies personnes, à qui vous pouvez écrire en deux lignes pour exprimer les évènements que vous souhaiteriez publier.",
|
"description": "Les mouvements sociaux doivent s'organiser et s'autofinancer.<br/>\n<br/>Avant de pouvoir publier, <strong> le compte doit être approuvé</strong>, considérez que <strong> derrière ce site vous trouverez de vraies personnes</strong>, à qui vous pouvez écrire en deux lignes pour exprimer les évènements que vous souhaiteriez publier.",
|
||||||
"first_user": "Administrateur créé",
|
"first_user": "Administrateur créé",
|
||||||
"complete": "L'inscription doit être confirmée.",
|
"complete": "L'inscription doit être confirmée.",
|
||||||
"error": "Erreur : "
|
"error": "Erreur : "
|
||||||
|
@ -213,7 +213,8 @@
|
||||||
"smtp_description": "<ul><li>L'administrateur reçoit un e-mail lorsqu'un événement anonyme est ajouté (si activé).</li><li>L'administrateur reçoit un e-mail pour chaque demande d'inscription (si activé).</li><li>L'utilisateur reçoit un e-mail suite à sa demande d'inscription.</li><li>L'utilisateur reçoit un e-mail lorsque son inscription est confirmée.</li><li>L'utilisateur reçoit un e-mail de confirmation s'il est inscrit directement par l'administrateur.</li><li>Les utilisateurs reçoivent un e-mail pour restaurer leur mot de passe s'ils l'oublient.</li></ul>",
|
"smtp_description": "<ul><li>L'administrateur reçoit un e-mail lorsqu'un événement anonyme est ajouté (si activé).</li><li>L'administrateur reçoit un e-mail pour chaque demande d'inscription (si activé).</li><li>L'utilisateur reçoit un e-mail suite à sa demande d'inscription.</li><li>L'utilisateur reçoit un e-mail lorsque son inscription est confirmée.</li><li>L'utilisateur reçoit un e-mail de confirmation s'il est inscrit directement par l'administrateur.</li><li>Les utilisateurs reçoivent un e-mail pour restaurer leur mot de passe s'ils l'oublient.</li></ul>",
|
||||||
"show_smtp_setup": "Paramètres d'e-mail",
|
"show_smtp_setup": "Paramètres d'e-mail",
|
||||||
"smtp_test_success": "Un e-mail de test a été envoyé à {admin_email}, veuillez vérifier votre boîte de réception",
|
"smtp_test_success": "Un e-mail de test a été envoyé à {admin_email}, veuillez vérifier votre boîte de réception",
|
||||||
"admin_email": "E-mail de l'administrateur"
|
"admin_email": "E-mail de l'administrateur",
|
||||||
|
"widget": "Vignette active"
|
||||||
},
|
},
|
||||||
"oauth": {
|
"oauth": {
|
||||||
"scopes": {
|
"scopes": {
|
||||||
|
|
277
locales/gl.json
Normal file
277
locales/gl.json
Normal file
|
@ -0,0 +1,277 @@
|
||||||
|
{
|
||||||
|
"common": {
|
||||||
|
"export": "Exportar",
|
||||||
|
"send": "Enviar",
|
||||||
|
"address": "Enderezo",
|
||||||
|
"when": "Cando",
|
||||||
|
"what": "Que",
|
||||||
|
"media": "Multimedia",
|
||||||
|
"login": "Acceder",
|
||||||
|
"email": "Email",
|
||||||
|
"password": "Contrasinal",
|
||||||
|
"register": "Rexistro",
|
||||||
|
"description": "Descrición",
|
||||||
|
"remove": "Eliminar",
|
||||||
|
"hide": "Agochar",
|
||||||
|
"search": "Buscar",
|
||||||
|
"edit": "Editar",
|
||||||
|
"events": "Eventos",
|
||||||
|
"enable": "Activar",
|
||||||
|
"disable": "Desactivar",
|
||||||
|
"me": "Ti",
|
||||||
|
"password_updated": "Cambiou o contrasinal.",
|
||||||
|
"resources": "Recursos",
|
||||||
|
"activate_user": "Confirmado",
|
||||||
|
"instances": "Instancias",
|
||||||
|
"copied": "Copiado",
|
||||||
|
"embed_help": "Copia o seguinte código ao teu sitio web e o evento será mostrado como aquí",
|
||||||
|
"feed_url_copied": "Abrir o URL da fonte copiado no teu lector de RSS",
|
||||||
|
"event": "Evento",
|
||||||
|
"pause": "Pausa",
|
||||||
|
"start": "Inicio",
|
||||||
|
"fediverse": "Fediverso",
|
||||||
|
"announcements": "Anuncios",
|
||||||
|
"reset": "Restablecer",
|
||||||
|
"label": "Etiqueta",
|
||||||
|
"confirm": "Confirmar",
|
||||||
|
"admin": "Admin",
|
||||||
|
"users": "Usuarias",
|
||||||
|
"displayname": "Nome público",
|
||||||
|
"activate": "Activar",
|
||||||
|
"actions": "Accións",
|
||||||
|
"title": "Título",
|
||||||
|
"n_resources": "sen recurso|un recurso|{n} recursos",
|
||||||
|
"federation": "Federación",
|
||||||
|
"place": "Lugar",
|
||||||
|
"delete": "Eliminar",
|
||||||
|
"info": "Info",
|
||||||
|
"add_event": "Engadir evento",
|
||||||
|
"associate": "Asociar",
|
||||||
|
"next": "Seguinte",
|
||||||
|
"where": "Onde",
|
||||||
|
"places": "Lugares",
|
||||||
|
"settings": "Opcións",
|
||||||
|
"deactivate": "Apagar",
|
||||||
|
"remove_admin": "Eliminar admin",
|
||||||
|
"logout_ok": "Non entraches",
|
||||||
|
"new_user": "Nova usuaria",
|
||||||
|
"ok": "Ok",
|
||||||
|
"cancel": "Cancelar",
|
||||||
|
"save": "Gardar",
|
||||||
|
"preview": "Vista previa",
|
||||||
|
"logout": "Saír",
|
||||||
|
"share": "Compartir",
|
||||||
|
"name": "Nome",
|
||||||
|
"edit_event": "Editar evento",
|
||||||
|
"copy": "Copiar",
|
||||||
|
"new_password": "Novo contrasinal",
|
||||||
|
"send_via_mail": "Enviar email",
|
||||||
|
"embed": "Incrustado",
|
||||||
|
"embed_title": "Inclúe este evento no teu sitio web",
|
||||||
|
"related": "Relacionado",
|
||||||
|
"add": "Engadir",
|
||||||
|
"recover_password": "Recuperar contrasinal",
|
||||||
|
"moderation": "Moderación",
|
||||||
|
"set_password": "Establecer contrasinal",
|
||||||
|
"copy_link": "Copiar ligazón",
|
||||||
|
"add_to_calendar": "Engadir ao calendario",
|
||||||
|
"follow_me_title": "Seguir actualizacións desde o fediverso",
|
||||||
|
"follow": "Seguir",
|
||||||
|
"user": "Usuaria",
|
||||||
|
"filter": "Filtro",
|
||||||
|
"skip": "Omitir",
|
||||||
|
"max_events": "N. máx. eventos",
|
||||||
|
"feed": "Fonte RSS",
|
||||||
|
"authorize": "Autorizar",
|
||||||
|
"url": "URL",
|
||||||
|
"theme": "Decorado",
|
||||||
|
"import": "Importar",
|
||||||
|
"tags": "Cancelos"
|
||||||
|
},
|
||||||
|
"recover": {
|
||||||
|
"not_valid_code": "Algo fallou."
|
||||||
|
},
|
||||||
|
"export": {
|
||||||
|
"email_description": "Podes ter eventos que che interesen a través do email.",
|
||||||
|
"insert_your_address": "Escribe o enderezo de email",
|
||||||
|
"ical_description": "As computadoras e teléfonos traen normalmente instalada unha aplicación de calendario que pode importar un calendario remoto.",
|
||||||
|
"list_description": "Se tes un sitio web e queres mostrar unha lista de eventos, usa o seguinte código",
|
||||||
|
"intro": "Ao contrario que nas plataformas asociais que fan todo o posible para obter e gardar datos das persoas usuarias, cremos que esta información, como as persoas, deben ser libres. Por isto, podes recibir actualizacións dos eventos que elixas, sen precisar vir a esta web.",
|
||||||
|
"feed_description": "Para ter actualizacións usando unha computadora ou un teléfono intelixente sen ter que vir a esta web, usa fontes RSS. </p>\n\n<p> Coas fontes RSS usas unha app especial para recibir información das webs que che interesan. É un bo xeito de seguir moitas webs de xeito rápido, se ter que crear unha conta ou outras complicacións. </p>\n\n<li> Se usas Android, recomendámosche <a href=\"https://f-droid.org/en/packages/net.frju.flym/\">Flym</a> ou Feeder </li>\n<li> Para iPhone / iPad podes usar <a href=\"https://itunes.apple.com/ua/app/feeds4u/id1038456442?mt=8\"> Feed4U </a> </li>\n<li> Na computadora recomendamos Feedbro, que se pode instalar en <a href=\"https://addons.mozilla.org/en-GB/firefox/addon/feedbroreader/\"> Firefox </a> ou en <a href=\"https://chrome.google.com/webstore/detail/feedbro/mefgmmbdailogpfhfblcnnjfmnpnmdfa\"> Chrome </a>. </li>\n<br/>\nSe engades esta ligazón ao teu lector de RSS poderás recibir as actualizacións."
|
||||||
|
},
|
||||||
|
"register": {
|
||||||
|
"complete": "É preciso confirmar o rexistro.",
|
||||||
|
"first_user": "Creada Admin",
|
||||||
|
"description": "Os movementos sociais deberían organizarse e auto-financiarse.<br/>\n<br/>Antes de poder publicar, <strong> a conta debe ser aprobada</strong>, ten en conta que <strong> detrás desta web hai persoas reais</strong>, así que escribe un par de liñas para coñecer o tipo de eventos que che gustaría publicar.",
|
||||||
|
"error": "Erro: "
|
||||||
|
},
|
||||||
|
"event": {
|
||||||
|
"media_description": "Podes engadir un panfleto (optativo)",
|
||||||
|
"added": "Evento engadido",
|
||||||
|
"saved": "Evento gardado",
|
||||||
|
"updated": "Evento actualizado",
|
||||||
|
"where_description": "Onde será o evento? Se non existe podes crealo.",
|
||||||
|
"not_found": "Non atopamos o evento",
|
||||||
|
"show_recurrent": "eventos recurrentes",
|
||||||
|
"show_past": "tamén eventos previos",
|
||||||
|
"only_future": "só eventos futuros",
|
||||||
|
"recurrent_2w_days": "Cada {days} días",
|
||||||
|
"recurrent_2m_ordinal": "|O {n} {days} en meses alternos|O {n} {days} en meses alternos",
|
||||||
|
"recurrent_2m_days": "|O {days} cada dous meses|O {days} cada dous meses",
|
||||||
|
"each_month": "Cada mes",
|
||||||
|
"due": "ata",
|
||||||
|
"from": "Desde",
|
||||||
|
"interact_with_me_at": "Contacta comigo no fediverso en",
|
||||||
|
"interact_with_me": "Sígueme",
|
||||||
|
"remove_recurrent_confirmation": "Tes a certeza de querer eliminar este evento recurrente?\nOs eventos pasados permanecerán, pero non se crearán novos eventos.",
|
||||||
|
"import_URL": "Importar desde URL",
|
||||||
|
"anon": "Anon",
|
||||||
|
"anon_description": "Podes engadir un evento sen rexistrarte nen acceder, pero deberás agardar a que alguén o lea e\nconfirme que é un evento axeitado. Non será posible modificalo.<br/><br/>\nPodes tamén <a href='/login'>acceder</a> ou <a href='/register'>crear unha conta</a>. Mais podes continuar e agardar a que se comprobe. ",
|
||||||
|
"same_day": "no mesmo día",
|
||||||
|
"tag_description": "Cancelo",
|
||||||
|
"description_description": "Descrición",
|
||||||
|
"what_description": "Título",
|
||||||
|
"added_anon": "Evento engadido, mais ten que ser confirmado.",
|
||||||
|
"confirmed": "Evento confirmado",
|
||||||
|
"remove_confirmation": "Tes a certeza de querer eliminar este evento?",
|
||||||
|
"multidate_description": "É un festival? Elixe cando comeza e remata",
|
||||||
|
"import_ICS": "Importar desde ICS",
|
||||||
|
"edit_recurrent": "Editar evento recurrente:",
|
||||||
|
"recurrent_description": "Elixe a frecuencia e os días",
|
||||||
|
"multidate": "Máis días",
|
||||||
|
"normal": "Normal",
|
||||||
|
"each_week": "Cada semana",
|
||||||
|
"recurrent": "Recurrente",
|
||||||
|
"normal_description": "Elixe o día.",
|
||||||
|
"recurrent_1w_days": "Cada {days}",
|
||||||
|
"recurrent_1m_ordinal": "O {n} {days} de cada mes",
|
||||||
|
"image_too_big": "A imaxe non pode superar os 4 MB",
|
||||||
|
"recurrent_1m_days": "|O {days} de cada mes|{days} de cada mes",
|
||||||
|
"each_2w": "Cada dúas semanas",
|
||||||
|
"follow_me_description": "Un dos xeitos de recibir actualizacións dos eventos que se publican aquí en {title},\né seguindo a conta <u>{account}</u> no fediverso, por exemplo a través de Mastodon, e posiblemente tamén engadir recursos para un evento desde alí.<br/><br/>\nSe nunco escoitaches falar de Mastodon e o fediverso recomendámosche ler <a href='https://www.savjee.be/videos/simply-explained/mastodon-and-fediverse-explained/'>este artigo</a>.<br/><br/>Escribe aquí a túa instancia (ex. mastodon.social)",
|
||||||
|
"ics": "ICS",
|
||||||
|
"import_description": "Podes importar eventos desde outras plataformas e outras instancias usando formatos estándar (ics e h-event)"
|
||||||
|
},
|
||||||
|
"admin": {
|
||||||
|
"place_description": "Se escribiches mal o lugar ou enderezo, podes cambialo.<br/>Cambiará o enderezo de tódolos eventos actuais e pasados asociados a este lugar.",
|
||||||
|
"disable_user_confirm": "Tes a certeza de querer desactivar a {user}?",
|
||||||
|
"federation": "Federación / ActivityPub",
|
||||||
|
"enable_federation": "Activar a federación",
|
||||||
|
"add_instance": "Engadir instancia",
|
||||||
|
"select_instance_timezone": "Zona horaria",
|
||||||
|
"enable_resources_help": "Permite engadir recursos ao evento desde o fediverso",
|
||||||
|
"user_add_help": "Vaise enviar un email á nova usuaria con instruccións para confirmar a subscrición e elexir un contrasinal",
|
||||||
|
"instance_name": "Nome da instancia",
|
||||||
|
"filter_users": "Filtrar usuarias",
|
||||||
|
"title_description": "Utilízase no título da páxina, no asunto do email e para as fontes RSS e ICS exportadas.",
|
||||||
|
"description_description": "Aparece na cabeceira a carón do título",
|
||||||
|
"instance_name_help": "Conta ActivityPub a seguir",
|
||||||
|
"add_trusted_instance": "Engade unha instancia amiga",
|
||||||
|
"edit_place": "Editar lugar",
|
||||||
|
"smtp_test_success": "Enviado un email de proba a {admin_email}, comproba a caixa de correo",
|
||||||
|
"smtp_test_button": "Enviar un email de proba",
|
||||||
|
"admin_email": "Email de Admin",
|
||||||
|
"widget": "Trebello",
|
||||||
|
"delete_user_confirm": "Tes a certeza de querer eliminar a {user}?",
|
||||||
|
"enable_resources": "Activar recursos",
|
||||||
|
"hide_boost_bookmark_help": "Agocha as pequenas iconas que indican o número de promocións e marcacións que chegan desde o fediverso",
|
||||||
|
"show_resource": "Mostrar recurso",
|
||||||
|
"filter_instances": "Filtrar instancias",
|
||||||
|
"resources": "Recursos",
|
||||||
|
"allow_registration_description": "Permitir o rexistro libre?",
|
||||||
|
"allow_anon_event": "Permitir eventos anónimos (haberá que confirmalos)?",
|
||||||
|
"event_confirm_description": "Aquí podes confirmar os eventos engadidos por usuarias anónimas",
|
||||||
|
"remove_admin": "Eliminar admin",
|
||||||
|
"delete_user": "Eliminar",
|
||||||
|
"user_create_ok": "Usuaria creada",
|
||||||
|
"hide_boost_bookmark": "Agochar promoción/marcador",
|
||||||
|
"delete_resource": "Eliminar recurso",
|
||||||
|
"delete_resource_confirm": "Tes a certeza de querer eliminar este recurso?",
|
||||||
|
"favicon": "Logotipo",
|
||||||
|
"user_remove_ok": "Usuaria eliminada",
|
||||||
|
"allow_recurrent_event": "Permitir eventos recurrentes",
|
||||||
|
"recurrent_event_visible": "Mostrar por defecto os eventos recurrentes",
|
||||||
|
"enable_federation_help": "Vai ser posible seguir esta instancia desde o fediverso",
|
||||||
|
"block": "Bloquear",
|
||||||
|
"block_user": "Bloquear usuaria",
|
||||||
|
"user_blocked": "Usuaria {user} bloqueada",
|
||||||
|
"unblock": "Desbloquear",
|
||||||
|
"hide_resource": "Agochar recurso",
|
||||||
|
"announcement_remove_ok": "Anuncio eliminado",
|
||||||
|
"instance_timezone_description": "Gancio está deseñada para recoller os eventos dun lugar concreto, como unha cidade. Tódolos eventos neste lugar mostraranse na zona horaria elexida para el.",
|
||||||
|
"user_block_confirm": "Tes a certeza de querer bloquear a usuaria {user}?",
|
||||||
|
"instance_block_confirm": "Tes a certeza de querer bloquear a instancia {instance}?",
|
||||||
|
"announcement_description": "Nesta sección podes incluír anuncios que aparecerán na páxina de inicio",
|
||||||
|
"instance_locale": "Idioma por defecto",
|
||||||
|
"delete_announcement_confirm": "Tes a certeza de querer eliminar o anuncio?",
|
||||||
|
"instance_locale_description": "Idioma preferido para as páxinas. A veces as mensaxes teñen que mostrarse no mesmo idioma para tódalas persoas (por exemplo cando publicas vía ActivityPub ou cando envías os emails). Nestos casos usarase o idioma elexido aquí arriba.",
|
||||||
|
"enable_trusted_instances": "Activar instancias amigas",
|
||||||
|
"trusted_instances_help": "A lista das instancias amigas será mostrada na cabeceira",
|
||||||
|
"delete_trusted_instance_confirm": "Tes a certeza de querer eliminar este elemento do menú de instancias amigas?",
|
||||||
|
"instance_place_help": "A etiqueta a mostrar nas instancias de outras",
|
||||||
|
"add_link": "Engadir ligazón",
|
||||||
|
"instance_place": "Lugar de referencia para esta instancia",
|
||||||
|
"is_dark": "Decorado escuro",
|
||||||
|
"footer_links": "Ligazóns do rodapé",
|
||||||
|
"delete_footer_link_confirm": "Tes certeza de eliminar a ligazón?",
|
||||||
|
"show_smtp_setup": "Axustes do email",
|
||||||
|
"smtp_hostname": "Servidor SMTP",
|
||||||
|
"new_announcement": "Novo anuncio",
|
||||||
|
"smtp_description": "<ul><li>Admin debería recibir un email cando se engade un evento anónimo (se está activo)</li><li>Admin debería recibir un email coas solicitudes de rexistro (se activo).</li><li>A usuaria debería recibir un email coa solicitude de rexistro.</li><li>A usuaria debería recibir un email confirmando o rexistro.</li><li>A usuaria debería recibir un email de confirmación cando fose subscrita directamente por Admin</li><li>As usuarias deberían recibir un email para restablecer o contrasinal se o esquecen</li></ul>"
|
||||||
|
},
|
||||||
|
"auth": {
|
||||||
|
"not_confirmed": "Aínda non foi confirmado…",
|
||||||
|
"fail": "Non accedeches. Tes certeza acerca do contrasinal?"
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"change_password": "Cambia o contrasinal",
|
||||||
|
"danger_section": "Sección perigosa",
|
||||||
|
"remove_account": "Ao premer o seguinte botón vas eliminar túa conta de usuaria. Os eventos que publicaches permanecerán.",
|
||||||
|
"password_updated": "Contrasinal cambiado.",
|
||||||
|
"remove_account_confirm": "Vas eliminar permanentemente a túa conta",
|
||||||
|
"update_confirm": "Queres gardar as modificacións?"
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
"nick_taken": "Este alcume xa está a ser utilizado.",
|
||||||
|
"email_taken": "Este email xa está a ser utilizado."
|
||||||
|
},
|
||||||
|
"confirm": {
|
||||||
|
"title": "Confirmación da usuaria",
|
||||||
|
"not_valid": "Algo fallou.",
|
||||||
|
"valid": "A túa conta foi confirmada, xa podes <a href=\"/login\">acceder</a>"
|
||||||
|
},
|
||||||
|
"ordinal": {
|
||||||
|
"1": "primeiro",
|
||||||
|
"3": "terceiro",
|
||||||
|
"4": "cuarto",
|
||||||
|
"5": "quinto",
|
||||||
|
"-1": "último",
|
||||||
|
"2": "segundo"
|
||||||
|
},
|
||||||
|
"validators": {
|
||||||
|
"required": "{fieldName} é requerido",
|
||||||
|
"email": "Escribe un email válido"
|
||||||
|
},
|
||||||
|
"about": "\n <p><a href='https://gancio.org'>Gancio</a> é unha axenda compartida para comunidades locais.</p>\n ",
|
||||||
|
"oauth": {
|
||||||
|
"authorization_request": "A aplicación <code>{app}</code> solicita a seguinte autorización para <code>{intance_name}</code>:",
|
||||||
|
"redirected_to": "Após a confirmación vas ser enviada a <code>{url}</code>",
|
||||||
|
"scopes": {
|
||||||
|
"event:write": "Engade e edita os teus eventos"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"setup": {
|
||||||
|
"completed": "Configuración completada",
|
||||||
|
"completed_description": "<p>Xa podes acceder con estas credenciais:<br/><br/>Identificador: <b>{email}</b><br/>Contrasinal: <b>{password}</b></p>",
|
||||||
|
"start": "Comezar"
|
||||||
|
},
|
||||||
|
"login": {
|
||||||
|
"forgot_password": "Esqueceches o contrasinal?",
|
||||||
|
"check_email": "Comproba a caixa de correo e a de spam.",
|
||||||
|
"insert_email": "Escribe o enderezo de email",
|
||||||
|
"ok": "Entraches en",
|
||||||
|
"description": "Ao acceder poderás publicar novos eventos.",
|
||||||
|
"not_registered": "Non te rexistraches?",
|
||||||
|
"error": "Non entraches. Comproba as credenciais."
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,5 +7,6 @@ module.exports = {
|
||||||
eu: 'Euskara',
|
eu: 'Euskara',
|
||||||
nb: 'Norwegian Bokmål',
|
nb: 'Norwegian Bokmål',
|
||||||
fr: 'Francais',
|
fr: 'Francais',
|
||||||
de: 'Deutsch'
|
de: 'Deutsch',
|
||||||
|
gl: 'Galego',
|
||||||
}
|
}
|
||||||
|
|
|
@ -223,7 +223,13 @@
|
||||||
"delete_footer_link_confirm": "Vuoi eliminare questo collegamento?",
|
"delete_footer_link_confirm": "Vuoi eliminare questo collegamento?",
|
||||||
"edit_place": "Modifica luogo",
|
"edit_place": "Modifica luogo",
|
||||||
"new_announcement": "Nuovo annuncio",
|
"new_announcement": "Nuovo annuncio",
|
||||||
"show_smtp_setup": "Impostazioni email"
|
"show_smtp_setup": "Impostazioni email",
|
||||||
|
"widget": "Widget",
|
||||||
|
"smtp_description": "<ul><li>L'amministratore riceve una email quando viene aggiunto un evento anonimo (se abilitati).</li><li>L'amministratore riceve le mail di richiesta di registrazione (se abilitata).</li><li>L persone ricevono una mail di conferma della registrazione richiesta.</li><li>Le persone ricevono le email della registrazione confermata.</li><li>Le persone ricevono una mail di avviso quando vengono registrate direttamente da admin.</li><li>Le persone ricevono la mail per modificare la password quando l'hanno dimenticata</li></ul>",
|
||||||
|
"smtp_hostname": "SMTP Hostname",
|
||||||
|
"smtp_test_success": "Una mail di test è stata inviata all'indirizzo {admin_email}, controlla la tua casella di posta",
|
||||||
|
"smtp_test_button": "Invia una mail di prova",
|
||||||
|
"admin_email": "E-mail dell'admin"
|
||||||
},
|
},
|
||||||
"auth": {
|
"auth": {
|
||||||
"not_confirmed": "Non ancora confermato…",
|
"not_confirmed": "Non ancora confermato…",
|
||||||
|
@ -269,6 +275,7 @@
|
||||||
"setup": {
|
"setup": {
|
||||||
"completed": "Setup completato",
|
"completed": "Setup completato",
|
||||||
"completed_description": "<p>Puoi entrare con le seguenti credenziali:<br/><br/>Utente: <b>{email}</b><br/>Password: <b>{password}<b/></p>",
|
"completed_description": "<p>Puoi entrare con le seguenti credenziali:<br/><br/>Utente: <b>{email}</b><br/>Password: <b>{password}<b/></p>",
|
||||||
|
"copy_password_dialog": "Sì, devi copiare la password!",
|
||||||
"start": "Inizia"
|
"start": "Inizia"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,14 @@
|
||||||
export default function ({ req, redirect, route }) {
|
export default async function ({ $config, req, res, redirect, route, error }) {
|
||||||
if (process.server) {
|
if (process.server) {
|
||||||
if (req.firstrun && route.path !== '/setup') {
|
if (res.locals.status === 'SETUP' && route.path !== '/setup/0') {
|
||||||
return redirect('/setup')
|
return redirect('/setup/0')
|
||||||
}
|
}
|
||||||
if (!req.firstrun && route.path === '/setup') {
|
|
||||||
|
if (res.locals.status === 'DBCONF' && route.path !== '/setup/1') {
|
||||||
|
return redirect('/setup/1')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res.locals.status === 'READY' && route.path.startsWith('/setup')) {
|
||||||
return redirect('/')
|
return redirect('/')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
const config = require('./server/config.js')
|
const config = require('./server/config.js')
|
||||||
|
const minifyTheme = require('minify-css-string').default
|
||||||
|
|
||||||
|
const isDev = (process.env.NODE_ENV !== 'production')
|
||||||
module.exports = {
|
module.exports = {
|
||||||
telemetry: false,
|
telemetry: false,
|
||||||
modern: (process.env.NODE_ENV === 'production') && 'client',
|
modern: (process.env.NODE_ENV === 'production') && 'client',
|
||||||
|
@ -11,31 +13,26 @@ 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' }
|
||||||
],
|
],
|
||||||
script: [{ src: '/gancio-events.es.js' }],
|
link: [{ rel: 'icon', type: 'image/png', href: '/logo.png' }],
|
||||||
link: [{ rel: 'icon', type: 'image/png', href: '/logo.png' }]
|
link: [{ rel: 'preload', type: 'image/png', href: '/logo.png', as: 'image' }],
|
||||||
|
script: [{ src: '/gancio-events.es.js', async: true, body: true }],
|
||||||
},
|
},
|
||||||
dev: (process.env.NODE_ENV !== 'production'),
|
dev: isDev,
|
||||||
server: config.server,
|
server: config.server,
|
||||||
|
|
||||||
|
|
||||||
vue: {
|
vue: {
|
||||||
config: {
|
config: {
|
||||||
ignoredElements: ['gancio-events']
|
ignoredElements: ['gancio-events', 'gancio-event']
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
css: ['./assets/style.less'],
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Customize the progress-bar component
|
** Customize the progress-bar component
|
||||||
*/
|
*/
|
||||||
loading: '~/components/Loading.vue',
|
loading: '~/components/Loading.vue',
|
||||||
/*
|
|
||||||
** Global CSS
|
|
||||||
*/
|
|
||||||
css: [
|
|
||||||
'vuetify/dist/vuetify.min.css',
|
|
||||||
'@mdi/font/css/materialdesignicons.css',
|
|
||||||
'@/assets/style.less'
|
|
||||||
],
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Plugins to load before mounting the App
|
** Plugins to load before mounting the App
|
||||||
|
@ -43,7 +40,6 @@ module.exports = {
|
||||||
plugins: [
|
plugins: [
|
||||||
'@/plugins/i18n.js',
|
'@/plugins/i18n.js',
|
||||||
'@/plugins/filters', // text filters, datetime filters, generic transformation helpers etc.
|
'@/plugins/filters', // text filters, datetime filters, generic transformation helpers etc.
|
||||||
'@/plugins/vuetify', // vuetify
|
|
||||||
'@/plugins/axios', // axios baseurl configuration
|
'@/plugins/axios', // axios baseurl configuration
|
||||||
'@/plugins/validators', // inject validators
|
'@/plugins/validators', // inject validators
|
||||||
'@/plugins/api', // api helpers
|
'@/plugins/api', // api helpers
|
||||||
|
@ -95,9 +91,33 @@ module.exports = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
buildModules: ['@nuxtjs/vuetify'],
|
||||||
|
vuetify: {
|
||||||
|
customVariables: ['~/assets/variables.scss'],
|
||||||
|
treeShake: true,
|
||||||
|
theme: {
|
||||||
|
options: {
|
||||||
|
customProperties: false,
|
||||||
|
variations: false,
|
||||||
|
minifyTheme,
|
||||||
|
},
|
||||||
|
dark: true,
|
||||||
|
themes: {
|
||||||
|
dark: {
|
||||||
|
primary: '#FF6E40'
|
||||||
|
},
|
||||||
|
light: {
|
||||||
|
primary: '#FF4500'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
defaultAssets: false
|
||||||
|
},
|
||||||
build: {
|
build: {
|
||||||
corejs: 3,
|
corejs: 3,
|
||||||
cache: true,
|
cache: true,
|
||||||
hardSource: true
|
hardSource: !isDev,
|
||||||
|
extractCSS: !isDev,
|
||||||
|
optimizeCSS: !isDev
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
39
package.json
39
package.json
|
@ -1,12 +1,13 @@
|
||||||
{
|
{
|
||||||
"name": "gancio",
|
"name": "gancio",
|
||||||
"version": "1.2.2",
|
"version": "1.4.3",
|
||||||
"description": "A shared agenda for local communities",
|
"description": "A shared agenda for local communities",
|
||||||
"author": "lesion",
|
"author": "lesion",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "nuxt build --modern",
|
"build": "nuxt build --modern",
|
||||||
"start:inspect": "NODE_ENV=production node --inspect node_modules/.bin/nuxt start --modern",
|
"start:inspect": "NODE_ENV=production node --inspect node_modules/.bin/nuxt start --modern",
|
||||||
"dev": "nuxt dev",
|
"dev": "nuxt dev",
|
||||||
|
"test": "cd tests/seeds; jest; cd ../..",
|
||||||
"start": "nuxt start --modern",
|
"start": "nuxt start --modern",
|
||||||
"doc": "cd docs && bundle exec jekyll b",
|
"doc": "cd docs && bundle exec jekyll b",
|
||||||
"doc:dev": "cd docs && bundle exec jekyll s --drafts",
|
"doc:dev": "cd docs && bundle exec jekyll s --drafts",
|
||||||
|
@ -26,57 +27,62 @@
|
||||||
"yarn.lock"
|
"yarn.lock"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@mdi/js": "^6.5.95",
|
||||||
"@nuxtjs/auth": "^4.9.1",
|
"@nuxtjs/auth": "^4.9.1",
|
||||||
"@nuxtjs/axios": "^5.13.5",
|
"@nuxtjs/axios": "^5.13.5",
|
||||||
"accept-language": "^3.0.18",
|
"accept-language": "^3.0.18",
|
||||||
"axios": "^0.24.0",
|
"axios": "^0.26.0",
|
||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
"body-parser": "^1.18.3",
|
"body-parser": "^1.19.2",
|
||||||
"cookie-parser": "^1.4.6",
|
"cookie-parser": "^1.4.6",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"dayjs": "^1.10.7",
|
"dayjs": "^1.10.7",
|
||||||
"dompurify": "^2.3.3",
|
"dompurify": "^2.3.6",
|
||||||
"email-templates": "^8.0.8",
|
"email-templates": "^8.0.9",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.3",
|
||||||
"express-oauth-server": "lesion/express-oauth-server#master",
|
"express-oauth-server": "lesion/express-oauth-server#master",
|
||||||
"http-signature": "^1.3.6",
|
"http-signature": "^1.3.6",
|
||||||
"ical.js": "^1.4.0",
|
"ical.js": "^1.5.0",
|
||||||
"ics": "^2.35.0",
|
"ics": "^2.35.0",
|
||||||
"jsdom": "^18.1.1",
|
"jsdom": "^19.0.0",
|
||||||
"jsonwebtoken": "^8.5.1",
|
"jsonwebtoken": "^8.5.1",
|
||||||
"linkify-html": "^3.0.4",
|
"linkify-html": "^3.0.4",
|
||||||
"linkifyjs": "3.0.4",
|
"linkifyjs": "3.0.5",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
|
"mariadb": "^2.5.6",
|
||||||
"microformat-node": "^2.0.1",
|
"microformat-node": "^2.0.1",
|
||||||
|
"minify-css-string": "^1.0.0",
|
||||||
"mkdirp": "^1.0.4",
|
"mkdirp": "^1.0.4",
|
||||||
"multer": "^1.4.3",
|
"multer": "^1.4.3",
|
||||||
"nuxt-edge": "^2.16.0-27305297.ab1c6cb4",
|
"nuxt-edge": "^2.16.0-27305297.ab1c6cb4",
|
||||||
"pg": "^8.6.0",
|
"pg": "^8.6.0",
|
||||||
"sequelize": "^6.12.0-alpha.1",
|
"sequelize": "^6.17.0",
|
||||||
"sequelize-slugify": "^1.6.0",
|
"sequelize-slugify": "^1.6.0",
|
||||||
"sharp": "^0.27.2",
|
"sharp": "^0.27.2",
|
||||||
"sqlite3": "mapbox/node-sqlite3#918052b",
|
"sqlite3": "mapbox/node-sqlite3#918052b",
|
||||||
"tiptap": "^1.32.0",
|
"tiptap": "^1.32.0",
|
||||||
"tiptap-extensions": "^1.35.0",
|
"tiptap-extensions": "^1.35.0",
|
||||||
"umzug": "^2.3.0",
|
"umzug": "^2.3.0",
|
||||||
"v-calendar": "2.3.4",
|
"v-calendar": "2.4.1",
|
||||||
"vue": "^2.6.14",
|
"vue": "^2.6.14",
|
||||||
"vue-i18n": "^8.26.7",
|
"vue-i18n": "^8.26.7",
|
||||||
"vue-template-compiler": "^2.6.14",
|
"vue-template-compiler": "^2.6.14",
|
||||||
"vuetify": "^2.6.1",
|
"vuetify": "npm:@vuetify/nightly@dev",
|
||||||
"winston": "^3.3.3",
|
"winston": "^3.6.0",
|
||||||
"winston-daily-rotate-file": "^4.5.5",
|
"winston-daily-rotate-file": "^4.6.1",
|
||||||
"yargs": "^17.2.0"
|
"yargs": "^17.2.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@mdi/font": "^6.5.95",
|
"@nuxtjs/vuetify": "^1.12.3",
|
||||||
|
"jest": "^27.5.1",
|
||||||
"less": "^4.1.1",
|
"less": "^4.1.1",
|
||||||
"less-loader": "^7",
|
"less-loader": "^7",
|
||||||
"prettier": "^2.3.0",
|
"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.43.5",
|
"sass": "^1.49.4",
|
||||||
"sequelize-cli": "^6.3.0",
|
"sequelize-cli": "^6.3.0",
|
||||||
|
"supertest": "^6.2.2",
|
||||||
"webpack": "4",
|
"webpack": "4",
|
||||||
"webpack-cli": "^4.7.2"
|
"webpack-cli": "^4.7.2"
|
||||||
},
|
},
|
||||||
|
@ -87,7 +93,6 @@
|
||||||
"jimp": "0.16.1",
|
"jimp": "0.16.1",
|
||||||
"resize-img": "2.0.0",
|
"resize-img": "2.0.0",
|
||||||
"underscore": "1.13.1",
|
"underscore": "1.13.1",
|
||||||
"@nuxtjs/vuetify/**/sass": "1.32.12",
|
|
||||||
"postcss": "7.0.36",
|
"postcss": "7.0.36",
|
||||||
"glob-parent": "5.1.2",
|
"glob-parent": "5.1.2",
|
||||||
"chokidar": "3.5.2",
|
"chokidar": "3.5.2",
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template lang="pug">
|
<template lang="pug">
|
||||||
v-container
|
v-container.container.pa-0.pa-md-3
|
||||||
v-card
|
v-card
|
||||||
v-tabs(v-model='selectedTab')
|
v-tabs(v-model='selectedTab' show-arrows)
|
||||||
|
|
||||||
//- SETTINGS
|
//- SETTINGS
|
||||||
v-tab {{$t('common.settings')}}
|
v-tab {{$t('common.settings')}}
|
||||||
|
@ -49,18 +49,19 @@
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import { mapState } from 'vuex'
|
import { mapState } from 'vuex'
|
||||||
import Users from '../components/admin/Users'
|
|
||||||
import Events from '../components/admin/Events'
|
|
||||||
import Places from '../components/admin/Places'
|
|
||||||
import Settings from '../components/admin/Settings'
|
|
||||||
import Federation from '../components/admin/Federation'
|
|
||||||
import Moderation from '../components/admin/Moderation'
|
|
||||||
import Announcement from '../components/admin/Announcement'
|
|
||||||
import Theme from '../components/admin/Theme'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Admin',
|
name: 'Admin',
|
||||||
components: { Users, Events, Places, Settings, Federation, Moderation, Announcement, Theme },
|
components: {
|
||||||
|
Users: () => import(/* webpackChunkName: "admin" */'../components/admin/Users'),
|
||||||
|
Events: () => import(/* webpackChunkName: "admin" */'../components/admin/Events'),
|
||||||
|
Places: () => import(/* webpackChunkName: "admin" */'../components/admin/Places'),
|
||||||
|
Settings: () => import(/* webpackChunkName: "admin" */'../components/admin/Settings'),
|
||||||
|
Federation: () => import(/* webpackChunkName: "admin" */'../components/admin/Federation.vue'),
|
||||||
|
Moderation: () => import(/* webpackChunkName: "admin" */'../components/admin/Moderation.vue'),
|
||||||
|
Announcement: () => import(/* webpackChunkName: "admin" */'../components/admin/Announcement.vue'),
|
||||||
|
Theme: () => import(/* webpackChunkName: "admin" */'../components/admin/Theme.vue')
|
||||||
|
},
|
||||||
middleware: ['auth'],
|
middleware: ['auth'],
|
||||||
async asyncData ({ $axios, params, store }) {
|
async asyncData ({ $axios, params, store }) {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template lang='pug'>
|
<template lang='pug'>
|
||||||
v-container
|
v-container.pa-0.pa-md-3
|
||||||
v-row.mt-5(align='center' justify='center')
|
v-row.mt-md-5.ma-0(align='center' justify='center')
|
||||||
v-col(cols='12' md="6" lg="5" xl="4")
|
v-col.pa-0.pa-md-3(cols='12' md="6" lg="5" xl="4")
|
||||||
v-form(v-model='valid' ref='form' lazy-validation @submit.prevent='submit')
|
v-form(v-model='valid' ref='form' lazy-validation @submit.prevent='submit')
|
||||||
v-card
|
v-card
|
||||||
v-card-title {{$t('common.login')}}
|
v-card-title {{$t('common.login')}}
|
||||||
|
@ -57,7 +57,6 @@ export default {
|
||||||
methods: {
|
methods: {
|
||||||
async forgot () {
|
async forgot () {
|
||||||
if (!this.email) {
|
if (!this.email) {
|
||||||
// this.$root.$message({ message: this.$t('login.insert_email'), color: 'error' })
|
|
||||||
this.$refs.email.focus()
|
this.$refs.email.focus()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template lang='pug'>
|
<template lang='pug'>
|
||||||
v-container
|
v-container.pa-0.pa-md-3
|
||||||
v-row.mt-5(align='center' justify='center')
|
v-row.mt-md-5.ma-0(align='center' justify='center')
|
||||||
v-col(cols='12' md="6" lg="5" xl="4")
|
v-col.pa-0.pa-md-3(cols='12' md="6" lg="5" xl="4")
|
||||||
|
|
||||||
v-card
|
v-card
|
||||||
v-card-title {{$t('common.register')}}
|
v-card-title {{$t('common.register')}}
|
||||||
|
@ -28,17 +28,19 @@ v-container
|
||||||
v-btn(@click='register'
|
v-btn(@click='register'
|
||||||
:disabled='!valid || loading' :loading='loading'
|
:disabled='!valid || loading' :loading='loading'
|
||||||
color='primary') {{$t('common.send')}}
|
color='primary') {{$t('common.send')}}
|
||||||
v-icon mdi-chevron-right
|
v-icon(v-text='mdiChevronRight')
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapState } from 'vuex'
|
import { mapState } from 'vuex'
|
||||||
import get from 'lodash/get'
|
import get from 'lodash/get'
|
||||||
|
import { mdiChevronRight } from '@mdi/js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Register',
|
name: 'Register',
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
|
mdiChevronRight,
|
||||||
loading: false,
|
loading: false,
|
||||||
user: {},
|
user: {},
|
||||||
valid: true
|
valid: true
|
||||||
|
|
|
@ -78,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 => (this.event.id && e.id !== this.event.id) && e.start_datetime >= start && e.start_datetime <= end)
|
const events = this.events.filter(e => e.start_datetime >= start && e.start_datetime <= end)
|
||||||
return events
|
return events
|
||||||
},
|
},
|
||||||
attributes () {
|
attributes () {
|
||||||
|
@ -161,8 +161,10 @@ export default {
|
||||||
this.type = 'normal'
|
this.type = 'normal'
|
||||||
}
|
}
|
||||||
this.events = await this.$api.getEvents({
|
this.events = await this.$api.getEvents({
|
||||||
start: dayjs().unix()
|
start: dayjs().unix(),
|
||||||
|
show_recurrent: true
|
||||||
})
|
})
|
||||||
|
this.events = this.events.filter(e => e.id !== this.event.id)
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
updateRecurrent (value) {
|
updateRecurrent (value) {
|
||||||
|
@ -194,7 +196,7 @@ export default {
|
||||||
} else if (what === 'fromHour') {
|
} else if (what === 'fromHour') {
|
||||||
if (value) {
|
if (value) {
|
||||||
const [hour, minute] = value.split(':')
|
const [hour, minute] = value.split(':')
|
||||||
const from = dayjs(this.value.from).hour(hour).minute(minute)
|
const from = dayjs(this.value.from).hour(hour).minute(minute).second(0)
|
||||||
this.$emit('input', { ...this.value, from, fromHour: true })
|
this.$emit('input', { ...this.value, from, fromHour: true })
|
||||||
} else {
|
} else {
|
||||||
this.$emit('input', { ...this.value, fromHour: false })
|
this.$emit('input', { ...this.value, fromHour: false })
|
||||||
|
@ -209,7 +211,7 @@ export default {
|
||||||
if (fromHour > Number(hour) && !this.value.multidate) {
|
if (fromHour > Number(hour) && !this.value.multidate) {
|
||||||
due = due.add(1, 'day')
|
due = due.add(1, 'day')
|
||||||
}
|
}
|
||||||
due = due.hour(hour).minute(minute)
|
due = due.hour(hour).minute(minute).second(0)
|
||||||
this.$emit('input', { ...this.value, due, dueHour: true })
|
this.$emit('input', { ...this.value, due, dueHour: true })
|
||||||
} else {
|
} else {
|
||||||
this.$emit('input', { ...this.value, due: null, dueHour: false })
|
this.$emit('input', { ...this.value, due: null, dueHour: false })
|
||||||
|
|
|
@ -41,13 +41,14 @@
|
||||||
v-else
|
v-else
|
||||||
:label="$t('common.media')"
|
:label="$t('common.media')"
|
||||||
:hint="$t('event.media_description')"
|
:hint="$t('event.media_description')"
|
||||||
prepend-icon="mdi-camera"
|
:prepend-icon="mdiCamera"
|
||||||
:value='value.image'
|
:value='value.image'
|
||||||
@change="selectMedia"
|
@change="selectMedia"
|
||||||
persistent-hint
|
persistent-hint
|
||||||
accept='image/*')
|
accept='image/*')
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
|
import { mdiCamera } from '@mdi/js'
|
||||||
export default {
|
export default {
|
||||||
name: 'MediaInput',
|
name: 'MediaInput',
|
||||||
props: {
|
props: {
|
||||||
|
@ -56,6 +57,7 @@ export default {
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
|
mdiCamera,
|
||||||
openMediaDetails: false,
|
openMediaDetails: false,
|
||||||
name: this.value.name || '',
|
name: this.value.name || '',
|
||||||
focalpoint: this.value.focalpoint || [0, 0],
|
focalpoint: this.value.focalpoint || [0, 0],
|
||||||
|
@ -87,7 +89,7 @@ export default {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
save () {
|
save () {
|
||||||
this.$emit('input', { url: this.value.url, image: this.value.image, name: this.name || this.value.image.name || '', focalpoint: [...this.focalpoint] })
|
this.$emit('input', { url: this.value.url, image: this.value.image, name: this.name || (this.event.title) || '', focalpoint: [...this.focalpoint] })
|
||||||
this.openMediaDetails = false
|
this.openMediaDetails = false
|
||||||
},
|
},
|
||||||
async remove () {
|
async remove () {
|
||||||
|
@ -96,7 +98,7 @@ export default {
|
||||||
this.$emit('remove')
|
this.$emit('remove')
|
||||||
},
|
},
|
||||||
selectMedia (v) {
|
selectMedia (v) {
|
||||||
this.$emit('input', { image: v, name: v.name, focalpoint: [0, 0] })
|
this.$emit('input', { image: v, name: this.event.title, focalpoint: [0, 0] })
|
||||||
},
|
},
|
||||||
handleStart (ev) {
|
handleStart (ev) {
|
||||||
ev.preventDefault()
|
ev.preventDefault()
|
||||||
|
|
|
@ -6,23 +6,24 @@
|
||||||
:label="$t('common.where')"
|
:label="$t('common.where')"
|
||||||
:hint="$t('event.where_description')"
|
:hint="$t('event.where_description')"
|
||||||
:search-input.sync="placeName"
|
:search-input.sync="placeName"
|
||||||
prepend-icon='mdi-map-marker'
|
:prepend-icon='mdiMapMarker'
|
||||||
persistent-hint
|
persistent-hint
|
||||||
:value="value.name"
|
:value="value.name"
|
||||||
:items="filteredPlaces"
|
:items="filteredPlaces"
|
||||||
no-filter
|
no-filter
|
||||||
item-text='name'
|
item-text='name'
|
||||||
@change='selectPlace')
|
@change='selectPlace')
|
||||||
template(v-slot:item="{ item }")
|
template(v-slot:item="{ item, attrs, on }")
|
||||||
|
v-list-item(v-bind='attrs' v-on='on')
|
||||||
v-list-item-content(two-line v-if='item.create')
|
v-list-item-content(two-line v-if='item.create')
|
||||||
v-list-item-title <v-icon color='primary'>mdi-plus</v-icon> {{item.name}}
|
v-list-item-title <v-icon color='primary' v-text='mdiPlus' :aria-label='add'></v-icon> {{item.name}}
|
||||||
v-list-item-content(two-line v-else)
|
v-list-item-content(two-line v-else)
|
||||||
v-list-item-title {{item.name}}
|
v-list-item-title(v-text='item.name')
|
||||||
v-list-item-subtitle {{item.address}}
|
v-list-item-subtitle(v-text='item.address')
|
||||||
|
|
||||||
v-col(cols=12 md=6)
|
v-col(cols=12 md=6)
|
||||||
v-text-field(ref='address'
|
v-text-field(ref='address'
|
||||||
prepend-icon='mdi-map'
|
:prepend-icon='mdiMap'
|
||||||
:disabled='disableAddress'
|
:disabled='disableAddress'
|
||||||
:rules="[ v => disableAddress ? true : $validators.required('common.address')(v)]"
|
:rules="[ v => disableAddress ? true : $validators.required('common.address')(v)]"
|
||||||
:label="$t('common.address')"
|
:label="$t('common.address')"
|
||||||
|
@ -32,6 +33,7 @@
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import { mapState } from 'vuex'
|
import { mapState } from 'vuex'
|
||||||
|
import { mdiMap, mdiMapMarker, mdiPlus } from '@mdi/js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'WhereInput',
|
name: 'WhereInput',
|
||||||
|
@ -40,6 +42,7 @@ export default {
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
|
mdiMap, mdiMapMarker, mdiPlus,
|
||||||
place: { },
|
place: { },
|
||||||
placeName: '',
|
placeName: '',
|
||||||
disableAddress: true
|
disableAddress: true
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
<template lang="pug">
|
<template lang="pug">
|
||||||
v-container.container.px-0.px-md-3
|
v-container.container.pa-0.pa-md-3
|
||||||
v-card
|
v-card
|
||||||
v-card-title
|
v-card-title
|
||||||
h4 {{edit?$t('common.edit_event'):$t('common.add_event')}}
|
h4 {{edit?$t('common.edit_event'):$t('common.add_event')}}
|
||||||
v-spacer
|
v-spacer
|
||||||
v-btn(link text color='primary' @click='openImportDialog=true')
|
v-btn(link text color='primary' @click='openImportDialog=true')
|
||||||
<v-icon>mdi-file-import</v-icon> {{$t('common.import')}}
|
<v-icon v-text='mdiFileImport'></v-icon> {{$t('common.import')}}
|
||||||
v-dialog(v-model='openImportDialog' :fullscreen='$vuetify.breakpoint.xsOnly')
|
v-dialog(v-model='openImportDialog' :fullscreen='$vuetify.breakpoint.xsOnly')
|
||||||
ImportDialog(@close='openImportDialog=false' @imported='eventImported')
|
ImportDialog(@close='openImportDialog=false' @imported='eventImported')
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@
|
||||||
@change='v => event.title = v'
|
@change='v => event.title = v'
|
||||||
:value = 'event.title'
|
:value = 'event.title'
|
||||||
:rules="[$validators.required('common.title')]"
|
:rules="[$validators.required('common.title')]"
|
||||||
prepend-icon='mdi-format-title'
|
:prepend-icon='mdiFormatTitle'
|
||||||
:label="$t('common.title')"
|
:label="$t('common.title')"
|
||||||
autofocus
|
autofocus
|
||||||
ref='title')
|
ref='title')
|
||||||
|
@ -49,11 +49,15 @@
|
||||||
//- tags
|
//- tags
|
||||||
v-col(cols=12 md=6)
|
v-col(cols=12 md=6)
|
||||||
v-combobox(v-model='event.tags'
|
v-combobox(v-model='event.tags'
|
||||||
prepend-icon="mdi-tag-multiple"
|
:prepend-icon="mdiTagMultiple"
|
||||||
chips small-chips multiple deletable-chips hide-no-data hide-selected persistent-hint
|
chips small-chips multiple deletable-chips hide-no-data hide-selected persistent-hint
|
||||||
:delimiters="[',', ' ']"
|
:delimiters="[',', ';']"
|
||||||
:items="tags.map(t => t.tag)"
|
:items="tags.map(t => t.tag)"
|
||||||
:label="$t('common.tags')")
|
:label="$t('common.tags')")
|
||||||
|
template(v-slot:selection="{ item, on, attrs, selected, parent}")
|
||||||
|
v-chip(v-bind="attrs" close :close-icon='mdiCloseCircle' @click:close='parent.selectItem(item)'
|
||||||
|
:input-value="selected" label small) {{item}}
|
||||||
|
</v-chip>
|
||||||
|
|
||||||
v-card-actions
|
v-card-actions
|
||||||
v-spacer
|
v-spacer
|
||||||
|
@ -64,16 +68,19 @@
|
||||||
<script>
|
<script>
|
||||||
import { mapActions, mapState } from 'vuex'
|
import { mapActions, mapState } from 'vuex'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import Editor from '@/components/Editor'
|
|
||||||
import List from '@/components/List'
|
import { mdiFileImport, mdiFormatTitle, mdiTagMultiple, mdiCloseCircle } from '@mdi/js'
|
||||||
import ImportDialog from './ImportDialog'
|
|
||||||
import DateInput from './DateInput'
|
|
||||||
import WhereInput from './WhereInput'
|
|
||||||
import MediaInput from './MediaInput'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'NewEvent',
|
name: 'NewEvent',
|
||||||
components: { List, Editor, ImportDialog, MediaInput, WhereInput, DateInput },
|
components: {
|
||||||
|
List: () => import(/* webpackChunkName: "add" */'@/components/List'),
|
||||||
|
Editor: () => import(/* webpackChunkName: "add" */'@/components/Editor'),
|
||||||
|
ImportDialog: () => import(/* webpackChunkName: "add" */'./ImportDialog.vue'),
|
||||||
|
MediaInput: () => import(/* webpackChunkName: "add" */'./MediaInput.vue'),
|
||||||
|
WhereInput: () => import(/* webpackChunkName: "add" */'./WhereInput.vue'),
|
||||||
|
DateInput: () => import(/* webpackChunkName: "add" */'./DateInput.vue')
|
||||||
|
},
|
||||||
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)
|
||||||
},
|
},
|
||||||
|
@ -114,6 +121,7 @@ export default {
|
||||||
const month = dayjs().month() + 1
|
const month = dayjs().month() + 1
|
||||||
const year = dayjs().year()
|
const year = dayjs().year()
|
||||||
return {
|
return {
|
||||||
|
mdiFileImport, mdiFormatTitle, mdiTagMultiple, mdiCloseCircle,
|
||||||
valid: false,
|
valid: false,
|
||||||
openImportDialog: false,
|
openImportDialog: false,
|
||||||
event: {
|
event: {
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
v-container#event.pa-0.pa-sm-2
|
v-container#event.pa-0.pa-sm-2
|
||||||
//- EVENT PAGE
|
//- EVENT PAGE
|
||||||
//- gancio supports microformats (http://microformats.org/wiki/h-event)
|
//- gancio supports microformats (http://microformats.org/wiki/h-event)
|
||||||
v-card.h-event
|
v-card.h-event(itemscope itemtype="https://schema.org/Event")
|
||||||
v-card-actions
|
v-card-actions
|
||||||
//- admin controls
|
//- admin controls
|
||||||
EventAdmin.mb-1(v-if='is_mine' :event='event')
|
EventAdmin.mb-1(v-if='is_mine' :event='event')
|
||||||
|
@ -11,32 +11,33 @@ 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='hasMedia' :src='event | mediaURL')
|
img.u-featured(v-show='false' v-if='hasMedia' :src='event | mediaURL' itemprop="image")
|
||||||
v-img.main_image.mb-3(
|
v-img.main_image.mb-3(
|
||||||
contain
|
contain
|
||||||
:alt='event | mediaURL("alt")'
|
:alt='event | mediaURL("alt")'
|
||||||
:src='event | mediaURL'
|
:src='event | mediaURL'
|
||||||
:lazy-src='event | mediaURL("thumb")'
|
:lazy-src='event | mediaURL("thumb")'
|
||||||
v-if='hasMedia')
|
v-if='hasMedia')
|
||||||
.p-description.text-body-1.pa-3.rounded(v-if='!hasMedia && event.description' v-html='event.description')
|
.p-description.text-body-1.pa-3.rounded(v-if='!hasMedia && event.description' itemprop='description' v-html='event.description')
|
||||||
|
|
||||||
v-col.col-12.col-lg-4
|
v-col.col-12.col-lg-4
|
||||||
v-card
|
v-card(outlined)
|
||||||
v-card-text
|
v-card-text
|
||||||
v-icon.float-right(v-if='event.parentId' color='success') mdi-repeat
|
v-icon.float-right(v-if='event.parentId' color='success' v-text='mdiRepeat')
|
||||||
.title.text-h5
|
.title.text-h5
|
||||||
b.p-name {{event.title}}
|
b.p-name(itemprop="name") {{event.title}}
|
||||||
|
|
||||||
time.dt-start.text-h6(:datetime='event.start_datetime|unixFormat("YYYY-MM-DD HH:mm")')
|
time.dt-start.text-h6(:datetime='event.start_datetime|unixFormat("YYYY-MM-DD HH:mm")' itemprop="startDate" :content="event.start_datetime|unixFormat('YYYY-MM-DDTHH:mm')")
|
||||||
v-icon mdi-calendar
|
v-icon(v-text='mdiCalendar')
|
||||||
b.ml-2 {{event|when}}
|
b.ml-2 {{event|when}}
|
||||||
|
.d-none.dt-end(itemprop="endDate" :content="event.end_datetime|unixFormat('YYYY-MM-DDTHH:mm')") {{event.end_datetime|unixFormat('YYYY-MM-DD HH:mm')}}
|
||||||
div.text-subtitle-1 {{event.start_datetime|from}}
|
div.text-subtitle-1 {{event.start_datetime|from}}
|
||||||
small(v-if='event.parentId') ({{event|recurrentDetail}})
|
small(v-if='event.parentId') ({{event|recurrentDetail}})
|
||||||
|
|
||||||
.text-h6.p-location
|
.text-h6.p-location(itemprop="location" itemscope itemtype="https://schema.org/Place")
|
||||||
v-icon mdi-map-marker
|
v-icon(v-text='mdiMapMarker')
|
||||||
b.vcard.ml-2 {{event.place && event.place.name}}
|
b.vcard.ml-2(itemprop="name") {{event.place && event.place.name}}
|
||||||
.text-subtitle-1.adr {{event.place && event.place.address}}
|
.text-subtitle-1.adr(itemprop='address') {{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')
|
||||||
|
@ -46,21 +47,16 @@ v-container#event.pa-0.pa-sm-2
|
||||||
|
|
||||||
//- info & actions
|
//- info & actions
|
||||||
v-toolbar
|
v-toolbar
|
||||||
v-tooltip(bottom) {{$t('common.copy_link')}}
|
v-btn.ml-2(large icon :title="$t('common.copy_link')" :aria-label="$t('common.copy_link')" color='primary'
|
||||||
template(v-slot:activator="{on, attrs} ")
|
@click='clipboard(`${settings.baseurl}/event/${event.slug || event.id}`)')
|
||||||
v-btn.ml-2(large icon v-on='on' color='primary' @click='clipboard(`${settings.baseurl}/event/${event.slug || event.id}`)')
|
v-icon(v-text='mdiContentCopy')
|
||||||
v-icon mdi-content-copy
|
v-btn.ml-2(large icon :title="$t('common.embed')" :aria-label="$t('common.embed')" @click='showEmbed=true' color='primary')
|
||||||
v-tooltip(bottom) {{$t('common.embed')}}
|
v-icon(v-text='mdiCodeTags')
|
||||||
template(v-slot:activator="{on, attrs} ")
|
v-btn.ml-2(large icon :title="$t('common.add_to_calendar')" color='primary' :aria-label="$t('common.add_to_calendar')"
|
||||||
v-btn.ml-2(large icon v-on='on' @click='showEmbed=true' color='primary')
|
|
||||||
v-icon mdi-code-tags
|
|
||||||
v-tooltip(bottom) {{$t('common.add_to_calendar')}}
|
|
||||||
template(v-slot:activator="{on, attrs} ")
|
|
||||||
v-btn.ml-2(large icon v-on='on' color='primary'
|
|
||||||
:href='`/api/event/${event.slug || event.id}.ics`')
|
:href='`/api/event/${event.slug || event.id}.ics`')
|
||||||
v-icon mdi-calendar-export
|
v-icon(v-text='mdiCalendarExport')
|
||||||
|
|
||||||
.p-description.text-body-1.pa-3.rounded(v-if='hasMedia && event.description' v-html='event.description')
|
.p-description.text-body-1.pa-3.rounded(v-if='hasMedia && event.description' itemprop='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')
|
||||||
|
@ -68,39 +64,41 @@ v-container#event.pa-0.pa-sm-2
|
||||||
//- small.mr-3 🔖 {{event.likes.length}}
|
//- small.mr-3 🔖 {{event.likes.length}}
|
||||||
//- small ✊ {{event.boost.length}}<br/>
|
//- small ✊ {{event.boost.length}}<br/>
|
||||||
|
|
||||||
v-dialog(v-model='showResources'
|
v-dialog(v-model='showResources' max-width="900" width="900" :fullscreen='$vuetify.breakpoint.xsOnly'
|
||||||
fullscreen
|
destroy-on-close)
|
||||||
destroy-on-close
|
|
||||||
scrollable
|
|
||||||
transition='dialog-bottom-transition')
|
|
||||||
v-card
|
v-card
|
||||||
v-btn.ma-2(icon dark @click='showResources = false')
|
v-btn.ma-2(icon dark @click='showResources = false')
|
||||||
v-icon mdi-close
|
v-icon(v-text='mdiClose')
|
||||||
v-carousel.pa-5(:interval='10000' ref='carousel' hide-delimiters v-model='currentAttachment'
|
v-carousel.pa-5(:interval='10000'
|
||||||
|
:next-icon='mdiArrowRight'
|
||||||
|
:prev-icon='mdiArrowLeft'
|
||||||
|
ref='carousel' hide-delimiters v-model='currentAttachment'
|
||||||
height='100%' show-arrows-on-over)
|
height='100%' show-arrows-on-over)
|
||||||
v-carousel-item(v-for='attachment in selectedResource.data.attachment'
|
v-carousel-item(v-for='attachment in selectedResource.data.attachment'
|
||||||
v-if='isImg(attachment)'
|
v-if='isImg(attachment)'
|
||||||
:key='attachment.url')
|
:key='attachment.url')
|
||||||
v-img(:src='attachment.url' contain max-width='100%' max-height='100%')
|
v-img(:src='attachment.url' contain max-height='90%')
|
||||||
v-card-actions.align-center.justify-center
|
v-card-actions.align-center.justify-center
|
||||||
span {{currentAttachmentLabel}}
|
span {{currentAttachmentLabel}}
|
||||||
|
|
||||||
v-card.grey.darken-4.mb-3#resources(v-if='settings.enable_resources' v-for='resource in event.resources'
|
v-card.mb-3.resources(v-if='settings.enable_resources' v-for='resource in event.resources'
|
||||||
:key='resource.id' :class='{disabled: resource.hidden}' elevation='10' outlined)
|
:key='resource.id' elevation='10' :flat='resource.hidden' outlined)
|
||||||
v-card-title
|
v-card-title
|
||||||
v-menu(v-if='$auth.user && $auth.user.is_admin' offset-y)
|
v-menu(v-if='$auth.user && $auth.user.is_admin' offset-y)
|
||||||
template(v-slot:activator="{ on }")
|
template(v-slot:activator="{ on }")
|
||||||
v-btn.mr-2(v-on='on' color='primary' small icon)
|
v-btn.mr-2(v-on='on' color='primary' small icon)
|
||||||
v-icon mdi-dots-vertical
|
v-icon(v-text='mdiDotsVertical')
|
||||||
v-list
|
v-list
|
||||||
v-list-item(v-if='!resource.hidden' @click='hideResource(resource, true)')
|
v-list-item(v-if='!resource.hidden' @click='hideResource(resource, true)')
|
||||||
v-list-item-title <v-icon left>mdi-eye-off</v-icon> {{$t('admin.hide_resource')}}
|
v-list-item-title <v-icon left v-text='mdiEyeOff'></v-icon> {{$t('admin.hide_resource')}}
|
||||||
v-list-item(v-else @click='hideResource(resource, false)')
|
v-list-item(v-else @click='hideResource(resource, false)')
|
||||||
v-list-item-title <v-icon left>mdi-eye</v-icon> {{$t('admin.show_resource')}}
|
v-list-item-title <v-icon left v-text='mdiEye'></v-icon> {{$t('admin.show_resource')}}
|
||||||
v-list-item(@click='deleteResource(resource)')
|
v-list-item(@click='deleteResource(resource)')
|
||||||
v-list-item-title <v-icon left>mdi-delete</v-icon> {{$t('admin.delete_resource')}}
|
v-list-item-title <v-icon left v-text='mdiDelete'></v-icon> {{$t('admin.delete_resource')}}
|
||||||
v-list-item(@click='blockUser(resource)')
|
v-list-item(@click='blockUser(resource)')
|
||||||
v-list-item-title <v-icon left>mdi-lock</v-icon> {{$t('admin.block_user')}}
|
v-list-item-title <v-icon left v-text='mdiLock'></v-icon> {{$t('admin.block_user')}}
|
||||||
|
|
||||||
|
v-icon.mr-1(v-show='resource.hidden' v-text='mdiEyeOff')
|
||||||
|
|
||||||
a(:href='resource.data.url || resource.data.context')
|
a(:href='resource.data.url || resource.data.context')
|
||||||
small {{resource.data.published|dateFormat('ddd, D MMMM HH:mm')}}
|
small {{resource.data.published|dateFormat('ddd, D MMMM HH:mm')}}
|
||||||
|
@ -108,7 +106,8 @@ v-container#event.pa-0.pa-sm-2
|
||||||
v-card-text
|
v-card-text
|
||||||
|
|
||||||
div.mt-1(v-html='resource_filter(resource.data.content)')
|
div.mt-1(v-html='resource_filter(resource.data.content)')
|
||||||
span(v-for='attachment in resource.data.attachment' :key='attachment.url')
|
div.d-flex.flex-wrap
|
||||||
|
span.mr-1(v-for='attachment in resource.data.attachment' :key='attachment.url')
|
||||||
audio(v-if='isAudio(attachment)' controls)
|
audio(v-if='isAudio(attachment)' controls)
|
||||||
source(:src='attachment.url')
|
source(:src='attachment.url')
|
||||||
v-img.cursorPointer(v-if='isImg(attachment)' :src='attachment.url' @click='showResource(resource)'
|
v-img.cursorPointer(v-if='isImg(attachment)' :src='attachment.url' @click='showResource(resource)'
|
||||||
|
@ -120,10 +119,10 @@ v-container#event.pa-0.pa-sm-2
|
||||||
.text-center.mt-5.mb-5
|
.text-center.mt-5.mb-5
|
||||||
v-btn.mr-2(nuxt icon outlined color='primary'
|
v-btn.mr-2(nuxt icon outlined color='primary'
|
||||||
:to='`/event/${event.prev}`' :disabled='!event.prev')
|
:to='`/event/${event.prev}`' :disabled='!event.prev')
|
||||||
v-icon mdi-arrow-left
|
v-icon(v-text='mdiArrowLeft')
|
||||||
v-btn(nuxt bottom right outlined icon color='primary'
|
v-btn(nuxt bottom right outlined icon color='primary'
|
||||||
:to='`/event/${event.next}`' :disabled='!event.next')
|
:to='`/event/${event.next}`' :disabled='!event.next')
|
||||||
v-icon mdi-arrow-right
|
v-icon(v-text='mdiArrowRight')
|
||||||
|
|
||||||
v-dialog(v-model='showEmbed' width='700px' :fullscreen='$vuetify.breakpoint.xsOnly')
|
v-dialog(v-model='showEmbed' width='700px' :fullscreen='$vuetify.breakpoint.xsOnly')
|
||||||
EmbedEvent(:event='event' @close='showEmbed=false')
|
EmbedEvent(:event='event' @close='showEmbed=false')
|
||||||
|
@ -131,18 +130,22 @@ v-container#event.pa-0.pa-sm-2
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import { mapState } from 'vuex'
|
import { mapState } from 'vuex'
|
||||||
import EventAdmin from './eventAdmin'
|
|
||||||
import EmbedEvent from './embedEvent'
|
|
||||||
import get from 'lodash/get'
|
import get from 'lodash/get'
|
||||||
import moment from 'dayjs'
|
import moment from 'dayjs'
|
||||||
import clipboard from '../../assets/clipboard'
|
import clipboard from '../../assets/clipboard'
|
||||||
|
|
||||||
const htmlToText = require('html-to-text')
|
const htmlToText = require('html-to-text')
|
||||||
|
|
||||||
|
import { mdiArrowLeft, mdiArrowRight, mdiDotsVertical, mdiCodeTags, mdiClose,
|
||||||
|
mdiEye, mdiEyeOff, mdiDelete, mdiRepeat, mdiLock,
|
||||||
|
mdiCalendarExport, mdiCalendar, mdiContentCopy, mdiMapMarker } from '@mdi/js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Event',
|
name: 'Event',
|
||||||
mixins: [clipboard],
|
mixins: [clipboard],
|
||||||
components: { EventAdmin, EmbedEvent },
|
components: {
|
||||||
|
EventAdmin: () => import(/* webpackChunkName: "event" */'./eventAdmin'),
|
||||||
|
EmbedEvent: () => import(/* webpackChunkName: "event" */'./embedEvent'),
|
||||||
|
},
|
||||||
async asyncData ({ $axios, params, error, store }) {
|
async asyncData ({ $axios, params, error, store }) {
|
||||||
try {
|
try {
|
||||||
const event = await $axios.$get(`/event/${params.slug}`)
|
const event = await $axios.$get(`/event/${params.slug}`)
|
||||||
|
@ -153,6 +156,8 @@ export default {
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
|
mdiArrowLeft, mdiArrowRight, mdiDotsVertical, mdiCodeTags, mdiCalendarExport, mdiCalendar,
|
||||||
|
mdiMapMarker, mdiContentCopy, mdiClose, mdiDelete, mdiEye, mdiEyeOff, mdiRepeat, mdiLock,
|
||||||
currentAttachment: 0,
|
currentAttachment: 0,
|
||||||
event: {},
|
event: {},
|
||||||
showEmbed: false,
|
showEmbed: false,
|
||||||
|
|
|
@ -2,10 +2,10 @@
|
||||||
v-card
|
v-card
|
||||||
v-card-title(v-text="$t('common.embed_title')")
|
v-card-title(v-text="$t('common.embed_title')")
|
||||||
v-card-text
|
v-card-text
|
||||||
v-alert.mb-3.mt-1(type='info' show-icon) {{$t('common.embed_help')}}
|
v-alert.mb-3.mt-1(type='info' show-icon :icon='mdiInformation') {{$t('common.embed_help')}}
|
||||||
v-alert.pa-5.my-4.blue-grey.darken-4.text-body-1.lime--text.text--lighten-3 <pre>{{code}}</pre>
|
v-alert.pa-5.my-4.blue-grey.darken-4.text-body-1.lime--text.text--lighten-3 <pre>{{code}}</pre>
|
||||||
v-btn.float-end(text color='primary' @click='clipboard(code)') {{$t("common.copy")}}
|
v-btn.float-end(text color='primary' @click='clipboard(code)') {{$t("common.copy")}}
|
||||||
v-icon.ml-1 mdi-content-copy
|
v-icon.ml-1(v-text='mdiContentCopy')
|
||||||
p.mx-auto
|
p.mx-auto
|
||||||
.mx-auto
|
.mx-auto
|
||||||
gancio-event(:id='event.id' :baseurl='settings.baseurl')
|
gancio-event(:id='event.id' :baseurl='settings.baseurl')
|
||||||
|
@ -17,9 +17,13 @@ v-card
|
||||||
<script>
|
<script>
|
||||||
import { mapState } from 'vuex'
|
import { mapState } from 'vuex'
|
||||||
import clipboard from '../../assets/clipboard'
|
import clipboard from '../../assets/clipboard'
|
||||||
|
import { mdiContentCopy, mdiInformation } from '@mdi/js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'EmbedEvent',
|
name: 'EmbedEvent',
|
||||||
|
data() {
|
||||||
|
return { mdiContentCopy, mdiInformation }
|
||||||
|
},
|
||||||
mixins: [clipboard],
|
mixins: [clipboard],
|
||||||
props: {
|
props: {
|
||||||
event: { type: Object, default: () => ({}) }
|
event: { type: Object, default: () => ({}) }
|
||||||
|
|
|
@ -1,22 +1,25 @@
|
||||||
<template lang='pug'>
|
<template lang='pug'>
|
||||||
div
|
div
|
||||||
v-btn(text color='primary' v-if='event.is_visible' @click='toggle(false)') {{$t(`common.${event.parentId?'skip':'hide'}`)}}
|
v-btn(text color='primary' v-if='event.is_visible' @click='toggle(false)') {{$t(`common.${event.parentId?'skip':'hide'}`)}}
|
||||||
v-btn(text color='success' v-else @click='toggle(false)') <v-icon color='yellow'>mdi-alert</v-icon> {{$t('common.confirm')}}
|
v-btn(text color='success' v-else @click='toggle(false)') <v-icon color='yellow' v-text='mdiAlert'></v-icon> {{$t('common.confirm')}}
|
||||||
v-btn(text color='primary' @click='$router.push(`/add/${event.id}`)') {{$t('common.edit')}}
|
v-btn(text color='primary' @click='$router.push(`/add/${event.id}`)') {{$t('common.edit')}}
|
||||||
v-btn(text color='primary' v-if='!event.parentId' @click='remove(false)') {{$t('common.remove')}}
|
v-btn(text color='primary' v-if='!event.parentId' @click='remove(false)') {{$t('common.remove')}}
|
||||||
|
|
||||||
template(v-if='event.parentId')
|
template(v-if='event.parentId')
|
||||||
v-divider
|
v-divider
|
||||||
span.mr-1 <v-icon>mdi-repeat</v-icon> {{$t('event.edit_recurrent')}}
|
span.mr-1 <v-icon v-text='mdiRepeat'></v-icon> {{$t('event.edit_recurrent')}}
|
||||||
v-btn(text color='primary' v-if='event.parent.is_visible' @click='toggle(true)') {{$t('common.pause')}}
|
v-btn(text color='primary' v-if='event.parent.is_visible' @click='toggle(true)') {{$t('common.pause')}}
|
||||||
v-btn(text color='primary' v-else @click='toggle(true)') {{$t('common.start')}}
|
v-btn(text color='primary' v-else @click='toggle(true)') {{$t('common.start')}}
|
||||||
v-btn(text color='primary' @click='$router.push(`/add/${event.parentId}`)') {{$t('common.edit')}}
|
v-btn(text color='primary' @click='$router.push(`/add/${event.parentId}`)') {{$t('common.edit')}}
|
||||||
v-btn(text color='primary' @click='remove(true)') {{$t('common.remove')}}
|
v-btn(text color='primary' @click='remove(true)') {{$t('common.remove')}}
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
|
import { mdiAlert, mdiRepeat } from '@mdi/js'
|
||||||
export default {
|
export default {
|
||||||
name: 'EventAdmin',
|
name: 'EventAdmin',
|
||||||
|
data () {
|
||||||
|
return { mdiAlert, mdiRepeat }
|
||||||
|
},
|
||||||
props: {
|
props: {
|
||||||
event: {
|
event: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<template lang="pug">
|
<template lang="pug">
|
||||||
v-container
|
v-container.pa-0.pa-md-3
|
||||||
v-card(outlined)
|
v-card
|
||||||
v-card-title {{$t('common.share')}}
|
v-card-title {{$t('common.share')}}
|
||||||
v-card-text
|
v-card-text
|
||||||
p.text-body-1 {{$t('export.intro')}}
|
p.text-body-1 {{$t('export.intro')}}
|
||||||
|
@ -11,7 +11,7 @@
|
||||||
Search(
|
Search(
|
||||||
:filters='filters'
|
:filters='filters'
|
||||||
@update='f => filters = f')
|
@update='f => filters = f')
|
||||||
v-tabs(v-model='type')
|
v-tabs(v-model='type' show-arrows)
|
||||||
|
|
||||||
//- TOFIX
|
//- TOFIX
|
||||||
//- v-tab {{$t('common.email')}}
|
//- v-tab {{$t('common.email')}}
|
||||||
|
@ -31,7 +31,7 @@
|
||||||
p(v-html='$t(`export.feed_description`)')
|
p(v-html='$t(`export.feed_description`)')
|
||||||
v-text-field(v-model='link' readonly)
|
v-text-field(v-model='link' readonly)
|
||||||
v-btn(slot='prepend' text color='primary' @click='clipboard(link)') {{$t("common.copy")}}
|
v-btn(slot='prepend' text color='primary' @click='clipboard(link)') {{$t("common.copy")}}
|
||||||
v-icon.ml-1 mdi-content-copy
|
v-icon.ml-1(v-text='mdiContentCopy')
|
||||||
|
|
||||||
v-tab ics/ical
|
v-tab ics/ical
|
||||||
v-tab-item
|
v-tab-item
|
||||||
|
@ -40,7 +40,7 @@
|
||||||
p(v-html='$t(`export.ical_description`)')
|
p(v-html='$t(`export.ical_description`)')
|
||||||
v-text-field(v-model='link')
|
v-text-field(v-model='link')
|
||||||
v-btn(slot='prepend' text color='primary' @click='clipboard(link)') {{$t("common.copy")}}
|
v-btn(slot='prepend' text color='primary' @click='clipboard(link)') {{$t("common.copy")}}
|
||||||
v-icon.ml-1 mdi-content-copy
|
v-icon.ml-1(v-text='mdiContentCopy')
|
||||||
|
|
||||||
v-tab List
|
v-tab List
|
||||||
v-tab-item
|
v-tab-item
|
||||||
|
@ -49,19 +49,23 @@
|
||||||
p(v-html='$t(`export.list_description`)')
|
p(v-html='$t(`export.list_description`)')
|
||||||
|
|
||||||
v-row
|
v-row
|
||||||
v-col.mr-2(:span='11')
|
v-col.col-12.col-lg-4
|
||||||
v-text-field(v-model='list.title' :label='$t("common.title")')
|
v-text-field(v-model='list.title' :label='$t("common.title")')
|
||||||
v-text-field(v-model='list.maxEvents' type='number' min='1' :label='$t("common.max_events")')
|
v-text-field(v-model='list.maxEvents' type='number' min='1' :label='$t("common.max_events")')
|
||||||
v-col.float-right(:span='12')
|
v-switch(v-model='list.theme' inset true-value='dark' false-value='light' :label="$t('admin.is_dark')")
|
||||||
span {{filters.places.join(',')}}
|
v-switch(v-model='list.sidebar' inset true-value='true' false-value='false' :label="$t('admin.widget')")
|
||||||
|
v-col.col-12.col-lg-8
|
||||||
gancio-events(:baseurl='settings.baseurl'
|
gancio-events(:baseurl='settings.baseurl'
|
||||||
:maxlength='list.maxEvents && Number(list.maxEvents)'
|
:maxlength='list.maxEvents && Number(list.maxEvents)'
|
||||||
:title='list.title'
|
:title='list.title'
|
||||||
|
:theme='list.theme'
|
||||||
:places='filters.places.join(",")'
|
:places='filters.places.join(",")'
|
||||||
:tags='filters.tags.join(",")')
|
:tags='filters.tags.join(",")'
|
||||||
|
:show_recurrent='filters.show_recurrent'
|
||||||
|
:sidebar="list.sidebar")
|
||||||
v-alert.pa-5.my-4.blue-grey.darken-4.text-body-1.lime--text.text--lighten-3 <pre>{{code}}</pre>
|
v-alert.pa-5.my-4.blue-grey.darken-4.text-body-1.lime--text.text--lighten-3 <pre>{{code}}</pre>
|
||||||
v-btn.float-end(text color='primary' @click='clipboard(code)') {{$t("common.copy")}}
|
v-btn.float-end(text color='primary' @click='clipboard(code)') {{$t("common.copy")}}
|
||||||
v-icon.ml-1 mdi-content-copy
|
v-icon.ml-1(v-text='mdiContentCopy')
|
||||||
|
|
||||||
v-tab(v-if='settings.enable_federation') {{$t('common.fediverse')}}
|
v-tab(v-if='settings.enable_federation') {{$t('common.fediverse')}}
|
||||||
v-tab-item(v-if='settings.enable_federation')
|
v-tab-item(v-if='settings.enable_federation')
|
||||||
|
@ -79,14 +83,17 @@
|
||||||
<script>
|
<script>
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import { mapState } from 'vuex'
|
import { mapState } from 'vuex'
|
||||||
import List from '@/components/List'
|
|
||||||
import FollowMe from '../components/FollowMe'
|
import FollowMe from '../components/FollowMe'
|
||||||
import Search from '@/components/Search'
|
import Search from '@/components/Search'
|
||||||
import clipboard from '../assets/clipboard'
|
import clipboard from '../assets/clipboard'
|
||||||
|
import { mdiContentCopy } from '@mdi/js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Exports',
|
name: 'Exports',
|
||||||
components: { List, FollowMe, Search },
|
components: {
|
||||||
|
FollowMe,
|
||||||
|
Search
|
||||||
|
},
|
||||||
mixins: [clipboard],
|
mixins: [clipboard],
|
||||||
async asyncData ({ $axios, params, store, $api }) {
|
async asyncData ({ $axios, params, store, $api }) {
|
||||||
const events = await $api.getEvents({
|
const events = await $api.getEvents({
|
||||||
|
@ -95,18 +102,24 @@ export default {
|
||||||
})
|
})
|
||||||
return { events }
|
return { events }
|
||||||
},
|
},
|
||||||
data () {
|
data ({ $store }) {
|
||||||
return {
|
return {
|
||||||
|
mdiContentCopy,
|
||||||
type: 'rss',
|
type: 'rss',
|
||||||
notification: { email: '' },
|
notification: { email: '' },
|
||||||
list: { title: 'Gancio', maxEvents: null },
|
list: {
|
||||||
|
title: $store.state.settings.title,
|
||||||
|
maxEvents: null,
|
||||||
|
theme: $store.state.settings['theme.is_dark'] ? 'dark' : 'light',
|
||||||
|
sidebar: 'true'
|
||||||
|
},
|
||||||
filters: { tags: [], places: [], show_recurrent: false },
|
filters: { tags: [], places: [], show_recurrent: false },
|
||||||
events: []
|
events: []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
head () {
|
head () {
|
||||||
return {
|
return {
|
||||||
title: `${this.settings.title} - ${this.$t('common.export')}`
|
title: `${this.settings.title} - ${this.$t('common.export')}`,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -114,7 +127,7 @@ export default {
|
||||||
code () {
|
code () {
|
||||||
const params = [`baseurl="${this.settings.baseurl}"`]
|
const params = [`baseurl="${this.settings.baseurl}"`]
|
||||||
|
|
||||||
if (this.list.title) {
|
if (this.list.title && this.list.sidebar === 'true') {
|
||||||
params.push(`title="${this.list.title}"`)
|
params.push(`title="${this.list.title}"`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,23 +140,27 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.filters.show_recurrent) {
|
if (this.filters.show_recurrent) {
|
||||||
params.push('show_recurrent')
|
params.push(`show_recurrent="${this.filters.show_recurrent}"`)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.list.maxEvents) {
|
if (this.list.maxEvents) {
|
||||||
params.push('maxlength=' + this.list.maxEvents)
|
params.push('maxlength=' + this.list.maxEvents)
|
||||||
}
|
}
|
||||||
|
|
||||||
return `<script src="${this.settings.baseurl}\/gancio-events.es.js'><\/script>\n<gancio-events ${params.join(' ')}></gancio-events>\n\n`
|
params.push('sidebar="' + this.list.sidebar + '"')
|
||||||
|
|
||||||
|
params.push(`theme="${this.list.theme}"`)
|
||||||
|
|
||||||
|
return `<script src="${this.settings.baseurl}\/gancio-events.es.js"><\/script>\n<gancio-events ${params.join(' ')}></gancio-events>\n\n`
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
link () {
|
link () {
|
||||||
const typeMap = ['rss', 'ics', 'list']
|
const typeMap = ['rss', 'ics']
|
||||||
const params = []
|
const params = []
|
||||||
|
|
||||||
if (this.filters.tags.length) {
|
if (this.filters.tags.length) {
|
||||||
params.push(`tags=${this.filters.tags.join(',')}`)
|
params.push(`tags=${this.filters.tags.map(encodeURIComponent).join(',')}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.filters.places.length) {
|
if (this.filters.places.length) {
|
||||||
|
@ -154,7 +171,7 @@ export default {
|
||||||
params.push('show_recurrent=true')
|
params.push('show_recurrent=true')
|
||||||
}
|
}
|
||||||
|
|
||||||
return `${this.settings.baseurl}/feed/${typeMap[this.type]}?${params.join('&')}`
|
return `${this.settings.baseurl}/feed/${typeMap[this.type]}${params.length ? '?' : ''}${params.join('&')}`
|
||||||
},
|
},
|
||||||
showLink () {
|
showLink () {
|
||||||
return (['rss', 'ics'].includes(this.type))
|
return (['rss', 'ics'].includes(this.type))
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
//- Calendar and search bar
|
//- Calendar and search bar
|
||||||
v-row.pt-0.pt-sm-2.pl-0.pl-sm-2
|
v-row.pt-0.pt-sm-2.pl-0.pl-sm-2
|
||||||
.col-xl-5.col-lg-5.col-md-7.col-sm-12.col-xs-12.pa-4.pa-sm-3
|
#calh.col-xl-5.col-lg-5.col-md-7.col-sm-12.col-xs-12.pa-4.pa-sm-3
|
||||||
//- this is needed as v-calendar does not support SSR
|
//- this is needed as v-calendar does not support SSR
|
||||||
//- https://github.com/nathanreyes/v-calendar/issues/336
|
//- https://github.com/nathanreyes/v-calendar/issues/336
|
||||||
client-only(placeholder='Calendar unavailable without js')
|
client-only(placeholder='Calendar unavailable without js')
|
||||||
|
@ -15,12 +15,11 @@
|
||||||
|
|
||||||
.col.pt-0.pt-md-2
|
.col.pt-0.pt-md-2
|
||||||
Search(:filters='filters' @update='updateFilters')
|
Search(:filters='filters' @update='updateFilters')
|
||||||
v-chip(v-if='selectedDay' close @click:close='dayChange({ date: selectedDay})') {{selectedDay}}
|
v-chip(v-if='selectedDay' close :close-icon='mdiCloseCircle' @click:close='dayChange()') {{selectedDay}}
|
||||||
|
|
||||||
//- Events
|
//- Events
|
||||||
#events.mb-2.mt-1.pl-1.pl-sm-2
|
#events.mb-2.mt-1.pl-1.pl-sm-2
|
||||||
//- div.event(v-for='(event, idx) in events' :key='event.id' v-intersect="(entries, observer, isIntersecting) => intersecting[event.id] = isIntersecting")
|
Event(:event='event' @destroy='destroy' v-for='(event, idx) in visibleEvents' :lazy='idx>2' :key='event.id' @tagclick='tagClick' @placeclick='placeClick')
|
||||||
Event(:event='event' @destroy='destroy' v-for='(event, idx) in visibleEvents' :key='event.id' @tagclick='tagClick' @placeclick='placeClick')
|
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -32,6 +31,7 @@ import Event from '@/components/Event'
|
||||||
import Announcement from '@/components/Announcement'
|
import Announcement from '@/components/Announcement'
|
||||||
import Search from '@/components/Search'
|
import Search from '@/components/Search'
|
||||||
import Calendar from '@/components/Calendar'
|
import Calendar from '@/components/Calendar'
|
||||||
|
import { mdiCloseCircle } from '@mdi/js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Index',
|
name: 'Index',
|
||||||
|
@ -47,6 +47,7 @@ export default {
|
||||||
},
|
},
|
||||||
data ({ $store }) {
|
data ({ $store }) {
|
||||||
return {
|
return {
|
||||||
|
mdiCloseCircle,
|
||||||
first: true,
|
first: true,
|
||||||
isCurrentMonth: true,
|
isCurrentMonth: true,
|
||||||
now: dayjs().unix(),
|
now: dayjs().unix(),
|
||||||
|
@ -174,12 +175,7 @@ export default {
|
||||||
this.setFilters(filters)
|
this.setFilters(filters)
|
||||||
},
|
},
|
||||||
dayChange (day) {
|
dayChange (day) {
|
||||||
const date = dayjs(day.date).format('YYYY-MM-DD')
|
this.selectedDay = day ? dayjs(day).format('YYYY-MM-DD') : null
|
||||||
if (this.selectedDay === date) {
|
|
||||||
this.selectedDay = null
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.selectedDay = date
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,15 +2,18 @@
|
||||||
v-container
|
v-container
|
||||||
v-card-title.d-block.text-h5.text-center(v-text="$t('setup.completed')")
|
v-card-title.d-block.text-h5.text-center(v-text="$t('setup.completed')")
|
||||||
v-card-text(v-html="$t('setup.completed_description', user)")
|
v-card-text(v-html="$t('setup.completed_description', user)")
|
||||||
|
v-alert.mb-3.mt-1(outlined type='warning' color='red' show-icon :icon='mdiAlert') {{$t('setup.copy_password_dialog')}}
|
||||||
v-card-actions
|
v-card-actions
|
||||||
v-btn(text @click='next' color='primary' :loading='loading' :disabled='loading') {{$t('setup.start')}}
|
v-btn(text @click='next' color='primary' :loading='loading' :disabled='loading') {{$t('setup.start')}}
|
||||||
v-icon mdi-arrow-right
|
v-icon(v-text='mdiArrowRight')
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
|
import { mdiArrowRight, mdiAlert } from '@mdi/js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
|
mdiArrowRight, mdiAlert,
|
||||||
loading: false,
|
loading: false,
|
||||||
user: {
|
user: {
|
||||||
email: 'admin',
|
email: 'admin',
|
||||||
|
|
|
@ -6,25 +6,30 @@
|
||||||
v-btn-toggle(text color='primary' v-model='db.dialect')
|
v-btn-toggle(text color='primary' v-model='db.dialect')
|
||||||
v-btn(value='sqlite' text) sqlite
|
v-btn(value='sqlite' text) sqlite
|
||||||
v-btn(value='postgres' text) postgres
|
v-btn(value='postgres' text) postgres
|
||||||
|
v-btn(value='mariadb' text) mariadb
|
||||||
template(v-if='db.dialect === "sqlite"')
|
template(v-if='db.dialect === "sqlite"')
|
||||||
v-text-field(v-model='db.storage' label='Path')
|
v-text-field(v-model='db.storage' label='Path')
|
||||||
template(v-if='db.dialect === "postgres"')
|
template(v-if='db.dialect !== "sqlite"')
|
||||||
v-text-field(v-model='db.hostname' label='Hostname' :rules="[$validators.required('hostname')]")
|
v-text-field(v-model='db.host' label='Hostname' :rules="[$validators.required('hostname')]")
|
||||||
v-text-field(v-model='db.database' label='Database' :rules="[$validators.required('database')]")
|
v-text-field(v-model='db.database' label='Database' :rules="[$validators.required('database')]")
|
||||||
v-text-field(v-model='db.username' label='Username' :rules="[$validators.required('username')]")
|
v-text-field(v-model='db.username' label='Username' :rules="[$validators.required('username')]")
|
||||||
v-text-field(type='password' v-model='db.password' label='Password' :rules="[$validators.required('password')]")
|
v-text-field(type='password' v-model='db.password' label='Password' :rules="[$validators.required('password')]")
|
||||||
|
|
||||||
v-card-actions
|
v-card-actions
|
||||||
v-btn(text @click='checkDb' color='primary' :loading='loading' :disabled='loading') {{$t('common.next')}}
|
v-btn(text @click='checkDb' color='primary' :loading='loading' :disabled='loading') {{$t('common.next')}}
|
||||||
v-icon mdi-arrow-right
|
v-icon(v-text='mdiArrowRight')
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
|
import { mdiArrowRight } from '@mdi/js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
|
mdiArrowRight,
|
||||||
db: {
|
db: {
|
||||||
|
dialect: 'sqlite',
|
||||||
storage: './gancio.sqlite',
|
storage: './gancio.sqlite',
|
||||||
hostname: 'localhost',
|
host: 'localhost',
|
||||||
database: 'gancio'
|
database: 'gancio'
|
||||||
},
|
},
|
||||||
loading: false
|
loading: false
|
||||||
|
|
|
@ -4,14 +4,14 @@
|
||||||
h2.mb-2.text-center Gancio Setup
|
h2.mb-2.text-center Gancio Setup
|
||||||
v-stepper.grey.lighten-5(v-model='step')
|
v-stepper.grey.lighten-5(v-model='step')
|
||||||
v-stepper-header
|
v-stepper-header
|
||||||
v-stepper-step(:complete='step > 1' step='1') Database
|
v-stepper-step(v-show='!dbdone' :complete='step > 1' step='1') Database
|
||||||
v-divider
|
v-divider(v-show='!dbdone')
|
||||||
v-stepper-step(:complete='step > 2' step='2') Configuration
|
v-stepper-step(:complete='step > 2' step='2') Configuration
|
||||||
v-divider
|
v-divider
|
||||||
v-stepper-step(:complete='step > 3' step='3') Finish
|
v-stepper-step(:complete='step > 3' step='3') Finish
|
||||||
|
|
||||||
v-stepper-items
|
v-stepper-items
|
||||||
v-stepper-content(step='1')
|
v-stepper-content(v-show='!dbdone' step='1')
|
||||||
DbStep(@complete='dbCompleted')
|
DbStep(@complete='dbCompleted')
|
||||||
v-stepper-content(step='2')
|
v-stepper-content(step='2')
|
||||||
Settings(setup, @complete='configCompleted')
|
Settings(setup, @complete='configCompleted')
|
||||||
|
@ -36,14 +36,16 @@ export default {
|
||||||
title: 'Setup',
|
title: 'Setup',
|
||||||
},
|
},
|
||||||
auth: false,
|
auth: false,
|
||||||
data () {
|
asyncData ({ params }) {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
dbdone: !!Number(params.db),
|
||||||
config: {
|
config: {
|
||||||
db: {
|
db: {
|
||||||
dialect: ''
|
dialect: ''
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
step: 1
|
step: 1 + Number(params.db)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
|
@ -3,6 +3,8 @@ import dayjs from 'dayjs'
|
||||||
import relativeTime from 'dayjs/plugin/relativeTime'
|
import relativeTime from 'dayjs/plugin/relativeTime'
|
||||||
import utc from 'dayjs/plugin/utc'
|
import utc from 'dayjs/plugin/utc'
|
||||||
import timezone from 'dayjs/plugin/timezone'
|
import timezone from 'dayjs/plugin/timezone'
|
||||||
|
import localizedFormat from 'dayjs/plugin/localizedFormat'
|
||||||
|
|
||||||
|
|
||||||
import 'dayjs/locale/it'
|
import 'dayjs/locale/it'
|
||||||
import 'dayjs/locale/es'
|
import 'dayjs/locale/es'
|
||||||
|
@ -15,6 +17,7 @@ import 'dayjs/locale/fr'
|
||||||
dayjs.extend(relativeTime)
|
dayjs.extend(relativeTime)
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
dayjs.extend(timezone)
|
dayjs.extend(timezone)
|
||||||
|
dayjs.extend(localizedFormat)
|
||||||
|
|
||||||
export default ({ app, store }) => {
|
export default ({ app, store }) => {
|
||||||
// set timezone to instance_timezone!!
|
// set timezone to instance_timezone!!
|
||||||
|
|
|
@ -4,12 +4,12 @@ import merge from 'lodash/merge'
|
||||||
|
|
||||||
Vue.use(VueI18n)
|
Vue.use(VueI18n)
|
||||||
|
|
||||||
export default async ({ app, store, req }) => {
|
export default async ({ app, store, res }) => {
|
||||||
const messages = {}
|
const messages = {}
|
||||||
if (process.server) {
|
if (process.server) {
|
||||||
store.commit('setLocale', req.acceptedLocale)
|
store.commit('setLocale', res.locals.acceptedLocale)
|
||||||
if (req.user_locale) {
|
if (res.locals.user_locale) {
|
||||||
store.commit('setUserLocale', req.user_locale)
|
store.commit('setUserLocale', res.locals.user_locale)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,34 +0,0 @@
|
||||||
import Vue from 'vue'
|
|
||||||
import Vuetify from 'vuetify'
|
|
||||||
|
|
||||||
// import it from 'vuetify/lib/locale/it.js'
|
|
||||||
// import en from 'vuetify/lib/locale/en.js'
|
|
||||||
// import es from 'vuetify/lib/locale/es'
|
|
||||||
// import no from 'vuetify/lib/locale/no'
|
|
||||||
// import fr from 'vuetify/lib/locale/fr'
|
|
||||||
// import ca from 'vuetify/lib/locale/ca'
|
|
||||||
|
|
||||||
|
|
||||||
export default ({ app }) => {
|
|
||||||
Vue.use(Vuetify)
|
|
||||||
app.vuetify = new Vuetify({
|
|
||||||
// lang: {
|
|
||||||
// locales: { en, it }, //, es, fr, no, ca },
|
|
||||||
// current: 'en'
|
|
||||||
// },
|
|
||||||
icons: {
|
|
||||||
iconfont: 'mdi'
|
|
||||||
},
|
|
||||||
theme: {
|
|
||||||
dark: true,
|
|
||||||
themes: {
|
|
||||||
dark: {
|
|
||||||
primary: '#FF6E40'
|
|
||||||
},
|
|
||||||
light: {
|
|
||||||
primary: '#FF4500'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
12
release.sh
Executable file
12
release.sh
Executable file
|
@ -0,0 +1,12 @@
|
||||||
|
RELEASE=v$(cat package.json | jq ".version" | sed -e 's/"//g')
|
||||||
|
echo "Releasing $RELEASE"
|
||||||
|
yarn build
|
||||||
|
yarn pack
|
||||||
|
yarn publish
|
||||||
|
yarn doc
|
||||||
|
gpg --detach-sign --local-user 5DAC477D5441B7A15ACBF680BBEB4DD39AC6CCA9 gancio-$RELEASE.tgz
|
||||||
|
cp gancio-$RELEASE.tgz releases/
|
||||||
|
mv gancio-$RELEASE.tgz releases/latest.tgz
|
||||||
|
cp gancio-$RELEASE.tgz.sig releases/
|
||||||
|
mv gancio-$RELEASE.tgz.sig releases/latest.tgz.sig
|
||||||
|
rsync -a docs/_site/ gancio.org:/var/www/gancio/
|
|
@ -16,24 +16,24 @@ const Auth = {
|
||||||
}
|
}
|
||||||
|
|
||||||
oauth.oauthServer.authenticate()(req, res, () => {
|
oauth.oauthServer.authenticate()(req, res, () => {
|
||||||
req.user = get(res, 'locals.oauth.token.user', null)
|
res.locals.user = get(res, 'locals.oauth.token.user', null)
|
||||||
next()
|
next()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
isAuth (req, res, next) {
|
isAuth (req, res, next) {
|
||||||
if (req.user) {
|
if (res.locals.user) {
|
||||||
next()
|
next()
|
||||||
} else {
|
} else {
|
||||||
res.sendStatus(404)
|
res.sendStatus(403)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
isAdmin (req, res, next) {
|
isAdmin (req, res, next) {
|
||||||
if (req.user.is_admin) {
|
if (res.locals.user && res.locals.user.is_admin) {
|
||||||
next()
|
next()
|
||||||
} else {
|
} else {
|
||||||
res.status(404)
|
res.sendStatus(403)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -100,7 +100,7 @@ const eventController = {
|
||||||
|
|
||||||
async get (req, res) {
|
async get (req, res) {
|
||||||
const format = req.params.format || 'json'
|
const format = req.params.format || 'json'
|
||||||
const is_admin = req.user && req.user.is_admin
|
const is_admin = res.locals.user && res.locals.user.is_admin
|
||||||
const slug = req.params.event_slug
|
const slug = req.params.event_slug
|
||||||
|
|
||||||
// retrocompatibility, old events URL does not use slug, use id as fallback
|
// retrocompatibility, old events URL does not use slug, use id as fallback
|
||||||
|
@ -145,21 +145,35 @@ const eventController = {
|
||||||
const next = await Event.findOne({
|
const next = await Event.findOne({
|
||||||
attributes: ['id', 'slug'],
|
attributes: ['id', 'slug'],
|
||||||
where: {
|
where: {
|
||||||
|
id: { [Op.not]: event.id },
|
||||||
is_visible: true,
|
is_visible: true,
|
||||||
recurrent: null,
|
recurrent: null,
|
||||||
start_datetime: { [Op.gt]: event.start_datetime }
|
[Op.or]: [
|
||||||
|
{ start_datetime: { [Op.gt]: event.start_datetime } },
|
||||||
|
{
|
||||||
|
start_datetime: event.start_datetime,
|
||||||
|
id: { [Op.gt]: event.id }
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
order: [['start_datetime', 'ASC']]
|
order: [['start_datetime', 'ASC'], ['id', 'ASC']]
|
||||||
})
|
})
|
||||||
|
|
||||||
const prev = await Event.findOne({
|
const prev = await Event.findOne({
|
||||||
attributes: ['id', 'slug'],
|
attributes: ['id', 'slug'],
|
||||||
where: {
|
where: {
|
||||||
is_visible: true,
|
is_visible: true,
|
||||||
|
id: { [Op.not]: event.id },
|
||||||
recurrent: null,
|
recurrent: null,
|
||||||
start_datetime: { [Op.lt]: event.start_datetime }
|
[Op.or]: [
|
||||||
|
{ start_datetime: { [Op.lt]: event.start_datetime } },
|
||||||
|
{
|
||||||
|
start_datetime: event.start_datetime,
|
||||||
|
id: { [Op.lt]: event.id }
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
order: [['start_datetime', 'DESC']]
|
order: [['start_datetime', 'DESC'], ['id', 'DESC']]
|
||||||
})
|
})
|
||||||
|
|
||||||
// TODO: also check if event is mine
|
// TODO: also check if event is mine
|
||||||
|
@ -192,7 +206,7 @@ const eventController = {
|
||||||
log.warn(`Trying to confirm a unknown event, id: ${id}`)
|
log.warn(`Trying to confirm a unknown event, id: ${id}`)
|
||||||
return res.sendStatus(404)
|
return res.sendStatus(404)
|
||||||
}
|
}
|
||||||
if (!req.user.is_admin && req.user.id !== event.userId) {
|
if (!res.locals.user.is_admin && res.locals.user.id !== event.userId) {
|
||||||
log.warn(`Someone unallowed is trying to confirm -> "${event.title} `)
|
log.warn(`Someone unallowed is trying to confirm -> "${event.title} `)
|
||||||
return res.sendStatus(403)
|
return res.sendStatus(403)
|
||||||
}
|
}
|
||||||
|
@ -218,7 +232,7 @@ const eventController = {
|
||||||
const id = Number(req.params.event_id)
|
const id = Number(req.params.event_id)
|
||||||
const event = await Event.findByPk(id)
|
const event = await Event.findByPk(id)
|
||||||
if (!event) { return req.sendStatus(404) }
|
if (!event) { return req.sendStatus(404) }
|
||||||
if (!req.user.is_admin && req.user.id !== event.userId) {
|
if (!res.locals.user.is_admin && res.locals.user.id !== event.userId) {
|
||||||
return res.sendStatus(403)
|
return res.sendStatus(403)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -276,6 +290,13 @@ const eventController = {
|
||||||
res.sendStatus(200)
|
res.sendStatus(200)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async isAnonEventAllowed (req, res, next) {
|
||||||
|
if (!res.locals.settings.allow_anon_event) {
|
||||||
|
return res.sendStatus(403)
|
||||||
|
}
|
||||||
|
next()
|
||||||
|
},
|
||||||
|
|
||||||
async add (req, res) {
|
async add (req, res) {
|
||||||
// req.err comes from multer streaming error
|
// req.err comes from multer streaming error
|
||||||
if (req.err) {
|
if (req.err) {
|
||||||
|
@ -287,21 +308,23 @@ const eventController = {
|
||||||
const body = req.body
|
const body = req.body
|
||||||
const recurrent = body.recurrent ? JSON.parse(body.recurrent) : null
|
const recurrent = body.recurrent ? JSON.parse(body.recurrent) : null
|
||||||
|
|
||||||
if (!body.place_name) {
|
const required_fields = [ 'title', 'place_name', 'start_datetime']
|
||||||
log.warn('Place is required')
|
const missing_field = required_fields.find(required_field => !body[required_field])
|
||||||
return res.status(400).send('Place is required')
|
if (missing_field) {
|
||||||
|
log.warn(`${missing_field} is required`)
|
||||||
|
return res.status(400).send(`${missing_field} is required`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const eventDetails = {
|
const eventDetails = {
|
||||||
title: body.title,
|
title: body.title,
|
||||||
// remove html tags
|
// remove html tags
|
||||||
description: helpers.sanitizeHTML(linkifyHtml(body.description)),
|
description: helpers.sanitizeHTML(linkifyHtml(body.description || '')),
|
||||||
multidate: body.multidate,
|
multidate: body.multidate,
|
||||||
start_datetime: body.start_datetime,
|
start_datetime: body.start_datetime,
|
||||||
end_datetime: body.end_datetime,
|
end_datetime: body.end_datetime,
|
||||||
recurrent,
|
recurrent,
|
||||||
// publish this event only if authenticated
|
// publish this event only if authenticated
|
||||||
is_visible: !!req.user
|
is_visible: !!res.locals.user
|
||||||
}
|
}
|
||||||
|
|
||||||
if (req.file || body.image_url) {
|
if (req.file || body.image_url) {
|
||||||
|
@ -344,9 +367,9 @@ const eventController = {
|
||||||
}
|
}
|
||||||
|
|
||||||
// associate user to event and reverse
|
// associate user to event and reverse
|
||||||
if (req.user) {
|
if (res.locals.user) {
|
||||||
await req.user.addEvent(event)
|
await res.locals.user.addEvent(event)
|
||||||
await event.setUser(req.user)
|
await event.setUser(res.locals.user)
|
||||||
}
|
}
|
||||||
|
|
||||||
// return created event to the client
|
// return created event to the client
|
||||||
|
@ -368,15 +391,15 @@ const eventController = {
|
||||||
},
|
},
|
||||||
|
|
||||||
async update (req, res) {
|
async update (req, res) {
|
||||||
if (req.err) {
|
if (res.locals.err) {
|
||||||
return res.status(400).json(req.err.toString())
|
return res.status(400).json(res.locals.err.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const body = req.body
|
const body = req.body
|
||||||
const event = await Event.findByPk(body.id)
|
const event = await Event.findByPk(body.id)
|
||||||
if (!event) { return res.sendStatus(404) }
|
if (!event) { return res.sendStatus(404) }
|
||||||
if (!req.user.is_admin && event.userId !== req.user.id) {
|
if (!res.locals.user.is_admin && event.userId !== res.locals.user.id) {
|
||||||
return res.sendStatus(403)
|
return res.sendStatus(403)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -456,7 +479,7 @@ const eventController = {
|
||||||
async remove (req, res) {
|
async remove (req, res) {
|
||||||
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 && (res.locals.user.is_admin || res.locals.user.id === event.userId)) {
|
||||||
if (event.media && event.media.length && !event.recurrent) {
|
if (event.media && event.media.length && !event.recurrent) {
|
||||||
const old_path = path.join(config.upload_path, event.media[0].url)
|
const old_path = path.join(config.upload_path, event.media[0].url)
|
||||||
const old_thumb_path = path.join(config.upload_path, 'thumb', event.media[0].url)
|
const old_thumb_path = path.join(config.upload_path, 'thumb', event.media[0].url)
|
||||||
|
@ -504,13 +527,19 @@ const eventController = {
|
||||||
where.start_datetime = { [Op.lte]: end }
|
where.start_datetime = { [Op.lte]: end }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (places) {
|
if (tags && places) {
|
||||||
where.placeId = places.split(',')
|
where[Op.or] = {
|
||||||
|
placeId: places ? places.split(',') : [],
|
||||||
|
'$tags.tag$': tags.split(',')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let where_tags = {}
|
|
||||||
if (tags) {
|
if (tags) {
|
||||||
where_tags = { where: { [Op.or]: { tag: tags.split(',') } } }
|
where['$tags.tag$'] = tags.split(',')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (places) {
|
||||||
|
where.placeId = places.split(',')
|
||||||
}
|
}
|
||||||
|
|
||||||
const events = await Event.findAll({
|
const events = await Event.findAll({
|
||||||
|
@ -526,7 +555,6 @@ const eventController = {
|
||||||
order: [Sequelize.literal('(SELECT COUNT("tagTag") FROM event_tags WHERE tagTag = tag) DESC')],
|
order: [Sequelize.literal('(SELECT COUNT("tagTag") FROM event_tags WHERE tagTag = tag) DESC')],
|
||||||
attributes: ['tag'],
|
attributes: ['tag'],
|
||||||
required: !!tags,
|
required: !!tags,
|
||||||
...where_tags,
|
|
||||||
through: { attributes: [] }
|
through: { attributes: [] }
|
||||||
},
|
},
|
||||||
{ model: Place, required: true, attributes: ['id', 'name', 'address'] }
|
{ model: Place, required: true, attributes: ['id', 'name', 'address'] }
|
||||||
|
@ -548,13 +576,15 @@ const eventController = {
|
||||||
* Select events based on params
|
* Select events based on params
|
||||||
*/
|
*/
|
||||||
async select (req, res) {
|
async select (req, res) {
|
||||||
|
const settings = res.locals.settings
|
||||||
const start = req.query.start || dayjs().unix()
|
const start = req.query.start || dayjs().unix()
|
||||||
const end = req.query.end
|
const end = req.query.end
|
||||||
const tags = req.query.tags
|
const tags = req.query.tags
|
||||||
const places = req.query.places
|
const places = req.query.places
|
||||||
const max = req.query.max
|
const max = req.query.max
|
||||||
const show_recurrent = settingsController.settings.allow_recurrent_event &&
|
|
||||||
(typeof req.query.show_recurrent !== 'undefined' ? req.query.show_recurrent === 'true' : settingsController.settings.recurrent_event_visible)
|
const show_recurrent = settings.allow_recurrent_event &&
|
||||||
|
typeof req.query.show_recurrent !== 'undefined' ? req.query.show_recurrent === 'true' : settings.recurrent_event_visible
|
||||||
|
|
||||||
res.json(await eventController._select({
|
res.json(await eventController._select({
|
||||||
start, end, places, tags, show_recurrent, max
|
start, end, places, tags, show_recurrent, max
|
||||||
|
@ -563,9 +593,8 @@ 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) {
|
async _createRecurrentOccurrence (e, startAt) {
|
||||||
log.debug(`Create recurrent event [${e.id}] ${e.title}"`)
|
log.debug(`Create recurrent event [${e.id}] ${e.title}"`)
|
||||||
const event = {
|
const event = {
|
||||||
parentId: e.id,
|
parentId: e.id,
|
||||||
|
@ -579,23 +608,20 @@ const eventController = {
|
||||||
|
|
||||||
const recurrent = e.recurrent
|
const recurrent = e.recurrent
|
||||||
const start_date = dayjs.unix(e.start_datetime)
|
const start_date = dayjs.unix(e.start_datetime)
|
||||||
const now = dayjs()
|
let cursor = start_date > startAt ? start_date : startAt
|
||||||
let cursor = start_date > now ? start_date : now
|
startAt = cursor
|
||||||
const duration = dayjs.unix(e.end_datetime).diff(start_date, 's')
|
const duration = dayjs.unix(e.end_datetime).diff(start_date, 's')
|
||||||
const frequency = recurrent.frequency
|
const frequency = recurrent.frequency
|
||||||
const type = recurrent.type
|
const type = recurrent.type
|
||||||
|
|
||||||
log.info(`NOW IS ${cursor} while event is at ${start_date} (freq: ${frequency})`)
|
|
||||||
|
|
||||||
cursor = cursor.hour(start_date.hour()).minute(start_date.minute()).second(0)
|
cursor = cursor.hour(start_date.hour()).minute(start_date.minute()).second(0)
|
||||||
log.info(`set cursor to correct date and hour => ${cursor}`)
|
|
||||||
|
|
||||||
if (!frequency) { return }
|
if (!frequency) { return }
|
||||||
|
|
||||||
// each week or 2
|
// each week or 2
|
||||||
if (frequency[1] === 'w') {
|
if (frequency[1] === 'w') {
|
||||||
cursor = cursor.day(start_date.day())
|
cursor = cursor.day(start_date.day())
|
||||||
if (cursor.isBefore(dayjs())) {
|
if (cursor.isBefore(startAt)) {
|
||||||
cursor = cursor.add(7, 'day')
|
cursor = cursor.add(7, 'day')
|
||||||
}
|
}
|
||||||
if (frequency[0] === '2') {
|
if (frequency[0] === '2') {
|
||||||
|
@ -605,24 +631,24 @@ const eventController = {
|
||||||
if (type === 'ordinal') {
|
if (type === 'ordinal') {
|
||||||
cursor = cursor.date(start_date.date())
|
cursor = cursor.date(start_date.date())
|
||||||
|
|
||||||
if (cursor.isBefore(dayjs())) {
|
if (cursor.isBefore(startAt)) {
|
||||||
cursor = cursor.add(1, 'month')
|
cursor = cursor.add(1, 'month')
|
||||||
}
|
}
|
||||||
} else { // weekday
|
} else { // weekday
|
||||||
// get weekday
|
// get weekday
|
||||||
log.info(type)
|
|
||||||
// get recurrent freq details
|
// get recurrent freq details
|
||||||
cursor = helpers.getWeekdayN(cursor, type, start_date.day())
|
cursor = helpers.getWeekdayN(cursor, type, start_date.day())
|
||||||
if (cursor.isBefore(dayjs())) {
|
if (cursor.isBefore(startAt)) {
|
||||||
cursor = cursor.add(4, 'week')
|
cursor = cursor.add(4, 'week')
|
||||||
cursor = helpers.getWeekdayN(cursor, type, start_date.day())
|
cursor = helpers.getWeekdayN(cursor, type, start_date.day())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log.info(cursor)
|
log.debug(cursor)
|
||||||
event.start_datetime = cursor.unix()
|
event.start_datetime = cursor.unix()
|
||||||
event.end_datetime = event.start_datetime + duration
|
event.end_datetime = event.start_datetime + duration
|
||||||
Event.create(event)
|
const newEvent = await Event.create(event)
|
||||||
|
return newEvent.addTags(e.tags)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -632,13 +658,19 @@ const eventController = {
|
||||||
// select recurrent events and its childs
|
// select recurrent events and its childs
|
||||||
const events = await Event.findAll({
|
const events = await Event.findAll({
|
||||||
where: { is_visible: true, recurrent: { [Op.ne]: null } },
|
where: { is_visible: true, recurrent: { [Op.ne]: null } },
|
||||||
include: [{ model: Event, as: 'child', required: false, where: { start_datetime: { [Op.gte]: start_datetime } } }],
|
include: [{ model: Tag, required: false },
|
||||||
order: ['start_datetime']
|
{ model: Event, as: 'child', required: false, where: { start_datetime: { [Op.gte]: start_datetime } }}],
|
||||||
|
order: [['child', 'start_datetime', 'DESC']]
|
||||||
|
})
|
||||||
|
|
||||||
|
// create a new occurrence for each recurring events but the one's that has an already visible occurrence coming
|
||||||
|
const creations = events.map(e => {
|
||||||
|
if (e.child.length) {
|
||||||
|
if (e.child.find(c => c.is_visible)) return
|
||||||
|
return eventController._createRecurrentOccurrence(e, dayjs.unix(e.child[0].start_datetime+1))
|
||||||
|
}
|
||||||
|
return eventController._createRecurrentOccurrence(e, dayjs())
|
||||||
})
|
})
|
||||||
// filter events that as no instance in future yet
|
|
||||||
const creations = events
|
|
||||||
.filter(e => e.child.length === 0)
|
|
||||||
.map(eventController._createRecurrentOccurrence)
|
|
||||||
|
|
||||||
return Promise.all(creations)
|
return Promise.all(creations)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ const Event = require('../models/event')
|
||||||
const Place = require('../models/place')
|
const Place = require('../models/place')
|
||||||
const Tag = require('../models/tag')
|
const Tag = require('../models/tag')
|
||||||
|
|
||||||
const { Op } = require('sequelize')
|
const { Op, literal } = require('sequelize')
|
||||||
const moment = require('dayjs')
|
const moment = require('dayjs')
|
||||||
const ics = require('ics')
|
const ics = require('ics')
|
||||||
|
|
||||||
|
@ -16,10 +16,17 @@ const exportController = {
|
||||||
|
|
||||||
const where = {}
|
const where = {}
|
||||||
const yesterday = moment().subtract('1', 'day').unix()
|
const yesterday = moment().subtract('1', 'day').unix()
|
||||||
let where_tags = {}
|
|
||||||
|
|
||||||
|
if (tags && places) {
|
||||||
|
where[Op.or] = {
|
||||||
|
placeId: places ? places.split(',') : [],
|
||||||
|
'$tags.tag$': tags.split(',')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (tags) {
|
if (tags) {
|
||||||
where_tags = { where: { tag: tags.split(',') } }
|
where['$tags.tag$'] = tags.split(',')
|
||||||
}
|
}
|
||||||
|
|
||||||
if (places) {
|
if (places) {
|
||||||
|
@ -39,7 +46,15 @@ const exportController = {
|
||||||
start_datetime: { [Op.gte]: yesterday },
|
start_datetime: { [Op.gte]: yesterday },
|
||||||
...where
|
...where
|
||||||
},
|
},
|
||||||
include: [{ model: Tag, required: false, ...where_tags }, { model: Place, attributes: ['name', 'id', 'address'] }]
|
include: [
|
||||||
|
{
|
||||||
|
model: Tag,
|
||||||
|
order: [literal('(SELECT COUNT("tagTag") FROM event_tags WHERE tagTag = tag) DESC')],
|
||||||
|
attributes: ['tag'],
|
||||||
|
required: !!tags,
|
||||||
|
through: { attributes: [] }
|
||||||
|
},
|
||||||
|
{ model: Place, attributes: ['name', 'id', 'address'] }]
|
||||||
})
|
})
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
@ -54,8 +69,9 @@ const exportController = {
|
||||||
},
|
},
|
||||||
|
|
||||||
feed (req, res, events) {
|
feed (req, res, events) {
|
||||||
|
const settings = res.locals.settings
|
||||||
res.type('application/rss+xml; charset=UTF-8')
|
res.type('application/rss+xml; charset=UTF-8')
|
||||||
res.render('feed/rss.pug', { events, settings: req.settings, moment })
|
res.render('feed/rss.pug', { events, settings, moment })
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -64,6 +80,7 @@ const exportController = {
|
||||||
* @param {*} alarms https://github.com/adamgibbons/ics#attributes (alarms)
|
* @param {*} alarms https://github.com/adamgibbons/ics#attributes (alarms)
|
||||||
*/
|
*/
|
||||||
ics (req, res, events, alarms = []) {
|
ics (req, res, events, alarms = []) {
|
||||||
|
const settings = res.locals.settings
|
||||||
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)
|
||||||
|
@ -74,10 +91,10 @@ const exportController = {
|
||||||
// startOutputType: 'utc',
|
// startOutputType: 'utc',
|
||||||
end,
|
end,
|
||||||
// endOutputType: 'utc',
|
// endOutputType: 'utc',
|
||||||
title: `[${req.settings.title}] ${e.title}`,
|
title: `[${settings.title}] ${e.title}`,
|
||||||
description: e.description,
|
description: e.description,
|
||||||
location: `${e.place.name} - ${e.place.address}`,
|
location: `${e.place.name} - ${e.place.address}`,
|
||||||
url: `${req.settings.baseurl}/event/${e.slug || e.id}`,
|
url: `${settings.baseurl}/event/${e.slug || e.id}`,
|
||||||
alarms
|
alarms
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -63,7 +63,7 @@ const oauthController = {
|
||||||
|
|
||||||
async getClients (req, res) {
|
async getClients (req, res) {
|
||||||
const tokens = await OAuthToken.findAll({
|
const tokens = await OAuthToken.findAll({
|
||||||
include: [{ model: User, where: { id: req.user.id } }, { model: OAuthClient, as: 'client' }],
|
include: [{ model: User, where: { id: res.locals.user.id } }, { model: OAuthClient, as: 'client' }],
|
||||||
raw: true,
|
raw: true,
|
||||||
nest: true
|
nest: true
|
||||||
})
|
})
|
||||||
|
|
|
@ -35,9 +35,11 @@ const resourceController = {
|
||||||
},
|
},
|
||||||
event: {
|
event: {
|
||||||
id: r.event.id,
|
id: r.event.id,
|
||||||
title: r.event.title
|
title: r.event.title,
|
||||||
|
slug: r.event.slug
|
||||||
},
|
},
|
||||||
ap_user: {
|
ap_user: {
|
||||||
|
url: get(r, 'ap_user.object.url', ''),
|
||||||
ap_id: get(r, 'ap_user.ap_id', ''),
|
ap_id: get(r, 'ap_user.ap_id', ''),
|
||||||
preferredUsername: get(r, 'ap_user.object.preferredUsername', '')
|
preferredUsername: get(r, 'ap_user.object.preferredUsername', '')
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ const pkg = require('../../../package.json')
|
||||||
const generateKeyPair = promisify(crypto.generateKeyPair)
|
const generateKeyPair = promisify(crypto.generateKeyPair)
|
||||||
const log = require('../../log')
|
const log = require('../../log')
|
||||||
const locales = require('../../../locales/index')
|
const locales = require('../../../locales/index')
|
||||||
|
const escape = require('lodash/escape')
|
||||||
|
|
||||||
|
|
||||||
let defaultHostname
|
let defaultHostname
|
||||||
|
@ -54,7 +55,7 @@ const settingsController = {
|
||||||
secretSettings: {},
|
secretSettings: {},
|
||||||
|
|
||||||
async load () {
|
async load () {
|
||||||
if (config.firstrun) {
|
if (config.status !== 'READY') {
|
||||||
settingsController.settings = defaultSettings
|
settingsController.settings = defaultSettings
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -109,7 +110,7 @@ const settingsController = {
|
||||||
|
|
||||||
// load custom plugins
|
// load custom plugins
|
||||||
const plugins_path = path.resolve(process.env.cwd || '', 'plugins')
|
const plugins_path = path.resolve(process.env.cwd || '', 'plugins')
|
||||||
if (fs.existsSync(plugins_path)) {
|
if (process.env.NODE_ENV === 'production' && fs.existsSync(plugins_path)) {
|
||||||
const notifier = require('../../notifier')
|
const notifier = require('../../notifier')
|
||||||
const pluginsFile = fs.readdirSync(plugins_path).filter(e => path.extname(e).toLowerCase() === '.js')
|
const pluginsFile = fs.readdirSync(plugins_path).filter(e => path.extname(e).toLowerCase() === '.js')
|
||||||
pluginsFile.forEach( pluginFile => {
|
pluginsFile.forEach( pluginFile => {
|
||||||
|
@ -162,11 +163,12 @@ const settingsController = {
|
||||||
await settingsController.set('smtp', smtp.smtp)
|
await settingsController.set('smtp', smtp.smtp)
|
||||||
const mail = require('../mail')
|
const mail = require('../mail')
|
||||||
try {
|
try {
|
||||||
await mail._send(settingsController.settings.admin_email, 'test', null, 'en')
|
await mail._send(settingsController.settings.admin_email, 'test')
|
||||||
|
|
||||||
return res.sendStatus(200)
|
return res.sendStatus(200)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
return res.status(400).send(String(e))
|
return res.status(400).send(escape(String(e)))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -190,11 +192,6 @@ const settingsController = {
|
||||||
settingsController.set('logo', baseImgPath)
|
settingsController.set('logo', baseImgPath)
|
||||||
res.sendStatus(200)
|
res.sendStatus(200)
|
||||||
})
|
})
|
||||||
},
|
|
||||||
|
|
||||||
getAllRequest (req, res) {
|
|
||||||
// get public settings and public configuration
|
|
||||||
res.json({ ...settingsController.settings, version: pkg.version })
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,21 +5,22 @@ const db = require('../models/index.js')
|
||||||
const config = require('../../config')
|
const config = require('../../config')
|
||||||
const settingsController = require('./settings')
|
const settingsController = require('./settings')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
|
const escape = require('lodash/escape')
|
||||||
|
|
||||||
const setupController = {
|
const setupController = {
|
||||||
|
|
||||||
async setupDb (req, res, next) {
|
async _setupDb (dbConf) {
|
||||||
log.debug('[SETUP] Check db')
|
|
||||||
const dbConf = req.body.db
|
|
||||||
if (!dbConf) {
|
if (!dbConf) {
|
||||||
return res.sendStatus(400)
|
throw Error('Empty DB configuration')
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dbConf.storage) {
|
if (dbConf.dialect === 'sqlite' && dbConf.storage) {
|
||||||
dbConf.storage = path.resolve(process.env.cwd || '', dbConf.storage)
|
dbConf.storage = path.resolve(process.env.cwd || '', dbConf.storage)
|
||||||
|
} else {
|
||||||
|
dbConf.storage = ''
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
|
||||||
// try to connect
|
// try to connect
|
||||||
dbConf.logging = false
|
dbConf.logging = false
|
||||||
await db.connect(dbConf)
|
await db.connect(dbConf)
|
||||||
|
@ -28,29 +29,37 @@ const setupController = {
|
||||||
const isEmpty = await db.isEmpty()
|
const isEmpty = await db.isEmpty()
|
||||||
if (!isEmpty) {
|
if (!isEmpty) {
|
||||||
log.warn(' ⚠ Non empty db! Please move your current db elsewhere than retry.')
|
log.warn(' ⚠ Non empty db! Please move your current db elsewhere than retry.')
|
||||||
return res.status(400).send(' ⚠ Non empty db! Please move your current db elsewhere than retry.')
|
throw Error(' ⚠ Non empty db! Please move your current db elsewhere than retry.')
|
||||||
}
|
}
|
||||||
|
|
||||||
await db.runMigrations()
|
await db.runMigrations()
|
||||||
|
|
||||||
config.db = dbConf
|
config.db = dbConf
|
||||||
config.firstrun = false
|
config.status = 'DBCONF'
|
||||||
config.db.logging = false
|
config.db.logging = false
|
||||||
config.baseurl = req.protocol + '://' + req.headers.host
|
|
||||||
config.hostname = new URL.URL(config.baseurl).hostname
|
|
||||||
|
|
||||||
const settingsController = require('./settings')
|
},
|
||||||
await settingsController.load()
|
|
||||||
return res.sendStatus(200)
|
async setupDb (req, res) {
|
||||||
|
log.debug('[SETUP] Check db')
|
||||||
|
const dbConf = req.body.db
|
||||||
|
|
||||||
|
try {
|
||||||
|
await setupController._setupDb(dbConf)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return res.status(400).send(String(e))
|
return res.status(400).send(String(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return res.sendStatus(200)
|
||||||
},
|
},
|
||||||
|
|
||||||
async restart (req, res) {
|
async restart (req, res) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
|
config.baseurl = req.protocol + '://' + req.headers.host
|
||||||
|
config.hostname = new URL.URL(config.baseurl).hostname
|
||||||
|
|
||||||
// write configuration
|
// write configuration
|
||||||
config.write()
|
config.write()
|
||||||
|
|
||||||
|
@ -72,12 +81,13 @@ const setupController = {
|
||||||
log.info('Restart needed')
|
log.info('Restart needed')
|
||||||
|
|
||||||
res.end()
|
res.end()
|
||||||
|
|
||||||
// exit process so pm2 || docker could restart me || service
|
// exit process so pm2 || docker could restart me || service
|
||||||
process.kill(process.pid)
|
setTimeout(() => process.kill(process.pid), 1000)
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log.error(String(e))
|
log.error(String(e))
|
||||||
return res.status(400).send(String(e))
|
return res.status(400).send(escape(String(e)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ const userController = {
|
||||||
if (!user) { return res.sendStatus(200) }
|
if (!user) { return res.sendStatus(200) }
|
||||||
|
|
||||||
user.recover_code = crypto.randomBytes(16).toString('hex')
|
user.recover_code = crypto.randomBytes(16).toString('hex')
|
||||||
mail.send(user.email, 'recover', { user, config }, req.settings.locale)
|
mail.send(user.email, 'recover', { user, config }, res.locals.locale)
|
||||||
|
|
||||||
await user.save()
|
await user.save()
|
||||||
res.sendStatus(200)
|
res.sendStatus(200)
|
||||||
|
@ -44,13 +44,13 @@ const userController = {
|
||||||
},
|
},
|
||||||
|
|
||||||
async current (req, res) {
|
async current (req, res) {
|
||||||
if (!req.user) { return res.status(400).send('Not logged') }
|
if (!res.locals.user) { return res.status(400).send('Not logged') }
|
||||||
const user = await User.scope('withoutPassword').findByPk(req.user.id)
|
const user = await User.scope('withoutPassword').findByPk(res.locals.user.id)
|
||||||
res.json(user)
|
res.json(user)
|
||||||
},
|
},
|
||||||
|
|
||||||
async getAll (req, res) {
|
async getAll (req, res) {
|
||||||
const users = await User.scope(req.user.is_admin ? 'withRecover' : 'withoutPassword').findAll({
|
const users = await User.scope(res.locals.user.is_admin ? 'withRecover' : 'withoutPassword').findAll({
|
||||||
order: [['is_admin', 'DESC'], ['createdAt', 'DESC']]
|
order: [['is_admin', 'DESC'], ['createdAt', 'DESC']]
|
||||||
})
|
})
|
||||||
res.json(users)
|
res.json(users)
|
||||||
|
@ -62,14 +62,14 @@ const userController = {
|
||||||
|
|
||||||
if (!user) { return res.status(404).json({ success: false, message: 'User not found!' }) }
|
if (!user) { return res.status(404).json({ success: false, message: 'User not found!' }) }
|
||||||
|
|
||||||
if (req.body.id !== req.user.id && !req.user.is_admin) {
|
if (req.body.id !== res.locals.user.id && !res.locals.user.is_admin) {
|
||||||
return res.status(400).json({ succes: false, message: 'Not allowed' })
|
return res.status(400).json({ succes: false, message: 'Not allowed' })
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!req.body.password) { delete req.body.password }
|
if (!req.body.password) { delete req.body.password }
|
||||||
|
|
||||||
if (!user.is_active && req.body.is_active && user.recover_code) {
|
if (!user.is_active && req.body.is_active && user.recover_code) {
|
||||||
mail.send(user.email, 'confirm', { user, config }, req.settings.locale)
|
mail.send(user.email, 'confirm', { user, config }, res.locals.settings.locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
await user.update(req.body)
|
await user.update(req.body)
|
||||||
|
@ -99,7 +99,7 @@ const userController = {
|
||||||
log.info('Register user ', req.body.email)
|
log.info('Register user ', req.body.email)
|
||||||
const user = await User.create(req.body)
|
const user = await User.create(req.body)
|
||||||
log.info(`Sending registration email to ${user.email}`)
|
log.info(`Sending registration email to ${user.email}`)
|
||||||
mail.send(user.email, 'register', { user, config }, req.settings.locale)
|
mail.send(user.email, 'register', { user, config }, res.locals.locale)
|
||||||
mail.send(settingsController.settings.admin_email, 'admin_register', { user, config })
|
mail.send(settingsController.settings.admin_email, 'admin_register', { user, config })
|
||||||
res.sendStatus(200)
|
res.sendStatus(200)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -113,7 +113,7 @@ const userController = {
|
||||||
req.body.is_active = true
|
req.body.is_active = true
|
||||||
req.body.recover_code = crypto.randomBytes(16).toString('hex')
|
req.body.recover_code = crypto.randomBytes(16).toString('hex')
|
||||||
const user = await User.scope('withRecover').create(req.body)
|
const user = await User.scope('withRecover').create(req.body)
|
||||||
mail.send(user.email, 'user_confirm', { user, config }, req.settings.locale)
|
mail.send(user.email, 'user_confirm', { user, config }, res.locals.locale)
|
||||||
res.json(user)
|
res.json(user)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log.error('User creation error:', e)
|
log.error('User creation error:', e)
|
||||||
|
|
|
@ -10,7 +10,7 @@ api.use(express.urlencoded({ extended: false }))
|
||||||
api.use(express.json())
|
api.use(express.json())
|
||||||
|
|
||||||
|
|
||||||
if (config.firstrun) {
|
if (config.status !== 'READY') {
|
||||||
|
|
||||||
const setupController = require('./controller/setup')
|
const setupController = require('./controller/setup')
|
||||||
const settingsController = require('./controller/settings')
|
const settingsController = require('./controller/settings')
|
||||||
|
@ -56,7 +56,7 @@ if (config.firstrun) {
|
||||||
```
|
```
|
||||||
*/
|
*/
|
||||||
api.get('/ping', (req, res) => res.sendStatus(200))
|
api.get('/ping', (req, res) => res.sendStatus(200))
|
||||||
api.get('/user', isAuth, (req, res) => res.json(req.user))
|
api.get('/user', isAuth, (req, res) => res.json(res.locals.user))
|
||||||
|
|
||||||
|
|
||||||
api.post('/user/recover', userController.forgotPassword)
|
api.post('/user/recover', userController.forgotPassword)
|
||||||
|
@ -80,10 +80,28 @@ if (config.firstrun) {
|
||||||
// update a place (modify address..)
|
// update a place (modify address..)
|
||||||
api.put('/place', isAdmin, eventController.updatePlace)
|
api.put('/place', isAdmin, eventController.updatePlace)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get events
|
||||||
|
* @category Event
|
||||||
|
* @name /api/events
|
||||||
|
* @type GET
|
||||||
|
* @param {integer} [start] - start timestamp (default: now)
|
||||||
|
* @param {integer} [end] - end timestamp (optional)
|
||||||
|
* @param {array} [tags] - List of tags
|
||||||
|
* @param {array} [places] - List of places
|
||||||
|
* @param {integer} [max] - Max events
|
||||||
|
* @param {boolean} [show_recurrent] - Show also recurrent events (default: as choosen in admin settings)
|
||||||
|
* @example ***Example***
|
||||||
|
* [https://demo.gancio.org/api/events](https://demo.gancio.org/api/events)
|
||||||
|
* [usage example](https://framagit.org/les/gancio/-/blob/master/webcomponents/src/GancioEvents.svelte#L18-42)
|
||||||
|
*/
|
||||||
|
|
||||||
|
api.get('/events', cors, eventController.select)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a new event
|
* Add a new event
|
||||||
* @category Event
|
* @category Event
|
||||||
* @name /event
|
* @name /api/event
|
||||||
* @type POST
|
* @type POST
|
||||||
* @info `Content-Type` has to be `multipart/form-data` to support image upload
|
* @info `Content-Type` has to be `multipart/form-data` to support image upload
|
||||||
* @param {string} title - event's title
|
* @param {string} title - event's title
|
||||||
|
@ -95,13 +113,12 @@ if (config.firstrun) {
|
||||||
* @param {array} tags - List of tags
|
* @param {array} tags - List of tags
|
||||||
* @param {object} [recurrent] - Recurrent event details
|
* @param {object} [recurrent] - Recurrent event details
|
||||||
* @param {string} [recurrent.frequency] - could be `1w` or `2w`
|
* @param {string} [recurrent.frequency] - could be `1w` or `2w`
|
||||||
* @param {string} [recurrent.type] - not used
|
|
||||||
* @param {array} [recurrent.days] - array of days
|
* @param {array} [recurrent.days] - array of days
|
||||||
* @param {image} [image] - Image
|
* @param {image} [image] - Image
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// allow anyone to add an event (anon event has to be confirmed, TODO: flood protection)
|
// allow anyone to add an event (anon event has to be confirmed, TODO: flood protection)
|
||||||
api.post('/event', upload.single('image'), eventController.add)
|
api.post('/event', eventController.isAnonEventAllowed, upload.single('image'), eventController.add)
|
||||||
|
|
||||||
api.put('/event', isAuth, upload.single('image'), eventController.update)
|
api.put('/event', isAuth, upload.single('image'), eventController.update)
|
||||||
api.get('/event/import', isAuth, helpers.importURL)
|
api.get('/event/import', isAuth, helpers.importURL)
|
||||||
|
@ -119,7 +136,6 @@ if (config.firstrun) {
|
||||||
api.post('/event/notification', eventController.addNotification)
|
api.post('/event/notification', eventController.addNotification)
|
||||||
api.delete('/event/notification/:code', eventController.delNotification)
|
api.delete('/event/notification/:code', eventController.delNotification)
|
||||||
|
|
||||||
api.get('/settings', settingsController.getAllRequest)
|
|
||||||
api.post('/settings', isAdmin, settingsController.setRequest)
|
api.post('/settings', isAdmin, settingsController.setRequest)
|
||||||
api.post('/settings/logo', isAdmin, multer({ dest: config.upload_path }).single('logo'), settingsController.setLogo)
|
api.post('/settings/logo', isAdmin, multer({ dest: config.upload_path }).single('logo'), settingsController.setLogo)
|
||||||
api.post('/settings/smtp', isAdmin, settingsController.testSMTP)
|
api.post('/settings/smtp', isAdmin, settingsController.testSMTP)
|
||||||
|
@ -134,9 +150,6 @@ if (config.firstrun) {
|
||||||
// export events (rss/ics)
|
// export events (rss/ics)
|
||||||
api.get('/export/:type', cors, exportController.export)
|
api.get('/export/:type', cors, exportController.export)
|
||||||
|
|
||||||
// get events in this range
|
|
||||||
api.get('/events', cors, eventController.select)
|
|
||||||
|
|
||||||
api.get('/instances', isAdmin, instanceController.getAll)
|
api.get('/instances', isAdmin, instanceController.getAll)
|
||||||
api.get('/instances/:instance_domain', isAdmin, instanceController.get)
|
api.get('/instances/:instance_domain', isAdmin, instanceController.get)
|
||||||
api.post('/instances/toggle_block', isAdmin, instanceController.toggleBlock)
|
api.post('/instances/toggle_block', isAdmin, instanceController.toggleBlock)
|
||||||
|
|
|
@ -7,7 +7,8 @@ const { Task, TaskManager } = require('../taskManager')
|
||||||
const locales = require('../../locales')
|
const locales = require('../../locales')
|
||||||
|
|
||||||
const mail = {
|
const mail = {
|
||||||
send (addresses, template, locals, locale = settingsController.settings.instance_locale) {
|
send (addresses, template, locals, locale) {
|
||||||
|
locale = locale || settingsController.settings.instance_locale
|
||||||
if (process.env.NODE_ENV === 'production' && (!settingsController.settings.admin_email || !settingsController.settings.smtp)) {
|
if (process.env.NODE_ENV === 'production' && (!settingsController.settings.admin_email || !settingsController.settings.smtp)) {
|
||||||
log.error(`Cannot send any email: SMTP Email configuration not completed!`)
|
log.error(`Cannot send any email: SMTP Email configuration not completed!`)
|
||||||
return
|
return
|
||||||
|
@ -21,7 +22,8 @@ const mail = {
|
||||||
TaskManager.add(task)
|
TaskManager.add(task)
|
||||||
},
|
},
|
||||||
|
|
||||||
_send (addresses, template, locals, locale = settingsController.settings.instance_locale) {
|
_send (addresses, template, locals, locale) {
|
||||||
|
locale = locale || settingsController.settings.instance_locale
|
||||||
const settings = settingsController.settings
|
const settings = settingsController.settings
|
||||||
log.info(`Send ${template} email to ${addresses} with locale ${locale}`)
|
log.info(`Send ${template} email to ${addresses} with locale ${locale}`)
|
||||||
const email = new Email({
|
const email = new Email({
|
||||||
|
|
|
@ -3,6 +3,7 @@ const Umzug = require('umzug')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const config = require('../../config')
|
const config = require('../../config')
|
||||||
const log = require('../../log')
|
const log = require('../../log')
|
||||||
|
const settingsController = require('../controller/settings')
|
||||||
|
|
||||||
const db = {
|
const db = {
|
||||||
sequelize: null,
|
sequelize: null,
|
||||||
|
@ -13,6 +14,7 @@ const db = {
|
||||||
},
|
},
|
||||||
connect (dbConf = config.db) {
|
connect (dbConf = config.db) {
|
||||||
log.debug(`Connecting to DB: ${JSON.stringify(dbConf)}`)
|
log.debug(`Connecting to DB: ${JSON.stringify(dbConf)}`)
|
||||||
|
dbConf.dialectOptions = { autoJsonMap: false }
|
||||||
db.sequelize = new Sequelize(dbConf)
|
db.sequelize = new Sequelize(dbConf)
|
||||||
return db.sequelize.authenticate()
|
return db.sequelize.authenticate()
|
||||||
},
|
},
|
||||||
|
@ -21,7 +23,7 @@ const db = {
|
||||||
return !(users && users.length)
|
return !(users && users.length)
|
||||||
},
|
},
|
||||||
async runMigrations () {
|
async runMigrations () {
|
||||||
const logging = config.firstrun ? false : log.debug.bind(log)
|
const logging = config.status !== 'READY' ? false : log.debug.bind(log)
|
||||||
const umzug = new Umzug({
|
const umzug = new Umzug({
|
||||||
storage: 'sequelize',
|
storage: 'sequelize',
|
||||||
storageOptions: { sequelize: db.sequelize },
|
storageOptions: { sequelize: db.sequelize },
|
||||||
|
@ -40,11 +42,12 @@ const db = {
|
||||||
return await umzug.up()
|
return await umzug.up()
|
||||||
},
|
},
|
||||||
async initialize () {
|
async initialize () {
|
||||||
if (!config.firstrun) {
|
if (config.status === 'READY') {
|
||||||
try {
|
try {
|
||||||
await db.connect()
|
await db.connect()
|
||||||
log.debug('Running migrations')
|
log.debug('Running migrations')
|
||||||
return db.runMigrations()
|
await db.runMigrations()
|
||||||
|
return settingsController.load()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log.warn(` ⚠️ Cannot connect to db, check your configuration => ${e}`)
|
log.warn(` ⚠️ Cannot connect to db, check your configuration => ${e}`)
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
|
|
|
@ -12,11 +12,11 @@ const oauthServer = new OAuthServer({
|
||||||
debug: true,
|
debug: true,
|
||||||
requireClientAuthentication: { password: false },
|
requireClientAuthentication: { password: false },
|
||||||
authenticateHandler: {
|
authenticateHandler: {
|
||||||
handle (req) {
|
handle (req, res) {
|
||||||
if (!req.user) {
|
if (!res.locals.user) {
|
||||||
throw new Error('Not authenticated!')
|
throw new Error('Not authenticated!')
|
||||||
}
|
}
|
||||||
return req.user
|
return res.locals.user
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -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.warn('[OAUTH USE] ' + error_msg)
|
||||||
res.status(500).send(error_msg)
|
res.status(500).send(error_msg)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ const DiskStorage = {
|
||||||
const thumbStream = fs.createWriteStream(thumbPath)
|
const thumbStream = fs.createWriteStream(thumbPath)
|
||||||
|
|
||||||
const resizer = sharp().resize(1200).jpeg({ quality: 98 })
|
const resizer = sharp().resize(1200).jpeg({ quality: 98 })
|
||||||
const thumbnailer = sharp().resize(400).jpeg({ quality: 90 })
|
const thumbnailer = sharp().resize(500).jpeg({ quality: 98 })
|
||||||
let onError = false
|
let onError = false
|
||||||
const err = e => {
|
const err = e => {
|
||||||
if (onError) {
|
if (onError) {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
const pkg = require('../package.json')
|
const pkg = require('../package.json')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
|
const accountsCLI = require('./cli/accounts')
|
||||||
|
|
||||||
process.env.cwd = process.env.GANCIO_DATA || path.resolve('./')
|
process.env.cwd = process.env.GANCIO_DATA || path.resolve('./')
|
||||||
|
|
||||||
|
@ -28,6 +29,7 @@ require('yargs')
|
||||||
process.env.config_path = absolute_config_path
|
process.env.config_path = absolute_config_path
|
||||||
return absolute_config_path
|
return absolute_config_path
|
||||||
})
|
})
|
||||||
|
.command(['accounts'], 'Manage accounts', accountsCLI)
|
||||||
.command(['start', 'run', '$0'], 'Start gancio', {}, start)
|
.command(['start', 'run', '$0'], 'Start gancio', {}, start)
|
||||||
.help('h')
|
.help('h')
|
||||||
.alias('h', 'help')
|
.alias('h', 'help')
|
||||||
|
|
54
server/cli/accounts.js
Normal file
54
server/cli/accounts.js
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
function _initializeDB () {
|
||||||
|
const config = require('../config')
|
||||||
|
config.load()
|
||||||
|
config.log_level = 'error'
|
||||||
|
const db = require('../api/models/index')
|
||||||
|
return db.initialize()
|
||||||
|
}
|
||||||
|
|
||||||
|
async function modify (args) {
|
||||||
|
await _initializeDB()
|
||||||
|
const helpers = require('../helpers')
|
||||||
|
const User = require('../api/models/user')
|
||||||
|
const user = await User.findOne({ where: { email: args.account } })
|
||||||
|
console.log()
|
||||||
|
if (!user) {
|
||||||
|
console.error(`User ${args.account} not found`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args['reset-password']) {
|
||||||
|
const password = helpers.randomString()
|
||||||
|
user.password = password
|
||||||
|
await user.save()
|
||||||
|
console.log(`New password for user ${user.email} is '${password}'`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function add (args) {
|
||||||
|
}
|
||||||
|
|
||||||
|
async function list () {
|
||||||
|
await _initializeDB()
|
||||||
|
const User = require('../api/models/user')
|
||||||
|
const users = await User.findAll()
|
||||||
|
console.log()
|
||||||
|
users.forEach(u => console.log(`${u.id}\tadmin: ${u.is_admin}\tenabled: ${u.is_active}\temail: ${u.email} - ${u.password}`))
|
||||||
|
console.log()
|
||||||
|
}
|
||||||
|
|
||||||
|
const accountsCLI = yargs => {
|
||||||
|
return yargs
|
||||||
|
.command('list', 'List all accounts', list)
|
||||||
|
.command('modify', 'Modify', {
|
||||||
|
account: {
|
||||||
|
describe: 'Account to modify'
|
||||||
|
},
|
||||||
|
'reset-password': {
|
||||||
|
describe: 'Resets the password of the given accoun '
|
||||||
|
}
|
||||||
|
}, modify)
|
||||||
|
.command('add', 'Add an account', {}, add)
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = accountsCLI
|
|
@ -3,7 +3,7 @@ const path = require('path')
|
||||||
const URL = require('url')
|
const URL = require('url')
|
||||||
|
|
||||||
let config = {
|
let config = {
|
||||||
firstrun: true,
|
status: 'SETUP',
|
||||||
baseurl: '',
|
baseurl: '',
|
||||||
hostname: '',
|
hostname: '',
|
||||||
server: {
|
server: {
|
||||||
|
@ -15,7 +15,7 @@ let config = {
|
||||||
db: {},
|
db: {},
|
||||||
upload_path: path.resolve(process.env.cwd || '', 'uploads'),
|
upload_path: path.resolve(process.env.cwd || '', 'uploads'),
|
||||||
write (config_path= process.env.config_path || './config.json') {
|
write (config_path= process.env.config_path || './config.json') {
|
||||||
delete config.firstrun
|
delete config.status
|
||||||
return fs.writeFileSync(config_path, JSON.stringify(config, null, 2))
|
return fs.writeFileSync(config_path, JSON.stringify(config, null, 2))
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -26,17 +26,15 @@ let config = {
|
||||||
if (fs.existsSync(config_path)) {
|
if (fs.existsSync(config_path)) {
|
||||||
const configContent = fs.readFileSync(config_path)
|
const configContent = fs.readFileSync(config_path)
|
||||||
config = Object.assign(config, JSON.parse(configContent))
|
config = Object.assign(config, JSON.parse(configContent))
|
||||||
config.firstrun = false
|
config.status = 'READY'
|
||||||
if (!config.hostname) {
|
if (!config.hostname) {
|
||||||
config.hostname = new URL.URL(config.baseurl).hostname
|
config.hostname = new URL.URL(config.baseurl).hostname
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
config.firstrun = true
|
config.status = 'SETUP'
|
||||||
console.info('> Configuration file does not exists, running setup..')
|
console.info('> Configuration file does not exists, running setup..')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
config.load()
|
|
||||||
|
|
||||||
module.exports = config
|
module.exports = config
|
|
@ -2,15 +2,17 @@ const config = require('../config')
|
||||||
const Helpers = require('./helpers')
|
const Helpers = require('./helpers')
|
||||||
const crypto = require('crypto')
|
const crypto = require('crypto')
|
||||||
const log = require('../log')
|
const log = require('../log')
|
||||||
|
const settingsController = require('../api/controller/settings')
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
// follow request from fediverse
|
// follow request from fediverse
|
||||||
async follow (req, res) {
|
async follow (req, res) {
|
||||||
const body = req.body
|
const body = req.body
|
||||||
|
const settings = res.locals.settings
|
||||||
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 !== settings.instance_name) {
|
||||||
log.warn(`Following the wrong user: ${username} instead of ${req.settings.instance_name} (could be a wrong config.baseurl)`)
|
log.warn(`Following the wrong user: ${username} instead of ${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')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,7 +20,7 @@ module.exports = {
|
||||||
// if (!user.followers.includes(body.actor)) {
|
// if (!user.followers.includes(body.actor)) {
|
||||||
// await user.addFollowers([req.fedi_user.id])
|
// await user.addFollowers([req.fedi_user.id])
|
||||||
// await user.update({ followers: [...user.followers, body.actor] })
|
// await user.update({ followers: [...user.followers, body.actor] })
|
||||||
await req.fedi_user.update({ follower: true })
|
await res.locals.fedi_user.update({ follower: true })
|
||||||
log.info(`Followed by ${body.actor}`)
|
log.info(`Followed by ${body.actor}`)
|
||||||
const guid = crypto.randomBytes(16).toString('hex')
|
const guid = crypto.randomBytes(16).toString('hex')
|
||||||
const message = {
|
const message = {
|
||||||
|
@ -28,24 +30,25 @@ module.exports = {
|
||||||
actor: `${config.baseurl}/federation/u/${username}`,
|
actor: `${config.baseurl}/federation/u/${username}`,
|
||||||
object: body
|
object: body
|
||||||
}
|
}
|
||||||
Helpers.signAndSend(JSON.stringify(message), req.fedi_user.object.inbox)
|
Helpers.signAndSend(JSON.stringify(message), res.locals.fedi_user.object.inbox)
|
||||||
res.sendStatus(200)
|
res.sendStatus(200)
|
||||||
},
|
},
|
||||||
|
|
||||||
// unfollow request from fediverse
|
// unfollow request from fediverse
|
||||||
async unfollow (req, res) {
|
async unfollow (req, res) {
|
||||||
|
const settings = res.locals.settings
|
||||||
const body = req.body
|
const body = req.body
|
||||||
const username = body.object.object.replace(`${config.baseurl}/federation/u/`, '')
|
const username = body.object.object.replace(`${config.baseurl}/federation/u/`, '')
|
||||||
if (username !== req.settings.instance_name) {
|
if (username !== settings.instance_name) {
|
||||||
log.warn(`Unfollowing wrong user: ${username} instead of ${req.settings.instance_name}`)
|
log.warn(`Unfollowing wrong user: ${username} instead of ${settings.instance_name}`)
|
||||||
return res.status(404).send('User not found')
|
return res.status(404).send('User not found')
|
||||||
}
|
}
|
||||||
|
|
||||||
if (body.actor !== body.object.actor || body.actor !== req.fedi_user.ap_id) {
|
if (body.actor !== body.object.actor || body.actor !== res.locals.fedi_user.ap_id) {
|
||||||
log.info('Unfollow an user created by a different actor !?!?')
|
log.info('Unfollow an user created by a different actor !?!?')
|
||||||
return res.status(400).send('Bad things')
|
return res.status(400).send('Bad things')
|
||||||
}
|
}
|
||||||
await req.fedi_user.update({ follower: false })
|
await res.locals.fedi_user.update({ follower: false })
|
||||||
log.info(`Unfollowed by ${body.actor}`)
|
log.info(`Unfollowed by ${body.actor}`)
|
||||||
res.sendStatus(200)
|
res.sendStatus(200)
|
||||||
}
|
}
|
||||||
|
|
|
@ -192,7 +192,7 @@ const Helpers = {
|
||||||
return res.status(401).send('User blocked')
|
return res.status(401).send('User blocked')
|
||||||
}
|
}
|
||||||
|
|
||||||
req.fedi_user = user
|
res.locals.fedi_user = user
|
||||||
|
|
||||||
// TODO: check Digest // cannot do this with json bodyparser
|
// TODO: check Digest // cannot do this with json bodyparser
|
||||||
// const digest = crypto.createHash('sha256')
|
// const digest = crypto.createHash('sha256')
|
||||||
|
|
|
@ -6,6 +6,7 @@ const Event = require('../api/models/event')
|
||||||
const User = require('../api/models/user')
|
const User = require('../api/models/user')
|
||||||
const Tag = require('../api/models/tag')
|
const Tag = require('../api/models/tag')
|
||||||
const Place = require('../api/models/place')
|
const Place = require('../api/models/place')
|
||||||
|
const settingsController = require('../api/controller/settings')
|
||||||
|
|
||||||
const Helpers = require('./helpers')
|
const Helpers = require('./helpers')
|
||||||
const Inbox = require('./inbox')
|
const Inbox = require('./inbox')
|
||||||
|
@ -20,7 +21,6 @@ router.use(cors())
|
||||||
|
|
||||||
// is federation enabled? middleware
|
// is federation enabled? middleware
|
||||||
router.use((req, res, next) => {
|
router.use((req, res, next) => {
|
||||||
const settingsController = require('../api/controller/settings')
|
|
||||||
if (settingsController.settings.enable_federation) { return next() }
|
if (settingsController.settings.enable_federation) { return next() }
|
||||||
log.debug('Federation disabled!')
|
log.debug('Federation disabled!')
|
||||||
return res.status(401).send('Federation disabled')
|
return res.status(401).send('Federation disabled')
|
||||||
|
@ -36,7 +36,7 @@ router.get('/m/:event_id', async (req, res) => {
|
||||||
|
|
||||||
const event = await Event.findByPk(req.params.event_id, { include: [User, Tag, Place] })
|
const event = await Event.findByPk(req.params.event_id, { include: [User, Tag, Place] })
|
||||||
if (!event) { return res.status(404).send('Not found') }
|
if (!event) { return res.status(404).send('Not found') }
|
||||||
return res.json(event.toAP(settingsController.settings.instance_name, req.settings.instance_locale))
|
return res.json(event.toAP(settingsController.settings.instance_name, settingsController.settings.instance_locale))
|
||||||
})
|
})
|
||||||
|
|
||||||
// get any message coming from federation
|
// get any message coming from federation
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue