/**
* <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 java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.olat.basesecurity.Invitation;
import org.olat.core.gui.UserRequest;
import org.olat.core.gui.components.Component;
import org.olat.core.gui.components.dropdown.Dropdown;
import org.olat.core.gui.components.dropdown.DropdownOrientation;
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.closablewrapper.CloseableModalController;
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.core.util.StringHelper;
import org.olat.core.util.mail.ContactList;
import org.olat.core.util.mail.MailBundle;
import org.olat.core.util.mail.MailContext;
import org.olat.core.util.mail.MailContextImpl;
import org.olat.core.util.mail.MailManager;
import org.olat.core.util.mail.MailTemplate;
import org.olat.core.util.mail.MailerResult;
import org.olat.login.LoginModule;
import org.olat.modules.portfolio.AssessmentSection;
import org.olat.modules.portfolio.Binder;
import org.olat.modules.portfolio.BinderConfiguration;
import org.olat.modules.portfolio.BinderSecurityCallback;
import org.olat.modules.portfolio.Page;
import org.olat.modules.portfolio.PortfolioElement;
import org.olat.modules.portfolio.PortfolioElementType;
import org.olat.modules.portfolio.PortfolioRoles;
import org.olat.modules.portfolio.PortfolioService;
import org.olat.modules.portfolio.Section;
import org.olat.modules.portfolio.SectionStatus;
import org.olat.modules.portfolio.model.AccessRightChange;
import org.olat.modules.portfolio.model.AccessRights;
import org.olat.modules.portfolio.ui.event.AccessRightsEvent;
import org.olat.modules.portfolio.ui.renderer.PortfolioRendererHelper;
import org.olat.modules.portfolio.ui.wizard.AccessRightsContext;
import org.olat.modules.portfolio.ui.wizard.AddMember_1_ChooseMemberStep;
import org.olat.user.UserManager;
import org.springframework.beans.factory.annotation.Autowired;
/**
*
* Initial date: 13.06.2016<br>
* @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
*
*/
public class PublishController extends BasicController implements TooledController {
private Dropdown accessDropdown;
private Link addAccessRightsLink, addInvitationLink;
private final VelocityContainer mainVC;
private final TooledStackedPanel stackPanel;
private CloseableModalController cmc;
private StepsMainRunController addMembersWizard;
private AccessRightsEditController editAccessRightsCtrl;
private InvitationEditRightsController addInvitationCtrl;
private InvitationEmailController addInvitationEmailCtrl;
private int counter;
private Binder binder;
private PortfolioElementRow binderRow;
private final BinderConfiguration config;
private final BinderSecurityCallback secCallback;
@Autowired
private LoginModule loginModule;
@Autowired
private MailManager mailManager;
@Autowired
private UserManager userManager;
@Autowired
private PortfolioService portfolioService;
public PublishController(UserRequest ureq, WindowControl wControl, TooledStackedPanel stackPanel,
BinderSecurityCallback secCallback, Binder binder, BinderConfiguration config) {
super(ureq, wControl);
this.binder = binder;
this.config = config;
this.secCallback = secCallback;
this.stackPanel = stackPanel;
mainVC = createVelocityContainer("publish");
mainVC.contextPut("binderTitle", StringHelper.escapeHtml(binder.getTitle()));
binderRow = new PortfolioElementRow(binder, null);
mainVC.contextPut("binderRow", binderRow);
putInitialPanel(mainVC);
reloadData();
}
@Override
public void initTools() {
if(secCallback.canEditAccessRights(binder)) {
accessDropdown = new Dropdown("access.rights", "access.rights", false, getTranslator());
accessDropdown.setIconCSS("o_icon o_icon-fw o_icon_new_portfolio");
accessDropdown.setElementCssClass("o_sel_pf_access");
accessDropdown.setOrientation(DropdownOrientation.right);
addAccessRightsLink = LinkFactory.createToolLink("add.member", translate("add.member"), this);
addAccessRightsLink.setIconLeftCSS("o_icon o_icon-lg o_icon_new_portfolio");
addAccessRightsLink.setElementCssClass("o_sel_pf_access_member");
accessDropdown.addComponent(addAccessRightsLink);
if(loginModule.isInvitationEnabled()) {
addInvitationLink = LinkFactory.createToolLink("add.invitation", translate("add.invitation"), this);
addInvitationLink.setIconLeftCSS("o_icon o_icon-lg o_icon_new_portfolio");
addInvitationLink.setElementCssClass("o_sel_pf_access_invitation");
accessDropdown.addComponent(addInvitationLink);
}
stackPanel.addTool(accessDropdown, Align.right);
}
}
@Override
protected void doDispose() {
//
}
public void reloadData() {
binderRow.getChildren().clear();
binderRow.getAccessRights().clear();
List<AccessRights> rights = portfolioService.getAccessRights(binder);
boolean canEditBinderAccessRights = secCallback.canEditAccessRights(binder);
for(AccessRights right:rights) {
if(right.getSectionKey() == null && right.getPageKey() == null) {
if(PortfolioRoles.invitee.equals(right.getRole())) {
continue;//only access
}
Link editLink = null;
if(canEditBinderAccessRights && !PortfolioRoles.owner.equals(right.getRole())) {
String id = "edit_" + (counter++);
editLink = LinkFactory.createLink(id, id, "edit_access", "edit", getTranslator(), mainVC, this, Link.LINK);
}
binderRow.getAccessRights().add(new AccessRightsRow(binder, right, editLink));
}
}
List<AssessmentSection> assessmentSections = portfolioService.getAssessmentSections(binder, getIdentity());
Map<Section,AssessmentSection> sectionToAssessmentSectionMap = new HashMap<>();
for(AssessmentSection assessmentSection:assessmentSections) {
sectionToAssessmentSectionMap.put(assessmentSection.getSection(), assessmentSection);
}
//sections
List<Section> sections = portfolioService.getSections(binder);
Map<Long,PortfolioElementRow> sectionMap = new HashMap<>();
for(Section section:sections) {
boolean canEditSectionAccessRights = secCallback.canEditAccessRights(section);
boolean canViewSectionAccessRights = secCallback.canViewAccessRights(section);
if(canEditSectionAccessRights || canViewSectionAccessRights) {
PortfolioElementRow sectionRow = new PortfolioElementRow(section, sectionToAssessmentSectionMap.get(section));
binderRow.getChildren().add(sectionRow);
sectionMap.put(section.getKey(), sectionRow);
for(AccessRights right:rights) {
if(section.getKey().equals(right.getSectionKey()) && right.getPageKey() == null) {
Link editLink = null;
if(canEditSectionAccessRights && !PortfolioRoles.owner.equals(right.getRole())) {
String id = "edit_" + (counter++);
editLink = LinkFactory.createLink(id, id, "edit_access", "edit", getTranslator(), mainVC, this, Link.LINK);
sectionRow.getAccessRights().add(new AccessRightsRow(section, right, editLink));
}
}
}
}
}
//pages
List<Page> pages = portfolioService.getPages(binder, null);
for(Page page:pages) {
boolean canEditPageAccessRights = secCallback.canEditAccessRights(page);
boolean canViewPageAccessRights = secCallback.canViewAccessRights(page);
if(canEditPageAccessRights || canViewPageAccessRights) {
Section section = page.getSection();
PortfolioElementRow sectionRow = sectionMap.get(section.getKey());
if(sectionRow == null) {
logError("Section not found: " + section.getKey() + " of page: " + page.getKey(), null);
continue;
}
PortfolioElementRow pageRow = new PortfolioElementRow(page, null);
sectionRow.getChildren().add(pageRow);
for(AccessRights right:rights) {
if(page.getKey().equals(right.getPageKey())) {
Link editLink = null;
if(canEditPageAccessRights && !PortfolioRoles.owner.equals(right.getRole())) {
String id = "edit_" + (counter++);
editLink = LinkFactory.createLink(id, id, "edit_access", "edit", getTranslator(), mainVC, this, Link.LINK);
pageRow.getAccessRights().add(new AccessRightsRow(page, right, editLink));
}
}
}
}
}
mainVC.setDirty(true);
}
@Override
protected void event(UserRequest ureq, Component source, Event event) {
if(addAccessRightsLink == source) {
doAddAccessRights(ureq);
} else if(addInvitationLink == source) {
doAddInvitationEmail(ureq);
} else if(source instanceof Link) {
Link link = (Link)source;
String cmd = link.getCommand();
if("edit_access".equals(cmd)) {
AccessRightsRow row = (AccessRightsRow)link.getUserObject();
if(PortfolioRoles.invitee.name().equals(row.getRole())
|| PortfolioRoles.readInvitee.name().equals(row.getRole())) {
doEditInvitation(ureq, row.getIdentity());
} else {
doEditAccessRights(ureq, row.getElement(), row.getIdentity());
}
}
}
}
@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();
if(event == Event.DONE_EVENT || event == Event.CHANGED_EVENT) {
reloadData();
}
cleanUp();
}
} else if(addInvitationEmailCtrl == source) {
if(event == Event.CANCELLED_EVENT || event == Event.DONE_EVENT || event == Event.CHANGED_EVENT) {
String email = addInvitationEmailCtrl.getEmail();
cmc.deactivate();
cleanUp();
if(event == Event.DONE_EVENT) {
doAddInvitation(ureq, email);
}
}
} else if(addInvitationCtrl == source) {
if(event == Event.CANCELLED_EVENT || event == Event.DONE_EVENT || event == Event.CHANGED_EVENT) {
getWindowControl().pop();
if(event == Event.DONE_EVENT || event == Event.CHANGED_EVENT) {
reloadData();
}
cleanUp();
}
} else if(editAccessRightsCtrl == source) {
if(event == Event.DONE_EVENT) {
List<AccessRightChange> changes = editAccessRightsCtrl.getChanges();
List<Identity> identities = Collections.singletonList(editAccessRightsCtrl.getMember());
portfolioService.changeAccessRights(identities, changes);
reloadData();
} else if(AccessRightsEvent.REMOVE_ALL_RIGHTS.equals(event.getCommand())) {
portfolioService.removeAccessRights(binder, editAccessRightsCtrl.getMember());
reloadData();
}
cmc.deactivate();
cleanUp();
} else if(cmc == source) {
cleanUp();
}
super.event(ureq, source, event);
}
private void cleanUp() {
removeAsListenerAndDispose(addInvitationEmailCtrl);
removeAsListenerAndDispose(editAccessRightsCtrl);
removeAsListenerAndDispose(addInvitationCtrl);
removeAsListenerAndDispose(addMembersWizard);
removeAsListenerAndDispose(cmc);
addInvitationEmailCtrl = null;
editAccessRightsCtrl = null;
addInvitationCtrl = null;
addMembersWizard = null;
cmc = null;
}
private void doAddInvitationEmail(UserRequest ureq) {
if(addInvitationEmailCtrl != null) return;
addInvitationEmailCtrl = new InvitationEmailController(ureq, getWindowControl(), binder);
listenTo(addInvitationEmailCtrl);
String title = translate("add.invitation");
cmc = new CloseableModalController(getWindowControl(), null, addInvitationEmailCtrl.getInitialComponent(), true, title, true);
listenTo(cmc);
cmc.activate();
}
private void doAddInvitation(UserRequest ureq, String email) {
if(addInvitationCtrl != null) return;
addInvitationCtrl = new InvitationEditRightsController(ureq, getWindowControl(), binder, email);
listenTo(addInvitationCtrl);
String title = translate("add.invitation");
cmc = new CloseableModalController(getWindowControl(), null, addInvitationCtrl.getInitialComponent(), true, title, true);
listenTo(cmc);
cmc.activate();
}
private void doEditInvitation(UserRequest ureq, Identity invitee) {
if(addInvitationCtrl != null) return;
addInvitationCtrl = new InvitationEditRightsController(ureq, getWindowControl(), binder, invitee);
listenTo(addInvitationCtrl);
String title = translate("add.invitation");
cmc = new CloseableModalController(getWindowControl(), null, addInvitationCtrl.getInitialComponent(), true, title, true);
listenTo(cmc);
cmc.activate();
}
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) {
AccessRightsContext rightsContext = (AccessRightsContext)runContext.get("rightsContext");
MailTemplate mailTemplate = (MailTemplate)runContext.get("mailTemplate");
addMembers(rightsContext, mailTemplate);
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 doEditAccessRights(UserRequest ureq, PortfolioElement element, Identity member) {
if(editAccessRightsCtrl != null) return;
boolean canEdit = secCallback.canEditAccessRights(element);
editAccessRightsCtrl = new AccessRightsEditController(ureq, getWindowControl(), binder, member, canEdit);
listenTo(editAccessRightsCtrl);
String title = translate("edit.access.rights");
cmc = new CloseableModalController(getWindowControl(), null, editAccessRightsCtrl.getInitialComponent(), true, title, true);
listenTo(cmc);
cmc.activate();
}
private void addMembers(AccessRightsContext rightsContext, MailTemplate mailTemplate) {
List<Identity> identities = rightsContext.getIdentities();
List<AccessRightChange> changes = rightsContext.getAccessRightChanges();
portfolioService.changeAccessRights(identities, changes);
if(mailTemplate != null) {
sendInvitation(identities, mailTemplate);
}
reloadData();
}
private void sendInvitation(List<Identity> identities, MailTemplate mailTemplate) {
ContactList contactList = new ContactList("Invitation");
contactList.addAllIdentites(identities);
boolean success = false;
try {
MailContext context = new MailContextImpl(binder, null, getWindowControl().getBusinessControl().getAsString());
MailBundle bundle = new MailBundle();
bundle.setContext(context);
bundle.setFromId(getIdentity());
bundle.setContactList(contactList);
bundle.setContent(mailTemplate.getSubjectTemplate(), mailTemplate.getBodyTemplate());
MailerResult result = mailManager.sendMessage(bundle);
success = result.isSuccessful();
} catch (Exception e) {
logError("Error on sending invitation mail to contactlist, invalid address.", e);
}
if (success) {
showInfo("invitation.mail.success");
} else {
showError("invitation.mail.failure");
}
}
public class AccessRightsRow {
private final AccessRights rights;
private final PortfolioElement element;
private String fullName;
private Link editLink;
public AccessRightsRow(PortfolioElement element, AccessRights rights, Link editLink) {
this.rights = rights;
this.editLink = editLink;
this.element = element;
if(rights.getInvitation() == null) {
fullName = userManager.getUserDisplayName(rights.getIdentity());
} else {
Invitation invitation = rights.getInvitation();
fullName = userManager.getUserDisplayName(invitation.getFirstName(), invitation.getLastName()) + " :: " + invitation.getKey();
}
if(editLink != null) {
editLink.setUserObject(this);
}
}
public String getRole() {
return rights.getRole().name();
}
public Identity getIdentity() {
return rights.getIdentity();
}
public PortfolioElement getElement() {
return element;
}
public String getFullName() {
return fullName;
}
public String getCssClass() {
if(PortfolioRoles.reviewer.equals(rights.getRole())) {
return "o_icon o_icon_reviewer o_icon-fw";
}
return "o_icon o_icon_user o_icon-fw";
}
public Link getEditLink() {
return editLink;
}
public String getExplanation() {
String explanation = null;
if(PortfolioRoles.owner.equals(rights.getRole())) {
explanation = translate("access.rights.owner.long");
} else if(PortfolioRoles.coach.equals(rights.getRole())) {
explanation = translate("access.rights.coach.long");
} else if(PortfolioRoles.reviewer.equals(rights.getRole())) {
explanation = translate("access.rights.reviewer.long");
} else if(PortfolioRoles.readInvitee.equals(rights.getRole())) {
explanation = translate("access.rights.invitee.long");
}
return explanation;
}
}
public class PortfolioElementRow {
private final PortfolioElement element;
private List<PortfolioElementRow> children;
private List<AccessRightsRow> accessRights = new ArrayList<>();
private final AssessmentSection assessmentSection;
public PortfolioElementRow(PortfolioElement element, AssessmentSection assessmentSection) {
this.element = element;
this.assessmentSection = assessmentSection;
}
public boolean isAssessable() {
return config.isAssessable();
}
public String getTitle() {
return element.getTitle();
}
public String getCssClassStatus() {
if(element.getType() == PortfolioElementType.section) {
Section section = (Section)element;
return section.getSectionStatus() == null
? SectionStatus.notStarted.cssClass() : section.getSectionStatus().cssClass();
}
return "";
}
public String getFormattedResult() {
if(element.getType() == PortfolioElementType.section) {
return PortfolioRendererHelper.getFormattedResult(assessmentSection, getTranslator());
}
return "";
}
public List<AccessRightsRow> getAccessRights() {
return accessRights;
}
public List<PortfolioElementRow> getChildren() {
if(children == null) {
children = new ArrayList<>();
}
return children;
}
}
}