Skip to content
Snippets Groups Projects
Commit 614589d7 authored by srosse's avatar srosse
Browse files

OO-2636: add new item in coaching tool to search the lectures

parent 102ffa12
No related branches found
No related tags found
No related merge requests found
Showing
with 1095 additions and 4 deletions
......@@ -43,7 +43,10 @@ import org.olat.core.id.context.StateEntry;
import org.olat.core.logging.activity.ThreadLocalUserActivityLogger;
import org.olat.core.util.resource.OresHelper;
import org.olat.core.util.tree.TreeHelper;
import org.olat.modules.lecture.LectureModule;
import org.olat.modules.lecture.ui.LecturesSearchController;
import org.olat.util.logging.activity.LoggingResourceable;
import org.springframework.beans.factory.annotation.Autowired;
/**
*
......@@ -64,6 +67,10 @@ public class CoachMainController extends MainLayoutBasicController implements Ac
private CourseListController courseListCtrl;
private StudentListController studentListCtrl;
private LayoutMain3ColsController columnLayoutCtr;
private LecturesSearchController lecturesSearchCtrl;
@Autowired
private LectureModule lectureModule;
public CoachMainController(UserRequest ureq, WindowControl control) {
super(ureq, control);
......@@ -145,6 +152,15 @@ public class CoachMainController extends MainLayoutBasicController implements Ac
listenTo(courseListCtrl);
}
selectedCtrl = courseListCtrl;
} else if("lectures".equalsIgnoreCase(cmd)) {
if(lecturesSearchCtrl == null) {
OLATResourceable ores = OresHelper.createOLATResourceableInstance("Lectures", 0l);
ThreadLocalUserActivityLogger.addLoggingResourceInfo(LoggingResourceable.wrapBusinessPath(ores));
WindowControl bwControl = BusinessControlFactory.getInstance().createBusinessWindowControl(ores, null, getWindowControl());
lecturesSearchCtrl = new LecturesSearchController(ureq, bwControl, content);
listenTo(lecturesSearchCtrl);
}
selectedCtrl = lecturesSearchCtrl;
} else if("search".equalsIgnoreCase(cmd)) {
if(userSearchCtrl == null) {
OLATResourceable ores = OresHelper.createOLATResourceableInstance("Search", 0l);
......@@ -191,6 +207,14 @@ public class CoachMainController extends MainLayoutBasicController implements Ac
courses.setAltText(translate("courses.menu.title.alt"));
root.addChild(courses);
if(lectureModule.isEnabled()) {
GenericTreeNode lectures = new GenericTreeNode();
lectures.setUserObject("Lectures");
lectures.setTitle(translate("lectures.menu.title"));
lectures.setAltText(translate("courses.menu.title.alt"));
root.addChild(lectures);
}
Roles roles = ureq.getUserSession().getRoles();
if(roles.isUserManager() || roles.isOLATAdmin()) {
GenericTreeNode search = new GenericTreeNode();
......
......@@ -15,7 +15,8 @@ error.search.form.too.many=Die Suche lieferte zu viele Treffer. Bitte schr\u00e4
group.name=Name
groups.menu.title.alt=Gruppen
groups.menu.title=Gruppen
lectures.menu.title=Lektionen
lectures.menu.title.alt=Lektionen und Absenzmanagement
home.link=Visitenkarte
main.menu.title.alt=\$:site.title.alt
main.menu.title=\$:site.title
......
......@@ -15,7 +15,8 @@ error.search.form.too.many=Too many search results. Please narrow down your sear
group.name=Name
groups.menu.title.alt=Groups
groups.menu.title=Groups
lectures.menu.title=Lectures
lectures.menu.title.alt=Lectures and absences management
home.link=visiting card
main.menu.title.alt=$\:site.title.alt
main.menu.title=$\:site.title
......
......@@ -25,10 +25,13 @@ import org.olat.basesecurity.Group;
import org.olat.basesecurity.IdentityRef;
import org.olat.core.id.Identity;
import org.olat.modules.lecture.model.LectureBlockAndRollCall;
import org.olat.modules.lecture.model.LectureBlockIdentityStatistics;
import org.olat.modules.lecture.model.LectureBlockStatistics;
import org.olat.modules.lecture.model.LectureBlockWithTeachers;
import org.olat.modules.lecture.model.LectureStatisticsSearchParameters;
import org.olat.repository.RepositoryEntry;
import org.olat.repository.RepositoryEntryRef;
import org.olat.user.propertyhandlers.UserPropertyHandler;
/**
*
......@@ -351,6 +354,14 @@ public interface LectureService {
*/
public List<LectureBlockStatistics> getParticipantsLecturesStatistics(RepositoryEntry entry);
/**
*
* @param params
* @return
*/
public List<LectureBlockIdentityStatistics> getLecturesStatistics(LectureStatisticsSearchParameters params,
List<UserPropertyHandler> userPropertyHandlers, Identity identity, boolean admin);
/**
* The list of roll calls within the specified course for the specified user
* after it's first admission.
......
......@@ -25,11 +25,15 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.persistence.TemporalType;
import javax.persistence.TypedQuery;
import org.olat.basesecurity.GroupRoles;
import org.olat.basesecurity.IdentityRef;
import org.olat.core.commons.persistence.DB;
import org.olat.core.commons.persistence.PersistenceHelper;
import org.olat.core.id.Identity;
import org.olat.core.util.StringHelper;
import org.olat.modules.lecture.LectureBlock;
import org.olat.modules.lecture.LectureBlockRef;
import org.olat.modules.lecture.LectureBlockRollCall;
......@@ -37,11 +41,14 @@ import org.olat.modules.lecture.LectureBlockStatus;
import org.olat.modules.lecture.LectureRollCallStatus;
import org.olat.modules.lecture.RepositoryEntryLectureConfiguration;
import org.olat.modules.lecture.model.LectureBlockAndRollCall;
import org.olat.modules.lecture.model.LectureBlockIdentityStatistics;
import org.olat.modules.lecture.model.LectureBlockRollCallImpl;
import org.olat.modules.lecture.model.LectureBlockStatistics;
import org.olat.modules.lecture.model.LectureStatisticsSearchParameters;
import org.olat.repository.RepositoryEntry;
import org.olat.repository.RepositoryEntryRef;
import org.olat.user.UserManager;
import org.olat.user.propertyhandlers.UserPropertyHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
......@@ -338,6 +345,207 @@ public class LectureBlockRollCallDAO {
calculateAttendanceRate(statisticsList, countAuthorizedAbsenceAsAttendant);
return statisticsList;
}
public List<LectureBlockIdentityStatistics> getStatistics(LectureStatisticsSearchParameters params,
List<UserPropertyHandler> userPropertyHandlers, Identity identity, boolean admin,
boolean absenceDefaultAuthorized, boolean countAuthorizedAbsenceAsAttendant,
boolean calculateAttendanceRate, double requiredAttendanceRateDefault) {
StringBuilder sb = new StringBuilder(2048);
sb.append("select ident.key as participantKey, ")
.append(" call.lecturesAttendedNumber as attendedLectures,")
.append(" call.lecturesAbsentNumber as absentLectures,")
.append(" call.absenceAuthorized as absenceAuthorized,")
.append(" block.key as blockKey,")
.append(" block.compulsory as compulsory,")
.append(" block.plannedLecturesNumber as blockPlanned,")
.append(" block.effectiveLecturesNumber as blockEffective,")
.append(" block.statusString as status,")
.append(" block.rollCallStatusString as rollCallStatus,")
.append(" block.endDate as rollCallEndDate,")
.append(" re.key as repoKey,")
.append(" re.displayname as repoDisplayName,")
.append(" config.overrideModuleDefault as overrideDef,")
.append(" config.calculateAttendanceRate as calculateRate,")
.append(" config.requiredAttendanceRate as repoConfigRate,")//rate enabled
.append(" summary.firstAdmissionDate as firstAdmissionDate,")
.append(" summary.requiredAttendanceRate as summaryRate");
for(UserPropertyHandler handler:userPropertyHandlers) {
sb.append(", user.").append(handler.getName()).append(" as ").append("p_").append(handler.getName());
}
sb.append(" from lectureblock block")
.append(" inner join block.entry re")
.append(" inner join block.groups blockToGroup")
.append(" inner join blockToGroup.group bGroup")
.append(" inner join bGroup.members membership")
.append(" inner join membership.identity ident")
.append(" inner join ident.user user")
.append(" left join lectureentryconfig as config on (re.key=config.entry.key)")
.append(" left join lectureblockrollcall as call on (call.identity.key=membership.identity.key and call.lectureBlock.key=block.key)")
.append(" left join lectureparticipantsummary as summary on (summary.identity.key=membership.identity.key and summary.entry.key=block.entry.key)")
.append(" where membership.role='").append(GroupRoles.participant.name()).append("'");
if(!admin) {
sb.append(" and (exists (select rel from repoentrytogroup as rel, bgroupmember as membership ")
.append(" where re.key=rel.entry.key and membership.group.key=rel.group.key and rel.defaultGroup=true and membership.identity.key=:identityKey")
.append(" and membership.role='").append(GroupRoles.owner.name()).append("'")
.append(" and re.access >= ").append(RepositoryEntry.ACC_OWNERS)
.append(" ) or exists (select membership.key from bgroupmember as membership ")
.append(" where block.teacherGroup.key=membership.group.key and membership.identity.key=:identityKey")
.append(" and (re.access >= ").append(RepositoryEntry.ACC_USERS)
.append(" or (re.access = ").append(RepositoryEntry.ACC_OWNERS).append(" and re.membersOnly=true))")
.append(" ))");
} else {
sb.append(" and re.access >= ").append(RepositoryEntry.ACC_OWNERS);
}
if(params.getLifecycle() != null) {
sb.append(" and re.lifecycle.key=:lifecycleKey");
}
if(params.getStartDate() != null) {
sb.append(" and block.startDate>=:startDate");
}
if(params.getEndDate() != null) {
sb.append(" and block.endDate<=:endDate");
}
if(params.getBulkIdentifiers() != null && params.getBulkIdentifiers().size() > 0) {
sb.append(" and (")
.append(" lower(ident.name) in (:bulkIdentifiers)")
.append(" or lower(ident.externalId) in (:bulkIdentifiers)")
.append(" or lower(user.email) in (:bulkIdentifiers)")
.append(" or lower(user.institutionalEmail) in (:bulkIdentifiers)")
.append(")");
}
Map<String,Object> queryParams = new HashMap<>();
appendUsersStatisticsSearchParams(params, queryParams, sb);
TypedQuery<Object[]> rawQuery = dbInstance.getCurrentEntityManager()
.createQuery(sb.toString(), Object[].class);
if(StringHelper.containsNonWhitespace(params.getLogin())) {
rawQuery.setParameter("login", params.getLogin());
}
if(params.getLifecycle() != null) {
rawQuery.setParameter("lifecycleKey", params.getLifecycle().getKey());
}
if(params.getStartDate() != null) {
rawQuery.setParameter("startDate", params.getStartDate(), TemporalType.TIMESTAMP);
}
if(params.getEndDate() != null) {
rawQuery.setParameter("endDate", params.getEndDate(), TemporalType.TIMESTAMP);
}
if(params.getBulkIdentifiers() != null && params.getBulkIdentifiers().size() > 0) {
rawQuery.setParameter("bulkIdentifiers", params.getBulkIdentifiers());
}
for(Map.Entry<String, Object> entry:queryParams.entrySet()) {
rawQuery.setParameter(entry.getKey(), entry.getValue());
}
if(!admin) {
rawQuery.setParameter("identityKey", identity.getKey());
}
Date now = new Date();
List<Object[]> rawObjects = rawQuery.getResultList();
Map<Membership,LectureBlockIdentityStatistics> stats = new HashMap<>();
for(Object[] rawObject:rawObjects) {
int pos = 0;//jump roll call key
Long identityKey = (Long)rawObject[pos++];
Long lecturesAttended = PersistenceHelper.extractLong(rawObject, pos++);
Long lecturesAbsent = PersistenceHelper.extractLong(rawObject, pos++);
Boolean absenceAuthorized = (Boolean)rawObject[pos++];
pos++;//jump block key
boolean compulsory = PersistenceHelper.extractBoolean(rawObject, pos++, true);
Long plannedLecturesNumber = PersistenceHelper.extractLong(rawObject, pos++);
Long effectiveLecturesNumber = PersistenceHelper.extractLong(rawObject, pos++);
if(effectiveLecturesNumber == null) {
effectiveLecturesNumber = plannedLecturesNumber;
}
String status = (String)rawObject[pos++];
String rollCallStatus = (String)rawObject[pos++];
Date rollCallEndDate = (Date)rawObject[pos++];
//entry and config
Long repoKey = PersistenceHelper.extractLong(rawObject, pos++);
String repoDisplayname = (String)rawObject[pos++];
boolean overrideDefault = PersistenceHelper.extractBoolean(rawObject, pos++, false);
Boolean repoCalculateRate = (Boolean)rawObject[pos++];
Double repoRequiredRate = (Double)rawObject[pos++];
//summary
Date firstAdmissionDate = (Date)rawObject[pos++];
Double persoRequiredRate = (Double)rawObject[pos++];
LectureBlockIdentityStatistics entryStatistics;
Membership memberKey = new Membership(identityKey, repoKey);
if(stats.containsKey(memberKey)) {
entryStatistics = stats.get(memberKey);
} else {
//user data
int numOfProperties = userPropertyHandlers.size();
String[] identityProps = new String[numOfProperties];
for(int i=0; i<numOfProperties; i++) {
identityProps[i] = (String)rawObject[pos++];
}
entryStatistics = createIdentityStatistics(identityKey, identityProps,
repoKey, repoDisplayname,
overrideDefault, repoCalculateRate, repoRequiredRate,
persoRequiredRate, calculateAttendanceRate, requiredAttendanceRateDefault);
stats.put(memberKey, entryStatistics);
}
appendStatistics(entryStatistics, compulsory, status,
rollCallEndDate, rollCallStatus,
lecturesAttended, lecturesAbsent,
absenceAuthorized, absenceDefaultAuthorized,
plannedLecturesNumber, effectiveLecturesNumber,
firstAdmissionDate, now);
}
List<LectureBlockIdentityStatistics> statisticsList = new ArrayList<>(stats.values());
calculateAttendanceRate(statisticsList, countAuthorizedAbsenceAsAttendant);
return statisticsList;
}
private void appendUsersStatisticsSearchParams(LectureStatisticsSearchParameters params, Map<String,Object> queryParams, StringBuilder sb) {
if(StringHelper.containsNonWhitespace(params.getLogin())) {
String login = PersistenceHelper.makeFuzzyQueryString(params.getLogin());
if (login.contains("_") && dbInstance.isOracle()) {
//oracle needs special ESCAPE sequence to search for escaped strings
sb.append(" and lower(ident.name) like :login ESCAPE '\\'");
} else if (dbInstance.isMySQL()) {
sb.append(" and ident.name like :login");
} else {
sb.append(" and lower(ident.name) like :login");
}
queryParams.put("login", login);
}
if(params.getUserProperties() != null && params.getUserProperties().size() > 0) {
Map<String,String> searchParams = new HashMap<>(params.getUserProperties());
int count = 0;
for(Map.Entry<String, String> entry:searchParams.entrySet()) {
String propName = entry.getKey();
String propValue = entry.getValue();
String qName = "p_" + ++count;
UserPropertyHandler handler = userManager.getUserPropertiesConfig().getPropertyHandler(propName);
if(dbInstance.isMySQL()) {
sb.append(" and user.").append(handler.getName()).append(" like :").append(qName);
} else {
sb.append(" and lower(user.").append(handler.getName()).append(") like :").append(qName);
if(dbInstance.isOracle()) {
sb.append(" escape '\\'");
}
}
queryParams.put(qName, PersistenceHelper.makeFuzzyQueryString(propValue));
}
}
}
public List<LectureBlockStatistics> getStatistics(RepositoryEntry entry,
RepositoryEntryLectureConfiguration config,
......@@ -424,7 +632,7 @@ public class LectureBlockRollCallDAO {
return statisticsList;
}
private void calculateAttendanceRate(List<LectureBlockStatistics> statisticsList, boolean countAuthorizedAbsenceAsAttendant) {
private void calculateAttendanceRate(List<? extends LectureBlockStatistics> statisticsList, boolean countAuthorizedAbsenceAsAttendant) {
for(LectureBlockStatistics statistics:statisticsList) {
long totalAttendedLectures = statistics.getTotalEffectiveLectures();
long totalAbsentLectures = statistics.getTotalAbsentLectures();
......@@ -452,6 +660,24 @@ public class LectureBlockRollCallDAO {
Boolean overrideDefault, Boolean repoCalculateRate, Double repoRequiredRate,
Double persoRequiredRate, boolean calculateAttendanceRate, double requiredAttendanceRateDefault) {
RequiredRate requiredRate = calculateRequiredRate(overrideDefault, repoCalculateRate, repoRequiredRate,
persoRequiredRate, calculateAttendanceRate, requiredAttendanceRateDefault);
return new LectureBlockStatistics(identityKey, entryKey, displayName, requiredRate.isCalculateRate(), requiredRate.getRequiredRate());
}
private LectureBlockIdentityStatistics createIdentityStatistics(Long identityKey, String[] identityProps, Long entryKey, String displayName,
Boolean overrideDefault, Boolean repoCalculateRate, Double repoRequiredRate,
Double persoRequiredRate, boolean calculateAttendanceRate, double requiredAttendanceRateDefault) {
RequiredRate requiredRate = calculateRequiredRate(overrideDefault, repoCalculateRate, repoRequiredRate,
persoRequiredRate, calculateAttendanceRate, requiredAttendanceRateDefault);
return new LectureBlockIdentityStatistics(identityKey, identityProps,
entryKey, displayName, requiredRate.isCalculateRate(), requiredRate.getRequiredRate());
}
private RequiredRate calculateRequiredRate(Boolean overrideDefault, Boolean repoCalculateRate, Double repoRequiredRate,
Double persoRequiredRate, boolean calculateAttendanceRate, double requiredAttendanceRateDefault) {
final boolean calculateRate;
if(repoCalculateRate != null && overrideDefault != null && !overrideDefault.booleanValue()) {
calculateRate = repoCalculateRate.booleanValue();
......@@ -469,7 +695,8 @@ public class LectureBlockRollCallDAO {
requiredRate = requiredAttendanceRateDefault;
}
}
return new LectureBlockStatistics(identityKey, entryKey, displayName, calculateRate, requiredRate);
return new RequiredRate(calculateRate, requiredRate);
}
private void appendStatistics(LectureBlockStatistics statistics, boolean compulsory, String blockStatus,
......@@ -518,4 +745,51 @@ public class LectureBlockRollCallDAO {
statistics.addTotalPlannedLectures(plannedLecturesNumber.longValue());
}
}
private static class Membership {
private final Long identityKey;
private final Long repoEntryKey;
public Membership(Long identityKey, Long repoEntryKey) {
this.identityKey = identityKey;
this.repoEntryKey = repoEntryKey;
}
@Override
public int hashCode() {
return identityKey.hashCode() + repoEntryKey.hashCode();
}
@Override
public boolean equals(Object obj) {
if(this == obj) {
return true;
}
if(obj instanceof Membership) {
Membership membership = (Membership)obj;
return identityKey != null && identityKey.equals(membership.identityKey)
&& repoEntryKey != null && repoEntryKey.equals(membership.repoEntryKey);
}
return false;
}
}
private static class RequiredRate {
private final boolean calculateRate;
private final double requiredRate;
public RequiredRate(boolean calculateRate, double requiredRate) {
this.calculateRate = calculateRate;
this.requiredRate = requiredRate;
}
public boolean isCalculateRate() {
return calculateRate;
}
public double getRequiredRate() {
return requiredRate;
}
}
}
......@@ -66,10 +66,12 @@ import org.olat.modules.lecture.LectureService;
import org.olat.modules.lecture.Reason;
import org.olat.modules.lecture.RepositoryEntryLectureConfiguration;
import org.olat.modules.lecture.model.LectureBlockAndRollCall;
import org.olat.modules.lecture.model.LectureBlockIdentityStatistics;
import org.olat.modules.lecture.model.LectureBlockImpl;
import org.olat.modules.lecture.model.LectureBlockStatistics;
import org.olat.modules.lecture.model.LectureBlockToTeacher;
import org.olat.modules.lecture.model.LectureBlockWithTeachers;
import org.olat.modules.lecture.model.LectureStatisticsSearchParameters;
import org.olat.modules.lecture.model.ParticipantAndLectureSummary;
import org.olat.modules.lecture.ui.ConfigurationHelper;
import org.olat.modules.lecture.ui.LectureAdminController;
......@@ -78,6 +80,7 @@ import org.olat.repository.RepositoryEntryRef;
import org.olat.repository.manager.RepositoryEntryDAO;
import org.olat.user.UserDataDeletable;
import org.olat.user.UserManager;
import org.olat.user.propertyhandlers.UserPropertyHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
......@@ -593,6 +596,18 @@ public class LectureServiceImpl implements LectureService, UserDataDeletable {
absenceDefaultAuthorized, countAuthorizedAbsenceAsAttendant,
calculateAttendanceRate, defaultRequiredAttendanceRate);
}
@Override
public List<LectureBlockIdentityStatistics> getLecturesStatistics(LectureStatisticsSearchParameters params,
List<UserPropertyHandler> userPropertyHandlers, Identity identity, boolean admin) {
boolean calculateAttendanceRate = lectureModule.isRollCallCalculateAttendanceRateDefaultEnabled();
boolean absenceDefaultAuthorized = lectureModule.isAbsenceDefaultAuthorized();
boolean countAuthorizedAbsenceAsAttendant = lectureModule.isCountAuthorizedAbsenceAsAttendant();
double defaultRequiredAttendanceRate = lectureModule.getRequiredAttendanceRateDefault();
return lectureBlockRollCallDao.getStatistics(params, userPropertyHandlers, identity, admin,
absenceDefaultAuthorized, countAuthorizedAbsenceAsAttendant,
calculateAttendanceRate, defaultRequiredAttendanceRate);
}
@Override
public List<LectureBlockAndRollCall> getParticipantLectureBlocks(RepositoryEntryRef entry, IdentityRef participant) {
......
package org.olat.modules.lecture.model;
/**
*
*
* Initial date: 16 juin 2017<br>
* @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
*
*/
public class LectureBlockIdentityStatistics extends LectureBlockStatistics {
private final String[] identityProps;
public LectureBlockIdentityStatistics(Long identityKey, String[] identityProps,
Long repoKey, String displayName, boolean calculateRate, double requiredRate) {
super(identityKey, repoKey, displayName, calculateRate, requiredRate);
this.identityProps = identityProps;
}
public String[] getIdentityProps() {
return identityProps;
}
public String getIdentityProp(int pos) {
return identityProps[pos];
}
}
package org.olat.modules.lecture.model;
import java.util.Date;
import java.util.List;
import java.util.Map;
import org.olat.repository.model.RepositoryEntryLifecycle;
/**
*
* Initial date: 16 juin 2017<br>
* @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
*
*/
public class LectureStatisticsSearchParameters {
private String login;
private List<String> bulkIdentifiers;
private Map<String,String> userProperties;
private Date startDate;
private Date endDate;
private RepositoryEntryLifecycle lifecycle;
public String getLogin() {
return login;
}
public void setLogin(String login) {
this.login = login;
}
public Date getStartDate() {
return startDate;
}
public void setStartDate(Date startDate) {
this.startDate = startDate;
}
public Date getEndDate() {
return endDate;
}
public void setEndDate(Date endDate) {
this.endDate = endDate;
}
public RepositoryEntryLifecycle getLifecycle() {
return lifecycle;
}
public void setLifecycle(RepositoryEntryLifecycle lifecycle) {
this.lifecycle = lifecycle;
}
public List<String> getBulkIdentifiers() {
return bulkIdentifiers;
}
public void setBulkIdentifiers(List<String> bulkIdentifiers) {
this.bulkIdentifiers = bulkIdentifiers;
}
public Map<String, String> getUserProperties() {
return userProperties;
}
public void setUserProperties(Map<String, String> userProperties) {
this.userProperties = userProperties;
}
}
/**
* <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.lecture.ui;
import java.util.List;
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.FormBasicController;
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.modules.lecture.LectureModule;
import org.olat.modules.lecture.model.LectureBlockIdentityStatistics;
import org.olat.modules.lecture.ui.LecturesListDataModel.StatsCols;
import org.olat.user.UserManager;
import org.olat.user.propertyhandlers.UserPropertyHandler;
import org.springframework.beans.factory.annotation.Autowired;
/**
*
* Initial date: 16 juin 2017<br>
* @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
*
*/
public class LecturesListController extends FormBasicController {
public static final int USER_PROPS_OFFSET = 500;
private FlexiTableElement tableEl;
private LecturesListDataModel tableModel;
private final String propsIdentifier;
private final boolean authorizedAbsenceEnabled;
private final List<UserPropertyHandler> userPropertyHandlers;
private final List<LectureBlockIdentityStatistics> statistics;
@Autowired
private UserManager userManager;
@Autowired
private LectureModule lectureModule;
public LecturesListController(UserRequest ureq, WindowControl wControl,
List<LectureBlockIdentityStatistics> statistics,
List<UserPropertyHandler> userPropertyHandlers, String propsIdentifier) {
super(ureq, wControl, "lectures_coaching");
setTranslator(userManager.getPropertyHandlerTranslator(getTranslator()));
this.statistics = statistics;
this.propsIdentifier = propsIdentifier;
this.userPropertyHandlers = userPropertyHandlers;
authorizedAbsenceEnabled = lectureModule.isAuthorizedAbsenceEnabled();
initForm(ureq);
}
@Override
protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
FlexiTableColumnModel columnsModel = FlexiTableDataModelFactory.createFlexiTableColumnModel();
columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(false, StatsCols.id));
int colIndex = USER_PROPS_OFFSET;
for (int i = 0; i < userPropertyHandlers.size(); i++) {
UserPropertyHandler userPropertyHandler = userPropertyHandlers.get(i);
boolean visible = userManager.isMandatoryUserProperty(propsIdentifier, userPropertyHandler);
columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(visible, userPropertyHandler.i18nColumnDescriptorLabelKey(), colIndex++, null,
true, userPropertyHandler.i18nColumnDescriptorLabelKey()));
}
columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(StatsCols.entry));
columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(StatsCols.plannedLectures));
columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(StatsCols.attendedLectures));
columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(StatsCols.absentLectures));
if(authorizedAbsenceEnabled) {
columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(StatsCols.authorizedAbsenceLectures));
}
tableModel = new LecturesListDataModel(columnsModel);
tableModel.setObjects(statistics);
tableEl = uifactory.addTableElement(getWindowControl(), "table", tableModel, 20, false, getTranslator(), formLayout);
tableEl.setExportEnabled(true);
}
@Override
protected void doDispose() {
//
}
@Override
protected void formOK(UserRequest ureq) {
//
}
}
/**
* <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.lecture.ui;
import java.util.List;
import org.olat.core.commons.persistence.SortKey;
import org.olat.core.gui.components.form.flexible.impl.elements.table.DefaultFlexiTableDataModel;
import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiSortableColumnDef;
import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableColumnModel;
import org.olat.core.gui.components.form.flexible.impl.elements.table.SortableFlexiTableDataModel;
import org.olat.core.gui.components.form.flexible.impl.elements.table.SortableFlexiTableModelDelegate;
import org.olat.modules.lecture.model.LectureBlockIdentityStatistics;
/**
*
* Initial date: 16 juin 2017<br>
* @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
*
*/
public class LecturesListDataModel extends DefaultFlexiTableDataModel<LectureBlockIdentityStatistics>
implements SortableFlexiTableDataModel<LectureBlockIdentityStatistics>{
public LecturesListDataModel(FlexiTableColumnModel columnModel) {
super(columnModel);
}
@Override
public void sort(SortKey orderBy) {
SortableFlexiTableModelDelegate<LectureBlockIdentityStatistics> sorter
= new SortableFlexiTableModelDelegate<>(orderBy, this, null);
List<LectureBlockIdentityStatistics> views = sorter.sort();
super.setObjects(views);
}
@Override
public Object getValueAt(int row, int col) {
LectureBlockIdentityStatistics stats = getObject(row);
return getValueAt(stats, col);
}
@Override
public Object getValueAt(LectureBlockIdentityStatistics row, int col) {
if(col >= 0 && col < StatsCols.values().length) {
switch(StatsCols.values()[col]) {
case id: return row.getIdentityKey();
case entry: return row.getDisplayName();
case plannedLectures: return positive(row.getTotalPersonalPlannedLectures());
case attendedLectures: return positive(row.getTotalAttendedLectures());
case absentLectures: return positive(row.getTotalAbsentLectures());
case authorizedAbsenceLectures: return positive(row.getTotalAuthorizedAbsentLectures());
}
}
int propPos = col - LecturesListController.USER_PROPS_OFFSET;
return row.getIdentityProp(propPos);
}
private static final long positive(long pos) {
return pos < 0 ? 0 : pos;
}
@Override
public DefaultFlexiTableDataModel<LectureBlockIdentityStatistics> createCopyWithEmptyList() {
return new LecturesListDataModel(getTableColumnModel());
}
public enum StatsCols implements FlexiSortableColumnDef {
id("table.header.id"),
entry("table.header.entry"),
plannedLectures("table.header.planned.lectures"),
attendedLectures("table.header.attended.lectures"),
absentLectures("table.header.absent.lectures"),
authorizedAbsenceLectures("table.header.authorized.absence")
;
private final String i18nKey;
private StatsCols(String i18nKey) {
this.i18nKey = i18nKey;
}
@Override
public String i18nHeaderKey() {
return i18nKey;
}
@Override
public boolean sortable() {
return true;
}
@Override
public String sortKey() {
return name();
}
}
}
/**
* <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.lecture.ui;
import java.util.List;
import org.olat.core.gui.UserRequest;
import org.olat.core.gui.components.Component;
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.controller.BasicController;
import org.olat.core.gui.control.generic.dtabs.Activateable2;
import org.olat.core.id.Roles;
import org.olat.core.id.context.ContextEntry;
import org.olat.core.id.context.StateEntry;
import org.olat.modules.lecture.LectureService;
import org.olat.modules.lecture.model.LectureBlockIdentityStatistics;
import org.olat.modules.lecture.model.LectureStatisticsSearchParameters;
import org.olat.user.propertyhandlers.UserPropertyHandler;
import org.springframework.beans.factory.annotation.Autowired;
/**
*
* Initial date: 16 juin 2017<br>
* @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
*
*/
public class LecturesSearchController extends BasicController implements Activateable2 {
private final TooledStackedPanel stackPanel;
private LecturesListController listCtrl;
private LecturesSearchFormController searchForm;
private final boolean admin;
@Autowired
private LectureService lectureService;
public LecturesSearchController(UserRequest ureq, WindowControl wControl, TooledStackedPanel stackPanel) {
super(ureq, wControl);
this.stackPanel = stackPanel;
Roles roles = ureq.getUserSession().getRoles();
admin = (roles.isUserManager() || roles.isOLATAdmin());
searchForm = new LecturesSearchFormController(ureq, getWindowControl());
listenTo(searchForm);
putInitialPanel(searchForm.getInitialComponent());
}
@Override
protected void doDispose() {
//
}
@Override
public void activate(UserRequest ureq, List<ContextEntry> entries, StateEntry state) {
//
}
@Override
protected void event(UserRequest ureq, Component source, Event event) {
//
}
@Override
protected void event(UserRequest ureq, Controller source, Event event) {
if(searchForm == source) {
if(event == Event.DONE_EVENT) {
doSearch(ureq);
}
}
super.event(ureq, source, event);
}
private void doSearch(UserRequest ureq) {
LectureStatisticsSearchParameters params = searchForm.getSearchParameters();
List<UserPropertyHandler> userPropertyHandlers = searchForm.getUserPropertyHandlers();
List<LectureBlockIdentityStatistics> statistics = lectureService
.getLecturesStatistics(params, userPropertyHandlers, getIdentity(), admin);
listCtrl = new LecturesListController(ureq, getWindowControl(), statistics,
userPropertyHandlers, LecturesSearchFormController.PROPS_IDENTIFIER);
listenTo(listCtrl);
stackPanel.popUpToRootController(ureq);
stackPanel.pushController(translate("results"), listCtrl);
}
}
/**
* <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.lecture.ui;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.olat.basesecurity.BaseSecurityModule;
import org.olat.core.gui.UserRequest;
import org.olat.core.gui.components.form.flexible.FormItem;
import org.olat.core.gui.components.form.flexible.FormItemContainer;
import org.olat.core.gui.components.form.flexible.elements.DateChooser;
import org.olat.core.gui.components.form.flexible.elements.FormLink;
import org.olat.core.gui.components.form.flexible.elements.SingleSelection;
import org.olat.core.gui.components.form.flexible.elements.TextElement;
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.control.Controller;
import org.olat.core.gui.control.Event;
import org.olat.core.gui.control.WindowControl;
import org.olat.core.util.StringHelper;
import org.olat.modules.lecture.model.LectureStatisticsSearchParameters;
import org.olat.repository.manager.RepositoryEntryLifecycleDAO;
import org.olat.repository.model.RepositoryEntryLifecycle;
import org.olat.user.UserManager;
import org.olat.user.propertyhandlers.EmailProperty;
import org.olat.user.propertyhandlers.UserPropertyHandler;
import org.springframework.beans.factory.annotation.Autowired;
/**
*
*
* Initial date: 16 juin 2017<br>
* @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
*
*/
public class LecturesSearchFormController extends FormBasicController {
protected static final String PROPS_IDENTIFIER = LecturesSearchFormController.class.getName();
private static final String[] dateKeys = new String[]{ "none", "private", "public"};
private TextElement login;
private TextElement bulkEl;
private FormLink searchButton;
private DateChooser startDateEl, endDateEl;
private FormLayoutContainer privateDatesCont;
private SingleSelection dateTypesEl, publicDatesEl;
private final boolean adminProps;
private List<UserPropertyHandler> userPropertyHandlers;
private final Map<String,FormItem> propFormItems = new HashMap<>();
@Autowired
private UserManager userManager;
@Autowired
private BaseSecurityModule securityModule;
@Autowired
private RepositoryEntryLifecycleDAO lifecycleDao;
public LecturesSearchFormController(UserRequest ureq, WindowControl wControl) {
super(ureq, wControl);
setTranslator(userManager.getPropertyHandlerTranslator(getTranslator()));
adminProps = securityModule.isUserAllowedAdminProps(ureq.getUserSession().getRoles());
initForm(ureq);
updateDatesVisibility();
}
@Override
protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
login = uifactory.addTextElement("login", "search.form.login", 128, "", formLayout);
login.setVisible(adminProps);
userPropertyHandlers = userManager.getUserPropertyHandlersFor(PROPS_IDENTIFIER, adminProps);
for (UserPropertyHandler userPropertyHandler : userPropertyHandlers) {
if (userPropertyHandler != null) {
FormItem fi = userPropertyHandler.addFormItem(getLocale(), null, PROPS_IDENTIFIER, false, formLayout);
// DO NOT validate email field => see OLAT-3324, OO-155, OO-222
if (userPropertyHandler instanceof EmailProperty && fi instanceof TextElement) {
TextElement textElement = (TextElement)fi;
textElement.setItemValidatorProvider(null);
}
propFormItems.put(userPropertyHandler.getName(), fi);
}
}
bulkEl = uifactory.addTextAreaElement("bulk", 4, 72, "", formLayout);
String[] dateValues = new String[] {
translate("dates.none"),
translate("dates.private"),
translate("dates.public")
};
dateTypesEl = uifactory.addRadiosVertical("dates", formLayout, dateKeys, dateValues);
dateTypesEl.select(dateKeys[0], true);
dateTypesEl.addActionListener(FormEvent.ONCHANGE);
List<RepositoryEntryLifecycle> cycles = lifecycleDao.loadPublicLifecycle();
List<RepositoryEntryLifecycle> filteredCycles = new ArrayList<>();
for(RepositoryEntryLifecycle cycle:cycles) {
if(cycle.getValidTo() == null) {
filteredCycles.add(cycle);
}
}
String[] publicKeys = new String[filteredCycles.size()];
String[] publicValues = new String[filteredCycles.size()];
int count = 0;
for(RepositoryEntryLifecycle cycle:filteredCycles) {
publicKeys[count] = cycle.getKey().toString();
StringBuilder sb = new StringBuilder(32);
boolean labelAvailable = StringHelper.containsNonWhitespace(cycle.getLabel());
if(labelAvailable) {
sb.append(cycle.getLabel());
}
if(StringHelper.containsNonWhitespace(cycle.getSoftKey())) {
if(labelAvailable) sb.append(" - ");
sb.append(cycle.getSoftKey());
}
publicValues[count++] = sb.toString();
}
publicDatesEl = uifactory.addDropdownSingleselect("public.dates", formLayout, publicKeys, publicValues, null);
String privateDatePage = velocity_root + "/cycle_dates.html";
privateDatesCont = FormLayoutContainer.createCustomFormLayout("private.date", getTranslator(), privateDatePage);
privateDatesCont.setRootForm(mainForm);
privateDatesCont.setLabel("private.dates", null);
formLayout.add("private.date", privateDatesCont);
startDateEl = uifactory.addDateChooser("date.start", "date.start", null, privateDatesCont);
startDateEl.setElementCssClass("o_sel_repo_lifecycle_validfrom");
endDateEl = uifactory.addDateChooser("date.end", "date.end", null, privateDatesCont);
endDateEl.setElementCssClass("o_sel_repo_lifecycle_validto");
FormLayoutContainer buttonCont = FormLayoutContainer.createButtonLayout("buttons", getTranslator());
formLayout.add(buttonCont);
uifactory.addFormSubmitButton("search", buttonCont);
}
private void updateDatesVisibility() {
if(dateTypesEl.isOneSelected()) {
String type = dateTypesEl.getSelectedKey();
if("none".equals(type)) {
publicDatesEl.setVisible(false);
privateDatesCont.setVisible(false);
} else if("public".equals(type)) {
publicDatesEl.setVisible(true);
privateDatesCont.setVisible(false);
} else if("private".equals(type)) {
publicDatesEl.setVisible(false);
privateDatesCont.setVisible(true);
}
}
}
@Override
protected void doDispose() {
//
}
public List<UserPropertyHandler> getUserPropertyHandlers() {
return userPropertyHandlers;
}
public LectureStatisticsSearchParameters getSearchParameters() {
LectureStatisticsSearchParameters params = new LectureStatisticsSearchParameters();
String type = dateTypesEl.getSelectedKey();
if("none".equals(type)) {
params.setStartDate(null);
params.setEndDate(null);
params.setLifecycle(null);
} else if("public".equals(type)) {
params.setStartDate(null);
params.setEndDate(null);
if(publicDatesEl.isOneSelected() && StringHelper.isLong(publicDatesEl.getSelectedKey())) {
RepositoryEntryLifecycle lifecycle = lifecycleDao.loadById(new Long(publicDatesEl.getSelectedKey()));
params.setLifecycle(lifecycle);
} else {
params.setLifecycle(null);
}
} else if("private".equals(type)) {
params.setStartDate(startDateEl.getDate());
params.setEndDate(endDateEl.getDate());
params.setLifecycle(null);
}
params.setLogin(getLogin());
params.setBulkIdentifiers(getBulkIdentifiers());
params.setUserProperties(getSearchProperties());
return params;
}
private String getLogin() {
return login.isVisible() && StringHelper.containsNonWhitespace(login.getValue())
? login.getValue() : null;
}
private Map<String,String> getSearchProperties() {
Map<String, String> userPropertiesSearch = new HashMap<>();
for (UserPropertyHandler userPropertyHandler : userPropertyHandlers) {
if (userPropertyHandler != null) {
FormItem ui = propFormItems.get(userPropertyHandler.getName());
String uiValue = userPropertyHandler.getStringValue(ui);
if(userPropertyHandler.getName().startsWith("genericCheckboxProperty")) {
if(!"false".equals(uiValue)) {
userPropertiesSearch.put(userPropertyHandler.getName(), uiValue);
}
} else if (StringHelper.containsNonWhitespace(uiValue) && !uiValue.equals("-")) {
userPropertiesSearch.put(userPropertyHandler.getName(), uiValue);
}
}
}
return userPropertiesSearch;
}
private List<String> getBulkIdentifiers() {
String val = bulkEl.getValue();
List<String> identifiers = new ArrayList<String>();
String[] lines = val.split("\r?\n");
for (int i = 0; i < lines.length; i++) {
String username = lines[i].trim();
if(username.length() > 0) {
identifiers.add(username);
}
}
return identifiers;
}
@Override
protected void formOK(UserRequest ureq) {
fireEvent(ureq, Event.DONE_EVENT);
}
@Override
protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) {
if (source == dateTypesEl) {
updateDatesVisibility();
} else if (source == searchButton) {
if(validate()) {
fireEvent (ureq, Event.DONE_EVENT);
}
}
super.formInnerEvent(ureq, source, event);
}
@Override
protected boolean validateFormLogic(UserRequest ureq) {
return validate() & super.validateFormLogic(ureq);
}
private boolean validate() {
return true;
}
}
<div class="o_date form-inline">
<span class="form-control-static">$r.translate("date.start")</span>
<div class="form-group o_sel_repo_lifecycle_validfrom">$r.render("date.start")</div>
<span class="form-control-static">$r.translate("date.end")</span>
<div class="form-group o_sel_repo_lifecycle_validto">$r.render("date.end")</div>
</div>
\ No newline at end of file
$r.render("table")
\ No newline at end of file
......@@ -13,6 +13,7 @@ appeal.title=Rekurs f\u00FCr\: "{0}"
authorized.absence=Entschuldigt
authorized.absence.reason=Begr\u00FCndung
autoclosed=Automatisch geschlossen
bulk=Bulk Email
cancelled=Abgesagt
cancel.lecture.blocks=Lektionen absagen
closed=Geschlossen
......@@ -28,6 +29,15 @@ confirm.delete.lectures=Wollen Sie wirklich diese Lektionenblock "{0}" l\u00F6sc
confirm.delete.reason=Wollen Sie wirklich diese Begr\u00FCndung "{0}" l\u00F6schen?
copy=Kopieren
current.lecture=Aktueller Lektionenblock
dates=$org.olat.repository\:cif.dates
dates.none=$org.olat.repository\:cif.dates.none
dates.private=$org.olat.repository\:cif.dates.private
dates.public=$org.olat.repository\:cif.dates.public
date.start=$org.olat.repository\:cif.date.start
date.end=$org.olat.repository\:cif.date.end
dates.public=$org.olat.repository\:cif.dates.public
private.dates=$org.olat.repository\:cif.private.dates
public.dates=$org.olat.repository\:cif.public.dates
delete.lectures.title=Lektionenblock l\u00F6schen
delete.title=Begr\u00FCndung l\u00F6schen
details=Details
......@@ -125,11 +135,13 @@ repo.lectures=Lektionen
repo.lectures.block=Lektionenbl\u00F6cke
repo.participants=Teilnehmer
repo.settings=Konfiguration
results=Resultaten
rollcall=Anwesenheitskontrolle
rollcall.comment=Bemerkung
rollcall.status=Status Lektionenblock
save.next=Speichern und weiter
save.temporary=Zwischen speichern
search.form.login=Benutzername
start.wizard=Wizard starten
sync.participants.calendar.enabled=Teilnehmer Kalender synchronizieren
sync.teachers.calendar.enabled=Lehrer Kalender synchronizieren
......
......@@ -13,6 +13,7 @@ appeal.title=Appeal for\: "{0}"
authorized.absence=Authorized
authorized.absence.reason=Reason
autoclosed=Automatically closed
bulk=Bulk Email
cancelled=Cancelled
cancel.lecture.blocks=Cancel lectures
closed=Closed
......@@ -28,6 +29,15 @@ confirm.delete.lectures=Do you really want to delete these lectures "{0}"?
confirm.delete.reason=Do you really want to delete this reason "{0}"?
copy=Copy
current.lecture=Current lectures
dates=$org.olat.repository\:cif.dates
dates.none=$org.olat.repository\:cif.dates.none
dates.private=$org.olat.repository\:cif.dates.private
dates.public=$org.olat.repository\:cif.dates.public
date.start=$org.olat.repository\:cif.date.start
date.end=$org.olat.repository\:cif.date.end
dates.public=$org.olat.repository\:cif.dates.public
private.dates=$org.olat.repository\:cif.private.dates
public.dates=$org.olat.repository\:cif.public.dates
delete.lectures.title=Delete lectures
delete.title=Delete reason
details=Details
......@@ -124,11 +134,13 @@ repo.lectures=Lectures
repo.lectures.block=Lectures blocs
repo.participants=Participants
repo.settings=Configuration
results=Results
rollcall=Roll call
rollcall.comment=Comment
rollcall.status=Roll call status
save.next=Save and next
save.temporary=Quick save
search.form.login=Username
start.wizard=Start wizard
sync.participants.calendar.enabled=Synchronize participants calendars
sync.teachers.calendar.enabled=Synchronize teachers calendars
......
......@@ -1264,6 +1264,29 @@
</bean>
</entry>
<entry key="org.olat.modules.lecture.ui.LecturesSearchFormController">
<bean class="org.olat.user.propertyhandlers.UserPropertyUsageContext">
<property name="description" value="Properties used in the user search for lectures of the coaching tool." />
<property name="propertyHandlers">
<list>
<ref bean="userPropertyFirstName" />
<ref bean="userPropertyLastName" />
<ref bean="userPropertyEmail" />
<ref bean="userPropertyInstitutionalName" />
<ref bean="userPropertyInstitutionalUserIdentifier" />
<ref bean="userPropertyInstitutionalEmail" />
<ref bean="userPropertyOrgUnit" />
</list>
</property>
<property name="mandatoryProperties">
<set>
<ref bean="userPropertyFirstName" />
<ref bean="userPropertyLastName" />
</set>
</property>
</bean>
</entry>
<entry key="org.olat.modules.portfolio.ui.PortfolioHomeController">
<bean class="org.olat.user.propertyhandlers.UserPropertyUsageContext">
<property name="description" value="Properties used in the portfolio v2.0." />
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment