From 82dd09f3d789ddf3c36ab2161ab5d4a62ff54780 Mon Sep 17 00:00:00 2001 From: srosse <stephane.rosse@frentix.com> Date: Fri, 5 Jul 2019 07:41:53 +0200 Subject: [PATCH] OO-3887: reimplement options for Adobe Connect --- .../collaboration/CollaborationTools.java | 2 +- .../olat/commons/calendar/CalendarUtils.java | 16 +- .../scheduler/_spring/schedulerContext.xml | 1 + .../course/nodes/AdobeConnectCourseNode.java | 27 +- .../adobeconnect/AdobeConnectConfigForm.java | 85 ++++++ .../AdobeConnectEditController.java | 26 +- .../_i18n/LocalStrings_de.properties | 4 + .../_i18n/LocalStrings_en.properties | 8 +- .../_i18n/LocalStrings_fr.properties | 4 + .../_i18n/LocalStrings_pt_BR.properties | 6 + .../olat/modules/_spring/modulesContext.xml | 21 +- .../adobeconnect/AdobeConnectManager.java | 37 ++- .../adobeconnect/AdobeConnectMeeting.java | 24 ++ .../adobeconnect/AdobeConnectModule.java | 73 ++++- .../_spring/adobeConnectContext.xml | 40 +++ .../manager/AbstractAdobeConnectProvider.java | 21 +- .../manager/AdobeConnect9Provider.java | 5 +- .../manager/AdobeConnectCleanupJob.java | 77 +++++ .../manager/AdobeConnectManagerImpl.java | 267 ++++++++++++++++-- .../manager/AdobeConnectMeetingDAO.java | 93 +++++- .../adobeconnect/manager/AdobeConnectSPI.java | 2 + .../manager/AdobeConnectUtils.java | 12 +- .../adobeconnect/manager/DFNprovider.java | 4 +- .../manager/NoAdapterProvider.java | 6 + .../model/AdobeConnectErrors.java | 9 + .../model/AdobeConnectMeetingImpl.java | 93 +++++- .../adobeconnect/model/AdobeConnectSco.java | 9 + .../AdobeConnectAdminMeetingsController.java | 6 + .../AdobeConnectConfigurationController.java | 56 ++++ .../AdobeConnectEditMeetingsController.java | 6 + .../adobeconnect/ui/AdobeConnectEvent.java | 53 ++++ .../ui/AdobeConnectMeetingController.java | 174 ++++++++++-- ...obeConnectMeetingDefaultConfiguration.java | 14 +- .../ui/AdobeConnectMeetingTableModel.java | 2 + .../ui/AdobeConnectMeetingsController.java | 13 +- .../ui/AdobeConnectRunController.java | 42 ++- .../AdobeConnectShareDocumentsController.java | 19 ++ .../ui/EditAdobeConnectMeetingController.java | 71 ++++- .../adobeconnect/ui/_content/meeting.html | 62 ++-- .../ui/_i18n/LocalStrings_de.properties | 19 ++ .../ui/_i18n/LocalStrings_en.properties | 23 +- .../ui/_i18n/LocalStrings_fr.properties | 7 +- .../_spring/databaseUpgradeContext.xml | 4 + .../database/mysql/alter_14_0_0_to_14_0_1.sql | 8 + .../database/mysql/setupDatabase.sql | 7 + .../oracle/alter_14_0_0_to_14_0_1.sql | 8 + .../database/oracle/setupDatabase.sql | 7 + .../postgresql/alter_14_0_0_to_14_0_1.sql | 8 + .../database/postgresql/setupDatabase.sql | 7 + .../manager/AdobeConnectMeetingDAOTest.java | 51 +++- 50 files changed, 1473 insertions(+), 166 deletions(-) create mode 100644 src/main/java/org/olat/course/nodes/adobeconnect/AdobeConnectConfigForm.java create mode 100644 src/main/java/org/olat/course/nodes/adobeconnect/_i18n/LocalStrings_pt_BR.properties create mode 100644 src/main/java/org/olat/modules/adobeconnect/_spring/adobeConnectContext.xml create mode 100644 src/main/java/org/olat/modules/adobeconnect/manager/AdobeConnectCleanupJob.java create mode 100644 src/main/java/org/olat/modules/adobeconnect/ui/AdobeConnectEvent.java create mode 100644 src/main/resources/database/mysql/alter_14_0_0_to_14_0_1.sql create mode 100644 src/main/resources/database/oracle/alter_14_0_0_to_14_0_1.sql create mode 100644 src/main/resources/database/postgresql/alter_14_0_0_to_14_0_1.sql diff --git a/src/main/java/org/olat/collaboration/CollaborationTools.java b/src/main/java/org/olat/collaboration/CollaborationTools.java index eb8409067eb..0589bfbf515 100644 --- a/src/main/java/org/olat/collaboration/CollaborationTools.java +++ b/src/main/java/org/olat/collaboration/CollaborationTools.java @@ -619,7 +619,7 @@ public class CollaborationTools implements Serializable { } public Controller createAdobeConnectController(final UserRequest ureq, WindowControl wControl, final BusinessGroup group, boolean admin) { - AdobeConnectMeetingDefaultConfiguration configuration = new AdobeConnectMeetingDefaultConfiguration(true); + AdobeConnectMeetingDefaultConfiguration configuration = new AdobeConnectMeetingDefaultConfiguration(true, true, true); return new AdobeConnectRunController(ureq, wControl, null, null, group, configuration, admin, admin, false); } diff --git a/src/main/java/org/olat/commons/calendar/CalendarUtils.java b/src/main/java/org/olat/commons/calendar/CalendarUtils.java index 6b183a75ba9..548ad882e10 100644 --- a/src/main/java/org/olat/commons/calendar/CalendarUtils.java +++ b/src/main/java/org/olat/commons/calendar/CalendarUtils.java @@ -87,7 +87,13 @@ public class CalendarUtils { return cal; } - + public static Calendar getStartOfDay(Calendar cal) { + cal.set(Calendar.HOUR_OF_DAY, 0); + cal.set(Calendar.MINUTE, 0); + cal.set(Calendar.SECOND, 0); + cal.set(Calendar.MILLISECOND, 0); + return cal; + } public static Date endOfDay(Date date) { Calendar cal = Calendar.getInstance(); @@ -124,7 +130,7 @@ public class CalendarUtils { String frequency = recur.getFrequency(); WeekDayList wdl = recur.getDayList(); Integer interval = recur.getInterval(); - if((wdl != null && wdl.size() > 0)) { + if((wdl != null && !wdl.isEmpty())) { // we only support one rule with daylist return KalendarEvent.WORKDAILY; } else if(interval != null && interval == 2) { @@ -142,10 +148,6 @@ public class CalendarUtils { return null; } - - - - /** * Create list with excluded dates based on the exclusion rule. * @param recurrenceExc @@ -176,7 +178,7 @@ public class CalendarUtils { * @return string with exclude rule */ public static String getRecurrenceExcludeRule(List<Date> dates) { - if(dates != null && dates.size() > 0) { + if(dates != null && !dates.isEmpty()) { DateList dl = new DateList(); for( Date date : dates ) { net.fortuna.ical4j.model.Date dd = CalendarUtils.createDate(date); diff --git a/src/main/java/org/olat/core/commons/services/scheduler/_spring/schedulerContext.xml b/src/main/java/org/olat/core/commons/services/scheduler/_spring/schedulerContext.xml index f27e65ef671..47a3ddc626e 100644 --- a/src/main/java/org/olat/core/commons/services/scheduler/_spring/schedulerContext.xml +++ b/src/main/java/org/olat/core/commons/services/scheduler/_spring/schedulerContext.xml @@ -55,6 +55,7 @@ How to add a new job: <ref bean="qualityTrigger"/> <ref bean="deleteUserDataExportTrigger"/> <ref bean="cspCleanupJob"/> + <ref bean="adobeCleanupTrigger"/> </list> </property> </bean> diff --git a/src/main/java/org/olat/course/nodes/AdobeConnectCourseNode.java b/src/main/java/org/olat/course/nodes/AdobeConnectCourseNode.java index 5d47185ac00..f2ca37bc81f 100644 --- a/src/main/java/org/olat/course/nodes/AdobeConnectCourseNode.java +++ b/src/main/java/org/olat/course/nodes/AdobeConnectCourseNode.java @@ -41,6 +41,7 @@ import org.olat.course.groupsandrights.CourseGroupManager; import org.olat.course.nodes.adobeconnect.AdobeConnectCourseNodeConfiguration; import org.olat.course.nodes.adobeconnect.AdobeConnectEditController; import org.olat.course.nodes.adobeconnect.compatibility.AdobeConnectCompatibilityConfiguration; +import org.olat.course.run.environment.CourseEnvironment; import org.olat.course.run.navigation.NodeRunConstructionResult; import org.olat.course.run.userview.NodeEvaluation; import org.olat.course.run.userview.UserCourseEnvironment; @@ -71,15 +72,26 @@ public class AdobeConnectCourseNode extends AbstractAccessableCourseNode { super(TYPE); } - @Override - public void updateModuleConfigDefaults(boolean isNewNode) { + private void updateModuleConfigDefaults(CourseEnvironment courseEnv, boolean isNewNode) { ModuleConfiguration config = getModuleConfiguration(); if(config.getConfigurationVersion() < 2) { Object oldConfiguration = config.get(CONF_VC_CONFIGURATION); if(oldConfiguration instanceof AdobeConnectCompatibilityConfiguration) { AdobeConnectCompatibilityConfiguration oldConfig = (AdobeConnectCompatibilityConfiguration)oldConfiguration; + config.setBooleanEntry(AdobeConnectEditController.ACCESS_BY_DATES, oldConfig.isUseMeetingDates()); config.setBooleanEntry(AdobeConnectEditController.GUEST_ACCESS_ALLOWED, oldConfig.isGuestAccessAllowed()); config.setBooleanEntry(AdobeConnectEditController.MODERATOR_START_MEETING, !oldConfig.isGuestStartMeetingAllowed()); + + if(courseEnv != null && oldConfig.getMeetingDatas() != null && !oldConfig.getMeetingDatas().isEmpty()) { + AdobeConnectManager adobeConnectManager = CoreSpringFactory.getImpl(AdobeConnectManager.class); + RepositoryEntry courseEntry = courseEnv.getCourseGroupManager().getCourseEntry(); + boolean hasMeetings = adobeConnectManager.hasMeetings(courseEntry, getIdent(), null); + if(!hasMeetings) { + synchronized(this) {// enough + adobeConnectManager.convert(oldConfig.getMeetingDatas(), courseEntry, getIdent()); + } + } + } } } config.setConfigurationVersion(2); @@ -94,7 +106,7 @@ public class AdobeConnectCourseNode extends AbstractAccessableCourseNode { @Override public TabbableController createEditController(UserRequest ureq, WindowControl wControl, BreadcrumbPanel stackPanel, ICourse course, UserCourseEnvironment userCourseEnv) { - updateModuleConfigDefaults(false); + updateModuleConfigDefaults(userCourseEnv.getCourseEnvironment(), false); CourseNode chosenNode = course.getEditorTreeModel().getCourseNode(userCourseEnv.getCourseEditorEnv().getCurrentCourseNodeId()); // create edit controller @@ -109,7 +121,7 @@ public class AdobeConnectCourseNode extends AbstractAccessableCourseNode { @Override public NodeRunConstructionResult createNodeRunConstructionResult(UserRequest ureq, WindowControl wControl, UserCourseEnvironment userCourseEnv, NodeEvaluation ne, String nodecmd) { - updateModuleConfigDefaults(false); + updateModuleConfigDefaults(userCourseEnv.getCourseEnvironment(), false); String providerId = getModuleConfiguration().getStringValue("vc_provider_id"); @@ -125,7 +137,12 @@ public class AdobeConnectCourseNode extends AbstractAccessableCourseNode { boolean moderator = admin || userCourseEnv.isCoach(); // create run controller RepositoryEntry entry = userCourseEnv.getCourseEnvironment().getCourseGroupManager().getCourseEntry(); - AdobeConnectMeetingDefaultConfiguration configuration = new AdobeConnectMeetingDefaultConfiguration(true); + + ModuleConfiguration config = getModuleConfiguration(); + boolean onlyDates = config.getBooleanSafe(AdobeConnectEditController.ACCESS_BY_DATES, false); + boolean guestAccess = config.getBooleanSafe(AdobeConnectEditController.GUEST_ACCESS_ALLOWED, false); + boolean moderatorStart = config.getBooleanSafe(AdobeConnectEditController.MODERATOR_START_MEETING, false); + AdobeConnectMeetingDefaultConfiguration configuration = new AdobeConnectMeetingDefaultConfiguration(onlyDates, guestAccess, moderatorStart); controller = new AdobeConnectRunController(ureq, wControl, entry, getIdent(), null, configuration, admin, moderator, userCourseEnv.isCourseReadOnly()); } diff --git a/src/main/java/org/olat/course/nodes/adobeconnect/AdobeConnectConfigForm.java b/src/main/java/org/olat/course/nodes/adobeconnect/AdobeConnectConfigForm.java new file mode 100644 index 00000000000..106bd5d1eb6 --- /dev/null +++ b/src/main/java/org/olat/course/nodes/adobeconnect/AdobeConnectConfigForm.java @@ -0,0 +1,85 @@ +/** + * <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.course.nodes.adobeconnect; + +import java.util.Collection; + +import org.olat.core.gui.UserRequest; +import org.olat.core.gui.components.form.flexible.FormItemContainer; +import org.olat.core.gui.components.form.flexible.elements.MultipleSelectionElement; +import org.olat.core.gui.components.form.flexible.impl.FormBasicController; +import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer; +import org.olat.core.gui.control.Controller; +import org.olat.core.gui.control.WindowControl; +import org.olat.course.editor.NodeEditController; +import org.olat.modules.ModuleConfiguration; + +/** + * Initial Date: 3 juil. 2019<br> + * + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class AdobeConnectConfigForm extends FormBasicController { + + private static final String[] accessKeys = new String[] { "dates", "open", "start" }; + + private MultipleSelectionElement accessEl; + + private final ModuleConfiguration config; + + public AdobeConnectConfigForm(UserRequest ureq, WindowControl wControl, ModuleConfiguration config) { + super(ureq, wControl); + this.config = config; + + initForm(ureq); + } + + @Override + protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { + String[] accessValues = new String[] { translate("vc.access.dates"), translate("vc.access.open"), translate("vc.access.start") }; + boolean onlyDates = config.getBooleanSafe(AdobeConnectEditController.ACCESS_BY_DATES, false); + boolean guestAccess = config.getBooleanSafe(AdobeConnectEditController.GUEST_ACCESS_ALLOWED, false); + boolean moderatorStart = config.getBooleanSafe(AdobeConnectEditController.MODERATOR_START_MEETING, false); + accessEl = uifactory.addCheckboxesVertical("vc.access.label", "vc.access.label", formLayout, accessKeys, accessValues, 1); + accessEl.select(accessKeys[0], onlyDates); + accessEl.select(accessKeys[1], guestAccess); + accessEl.select(accessKeys[2], moderatorStart); + + FormLayoutContainer buttonsCont = FormLayoutContainer.createButtonLayout("buttons", getTranslator()); + formLayout.add(buttonsCont); + uifactory.addFormCancelButton("cancel", buttonsCont, ureq, getWindowControl()); + uifactory.addFormSubmitButton("save", buttonsCont); + } + + @Override + protected void doDispose() { + // + } + + @Override + protected void formOK(UserRequest ureq) { + Collection<String> selectedKeys = accessEl.getSelectedKeys(); + config.setBooleanEntry(AdobeConnectEditController.ACCESS_BY_DATES, selectedKeys.contains(accessKeys[0])); + config.setBooleanEntry(AdobeConnectEditController.GUEST_ACCESS_ALLOWED, selectedKeys.contains(accessKeys[1])); + config.setBooleanEntry(AdobeConnectEditController.MODERATOR_START_MEETING, selectedKeys.contains(accessKeys[2])); + fireEvent(ureq, NodeEditController.NODECONFIG_CHANGED_EVENT); + } +} diff --git a/src/main/java/org/olat/course/nodes/adobeconnect/AdobeConnectEditController.java b/src/main/java/org/olat/course/nodes/adobeconnect/AdobeConnectEditController.java index 052c52d51e9..0a41a654fcb 100644 --- a/src/main/java/org/olat/course/nodes/adobeconnect/AdobeConnectEditController.java +++ b/src/main/java/org/olat/course/nodes/adobeconnect/AdobeConnectEditController.java @@ -22,6 +22,7 @@ package org.olat.course.nodes.adobeconnect; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.Component; import org.olat.core.gui.components.tabbedpane.TabbedPane; +import org.olat.core.gui.components.velocity.VelocityContainer; import org.olat.core.gui.control.Controller; import org.olat.core.gui.control.ControllerEventListener; import org.olat.core.gui.control.Event; @@ -34,6 +35,7 @@ import org.olat.course.condition.ConditionEditController; import org.olat.course.editor.NodeEditController; import org.olat.course.nodes.AdobeConnectCourseNode; import org.olat.course.run.userview.UserCourseEnvironment; +import org.olat.modules.ModuleConfiguration; /** * @@ -42,7 +44,8 @@ import org.olat.course.run.userview.UserCourseEnvironment; * */ public class AdobeConnectEditController extends ActivateableTabbableDefaultController implements ControllerEventListener { - + + public static final String ACCESS_BY_DATES = "accessByDates"; public static final String GUEST_ACCESS_ALLOWED = "guestAccessAllowed"; public static final String MODERATOR_START_MEETING = "moderatorStartMeeting"; @@ -51,17 +54,21 @@ public class AdobeConnectEditController extends ActivateableTabbableDefaultContr private static final String[] paneKeys = { PANE_TAB_VCCONFIG, PANE_TAB_ACCESSIBILITY }; private TabbedPane tabPane; - + private final VelocityContainer myContent; + + private AdobeConnectConfigForm configCtrl; private ConditionEditController accessibilityCondContr; + private final ModuleConfiguration config; private final AdobeConnectCourseNode courseNode; public AdobeConnectEditController(UserRequest ureq, WindowControl wControl, AdobeConnectCourseNode courseNode, ICourse course, UserCourseEnvironment userCourseEnv) { super(ureq, wControl); this.courseNode = courseNode; + config = courseNode.getModuleConfiguration(); - String providerId = courseNode.getModuleConfiguration().getStringValue("vc_provider_id"); + String providerId = config.getStringValue("vc_provider_id"); if("wimba".equals(providerId)) { showWarning("wimba.not.supported.message"); } @@ -70,6 +77,12 @@ public class AdobeConnectEditController extends ActivateableTabbableDefaultContr accessibilityCondContr = new ConditionEditController(ureq, wControl, userCourseEnv, accessCondition, AssessmentHelper.getAssessableNodes(course.getEditorTreeModel(), courseNode)); listenTo(accessibilityCondContr); + + myContent = createVelocityContainer("edit"); + + configCtrl = new AdobeConnectConfigForm(ureq, getWindowControl(), config); + listenTo(configCtrl); + myContent.put("configuration", configCtrl.getInitialComponent()); } @Override @@ -100,6 +113,12 @@ public class AdobeConnectEditController extends ActivateableTabbableDefaultContr courseNode.setPreConditionAccess(cond); fireEvent(ureq, NodeEditController.NODECONFIG_CHANGED_EVENT); } + } else if (source == configCtrl) { + if (event == Event.CANCELLED_EVENT) { + // do nothing + } else if (event == Event.DONE_EVENT || event == NodeEditController.NODECONFIG_CHANGED_EVENT) { + fireEvent(ureq, NodeEditController.NODECONFIG_CHANGED_EVENT); + } } } @@ -108,5 +127,6 @@ public class AdobeConnectEditController extends ActivateableTabbableDefaultContr tabPane = tabbedPane; tabbedPane.addTab(translate(PANE_TAB_ACCESSIBILITY), accessibilityCondContr.getWrappedDefaultAccessConditionVC(translate("condition.accessibility.title"))); + tabbedPane.addTab(translate(PANE_TAB_VCCONFIG), myContent); } } diff --git a/src/main/java/org/olat/course/nodes/adobeconnect/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/course/nodes/adobeconnect/_i18n/LocalStrings_de.properties index 41298615e0f..3bfb6734b92 100644 --- a/src/main/java/org/olat/course/nodes/adobeconnect/_i18n/LocalStrings_de.properties +++ b/src/main/java/org/olat/course/nodes/adobeconnect/_i18n/LocalStrings_de.properties @@ -4,5 +4,9 @@ pane.tab.vcconfig=Konfiguration condition.accessibility.title=Zugang guest.allowed=Zutritt moderator.start.meeting=Raum \u00F6ffnen +vc.access.label=Zugang +vc.access.start=Nur Moderatoren d\u00fcrfen diesen Raum er\u00f6ffnen +vc.access.open=Moderator muss im Raum online sein, um Zutritt f\u00fcr Teilnehmer zu best\u00e4tigen +vc.options.label=Zutrittsberechtigung wimba.not.supported.title=Wimba Classroom wimba.not.supported.message=Wimba Classroom ist nicht unterscht\u00FCtzt. diff --git a/src/main/java/org/olat/course/nodes/adobeconnect/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/course/nodes/adobeconnect/_i18n/LocalStrings_en.properties index 35246ff0e05..95252b2306f 100644 --- a/src/main/java/org/olat/course/nodes/adobeconnect/_i18n/LocalStrings_en.properties +++ b/src/main/java/org/olat/course/nodes/adobeconnect/_i18n/LocalStrings_en.properties @@ -3,7 +3,13 @@ condition.accessibility.title=Access guest.allowed=Access moderator.start.meeting=Room opening pane.tab.accessibility=Access -pane.tab.vcconfig=Room +pane.tab.vcconfig=Configuration title_vc=Adobe Connect +vc.options.label=Access authorisation +vc.access.label=Access authorisation +vc.access.dates=Virtual classroom shall only be available at defined dates +vc.access.label=Access authorisation +vc.access.open=Moderator must be in classroom to grant access to users +vc.access.start=Only moderators are allowed to open this virtual classroom wimba.not.supported.message=Wimba Classroom is not supported. wimba.not.supported.title=Wimba Classroom diff --git a/src/main/java/org/olat/course/nodes/adobeconnect/_i18n/LocalStrings_fr.properties b/src/main/java/org/olat/course/nodes/adobeconnect/_i18n/LocalStrings_fr.properties index cb979a3a027..a720b18df8b 100644 --- a/src/main/java/org/olat/course/nodes/adobeconnect/_i18n/LocalStrings_fr.properties +++ b/src/main/java/org/olat/course/nodes/adobeconnect/_i18n/LocalStrings_fr.properties @@ -7,3 +7,7 @@ pane.tab.vcconfig=Configuration title_vc=Adobe Connect wimba.not.supported.message=Wimba Classroom n'est plus support\u00E9. wimba.not.supported.title=Wimba Classroom +vc.options.label=Autorisation d'acc\u00E8s +vc.access.label=Autorisation d'acc\u00E8s +vc.access.open=Les mod\u00E9rateur de la classe doivent \u00EAtre connect\u00E9 pour confirmer l'acc\u00E8s des participants +vc.access.start=Seuls les mod\u00E9rateurs ont le droit d'ouvrir cette classe diff --git a/src/main/java/org/olat/course/nodes/adobeconnect/_i18n/LocalStrings_pt_BR.properties b/src/main/java/org/olat/course/nodes/adobeconnect/_i18n/LocalStrings_pt_BR.properties new file mode 100644 index 00000000000..9a14be3205b --- /dev/null +++ b/src/main/java/org/olat/course/nodes/adobeconnect/_i18n/LocalStrings_pt_BR.properties @@ -0,0 +1,6 @@ +#Tue Nov 22 17:51:38 CET 2016 +vc.options.label=Autoriza\u00E7\u00E3o de acesso +vc.access.dates=A sala de aula virtual s\u00F3 estar\u00E1 dispon\u00EDvel em datas definidas +vc.access.label=Autoriza\u00E7\u00E3o de acesso +vc.access.open=O moderador deve estar em sala de aula para conceder acesso aos usu\u00E1rios +vc.access.start=Somente os moderadores podem abrir esta sala de aula virtual diff --git a/src/main/java/org/olat/modules/_spring/modulesContext.xml b/src/main/java/org/olat/modules/_spring/modulesContext.xml index b21c3eecc93..073544232c4 100644 --- a/src/main/java/org/olat/modules/_spring/modulesContext.xml +++ b/src/main/java/org/olat/modules/_spring/modulesContext.xml @@ -10,6 +10,7 @@ <context:component-scan base-package="org.olat.modules"/> + <import resource="classpath:/org/olat/modules/adobeconnect/_spring/adobeConnectContext.xml"/> <import resource="classpath:/org/olat/modules/coach/_spring/coachContext.xml"/> <import resource="classpath:/org/olat/modules/iq/_spring/iqContext.xml"/> <import resource="classpath:/org/olat/modules/lecture/_spring/lectureContext.xml"/> @@ -175,26 +176,6 @@ </property> </bean> - <!-- Adobe Connect admin. panel --> - <bean class="org.olat.core.extensions.action.GenericActionExtension" init-method="initExtensionPoints"> - <property name="order" value="7211" /> - <property name="actionController"> - <bean class="org.olat.core.gui.control.creator.AutoCreator" scope="prototype"> - <property name="className" value="org.olat.modules.adobeconnect.ui.AdobeConnectAdminController"/> - </bean> - </property> - <property name="navigationKey" value="adobeconnect" /> - <property name="parentTreeNodeIdentifier" value="externalToolsParent" /> - <property name="i18nActionKey" value="admin.menu.title"/> - <property name="i18nDescriptionKey" value="admin.menu.title.alt"/> - <property name="translationPackage" value="org.olat.modules.adobeconnect.ui"/> - <property name="extensionPoints"> - <list> - <value>org.olat.admin.SystemAdminMainController</value> - </list> - </property> - </bean> - <!-- OpenMeetings admin. panel --> <bean class="org.olat.core.extensions.action.GenericActionExtension" init-method="initExtensionPoints"> <property name="order" value="7210" /> diff --git a/src/main/java/org/olat/modules/adobeconnect/AdobeConnectManager.java b/src/main/java/org/olat/modules/adobeconnect/AdobeConnectManager.java index 435521febc3..352de5556bd 100644 --- a/src/main/java/org/olat/modules/adobeconnect/AdobeConnectManager.java +++ b/src/main/java/org/olat/modules/adobeconnect/AdobeConnectManager.java @@ -24,6 +24,7 @@ import java.util.List; import java.util.Locale; import org.olat.core.id.Identity; +import org.olat.course.nodes.adobeconnect.compatibility.MeetingCompatibilityDate; import org.olat.group.BusinessGroup; import org.olat.modules.adobeconnect.model.AdobeConnectErrors; import org.olat.modules.adobeconnect.model.AdobeConnectSco; @@ -39,11 +40,22 @@ public interface AdobeConnectManager { public boolean checkConnection(String url, String login, String password, AdobeConnectErrors error); - public void createMeeting(String name, String description, String templateId, - Date start, Date end, Locale locale, boolean allAccess, + public void createMeeting(String name, String description, String templateId, boolean permanent, + Date start, long leadTime, Date end, long followupTime, Locale locale, boolean allAccess, RepositoryEntry entry, String subIdent, BusinessGroup businessGroup, Identity actingIdentity, AdobeConnectErrors error); + /** + * The method creates only the Adobe Connect Meeting without membership. + * + * @param meeting The meeting reference + * @param locale The locale + * @param allAccess If guests has access without confirmation of the moderator + * @param error The error collector + * @return An updated meeting reference + */ + public AdobeConnectMeeting createAdobeMeeting(AdobeConnectMeeting meeting, Locale locale, boolean allAccess, AdobeConnectErrors error); + /** * * @param meeting The meeting to update @@ -53,11 +65,23 @@ public interface AdobeConnectManager { * @param endDate The end date to update or null * @param error */ - public AdobeConnectMeeting updateMeeting(AdobeConnectMeeting meeting, String name, String description, - String templateId, Date startDate, Date endDate, AdobeConnectErrors error); + public AdobeConnectMeeting updateMeeting(AdobeConnectMeeting meeting, String name, String description, String templateId, + boolean permanent, Date startDate, long leadTime, Date endDate, long followupTime, AdobeConnectErrors error); public AdobeConnectMeeting shareDocuments(AdobeConnectMeeting meeting, List<AdobeConnectSco> documents); + public void convert(List<MeetingCompatibilityDate> meetings, RepositoryEntry entry, String subIdent); + + /** + * Reload the meeting. + * + * @param meeting The meeting to reload. + * @return A fresh meeting object + */ + public AdobeConnectMeeting getMeeting(AdobeConnectMeeting meeting); + + public List<AdobeConnectMeeting> getMeetingsBefore(Date date); + public boolean deleteMeeting(AdobeConnectMeeting meeting, AdobeConnectErrors error); public List<AdobeConnectSco> getTemplates(); @@ -73,6 +97,8 @@ public interface AdobeConnectManager { */ public List<AdobeConnectMeeting> getMeetings(RepositoryEntry entry, String subIdent, BusinessGroup businessGroup); + public boolean hasMeetings(RepositoryEntry entry, String subIdent, BusinessGroup businessGroup); + public List<AdobeConnectMeeting> getAllMeetings(); /** @@ -96,6 +122,9 @@ public interface AdobeConnectManager { public boolean registerFor(AdobeConnectMeeting meeting, Identity identity, AdobeConnectMeetingPermission permission, AdobeConnectErrors error); + + public String open(AdobeConnectMeeting meeting, Identity identity, AdobeConnectErrors error); + public String join(AdobeConnectMeeting meeting, Identity identity, AdobeConnectErrors error); public String linkTo(AdobeConnectSco content, Identity identity, AdobeConnectErrors error); diff --git a/src/main/java/org/olat/modules/adobeconnect/AdobeConnectMeeting.java b/src/main/java/org/olat/modules/adobeconnect/AdobeConnectMeeting.java index c4b12626434..1eed5c4d42d 100644 --- a/src/main/java/org/olat/modules/adobeconnect/AdobeConnectMeeting.java +++ b/src/main/java/org/olat/modules/adobeconnect/AdobeConnectMeeting.java @@ -45,18 +45,42 @@ public interface AdobeConnectMeeting extends ModifiedInfo, CreateInfo { public void setDescription(String description); + public boolean isPermanent(); + + public void setPermanent(boolean permanent); + public Date getStartDate(); public void setStartDate(Date date); + public long getLeadTime(); + + public void setLeadTime(long leadTime); + + public Date getStartWithLeadTime(); + public Date getEndDate(); public void setEndDate(Date date); + public long getFollowupTime(); + + public void setFollowupTime(long followupTime); + + public Date getEndWithFollowupTime(); + + public boolean isOpened(); + + public void setOpened(boolean open); + public String getScoId(); public String getFolderId(); + public String getTemplateId(); + + public void setTemplateId(String templateId); + public List<String> getSharedDocumentIds(); public void setSharedDocumentIds(List<String> ids); diff --git a/src/main/java/org/olat/modules/adobeconnect/AdobeConnectModule.java b/src/main/java/org/olat/modules/adobeconnect/AdobeConnectModule.java index 38cda7fdeab..308343be468 100644 --- a/src/main/java/org/olat/modules/adobeconnect/AdobeConnectModule.java +++ b/src/main/java/org/olat/modules/adobeconnect/AdobeConnectModule.java @@ -50,7 +50,11 @@ public class AdobeConnectModule extends AbstractSpringModule implements ConfigOn private static final String ADMIN_CRED = "vc.adobe.adminpassword"; private static final String ACCOUNTID = "vc.adobe.accountid"; private static final String PROVIDERID = "vc.adobe.providerid"; - + private static final String CLEAN_MEETINGS = "vc.adobe.cleanupMeetings"; + private static final String DAYS_TO_KEEP = "vc.adobe.daysToKeep"; + private static final String SINGLE_MEETING_MODE = "vc.adobe.single.meeting.mode"; + private static final String CREATE_MEETING_IMMEDIATELY = "vc.adobe.createMeetingImmediately"; + @Value("${vc.adobe.enabled}") private boolean enabled; @Value("${vc.adobe.protocol:https}") @@ -73,6 +77,16 @@ public class AdobeConnectModule extends AbstractSpringModule implements ConfigOn private String accountId; @Value("${vc.adobe.provider:connect9}") private String providerId; + @Value("${vc.adobe.cleanupMeetings:false}") + private String cleanupMeetings; + @Value("${vc.adobe.daysToKeep:}") + private String daysToKeep; + @Value("${vc.adobe.single.meeting.mode:true}") + private String singleMeetingMode; + @Value("${vc.adobe.createMeetingImmediately:true}") + private String createMeetingImmediately; + @Value("${vc.adobe.login.compatibility.mode:false}") + private String loginCompatibilityMode; @Autowired public AdobeConnectModule(CoordinatorManager coordinatorManager) { @@ -98,6 +112,10 @@ public class AdobeConnectModule extends AbstractSpringModule implements ConfigOn contextPath = getStringPropertyValue(CONTEXTPATH, contextPath); accountId = getStringPropertyValue(ACCOUNTID, accountId); providerId = getStringPropertyValue(PROVIDERID, providerId); + cleanupMeetings = getStringPropertyValue(CLEAN_MEETINGS, cleanupMeetings); + daysToKeep = getStringPropertyValue(DAYS_TO_KEEP, daysToKeep); + singleMeetingMode = getStringPropertyValue(SINGLE_MEETING_MODE, singleMeetingMode); + createMeetingImmediately = getStringPropertyValue(CREATE_MEETING_IMMEDIATELY, createMeetingImmediately); } @Override @@ -243,11 +261,62 @@ public class AdobeConnectModule extends AbstractSpringModule implements ConfigOn } public String getAccountId() { + if("-".equals(accountId)) { + return null; + } return accountId; } public void setAccountId(String accountId) { this.accountId = accountId; - setStringProperty(ACCOUNTID, accountId, true); + setStringProperty(ACCOUNTID, StringHelper.containsNonWhitespace(accountId) ? accountId : "-" , true); + } + + public boolean isCleanupMeetings() { + return "true".equals(cleanupMeetings); + } + + public void setCleanupMeetings(boolean enable) { + cleanupMeetings = enable ? "true" : "false"; + setStringProperty(CLEAN_MEETINGS, cleanupMeetings, true); + } + + public long getDaysToKeep() { + if(StringHelper.isLong(daysToKeep)) { + return Long.parseLong(daysToKeep); + } + return -1; + } + + public void setDaysToKeep(String daysToKeep) { + this.daysToKeep = daysToKeep; + setStringProperty(DAYS_TO_KEEP, daysToKeep, true); + } + + public boolean isSingleMeetingMode() { + return "true".equals(singleMeetingMode); + } + + public void setSingleMeetingMode(boolean enable) { + singleMeetingMode = enable ? "true" : "false"; + setStringProperty(SINGLE_MEETING_MODE, singleMeetingMode, true); + } + + public boolean isCreateMeetingImmediately() { + return "true".equals(createMeetingImmediately); + } + + public void setCreateMeetingImmediately(boolean enable) { + createMeetingImmediately = enable ? "true" : "false"; + setStringProperty(CREATE_MEETING_IMMEDIATELY, createMeetingImmediately, true); + } + + /** + * + * @return true if the app. needs to be compatible with the + * login and password format of the old implementation. + */ + public boolean isLoginCompatibilityMode() { + return "true".equals(loginCompatibilityMode); } } diff --git a/src/main/java/org/olat/modules/adobeconnect/_spring/adobeConnectContext.xml b/src/main/java/org/olat/modules/adobeconnect/_spring/adobeConnectContext.xml new file mode 100644 index 00000000000..eea808023ce --- /dev/null +++ b/src/main/java/org/olat/modules/adobeconnect/_spring/adobeConnectContext.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="UTF-8"?> +<beans xmlns="http://www.springframework.org/schema/beans" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:context="http://www.springframework.org/schema/context" + xsi:schemaLocation=" + http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/context + http://www.springframework.org/schema/context/spring-context.xsd"> + + <bean class="org.olat.core.extensions.action.GenericActionExtension" init-method="initExtensionPoints"> + <property name="order" value="7211" /> + <property name="actionController"> + <bean class="org.olat.core.gui.control.creator.AutoCreator" scope="prototype"> + <property name="className" value="org.olat.modules.adobeconnect.ui.AdobeConnectAdminController"/> + </bean> + </property> + <property name="navigationKey" value="adobeconnect" /> + <property name="parentTreeNodeIdentifier" value="externalToolsParent" /> + <property name="i18nActionKey" value="admin.menu.title"/> + <property name="i18nDescriptionKey" value="admin.menu.title.alt"/> + <property name="translationPackage" value="org.olat.modules.adobeconnect.ui"/> + <property name="extensionPoints"> + <list> + <value>org.olat.admin.SystemAdminMainController</value> + </list> + </property> + </bean> + + <bean id="adobeCleanupTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean"> + <property name="jobDetail" ref="adobeConnectCleanupJob" /> + <property name="cronExpression" value="0 32 2 * * ?"/> + <property name="startDelay" value="45000" /> + </bean> + + <bean id="adobeConnectCleanupJob" class="org.springframework.scheduling.quartz.JobDetailFactoryBean" lazy-init="true"> + <property name="jobClass" value="org.olat.modules.adobeconnect.manager.AdobeConnectCleanupJob" /> + </bean> + +</beans> \ No newline at end of file diff --git a/src/main/java/org/olat/modules/adobeconnect/manager/AbstractAdobeConnectProvider.java b/src/main/java/org/olat/modules/adobeconnect/manager/AbstractAdobeConnectProvider.java index 715479ee5a2..da82eec5a19 100644 --- a/src/main/java/org/olat/modules/adobeconnect/manager/AbstractAdobeConnectProvider.java +++ b/src/main/java/org/olat/modules/adobeconnect/manager/AbstractAdobeConnectProvider.java @@ -167,6 +167,16 @@ public abstract class AbstractAdobeConnectProvider implements AdobeConnectSPI { return sendScoRequest(builder, errors); } + @Override + public List<AdobeConnectSco> getMeetingByName(String name, AdobeConnectErrors errors) { + UriBuilder builder = adobeConnectModule.getAdobeConnectUriBuilder(); + builder + .queryParam("action", "sco-search-by-field") + .queryParam("query", PREFIX + name) + .queryParam("filter-type", "meeting"); + return sendScoRequest(builder, errors); + } + @Override public boolean updateScoMeeting(String scoId, String name, String description, String templateId, Date startDate, Date endDate, AdobeConnectErrors errors) { @@ -476,6 +486,7 @@ public abstract class AbstractAdobeConnectProvider implements AdobeConnectSPI { AdobeConnectSco connectSco = new AdobeConnectSco(); connectSco.setScoId(sco.getAttribute("sco-id")); + connectSco.setFolderId(sco.getAttribute("folder-id")); connectSco.setType(sco.getAttribute("type")); connectSco.setIcon(sco.getAttribute("icon")); String urlPath = AdobeConnectUtils.getFirstElementValue(sco, "url-path"); @@ -532,11 +543,15 @@ public abstract class AbstractAdobeConnectProvider implements AdobeConnectSPI { } UriBuilder builder = adobeConnectModule.getAdobeConnectUriBuilder(); - URI uri = builder + builder = builder .queryParam("action", "login") .queryParam("login", adobeConnectModule.getAdminLogin()) .queryParam("password", adobeConnectModule.getAdminPassword()) - .queryParam("session", session.getSession()) + .queryParam("session", session.getSession()); + if(StringHelper.containsNonWhitespace(adobeConnectModule.getAccountId())) { + builder = builder.queryParam("account-id", adobeConnectModule.getAccountId()); + } + URI uri = builder .build(); HttpGet getLogin = new HttpGet(uri); @@ -653,6 +668,8 @@ public abstract class AbstractAdobeConnectProvider implements AdobeConnectSPI { permission.setPermissionId(permissionEl.getAttribute("permission-id")); permissions.add(permission); } + } else if(AdobeConnectUtils.isStatusNoData(doc)) { + // ok, there isn't any permissions } else { AdobeConnectUtils.error(doc, errors); } diff --git a/src/main/java/org/olat/modules/adobeconnect/manager/AdobeConnect9Provider.java b/src/main/java/org/olat/modules/adobeconnect/manager/AdobeConnect9Provider.java index 5ad09e7092f..dcb2d09f88f 100644 --- a/src/main/java/org/olat/modules/adobeconnect/manager/AdobeConnect9Provider.java +++ b/src/main/java/org/olat/modules/adobeconnect/manager/AdobeConnect9Provider.java @@ -114,13 +114,16 @@ public class AdobeConnect9Provider extends AbstractAdobeConnectProvider { } return null; } - + String login = Encoder.decrypt(authentication.getCredential(), authentication.getSalt(), Encoder.Algorithm.aes); UriBuilder builder = adobeConnectModule.getAdobeConnectUriBuilder(); builder .queryParam("action", "login") .queryParam("login", authentication.getAuthusername()) .queryParam("password", login); + if(StringHelper.containsNonWhitespace(adobeConnectModule.getAccountId())) { + builder = builder.queryParam("account-id", adobeConnectModule.getAccountId()); + } URI uri = builder.build(); BreezeSession session = null; diff --git a/src/main/java/org/olat/modules/adobeconnect/manager/AdobeConnectCleanupJob.java b/src/main/java/org/olat/modules/adobeconnect/manager/AdobeConnectCleanupJob.java new file mode 100644 index 00000000000..f98bec75955 --- /dev/null +++ b/src/main/java/org/olat/modules/adobeconnect/manager/AdobeConnectCleanupJob.java @@ -0,0 +1,77 @@ +/** + * <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.adobeconnect.manager; + +import java.util.Calendar; +import java.util.List; + +import org.apache.logging.log4j.Logger; +import org.olat.commons.calendar.CalendarUtils; +import org.olat.core.CoreSpringFactory; +import org.olat.core.commons.services.scheduler.JobWithDB; +import org.olat.core.logging.Tracing; +import org.olat.modules.adobeconnect.AdobeConnectManager; +import org.olat.modules.adobeconnect.AdobeConnectMeeting; +import org.olat.modules.adobeconnect.AdobeConnectModule; +import org.olat.modules.adobeconnect.model.AdobeConnectErrors; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; + +/** + * + * Initial date: 4 juil. 2019<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class AdobeConnectCleanupJob extends JobWithDB { + + private static final Logger log = Tracing.createLoggerFor(AdobeConnectCleanupJob.class); + + @Override + public void executeWithDB(JobExecutionContext arg0) throws JobExecutionException { + AdobeConnectModule adobeConnectModule = CoreSpringFactory.getImpl(AdobeConnectModule.class); + if(adobeConnectModule.isCleanupMeetings() && adobeConnectModule.getDaysToKeep() > 0) { + cleanUp(adobeConnectModule.getDaysToKeep()); + } + } + + private void cleanUp(long days) { + Calendar cal = Calendar.getInstance(); + cal.add(Calendar.DATE, -(int)days); + CalendarUtils.getStartOfDay(cal); + + AdobeConnectManager adobeConnectManager = CoreSpringFactory.getImpl(AdobeConnectManager.class); + List<AdobeConnectMeeting> oldMeetings = adobeConnectManager.getMeetingsBefore(cal.getTime()); + for(AdobeConnectMeeting oldMeeting:oldMeetings) { + try { + AdobeConnectErrors errors = new AdobeConnectErrors(); + adobeConnectManager.deleteMeeting(oldMeeting, errors); + if(errors.hasErrors()) { + log.error("Error trying to delete adobe connect meeting with id {} with message: {}", + (oldMeeting != null && oldMeeting.getKey() != null ? oldMeeting.getKey() : "NULL"), + errors.getErrorMessages()); + } + } catch (Exception e) { + log.error("", e); + } + } + } + +} diff --git a/src/main/java/org/olat/modules/adobeconnect/manager/AdobeConnectManagerImpl.java b/src/main/java/org/olat/modules/adobeconnect/manager/AdobeConnectManagerImpl.java index 4eaf57600c7..54b01378899 100644 --- a/src/main/java/org/olat/modules/adobeconnect/manager/AdobeConnectManagerImpl.java +++ b/src/main/java/org/olat/modules/adobeconnect/manager/AdobeConnectManagerImpl.java @@ -39,11 +39,14 @@ import org.apache.http.util.EntityUtils; import org.apache.logging.log4j.Logger; import org.olat.basesecurity.Authentication; import org.olat.basesecurity.BaseSecurity; +import org.olat.core.commons.persistence.DB; +import org.olat.core.helpers.Settings; import org.olat.core.id.Identity; import org.olat.core.logging.Tracing; import org.olat.core.util.Encoder; import org.olat.core.util.StringHelper; import org.olat.core.util.WebappHelper; +import org.olat.course.nodes.adobeconnect.compatibility.MeetingCompatibilityDate; import org.olat.group.BusinessGroup; import org.olat.group.DeletableGroupData; import org.olat.modules.adobeconnect.AdobeConnectManager; @@ -52,6 +55,7 @@ import org.olat.modules.adobeconnect.AdobeConnectMeetingPermission; import org.olat.modules.adobeconnect.AdobeConnectModule; import org.olat.modules.adobeconnect.AdobeConnectUser; import org.olat.modules.adobeconnect.model.AdobeConnectErrors; +import org.olat.modules.adobeconnect.model.AdobeConnectMeetingImpl; import org.olat.modules.adobeconnect.model.AdobeConnectPrincipal; import org.olat.modules.adobeconnect.model.AdobeConnectSco; import org.olat.modules.adobeconnect.model.BreezeSession; @@ -76,6 +80,8 @@ public class AdobeConnectManagerImpl implements AdobeConnectManager, DeletableGr private AdobeConnectSPI adapter; + @Autowired + private DB dbInstance; @Autowired private UserManager userManager; @Autowired @@ -101,6 +107,17 @@ public class AdobeConnectManagerImpl implements AdobeConnectManager, DeletableGr return adapter == null ? new NoAdapterProvider() : adapter; } + @Override + public AdobeConnectMeeting getMeeting(AdobeConnectMeeting meeting) { + if(meeting == null || meeting.getKey() == null) return meeting; + return adobeConnectMeetingDao.loadByKey(meeting.getKey()); + } + + @Override + public List<AdobeConnectMeeting> getMeetingsBefore(Date date) { + return adobeConnectMeetingDao.getMeetingsBefore(date); + } + @Override public void deleteUserData(Identity identity, String newDeletedUserName) { adobeConnectUserDao.deleteAdobeConnectUser(identity); @@ -110,11 +127,11 @@ public class AdobeConnectManagerImpl implements AdobeConnectManager, DeletableGr public boolean deleteGroupDataFor(BusinessGroup group) { List<AdobeConnectMeeting> meetings = adobeConnectMeetingDao.getMeetings(group); - AdobeConnectErrors erros = new AdobeConnectErrors(); + AdobeConnectErrors errors = new AdobeConnectErrors(); for(AdobeConnectMeeting meeting:meetings) { - deleteMeeting(meeting, erros); + deleteMeeting(meeting, errors); } - return erros.hasErrors(); + return errors.hasErrors(); } private String generateFolderName(RepositoryEntry entry, String subIdent, BusinessGroup businessGroup) { @@ -133,8 +150,73 @@ public class AdobeConnectManagerImpl implements AdobeConnectManager, DeletableGr } @Override - public void createMeeting(String name, String description, String templateId, - Date start, Date end, Locale locale, boolean allAccess, + public void createMeeting(String name, String description, String templateId, boolean permanent, + Date start, long leadtime, Date end, long followupTime, Locale locale, boolean allAccess, + RepositoryEntry entry, String subIdent, BusinessGroup businessGroup, + Identity actingIdentity, AdobeConnectErrors errors) { + if(adobeConnectModule.isCreateMeetingImmediately()) { + createAdobeMeeting(name, description, templateId, permanent, start, leadtime, end, followupTime, locale, + allAccess, entry, subIdent, businessGroup, actingIdentity, errors); + } else { + adobeConnectMeetingDao.createMeeting(name, description, permanent, + start, leadtime, end, followupTime, templateId, null, null, null, entry, subIdent, businessGroup); + } + } + + @Override + public AdobeConnectMeeting createAdobeMeeting(AdobeConnectMeeting meeting, Locale locale, boolean allAccess, AdobeConnectErrors errors) { + AdobeConnectSco sco = null; + if(adobeConnectModule.isSingleMeetingMode()) { + sco = getSingleMeetingRoom(meeting.getEntry(), meeting.getSubIdent(), meeting.getBusinessGroup(), errors); + } else if(meeting.isPermanent()) { + sco = getPermanentMeetingRoom(meeting.getEntry(), meeting.getSubIdent(), meeting.getBusinessGroup(), errors); + } + if(sco == null) { + AdobeConnectSco folder; + String folderName = generateFolderName(meeting.getEntry(), meeting.getSubIdent(), meeting.getBusinessGroup()); + List<AdobeConnectSco> folderScos = getAdapter().getFolderByName(folderName, errors); + if(folderScos == null || folderScos.isEmpty()) { + folder = getAdapter().createFolder(folderName, errors); + } else { + folder = folderScos.get(0); + } + + if(!errors.hasErrors()) { + sco = getAdapter().createScoMeeting(meeting.getName(), meeting.getDescription(), folder.getScoId(), + meeting.getTemplateId(), meeting.getStartDate(), meeting.getEndDate(), locale, errors); + } + } + if(sco != null) { + ((AdobeConnectMeetingImpl)meeting).setFolderId(sco.getFolderId()); + ((AdobeConnectMeetingImpl)meeting).setScoId(sco.getScoId()); + ((AdobeConnectMeetingImpl)meeting).setEnvName(adobeConnectModule.getBaseUrl()); + meeting = adobeConnectMeetingDao.updateMeeting(meeting); + } + dbInstance.commit(); + return meeting; + } + + /** + * Create the Adobe Meeting first and the OpenOlat database object only + * if the meeting was successfully scheduled. + * + * @param name The name of the meeting + * @param description The description of the meeting + * @param templateId The template + * @param start Date to start the meeting + * @param leadtime Preparation time + * @param end End of the meeting + * @param followupTime Follow-up time + * @param locale Language + * @param allAccess + * @param entry The course + * @param subIdent The course node identifier (for example) + * @param businessGroup The business group + * @param actingIdentity The user which create the meeting + * @param errors Errors + */ + private void createAdobeMeeting(String name, String description, String templateId, boolean permanent, + Date start, long leadtime, Date end, long followupTime, Locale locale, boolean allAccess, RepositoryEntry entry, String subIdent, BusinessGroup businessGroup, Identity actingIdentity, AdobeConnectErrors errors) { @@ -151,13 +233,17 @@ public class AdobeConnectManagerImpl implements AdobeConnectManager, DeletableGr return;// we need a folder } - AdobeConnectSco sco = getAdapter().createScoMeeting(name, description, folder.getScoId(), templateId, start, end, locale, errors); + AdobeConnectSco sco = null; + if(adobeConnectModule.isSingleMeetingMode()) { + sco = getSingleMeetingRoom(entry, subIdent, businessGroup, errors); + } else if(permanent) { + sco = getPermanentMeetingRoom(entry, subIdent, businessGroup, errors); + } + if(sco == null) { + sco = getAdapter().createScoMeeting(name, description, folder.getScoId(), templateId, start, end, locale, errors); + } if(sco != null) { - getAdapter().setPermissions(sco.getScoId(), true, errors); - AdobeConnectPrincipal admin = getAdapter().adminCommonInfo(errors); - if(admin != null) { - getAdapter().setMember(sco.getScoId(), admin.getPrincipalId(), AdobeConnectMeetingPermission.host.permission(), errors); - } + getAdapter().setPermissions(sco.getScoId(), allAccess, errors); String actingUser = getOrCreateUser(actingIdentity, true, errors); if(actingUser != null) { @@ -165,8 +251,8 @@ public class AdobeConnectManagerImpl implements AdobeConnectManager, DeletableGr } // try harder if the meeting hasn't a single host - if(actingUser == null && admin == null) { - admin = getAdapter().getPrincipalByLogin(adobeConnectModule.getAdminLogin(), errors); + if(actingUser == null) { + AdobeConnectPrincipal admin = getAdapter().getPrincipalByLogin(adobeConnectModule.getAdminLogin(), errors); if(admin != null) { getAdapter().setMember(sco.getScoId(), admin.getPrincipalId(), AdobeConnectMeetingPermission.host.permission(), errors); } @@ -174,24 +260,70 @@ public class AdobeConnectManagerImpl implements AdobeConnectManager, DeletableGr String scoId = sco.getScoId(); String envName = adobeConnectModule.getBaseUrl(); - adobeConnectMeetingDao.createMeeting(name, description, start, end, scoId, folder.getScoId(), envName, entry, subIdent, businessGroup); + adobeConnectMeetingDao.createMeeting(name, description, + permanent, start, leadtime, end, followupTime, + templateId, scoId, folder.getScoId(), envName, entry, subIdent, businessGroup); + } + } + + /** + * Search for a meeting room in single meeting mode. + * + * @return The meeting or null if not found. + */ + private AdobeConnectSco getSingleMeetingRoom(RepositoryEntry entry, String subIdent, BusinessGroup businessGroup, + AdobeConnectErrors errors) { + List<AdobeConnectMeeting> currentMeetings = getMeetings(entry, subIdent, businessGroup); + if(currentMeetings != null && !currentMeetings.isEmpty()) { + AdobeConnectMeeting meeting = currentMeetings.get(0); + return getAdapter().getScoMeeting(meeting, errors); + } + return null; + } + + private AdobeConnectSco getPermanentMeetingRoom(RepositoryEntry entry, String subIdent, BusinessGroup businessGroup, + AdobeConnectErrors errors) { + List<AdobeConnectMeeting> currentMeetings = getMeetings(entry, subIdent, businessGroup); + if(currentMeetings != null && !currentMeetings.isEmpty()) { + for(AdobeConnectMeeting meeting:currentMeetings) { + if(meeting.isPermanent()) { + return getAdapter().getScoMeeting(meeting, errors); + } + } } + return null; } @Override public AdobeConnectMeeting updateMeeting(AdobeConnectMeeting meeting, String name, String description, String templateId, - Date start, Date end, AdobeConnectErrors errors) { - boolean ok = getAdapter().updateScoMeeting(meeting.getScoId(), name, description, templateId, start, end, errors); - if(ok) { - meeting.setName(name); - meeting.setDescription(description); - meeting.setStartDate(start); - meeting.setEndDate(end); - meeting = adobeConnectMeetingDao.updateMeeting(meeting); + boolean permanent, Date start, long leadTime, Date end, long followupTime, AdobeConnectErrors errors) { + if(StringHelper.containsNonWhitespace(meeting.getScoId())) { + boolean ok = getAdapter().updateScoMeeting(meeting.getScoId(), name, description, templateId, start, end, errors); + if(ok) { + meeting = updateMeeting(meeting, name, description, permanent, start, leadTime, end, followupTime); + } + } else { + meeting = updateMeeting(meeting, name, description, permanent, start, leadTime, end, followupTime); } return meeting; } + private AdobeConnectMeeting updateMeeting(AdobeConnectMeeting meeting, String name, String description, + boolean permanent, Date start, long leadTime, Date end, long followupTime) { + meeting.setName(name); + meeting.setDescription(description); + if(adobeConnectModule.isSingleMeetingMode()) { + meeting.setPermanent(true); + } else if(!StringHelper.containsNonWhitespace(meeting.getScoId())) { + meeting.setPermanent(permanent); + } + meeting.setStartDate(start); + meeting.setLeadTime(leadTime); + meeting.setEndDate(end); + meeting.setFollowupTime(followupTime); + return adobeConnectMeetingDao.updateMeeting(meeting); + } + @Override public AdobeConnectMeeting shareDocuments(AdobeConnectMeeting meeting, List<AdobeConnectSco> documents) { meeting = adobeConnectMeetingDao.loadByKey(meeting.getKey()); @@ -238,6 +370,14 @@ public class AdobeConnectManagerImpl implements AdobeConnectManager, DeletableGr return registered; } + @Override + public String open(AdobeConnectMeeting meeting, Identity identity, AdobeConnectErrors error) { + meeting.setOpened(true); + meeting = adobeConnectMeetingDao.updateMeeting(meeting); + dbInstance.commit(); + return join(meeting, identity, error); + } + @Override public String join(AdobeConnectMeeting meeting, Identity identity, AdobeConnectErrors error) { String actingUser = getOrCreateUser(identity, false, error); @@ -299,15 +439,80 @@ public class AdobeConnectManagerImpl implements AdobeConnectManager, DeletableGr @Override public boolean deleteMeeting(AdobeConnectMeeting meeting, AdobeConnectErrors errors) { boolean deleted = false; - AdobeConnectErrors error = new AdobeConnectErrors(); - if(getAdapter().deleteScoMeeting(meeting, error)) { + boolean deleteAdobeConnect = canDeleteAdobeMeeting(meeting); + if(deleteAdobeConnect) { + AdobeConnectErrors error = new AdobeConnectErrors(); + if(getAdapter().deleteScoMeeting(meeting, error)) { + AdobeConnectMeeting reloadedMeeting = adobeConnectMeetingDao.loadByKey(meeting.getKey()); + adobeConnectMeetingDao.deleteMeeting(reloadedMeeting); + deleted = true; + } + errors.append(error); + } else { AdobeConnectMeeting reloadedMeeting = adobeConnectMeetingDao.loadByKey(meeting.getKey()); adobeConnectMeetingDao.deleteMeeting(reloadedMeeting); deleted = true; } - errors.append(error); return deleted; } + + private boolean canDeleteAdobeMeeting(AdobeConnectMeeting meeting) { + List<AdobeConnectMeeting> sharedMeetings; + if(meeting.getEntry() != null) { + sharedMeetings = adobeConnectMeetingDao.getMeetings(meeting.getEntry(), meeting.getSubIdent()); + } else if(meeting.getBusinessGroup() != null) { + sharedMeetings = adobeConnectMeetingDao.getMeetings(meeting.getBusinessGroup()); + } else { + return true; + } + + boolean foundSharedMeeting = false; + for(AdobeConnectMeeting sharedMeeting:sharedMeetings) { + if(!sharedMeeting.equals(meeting) + && sharedMeeting.getScoId() != null + && sharedMeeting.getScoId().equals(meeting.getScoId())) { + foundSharedMeeting |= true; + } + } + return !foundSharedMeeting; + } + + @Override + public void convert(List<MeetingCompatibilityDate> meetingsData, RepositoryEntry entry, String subIdent) { + boolean hasMeetings = adobeConnectMeetingDao.hasMeetings(entry, subIdent); + if(hasMeetings) { + return; // do the conversion only once + } + + String scoId = null; + String folderId = null; + String envName = null; + + AdobeConnectErrors errors = new AdobeConnectErrors(); + String roomId = entry.getOlatResource().getResourceableId() + "_" + subIdent; + List<AdobeConnectSco> meetings = getAdapter().getMeetingByName(roomId, errors); + if(meetings != null && !meetings.isEmpty()) { + AdobeConnectSco meeting = meetings.get(0); + scoId = meeting.getScoId(); + folderId = meeting.getFolderId(); + envName = adobeConnectModule.getBaseUrl(); + } + + for(MeetingCompatibilityDate meetingData:meetingsData) { + adobeConnectMeetingDao.createMeeting(meetingData.getTitle(), meetingData.getDescription(), true, + meetingData.getStart(), 15, meetingData.getEnd(), 15, null, scoId, folderId, envName, entry, subIdent, null); + } + } + + @Override + public boolean hasMeetings(RepositoryEntry entry, String subIdent, BusinessGroup businessGroup) { + if(entry != null) { + return adobeConnectMeetingDao.hasMeetings(entry, subIdent); + } else if(businessGroup != null) { + return adobeConnectMeetingDao.hasMeetings(businessGroup); + } + return false; + } @Override public List<AdobeConnectMeeting> getMeetings(RepositoryEntry entry, String subIdent, BusinessGroup businessGroup) { @@ -328,12 +533,20 @@ public class AdobeConnectManagerImpl implements AdobeConnectManager, DeletableGr String envName = adobeConnectModule.getBaseUrl(); AdobeConnectUser user = adobeConnectUserDao.getUser(identity, envName); if(user == null && create) { - String login = identity.getUser().getEmail(); + boolean compatible = adobeConnectModule.isLoginCompatibilityMode(); + String login; + if(compatible) { + login = "olat-" + identity.getName(); + } else { + login = identity.getUser().getEmail(); + } AdobeConnectPrincipal aUser = getAdapter().getPrincipalByLogin(login, error); String creds = null; if(aUser == null) { - if(getAdapter().isManagedPassword()) { + if(compatible) { + creds = Encoder.md5hash(identity.getName() + "@" + Settings.getApplicationName()); + } else if(getAdapter().isManagedPassword()) { creds = UUID.randomUUID().toString().replace("-", ""); if(creds.length() > 32) { creds = creds.substring(0, 32); diff --git a/src/main/java/org/olat/modules/adobeconnect/manager/AdobeConnectMeetingDAO.java b/src/main/java/org/olat/modules/adobeconnect/manager/AdobeConnectMeetingDAO.java index 4e9ed51d793..ab5aaf94e95 100644 --- a/src/main/java/org/olat/modules/adobeconnect/manager/AdobeConnectMeetingDAO.java +++ b/src/main/java/org/olat/modules/adobeconnect/manager/AdobeConnectMeetingDAO.java @@ -23,6 +23,7 @@ import java.util.Calendar; import java.util.Date; import java.util.List; +import javax.persistence.TemporalType; import javax.persistence.TypedQuery; import org.olat.core.commons.persistence.DB; @@ -48,16 +49,19 @@ public class AdobeConnectMeetingDAO { @Autowired private DB dbInstance; - public AdobeConnectMeeting createMeeting(String name, String description, Date start, Date end, - String scoId, String folderId, String envName, + public AdobeConnectMeeting createMeeting(String name, String description, + boolean permanent, Date start, long leadTime, Date end, long followupTime, + String templateId, String scoId, String folderId, String envName, RepositoryEntry entry, String subIdent, BusinessGroup businessGroup) { AdobeConnectMeetingImpl meeting = new AdobeConnectMeetingImpl(); meeting.setCreationDate(new Date()); meeting.setLastModified(meeting.getCreationDate()); meeting.setName(name); meeting.setDescription(description); - meeting.setStartDate(cleanDate(start)); - meeting.setEndDate(cleanDate(end)); + meeting.setOpened(false); + meeting.setPermanent(permanent); + updateDates(meeting, start, leadTime, end, followupTime); + meeting.setTemplateId(templateId); meeting.setScoId(scoId); meeting.setFolderId(folderId); meeting.setEnvName(envName); @@ -74,11 +78,42 @@ public class AdobeConnectMeetingDAO { public AdobeConnectMeeting updateMeeting(AdobeConnectMeeting meeting) { AdobeConnectMeetingImpl meet = (AdobeConnectMeetingImpl)meeting; meet.setLastModified(new Date()); - meet.setStartDate(cleanDate(meet.getStartDate())); - meet.setEndDate(cleanDate(meet.getEndDate())); + updateDates(meet, meet.getStartDate(), meet.getLeadTime(), meet.getEndDate(), meet.getFollowupTime()); return dbInstance.getCurrentEntityManager().merge(meet); } + private void updateDates(AdobeConnectMeetingImpl meet, Date start, long leadTime, Date end, long followupTime) { + Calendar cal = Calendar.getInstance(); + if(start == null) { + meet.setStartDate(null); + meet.setLeadTime(0); + meet.setStartWithLeadTime(null); + } else { + start = cleanDate(start); + if(leadTime > 0) { + cal.add(Calendar.MINUTE, -(int)leadTime); + } + meet.setStartDate(start); + meet.setLeadTime(leadTime); + meet.setStartWithLeadTime(cal.getTime()); + } + + if(end == null) { + meet.setEndDate(null); + meet.setFollowupTime(0); + meet.setEndWithFollowupTime(null); + } else { + end = cleanDate(end); + cal.setTime(end); + if(followupTime > 0) { + cal.add(Calendar.MINUTE, (int)followupTime); + } + meet.setEndDate(end); + meet.setFollowupTime(followupTime); + meet.setEndWithFollowupTime(cal.getTime()); + } + } + /** * Remove seconds and milliseconds * @return @@ -107,6 +142,19 @@ public class AdobeConnectMeetingDAO { return meetings == null || meetings.isEmpty() ? null : meetings.get(0); } + public List<AdobeConnectMeeting> getMeetingsBefore(Date date) { + StringBuilder sb = new StringBuilder(256); + sb.append("select meeting from adobeconnectmeeting as meeting") + .append(" inner join fetch meeting.entry as v") + .append(" inner join fetch v.olatResource as resource") + .append(" where meeting.endWithFollowupTime is not null and meeting.endWithFollowupTime<:date"); + + return dbInstance.getCurrentEntityManager() + .createQuery(sb.toString(), AdobeConnectMeeting.class) + .setParameter("date", date, TemporalType.TIMESTAMP) + .getResultList(); + } + public List<AdobeConnectMeeting> getMeetings(RepositoryEntryRef entry, String subIdent) { StringBuilder sb = new StringBuilder(256); sb.append("select meeting from adobeconnectmeeting as meeting") @@ -126,6 +174,27 @@ public class AdobeConnectMeetingDAO { return query.getResultList(); } + public boolean hasMeetings(RepositoryEntryRef entry, String subIdent) { + StringBuilder sb = new StringBuilder(256); + sb.append("select meeting.key from adobeconnectmeeting as meeting") + .append(" inner join meeting.entry as v") + .append(" where v.key=:entryKey"); + if(StringHelper.containsNonWhitespace(subIdent)) { + sb.append(" and meeting.subIdent=:subIdent"); + } + + TypedQuery<Long> query= dbInstance.getCurrentEntityManager() + .createQuery(sb.toString(), Long.class) + .setParameter("entryKey", entry.getKey()); + if(StringHelper.containsNonWhitespace(subIdent)) { + query.setParameter("subIdent", subIdent); + } + List<Long> keys = query.setFirstResult(0) + .setMaxResults(1) + .getResultList(); + return keys != null && !keys.isEmpty() && keys.get(0) != null && keys.get(0).longValue() >= 0; + } + public List<AdobeConnectMeeting> getMeetings(BusinessGroupRef businessGroup) { StringBuilder sb = new StringBuilder(256); sb.append("select meeting from adobeconnectmeeting as meeting") @@ -138,6 +207,18 @@ public class AdobeConnectMeetingDAO { .getResultList(); } + public boolean hasMeetings(BusinessGroupRef businessGroup) { + StringBuilder sb = new StringBuilder(256); + sb.append("select meeting.key from adobeconnectmeeting as meeting") + .append(" where meeting.businessGroup.key=:groupKey"); + + List<Long> keys = dbInstance.getCurrentEntityManager() + .createQuery(sb.toString(), Long.class) + .setParameter("groupKey", businessGroup.getKey()) + .getResultList(); + return keys != null && !keys.isEmpty() && keys.get(0) != null && keys.get(0).longValue() >= 0; + } + public List<AdobeConnectMeeting> getAllMeetings() { StringBuilder sb = new StringBuilder(256); sb.append("select meeting from adobeconnectmeeting as meeting") diff --git a/src/main/java/org/olat/modules/adobeconnect/manager/AdobeConnectSPI.java b/src/main/java/org/olat/modules/adobeconnect/manager/AdobeConnectSPI.java index 01a28250506..e90c74cb80c 100644 --- a/src/main/java/org/olat/modules/adobeconnect/manager/AdobeConnectSPI.java +++ b/src/main/java/org/olat/modules/adobeconnect/manager/AdobeConnectSPI.java @@ -66,6 +66,8 @@ public interface AdobeConnectSPI { */ public List<AdobeConnectSco> getFolderByName(String name, AdobeConnectErrors errors); + public List<AdobeConnectSco> getMeetingByName(String name, AdobeConnectErrors errors); + public List<AdobeConnectSco> getTemplates(); public List<AdobeConnectSco> getRecordings(AdobeConnectMeeting meeting, AdobeConnectErrors error); diff --git a/src/main/java/org/olat/modules/adobeconnect/manager/AdobeConnectUtils.java b/src/main/java/org/olat/modules/adobeconnect/manager/AdobeConnectUtils.java index 02494744a98..86ff21c7baa 100644 --- a/src/main/java/org/olat/modules/adobeconnect/manager/AdobeConnectUtils.java +++ b/src/main/java/org/olat/modules/adobeconnect/manager/AdobeConnectUtils.java @@ -74,7 +74,6 @@ public class AdobeConnectUtils { HttpEntity entity = response.getEntity(); Document doc = getDocumentFromEntity(entity); if(isStatusOk(doc)) { - print(doc); Header header = response.getFirstHeader("Set-Cookie"); if(header != null) { session = BreezeSession.valueOf(header); @@ -86,6 +85,8 @@ public class AdobeConnectUtils { session = infoSession; } } + } else { + print(doc); } } catch (Exception e) { log.error("", e); @@ -166,6 +167,15 @@ public class AdobeConnectUtils { return true; } + protected static final boolean isStatusNoData(Document doc) { + NodeList permissionList = doc.getElementsByTagName("status"); + if(permissionList != null && permissionList.getLength() == 1) { + Element status = (Element)permissionList.item(0); + return "no-data".equalsIgnoreCase(status.getAttribute("code")); + } + return true; + } + protected static final void error(Document doc, AdobeConnectErrors errors) { NodeList permissionList = doc.getElementsByTagName("status"); if(permissionList != null && permissionList.getLength() == 1) { diff --git a/src/main/java/org/olat/modules/adobeconnect/manager/DFNprovider.java b/src/main/java/org/olat/modules/adobeconnect/manager/DFNprovider.java index 5f649a7d42e..21d9f3ceae6 100644 --- a/src/main/java/org/olat/modules/adobeconnect/manager/DFNprovider.java +++ b/src/main/java/org/olat/modules/adobeconnect/manager/DFNprovider.java @@ -51,9 +51,11 @@ public class DFNprovider extends AbstractAdobeConnectProvider { private static final Logger log = Tracing.createLoggerFor(DFNprovider.class); + public static final String DFN_ID = "dfn"; + @Override public String getId() { - return "dfn"; + return DFN_ID; } @Override diff --git a/src/main/java/org/olat/modules/adobeconnect/manager/NoAdapterProvider.java b/src/main/java/org/olat/modules/adobeconnect/manager/NoAdapterProvider.java index 240b8a22a7d..3757e27ad18 100644 --- a/src/main/java/org/olat/modules/adobeconnect/manager/NoAdapterProvider.java +++ b/src/main/java/org/olat/modules/adobeconnect/manager/NoAdapterProvider.java @@ -91,6 +91,12 @@ public class NoAdapterProvider implements AdobeConnectSPI { return new ArrayList<>(); } + @Override + public List<AdobeConnectSco> getMeetingByName(String name, AdobeConnectErrors errors) { + errors.append(new AdobeConnectError(AdobeConnectErrorCodes.serverNotAvailable)); + return new ArrayList<>(); + } + @Override public List<AdobeConnectSco> getTemplates() { return new ArrayList<>(); diff --git a/src/main/java/org/olat/modules/adobeconnect/model/AdobeConnectErrors.java b/src/main/java/org/olat/modules/adobeconnect/model/AdobeConnectErrors.java index c43b9d7cc7e..379020bb2c2 100644 --- a/src/main/java/org/olat/modules/adobeconnect/model/AdobeConnectErrors.java +++ b/src/main/java/org/olat/modules/adobeconnect/model/AdobeConnectErrors.java @@ -51,4 +51,13 @@ public class AdobeConnectErrors implements Serializable { public boolean hasErrors() { return !errors.isEmpty(); } + + public String getErrorMessages() { + StringBuilder sb = new StringBuilder(256); + for(AdobeConnectError error:errors) { + if(sb.length() > 0) sb.append(", "); + sb.append(error.getCode() == null ? "UNKOWN" : error.getCode().name()); + } + return sb.toString(); + } } diff --git a/src/main/java/org/olat/modules/adobeconnect/model/AdobeConnectMeetingImpl.java b/src/main/java/org/olat/modules/adobeconnect/model/AdobeConnectMeetingImpl.java index 2f6a12ff36e..3107824d5fb 100644 --- a/src/main/java/org/olat/modules/adobeconnect/model/AdobeConnectMeetingImpl.java +++ b/src/main/java/org/olat/modules/adobeconnect/model/AdobeConnectMeetingImpl.java @@ -72,14 +72,33 @@ public class AdobeConnectMeetingImpl implements Persistable, AdobeConnectMeeting private String description; @Column(name="a_start_date", nullable=true, insertable=true, updatable=true) private Date startDate; + @Column(name="a_leadtime", nullable=true, insertable=true, updatable=true) + private long leadTime; + @Temporal(TemporalType.TIMESTAMP) + @Column(name="a_start_with_leadtime", nullable=true, insertable=true, updatable=true) + private Date startWithLeadTime; + @Column(name="a_end_date", nullable=true, insertable=true, updatable=true) private Date endDate; + @Column(name="a_followuptime", nullable=true, insertable=true, updatable=true) + private long followupTime; + @Temporal(TemporalType.TIMESTAMP) + @Column(name="a_end_with_followuptime", nullable=true, insertable=true, updatable=true) + private Date endWithFollowupTime; - @Column(name="a_sco_id", nullable=true, insertable=true, updatable=false) + @Column(name="a_permanent", nullable=false, insertable=true, updatable=true) + private boolean permanent; + + @Column(name="a_opened", nullable=false, insertable=true, updatable=true) + private boolean opened; + + @Column(name="a_template_id", nullable=true, insertable=true, updatable=true) + private String templateId; + @Column(name="a_sco_id", nullable=true, insertable=true, updatable=true) private String scoId; - @Column(name="a_folder_id", nullable=true, insertable=true, updatable=false) + @Column(name="a_folder_id", nullable=true, insertable=true, updatable=true) private String folderId; - @Column(name="a_env_name", nullable=true, insertable=true, updatable=false) + @Column(name="a_env_name", nullable=true, insertable=true, updatable=true) private String envName; @Column(name="a_shared_documents", nullable=true, insertable=true, updatable=true) @@ -142,6 +161,16 @@ public class AdobeConnectMeetingImpl implements Persistable, AdobeConnectMeeting this.folderId = folderId; } + @Override + public String getTemplateId() { + return templateId; + } + + @Override + public void setTemplateId(String templateId) { + this.templateId = templateId; + } + @Override public String getName() { return name; @@ -162,6 +191,16 @@ public class AdobeConnectMeetingImpl implements Persistable, AdobeConnectMeeting this.description = description; } + @Override + public boolean isPermanent() { + return permanent; + } + + @Override + public void setPermanent(boolean permanent) { + this.permanent = permanent; + } + @Override public Date getStartDate() { return startDate; @@ -172,6 +211,25 @@ public class AdobeConnectMeetingImpl implements Persistable, AdobeConnectMeeting this.startDate = start; } + @Override + public long getLeadTime() { + return leadTime; + } + + @Override + public void setLeadTime(long leadTime) { + this.leadTime = leadTime; + } + + @Override + public Date getStartWithLeadTime() { + return startWithLeadTime; + } + + public void setStartWithLeadTime(Date startWithLeadTime) { + this.startWithLeadTime = startWithLeadTime; + } + @Override public Date getEndDate() { return endDate; @@ -182,6 +240,25 @@ public class AdobeConnectMeetingImpl implements Persistable, AdobeConnectMeeting this.endDate = end; } + @Override + public long getFollowupTime() { + return followupTime; + } + + @Override + public void setFollowupTime(long followupTime) { + this.followupTime = followupTime; + } + + @Override + public Date getEndWithFollowupTime() { + return endWithFollowupTime; + } + + public void setEndWithFollowupTime(Date endWithFollowupTime) { + this.endWithFollowupTime = endWithFollowupTime; + } + @Override public String getEnvName() { return envName; @@ -191,6 +268,16 @@ public class AdobeConnectMeetingImpl implements Persistable, AdobeConnectMeeting this.envName = envName; } + @Override + public boolean isOpened() { + return opened; + } + + @Override + public void setOpened(boolean opened) { + this.opened = opened; + } + public String getSharedDocuments() { return sharedDocuments; } diff --git a/src/main/java/org/olat/modules/adobeconnect/model/AdobeConnectSco.java b/src/main/java/org/olat/modules/adobeconnect/model/AdobeConnectSco.java index ef890ee013d..73bf4d92440 100644 --- a/src/main/java/org/olat/modules/adobeconnect/model/AdobeConnectSco.java +++ b/src/main/java/org/olat/modules/adobeconnect/model/AdobeConnectSco.java @@ -30,6 +30,7 @@ import java.util.Date; public class AdobeConnectSco { private String scoId; + private String folderId; private String type; private String urlPath; private String name; @@ -47,6 +48,14 @@ public class AdobeConnectSco { this.scoId = scoId; } + public String getFolderId() { + return folderId; + } + + public void setFolderId(String folderId) { + this.folderId = folderId; + } + public String getType() { return type; } diff --git a/src/main/java/org/olat/modules/adobeconnect/ui/AdobeConnectAdminMeetingsController.java b/src/main/java/org/olat/modules/adobeconnect/ui/AdobeConnectAdminMeetingsController.java index ad8e179cdd1..28f1c100751 100644 --- a/src/main/java/org/olat/modules/adobeconnect/ui/AdobeConnectAdminMeetingsController.java +++ b/src/main/java/org/olat/modules/adobeconnect/ui/AdobeConnectAdminMeetingsController.java @@ -45,6 +45,7 @@ import org.olat.core.gui.control.generic.modal.DialogBoxUIFactory; import org.olat.core.util.StringHelper; import org.olat.modules.adobeconnect.AdobeConnectManager; import org.olat.modules.adobeconnect.AdobeConnectMeeting; +import org.olat.modules.adobeconnect.AdobeConnectModule; import org.olat.modules.adobeconnect.model.AdobeConnectErrors; import org.olat.modules.adobeconnect.ui.AdobeConnectMeetingTableModel.ACMeetingsCols; import org.olat.modules.gotomeeting.ui.GoToMeetingTableModel.MeetingsCols; @@ -63,6 +64,8 @@ public class AdobeConnectAdminMeetingsController extends FormBasicController { private DialogBoxController confirmDelete; + @Autowired + private AdobeConnectModule adobeConnectModule; @Autowired private AdobeConnectManager adobeConnectManager; @@ -76,6 +79,9 @@ public class AdobeConnectAdminMeetingsController extends FormBasicController { protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { FlexiTableColumnModel columnsModel = FlexiTableDataModelFactory.createFlexiTableColumnModel(); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(ACMeetingsCols.name)); + if(!adobeConnectModule.isSingleMeetingMode()) { + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(ACMeetingsCols.permanent)); + } columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(ACMeetingsCols.start)); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(ACMeetingsCols.end)); FlexiCellRenderer renderer = new StaticFlexiCellRenderer("resource", new TextFlexiCellRenderer()); diff --git a/src/main/java/org/olat/modules/adobeconnect/ui/AdobeConnectConfigurationController.java b/src/main/java/org/olat/modules/adobeconnect/ui/AdobeConnectConfigurationController.java index 4fa2ce0035b..7715865eafd 100644 --- a/src/main/java/org/olat/modules/adobeconnect/ui/AdobeConnectConfigurationController.java +++ b/src/main/java/org/olat/modules/adobeconnect/ui/AdobeConnectConfigurationController.java @@ -21,6 +21,7 @@ package org.olat.modules.adobeconnect.ui; import java.net.URI; import java.net.URISyntaxException; +import java.util.Arrays; import java.util.List; import org.olat.collaboration.CollaborationToolsFactory; @@ -54,14 +55,21 @@ import org.springframework.beans.factory.annotation.Autowired; */ public class AdobeConnectConfigurationController extends FormBasicController { + private static final String[] CLEAN_KEYS = { "-", "1", "2", "3", "4", "5", "7", "14", "21", "30" }; + private static final String[] CREATE_KEYS = { "immediately", "differed" }; + private static final String[] SINGLE_KEYS = { "single", "perdate" }; private static final String PLACEHOLDER = "xxx-placeholder-xxx"; private FormLink checkLink; private TextElement urlEl; private TextElement loginEl; private TextElement passwordEl; + private TextElement accountIdEl; private SpacerElement spacerEl; private SingleSelection providerEl; + private SingleSelection cleanMeetingsEl; + private SingleSelection createMeetingEl; + private SingleSelection singleMeetingEl; private MultipleSelectionElement moduleEnabled; private static final String[] enabledKeys = new String[]{"on"}; @@ -129,6 +137,43 @@ public class AdobeConnectConfigurationController extends FormBasicController { passwordEl = uifactory.addPasswordElement("aconnect-password", "option.adminpassword", 32, credential, formLayout); passwordEl.setAutocomplete("new-password"); + String accountId = adobeConnectModule.getAccountId(); + accountIdEl = uifactory.addTextElement("aconnect-id", "option.accountid", 32, accountId, formLayout); + accountIdEl.setHelpTextKey("option.accountid.explain", null); + + // delete meeting + String[] cleanValues = Arrays.copyOf(CLEAN_KEYS, CLEAN_KEYS.length); + cleanValues[0] = translate("option.dont.clean.meetings"); + cleanMeetingsEl = uifactory.addDropdownSingleselect("option.clean.meetings", formLayout, CLEAN_KEYS, cleanValues); + if(adobeConnectModule.isCleanupMeetings()) { + long days = adobeConnectModule.getDaysToKeep(); + String dayStr = Long.toString(days); + for(String key:CLEAN_KEYS) { + if(dayStr.equals(key)) { + cleanMeetingsEl.select(key, true); + } + } + } else { + cleanMeetingsEl.select(CLEAN_KEYS[0], true); + } + + // create meeting ASAP + String[] createValues = new String[] { translate("option.create.meeting.immediately"), translate("option.create.meeting.differed") }; + createMeetingEl = uifactory.addRadiosHorizontal("option.create.meeting", "option.create.meeting", formLayout, CREATE_KEYS, createValues); + if(adobeConnectModule.isCreateMeetingImmediately()) { + createMeetingEl.select(CREATE_KEYS[0], true); + } else { + createMeetingEl.select(CREATE_KEYS[1], true); + } + + String[] singleMeetingValues = new String[] { translate("option.single.meeting.single"), translate("option.single.meeting.perdate") }; + singleMeetingEl = uifactory.addRadiosHorizontal("option.single.meeting", formLayout, SINGLE_KEYS, singleMeetingValues); + if(adobeConnectModule.isSingleMeetingMode()) { + singleMeetingEl.select(SINGLE_KEYS[0], true); + } else { + singleMeetingEl.select(SINGLE_KEYS[1], true); + } + //buttons save - check FormLayoutContainer buttonLayout = FormLayoutContainer.createButtonLayout("save", getTranslator()); formLayout.add(buttonLayout); @@ -247,6 +292,16 @@ public class AdobeConnectConfigurationController extends FormBasicController { adobeConnectModule.setAdobeConnectURI(new URI(url)); adobeConnectModule.setAdminLogin(loginEl.getValue()); adobeConnectModule.setProviderId(providerEl.getSelectedKey()); + adobeConnectModule.setAccountId(accountIdEl.getValue()); + if(cleanMeetingsEl.isSelected(0)) { + adobeConnectModule.setCleanupMeetings(false); + adobeConnectModule.setDaysToKeep(null); + } else { + adobeConnectModule.setCleanupMeetings(true); + adobeConnectModule.setDaysToKeep(cleanMeetingsEl.getSelectedKey()); + } + adobeConnectModule.setSingleMeetingMode(singleMeetingEl.isSelected(0)); + adobeConnectModule.setCreateMeetingImmediately(createMeetingEl.isSelected(0)); String credential = passwordEl.getValue(); if(!PLACEHOLDER.equals(credential)) { adobeConnectModule.setAdminPassword(credential); @@ -258,6 +313,7 @@ public class AdobeConnectConfigurationController extends FormBasicController { adobeConnectModule.setAdobeConnectURI(null); adobeConnectModule.setAdminLogin(null); adobeConnectModule.setAdminPassword(null); + adobeConnectModule.setAccountId(null); } CollaborationToolsFactory.getInstance().initAvailableTools(); } catch (URISyntaxException e) { diff --git a/src/main/java/org/olat/modules/adobeconnect/ui/AdobeConnectEditMeetingsController.java b/src/main/java/org/olat/modules/adobeconnect/ui/AdobeConnectEditMeetingsController.java index 929491e6f31..593ac41fd02 100644 --- a/src/main/java/org/olat/modules/adobeconnect/ui/AdobeConnectEditMeetingsController.java +++ b/src/main/java/org/olat/modules/adobeconnect/ui/AdobeConnectEditMeetingsController.java @@ -44,6 +44,7 @@ import org.olat.core.gui.control.generic.modal.DialogBoxUIFactory; import org.olat.group.BusinessGroup; import org.olat.modules.adobeconnect.AdobeConnectManager; import org.olat.modules.adobeconnect.AdobeConnectMeeting; +import org.olat.modules.adobeconnect.AdobeConnectModule; import org.olat.modules.adobeconnect.model.AdobeConnectErrors; import org.olat.modules.adobeconnect.ui.AdobeConnectMeetingTableModel.ACMeetingsCols; import org.olat.modules.gotomeeting.ui.GoToMeetingTableModel.MeetingsCols; @@ -72,6 +73,8 @@ public class AdobeConnectEditMeetingsController extends FormBasicController { private final BusinessGroup businessGroup; private final AdobeConnectMeetingDefaultConfiguration configuration; + @Autowired + private AdobeConnectModule adobeConnectModule; @Autowired private AdobeConnectManager adobeConnectManager; @@ -97,6 +100,9 @@ public class AdobeConnectEditMeetingsController extends FormBasicController { //add the table FlexiTableColumnModel columnsModel = FlexiTableDataModelFactory.createFlexiTableColumnModel(); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(ACMeetingsCols.name)); + if(!adobeConnectModule.isSingleMeetingMode()) { + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(ACMeetingsCols.permanent)); + } columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(ACMeetingsCols.start)); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(ACMeetingsCols.end)); if(!readOnly) { diff --git a/src/main/java/org/olat/modules/adobeconnect/ui/AdobeConnectEvent.java b/src/main/java/org/olat/modules/adobeconnect/ui/AdobeConnectEvent.java new file mode 100644 index 00000000000..e18887e4b80 --- /dev/null +++ b/src/main/java/org/olat/modules/adobeconnect/ui/AdobeConnectEvent.java @@ -0,0 +1,53 @@ +/** + * <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.adobeconnect.ui; + +import org.olat.core.util.event.MultiUserEvent; + +/** + * + * Initial date: 4 juil. 2019<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class AdobeConnectEvent extends MultiUserEvent { + + private static final long serialVersionUID = 3199767160246830180L; + + public static final String OPEN_MEETING = "adobe-connect-open-meeting"; + public static final String CREATE_MEETING = "adobe-connect-create-meeting"; + + private final Long meetingKey; + private final Long actingIdentityKey; + + public AdobeConnectEvent(String name, Long meetingKey, Long actingIdentityKey) { + super(name); + this.meetingKey = meetingKey; + this.actingIdentityKey = actingIdentityKey; + } + + public Long getMeetingKey() { + return meetingKey; + } + + public Long getActingIdentityKey() { + return actingIdentityKey; + } +} diff --git a/src/main/java/org/olat/modules/adobeconnect/ui/AdobeConnectMeetingController.java b/src/main/java/org/olat/modules/adobeconnect/ui/AdobeConnectMeetingController.java index 58f5c118179..2bcc7a37702 100644 --- a/src/main/java/org/olat/modules/adobeconnect/ui/AdobeConnectMeetingController.java +++ b/src/main/java/org/olat/modules/adobeconnect/ui/AdobeConnectMeetingController.java @@ -45,8 +45,12 @@ import org.olat.core.gui.control.WindowControl; import org.olat.core.gui.control.generic.closablewrapper.CloseableModalController; import org.olat.core.gui.media.MediaResource; import org.olat.core.gui.media.RedirectMediaResource; +import org.olat.core.id.OLATResourceable; import org.olat.core.util.Formatter; import org.olat.core.util.StringHelper; +import org.olat.core.util.coordinate.CoordinatorManager; +import org.olat.core.util.event.GenericEventListener; +import org.olat.core.util.resource.OresHelper; import org.olat.modules.adobeconnect.AdobeConnectManager; import org.olat.modules.adobeconnect.AdobeConnectMeeting; import org.olat.modules.adobeconnect.AdobeConnectMeetingPermission; @@ -62,7 +66,7 @@ import org.springframework.beans.factory.annotation.Autowired; * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com * */ -public class AdobeConnectMeetingController extends FormBasicController { +public class AdobeConnectMeetingController extends FormBasicController implements GenericEventListener { private final boolean readOnly; private final boolean moderator; @@ -70,11 +74,18 @@ public class AdobeConnectMeetingController extends FormBasicController { private AdobeConnectMeeting meeting; private boolean registered; - private final boolean validMeeting; + private boolean meetingCreated; + private boolean validMeeting; + private final boolean guestsAccess; + private final boolean userMeetingsDates; + private final boolean moderatorStartMeeting; + private final OLATResourceable meetingOres; private int counter; private Link joinButton; + private FormLink registerButton; + private FormLink createMeetingButton; private FormLink sharedDocumentButton; private FlexiTableElement contentTableEl; private AdobeConnectContentTableModel contentModel; @@ -88,17 +99,25 @@ public class AdobeConnectMeetingController extends FormBasicController { private AdobeConnectManager adobeConnectManager; public AdobeConnectMeetingController(UserRequest ureq, WindowControl wControl, - AdobeConnectMeeting meeting, boolean administrator, boolean moderator, boolean readOnly) { + AdobeConnectMeeting meeting, AdobeConnectMeetingDefaultConfiguration configuration, + boolean administrator, boolean moderator, boolean readOnly) { super(ureq, wControl, "meeting"); this.meeting = meeting; this.readOnly = readOnly; this.moderator = moderator; this.administrator = administrator; + meetingOres = OresHelper.createOLATResourceableInstance(AdobeConnectMeeting.class.getSimpleName(), meeting.getKey()); + CoordinatorManager.getInstance().getCoordinator().getEventBus().registerFor(this, getIdentity(), meetingOres); + + meetingCreated = StringHelper.containsNonWhitespace(meeting.getScoId()); validMeeting = adobeConnectModule.getBaseUrl().equals(meeting.getEnvName()); + userMeetingsDates = configuration.isUseMeetingDates(); + moderatorStartMeeting = configuration.isModeratorStartMeeting(); + guestsAccess = configuration.isAllowGuestAccess(); initForm(ureq); - if(validMeeting) { + if(validMeeting && meetingCreated) { AdobeConnectErrors errors = new AdobeConnectErrors(); registered = adobeConnectManager.isRegistered(meeting, getIdentity(), getPermission(), errors); loadModel(); @@ -106,7 +125,7 @@ public class AdobeConnectMeetingController extends FormBasicController { getWindowControl().setWarning(AdobeConnectErrorHelper.formatErrors(getTranslator(), errors)); } } - updateButtons(); + updateButtonsAndStatus(); } @Override @@ -125,18 +144,17 @@ public class AdobeConnectMeetingController extends FormBasicController { if(meeting.getEndDate() != null) { String end = Formatter.getInstance(getLocale()).formatDateAndTime(meeting.getEndDate()); layoutCont.contextPut("end", end); - if(ended) { - layoutCont.contextPut("ended", Boolean.TRUE); - } } - - layoutCont.contextPut("validMeeting", Boolean.valueOf(validMeeting)); } registerButton = uifactory.addFormLink("meeting.register.button", flc, Link.BUTTON); registerButton.setVisible(!ended); + sharedDocumentButton = uifactory.addFormLink("meeting.share.documents", flc, Link.BUTTON); sharedDocumentButton.setVisible(administrator || moderator); + + createMeetingButton = uifactory.addFormLink("meeting.create.button", flc, Link.BUTTON); + createMeetingButton.setVisible(!StringHelper.containsNonWhitespace(meeting.getScoId())); joinButton = LinkFactory.createButtonLarge("meeting.join.button", flc.getFormItemComponent(), this); joinButton.setTarget("_blank"); @@ -149,6 +167,82 @@ public class AdobeConnectMeetingController extends FormBasicController { return meeting != null && meeting.getEndDate() != null && new Date().after(meeting.getEndDate()); } + private boolean isValidDates() { + if(!userMeetingsDates) { + return true; + } + Date now = new Date(); + Date start = meeting.getStartWithLeadTime(); + Date end = meeting.getEndWithFollowupTime(); + + if(start != null && start.compareTo(now) >= 0) { + return false; + } + if(end != null && end.compareTo(now) <= 0) { + return false; + } + return true; + } + + private void reloadButtonsAndStatus() { + meeting = adobeConnectManager.getMeeting(meeting); + updateButtonsAndStatus(); + } + + private void updateButtonsAndStatus() { + boolean invalidProvider = meeting.getEnvName() != null && !meeting.getEnvName().equals(adobeConnectModule.getBaseUrl()); + boolean meetingsExists = StringHelper.containsNonWhitespace(meeting.getScoId()); + boolean isEnded = isEnded(); + + meetingCreated = StringHelper.containsNonWhitespace(meeting.getScoId()); + validMeeting = adobeConnectModule.getBaseUrl().equals(meeting.getEnvName()); + + flc.contextPut("invalidProvider", Boolean.valueOf(invalidProvider)); + flc.contextPut("meetingsExists", Boolean.valueOf(meetingsExists)); + flc.contextPut("ended", Boolean.valueOf(isEnded)); + + boolean accessible = !isEnded() || administrator || moderator; + boolean canCreate = !StringHelper.containsNonWhitespace(meeting.getScoId()) + && (administrator || moderator); + createMeetingButton.setVisible(canCreate); + + if(canCreate) { + registerButton.setVisible(false); + joinButton.setVisible(false); + } else if(moderator || administrator) { + registerButton.setVisible(false); + + joinButton.setVisible(accessible); + joinButton.setEnabled(!readOnly && validMeeting); + + if(!meeting.isOpened() && moderatorStartMeeting) { + joinButton.setCustomDisplayText(translate("meeting.start.button")); + } else if(isValidDates()) { + joinButton.setCustomDisplayText(translate("meeting.join.button")); + } else { + joinButton.setCustomDisplayText(translate("meeting.go.button")); + } + } else { + registerButton.setVisible(accessible && !registered && !readOnly && validMeeting); + boolean validDates = isValidDates(); + + joinButton.setVisible(accessible && registered); + if(!meeting.isOpened() && moderatorStartMeeting) { + joinButton.setEnabled(false); + } else { + joinButton.setEnabled(!readOnly && validMeeting && validDates); + } + + if(validDates && !meeting.isOpened() && moderatorStartMeeting) { + flc.contextPut("notStarted", Boolean.TRUE); + } else if(validDates || isEnded) { + flc.contextPut("notStarted", Boolean.FALSE); + } else { + flc.contextPut("notStarted", Boolean.TRUE); + } + } + } + protected void initContent(FormItemContainer formLayout) { FlexiTableColumnModel columnsModel = FlexiTableDataModelFactory.createFlexiTableColumnModel(); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(ACContentsCols.icon, new AdobeConnectIconRenderer())); @@ -183,7 +277,7 @@ public class AdobeConnectMeetingController extends FormBasicController { } contentModel.setObjects(rows); contentTableEl.reset(true, true, true); - contentTableEl.setVisible(true); + contentTableEl.setVisible(!rows.isEmpty() || administrator || moderator); sharedDocumentButton.setVisible(!scos.isEmpty() && (administrator || moderator)); flc.contextPut("notRegistered", Boolean.valueOf(!registered)); } else { @@ -191,17 +285,20 @@ public class AdobeConnectMeetingController extends FormBasicController { sharedDocumentButton.setVisible(false); } } - - private void updateButtons() { - boolean accessible = !isEnded() || administrator || moderator; - registerButton.setVisible(accessible && !registered && !readOnly && validMeeting); - joinButton.setVisible(accessible && registered); - joinButton.setEnabled(!readOnly && validMeeting); - } @Override protected void doDispose() { - // + CoordinatorManager.getInstance().getCoordinator().getEventBus().deregisterFor(this, meetingOres); + } + + @Override + public void event(Event event) { + if(event instanceof AdobeConnectEvent) { + AdobeConnectEvent ace = (AdobeConnectEvent)event; + if(ace.getMeetingKey() != null && ace.getMeetingKey().equals(meeting.getKey())) { + reloadButtonsAndStatus(); + } + } } @Override @@ -245,18 +342,51 @@ public class AdobeConnectMeetingController extends FormBasicController { doRegister(); } else if(sharedDocumentButton == source) { doShareDocuments(ureq); + } else if(createMeetingButton == source) { + doCreateMeeting(ureq); } super.formInnerEvent(ureq, source, event); } - private void doJoin(UserRequest ureq) { + private void doCreateMeeting(UserRequest ureq) { + meeting = adobeConnectManager.getMeeting(meeting); AdobeConnectErrors errors = new AdobeConnectErrors(); - String meetingUrl = adobeConnectManager.join(meeting, getIdentity(), errors); + meeting = adobeConnectManager.createAdobeMeeting(meeting, getLocale(), guestsAccess, errors); + updateButtonsAndStatus(); + if(errors.hasErrors()) { getWindowControl().setError(AdobeConnectErrorHelper.formatErrors(getTranslator(), errors)); } else { + AdobeConnectEvent createEvent = new AdobeConnectEvent(AdobeConnectEvent.CREATE_MEETING, meeting.getKey(), getIdentity().getKey()); + CoordinatorManager.getInstance().getCoordinator().getEventBus().fireEventToListenersOf(createEvent, meetingOres); + } + } + + private void doJoin(UserRequest ureq) { + meeting = adobeConnectManager.getMeeting(meeting); + if(meeting == null) { + showWarning("warning.no.meeting"); + fireEvent(ureq, Event.BACK_EVENT); + return; + } + + String meetingUrl = null; + AdobeConnectErrors errors = new AdobeConnectErrors(); + if(meeting.isOpened() || !moderatorStartMeeting) { + meetingUrl = adobeConnectManager.join(meeting, getIdentity(), errors); + } else if(moderator || administrator) { + meetingUrl = adobeConnectManager.open(meeting, getIdentity(), errors); + AdobeConnectEvent openEvent = new AdobeConnectEvent(AdobeConnectEvent.OPEN_MEETING, meeting.getKey(), getIdentity().getKey()); + CoordinatorManager.getInstance().getCoordinator().getEventBus().fireEventToListenersOf(openEvent, meetingOres); + } + + if(errors.hasErrors()) { + getWindowControl().setError(AdobeConnectErrorHelper.formatErrors(getTranslator(), errors)); + } else if(StringHelper.containsNonWhitespace(meetingUrl)) { MediaResource redirect = new RedirectMediaResource(meetingUrl); ureq.getDispatchResult().setResultingMediaResource(redirect); + } else { + showWarning("warning.no.access"); } } @@ -265,7 +395,7 @@ public class AdobeConnectMeetingController extends FormBasicController { registered = adobeConnectManager.registerFor(meeting, getIdentity(), getPermission(), errors); if(registered) { showInfo("meeting.successfully.registered"); - updateButtons(); + reloadButtonsAndStatus(); } else if(errors.hasErrors()) { getWindowControl().setError(AdobeConnectErrorHelper.formatErrors(getTranslator(), errors)); } else { diff --git a/src/main/java/org/olat/modules/adobeconnect/ui/AdobeConnectMeetingDefaultConfiguration.java b/src/main/java/org/olat/modules/adobeconnect/ui/AdobeConnectMeetingDefaultConfiguration.java index 72ce6eb1816..4aacd6c4d82 100644 --- a/src/main/java/org/olat/modules/adobeconnect/ui/AdobeConnectMeetingDefaultConfiguration.java +++ b/src/main/java/org/olat/modules/adobeconnect/ui/AdobeConnectMeetingDefaultConfiguration.java @@ -27,14 +27,26 @@ package org.olat.modules.adobeconnect.ui; */ public class AdobeConnectMeetingDefaultConfiguration { + private final boolean useMeetingDates; private final boolean allowGuestAccess; + private final boolean moderatorStartMeeting; - public AdobeConnectMeetingDefaultConfiguration(boolean allowGuestAccess) { + public AdobeConnectMeetingDefaultConfiguration(boolean useMeetingDates, + boolean allowGuestAccess, boolean moderatorStartMeeting) { this.allowGuestAccess = allowGuestAccess; + this.useMeetingDates = useMeetingDates; + this.moderatorStartMeeting = moderatorStartMeeting; } public boolean isAllowGuestAccess() { return allowGuestAccess; } + public boolean isUseMeetingDates() { + return useMeetingDates; + } + + public boolean isModeratorStartMeeting() { + return moderatorStartMeeting; + } } diff --git a/src/main/java/org/olat/modules/adobeconnect/ui/AdobeConnectMeetingTableModel.java b/src/main/java/org/olat/modules/adobeconnect/ui/AdobeConnectMeetingTableModel.java index 5d9d53efc6b..e0857a6820d 100644 --- a/src/main/java/org/olat/modules/adobeconnect/ui/AdobeConnectMeetingTableModel.java +++ b/src/main/java/org/olat/modules/adobeconnect/ui/AdobeConnectMeetingTableModel.java @@ -64,6 +64,7 @@ implements SortableFlexiTableDataModel<AdobeConnectMeeting> { public Object getValueAt(AdobeConnectMeeting row, int col) { switch(ACMeetingsCols.values()[col]) { case name: return row.getName(); + case permanent: return row.isPermanent(); case start: return row.getStartDate(); case end: return row.getEndDate(); case resource: return getResourceName(row); @@ -89,6 +90,7 @@ implements SortableFlexiTableDataModel<AdobeConnectMeeting> { public enum ACMeetingsCols implements FlexiSortableColumnDef { name("meeting.name"), + permanent("table.header.permanent"), start("meeting.start"), end("meeting.end"), resource("meeting.resource"); diff --git a/src/main/java/org/olat/modules/adobeconnect/ui/AdobeConnectMeetingsController.java b/src/main/java/org/olat/modules/adobeconnect/ui/AdobeConnectMeetingsController.java index 09fed45ec1f..81580908dc5 100644 --- a/src/main/java/org/olat/modules/adobeconnect/ui/AdobeConnectMeetingsController.java +++ b/src/main/java/org/olat/modules/adobeconnect/ui/AdobeConnectMeetingsController.java @@ -41,6 +41,7 @@ import org.olat.core.gui.control.WindowControl; import org.olat.group.BusinessGroup; import org.olat.modules.adobeconnect.AdobeConnectManager; import org.olat.modules.adobeconnect.AdobeConnectMeeting; +import org.olat.modules.adobeconnect.AdobeConnectModule; import org.olat.modules.adobeconnect.ui.AdobeConnectMeetingTableModel.ACMeetingsCols; import org.olat.repository.RepositoryEntry; import org.springframework.beans.factory.annotation.Autowired; @@ -61,16 +62,20 @@ public class AdobeConnectMeetingsController extends FormBasicController { private final RepositoryEntry entry; private final String subIdent; private final BusinessGroup businessGroup; + private final boolean showPermanentCol; + @Autowired + private AdobeConnectModule adobeConnectModule; @Autowired private AdobeConnectManager adobeConnectManager; public AdobeConnectMeetingsController(UserRequest ureq, WindowControl wControl, RepositoryEntry entry, String subIdent, - BusinessGroup businessGroup) { + BusinessGroup businessGroup, boolean administrator, boolean moderator) { super(ureq, wControl, "meetings"); this.entry = entry; this.subIdent = subIdent; this.businessGroup = businessGroup; + showPermanentCol = (administrator || moderator) && !adobeConnectModule.isSingleMeetingMode(); initForm(ureq); updateModel(); @@ -81,6 +86,9 @@ public class AdobeConnectMeetingsController extends FormBasicController { // upcoming meetings table FlexiTableColumnModel columnsModel = FlexiTableDataModelFactory.createFlexiTableColumnModel(); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(ACMeetingsCols.name)); + if(showPermanentCol) { + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(ACMeetingsCols.permanent)); + } columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(ACMeetingsCols.start)); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(ACMeetingsCols.end)); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel("select", translate("select"), "select")); @@ -97,6 +105,9 @@ public class AdobeConnectMeetingsController extends FormBasicController { // past meetings FlexiTableColumnModel pastColumnsModel = FlexiTableDataModelFactory.createFlexiTableColumnModel(); pastColumnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(ACMeetingsCols.name)); + if(showPermanentCol) { + pastColumnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(ACMeetingsCols.permanent)); + } pastColumnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(ACMeetingsCols.start)); pastColumnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(ACMeetingsCols.end)); pastColumnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel("select", translate("select"), "select")); diff --git a/src/main/java/org/olat/modules/adobeconnect/ui/AdobeConnectRunController.java b/src/main/java/org/olat/modules/adobeconnect/ui/AdobeConnectRunController.java index 75f72407de9..302f6feb3bb 100644 --- a/src/main/java/org/olat/modules/adobeconnect/ui/AdobeConnectRunController.java +++ b/src/main/java/org/olat/modules/adobeconnect/ui/AdobeConnectRunController.java @@ -101,7 +101,7 @@ public class AdobeConnectRunController extends BasicController implements Activa doOpenMeetings(ureq); } else { mainVC = createVelocityContainer("run"); - meetingsCtrl = new AdobeConnectMeetingsController(ureq, wControl, entry, subIdent, group); + meetingsCtrl = new AdobeConnectMeetingsController(ureq, wControl, entry, subIdent, group, admin, moderator); listenTo(meetingsCtrl); mainVC.put("meetings", meetingsCtrl.getInitialComponent()); } @@ -123,14 +123,23 @@ public class AdobeConnectRunController extends BasicController implements Activa if("Meetings".equalsIgnoreCase(type)) { if(canView) { doOpenMeetings(ureq); + if(segmentView != null) { + segmentView.select(meetingsLink); + } } } else if("Administration".equalsIgnoreCase(type)) { if(administrator) { doOpenAdmin(ureq); + if(segmentView != null) { + segmentView.select(adminLink); + } } } else if("Meeting".equalsIgnoreCase(type)) { if(canView) { doOpenMeetings(ureq); + if(segmentView != null) { + segmentView.select(meetingsLink); + } Long meetingKey = entries.get(0).getOLATResourceable().getResourceableId(); if(meetingsCtrl.hasMeetingByKey(meetingKey)) { doSelectMeeting(ureq, meetingsCtrl.getMeetingByKey(meetingKey)); @@ -142,9 +151,7 @@ public class AdobeConnectRunController extends BasicController implements Activa @Override protected void event(UserRequest ureq, Component source, Event event) { if(backLink == source) { - mainVC.remove(meetingCtrl.getInitialComponent()); - removeAsListenerAndDispose(meetingCtrl); - meetingCtrl = null; + back(); } else if(source == segmentView) { if(event instanceof SegmentViewEvent) { SegmentViewEvent sve = (SegmentViewEvent)event; @@ -166,14 +173,24 @@ public class AdobeConnectRunController extends BasicController implements Activa SelectAdobeConnectMeetingEvent se = (SelectAdobeConnectMeetingEvent)event; doSelectMeeting(ureq, se.getMeeting()); } + } else if(meetingCtrl == source) { + if(event == Event.BACK_EVENT) { + back(); + } } } + private void back() { + mainVC.remove(meetingCtrl.getInitialComponent()); + removeAsListenerAndDispose(meetingCtrl); + meetingCtrl = null; + } + private void doOpenMeetings(UserRequest ureq) { if(meetingsCtrl == null) { WindowControl bwControl = addToHistory(ureq, OresHelper.createOLATResourceableInstance("Meetings", 0l), null); meetingsCtrl = new AdobeConnectMeetingsController(ureq, bwControl, - entry, subIdent, group); + entry, subIdent, group, administrator, moderator); listenTo(meetingsCtrl); } else { meetingsCtrl.updateModel(); @@ -196,10 +213,15 @@ public class AdobeConnectRunController extends BasicController implements Activa private void doSelectMeeting(UserRequest ureq, AdobeConnectMeeting meeting) { removeAsListenerAndDispose(meetingCtrl); - - WindowControl bwControl = addToHistory(ureq, OresHelper.createOLATResourceableInstance("Meeting", meeting.getKey()), null); - meetingCtrl = new AdobeConnectMeetingController(ureq, bwControl, meeting, administrator, moderator, readOnly); - listenTo(meetingCtrl); - mainVC.put("meeting", meetingCtrl.getInitialComponent()); + meetingCtrl = null; + + if(meeting == null) { + showWarning("warning.no.meeting"); + } else { + WindowControl bwControl = addToHistory(ureq, OresHelper.createOLATResourceableInstance("Meeting", meeting.getKey()), null); + meetingCtrl = new AdobeConnectMeetingController(ureq, bwControl, meeting, configuration, administrator, moderator, readOnly); + listenTo(meetingCtrl); + mainVC.put("meeting", meetingCtrl.getInitialComponent()); + } } } diff --git a/src/main/java/org/olat/modules/adobeconnect/ui/AdobeConnectShareDocumentsController.java b/src/main/java/org/olat/modules/adobeconnect/ui/AdobeConnectShareDocumentsController.java index 04ec13d54ac..8e11d22fa9c 100644 --- a/src/main/java/org/olat/modules/adobeconnect/ui/AdobeConnectShareDocumentsController.java +++ b/src/main/java/org/olat/modules/adobeconnect/ui/AdobeConnectShareDocumentsController.java @@ -1,3 +1,22 @@ +/** + * <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.adobeconnect.ui; import java.util.ArrayList; diff --git a/src/main/java/org/olat/modules/adobeconnect/ui/EditAdobeConnectMeetingController.java b/src/main/java/org/olat/modules/adobeconnect/ui/EditAdobeConnectMeetingController.java index bf55f5577d9..a8e1517c333 100644 --- a/src/main/java/org/olat/modules/adobeconnect/ui/EditAdobeConnectMeetingController.java +++ b/src/main/java/org/olat/modules/adobeconnect/ui/EditAdobeConnectMeetingController.java @@ -39,6 +39,7 @@ import org.olat.core.util.StringHelper; import org.olat.group.BusinessGroup; import org.olat.modules.adobeconnect.AdobeConnectManager; import org.olat.modules.adobeconnect.AdobeConnectMeeting; +import org.olat.modules.adobeconnect.AdobeConnectModule; import org.olat.modules.adobeconnect.model.AdobeConnectErrors; import org.olat.modules.adobeconnect.model.AdobeConnectSco; import org.olat.repository.RepositoryEntry; @@ -56,6 +57,8 @@ public class EditAdobeConnectMeetingController extends FormBasicController { private TextElement nameEl; private TextElement descriptionEl; + private TextElement leadTimeEl; + private TextElement followupTimeEl; private DateChooser startDateEl; private DateChooser endDateEl; private MultipleSelectionElement permanentEl; @@ -67,6 +70,8 @@ public class EditAdobeConnectMeetingController extends FormBasicController { private final AdobeConnectMeetingDefaultConfiguration configuration; private AdobeConnectMeeting meeting; + @Autowired + private AdobeConnectModule adobeConnectModule; @Autowired private AdobeConnectManager adobeConnectManager; @@ -99,6 +104,10 @@ public class EditAdobeConnectMeetingController extends FormBasicController { String name = meeting == null ? "" : meeting.getName(); nameEl = uifactory.addTextElement("meeting.name", "meeting.name", 128, name, formLayout); nameEl.setMandatory(true); + if(!StringHelper.containsNonWhitespace(name)) { + nameEl.setFocus(true); + } + String description = meeting == null ? "" : meeting.getDescription(); descriptionEl = uifactory.addTextAreaElement("meeting.description", "meeting.description", 2000, 8, 72, false, false, description, formLayout); @@ -118,17 +127,32 @@ public class EditAdobeConnectMeetingController extends FormBasicController { String[] permValues = new String[] { translate("meeting.permanent.on") }; permanentEl = uifactory.addCheckboxesHorizontal("meeting.permanent", formLayout, permKeys, permValues); permanentEl.addActionListener(FormEvent.ONCHANGE); + boolean permanent = meeting == null ? adobeConnectModule.isSingleMeetingMode() : meeting.isPermanent(); + permanentEl.select(permKeys[0], permanent); + boolean permanentDisabled = adobeConnectModule.isCreateMeetingImmediately() + || (meeting != null && StringHelper.containsNonWhitespace(meeting.getScoId())); + // if single mode -> always permanent + // if sco linked -> cannot changed it + permanentEl.setEnabled(!permanentDisabled); + permanentEl.setHelpTextKey("meeting.permanent.explain", null); Date startDate = meeting == null ? null : meeting.getStartDate(); startDateEl = uifactory.addDateChooser("meeting.start", "meeting.start", startDate, formLayout); startDateEl.setMandatory(true); startDateEl.setDateChooserTimeEnabled(true); + + String leadtime = meeting == null ? null : Long.toString(meeting.getLeadTime()); + leadTimeEl = uifactory.addTextElement("meeting.leadTime", 8, leadtime, formLayout); + Date endDate = meeting == null ? null : meeting.getEndDate(); endDateEl = uifactory.addDateChooser("meeting.end", "meeting.end", endDate, formLayout); endDateEl.setMandatory(true); endDateEl.setDefaultValue(startDateEl); endDateEl.setDateChooserTimeEnabled(true); + String followup = meeting == null ? null : Long.toString(meeting.getFollowupTime()); + followupTimeEl = uifactory.addTextElement("meeting.followupTime", 8, followup, formLayout); + FormLayoutContainer buttonLayout = FormLayoutContainer.createButtonLayout("buttons", getTranslator()); formLayout.add("buttons", buttonLayout); uifactory.addFormCancelButton("cancel", buttonLayout, ureq, getWindowControl()); @@ -137,8 +161,8 @@ public class EditAdobeConnectMeetingController extends FormBasicController { private void updateUI() { boolean permanent = permanentEl.isAtLeastSelected(1); - startDateEl.setVisible(!permanent); - endDateEl.setVisible(!permanent); + startDateEl.setMandatory(!permanent); + endDateEl.setMandatory(!permanent); } @Override @@ -178,6 +202,18 @@ public class EditAdobeConnectMeetingController extends FormBasicController { } } + allOk &= validateTime(leadTimeEl); + allOk &= validateTime(followupTimeEl); + return allOk; + } + + private boolean validateTime(TextElement el) { + boolean allOk = true; + el.clearError(); + if(StringHelper.containsNonWhitespace(el.getValue()) && !StringHelper.isLong(el.getValue())) { + el.setErrorKey("form.error.nointeger", null); + allOk &= false; + } return allOk; } @@ -193,23 +229,36 @@ public class EditAdobeConnectMeetingController extends FormBasicController { protected void formOK(UserRequest ureq) { AdobeConnectErrors errors = new AdobeConnectErrors(); - Date startDate = null; - Date endDate = null; - if(!permanentEl.isAtLeastSelected(1)) { - startDate = startDateEl.getDate(); - endDate = endDateEl.getDate(); + Date startDate = startDateEl.getDate(); + Date endDate = endDateEl.getDate(); + boolean permanent; + if(permanentEl.isVisible()) { + permanent = permanentEl.isAtLeastSelected(1); + } else { + permanent = true; + } + + long leadTime = 0; + if(leadTimeEl.isVisible() && StringHelper.isLong(leadTimeEl.getValue())) { + leadTime = Long.valueOf(leadTimeEl.getValue()); } + long followupTime = 0; + if(followupTimeEl.isVisible() && StringHelper.isLong(followupTimeEl.getValue())) { + followupTime = Long.valueOf(followupTimeEl.getValue()); + } + String templateId = null; if(templateEl.isVisible() && StringHelper.containsNonWhitespace(templateEl.getSelectedKey())) { templateId = templateEl.getSelectedKey(); } - + if(meeting == null) { - adobeConnectManager.createMeeting(nameEl.getValue(), descriptionEl.getValue(), templateId, - startDate, endDate, getLocale(), configuration.isAllowGuestAccess(), entry, subIdent, businessGroup, getIdentity(), errors); + adobeConnectManager.createMeeting(nameEl.getValue(), descriptionEl.getValue(), templateId, permanent, + startDate, leadTime, endDate, followupTime, getLocale(), configuration.isAllowGuestAccess(), + entry, subIdent, businessGroup, getIdentity(), errors); } else { adobeConnectManager.updateMeeting(meeting, nameEl.getValue(), descriptionEl.getValue(), - templateId, startDate, endDate, errors); + templateId, permanent, startDate, leadTime, endDate, followupTime, errors); } if(errors.hasErrors()) { diff --git a/src/main/java/org/olat/modules/adobeconnect/ui/_content/meeting.html b/src/main/java/org/olat/modules/adobeconnect/ui/_content/meeting.html index b582aed5942..4cfe9e672ff 100644 --- a/src/main/java/org/olat/modules/adobeconnect/ui/_content/meeting.html +++ b/src/main/java/org/olat/modules/adobeconnect/ui/_content/meeting.html @@ -11,32 +11,42 @@ #if($r.isTrue($ended)) <div class="o_block_large o_warning">$r.translate("meeting.ended")</div> #end -#if($r.isFalse($validMeeting)) - <div class="o_block_large o_error">$r.translate("error.invalid.meeting")</div> -#end -<div class="o_button_group"> -#if($r.available("meeting.start.button") && $r.visible("meeting.start.button")) - $r.render("meeting.start.button") -#end -#if($r.available("meeting.register.button") && $r.visible("meeting.register.button")) - $r.render("meeting.register.button") -#end -#if($r.available("meeting.join.button") && $r.visible("meeting.join.button")) - $r.render("meeting.join.button") -#end -</div> -#if($r.visible("meeting.share.documents") || $r.visible("meetingContents")) -<fieldset> - <legend>$r.translate("meetings.content")</legend> - #if($r.visible("meeting.share.documents")) - <div class="o_button_group o_button_group_right"> - $r.render("meeting.share.documents") - </div> +#if($r.isTrue($invalidProvider)) + <div class="o_block_large o_error">$r.translate("error.invalid.meeting")</div> +#else + #if($r.isFalse($meetingsExists) || $r.isTrue($notStarted)) + <div class="o_block_large o_info">$r.translate("meeting.create.intro")</div> #end - #if($r.isTrue($notRegistered)) - <div class="o_warning">$r.translate("warning.not.registered.shared.documents")</div> + + <div class="o_button_group"> + #if($r.available("meeting.create.button") && $r.visible("meeting.create.button")) + $r.render("meeting.create.button") #end - $r.render("meetingContents") -</fieldset> -#end \ No newline at end of file + #if($r.available("meeting.start.button") && $r.visible("meeting.start.button")) + $r.render("meeting.start.button") + #end + #if($r.available("meeting.register.button") && $r.visible("meeting.register.button")) + $r.render("meeting.register.button") + #end + #if($r.available("meeting.join.button") && $r.visible("meeting.join.button")) + $r.render("meeting.join.button") + #end + </div> + + #if($r.isTrue($meetingsExists) && ($r.visible("meeting.share.documents") || $r.visible("meetingContents"))) + <fieldset> + <legend>$r.translate("meetings.content")</legend> + #if($r.visible("meeting.share.documents")) + <div class="o_button_group o_button_group_right"> + $r.render("meeting.share.documents") + </div> + #end + #if($r.isTrue($notRegistered)) + <div class="o_warning">$r.translate("warning.not.registered.shared.documents")</div> + #end + $r.render("meetingContents") + </fieldset> + #end +#end + diff --git a/src/main/java/org/olat/modules/adobeconnect/ui/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/modules/adobeconnect/ui/_i18n/LocalStrings_de.properties index 7e72617918b..385f08bc21b 100644 --- a/src/main/java/org/olat/modules/adobeconnect/ui/_i18n/LocalStrings_de.properties +++ b/src/main/java/org/olat/modules/adobeconnect/ui/_i18n/LocalStrings_de.properties @@ -33,14 +33,20 @@ error.prefix=Ein Fehler ist aufgetreten\: error.rangeError=Datumsbereich ist nicht g\u00FCltig. error.start.after.end=Das Datum f\u00FCr das Ende des Meetings darf nicht vor dem Beginn Datum sein. error.unkown=Unerwartete Fehler +meeting.create.intro=Das Meeting wurde noch nicht er\u00f6ffnet. Teilnehmer k\u00F6nnen den Raum f\u00fcr ein geplantes Meeting ggf. nicht betreten. +meeting.create.button=Meeting er\u00f6ffnen meeting.deleted=Meeting wurde gel\u00F6scht. meeting.description=Beschreibung meeting.end=Enddatum meeting.ended=Meeting ist schon beendet. meeting.join.button=Meeting beitreten +meeting.followupTime=Nachlaufzeit (Min.) +meeting.leadTime=Vorlaufzeit (Min.) +meeting.go.button=Zum Meeting Raum gehen meeting.name=Name meeting.permanent=Typ meeting.permanent.on=Dauernd +meeting.permanent.explain=Dauernd Meetings werden den gleichen Meeting Raum in einem Kursbaustein oder ein Gruppe teilen. meeting.register.button=Anmelden meeting.resource=Resource meeting.share.documents=Dateien bereitstellen @@ -59,8 +65,21 @@ no.meeting.configured=Es sind noch keine Meetings konfiguriert. no.shared.contents=Keine Meeting Dateien verf\u00FCgbar. no.template=Keine Vorlage no.upcoming.meetings=Sie haben keine zuk\u00FCnftigen Meetings. +option.accountid=Konto ID +option.accountid.explain=Konto ID ist nicht erfolderlich. Wenn Konto ID nicht spezifieziert ist, wird der Konto von dem oben definierten Benutzer ben\u00FCtzt. option.adminlogin=Benutzername option.adminpassword=Passwort option.baseurl=URL Adobe Connect Server option.baseurl.example=https\://meet73287594.adobeconnect.com/api/xml +option.clean.meetings=Meetings aufra\u00FCmen (Tage) +option.dont.clean.meetings=Nie +option.create.meeting=Erstellungs von Meetings +option.create.meeting.immediately=Sofort +option.create.meeting.differed=Differed +option.single.meeting=Verteilte Meetings +option.single.meeting.single=Erstellt nur ein Meeting Raum pro Kursbaustein oder Gruppe +option.single.meeting.perdate=Erstellt ein Meeting Raum pro Datum +table.header.permanent=Dauernd +warning.no.access=Sie k\u00F6nnen noch nicht den Meeting beitreten. +warning.no.meeting=Das Meeting wurde gel\u00F6scht. warning.not.registered.shared.documents=Nur die Personen die sich an den Meeting angemeldet haben d\u00FCrfen die Dokumenten ansehen. diff --git a/src/main/java/org/olat/modules/adobeconnect/ui/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/modules/adobeconnect/ui/_i18n/LocalStrings_en.properties index 76c30e7a3ae..662b7e301a1 100644 --- a/src/main/java/org/olat/modules/adobeconnect/ui/_i18n/LocalStrings_en.properties +++ b/src/main/java/org/olat/modules/adobeconnect/ui/_i18n/LocalStrings_en.properties @@ -33,14 +33,20 @@ error.prefix=An error happened\: error.rangeError=The range is not valid. error.start.after.end=The end date of the meeting must not be before the start date. error.unkown=Unkown error +meeting.create.button=Open the meeting +meeting.create.intro=The meeting has not been opened, yet. Participants are not able to enter the classroom for a meeting. meeting.deleted=Meeting successfully deleted. meeting.description=Description meeting.end=End date meeting.ended=Meeting already ended. meeting.join.button=Join the meeting +meeting.leadTime=Prep time (min.) +meeting.followupTime=Follow-up (min.) +meeting.go.button=Go to the meeting room meeting.name=Name meeting.permanent=Typ -meeting.permanent.on=Dauernd +meeting.permanent.on=Permanent +meeting.permanent.explain=Permanent meetings will share the same meeting room within a course element or a group. meeting.register.button=Register meeting.resource=Resource meeting.share.documents=Share files @@ -56,11 +62,24 @@ meetings.title=Meetings meetings.upcoming=Upcoming meetings no.contents=This meeting doesn't have any content no.meeting.configured=No meetings have been configured yet. -no.shared.contents=No future meetings scheduled. +no.shared.contents=No shared documents available. no.template=No template no.upcoming.meetings=You don't have any upcoming meeting. +option.accountid=Account ID +option.accountid.explain=Account ID is optional. If the account ID is not specified, the account of the user above will be used. option.adminlogin=Username option.adminpassword=Password option.baseurl=URL Adobe Connect Server option.baseurl.example=https\://meet73287594.adobeconnect.com/api/xml +option.clean.meetings=Clean-up meetings (days) +option.dont.clean.meetings=Never +option.create.meeting=Create meetings +option.create.meeting.immediately=Immediately +option.create.meeting.differed=Differed +option.single.meeting=Shared meetings +option.single.meeting.single=Create only one meeting room per course element or group +option.single.meeting.perdate=Create a meeting room per date +table.header.permanent=Permanent +warning.no.access=You cannot access the meeting yet. +warning.no.meeting=The meeting has been deleted. warning.not.registered.shared.documents=Only the persons which attended the meeting are allowed to open the shared documents. diff --git a/src/main/java/org/olat/modules/adobeconnect/ui/_i18n/LocalStrings_fr.properties b/src/main/java/org/olat/modules/adobeconnect/ui/_i18n/LocalStrings_fr.properties index ed9ca1242bb..5ae779e3b78 100644 --- a/src/main/java/org/olat/modules/adobeconnect/ui/_i18n/LocalStrings_fr.properties +++ b/src/main/java/org/olat/modules/adobeconnect/ui/_i18n/LocalStrings_fr.properties @@ -33,11 +33,15 @@ error.prefix=Une erreur s'est produite\: error.rangeError=L'intervalle n'est pas valide. error.start.after.end=La date de fin du rendez-vous ne peut se trouver avant la date de d\u00E9but. error.unkown=Erreur inconnue +meeting.create.button=Ouvrir le meeting +meeting.create.intro=Le meeting n'est pas encore disponible. Les participants \u00E0 cette classe ne peuvent pas encore y acc\u00E9der. meeting.deleted=Le meeting a \u00E9t\u00E9 effac\u00E9 avec succ\u00E8s. meeting.description=Description meeting.end=Date de fin meeting.ended=Le meeting est termin\u00E9. meeting.join.button=Rejoindre le meeting +meeting.followupTime=P\u00E9riode de temporisation (min.) +meeting.leadTime=Pr\u00E9paration (min.) meeting.name=Nom meeting.permanent=Type meeting.permanent.on=Permanent @@ -56,11 +60,12 @@ meetings.title=Meetings meetings.upcoming=Meetings \u00E0 venir no.contents=Ce meeting n'a pas de contenu. no.meeting.configured=Aucun meeting n'a \u00E9t\u00E9 configur\u00E9 pour l'instant. -no.shared.contents=Pas de meetings pr\u00E9vus \u00E0 l'avenir. +no.shared.contents=Pas de documents partag\u00E9s. no.template=Aucun mod\u00E8le no.upcoming.meetings=Vous n'avez pas meeting pr\u00E9vu \u00E0 l'avenir. option.adminlogin=Nom d'utilisateur option.adminpassword=Mot de passe option.baseurl=URL du serveur Adobe Connect option.baseurl.example=https\://meet73287594.adobeconnect.com/api/xml +warning.no.access=Vous ne pouvez pas encore acc\u00E9der au meeting. warning.not.registered.shared.documents=Seules les personnes qui se sont enregistr\u00E9s peuvent voir les documents partag\u00E9s. diff --git a/src/main/java/org/olat/upgrade/_spring/databaseUpgradeContext.xml b/src/main/java/org/olat/upgrade/_spring/databaseUpgradeContext.xml index 0085fd554ec..f0129fe3c21 100644 --- a/src/main/java/org/olat/upgrade/_spring/databaseUpgradeContext.xml +++ b/src/main/java/org/olat/upgrade/_spring/databaseUpgradeContext.xml @@ -188,6 +188,10 @@ <constructor-arg index="0" value="OLAT_14.0.0" /> <property name="alterDbStatements" value="alter_13_2_x_to_14_0_0.sql" /> </bean> + <bean id="database_upgrade_14_0_1" class="org.olat.upgrade.DatabaseUpgrade"> + <constructor-arg index="0" value="OLAT_14.0.1" /> + <property name="alterDbStatements" value="alter_14_0_0_to_14_0_1.sql" /> + </bean> </list> </property> </bean> diff --git a/src/main/resources/database/mysql/alter_14_0_0_to_14_0_1.sql b/src/main/resources/database/mysql/alter_14_0_0_to_14_0_1.sql new file mode 100644 index 00000000000..0a63ae9e9af --- /dev/null +++ b/src/main/resources/database/mysql/alter_14_0_0_to_14_0_1.sql @@ -0,0 +1,8 @@ +-- Adobe Connect +alter table o_aconnect_meeting add column a_leadtime bigint default 0 not null; +alter table o_aconnect_meeting add column a_start_with_leadtime datetime; +alter table o_aconnect_meeting add column a_followuptime bigint default 0 not null; +alter table o_aconnect_meeting add column a_end_with_followuptime datetime; +alter table o_aconnect_meeting add column a_opened bool default false not null; +alter table o_aconnect_meeting add column a_template_id varchar(32) default null; +alter table o_aconnect_meeting add column a_permanent bool default false not null; \ No newline at end of file diff --git a/src/main/resources/database/mysql/setupDatabase.sql b/src/main/resources/database/mysql/setupDatabase.sql index 2724ce92ba9..e3feb3aa892 100644 --- a/src/main/resources/database/mysql/setupDatabase.sql +++ b/src/main/resources/database/mysql/setupDatabase.sql @@ -1165,8 +1165,15 @@ create table o_aconnect_meeting ( a_env_name varchar(128) default null, a_name varchar(128) not null, a_description varchar(2000) default null, + a_permanent bool default false not null, a_start_date datetime default null, + a_leadtime bigint default 0 not null, + a_start_with_leadtime datetime, a_end_date datetime default null, + a_followuptime bigint default 0 not null, + a_end_with_followuptime datetime, + a_opened bool default false not null, + a_template_id varchar(32) default null, a_shared_documents varchar(2000) default null, fk_entry_id bigint default null, a_sub_ident varchar(64) default null, diff --git a/src/main/resources/database/oracle/alter_14_0_0_to_14_0_1.sql b/src/main/resources/database/oracle/alter_14_0_0_to_14_0_1.sql new file mode 100644 index 00000000000..1c4d9148792 --- /dev/null +++ b/src/main/resources/database/oracle/alter_14_0_0_to_14_0_1.sql @@ -0,0 +1,8 @@ +-- Adobe Connect +alter table o_aconnect_meeting add a_leadtime number(20) default 0 not null; +alter table o_aconnect_meeting add a_start_with_leadtime timestamp; +alter table o_aconnect_meeting add a_followuptime number(20) default 0 not null; +alter table o_aconnect_meeting add a_end_with_followuptime timestamp; +alter table o_aconnect_meeting add a_opened number default 0 not null; +alter table o_aconnect_meeting add a_template_id varchar(32) default null; +alter table o_aconnect_meeting add a_permanent number default 0 not null; diff --git a/src/main/resources/database/oracle/setupDatabase.sql b/src/main/resources/database/oracle/setupDatabase.sql index 8d57a386cf4..e479ae1928e 100644 --- a/src/main/resources/database/oracle/setupDatabase.sql +++ b/src/main/resources/database/oracle/setupDatabase.sql @@ -1230,8 +1230,15 @@ create table o_aconnect_meeting ( a_env_name varchar(128) default null, a_name varchar(128) not null, a_description varchar(2000) default null, + a_permanent number default 0 not null, a_start_date timestamp default null, + a_leadtime number(20) default 0 not null, + a_start_with_leadtime timestamp, a_end_date timestamp default null, + a_followuptime number(20) default 0 not null, + a_end_with_followuptime timestamp, + a_opened number default 0 not null, + a_template_id varchar(32) default null, a_shared_documents varchar(2000) default null, fk_entry_id number(20) default null, a_sub_ident varchar(64) default null, diff --git a/src/main/resources/database/postgresql/alter_14_0_0_to_14_0_1.sql b/src/main/resources/database/postgresql/alter_14_0_0_to_14_0_1.sql new file mode 100644 index 00000000000..df0fd542f10 --- /dev/null +++ b/src/main/resources/database/postgresql/alter_14_0_0_to_14_0_1.sql @@ -0,0 +1,8 @@ +-- Adobe Connect +alter table o_aconnect_meeting add column a_leadtime bigint default 0 not null; +alter table o_aconnect_meeting add column a_start_with_leadtime timestamp; +alter table o_aconnect_meeting add column a_followuptime bigint default 0 not null; +alter table o_aconnect_meeting add column a_end_with_followuptime timestamp; +alter table o_aconnect_meeting add column a_opened bool default false not null; +alter table o_aconnect_meeting add column a_template_id varchar(32) default null; +alter table o_aconnect_meeting add column a_permanent bool default false not null; \ No newline at end of file diff --git a/src/main/resources/database/postgresql/setupDatabase.sql b/src/main/resources/database/postgresql/setupDatabase.sql index 4a192014bbb..b517b27b7d5 100644 --- a/src/main/resources/database/postgresql/setupDatabase.sql +++ b/src/main/resources/database/postgresql/setupDatabase.sql @@ -1190,8 +1190,15 @@ create table o_aconnect_meeting ( a_env_name varchar(128) default null, a_name varchar(128) not null, a_description varchar(2000) default null, + a_permanent bool default false not null, a_start_date timestamp default null, + a_leadtime bigint default 0 not null, + a_start_with_leadtime timestamp, a_end_date timestamp default null, + a_followuptime bigint default 0 not null, + a_end_with_followuptime timestamp, + a_opened bool default false not null, + a_template_id varchar(32) default null, a_shared_documents varchar(2000) default null, fk_entry_id int8 default null, a_sub_ident varchar(64) default null, diff --git a/src/test/java/org/olat/modules/adobeconnect/manager/AdobeConnectMeetingDAOTest.java b/src/test/java/org/olat/modules/adobeconnect/manager/AdobeConnectMeetingDAOTest.java index e984e5ffae0..d009d965724 100644 --- a/src/test/java/org/olat/modules/adobeconnect/manager/AdobeConnectMeetingDAOTest.java +++ b/src/test/java/org/olat/modules/adobeconnect/manager/AdobeConnectMeetingDAOTest.java @@ -19,6 +19,7 @@ */ package org.olat.modules.adobeconnect.manager; +import java.util.Calendar; import java.util.Date; import java.util.List; import java.util.UUID; @@ -51,7 +52,7 @@ public class AdobeConnectMeetingDAOTest extends OlatTestCase { @Test public void createMeeting() { - AdobeConnectMeeting meeting = adobeConnectMeetingDao.createMeeting("New meeting", "Very interessant", new Date(), new Date(), "sco-id", "folder-id", "DFN", null, null, null); + AdobeConnectMeeting meeting = adobeConnectMeetingDao.createMeeting("New meeting", "Very interessant", false, new Date(), 15, new Date(), 15, null, "sco-id", "folder-id", "DFN", null, null, null); dbInstance.commit(); Assert.assertNotNull(meeting); Assert.assertNotNull(meeting.getKey()); @@ -70,7 +71,7 @@ public class AdobeConnectMeetingDAOTest extends OlatTestCase { RepositoryEntry entry = JunitTestHelper.createAndPersistRepositoryEntry(); String subIdent = UUID.randomUUID().toString(); - AdobeConnectMeeting meeting = adobeConnectMeetingDao.createMeeting("Course meeting", "Annoying", new Date(), new Date(), "sco-id", "folder-id", "DFN", entry, subIdent, null); + AdobeConnectMeeting meeting = adobeConnectMeetingDao.createMeeting("Course meeting", "Annoying", false, new Date(), 10, new Date(), 10, null, "sco-id", "folder-id", "DFN", entry, subIdent, null); dbInstance.commit(); Assert.assertNotNull(meeting); Assert.assertNotNull(meeting.getKey()); @@ -92,7 +93,7 @@ public class AdobeConnectMeetingDAOTest extends OlatTestCase { RepositoryEntry entry = JunitTestHelper.createAndPersistRepositoryEntry(); String subIdent = UUID.randomUUID().toString(); - AdobeConnectMeeting meeting = adobeConnectMeetingDao.createMeeting("Key meeting", "Primary", new Date(), new Date(), "sco-pid", "folder-id", null, entry, subIdent, null); + AdobeConnectMeeting meeting = adobeConnectMeetingDao.createMeeting("Key meeting", "Primary", false, new Date(), 10, new Date(), 10, null, "sco-pid", "folder-id", null, entry, subIdent, null); dbInstance.commitAndCloseSession(); // load the meeting @@ -112,14 +113,29 @@ public class AdobeConnectMeetingDAOTest extends OlatTestCase { Assert.assertEquals(subIdent, meeting.getSubIdent()); Assert.assertNull(meeting.getBusinessGroup()); } + + @Test + public void getAllMeetings() { + RepositoryEntry entry = JunitTestHelper.createAndPersistRepositoryEntry(); + String subIdent = UUID.randomUUID().toString(); + + AdobeConnectMeeting meeting1 = adobeConnectMeetingDao.createMeeting("Course A", "Primary", false, new Date(), 5, new Date(), 5, null, "sco-cid-1", "folder-id", null, entry, subIdent, null); + AdobeConnectMeeting meeting2 = adobeConnectMeetingDao.createMeeting("Course A", "Primary", false, new Date(), 5, new Date(), 5, "tmp-id-1", "sco-cid-2", "folder-id", null, entry, subIdent, null); + dbInstance.commitAndCloseSession(); + + List<AdobeConnectMeeting> allMeetings = adobeConnectMeetingDao.getAllMeetings(); + Assert.assertTrue(allMeetings.size() >= 2); + Assert.assertTrue(allMeetings.contains(meeting1)); + Assert.assertTrue(allMeetings.contains(meeting2)); + } @Test public void getMeetings_repositoryEntry() { RepositoryEntry entry = JunitTestHelper.createAndPersistRepositoryEntry(); String subIdent = UUID.randomUUID().toString(); - AdobeConnectMeeting meeting1 = adobeConnectMeetingDao.createMeeting("Course A", "Primary", new Date(), new Date(), "sco-cid-1", "folder-id", null, entry, subIdent, null); - AdobeConnectMeeting meeting2 = adobeConnectMeetingDao.createMeeting("Course A", "Primary", new Date(), new Date(), "sco-cid-2", "folder-id", null, entry, subIdent, null); + AdobeConnectMeeting meeting1 = adobeConnectMeetingDao.createMeeting("Course A", "Primary", true, new Date(), 5, new Date(), 5, null, "sco-cid-1", "folder-id", null, entry, subIdent, null); + AdobeConnectMeeting meeting2 = adobeConnectMeetingDao.createMeeting("Course A", "Primary", true, new Date(), 5, new Date(), 5, "tmp-id-1", "sco-cid-2", "folder-id", null, entry, subIdent, null); dbInstance.commitAndCloseSession(); // load meetings @@ -134,8 +150,8 @@ public class AdobeConnectMeetingDAOTest extends OlatTestCase { BusinessGroup group = businessGroupDao.createAndPersist(null, "Connected group", "Adobe connected group", -1, -1, false, false, false, false, false); dbInstance.commit(); - AdobeConnectMeeting meeting1 = adobeConnectMeetingDao.createMeeting("Course A", "Primary", new Date(), new Date(), "sco-cid-1", "folder-id", null, null, null, group); - AdobeConnectMeeting meeting2 = adobeConnectMeetingDao.createMeeting("Course A", "Primary", new Date(), new Date(), "sco-cid-2", "folder-id", null, null, null, group); + AdobeConnectMeeting meeting1 = adobeConnectMeetingDao.createMeeting("Course A", "Primary", false, new Date(), 5, new Date(), 5, null, "sco-cid-1", "folder-id", null, null, null, group); + AdobeConnectMeeting meeting2 = adobeConnectMeetingDao.createMeeting("Course A", "Primary", false, new Date(), 5, new Date(), 5, null, "sco-cid-2", "folder-id", null, null, null, group); dbInstance.commitAndCloseSession(); // load meetings @@ -145,5 +161,24 @@ public class AdobeConnectMeetingDAOTest extends OlatTestCase { Assert.assertTrue(meetings.contains(meeting2)); } - + @Test + public void getMeetingsBefore() { + RepositoryEntry entry = JunitTestHelper.createAndPersistRepositoryEntry(); + String subIdent = UUID.randomUUID().toString(); + + Calendar cal = Calendar.getInstance(); + cal.add(Calendar.DATE, -3); + Date oldDate = cal.getTime(); + AdobeConnectMeeting oldMeeting = adobeConnectMeetingDao.createMeeting("Course old", "Primary", false, oldDate, 5, oldDate, 5, null, "sco-cid-1", "folder-id", null, entry, subIdent, null); + AdobeConnectMeeting newMeeting = adobeConnectMeetingDao.createMeeting("Course new", "Primary", false, new Date(), 5, new Date(), 5, null, "sco-cid-1", "folder-id", null, entry, subIdent, null); + dbInstance.commitAndCloseSession(); + + cal.setTime(new Date()); + cal.add(Calendar.DATE, -1); + Date beforeDate = cal.getTime(); + List<AdobeConnectMeeting> allMeetings = adobeConnectMeetingDao.getMeetingsBefore(beforeDate); + Assert.assertTrue(allMeetings.size() >= 1); + Assert.assertTrue(allMeetings.contains(oldMeeting)); + Assert.assertFalse(allMeetings.contains(newMeeting)); + } } -- GitLab