#13: GUI to avoid changes by unregistered users

This commit is contained in:
Davide Alberani 2017-02-26 16:04:29 +01:00
parent cffaaf9fd4
commit 5a31cc29ab
9 changed files with 80 additions and 26 deletions

View file

@ -27,7 +27,8 @@ module.exports = {
'/login': 'http://localhost:3000', '/login': 'http://localhost:3000',
'/logout': 'http://localhost:3000', '/logout': 'http://localhost:3000',
'/user': 'http://localhost:3000', '/user': 'http://localhost:3000',
'/groups': 'http://localhost:3000' '/groups': 'http://localhost:3000',
'/settings': 'http://localhost:3000'
}, },
// CSS Sourcemaps off by default because relative paths are "buggy" // CSS Sourcemaps off by default because relative paths are "buggy"
// with this option, according to the CSS-Loader README // with this option, according to the CSS-Loader README

View file

@ -13,6 +13,7 @@ These are the paths you see in the browser (VueJS does client-side routing: no r
- /#/day/:day - show groups for the given date (in yyyy-mm-dd format) - /#/day/:day - show groups for the given date (in yyyy-mm-dd format)
- /#/user/ - list of all users (only visible by admins) - /#/user/ - list of all users (only visible by admins)
- /#/user/:id - show setting for the give user ID - /#/user/:id - show setting for the give user ID
- /#/settings - global setting (only visible by admins)
Web server Web server
---------- ----------
@ -33,6 +34,8 @@ Web server
- /users/:id GET - a single user - /users/:id GET - a single user
- /users/:id PUT - update a user - /users/:id PUT - update a user
- /users/current GET - information about the currently logged in user - /users/current GET - information about the currently logged in user
- /settings GET - get all global settings
- /settings POST and PUT - add one or more new settings, or update existing ones
- /login POST - login of a user - /login POST - login of a user
- /logout GET - log the current user out - /logout GET - log the current user out

View file

@ -9,12 +9,12 @@
<div class="md-title day-info-title"> <div class="md-title day-info-title">
<md-icon class="day-icon">today</md-icon>&nbsp;{{ day.day }} <md-icon class="day-icon">today</md-icon>&nbsp;{{ day.day }}
</div> </div>
<md-menu md-align-trigger> <md-menu v-if="loggedInUser.isAdmin || !settings.protectDayNotes" md-align-trigger>
<md-button class="md-icon-button" md-menu-trigger> <md-button class="md-icon-button" md-menu-trigger>
<md-icon>more_vert</md-icon> <md-icon>more_vert</md-icon>
</md-button> </md-button>
<md-menu-content> <md-menu-content>
<md-menu-item @click="openNotesDialog()"> <md-menu-item v-if="loggedInUser.isAdmin || !settings.protectDayNotes" @click="openNotesDialog()">
<span>edit notes</span> <span>edit notes</span>
<md-icon>edit</md-icon> <md-icon>edit</md-icon>
</md-menu-item> </md-menu-item>
@ -81,32 +81,45 @@ export default {
return { return {
dates: datesWithGroups dates: datesWithGroups
}; };
},
loggedInUser() {
return this.$store.state.loggedInUser;
},
settings() {
return this.$store.state.settings || {};
} }
}, },
beforeCreate: function() { beforeCreate: function() {
this.daysUrl = this.$resource('days{/day}'); this.daysUrl = this.$resource('days{/day}');
this.daysInfoUrl = this.$resource('days{/day}/info'); this.daysInfoUrl = this.$resource('days{/day}/info');
this.settingsUrl = this.$resource('settings');
}, },
mounted: function() { mounted: function() {
var [year, month, day] = (this.$route.params.day || '').split('-'); this.fetchSettings(this.initialize);
year = parseInt(year);
month = parseInt(month) - 1;
day = parseInt(day);
if (!isNaN(year) && !isNaN(month) && !isNaN(day)) {
this.date = new Date(year, month, day);
}
if (!(this.date && !isNaN(this.date.getTime()))) {
this.date = new Date();
}
$('div.calendar span.prev').on('click', this.changeMonth);
$('div.calendar span.next').on('click', this.changeMonth);
$('div.calendar span.month').on('click', this.changeMonth);
this.reload();
}, },
methods: { methods: {
initialize() {
var [year, month, day] = (this.$route.params.day || '').split('-');
year = parseInt(year);
month = parseInt(month) - 1;
day = parseInt(day);
if (!isNaN(year) && !isNaN(month) && !isNaN(day)) {
this.date = new Date(year, month, day);
}
if (!(this.date && !isNaN(this.date.getTime()))) {
this.date = new Date();
}
$('div.calendar span.prev').on('click', this.changeMonth);
$('div.calendar span.next').on('click', this.changeMonth);
$('div.calendar span.month').on('click', this.changeMonth);
this.reload();
},
reload() { reload() {
var ym = this.dateToString(this.date, true); var ym = this.dateToString(this.date, true);
this.getSummary({start: ym, end: ym}); this.getSummary({start: ym, end: ym});
@ -193,6 +206,21 @@ export default {
this.day.notes = json.notes; this.day.notes = json.notes;
this.reload(); this.reload();
}); });
},
fetchSettings(cb) {
this.settingsUrl.get().then((response) => {
return response.json();
}, (response) => {
this.$refs.dialogObj.show({text: 'unable to fetch settings'});
}).then((json) => {
if (!json || json.error) {
this.$refs.dialogObj.show({text: 'unable to fetch settings: ' + (json && json.message) || ''});
} else {
this.$store.commit('updateSettings', json);
}
cb();
});
} }
}, },

View file

@ -52,6 +52,9 @@ export default {
computed: { computed: {
loggedInUser() { loggedInUser() {
return this.$store.state.loggedInUser; return this.$store.state.loggedInUser;
},
settings() {
return this.$store.state.settings || {};
} }
}, },
@ -61,7 +64,7 @@ export default {
methods: { methods: {
isAuthorized(ownerID) { isAuthorized(ownerID) {
return !ownerID || this.$store.state.loggedInUser.isAdmin || (this.$store.state.loggedInUser._id && this.$store.state.loggedInUser._id == ownerID); return (!ownerID && !this.$store.state.settings.protectUnregistered) || this.$store.state.loggedInUser.isAdmin || (this.$store.state.loggedInUser._id && this.$store.state.loggedInUser._id == ownerID);
}, },
editAttendee() { editAttendee() {

View file

@ -7,16 +7,16 @@
<div class="md-title group-title"> <div class="md-title group-title">
<md-icon class="group-icon">folder_open</md-icon>&nbsp;{{ group.group }}&nbsp;<span class="counter">{{ counter }}</span> <md-icon class="group-icon">folder_open</md-icon>&nbsp;{{ group.group }}&nbsp;<span class="counter">{{ counter }}</span>
</div> </div>
<md-menu md-align-trigger> <md-menu v-if="loggedInUser.isAdmin || !settings.protectGroupNotes || !settings.protectGroupName" md-align-trigger>
<md-button class="md-icon-button" md-menu-trigger> <md-button class="md-icon-button" md-menu-trigger>
<md-icon>more_vert</md-icon> <md-icon>more_vert</md-icon>
</md-button> </md-button>
<md-menu-content> <md-menu-content>
<md-menu-item @click="openNotesDialog()"> <md-menu-item v-if="loggedInUser.isAdmin || !settings.protectGroupNotes" @click="openNotesDialog()">
<span>edit notes</span> <span>edit notes</span>
<md-icon>edit</md-icon> <md-icon>edit</md-icon>
</md-menu-item> </md-menu-item>
<md-menu-item @click="openRenameGroupDialog()"> <md-menu-item v-if="loggedInUser.isAdmin || !settings.protectGroupName" @click="openRenameGroupDialog()">
<span>rename group</span> <span>rename group</span>
<md-icon>label</md-icon> <md-icon>label</md-icon>
</md-menu-item> </md-menu-item>
@ -146,6 +146,9 @@ export default {
}, },
loggedInUser() { loggedInUser() {
return this.$store.state.loggedInUser; return this.$store.state.loggedInUser;
},
settings() {
return this.$store.state.settings || {};
} }
}, },

View file

@ -11,6 +11,10 @@
<router-link :to="{name: 'home'}" class="home-link">ibt2</router-link> <router-link :to="{name: 'home'}" class="home-link">ibt2</router-link>
</h2> </h2>
<span v-if="loggedInUser.username"> <span v-if="loggedInUser.username">
<md-button v-if="loggedInUser.isAdmin" id="users-icon" class="md-icon-button" @click="toSettingsPage()">
<md-tooltip md-direction="left">global settings</md-tooltip>
<md-icon>settings</md-icon>
</md-button>
<md-button v-if="loggedInUser.isAdmin" id="users-icon" class="md-icon-button" @click="toUsersPage()"> <md-button v-if="loggedInUser.isAdmin" id="users-icon" class="md-icon-button" @click="toUsersPage()">
<md-tooltip md-direction="left">list of users</md-tooltip> <md-tooltip md-direction="left">list of users</md-tooltip>
<md-icon>people_outline</md-icon> <md-icon>people_outline</md-icon>
@ -108,6 +112,10 @@ export default {
this.$router.push('/user/'); this.$router.push('/user/');
}, },
toSettingsPage() {
this.$router.push('/settings/');
},
focusToLoginForm() { focusToLoginForm() {
var that = this; var that = this;
setTimeout(function() { that.$refs.usernameInput.$el.focus(); }, 400); setTimeout(function() { that.$refs.usernameInput.$el.focus(); }, 400);

View file

@ -17,8 +17,8 @@
</md-input-container> </md-input-container>
<md-switch v-if="loggedInUser.isAdmin" v-model="user.isAdmin" class="md-warn">is admin</md-switch> <md-switch v-if="loggedInUser.isAdmin" v-model="user.isAdmin" class="md-warn">is admin</md-switch>
<br />
<br />
<md-button id="save-button" class="md-raised md-primary" @click="save()">Save</md-button> <md-button id="save-button" class="md-raised md-primary" @click="save()">Save</md-button>
</md-card-content> </md-card-content>
</md-card> </md-card>
@ -32,7 +32,7 @@ import IbtDialog from './IbtDialog.vue';
import IbtSnackbar from './IbtSnackbar.vue'; import IbtSnackbar from './IbtSnackbar.vue';
export default { export default {
data () { data() {
return { return {
user: {_id: null, email: '', password: null, isAdmin: false}, user: {_id: null, email: '', password: null, isAdmin: false},
password: null password: null

View file

@ -26,6 +26,7 @@ import store_data from './store.js';
import App from './App'; import App from './App';
import User from './User'; import User from './User';
import Users from './Users'; import Users from './Users';
import GlobalSettings from './GlobalSettings';
import Toolbar from './Toolbar'; import Toolbar from './Toolbar';
import IbtFooter from './IbtFooter'; import IbtFooter from './IbtFooter';
@ -41,7 +42,8 @@ var routes = [
{path: '/day/', name: 'days', component: App}, {path: '/day/', name: 'days', component: App},
{path: '/day/:day', name: 'day', component: App}, {path: '/day/:day', name: 'day', component: App},
{path: '/user/', name: 'users', component: Users}, {path: '/user/', name: 'users', component: Users},
{path: '/user/:id', name: 'user', component: User} {path: '/user/:id', name: 'user', component: User},
{path: '/settings/', name: 'settings', component: GlobalSettings},
]; ];
const store = new Vuex.Store(store_data); const store = new Vuex.Store(store_data);
@ -52,5 +54,5 @@ var vue = new Vue({
store: store, store: store,
template: '<div id="app"><toolbar /><router-view class="view"></router-view><ibt-footer /></div>', template: '<div id="app"><toolbar /><router-view class="view"></router-view><ibt-footer /></div>',
router: router, router: router,
components: { App, Toolbar, IbtFooter, Users, User } components: { App, Toolbar, IbtFooter, Users, User, GlobalSettings }
}); });

View file

@ -4,14 +4,20 @@
export default { export default {
state: { state: {
loggedInUser: {username: ''} loggedInUser: {username: ''},
settings: {}
}, },
mutations: { mutations: {
clearLoggedInUser(state, user) { clearLoggedInUser(state, user) {
state.loggedInUser = {username: ''}; state.loggedInUser = {username: ''};
}, },
setLoggedInUser(state, user) { setLoggedInUser(state, user) {
state.loggedInUser = user; state.loggedInUser = user;
},
updateSettings(state, settings) {
state.settings = settings;
} }
} }
}; };