diff --git a/.hgtags b/.hgtags index f9d3c165af3a98b81bd66331d74a9d4c7950a5d3..6a0e5499a704648d92be32c78177e04356d99982 100644 --- a/.hgtags +++ b/.hgtags @@ -134,3 +134,4 @@ bc8ce641a5620f5717e9a73e31028d41ab7cdc5d OpenOLAT 10.4.4 d355c6357d0e5aeb25fc978b747824447a214b88 OpenOLAT 10.4.5 fef7f6b2e2628d242e93f86d208f4cfb9c807d7c OpenOLAT 10.4.6 b38ed621ea53507fbf41eb616648ebad2340e613 OpenOLAT 10.4.7 +273a6270a08460c07fdfbb53e31547b9c7e1c8c9 OpenOLAT 10.4.8 diff --git a/src/main/java/org/olat/commons/calendar/ICalServlet.java b/src/main/java/org/olat/commons/calendar/ICalServlet.java index bde343b33759982fd8ba9831737a775418e879bb..8bd448aa93306dc0d2cd920a9b5a29e14ea5c9d3 100644 --- a/src/main/java/org/olat/commons/calendar/ICalServlet.java +++ b/src/main/java/org/olat/commons/calendar/ICalServlet.java @@ -141,7 +141,14 @@ public class ICalServlet extends HttpServlet { private void getIcalDocument(String requestUrl, HttpServletResponse response) throws ValidationException, IOException { // get the individual path tokens - String pathInfo = requestUrl.replaceAll(".ics", ""); + String pathInfo; + int icsIndex = requestUrl.indexOf(".ics"); + if(icsIndex > 0) { + pathInfo = requestUrl.substring(0, icsIndex); + } else { + pathInfo = requestUrl; + } + String[] pathInfoTokens = pathInfo.split("/"); if(pathInfoTokens.length < 4) { response.sendError(HttpServletResponse.SC_BAD_REQUEST, requestUrl); diff --git a/src/main/java/org/olat/core/commons/services/webdav/ui/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/core/commons/services/webdav/ui/_i18n/LocalStrings_de.properties index 84dc0fcb2b3126980f0834de835c3b8c9bdbee03..d3a57dcf03009d69f05f5abed55eb9d7a9b2af92 100644 --- a/src/main/java/org/olat/core/commons/services/webdav/ui/_i18n/LocalStrings_de.properties +++ b/src/main/java/org/olat/core/commons/services/webdav/ui/_i18n/LocalStrings_de.properties @@ -10,4 +10,4 @@ webdav.on=ein webdav.termsfolders=Kurse nach Semesterdaten gruppieren webdav.for.learners.participants=Zugriff für Studenten / Betreuer Kursen webdav.for.learners.bookmarks=Zugriff für Studenten / Betreuer Favoriten -webdav.prepend.reference=Kursreferenz zu Titel voranstellen \ No newline at end of file +webdav.prepend.reference=Kennzeichen zu Titel voranstellen \ No newline at end of file diff --git a/src/main/java/org/olat/core/commons/services/webdav/ui/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/core/commons/services/webdav/ui/_i18n/LocalStrings_en.properties index 9312c822f53a360c1086398fd475cf8a428c6e58..75ddad6c1c908002c56912dd6e8a608d562f6b9a 100644 --- a/src/main/java/org/olat/core/commons/services/webdav/ui/_i18n/LocalStrings_en.properties +++ b/src/main/java/org/olat/core/commons/services/webdav/ui/_i18n/LocalStrings_en.properties @@ -10,4 +10,4 @@ webdav.on=enabled webdav.termsfolders=Group courses by semester terms webdav.for.learners.participants=Enable access for courses where user is participant or coach webdav.for.learners.bookmarks=Enable for courses that users marked as favorite -webdav.prepend.reference=Prepend course reference to title \ No newline at end of file +webdav.prepend.reference=Prepend external course reference to title \ No newline at end of file diff --git a/src/main/java/org/olat/core/commons/services/webdav/ui/_i18n/LocalStrings_fr.properties b/src/main/java/org/olat/core/commons/services/webdav/ui/_i18n/LocalStrings_fr.properties index 4766cd4d51b7ea1da3b903886e09e78acfafe955..121d00a7deade1acf34d6e2897a0c6419d9cb329 100644 --- a/src/main/java/org/olat/core/commons/services/webdav/ui/_i18n/LocalStrings_fr.properties +++ b/src/main/java/org/olat/core/commons/services/webdav/ui/_i18n/LocalStrings_fr.properties @@ -9,5 +9,5 @@ webdav.for.learners.participants=Acc\u00E8s aux cours pour les \u00E9tudiants et webdav.link=Montre les liens WebDAV webdav.module=Acc\u00E8s WebDAV webdav.on=on -webdav.prepend.reference=Ajouter la r\u00E9f\u00E9rence au nom du cours +webdav.prepend.reference=Ajouter la r\u00E9f\u00E9rence externe au nom du cours webdav.termsfolders=Grouper les cours par semestre diff --git a/src/main/java/org/olat/course/CoursefolderWebDAVMergeSource.java b/src/main/java/org/olat/course/CoursefolderWebDAVMergeSource.java index 9173659a3d656503ad3f9d17e873e534a4986c07..062e2f20d4c0aa933380d0f86771c4e1cb24f0be 100644 --- a/src/main/java/org/olat/course/CoursefolderWebDAVMergeSource.java +++ b/src/main/java/org/olat/course/CoursefolderWebDAVMergeSource.java @@ -112,8 +112,8 @@ class CoursefolderWebDAVMergeSource extends WebDAVMergeSource { duplicates.add(re); String displayName = re.getDisplayname(); - if(prependReference && StringHelper.containsNonWhitespace(re.getExternalId())) { - displayName = re.getExternalId() + " " + displayName; + if(prependReference && StringHelper.containsNonWhitespace(re.getExternalRef())) { + displayName = re.getExternalRef() + " " + displayName; } String courseTitle = RequestUtil.normalizeFilename(displayName); NamedContainerImpl cfContainer = new CoursefolderWebDAVNamedContainer(courseTitle, re, editor ? null : identityEnv); diff --git a/src/main/java/org/olat/course/nodes/ENCourseNode.java b/src/main/java/org/olat/course/nodes/ENCourseNode.java index f91d50c08c29d62ca2d434da0541a8367158b1a6..c41f9c5fb4e0145fd00a47caf433b58f5962d714 100644 --- a/src/main/java/org/olat/course/nodes/ENCourseNode.java +++ b/src/main/java/org/olat/course/nodes/ENCourseNode.java @@ -27,8 +27,11 @@ package org.olat.course.nodes; import java.io.File; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; +import org.olat.core.CoreSpringFactory; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.stack.BreadcrumbPanel; import org.olat.core.gui.control.Controller; @@ -53,6 +56,10 @@ import org.olat.course.properties.PersistingCoursePropertyManager; import org.olat.course.run.navigation.NodeRunConstructionResult; import org.olat.course.run.userview.NodeEvaluation; import org.olat.course.run.userview.UserCourseEnvironment; +import org.olat.group.BusinessGroupService; +import org.olat.group.BusinessGroupShort; +import org.olat.group.area.BGArea; +import org.olat.group.area.BGAreaManager; import org.olat.modules.ModuleConfiguration; import org.olat.repository.RepositoryEntry; @@ -66,6 +73,7 @@ import org.olat.repository.RepositoryEntry; */ public class ENCourseNode extends AbstractAccessableCourseNode { private static final String PACKAGE = Util.getPackageName(ENCourseNode.class); + private static final String PACKAGE_COND = Util.getPackageName(ConditionEditController.class); /** * property name for the initial enrollment date will be set only the first @@ -186,44 +194,28 @@ public class ENCourseNode extends AbstractAccessableCourseNode { oneClickStatusCache = null; // only here we know which translator to take for translating condition // error messages - String translatorStr = Util.getPackageName(ConditionEditController.class); - List<StatusDescription> condErrs = isConfigValidWithTranslator(cev, translatorStr, getConditionExpressions()); + List<StatusDescription> condErrs = isConfigValidWithTranslator(cev, PACKAGE_COND, getConditionExpressions()); List<StatusDescription> missingNames = new ArrayList<StatusDescription>(); /* * check group and area names for existence */ - String nodeId = getIdent(); ModuleConfiguration mc = getModuleConfiguration(); - String areaStr = (String) mc.get(CONFIG_AREANAME); - if (areaStr != null) { - String[] areas = areaStr.split(","); - for (int i = 0; i < areas.length; i++) { - String trimmed = areas[i] != null ? - FilterFactory.getHtmlTagsFilter().filter(areas[i]).trim() : areas[i]; - if (!trimmed.equals("") && !cev.existsArea(trimmed)) { - StatusDescription sd = new StatusDescription(StatusDescription.WARNING, "error.notfound.name", "solution.checkgroupmanagement", - new String[] { "NONE", trimmed }, translatorStr); - sd.setDescriptionForUnit(nodeId); - missingNames.add(sd); - } - } + String areaNames = (String) mc.get(CONFIG_AREANAME); + List<Long> areaKeys = mc.getList(ENCourseNode.CONFIG_AREA_IDS, Long.class); + List<String> missingAreas = getMissingAreas(areaKeys, areaNames, cev); + if(missingAreas.size() > 0) { + missingNames.add(addStatusErrorMissing(missingAreas)); } - String groupStr = (String) mc.get(CONFIG_GROUPNAME); - if (groupStr != null) { - String[] groups = groupStr.split(","); - for (int i = 0; i < groups.length; i++) { - String trimmed = groups[i] != null ? - FilterFactory.getHtmlTagsFilter().filter(groups[i]).trim() : groups[i]; - if (!trimmed.equals("") && !cev.existsGroup(trimmed)) { - StatusDescription sd = new StatusDescription(StatusDescription.WARNING, "error.notfound.name", "solution.checkgroupmanagement", - new String[] { "NONE", trimmed }, translatorStr); - sd.setDescriptionForUnit(nodeId); - missingNames.add(sd); - } - } + + String groupNames = (String) mc.get(CONFIG_GROUPNAME); + List<Long> groupKeys = mc.getList(ENCourseNode.CONFIG_GROUP_IDS, Long.class); + List<String> missingGroups = getMissingBusinessGroups(groupKeys, groupNames, cev); + if(missingGroups.size() > 0) { + missingNames.add(addStatusErrorMissing(missingGroups)); } + missingNames.addAll(condErrs); /* * sort -> Errors > Warnings > Infos and remove NOERRORS, if @@ -232,6 +224,122 @@ public class ENCourseNode extends AbstractAccessableCourseNode { oneClickStatusCache = StatusDescriptionHelper.sort(missingNames); return oneClickStatusCache; } + + private StatusDescription addStatusErrorMissing(List<String> missingObjects) { + String labelKey = missingObjects.size() == 1 ? "error.notfound.name" : "error.notfound.names"; + StringBuilder missing = new StringBuilder(); + for(String missingObject:missingObjects) { + if(missing.length() > 0) missing.append(", "); + missing.append(missingObject); + } + + StatusDescription sd = new StatusDescription(StatusDescription.WARNING, labelKey, "solution.checkgroupmanagement", + new String[] { "NONE", missing.toString() }, PACKAGE_COND); + sd.setDescriptionForUnit(getIdent()); + return sd; + } + + public List<String> getMissingAreas(List<Long> areaKeys, String areaNames, CourseEditorEnv cev) { + List<String> missingNames = new ArrayList<>(); + if(areaKeys == null || areaKeys.isEmpty()) { + if (areaNames != null) { + String[] areas = areaNames.split(","); + for (int i = 0; i < areas.length; i++) { + String trimmed = areas[i] != null ? + FilterFactory.getHtmlTagsFilter().filter(areas[i]).trim() : areas[i]; + if (!trimmed.equals("") && !cev.existsGroup(trimmed)) { + missingNames.add(trimmed); + } + } + } + } else { + Set<Long> missingAreas = new HashSet<Long>(); + List<BGArea> existingAreas = CoreSpringFactory.getImpl(BGAreaManager.class).loadAreas(areaKeys); + + List<String> knowNames = new ArrayList<>(); + if (areaNames != null) { + String[] areas = areaNames.split(","); + for (int i = 0; i < areas.length; i++) { + String trimmed = areas[i] != null ? FilterFactory.getHtmlTagsFilter().filter(areas[i]).trim() : areas[i]; + knowNames.add(trimmed); + } + } + + a_a: + for(Long areaKey:areaKeys) { + for(BGArea area:existingAreas) { + if(area.getKey().equals(areaKey)) { + String trimmed = area.getName() != null ? FilterFactory.getHtmlTagsFilter().filter(area.getName()).trim() : area.getName(); + knowNames.remove(trimmed); + continue a_a; + } + } + missingAreas.add(areaKey); + } + + if(missingAreas.size() > 0 ) { + if(knowNames.size() > 0) { + missingNames.addAll(knowNames); + } else { + for(Long missingArea:missingAreas) { + missingNames.add(missingArea.toString()); + } + } + } + } + return missingNames; + } + + public List<String> getMissingBusinessGroups(List<Long> groupKeys, String groupNames, CourseEditorEnv cev) { + List<String> missingNames = new ArrayList<>(); + if(groupKeys == null || groupKeys.isEmpty()) { + if (groupNames != null) { + String[] groups = groupNames.split(","); + for (int i = 0; i < groups.length; i++) { + String trimmed = groups[i] != null ? + FilterFactory.getHtmlTagsFilter().filter(groups[i]).trim() : groups[i]; + if (!trimmed.equals("") && !cev.existsGroup(trimmed)) { + missingNames.add(trimmed); + } + } + } + } else { + Set<Long> missingGroups = new HashSet<Long>(); + List<BusinessGroupShort> existingGroups = CoreSpringFactory.getImpl(BusinessGroupService.class).loadShortBusinessGroups(groupKeys); + + List<String> knowNames = new ArrayList<>(); + if (groupNames != null) { + String[] groups = groupNames.split(","); + for (int i = 0; i < groups.length; i++) { + String trimmed = groups[i] != null ? FilterFactory.getHtmlTagsFilter().filter(groups[i]).trim() : groups[i]; + knowNames.add(trimmed); + } + } + + a_a: + for(Long groupKey:groupKeys) { + for(BusinessGroupShort group:existingGroups) { + if(group.getKey().equals(groupKey)) { + String trimmed = group.getName() != null ? FilterFactory.getHtmlTagsFilter().filter(group.getName()).trim() : group.getName(); + knowNames.remove(trimmed); + continue a_a; + } + } + missingGroups.add(groupKey); + } + + if(missingGroups.size() > 0 ) { + if(knowNames.size() > 0) { + missingNames.addAll(knowNames); + } else { + for(Long missingGroup:missingGroups) { + missingNames.add(missingGroup.toString()); + } + } + } + } + return missingNames; + } /** * @see org.olat.course.nodes.CourseNode#getReferencedRepositoryEntry() diff --git a/src/main/java/org/olat/group/ui/main/AbstractMemberListController.java b/src/main/java/org/olat/group/ui/main/AbstractMemberListController.java index 6ae0b81f38f83ed23be05f89256b74ec0aee71f2..4d2478e4edf7560589204edac014c99f96c12277 100644 --- a/src/main/java/org/olat/group/ui/main/AbstractMemberListController.java +++ b/src/main/java/org/olat/group/ui/main/AbstractMemberListController.java @@ -450,29 +450,33 @@ public abstract class AbstractMemberListController extends FormBasicController i } protected void confirmDelete(UserRequest ureq, List<MemberView> members) { - int numOfOwners = - repoEntry == null ? businessGroupService.countMembers(businessGroup, GroupRoles.coach.name()) - : repositoryService.countMembers(repoEntry, GroupRoles.owner.name()); - - int numOfRemovedOwner = 0; - List<Long> identityKeys = new ArrayList<Long>(); - for(MemberView member:members) { - identityKeys.add(member.getIdentityKey()); - if(member.getMembership().isOwner()) { - numOfRemovedOwner++; - } - } - if(numOfRemovedOwner == 0 || numOfOwners - numOfRemovedOwner > 0) { - List<Identity> ids = securityManager.loadIdentityByKeys(identityKeys); - leaveDialogBox = new MemberLeaveConfirmationController(ureq, getWindowControl(), ids); - listenTo(leaveDialogBox); - - cmc = new CloseableModalController(getWindowControl(), translate("close"), leaveDialogBox.getInitialComponent(), - true, translate("edit.member")); - cmc.activate(); - listenTo(cmc); + if(members.isEmpty()) { + showWarning("error.select.one.user"); } else { - showWarning("error.atleastone"); + int numOfOwners = + repoEntry == null ? businessGroupService.countMembers(businessGroup, GroupRoles.coach.name()) + : repositoryService.countMembers(repoEntry, GroupRoles.owner.name()); + + int numOfRemovedOwner = 0; + List<Long> identityKeys = new ArrayList<Long>(); + for(MemberView member:members) { + identityKeys.add(member.getIdentityKey()); + if(member.getMembership().isOwner()) { + numOfRemovedOwner++; + } + } + if(numOfRemovedOwner == 0 || numOfOwners - numOfRemovedOwner > 0) { + List<Identity> ids = securityManager.loadIdentityByKeys(identityKeys); + leaveDialogBox = new MemberLeaveConfirmationController(ureq, getWindowControl(), ids); + listenTo(leaveDialogBox); + + cmc = new CloseableModalController(getWindowControl(), translate("close"), leaveDialogBox.getInitialComponent(), + true, translate("edit.member")); + cmc.activate(); + listenTo(cmc); + } else { + showWarning("error.atleastone"); + } } } @@ -489,14 +493,18 @@ public abstract class AbstractMemberListController extends FormBasicController i } protected void openEdit(UserRequest ureq, List<MemberView> members) { - List<Long> identityKeys = getMemberKeys(members); - List<Identity> identities = securityManager.loadIdentityByKeys(identityKeys); - editMembersCtrl = new EditMembershipController(ureq, getWindowControl(), identities, repoEntry, businessGroup); - listenTo(editMembersCtrl); - cmc = new CloseableModalController(getWindowControl(), translate("close"), editMembersCtrl.getInitialComponent(), - true, translate("edit.member")); - cmc.activate(); - listenTo(cmc); + if(members.isEmpty()) { + showWarning("error.select.one.user"); + } else { + List<Long> identityKeys = getMemberKeys(members); + List<Identity> identities = securityManager.loadIdentityByKeys(identityKeys); + editMembersCtrl = new EditMembershipController(ureq, getWindowControl(), identities, repoEntry, businessGroup); + listenTo(editMembersCtrl); + cmc = new CloseableModalController(getWindowControl(), translate("close"), editMembersCtrl.getInitialComponent(), + true, translate("edit.member")); + cmc.activate(); + listenTo(cmc); + } } protected void doSearch(String search) { diff --git a/src/main/java/org/olat/group/ui/main/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/group/ui/main/_i18n/LocalStrings_de.properties index 2d5e11100daa86c78ad6ce411d49f0a61c98c652..cf56f2dbbd5a0c46e87caea97dc0c3b4256f2e95 100644 --- a/src/main/java/org/olat/group/ui/main/_i18n/LocalStrings_de.properties +++ b/src/main/java/org/olat/group/ui/main/_i18n/LocalStrings_de.properties @@ -1,7 +1,7 @@ #Mon Mar 02 09:54:04 CET 2009 create.form.title=Neue Gruppe erstellen create.group=Gruppe erstellen -create.group.description=Erzeugen Sie eine neue Gruppe mit der unten stehenden Schaltfläche. Als Betreuer dieser Gruppe können Sie danach die Gruppenwerkzeuge freischalten, Benutzer hinzufügen oder die Gruppe veröffentlichen. +create.group.description=Erzeugen Sie eine neue Gruppe mit der unten stehenden Schaltfl\u00E4che. Als Betreuer dieser Gruppe k\u00F6nnen Sie danach die Gruppenwerkzeuge freischalten, Benutzer hinzuf\u00FCgen oder die Gruppe ver\u00F6ffentlichen. copy.group=Kopieren deup.members=Mitglieder bereinigen dedup.members.typ=Rolle @@ -10,7 +10,7 @@ dedup.members.particpants=Teilnehmer dedup.members.info1=Wollen Sie die Mitglieder wirklich bereinigen? dedup.members.info2=Bei diesem Prozess werden jene Benutzer als Kursmitglieder ausgetragen, die sowohl Kurs- als auch Gruppenmitglied des Kurses sind (Duplikate). dedup.members.info3={0} Benutzer wurden gefunden die sowohl Kursmitglieder als auch Gruppenmitglieder des Kurses sind. -dedup.members.info4=Wählen Sie ob die Bereinigung für Betreuer und/oder die Teilnehmer durchgeführt werden soll. +dedup.members.info4=W\u00E4hlen Sie ob die Bereinigung f\u00FCr Betreuer und/oder die Teilnehmer durchgef\u00FChrt werden soll. dialog.modal.bg.delete.title=Gruppe l\u00F6schen? dialog.modal.bg.delete.text=Wollen Sie die Gruppe "{0}" wirklich l\u00F6schen? dialog.modal.bg.mail.text=Wollen Sie die Mitglieder per Mail benachrichtigen? @@ -22,8 +22,9 @@ remove.send.mail=Benachrichtigung remove.send.mail.label=E-Mail versenden error.atleastone=Es muss mindestens einen Besitzer im Kurs geben. error.msg.send.no.rcps=$org.olat.modules.co\:error.msg.send.no.rcps -error.managed.group=Die gewählte Gruppe "{0}" wird von einem externen System verwaltet. Die Operation kann nicht ausgeführt werden. -error.select.one=Sie müssen mindestens eine Gruppe w\u00E4hlen. +error.managed.group=Die gew\u00E4hlte Gruppe "{0}" wird von einem externen System verwaltet. Die Operation kann nicht ausgef\u00FChrt werden. +error.select.one=Sie m\u00FCssen mindestens eine Gruppe w\u00E4hlen. +error.select.one.user=Sie m\u00FCssen mindestens einen Benutzer w\u00E4hlen. mail.member=E-Mail main.menu.title=Gruppen main.menu.title.alt=Gruppen @@ -32,7 +33,7 @@ index.intro=In der untenstehenden Liste finden Sie alle Gruppen, an denen Sie te index.table.nogroup=Sie sind in keiner Gruppe eingetragen. info.group.deleted=Die Gruppe wurde gel\u00F6scht. request.leaving.subject=Anfrage Gruppe "{0}" mit ID "{1}" zu verlassen -request.leaving.body=Sehr geehrter Gruppenbetreuer<br /><br />Ich m\u00F6chte dieser Gruppe verlasse<br /><br />Mit freundliche Grüsse<br />{3} {4}<br />\Gruppename: {0}<br />Gruppe ID:{1}<br />Gruppekurse: {2} +request.leaving.body=Sehr geehrter Gruppenbetreuer<br /><br />Ich m\u00F6chte dieser Gruppe verlasse<br /><br />Mit freundliche Gr\u00FCsse<br />{3} {4}<br />\Gruppename: {0}<br />Gruppe ID:{1}<br />Gruppekurse: {2} menu.group.admin=Gruppenverwaltung menu.group.admin.alt=Gruppenverwaltung menu.index=Gruppen @@ -42,10 +43,10 @@ my.groups.alt=Arbeiten Sie mit Ihren Gruppen open.groups=Ver\u00F6ffentlichte Gruppen open.groups.alt=Arbeitsgruppen in die \u00F6ffentlich sind und an denen ich teilnehmen kann. msg.atleastone=Es muss mindestens ein Betreuer in der Gruppe eingetragen sein. -msg.alleastone.editable.group=Sie m\u00FCssen mindestens eine Gruppe wählen, die Sie besitzen. +msg.alleastone.editable.group=Sie m\u00FCssen mindestens eine Gruppe w\u00E4hlen, die Sie besitzen. user.notfound=Folgende Benutzer wurden nicht gefunden: {0} -open.header=Öffentliche Gruppen +open.header=\u00F6ffentliche Gruppen open.intro=In dieser Liste finden Sie alle ver\u00F6ffentlichten Gruppen denen Sie beitreten k\u00F6nnen. open.nogroup=Es wurden keine Gruppe gefunden die Ihren Kriterien entsprechen. opengroups.all=Alle Gruppen @@ -64,7 +65,7 @@ search.waiting=Warteliste search.public=Public group search.all=Alle search.none=- -search.open=Veröffentlicht +search.open=Ver\u00F6ffentlicht search.roles=Rolle search.yes=Ja search.no=Nein @@ -103,16 +104,16 @@ table.header.role=Rolle table.header.firstTime=Beitritt table.header.lastTime=Zuletzt besucht table.header.key=ID -table.header.freePlaces=Plätze +table.header.freePlaces=Pl\u00E4tze table.header.tutorsCount=Betreuer table.header.participantsCount=Belegt table.header.waitingListCount=Warteliste table.access=Beitreten table.access.waitingList=Warteliste eintragen -table.delete=Löschen +table.delete=L\u00F6schen table.email=E-Mail versenden table.duplicate=Duplizieren -table.merge=Zusammenführen +table.merge=Zusammenf\u00FChren table.users.management=Benutzer verwalten table.config=Konfigurieren table.leave=Verlassen @@ -121,13 +122,13 @@ tools.add.header=Erstellen tools.delete.header=L\u00F6schen tools.delete.unusedgroup=Arbeitsgruppen action=Aktion -merge.group=Zusammenführen +merge.group=Zusammenf\u00FChren email.group=E-Mail versenden config.group=Konfigurieren users.group=Benutzer verwalten notification.mail.added.body=*** Das ist eine automatisch generierte Nachricht. Bitte antworten Sie nicht auf diese Nachricht *** \n\nSie wurden von {0} {1} ({2}) in eine Arbeitsgruppe eingeladen\: \n\nGruppenname\: $groupname\nBeschreibung\: $groupdescription\n\nSind Sie damit nicht einverstanden, so k\u00F6nnen Sie sich aus der Arbeitsgruppe wieder austragen. -notification.mail.added.self.body=*** Das ist eine automatisch generierte Nachricht. Bitte antworten Sie nicht auf diese Nachricht *** \n\n Sie haben in OLAT eine Arbeitsgruppe eröffnet\: \n\nGruppenname\: $groupname\nBeschreibung\: $groupdescription\n\nSie k\u00F6nnen die Gruppe jederzeit wieder löschen. +notification.mail.added.self.body=*** Das ist eine automatisch generierte Nachricht. Bitte antworten Sie nicht auf diese Nachricht *** \n\n Sie haben in OLAT eine Arbeitsgruppe er\u00F6ffnet\: \n\nGruppenname\: $groupname\nBeschreibung\: $groupdescription\n\nSie k\u00F6nnen die Gruppe jederzeit wieder l\u00F6schen. notification.mail.added.self.subject=Gruppe $groupname notification.mail.added.subject=Gruppe $groupname notification.mail.deleted.body=*** Das ist eine automatisch generierte Nachricht. Bitte antworten Sie nicht auf diese Nachricht *** \n\n Sie wurden von {0} {1} ({2}) aus der Arbeitsgruppe ausgetragen, da sie gel\u00F6scht wurde\: \n\nGruppenname\: $groupname\nBeschreibung\: $groupdescription\n\nBei Fragen kontaktieren Sie bitte {0} {1} ({2}). @@ -138,18 +139,18 @@ notification.mail.removed.self.subject=Gruppe $groupname\: Sie haben sich ausget notification.mail.removed.subject=Gruppe $groupname\: Sie wurden ausgetragen. -pending.reservations=<h4>Bestätigung Teilnahme in Gruppen und Kursen</h4>Sie wurden in die folgenden Gruppen oder Kurse eingeladen. Wählen Sie für alle aufgeführten Gruppe und Kurse die Schaltfläche "$:accept" oder "$:reject" und schliessen Sie mit "$org.olat.core:ok" ab. Sie können die Bestätigung auch zu einem späteren Zeitpunkt durchführen, wählen Sie in dem Fall "$org.olat.core:cancel". +pending.reservations=<h4>Best\u00E4tigung Teilnahme in Gruppen und Kursen</h4>Sie wurden in die folgenden Gruppen oder Kurse eingeladen. W\u00E4hlen Sie f\u00FCr alle aufgef\u00FChrten Gruppe und Kurse die Schaltfl\u00E4che "$:accept" oder "$:reject" und schliessen Sie mit "$org.olat.core:ok" ab. Sie k\u00F6nnen die Best\u00E4tigung auch zu einem sp\u00E4teren Zeitpunkt durchf\u00FChren, w\u00E4hlen Sie in dem Fall "$org.olat.core:cancel". course.membership.creation=Kurs Beitritt -course.lastTime=Zuletzt geöffnet +course.lastTime=Zuletzt ge\u00F6ffnet course.numOfVisits=Anzahl Kursaufrufe home=Visitenkarte assessment=Bewertungswerkzeug -add.member=Mitglied hinzufügen +add.member=Mitglied hinzuf\u00FCgen edit.member=Mitglied bearbeiten edit.members=Bearbeiten edit.member.groups=Gruppenmitgliedschaften -import.member=Mitglieder hinzufügen +import.member=Mitglieder hinzuf\u00FCgen @@ -159,7 +160,7 @@ role.group.waiting=Warteliste role.repo.owner=Besitzer role.repo.tutor=Betreuer role.repo.participant=Teilnehmer -role.pending=Bestätigung ausstehend +role.pending=Best\u00E4tigung ausstehend edit.member.title=Mitgliederrechte Kurs "{0}" @@ -182,10 +183,10 @@ table.header.waitingList=Warteliste reservation.coach=als Betreuer group.used.in.course=Diese Gruppe wird verwendet in folgenden Kursen: -accept=Bestätigen +accept=Best\u00E4tigen reject=Ablehnen -confirm.accept=Mitgliedschaft wird bestätigt +confirm.accept=Mitgliedschaft wird best\u00E4tigt confirm.reject=Mitgliedschaft wird abgelehnt nomembers=Es wurden keine Mitglieder gefunden die Ihren Kriterien entsprechen. diff --git a/src/main/java/org/olat/group/ui/main/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/group/ui/main/_i18n/LocalStrings_en.properties index 1d1702cb6aca905313f967326a2240a73666a4cf..8d5c09397ae959f889e022a319915a1f5c9d879f 100644 --- a/src/main/java/org/olat/group/ui/main/_i18n/LocalStrings_en.properties +++ b/src/main/java/org/olat/group/ui/main/_i18n/LocalStrings_en.properties @@ -45,6 +45,7 @@ error.atleastone=At least one owner is required in a course. error.managed.group=The selected group "{0}" is managed by an external system. The operation cannot be executed. error.msg.send.no.rcps=$org.olat.modules.co\:error.msg.send.no.rcps error.select.one=You need to select at least one group. +error.select.one.user=You need to select at least one user. group.access.success=Access to group is successful group.used.in.course=This group is used in the following courses\: hide=Hide information diff --git a/src/main/java/org/olat/group/ui/main/_i18n/LocalStrings_fr.properties b/src/main/java/org/olat/group/ui/main/_i18n/LocalStrings_fr.properties index 691a63a3626ac1008e9bbe8944cb9258f278d33d..e154ff9fb48dc419b64b051485aeda452acd75c8 100644 --- a/src/main/java/org/olat/group/ui/main/_i18n/LocalStrings_fr.properties +++ b/src/main/java/org/olat/group/ui/main/_i18n/LocalStrings_fr.properties @@ -45,6 +45,7 @@ error.atleastone=Le cours doit avoir au moins un propri\u00E9taire. error.managed.group=Les groupes s\u00E9lectionn\u00E9s "{0}" sont administr\u00E9s par un syst\u00E8me externe. L'op\u00E9ration ne peut pas \u00EAtre effectu\u00E9e. error.msg.send.no.rcps=$org.olat.modules.co\:error.msg.send.no.rcps error.select.one=Vous devez s\u00E9lectionner au moins un groupe. +error.select.one.user=Vous devez s\u00E9lectionner au moins un utilisateur. group.access.success=L'acc\u00E8s au groupe a \u00E9t\u00E9 couronn\u00E9 de succ\u00E8s. group.used.in.course=Ce groupe est utilis\u00E9 par les cours suivants\: hide=Masquer les informations 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 2bd44dfbc09ff12f1cd1ef2d970655ef47a19198..27df703bce9e0e5439c1c591d62a3b95a6ffc8ea 100644 --- a/src/main/java/org/olat/modules/coach/manager/CoachingDAO.java +++ b/src/main/java/org/olat/modules/coach/manager/CoachingDAO.java @@ -527,19 +527,38 @@ public class CoachingDAO { private boolean getCoursesStatisticsUserInfosForOwner(Identity coach, Map<Long,CourseStatEntry> map) { NativeQueryBuilder sb = new NativeQueryBuilder(1024, dbInstance); - sb.append("select") - .append(" sg_re.repositoryentry_id as re_id,") - .append(" count(distinct sg_participant.fk_identity_id) as student_id,") - .append(" count(distinct pg_initial_launch.id) as pg_id") - .append(" from o_repositoryentry sg_re ") - .append(" inner join o_re_to_group owngroup on (owngroup.fk_entry_id = sg_re.repositoryentry_id and owngroup.r_defgroup=").appendTrue().append(")") - .append(" inner join o_bs_group_member sg_coach on (sg_coach.fk_group_id=owngroup.fk_group_id and sg_coach.g_role = 'owner')") - .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") - .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.accesscode >= ").append(RepositoryEntry.ACC_OWNERS) - .append(" group by sg_re.repositoryentry_id"); + if(dbInstance.isMySQL()) { + sb.append("select") + .append(" sg_re.repositoryentry_id as re_id,") + .append(" count(distinct sg_participant.fk_identity_id) as student_id,") + .append(" count(distinct pg_initial_launch.id) as pg_id") + .append(" from o_repositoryentry sg_re ") + .append(" inner join o_re_to_group owngroup on (owngroup.fk_entry_id = sg_re.repositoryentry_id and owngroup.r_defgroup=").appendTrue().append(")") + .append(" inner join o_bs_group_member sg_coach on (sg_coach.fk_group_id=owngroup.fk_group_id and sg_coach.g_role = 'owner')") + .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") + .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.accesscode >= ").append(RepositoryEntry.ACC_OWNERS) + .append(" group by sg_re.repositoryentry_id"); + } else { + sb.append("select") + .append(" sg_re.repositoryentry_id as re_id,") + .append(" count(distinct sg_participant.fk_identity_id) as student_id,") + .append(" count(distinct pg_initial_launch.id) as pg_id") + .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") + .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.accesscode >= ").append(RepositoryEntry.ACC_OWNERS).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)") + .append(" where owngroup.fk_entry_id = sg_re.repositoryentry_id and owngroup.r_defgroup=").appendTrue().append(" and sg_owner.g_role='owner' and sg_owner.fk_identity_id=:coachKey") + .append(" )") + .append(" group by sg_re.repositoryentry_id"); + } List<?> rawList = dbInstance.getCurrentEntityManager() .createNativeQuery(sb.toString()) @@ -669,20 +688,39 @@ public class CoachingDAO { private boolean getStudentsStastisticInfosForOwner(IdentityRef coach, Map<Long, StudentStatEntry> map) { NativeQueryBuilder sb = new NativeQueryBuilder(1024, dbInstance); - sb.append("select") - .append(" sg_participant.fk_identity_id as part_id,") - .append(" ").appendToArray("sg_re.repositoryentry_id").append(" as re_ids,") - .append(" ").appendToArray("pg_initial_launch.id").append(" as pg_ids") - .append(" from o_repositoryentry sg_re") - .append(" inner join o_olatresource sg_res on (sg_res.resource_id = sg_re.fk_olatresource and sg_res.resname = 'CourseModule')") - .append(" inner join o_re_to_group owngroup on (owngroup.fk_entry_id = sg_re.repositoryentry_id and owngroup.r_defgroup=").appendTrue().append(")") - .append(" inner join o_bs_group_member sg_owner on (sg_owner.fk_group_id=owngroup.fk_group_id and sg_owner.g_role = 'owner')") - .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") - .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.accesscode >= ").append(RepositoryEntry.ACC_OWNERS) - .append(" group by sg_participant.fk_identity_id"); + if(dbInstance.isMySQL()) { + sb.append("select") + .append(" sg_participant.fk_identity_id as part_id,") + .append(" ").appendToArray("sg_re.repositoryentry_id").append(" as re_ids,") + .append(" ").appendToArray("pg_initial_launch.id").append(" as pg_ids") + .append(" from o_repositoryentry sg_re") + .append(" inner join o_olatresource sg_res on (sg_res.resource_id = sg_re.fk_olatresource and sg_res.resname = 'CourseModule')") + .append(" inner join o_re_to_group owngroup on (owngroup.fk_entry_id = sg_re.repositoryentry_id and owngroup.r_defgroup=").appendTrue().append(")") + .append(" inner join o_bs_group_member sg_owner on (sg_owner.fk_group_id=owngroup.fk_group_id and sg_owner.g_role = 'owner')") + .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") + .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.accesscode >= ").append(RepositoryEntry.ACC_OWNERS) + .append(" group by sg_participant.fk_identity_id"); + } else { + sb.append("select") + .append(" sg_participant.fk_identity_id as part_id,") + .append(" ").appendToArray("sg_re.repositoryentry_id").append(" as re_ids,") + .append(" ").appendToArray("pg_initial_launch.id").append(" as pg_ids") + .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") + .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.accesscode >= ").append(RepositoryEntry.ACC_OWNERS).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)") + .append(" where owngroup.fk_entry_id = sg_re.repositoryentry_id and owngroup.r_defgroup=").appendTrue().append(" and sg_owner.g_role='owner' and sg_owner.fk_identity_id=:coachKey") + .append(" )") + .append(" group by sg_participant.fk_identity_id"); + } List<?> rawList = dbInstance.getCurrentEntityManager() .createNativeQuery(sb.toString()) diff --git a/src/test/java/org/olat/selenium/page/course/EnrollmentConfigurationPage.java b/src/test/java/org/olat/selenium/page/course/EnrollmentConfigurationPage.java index a3a95c92ebbcf9840187adcc0893549a147fbfd3..d9996f952aaaa5888d3e2fc5fe02d0f1094cb9e2 100644 --- a/src/test/java/org/olat/selenium/page/course/EnrollmentConfigurationPage.java +++ b/src/test/java/org/olat/selenium/page/course/EnrollmentConfigurationPage.java @@ -84,6 +84,7 @@ public class EnrollmentConfigurationPage { By createGroupBy = By.cssSelector("div.o_button_group_right a"); browser.findElement(createGroupBy).click(); OOGraphene.waitBusy(browser); + OOGraphene.waitModalDialog(browser); //fill the form By nameBy = By.cssSelector(".o_sel_group_edit_title input[type='text']"); diff --git a/src/test/java/org/olat/selenium/page/course/InfoMessageCEPage.java b/src/test/java/org/olat/selenium/page/course/InfoMessageCEPage.java index 572e719a6738a12e8cf9c88fd6b05b0a7012f1f4..96d27ffe252c06d9878710a5d4137026e3d13f1a 100644 --- a/src/test/java/org/olat/selenium/page/course/InfoMessageCEPage.java +++ b/src/test/java/org/olat/selenium/page/course/InfoMessageCEPage.java @@ -57,6 +57,7 @@ public class InfoMessageCEPage { By createBy = By.className("o_sel_course_info_create_msg"); browser.findElement(createBy).click(); OOGraphene.waitBusy(browser); + OOGraphene.waitModalDialog(browser); return this; } diff --git a/src/test/java/org/olat/selenium/page/forum/ForumPage.java b/src/test/java/org/olat/selenium/page/forum/ForumPage.java index 96654244e9accac812e4878e4ed312908cfad295..63cabef405dece6864da9ffa391de9e12de636fb 100644 --- a/src/test/java/org/olat/selenium/page/forum/ForumPage.java +++ b/src/test/java/org/olat/selenium/page/forum/ForumPage.java @@ -102,6 +102,7 @@ public class ForumPage { By newThreadBy = By.className("o_sel_forum_thread_new"); browser.findElement(newThreadBy).click(); OOGraphene.waitBusy(browser); + OOGraphene.waitModalDialog(browser); //fill the form By titleBy = By.cssSelector("div.modal-content form div.o_sel_forum_message_title input[type='text']"); diff --git a/src/test/java/org/olat/selenium/page/graphene/OOGraphene.java b/src/test/java/org/olat/selenium/page/graphene/OOGraphene.java index 8b6bf0549a32e14f578926f4b09570733a439ce6..0b5b18b4154bd1c7060dca3eb4cc85575e012342 100644 --- a/src/test/java/org/olat/selenium/page/graphene/OOGraphene.java +++ b/src/test/java/org/olat/selenium/page/graphene/OOGraphene.java @@ -49,6 +49,11 @@ public class OOGraphene { private static final By closeBlueBoxButtonBy = By.cssSelector("div.o_alert_info div.o_sel_info_message i.o_icon.o_icon_close"); private static final By closeModalDialogButtonBy = By.cssSelector("div.modal-dialog div.modal-header button.close"); + public static void waitModalDialog(WebDriver browser) { + By modalBy = By.cssSelector("div.modal-dialog div.modal-body"); + waitElement(modalBy, 5, browser); + } + public static void waitBusy(WebDriver browser) { Graphene.waitModel(browser).pollingEvery(poolingDuration, TimeUnit.MILLISECONDS).until(new BusyPredicate()); } diff --git a/src/test/java/org/olat/selenium/page/portfolio/PortfolioPage.java b/src/test/java/org/olat/selenium/page/portfolio/PortfolioPage.java index ae53a7a8dd286ca4975d07cc07a3e217df011bb8..a72884ce978ec8670483fb6da6d3ada837319719 100644 --- a/src/test/java/org/olat/selenium/page/portfolio/PortfolioPage.java +++ b/src/test/java/org/olat/selenium/page/portfolio/PortfolioPage.java @@ -274,14 +274,9 @@ public class PortfolioPage { * @return */ public PortfolioPage selectMapInEditor(String mapTitle) { - By mapNodeBy = By.cssSelector("div.o_ep_toc_editor span.o_tree_level_label_open.o_tree_l1>a"); - WebElement selectedNode = null; - List<WebElement> level1Nodes = browser.findElements(mapNodeBy); - for(WebElement level1Node:level1Nodes) { - if(level1Node.getText().contains(mapTitle)) { - selectedNode = level1Node; - } - } + By mapNodeBy = By.xpath("//div[contains(@class,'o_ep_toc_editor')]//span[contains(@class,'o_tree_level_label_open')][contains(@class,'o_tree_l1')]//a//span[text()[contains(.,'" + mapTitle + "')]]"); + OOGraphene.waitElement(mapNodeBy, 5, browser); + WebElement selectedNode = browser.findElement(mapNodeBy); Assert.assertNotNull(selectedNode); selectedNode.click(); OOGraphene.waitBusy(browser); @@ -290,9 +285,8 @@ public class PortfolioPage { public PortfolioPage selectMapInEditor() { By mapNodeBy = By.cssSelector("div.o_ep_toc_editor span.o_tree_link.o_tree_l1.o_tree_l1>a"); - List<WebElement> level1Nodes = browser.findElements(mapNodeBy); - Assert.assertFalse(level1Nodes.isEmpty()); - level1Nodes.get(0).click(); + OOGraphene.waitElement(mapNodeBy, 5, browser); + browser.findElement(mapNodeBy).click(); OOGraphene.waitBusy(browser); return this; } @@ -320,9 +314,8 @@ public class PortfolioPage { */ public PortfolioPage selectFirstPageInEditor() { By pageNodeBy = By.cssSelector("div.o_ep_toc_editor span.o_tree_level_label_leaf.o_tree_l2>a"); - List<WebElement> level2Nodes = browser.findElements(pageNodeBy); - Assert.assertFalse(level2Nodes.isEmpty()); - level2Nodes.get(0).click(); + OOGraphene.waitElement(pageNodeBy, 5, browser); + browser.findElement(pageNodeBy).click(); OOGraphene.waitBusy(browser); return this; }