From b879ffd9826a36ba599ba987fed1c8ca49d5513e Mon Sep 17 00:00:00 2001
From: srosse <none@none>
Date: Thu, 22 Jun 2017 16:02:10 +0200
Subject: [PATCH] OO-2837: add the possibility to lazy load tabs in the tabbed
 pane and use it for business group administration and user overview

---
 .../olat/admin/user/UserAdminController.java  | 183 ++++++++++--------
 .../gui/components/tabbedpane/TabCreator.java |  35 ++++
 .../gui/components/tabbedpane/TabbedPane.java |  80 ++++++--
 .../tabbedpane/TabbedPaneRenderer.java        |  14 +-
 ...ActivateableTabbableDefaultController.java |   2 +-
 .../ui/edit/BusinessGroupEditController.java  |  95 +++++----
 .../olat/modules/wiki/WikiMainController.java |  24 +--
 7 files changed, 277 insertions(+), 156 deletions(-)
 create mode 100644 src/main/java/org/olat/core/gui/components/tabbedpane/TabCreator.java

diff --git a/src/main/java/org/olat/admin/user/UserAdminController.java b/src/main/java/org/olat/admin/user/UserAdminController.java
index b75bc99ac9d..be4c41ca2e7 100644
--- a/src/main/java/org/olat/admin/user/UserAdminController.java
+++ b/src/main/java/org/olat/admin/user/UserAdminController.java
@@ -43,6 +43,7 @@ import org.olat.core.gui.components.link.Link;
 import org.olat.core.gui.components.link.LinkFactory;
 import org.olat.core.gui.components.panel.Panel;
 import org.olat.core.gui.components.stack.BreadcrumbedStackedPanel;
+import org.olat.core.gui.components.tabbedpane.TabCreator;
 import org.olat.core.gui.components.tabbedpane.TabbedPane;
 import org.olat.core.gui.components.velocity.VelocityContainer;
 import org.olat.core.gui.control.Controller;
@@ -106,7 +107,7 @@ public class UserAdminController extends BasicController implements Activateable
 
 	// controllers used in tabbed pane
 	private TabbedPane userTabP;
-	private Controller prefsCtr, propertiesCtr, pwdCtr, quotaCtr, policiesCtr, rolesCtr, userShortDescrCtr;
+	private Controller prefsCtr, propertiesCtr, pwdCtr, quotaCtr, rolesCtr, userShortDescrCtr;
 	private DisplayPortraitController portraitCtr;
 	private UserAuthenticationsEditorController authenticationsCtr;
 	private Link backLink;
@@ -150,6 +151,7 @@ public class UserAdminController extends BasicController implements Activateable
 			myContent = createVelocityContainer("udispatcher");
 			backLink = LinkFactory.createLinkBack(myContent, this);
 			userShortDescrCtr = new UserShortDescription(ureq, wControl, identity);
+			listenTo(userShortDescrCtr);
 			myContent.put("userShortDescription", userShortDescrCtr.getInitialComponent());
 			
 			setBackButtonEnabled(true); // default
@@ -175,8 +177,6 @@ public class UserAdminController extends BasicController implements Activateable
 				List<ContextEntry> subEntries = entries.subList(2, entries.size());
 				userTabP.activate(ureq, subEntries, state);
 			}
-		} else if (userTabP != null) {
-			userTabP.setSelectedPane(translate(entryPoint));
 		}
 	}
 
@@ -210,12 +210,14 @@ public class UserAdminController extends BasicController implements Activateable
 			if (event.getCommand().equals("PropFound")){
 				PropFoundEvent foundEvent = (PropFoundEvent) event;
 				Property myfoundProperty = foundEvent.getProperty();				
-				this.showInfo(NLS_FOUND_PROPERTY,myfoundProperty.getKey().toString());								
+				showInfo(NLS_FOUND_PROPERTY, myfoundProperty.getKey().toString());								
 			}
 		} else if (source == pwdCtr) {
 			if (event == Event.DONE_EVENT) {
 				// rebuild authentication tab, could be wrong now
-				if (authenticationsCtr != null) authenticationsCtr.rebuildAuthenticationsTableDataModel();
+				if (authenticationsCtr != null) {
+					authenticationsCtr.rebuildAuthenticationsTableDataModel();
+				}
 			}
 		} else if (source == userProfileCtr){
 			if (event == Event.DONE_EVENT){
@@ -302,72 +304,131 @@ public class UserAdminController extends BasicController implements Activateable
 		listenTo(userProfileCtr);
 		userTabP.addTab(translate(NLS_EDIT_UPROFILE), userProfileCtr.getInitialComponent());
 		
-		prefsCtr = new ChangePrefsController(ureq, getWindowControl(), identity);
-		userTabP.addTab(translate(NLS_EDIT_UPREFS), prefsCtr.getInitialComponent());
+		userTabP.addTab(translate(NLS_EDIT_UPREFS), new TabCreator() {
+			@Override
+			public Component create(UserRequest uureq) {
+				prefsCtr = new ChangePrefsController(uureq, getWindowControl(), identity);
+				listenTo(prefsCtr);
+				return prefsCtr.getInitialComponent();
+			}
+		});
 
 		if (isPasswordChangesAllowed(identity)) {
-			pwdCtr =  new UserChangePasswordController(ureq, getWindowControl(), identity);				
-			listenTo(pwdCtr); // listen when finished to update authentications model
-			userTabP.addTab(translate(NLS_EDIT_UPWD), pwdCtr.getInitialComponent());
+			userTabP.addTab(translate(NLS_EDIT_UPWD), new TabCreator() {
+				@Override
+				public Component create(UserRequest uureq) {
+					pwdCtr =  new UserChangePasswordController(uureq, getWindowControl(), identity);				
+					listenTo(pwdCtr); 
+					return pwdCtr.getInitialComponent();
+				}
+			});
 		}
 		
 		Boolean canAuth = BaseSecurityModule.USERMANAGER_ACCESS_TO_AUTH;
 		if (canAuth.booleanValue() || isOlatAdmin) {
-			authenticationsCtr =  new UserAuthenticationsEditorController(ureq, getWindowControl(), identity);
-			userTabP.addTab(translate(NLS_EDIT_UAUTH), authenticationsCtr.getInitialComponent());
+			userTabP.addTab(translate(NLS_EDIT_UAUTH), new TabCreator() {
+				@Override
+				public Component create(UserRequest uureq) {
+					authenticationsCtr =  new UserAuthenticationsEditorController(uureq, getWindowControl(), identity);
+					listenTo(authenticationsCtr);
+					return authenticationsCtr.getInitialComponent();
+				}
+			});
 		}
 		
 		Boolean canProp = BaseSecurityModule.USERMANAGER_ACCESS_TO_PROP;
 		if (canProp.booleanValue() || isOlatAdmin) {
-			propertiesCtr = new UserPropertiesController(ureq, getWindowControl(), identity);			
-			this.listenTo(propertiesCtr);
-			userTabP.addTab(translate(NLS_EDIT_UPROP), propertiesCtr.getInitialComponent());
+			userTabP.addTab(translate(NLS_EDIT_UPROP), new TabCreator() {
+				@Override
+				public Component create(UserRequest uureq) {
+					propertiesCtr = new UserPropertiesController(uureq, getWindowControl(), identity);			
+					listenTo(propertiesCtr);
+					return propertiesCtr.getInitialComponent();
+				}
+			});
 		}
 		
 		Boolean canStartGroups = BaseSecurityModule.USERMANAGER_CAN_START_GROUPS;
-		grpCtr = new GroupOverviewController(ureq, getWindowControl(), identity, canStartGroups);
-		listenTo(grpCtr);
-		userTabP.addTab(translate(NLS_VIEW_GROUPS), grpCtr.getInitialComponent());
-		
-		courseCtr = new CourseOverviewController(ureq, getWindowControl(), identity);
-		listenTo(courseCtr);
-		userTabP.addTab(translate(NLS_VIEW_COURSES), courseCtr.getInitialComponent());
+		userTabP.addTab(translate(NLS_VIEW_GROUPS), new TabCreator() {
+			@Override
+			public Component create(UserRequest uureq) {
+				grpCtr = new GroupOverviewController(uureq, getWindowControl(), identity, canStartGroups);
+				listenTo(grpCtr);
+				return grpCtr.getInitialComponent();
+			}
+		});
+
+		userTabP.addTab(translate(NLS_VIEW_COURSES), new TabCreator() {
+			@Override
+			public Component create(UserRequest uureq) {
+				courseCtr = new CourseOverviewController(uureq, getWindowControl(), identity);
+				listenTo(courseCtr);
+				return courseCtr.getInitialComponent();
+			}
+		});
 		
-		if (isOlatAdmin) {
-			efficicencyCtrl = new CertificateAndEfficiencyStatementListController(ureq, getWindowControl(), identity, true);
-			listenTo(efficicencyCtrl);
-			BreadcrumbedStackedPanel stackPanel = new BreadcrumbedStackedPanel("statements", getTranslator(), efficicencyCtrl);
-			stackPanel.pushController(translate(NLS_VIEW_EFF_STATEMENTS), efficicencyCtrl);
-			efficicencyCtrl.setBreadcrumbPanel(stackPanel);
-			stackPanel.setInvisibleCrumb(1);
-			userTabP.addTab(translate(NLS_VIEW_EFF_STATEMENTS), stackPanel);
+		if (isOlatAdmin) {	
+			userTabP.addTab(translate(NLS_VIEW_EFF_STATEMENTS), new TabCreator() {
+				@Override
+				public Component create(UserRequest uureq) {
+					efficicencyCtrl = new CertificateAndEfficiencyStatementListController(uureq, getWindowControl(), identity, true);
+					listenTo(efficicencyCtrl);
+					BreadcrumbedStackedPanel stackPanel = new BreadcrumbedStackedPanel("statements", getTranslator(), efficicencyCtrl);
+					stackPanel.pushController(translate(NLS_VIEW_EFF_STATEMENTS), efficicencyCtrl);
+					efficicencyCtrl.setBreadcrumbPanel(stackPanel);
+					stackPanel.setInvisibleCrumb(1);
+					return stackPanel;
+				}
+			});
 		}
 
 		Boolean canSubscriptions = BaseSecurityModule.USERMANAGER_CAN_MODIFY_SUBSCRIPTIONS;
 		if (canSubscriptions.booleanValue() || isOlatAdmin) {
-			Controller subscriptionsCtr = new NotificationSubscriptionController(ureq, getWindowControl(), identity, true);
-			listenTo(subscriptionsCtr); // auto-dispose
-			userTabP.addTab(translate(NLS_VIEW_SUBSCRIPTIONS), subscriptionsCtr.getInitialComponent());			
+			userTabP.addTab(translate(NLS_VIEW_SUBSCRIPTIONS), new TabCreator() {
+				@Override
+				public Component create(UserRequest uureq) {
+					Controller subscriptionsCtr = new NotificationSubscriptionController(uureq, getWindowControl(), identity, true);
+					listenTo(subscriptionsCtr);
+					return subscriptionsCtr.getInitialComponent();
+				}
+				
+			});			
 		}
 		
-		rolesCtr = new SystemRolesAndRightsController(getWindowControl(), ureq, identity);
-		userTabP.addTab(translate(NLS_EDIT_UROLES), rolesCtr.getInitialComponent());
+		userTabP.addTab(translate(NLS_EDIT_UROLES), new TabCreator() {
+			@Override
+			public Component create(UserRequest uureq) {
+				rolesCtr = new SystemRolesAndRightsController(getWindowControl(), uureq, identity);
+				listenTo(rolesCtr);
+				return rolesCtr.getInitialComponent();
+			}
+		});
 		
 		Boolean canQuota = BaseSecurityModule.USERMANAGER_ACCESS_TO_QUOTA;
 		if (canQuota.booleanValue() || isOlatAdmin) {
-			String relPath = FolderConfig.getUserHomes() + "/" + identity.getName();
-			quotaCtr = QuotaManager.getInstance().getQuotaEditorInstance(ureq, getWindowControl(), relPath, false);
-			userTabP.addTab(translate(NLS_EDIT_UQUOTA), quotaCtr.getInitialComponent());
+			userTabP.addTab(translate(NLS_EDIT_UQUOTA), new TabCreator() {
+				@Override
+				public Component create(UserRequest uureq) {
+					String relPath = FolderConfig.getUserHomes() + "/" + identity.getName();
+					quotaCtr = QuotaManager.getInstance().getQuotaEditorInstance(uureq, getWindowControl(), relPath, false);
+					return quotaCtr.getInitialComponent();
+				}
+			});
 		}
 		
 		if(lectureModule.isEnabled()) {
-			lecturesCtrl = new ParticipantLecturesOverviewController(ureq, getWindowControl(), identity, true, true, true);
-			listenTo(lecturesCtrl);
-			BreadcrumbedStackedPanel stackPanel = new BreadcrumbedStackedPanel("lectures", getTranslator(), lecturesCtrl);
-			stackPanel.pushController(translate(NLS_VIEW_LECTURES), lecturesCtrl);
-			lecturesCtrl.setBreadcrumbPanel(stackPanel);
-			stackPanel.setInvisibleCrumb(1);
-			userTabP.addTab(translate(NLS_VIEW_LECTURES), stackPanel);
+			userTabP.addTab(translate(NLS_VIEW_LECTURES), new TabCreator() {
+				@Override
+				public Component create(UserRequest uureq) {
+					lecturesCtrl = new ParticipantLecturesOverviewController(uureq, getWindowControl(), identity, true, true, true);
+					listenTo(lecturesCtrl);
+					BreadcrumbedStackedPanel stackPanel = new BreadcrumbedStackedPanel("lectures", getTranslator(), lecturesCtrl);
+					stackPanel.pushController(translate(NLS_VIEW_LECTURES), lecturesCtrl);
+					lecturesCtrl.setBreadcrumbPanel(stackPanel);
+					stackPanel.setInvisibleCrumb(1);
+					return stackPanel;
+				}
+			});
 		}
 		
 		// now push to velocity
@@ -414,36 +475,6 @@ public class UserAdminController extends BasicController implements Activateable
 	 */
 	@Override
 	protected void doDispose() {
-		//child controllers registered with listenTo get disposed in BasicController
-		if (quotaCtr != null) {
-			quotaCtr.dispose();
-			quotaCtr = null;
-		}
-		if (authenticationsCtr != null) {
-			authenticationsCtr.dispose();
-			authenticationsCtr = null;
-		}
-		if (prefsCtr != null) {
-			prefsCtr.dispose();
-			prefsCtr = null;
-		}			
-		if (policiesCtr != null) {
-			policiesCtr.dispose();
-			policiesCtr = null;
-		}
-		if (rolesCtr != null) {
-			rolesCtr.dispose();
-			rolesCtr = null;
-		}
-		if (portraitCtr != null) {
-			portraitCtr.dispose();
-			portraitCtr = null;
-		}
-		if (userShortDescrCtr!=null) {
-			userShortDescrCtr.dispose();
-			userShortDescrCtr = null;
-		}
+		//
 	}
-
-
 }
\ No newline at end of file
diff --git a/src/main/java/org/olat/core/gui/components/tabbedpane/TabCreator.java b/src/main/java/org/olat/core/gui/components/tabbedpane/TabCreator.java
new file mode 100644
index 00000000000..334add4fd93
--- /dev/null
+++ b/src/main/java/org/olat/core/gui/components/tabbedpane/TabCreator.java
@@ -0,0 +1,35 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.core.gui.components.tabbedpane;
+
+import org.olat.core.gui.UserRequest;
+import org.olat.core.gui.components.Component;
+
+/**
+ * 
+ * Initial date: 22 juin 2017<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public interface TabCreator {
+	
+	public Component create(UserRequest ureq);
+
+}
diff --git a/src/main/java/org/olat/core/gui/components/tabbedpane/TabbedPane.java b/src/main/java/org/olat/core/gui/components/tabbedpane/TabbedPane.java
index 8c08d34ca6f..edce7e12849 100644
--- a/src/main/java/org/olat/core/gui/components/tabbedpane/TabbedPane.java
+++ b/src/main/java/org/olat/core/gui/components/tabbedpane/TabbedPane.java
@@ -96,10 +96,10 @@ public class TabbedPane extends Container implements Activateable2 {
 	 */
 	private void dispatchRequest(UserRequest ureq, int newTaid) {
 		if (isEnabled(newTaid) && newTaid >= 0 && newTaid < getTabCount()) {
-			Component oldSelComp = getTabAt(selectedPane);	
-			setSelectedPane(newTaid);
-			Component newSelComp = getTabAt(selectedPane);
-			fireEvent(ureq, new TabbedPaneChangedEvent(oldSelComp, newSelComp));
+			TabPane pane = getTabPaneAt(selectedPane);	
+			setSelectedPane(ureq, newTaid);
+			TabPane newPane = getTabPaneAt(selectedPane);
+			fireEvent(ureq, new TabbedPaneChangedEvent(pane.getComponent(), newPane.getComponent()));
 		}
 	}
 	
@@ -108,16 +108,21 @@ public class TabbedPane extends Container implements Activateable2 {
 	 * 
 	 * @param selectedPane The selectedPane to set
 	 */
-	public void setSelectedPane(int newSelectedPane) {
+	public void setSelectedPane(UserRequest ureq, int newSelectedPane) {
 		// get old selected component and remove it from render tree
-		Component oldSelComp = getTabAt(selectedPane);
-		remove(oldSelComp);
+		TabPane oldSelectedTab = getTabPaneAt(selectedPane);
+		if(oldSelectedTab.getComponent() != null) {
+			remove(oldSelectedTab.getComponent());
+		}
 		
 		// activate new
 		selectedPane = newSelectedPane;
-		Component newSelComp = getTabAt(newSelectedPane);
-		super.put("atp", newSelComp); 
-		//setDirty(true); not needed since: line above marks this container automatically dirty
+		TabPane newSelectedTab = getTabPaneAt(newSelectedPane);
+		Component component = newSelectedTab.getComponent();
+		if(component == null && newSelectedTab.getTabCreator() != null) {
+			component = newSelectedTab.createComponent(ureq);
+		}
+		super.put("atp", component); 
 	}
 	
 	public OLATResourceable getTabResource() {
@@ -153,6 +158,16 @@ public class TabbedPane extends Container implements Activateable2 {
 		return tabPanes.size() - 1;
 	}
 	
+	public int addTab(String displayName, TabCreator creator) {
+		TabPane tab = new TabPane(displayName, creator);
+		tabPanes.add(tab);
+		if (selectedPane == -1) {
+			selectedPane = 0; // if no pane has been selected, select the first one
+			super.put("atp", tab.getComponent()); 
+		}
+		return tabPanes.size() - 1;
+	}
+	
 	public boolean containsTab(Component component) {
 		boolean found = false;
 		for(int i=tabPanes.size(); i-->0; ) {
@@ -210,16 +225,22 @@ public class TabbedPane extends Container implements Activateable2 {
 		if(index >= 0 && index < tabPanes.size()) {
 			tabPanes.remove(index);
 			if(selectedPane == index) {
-				setSelectedPane(0);
+				for(int i=0; i<tabPanes.size(); i++) {
+					if(tabPanes.get(i).getComponent() != null) {
+						setSelectedPane(null, i);
+						break;
+					}
+				}
 			}
-			setDirty(true);
 		}
 	}
 
 	public void removeAll() {
 		if (selectedPane != -1) {
-			Component oldSelComp = getTabAt(selectedPane);
-			remove(oldSelComp);
+			TabPane selected = getTabPaneAt(selectedPane);
+			if(selected != null && selected.getComponent() != null) {
+				remove(selected.getComponent());
+			}
 		}
 		tabPanes.clear();
 		selectedPane = -1;
@@ -227,12 +248,19 @@ public class TabbedPane extends Container implements Activateable2 {
 	}
 	
 	/**
-	 * @param position
-	 * @return
+	 * The method doessn't instantiate any component via
+	 * the TabCreaator interface.
+	 * 
+	 * @param position The index of the tab
+	 * @return A component
 	 */
 	protected Component getTabAt(int position) {
 		return tabPanes.get(position).getComponent();
 	}
+	
+	protected TabPane getTabPaneAt(int position) {
+		return tabPanes.get(position);
+	}
 
 	/**
 	 * @param position
@@ -276,11 +304,11 @@ public class TabbedPane extends Container implements Activateable2 {
 	 * @deprecated
 	 * @param displayName
 	 */
-	public void setSelectedPane(String displayName) {
+	public void setSelectedPane(UserRequest ureq, String displayName) {
 		if (displayName == null) return;
 		int pos = indexOfTab(displayName);
 		if (pos > -1) {
-			setSelectedPane(pos);
+			setSelectedPane(ureq, pos);
 		}
 	}
 
@@ -341,6 +369,7 @@ public class TabbedPane extends Container implements Activateable2 {
 		private final String displayName;
 		private Component component;
 		private Controller controller;
+		private TabCreator creator;
 		
 		public TabPane(String displayName, Component component) {
 			this.displayName = displayName;
@@ -355,6 +384,12 @@ public class TabbedPane extends Container implements Activateable2 {
 			this.enabled = true;
 		}
 		
+		public TabPane(String displayName, TabCreator creator) {
+			this.displayName = displayName;
+			this.creator = creator;
+			this.enabled = true;
+		}
+		
 		public boolean isEnabled() {
 			return enabled;
 		}
@@ -367,6 +402,15 @@ public class TabbedPane extends Container implements Activateable2 {
 			return displayName;
 		}
 		
+		public TabCreator getTabCreator() {
+			return creator;
+		}
+		
+		public Component createComponent(UserRequest ureq) {
+			component = creator.create(ureq);
+			return component;
+		}
+		
 		public Controller getController() {
 			return controller;
 		}
diff --git a/src/main/java/org/olat/core/gui/components/tabbedpane/TabbedPaneRenderer.java b/src/main/java/org/olat/core/gui/components/tabbedpane/TabbedPaneRenderer.java
index 4af9a87fbf8..fd5f199371f 100644
--- a/src/main/java/org/olat/core/gui/components/tabbedpane/TabbedPaneRenderer.java
+++ b/src/main/java/org/olat/core/gui/components/tabbedpane/TabbedPaneRenderer.java
@@ -100,9 +100,11 @@ public class TabbedPaneRenderer implements ComponentRenderer {
 		TabbedPane tp = (TabbedPane)source;
 		int cnt = tp.getTabCount();
 		if (cnt > 0 && tp.getSelectedPane() < cnt) {
-			Component toRender = tp.getTabAt(tp.getSelectedPane());
+			Component paneToRender = tp.getTabAt(tp.getSelectedPane());
 			// delegate header rendering to the selected pane
-			renderer.renderHeaderIncludes(sb, toRender, rstate);
+			if(paneToRender != null) {
+				renderer.renderHeaderIncludes(sb, paneToRender, rstate);
+			}
 		}
 	}
 
@@ -115,11 +117,11 @@ public class TabbedPaneRenderer implements ComponentRenderer {
 		TabbedPane tp = (TabbedPane)source;
 		int cnt = tp.getTabCount();
 		if (cnt > 0 && tp.getSelectedPane() < cnt) {
-			Component toRender = tp.getTabAt(tp.getSelectedPane());
+			Component paneToRender = tp.getTabAt(tp.getSelectedPane());
 			//	delegate js rendering to the selected pane
-			renderer.renderBodyOnLoadJSFunctionCall(sb, toRender, rstate);
+			if(paneToRender != null) {
+				renderer.renderBodyOnLoadJSFunctionCall(sb, paneToRender, rstate);
+			}
 		}
 	}
-	
-
 }
diff --git a/src/main/java/org/olat/core/gui/control/generic/tabbable/ActivateableTabbableDefaultController.java b/src/main/java/org/olat/core/gui/control/generic/tabbable/ActivateableTabbableDefaultController.java
index cdb72f7b96a..e46ffe38e11 100644
--- a/src/main/java/org/olat/core/gui/control/generic/tabbable/ActivateableTabbableDefaultController.java
+++ b/src/main/java/org/olat/core/gui/control/generic/tabbable/ActivateableTabbableDefaultController.java
@@ -89,7 +89,7 @@ public abstract class ActivateableTabbableDefaultController extends TabbableDefa
 		}
 		if (foundKey) {
 			// it is a tab which we know
-			myTabbedPane.setSelectedPane(translate(tabKey));
+			myTabbedPane.setSelectedPane(ureq, translate(tabKey));
 		} else {
 			// it may be a tab of our children
 			ActivateableTabbableDefaultController[] children = getChildren();
diff --git a/src/main/java/org/olat/group/ui/edit/BusinessGroupEditController.java b/src/main/java/org/olat/group/ui/edit/BusinessGroupEditController.java
index 83b1eab137b..7f2296d8f3b 100644
--- a/src/main/java/org/olat/group/ui/edit/BusinessGroupEditController.java
+++ b/src/main/java/org/olat/group/ui/edit/BusinessGroupEditController.java
@@ -32,6 +32,7 @@ import org.olat.core.gui.UserRequest;
 import org.olat.core.gui.components.Component;
 import org.olat.core.gui.components.panel.Panel;
 import org.olat.core.gui.components.stack.TooledStackedPanel;
+import org.olat.core.gui.components.tabbedpane.TabCreator;
 import org.olat.core.gui.components.tabbedpane.TabbedPane;
 import org.olat.core.gui.components.tabbedpane.TabbedPaneChangedEvent;
 import org.olat.core.gui.components.velocity.VelocityContainer;
@@ -85,6 +86,7 @@ public class BusinessGroupEditController extends BasicController implements Cont
 
 	private TabbedPane tabbedPane;
 	private VelocityContainer mainVC;
+	private final TooledStackedPanel toolbarPanel;
 
 	private LockResult lockEntry;
 	private DialogBoxController alreadyLockedDialogController;
@@ -110,6 +112,7 @@ public class BusinessGroupEditController extends BasicController implements Cont
 	 */
 	public BusinessGroupEditController(UserRequest ureq, WindowControl wControl, TooledStackedPanel toolbarPanel, BusinessGroup businessGroup) {
 		super(ureq, wControl);
+		this.toolbarPanel = toolbarPanel;
 		
 		// OLAT-4955: setting the stickyActionType here passes it on to any controller defined in the scope of the editor,
 		//            basically forcing any logging action called within the bg editor to be of type 'admin'
@@ -136,13 +139,7 @@ public class BusinessGroupEditController extends BasicController implements Cont
 				//create some controllers
 				editDetailsController = new BusinessGroupEditDetailsController(ureq, getWindowControl(), businessGroup);
 				listenTo(editDetailsController);
-				
-				collaborationToolsController = new BusinessGroupToolsController(ureq, getWindowControl(), businessGroup);
-				listenTo(collaborationToolsController);
-				
-				membersController = new BusinessGroupMembersController(ureq, getWindowControl(), toolbarPanel, businessGroup);
-				listenTo(membersController);
-				
+
 				tabbedPane = new TabbedPane("bgTabbs", ureq.getLocale());
 				tabbedPane.addListener(this);
 				setAllTabs(ureq);
@@ -181,56 +178,68 @@ public class BusinessGroupEditController extends BasicController implements Cont
 	 */
 	private void setAllTabs(UserRequest ureq) {
 		hasResources = businessGroupService.hasResources(currBusinessGroup);
-		
 		tabAccessCtrl = getAccessController(ureq);
-		
 		int currentSelectedPane = tabbedPane.getSelectedPane();
 
 		tabbedPane.removeAll();
+		
 		editDetailsController.setAllowWaitingList(tabAccessCtrl == null || !tabAccessCtrl.isPaymentMethodInUse());
 		tabbedPane.addTab(translate("group.edit.tab.details"), editDetailsController.getInitialComponent());
-		tabbedPane.addTab(translate("group.edit.tab.collabtools"), collaborationToolsController.getInitialComponent());
+		tabbedPane.addTab(translate("group.edit.tab.collabtools"), new TabCreator() {
+			@Override
+			public Component create(UserRequest uureq) {
+				collaborationToolsController = new BusinessGroupToolsController(uureq, getWindowControl(), currBusinessGroup);
+				listenTo(collaborationToolsController);
+				return collaborationToolsController.getInitialComponent();
+			}
+		});
+		
+		membersTab = tabbedPane.addTab(translate("group.edit.tab.members"), new TabCreator() {
+			@Override
+			public Component create(UserRequest uureq) {
+				if(membersController == null) {
+					membersController = new BusinessGroupMembersController(ureq, getWindowControl(), toolbarPanel, currBusinessGroup);
+					listenTo(membersController);
+				} else {
+					membersController.updateBusinessGroup(currBusinessGroup);
+				}
+				return membersController.getInitialComponent();
+			}
+		});
 		
-		membersController.updateBusinessGroup(currBusinessGroup);
-		membersTab = tabbedPane.addTab(translate("group.edit.tab.members"), membersController.getInitialComponent());
 		//resources (optional)
-		resourceController = getResourceController(ureq);
-		if(resourceController != null) {
-			tabbedPane.addTab(translate("group.edit.tab.resources"), resourceController.getInitialComponent());
+		Roles roles = ureq.getUserSession().getRoles();
+		boolean resourceEnabled = roles.isOLATAdmin() || roles.isGroupManager() || roles.isAuthor() || hasResources;
+		if(resourceEnabled) {
+			tabbedPane.addTab(translate("group.edit.tab.resources"), new TabCreator() {
+				@Override
+				public Component create(UserRequest uureq) {
+					if(resourceController == null) {
+						resourceController = new BusinessGroupEditResourceController(uureq, getWindowControl(), currBusinessGroup);
+						listenTo(resourceController);
+					}
+					return resourceController.getInitialComponent();
+				}
+			});
+		} else {
+			removeAsListenerAndDispose(resourceController);
+			resourceController = null;
 		}
-		
+
 		if(tabAccessCtrl != null) {
-			tabbedPane.addTab(translate("group.edit.tab.accesscontrol"), tabAccessCtrl.getInitialComponent());
+			tabbedPane.addTab(translate("group.edit.tab.accesscontrol"), new TabCreator() {
+				@Override
+				public Component create(UserRequest uureq) {
+					return tabAccessCtrl.getInitialComponent();
+				}
+			});
 		}
-		
+
 		if(currentSelectedPane > 0) {
-			tabbedPane.setSelectedPane(currentSelectedPane);
-		}
-	}
-	
-	/**
-	 * The resources / courses tab is enabled if the user is
-	 * an administrator, a group manager or an author. Or if the group has
-	 * already some resources.
-	 * 
-	 * @param ureq
-	 * @return
-	 */
-	private BusinessGroupEditResourceController getResourceController(UserRequest ureq) {
-		Roles roles = ureq.getUserSession().getRoles();
-		boolean enabled = roles.isOLATAdmin() || roles.isGroupManager() || roles.isAuthor() || hasResources;
-		if(enabled) {
-			if(resourceController == null) {
-				resourceController = new BusinessGroupEditResourceController(ureq, getWindowControl(), currBusinessGroup);
-				listenTo(resourceController);
-			}
-			return resourceController;
+			tabbedPane.setSelectedPane(ureq, currentSelectedPane);
 		}
-		removeAsListenerAndDispose(resourceController);
-		resourceController = null;
-		return null;
 	}
-	
+
 	private BusinessGroupEditAccessController getAccessController(UserRequest ureq) {
 		if(tabAccessCtrl == null && acModule.isEnabled()) { 
 			tabAccessCtrl = new BusinessGroupEditAccessController(ureq, getWindowControl(), currBusinessGroup);
diff --git a/src/main/java/org/olat/modules/wiki/WikiMainController.java b/src/main/java/org/olat/modules/wiki/WikiMainController.java
index aec5fa42554..a7c12ceec96 100644
--- a/src/main/java/org/olat/modules/wiki/WikiMainController.java
+++ b/src/main/java/org/olat/modules/wiki/WikiMainController.java
@@ -350,7 +350,7 @@ public class WikiMainController extends BasicController implements CloneableCont
 		content.put("wikiTabs", tabs);
 		// if not content yet switch to the edit tab
 		if (page.getContent().equals("") && securityCallback.mayEditAndCreateArticle()) {
-			tabs.setSelectedPane(2);
+			tabs.setSelectedPane(ureq, 2);
 			tryToSetEditLock(page, ureq, ores);
 		}
 		updatePageContext(ureq, page);
@@ -561,7 +561,7 @@ public class WikiMainController extends BasicController implements CloneableCont
 				editContent.contextPut("linkList", wiki.getListOfAllPageNames());
 				tryToSetEditLock(page, ureq, ores);
 				updatePageContext(ureq, page);
-				tabs.setSelectedPane(2);
+				tabs.setSelectedPane(ureq, 2);
 			}
 		} else if (source == toMainPageLink) { // home link
 			page = openIndexPage(ureq, wiki);
@@ -575,7 +575,7 @@ public class WikiMainController extends BasicController implements CloneableCont
 			tryToSetEditLock(page, ureq, ores);
 			updatePageContext(ureq, page);
 			// wikiEditForm.setPage(page);
-			tabs.setSelectedPane(2);
+			tabs.setSelectedPane(ureq, 2);
 		} else if (source == archiveLink) {
 			// archive a snapshot of the wiki in the users personal folder
 			archiveWikiDialogCtr = activateOkCancelDialog(ureq, null, translate("archive.question"),
@@ -599,7 +599,7 @@ public class WikiMainController extends BasicController implements CloneableCont
 			editContent.remove(wikiVersionDisplayComp);
 		} else if (source == revertVersionButton) {
 			wikiEditForm.setPage(selectedPage);
-			tabs.setSelectedPane(2);
+			tabs.setSelectedPane(ureq, 2);
 			tryToSetEditLock(page, ureq, ores);
 		} else if (source instanceof Link && "select-page".equals(command)) {
 			String name = source.getComponentName();
@@ -690,7 +690,7 @@ public class WikiMainController extends BasicController implements CloneableCont
 		Link pageLink = LinkFactory.createToolLink(page.getPageName(), "select-page", page.getPageName(), this);
 		breadcrumpDropdown.addComponent(pageLink);
 		setTabsEnabled(true);
-		tabs.setSelectedPane(0);
+		tabs.setSelectedPane(ureq, 0);
 		return page;
 	}
 
@@ -701,7 +701,7 @@ public class WikiMainController extends BasicController implements CloneableCont
 		breadcrumpDropdown.addComponent(pageLink);
 		updatePageContext(ureq, page);
 		setTabsEnabled(true);
-		tabs.setSelectedPane(0);
+		tabs.setSelectedPane(ureq, 0);
 		addToHistory(ureq, OresHelper.createOLATResourceableTypeWithoutCheck("index"), null);
 		return page;
 	}
@@ -713,7 +713,7 @@ public class WikiMainController extends BasicController implements CloneableCont
 		articleContent.contextPut("page", recentChanges);
 		wikiArticleComp.setWikiContent(recentChanges.getContent());
 		setTabsEnabled(false);
-		tabs.setSelectedPane(0);
+		tabs.setSelectedPane(ureq, 0);
 		addToHistory(ureq, OresHelper.createOLATResourceableTypeWithoutCheck("lastChanges"), null);
 	}
 
@@ -724,7 +724,7 @@ public class WikiMainController extends BasicController implements CloneableCont
 		wikiArticleComp.setWikiContent(a2zPage.getContent());
 		clearPortfolioLink();
 		setTabsEnabled(false);
-		tabs.setSelectedPane(0);
+		tabs.setSelectedPane(ureq, 0);
 		addToHistory(ureq, OresHelper.createOLATResourceableTypeWithoutCheck("az"), null);
 	}
 
@@ -816,7 +816,7 @@ public class WikiMainController extends BasicController implements CloneableCont
 		updatePageContext(ureq, page);
 		doReleaseEditLock();
 		tryToSetEditLock(page, ureq, ores);
-		tabs.setSelectedPane(2);
+		tabs.setSelectedPane(ureq, 2);
 		return page;
 	}
 
@@ -891,7 +891,7 @@ public class WikiMainController extends BasicController implements CloneableCont
 				WikiManager.getInstance().deleteWikiPage(ores, page);
 				page = wiki.getPage(WikiPage.WIKI_INDEX_PAGE);
 				updatePageContext(ureq, page);
-				tabs.setSelectedPane(0);
+				tabs.setSelectedPane(ureq, 0);
 			}
 		} else if (source == mediaTableCtr) {
 			if (event.getCommand().equals(Table.COMMANDLINK_ROWACTION_CLICKED)) {
@@ -947,7 +947,7 @@ public class WikiMainController extends BasicController implements CloneableCont
 				Link pageLink = LinkFactory.createToolLink(page.getPageName(), "select-page", page.getPageName(), this);
 				breadcrumpDropdown.addComponent(pageLink);
 			}
-			tabs.setSelectedPane(0);
+			tabs.setSelectedPane(ureq, 0);
 		} else if (source == wikiEditForm) {
 			// set recent page id to the page currently used
 			this.pageId = page.getPageId();
@@ -1011,7 +1011,7 @@ public class WikiMainController extends BasicController implements CloneableCont
 			}
 
 			if (wantClose) {
-				tabs.setSelectedPane(0);
+				tabs.setSelectedPane(ureq, 0);
 				doReleaseEditLock();
 				return;
 			}
-- 
GitLab