diff --git a/src/main/java/org/olat/collaboration/CollaborationTools.java b/src/main/java/org/olat/collaboration/CollaborationTools.java index 6e1da8f06550055b1c2096d49f5f60fbc2b3788d..2dff99d23e78423b70da0e15c811477b842fe65f 100644 --- a/src/main/java/org/olat/collaboration/CollaborationTools.java +++ b/src/main/java/org/olat/collaboration/CollaborationTools.java @@ -39,6 +39,7 @@ import org.olat.basesecurity.BaseSecurityManager; import org.olat.basesecurity.Constants; import org.olat.basesecurity.GroupRoles; import org.olat.commons.calendar.CalendarManager; +import org.olat.commons.calendar.manager.ImportToCalendarManager; import org.olat.commons.calendar.ui.CalendarController; import org.olat.commons.calendar.ui.WeeklyCalendarController; import org.olat.commons.calendar.ui.components.KalendarRenderWrapper; @@ -671,8 +672,8 @@ public class CollaborationTools implements Serializable { * Delete calendar if exists */ if (businessGroupTodelete != null) { - CalendarManager calManager = CoreSpringFactory.getImpl(CalendarManager.class); - calManager.deleteGroupCalendar(businessGroupTodelete); + CoreSpringFactory.getImpl(ImportToCalendarManager.class).deleteGroupImportedCalendars(businessGroupTodelete); + CoreSpringFactory.getImpl(CalendarManager.class).deleteGroupCalendar(businessGroupTodelete); } /* diff --git a/src/main/java/org/olat/commons/calendar/ImportCalendarJob.java b/src/main/java/org/olat/commons/calendar/ImportCalendarJob.java index 72beccee3076b35cd6b9879f4deaadfddb3c0672..8e6f2527213a9ebd73eac7ccd3f2bebd7e07270e 100644 --- a/src/main/java/org/olat/commons/calendar/ImportCalendarJob.java +++ b/src/main/java/org/olat/commons/calendar/ImportCalendarJob.java @@ -25,6 +25,8 @@ */ package org.olat.commons.calendar; +import java.util.Random; + import org.olat.commons.calendar.manager.ImportToCalendarManager; import org.olat.core.CoreSpringFactory; import org.olat.core.commons.services.scheduler.JobWithDB; @@ -40,12 +42,25 @@ import org.quartz.JobExecutionContext; */ public class ImportCalendarJob extends JobWithDB { + private static final Random random = new Random(); + @Override public void executeWithDB(JobExecutionContext context) { try { + jitter(); CoreSpringFactory.getImpl(ImportToCalendarManager.class).updateCalendarIn(); } catch (Exception e) { log.error("", e); } } + + private void jitter() { + try { + double millis = random.nextDouble() * 180000.0d; + long wait = Math.round(millis); + Thread.sleep(wait); + } catch (InterruptedException e) { + log.error("", e); + } + } } diff --git a/src/main/java/org/olat/commons/calendar/_spring/calendarContext.xml b/src/main/java/org/olat/commons/calendar/_spring/calendarContext.xml index 750df1f6e9493b8291509c671d815ecafb191bae..f97c44e8e3819a685149c44b007de3d79f0d2188 100644 --- a/src/main/java/org/olat/commons/calendar/_spring/calendarContext.xml +++ b/src/main/java/org/olat/commons/calendar/_spring/calendarContext.xml @@ -32,6 +32,7 @@ <bean id="calendarImportJob" class="org.springframework.scheduling.quartz.JobDetailFactoryBean"> <property name="jobClass" value="org.olat.commons.calendar.ImportCalendarJob" /> + <property name="durability" value="true" /> </bean> <bean class="org.olat.core.extensions.action.GenericActionExtension" init-method="initExtensionPoints"> diff --git a/src/main/java/org/olat/commons/calendar/manager/ICalFileCalendarUserDeleteManager.java b/src/main/java/org/olat/commons/calendar/manager/ICalFileCalendarUserDeleteManager.java index eaf82a91bb71b806807ec083070d2e93035541ef..a003a306b92447879947fc992669149e511fc5fb 100644 --- a/src/main/java/org/olat/commons/calendar/manager/ICalFileCalendarUserDeleteManager.java +++ b/src/main/java/org/olat/commons/calendar/manager/ICalFileCalendarUserDeleteManager.java @@ -42,9 +42,13 @@ public class ICalFileCalendarUserDeleteManager implements UserDataDeletable { @Autowired private CalendarManager calendarManager; + @Autowired + private ImportToCalendarManager importToCalendarManager; @Override public void deleteUserData(Identity identity, String newDeletedUserName, File archivePath) { + importToCalendarManager.deletePersonalImportedCalendars(identity); + log.debug("Personal imported calendars deleted for identity=" + identity); calendarManager.deletePersonalCalendar(identity); log.debug("Personal calendar deleted for identity=" + identity); } diff --git a/src/main/java/org/olat/commons/calendar/manager/ImportToCalendarManager.java b/src/main/java/org/olat/commons/calendar/manager/ImportToCalendarManager.java index 1720bd39bb29e900f4a1bf5c6bb5b24bfbefaf4e..174defb0fefa1d0b7f352d6022bcfb9cf86fda17 100644 --- a/src/main/java/org/olat/commons/calendar/manager/ImportToCalendarManager.java +++ b/src/main/java/org/olat/commons/calendar/manager/ImportToCalendarManager.java @@ -30,14 +30,22 @@ import java.io.InputStream; import java.net.URL; import java.util.Date; import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; +import org.olat.basesecurity.BaseSecurity; import org.olat.commons.calendar.CalendarManager; import org.olat.commons.calendar.model.ImportedToCalendar; import org.olat.commons.calendar.model.Kalendar; import org.olat.commons.calendar.ui.components.KalendarRenderWrapper; import org.olat.core.commons.persistence.DBFactory; +import org.olat.core.id.Identity; +import org.olat.core.id.OLATResourceable; import org.olat.core.logging.OLog; import org.olat.core.logging.Tracing; +import org.olat.group.BusinessGroup; +import org.olat.group.manager.BusinessGroupDAO; +import org.olat.repository.RepositoryEntry; +import org.olat.repository.manager.RepositoryEntryDAO; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -54,12 +62,19 @@ import org.springframework.stereotype.Service; */ @Service public class ImportToCalendarManager { - + + private AtomicInteger counter = new AtomicInteger(0); private static final OLog log = Tracing.createLoggerFor(ImportToCalendarManager.class); + @Autowired + private BaseSecurity securityManager; @Autowired private CalendarManager calendarManager; @Autowired + private BusinessGroupDAO businessGroupDao; + @Autowired + private RepositoryEntryDAO repositoryEntryDao; + @Autowired private ImportedToCalendarDAO importedToCalendarDao; /** @@ -70,30 +85,64 @@ public class ImportToCalendarManager { List<ImportedToCalendar> importedToCalendars = importedToCalendarDao.getImportedToCalendars(); log.audit("Begin to update " + importedToCalendars.size() + " calendars."); + //make a full check only every 10 runs + boolean check = counter.incrementAndGet() % 10 == 0; + int count = 0; for(ImportedToCalendar importedToCalendar:importedToCalendars) { String type = importedToCalendar.getToType(); String id = importedToCalendar.getToCalendarId(); String importUrl = importedToCalendar.getUrl(); - - Kalendar cal = calendarManager.getCalendar(type, id); - try(InputStream in = new URL(importUrl).openStream()) { - if(calendarManager.synchronizeCalendarFrom(in, importUrl, cal)) { - log.audit("Updated successfully calendar: " + type + " / " + id); - } else { - log.audit("Failed to update calendar: " + type + " / " + id); + if(check || check(importedToCalendar)) { + try(InputStream in = new URL(importUrl).openStream()) { + Kalendar cal = calendarManager.getCalendar(type, id); + if(calendarManager.synchronizeCalendarFrom(in, importUrl, cal)) { + log.audit("Updated successfully calendar: " + type + " / " + id); + } else { + log.audit("Failed to update calendar: " + type + " / " + id); + } + } catch(Exception ex) { + log.error("Cannot synchronize calendar (" + importedToCalendar.getKey() + ") from url: " + importUrl, ex); } - } catch(Exception ex) { - log.error("Cannot synchronize calendar from url: " + importUrl, ex); + } else { + log.audit("Delete imported calendar because of missing resource: " + type + " " + id + " with URL: " + importUrl); + deleteImportedCalendars(type, id); } if(count++ % 20 == 0) { DBFactory.getInstance().commit(); + + try { + Thread.sleep(1000);// sleep to don't overload the system + } catch (InterruptedException e) { + log.error("", e); + } } } return false; } + + private boolean check(ImportedToCalendar importedToCalendar) { + String id = importedToCalendar.getToCalendarId(); + String type = importedToCalendar.getToType(); + if(CalendarManager.TYPE_USER.equals(type)) { + Identity identity = securityManager.findIdentityByNameCaseInsensitive(id); + return identity != null && identity.getStatus() != null && identity.getStatus().intValue() < Identity.STATUS_DELETED; + } + if(CalendarManager.TYPE_COURSE.equals(type)) { + Long resourceId = new Long(id); + RepositoryEntry entry = repositoryEntryDao.loadByResourceId("CourseModule", resourceId); + return entry != null; + } + if(CalendarManager.TYPE_GROUP.equals(type)) { + Long resourceId = new Long(id); + BusinessGroup group = businessGroupDao.loadByResourceId(resourceId); + return group != null; + } + return true; + } + /** * Append the stream of events to the specified calendar. * @@ -139,4 +188,23 @@ public class ImportToCalendarManager { return false; } } + + public void deletePersonalImportedCalendars(Identity identity) { + deleteImportedCalendars(CalendarManager.TYPE_USER, identity.getName()); + } + + public void deleteGroupImportedCalendars(BusinessGroup businessGroup) { + deleteImportedCalendars(CalendarManager.TYPE_GROUP, businessGroup.getResourceableId().toString()); + } + + public void deleteCourseImportedCalendars(OLATResourceable course) { + deleteImportedCalendars(CalendarManager.TYPE_COURSE, course.getResourceableId().toString()); + } + + private void deleteImportedCalendars(String type, String id) { + List<ImportedToCalendar> importedToCalendars = importedToCalendarDao.getImportedToCalendars(id, type); + for(ImportedToCalendar importedToCalendar:importedToCalendars) { + importedToCalendarDao.delete(importedToCalendar); + } + } } diff --git a/src/main/java/org/olat/commons/calendar/manager/ImportedToCalendarDAO.java b/src/main/java/org/olat/commons/calendar/manager/ImportedToCalendarDAO.java index 21893f946b9c0049906339adf7a86ab0a3ac7b27..c77279d712aa8c3f72c4e4a118fae88dd9a3f5c1 100644 --- a/src/main/java/org/olat/commons/calendar/manager/ImportedToCalendarDAO.java +++ b/src/main/java/org/olat/commons/calendar/manager/ImportedToCalendarDAO.java @@ -65,9 +65,24 @@ public class ImportedToCalendarDAO { .getResultList(); } + public List<ImportedToCalendar> getImportedToCalendars(String toCalendarId, String toType) { + return dbInstance.getCurrentEntityManager() + .createNamedQuery("importedToCalendarByIdAndType", ImportedToCalendar.class) + .setParameter("toCalendarId", toCalendarId) + .setParameter("toType", toType) + .getResultList(); + } + public List<ImportedToCalendar> getImportedToCalendars() { return dbInstance.getCurrentEntityManager() .createNamedQuery("importedToCalendars", ImportedToCalendar.class) .getResultList(); } + + public void delete(ImportedToCalendar importedToCalendar) { + ImportedToCalendar reloadedImportedToCalendar = dbInstance.getCurrentEntityManager() + .getReference(ImportedToCalendar.class, importedToCalendar.getKey()); + dbInstance.getCurrentEntityManager() + .remove(reloadedImportedToCalendar); + } } diff --git a/src/main/java/org/olat/commons/calendar/model/ImportedToCalendar.java b/src/main/java/org/olat/commons/calendar/model/ImportedToCalendar.java index e9f0889d19f32f75c3df13c42d037e241c11eb9c..67e3f55addf02ee0e8023da122f73c3dcd88626c 100644 --- a/src/main/java/org/olat/commons/calendar/model/ImportedToCalendar.java +++ b/src/main/java/org/olat/commons/calendar/model/ImportedToCalendar.java @@ -46,6 +46,7 @@ import org.olat.core.id.Persistable; @Table(name="o_cal_import_to") @NamedQueries({ @NamedQuery(name="importedToCalendarByIdTypeAndUrl", query="select cal from importedtocal cal where cal.toCalendarId=:toCalendarId and cal.toType=:toType and cal.url=:url"), + @NamedQuery(name="importedToCalendarByIdAndType", query="select cal from importedtocal cal where cal.toCalendarId=:toCalendarId and cal.toType=:toType"), @NamedQuery(name="importedToCalendars", query="select cal from importedtocal cal") }) public class ImportedToCalendar implements ModifiedInfo, Persistable { 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 19d35e7235acd5115e02696650a88a9f2d692cb3..b8b97d9bc2753a69f582b33884f5fa63816ce6d2 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 @@ -52,6 +52,7 @@ How to add a new job: <ref bean="reminderTrigger"/> <ref bean="videoTranscodingTrigger"/> <ref bean="automaticLifecycleTrigger"/> + <ref bean="calendarImportTrigger"/> <ref bean="autoCloseLecturesTrigger"/> <ref bean="reminderLecturesTrigger"/> </list> diff --git a/src/main/java/org/olat/course/CourseFactory.java b/src/main/java/org/olat/course/CourseFactory.java index 81dd4854444f24629e6ac0bb657e33d722ba5a8d..8be9355f24c0444bfaf03c030168e752085986cf 100644 --- a/src/main/java/org/olat/course/CourseFactory.java +++ b/src/main/java/org/olat/course/CourseFactory.java @@ -46,6 +46,7 @@ import org.apache.commons.io.IOUtils; import org.olat.admin.quota.QuotaConstants; import org.olat.commons.calendar.CalendarManager; import org.olat.commons.calendar.CalendarNotificationManager; +import org.olat.commons.calendar.manager.ImportToCalendarManager; import org.olat.commons.calendar.ui.components.KalendarRenderWrapper; import org.olat.core.CoreSpringFactory; import org.olat.core.commons.fullWebApp.LayoutMain3ColsController; @@ -376,8 +377,9 @@ public class CourseFactory { CoursePropertyManager propertyManager = PersistingCoursePropertyManager.getInstance(res); propertyManager.deleteAllCourseProperties(); // delete course calendar - CalendarManager calManager = CoreSpringFactory.getImpl(CalendarManager.class); - calManager.deleteCourseCalendar(res); + CoreSpringFactory.getImpl(ImportToCalendarManager.class).deleteCourseImportedCalendars(res); + CoreSpringFactory.getImpl(CalendarManager.class).deleteCourseCalendar(res); + // delete IM messages CoreSpringFactory.getImpl(InstantMessagingService.class).deleteMessages(res); //delete tasks diff --git a/src/main/java/org/olat/group/manager/BusinessGroupDAO.java b/src/main/java/org/olat/group/manager/BusinessGroupDAO.java index d61de9b20c3728c794a5cbd2dcad6310f2df4a21..58ccb59e100cd08db65b517a6b53d6cf951e1906 100644 --- a/src/main/java/org/olat/group/manager/BusinessGroupDAO.java +++ b/src/main/java/org/olat/group/manager/BusinessGroupDAO.java @@ -219,6 +219,19 @@ public class BusinessGroupDAO { return groups; } + public BusinessGroup loadByResourceId(Long resourceId) { + StringBuilder sb = new StringBuilder(); + sb.append("select bgi from businessgroup bgi ") + .append(" inner join fetch bgi.resource resource") + .append(" where resource.resName='BusinessGroup' and resource.resId=:resId"); + + List<BusinessGroup> groups = dbInstance.getCurrentEntityManager() + .createQuery(sb.toString(), BusinessGroup.class) + .setParameter("resId", resourceId) + .getResultList(); + return groups == null || groups.isEmpty() ? null : groups.get(0); + } + public BusinessGroup loadForUpdate(Long id) { StringBuilder sb = new StringBuilder(); sb.append("select bgi from businessgroup bgi ") diff --git a/src/main/java/org/olat/modules/video/_spring/videoContext.xml b/src/main/java/org/olat/modules/video/_spring/videoContext.xml index 077b68312b4dc2530cfb9fa826315134b9bf2d9a..80ba9aafa16efe8fcc0d395237d6e2f167c58ea8 100644 --- a/src/main/java/org/olat/modules/video/_spring/videoContext.xml +++ b/src/main/java/org/olat/modules/video/_spring/videoContext.xml @@ -32,6 +32,7 @@ <bean id="videoTranscodingJobDetail" class="org.springframework.scheduling.quartz.JobDetailFactoryBean"> <property name="jobClass" value="org.olat.modules.video.manager.VideoTranscodingJob" /> + <property name="durability" value="true" /> </bean> <bean class="org.olat.core.extensions.action.GenericActionExtension" init-method="initExtensionPoints"> diff --git a/src/main/java/org/olat/repository/RepositoryEntry.java b/src/main/java/org/olat/repository/RepositoryEntry.java index a4f37ffa8fa6f1c259005058c823b79cfc400671..29cfb8ce0b84d63458844e355ef307f631b163d6 100644 --- a/src/main/java/org/olat/repository/RepositoryEntry.java +++ b/src/main/java/org/olat/repository/RepositoryEntry.java @@ -74,6 +74,7 @@ import org.olat.resource.OLATResourceImpl; @NamedQuery(name="filterRepositoryEntryMembership", query="select v.key, membership.identity.key from repositoryentry as v inner join v.groups as relGroup inner join relGroup.group as baseGroup inner join baseGroup.members as membership on membership.role in ('owner','coach','participant') where membership.identity.key=:identityKey and v.key in (:repositoryEntryKey)"), @NamedQuery(name="loadRepositoryEntryByKey", query="select v from repositoryentry as v inner join fetch v.olatResource as ores inner join fetch v.statistics as statistics left join fetch v.lifecycle as lifecycle where v.key = :repoKey"), @NamedQuery(name="loadRepositoryEntryByResourceKey", query="select v from repositoryentry as v inner join fetch v.olatResource as ores inner join fetch v.statistics as statistics left join fetch v.lifecycle as lifecycle where ores.key = :resourceKey"), + @NamedQuery(name="loadRepositoryEntryByResourceId", query="select v from repositoryentry as v inner join fetch v.olatResource as ores inner join fetch v.statistics as statistics left join fetch v.lifecycle as lifecycle where ores.resId=:resId and ores.resName=:resName"), @NamedQuery(name="getDisplayNameByResourceKey", query="select v.displayname from repositoryentry v where v.olatResource.key=:resKey"), @NamedQuery(name="getDisplayNameByOlatResourceRedId", query="select v.displayname from repositoryentry v inner join v.olatResource as ores where ores.resId=:resid"), @NamedQuery(name="getDisplayNameByRepositoryEntryKey", query="select v.displayname from repositoryentry v where v.key=:reKey") diff --git a/src/main/java/org/olat/repository/manager/RepositoryEntryDAO.java b/src/main/java/org/olat/repository/manager/RepositoryEntryDAO.java index cac0c3838c000aa90a12f3709db0c5eac81a7228..43eb4e542c6a4a9d3072627ff3b50452a1721ca9 100644 --- a/src/main/java/org/olat/repository/manager/RepositoryEntryDAO.java +++ b/src/main/java/org/olat/repository/manager/RepositoryEntryDAO.java @@ -102,6 +102,18 @@ public class RepositoryEntryDAO { .getResultList(); } + public RepositoryEntry loadByResourceId(String resourceName, Long resourceId) { + List<RepositoryEntry> entries = dbInstance.getCurrentEntityManager() + .createNamedQuery("loadRepositoryEntryByResourceId", RepositoryEntry.class) + .setParameter("resId", resourceId) + .setParameter("resName", resourceName) + .getResultList(); + if(entries.isEmpty()) { + return null; + } + return entries.get(0); + } + public List<RepositoryEntry> searchByIdAndRefs(String idAndRefs) { StringBuilder sb = new StringBuilder(); sb.append("select v from ").append(RepositoryEntry.class.getName()).append(" as v ") diff --git a/src/main/webapp/static/js/openolat/iframe.js b/src/main/webapp/static/js/openolat/iframe.js index fe2833f4ed4dcad03e586c39a06e0c9a12546abd..78e7442fcdc87f2b86baf2eacdbe59ab14698c87 100644 --- a/src/main/webapp/static/js/openolat/iframe.js +++ b/src/main/webapp/static/js/openolat/iframe.js @@ -178,7 +178,7 @@ function b_sendNewUriEventToParent() { } function b_addOnloadEvent(fnc){ -//console.log("b_addOnloadEvent window.name=" + window.name + " b_iframeid=" + b_iframeid + " fnc=" + fnc); + //console.log("b_addOnloadEvent window.name=" + window.name + " b_iframeid=" + b_iframeid + " fnc=" + fnc); try { // Only continue if we can locate the main window var mainwindow = b_getMainWindow(window.parent); @@ -211,29 +211,31 @@ function b_anchorFirefoxWorkaround() { var anchors = document.getElementsByTagName('a'); for (var i=0; i < anchors.length; i++) { var anchor = anchors[i]; - var href = anchor.getAttribute('href'); - if(href && href[0] == "#") { - var name = href.substring(1); - anchor.addEventListener('click', function() { - try { - var nameElement = document.getElementsByName(name); - var element = null; - if(nameElement != null && nameElement.length > 0) { + var href = anchor.getAttribute('href'); + if(href && href[0] == "#") { + anchor.addEventListener('click', function(el) { + try { + var href = el.target.getAttribute('href'); + var name = href.substring(1); + var nameElement = document.getElementsByName(name); + + var element = null; + if(nameElement != null && nameElement.length > 0) { element = nameElement[0]; - } else { - var idElement = document.getElementById(name); - if(idElement != null) { - element = idElement; - } - } - if(element && window && window.parent) { + } else { + var idElement = document.getElementById(name); + if(idElement != null) { + element = idElement; + } + } + if(element && window && window.parent) { var offset = b_anchorFirefoxWorkaroundCumulativeOffset(element); window.parent.scrollTo(offset[0], offset[1]); - } - } catch(e) { - //console.log(e); - } - return true; + } + } catch(e) { + //console.log(e); + } + return true; }); } }