/**
* <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.coach.ui;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.olat.NewControllerFactory;
import org.olat.basesecurity.BaseSecurityModule;
import org.olat.core.gui.UserRequest;
import org.olat.core.gui.components.Component;
import org.olat.core.gui.components.form.flexible.FormItem;
import org.olat.core.gui.components.form.flexible.FormItemContainer;
import org.olat.core.gui.components.form.flexible.elements.FlexiTableElement;
import org.olat.core.gui.components.form.flexible.impl.FormBasicController;
import org.olat.core.gui.components.form.flexible.impl.FormEvent;
import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer;
import org.olat.core.gui.components.form.flexible.impl.elements.table.DateFlexiCellRenderer;
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.components.form.flexible.impl.elements.table.SelectionEvent;
import org.olat.core.gui.components.link.Link;
import org.olat.core.gui.components.link.LinkFactory;
import org.olat.core.gui.components.stack.PopEvent;
import org.olat.core.gui.components.stack.TooledController;
import org.olat.core.gui.components.stack.TooledStackedPanel;
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.closablewrapper.CloseableModalController;
import org.olat.core.gui.control.generic.dtabs.Activateable2;
import org.olat.core.id.Identity;
import org.olat.core.id.OLATResourceable;
import org.olat.core.id.context.BusinessControl;
import org.olat.core.id.context.BusinessControlFactory;
import org.olat.core.id.context.ContextEntry;
import org.olat.core.id.context.StateEntry;
import org.olat.core.util.StringHelper;
import org.olat.core.util.coordinate.CoordinatorManager;
import org.olat.core.util.event.GenericEventListener;
import org.olat.core.util.mail.ContactList;
import org.olat.core.util.mail.ContactMessage;
import org.olat.core.util.resource.OresHelper;
import org.olat.course.assessment.bulk.PassedCellRenderer;
import org.olat.course.certificate.CertificateEvent;
import org.olat.course.certificate.CertificateLight;
import org.olat.course.certificate.CertificatesManager;
import org.olat.course.certificate.ui.DownloadCertificateCellRenderer;
import org.olat.modules.assessment.ui.ScoreCellRenderer;
import org.olat.modules.co.ContactFormController;
import org.olat.modules.coach.CoachingService;
import org.olat.modules.coach.model.EfficiencyStatementEntry;
import org.olat.modules.coach.model.IdentityResourceKey;
import org.olat.modules.coach.model.StudentStatEntry;
import org.olat.modules.coach.ui.EfficiencyStatementEntryTableDataModel.Columns;
import org.olat.repository.RepositoryEntry;
import org.olat.user.UserManager;
import org.olat.user.propertyhandlers.UserPropertyHandler;
import org.springframework.beans.factory.annotation.Autowired;
/**
*
* Description:<br>
* Overview of all students under the scrutiny of a coach.
*
* <P>
* Initial Date: 8 févr. 2012 <br>
*
* @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
*/
public class StudentCoursesController extends FormBasicController implements Activateable2, GenericEventListener, TooledController {
private final Link homeLink, contactLink;
private Link nextStudent, detailsStudentCmp, previousStudent;
private FlexiTableElement tableEl;
private final TooledStackedPanel stackPanel;
private EfficiencyStatementEntryTableDataModel model;
private CloseableModalController cmc;
private ContactFormController contactCtrl;
private EfficiencyStatementDetailsController statementCtrl;
private boolean hasChanged = false;
private final int index;
private final int numOfStudents;
private final Identity student;
private final boolean fullAccess;
private final StudentStatEntry statEntry;
private final boolean isAdministrativeUser;
private final List<UserPropertyHandler> userPropertyHandlers;
@Autowired
private UserManager userManager;
@Autowired
private CoachingService coachingService;
@Autowired
private BaseSecurityModule securityModule;
@Autowired
private CertificatesManager certificatesManager;
public StudentCoursesController(UserRequest ureq, WindowControl wControl, TooledStackedPanel stackPanel,
StudentStatEntry statEntry, Identity student, int index, int numOfStudents, boolean fullAccess) {
super(ureq, wControl, "student_course_list");
setTranslator(userManager.getPropertyHandlerTranslator(getTranslator()));
isAdministrativeUser = securityModule.isUserAllowedAdminProps(ureq.getUserSession().getRoles());
userPropertyHandlers = userManager.getUserPropertyHandlersFor(UserListController.usageIdentifyer, isAdministrativeUser);
this.index = index;
this.student = student;
this.statEntry = statEntry;
this.fullAccess = fullAccess;
this.stackPanel = stackPanel;
this.numOfStudents = numOfStudents;
initForm(ureq);
loadModel();
contactLink = LinkFactory.createButton("contact.link", flc.getFormItemComponent(), this);
contactLink.setIconLeftCSS("o_icon o_icon_mail");
flc.getFormItemComponent().put("contact", contactLink);
homeLink = LinkFactory.createButton("home.link", flc.getFormItemComponent(), this);
homeLink.setIconLeftCSS("o_icon o_icon_home");
flc.getFormItemComponent().put("home", homeLink);
CoordinatorManager.getInstance().getCoordinator().getEventBus()
.registerFor(this, getIdentity(), CertificatesManager.ORES_CERTIFICATE_EVENT);
}
@Override
public void initTools() {
previousStudent = LinkFactory.createToolLink("previous.student", translate("previous.student"), this);
previousStudent.setIconLeftCSS("o_icon o_icon_previous");
previousStudent.setEnabled(numOfStudents > 1);
stackPanel.addTool(previousStudent);
String fullName = StringHelper.escapeHtml(userManager.getUserDisplayName(student));
String details = translate("students.details", new String[]{
fullName, Integer.toString(index + 1), Integer.toString(numOfStudents)
});
detailsStudentCmp = LinkFactory.createToolLink("details.student", details, this);
detailsStudentCmp.setIconLeftCSS("o_icon o_icon_user");
stackPanel.addTool(detailsStudentCmp);
nextStudent = LinkFactory.createToolLink("next.student", translate("next.student"), this);
nextStudent.setIconLeftCSS("o_icon o_icon_next");
nextStudent.setEnabled(numOfStudents > 1);
stackPanel.addTool(nextStudent);
stackPanel.addListener(this);
}
@Override
protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
if(formLayout instanceof FormLayoutContainer) {
FormLayoutContainer layoutCont = (FormLayoutContainer)formLayout;
String fullName = userManager.getUserDisplayName(student);
layoutCont.contextPut("studentName", StringHelper.escapeHtml(fullName));
}
//add the table
FlexiTableColumnModel columnsModel = FlexiTableDataModelFactory.createFlexiTableColumnModel();
if(isAdministrativeUser) {
columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(false, Columns.name, "select"));
}
int colIndex = UserListController.USER_PROPS_OFFSET;
for (int i = 0; i < userPropertyHandlers.size(); i++) {
UserPropertyHandler userPropertyHandler = userPropertyHandlers.get(i);
columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(false, userPropertyHandler.i18nColumnDescriptorLabelKey(), colIndex++, "select",
true, userPropertyHandler.i18nColumnDescriptorLabelKey()));
}
columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(Columns.repoName, "select"));
columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(Columns.passed, new PassedCellRenderer()));
columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(Columns.score, new ScoreCellRenderer()));
columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(Columns.certificate, new DownloadCertificateCellRenderer()));
columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(Columns.recertification, new DateFlexiCellRenderer(getLocale())));
columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(Columns.progress, new ProgressRenderer(true, getTranslator())));
columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(Columns.lastModification));
model = new EfficiencyStatementEntryTableDataModel(columnsModel);
tableEl = uifactory.addTableElement(getWindowControl(), "table", model, 20, false, getTranslator(), formLayout);
tableEl.setExportEnabled(true);
tableEl.setEmtpyTableMessageKey("error.no.found");
tableEl.setAndLoadPersistedPreferences(ureq, "fStudentCourseListController");
}
@Override
protected void doDispose() {
stackPanel.removeListener(this);
CoordinatorManager.getInstance().getCoordinator().getEventBus()
.deregisterFor(this, CertificatesManager.ORES_CERTIFICATE_EVENT);
}
@Override
public void event(Event event) {
if(event instanceof CertificateEvent) {
CertificateEvent ce = (CertificateEvent)event;
if(student.getKey().equals(ce.getOwnerKey())) {
updateCertificate(ce.getCertificateKey());
}
}
}
private void updateCertificate(Long certificateKey) {
CertificateLight certificate = certificatesManager.getCertificateLightById(certificateKey);
model.putCertificate(certificate);
}
public StudentStatEntry getEntry() {
return statEntry;
}
private List<EfficiencyStatementEntry> loadModel() {
List<RepositoryEntry> courses = fullAccess ? coachingService.getUserCourses(student)
: coachingService.getStudentsCourses(getIdentity(), student);
List<EfficiencyStatementEntry> statements = coachingService.getEfficencyStatements(student, courses, userPropertyHandlers, getLocale());
List<CertificateLight> certificates = certificatesManager.getLastCertificates(student);
ConcurrentMap<IdentityResourceKey, CertificateLight> certificateMap = new ConcurrentHashMap<>();
for(CertificateLight certificate:certificates) {
IdentityResourceKey key = new IdentityResourceKey(student.getKey(), certificate.getOlatResourceKey());
certificateMap.put(key, certificate);
}
model.setObjects(statements, certificateMap);
tableEl.reset();
tableEl.reloadData();
return statements;
}
private void reloadModel() {
if(hasChanged) {
//reload
loadModel();
hasChanged = false;
}
}
@Override
protected void formOK(UserRequest ureq) {
//
}
@Override
protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) {
if(tableEl == source) {
if(event instanceof SelectionEvent) {
SelectionEvent se = (SelectionEvent)event;
String cmd = se.getCommand();
EfficiencyStatementEntry selectedRow = model.getObject(se.getIndex());
if("select".equals(cmd)) {
selectDetails(ureq, selectedRow);
}
}
}
super.formInnerEvent(ureq, source, event);
}
@Override
public void event(UserRequest ureq, Component source, Event event) {
if(previousStudent == source || nextStudent == source) {
fireEvent(ureq, event);
} else if (source == homeLink) {
openHome(ureq);
} else if (source == contactLink) {
contact(ureq);
} else if(stackPanel == source) {
if(event instanceof PopEvent) {
PopEvent pe = (PopEvent)event;
if(pe.getController() == statementCtrl && hasChanged) {
reloadModel();
}
}
}
super.event(ureq, source, event);
}
@Override
protected void event(UserRequest ureq, Controller source, Event event) {
if (source == statementCtrl) {
if(event == Event.CHANGED_EVENT) {
hasChanged = true;
fireEvent(ureq, event);
} else if ("next".equals(event.getCommand())) {
nextEntry(ureq);
} else if ("previous".equals(event.getCommand())) {
previousEntry(ureq);
}
} else if (source == cmc) {
removeAsListenerAndDispose(cmc);
removeAsListenerAndDispose(contactCtrl);
cmc = null;
contactCtrl = null;
} else if (source == contactCtrl) {
cmc.deactivate();
removeAsListenerAndDispose(cmc);
removeAsListenerAndDispose(contactCtrl);
cmc = null;
contactCtrl = null;
}
super.event(ureq, source, event);
}
@Override
public void activate(UserRequest ureq, List<ContextEntry> entries, StateEntry state) {
if(entries == null || entries.isEmpty()) return;
ContextEntry ce = entries.get(0);
OLATResourceable ores = ce.getOLATResourceable();
if("RepositoryEntry".equals(ores.getResourceableTypeName())) {
Long entryKey = ores.getResourceableId();
for(EfficiencyStatementEntry entry:model.getObjects()) {
if(entryKey.equals(entry.getCourse().getKey())) {
selectDetails(ureq, entry);
statementCtrl.activate(ureq, entries.subList(1, entries.size()), ce.getTransientState());
break;
}
}
}
}
private void contact(UserRequest ureq) {
removeAsListenerAndDispose(cmc);
ContactMessage cmsg = new ContactMessage(getIdentity());
ContactList contactList = new ContactList("to");
contactList.add(student);
cmsg.addEmailTo(contactList);
contactCtrl = new ContactFormController(ureq, getWindowControl(), true, false, false, cmsg);
listenTo(contactCtrl);
cmc = new CloseableModalController(getWindowControl(), translate("close"), contactCtrl.getInitialComponent());
cmc.activate();
listenTo(cmc);
}
private void nextEntry(UserRequest ureq) {
EfficiencyStatementEntry currentEntry = statementCtrl.getEntry();
int nextIndex = model.getObjects().indexOf(currentEntry) + 1;
if(nextIndex < 0 || nextIndex >= model.getRowCount()) {
nextIndex = 0;
}
EfficiencyStatementEntry nextEntry = model.getObject(nextIndex);
selectDetails(ureq, nextEntry);
}
private void previousEntry(UserRequest ureq) {
EfficiencyStatementEntry currentEntry = statementCtrl.getEntry();
int previousIndex = model.getObjects().indexOf(currentEntry) - 1;
if(previousIndex < 0 || previousIndex >= model.getRowCount()) {
previousIndex = model.getRowCount() - 1;
}
EfficiencyStatementEntry previousEntry = model.getObject(previousIndex);
selectDetails(ureq, previousEntry);
}
private void selectDetails(UserRequest ureq, EfficiencyStatementEntry entry) {
boolean selectAssessmentTool = false;
if(statementCtrl != null) {
selectAssessmentTool = statementCtrl.isAssessmentToolSelected();
}
OLATResourceable ores = OresHelper.createOLATResourceableInstance(RepositoryEntry.class, entry.getCourse().getKey());
WindowControl bwControl = addToHistory(ureq, ores, null);
String displayName = entry.getCourseDisplayName();
int entryIndex = model.getObjects().indexOf(entry);
String details = translate("students.details", new String[] {
displayName, String.valueOf(entryIndex), String.valueOf(model.getRowCount())
});
statementCtrl = new EfficiencyStatementDetailsController(ureq, bwControl, stackPanel,
entry, student, details, entryIndex, model.getRowCount(), selectAssessmentTool);
listenTo(statementCtrl);
stackPanel.popUpToController(this);
stackPanel.pushController(displayName, statementCtrl);
}
private void openHome(UserRequest ureq) {
List<ContextEntry> ces = new ArrayList<ContextEntry>(4);
ces.add(BusinessControlFactory.getInstance().createContextEntry(student));
BusinessControl bc = BusinessControlFactory.getInstance().createFromContextEntries(ces);
WindowControl bwControl = BusinessControlFactory.getInstance().createBusinessWindowControl(bc, getWindowControl());
NewControllerFactory.getInstance().launch(ureq, bwControl);
}
}