diff --git a/assets/style.less b/assets/style.less
index 00bd4bef..4e19d786 100644
--- a/assets/style.less
+++ b/assets/style.less
@@ -1,14 +1,29 @@
-@background: #222C32;
+@home_background: #222C32;
+@background: white;
@success: #c7ffbc;
-// @info
+
+#__nuxt, #__layout {
+ height: 100%;
+}
+
+#home {
+ background-color: @home_background;
+ min-height: 100%;
+}
+
+a, a:hover {
+ text-decoration: none;
+}
html, body {
margin: 0px;
- background-color: @background !important;
+ background-color: @background;
width: 100%;
+ height: 100%;
overflow-x: hidden;
box-sizing: border-box;
font-family: BlinkMacSystemFont,-apple-system,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,Helvetica,Arial,sans-serif !important;
+ font-size: 15px;
}
* {
@@ -18,14 +33,11 @@ html, body {
.el-form-item {
margin-bottom: 5px;
}
-// .el-divider__text {
-// background-color: @background;
-// color: white;
-// border-radius: 5px;
-// }
+.el-main,
.el-card {
- max-width: 660px;
+ max-width: 1000px;
+ border-radius: 0px;
margin: 30px auto;
}
@@ -54,7 +66,7 @@ html, body {
.page-enter, .page-leave-active {
transition: opacity .3s, transform .2s;
opacity: 0;
- // transform: translateY(30px);
+ transform: translateX(30px);
}
pre {
@@ -69,12 +81,20 @@ pre {
}
@media only screen and (max-width: 768px) {
+ html {
+ font-size: 10px;
+ }
+
.el-card {
margin-top: 0px !important;
border-radius: 0px;
padding: 0px;
}
+ .el-main{
+ padding: 5px 0px;
+ }
+
.el-menu-item {
padding: 0px 17px;
}
diff --git a/locales/email/it.json b/locales/email/it.json
index cf00a5ea..a75891a1 100644
--- a/locales/email/it.json
+++ b/locales/email/it.json
@@ -4,6 +4,10 @@
"content": "Abbiamo ricevuto la richiesta di registrazione. La confermeremo quanto prima.\n Ciao"
},
"confirm": {
+ "subject": "Puoi iniziare a pubblicare eventi",
+ "content": "Ciao, il tuo account su {{config.title}} è stato creato."
+ },
+ "user_confirm": {
"subject": "Puoi iniziare a pubblicare eventi",
"content": "Ciao, il tuo account su {{config.title}} è stato creato. Confermalo."
},
diff --git a/package.json b/package.json
index 498eb820..d4c46af3 100644
--- a/package.json
+++ b/package.json
@@ -63,6 +63,7 @@
"less": "^3.10.3",
"lodash": "^4.17.14",
"mkdirp": "^0.5.1",
+ "moment-timezone": "^0.5.26",
"morgan": "^1.9.1",
"multer": "^1.4.2",
"node-fetch": "^2.6.0",
diff --git a/pages/add/_edit.vue b/pages/add/_edit.vue
index a99cc1ce..80786a9f 100644
--- a/pages/add/_edit.vue
+++ b/pages/add/_edit.vue
@@ -1,5 +1,5 @@
- el-card
+ el-main
nuxt-link.float-right(to='/')
el-button(circle icon='el-icon-close' type='danger' size='small' plain)
@@ -390,8 +390,8 @@ export default {
formData.append('place_address', this.event.place.address)
formData.append('description', this.event.description)
formData.append('multidate', this.event.type === 'multidate')
- formData.append('start_datetime', start_datetime.utc(true).unix())
- formData.append('end_datetime', end_datetime.utc(true).unix())
+ formData.append('start_datetime', start_datetime.unix())
+ formData.append('end_datetime', end_datetime.unix())
if (this.edit) {
formData.append('id', this.event.id)
diff --git a/pages/admin.vue b/pages/admin.vue
index fa8188d8..dfb8b272 100644
--- a/pages/admin.vue
+++ b/pages/admin.vue
@@ -31,7 +31,7 @@
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') {{dperPageata.row.place.name}}
+ template(slot-scope='data') {{data.row.place.name}}
el-table-column(:label='$t("common.confirm")' width='250')
template(slot-scope='data')
el-button(type='primary' @click='confirm(data.row.id)' size='mini') {{$t('common.confirm')}}
diff --git a/pages/index.vue b/pages/index.vue
index 1e7ee70c..1be52900 100644
--- a/pages/index.vue
+++ b/pages/index.vue
@@ -7,11 +7,18 @@
+
\ No newline at end of file
diff --git a/pages/user_confirm_pwd/_code.vue b/pages/user_confirm_pwd/_code.vue
new file mode 100644
index 00000000..03f5ed08
--- /dev/null
+++ b/pages/user_confirm_pwd/_code.vue
@@ -0,0 +1,53 @@
+
+ el-card
+ nuxt-link.float-right(to='/')
+ el-button(circle icon='el-icon-close' type='danger' size='small' plain)
+
+ h5 {{$t('common.set_password')}}
+ div(v-if='valid')
+ el-form
+ el-form-item {{$t('common.new_password')}}
+ el-input(type='password', v-model='new_password')
+ el-button(plain type="success" icon='el-icon-send', @click='change_password') {{$t('common.send')}}
+
+ div(v-else) {{$t('recover.not_valid_code')}}
+
+
+
diff --git a/plugins/element-ui.js b/plugins/element-ui.js
index 8dc97ea1..583d8c51 100644
--- a/plugins/element-ui.js
+++ b/plugins/element-ui.js
@@ -1,7 +1,8 @@
import Vue from 'vue'
import { Button, Select, Tag, Option, Table, FormItem, Card, Row, Col, Upload, Checkbox, RadioButton, RadioGroup,
- Form, Tabs, TabPane, Switch, Input, Loading, TimeSelect, Badge, ButtonGroup, Divider, Step, Steps, Radio,
- TableColumn, ColorPicker, Pagination, Popover, Tooltip, Dialog, Image, Backtop, Collapse, CollapseItem,
+ Form, Tabs, TabPane, Switch, Input, Loading, TimeSelect, Badge, ButtonGroup, Divider, Step, Steps, Radio, Main,
+ TableColumn, ColorPicker, Pagination, Popover, Tooltip, Dialog, Image, Backtop, Collapse, CollapseItem, Link,
+ Dropdown, DropdownMenu, DropdownItem, Submenu, PageHeader, Header,
Container, Footer, Timeline, TimelineItem, Menu, MenuItem } from 'element-ui'
import locale from 'element-ui/lib/locale'
@@ -14,6 +15,14 @@ const locales = {
export default ({ app, store }) => {
locale.use(locales[store.state.locale])
Vue.use(Button)
+ Vue.use(Dropdown)
+ Vue.use(Header)
+ Vue.use(PageHeader)
+ Vue.use(Submenu)
+ Vue.use(DropdownItem)
+ Vue.use(DropdownMenu)
+ Vue.use(Main)
+ Vue.use(Link)
Vue.use(RadioButton)
Vue.use(RadioGroup)
Vue.use(Radio)
diff --git a/plugins/filters.js b/plugins/filters.js
index 3804e7e1..44d06588 100644
--- a/plugins/filters.js
+++ b/plugins/filters.js
@@ -1,32 +1,32 @@
import Vue from 'vue'
-import moment from 'moment'
-// import 'dayjs/locale/it'
-// import 'dayjs/locale/es'
+import moment from 'moment-timezone'
export default ({ app, store }) => {
+
+ // set timezone to instance_timezone!!
+ // to show local time relative to event's place
+ // not where in the worlds I'm looking at the page from
+ moment.tz.setDefault(store.state.settings.instance_timezone)
+
// replace links with anchors
// TODO: remove fb tracking id
- Vue.filter('linkify', value => value.replace(/(https?:\/\/[^\s]+)/g, '$1'))
+ Vue.filter('linkify', value => value.replace(/(https?:\/\/([^\s]+))/g, '$2'))
+
Vue.filter('url2host', url => url.match(/^https?:\/\/(.[^/:]+)/i)[1])
- Vue.filter('datetime', value => moment(value).utc(false).locale(store.state.locale).format('ddd, D MMMM HH:mm'))
- // Vue.filter('short_datetime', value => moment(value).locale(store.state.locale).format('D/MM HH:mm'))
- // Vue.filter('hour', value => moment(value).locale(store.state.locale).format('HH:mm'))
+ Vue.filter('datetime', value => moment(value).locale(store.state.locale).format('ddd, D MMMM HH:mm'))
// shown in mobile homepage
- Vue.filter('day', value => moment.unix(value).utc(false).locale(store.state.locale).format('dddd, D MMM'))
- // Vue.filter('month', value => moment(value).locale(store.state.locale).format('MMM'))
+ Vue.filter('day', value => moment.unix(value).locale(store.state.locale).format('dddd, D MMM'))
+ Vue.filter('to', value => moment().to(value.start_datetime*1000))
// format event start/end datetime based on page
Vue.filter('when', (event, where) => {
moment.locale(store.state.locale)
- // show local time relative to event's place
- // (not where in the worlds I'm looking at the page from)
- const start = moment.unix(event.start_datetime).utc(false)
- const end = moment.unix(event.end_datetime).utc(false)
+ const start = moment.unix(event.start_datetime)
+ const end = moment.unix(event.end_datetime)
- const normal = `${start.format('dddd, D MMMM (HH:mm-')}${end.format('HH:mm)')}`
-
+ const normal = `${start.format('dddd, D MMMM (HH:mm-')}${end.format('HH:mm) z Z ')}`
// recurrent event
if (event.recurrent && where !== 'home') {
const { frequency, days, type } = JSON.parse(event.recurrent)
diff --git a/plugins/vue-awesome.js b/plugins/vue-awesome.js
index cf51e673..9e5b5078 100644
--- a/plugins/vue-awesome.js
+++ b/plugins/vue-awesome.js
@@ -31,7 +31,7 @@ import 'vue-awesome/icons/envelope'
import 'vue-awesome/icons/calendar-day'
import 'vue-awesome/icons/calendar-week'
import 'vue-awesome/icons/calendar-alt'
-import 'vue-awesome/icons/circle-notch'
+import 'vue-awesome/icons/network-wired'
import Icon from 'vue-awesome/components/Icon'
diff --git a/server/api/controller/event.js b/server/api/controller/event.js
index 1f803ee5..3f58f8ac 100644
--- a/server/api/controller/event.js
+++ b/server/api/controller/event.js
@@ -1,10 +1,11 @@
const crypto = require('crypto')
-const moment = require('moment')
+const moment = require('moment-timezone')
const { Op } = require('sequelize')
const lodash = require('lodash')
const { event: Event, comment: Comment, tag: Tag, place: Place,
user: User, notification: Notification, event_notification: EventNotification } = require('../models')
const Sequelize = require('sequelize')
+const exportController = require('./export')
const debug = require('debug')('controller:event')
const eventController = {
@@ -90,6 +91,7 @@ const eventController = {
// TODO retrieve next/prev event also
// select id, start_datetime, title from events where start_datetime > (select start_datetime from events where id=89) order by start_datetime limit 20;
async get (req, res) {
+ const format = req.params.format || 'json'
const is_admin = req.user && req.user.is_admin
const id = req.params.event_id
const event = await Event.findByPk(id, {
@@ -107,7 +109,11 @@ const eventController = {
})
if (event && (event.is_visible || is_admin)) {
- res.json(event)
+ if (format === 'json') {
+ res.json(event)
+ } else if (format === 'ics') {
+ exportController.ics(req, res, [event])
+ }
} else {
res.sendStatus(404)
}
@@ -190,13 +196,11 @@ const eventController = {
.month(req.params.month)
.startOf('month')
.startOf('week')
- .utc(false)
let end = moment()
.year(req.params.year)
.month(req.params.month)
.endOf('month')
- .utc(false)
const shownDays = end.diff(start, 'days')
if (shownDays <= 35) { end = end.add(1, 'week') }
@@ -235,7 +239,7 @@ const eventController = {
if (!recurrent.frequency) { return false }
let cursor = moment(start).startOf('week')
- const start_date = moment.unix(e.start_datetime).utc(false)
+ const start_date = moment.unix(e.start_datetime)
const duration = moment.unix(e.end_datetime).diff(start_date, 's')
const frequency = recurrent.frequency
const days = recurrent.days
@@ -280,7 +284,7 @@ const eventController = {
cursor.day(d - 1)
}
if (cursor.isAfter(dueTo) || cursor.isBefore(start)) { return }
- e.start_datetime = cursor.utc(true).unix()
+ e.start_datetime = cursor.unix()
e.end_datetime = e.start_datetime + duration
events.push(Object.assign({}, e))
})
diff --git a/server/api/controller/export.js b/server/api/controller/export.js
index ad7753c6..5300c5bf 100644
--- a/server/api/controller/export.js
+++ b/server/api/controller/export.js
@@ -1,7 +1,6 @@
const { event: Event, place: Place, tag: Tag } = require('../models')
const { Op } = require('sequelize')
-const moment = require('moment')
-const config = require('config')
+const moment = require('moment-timezone')
const ics = require('ics')
const exportController = {
@@ -36,31 +35,38 @@ const exportController = {
switch (type) {
case 'rss':
case 'feed':
- return exportController.feed(res, events.slice(0, 20))
+ return exportController.feed(req, res, events.slice(0, 20))
case 'ics':
- return exportController.ics(res, events)
+ return exportController.ics(req, res, events)
case 'json':
return res.json(events)
}
},
- feed (res, events) {
+ feed (req, res, events) {
res.type('application/rss+xml; charset=UTF-8')
- res.render('feed/rss.pug', { events, config, moment })
+ res.render('feed/rss.pug', { events, settings: req.settings, moment })
},
- ics (res, events) {
+ ics (req, res, events) {
const eventsMap = events.map(e => {
- const tmpStart = moment.unix(e.start_datetime).utc(false)
- const tmpEnd = moment.unix(e.end_datetime).utc(false)
- const start = [tmpStart.year(), tmpStart.month() + 1, tmpStart.date(), tmpStart.hour(), tmpStart.minute()]
- const end = [tmpEnd.year(), tmpEnd.month() + 1, tmpEnd.date(), tmpEnd.hour(), tmpEnd.minute()]
+ const tmpStart = moment.unix(e.start_datetime)
+ const tmpEnd = moment.unix(e.end_datetime)
+ const start = tmpStart.utc(true).format('YYYY-M-D-H-m').split('-')
+ const end = tmpEnd.utc(true).format('YYYY-M-D-H-m').split('-')
return {
start,
+ // startOutputType: 'utc',
end,
- title: e.title,
+ // endOutputType: 'utc',
+ title: `[1${req.settings.title}] ${e.title}`,
description: e.description,
- location: e.place.name + ' ' + e.place.address
+ location: `${e.place.name} - ${e.place.address}`,
+ url: `${req.settings.baseurl}/event/${e.id}`,
+ alarms: [{
+ action: 'display',
+ trigger: {hours: 1, before: true}
+ }]
}
})
res.type('text/calendar; charset=UTF-8')
diff --git a/server/api/controller/user.js b/server/api/controller/user.js
index a9900192..275cf994 100644
--- a/server/api/controller/user.js
+++ b/server/api/controller/user.js
@@ -249,8 +249,10 @@ const userController = {
}
req.body.recover_code = crypto.randomBytes(16).toString('hex')
+ debug('Register user ', req.body.email)
const user = await User.create(req.body)
try {
+ debug(`Sending registration email to ${user.email}`)
mail.send(user.email, 'register', { user, config })
mail.send(config.admin_email, 'admin_register', { user, config })
} catch (e) {
diff --git a/server/api/index.js b/server/api/index.js
index 28bf44c0..a006c96a 100644
--- a/server/api/index.js
+++ b/server/api/index.js
@@ -24,14 +24,6 @@ api.use(bodyParser.json())
const jwt = expressJwt({
secret: config.secret,
credentialsRequired: false,
- // getToken: function fromHeaderOrQuerystring (req) {
- // if (req.headers.authorization && req.headers.authorization.split(' ')[0] === 'Bearer') {
- // return req.headers.authorization.split(' ')[1]
- // } else if (req.cookies && req.cookies['auth._token.local']) {
- // const [ prefix, token ] = req.cookies['auth._token.local'].split(' ')
- // if (prefix === 'Bearer') { return token }
- // }
- // }
})
// api.use(jwt)
@@ -95,7 +87,7 @@ api.get('/event/confirm/:event_id', isAuth, isAdmin, eventController.confirm)
api.get('/event/unconfirm/:event_id', isAuth, isAdmin, eventController.unconfirm)
// get event
-api.get('/event/:event_id', fillUser, eventController.get)
+api.get('/event/:event_id.:format?', fillUser, eventController.get)
// export events (rss/ics)
api.get('/export/:type', exportController.export)
diff --git a/server/api/mail.js b/server/api/mail.js
index e9eacf52..34fcd96d 100644
--- a/server/api/mail.js
+++ b/server/api/mail.js
@@ -46,7 +46,7 @@ const mail = {
...locals,
locale: 'it', // TOFIX
config: { title: config.title, baseurl: config.baseurl, description: config.description },
- datetime: datetime => moment.unix(datetime).utc(false).format('ddd, D MMMM HH:mm')
+ datetime: datetime => moment.unix(datetime).format('ddd, D MMMM HH:mm')
}
}
return email.send(msg)
diff --git a/server/api/models/event.js b/server/api/models/event.js
index d5fd724e..104e7bca 100644
--- a/server/api/models/event.js
+++ b/server/api/models/event.js
@@ -37,11 +37,14 @@ module.exports = (sequelize, DataTypes) => {
}
event.prototype.toAP = function (username, follower = []) {
- const tags = this.tags && this.tags.map(t => `#${t.tag}`).join(' ')
+ const tags = this.tags && this.tags.map(t => {
+ const tag = t.tag.replace(/[ #]/g, '_')
+ return `#${t.tag}`
+ }).join(' ')
const content = `${this.title}
📍${this.place.name}
- ⏰ ${moment.unix(this.start_datetime).utc(false).format('dddd, D MMMM (HH:mm)')}
+ ⏰ ${moment.unix(this.start_datetime).format('dddd, D MMMM (HH:mm)')}
${this.description.length > 200 ? this.description.substr(0, 200) + '...' : this.description}
${tags}
`
diff --git a/server/emails/user_confirm/html.pug b/server/emails/user_confirm/html.pug
index 7fcd2ce1..cf155730 100644
--- a/server/emails/user_confirm/html.pug
+++ b/server/emails/user_confirm/html.pug
@@ -1,3 +1,3 @@
extends ../layout.pug
block content
- p !{t('confirm.content', { config, user })}
+ p !{t('user_confirm.content', { config, user })}
diff --git a/server/emails/user_confirm/subject.pug b/server/emails/user_confirm/subject.pug
index 72a6cbae..9ccb1236 100644
--- a/server/emails/user_confirm/subject.pug
+++ b/server/emails/user_confirm/subject.pug
@@ -1 +1 @@
-| [#{config.title}] #{t('confirm.subject')}
\ No newline at end of file
+| [#{config.title}] #{t('user_confirm.subject')}
\ No newline at end of file
diff --git a/server/emails/user_confirm/text.pug b/server/emails/user_confirm/text.pug
index 89326a44..7a89d2b0 100644
--- a/server/emails/user_confirm/text.pug
+++ b/server/emails/user_confirm/text.pug
@@ -1 +1 @@
-p !{t('confirm.content', { config, user })}
+p !{t('user_confirm.content', { config, user })}
diff --git a/server/helpers.js b/server/helpers.js
index 7e5535be..1c3c9721 100644
--- a/server/helpers.js
+++ b/server/helpers.js
@@ -2,6 +2,7 @@ const settingsController = require('./api/controller/settings')
const acceptLanguage = require('accept-language')
const expressJwt = require('express-jwt')
const debug = require('debug')
+const moment = require('moment-timezone')
const config = require('config')
const package = require('../package.json')
@@ -39,6 +40,7 @@ module.exports = {
acceptLanguage.languages(supportedLanguages)
req.settings.locale = acceptLanguage.get(acceptedLanguages)
req.settings.user_locale = settingsController.user_locale[req.settings.locale]
+ moment.locale(req.settings.locale)
// auth
jwt(req, res, () => {
diff --git a/store/index.js b/store/index.js
index 6b57a2a2..e0d03e30 100644
--- a/store/index.js
+++ b/store/index.js
@@ -9,6 +9,7 @@ export const state = () => ({
tags: [],
places: [],
settings: {
+ instance_timezone: 'Europe/Rome',
allow_registration: true,
allow_anon_event: true,
allow_recurrent_event: true,
diff --git a/views/feed/rss.pug b/views/feed/rss.pug
index 574e7414..b758af09 100644
--- a/views/feed/rss.pug
+++ b/views/feed/rss.pug
@@ -1,22 +1,22 @@
doctype xml
rss(version='2.0' xmlns:atom="http://www.w3.org/2005/Atom")
channel
- atom:link(href="#{config.baseurl}/feed/rss" rel="self" type="application/rss+xml")
- title #{config.title}
- link #{config.baseurl}
- description #{config.description}
+ atom:link(href="#{settings.baseurl}/feed/rss" rel="self" type="application/rss+xml")
+ title #{settings.title}
+ link #{settings.baseurl}
+ description #{settings.description}
each event in events
item
- title [#{moment.unix(event.start_datetime).utc(false).format("YY-MM-DD")}] #{event.title} @#{event.place.name}
- link #{config.baseurl}/event/#{event.id}
+ title [#{moment.unix(event.start_datetime).format("YY-MM-DD")}] #{event.title} @#{event.place.name}
+ link #{settings.baseurl}/event/#{event.id}
description
| #{event.title}
| #{event.place.name} - #{event.place.address}
- | (#{moment.unix(event.start_datetime).utc(false).format("dddd, D MMMM HH:mm")})
+ | (#{moment.unix(event.start_datetime).format("dddd, D MMMM HH:mm")})
if (event.image_path)
- |
+ |
| !{event.description}
| ]]>
pubDate= new Date(event.createdAt).toUTCString()
- guid(isPermaLink='false') #{config.baseurl}/event/#{event.id}
+ guid(isPermaLink='false') #{settings.baseurl}/event/#{event.id}