From 37f1140ed6042392a7fe9ab41be9814fe8b40677 Mon Sep 17 00:00:00 2001
From: srosse <none@none>
Date: Thu, 16 Jun 2016 10:42:26 +0200
Subject: [PATCH] OO-2057: wrapper for access rights

---
 .../portfolio/BinderSecurityCallback.java     |  35 +++
 .../portfolio/BinderSecurityCallbackImpl.java |  47 ++++
 .../modules/portfolio/PortfolioRoles.java     |  34 +++
 .../modules/portfolio/model/AccessRights.java |  79 ++++++
 .../ui/AccessRightsEditController.java        |  60 +++++
 .../portfolio/ui/BinderController.java        |   8 +-
 .../portfolio/ui/BinderListController.java    |   5 +-
 .../portfolio/ui/PublishController.java       |  81 ++++++-
 .../portfolio/ui/_content/access_rights.html  |   1 +
 .../ui/_i18n/LocalStrings_en.properties       |   2 +
 .../AccessRightsEditStepController.java       |  68 ++++++
 .../wizard/AddMember_1_ChooseMemberStep.java  |  55 +++++
 .../AddMember_2_ConfirmMemberChoiceStep.java  |  55 +++++
 .../AddMember_3_ChoosePermissionStep.java     |  60 +++++
 .../ui/wizard/AddMember_4_MailStep.java       |  58 +++++
 .../ui/wizard/MemberMailController.java       |  79 ++++++
 .../ui/wizard/MemberSearchController.java     | 105 ++++++++
 .../ui/wizard/MembersOverviewDataModel.java   |  87 +++++++
 .../MembersOverviewIdentitiesController.java  | 229 ++++++++++++++++++
 .../wizard/_content/access_rights_step.html   |   1 +
 .../ui/wizard/_content/import_search.html     |   1 +
 .../ui/wizard/_content/mail_template.html     |   0
 .../wizard/_i18n/LocalStrings_de.properties   |   1 +
 .../wizard/_i18n/LocalStrings_en.properties   |   5 +
 24 files changed, 1146 insertions(+), 10 deletions(-)
 create mode 100644 src/main/java/org/olat/modules/portfolio/BinderSecurityCallback.java
 create mode 100644 src/main/java/org/olat/modules/portfolio/BinderSecurityCallbackImpl.java
 create mode 100644 src/main/java/org/olat/modules/portfolio/PortfolioRoles.java
 create mode 100644 src/main/java/org/olat/modules/portfolio/model/AccessRights.java
 create mode 100644 src/main/java/org/olat/modules/portfolio/ui/AccessRightsEditController.java
 create mode 100644 src/main/java/org/olat/modules/portfolio/ui/_content/access_rights.html
 create mode 100644 src/main/java/org/olat/modules/portfolio/ui/wizard/AccessRightsEditStepController.java
 create mode 100644 src/main/java/org/olat/modules/portfolio/ui/wizard/AddMember_1_ChooseMemberStep.java
 create mode 100644 src/main/java/org/olat/modules/portfolio/ui/wizard/AddMember_2_ConfirmMemberChoiceStep.java
 create mode 100644 src/main/java/org/olat/modules/portfolio/ui/wizard/AddMember_3_ChoosePermissionStep.java
 create mode 100644 src/main/java/org/olat/modules/portfolio/ui/wizard/AddMember_4_MailStep.java
 create mode 100644 src/main/java/org/olat/modules/portfolio/ui/wizard/MemberMailController.java
 create mode 100644 src/main/java/org/olat/modules/portfolio/ui/wizard/MemberSearchController.java
 create mode 100644 src/main/java/org/olat/modules/portfolio/ui/wizard/MembersOverviewDataModel.java
 create mode 100644 src/main/java/org/olat/modules/portfolio/ui/wizard/MembersOverviewIdentitiesController.java
 create mode 100644 src/main/java/org/olat/modules/portfolio/ui/wizard/_content/access_rights_step.html
 create mode 100644 src/main/java/org/olat/modules/portfolio/ui/wizard/_content/import_search.html
 create mode 100644 src/main/java/org/olat/modules/portfolio/ui/wizard/_content/mail_template.html
 create mode 100644 src/main/java/org/olat/modules/portfolio/ui/wizard/_i18n/LocalStrings_de.properties
 create mode 100644 src/main/java/org/olat/modules/portfolio/ui/wizard/_i18n/LocalStrings_en.properties

diff --git a/src/main/java/org/olat/modules/portfolio/BinderSecurityCallback.java b/src/main/java/org/olat/modules/portfolio/BinderSecurityCallback.java
new file mode 100644
index 00000000000..63b2f07e045
--- /dev/null
+++ b/src/main/java/org/olat/modules/portfolio/BinderSecurityCallback.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.modules.portfolio;
+
+/**
+ * 
+ * Initial date: 15.06.2016<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public interface BinderSecurityCallback {
+	
+	public boolean canEditBinder();
+	
+	public boolean canEditMetadataBinder();
+ 
+
+}
diff --git a/src/main/java/org/olat/modules/portfolio/BinderSecurityCallbackImpl.java b/src/main/java/org/olat/modules/portfolio/BinderSecurityCallbackImpl.java
new file mode 100644
index 00000000000..05c820f75b5
--- /dev/null
+++ b/src/main/java/org/olat/modules/portfolio/BinderSecurityCallbackImpl.java
@@ -0,0 +1,47 @@
+/**
+ * <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.modules.portfolio;
+
+/**
+ * 
+ * Initial date: 15.06.2016<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class BinderSecurityCallbackImpl implements BinderSecurityCallback {
+	
+	private final boolean owner;
+	
+	public BinderSecurityCallbackImpl(boolean owner) {
+		this.owner = owner;
+	}
+	
+	@Override
+	public boolean canEditBinder() {
+		return owner;
+	}
+
+	@Override
+	public boolean canEditMetadataBinder() {
+		return owner;
+	}
+ 
+
+}
diff --git a/src/main/java/org/olat/modules/portfolio/PortfolioRoles.java b/src/main/java/org/olat/modules/portfolio/PortfolioRoles.java
new file mode 100644
index 00000000000..20be9b86593
--- /dev/null
+++ b/src/main/java/org/olat/modules/portfolio/PortfolioRoles.java
@@ -0,0 +1,34 @@
+/**
+ * <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.modules.portfolio;
+
+/**
+ * 
+ * Initial date: 16.06.2016<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public enum PortfolioRoles {
+	
+	owner,//same as GroupRoles.owner
+	coach,
+	reviewer,
+
+}
diff --git a/src/main/java/org/olat/modules/portfolio/model/AccessRights.java b/src/main/java/org/olat/modules/portfolio/model/AccessRights.java
new file mode 100644
index 00000000000..f0676a1db94
--- /dev/null
+++ b/src/main/java/org/olat/modules/portfolio/model/AccessRights.java
@@ -0,0 +1,79 @@
+/**
+ * <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.modules.portfolio.model;
+
+import org.olat.core.id.Identity;
+import org.olat.modules.portfolio.PortfolioRoles;
+
+/**
+ * 
+ * Initial date: 16.06.2016<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class AccessRights {
+	
+	private Long binderKey;
+	private Long sectionKey;
+	private Long pageKey;
+	
+	private Identity identity;
+	private PortfolioRoles role;
+	
+	public Long getBinderKey() {
+		return binderKey;
+	}
+	
+	public void setBinderKey(Long binderKey) {
+		this.binderKey = binderKey;
+	}
+	
+	public Long getSectionKey() {
+		return sectionKey;
+	}
+	
+	public void setSectionKey(Long sectionKey) {
+		this.sectionKey = sectionKey;
+	}
+	
+	public Long getPageKey() {
+		return pageKey;
+	}
+	
+	public void setPageKey(Long pageKey) {
+		this.pageKey = pageKey;
+	}
+	
+	public Identity getIdentity() {
+		return identity;
+	}
+	
+	public void setIdentity(Identity identity) {
+		this.identity = identity;
+	}
+	
+	public PortfolioRoles getRole() {
+		return role;
+	}
+	
+	public void setRole(PortfolioRoles role) {
+		this.role = role;
+	}
+}
diff --git a/src/main/java/org/olat/modules/portfolio/ui/AccessRightsEditController.java b/src/main/java/org/olat/modules/portfolio/ui/AccessRightsEditController.java
new file mode 100644
index 00000000000..c433c7219bf
--- /dev/null
+++ b/src/main/java/org/olat/modules/portfolio/ui/AccessRightsEditController.java
@@ -0,0 +1,60 @@
+/**
+ * <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.modules.portfolio.ui;
+
+import org.olat.core.gui.UserRequest;
+import org.olat.core.gui.components.form.flexible.FormItemContainer;
+import org.olat.core.gui.components.form.flexible.impl.FormBasicController;
+import org.olat.core.gui.control.Controller;
+import org.olat.core.gui.control.WindowControl;
+import org.olat.modules.portfolio.Binder;
+
+/**
+ * 
+ * Initial date: 16.06.2016<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class AccessRightsEditController extends FormBasicController {
+	
+	private final Binder binder;
+	
+	public AccessRightsEditController(UserRequest ureq, WindowControl wControl, Binder binder) {
+		super(ureq, wControl, "access_rights");
+		this.binder = binder;
+		
+		initForm(ureq);
+	}
+	
+	@Override
+	protected void doDispose() {
+		//
+	}
+
+	@Override
+	protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
+		//
+	}
+
+	@Override
+	protected void formOK(UserRequest ureq) {
+		//
+	}
+}
diff --git a/src/main/java/org/olat/modules/portfolio/ui/BinderController.java b/src/main/java/org/olat/modules/portfolio/ui/BinderController.java
index 511721250d7..4744fe46eeb 100644
--- a/src/main/java/org/olat/modules/portfolio/ui/BinderController.java
+++ b/src/main/java/org/olat/modules/portfolio/ui/BinderController.java
@@ -39,6 +39,7 @@ import org.olat.core.gui.control.generic.dtabs.Activateable2;
 import org.olat.core.id.context.ContextEntry;
 import org.olat.core.id.context.StateEntry;
 import org.olat.modules.portfolio.Binder;
+import org.olat.modules.portfolio.BinderSecurityCallback;
 
 /**
  * 
@@ -58,11 +59,14 @@ public class BinderController extends BasicController implements TooledControlle
 	private BinderPageListController entriesCtrl;
 	
 	private Binder binder;
+	private final BinderSecurityCallback secCallback;
 
-	public BinderController(UserRequest ureq, WindowControl wControl, TooledStackedPanel stackPanel, Binder binder) {
+	public BinderController(UserRequest ureq, WindowControl wControl, TooledStackedPanel stackPanel,
+			BinderSecurityCallback secCallback, Binder binder) {
 		super(ureq, wControl);
 		this.binder = binder;
 		this.stackPanel = stackPanel;
+		this.secCallback = secCallback;
 		
 		segmentButtonsCmp = new ButtonGroupComponent("segments");
 		overviewLink = LinkFactory.createLink("portfolio.overview", getTranslator(), this);
@@ -145,7 +149,7 @@ public class BinderController extends BasicController implements TooledControlle
 	}
 	
 	private void doOpenPublish(UserRequest ureq) {
-		publishCtrl = new PublishController(ureq, getWindowControl(), binder);
+		publishCtrl = new PublishController(ureq, getWindowControl(), stackPanel, secCallback, binder);
 		listenTo(publishCtrl);
 		
 		stackPanel.popUpToController(this);
diff --git a/src/main/java/org/olat/modules/portfolio/ui/BinderListController.java b/src/main/java/org/olat/modules/portfolio/ui/BinderListController.java
index 93fa5c781bf..736996cd405 100644
--- a/src/main/java/org/olat/modules/portfolio/ui/BinderListController.java
+++ b/src/main/java/org/olat/modules/portfolio/ui/BinderListController.java
@@ -53,6 +53,8 @@ import org.olat.core.id.context.StateEntry;
 import org.olat.core.util.StringHelper;
 import org.olat.core.util.resource.OresHelper;
 import org.olat.modules.portfolio.Binder;
+import org.olat.modules.portfolio.BinderSecurityCallback;
+import org.olat.modules.portfolio.BinderSecurityCallbackImpl;
 import org.olat.modules.portfolio.PortfolioService;
 import org.olat.modules.portfolio.ui.BindersDataModel.PortfolioCols;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -229,7 +231,8 @@ public class BinderListController extends FormBasicController
 			
 			OLATResourceable binderOres = OresHelper.createOLATResourceableInstance("Binder", binderKey);
 			WindowControl swControl = addToHistory(ureq, binderOres, null);
-			binderCtrl = new BinderController(ureq, swControl, stackPanel, binder);
+			BinderSecurityCallback secCallback = new BinderSecurityCallbackImpl(true);
+			binderCtrl = new BinderController(ureq, swControl, stackPanel, secCallback, binder);
 			String displayName = StringHelper.escapeHtml(binder.getTitle());
 			stackPanel.pushController(displayName, binderCtrl);
 			return binderCtrl;
diff --git a/src/main/java/org/olat/modules/portfolio/ui/PublishController.java b/src/main/java/org/olat/modules/portfolio/ui/PublishController.java
index c73d71cef0f..3929ba34335 100644
--- a/src/main/java/org/olat/modules/portfolio/ui/PublishController.java
+++ b/src/main/java/org/olat/modules/portfolio/ui/PublishController.java
@@ -24,13 +24,25 @@ import java.util.List;
 import org.olat.basesecurity.GroupRoles;
 import org.olat.core.gui.UserRequest;
 import org.olat.core.gui.components.Component;
+import org.olat.core.gui.components.link.Link;
+import org.olat.core.gui.components.link.LinkFactory;
+import org.olat.core.gui.components.stack.TooledController;
+import org.olat.core.gui.components.stack.TooledStackedPanel;
+import org.olat.core.gui.components.stack.TooledStackedPanel.Align;
 import org.olat.core.gui.components.velocity.VelocityContainer;
+import org.olat.core.gui.control.Controller;
 import org.olat.core.gui.control.Event;
 import org.olat.core.gui.control.WindowControl;
 import org.olat.core.gui.control.controller.BasicController;
+import org.olat.core.gui.control.generic.wizard.Step;
+import org.olat.core.gui.control.generic.wizard.StepRunnerCallback;
+import org.olat.core.gui.control.generic.wizard.StepsMainRunController;
+import org.olat.core.gui.control.generic.wizard.StepsRunContext;
 import org.olat.core.id.Identity;
 import org.olat.modules.portfolio.Binder;
+import org.olat.modules.portfolio.BinderSecurityCallback;
 import org.olat.modules.portfolio.PortfolioService;
+import org.olat.modules.portfolio.ui.wizard.AddMember_1_ChooseMemberStep;
 import org.springframework.beans.factory.annotation.Autowired;
 
 /**
@@ -39,42 +51,97 @@ import org.springframework.beans.factory.annotation.Autowired;
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
  *
  */
-public class PublishController extends BasicController {
+public class PublishController extends BasicController implements TooledController {
 	
+	private Link addAccessRightsLink;
 	private final VelocityContainer mainVC;
+	private final TooledStackedPanel stackPanel;
+	
+	private StepsMainRunController addMembersWizard;
 	
 	private Binder binder;
+	private final BinderSecurityCallback secCallback;
+	
 	private List<Identity> owners;
 	
 	@Autowired
 	private PortfolioService portfolioService;
 	
-	public PublishController(UserRequest ureq, WindowControl wControl, Binder binder) {
+	public PublishController(UserRequest ureq, WindowControl wControl, TooledStackedPanel stackPanel,
+			BinderSecurityCallback secCallback, Binder binder) {
 		super(ureq, wControl);
 		this.binder = binder;
+		this.secCallback = secCallback;
+		this.stackPanel = stackPanel;
+		
 		mainVC = createVelocityContainer("publish");
 		mainVC.contextPut("binderTitle", binder.getTitle());
 		
 		owners = portfolioService.getMembers(binder, GroupRoles.owner.name());
 
 		mainVC.contextPut("owners", owners);
-		
-		
+
 		putInitialPanel(mainVC);
 	}
 	
+	@Override
+	public void initTools() {
+		addAccessRightsLink = LinkFactory.createToolLink("edit.binder.metadata", translate("edit.binder.metadata"), this);
+		addAccessRightsLink.setIconLeftCSS("o_icon o_icon-lg o_icon_new_portfolio");
+		stackPanel.addTool(addAccessRightsLink, Align.right);
+	}
+
 	@Override
 	protected void doDispose() {
 		//
 	}
+	
+	public void reloadData() {
+		
+	}
 
 	@Override
 	protected void event(UserRequest ureq, Component source, Event event) {
-		// 
+		if(addAccessRightsLink == source) {
+			doAddAccessRights(ureq);
+		}
 	}
-
-
 	
+	@Override
+	protected void event(UserRequest ureq, Controller source, Event event) {
+		if (addMembersWizard == source) {
+			if(event == Event.CANCELLED_EVENT || event == Event.DONE_EVENT || event == Event.CHANGED_EVENT) {
+				getWindowControl().pop();
+				removeAsListenerAndDispose(addMembersWizard);
+				addMembersWizard = null;
+				if(event == Event.DONE_EVENT || event == Event.CHANGED_EVENT) {
+					reloadData();
+				}
+			}
+		}
+		super.event(ureq, source, event);
+	}
 	
+	private void doAddAccessRights(UserRequest ureq) {
+		removeAsListenerAndDispose(addMembersWizard);
 
+		Step start = new AddMember_1_ChooseMemberStep(ureq, binder);
+		StepRunnerCallback finish = new StepRunnerCallback() {
+			@Override
+			public Step execute(UserRequest uureq, WindowControl wControl, StepsRunContext runContext) {
+				addMembers(uureq, runContext);
+				return StepsMainRunController.DONE_MODIFIED;
+			}
+		};
+		
+		addMembersWizard = new StepsMainRunController(ureq, getWindowControl(), start, finish, null,
+				translate("add.member"), "o_sel_course_member_import_1_wizard");
+		listenTo(addMembersWizard);
+		getWindowControl().pushAsModalDialog(addMembersWizard.getInitialComponent());
+		
+	}
+	
+	private void addMembers(UserRequest ureq, StepsRunContext runContext) {
+		
+	}
 }
diff --git a/src/main/java/org/olat/modules/portfolio/ui/_content/access_rights.html b/src/main/java/org/olat/modules/portfolio/ui/_content/access_rights.html
new file mode 100644
index 00000000000..f97f40f82bb
--- /dev/null
+++ b/src/main/java/org/olat/modules/portfolio/ui/_content/access_rights.html
@@ -0,0 +1 @@
+<h1>Access rights</h1>
\ No newline at end of file
diff --git a/src/main/java/org/olat/modules/portfolio/ui/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/modules/portfolio/ui/_i18n/LocalStrings_en.properties
index 16240367bf8..ddbbcc6bae0 100644
--- a/src/main/java/org/olat/modules/portfolio/ui/_i18n/LocalStrings_en.properties
+++ b/src/main/java/org/olat/modules/portfolio/ui/_i18n/LocalStrings_en.properties
@@ -8,6 +8,7 @@ portfoliotask=Portfolio Task
 portfoliotask.none=None
 
 add.html=HTML
+add.member=Add member
 begin.date=Begin
 binder.by=by {0}
 categories=Categories
@@ -64,6 +65,7 @@ table.of.contents=Table of contents {0}
 table.header.key=ID
 table.header.title=Title
 table.header.open=Start
+table.user.login=User name
 title=Title
 warning.portfolio.not.found=The portfolio cannot be found, probably deleted in the mean time
 
diff --git a/src/main/java/org/olat/modules/portfolio/ui/wizard/AccessRightsEditStepController.java b/src/main/java/org/olat/modules/portfolio/ui/wizard/AccessRightsEditStepController.java
new file mode 100644
index 00000000000..18700d27619
--- /dev/null
+++ b/src/main/java/org/olat/modules/portfolio/ui/wizard/AccessRightsEditStepController.java
@@ -0,0 +1,68 @@
+/**
+ * <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.modules.portfolio.ui.wizard;
+
+import org.olat.core.gui.UserRequest;
+import org.olat.core.gui.components.form.flexible.FormItemContainer;
+import org.olat.core.gui.components.form.flexible.impl.Form;
+import org.olat.core.gui.control.Controller;
+import org.olat.core.gui.control.WindowControl;
+import org.olat.core.gui.control.generic.wizard.StepFormBasicController;
+import org.olat.core.gui.control.generic.wizard.StepsEvent;
+import org.olat.core.gui.control.generic.wizard.StepsRunContext;
+import org.olat.modules.portfolio.Binder;
+import org.olat.modules.portfolio.ui.AccessRightsEditController;
+
+/**
+ * 
+ * Initial date: 16.06.2016<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class AccessRightsEditStepController extends StepFormBasicController {
+	
+	private final Binder binder;
+	private final AccessRightsEditController accessRightsCtrl;
+	
+	public AccessRightsEditStepController(UserRequest ureq, WindowControl wControl, Binder binder, Form form, StepsRunContext runContext) {
+		super(ureq, wControl, form, runContext, LAYOUT_CUSTOM, "access_rights_step");
+		this.binder = binder;
+		
+		accessRightsCtrl = new AccessRightsEditController(ureq, getWindowControl(), binder);
+		listenTo(accessRightsCtrl);
+		
+		initForm(ureq);
+	}
+
+	@Override
+	protected void doDispose() {
+		//
+	}
+	
+	@Override
+	protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
+		formLayout.add("access_rights", accessRightsCtrl.getInitialFormItem());
+	}
+
+	@Override
+	protected void formOK(UserRequest ureq) {
+		fireEvent(ureq, StepsEvent.ACTIVATE_NEXT);
+	}
+}
diff --git a/src/main/java/org/olat/modules/portfolio/ui/wizard/AddMember_1_ChooseMemberStep.java b/src/main/java/org/olat/modules/portfolio/ui/wizard/AddMember_1_ChooseMemberStep.java
new file mode 100644
index 00000000000..602199cb3ba
--- /dev/null
+++ b/src/main/java/org/olat/modules/portfolio/ui/wizard/AddMember_1_ChooseMemberStep.java
@@ -0,0 +1,55 @@
+/**
+ * <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.modules.portfolio.ui.wizard;
+
+import org.olat.core.gui.UserRequest;
+import org.olat.core.gui.components.form.flexible.impl.Form;
+import org.olat.core.gui.control.WindowControl;
+import org.olat.core.gui.control.generic.wizard.BasicStep;
+import org.olat.core.gui.control.generic.wizard.PrevNextFinishConfig;
+import org.olat.core.gui.control.generic.wizard.StepFormController;
+import org.olat.core.gui.control.generic.wizard.StepsRunContext;
+import org.olat.modules.portfolio.Binder;
+
+/**
+ * 
+ * Initial date: 15.06.2016<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class AddMember_1_ChooseMemberStep extends BasicStep {
+	
+	public AddMember_1_ChooseMemberStep(UserRequest ureq, Binder binder) {
+		super(ureq);
+		setNextStep(new AddMember_2_ConfirmMemberChoiceStep(ureq, binder));
+		setI18nTitleAndDescr("add.choose.title", "add.choose.title");
+	}
+
+	@Override
+	public PrevNextFinishConfig getInitialPrevNextFinishConfig() {
+		return new PrevNextFinishConfig(false, true, false);
+	}
+
+	@Override
+	public StepFormController getStepController(UserRequest ureq, WindowControl wControl, StepsRunContext runContext, Form form) {
+		MemberSearchController controller = new MemberSearchController(ureq, wControl, form, runContext);
+		return controller;
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/org/olat/modules/portfolio/ui/wizard/AddMember_2_ConfirmMemberChoiceStep.java b/src/main/java/org/olat/modules/portfolio/ui/wizard/AddMember_2_ConfirmMemberChoiceStep.java
new file mode 100644
index 00000000000..d440861b522
--- /dev/null
+++ b/src/main/java/org/olat/modules/portfolio/ui/wizard/AddMember_2_ConfirmMemberChoiceStep.java
@@ -0,0 +1,55 @@
+/**
+ * <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.modules.portfolio.ui.wizard;
+
+import org.olat.core.gui.UserRequest;
+import org.olat.core.gui.components.form.flexible.impl.Form;
+import org.olat.core.gui.control.WindowControl;
+import org.olat.core.gui.control.generic.wizard.BasicStep;
+import org.olat.core.gui.control.generic.wizard.PrevNextFinishConfig;
+import org.olat.core.gui.control.generic.wizard.StepFormController;
+import org.olat.core.gui.control.generic.wizard.StepsRunContext;
+import org.olat.modules.portfolio.Binder;
+
+/**
+ * 
+ * Initial date: 15.06.2016<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class AddMember_2_ConfirmMemberChoiceStep extends BasicStep {
+	
+	public AddMember_2_ConfirmMemberChoiceStep(UserRequest ureq, Binder binder) {
+		super(ureq);
+		setNextStep(new AddMember_3_ChoosePermissionStep(ureq, binder));
+		setI18nTitleAndDescr("add.confirm.title", "add.confirm.title");
+	}
+
+	@Override
+	public PrevNextFinishConfig getInitialPrevNextFinishConfig() {
+		return new PrevNextFinishConfig(true, true, false);
+	}
+
+	@Override
+	public StepFormController getStepController(UserRequest ureq, WindowControl wControl, StepsRunContext runContext, Form form) {
+		MembersOverviewIdentitiesController controller = new MembersOverviewIdentitiesController(ureq, wControl, form, runContext);
+		return controller;
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/org/olat/modules/portfolio/ui/wizard/AddMember_3_ChoosePermissionStep.java b/src/main/java/org/olat/modules/portfolio/ui/wizard/AddMember_3_ChoosePermissionStep.java
new file mode 100644
index 00000000000..8f15c1b3d52
--- /dev/null
+++ b/src/main/java/org/olat/modules/portfolio/ui/wizard/AddMember_3_ChoosePermissionStep.java
@@ -0,0 +1,60 @@
+/**
+ * <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.modules.portfolio.ui.wizard;
+
+import org.olat.core.gui.UserRequest;
+import org.olat.core.gui.components.form.flexible.impl.Form;
+import org.olat.core.gui.control.WindowControl;
+import org.olat.core.gui.control.generic.wizard.BasicStep;
+import org.olat.core.gui.control.generic.wizard.PrevNextFinishConfig;
+import org.olat.core.gui.control.generic.wizard.StepFormController;
+import org.olat.core.gui.control.generic.wizard.StepsRunContext;
+import org.olat.modules.portfolio.Binder;
+
+/**
+ * 
+ * Initial date: 15.06.2016<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class AddMember_3_ChoosePermissionStep extends BasicStep {
+
+	private final Binder binder;
+	
+	public AddMember_3_ChoosePermissionStep(UserRequest ureq, Binder binder) {
+		super(ureq);
+		this.binder = binder;
+		setNextStep(new AddMember_4_MailStep(ureq, binder));
+		setI18nTitleAndDescr("add.permission.title", "add.permission.title");
+	}
+
+	@Override
+	public PrevNextFinishConfig getInitialPrevNextFinishConfig() {
+		return new PrevNextFinishConfig(true, true, false);
+	}
+
+	@Override
+	public StepFormController getStepController(UserRequest ureq, WindowControl wControl, StepsRunContext runContext, Form form) {
+		AccessRightsEditStepController controller
+			= new AccessRightsEditStepController(ureq, wControl, binder, form, runContext);
+		return controller;
+	}
+	
+}
diff --git a/src/main/java/org/olat/modules/portfolio/ui/wizard/AddMember_4_MailStep.java b/src/main/java/org/olat/modules/portfolio/ui/wizard/AddMember_4_MailStep.java
new file mode 100644
index 00000000000..a5633c6fc40
--- /dev/null
+++ b/src/main/java/org/olat/modules/portfolio/ui/wizard/AddMember_4_MailStep.java
@@ -0,0 +1,58 @@
+/**
+ * <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.modules.portfolio.ui.wizard;
+
+import org.olat.core.gui.UserRequest;
+import org.olat.core.gui.components.form.flexible.impl.Form;
+import org.olat.core.gui.control.WindowControl;
+import org.olat.core.gui.control.generic.wizard.BasicStep;
+import org.olat.core.gui.control.generic.wizard.PrevNextFinishConfig;
+import org.olat.core.gui.control.generic.wizard.StepFormController;
+import org.olat.core.gui.control.generic.wizard.StepsRunContext;
+import org.olat.modules.portfolio.Binder;
+
+/**
+ * 
+ * Initial date: 15.06.2016<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class AddMember_4_MailStep extends BasicStep {
+
+	private final Binder binder;
+	
+	public AddMember_4_MailStep(UserRequest ureq, Binder binder) {
+		super(ureq);
+		this.binder = binder;
+		setNextStep(NOSTEP);
+		setI18nTitleAndDescr("add.mail.title", "add.mail.title");
+	}
+
+	@Override
+	public PrevNextFinishConfig getInitialPrevNextFinishConfig() {
+		return new PrevNextFinishConfig(true, false, true);
+	}
+
+	@Override
+	public StepFormController getStepController(UserRequest ureq, WindowControl wControl, StepsRunContext runContext, Form form) {
+		MemberMailController controller = new MemberMailController(ureq, wControl, binder, form, runContext);
+		return controller;
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/org/olat/modules/portfolio/ui/wizard/MemberMailController.java b/src/main/java/org/olat/modules/portfolio/ui/wizard/MemberMailController.java
new file mode 100644
index 00000000000..d7b2cf59f46
--- /dev/null
+++ b/src/main/java/org/olat/modules/portfolio/ui/wizard/MemberMailController.java
@@ -0,0 +1,79 @@
+/**
+ * <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.modules.portfolio.ui.wizard;
+
+import org.olat.core.gui.UserRequest;
+import org.olat.core.gui.components.form.flexible.FormItemContainer;
+import org.olat.core.gui.components.form.flexible.impl.Form;
+import org.olat.core.gui.control.Controller;
+import org.olat.core.gui.control.WindowControl;
+import org.olat.core.gui.control.generic.wizard.StepFormBasicController;
+import org.olat.core.gui.control.generic.wizard.StepsEvent;
+import org.olat.core.gui.control.generic.wizard.StepsRunContext;
+import org.olat.core.util.mail.MailTemplate;
+import org.olat.modules.portfolio.Binder;
+
+/**
+ * 
+ * Initial date: 16.06.2016<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class MemberMailController extends StepFormBasicController {
+	
+	private MailTemplate mailTemplate;
+	private final Binder binder;
+
+	public MemberMailController(UserRequest ureq, WindowControl wControl, Binder binder,
+			Form rootForm, StepsRunContext runContext) {
+		super(ureq, wControl, rootForm, runContext, LAYOUT_CUSTOM, "mail_template");
+		this.binder = binder;
+		initForm (ureq);
+	}
+	
+	@Override
+	protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
+		//formLayout.add("template", mailTemplateForm.getInitialFormItem());
+	}
+	
+	@Override
+	protected void doDispose() {
+		//
+	}
+	
+	@Override
+	protected boolean validateFormLogic(UserRequest ureq) {
+		boolean allOk = true;// mailTemplateForm.validateFormLogic(ureq);
+		return allOk & super.validateFormLogic(ureq);
+	}
+
+	@Override
+	protected void formOK(UserRequest ureq) {
+		/*if(mailTemplateForm.sendMailSwitchEnabled()) {
+			if(!mailTemplateForm.isDefaultTemplate()) {
+				mailTemplateForm.updateTemplateFromForm(mailTemplate);
+			}
+			addToRunContext("mailTemplate", mailTemplate);
+		} else {
+			addToRunContext("mailTemplate", null);
+		}*/
+		fireEvent (ureq, StepsEvent.ACTIVATE_NEXT);
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/org/olat/modules/portfolio/ui/wizard/MemberSearchController.java b/src/main/java/org/olat/modules/portfolio/ui/wizard/MemberSearchController.java
new file mode 100644
index 00000000000..68f3d41b2b9
--- /dev/null
+++ b/src/main/java/org/olat/modules/portfolio/ui/wizard/MemberSearchController.java
@@ -0,0 +1,105 @@
+/**
+ * <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.modules.portfolio.ui.wizard;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.olat.admin.user.UserSearchFlexiController;
+import org.olat.basesecurity.events.MultiIdentityChosenEvent;
+import org.olat.basesecurity.events.SingleIdentityChosenEvent;
+import org.olat.core.gui.UserRequest;
+import org.olat.core.gui.components.form.flexible.FormItemContainer;
+import org.olat.core.gui.components.form.flexible.impl.Form;
+import org.olat.core.gui.control.Controller;
+import org.olat.core.gui.control.Event;
+import org.olat.core.gui.control.WindowControl;
+import org.olat.core.gui.control.generic.wizard.StepFormBasicController;
+import org.olat.core.gui.control.generic.wizard.StepsEvent;
+import org.olat.core.gui.control.generic.wizard.StepsRunContext;
+import org.olat.core.id.Identity;
+
+
+/**
+ * 
+ * Initial date: 15.06.2016<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class MemberSearchController extends StepFormBasicController {
+	
+	private UserSearchFlexiController searchController; 
+
+	public MemberSearchController(UserRequest ureq, WindowControl wControl, Form rootForm, StepsRunContext runContext) {
+		super(ureq, wControl, rootForm, runContext, LAYOUT_CUSTOM, "import_search");
+
+		searchController = new UserSearchFlexiController(ureq, wControl, rootForm);
+		listenTo(searchController);
+		initForm (ureq);
+	}
+
+	@Override
+	protected void event(UserRequest ureq, Controller source, Event event) {
+		if(event instanceof SingleIdentityChosenEvent) {
+			SingleIdentityChosenEvent e = (SingleIdentityChosenEvent)event;
+			String key = e.getChosenIdentity().getKey().toString();
+			addToRunContext("keys", Collections.singletonList(key));
+			fireEvent(ureq, StepsEvent.ACTIVATE_NEXT);
+		} else if(event instanceof MultiIdentityChosenEvent) {
+			MultiIdentityChosenEvent e = (MultiIdentityChosenEvent)event;
+			Collection<String> keys = new ArrayList<String>();
+			for(Identity identity: e.getChosenIdentities()) {
+				keys.add(identity.getKey().toString());
+			}
+			addToRunContext("keys", keys);
+			fireEvent(ureq, StepsEvent.ACTIVATE_NEXT);
+		} else {
+			super.event(ureq, source, event);
+		}
+	}
+
+	@Override
+	protected void formNext(UserRequest ureq) {
+		List<Identity> identities = searchController.getSelectedIdentities();
+		Collection<String> keys = new ArrayList<String>();
+		for(Identity identity: identities) {
+			keys.add(identity.getKey().toString());
+		}
+		addToRunContext("keys", keys);
+		fireEvent(ureq, StepsEvent.ACTIVATE_NEXT);
+	}
+
+	@Override
+	protected void formOK(UserRequest ureq) {
+		//do nothing
+	}
+
+	@Override
+	protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
+		formLayout.add("search", searchController.getInitialFormItem());
+	}
+
+	@Override
+	protected void doDispose() {
+		//
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/org/olat/modules/portfolio/ui/wizard/MembersOverviewDataModel.java b/src/main/java/org/olat/modules/portfolio/ui/wizard/MembersOverviewDataModel.java
new file mode 100644
index 00000000000..d8592132e64
--- /dev/null
+++ b/src/main/java/org/olat/modules/portfolio/ui/wizard/MembersOverviewDataModel.java
@@ -0,0 +1,87 @@
+/**
+ * <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.modules.portfolio.ui.wizard;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableColumnModel;
+import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableDataModel;
+import org.olat.core.gui.components.table.DefaultTableDataModel;
+import org.olat.core.id.Identity;
+import org.olat.user.propertyhandlers.UserPropertyHandler;
+
+/**
+ * 
+ * Initial date: 16.06.2016<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class MembersOverviewDataModel extends DefaultTableDataModel<Identity> implements FlexiTableDataModel<Identity> {
+	private final Locale locale;
+	private final boolean isAdministrativeUser;
+	private FlexiTableColumnModel columnModel;
+	private final List<UserPropertyHandler> userPropertyHandlers;
+	
+	public MembersOverviewDataModel(List<Identity> identities, List<UserPropertyHandler> userPropertyHandlers,
+			boolean isAdministrativeUser, Locale locale, FlexiTableColumnModel columnModel) {
+		super(identities);
+		this.locale = locale;
+		this.columnModel = columnModel;
+		this.isAdministrativeUser = isAdministrativeUser;
+		this.userPropertyHandlers = userPropertyHandlers;
+	}
+
+	@Override
+	public FlexiTableColumnModel getTableColumnModel() {
+		return columnModel;
+	}
+
+	@Override
+	public void setTableColumnModel(FlexiTableColumnModel tableColumnModel) {
+		columnModel = tableColumnModel;
+	}
+
+	@Override
+	public int getColumnCount() {
+		return columnModel.getColumnCount();
+	}
+
+	@Override
+	public Object getValueAt(int row, int col) {
+		Identity identity = getObject(row);
+		if(col == 0 && isAdministrativeUser) {
+			return identity.getName();
+		}
+
+		int pos = isAdministrativeUser ? col - 1 : col;
+		if(pos >= 0 && pos < userPropertyHandlers.size()) {
+			UserPropertyHandler handler = userPropertyHandlers.get(pos);
+			return handler.getUserProperty(identity.getUser(), locale);
+		}
+		return "";
+	}
+
+	@Override
+	public MembersOverviewDataModel createCopyWithEmptyList() {
+		return new MembersOverviewDataModel(new ArrayList<Identity>(), userPropertyHandlers, isAdministrativeUser, locale, columnModel);
+	}
+}
diff --git a/src/main/java/org/olat/modules/portfolio/ui/wizard/MembersOverviewIdentitiesController.java b/src/main/java/org/olat/modules/portfolio/ui/wizard/MembersOverviewIdentitiesController.java
new file mode 100644
index 00000000000..355c9c04ae7
--- /dev/null
+++ b/src/main/java/org/olat/modules/portfolio/ui/wizard/MembersOverviewIdentitiesController.java
@@ -0,0 +1,229 @@
+/**
+ * <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.modules.portfolio.ui.wizard;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.olat.basesecurity.BaseSecurity;
+import org.olat.basesecurity.BaseSecurityModule;
+import org.olat.basesecurity.Constants;
+import org.olat.basesecurity.SecurityGroup;
+import org.olat.core.commons.persistence.PersistenceHelper;
+import org.olat.core.gui.UserRequest;
+import org.olat.core.gui.components.form.flexible.FormItemContainer;
+import org.olat.core.gui.components.form.flexible.elements.FlexiTableElement;
+import org.olat.core.gui.components.form.flexible.impl.Form;
+import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer;
+import org.olat.core.gui.components.form.flexible.impl.elements.table.DefaultFlexiColumnModel;
+import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableColumnModel;
+import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableDataModelFactory;
+import org.olat.core.gui.control.Controller;
+import org.olat.core.gui.control.WindowControl;
+import org.olat.core.gui.control.generic.wizard.StepFormBasicController;
+import org.olat.core.gui.control.generic.wizard.StepsEvent;
+import org.olat.core.gui.control.generic.wizard.StepsRunContext;
+import org.olat.core.gui.translator.Translator;
+import org.olat.core.id.Identity;
+import org.olat.core.id.UserConstants;
+import org.olat.core.util.Util;
+import org.olat.modules.portfolio.ui.PortfolioHomeController;
+import org.olat.modules.portfolio.ui.PublishController;
+import org.olat.user.UserManager;
+import org.olat.user.propertyhandlers.UserPropertyHandler;
+import org.springframework.beans.factory.annotation.Autowired;
+
+/**
+ * 
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ */
+public class MembersOverviewIdentitiesController extends StepFormBasicController {
+
+	protected static final String USER_PROPS_ID = PortfolioHomeController.class.getCanonicalName();
+	
+	private List<Identity> oks;
+	private List<String> notfounds;
+	private boolean isAdministrativeUser;
+	
+	@Autowired
+	private UserManager userManager;
+	@Autowired
+	private BaseSecurity securityManager;
+	@Autowired
+	private BaseSecurityModule securityModule;
+
+	public MembersOverviewIdentitiesController(UserRequest ureq, WindowControl wControl, Form rootForm, StepsRunContext runContext) {
+		super(ureq, wControl, rootForm, runContext, LAYOUT_VERTICAL, null);
+		setTranslator(userManager.getPropertyHandlerTranslator(getTranslator()));
+		setTranslator(Util.createPackageTranslator(PublishController.class, getLocale(), getTranslator()));
+
+		oks = null;
+		if(containsRunContextKey("logins")) {
+			String logins = (String)runContext.get("logins");
+			loadModel(logins);
+		} else if(containsRunContextKey("keys")) {
+			@SuppressWarnings("unchecked")
+			List<String> keys = (List<String>)runContext.get("keys");
+			loadModel(keys);
+		}
+
+		isAdministrativeUser = securityModule.isUserAllowedAdminProps(ureq.getUserSession().getRoles());
+
+		initForm (ureq);
+	}
+	
+	@Override
+	protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
+		if(notfounds != null && !notfounds.isEmpty()) {
+			String page = velocity_root + "/warn_notfound.html";
+			FormLayoutContainer warnLayout = FormLayoutContainer.createCustomFormLayout("warnNotFounds", getTranslator(), page);
+			warnLayout.setRootForm(mainForm);
+			formLayout.add(warnLayout);
+			
+			StringBuilder sb = new StringBuilder();
+			for(String notfound:notfounds) {
+				if(sb.length() > 0) sb.append(", ");
+				sb.append(notfound);
+			}
+			String msg = translate("user.notfound", new String[]{sb.toString()});
+			addToRunContext("notFounds", sb.toString());
+			warnLayout.contextPut("notFounds", msg);
+		}
+		
+		//add the table
+		FlexiTableColumnModel tableColumnModel = FlexiTableDataModelFactory.createFlexiTableColumnModel();
+		int colIndex = 0;
+		if(isAdministrativeUser) {
+			tableColumnModel.addFlexiColumnModel(new DefaultFlexiColumnModel("table.user.login", colIndex++));
+		}
+		List<UserPropertyHandler> userPropertyHandlers = userManager.getUserPropertyHandlersFor(USER_PROPS_ID, isAdministrativeUser);
+		List<UserPropertyHandler> resultingPropertyHandlers = new ArrayList<UserPropertyHandler>();
+		// followed by the users fields
+		for (int i = 0; i < userPropertyHandlers.size(); i++) {
+			UserPropertyHandler userPropertyHandler	= userPropertyHandlers.get(i);
+			boolean visible = UserManager.getInstance().isMandatoryUserProperty(USER_PROPS_ID , userPropertyHandler);
+			if(visible) {
+				resultingPropertyHandlers.add(userPropertyHandler);
+				tableColumnModel.addFlexiColumnModel(new DefaultFlexiColumnModel(userPropertyHandler.i18nColumnDescriptorLabelKey(), colIndex++));
+			}
+		}
+		
+		Translator myTrans = userManager.getPropertyHandlerTranslator(getTranslator());
+		MembersOverviewDataModel userTableModel = new MembersOverviewDataModel(oks, resultingPropertyHandlers,
+				isAdministrativeUser, getLocale(), tableColumnModel);
+		FlexiTableElement tableEl = uifactory.addTableElement(getWindowControl(), "users", userTableModel, myTrans, formLayout);
+		tableEl.setCustomizeColumns(false);
+	}
+	
+	private void loadModel(List<String> keys) {
+		oks = new ArrayList<Identity>();
+		List<String> isanonymous = new ArrayList<String>();
+		notfounds = new ArrayList<String>();
+
+		SecurityGroup anonymousSecGroup = securityManager.findSecurityGroupByName(Constants.GROUP_ANONYMOUS);
+		for (String identityKey : keys) {
+			Identity ident = securityManager.loadIdentityByKey(Long.parseLong(identityKey));
+			if (ident == null) { // not found, add to not-found-list
+				notfounds.add(identityKey);
+			} else if (securityManager.isIdentityInSecurityGroup(ident, anonymousSecGroup)) {
+				isanonymous.add(identityKey);
+			} else if (!PersistenceHelper.containsPersistable(oks, ident)) {
+				oks.add(ident);
+			}
+		}
+	}
+	
+	private void loadModel(String inp) {
+		oks = new ArrayList<Identity>();
+		notfounds = new ArrayList<String>();
+
+		SecurityGroup anonymousSecGroup = securityManager.findSecurityGroupByName(Constants.GROUP_ANONYMOUS);
+
+		List<String> identList = new ArrayList<String>();
+		String[] lines = inp.split("\r?\n");
+		for (int i = 0; i < lines.length; i++) {
+			String username = lines[i].trim();
+			if(username.length() > 0) {
+				identList.add(username);
+			}
+		}
+		
+		//search by institutionalUserIdentifier, case sensitive
+		List<Identity> institutIdentities = securityManager.findIdentitiesByNumber(identList);
+		for(Identity identity:institutIdentities) {
+			String userIdent = identity.getUser().getProperty(UserConstants.INSTITUTIONALUSERIDENTIFIER, null);
+			if(userIdent != null) {
+				identList.remove(userIdent);
+			}
+			if (!PersistenceHelper.containsPersistable(oks, identity)
+					&& !securityManager.isIdentityInSecurityGroup(identity, anonymousSecGroup)) {
+				oks.add(identity);
+			}
+		}
+		// make a lowercase copy of identList for processing username and email
+		List<String> identListLowercase = new ArrayList<String>(identList.size());
+		for (String ident:identList) {
+			identListLowercase.add(ident.toLowerCase());
+		}
+		//search by names, must be lower case
+		List<Identity> identities = securityManager.findIdentitiesByName(identListLowercase);
+		for(Identity identity:identities) {
+			identListLowercase.remove(identity.getName().toLowerCase());
+			if (!PersistenceHelper.containsPersistable(oks, identity)
+					&& !securityManager.isIdentityInSecurityGroup(identity, anonymousSecGroup)) {
+				oks.add(identity);
+			}
+		}
+		
+		//search by email, case insensitive
+		List<Identity> mailIdentities = userManager.findIdentitiesByEmail(identListLowercase);
+		for(Identity identity:mailIdentities) {
+			String email = identity.getUser().getProperty(UserConstants.EMAIL, null);
+			if(email != null) {
+				identListLowercase.remove(email.toLowerCase());
+			}
+			String institutEmail = identity.getUser().getProperty(UserConstants.INSTITUTIONALEMAIL, null);
+			if(institutEmail != null) {
+				identListLowercase.remove(institutEmail.toLowerCase());
+			}
+			if (!PersistenceHelper.containsPersistable(oks, identity)
+					&& !securityManager.isIdentityInSecurityGroup(identity, anonymousSecGroup)) {
+				oks.add(identity);
+			}
+		}
+		
+		notfounds.addAll(identListLowercase);
+	}
+
+	public boolean validate() {
+		return true;
+	}
+
+	@Override
+	protected void formOK(UserRequest ureq) {
+		addToRunContext("members", oks);
+		fireEvent(ureq, StepsEvent.ACTIVATE_NEXT);
+	}
+
+	@Override
+	protected void doDispose() {
+		//
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/org/olat/modules/portfolio/ui/wizard/_content/access_rights_step.html b/src/main/java/org/olat/modules/portfolio/ui/wizard/_content/access_rights_step.html
new file mode 100644
index 00000000000..4dc15f93ce0
--- /dev/null
+++ b/src/main/java/org/olat/modules/portfolio/ui/wizard/_content/access_rights_step.html
@@ -0,0 +1 @@
+$r.render("access_rights")
\ No newline at end of file
diff --git a/src/main/java/org/olat/modules/portfolio/ui/wizard/_content/import_search.html b/src/main/java/org/olat/modules/portfolio/ui/wizard/_content/import_search.html
new file mode 100644
index 00000000000..6ef3ecad8c2
--- /dev/null
+++ b/src/main/java/org/olat/modules/portfolio/ui/wizard/_content/import_search.html
@@ -0,0 +1 @@
+$r.render("search")
\ No newline at end of file
diff --git a/src/main/java/org/olat/modules/portfolio/ui/wizard/_content/mail_template.html b/src/main/java/org/olat/modules/portfolio/ui/wizard/_content/mail_template.html
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/src/main/java/org/olat/modules/portfolio/ui/wizard/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/modules/portfolio/ui/wizard/_i18n/LocalStrings_de.properties
new file mode 100644
index 00000000000..88cd94609c4
--- /dev/null
+++ b/src/main/java/org/olat/modules/portfolio/ui/wizard/_i18n/LocalStrings_de.properties
@@ -0,0 +1 @@
+#Mon Mar 02 09:54:04 CET 2009
diff --git a/src/main/java/org/olat/modules/portfolio/ui/wizard/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/modules/portfolio/ui/wizard/_i18n/LocalStrings_en.properties
new file mode 100644
index 00000000000..03663cfab37
--- /dev/null
+++ b/src/main/java/org/olat/modules/portfolio/ui/wizard/_i18n/LocalStrings_en.properties
@@ -0,0 +1,5 @@
+#Tue Apr 10 10:28:36 CEST 2012
+add.choose.title=Choose a user
+add.confirm.title=Overview
+add.permission.title=Access rights
+add.mail.title=Send mail
-- 
GitLab