Davide Alberani 8 年 前
コミット
9596922b7d

+ 16 - 16
angular_app/events-list.html

@@ -31,25 +31,25 @@
                     <tr>
                         <th><strong>{{'Event' | translate}}</strong></th>
                         <th ng-if="hasPermission('event|update')" class="hcenter"><strong>{{'Attendees / Registered' | translate}}</strong></th>
-                        <th ng-if="hasPermission('event|update')"><strong>{{'Actions' | translate}}</strong></th>
+                        <th><strong>{{'Actions' | translate}}</strong></th>
                     </tr>
                 </thead>
                 <tbody>
-                <tr ng-repeat="event in events | splittedFilter:query | orderBy:eventsOrderProp">
-                    <td>
-                        <span><strong>{{event.title}}</strong></span>
-                        <p>{{'Begins:' | translate}} {{event['begin-date'] | date:'fullDate'}} {{event['begin-time'] | date:'HH:mm'}}<br/>
-                        {{'Ends:' | translate}} {{event['end-date'] | date:'fullDate' }} {{event['end-time'] | date:'HH:mm'}}</p>
-                    </td>
-                    <td ng-if="hasPermission('persons|read')" class="hcenter">
-                        <p><span ng-init="attendeesNr = (event.persons | attendeesFilter).length">{{attendeesNr}}</span> / {{event.persons.length || 0}} ({{((attendeesNr / (event.persons.length || 0) * 100) || 0).toFixed()}}%)</p>
-                    </td>
-                    <td ng-if="hasPermission('event|update')">
-                        <button ng-if="hasPermission('ticket|create')" ng-click="$state.go('event.ticket.new', {id: event._id})" class="btn btn-link fa fa-user-plus" type="button" title="{{'Register' | translate}}"></button>
-                        <button ng-if="hasPermission('persons|update')" ng-click="$state.go('event.info', {id: event._id})" class="btn btn-link fa fa-list" type="button" title="{{'Manage attendees' | translate}}"></button>
-                        <button ng-if="hasPermission('event|delete')" ng-click="remove(event._id)" type="button" class="btn btn-link fa fa-trash fa-lg" title="{{'Delete' | translate}}"></button>
-                    </td>
-                </tr>
+                    <tr ng-repeat="event in events | splittedFilter:query | orderBy:eventsOrderProp">
+                        <td>
+                            <span><strong>{{event.title}}</strong></span>
+                            <p>{{'Begins:' | translate}} {{event['begin-date'] | date:'fullDate'}} {{event['begin-time'] | date:'HH:mm'}}<br/>
+                            {{'Ends:' | translate}} {{event['end-date'] | date:'fullDate' }} {{event['end-time'] | date:'HH:mm'}}</p>
+                        </td>
+                        <td ng-if="hasPermission('persons|read')" class="hcenter">
+                            <p><span ng-init="attendeesNr = (event.persons | attendeesFilter).length">{{attendeesNr}}</span> / {{event.persons.length || 0}} ({{((attendeesNr / (event.persons.length || 0) * 100) || 0).toFixed()}}%)</p>
+                        </td>
+                        <td>
+                            <button ng-if="hasPermission('event:ticket|create')" ng-click="$state.go('event.ticket.new', {id: event._id})" class="btn btn-link fa fa-user-plus" type="button" title="{{'Register' | translate}}"></button>
+                            <button ng-if="hasPermission('persons|update')" ng-click="$state.go('event.info', {id: event._id})" class="btn btn-link fa fa-list" type="button" title="{{'Manage attendees' | translate}}"></button>
+                            <button ng-if="hasPermission('event|delete')" ng-click="remove(event._id)" type="button" class="btn btn-link fa fa-trash fa-lg" title="{{'Delete' | translate}}"></button>
+                        </td>
+                    </tr>
                 </tbody>
             </table>
         </div>

+ 2 - 3
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) {

+ 25 - 11
angular_app/js/controllers.js

@@ -221,25 +221,29 @@ eventManControllers.controller('EventDetailsCtrl', ['$scope', '$state', 'Event',
                 return false;
             }
             $scope.event.persons.push(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;
-                });
-                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;
+            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;
+                });
+                if (all_person_idx != -1) {
+                    $scope.allPersons.splice(all_person_idx, 1);
+                }
+            }, 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);

+ 1 - 0
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;
                 }
             },

+ 1 - 1
angular_app/ticket-edit.html

@@ -37,7 +37,7 @@
                                 <input name="job_title" class="form-control" placeholder="{{'Evil Ruler' | translate}}"  ng-model="newPerson.job_title">
                             </div>
 
-                            <button reset-focus ng-disabled="!(newPerson.name && newPerson.surname)" ng-click="fastAddAttendee(newPerson, true)" class="btn btn-success top5">
+                            <button reset-focus ng-disabled="!(newPerson.name && newPerson.surname)" ng-click="addRegisteredPerson(newPerson, true)" class="btn btn-success top5">
                                 <span class="fa fa-plus-circle vcenter"></span>
                                 {{'Add' | translate}}
                             </button>

+ 5 - 1
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)
 

+ 28 - 26
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<event_id>\w+)/+updates/?", WebSocketEventUpdatesHandler)
-    _persons_path = r"/persons/?(?P<id_>\w+)?/?(?P<resource>\w+)?/?(?P<resource_id>\w+)?"
-    _events_path = r"/events/?(?P<id_>\w+)?/?(?P<resource>\w+)?/?(?P<resource_id>\w+)?"
-    _tickets_path = r"/tickets/?(?P<id_>\w+)?/?(?P<resource>\w+)?/?(?P<resource_id>\w+)?"
+    _ws_handler = (r"/ws/+event/+(?P<event_id>[\w\d_-]+)/+updates/?", WebSocketEventUpdatesHandler)
+    _persons_path = r"/persons/?(?P<id_>[\w\d_-]+)?/?(?P<resource>[\w\d_-]+)?/?(?P<resource_id>[\w\d_-]+)?"
+    _events_path = r"/events/?(?P<id_>[\w\d_-]+)?/?(?P<resource>[\w\d_-]+)?/?(?P<resource_id>[\w\d_-]+)?"
+    _events_path = r"/events/?(?P<id_>[\w\d_-]+)?/?(?P<resource>[\w\d_-]+)?/?(?P<resource_id>[\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),