major UI modification

This commit is contained in:
les
2020-07-28 12:24:39 +02:00
parent 2758541df0
commit 411560c218
27 changed files with 770 additions and 572 deletions

View File

@@ -2,7 +2,7 @@
#calendar #calendar
v-calendar( v-calendar(
title-position='left' title-position='left'
is-dark :is-dark="settings['theme.is_dark']"
@update:from-page='updatePage' @update:from-page='updatePage'
:columns="$screens({ default: 1, lg: 2 })" :columns="$screens({ default: 1, lg: 2 })"
:locale='$i18n.locale' :locale='$i18n.locale'
@@ -29,7 +29,7 @@ export default {
}, },
computed: { computed: {
...mapGetters(['filteredEventsWithPast']), ...mapGetters(['filteredEventsWithPast']),
...mapState(['tags', 'filters', 'in_past']), ...mapState(['tags', 'filters', 'in_past', 'settings']),
// TODO: could be better // TODO: could be better
attributes () { attributes () {

86
components/Confirm.vue Normal file
View File

@@ -0,0 +1,86 @@
<template lang="pug">
v-dialog(v-model='show'
:color='options.color'
:title='title'
:max-width='options.width'
:style="{ zIndex: options.zIndex, position: 'absolute' }"
@keydown.esc='cancel')
v-card
v-card-title {{ title }}
v-card-text.pa-4(v-show='!!message') {{ message }}
v-card-actions.pt-0
v-spacer
v-btn(color='primary darken-1' text
@click='agree') {{$t('common.ok')}}
v-btn(color='secondary'
text @click='cancel') {{$t('common.cancel')}}
</template>
<script>
/**
* Vuetify Confirm Dialog component
*
* Call it:
* this.$refs.confirm.open('Delete', 'Are you sure?', { color: 'red' }).then((confirm) => {})
*
* Or use await:
* if (await this.$refs.confirm.open('Delete', 'Are you sure?', { color: 'red' })) {
* // yes
* }
* else {
* // cancel
* }
*
*/
export default {
data: () => ({
dialog: false,
resolve: null,
reject: null,
message: null,
title: null,
options: {
color: 'danger',
width: 350,
zIndex: 500
}
}),
computed: {
show: {
get () {
return this.dialog
},
set (value) {
this.dialog = value
if (value === false) {
this.cancel()
}
}
}
},
created () {
this.$root.$confirm = this.open
},
methods: {
open (title, message, options) {
this.dialog = true
this.title = title
this.message = message
this.options = Object.assign(this.options, options)
return new Promise((resolve, reject) => {
this.resolve = resolve
this.reject = reject
})
},
agree () {
this.resolve(true)
this.dialog = false
},
cancel () {
this.resolve(false)
this.dialog = false
}
}
}
</script>

View File

@@ -1,7 +1,17 @@
<template lang='pug'> <template lang='pug'>
.editor(:class='{ "with-border": border }') .editor
editor-menu-bubble(:editor='editor' :keep-in-bounds='true' v-slot='{ commands, isActive, getMarkAttrs, menu }') editor-menu-bar(:editor='editor' :keep-in-bounds='true' v-slot='{ commands, isActive, getMarkAttrs, menu }')
v-button-group.menububble(:class="{ 'is-active': menu.isActive }" :style="`left: ${menu.left}px; bottom: ${menu.bottom}px;`") v-btn-toggle(dense)
v-btn(icon
:color="isActive.bold() && 'primary' || ''"
@click="commands.bold")
v-icon mdi-format-bold
v-btn(icon
:color="isActive.underline() && 'primary' || ''"
@click="commands.underline")
v-icon mdi-format-italic
//- v-button-group.menububble(:class="{ 'is-active': menu.isActive }" :style="`left: ${menu.left}px; bottom: ${menu.bottom}px;`")
v-popover(trigger='hover' placement='bottom-start') v-popover(trigger='hover' placement='bottom-start')
v-btn.float-left(slot='reference' size='mini') <v-icon name='question'/> v-btn.float-left(slot='reference' size='mini') <v-icon name='question'/>
template template
@@ -66,6 +76,7 @@ export default {
}, },
data () { data () {
return { return {
options: [],
linkActive: false, linkActive: false,
editor: null, editor: null,
update: false update: false
@@ -112,11 +123,17 @@ export default {
</script> </script>
<style lang='less'> <style lang='less'>
// .editor { .editor {
font-family: sans-serif;
font-size: 1.1em;
scrollbar-width: thin;
border-color: currentColor;
border-style: solid;
border-width: 0 0 thin 0;
}
// position: relative; // position: relative;
// overflow-y: auto; // overflow-y: auto;
// padding-top: 1.7em; // padding-top: 1.7em;
// scrollbar-width: thin;
// &.with-border { // &.with-border {
// border: 1px solid #ddd; // border: 1px solid #ddd;

View File

@@ -1,24 +1,31 @@
<template lang="pug"> <template lang="pug">
v-card.h-event.mt-1 v-card.h-event.event.mt-1
nuxt-link(:to='`/event/${event.id}`') nuxt-link(:to='`/event/${event.id}`')
v-img(:src="`/media/thumb/${event.image_path}`" height="250" position="top top" class="white--text align-end") v-img(:src="`/media/thumb/${event.image_path}`"
v-list-item gradient="to bottom, rgba(0,0,0,.1), rgba(0,0,0,.7)"
height="250" position="top top"
class="white--text align-end")
v-card-title {{event.title}}
//- v-list-item
v-list-item-content v-list-item-content
v-list-item-title.headline {{ event.title }} v-list-item-title.headline {{ event.title }}
v-list-item-subtitle v-list-item-subtitle
time(:datetime='event.start_datetime|unixFormat("YYYY-MM-DD HH:mm")') <v-icon>mdi-date</v-icon> {{ event|when }} time(:datetime='event.start_datetime|unixFormat("YYYY-MM-DD HH:mm")') <v-icon>mdi-date</v-icon> {{ event|when }}
v-list-item-subtitle v-list-item-subtitle
span <v-icon>md-location-outline</v-icon> {{event.place.name}} span <v-icon>md-location-outline</v-icon> {{event.place.name}}
v-card-text
//- v-card-text time(:datetime='event.start_datetime|unixFormat("YYYY-MM-DD HH:mm")') <v-icon>mdi-event</v-icon> {{ event|when }}
div <v-icon>mdi-location-on</v-icon> {{event.place.name}}
//- v-btn.ml-1(type='text' plain rounded size='mini' v-for='tag in event.tags' :key='tag' @click='addTag(tag)') {{tag}} -->
v-card-actions v-card-actions
v-btn(text color='primary' nuxt :to='`/event/${event.id}`') {{$t('common.read')}} v-chip.ml-1(v-for='tag in event.tags' link :key='tag' outlined color='primary' small) {{tag}}
v-spacer v-spacer
v-btn(icon) v-btn(icon)
v-icon mdi-bookmark v-icon mdi-bookmark
v-btn(icon color='yellow') v-btn(icon color='yellow')
v-icon mdi-share-variant v-icon mdi-share-variant
v-btn(icon color='primary' nuxt :to='`/event/${event.id}`')
v-icon mdi-chevron-right
//- <!-- //- v-card.event.h-event.mt-1(max-width="400") //- <!-- //- v-card.event.h-event.mt-1(max-width="400")
//- p ciao //- p ciao
@@ -98,14 +105,39 @@ export default {
} }
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
.h-event { .event {
width: 300px; width: 300px;
max-width: 450px; max-width: 450px;
flex-grow: 1; flex-grow: 1;
margin: .2em; margin: .2em;
background-color: #202020; // background-color: #202020;
// overflow: hidden;
.title {
line-height: 1.2em;
max-height: 2.4em;
overflow: hidden; overflow: hidden;
}
a {
text-decoration: none;
}
// .title::before {
// content: "...";
// position: absolute;
// bottom: 0;
// right: 0;
// }
// .title::after {
// content: "";
// position: absolute;
// right: 0; /* note: not using bottom */
// width: 1rem;
// height: 1rem;
// background: white;
// }
} }
// a:hover { // a:hover {
// text-decoration: none; // text-decoration: none;
// .title { // .title {

View File

@@ -26,7 +26,6 @@ export default {
components: { Calendar, Event, Search, Announcement }, components: { Calendar, Event, Search, Announcement },
computed: { computed: {
events () { events () {
console.error('dentro computed di events in HOME!')
return this.in_past ? this.filteredEventsWithPast : this.filteredEvents return this.in_past ? this.filteredEventsWithPast : this.filteredEvents
}, },
...mapGetters(['filteredEvents', 'filteredEventsWithPast']), ...mapGetters(['filteredEvents', 'filteredEventsWithPast']),

View File

@@ -1,8 +1,8 @@
<template lang='pug'> <template lang='pug'>
div#list div#list
el-divider(v-if='title') {{title}} p(v-if='title') {{title}}
el-timeline v-timeline
el-timeline-item( v-timeline-item(
v-for='event in events' v-for='event in events'
:key='`${event.id}_${event.start_datetime}`' :key='`${event.id}_${event.start_datetime}`'
:timestamp='event|when' :timestamp='event|when'

View File

@@ -19,21 +19,21 @@ export default {
return { return {
icon: 'md-alert', icon: 'md-alert',
color: 'primary', color: 'primary',
bottom: true, bottom: false,
top: false, top: true,
left: false, left: false,
right: true, right: false,
active: false, active: false,
timeout: 5000, timeout: 5000,
message: '' message: ''
} }
}, },
created () { created () {
this.$root.$on('message', snackbar => { this.$root.$message = snackbar => {
this.active = true this.active = true
this.message = snackbar.message this.message = snackbar.message
this.color = snackbar.color || 'primary' this.color = snackbar.color || 'primary'
}) }
} }
} }
</script> </script>

View File

@@ -1,11 +1,24 @@
<template lang='pug'> <template lang='pug'>
v-container v-container
v-subheader(v-html="$t('admin.announcement_description')") v-subheader(v-html="$t('admin.announcement_description')")
v-dialog(v-model='dialog' width='800')
v-card
v-card-title {{$t('common.new_user')}}
v-card-text
v-form
v-text-field(v-model='announcement.title' :placeholder='$t("common.title")') v-text-field(v-model='announcement.title' :placeholder='$t("common.title")')
Editor.mt-2(v-model='announcement.announcement' border no-save style='max-height: 400px;') Editor.mt-2(v-model='announcement.announcement' border no-save style='max-height: 400px;')
v-btn.mt-2(@click='save' type='success' plain) {{$t(`common.${editing?'save':'send'}`)}} v-btn.mt-2(@click='save' type='success' plain) {{$t(`common.${editing?'save':'send'}`)}}
v-data-table(:items='announcements') v-data-table(
:headers='headers'
:items='announcements')
template(v-slot:item.actions='{ item }')
v-btn(text small @click.stop='toggle(item)'
:color='item.visible?"warning":"success"') {{item.visible?$t('common.deactivate'):$t('common.activate')}}
v-btn(text small @click='edit(item)') {{$t('common.edit')}}
v-btn(text small @click='remove(item)'
color='error') {{$t('common.delete')}}
//- el-table-column(:label="$t('common.actions')") //- el-table-column(:label="$t('common.actions')")
//- template(slot-scope='data') //- template(slot-scope='data')
@@ -20,7 +33,6 @@
</template> </template>
<script> <script>
import { Message, MessageBox } from 'element-ui'
import { mapActions } from 'vuex' import { mapActions } from 'vuex'
import cloneDeep from 'lodash/cloneDeep' import cloneDeep from 'lodash/cloneDeep'
import Editor from '../Editor' import Editor from '../Editor'
@@ -30,8 +42,13 @@ export default {
components: { Editor, Announcement }, components: { Editor, Announcement },
data () { data () {
return { return {
dialog: false,
editing: false, editing: false,
announcements: [], announcements: [],
headers: [
{ value: 'title', text: 'Title' },
{ value: 'actions', text: 'Actions', align: 'right' }
],
announcement: { title: '', announcement: '' } announcement: { title: '', announcement: '' }
} }
}, },
@@ -44,7 +61,7 @@ export default {
this.announcement.title = announcement.title this.announcement.title = announcement.title
this.announcement.announcement = announcement.announcement this.announcement.announcement = announcement.announcement
this.announcement.id = announcement.id this.announcement.id = announcement.id
this.editing = true this.dialog = true
}, },
async toggle (announcement) { async toggle (announcement) {
try { try {
@@ -55,7 +72,7 @@ export default {
} catch (e) {} } catch (e) {}
}, },
remove (announcement) { remove (announcement) {
MessageBox.confirm(this.$t('admin.delete_announcement_confirm'), this.$root.$confirm(this.$t('admin.delete_announcement_confirm'),
this.$t('common.confirm'), { this.$t('common.confirm'), {
confirmButtonText: this.$t('common.ok'), confirmButtonText: this.$t('common.ok'),
cancelButtonText: this.$t('common.cancel'), cancelButtonText: this.$t('common.cancel'),
@@ -63,9 +80,7 @@ export default {
}) })
.then(() => this.$axios.delete(`/announcements/${announcement.id}`)) .then(() => this.$axios.delete(`/announcements/${announcement.id}`))
.then(() => { .then(() => {
Message({ this.$root.$message({
showClose: true,
type: 'success',
message: this.$t('admin.announcement_remove_ok') message: this.$t('admin.announcement_remove_ok')
}) })
this.announcements = this.announcements.filter(a => a.id !== announcement.id) this.announcements = this.announcements.filter(a => a.id !== announcement.id)

View File

@@ -4,8 +4,7 @@
:label="$t('admin.enable_federation')" :label="$t('admin.enable_federation')"
persistent-hint persistent-hint
inset inset
:hint="$t('admin.enable_federation_help')" :hint="$t('admin.enable_federation_help')")
)
template(v-if='enable_federation') template(v-if='enable_federation')
@@ -39,39 +38,29 @@
@blur='save("instance_place", instance_place)' @blur='save("instance_place", instance_place)'
) )
//- div.mt-4 {{$t('admin.add_trusted_instance')}} v-dialog(v-model='dialogAddInstance' width="500px")
v-card
v-card-title {{$t('admin.add_trusted_instance')}}
v-card-text
v-text-field.mt-4(v-model='instance_url' v-text-field.mt-4(v-model='instance_url'
:full-width='false' :full-width='false'
persistent-hint persistent-hint
:hint="$t('admin.add_trusted_instance')" :hint="$t('admin.add_trusted_instance')"
:label="$t('common.url')" :label="$t('common.url')"
append-outer-icon="mdi-send" append-outer-icon="mdi-send"
@click:append-outer='createTrustedInstance' @click:append-outer='createTrustedInstance')
)
v-btn(@click='dialogAddInstance = true') Add instance
v-data-table.mt-4( v-data-table.mt-4(
:headers='headers' :headers='headers'
:items='settings.trusted_instances' :items='settings.trusted_instances')
) template(v-slot:item.actions="{item}")
//- el-table-column(:label="$t('common.name')") v-btn(icon @click='deleteInstance(item)' color='error')
//- template(slot-scope='data') v-icon mdi-delete-forever
//- span {{data.row.name}}
//- el-table-column(:label="$t('common.url')")
//- template(slot-scope='data')
//- span {{data.row.url}}
//- el-table-column(:label="$t('common.place')")
//- template(slot-scope='data')
//- span {{data.row.label}}
//- el-table-column(:label="$t('common.actions')")
//- template(slot-scope='data')
//- el-button(size='mini'
//- type='danger'
//- @click='deleteInstance(data.row)') {{$t('admin.delete_user')}}
</template> </template>
<script> <script>
import { mapActions, mapState } from 'vuex' import { mapActions, mapState } from 'vuex'
import { Message, MessageBox } from 'element-ui'
import axios from 'axios' import axios from 'axios'
export default { export default {
@@ -81,7 +70,14 @@ export default {
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,
url2host: $options.filters.url2host url2host: $options.filters.url2host,
dialogAddInstance: false,
headers: [
{ value: 'name', text: 'Name' },
{ value: 'url', text: 'URL' },
{ value: 'label', text: 'Place' },
{ value: 'actions', text: 'Actions', align: 'right' }
]
} }
}, },
computed: { computed: {
@@ -103,9 +99,6 @@ export default {
set (value) { this.setSetting({ key: 'enable_trusted_instances', value }) } set (value) { this.setSetting({ key: 'enable_trusted_instances', value }) }
} }
}, },
mounted () {
console.error(this.$options.filters)
},
methods: { methods: {
...mapActions(['setSetting']), ...mapActions(['setSetting']),
async createTrustedInstance () { async createTrustedInstance () {
@@ -121,26 +114,20 @@ export default {
}) })
this.instance_url = '' this.instance_url = ''
} catch (e) { } catch (e) {
Message({ this.$root.$message({
showClose: true,
type: 'error', type: 'error',
message: e message: e
}) })
} }
}, },
deleteInstance (instance) { async deleteInstance (instance) {
MessageBox.confirm(this.$t('admin.delete_trusted_instance_confirm'), const ret = await this.$root.$confirm(this.$t('admin.delete_trusted_instance_confirm'),
this.$t('common.confirm'), { this.$t('common.confirm'), { type: 'error' })
confirmButtonText: this.$t('common.ok'), if (!ret) { return }
cancelButtonText: this.$t('common.cancel'),
type: 'error'
}
).then(() => {
this.setSetting({ this.setSetting({
key: 'trusted_instances', key: 'trusted_instances',
value: this.settings.trusted_instances.filter(i => i.url !== instance.url) value: this.settings.trusted_instances.filter(i => i.url !== instance.url)
}) })
})
}, },
save (key, value) { save (key, value) {
if (this.settings[key] !== value) { if (this.settings[key] !== value) {

View File

@@ -1,70 +1,66 @@
<template lang='pug'> <template lang='pug'>
div v-container
el-row v-row
el-col(:span='12') v-col(:span='12')
el-divider {{$t('common.instances')}} //- el-divider {{$t('common.instances')}}
el-input(v-model='instancesFilter' :placeholder="$t('admin.filter_instances')") v-text-field(v-model='instancesFilter' :placeholder="$t('admin.filter_instances')")
client-only v-data-table(:data='paginatedInstances' small @row-click='instanceSelected')
el-pagination(v-if='instances.length>perPage' :page-size='perPage' :currentPage.sync='instancePage' :total='instances.length') //- el-table-column(label='Domain' width='180')
el-table(:data='paginatedInstances' small @row-click='instanceSelected') //- template(slot-scope='data')
el-table-column(label='Domain' width='180') //- span(slot='reference') {{data.row.domain}}
template(slot-scope='data') //- el-table-column(label='Name' width='100')
span(slot='reference') {{data.row.domain}} //- template(slot-scope='data')
el-table-column(label='Name' width='100') //- span(slot='reference') {{data.row.name}}
template(slot-scope='data') //- el-table-column(:label="$t('common.users')" width='70')
span(slot='reference') {{data.row.name}} //- template(slot-scope='data')
el-table-column(:label="$t('common.users')" width='70') //- span(slot='reference') {{data.row.users}}
template(slot-scope='data') //- el-table-column(:label="$t('common.actions')" width='120')
span(slot='reference') {{data.row.users}} //- template(slot-scope='data')
el-table-column(:label="$t('common.actions')" width='120') //- el-button-group
template(slot-scope='data') //- el-button(size='mini'
el-button-group //- :type='data.row.blocked?"danger":"warning"'
el-button(size='mini' //- @click='toggleBlock(data.row)') {{data.row.blocked?$t('admin.unblock'):$t('admin.block')}}
:type='data.row.blocked?"danger":"warning"'
@click='toggleBlock(data.row)') {{data.row.blocked?$t('admin.unblock'):$t('admin.block')}}
el-col.float-right(:span='11' align='right') v-col.float-right(:span='11' align='right')
el-divider {{$t('common.users')}} //- el-divider {{$t('common.users')}}
el-input(v-model='usersFilter' :placeholder="$t('admin.filter_users')") v-text-field(v-model='usersFilter' :placeholder="$t('admin.filter_users')")
client-only v-data-table(:data='paginatedSelectedUsers' small)
el-pagination(v-if='users.length>perPage' :page-size='perPage' :currentPage.sync='userPage' :total='users.length') //- el-table-column(:label="$t('common.user')" width='150')
el-table(:data='paginatedSelectedUsers' small) //- template(slot-scope='data')
el-table-column(:label="$t('common.user')" width='150') //- span(slot='reference')
template(slot-scope='data') //- a(:href='data.row.object.id' target='_blank') {{data.row.object.name}}
span(slot='reference') //- small ({{data.row.object.preferredUsername}})
a(:href='data.row.object.id' target='_blank') {{data.row.object.name}} //- el-table-column(:label="$t('common.resources')" width='90')
small ({{data.row.object.preferredUsername}}) //- template(slot-scope='data')
el-table-column(:label="$t('common.resources')" width='90') //- span {{data.row.resources.length}}
template(slot-scope='data') //- el-table-column(:label="$t('common.actions')" width='200')
span {{data.row.resources.length}} //- template(slot-scope='data')
el-table-column(:label="$t('common.actions')" width='200') //- el-button-group
template(slot-scope='data') //- el-button(size='mini'
el-button-group //- :type='data.row.blocked?"danger":"warning"'
el-button(size='mini' //- @click='toggleUserBlock(data.row)') {{data.row.blocked?$t('admin.unblock'):$t('admin.block')}}
:type='data.row.blocked?"danger":"warning"'
@click='toggleUserBlock(data.row)') {{data.row.blocked?$t('admin.unblock'):$t('admin.block')}}
div div
el-divider {{$t('common.resources')}} //- el-divider {{$t('common.resources')}}
el-table(:data='paginatedResources' small :row-style='resourceStyle') v-table(:data='paginatedResources' small :row-style='resourceStyle')
el-table-column(:label="$t('common.event')") //- el-table-column(:label="$t('common.event')")
template(slot-scope='data') //- template(slot-scope='data')
span {{data.row.event}} //- span {{data.row.event}}
el-table-column(:label="$t('common.resources')") //- el-table-column(:label="$t('common.resources')")
template(slot-scope='data') //- template(slot-scope='data')
span(:class='{disabled: data.row.hidden}' v-html='data.row.data.content') //- span(:class='{disabled: data.row.hidden}' v-html='data.row.data.content')
el-table-column(:label="$t('common.user')" width='200') //- el-table-column(:label="$t('common.user')" width='200')
template(slot-scope='data') //- template(slot-scope='data')
span(:class='{disabled: data.row.hidden}' v-html='data.row.data.actor') //- span(:class='{disabled: data.row.hidden}' v-html='data.row.data.actor')
el-table-column(:label="$t('common.actions')" width="150") //- el-table-column(:label="$t('common.actions')" width="150")
template(slot-scope='data') //- template(slot-scope='data')
el-dropdown //- el-dropdown
el-button(type="primary" icon="el-icon-arrow-down" size='mini') {{$t('common.moderation')}} //- el-button(type="primary" icon="el-icon-arrow-down" size='mini') {{$t('common.moderation')}}
el-dropdown-menu(slot='dropdown') //- el-dropdown-menu(slot='dropdown')
el-dropdown-item(v-if='!data.row.hidden' icon='el-icon-remove' @click.native='hideResource(data.row, true)') {{$t('admin.hide_resource')}} //- el-dropdown-item(v-if='!data.row.hidden' icon='el-icon-remove' @click.native='hideResource(data.row, true)') {{$t('admin.hide_resource')}}
el-dropdown-item(v-else icon='el-icon-success' @click.native='hideResource(data.row, false)') {{$t('admin.show_resource')}} //- el-dropdown-item(v-else icon='el-icon-success' @click.native='hideResource(data.row, false)') {{$t('admin.show_resource')}}
el-dropdown-item(icon='el-icon-delete' @click.native='deleteResource(data.row)') {{$t('admin.delete_resource')}} //- el-dropdown-item(icon='el-icon-delete' @click.native='deleteResource(data.row)') {{$t('admin.delete_resource')}}
el-dropdown-item(icon='el-icon-lock' @click.native='toggleUserBlock(data.row.ap_user)') {{$t('admin.block_user')}} //- el-dropdown-item(icon='el-icon-lock' @click.native='toggleUserBlock(data.row.ap_user)') {{$t('admin.block_user')}}
</template> </template>
<script> <script>
import { mapState, mapActions } from 'vuex' import { mapState, mapActions } from 'vuex'
@@ -135,7 +131,7 @@ export default {
async toggleUserBlock (ap_user) { async toggleUserBlock (ap_user) {
try { try {
if (!ap_user.blocked) { if (!ap_user.blocked) {
await MessageBox.confirm(this.$t('admin.user_block_confirm'), { await this.$root.$confirm(this.$t('admin.user_block_confirm'), {
confirmButtonText: this.$t('common.ok'), confirmButtonText: this.$t('common.ok'),
cancelButtonText: this.$t('common.cancel'), cancelButtonText: this.$t('common.cancel'),
type: 'error' type: 'error'
@@ -146,7 +142,7 @@ export default {
} catch (e) { } } catch (e) { }
}, },
deleteResource (resource) { deleteResource (resource) {
MessageBox.confirm(this.$t('admin.delete_resource_confirm'), this.$root.$confirm(this.$t('admin.delete_resource_confirm'),
this.$t('common.confirm'), { this.$t('common.confirm'), {
confirmButtonText: this.$t('common.ok'), confirmButtonText: this.$t('common.ok'),
cancelButtonText: this.$t('common.cancel'), cancelButtonText: this.$t('common.cancel'),
@@ -164,8 +160,3 @@ export default {
} }
} }
</script> </script>
<style lang="less">
.instance_thumb {
height: 20px;
}
</style>

View File

@@ -1,6 +1,8 @@
<template lang='pug'> <template lang='pug'>
v-container v-container
v-subheader(v-html="$t('admin.place_description')") v-subheader(v-html="$t('admin.place_description')")
v-dialog
v-form.mb-2 v-form.mb-2
//- el-form-item(:label="$t('common.name')") //- el-form-item(:label="$t('common.name')")
//- el-input.mr-1(:placeholder='$t("common.name")' v-model='place.name') //- el-input.mr-1(:placeholder='$t("common.name")' v-model='place.name')
@@ -17,6 +19,8 @@
v-btn(@click='savePlace') {{$t('common.save')}} v-btn(@click='savePlace') {{$t('common.save')}}
v-data-table( v-data-table(
@click:row='selectPlace'
:headers='headers'
:items='places') :items='places')
</template> </template>
<script> <script>
@@ -24,17 +28,20 @@ import { mapState } from 'vuex'
export default { export default {
data () { data () {
return { return {
place: { name: '', address: '', id: null } place: { name: '', address: '', id: null },
headers: [
{ value: 'name', text: 'Name' }
]
} }
}, },
computed: mapState(['places']), computed: mapState(['places']),
methods: { methods: {
placeSelected (items) { selectPlace (item) {
if (items.length === 0) { // if (items.length === 0) {
this.place.name = this.place.address = '' // this.place.name = this.place.address = ''
return // return
} // }
const item = items[0] // const item = items[0]
this.place.name = item.name this.place.name = item.name
this.place.address = item.address this.place.address = item.address
this.place.id = item.id this.place.id = item.id

View File

@@ -20,8 +20,6 @@
:label="$t('admin.instance_locale')" :label="$t('admin.instance_locale')"
:hint="$t('admin.instance_locale_description')" :hint="$t('admin.instance_locale_description')"
persistent-hint persistent-hint
item-text='locale'
item-value='locale'
:items='locales' :items='locales'
) )
@@ -38,20 +36,6 @@
persistent-hint persistent-hint
@blur='save("description", description)') @blur='save("description", description)')
v-file-input.mt-5(ref='upload'
:label="$t('admin.favicon')"
:action='`${settings.baseurl}/api/settings/logo`'
:on-success="forceLogoReload"
name='logo'
:show-file-list="true"
accept='image/png'
:limit='1'
:multiple='false')
//- el-button-group
el-button(size='small' type='primary' plain) Select file
el-button(size='small' type='success' plain @click='resetLogo') Reset
v-img(:src='`${settings.baseurl}/favicon.ico?${logoKey}`')
v-switch.mt-5(v-model='allow_registration' v-switch.mt-5(v-model='allow_registration'
inset inset
:label="$t('admin.allow_registration_description')") :label="$t('admin.allow_registration_description')")
@@ -82,8 +66,7 @@ export default {
return { return {
title: $store.state.settings.title, title: $store.state.settings.title,
description: $store.state.settings.description, description: $store.state.settings.description,
locales, locales: Object.keys(locales).map(locale => ({ value: locale, text: locales[locale] }))
logoKey: 0
} }
}, },
computed: { computed: {
@@ -123,15 +106,6 @@ export default {
}, },
methods: { methods: {
...mapActions(['setSetting']), ...mapActions(['setSetting']),
forceLogoReload () {
this.$refs.upload.clearFiles()
this.logoKey++
},
resetLogo (e) {
this.setSetting({ key: 'favicon', value: null })
.then(this.forceLogoReload)
e.stopPropagation()
},
save (key, value) { save (key, value) {
if (this.settings[key] !== value) { if (this.settings[key] !== value) {
this.setSetting({ key, value }) this.setSetting({ key, value })

View File

@@ -0,0 +1,85 @@
<template lang="pug">
v-container
//- LOGO
v-file-input.mt-5(ref='upload'
:label="$t('admin.favicon')"
@change='uploadLogo'
accept='image/*')
template(slot='append-outer')
v-btn(small @click='resetLogo') Reset
v-img(:src='`${settings.baseurl}/favicon.ico?${logoKey}`'
max-width="100px" max-height="80px" contain)
//- el-button-group
el-button(size='small' type='primary' plain) Select file
el-button(size='small' type='success' plain @click='resetLogo') Reset
v-switch.mt-5(v-model='is_dark'
inset
:label="$t('admin.is_dark')")
v-color-picker(
mode='hexa'
:label="$t('common.primary_color')"
v-model='primary_color')
</template>
<script>
import { mapActions, mapState } from 'vuex'
export default {
name: 'Theme',
data () {
return {
logoKey: 0
}
},
computed: {
...mapState(['settings']),
is_dark: {
get () { return this.settings['theme.is_dark'] },
set (value) {
this.$vuetify.theme.dark = value
this.setSetting({ key: 'theme.is_dark', value })
}
},
primary_color: {
get () { return this.settings['theme.primary'] },
set (value) {
// this.$vuetify.theme.themes.dark.primary = value.hex
this.$vuetify.theme.themes.light.primary = value.hex
this.setSetting({ key: 'theme.primary', value: value.hex })
}
}
},
methods: {
...mapActions(['setSetting']),
forceLogoReload () {
this.$refs.upload.reset()
this.logoKey++
},
resetLogo (e) {
this.setSetting({ key: 'logo', value: null })
.then(this.forceLogoReload)
e.stopPropagation()
},
async uploadLogo (file) {
const formData = new FormData()
formData.append('logo', file)
try {
await this.$axios.$post('/settings/logo', formData)
this.$root.$emit('message', {
message: 'Logo updated'
})
this.forceLogoReload()
} catch (e) {
}
},
save (key, value) {
if (this.settings[key] !== value) {
this.setSetting({ key, value })
}
}
}
}
</script>

View File

@@ -1,59 +1,47 @@
<template lang="pug"> <template lang="pug">
v-container v-card
v-card-title {{$t('common.users')}}
v-spacer
v-text-field(v-model='search'
append-icon='mdi-magnify'
label='Search',
single-line hide-details)
//- ADD NEW USER //- ADD NEW USER
v-dialog(v-model='newUser' width='500') v-dialog(v-model='newUserDialog' width='500')
template(v-slot:activator="{ on }")
v-btn(text v-on='on') <v-icon>mdi-plus</v-icon> {{$t('common.new_user')}}
v-card v-card
v-card-title <v-icon name='plus'/> {{$t('common.new_user')}} v-card-title {{$t('common.new_user')}}
v-card-text v-card-text
v-form(inline @submit.native.prevent='create_user') v-form
v-text-field(v-model='new_user.email' v-text-field(v-model='new_user.email'
:label="$t('common.email')") :label="$t('common.email')"
:rules="[validators.required('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') {{$t('admin.user_add_help')}}
v-card-actions v-card-actions
v-btn(@click='create_user' color='success' plain) {{$t('common.send')}} v-btn(@click='createUser' color='primary') {{$t('common.send')}}
v-btn(@click='newUserDialog=false' color='danger' plain) {{$t('common.close')}}
//- USERS LIST //- USERS LIST
v-data-table( v-data-table(
:headers='headers' :headers='headers'
:items='users') :items='users'
:search='search')
template(v-slot:item.actions='{item}') template(v-slot:item.actions='{item}')
v-btn(text small @click='toggle(item)' v-btn(text small @click='toggle(item)'
:color='item.is_active?"warning":"success"') {{item.is_active?$t('common.deactivate'):$t('common.activate')}} :color='item.is_active?"warning":"success"') {{item.is_active?$t('common.deactivate'):$t('common.activate')}}
v-btn(text small @click='toggleAdmin(item)' v-btn(text small @click='toggleAdmin(item)'
:color='item.is_admin?"warning":"error"') {{item.is_admin?$t('common.remove_admin'):$t('common.admin')}} :color='item.is_admin?"warning":"error"') {{item.is_admin?$t('common.remove_admin'):$t('common.admin')}}
v-btn(text small @click='deleteUser(item)' v-btn(text small @click='deleteUser(item)'
:color='danger') {{$t('admin.delete_user')}} color='error') {{$t('admin.delete_user')}}
//- el-table-column(label='Email' width='220') v-btn(text color='primary' small @click='newUserDialog = true') <v-icon>mdi-plus-user</v-icon> {{$t('common.new_user')}}
//- template(slot-scope='data')
//- el-popover(trigger='hover' :content='data.row.description' width='400')
//- span(slot='reference') {{data.row.email}}
//- el-table-column(:label="$t('common.actions')")
//- template(slot-scope='data')
//- div(v-if='data.row.id!==$auth.user.id')
//- el-button-group
//- el-button(size='mini'
//- :type='data.row.is_active?"warning":"success"'
//- @click='toggle(data.row)') {{data.row.is_active?$t('common.deactivate'):$t('common.activate')}}
//- el-button(size='mini'
//- :type='data.row.is_admin?"danger":"warning"'
//- @click='toggleAdmin(data.row)') {{data.row.is_admin?$t('admin.remove_admin'):$t('common.admin')}}
//- el-button(size='mini'
//- type='danger'
//- @click='delete_user(data.row)') {{$t('admin.delete_user')}}
//- div(v-else)
//- span {{$t('common.me')}}
//- v-pagination(:page-size='perPage' :currentPage.sync='userPage' v-if='perPage<users_.length' :total='users_.length')
</template> </template>
<script> <script>
import { Message, MessageBox } from 'element-ui'
import { mapState } from 'vuex' import { mapState } from 'vuex'
import { validators } from '../../plugins/helpers'
export default { export default {
name: 'Users', name: 'Users',
@@ -62,12 +50,16 @@ export default {
}, },
data () { data () {
return { return {
validators,
newUserDialog: false,
new_user: { new_user: {
email: '', email: '',
is_admin: false is_admin: false
}, },
search: '',
headers: [ headers: [
{ value: 'email', text: 'Email' }, { value: 'email', text: 'Email' },
{ value: 'is_active', text: 'Active' },
{ value: 'actions', text: 'Actions', align: 'right' } { value: 'actions', text: 'Actions', align: 'right' }
] ]
} }
@@ -75,7 +67,7 @@ export default {
computed: mapState(['settings']), computed: mapState(['settings']),
methods: { methods: {
deleteUser (user) { deleteUser (user) {
MessageBox.confirm(this.$t('admin.delete_user_confirm'), this.$root.$confirm(this.$t('admin.delete_user_confirm'),
this.$t('common.confirm'), { this.$t('common.confirm'), {
confirmButtonText: this.$t('common.ok'), confirmButtonText: this.$t('common.ok'),
cancelButtonText: this.$t('common.cancel'), cancelButtonText: this.$t('common.cancel'),
@@ -83,11 +75,7 @@ export default {
}) })
.then(() => this.$axios.delete(`/user/${user.id}`)) .then(() => this.$axios.delete(`/user/${user.id}`))
.then(() => { .then(() => {
Message({ this.$root.$message({ message: this.$t('admin.user_remove_ok')})
showClose: true,
type: 'success',
message: this.$t('admin.user_remove_ok')
})
this.users_ = this.users_.filter(u => u.id !== user.id) this.users_ = this.users_.filter(u => u.id !== user.id)
}) })
}, },
@@ -103,11 +91,12 @@ export default {
console.error(e) console.error(e)
} }
}, },
async create_user () { async createUser () {
try { try {
this.loading = true this.loading = true
const user = await this.$axios.$post('/user', this.new_user) const user = await this.$axios.$post('/user', this.new_user)
this.new_user = { email: '', is_admin: false } this.new_user = { email: '', is_admin: false }
Message({ Message({
showClose: true, showClose: true,
type: 'success', type: 'success',

View File

@@ -1,42 +1,25 @@
<template lang='pug'> <template lang='pug'>
v-app(app) v-app(app)
Snackbar Snackbar
Confirm
Nav Nav
v-main(app) v-main(app)
v-scroll-x-transition(hide-on-leave) v-scroll-y-transition(hide-on-leave)
nuxt nuxt
Footer Footer
//- el-container#main(:class='{dark: $route.name==="index" || $route.name==="announcement-id"}')
el-dialog(:visible.sync='showFollowMe')
h4(slot='title') {{$t('common.follow_me_title')}}
FollowMe
el-backtop
Nav
#content
nuxt
el-footer.mt-1#footer
#links
</template> </template>
<script> <script>
import Nav from '~/components/Nav.vue' import Nav from '~/components/Nav.vue'
import FollowMe from '../components/FollowMe' import FollowMe from '../components/FollowMe'
import Snackbar from '../components/Snackbar' import Snackbar from '../components/Snackbar'
import Footer from '../components/Footer' import Footer from '../components/Footer'
import Confirm from '../components/Confirm'
export default { export default {
name: 'Default', name: 'Default',
components: { Nav, FollowMe, Snackbar, Footer }, components: { Nav, FollowMe, Snackbar, Footer, Confirm },
data () {
return { showFollowMe: false }
},
watch: {
'$route.name' (name) {
this.$vuetify.theme.dark = name !== 'NewEvent'
}
}
} }
</script> </script>

View File

@@ -1,9 +1,25 @@
<template lang='pug'> <template lang='pug'>
el-container#modal v-app(app)
el-header Snackbar
.row.p-0.m-0 Confirm
.col.p-0 Nav
.col-xl-5.col-lg-6.col-sm-10.col-xs-12.col-md-7.p-0
v-main(app)
v-scroll-y-transition(hide-on-leave)
nuxt nuxt
.col.p-0
Footer
</template> </template>
<script>
import Nav from '~/components/Nav.vue'
import FollowMe from '../components/FollowMe'
import Snackbar from '../components/Snackbar'
import Footer from '../components/Footer'
import Confirm from '../components/Confirm'
export default {
name: 'Default',
components: { Nav, FollowMe, Snackbar, Footer, Confirm },
}
</script>

View File

@@ -8,6 +8,11 @@
v-tab-item v-tab-item
Settings Settings
//- THEME
v-tab {{$t('common.theme')}}
v-tab-item
Theme
//- USERS //- USERS
v-tab v-tab
v-badge(:value='unconfirmedUsers.length' :content='unconfirmedUsers.length') {{$t('common.users')}} v-badge(:value='unconfirmedUsers.length' :content='unconfirmedUsers.length') {{$t('common.users')}}
@@ -21,24 +26,13 @@
//- EVENTS //- EVENTS
v-tab v-tab
v-badge(:content='events.length') {{$t('common.events')}} v-badge(:value='events.length') {{$t('common.events')}}
v-tab-item v-tab-item
p {{$t('admin.event_confirm_description')}} v-container
v-subheader {{$t('admin.event_confirm_description')}}
v-data-table( v-data-table(
:items='events' :items='events'
:headers='eventHeaders' :headers='eventHeaders')
)
//- el-table-column(:label='$t("common.name")' width='300')
//- template(slot-scope='data') {{data.row.title}}
//- el-table-column(:label='$t("common.where")' width='250')
//- template(slot-scope='data') {{data.row.place.name}}
//- el-table-column(:label='$t("common.confirm")' width='250')
//- template(slot-scope='data')
//- el-button-group
//- el-button(type='primary' @click='confirm(data.row.id)' size='mini') {{$t('common.confirm')}}
//- el-button(type='success' @click='preview(data.row.id)' size='mini') {{$t('common.preview')}}
//- client-only
//- el-pagination(v-if='events.length>perPage' :page-size='perPage' :currentPage.sync='eventPage' :total='events.length')
//- ANNOUNCEMENTS //- ANNOUNCEMENTS
v-tab {{$t('common.announcements')}} v-tab {{$t('common.announcements')}}
@@ -65,10 +59,11 @@ import Settings from '../components/admin/Settings'
import Federation from '../components/admin/Federation' import Federation from '../components/admin/Federation'
import Moderation from '../components/admin/Moderation' import Moderation from '../components/admin/Moderation'
import Announcement from '../components/admin/Announcement' import Announcement from '../components/admin/Announcement'
import Theme from '../components/admin/Theme'
export default { export default {
name: 'Admin', name: 'Admin',
components: { Users, Places, Settings, Federation, Moderation, Announcement }, components: { Users, Places, Settings, Federation, Moderation, Announcement, Theme },
middleware: ['auth'], middleware: ['auth'],
async asyncData ({ $axios, params, store }) { async asyncData ({ $axios, params, store }) {
try { try {
@@ -83,7 +78,10 @@ export default {
data () { data () {
return { return {
description: '', description: '',
events: [] events: [],
eventHeaders: [
{ value: 'title', text: 'Title' }
]
} }
}, },
computed: { computed: {

View File

@@ -7,18 +7,19 @@
v-card-subtitle(v-text="$t('login.description')") v-card-subtitle(v-text="$t('login.description')")
v-card-text v-card-text
v-form
v-text-field(v-model='email' type='email' v-text-field(v-model='email' type='email'
:rules='validators.email' autofocus
:placeholder='$t("common.email")' :placeholder='$t("common.email")'
ref='email') ref='email')
v-text-field(v-model='password' v-text-field(v-model='password'
:rules='validators.password'
type='password' type='password'
:placeholder='$t("common.password")') :placeholder='$t("common.password")')
v-card-actions v-card-actions
v-btn(color='success' v-btn(color='success'
text
:disabled='disabled' :disabled='disabled'
@click='submit') {{$t('common.login')}} @click='submit') {{$t('common.login')}}
@@ -32,12 +33,13 @@
<script> <script>
import { mapState } from 'vuex' import { mapState } from 'vuex'
import { Message } from 'element-ui' import { validators } from '../plugins/helpers'
export default { export default {
name: 'Login', name: 'Login',
data () { data () {
return { return {
validators,
password: '', password: '',
email: '', email: '',
loading: false loading: false
@@ -49,20 +51,17 @@ export default {
return !this.email || !this.password return !this.email || !this.password
} }
}, },
mounted () {
this.$refs.email.focus()
},
methods: { methods: {
async forgot () { async forgot () {
if (!this.email) { if (!this.email) {
Message({ message: this.$t('login.insert_email'), showClose: true, type: 'error' }) this.$root.$message({ message: this.$t('login.insert_email'), color: 'error' })
this.$refs.email.focus() this.$refs.email.focus()
return return
} }
this.loading = true this.loading = true
await this.$axios.$post('/user/recover', { email: this.email }) await this.$axios.$post('/user/recover', { email: this.email })
this.loading = false this.loading = false
Message({ message: this.$t('login.check_email'), type: 'success' }) this.$root.$message({ message: this.$t('login.check_email'), color: 'success' })
}, },
async submit (e) { async submit (e) {
if (this.disabled) { return false } if (this.disabled) { return false }
@@ -76,9 +75,9 @@ export default {
data.append('client_id', 'self') data.append('client_id', 'self')
await this.$auth.loginWith('local', { data }) await this.$auth.loginWith('local', { data })
this.loading = false this.loading = false
Message({ message: this.$t('login.ok'), showClose: true, type: 'success' }) this.$root.$message({ message: this.$t('login.ok'), color: 'success' })
} catch (e) { } catch (e) {
Message({ message: this.$t('login.error'), showClose: true, type: 'error' }) this.$root.$message({ message: this.$t('login.error'), color: 'error' })
this.loading = false this.loading = false
return return
} }

View File

@@ -8,28 +8,35 @@
v-card-text v-card-text
p(v-html="$t('register.description')") p(v-html="$t('register.description')")
v-text-field(ref='email' v-model='user.email' type='email' required v-form(ref='form')
:placeholder='$t("common.email")' autocomplete='email' v-text-field(ref='email'
prefix-icon='el-icon-message') v-model='user.email' type='email'
:rules="validators.email"
:label='$t("common.email")' autocomplete='email')
v-text-field(v-model='user.password' type="password" v-text-field(v-model='user.password' type="password"
placeholder="Password") :rules="validators.password"
:label="$t('common.password')")
v-text-field(v-model='user.description' textarea rows='3' :placeholder="$t('common.description')") v-textarea(v-model='user.description'
:rules="[validators.required('description')]"
:label="$t('common.description')")
v-card-actions v-card-actions
v-btn(plain type="success" :disabled='disabled' @click='register') {{$t('common.send')}} <v-icon name='chevron-right'/> v-btn(@click='register' color='primary') {{$t('common.send')}}
v-icon mdi-chevron-right
</template> </template>
<script> <script>
import { mapState } from 'vuex' import { mapState } from 'vuex'
import { Message } from 'element-ui'
import get from 'lodash/get' import get from 'lodash/get'
import { validators } from '../plugins/helpers'
export default { export default {
name: 'Register', name: 'Register',
data () { data () {
return { return {
validators,
loading: false, loading: false,
user: {} user: {}
} }
@@ -41,34 +48,30 @@ export default {
}, },
computed: { computed: {
...mapState(['settings']), ...mapState(['settings']),
disabled () { // disabled () {
if (process.server) { return false } // if (process.server) { return false }
return !this.user.password || !this.user.email || !this.user.description // return !this.user.password || !this.user.email || !this.user.description
} // }
}, },
mounted () { mounted () {
this.$refs.email.focus() this.$refs.email.focus()
}, },
methods: { methods: {
async register () { async register () {
const valid = this.$refs.form.validate()
if (!valid) { return }
try { try {
this.loading = true this.loading = true
const user = await this.$axios.$post('/user/register', this.user) const user = await this.$axios.$post('/user/register', this.user)
// this is the first user registered // this is the first user registered
const first_user = user && user.is_admin && user.is_active const first_user = user && user.is_admin && user.is_active
Message({ this.$root.$message({
showClose: true, message: first_user ? this.$t('register.first_user') : this.$t('register.complete')
message: first_user ? this.$t('register.first_user') : this.$t('register.complete'),
type: 'success'
}) })
this.$router.replace('/') this.$router.replace('/')
} catch (e) { } catch (e) {
const error = get(e, 'response.data.errors[0].message', String(e)) const error = get(e, 'response.data.errors[0].message', String(e))
Message({ this.$root.$message({ message: this.$t(error), color: 'error' })
showClose: true,
message: this.$t(error),
type: 'error'
})
} }
this.loading = false this.loading = false
} }

View File

@@ -1,7 +1,10 @@
<template lang="pug"> <template lang="pug">
v-container v-container
v-card
v-card-text
h2.text-center {{edit?$t('common.edit_event'):$t('common.add_event')}} h2.text-center {{edit?$t('common.edit_event'):$t('common.add_event')}}
v-form p {{valid}}
v-form(v-model='valid')
//- NOT LOGGED EVENT //- NOT LOGGED EVENT
div(v-if='!$auth.loggedIn') div(v-if='!$auth.loggedIn')
@@ -10,19 +13,27 @@
//- title //- title
v-text-field.mb-3(v-model='event.title' v-text-field.mb-3(v-model='event.title'
:rules="[validators.required('title')]"
:label="$t('event.what_description')" :label="$t('event.what_description')"
ref='title') ref='title')
//- description //- description
//- span {{$t('event.description_description')}} //- span {{$t('event.description_description')}}
//- Editor.mb-3(v-model='event.description' border no-save style='max-height: 400px;') Editor(
v-model='event.description'
:label="$t('event.description')"
style='max-height: 400px;')
//- tags //- tags
//- div {{$t('event.tag_description')}} //- div {{$t('event.tag_description')}}
//- client-only //- client-only
//- v-select.m b-3(v-model='event.tags' multiple filterable v-combobox(v-model='event.tags'
//- @input.native='queryTags=$event.target.value' @change='queryTags=""' chips multiple
//- allow-create default-first-option placeholder='Tag') :items="tags"
item-text='tag',
item-value='tag'
:hints="$t('event.tag_description')"
:label="$t('common.tags')")
//- v-option(v-for='tag in filteredTags' :key='tag.tag' :label='tag.tag' :value='tag.tag') //- v-option(v-for='tag in filteredTags' :key='tag.tag' :label='tag.tag' :value='tag.tag')
//- WHERE //- WHERE
@@ -34,7 +45,7 @@
:label="$t('common.where')" :label="$t('common.where')"
:items="places" :items="places"
item-text="name" item-text="name"
item-value="id" item-value="name"
@change='selectPlace') @change='selectPlace')
//- div {{$t("common.address")}} //- div {{$t("common.address")}}
@@ -43,20 +54,21 @@
//- WHEN //- WHEN
//- v-divider <v-icon name='clock'/> {{$t('common.when')}} //- v-divider <v-icon name='clock'/> {{$t('common.when')}}
.text-center .text-center
v-btn-toggle(v-model="event.type") v-btn-toggle(v-model="event.type" color='primary')
v-btn(value='normal' label="normal") <v-icon name='calendar-day'/> {{$t('event.normal')}} v-btn(value='normal' label="normal") {{$t('event.normal')}}
v-btn(value='multidate' label="multidate") <v-icon name='calendar-week'/> {{$t('event.multidate')}} v-btn(value='multidate' label="multidate") {{$t('event.multidate')}}
v-btn(v-if='settings.allow_recurrent_event' value='recurrent' label="recurrent") <v-icon name='calendar-alt'/> {{$t('event.recurrent')}} v-btn(v-if='settings.allow_recurrent_event' value='recurrent' label="recurrent") {{$t('event.recurrent')}}
br
span {{$t(`event.${event.type}_description`)}} p {{$t(`event.${event.type}_description`)}}
v-select.ml-2(v-if='event.type==="recurrent"' v-model='event.recurrent.frequency' placeholder='Frequenza') v-select(v-if='event.type==="recurrent"'
v-option(:label="$t('event.each_week')" value='1w' key='1w') :items="frequencies"
v-option(:label="$t('event.each_2w')" value='2w' key='2w') v-model='event.recurrent.frequency')
//- v-option(:label="$t('event.each_week')" value='1w' key='1w')
//- v-option(:label="$t('event.each_2w')" value='2w' key='2w')
//- el-option(:label="$t('event.each_month')" value='1m' key='1m') //- el-option(:label="$t('event.each_month')" value='1m' key='1m')
client-only client-only
#picker.mx-auto v-date-picker.mx-auto(
v-date-picker.mb-2.mt-3(
:mode='datePickerMode' :mode='datePickerMode'
:attributes='attributes' :attributes='attributes'
v-model='date' v-model='date'
@@ -65,6 +77,7 @@
is-dark is-dark
is-inline is-inline
is-expanded is-expanded
:style="{width: '500px'}"
:min-date='event.type !== "recurrent" && new Date()') :min-date='event.type !== "recurrent" && new Date()')
div.text-center.mb-2(v-if='event.type === "recurrent"') div.text-center.mb-2(v-if='event.type === "recurrent"')
@@ -73,29 +86,47 @@
v-radio-button(v-for='whenPattern in whenPatterns' :label='whenPattern.key' :key='whenPatterns.key') v-radio-button(v-for='whenPattern in whenPatterns' :label='whenPattern.key' :key='whenPatterns.key')
span {{whenPattern.label}} span {{whenPattern.label}}
.text-center v-row
v-col
v-menu(v-model='fromDateMenu')
template(v-slot:activator='{ on }')
v-text-field(
:label="$t('event.from')"
v-on='on'
:value='time.start'
readonly)
v-time-picker.mr-2( v-time-picker.mr-2(
:label="$t('event.from')" :label="$t('event.from')"
ref='time_start' ref='time_start'
v-model="time.start") v-model="time.start")
v-time-picker(v-model='time.end' v-col
:label="$t('event.due')") v-menu(v-model='dueDateMenu')
template(v-slot:activator='{ on }')
v-text-field(
:label="$t('event.due')"
v-on='on'
:value='time.end'
readonly)
v-time-picker.mr-2(
:label="$t('event.due')"
v-model="time.end")
List(v-if='event.type==="normal" && todayEvents.length' :events='todayEvents' :title='$t("event.same_day")') List(v-if='event.type==="normal" && todayEvents.length' :events='todayEvents' :title='$t("event.same_day")')
//- MEDIA / FLYER / POSTER //- MEDIA / FLYER / POSTER
p {{JSON.stringify(event.image)}}
v-file-input( v-file-input(
:label="$t('common.media')" :label="$t('common.media')"
:hint="$t('event.media_description')" :hint="$t('event.media_description')"
filled
prepend-icon="mdi-camera" prepend-icon="mdi-camera"
v-model='event.image' v-model='event.image'
persistent-hint persistent-hint
accept='image/*') accept='image/*')
v-btn.mt-2.float-right(@click='done' :disabled='!couldProceed') {{edit?$t('common.edit'):$t('common.send')}}
v-card-actions
v-spacer
v-btn(@click='done' color='primary') {{edit?$t('common.edit'):$t('common.send')}}
</template> </template>
<script> <script>
@@ -104,7 +135,7 @@ import _ from 'lodash'
import moment from 'moment-timezone' import moment from 'moment-timezone'
import Editor from '@/components/Editor' import Editor from '@/components/Editor'
import List from '@/components/List' import List from '@/components/List'
import { Message } from 'element-ui' import { validators } from '../../plugins/helpers'
export default { export default {
name: 'NewEvent', name: 'NewEvent',
@@ -152,6 +183,10 @@ export default {
const month = moment().month() + 1 const month = moment().month() + 1
const year = moment().year() const year = moment().year()
return { return {
validators,
valid: false,
dueDateMenu: false,
fromDateMenu: false,
event: { event: {
type: 'normal', type: 'normal',
place: { name: '', address: '' }, place: { name: '', address: '' },
@@ -170,7 +205,12 @@ export default {
loading: false, loading: false,
mediaUrl: '', mediaUrl: '',
queryTags: '', queryTags: '',
disableAddress: true disableAddress: true,
frequencies: [
{ value: '1w', text: this.$t('event.each_week') },
{ value: '2w', text: this.$t('event.each_2w') },
{ value: '1m', text: this.$t('event.each_month') }
]
} }
}, },
computed: { computed: {
@@ -289,7 +329,6 @@ export default {
cb(ret) cb(ret)
}, },
selectPlace (p) { selectPlace (p) {
console.error('sono dentro selectPlace ', p)
const place = this.places.find(place => place.id === p) const place = this.places.find(place => place.id === p)
if (place && place.address) { if (place && place.address) {
this.event.place.address = place.address this.event.place.address = place.address
@@ -312,7 +351,6 @@ export default {
uploadedFile (files) { uploadedFile (files) {
// const file = files[0] // const file = files[0]
console.error('dentro uploadedfile', arguments)
// if (file.size / 1024 / 1024 > 4) { // if (file.size / 1024 / 1024 > 4) {
// Message({ type: 'warning', showClose: true, message: this.$tc('event.image_too_big') }) // Message({ type: 'warning', showClose: true, message: this.$tc('event.image_too_big') })
// this.fileList = [] // this.fileList = []
@@ -358,7 +396,8 @@ export default {
} }
if (this.event.image) { if (this.event.image) {
formData.append('image', this.event.image[0]) console.error(this.event.image)
formData.append('image', this.event.image)
} }
formData.append('title', this.event.title) formData.append('title', this.event.title)
formData.append('place_name', this.event.place.name) formData.append('place_name', this.event.place.name)
@@ -381,15 +420,15 @@ export default {
this.updateMeta() this.updateMeta()
this.$router.replace('/') this.$router.replace('/')
this.loading = false this.loading = false
Message({ type: 'success', showClose: true, message: this.$auth.loggedIn ? this.$t('event.added') : this.$t('event.added_anon') }) this.$root.$message({ type: 'success', message: this.$auth.loggedIn ? this.$t('event.added') : this.$t('event.added_anon') })
} catch (e) { } catch (e) {
console.error(e.response) console.error(e.response)
switch (e.request.status) { switch (e.request.status) {
case 413: case 413:
Message({ type: 'error', showClose: true, message: this.$t('event.image_too_big') }) this.$root.$message({ type: 'error', message: this.$t('event.image_too_big') })
break break
default: default:
Message({ type: 'error', showClose: true, message: e.response.data }) this.$root.$message({ type: 'error', message: e.response.data })
} }
this.loading = false this.loading = false
} }

View File

@@ -1,10 +1,8 @@
<template lang="pug"> <template lang="pug">
el-container.announcement-page.text-white v-container.announcement-page.text-white
el-header.text-white
h3 <i style='color: red' class='el-icon-info'/> {{announcement.title}} h3 <i style='color: red' class='el-icon-info'/> {{announcement.title}}
el-main p.mt-4(v-html='announcement.announcement')
pre.mt-4(v-html='announcement.announcement')
</template> </template>
<script> <script>
@@ -36,39 +34,39 @@ export default {
} }
</script> </script>
<style lang='less'> <style lang='less'>
.announcement-page { // .announcement-page {
.el-header { // .el-header {
height: auto !important; // height: auto !important;
padding-top: 1em; // padding-top: 1em;
border-bottom: 1px solid lightgray; // border-bottom: 1px solid lightgray;
} // }
.title { // .title {
max-width: 80%; // max-width: 80%;
max-height: 0.1rem; // max-height: 0.1rem;
overflow: hidden; // overflow: hidden;
font-size: 1.6rem; // font-size: 1.6rem;
line-height: 1; // line-height: 1;
padding-right: 40px; // padding-right: 40px;
} // }
pre { // pre {
white-space: pre-line; // white-space: pre-line;
word-break: break-word; // word-break: break-word;
color: #aaa; // color: #aaa;
font-size: 1.2em; // font-size: 1.2em;
font-family: inherit; // font-family: inherit;
} // }
} // }
@media only screen and (max-width: 768px) { // @media only screen and (max-width: 768px) {
#eventDetail { // #eventDetail {
.title { // .title {
font-size: 1em; // font-size: 1em;
font-weight: bold; // font-weight: bold;
} // }
} // }
} // }
</style> </style>

View File

@@ -1,58 +1,27 @@
<template lang="pug"> <template lang="pug">
v-card#eventDetail.h-event.d-inline-block-mx-auto v-card.h-event.eventDetail
v-toolbar(prominent) //- .d-block
v-container
v-list-item(two-line) v-list-item(two-line)
v-list-item-content v-list-item-content
h2(v-text='event.title') v-list-item-title
v-list-item-subtitle
time.dt-start(:datetime='event.start_datetime|unixFormat("YYYY-MM-DD HH:mm")') time.dt-start(:datetime='event.start_datetime|unixFormat("YYYY-MM-DD HH:mm")')
v-icon mdi-date v-icon mdi-date
b {{event|when}} b {{event|when}}
small ({{event.start_datetime|from}}) small ({{event.start_datetime|from}})
v-list-item-subtitle v-list-item-title
i.el-icon-location-outline i.el-icon-location-outline
b.p-location {{event.place.name}} b.p-location {{event.place.name}}
span - {{event.place.address}} span - {{event.place.address}}
v-spacer
v-btn.mr-1(nuxt :to='`/event/${event.prev}`' color='primary' icon :disabled='!event.prev')
v-icon mdi-arrow-left
v-btn(nuxt :to='`/event/${event.next}`' color='primary' :disabled='!event.next' icon)
v-icon mdi-arrow-right
//- h2 {{event.title}}
//- v-toolbar-subtitle
//- v-toolbar(prominent)
//- v-list-item
//- h3 {{event.title}}
//- v-row(justify='space-between')
v-col(cols='auto')
v-list-item(two-line)
v-list-item-content
v-list-item-title.headline(v-text='event.title')
v-list-item-subtitle
time.dt-start(:datetime='event.start_datetime|unixFormat("YYYY-MM-DD HH:mm")')
v-icon mdi-date
b {{event|when}}
small ({{event.start_datetime|from}})
v-list-item-subtitle
i.el-icon-location-outline
b.p-location {{event.place.name}}
span - {{event.place.address}}
v-col
v-btn.mr-1(nuxt :to='`/event/${event.prev}`' color='primary' icon :disabled='!event.prev')
v-icon mdi-arrow-left
v-btn(nuxt :to='`/event/${event.next}`' color='primary' :disabled='!event.next' icon)
v-icon mdi-arrow-right
template(slot='extension')
h2 {{event.title}} h2 {{event.title}}
//- v-list-item
//- i.el-icon-location-outline .v-btn--absolute.v-btn--right.v-btn--top
//- b.p-location {{event.place.name}} v-btn.mr-1(nuxt icon outlined color='primary'
//- span - {{event.place.address}} :to='`/event/${event.prev}`' :disabled='!event.prev')
//- v-spacer v-icon mdi-arrow-left
//- v-chip.p-category.ml-1(v-for='tag in event.tags' :key='tag' small color='secondary') {{tag}} v-btn(nuxt bottom right outlined icon color='primary'
:to='`/event/${event.next}`' :disabled='!event.next')
v-icon mdi-arrow-right
v-card-text v-card-text
v-dialog.embedDialog(:visible.sync='showEmbed') v-dialog.embedDialog(:visible.sync='showEmbed')
@@ -65,30 +34,22 @@
//- event image //- event image
v-img.main_image.mb-3( v-img.main_image.mb-3(
lazy lazy
contain
:src='imgPath' :src='imgPath'
:lazy-src='thumbImgPath' :lazy-src='thumbImgPath'
v-if='event.image_path') v-if='event.image_path')
p.p-description(v-html='event.description') p.p-description(v-html='event.description')
v-btn.p-category.ml-1(type='text' plain round size='mini' v-for='tag in event.tags' :key='tag') {{tag}} v-chip.p-category.ml-1(small v-for='tag in event.tags' color='primary' outlined :key='tag') {{tag}}
//- info & actions //- info & actions
//- v-col v-btn(text color='primary'
v-list
time.dt-start(:datetime='event.start_datetime|unixFormat("YYYY-MM-DD HH:mm")') <i class='el-icon-date'></i> <b>{{event|when}}</b> <br/><small>{{event.start_datetime|from}}</small>
p
i.el-icon-location-outline
b.p-location {{event.place.name}}
span - {{event.place.address}}
v-divider {{$t('common.actions')}}
v-list-item(
v-clipboard:success='copyLink' v-clipboard:success='copyLink'
v-clipboard:copy='`${settings.baseurl}/event/${event.id}`') <i class='el-icon-paperclip'></i> {{$t('common.copy_link')}} v-clipboard:copy='`${settings.baseurl}/event/${event.id}`') {{$t('common.copy_link')}}
v-list-item(@click='showEmbed=true') <i class='el-icon-copy-document'></i> {{$t('common.embed')}} v-btn(@click='showEmbed=true' text color='primary') {{$t('common.embed')}}
v-list-item v-btn(:href='`${settings.baseurl}/api/event/${event.id}.ics`' text color='primary') {{$t('common.add_to_calendar')}}
a(:href='`${settings.baseurl}/api/event/${event.id}.ics`') <i class='el-icon-date'></i> {{$t('common.add_to_calendar')}}
EventAdmin(v-if='is_mine' :event='event') EventAdmin(v-if='is_mine' :event='event')
//- hr //- hr
@@ -137,7 +98,6 @@ import { mapState } from 'vuex'
import EventAdmin from './eventAdmin' import EventAdmin from './eventAdmin'
import EmbedEvent from './embedEvent' import EmbedEvent from './embedEvent'
import FollowMe from '../../components/FollowMe' import FollowMe from '../../components/FollowMe'
import { Message, MessageBox } from 'element-ui'
import moment from 'moment-timezone' import moment from 'moment-timezone'
const htmlToText = require('html-to-text') const htmlToText = require('html-to-text')
@@ -269,7 +229,7 @@ export default {
}, },
async remove () { async remove () {
try { try {
await MessageBox.confirm(this.$t('event.remove_confirmation'), this.$t('common.confirm'), { await this.$root.$confirm(this.$t('event.remove_confirmation'), this.$t('common.confirm'), {
confirmButtonText: this.$t('common.ok'), confirmButtonText: this.$t('common.ok'),
cancelButtonText: this.$t('common.cancel'), cancelButtonText: this.$t('common.cancel'),
type: 'error' type: 'error'
@@ -300,18 +260,18 @@ export default {
}, },
async blockUser (resource) { async blockUser (resource) {
try { try {
await MessageBox.confirm(this.$t('admin.user_block_confirm'), { await this.$root.$confirm(this.$t('admin.user_block_confirm'), {
confirmButtonText: this.$t('common.ok'), confirmButtonText: this.$t('common.ok'),
cancelButtonText: this.$t('common.cancel'), cancelButtonText: this.$t('common.cancel'),
type: 'error' type: 'error'
}) })
await this.$axios.post('/instances/toggle_user_block', { ap_id: resource.ap_user.ap_id }) await this.$axios.post('/instances/toggle_user_block', { ap_id: resource.ap_user.ap_id })
Message({ message: this.$t('admin.user_blocked', { user: resource.ap_user.ap_id }), type: 'success', showClose: true }) this.$root.$message({ message: this.$t('admin.user_blocked', { user: resource.ap_user.ap_id }), type: 'success' })
} catch (e) { } } catch (e) { }
}, },
async deleteResource (resource) { async deleteResource (resource) {
try { try {
await MessageBox.confirm(this.$t('admin.delete_resource_confirm'), await this.$root.$confirm(this.$t('admin.delete_resource_confirm'),
this.$t('common.confirm'), { this.$t('common.confirm'), {
confirmButtonText: this.$t('common.ok'), confirmButtonText: this.$t('common.ok'),
cancelButtonText: this.$t('common.cancel'), cancelButtonText: this.$t('common.cancel'),
@@ -322,7 +282,7 @@ export default {
} catch (e) { } } catch (e) { }
}, },
copyLink () { copyLink () {
Message({ message: this.$t('common.copied'), type: 'success', showClose: true }) this.$root.$message({ message: this.$t('common.copied'), type: 'success' })
}, },
// TOFIX // TOFIX
resource_filter (value) { resource_filter (value) {
@@ -343,7 +303,21 @@ export default {
} }
</script> </script>
<style lang='less'> <style lang='less'>
// #eventDetail { .eventDetail {
.toolbar {
height: auto !important;
padding: 1em 0;
box-sizing: content-box;
}
.main_image {
width: 100%;
transition: height .100s;
margin: 0 auto;
max-height: 83vh;
}
}
// time { // time {
// margin: 0rem 0rem 0rem 1rem; // margin: 0rem 0rem 0rem 1rem;
// display: inline-block; // display: inline-block;

View File

@@ -1,26 +1,21 @@
<template lang='pug'> <template lang='pug'>
div div
v-divider {{$t('common.admin')}} 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-else @click='toggle(false)') {{$t('common.confirm')}}
v-menu.menu v-btn(text color='primary' @click='$router.push(`/add/${event.id}`)') {{$t('common.edit')}}
v-menu-item v-btn(text color='primary' v-if='!event.parentId' @click='remove(false)') {{$t('common.remove')}}
div(v-if='event.is_visible' @click='toggle(false)') <i class='el-icon-open'/> {{$t(`common.${event.parentId?'skip':'hide'}`)}}
div(v-else @click='toggle(false)') <i class='el-icon-turn-off'/> {{$t('common.confirm')}}
v-menu-item(@click='$router.push(`/add/${event.id}`)') <i class='el-icon-edit'/> {{$t('common.edit')}}
v-menu-item(v-if='!event.parentId' @click='remove(false)') <i class='el-icon-delete'/> {{$t('common.remove')}}
template(v-if='event.parentId') template(v-if='event.parentId')
v-divider {{$t('event.recurrent')}} v-divider {{$t('event.recurrent')}}
p.text-secondary p.text-secondary
i.el-icon-refresh i.el-icon-refresh
small {{event|recurrentDetail}}<br/> small {{event|recurrentDetail}}
v-menu-item(v-if='event.parent.is_visible' @click='toggle(true)') <i class='el-icon-video-pause'/> {{$t('common.pause')}} v-btn(text color='primary' v-if='event.parent.is_visible' @click='toggle(true)') {{$t('common.pause')}}
v-menu-item(v-else @click='toggle(true)') <i class='el-icon-video-play'/> {{$t('common.start')}} v-btn(text color='primary' v-else @click='toggle(true)') {{$t('common.start')}}
v-menu-item(@click='$router.push(`/add/${event.parentId}`)') <i class='el-icon-edit'/> {{$t('common.edit')}} v-btn(text color='primary' @click='$router.push(`/add/${event.parentId}`)') {{$t('common.edit')}}
v-menu-item(@click='remove(true)') <i class='el-icon-delete'/> {{$t('common.remove')}} v-btn(text color='primary' @click='remove(true)') {{$t('common.remove')}}
</template> </template>
<script> <script>
import { MessageBox } from 'element-ui'
import { mapActions } from 'vuex' import { mapActions } from 'vuex'
export default { export default {
@@ -34,19 +29,14 @@ export default {
methods: { methods: {
...mapActions(['delEvent']), ...mapActions(['delEvent']),
async remove (parent = false) { async remove (parent = false) {
try { const ret = await this.$root.$confirm(this.$t(`event.remove_${parent ? 'recurrent_' : ''}confirmation`), this.$t('common.confirm'), {
await MessageBox.confirm(this.$t(`event.remove_${parent ? 'recurrent_' : ''}confirmation`), this.$t('common.confirm'), {
confirmButtonText: this.$t('common.ok'),
cancelButtonText: this.$t('common.cancel'),
type: 'error' type: 'error'
}) })
if (!ret) { return }
const id = parent ? this.event.parentId : this.event.id const id = parent ? this.event.parentId : this.event.id
await this.$axios.delete(`/event/${id}`) await this.$axios.delete(`/event/${id}`)
this.delEvent(Number(id)) this.delEvent(Number(id))
this.$router.replace('/') this.$router.replace('/')
} catch (e) {
console.error(e)
}
}, },
async toggle (parent = false) { async toggle (parent = false) {
const id = parent ? this.event.parentId : this.event.id const id = parent ? this.event.parentId : this.event.id

View File

@@ -1,19 +1,25 @@
<template lang='pug'> <template lang='pug'>
el-card.mt-5 v-row.mt-5(align='center' justify='center')
h4(slot='header') v-col(cols='12' md="6" lg="5" xl="4")
nuxt-link(to='/') v-card
img(src='/favicon.ico') v-card-title {{settings.title}} - {{$t('common.recover_password')}}
span {{settings.title}} - {{$t('common.recover_password')}} //- nuxt-link(to='/')
//- v-img(src='/logo.png')
//- span {{settings.title}} - {{$t('common.recover_password')}}
v-card-text
div(v-if='valid') div(v-if='valid')
el-input(type='password', :placeholder='$t("common.new_password")' v-model='new_password' prefix-icon='el-icon-lock') v-text-field(type='password'
:rules="validators.password"
autofocus :placeholder='$t("common.new_password")'
v-model='new_password')
div(v-else) {{$t('recover.not_valid_code')}} div(v-else) {{$t('recover.not_valid_code')}}
el-button.mt-2(plain v-if='valid' type="success" icon='el-icon-check' v-card-actions
@click='change_password') {{$t('common.send')}} v-btn(v-if='valid' color='primary' @click='change_password') {{$t('common.send')}}
</template> </template>
<script> <script>
import { Message } from 'element-ui'
import { mapState } from 'vuex' import { mapState } from 'vuex'
import { validators } from '../../plugins/helpers'
export default { export default {
name: 'Recover', name: 'Recover',
@@ -28,22 +34,19 @@ export default {
} }
}, },
data () { data () {
return { new_password: '' } return { new_password: '', validators }
}, },
computed: mapState(['settings']), computed: mapState(['settings']),
methods: { methods: {
async change_password () { async change_password () {
try { try {
await this.$axios.$post('/user/recover_password', { recover_code: this.code, password: this.new_password }) await this.$axios.$post('/user/recover_password', { recover_code: this.code, password: this.new_password })
Message({ this.$root.$message({
showClose: true,
type: 'success',
message: this.$t('common.password_updated') message: this.$t('common.password_updated')
}) })
this.$router.replace('/login') this.$router.replace('/login')
} catch (e) { } catch (e) {
Message({ this.$root.$message({
showClose: true,
type: 'warning', type: 'warning',
message: e message: e
}) })

13
plugins/helpers.js Normal file
View File

@@ -0,0 +1,13 @@
const linkify = require('linkifyjs')
export const validators = {
required (fieldName) {
return value => !!value || `validators.required.${fieldName}`
},
email: [
v => !!v || 'validators.required.email',
v => (v && !!linkify.test(v, 'email')) || 'validators.valid.email'
],
password: [
v => !!v || 'validators.required.password'
]
}

View File

@@ -112,20 +112,19 @@ const settingsController = {
return res.status(400).send('Mmmmm sould not be here!') return res.status(400).send('Mmmmm sould not be here!')
} }
const uploaded_path = path.join(req.file.destination, req.file.filename) const uploadedPath = path.join(req.file.destination, req.file.filename)
const logo_path = path.resolve(config.upload_path, 'favicon') const baseImgPath = path.resolve(config.upload_path, 'logo')
const favicon_path = path.resolve(config.upload_path, 'favicon')
// convert and resize to png // convert and resize to png
sharp(uploaded_path) sharp(uploadedPath)
.resize(400) .resize(400)
.png({ quality: 90 }) .png({ quality: 90 })
.toFile(logo_path + '.png', async (err, info) => { .toFile(baseImgPath + '.png', async (err, info) => {
console.error(err) console.error(err)
const image = await readFile(logo_path + '.png') const image = await readFile(baseImgPath + '.png')
const favicon = await toIco([image], { sizes: [64], resize: true }) const favicon = await toIco([image], { sizes: [64], resize: true })
writeFile(favicon_path + '.ico', favicon) writeFile(baseImgPath + '.ico', favicon)
settingsController.set('favicon', favicon_path) settingsController.set('logo', baseImgPath)
res.sendStatus(200) res.sendStatus(200)
}) })
}, },

View File

@@ -29,15 +29,16 @@ app.use((req, res, next) => {
app.use('/media/', express.static(config.upload_path)) app.use('/media/', express.static(config.upload_path))
// initialize instance settings / authentication / locale // initialize instance settings / authentication / locale
app.use(helpers.initSettings) app.use(helpers.initSettings)
// serve favicon and static content // serve favicon and static content
app.use('/logo.png', (req, res, next) => { app.use('/logo.png', (req, res, next) => {
const logo_path = req.settings.favicon || './static/gancio' const logoPath = req.settings.logo || './static/gancio'
return express.static(logo_path + '.png')(req, res, next) return express.static(logoPath + '.png')(req, res, next)
}) })
app.use('/favicon.ico', (req, res, next) => { app.use('/favicon.ico', (req, res, next) => {
const favicon_path = req.settings.favicon || './assets/favicon' const faviconPath = req.settings.logo || './assets/favicon'
return express.static(favicon_path + '.ico')(req, res, next) return express.static(faviconPath + '.ico')(req, res, next)
}) })
// rss/ics/atom feed // rss/ics/atom feed