diff --git a/angular_app/events-list.html b/angular_app/events-list.html index bfbbe2c..56f2e28 100644 --- a/angular_app/events-list.html +++ b/angular_app/events-list.html @@ -31,25 +31,25 @@ {{'Event' | translate}} {{'Attendees / Registered' | translate}} - {{'Actions' | translate}} + {{'Actions' | translate}} - - - {{event.title}} -

{{'Begins:' | translate}} {{event['begin-date'] | date:'fullDate'}} {{event['begin-time'] | date:'HH:mm'}}
- {{'Ends:' | translate}} {{event['end-date'] | date:'fullDate' }} {{event['end-time'] | date:'HH:mm'}}

- - -

{{attendeesNr}} / {{event.persons.length || 0}} ({{((attendeesNr / (event.persons.length || 0) * 100) || 0).toFixed()}}%)

- - - - - - - + + + {{event.title}} +

{{'Begins:' | translate}} {{event['begin-date'] | date:'fullDate'}} {{event['begin-time'] | date:'HH:mm'}}
+ {{'Ends:' | translate}} {{event['end-date'] | date:'fullDate' }} {{event['end-time'] | date:'HH:mm'}}

+ + +

{{attendeesNr}} / {{event.persons.length || 0}} ({{((attendeesNr / (event.persons.length || 0) * 100) || 0).toFixed()}}%)

+ + + + + + + diff --git a/angular_app/js/app.js b/angular_app/js/app.js index e6e227e..0ee06f9 100644 --- a/angular_app/js/app.js +++ b/angular_app/js/app.js @@ -59,13 +59,12 @@ eventManApp.run(['$rootScope', '$state', '$stateParams', '$log', 'Info', /* Check GUI privileges. */ $rootScope.hasPermission = function(permission) { - if (!($rootScope.info && $rootScope.info.user && - $rootScope.info.user.username && $rootScope.info.user.permissions)) { + if (!($rootScope.info && $rootScope.info.user && $rootScope.info.user.permissions)) { return false; } var granted = false; var splitted_permission = permission.split('|'); - var global_permission = splitted_permission + '|all'; + var global_permission = splitted_permission[0] + '|all'; angular.forEach($rootScope.info.user.permissions || [], function(value, idx) { diff --git a/angular_app/js/controllers.js b/angular_app/js/controllers.js index 495fbbc..67291f2 100644 --- a/angular_app/js/controllers.js +++ b/angular_app/js/controllers.js @@ -221,6 +221,21 @@ eventManControllers.controller('EventDetailsCtrl', ['$scope', '$state', 'Event', return false; } $scope.event.persons.push(person); + }; + + $scope._addAttendee = function(person) { + person.person_id = person._id; + person._id = $stateParams.id; // that's the id of the event, not the person. + Event.addPerson(person, function() { + if (!$scope.newTicket) { + $scope._localAddAttendee(person); + } + }); + $scope.query = ''; + return person; + }; + + $scope._setAttended = function(person) { $scope.setPersonAttribute(person, 'attended', true, function() { var all_person_idx = $scope.allPersons.findIndex(function(el, idx, array) { return person.person_id == el.person_id; @@ -228,18 +243,7 @@ eventManControllers.controller('EventDetailsCtrl', ['$scope', '$state', 'Event', if (all_person_idx != -1) { $scope.allPersons.splice(all_person_idx, 1); } - }, hideMessage); - }; - - $scope._addAttendee = function(person) { - person.person_id = person._id; - person._id = $stateParams.id; - Event.addPerson(person, function() { - if (!$scope.newTicket) { - $scope._localAddAttendee(person); - } - }); - $scope.query = ''; + }, true); }; $scope.fastAddAttendee = function(person, isNew) { @@ -248,14 +252,24 @@ eventManControllers.controller('EventDetailsCtrl', ['$scope', '$state', 'Event', if (isNew) { var personObj = new Person(person); personObj.$save(function(p) { - $scope._addAttendee(angular.copy(p)); + person = $scope._addAttendee(angular.copy(p)); + if (!$scope.newTicket) { + $scope._setAttended(person); + } $scope.newPerson = {}; }); } else { - $scope._addAttendee(angular.copy(person)); + person = $scope._addAttendee(angular.copy(person)); + if (!$scope.newTicket) { + $scope._setAttended(person); + } } }; + $scope.addRegisteredPerson = function(person) { + $scope.fastAddAttendee(person, true); + }; + $scope.setPersonAttribute = function(person, key, value, callback, hideMessage) { $log.debug('EventDetailsCtrl.setPersonAttribute.event_id: ' + $stateParams.id); $log.debug('EventDetailsCtrl.setPersonAttribute.person_id: ' + person.person_id); diff --git a/angular_app/js/services.js b/angular_app/js/services.js index 74fa9d9..c2fcea4 100644 --- a/angular_app/js/services.js +++ b/angular_app/js/services.js @@ -32,6 +32,7 @@ eventManServices.factory('Event', ['$resource', '$rootScope', angular.forEach(data.events || [], function(event_, event_idx) { convert_dates(event_); }); + return data.events; } }, diff --git a/angular_app/ticket-edit.html b/angular_app/ticket-edit.html index 44189f2..e5f31c9 100644 --- a/angular_app/ticket-edit.html +++ b/angular_app/ticket-edit.html @@ -37,7 +37,7 @@ - diff --git a/backend.py b/backend.py index ca6ea89..1c71f25 100644 --- a/backend.py +++ b/backend.py @@ -146,19 +146,23 @@ class EventManDB(object): query = convert(query or {}) return list(db[collection].find(query)) - def add(self, collection, data): + def add(self, collection, data, _id=None): """Insert a new document. :param collection: insert the document in this collection :type collection: str :param data: the document to store :type data: dict + :param _id: the _id of the document to store; if None, it's generated + :type _id: object :return: the document, as created in the database :rtype: dict """ db = self.connect() data = convert(data) + if _id is not None: + data['_id'] = _id _id = db[collection].insert(data) return self.get(collection, _id) diff --git a/eventman_server.py b/eventman_server.py index 5b32e89..d702625 100755 --- a/eventman_server.py +++ b/eventman_server.py @@ -21,6 +21,9 @@ import os import re import glob import json +import time +import string +import random import logging import datetime @@ -62,7 +65,13 @@ def authenticated(method): class BaseHandler(tornado.web.RequestHandler): """Base class for request handlers.""" - permissions = {} + permissions = { + 'event|read': True, + 'events|read': True, + 'event:ticket|all': True, + 'event:persons|all': True, + 'person|create': True + } # A property to access the first value of each argument. arguments = property(lambda self: dict([(k, v[0]) @@ -122,15 +131,15 @@ class BaseHandler(tornado.web.RequestHandler): @property def current_user_info(self): current_user = self.current_user + user_info = {'permissions': set([k for (k, v) in self.permissions.iteritems() if v is True])} if current_user: - user_info = {} user_info['username'] = current_user res = self.db.query('users', {'username': current_user}) if res: user = res[0] - user_info['permissions'] = user.get('permissions') or [] - return user_info - return {} + user_info['permissions'].update(set(user.get('permissions') or [])) + user_info['permissions'] = list(user_info['permissions']) + return user_info def has_permission(self, permission): """Check permissions of the current user. @@ -188,6 +197,8 @@ class CollectionHandler(BaseHandler): # set of documents used to store incremental sequences counters_collection = 'counters' + _id_chars = string.ascii_lowercase + string.digits + def get_next_seq(self, seq): """Increment and return the new value of a ever-incrementing counter. @@ -205,6 +216,12 @@ class CollectionHandler(BaseHandler): operation='increment') return doc.get('seq', 0) + def gen_id(self, seq='ids'): + t = str(time.time()) + seq = str(self.get_next_seq(seq)).replace('.', '_') + rand = ''.join([random.choice(self._id_chars) for x in xrange(32)]) + return '-'.join((t, seq, rand)) + def _filter_results(self, results, params): """Filter a list using keys and values from a dictionary. @@ -306,7 +323,7 @@ class CollectionHandler(BaseHandler): permission = '%s|%s' % (self.document, 'create' if method == 'post' else 'update') if not self.has_permission(permission): return self.build_error(status=401, message='insufficient permissions: %s' % permission) - newData = self.db.add(self.collection, data) + newData = self.db.add(self.collection, data, _id=self.gen_id()) else: permission = '%s|%s' % (self.collection, 'create' if method == 'post' else 'update') if not self.has_permission(permission): @@ -421,7 +438,6 @@ class PersonsHandler(CollectionHandler): document = 'person' collection = 'persons' object_id = 'person_id' - permissions = {} def handle_get_events(self, id_, resource_id=None, **kwargs): # Get a list of events attended by this person. @@ -459,9 +475,6 @@ class EventsHandler(CollectionHandler): document = 'event' collection = 'events' object_id = 'event_id' - permissions = { - 'events|read': True - } def _get_person_data(self, person_id_or_query, persons): """Filter a list of persons returning the first item with a given person_id @@ -498,6 +511,7 @@ class EventsHandler(CollectionHandler): del data['_id'] self.send_ws_message('event/%s/updates' % id_, json.dumps(ret)) if not doc: + data['_id'] = self.gen_id() merged, doc = self.db.update('events', {'_id': id_}, {'persons': data}, @@ -562,16 +576,6 @@ class EventsHandler(CollectionHandler): return ret -class TicketsHandler(CollectionHandler): - """Handle requests for Tickets.""" - document = 'ticket' - collection = 'tickets' - object_id = 'ticket_id' - permissions = { - 'ticket|read': True - } - - class EbCSVImportPersonsHandler(BaseHandler): """Importer for CSV files exported from eventbrite.""" csvRemap = { @@ -816,17 +820,15 @@ def run(): db_connector.add('settings', {'setting': 'server_cookie_secret', 'cookie_secret': cookie_secret}) - _ws_handler = (r"/ws/+event/+(?P\w+)/+updates/?", WebSocketEventUpdatesHandler) - _persons_path = r"/persons/?(?P\w+)?/?(?P\w+)?/?(?P\w+)?" - _events_path = r"/events/?(?P\w+)?/?(?P\w+)?/?(?P\w+)?" - _tickets_path = r"/tickets/?(?P\w+)?/?(?P\w+)?/?(?P\w+)?" + _ws_handler = (r"/ws/+event/+(?P[\w\d_-]+)/+updates/?", WebSocketEventUpdatesHandler) + _persons_path = r"/persons/?(?P[\w\d_-]+)?/?(?P[\w\d_-]+)?/?(?P[\w\d_-]+)?" + _events_path = r"/events/?(?P[\w\d_-]+)?/?(?P[\w\d_-]+)?/?(?P[\w\d_-]+)?" + _events_path = r"/events/?(?P[\w\d_-]+)?/?(?P[\w\d_-]+)?/?(?P[\w\d_-]+)?" application = tornado.web.Application([ (_persons_path, PersonsHandler, init_params), (r'/v%s%s' % (API_VERSION, _persons_path), PersonsHandler, init_params), (_events_path, EventsHandler, init_params), (r'/v%s%s' % (API_VERSION, _events_path), EventsHandler, init_params), - (_tickets_path, TicketsHandler, init_params), - (r'/v%s%s' % (API_VERSION, _tickets_path), TicketsHandler, init_params), (r"/(?:index.html)?", RootHandler, init_params), (r"/ebcsvpersons", EbCSVImportPersonsHandler, init_params), (r"/settings", SettingsHandler, init_params),