fixes #145: remove Person collection

This commit is contained in:
Davide Alberani 2016-07-09 14:20:48 +02:00
parent 4e7123d4bb
commit eeadf5759d
13 changed files with 156 additions and 310 deletions

View file

@ -3,7 +3,7 @@
<div class="panel panel-primary table-striped top5"> <div class="panel panel-primary table-striped top5">
<div class="panel-heading"> <div class="panel-heading">
<h1> <h1>
<button ng-if="event._id && hasPermission('persons|read')" ng-click="$state.go('event.tickets', {id: event._id})" class="btn btn-success"> <button ng-if="event._id && hasPermission('tickets-all|read')" ng-click="$state.go('event.tickets', {id: event._id})" class="btn btn-success">
<span class="fa fa-ticket vcenter"></span> <span class="fa fa-ticket vcenter"></span>
{{'Tickets' | translate}} {{'Tickets' | translate}}
</button> </button>

View file

@ -1,8 +1,2 @@
<!-- main view for Event --> <!-- main view for Event -->
<!-- div class="container"> <div ui-view></div>
<ul class="nav nav-tabs">
<li ui-sref-active="active"><a ui-sref="event.tickets({id: $state.params.id})">Info</a></li>
<li ui-sref-active="active"><a ui-sref="event.edit({id: $state.params.id})">Edit</a></li>
</ul -->
<div ui-view></div>
<!-- /div -->

View file

@ -19,7 +19,7 @@
--><div class="col-md-5 col-xs-5 vcenter"> --><div class="col-md-5 col-xs-5 vcenter">
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-6">
<h2><div class="label label-warning vcenter">{{'Registered:' | translate}} {{((event.persons || []) | registeredFilter).length}}</div></h2> <h2><div class="label label-warning vcenter">{{'Registered:' | translate}} {{((event.tickets || []) | registeredFilter).length}}</div></h2>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<h2><div class="label label-info vcenter">{{'Attendees:' | translate}} {{countAttendees}}</div></h2> <h2><div class="label label-info vcenter">{{'Attendees:' | translate}} {{countAttendees}}</div></h2>
@ -33,12 +33,12 @@
<div class="row"> <div class="row">
<div class="col-md-8"> <div class="col-md-8">
<div class="panel panel-primary table-striped top5"> <div class="panel panel-primary table-striped top5">
<div class="panel-heading">{{'Persons' | translate}}</div> <div class="panel-heading">{{'tickets' | translate}}</div>
<div class="panel-body"> <div class="panel-body">
<form class="form-inline"> <form class="form-inline">
<div class="form-group"> <div class="form-group">
<label for="query-persons">{{'Search:' | translate}}</label> <label for="query-tickets">{{'Search:' | translate}}</label>
<input eventman-focus type="text" id="query-persons" class="form-control" placeholder="{{'Name or email' | translate}}" ng-model="query" ng-model-options="{debounce: 600}"> <input eventman-focus type="text" id="query-tickets" class="form-control" placeholder="{{'Name or email' | translate}}" ng-model="query" ng-model-options="{debounce: 600}">
</div> </div>
</form> </form>
<table class="table table-striped"> <table class="table table-striped">
@ -54,30 +54,30 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr ng-repeat="person in (event.persons || []) | splittedFilter:query | registeredFilter | orderBy:personsOrder"> <tr ng-repeat="ticket in (event.tickets || []) | splittedFilter:query | registeredFilter | orderBy:ticketsOrder">
<td class="text-right">{{$index+1}}</td> <td class="text-right">{{$index+1}}</td>
<td> <td>
<span> <span>
<strong> <strong>
<a ui-sref="event.ticket.edit({id: event._id, ticket_id: person._id})"><span>{{person.name}}</span>&nbsp;<span>{{person.surname}}</span></a> <a ui-sref="event.ticket.edit({id: event._id, ticket_id: ticket._id})"><span>{{ticket.name}}</span>&nbsp;<span>{{ticket.surname}}</span></a>
</strong> </strong>
</span> </span>
<span ng-if="person.email">&nbsp;&lt;{{person.email}}&gt;</span> <span ng-if="ticket.email">&nbsp;&lt;{{ticket.email}}&gt;</span>
<p ng-if="person.company || person.job_title"><i ng-if="person.job_title">{{person.job_title}}</i><span ng-if="person.company && person.job_title">&nbsp;@&nbsp;</span><i ng-if="person.company">{{person.company}}</i></p> <p ng-if="ticket.company || ticket.job_title"><i ng-if="ticket.job_title">{{ticket.job_title}}</i><span ng-if="ticket.company && ticket.job_title">&nbsp;@&nbsp;</span><i ng-if="ticket.company">{{ticket.company}}</i></p>
</td> </td>
<td class="text-center"> <td class="text-center">
<button class="btn btn-link" reset-focus name="switch-attended" ng-click="setTicketAttributeAndRefocus(person, 'attended', !person.attended)"><span class="fa fa-lg {{(person.attended) && 'fa-check-circle text-success' || 'fa-times-circle text-danger'}}"></span></button> <button class="btn btn-link" reset-focus name="switch-attended" ng-click="setTicketAttributeAndRefocus(ticket, 'attended', !ticket.attended)"><span class="fa fa-lg {{(ticket.attended) && 'fa-check-circle text-success' || 'fa-times-circle text-danger'}}"></span></button>
</td> </td>
<td class="text-center" ng-repeat="col in customFields"> <td class="text-center" ng-repeat="col in customFields">
<span ng-if="col.type == 'boolean'"> <span ng-if="col.type == 'boolean'">
<button class="btn btn-link" ng-click="setTicketAttribute(person, col.key, !person[col.key])"><span class="fa fa-lg {{(person[col.key]) && 'fa-check-circle text-success' || 'fa-times-circle text-danger'}}"></span></button> <button class="btn btn-link" ng-click="setTicketAttribute(ticket, col.key, !ticket[col.key])"><span class="fa fa-lg {{(ticket[col.key]) && 'fa-check-circle text-success' || 'fa-times-circle text-danger'}}"></span></button>
</span> </span>
<span ng-if="col.type != 'boolean'"> <span ng-if="col.type != 'boolean'">
{{person[col.key]}} {{ticket[col.key]}}
</span> </span>
</td> </td>
<td class="text-center"> <td class="text-center">
<button ng-click="deleteTicket(person)" type="button" class="btn btn-link fa fa-lg fa-trash"></button> <button ng-click="deleteTicket(ticket)" type="button" class="btn btn-link fa fa-lg fa-trash"></button>
</td> </td>
</tr> </tr>
</tbody> </tbody>

View file

@ -46,12 +46,12 @@
<p>{{'Begins:' | translate}} {{event['begin-date'] | date:'fullDate'}} {{event['begin-time'] | date:'HH:mm'}}<br/> <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> {{'Ends:' | translate}} {{event['end-date'] | date:'fullDate' }} {{event['end-time'] | date:'HH:mm'}}</p>
</td> </td>
<td ng-if="hasPermission('persons|read')" class="hcenter"> <td ng-if="hasPermission('tickets-all|read')" class="hcenter">
<p><span ng-init="attendeesNr = ((event.persons || []) | attendeesFilter).length">{{attendeesNr}}</span> / {{((event.persons || []) | registeredFilter).length}} ({{((attendeesNr / ((event.persons || []) | registeredFilter).length * 100) || 0).toFixed()}}%)</p> <p><span ng-init="attendeesNr = ((event.tickets || []) | attendeesFilter).length">{{attendeesNr}}</span> / {{((event.tickets || []) | registeredFilter).length}} ({{((attendeesNr / ((event.tickets || []) | registeredFilter).length * 100) || 0).toFixed()}}%)</p>
</td> </td>
<td> <td>
<button ng-if="hasPermission('event:tickets|create')" ng-click="$state.go('event.ticket.new', {id: event._id})" class="btn btn-link fa fa-user-plus" type="button" title="{{'Join this event' | translate}}"></button> <button ng-if="hasPermission('event:tickets|create')" ng-click="$state.go('event.ticket.new', {id: event._id})" class="btn btn-link fa fa-user-plus" type="button" title="{{'Join this event' | translate}}"></button>
<button ng-if="hasPermission('persons|update')" ng-click="$state.go('event.tickets', {id: event._id})" class="btn btn-link fa fa-list" type="button" title="{{'Manage attendees' | translate}}"></button> <button ng-if="hasPermission('tickets|update')" ng-click="$state.go('event.tickets', {id: event._id})" class="btn btn-link fa fa-list" type="button" title="{{'Manage attendees' | translate}}"></button>
<button ng-if="hasPermission('event|update')" ng-click="$state.go('event.edit', {id: event._id})" type="button" class="btn btn-link fa fa-cog fa-lg" title="{{'Edit' | translate}}"></button> <button ng-if="hasPermission('event|update')" ng-click="$state.go('event.edit', {id: event._id})" type="button" class="btn btn-link fa fa-cog fa-lg" title="{{'Edit' | 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> <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> </td>

View file

@ -80,8 +80,8 @@
</div> </div>
<div class="collapse navbar-collapse"> <div class="collapse navbar-collapse">
<ul class="nav navbar-nav navbar-right"> <ul class="nav navbar-nav navbar-right">
<li ng-if="info.user.username"> <li ng-if="info && info.user && info.user.username && info.user._id">
<span class="btn">{{info.user.username}}</span> <span class="btn"><a ui-sref="user.edit({id: info.user._id})">{{info.user.username}}</a></span>
<span class="btn btn-link"> <span class="btn btn-link">
<a ng-controller="UsersCtrl" ng-click="logout()"><span class="fa fa-sign-out vcenter"></span>&nbsp;{{'logout' | translate}}</a> <a ng-controller="UsersCtrl" ng-click="logout()"><span class="fa fa-sign-out vcenter"></span>&nbsp;{{'logout' | translate}}</a>
</span> </span>

View file

@ -72,7 +72,7 @@ eventManControllers.controller('EventsListCtrl', ['$scope', 'Event', '$uibModal'
$scope.events = Event.all(function(events) { $scope.events = Event.all(function(events) {
if (events && $state.is('tickets')) { if (events && $state.is('tickets')) {
angular.forEach(events, function(evt, idx) { angular.forEach(events, function(evt, idx) {
var evt_tickets = (evt.persons || []).slice(0); var evt_tickets = (evt.tickets || []).slice(0);
angular.forEach(evt_tickets, function(obj, obj_idx) { angular.forEach(evt_tickets, function(obj, obj_idx) {
obj.event_title = evt.title; obj.event_title = evt.title;
obj.event_id = evt._id; obj.event_id = evt._id;
@ -133,7 +133,7 @@ eventManControllers.controller('EventsListCtrl', ['$scope', 'Event', '$uibModal'
eventManControllers.controller('EventDetailsCtrl', ['$scope', '$state', 'Event', '$log', '$translate', '$rootScope', eventManControllers.controller('EventDetailsCtrl', ['$scope', '$state', 'Event', '$log', '$translate', '$rootScope',
function ($scope, $state, Event, $log, $translate, $rootScope) { function ($scope, $state, Event, $log, $translate, $rootScope) {
$scope.event = {}; $scope.event = {};
$scope.event.persons = []; $scope.event.tickets = [];
$scope.event.formSchema = {}; $scope.event.formSchema = {};
$scope.eventFormDisabled = false; $scope.eventFormDisabled = false;
@ -146,10 +146,10 @@ eventManControllers.controller('EventDetailsCtrl', ['$scope', '$state', 'Event',
// store a new Event or update an existing one // store a new Event or update an existing one
$scope.save = function() { $scope.save = function() {
// avoid override of event.persons list. // avoid override of event.tickets list.
var this_event = angular.copy($scope.event); var this_event = angular.copy($scope.event);
if (this_event.persons) { if (this_event.tickets) {
delete this_event.persons; delete this_event.tickets;
} }
if (this_event._id === undefined) { if (this_event._id === undefined) {
$scope.event = Event.save(this_event); $scope.event = Event.save(this_event);
@ -169,7 +169,7 @@ eventManControllers.controller('EventDetailsCtrl', ['$scope', '$state', 'Event',
eventManControllers.controller('EventTicketsCtrl', ['$scope', '$state', 'Event', 'EventTicket', 'Setting', '$log', '$translate', '$rootScope', 'EventUpdates', '$uibModal', eventManControllers.controller('EventTicketsCtrl', ['$scope', '$state', 'Event', 'EventTicket', 'Setting', '$log', '$translate', '$rootScope', 'EventUpdates', '$uibModal',
function ($scope, $state, Event, EventTicket, Setting, $log, $translate, $rootScope, EventUpdates, $uibModal) { function ($scope, $state, Event, EventTicket, Setting, $log, $translate, $rootScope, EventUpdates, $uibModal) {
$scope.personsOrder = ["name", "surname"]; $scope.ticketsOrder = ["name", "surname"];
$scope.countAttendees = 0; $scope.countAttendees = 0;
$scope.message = {}; $scope.message = {};
$scope.event = {}; $scope.event = {};
@ -178,7 +178,7 @@ eventManControllers.controller('EventTicketsCtrl', ['$scope', '$state', 'Event',
$scope.formSchema = {}; $scope.formSchema = {};
$scope.formData = {}; $scope.formData = {};
$scope.guiOptions = {dangerousActionsEnabled: false}; $scope.guiOptions = {dangerousActionsEnabled: false};
$scope.customFields = Setting.query({setting: 'person_custom_field', in_event_details: true}); $scope.customFields = Setting.query({setting: 'ticket_custom_field', in_event_details: true});
$scope.formFieldsMap = {}; $scope.formFieldsMap = {};
$scope.formFieldsMapRev = {}; $scope.formFieldsMapRev = {};
@ -186,7 +186,7 @@ eventManControllers.controller('EventTicketsCtrl', ['$scope', '$state', 'Event',
if ($state.params.id) { if ($state.params.id) {
$scope.event = Event.get({id: $state.params.id}, function(data) { $scope.event = Event.get({id: $state.params.id}, function(data) {
$scope.$watchCollection(function() { $scope.$watchCollection(function() {
return $scope.event.persons; return $scope.event.tickets;
}, function(prev, old) { }, function(prev, old) {
$scope.calcAttendees(); $scope.calcAttendees();
} }
@ -215,7 +215,7 @@ eventManControllers.controller('EventTicketsCtrl', ['$scope', '$state', 'Event',
if ($state.is('event.tickets')) { if ($state.is('event.tickets')) {
$scope.allPersons = Event.group_persons({id: $state.params.id}); $scope.allPersons = Event.group_persons({id: $state.params.id});
// Handle WebSocket connection used to update the list of persons. // Handle WebSocket connection used to update the list of tickets.
$scope.EventUpdates = EventUpdates; $scope.EventUpdates = EventUpdates;
$scope.EventUpdates.open(); $scope.EventUpdates.open();
$scope.$watchCollection(function() { $scope.$watchCollection(function() {
@ -230,23 +230,23 @@ eventManControllers.controller('EventTicketsCtrl', ['$scope', '$state', 'Event',
$log.debug('do not process our own message'); $log.debug('do not process our own message');
return false; return false;
} }
if (!$scope.event.persons) { if (!$scope.event.tickets) {
$scope.event.persons = []; $scope.event.tickets = [];
} }
var person_idx = $scope.event.persons.findIndex(function(el, idx, array) { var ticket_idx = $scope.event.tickets.findIndex(function(el, idx, array) {
return data._id == el._id; return data._id == el._id;
}); });
if (person_idx != -1) { if (ticket_idx != -1) {
$log.debug('_id ' + data._id + ' found'); $log.debug('_id ' + data._id + ' found');
} else { } else {
$log.debug('_id ' + data._id + ' not found'); $log.debug('_id ' + data._id + ' not found');
} }
if (data.action == 'update' && person_idx != -1 && $scope.event.persons[person_idx] != data.person) { if (data.action == 'update' && ticket_idx != -1 && $scope.event.tickets[ticket_idx] != data.ticket) {
$scope.event.persons[person_idx] = data.person; $scope.event.tickets[ticket_idx] = data.ticket;
} else if (data.action == 'add' && person_idx == -1) { } else if (data.action == 'add' && ticket_idx == -1) {
$scope._localAddTicket(data.person); $scope._localAddTicket(data.ticket);
} else if (data.action == 'delete' && person_idx != -1) { } else if (data.action == 'delete' && ticket_idx != -1) {
$scope._localRemoveTicket({_id: data._id}); $scope._localRemoveTicket({_id: data._id});
} }
} }
@ -257,12 +257,12 @@ eventManControllers.controller('EventTicketsCtrl', ['$scope', '$state', 'Event',
} }
$scope.calcAttendees = function() { $scope.calcAttendees = function() {
if (!($scope.event && $scope.event.persons)) { if (!($scope.event && $scope.event.tickets)) {
$scope.countAttendees = 0; $scope.countAttendees = 0;
return; return;
} }
var attendees = 0; var attendees = 0;
angular.forEach($scope.event.persons, function(value, key) { angular.forEach($scope.event.tickets, function(value, key) {
if (value.attended && !value.cancelled) { if (value.attended && !value.cancelled) {
attendees += 1; attendees += 1;
} }
@ -272,30 +272,30 @@ eventManControllers.controller('EventTicketsCtrl', ['$scope', '$state', 'Event',
/* Stuff to do when a ticket is added, modified or removed locally. */ /* Stuff to do when a ticket is added, modified or removed locally. */
$scope._localAddTicket = function(ticket, original_person) { $scope._localAddTicket = function(ticket, original_ticket) {
if (!$state.is('event.tickets')) { if (!$state.is('event.tickets')) {
return true; return true;
} }
var ret = true; var ret = true;
if (!$scope.event.persons) { if (!$scope.event.tickets) {
$scope.event.persons = []; $scope.event.tickets = [];
} }
var ticket_idx = $scope.event.persons.findIndex(function(el, idx, array) { var ticket_idx = $scope.event.tickets.findIndex(function(el, idx, array) {
return ticket._id == el._id; return ticket._id == el._id;
}); });
if (ticket_idx != -1) { if (ticket_idx != -1) {
$log.warn('ticket already present: not added'); $log.warn('ticket already present: not added');
ret = false; ret = false;
} else { } else {
$scope.event.persons.push(ticket); $scope.event.tickets.push(ticket);
} }
// Try to remove this person from the allPersons list using ID or email. // Try to remove this person from the allPersons list using ID or email.
var field = null; var field = null;
var field_value = null; var field_value = null;
if (original_person && original_person._id) { if (original_ticket && original_ticket._id) {
field = '_id'; field = '_id';
field_value = original_person._id; field_value = original_ticket._id;
} else if (ticket.email) { } else if (ticket.email) {
field = 'email'; field = 'email';
field_value = ticket.email; field_value = ticket.email;
@ -312,34 +312,36 @@ eventManControllers.controller('EventTicketsCtrl', ['$scope', '$state', 'Event',
}; };
$scope._localUpdateTicket = function(ticket) { $scope._localUpdateTicket = function(ticket) {
if (!$scope.event.persons) { if (!$scope.event.tickets) {
$scope.event.persons = []; $scope.event.tickets = [];
} }
var ticket_idx = $scope.event.persons.findIndex(function(el, idx, array) { var ticket_idx = $scope.event.tickets.findIndex(function(el, idx, array) {
return ticket._id == el._id; return ticket._id == el._id;
}); });
if (ticket_idx == -1) { if (ticket_idx == -1) {
$log.warn('ticket not present: not updated'); $log.warn('ticket not present: not updated');
return false; return false;
} }
$scope.event.persons[ticket_idx] = ticket; $scope.event.tickets[ticket_idx] = ticket;
}; };
$scope._localRemoveTicket = function(person) { $scope._localRemoveTicket = function(ticket) {
if (!(person && person._id && $scope.event.persons)) { if (!(ticket && ticket._id && $scope.event.tickets)) {
return; return;
} }
var person_idx = $scope.event.persons.findIndex(function(el, idx, array) { var ticket_idx = $scope.event.tickets.findIndex(function(el, idx, array) {
return person._id == el._id; return ticket._id == el._id;
}); });
if (person_idx == -1) { if (ticket_idx == -1) {
$log.warn('unable to find and delete ticket _id ' + person._id); $log.warn('unable to find and delete ticket _id ' + ticket._id);
return; return;
} }
var removed_person = $scope.event.persons.splice(person_idx, 1); var removed_person = $scope.event.tickets.splice(ticket_idx, 1);
// to be used to populate allPersons, if needed. // to be used to populate allPersons, if needed.
if (removed_person.length) { if (removed_person.length) {
person = removed_person[0]; person = removed_person[0];
} else {
return;
} }
if (!$scope.allPersons) { if (!$scope.allPersons) {
$scope.allPersons = []; $scope.allPersons = [];
@ -352,23 +354,23 @@ eventManControllers.controller('EventTicketsCtrl', ['$scope', '$state', 'Event',
} }
}; };
$scope.setTicketAttribute = function(person, key, value, callback, hideMessage) { $scope.setTicketAttribute = function(ticket, key, value, callback, hideMessage) {
$log.debug('setTicketAttribute for _id ' + person._id + ' key: ' + key + ' value: ' + value); $log.debug('setTicketAttribute for _id ' + ticket._id + ' key: ' + key + ' value: ' + value);
var newData = {event_id: $state.params.id, _id: person._id}; var newData = {event_id: $state.params.id, _id: ticket._id};
newData[key] = value; newData[key] = value;
EventTicket.update(newData, function(data) { EventTicket.update(newData, function(data) {
if (!(data && data._id && data.person)) { if (!(data && data._id && data.ticket)) {
return; return;
} }
var person_idx = $scope.event.persons.findIndex(function(el, idx, array) { var ticket_idx = $scope.event.tickets.findIndex(function(el, idx, array) {
return data._id == el._id; return data._id == el._id;
}); });
if (person_idx == -1) { if (ticket_idx == -1) {
$log.warn('unable to find ticket _id ' + _id); $log.warn('unable to find ticket _id ' + _id);
return; return;
} }
if ($scope.event.persons[person_idx] != data.person) { if ($scope.event.tickets[ticket_idx] != data.ticket) {
$scope.event.persons[person_idx] = data.person; $scope.event.tickets[ticket_idx] = data.ticket;
} }
if (callback) { if (callback) {
callback(data); callback(data);
@ -376,9 +378,9 @@ eventManControllers.controller('EventTicketsCtrl', ['$scope', '$state', 'Event',
if (key === 'attended' && !hideMessage) { if (key === 'attended' && !hideMessage) {
var msg = {}; var msg = {};
if (value) { if (value) {
msg.message = '' + person.name + ' ' + person.surname + ' successfully added to event ' + $scope.event.title; msg.message = '' + ticket.name + ' ' + ticket.surname + ' successfully added to event ' + $scope.event.title;
} else { } else {
msg.message = '' + person.name + ' ' + person.surname + ' successfully removed from event ' + $scope.event.title; msg.message = '' + ticket.name + ' ' + ticket.surname + ' successfully removed from event ' + $scope.event.title;
msg.isError = true; msg.isError = true;
} }
$scope.showMessage(msg); $scope.showMessage(msg);
@ -386,30 +388,30 @@ eventManControllers.controller('EventTicketsCtrl', ['$scope', '$state', 'Event',
}); });
}; };
$scope.setTicketAttributeAndRefocus = function(person, key, value) { $scope.setTicketAttributeAndRefocus = function(ticket, key, value) {
$scope.setTicketAttribute(person, key, value); $scope.setTicketAttribute(ticket, key, value);
$scope.query = ''; $scope.query = '';
}; };
$scope._setAttended = function(person) { $scope._setAttended = function(ticket) {
$scope.setTicketAttribute(person, 'attended', true, null, true); $scope.setTicketAttribute(ticket, 'attended', true, null, true);
}; };
$scope.deleteTicket = function(person) { $scope.deleteTicket = function(ticket) {
EventTicket.delete({ EventTicket.delete({
event_id: $state.params.id, event_id: $state.params.id,
ticket_id: person._id ticket_id: ticket._id
}, function() { }, function() {
$scope._localRemoveTicket(person); $scope._localRemoveTicket(ticket);
}); });
}; };
$scope.addTicket = function(person) { $scope.addTicket = function(ticket) {
person.event_id = $state.params.id; ticket.event_id = $state.params.id;
EventTicket.add(person, function(ticket) { EventTicket.add(ticket, function(ticket) {
$log.debug('addTicket'); $log.debug('addTicket');
$log.debug(ticket); $log.debug(ticket);
$scope._localAddTicket(ticket, person); $scope._localAddTicket(ticket, ticket);
if (!$state.is('event.tickets')) { if (!$state.is('event.tickets')) {
$state.go('event.ticket.edit', {id: $scope.event._id, ticket_id: ticket._id}); $state.go('event.ticket.edit', {id: $scope.event._id, ticket_id: ticket._id});
} else { } else {
@ -425,7 +427,7 @@ eventManControllers.controller('EventTicketsCtrl', ['$scope', '$state', 'Event',
$scope.updateTicket = function(ticket, cb) { $scope.updateTicket = function(ticket, cb) {
ticket.event_id = $state.params.id; ticket.event_id = $state.params.id;
EventTicket.update(ticket, function(t) { EventTicket.update(ticket, function(t) {
$scope._localUpdateTicket(t.person); $scope._localUpdateTicket(t.ticket);
if (cb) { if (cb) {
cb(t); cb(t);
} }
@ -499,14 +501,14 @@ eventManControllers.controller('EventTicketsCtrl', ['$scope', '$state', 'Event',
} else { } else {
inv_key = '-' + key; inv_key = '-' + key;
} }
angular.forEach($scope.personsOrder, angular.forEach($scope.ticketsOrder,
function(value, idx) { function(value, idx) {
if (value !== key && value !== inv_key) { if (value !== key && value !== inv_key) {
new_order.push(value); new_order.push(value);
} }
} }
); );
$scope.personsOrder = new_order; $scope.ticketsOrder = new_order;
}; };
$scope.showMessage = function(cfg) { $scope.showMessage = function(cfg) {

View file

@ -2,27 +2,6 @@
/* Filters for EventMan(ager) lists of objects. */ /* Filters for EventMan(ager) lists of objects. */
/* Filter for events that have (or not) information about a registered person. */
eventManApp.filter('eventWithPersonData', ['$filter',
function($filter) {
return function(inputArray, mustBePresent) {
if (mustBePresent === undefined) {
mustBePresent = true;
}
inputArray = inputArray || [];
var returnArray = [];
for (var x=0; x < inputArray.length; x++) {
var found = inputArray[x].person_data && inputArray[x].person_data.person_id;
if ((found && mustBePresent) || (!found && !mustBePresent)) {
returnArray.push(inputArray[x]);
}
}
return returnArray;
};
}]
);
/* Filter for persons (not) registered for a given event. */ /* Filter for persons (not) registered for a given event. */
eventManApp.filter('personRegistered', ['$filter', eventManApp.filter('personRegistered', ['$filter',
function($filter) { function($filter) {
@ -33,14 +12,14 @@ eventManApp.filter('personRegistered', ['$filter',
inputArray = inputArray || []; inputArray = inputArray || [];
var returnArray = []; var returnArray = [];
var registeredIDs = []; var registeredIDs = [];
if (!(data.event && data.event.persons && data.event.persons.length)) { if (!(data.event && data.event.tickets && data.event.tickets.length)) {
return inputArray; return inputArray;
} }
for (var x=0; x < data.event.persons.length; x++) { for (var x=0; x < data.event.tickets.length; x++) {
if (!data.includeCancelled && data.event.persons[x].cancelled) { if (!data.includeCancelled && data.event.tickets[x].cancelled) {
continue; continue;
} }
registeredIDs.push(data.event.persons[x].person_id); registeredIDs.push(data.event.tickets[x]._id);
} }
for (var x=0; x < inputArray.length; x++) { for (var x=0; x < inputArray.length; x++) {
var found = registeredIDs.indexOf(inputArray[x]._id) != -1; var found = registeredIDs.indexOf(inputArray[x]._id) != -1;
@ -68,7 +47,7 @@ eventManApp.filter('splittedFilter', ['$filter',
); );
/* Filter that returns only the (not) registered persons at an event. */ /* Filter that returns only the (not) registered tickets at an event. */
eventManApp.filter('registeredFilter', ['$filter', eventManApp.filter('registeredFilter', ['$filter',
function($filter) { function($filter) {
return function(inputArray, data) { return function(inputArray, data) {

View file

@ -44,10 +44,10 @@ eventManServices.factory('Event', ['$resource', '$rootScope',
data = angular.fromJson(data); data = angular.fromJson(data);
convert_dates(data); convert_dates(data);
// strip empty keys. // strip empty keys.
angular.forEach(data.persons || [], function(person, person_idx) { angular.forEach(data.tickets || [], function(ticket, ticket_idx) {
angular.forEach(person, function(value, key) { angular.forEach(ticket, function(value, key) {
if (value === "") { if (value === "") {
delete person[key]; delete ticket[key];
} }
}); });
}); });
@ -84,7 +84,7 @@ eventManServices.factory('EventTicket', ['$resource', '$rootScope',
interceptor : {responseError: $rootScope.errorHandler}, interceptor : {responseError: $rootScope.errorHandler},
transformResponse: function(data, headers) { transformResponse: function(data, headers) {
data = angular.fromJson(data); data = angular.fromJson(data);
return data.person; return data.ticket;
} }
}, },
@ -98,7 +98,7 @@ eventManServices.factory('EventTicket', ['$resource', '$rootScope',
if (data.error) { if (data.error) {
return data; return data;
} }
return data.persons; return data.tickets;
} }
}, },
@ -110,7 +110,7 @@ eventManServices.factory('EventTicket', ['$resource', '$rootScope',
params: {uuid: $rootScope.app_uuid}, params: {uuid: $rootScope.app_uuid},
transformResponse: function(data, headers) { transformResponse: function(data, headers) {
data = angular.fromJson(data); data = angular.fromJson(data);
return data.person; return data.ticket;
} }
}, },
@ -236,7 +236,7 @@ eventManServices.factory('User', ['$resource', '$rootScope',
); );
/* WebSocket collection used to update the list of persons of an Event. */ /* WebSocket collection used to update the list of tickets of an Event. */
eventManApp.factory('EventUpdates', ['$websocket', '$location', '$log', eventManApp.factory('EventUpdates', ['$websocket', '$location', '$log',
function($websocket, $location, $log) { function($websocket, $location, $log) {

View file

@ -1,69 +0,0 @@
<!-- show details of a Person -->
<div class="container">
<div class="panel panel-primary table-striped top5">
<div class="panel-heading">
<h1>
<button ng-if="person._id" ng-click="$state.go('person.info', {id: person._id})" class="btn btn-success">
<span class="fa fa-info-circle vcenter"></span>
{{'Info' | translate}}
</button>
&nbsp;<span ng-if="!(person.name || person.surname)">{{'New person' | translate}}</span>{{person.name}} {{person.surname}}
</h1>
</div>
<div class="panel-body">
<form name="personForm" ng-model="persondetails" ng-submit="save()">
<div ng-class="{clearfix: true, alert: true, 'alert-success': !personForm.$dirty, 'alert-danger': personForm.$dirty}">
<button type="button" class="btn btn-default pull-right" ng-click="save($event)" ng-disabled="!personForm.$dirty">
<span class="fa fa-floppy-o vcenter"></span>
{{'save' | translate}}
</button>
</div>
<div class="input-group input-group-lg">
<span class="input-group-addon min120">{{'Name' | translate}}</span>
<input type="text" class="form-control" placeholder="{{'Name' | translate}}" ng-model="person.name" ng-required="1">
</div>
<div class="input-group input-group-lg top5">
<span class="input-group-addon min120">{{'Surname' | translate}}</span>
<input type="text" class="form-control" placeholder="{{'Surname' | translate}}" ng-model="person.surname">
</div>
<div class="input-group input-group-lg top5">
<span class="input-group-addon min120">{{'Email' | translate}}</span>
<input type="email" name="email" class="form-control" placeholder="{{'name.surname@example.com' | translate}}" ng-model="person.email">
</div>
<div class="input-group input-group-lg top5">
<span class="input-group-addon min120">{{'Company' | translate}}</span>
<input name="company" class="form-control" placeholder="{{'Acme Corporation' | translate}}" ng-model="person.company">
</div>
<div class="input-group input-group-lg top5">
<span class="input-group-addon min120">{{'Job' | translate}}</span>
<input name="job_title" class="form-control" placeholder="{{'Evil Ruler' | translate}}" ng-model="person.job_title">
</div>
<div class="form-group top5">
<label for="addToEvent">{{'Add to event:' | translate}}</label>
<select class="form-control" id="addToEvent" ng-model="addToEvent">
<option value=""></option>
<option ng-repeat="event in events | eventWithPersonData:false" value="{{event._id}}">{{event.title}}</option>
</select>
<option>
<tr ng-repeat="event in events | splittedFilter:query | orderBy:eventsOrderProp">
</div>
<div ng-repeat="custom in customFields" class="form-group top5">
<label for="custom_{{custom['key']}}">{{custom.label | translate}}</span>
<input ng-if="custom.type == 'boolean'" id="custom_{{custm['key']}}" type="checkbox" class="form-control" placeholder="{{custom.label | translate}}" ng-model="person[custom.key]">
<input ng-if="custom.type != 'boolean'" id="custom_{{custm['key']}}" type="text" class="form-control" placeholder="{{custom.label | translate}}" ng-model="person[custom.key]">
</div>
<input type="submit" class="outside-screen" />
</form>
</div>
</div>
</div>

View file

@ -1,50 +0,0 @@
<!-- show details of a Person -->
<div class="container">
<h1>{{person.name}} {{person.surname}}
<button ng-if="person._id" ng-click="$state.go('person.edit', {id: person._id})" class="btn btn-success">
<span class="fa fa-pencil-square-o vcenter"></span>
{{'Edit' | translate}}
</button>
</h1>
<div class="panel panel-primary table-striped top5">
<div class="panel-heading">Events</div>
<div class="panel-body">
<form class="form-inline">
<div class="form-group">
<label for="query-persons">{{'Search:' | translate}}</label>
<input eventman-focus type="text" id="query-persons" class="form-control" placeholder="{{'Name or email' | translate}}" ng-model="query" ng-model-options="{debounce: 600}">
</div>
<div class="form-group">
<label for="events-order">{{'Sort by:' | translate}}</label>
<select id="events-order" class="form-control" ng-model="eventsOrderProp">
<option value="title">{{'Title' | translate}}</option>
<option value="-title">{{'Title (descending)' | translate}}</option>
<option value="begin_date">{{'Date' | translate}}</option>
<option value="-begin_date">{{'Date (descending)' | translate}}</option>
</select>
</div>
</form>
<table class="table">
<thead>
<tr>
<th>{{'Event' | translate}}</th>
<th class="text-center">{{'Registered' | translate}}</th>
<th class="text-center">{{'Attended' | translate}}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="event in events | splittedFilter:query | orderBy:eventsOrderProp">
<td><strong><a ui-sref="event.tickets({id: event._id})">{{event.title}}</a></strong></td>
<td class="text-center">
<button class="btn btn-link" name="switch-registered" ng-click="switchRegistered(event, person, !event.person_data.person_id)"><span class="fa fa-lg {{(event.person_data.person_id) && 'fa-check-circle text-success' || 'fa-times-circle text-danger'}}"></span></button>
</td>
<td class="text-center">
<button ng-disabled="!event.person_data.person_id" class="btn btn-link" name="switch-attended" ng-click="setPersonAttributeAtEvent(event, 'attended', !event.person_data.attended)"><span class="fa fa-lg {{(event.person_data.attended) && 'fa-check-circle text-success' || 'fa-times-circle text-danger'}}"></span></button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>

View file

@ -1,2 +0,0 @@
<!-- main view for Person -->
<div ui-view></div>

View file

@ -24,7 +24,7 @@ re_objectid = re.compile(r'[0-9a-f]{24}')
_force_conversion = { _force_conversion = {
'seq_hex': str, 'seq_hex': str,
'persons.seq_hex': str 'tickets.seq_hex': str
} }

View file

@ -88,7 +88,6 @@ class BaseHandler(tornado.web.RequestHandler):
'event:tickets|all': True, 'event:tickets|all': True,
'event:tickets-all|create': True, 'event:tickets-all|create': True,
'events|read': True, 'events|read': True,
'persons|create': True,
'users|create': True 'users|create': True
} }
@ -556,16 +555,16 @@ class EventsHandler(CollectionHandler):
collection = 'events' collection = 'events'
def filter_get(self, output): def filter_get(self, output):
if not self.has_permission('persons-all|read'): if not self.has_permission('tckets-all|read'):
if 'persons' in output: if 'tickets' in output:
output['persons'] = [] output['tickets'] = []
return output return output
def filter_get_all(self, output): def filter_get_all(self, output):
if not self.has_permission('persons-all|read'): if not self.has_permission('tickets-all|read'):
for event in output.get('events') or []: for event in output.get('events') or []:
if 'persons' in event: if 'tickets' in event:
event['persons'] = [] event['tickets'] = []
return output return output
def filter_input_post(self, data): def filter_input_post(self, data):
@ -592,56 +591,56 @@ class EventsHandler(CollectionHandler):
group_id = this_event.get('group_id') group_id = this_event.get('group_id')
if group_id is None: if group_id is None:
return {'persons': persons} return {'persons': persons}
this_persons = [p for p in (this_event.get('persons') or []) if not p.get('cancelled')] this_persons = [p for p in (this_event.get('tickets') or []) if not p.get('cancelled')]
this_emails = filter(None, [p.get('email') for p in this_persons]) this_emails = filter(None, [p.get('email') for p in this_persons])
all_query = {'group_id': group_id} all_query = {'group_id': group_id}
events = self.db.query('events', all_query) events = self.db.query('events', all_query)
for event in events: for event in events:
if id_ is not None and str(event.get('_id')) == id_: if id_ is not None and str(event.get('_id')) == id_:
continue continue
persons += [p for p in (event.get('persons') or []) if p.get('email') and p.get('email') not in this_emails] persons += [p for p in (event.get('tickets') or []) if p.get('email') and p.get('email') not in this_emails]
return {'persons': persons} return {'persons': persons}
def _get_person_data(self, person_id_or_query, persons): def _get_ticket_data(self, ticket_id_or_query, tickets):
"""Filter a list of persons returning the first item with a given person_id """Filter a list of tickets returning the first item with a given _id
or which set of keys specified in a dictionary match their respective values.""" or which set of keys specified in a dictionary match their respective values."""
for person in persons: for ticket in tickets:
if isinstance(person_id_or_query, dict): if isinstance(ticket_id_or_query, dict):
if all(person.get(k) == v for k, v in person_id_or_query.iteritems()): if all(ticket.get(k) == v for k, v in ticket_id_or_query.iteritems()):
return person return ticket
else: else:
if str(person.get('_id')) == person_id_or_query: if str(ticket.get('_id')) == ticket_id_or_query:
return person return ticket
return {} return {}
def handle_get_persons(self, id_, resource_id=None, match_query=None): def handle_get_tickets(self, id_, resource_id=None, match_query=None):
# Return every person registered at this event, or the information # Return every ticket registered at this event, or the information
# about a specific person. # about a specific ticket.
query = {'_id': id_} query = {'_id': id_}
event = self.db.query('events', query)[0] event = self.db.query('events', query)[0]
if match_query is None: if match_query is None:
match_query = resource_id match_query = resource_id
if resource_id: if resource_id:
return {'person': self._get_person_data(match_query, event.get('persons') or [])} return {'ticket': self._get_ticket_data(match_query, event.get('tickets') or [])}
persons = self._filter_results(event.get('persons') or [], self.arguments) tickets = self._filter_results(event.get('tickets') or [], self.arguments)
return {'persons': persons} return {'tickets': tickets}
def handle_get_tickets(self, id_, resource_id=None): def __handle_get_tickets_REMOVE(self, id_, resource_id=None):
if resource_id is None and not self.has_permission('event:tickets|all'): if resource_id is None and not self.has_permission('event:tickets|all'):
return self.build_error(status=401, message='insufficient permissions: event:tickets|all') return self.build_error(status=401, message='insufficient permissions: event:tickets|all')
return self.handle_get_persons(id_, resource_id, {'_id': resource_id}) return self.handle_get_persons(id_, resource_id, {'_id': resource_id})
def handle_post_persons(self, id_, person_id, data): def handle_post_tickets(self, id_, ticket_id, data):
# Add a person to the list of persons registered at this event. # Add a ticket to the list of tickets registered at this event.
uuid, arguments = self.uuid_arguments uuid, arguments = self.uuid_arguments
self._clean_dict(data) self._clean_dict(data)
data['seq'] = self.get_next_seq('event_%s_persons' % id_) data['seq'] = self.get_next_seq('event_%s_tickets' % id_)
data['seq_hex'] = '%06X' % data['seq'] data['seq_hex'] = '%06X' % data['seq']
if person_id is None: if ticket_id is None:
doc = {} doc = {}
else: else:
doc = self.db.query('events', {'_id': id_, 'persons._id': person_id}) doc = self.db.query('events', {'_id': id_, 'tickets._id': ticket_id})
ret = {'action': 'add', '_id': person_id, 'person': data, 'uuid': uuid} ret = {'action': 'add', '_id': ticket_id, 'ticket': data, 'uuid': uuid}
if '_id' in data: if '_id' in data:
del data['_id'] del data['_id']
self.send_ws_message('event/%s/tickets/updates' % id_, json.dumps(ret)) self.send_ws_message('event/%s/tickets/updates' % id_, json.dumps(ret))
@ -649,78 +648,71 @@ class EventsHandler(CollectionHandler):
data['_id'] = self.gen_id() data['_id'] = self.gen_id()
merged, doc = self.db.update('events', merged, doc = self.db.update('events',
{'_id': id_}, {'_id': id_},
{'persons': data}, {'tickets': data},
operation='appendUnique', operation='appendUnique',
create=False) create=False)
return ret return ret
handle_post_tickets = handle_post_persons def handle_put_tickets(self, id_, ticket_id, data, ticket=True):
# Update an existing entry for a ticket registered at this event.
def handle_put_persons(self, id_, person_id, data, ticket=False):
# Update an existing entry for a person registered at this event.
self._clean_dict(data) self._clean_dict(data)
uuid, arguments = self.uuid_arguments uuid, arguments = self.uuid_arguments
query = dict([('persons.%s' % k, v) for k, v in arguments.iteritems()]) query = dict([('tickets.%s' % k, v) for k, v in arguments.iteritems()])
query['_id'] = id_ query['_id'] = id_
if person_id is not None: if ticket_id is not None:
query['persons._id'] = person_id query['tickets._id'] = ticket_id
person_query = {'_id': person_id} ticket_query = {'_id': ticket_id}
else: else:
person_query = self.arguments ticket_query = self.arguments
old_person_data = {} old_ticket_data = {}
current_event = self.db.query(self.collection, query) current_event = self.db.query(self.collection, query)
if current_event: if current_event:
current_event = current_event[0] current_event = current_event[0]
else: else:
current_event = {} current_event = {}
old_person_data = self._get_person_data(person_query, old_ticket_data = self._get_ticket_data(ticket_query,
current_event.get('persons') or []) current_event.get('tickets') or [])
merged, doc = self.db.update('events', query, merged, doc = self.db.update('events', query,
data, updateList='persons', create=False) data, updateList='tickets', create=False)
new_person_data = self._get_person_data(person_query, new_ticket_data = self._get_ticket_data(ticket_query,
doc.get('persons') or []) doc.get('tickets') or [])
env = self._dict2env(new_person_data) env = self._dict2env(new_ticket_data)
# always takes the person_id from the new person (it may have # always takes the ticket_id from the new ticket (it may have
# been a ticket_id). # been a ticket_id).
ticket_id = str(new_person_data.get('_id')) ticket_id = str(new_ticket_data.get('_id'))
env.update({'PERSON_ID': ticket_id, 'TICKED_ID': ticket_id, 'EVENT_ID': id_, env.update({'PERSON_ID': ticket_id, 'TICKED_ID': ticket_id, 'EVENT_ID': id_,
'EVENT_TITLE': doc.get('title', ''), 'WEB_USER': self.current_user, 'EVENT_TITLE': doc.get('title', ''), 'WEB_USER': self.current_user,
'WEB_REMOTE_IP': self.request.remote_ip}) 'WEB_REMOTE_IP': self.request.remote_ip})
stdin_data = {'old': old_person_data, stdin_data = {'old': old_ticket_data,
'new': new_person_data, 'new': new_ticket_data,
'event': doc, 'event': doc,
'merged': merged 'merged': merged
} }
self.run_triggers('update_person_in_event', stdin_data=stdin_data, env=env) self.run_triggers('update_ticket_in_event', stdin_data=stdin_data, env=env)
if old_person_data and old_person_data.get('attended') != new_person_data.get('attended'): if old_ticket_data and old_ticket_data.get('attended') != new_ticket_data.get('attended'):
if new_person_data.get('attended'): if new_ticket_data.get('attended'):
self.run_triggers('attends', stdin_data=stdin_data, env=env) self.run_triggers('attends', stdin_data=stdin_data, env=env)
ret = {'action': 'update', '_id': ticket_id, 'person': new_person_data, 'uuid': uuid} ret = {'action': 'update', '_id': ticket_id, 'ticket': new_ticket_data, 'uuid': uuid}
if old_person_data != new_person_data: if old_ticket_data != new_ticket_data:
self.send_ws_message('event/%s/tickets/updates' % id_, json.dumps(ret)) self.send_ws_message('event/%s/tickets/updates' % id_, json.dumps(ret))
return ret return ret
def handle_put_tickets(self, id_, person_id, data): def handle_delete_tickets(self, id_, ticket_id):
return self.handle_put_persons(id_, person_id, data, True) # Remove a specific ticket from the list of tickets registered at this event.
def handle_delete_persons(self, id_, person_id):
# Remove a specific person from the list of persons registered at this event.
uuid, arguments = self.uuid_arguments uuid, arguments = self.uuid_arguments
doc = self.db.query('events', doc = self.db.query('events',
{'_id': id_, 'persons._id': person_id}) {'_id': id_, 'tickets._id': ticket_id})
ret = {'action': 'delete', '_id': person_id, 'uuid': uuid} ret = {'action': 'delete', '_id': ticket_id, 'uuid': uuid}
if doc: if doc:
merged, doc = self.db.update('events', merged, doc = self.db.update('events',
{'_id': id_}, {'_id': id_},
{'persons': {'_id': person_id}}, {'tickets': {'_id': ticket_id}},
operation='delete', operation='delete',
create=False) create=False)
self.send_ws_message('event/%s/tickets/updates' % id_, json.dumps(ret)) self.send_ws_message('event/%s/tickets/updates' % id_, json.dumps(ret))
return ret return ret
handle_delete_tickets = handle_delete_persons
class UsersHandler(CollectionHandler): class UsersHandler(CollectionHandler):
"""Handle requests for Users.""" """Handle requests for Users."""
@ -732,11 +724,11 @@ class UsersHandler(CollectionHandler):
del data['password'] del data['password']
if '_id' in data: if '_id' in data:
tickets = [] tickets = []
events = self.db.query('events', {'persons.created_by': data['_id']}) events = self.db.query('events', {'tickets.created_by': data['_id']})
for event in events: for event in events:
event_title = event.get('title') or '' event_title = event.get('title') or ''
event_id = str(event.get('_id')) event_id = str(event.get('_id'))
evt_tickets = self._filter_results(event.get('persons') or [], {'created_by': data['_id']}) evt_tickets = self._filter_results(event.get('tickets') or [], {'created_by': data['_id']})
for evt_ticket in evt_tickets: for evt_ticket in evt_tickets:
evt_ticket['event_title'] = event_title evt_ticket['event_title'] = event_title
evt_ticket['event_id'] = event_id evt_ticket['event_id'] = event_id