From 541cfe52781fc648b0218542b95a9370c33c8013 Mon Sep 17 00:00:00 2001 From: srosse <stephane.rosse@frentix.com> Date: Thu, 18 Oct 2018 15:12:27 +0200 Subject: [PATCH] OO-3584: widen access to course in coaching tool, show the status --- .../org/olat/home/HomeCalendarManager.java | 3 +- .../modules/coach/manager/CoachingDAO.java | 67 +++++----- .../modules/coach/model/CourseStatEntry.java | 11 ++ .../coach/ui/CourseListController.java | 123 ++++++++++-------- .../coach/ui/CoursesTableDataModel.java | 110 +++++++++------- ...fficiencyStatementEntryTableDataModel.java | 4 +- .../coach/ui/StudentCoursesController.java | 2 +- .../coach/ui/_content/course_list.html | 2 +- .../coach/ui/_i18n/LocalStrings_de.properties | 1 + .../coach/ui/_i18n/LocalStrings_en.properties | 1 + .../RepositoryEntryMyCourseQueries.java | 2 +- .../repository/ui/RepositoryTableModel.java | 3 +- .../repository/ui/author/AccessRenderer.java | 16 ++- 13 files changed, 192 insertions(+), 153 deletions(-) diff --git a/src/main/java/org/olat/home/HomeCalendarManager.java b/src/main/java/org/olat/home/HomeCalendarManager.java index a9e08056d57..91b49058cfb 100644 --- a/src/main/java/org/olat/home/HomeCalendarManager.java +++ b/src/main/java/org/olat/home/HomeCalendarManager.java @@ -249,7 +249,6 @@ public class HomeCalendarManager implements PersonalCalendarManager, UserDataDel ICourse course = CourseFactory.loadCourse(courseEntry); if(CourseCalendars.isCourseCalendarEnabled(course)) { //calendar course aren't enabled per default but course node of type calendar are always possible - //REVIEW if (!course.getCourseEnvironment().getCourseConfig().isCalendarEnabled()) continue; // add course calendar KalendarRenderWrapper courseCalendarWrapper = calendarManager.getCourseCalendar(course); boolean isPrivileged = GroupRoles.owner.name().equals(role) || editoredResources.contains(courseEntry.getOlatResource()); @@ -295,7 +294,7 @@ public class HomeCalendarManager implements PersonalCalendarManager, UserDataDel .append(" inner join retogroup.group as baseGroup") .append(" inner join baseGroup.members as membership") .append(" where v.olatResource.resName='CourseModule' and membership.identity.key=:identityKey and") - .append(" (")//TODO repo access + .append(" (") .append(" (v.status ").in(RepositoryEntryStatusEnum.reviewToClosed()).append(" and membership.role='").append(GroupRoles.owner.name()).append("')") .append(" or") .append(" (v.status ").in(RepositoryEntryStatusEnum.coachPublishedToClosed()).append(" and membership.role='").append(GroupRoles.coach.name()).append("')") diff --git a/src/main/java/org/olat/modules/coach/manager/CoachingDAO.java b/src/main/java/org/olat/modules/coach/manager/CoachingDAO.java index 38956cbfdb4..b93270cff2e 100644 --- a/src/main/java/org/olat/modules/coach/manager/CoachingDAO.java +++ b/src/main/java/org/olat/modules/coach/manager/CoachingDAO.java @@ -85,7 +85,7 @@ public class CoachingDAO { .append(" inner join v.groups as relGroup") .append(" inner join relGroup.group as baseGroup") .append(" inner join baseGroup.members as membership on (membership.identity.key=:identityKey and membership.role ").in(GroupRoles.owner, GroupRoles.coach.name()).append(")") - .append(" where v.status ").in(RepositoryEntryStatusEnum.publishedAndClosed()); + .append(" where v.status ").in(RepositoryEntryStatusEnum.coachPublishedToClosed()); List<Long> firstKey = dbInstance.getCurrentEntityManager() .createQuery(sb.toString(), Long.class) @@ -155,7 +155,7 @@ public class CoachingDAO { .append(" inner join o_bs_group_member sg_coach on (sg_coach.fk_group_id=togroup.fk_group_id and sg_coach.g_role = 'coach') ") .append(" inner join o_repositoryentry sg_re on (togroup.fk_entry_id = sg_re.repositoryentry_id) ") .append(" inner join o_olatresource sg_res on (sg_res.resource_id = sg_re.fk_olatresource and sg_res.resname = 'CourseModule') ") - .append(" where sg_coach.fk_identity_id=:coachKey and sg_re.status ").in(RepositoryEntryStatusEnum.publishedAndClosed()) + .append(" where sg_coach.fk_identity_id=:coachKey and sg_re.status ").in(RepositoryEntryStatusEnum.coachPublishedToClosed()) .append(" ) or infos.fk_group_id in ( select ") .append(" distinct togroup.fk_group_id ") .append(" from o_re_to_group togroup ") @@ -164,7 +164,7 @@ public class CoachingDAO { .append(" inner join o_re_to_group owngroup on (owngroup.fk_entry_id = sg_re.repositoryentry_id) ") .append(" inner join o_bs_group_member sg_owner on (sg_owner.fk_group_id=owngroup.fk_group_id and sg_owner.g_role ") .in(GroupRoles.owner).append(")") - .append(" where togroup.r_defgroup=").appendFalse().append(" and sg_owner.fk_identity_id=:coachKey and sg_re.status ").in(RepositoryEntryStatusEnum.publishedAndClosed()) + .append(" where togroup.r_defgroup=").appendFalse().append(" and sg_owner.fk_identity_id=:coachKey and sg_re.status ").in(RepositoryEntryStatusEnum.coachPublishedToClosed()) .append(" ) "); List<?> rawList = dbInstance.getCurrentEntityManager() @@ -196,8 +196,8 @@ public class CoachingDAO { .append(" inner join o_bs_group_member sg_participant on (sg_participant.fk_group_id=sg_coach.fk_group_id and sg_participant.g_role='participant') ") .append(" left join o_as_user_course_infos pg_initial_launch ") .append(" on (pg_initial_launch.fk_resource_id = sg_re.fk_olatresource and pg_initial_launch.fk_identity = sg_participant.fk_identity_id) ") - .append(" where sg_coach.fk_identity_id=:coachKey")//TODO repo access - .append(" and sg_re.status ").in(RepositoryEntryStatusEnum.publishedAndClosed()).append(" and sg_coach.g_role ").in(GroupRoles.coach)//BAR + .append(" where sg_coach.fk_identity_id=:coachKey") + .append(" and sg_re.status ").in(RepositoryEntryStatusEnum.coachPublishedToClosed()).append(" and sg_coach.g_role ").in(GroupRoles.coach)//BAR .append(" group by togroup.fk_group_id, togroup.fk_entry_id "); List<?> rawList = dbInstance.getCurrentEntityManager() @@ -236,8 +236,8 @@ public class CoachingDAO { .append(" inner join o_bs_group_member sg_participant on (sg_participant.fk_group_id=togroup.fk_group_id and sg_participant.g_role='participant') ") .append(" left join o_as_user_course_infos pg_initial_launch ") .append(" on (pg_initial_launch.fk_resource_id = sg_re.fk_olatresource and pg_initial_launch.fk_identity = sg_participant.fk_identity_id) ") - .append(" where sg_owner.fk_identity_id=:coachKey and sg_re.status ").in(RepositoryEntryStatusEnum.publishedAndClosed()) - .append(" group by togroup.fk_group_id, togroup.fk_entry_id ");//TODO repo access + .append(" where sg_owner.fk_identity_id=:coachKey and sg_re.status ").in(RepositoryEntryStatusEnum.coachPublishedToClosed()) + .append(" group by togroup.fk_group_id, togroup.fk_entry_id "); List<?> rawList = dbInstance.getCurrentEntityManager() .createNativeQuery(sb.toString()) @@ -281,7 +281,7 @@ public class CoachingDAO { .append(" inner join o_bs_group_member sg_participant on (sg_participant.fk_group_id=sg_coach.fk_group_id and sg_participant.g_role='participant') ") .append(" inner join o_as_eff_statement sg_statement on (sg_statement.fk_identity = sg_participant.fk_identity_id and sg_statement.fk_resource_id = sg_re.fk_olatresource) ") .append(" where sg_coach.fk_identity_id=:coachKey and sg_coach.g_role = 'coach'") - .append(" and sg_re.status ").in(RepositoryEntryStatusEnum.publishedAndClosed())//TODO repo access + .append(" and sg_re.status ").in(RepositoryEntryStatusEnum.coachPublishedToClosed()) .append(") ").appendAs().append(" fin_statement ") .append("group by fin_statement.bgp_id, fin_statement.re_id "); @@ -330,8 +330,8 @@ public class CoachingDAO { .append(" inner join o_re_to_group togroup on (togroup.r_defgroup=").appendFalse().append(" and togroup.fk_entry_id = sg_re.repositoryentry_id)") .append(" inner join o_bs_group_member sg_participant on (sg_participant.fk_group_id=togroup.fk_group_id and sg_participant.g_role='participant')") .append(" inner join o_as_eff_statement sg_statement on (sg_statement.fk_identity = sg_participant.fk_identity_id and sg_statement.fk_resource_id = sg_re.fk_olatresource) ") - .append(" where sg_owner.fk_identity_id=:coachKey and sg_re.status ").in(RepositoryEntryStatusEnum.publishedAndClosed()) - .append(") ").appendAs().append(" fin_statement ")//TODO repo access + .append(" where sg_owner.fk_identity_id=:coachKey and sg_re.status ").in(RepositoryEntryStatusEnum.coachPublishedToClosed()) + .append(") ").appendAs().append(" fin_statement ") .append("group by fin_statement.bgp_id, fin_statement.re_id"); List<?> rawList = dbInstance.getCurrentEntityManager() @@ -380,15 +380,15 @@ public class CoachingDAO { private boolean getCourses(IdentityRef coach, Map<Long,CourseStatEntry> map) { NativeQueryBuilder sb = new NativeQueryBuilder(1024, dbInstance); - sb.append("select v.key, v.displayname") + sb.append("select v.key, v.displayname, v.status") .append(" from repositoryentry v") .append(" inner join v.olatResource as res") .append(" inner join v.groups as relGroup") .append(" inner join relGroup.group as baseGroup") .append(" inner join baseGroup.members as coach on coach.role ") .in(GroupRoles.coach, GroupRoles.owner.name()) - .append(" where coach.identity.key=:coachKey and res.resName='CourseModule'")//TODO repo access - .append(" and v.status ").in(RepositoryEntryStatusEnum.publishedAndClosed()); + .append(" where coach.identity.key=:coachKey and res.resName='CourseModule'") + .append(" and v.status ").in(RepositoryEntryStatusEnum.coachPublishedToClosed()); List<Object[]> rawList = dbInstance.getCurrentEntityManager() .createQuery(sb.toString(), Object[].class) @@ -399,6 +399,7 @@ public class CoachingDAO { CourseStatEntry entry = new CourseStatEntry(); entry.setRepoKey(((Number)rawStat[0]).longValue()); entry.setRepoDisplayName((String)rawStat[1]); + entry.setRepoStatus(RepositoryEntryStatusEnum.valueOf((String)rawStat[2])); map.put(entry.getRepoKey(), entry); } return !rawList.isEmpty(); @@ -416,7 +417,7 @@ public class CoachingDAO { .append(" inner join o_bs_group_member sg_participant on (sg_participant.fk_group_id=sg_coach.fk_group_id and sg_participant.g_role='").append(GroupRoles.participant).append("')") .append(" left join o_as_user_course_infos pg_initial_launch") .append(" on (pg_initial_launch.fk_resource_id = sg_re.fk_olatresource and pg_initial_launch.fk_identity = sg_participant.fk_identity_id)") - .append(" where sg_coach.fk_identity_id=:coachKey and sg_re.status ").in(RepositoryEntryStatusEnum.publishedAndClosed())//TODO repo access + .append(" where sg_coach.fk_identity_id=:coachKey and sg_re.status ").in(RepositoryEntryStatusEnum.coachPublishedToClosed()) .append(" group by sg_re.repositoryentry_id"); List<?> rawList = dbInstance.getCurrentEntityManager() @@ -451,8 +452,8 @@ public class CoachingDAO { .append(" inner join o_bs_group_member sg_participant on (sg_participant.fk_group_id=togroup.fk_group_id and sg_participant.g_role='participant')") .append(" left join o_as_user_course_infos pg_initial_launch") .append(" on (pg_initial_launch.fk_resource_id = sg_re.fk_olatresource and pg_initial_launch.fk_identity = sg_participant.fk_identity_id)") - .append(" where sg_coach.fk_identity_id=:coachKey and sg_re.status ").in(RepositoryEntryStatusEnum.publishedAndClosed()) - .append(" group by sg_re.repositoryentry_id");//TODO repo access + .append(" where sg_coach.fk_identity_id=:coachKey and sg_re.status ").in(RepositoryEntryStatusEnum.coachPublishedToClosed()) + .append(" group by sg_re.repositoryentry_id"); } else { sb.append("select") .append(" sg_re.repositoryentry_id as re_id,") @@ -461,9 +462,9 @@ public class CoachingDAO { .append(" from o_repositoryentry sg_re ") .append(" inner join o_re_to_group togroup on (togroup.fk_entry_id = sg_re.repositoryentry_id)") .append(" inner join o_bs_group_member sg_participant on (sg_participant.fk_group_id=togroup.fk_group_id and sg_participant.g_role='participant')") - .append(" left join o_as_user_course_infos pg_initial_launch")//TODO repo access + .append(" left join o_as_user_course_infos pg_initial_launch") .append(" on (pg_initial_launch.fk_resource_id = sg_re.fk_olatresource and pg_initial_launch.fk_identity = sg_participant.fk_identity_id)") - .append(" where sg_re.status ").in(RepositoryEntryStatusEnum.publishedAndClosed()).append(" and sg_re.fk_olatresource in (") + .append(" where sg_re.status ").in(RepositoryEntryStatusEnum.coachPublishedToClosed()).append(" and sg_re.fk_olatresource in (") .append(" select sg_res.resource_id from o_olatresource sg_res where sg_res.resname = 'CourseModule'") .append(" ) and exists (") .append(" select owngroup.id from o_re_to_group owngroup inner join o_bs_group_member sg_owner on (sg_owner.fk_group_id=owngroup.fk_group_id)") @@ -506,7 +507,7 @@ public class CoachingDAO { .append(" inner join o_bs_group_member sg_coach on (sg_coach.fk_group_id=togroup.fk_group_id and sg_coach.g_role in ('owner','coach')) ") .append(" inner join o_bs_group_member sg_participant on (sg_participant.fk_group_id=sg_coach.fk_group_id and sg_participant.g_role='participant') ") .append(" inner join o_as_eff_statement sg_statement on (sg_statement.fk_identity = sg_participant.fk_identity_id and sg_statement.fk_resource_id = sg_re.fk_olatresource) ") - .append(" where sg_coach.fk_identity_id=:coachKey and sg_re.status ").in(RepositoryEntryStatusEnum.publishedAndClosed()) + .append(" where sg_coach.fk_identity_id=:coachKey and sg_re.status ").in(RepositoryEntryStatusEnum.coachPublishedToClosed()) .append(") or fin_statement.id in ( select ") .append(" distinct sg_statement.id ") .append(" from o_repositoryentry sg_re ") @@ -516,7 +517,7 @@ public class CoachingDAO { .append(" inner join o_re_to_group togroup on (togroup.fk_entry_id = sg_re.repositoryentry_id) ") .append(" inner join o_bs_group_member sg_participant on (sg_participant.fk_group_id=togroup.fk_group_id and sg_participant.g_role='participant') ") .append(" inner join o_as_eff_statement sg_statement on (sg_statement.fk_identity = sg_participant.fk_identity_id and sg_statement.fk_resource_id = sg_re.fk_olatresource) ") - .append(" where sg_coach.fk_identity_id=:coachKey and sg_re.status ").in(RepositoryEntryStatusEnum.publishedAndClosed()).append(") ") + .append(" where sg_coach.fk_identity_id=:coachKey and sg_re.status ").in(RepositoryEntryStatusEnum.coachPublishedToClosed()).append(") ") .append("group by fin_statement.course_repo_key "); List<?> rawList = dbInstance.getCurrentEntityManager() @@ -585,7 +586,7 @@ public class CoachingDAO { .append(" inner join o_user sg_participant_user on (sg_participant_user.fk_identity=sg_participant_id.id)") .append(" left join o_as_user_course_infos pg_initial_launch") .append(" on (pg_initial_launch.fk_resource_id = sg_re.fk_olatresource and pg_initial_launch.fk_identity = sg_participant.fk_identity_id)") - .append(" where sg_coach.fk_identity_id=:coachKey and sg_re.status ").in(RepositoryEntryStatusEnum.publishedAndClosed())//TODO repo access + .append(" where sg_coach.fk_identity_id=:coachKey and sg_re.status ").in(RepositoryEntryStatusEnum.coachPublishedToClosed()) .append(" group by sg_participant_id.id, sg_participant_user.user_id"); if(dbInstance.isOracle()) { sb.append(", sg_participant_id.name"); @@ -651,9 +652,9 @@ public class CoachingDAO { .append(" inner join o_bs_group_member sg_participant on (sg_participant.fk_group_id=togroup.fk_group_id and sg_participant.g_role='participant')") .append(" inner join o_bs_identity sg_participant_id on (sg_participant_id.id=sg_participant.fk_identity_id)") .append(" inner join o_user sg_participant_user on (sg_participant_user.fk_identity=sg_participant_id.id)") - .append(" left join o_as_user_course_infos pg_initial_launch")//TODO repo access + .append(" left join o_as_user_course_infos pg_initial_launch") .append(" on (pg_initial_launch.fk_resource_id = sg_re.fk_olatresource and pg_initial_launch.fk_identity = sg_participant_id.id)") - .append(" where sg_re.status ").in(RepositoryEntryStatusEnum.publishedAndClosed()).append(" and sg_re.fk_olatresource in (") + .append(" where sg_re.status ").in(RepositoryEntryStatusEnum.coachPublishedToClosed()).append(" and sg_re.fk_olatresource in (") .append(" select sg_res.resource_id from o_olatresource sg_res where sg_res.resname = 'CourseModule'") .append(" )") .append(" group by sg_participant_id.id, sg_participant_user.user_id"); @@ -744,7 +745,7 @@ public class CoachingDAO { .append(" inner join o_bs_group_member sg_participant on (sg_participant.fk_group_id=sg_coach.fk_group_id and sg_participant.g_role='participant')") .append(" inner join o_as_eff_statement sg_statement") .append(" on (sg_statement.fk_identity = sg_participant.fk_identity_id and sg_statement.fk_resource_id = sg_re.fk_olatresource)") - .append(" where sg_re.status ").in(RepositoryEntryStatusEnum.publishedAndClosed())//TODO repo access + .append(" where sg_re.status ").in(RepositoryEntryStatusEnum.coachPublishedToClosed()) .append(" )"); } if(hasOwned) { @@ -760,9 +761,9 @@ public class CoachingDAO { .append(" and sg_owner.g_role='owner' and sg_owner.fk_identity_id=:coachKey and owngroup.r_defgroup=").appendTrue().append(")") .append(" inner join o_re_to_group togroup on (togroup.fk_entry_id = sg_re.repositoryentry_id) ") .append(" inner join o_bs_group_member sg_participant on (sg_participant.fk_group_id=togroup.fk_group_id and sg_participant.g_role='participant')") - .append(" inner join o_as_eff_statement sg_statement ")//TODO repo access + .append(" inner join o_as_eff_statement sg_statement ") .append(" on (sg_statement.fk_identity = sg_participant.fk_identity_id and sg_statement.fk_resource_id = sg_re.fk_olatresource)") - .append(" where sg_re.status ").in(RepositoryEntryStatusEnum.publishedAndClosed()).append(")"); + .append(" where sg_re.status ").in(RepositoryEntryStatusEnum.coachPublishedToClosed()).append(")"); } sb.append(" group by fin_statement.fk_identity"); @@ -821,7 +822,7 @@ public class CoachingDAO { .append(" left join o_as_user_course_infos pg_initial_launch ") .append(" on (pg_initial_launch.fk_resource_id = sg_re.fk_olatresource and pg_initial_launch.fk_identity = id_participant.id) ") .append(" inner join o_user user_participant on (user_participant.fk_identity=id_participant.id)") - .append(" where sg_re.status ").in(RepositoryEntryStatusEnum.publishedAndClosed()).append(" ");//TODO repo access + .append(" where sg_re.status ").in(RepositoryEntryStatusEnum.coachPublishedToClosed()).append(" "); appendUsersStatisticsSearchParams(params, queryParams, sb) .append(" group by sg_participant_id.id, sg_participant_user.user_id"); if(dbInstance.isOracle()) { @@ -872,8 +873,8 @@ public class CoachingDAO { .append(" inner join o_bs_group_member sg_participant on (sg_participant.fk_group_id=togroup.fk_group_id and sg_participant.g_role='participant') ") .append(" inner join o_as_eff_statement sg_statement on (sg_statement.fk_identity = sg_participant.fk_identity_id and sg_statement.fk_resource_id = sg_re.fk_olatresource) ") .append(" inner join o_bs_identity id_participant on (sg_participant.fk_identity_id = id_participant.id) "); - appendUsersStatisticsJoins(params, sb)//TODO repo access - .append(" where sg_re.status ").in(RepositoryEntryStatusEnum.publishedAndClosed()); + appendUsersStatisticsJoins(params, sb) + .append(" where sg_re.status ").in(RepositoryEntryStatusEnum.coachPublishedToClosed()); appendUsersStatisticsSearchParams(params, queryParams, sb) .append(") ") .append("group by fin_statement.fk_identity "); @@ -1067,7 +1068,7 @@ public class CoachingDAO { .append(" inner join baseGroup.members as coach on coach.role='coach'") .append(" inner join baseGroup.members as participant on participant.role='participant'") .append(" where coach.identity.key=:coachKey and participant.identity.key=:studentKey") - .append(" and re.status ").in(RepositoryEntryStatusEnum.publishedAndClosed());//TODO repo access + .append(" and re.status ").in(RepositoryEntryStatusEnum.coachPublishedToClosed()); List<RepositoryEntry> coachedEntries = dbInstance.getCurrentEntityManager() .createQuery(sb.toString(), RepositoryEntry.class) @@ -1085,7 +1086,7 @@ public class CoachingDAO { .append(" inner join relGroup.group as baseGroup") .append(" inner join baseGroup.members as participant on participant.role='participant'") .append(" where owner.identity.key=:coachKey and participant.identity.key=:studentKey") - .append(" and re.status ").in(RepositoryEntryStatusEnum.publishedAndClosed());//TODO repo access + .append(" and re.status ").in(RepositoryEntryStatusEnum.coachPublishedToClosed()); List<RepositoryEntry> ownedEntries = dbInstance.getCurrentEntityManager() .createQuery(sc.toString(), RepositoryEntry.class) @@ -1104,8 +1105,8 @@ public class CoachingDAO { .append(" inner join fetch v.olatResource res") .append(" inner join v.groups as relGroup") .append(" inner join relGroup.group as baseGroup") - .append(" inner join baseGroup.members as participant on participant.role='participant'")//TODO repo access - .append(" where res.resName='CourseModule' and v.status ").in(RepositoryEntryStatusEnum.publishedAndClosed()).append(" and participant.identity.key=:studentKey"); + .append(" inner join baseGroup.members as participant on participant.role='participant'") + .append(" where res.resName='CourseModule' and v.status ").in(RepositoryEntryStatusEnum.coachPublishedToClosed()).append(" and participant.identity.key=:studentKey"); return dbInstance.getCurrentEntityManager() .createQuery(sb.toString(), RepositoryEntry.class) diff --git a/src/main/java/org/olat/modules/coach/model/CourseStatEntry.java b/src/main/java/org/olat/modules/coach/model/CourseStatEntry.java index 3bc1511b120..3b2ea56d883 100644 --- a/src/main/java/org/olat/modules/coach/model/CourseStatEntry.java +++ b/src/main/java/org/olat/modules/coach/model/CourseStatEntry.java @@ -19,6 +19,8 @@ */ package org.olat.modules.coach.model; +import org.olat.repository.RepositoryEntryStatusEnum; + /** * * Dummy bean to transport statistic values about course @@ -29,6 +31,7 @@ public class CourseStatEntry { // s.repoKey, private Long repoKey; private String repoDisplayName; + private RepositoryEntryStatusEnum status; private int countStudents; private int countPassed; private int countFailed; @@ -71,6 +74,14 @@ public class CourseStatEntry { this.repoDisplayName = repoDisplayName; } + public RepositoryEntryStatusEnum getRepoStatus() { + return status; + } + + public void setRepoStatus(RepositoryEntryStatusEnum status) { + this.status = status; + } + public int getCountStudents() { return countStudents; } diff --git a/src/main/java/org/olat/modules/coach/ui/CourseListController.java b/src/main/java/org/olat/modules/coach/ui/CourseListController.java index 9ea8897686d..bba7d9acd81 100644 --- a/src/main/java/org/olat/modules/coach/ui/CourseListController.java +++ b/src/main/java/org/olat/modules/coach/ui/CourseListController.java @@ -23,20 +23,20 @@ import java.util.List; 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.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.stack.PopEvent; import org.olat.core.gui.components.stack.TooledStackedPanel; -import org.olat.core.gui.components.table.ColumnDescriptor; -import org.olat.core.gui.components.table.CustomRenderColumnDescriptor; -import org.olat.core.gui.components.table.DefaultColumnDescriptor; -import org.olat.core.gui.components.table.TableController; -import org.olat.core.gui.components.table.TableDataModel; -import org.olat.core.gui.components.table.TableEvent; -import org.olat.core.gui.components.table.TableGuiConfiguration; -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.dtabs.Activateable2; import org.olat.core.id.OLATResourceable; import org.olat.core.id.context.ContextEntry; @@ -48,6 +48,7 @@ import org.olat.modules.coach.model.CourseStatEntry; import org.olat.modules.coach.ui.CoursesTableDataModel.Columns; import org.olat.repository.RepositoryEntry; import org.olat.repository.RepositoryManager; +import org.olat.repository.ui.author.AccessRenderer; import org.springframework.beans.factory.annotation.Autowired; /** @@ -60,11 +61,12 @@ import org.springframework.beans.factory.annotation.Autowired; * * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com */ -public class CourseListController extends BasicController implements Activateable2 { +public class CourseListController extends FormBasicController implements Activateable2 { + private FlexiTableElement tableEl; + private CoursesTableDataModel tableModel; private final TooledStackedPanel stackPanel; - private final TableController tableCtr; - private final VelocityContainer mainVC; + private CourseController courseCtrl; private boolean hasChanged = false; @@ -75,37 +77,35 @@ public class CourseListController extends BasicController implements Activateabl private RepositoryManager repositoryManager; public CourseListController(UserRequest ureq, WindowControl wControl, TooledStackedPanel stackPanel) { - super(ureq, wControl); + super(ureq, wControl, "course_list"); this.stackPanel = stackPanel; stackPanel.addListener(this); - TableGuiConfiguration tableConfig = new TableGuiConfiguration(); - tableConfig.setTableEmptyMessage(translate("error.no.found")); - tableConfig.setDownloadOffered(true); - tableConfig.setPreferencesOffered(true, "courseListController"); - - tableCtr = new TableController(tableConfig, ureq, getWindowControl(), null, null, null, null, true, getTranslator()); - tableCtr.addColumnDescriptor(new DefaultColumnDescriptor("table.header.course.name", Columns.name.ordinal(), "select", getLocale())); - tableCtr.addColumnDescriptor(new DefaultColumnDescriptor("table.header.countStudents", Columns.countStudents.ordinal(), null, getLocale())); - tableCtr.addColumnDescriptor(new CustomRenderColumnDescriptor("table.header.login", Columns.initialLaunch.ordinal(), null, getLocale(), - ColumnDescriptor.ALIGNMENT_LEFT, new LightIconRenderer())); - tableCtr.addColumnDescriptor(new CustomRenderColumnDescriptor("table.header.passed", Columns.countPassed.ordinal(), null, getLocale(), - ColumnDescriptor.ALIGNMENT_LEFT, new ProgressRenderer(false, getTranslator()))); - tableCtr.addColumnDescriptor(new CustomRenderColumnDescriptor("table.header.averageScore", Columns.averageScore.ordinal(), null, getLocale(), - ColumnDescriptor.ALIGNMENT_RIGHT, new ScoreCellRenderer())); - - listenTo(tableCtr); + initForm(ureq); loadModel(); - - mainVC = createVelocityContainer("course_list"); - mainVC.put("coursTable", tableCtr.getInitialComponent()); - putInitialPanel(mainVC); } + @Override + protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { + FlexiTableColumnModel columnsModel = FlexiTableDataModelFactory.createFlexiTableColumnModel(); + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(Columns.name, "select")); + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(Columns.access, new AccessRenderer(getLocale()))); + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(Columns.countStudents)); + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(Columns.initialLaunch, new LightIconRenderer())); + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(Columns.countPassed, new ProgressRenderer(false, getTranslator()))); + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(Columns.averageScore, new ScoreCellRenderer())); + + tableModel = new CoursesTableDataModel(columnsModel); + tableEl = uifactory.addTableElement(getWindowControl(), "table", tableModel, 24, false, getTranslator(), formLayout); + tableEl.setExportEnabled(true); + tableEl.setEmtpyTableMessageKey("error.no.found"); + tableEl.setAndLoadPersistedPreferences(ureq, "courseListController-v2"); + } + private void loadModel() { List<CourseStatEntry> courseStatistics = coachingService.getCoursesStatistics(getIdentity()); - TableDataModel<CourseStatEntry> model = new CoursesTableDataModel(courseStatistics); - tableCtr.setTableDataModel(model); + tableModel.setObjects(courseStatistics); + tableEl.reset(false, true, true); } private void reloadModel() { @@ -121,7 +121,7 @@ public class CourseListController extends BasicController implements Activateabl } @Override - protected void event(UserRequest ureq, Component source, Event event) { + public void event(UserRequest ureq, Component source, Event event) { if(source == stackPanel) { if(event instanceof PopEvent) { PopEvent pe = (PopEvent)event; @@ -130,19 +130,32 @@ public class CourseListController extends BasicController implements Activateabl } } } + super.event(ureq, source, event); + } + + @Override + protected void formOK(UserRequest ureq) { + // } @Override - protected void event(UserRequest ureq, Controller source, Event event) { - if(source == tableCtr) { - if(event instanceof TableEvent) { - TableEvent e = (TableEvent) event; - if("select".equals(e.getActionId())) { - CourseStatEntry courseStat = (CourseStatEntry)tableCtr.getTableDataModel().getObject(e.getRowId()); + protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) { + if(tableEl == source) { + if(event instanceof SelectionEvent) { + SelectionEvent se = (SelectionEvent)event; + if("select".equals(se.getCommand())) { + CourseStatEntry courseStat = tableModel.getObject(se.getIndex()); selectCourse(ureq, courseStat); } } - } else if (source == courseCtrl) { + } + + super.formInnerEvent(ureq, source, event); + } + + @Override + protected void event(UserRequest ureq, Controller source, Event event) { + if (source == courseCtrl) { if(event == Event.CHANGED_EVENT) { hasChanged = true; } else if("next.course".equals(event.getCommand())) { @@ -160,10 +173,10 @@ public class CourseListController extends BasicController implements Activateabl ContextEntry ce = entries.get(0); OLATResourceable ores = ce.getOLATResourceable(); - if("RepositoryEntry".equals(ores.getResourceableTypeName())) { + if("RepositoryEntry".equalsIgnoreCase(ores.getResourceableTypeName())) { Long repoKey = ores.getResourceableId(); - for(int i=tableCtr.getRowCount(); i-->0; ) { - CourseStatEntry courseStat = (CourseStatEntry)tableCtr.getTableDataModel().getObject(i); + for(int i=tableModel.getRowCount(); i-->0; ) { + CourseStatEntry courseStat = tableModel.getObject(i); if(repoKey.equals(courseStat.getRepoKey())) { selectCourse(ureq, courseStat); if(courseCtrl != null) { @@ -177,21 +190,21 @@ public class CourseListController extends BasicController implements Activateabl private void previousCourse(UserRequest ureq) { CourseStatEntry currentEntry = courseCtrl.getEntry(); - int previousIndex = tableCtr.getIndexOfSortedObject(currentEntry) - 1; - if(previousIndex < 0 || previousIndex >= tableCtr.getRowCount()) { - previousIndex = tableCtr.getRowCount() - 1; + int previousIndex = tableModel.getIndexOfObject(currentEntry) - 1; + if(previousIndex < 0 || previousIndex >= tableModel.getRowCount()) { + previousIndex = tableModel.getRowCount() - 1; } - CourseStatEntry previousEntry = (CourseStatEntry)tableCtr.getSortedObjectAt(previousIndex); + CourseStatEntry previousEntry = tableModel.getObject(previousIndex); selectCourse(ureq, previousEntry); } private void nextCourse(UserRequest ureq) { CourseStatEntry currentEntry = courseCtrl.getEntry(); - int nextIndex = tableCtr.getIndexOfSortedObject(currentEntry) + 1; - if(nextIndex < 0 || nextIndex >= tableCtr.getRowCount()) { + int nextIndex = tableModel.getIndexOfObject(currentEntry) + 1; + if(nextIndex < 0 || nextIndex >= tableModel.getRowCount()) { nextIndex = 0; } - CourseStatEntry nextEntry = (CourseStatEntry)tableCtr.getSortedObjectAt(nextIndex); + CourseStatEntry nextEntry = tableModel.getObject(nextIndex); selectCourse(ureq, nextEntry); } @@ -204,8 +217,8 @@ public class CourseListController extends BasicController implements Activateabl OLATResourceable ores = OresHelper.createOLATResourceableInstance(RepositoryEntry.class, re.getKey()); WindowControl bwControl = addToHistory(ureq, ores, null); - int index = tableCtr.getIndexOfSortedObject(courseStat); - courseCtrl = new CourseController(ureq, bwControl, stackPanel, re, courseStat, index, tableCtr.getRowCount()); + int index = tableModel.getIndexOfObject(courseStat); + courseCtrl = new CourseController(ureq, bwControl, stackPanel, re, courseStat, index, tableModel.getRowCount()); listenTo(courseCtrl); stackPanel.popUpToRootController(ureq); stackPanel.pushController(re.getDisplayname(), courseCtrl); diff --git a/src/main/java/org/olat/modules/coach/ui/CoursesTableDataModel.java b/src/main/java/org/olat/modules/coach/ui/CoursesTableDataModel.java index 93cd51221c6..5762a28a8e4 100644 --- a/src/main/java/org/olat/modules/coach/ui/CoursesTableDataModel.java +++ b/src/main/java/org/olat/modules/coach/ui/CoursesTableDataModel.java @@ -19,13 +19,13 @@ */ package org.olat.modules.coach.ui; -import java.util.ArrayList; -import java.util.List; - -import org.olat.core.gui.components.table.TableDataModel; +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.coach.model.CourseStatEntry; -import org.olat.modules.coach.ui.ProgressValue; - import org.olat.modules.coach.ui.LightedValue.Light; /** @@ -37,41 +37,41 @@ import org.olat.modules.coach.ui.LightedValue.Light; * * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com */ -public class CoursesTableDataModel implements TableDataModel<CourseStatEntry> { - - private List<CourseStatEntry> statistics; +public class CoursesTableDataModel extends DefaultFlexiTableDataModel<CourseStatEntry> + implements SortableFlexiTableDataModel<CourseStatEntry> { + + public CoursesTableDataModel(FlexiTableColumnModel columnsModel) { + super(columnsModel); + } - public CoursesTableDataModel(List<CourseStatEntry> cours) { - this.statistics = cours; + public int getIndexOfObject(CourseStatEntry entry) { + return getObjects().indexOf(entry); } @Override - public int getColumnCount() { - return 5; + public void sort(SortKey orderBy) { + super.setObjects(new SortableFlexiTableModelDelegate<>(orderBy, this, null).sort()); } @Override - public int getRowCount() { - return statistics == null ? 0 : statistics.size(); + public Object getValueAt(int row, int col) { + CourseStatEntry c = getObject(row); + return getValueAt(c, col); } @Override - public Object getValueAt(int row, int col) { - CourseStatEntry c = statistics.get(row); + public Object getValueAt(CourseStatEntry row, int col) { switch(Columns.getValueAt(col)) { - case name: { - return c.getRepoDisplayName(); - } - case countStudents: { - return new Integer(c.getCountStudents()); - } + case name: return row.getRepoDisplayName(); + case access: return row.getRepoStatus(); + case countStudents: return Integer.valueOf(row.getCountStudents()); case initialLaunch: { - int count = c.getCountStudents(); + int count = row.getCountStudents(); if(count == 0) { return new LightedValue(null, Light.grey); } - int launch = c.getInitialLaunch(); + int launch = row.getInitialLaunch(); Light light = Light.yellow; if(launch == count) { light = Light.green; @@ -81,58 +81,70 @@ public class CoursesTableDataModel implements TableDataModel<CourseStatEntry> { return new LightedValue(launch, light); } case countPassed: { - int numOfStudents = c.getCountStudents(); + int numOfStudents = row.getCountStudents(); if(numOfStudents == 0) { return numOfStudents; } ProgressValue val = new ProgressValue(); val.setTotal(numOfStudents); - val.setGreen(c.getCountPassed()); + val.setGreen(row.getCountPassed()); return val; } case countPassedLight: { - int count = c.getCountStudents(); + int count = row.getCountStudents(); if(count == 0) { return new LightedValue(null, Light.grey); } - int passed = c.getCountPassed(); + int passed = row.getCountPassed(); Light light = Light.yellow; if(passed == count) { light = Light.green; } else if (passed == 0) { light = Light.red; } - return new LightedValue(c.getCountPassed(), light); + return new LightedValue(row.getCountPassed(), light); } - case averageScore: return c.getAverageScore(); + case averageScore: return row.getAverageScore(); } return null; } - @Override - public CourseStatEntry getObject(int row) { - return statistics.get(row); - } - - @Override - public void setObjects(List<CourseStatEntry> objects) { - statistics = objects; - } - @Override public CoursesTableDataModel createCopyWithEmptyList() { - return new CoursesTableDataModel(new ArrayList<CourseStatEntry>()); + return new CoursesTableDataModel(getTableColumnModel()); } - public static enum Columns { - name, - countStudents, - initialLaunch, - countPassed, - countPassedLight, - averageScore; + public enum Columns implements FlexiSortableColumnDef { + name("table.header.course.name"), + access("table.header.course.access"), + countStudents("table.header.countStudents"), + initialLaunch("table.header.login"), + countPassed("table.header.passed"), + countPassedLight("table.header.passed"), + averageScore("table.header.averageScore"); + + private final String i18nKey; + + private Columns(String i18nKey) { + this.i18nKey = i18nKey; + } + + @Override + public String i18nHeaderKey() { + return i18nKey; + } + + @Override + public boolean sortable() { + return true; + } + + @Override + public String sortKey() { + return name(); + } public static Columns getValueAt(int ordinal) { if(ordinal >= 0 && ordinal < values().length) { diff --git a/src/main/java/org/olat/modules/coach/ui/EfficiencyStatementEntryTableDataModel.java b/src/main/java/org/olat/modules/coach/ui/EfficiencyStatementEntryTableDataModel.java index 01fedae3e2c..57047cbf8e6 100644 --- a/src/main/java/org/olat/modules/coach/ui/EfficiencyStatementEntryTableDataModel.java +++ b/src/main/java/org/olat/modules/coach/ui/EfficiencyStatementEntryTableDataModel.java @@ -55,7 +55,7 @@ public class EfficiencyStatementEntryTableDataModel extends DefaultFlexiTableDat } public boolean contains(IdentityResourceKey key) { - return certificateMap == null ? false : certificateMap.containsKey(key); + return certificateMap != null && certificateMap.containsKey(key); } public void putCertificate(CertificateLight certificate) { @@ -184,7 +184,7 @@ public class EfficiencyStatementEntryTableDataModel extends DefaultFlexiTableDat return new EfficiencyStatementEntryTableDataModel(getTableColumnModel()); } - public static enum Columns implements FlexiSortableColumnDef { + public enum Columns implements FlexiSortableColumnDef { name("student.name"), repoName("table.header.course.name"), score("table.header.score"), diff --git a/src/main/java/org/olat/modules/coach/ui/StudentCoursesController.java b/src/main/java/org/olat/modules/coach/ui/StudentCoursesController.java index e9503014c47..31e38aa0d11 100644 --- a/src/main/java/org/olat/modules/coach/ui/StudentCoursesController.java +++ b/src/main/java/org/olat/modules/coach/ui/StudentCoursesController.java @@ -429,7 +429,7 @@ public class StudentCoursesController extends FormBasicController implements Act } private void openHome(UserRequest ureq) { - List<ContextEntry> ces = new ArrayList<ContextEntry>(4); + List<ContextEntry> ces = new ArrayList<>(4); ces.add(BusinessControlFactory.getInstance().createContextEntry(student)); BusinessControl bc = BusinessControlFactory.getInstance().createFromContextEntries(ces); diff --git a/src/main/java/org/olat/modules/coach/ui/_content/course_list.html b/src/main/java/org/olat/modules/coach/ui/_content/course_list.html index 5ee3be38cfb..bade9402acd 100644 --- a/src/main/java/org/olat/modules/coach/ui/_content/course_list.html +++ b/src/main/java/org/olat/modules/coach/ui/_content/course_list.html @@ -1 +1 @@ -$r.render("coursTable") \ No newline at end of file +$r.render("table") \ No newline at end of file diff --git a/src/main/java/org/olat/modules/coach/ui/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/modules/coach/ui/_i18n/LocalStrings_de.properties index a685c759bc5..b631e5065a0 100644 --- a/src/main/java/org/olat/modules/coach/ui/_i18n/LocalStrings_de.properties +++ b/src/main/java/org/olat/modules/coach/ui/_i18n/LocalStrings_de.properties @@ -52,6 +52,7 @@ table.header.averageScore=Durchschnitt table.header.certificate=Zertifikat table.header.countCourses=# Kurs table.header.countStudents=# Teilnehmer +table.header.course.access=Lebenszyklus table.header.course.name=Kurs table.header.lastScoreDate=$org.olat.course.assessment\:table.header.lastScoreDate table.header.lastUserModificationDate=$org.olat.course.assessment\:table.header.lastUserModificationDate diff --git a/src/main/java/org/olat/modules/coach/ui/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/modules/coach/ui/_i18n/LocalStrings_en.properties index 091fd79bd70..d476849fe1c 100644 --- a/src/main/java/org/olat/modules/coach/ui/_i18n/LocalStrings_en.properties +++ b/src/main/java/org/olat/modules/coach/ui/_i18n/LocalStrings_en.properties @@ -56,6 +56,7 @@ table.header.averageScore=Average table.header.certificate=Certificate table.header.countCourses=\# Courses table.header.countStudents=\# Members +table.header.course.access=Life cycle table.header.course.name=Courses table.header.lastCoachModificationDate=$org.olat.course.assessment\:table.header.lastCoachModificationDate table.header.lastScoreDate=$org.olat.course.assessment\:table.header.lastScoreDate diff --git a/src/main/java/org/olat/repository/manager/RepositoryEntryMyCourseQueries.java b/src/main/java/org/olat/repository/manager/RepositoryEntryMyCourseQueries.java index 970dfc18933..cc8201bcc4c 100644 --- a/src/main/java/org/olat/repository/manager/RepositoryEntryMyCourseQueries.java +++ b/src/main/java/org/olat/repository/manager/RepositoryEntryMyCourseQueries.java @@ -364,7 +364,7 @@ public class RepositoryEntryMyCourseQueries { private boolean appendMyViewAccessSubSelect(QueryBuilder sb, Roles roles, List<Filter> filters, boolean membershipMandatory) { if(roles.isGuestOnly()) { - sb.append(" v.guests=true and v.status ").in(RepositoryEntryStatusEnum.publishedAndClosed());//TODO repo access + sb.append(" v.guests=true and v.status ").in(RepositoryEntryStatusEnum.publishedAndClosed()); return false; } diff --git a/src/main/java/org/olat/repository/ui/RepositoryTableModel.java b/src/main/java/org/olat/repository/ui/RepositoryTableModel.java index fa16f3e16aa..f63f2766a78 100644 --- a/src/main/java/org/olat/repository/ui/RepositoryTableModel.java +++ b/src/main/java/org/olat/repository/ui/RepositoryTableModel.java @@ -213,7 +213,6 @@ public class RepositoryTableModel extends DefaultTableDataModel<RepositoryEntry> RepoCols.lifecycleEnd.ordinal(), null, loc, ColumnDescriptor.ALIGNMENT_LEFT, dateRenderer)); tableCtr.addColumnDescriptor(new DefaultColumnDescriptor("table.header.author", RepoCols.author.ordinal(), null, loc)); - AccessRenderer accessRenderer = new AccessRenderer(translator.getLocale()); ColumnDescriptor accessColDesc = new CustomRenderColumnDescriptor("table.header.access", RepoCols.repoEntry.ordinal(), null, loc, ColumnDescriptor.ALIGNMENT_LEFT, accessRenderer) { @Override @@ -227,7 +226,7 @@ public class RepositoryTableModel extends DefaultTableDataModel<RepositoryEntry> RepositoryEntry re2 = (RepositoryEntry)o2; int ar1 = re1.getEntryStatus().ordinal(); - if(!re1.isAllUsers() && !re1.isGuests()) {//TODO repo access + if(!re1.isAllUsers() && !re1.isGuests()) { ar1 = 99; } diff --git a/src/main/java/org/olat/repository/ui/author/AccessRenderer.java b/src/main/java/org/olat/repository/ui/author/AccessRenderer.java index 537eb005f4f..5006b97d1ff 100644 --- a/src/main/java/org/olat/repository/ui/author/AccessRenderer.java +++ b/src/main/java/org/olat/repository/ui/author/AccessRenderer.java @@ -58,21 +58,23 @@ public class AccessRenderer implements FlexiCellRenderer, CustomCellRenderer { } else if(val instanceof RepositoryEntry) { RepositoryEntry re = (RepositoryEntry)val; render(sb, re.getEntryStatus()); + } else if(val instanceof RepositoryEntryStatusEnum) { + RepositoryEntryStatusEnum entryStatus = (RepositoryEntryStatusEnum)val; + render(sb, entryStatus); } } @Override public void render(StringOutput sb, Renderer renderer, Object val, Locale locale, int alignment, String action) { // use the FlexiCellRenderer method - this.render(renderer, sb, val, -1, null, null, null); + render(renderer, sb, val, -1, null, null, null); } public void render(StringOutput sb, RepositoryEntryStatusEnum status) { - - sb.append("<span class='o_labeled_light o_repo_status_").append(status.name()); - sb.append("' title=\"").append(StringHelper.escapeHtml(translator.translate("status." + status.name() + ".desc"))).append("\">"); - sb.append("<i class='o_icon o_icon-fw o_icon_repo_status_").append(status.name()).append("'> </i> <span>"); - sb.append(translator.translate("table.status.".concat(status.name()))); - sb.append("</span></span>"); + sb.append("<span class='o_labeled_light o_repo_status_").append(status.name()) + .append("' title=\"").append(StringHelper.escapeHtml(translator.translate("status." + status.name() + ".desc"))).append("\">") + .append("<i class='o_icon o_icon-fw o_icon_repo_status_").append(status.name()).append("'> </i> <span>") + .append(translator.translate("table.status.".concat(status.name()))) + .append("</span></span>"); } } -- GitLab