From 99dfb4a9797cc177be8ffbf34a7218bc2c6ba5eb Mon Sep 17 00:00:00 2001
From: uhensler <urs.hensler@frentix.com>
Date: Thu, 25 Jun 2020 13:16:41 +0200
Subject: [PATCH] OO-4630: Coach can remove users from an appointment

---
 .../flexible/impl/elements/FormSubmit.java    |  5 ++
 .../appointments/AppointmentsService.java     |  2 +
 .../manager/AppointmentsServiceImpl.java      | 13 ++++
 ...bstractParticipationRemoveController.java} | 66 +++++++++++++------
 .../appointments/ui/AppointmentDataModel.java |  4 +-
 .../ui/AppointmentDeleteController.java       | 33 +---------
 .../ui/AppointmentListController.java         | 36 +++++-----
 .../ui/AppointmentListEditController.java     |  2 +-
 .../nodes/appointments/ui/AppointmentRow.java | 14 ++--
 ...ava => ParticipationRemoveController.java} | 13 ++--
 .../ui/_content/appointment_row.html          | 12 ++--
 .../ui/_i18n/LocalStrings_de.properties       | 20 +++---
 .../ui/_i18n/LocalStrings_en.properties       | 20 +++---
 13 files changed, 129 insertions(+), 111 deletions(-)
 rename src/main/java/org/olat/course/nodes/appointments/ui/{AbstractRebookController.java => AbstractParticipationRemoveController.java} (79%)
 rename src/main/java/org/olat/course/nodes/appointments/ui/{RebookController.java => ParticipationRemoveController.java} (86%)

diff --git a/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/FormSubmit.java b/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/FormSubmit.java
index 20d1677530d..1d155171b9a 100644
--- a/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/FormSubmit.java
+++ b/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/FormSubmit.java
@@ -59,6 +59,11 @@ public class FormSubmit extends FormButton implements Submit{
 		this.action = FormEvent.ONCLICK;
 	}
 
+	public void setI18nKey(String i18nKey) {
+		this.i18nKey = i18nKey;
+		getComponent().setDirty(true);
+	}
+
 	@Override
 	protected void rootFormAvailable(){
 		String formItemId = getFormItemId();
diff --git a/src/main/java/org/olat/course/nodes/appointments/AppointmentsService.java b/src/main/java/org/olat/course/nodes/appointments/AppointmentsService.java
index 3d2807671ed..99757bd60ce 100644
--- a/src/main/java/org/olat/course/nodes/appointments/AppointmentsService.java
+++ b/src/main/java/org/olat/course/nodes/appointments/AppointmentsService.java
@@ -109,6 +109,8 @@ public interface AppointmentsService {
 			Collection<? extends ParticipationRef> participationRefs, Identity rebookedBy, boolean autoConfirmation);
 
 	public void deleteParticipation(Participation participation);
+	
+	public void deleteParticipations(Collection<? extends ParticipationRef> participationRefs);
 
 	public Long getParticipationCount(ParticipationSearchParams params);
 
diff --git a/src/main/java/org/olat/course/nodes/appointments/manager/AppointmentsServiceImpl.java b/src/main/java/org/olat/course/nodes/appointments/manager/AppointmentsServiceImpl.java
index 71076624bd6..5f45a649f78 100644
--- a/src/main/java/org/olat/course/nodes/appointments/manager/AppointmentsServiceImpl.java
+++ b/src/main/java/org/olat/course/nodes/appointments/manager/AppointmentsServiceImpl.java
@@ -406,6 +406,8 @@ public class AppointmentsServiceImpl implements AppointmentsService {
 				ParticipationSearchParams currentParticipationParams = new ParticipationSearchParams();
 				currentParticipationParams.setTopic(reloadedAppointment.getTopic());
 				currentParticipationParams.setIdentity(identity);
+				currentParticipationParams.setFetchAppointments(true);
+				currentParticipationParams.setFetchIdentities(true);
 				List<Participation> loadParticipations = participationDao.loadParticipations(currentParticipationParams);
 				loadParticipations.forEach(currentParticipation -> deleteParticipation(currentParticipation));
 			}
@@ -439,6 +441,7 @@ public class AppointmentsServiceImpl implements AppointmentsService {
 		ParticipationSearchParams pParams = new ParticipationSearchParams();
 		pParams.setParticipations(participationRefs);
 		pParams.setFetchIdentities(true);
+		pParams.setFetchAppointments(true);
 		List<Participation> fromParticipations = participationDao.loadParticipations(pParams);
 		if (fromParticipations.isEmpty()) {
 			return ParticipationResult.NO_PARTICIPATIONS;
@@ -487,6 +490,16 @@ public class AppointmentsServiceImpl implements AppointmentsService {
 		
 		return ParticipationResult.of(participations);
 	}
+	
+	@Override
+	public void deleteParticipations(Collection<? extends ParticipationRef> participationRefs) {
+		ParticipationSearchParams participationParams = new ParticipationSearchParams();
+		participationParams.setParticipations(participationRefs);
+		participationParams.setFetchAppointments(true);
+		participationParams.setFetchIdentities(true);
+		List<Participation> loadParticipations = participationDao.loadParticipations(participationParams);
+		loadParticipations.forEach(participation -> deleteParticipation(participation));
+	}
 
 	@Override
 	public void deleteParticipation(Participation participation) {
diff --git a/src/main/java/org/olat/course/nodes/appointments/ui/AbstractRebookController.java b/src/main/java/org/olat/course/nodes/appointments/ui/AbstractParticipationRemoveController.java
similarity index 79%
rename from src/main/java/org/olat/course/nodes/appointments/ui/AbstractRebookController.java
rename to src/main/java/org/olat/course/nodes/appointments/ui/AbstractParticipationRemoveController.java
index 1ca1bc59884..f3a2bd9fdf4 100644
--- a/src/main/java/org/olat/course/nodes/appointments/ui/AbstractRebookController.java
+++ b/src/main/java/org/olat/course/nodes/appointments/ui/AbstractParticipationRemoveController.java
@@ -21,6 +21,7 @@ package org.olat.course.nodes.appointments.ui;
 
 import static java.util.Collections.emptyList;
 import static org.olat.core.gui.components.util.KeyValues.entry;
+import static org.olat.core.gui.translator.TranslatorHelper.translateAll;
 
 import java.text.DateFormat;
 import java.util.Arrays;
@@ -39,6 +40,7 @@ import org.olat.core.gui.components.form.flexible.elements.StaticTextElement;
 import org.olat.core.gui.components.form.flexible.impl.FormBasicController;
 import org.olat.core.gui.components.form.flexible.impl.FormEvent;
 import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer;
+import org.olat.core.gui.components.form.flexible.impl.elements.FormSubmit;
 import org.olat.core.gui.components.util.KeyValues;
 import org.olat.core.gui.control.Controller;
 import org.olat.core.gui.control.Event;
@@ -49,6 +51,8 @@ import org.olat.course.nodes.appointments.AppointmentSearchParams;
 import org.olat.course.nodes.appointments.AppointmentsService;
 import org.olat.course.nodes.appointments.Participation;
 import org.olat.course.nodes.appointments.ParticipationRef;
+import org.olat.course.nodes.appointments.ParticipationResult;
+import org.olat.course.nodes.appointments.ParticipationResult.Status;
 import org.olat.course.nodes.appointments.ParticipationSearchParams;
 import org.olat.user.UserManager;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -59,13 +63,18 @@ import org.springframework.beans.factory.annotation.Autowired;
  * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com
  *
  */
-public abstract class AbstractRebookController extends FormBasicController {
+public abstract class AbstractParticipationRemoveController extends FormBasicController {
 	
 	private static final String[] EMPTY = new String[0];
+	private static final String DELETE = "remove.user.delete";
+	private static final String CHANGE = "remove.user.rebook";
+	private static final String[] PARTICIPATIONS = { DELETE, CHANGE };
 
+	private SingleSelection changeEl;
 	private MultipleSelectionElement participationsEl;
 	private SingleSelection appointmentsEl;
 	private StaticTextElement noAppointmentsEl;
+	private FormSubmit submitButton;
 	
 	private final DateFormat dateFormat;
 	private final Appointment currentAppointment;
@@ -77,7 +86,7 @@ public abstract class AbstractRebookController extends FormBasicController {
 	@Autowired
 	private UserManager userManager;
 
-	public AbstractRebookController(UserRequest ureq, WindowControl wControl, Appointment appointment) {
+	public AbstractParticipationRemoveController(UserRequest ureq, WindowControl wControl, Appointment appointment) {
 		super(ureq, wControl);
 		this.currentAppointment = appointment;
 		
@@ -96,13 +105,11 @@ public abstract class AbstractRebookController extends FormBasicController {
 	
 	abstract boolean isAllParticipationsSelected();
 	
-	abstract boolean isShowAppointments();
-	
 	abstract String getSubmitI18nKey();
 	
 	abstract void initFormTop(FormItemContainer formLayout, Controller listener, UserRequest ureq);
 	
-	abstract void onAfterRebooking();
+	abstract void onAfterRemoving();
 	
 	AppointmentsService getAppointmentsService() {
 		return appointmentsService;
@@ -116,22 +123,31 @@ public abstract class AbstractRebookController extends FormBasicController {
 		return participations.size();
 	}
 	
+	boolean isRebook() {
+		return changeEl != null && changeEl.isOneSelected() && CHANGE.equals(changeEl.getSelectedKey());
+	}
+	
 	@Override
 	protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
 		initFormTop(formLayout, listener, ureq);
 		
-		participationsEl = uifactory.addCheckboxesVertical("rebook.participation", formLayout,  EMPTY, EMPTY, 2);
+		changeEl = uifactory.addRadiosHorizontal("remove.user.change", formLayout, PARTICIPATIONS,
+				translateAll(getTranslator(), PARTICIPATIONS));
+		changeEl.addActionListener(FormEvent.ONCHANGE);
+		changeEl.select(DELETE, true);
+		
+		participationsEl = uifactory.addCheckboxesVertical("remove.user.participation", formLayout,  EMPTY, EMPTY, 2);
 		participationsEl.addActionListener(FormEvent.ONCHANGE);
 				
-		appointmentsEl = uifactory.addRadiosVertical("rebook.appointments", "rebook.appointments", formLayout, EMPTY, EMPTY);
+		appointmentsEl = uifactory.addRadiosVertical("remove.user.appointments", "remove.user.appointments", formLayout, EMPTY, EMPTY);
 		
-		noAppointmentsEl = uifactory.addStaticTextElement("rebook.no.appointments",
-				translate("rebook.no.appointments.text"), formLayout);
+		noAppointmentsEl = uifactory.addStaticTextElement("remove.user.no.appointments",
+				translate("remove.user.no.appointments.text"), formLayout);
 		
 		FormLayoutContainer buttonCont = FormLayoutContainer.createButtonLayout("buttons", getTranslator());
 		buttonCont.setRootForm(mainForm);
 		formLayout.add(buttonCont);
-		uifactory.addFormSubmitButton(getSubmitI18nKey(), buttonCont);
+		submitButton = uifactory.addFormSubmitButton(getSubmitI18nKey(), buttonCont);
 		uifactory.addFormCancelButton("cancel", buttonCont, ureq, getWindowControl());
 		
 		updateUI();
@@ -139,6 +155,7 @@ public abstract class AbstractRebookController extends FormBasicController {
 	
 	void updateUI(){
 		if (participations.isEmpty()) {
+			changeEl.setVisible(false);
 			participationsEl.setVisible(false);
 			appointmentsEl.setVisible(false);
 			noAppointmentsEl.setVisible(false);
@@ -171,7 +188,8 @@ public abstract class AbstractRebookController extends FormBasicController {
 	}
 
 	private void updateAppointmentsUI() {
-		if (isShowAppointments()) {
+		boolean showAppointments = isRebook();
+		if (showAppointments) {
 			selectedAppointmentKey = appointmentsEl.isOneSelected()
 					? appointmentsEl.getSelectedKey()
 					: selectedAppointmentKey;
@@ -209,6 +227,8 @@ public abstract class AbstractRebookController extends FormBasicController {
 			appointmentsEl.setVisible(false);
 			noAppointmentsEl.setVisible(false);
 		}
+		
+		submitButton.setI18nKey(getSubmitI18nKey());
 	}
 	
 	private boolean hasFreeParticipations(Appointment appointment,
@@ -230,7 +250,9 @@ public abstract class AbstractRebookController extends FormBasicController {
 	
 	@Override
 	protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) {
-		if (source == participationsEl) {
+		if (source == changeEl) {
+			updateAppointmentsUI();
+		} else if (source == participationsEl) {
 			updateAppointmentsUI();
 		}
 		super.formInnerEvent(ureq, source, event);
@@ -266,17 +288,23 @@ public abstract class AbstractRebookController extends FormBasicController {
 
 	@Override
 	protected void formOK(UserRequest ureq) {
-		if (appointmentsEl.isVisible()) {
-			Collection<ParticipationRef> participationRefs = participationsEl.getSelectedKeys().stream()
-					.map(Long::valueOf)
-					.map(ParticipationRef::of)
-					.collect(Collectors.toList());
+		Collection<ParticipationRef> participationRefs = participationsEl.getSelectedKeys().stream()
+				.map(Long::valueOf)
+				.map(ParticipationRef::of)
+				.collect(Collectors.toList());
+		if (isRebook()) {
 			Long appointmentKey = Long.valueOf(appointmentsEl.getSelectedKey());
-			appointmentsService.rebookParticipations(AppointmentRef.of(appointmentKey), participationRefs,
+			ParticipationResult result = appointmentsService.rebookParticipations(AppointmentRef.of(appointmentKey), participationRefs,
 					getIdentity(), currentAppointment.getTopic().isAutoConfirmation());
+			if (result.getStatus() != Status.ok) {
+				showWarning("error.rebook");
+				return;
+			}
+		} else {
+			appointmentsService.deleteParticipations(participationRefs);
 		}
 		
-		onAfterRebooking();
+		onAfterRemoving();
 		
 		fireEvent(ureq, Event.DONE_EVENT);
 	}
diff --git a/src/main/java/org/olat/course/nodes/appointments/ui/AppointmentDataModel.java b/src/main/java/org/olat/course/nodes/appointments/ui/AppointmentDataModel.java
index 5090a8b90fc..a314a15edaa 100644
--- a/src/main/java/org/olat/course/nodes/appointments/ui/AppointmentDataModel.java
+++ b/src/main/java/org/olat/course/nodes/appointments/ui/AppointmentDataModel.java
@@ -118,7 +118,7 @@ implements SortableFlexiTableDataModel<AppointmentRow>, FilterableFlexiTableMode
 			case participants: return row.getParticipantsWrapper();
 			case select: return row.getSelectLink();
 			case addUser: return row.getAddUserLink();
-			case rebook: return row.getRebookLink();
+			case removeUser: return row.getRemoveLink();
 			case confirm: return row.getConfirmLink();
 			case delete: return row.getDeleteLink();
 			case edit: return row.getEditLink();
@@ -144,7 +144,7 @@ implements SortableFlexiTableDataModel<AppointmentRow>, FilterableFlexiTableMode
 		participants("participants"),
 		select("select"),
 		addUser("add.user"),
-		rebook("rebook"),
+		removeUser("remove.user"),
 		confirm("confirm"),
 		edit("edit"),
 		delete("delete");
diff --git a/src/main/java/org/olat/course/nodes/appointments/ui/AppointmentDeleteController.java b/src/main/java/org/olat/course/nodes/appointments/ui/AppointmentDeleteController.java
index 546bdf927fd..5681c3cb521 100644
--- a/src/main/java/org/olat/course/nodes/appointments/ui/AppointmentDeleteController.java
+++ b/src/main/java/org/olat/course/nodes/appointments/ui/AppointmentDeleteController.java
@@ -19,13 +19,8 @@
  */
 package org.olat.course.nodes.appointments.ui;
 
-import static org.olat.core.gui.translator.TranslatorHelper.translateAll;
-
 import org.olat.core.gui.UserRequest;
-import org.olat.core.gui.components.form.flexible.FormItem;
 import org.olat.core.gui.components.form.flexible.FormItemContainer;
-import org.olat.core.gui.components.form.flexible.elements.SingleSelection;
-import org.olat.core.gui.components.form.flexible.impl.FormEvent;
 import org.olat.core.gui.control.Controller;
 import org.olat.core.gui.control.WindowControl;
 import org.olat.course.nodes.appointments.Appointment;
@@ -37,13 +32,7 @@ import org.olat.course.nodes.appointments.Topic.Type;
  * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com
  *
  */
-public class AppointmentDeleteController extends AbstractRebookController {
-	
-	private static final String DELETE = "appointment.delete.no";
-	private static final String CHANGE = "appointment.delete.yes";
-	private static final String[] PARTICIPATIONS = { DELETE, CHANGE };
-	
-	private SingleSelection changeEl;
+public class AppointmentDeleteController extends AbstractParticipationRemoveController {
 
 	public AppointmentDeleteController(UserRequest ureq, WindowControl wControl, Appointment appointment) {
 		super(ureq, wControl, appointment);
@@ -64,11 +53,6 @@ public class AppointmentDeleteController extends AbstractRebookController {
 		return true;
 	}
 
-	@Override
-	boolean isShowAppointments() {
-		return changeEl != null && changeEl.isOneSelected() && CHANGE.equals(changeEl.getSelectedKey());
-	}
-
 	@Override
 	String getSubmitI18nKey() {
 		return "delete";
@@ -78,26 +62,13 @@ public class AppointmentDeleteController extends AbstractRebookController {
 	protected void initFormTop(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
 		if (Type.finding != getCurrentAppointment().getTopic().getType() && getNumParticipations() > 0) {
 			setFormDescription("appointment.delete.participations", new String[] { String.valueOf(getNumParticipations()) } );
-		
-			changeEl = uifactory.addRadiosHorizontal("appointment.delete.rebook", formLayout, PARTICIPATIONS,
-					translateAll(getTranslator(), PARTICIPATIONS));
-			changeEl.addActionListener(FormEvent.ONCHANGE);
-			changeEl.select(DELETE, true);
 		} else {
 			setFormDescription("confirm.appointment.delete");
 		}
 	}
 
 	@Override
-	protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) {
-		if (source == changeEl) {
-			super.updateUI();
-		}
-		super.formInnerEvent(ureq, source, event);
-	}
-
-	@Override
-	void onAfterRebooking() {
+	void onAfterRemoving() {
 		getAppointmentsService().deleteAppointment(getCurrentAppointment());
 	}
 	
diff --git a/src/main/java/org/olat/course/nodes/appointments/ui/AppointmentListController.java b/src/main/java/org/olat/course/nodes/appointments/ui/AppointmentListController.java
index 66e9a805ed4..3de3a6014b0 100644
--- a/src/main/java/org/olat/course/nodes/appointments/ui/AppointmentListController.java
+++ b/src/main/java/org/olat/course/nodes/appointments/ui/AppointmentListController.java
@@ -83,7 +83,7 @@ public abstract class AppointmentListController extends FormBasicController impl
 	
 	private static final String CMD_SELECT = "select";
 	private static final String CMD_ADD_USER = "add";
-	private static final String CMD_REBOOK = "rebook";
+	private static final String CMD_REMOVE = "remove";
 	private static final String CMD_CONFIRM = "confirm";
 	private static final String CMD_DELETE = "delete";
 	private static final String CMD_EDIT = "edit";
@@ -98,7 +98,7 @@ public abstract class AppointmentListController extends FormBasicController impl
 	private DialogBoxController confirmParticipationCrtl;
 	private AppointmentEditController appointmentEditCtrl;
 	private UserSearchController userSearchCtrl;
-	private RebookController rebookCtrl;
+	private ParticipationRemoveController removeCtrl;
 	private AppointmentDeleteController appointmentDeleteCtrl;
 
 	protected Topic topic;
@@ -199,9 +199,9 @@ public abstract class AppointmentListController extends FormBasicController impl
 			addUserModel.setExportable(false);
 			columnsModel.addFlexiColumnModel(addUserModel);
 			if (Type.finding != topic.getType()) {
-				DefaultFlexiColumnModel rebookModel = new DefaultFlexiColumnModel(AppointmentCols.rebook);
-				rebookModel.setExportable(false);
-				columnsModel.addFlexiColumnModel(rebookModel);
+				DefaultFlexiColumnModel removeModel = new DefaultFlexiColumnModel(AppointmentCols.removeUser);
+				removeModel.setExportable(false);
+				columnsModel.addFlexiColumnModel(removeModel);
 			}
 			DefaultFlexiColumnModel confirmModel = new DefaultFlexiColumnModel(AppointmentCols.confirm);
 			confirmModel.setExportable(false);
@@ -362,10 +362,10 @@ public abstract class AppointmentListController extends FormBasicController impl
 		row.setAddUserLink(link);
 	}
 
-	protected void forgeRebookLink(AppointmentRow row) {
-		FormLink link = uifactory.addFormLink("rebook_" + row.getKey(), CMD_REBOOK, "rebook", null, null, Link.LINK);
+	protected void forgeRemoveLink(AppointmentRow row) {
+		FormLink link = uifactory.addFormLink("remove_" + row.getKey(), CMD_REMOVE, "remove.user", null, null, Link.LINK);
 		link.setUserObject(row);
-		row.setRebookLink(link);
+		row.setRemoveLink(link);
 	}
 	
 	protected void forgeConfirmLink(AppointmentRow row, boolean confirmable) {
@@ -411,9 +411,9 @@ public abstract class AppointmentListController extends FormBasicController impl
 			} else if (CMD_ADD_USER.equals(cmd)) {
 				AppointmentRow row = (AppointmentRow)link.getUserObject();
 				doSelectUser(ureq, row.getAppointment());
-			} else if (CMD_REBOOK.equals(cmd)) {
+			} else if (CMD_REMOVE.equals(cmd)) {
 				AppointmentRow row = (AppointmentRow)link.getUserObject();
-				doRebook(ureq, row.getAppointment());
+				doRemove(ureq, row.getAppointment());
 			} 
 		}
 		super.formInnerEvent(ureq, source, event);
@@ -457,7 +457,7 @@ public abstract class AppointmentListController extends FormBasicController impl
 			}
 			cmc.deactivate();
 			cleanUp();
-		} else if (rebookCtrl == source) {
+		} else if (removeCtrl == source) {
 			if (event == Event.DONE_EVENT) {
 				updateModel();
 			}
@@ -473,12 +473,12 @@ public abstract class AppointmentListController extends FormBasicController impl
 		removeAsListenerAndDispose(appointmentDeleteCtrl);
 		removeAsListenerAndDispose(appointmentEditCtrl);
 		removeAsListenerAndDispose(userSearchCtrl);
-		removeAsListenerAndDispose(rebookCtrl);
+		removeAsListenerAndDispose(removeCtrl);
 		removeAsListenerAndDispose(cmc);
 		appointmentDeleteCtrl = null;
 		appointmentEditCtrl = null;
 		userSearchCtrl = null;
-		rebookCtrl = null;
+		removeCtrl = null;
 		cmc = null;
 	}
 
@@ -576,12 +576,12 @@ public abstract class AppointmentListController extends FormBasicController impl
 		updateModel();
 	}
 	
-	private void doRebook(UserRequest ureq, Appointment appointment) {
-		rebookCtrl = new RebookController(ureq, getWindowControl(), appointment);
-		listenTo(rebookCtrl);
+	private void doRemove(UserRequest ureq, Appointment appointment) {
+		removeCtrl = new ParticipationRemoveController(ureq, getWindowControl(), appointment);
+		listenTo(removeCtrl);
 
-		cmc = new CloseableModalController(getWindowControl(), "close", rebookCtrl.getInitialComponent(),
-				true, translate("rebook.title"));
+		cmc = new CloseableModalController(getWindowControl(), "close", removeCtrl.getInitialComponent(),
+				true, translate("remove.user.title"));
 		listenTo(cmc);
 		cmc.activate();
 	}
diff --git a/src/main/java/org/olat/course/nodes/appointments/ui/AppointmentListEditController.java b/src/main/java/org/olat/course/nodes/appointments/ui/AppointmentListEditController.java
index c015b3af4b4..cea2b295ffc 100644
--- a/src/main/java/org/olat/course/nodes/appointments/ui/AppointmentListEditController.java
+++ b/src/main/java/org/olat/course/nodes/appointments/ui/AppointmentListEditController.java
@@ -142,7 +142,7 @@ public class AppointmentListEditController extends AppointmentListController {
 				? false
 				: participations.size() > 0;
 		if (rebookable) {
-			forgeRebookLink(row);
+			forgeRemoveLink(row);
 		}
 		
 		if (Type.finding == topic.getType()) {
diff --git a/src/main/java/org/olat/course/nodes/appointments/ui/AppointmentRow.java b/src/main/java/org/olat/course/nodes/appointments/ui/AppointmentRow.java
index c631e21a463..c60b7f033e1 100644
--- a/src/main/java/org/olat/course/nodes/appointments/ui/AppointmentRow.java
+++ b/src/main/java/org/olat/course/nodes/appointments/ui/AppointmentRow.java
@@ -53,7 +53,7 @@ public class AppointmentRow {
 	private String selectionCSS;
 	private FormLink selectLink;
 	private FormLink addUserLink;
-	private FormLink rebookLink;
+	private FormLink removeLink;
 	private FormLink confirmLink;
 	private FormLink deleteLink;
 	private FormLink editLink;
@@ -222,16 +222,16 @@ public class AppointmentRow {
 		this.addUserLink = addUserLink;
 	}
 	
-	public FormLink getRebookLink() {
-		return rebookLink;
+	public FormLink getRemoveLink() {
+		return removeLink;
 	}
 
-	public String getRebookLinkName() {
-		return rebookLink != null? rebookLink.getName(): null;
+	public String getRemoveLinkName() {
+		return removeLink != null? removeLink.getName(): null;
 	}
 
-	public void setRebookLink(FormLink rebookLink) {
-		this.rebookLink = rebookLink;
+	public void setRemoveLink(FormLink removeLink) {
+		this.removeLink = removeLink;
 	}
 	
 	public FormLink getConfirmLink() {
diff --git a/src/main/java/org/olat/course/nodes/appointments/ui/RebookController.java b/src/main/java/org/olat/course/nodes/appointments/ui/ParticipationRemoveController.java
similarity index 86%
rename from src/main/java/org/olat/course/nodes/appointments/ui/RebookController.java
rename to src/main/java/org/olat/course/nodes/appointments/ui/ParticipationRemoveController.java
index 172bd00f8e1..d424d71bb23 100644
--- a/src/main/java/org/olat/course/nodes/appointments/ui/RebookController.java
+++ b/src/main/java/org/olat/course/nodes/appointments/ui/ParticipationRemoveController.java
@@ -31,9 +31,9 @@ import org.olat.course.nodes.appointments.Appointment;
  * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com
  *
  */
-public class RebookController extends AbstractRebookController {
+public class ParticipationRemoveController extends AbstractParticipationRemoveController {
 
-	public RebookController(UserRequest ureq, WindowControl wControl, Appointment appointment) {
+	public ParticipationRemoveController(UserRequest ureq, WindowControl wControl, Appointment appointment) {
 		super(ureq, wControl, appointment);
 	}
 
@@ -47,11 +47,6 @@ public class RebookController extends AbstractRebookController {
 		return false;
 	}
 
-	@Override
-	boolean isShowAppointments() {
-		return true;
-	}
-
 	@Override
 	boolean isAllParticipationsSelected() {
 		return false;
@@ -59,7 +54,7 @@ public class RebookController extends AbstractRebookController {
 
 	@Override
 	String getSubmitI18nKey() {
-		return "rebook";
+		return isRebook()? "rebook": "delete";
 	}
 
 	@Override
@@ -68,7 +63,7 @@ public class RebookController extends AbstractRebookController {
 	}
 
 	@Override
-	void onAfterRebooking() {
+	void onAfterRemoving() {
 		//
 	}
 
diff --git a/src/main/java/org/olat/course/nodes/appointments/ui/_content/appointment_row.html b/src/main/java/org/olat/course/nodes/appointments/ui/_content/appointment_row.html
index f57e277b6e0..e1690233631 100644
--- a/src/main/java/org/olat/course/nodes/appointments/ui/_content/appointment_row.html
+++ b/src/main/java/org/olat/course/nodes/appointments/ui/_content/appointment_row.html
@@ -71,19 +71,19 @@
 			<div class="o_buttons">
 				<div class="o_button_group o_button_group_right">
 					#if($row.addUserLinkName)
-						$r.render($row.addUserLinkName)
+						&nbsp;$r.render($row.addUserLinkName)
 					#end
-					#if($row.rebookLinkName)
-						$r.render($row.rebookLinkName)
+					#if($row.removeLinkName)
+						&nbsp;$r.render($row.removeLinkName)
 					#end
 					#if($row.confirmLinkName)
-						$r.render($row.confirmLinkName)
+						&nbsp;$r.render($row.confirmLinkName)
 					#end
 					#if($row.deleteLinkName)
-						$r.render($row.deleteLinkName)
+						&nbsp;$r.render($row.deleteLinkName)
 					#end
 					#if($row.editLinkName)
-						$r.render($row.editLinkName)
+						&nbsp;$r.render($row.editLinkName)
 					#end
 				</div>
 			</div>
diff --git a/src/main/java/org/olat/course/nodes/appointments/ui/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/course/nodes/appointments/ui/_i18n/LocalStrings_de.properties
index dc79622e58f..11bc8ef52e7 100644
--- a/src/main/java/org/olat/course/nodes/appointments/ui/_i18n/LocalStrings_de.properties
+++ b/src/main/java/org/olat/course/nodes/appointments/ui/_i18n/LocalStrings_de.properties
@@ -5,11 +5,8 @@ add.topic=Termine hinzuf\u00fcgen
 add.topic.title=$:\add.topic
 add.user=Benutzer hinzuf\u00fcgen
 add.user.title=$:\add.user
-appointment.details=Details
-appointment.delete.rebook=Teilnehmer umbuchen?
-appointment.delete.no=Nein
 appointment.delete.participations=Es haben {0} Teilnehmer diesen Termin gew\u00e4hlt. Sollen diese Teilnehmer umgebucht werden?
-appointment.delete.yes=Ja
+appointment.details=Details
 appointment.end=Ende
 appointment.id=ID
 appointment.init.value=Dieser Initialwert wird bei allen Terminen eingetragen. Er kann sp\u00e4ter in jedem Termin separat bearbeitet werden.
@@ -69,6 +66,7 @@ error.config.not.changeable=Die Konfiguration kann nicht mehr ge\u00e4ndert werd
 error.first.after.start=Der letzte Termin darf nicht vor dem ersten Termin liegen.
 error.not.as.many.participations.left=Es k\u00f6nnen nicht mehr so viele Benutzer zu diesem Termin hinzugef\u00fcgt werden.
 error.positiv.number=Geben Sie eine positive Ganzzahl ein.
+error.rebook=Das Umbuchen der Teilnehmer ist fehlgeschlagen.
 error.select.appointment=Sie m\u00fcssen einen Termin ausw\u00e4hlen.
 error.select.participant=Sie m\u00fcssen einen Teilnehmer ausw\u00e4hlen.
 error.start.after.end=Das Ende darf nicht vor dem Start liegen.
@@ -124,11 +122,15 @@ participations.selected.many.one={0} Teilnehmer haben einen Termin ausgew\u00e4h
 participations.selected.one.many=Ein Teilnehmer hat {0} Termine ausgew\u00e4hlt.
 participations.selected.one.one=Ein Teilnehmer hat einen Termin ausgew\u00e4hlt.
 rebook=Umbuchen
-rebook.appointments=Termin
-rebook.participation=Teilnehmer
-rebook.no.appointments=$\:rebook.appointments
-rebook.no.appointments.text=<i>Es gibt keine Termine mit gen\u00fcgend freien Pl\u00e4tzen.</i>
-rebook.title=Teilnehmer umbuchen
+remove.user=Benutzer entfernen
+remove.user.appointments=Termin
+remove.user.change=Entfernen
+remove.user.delete=L\u00f6schen
+remove.user.no.appointments.text=<i>Es gibt keine Termine mit gen\u00fcgend freien Pl\u00e4tzen.</i>
+remove.user.no.appointments=$\:remove.user.appointments
+remove.user.participation=Teilnehmer
+remove.user.rebook=Umbuchen
+remove.user.title=Teilnehmer entfernen
 role.coach=Betreuer
 save.back=Speichern und zur\u00fcck
 select=Ausw\u00e4hlen
diff --git a/src/main/java/org/olat/course/nodes/appointments/ui/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/course/nodes/appointments/ui/_i18n/LocalStrings_en.properties
index 3468e586b40..08635ab375b 100644
--- a/src/main/java/org/olat/course/nodes/appointments/ui/_i18n/LocalStrings_en.properties
+++ b/src/main/java/org/olat/course/nodes/appointments/ui/_i18n/LocalStrings_en.properties
@@ -5,11 +5,8 @@ add.topic=Add appointments
 add.topic.title=$:\add.topic
 add.user=Add user
 add.user.title=$:\add.user
-appointment.details=Details
-appointment.delete.rebook=Rebook appointment?
-appointment.delete.no=No
 appointment.delete.participations={0} participants have selected this appointment. Should these participants be rebooked?
-appointment.delete.yes=Yes
+appointment.details=Details
 appointment.end=End
 appointment.id=ID
 appointment.init.value=This initial value is set for all appointments. It can be edited separately in each appointment later.
@@ -68,6 +65,7 @@ error.config.not.changeable=The configuration can no longer be changed. It has b
 error.first.after.start=The last appointment must not be before the first appointment.
 error.not.as.many.participations.left=Not so many users can be added to this appointment.
 error.positiv.number=It has to be a positive integer.
+error.rebook=The rebooking of the participants has failed.
 error.select.appointment=You have to select an appointment.
 error.select.participant=You have to select a participant.
 error.start.after.end=The end date must not be before the start date.
@@ -123,11 +121,15 @@ participations.selected.many.one={0} participants have selected one appointment.
 participations.selected.one.many=One participant has selected {0} appointments.
 participations.selected.one.one=One participants has selected one appointment.
 rebook=Rebook
-rebook.appointments=Appointment
-rebook.participation=Participants
-rebook.no.appointments=$\:rebook.appointments
-rebook.no.appointments.text=<i>There are no appointments with enough free places.</i>
-rebook.title=Rebook participants
+remove.user=Remove user
+remove.user.appointments=Appointment
+remove.user.change=Remove
+remove.user.delete=Delete
+remove.user.no.appointments.text=<i>There are no appointments with enough free places.</i>
+remove.user.no.appointments=$\:remove.user.appointments
+remove.user.participation=Participants
+remove.user.rebook=Rebook
+remove.user.title=Remove user
 role.coach=Coach
 save.back=Save and back
 select=Select
-- 
GitLab