mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2025-01-29 18:52:22 +01:00
batch delete periodic events
This commit is contained in:
parent
6090d2e05a
commit
54be21729b
@ -4,6 +4,7 @@
|
||||
- Automated setup assistant
|
||||
- An administrator can delete a member
|
||||
- An event reservation can be cancelled, if reservation cancellation is enabled
|
||||
- Batch delete recurring events
|
||||
- Ability to import iCalendar agendas in the public calendar, through URLs to ICS files (RFC 5545)
|
||||
- Ability to configure the duration of a reservation slot, using `SLOT_DURATION`. Previously, only 60 minutes slots were allowed
|
||||
- Ability to force the email validation when a new user registers. This is optionally configured with `USER_CONFIRMATION_NEEDED_TO_SIGN_IN`
|
||||
|
@ -183,26 +183,22 @@ Application.Controllers.controller('ShowEventController', ['$scope', '$state', '
|
||||
* @param event {$resource} angular's Event $resource
|
||||
*/
|
||||
$scope.deleteEvent = function (event) {
|
||||
dialogs.confirm({
|
||||
// open a confirmation dialog
|
||||
const modalInstance = $uibModal.open({
|
||||
animation: true,
|
||||
templateUrl: '<%= asset_path "events/deleteRecurrent.html" %>',
|
||||
size: 'md',
|
||||
controller: 'DeleteRecurrentEventController',
|
||||
resolve: {
|
||||
object () {
|
||||
return {
|
||||
title: _t('app.public.events_show.confirmation_required'),
|
||||
msg: _t('app.public.events_show.do_you_really_want_to_delete_this_event')
|
||||
};
|
||||
}
|
||||
eventPromise: ['Event', function (Event) { return Event.get({ id: $scope.event.id }).$promise; }]
|
||||
}
|
||||
}, function () {
|
||||
// the admin has confirmed, delete
|
||||
event.$delete(function () {
|
||||
});
|
||||
// once the dialog was closed, do things depending on the result
|
||||
modalInstance.result.then(function (res) {
|
||||
if (res.status == 'success') {
|
||||
$state.go('app.public.events_list');
|
||||
return growl.info(_t('app.public.events_show.event_successfully_deleted'));
|
||||
}, function (error) {
|
||||
console.error(error);
|
||||
growl.error(_t('app.public.events_show.unable_to_delete_the_event_because_some_users_alredy_booked_it'));
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@ -816,3 +812,71 @@ function __range__ (left, right, inclusive) {
|
||||
}
|
||||
return range;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Controller used in the event deletion modal window
|
||||
*/
|
||||
Application.Controllers.controller('DeleteRecurrentEventController', ['$scope', '$uibModalInstance', 'Event', 'eventPromise', 'growl', '_t',
|
||||
function ($scope, $uibModalInstance, Event, eventPromise, growl, _t) {
|
||||
|
||||
// is the current event (to be deleted) recurrent?
|
||||
$scope.isRecurrent = eventPromise.recurrence_events.length > 0;
|
||||
|
||||
// with recurrent slots: how many slots should we delete?
|
||||
$scope.deleteMode = 'single';
|
||||
|
||||
/**
|
||||
* Confirmation callback
|
||||
*/
|
||||
$scope.ok = function () {
|
||||
const { id, start_at, end_at } = eventPromise;
|
||||
// the admin has confirmed, delete the slot
|
||||
Event.delete(
|
||||
{ id, mode: $scope.deleteMode },
|
||||
function (res) {
|
||||
// delete success
|
||||
if (res.deleted > 1) {
|
||||
growl.success(_t(
|
||||
'app.public.events_show.events_deleted',
|
||||
{COUNT: res.deleted - 1}
|
||||
));
|
||||
} else {
|
||||
growl.success(_t(
|
||||
'app.public.events_show.event_successfully_deleted'
|
||||
));
|
||||
}
|
||||
$uibModalInstance.close({
|
||||
status: 'success',
|
||||
events: res.details.map(function (d) { return d.event.id })
|
||||
});
|
||||
},
|
||||
function (res) {
|
||||
// not everything was deleted
|
||||
const { data } = res;
|
||||
if (data.total > 1) {
|
||||
growl.warning(_t(
|
||||
'app.public.events_show.events_not_deleted',
|
||||
{TOTAL: data.total, COUNT: data.total - data.deleted}
|
||||
));
|
||||
} else {
|
||||
growl.error(_t(
|
||||
'app.public.events_show.unable_to_delete_the_event'
|
||||
));
|
||||
}
|
||||
$uibModalInstance.close({
|
||||
status: 'failed',
|
||||
availabilities: data.details.filter(function (d) { return d.status }).map(function (d) { return d.event.id })
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancellation callback
|
||||
*/
|
||||
$scope.cancel = function () {
|
||||
$uibModalInstance.dismiss('cancel');
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
|
26
app/assets/templates/events/deleteRecurrent.html
Normal file
26
app/assets/templates/events/deleteRecurrent.html
Normal file
@ -0,0 +1,26 @@
|
||||
<div class="modal-header">
|
||||
<img ng-src="{{logoBlack.custom_asset_file_attributes.attachment_url}}" alt="{{logo.custom_asset_file_attributes.attachment}}" class="modal-logo"/>
|
||||
<h1 translate>{{ 'app.public.events_show.confirmation_required' }}</h1>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p ng-hide="isRecurrent" translate>{{ 'app.public.events_show.do_you_really_want_to_delete_this_event' }}</p>
|
||||
<p ng-show="isRecurrent" translate>{{ 'app.public.events_show.delete_recurring_event' }}</p>
|
||||
<div ng-show="isRecurrent" class="form-group">
|
||||
<label class="checkbox">
|
||||
<input type="radio" name="delete_mode" ng-model="deleteMode" value="single" required/>
|
||||
<span translate>{{ 'app.public.events_show.delete_this_event' }}</span>
|
||||
</label>
|
||||
<label class="checkbox">
|
||||
<input type="radio" name="delete_mode" ng-model="deleteMode" value="next" required/>
|
||||
<span translate>{{ 'app.public.events_show.delete_this_and_next' }}</span>
|
||||
</label>
|
||||
<label class="checkbox">
|
||||
<input type="radio" name="delete_mode" ng-model="deleteMode" value="all" required/>
|
||||
<span translate>{{ 'app.public.events_show.delete_all' }}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-info" ng-click="ok()" translate>{{ 'app.shared.buttons.delete' }}</button>
|
||||
<button class="btn btn-default" ng-click="cancel()" translate>{{ 'app.shared.buttons.cancel' }}</button>
|
||||
</div>
|
@ -74,10 +74,11 @@ class API::EventsController < API::ApiController
|
||||
|
||||
def destroy
|
||||
authorize Event
|
||||
if @event.safe_destroy
|
||||
head :no_content
|
||||
res = EventService.delete(params[:id], params[:mode])
|
||||
if res.all? { |r| r[:status] }
|
||||
render json: { deleted: res.length, details: res }, status: :ok
|
||||
else
|
||||
head :unprocessable_entity
|
||||
render json: { total: res.length, deleted: res.select { |r| r[:status] }.length, details: res }, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -41,4 +41,35 @@ class EventService
|
||||
end
|
||||
{ start_at: start_at, end_at: end_at }
|
||||
end
|
||||
|
||||
# delete one or more events (if periodic)
|
||||
def self.delete(event_id, mode = 'single')
|
||||
results = []
|
||||
event = Event.find(event_id)
|
||||
events = case mode
|
||||
when 'single'
|
||||
[event]
|
||||
when 'next'
|
||||
Event.includes(:availability)
|
||||
.where(
|
||||
'availabilities.start_at >= ? AND events.recurrence_id = ?',
|
||||
event.availability.start_at,
|
||||
event.recurrence_id
|
||||
)
|
||||
.references(:availabilities, :events)
|
||||
when 'all'
|
||||
Event.where(
|
||||
'recurrence_id = ?',
|
||||
event.recurrence_id
|
||||
)
|
||||
else
|
||||
[]
|
||||
end
|
||||
|
||||
events.each do |e|
|
||||
# here we use double negation because safe_destroy can return either a boolean (false) or an Availability (in case of delete success)
|
||||
results.push status: !!e.safe_destroy, event: e # rubocop:disable Style/DoubleNegation
|
||||
end
|
||||
results
|
||||
end
|
||||
end
|
||||
|
@ -117,7 +117,6 @@ en:
|
||||
# confirmation modal
|
||||
you_will_receive_confirmation_instructions_by_email: You will receive confirmation instructions by email.
|
||||
|
||||
|
||||
# forgotten password modal
|
||||
your_email_address_is_unknown: "Your e-mail address is unknown."
|
||||
you_will_receive_in_a_moment_an_email_with_instructions_to_reset_your_password: "You will receive in a moment, an e-mail with instructions to reset your password."
|
||||
@ -324,8 +323,14 @@ en:
|
||||
you_can_shift_this_reservation_on_the_following_slots: "You can shift this reservation on the following slots:"
|
||||
confirmation_required: "Confirmation required"
|
||||
do_you_really_want_to_delete_this_event: "Do you really want to delete this event?"
|
||||
delete_recurring_event: "You're about to delete a periodic event. What do you want to do?"
|
||||
delete_this_event: "Only this event"
|
||||
delete_this_and_next: "This event and the following"
|
||||
delete_all: "All events"
|
||||
event_successfully_deleted: "Event successfully deleted"
|
||||
unable_to_delete_the_event_because_some_users_alredy_booked_it: "Unable to delete this event, it may have been already reserved by some users."
|
||||
events_deleted: "The event, and {COUNT, plural, =1{one other} other{{COUNT} others}}, have been deleted"
|
||||
unable_to_delete_the_event: "Unable to delete the event, it may be booked by a member"
|
||||
events_not_deleted: "On {TOTAL} events, {COUNT, plural, =1{one was not deleted} other{{COUNT} were not deleted}}. Some reservations may exists on {COUNT, plural, =1{it} other{them}}."
|
||||
cancel_the_reservation: "Cancel the reservation"
|
||||
do_you_really_want_to_cancel_this_reservation_this_apply_to_all_booked_tickets: "Do you really want to cancel this reservation? This apply to ALL booked tickets."
|
||||
reservation_was_successfully_cancelled: "Reservation was successfully cancelled"
|
||||
|
@ -323,8 +323,14 @@ es:
|
||||
you_can_shift_this_reservation_on_the_following_slots: "Puede cambiar la reserva en los siguientes campos:"
|
||||
confirmation_required: "Confirmation required"
|
||||
do_you_really_want_to_delete_this_event: "Do you really want to delete this event?"
|
||||
delete_recurring_event: "You're about to delete a periodic event. What do you want to do?"
|
||||
delete_this_event: "Only this event"
|
||||
delete_this_and_next: "This event and the following"
|
||||
delete_all: "All events"
|
||||
event_successfully_deleted: "Event successfully deleted"
|
||||
unable_to_delete_the_event_because_some_users_alredy_booked_it: "Unable to delete this event, it may have been already reserved by some users."
|
||||
events_deleted: "The event, and {COUNT, plural, =1{one other} other{{COUNT} others}}, have been deleted"
|
||||
unable_to_delete_the_event: "Unable to delete the event, it may be booked by a member"
|
||||
events_not_deleted: "On {TOTAL} events, {COUNT, plural, =1{one was not deleted} other{{COUNT} were not deleted}}. Some reservations may exists on {COUNT, plural, =1{it} other{them}}."
|
||||
cancel_the_reservation: "Cancel the reservation"
|
||||
do_you_really_want_to_cancel_this_reservation_this_apply_to_all_booked_tickets: "Do you really want to cancel this reservation? This apply to ALL booked tickets."
|
||||
reservation_was_successfully_cancelled: "Reservation was successfully cancelled"
|
||||
|
@ -323,8 +323,14 @@ fr:
|
||||
you_can_shift_this_reservation_on_the_following_slots: "Vous pouvez déplacer cette réservation sur les créneaux suivants :"
|
||||
confirmation_required: "Confirmation requise"
|
||||
do_you_really_want_to_delete_this_event: "Voulez-vous vraiment supprimer cet évènement ?"
|
||||
delete_recurring_event: "Vous êtes sur le point de supprimer un évènement périodique. Que voulez-vous supprimer ?"
|
||||
delete_this_event: "Uniquement cet évènement"
|
||||
delete_this_and_next: "Cet évènement et tous les suivants"
|
||||
delete_all: "Tous les évènements"
|
||||
event_successfully_deleted: "L'évènement a bien été supprimé."
|
||||
unable_to_delete_the_event_because_some_users_alredy_booked_it: "Impossible de supprimer l'évènement, il est peut-être déjà réservé par certains utilisateurs."
|
||||
events_deleted: "L'évènement, ainsi {COUNT, plural, =1{qu'un autre} other{que {COUNT} autres}}, ont été supprimés"
|
||||
unable_to_delete_the_event: "L'évènement n'a pu être supprimé, probablement car il est déjà réservé par un membre"
|
||||
events_not_deleted: "Sur {TOTAL} évènements, {COUNT, plural, =1{un n'a pas pu être supprimé} other{{COUNT} n'ont pas pu être supprimés}}. Il est possible que des réservations existent sur {COUNT, plural, =1{celui-ci} other{ceux-ci}}."
|
||||
cancel_the_reservation: "Annuler la réservation"
|
||||
do_you_really_want_to_cancel_this_reservation_this_apply_to_all_booked_tickets: "Êtes vous sur de vouloir annuler cette réservation? Ceci s'applique à TOUTES les places réservées."
|
||||
reservation_was_successfully_cancelled: "La réservation a bien été annulée."
|
||||
|
@ -323,8 +323,14 @@ pt:
|
||||
you_can_shift_this_reservation_on_the_following_slots: "Você pode alterar essa reserva nos campos a seguir:"
|
||||
confirmation_required: "Confirmação obrigatória"
|
||||
do_you_really_want_to_delete_this_event: "Vocêrealmente deseja remover este evento?"
|
||||
delete_recurring_event: "You're about to delete a periodic event. What do you want to do?"
|
||||
delete_this_event: "Only this event"
|
||||
delete_this_and_next: "This event and the following"
|
||||
delete_all: "All events"
|
||||
event_successfully_deleted: "Evento excluído com sucesso"
|
||||
unable_to_delete_the_event_because_some_users_alredy_booked_it: "Não foi possível excluir este evento, já pode ter sido reservado por alguns usuários."
|
||||
events_deleted: "The event, and {COUNT, plural, =1{one other} other{{COUNT} others}}, have been deleted"
|
||||
unable_to_delete_the_event: "Unable to delete the event, it may be booked by a member"
|
||||
events_not_deleted: "On {TOTAL} events, {COUNT, plural, =1{one was not deleted} other{{COUNT} were not deleted}}. Some reservations may exists on {COUNT, plural, =1{it} other{them}}."
|
||||
cancel_the_reservation: "Cancel the reservation"
|
||||
do_you_really_want_to_cancel_this_reservation_this_apply_to_all_booked_tickets: "Do you really want to cancel this reservation? This apply to ALL booked tickets."
|
||||
reservation_was_successfully_cancelled: "Reservation was successfully cancelled"
|
||||
|
Loading…
x
Reference in New Issue
Block a user