diff --git a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/query-calendar-common-SqlMap.xml b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/query-calendar-common-SqlMap.xml index 3b9f8c1873..9dee99e38e 100644 --- a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/query-calendar-common-SqlMap.xml +++ b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/query-calendar-common-SqlMap.xml @@ -12,7 +12,9 @@ - + + + @@ -28,6 +30,8 @@ childNode.audit_creator as audit_creator, prop_fromdate.string_value as from_date, prop_todate.string_value as to_date, + prop_recurrenceRule.string_value as recurrence_rule, + prop_recurrenceLastMeeting.string_value as recurrence_last_meeting, prop_name.string_value as name from alf_child_assoc assoc @@ -35,6 +39,8 @@ join alf_store childStore on (childStore.id = childNode.store_id) left join alf_node_properties prop_fromdate on (prop_fromdate.node_id = childNode.id and prop_fromdate.qname_id = #{fromDateQNameId}) left join alf_node_properties prop_todate on (prop_todate.node_id = childNode.id and prop_todate.qname_id = #{toDateQNameId}) + left join alf_node_properties prop_recurrenceRule on (prop_recurrenceRule.node_id = childNode.id and prop_recurrenceRule.qname_id = #{recurrenceRuleQNameId}) + left join alf_node_properties prop_recurrenceLastMeeting on (prop_recurrenceLastMeeting.node_id = childNode.id and prop_recurrenceLastMeeting.qname_id = #{recurrenceLastMeetingQNameId}) left join alf_node_properties prop_name on (prop_name.node_id = childNode.id and prop_name.qname_id = #{nameQNameId}) where assoc.parent_node_id IN #{nodeRef} diff --git a/source/java/org/alfresco/repo/calendar/CalendarHelpersTest.java b/source/java/org/alfresco/repo/calendar/CalendarHelpersTest.java index 130c11d056..89efdd02ea 100644 --- a/source/java/org/alfresco/repo/calendar/CalendarHelpersTest.java +++ b/source/java/org/alfresco/repo/calendar/CalendarHelpersTest.java @@ -20,9 +20,7 @@ package org.alfresco.repo.calendar; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -32,35 +30,10 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import org.alfresco.model.ContentModel; -import org.alfresco.query.PagingRequest; -import org.alfresco.query.PagingResults; -import org.alfresco.repo.policy.BehaviourFilter; -import org.alfresco.repo.security.authentication.AuthenticationUtil; -import org.alfresco.repo.site.SiteModel; -import org.alfresco.repo.transaction.RetryingTransactionHelper; -import org.alfresco.service.cmr.calendar.CalendarEntry; import org.alfresco.service.cmr.calendar.CalendarEntryDTO; import org.alfresco.service.cmr.calendar.CalendarRecurrenceHelper; import org.alfresco.service.cmr.calendar.CalendarService; -import org.alfresco.service.cmr.dictionary.DictionaryService; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.cmr.security.MutableAuthenticationService; -import org.alfresco.service.cmr.security.PermissionService; -import org.alfresco.service.cmr.security.PersonService; -import org.alfresco.service.cmr.site.SiteInfo; -import org.alfresco.service.cmr.site.SiteService; -import org.alfresco.service.cmr.site.SiteVisibility; -import org.alfresco.service.cmr.tagging.TaggingService; -import org.alfresco.util.ApplicationContextHelper; -import org.alfresco.util.PropertyMap; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; import org.junit.Test; -import org.springframework.context.ApplicationContext; /** * Test cases for the helpers relating to the {@link CalendarService}, @@ -624,6 +597,13 @@ public class CalendarHelpersTest CalendarRecurrenceHelper.buildMonthlyRecurrences( currentDate, dates, params, onOrAfter, until, firstOnly, interval); } + + protected static void buildYearlyRecurrences(Calendar currentDate, List dates, + Map params, Date onOrAfter, Date until, boolean firstOnly, int interval) + { + CalendarRecurrenceHelper.buildYearlyRecurrences( + currentDate, dates, params, onOrAfter, until, firstOnly, interval); + } } private static Date date(int year, int month, int day) diff --git a/source/java/org/alfresco/repo/calendar/CalendarServiceImpl.java b/source/java/org/alfresco/repo/calendar/CalendarServiceImpl.java index 15a95bdba0..21203867d9 100644 --- a/source/java/org/alfresco/repo/calendar/CalendarServiceImpl.java +++ b/source/java/org/alfresco/repo/calendar/CalendarServiceImpl.java @@ -69,8 +69,8 @@ public class CalendarServiceImpl implements CalendarService */ private static final int MAX_QUERY_ENTRY_COUNT = 10000; - private static final String CANNED_QUERY_GET_CHILDREN = "calendarGetChildrenCannedQueryFactory"; - private static final String CANNED_QUERY_GET_ENTRIES = "calendarGetCalendarEntriesCannedQueryFactory"; + protected static final String CANNED_QUERY_GET_CHILDREN = "calendarGetChildrenCannedQueryFactory"; + protected static final String CANNED_QUERY_GET_ENTRIES = "calendarGetCalendarEntriesCannedQueryFactory"; /** * The logger @@ -262,7 +262,6 @@ public class CalendarServiceImpl implements CalendarService } // Make the changes - taggingService.clearTags(nodeRef); for(String tag : toDel) { taggingService.removeTag(nodeRef, tag); @@ -346,20 +345,31 @@ public class CalendarServiceImpl implements CalendarService @Override public CalendarEntry updateCalendarEntry(CalendarEntry entry) { - Map properties = CalendarEntryImpl.toNodeProperties(entry); - + // Sanity check what we were given if(entry.getNodeRef() == null) { throw new IllegalArgumentException("Can't update a calendar entry that was never persisted, call create instead"); } - // Update the existing one + // Get the Calendar properties + Map properties = CalendarEntryImpl.toNodeProperties(entry); + + // Merge in the non calendar ones + for(Map.Entry prop : nodeService.getProperties(entry.getNodeRef()).entrySet()) + { + if(! prop.getKey().getNamespaceURI().equals(CalendarModel.CALENDAR_MODEL_URL)) + { + properties.put(prop.getKey(), prop.getValue()); + } + } + + // Save the new properties nodeService.setProperties(entry.getNodeRef(), properties); - // Update tags + // Update the tags handleTags(entry); - // Nothing changed + // Nothing was changed on the entry itself return entry; } diff --git a/source/java/org/alfresco/repo/calendar/CalendarServiceImplTest.java b/source/java/org/alfresco/repo/calendar/CalendarServiceImplTest.java index a0981fe4a7..86abb93d5a 100644 --- a/source/java/org/alfresco/repo/calendar/CalendarServiceImplTest.java +++ b/source/java/org/alfresco/repo/calendar/CalendarServiceImplTest.java @@ -30,8 +30,14 @@ import java.util.Date; import java.util.List; import org.alfresco.model.ContentModel; +import org.alfresco.query.CannedQueryFactory; +import org.alfresco.query.CannedQueryResults; import org.alfresco.query.PagingRequest; import org.alfresco.query.PagingResults; +import org.alfresco.repo.calendar.cannedqueries.CalendarEntity; +import org.alfresco.repo.calendar.cannedqueries.GetCalendarEntriesCannedQuery; +import org.alfresco.repo.calendar.cannedqueries.GetCalendarEntriesCannedQueryFactory; +import org.alfresco.repo.calendar.cannedqueries.GetCalendarEntriesCannedQueryTestHook; import org.alfresco.repo.policy.BehaviourFilter; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.site.SiteModel; @@ -50,7 +56,9 @@ import org.alfresco.service.cmr.site.SiteService; import org.alfresco.service.cmr.site.SiteVisibility; import org.alfresco.service.cmr.tagging.TaggingService; import org.alfresco.util.ApplicationContextHelper; +import org.alfresco.util.ISO8601DateFormat; import org.alfresco.util.PropertyMap; +import org.alfresco.util.registry.NamedObjectRegistry; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; @@ -82,6 +90,7 @@ public class CalendarServiceImplTest private static PermissionService PERMISSION_SERVICE; private static SiteService SITE_SERVICE; private static TaggingService TAGGING_SERVICE; + private static GetCalendarEntriesCannedQueryFactory CALENDAR_CQ_FACTORY; private static final String TEST_USER = CalendarServiceImplTest.class.getSimpleName() + "_testuser"; private static final String ADMIN_USER = AuthenticationUtil.getAdminUserName(); @@ -111,7 +120,14 @@ public class CalendarServiceImplTest PERMISSION_SERVICE = (PermissionService)testContext.getBean("permissionService"); SITE_SERVICE = (SiteService)testContext.getBean("siteService"); TAGGING_SERVICE = (TaggingService)testContext.getBean("TaggingService"); + + // Get the canned query registry, and from that the factory + NamedObjectRegistry> calendarCannedQueryRegistry = + (NamedObjectRegistry>)testContext.getBean("calendarCannedQueryRegistry"); + CALENDAR_CQ_FACTORY = (GetCalendarEntriesCannedQueryFactory) + calendarCannedQueryRegistry.getNamedObject(CalendarServiceImpl.CANNED_QUERY_GET_ENTRIES); + // Do the setup as admin AuthenticationUtil.setFullyAuthenticatedUser(ADMIN_USER); createUser(TEST_USER); @@ -335,6 +351,7 @@ public class CalendarServiceImplTest "Title", "Description", "Location", new Date(1), new Date(1234) ); entry = CALENDAR_SERVICE.createCalendarEntry(CALENDAR_SITE.getShortName(), entry); + testNodesToTidy.add(entry.getNodeRef()); // Check assertEquals(0, entry.getTags().size()); @@ -364,9 +381,15 @@ public class CalendarServiceImplTest entry.getTags().add(TAG_1); CALENDAR_SERVICE.updateCalendarEntry(entry); - // Check - entry = CALENDAR_SERVICE.getCalendarEntry(CALENDAR_SITE.getShortName(), entry.getSystemName()); - assertEquals(2, entry.getTags().size()); + // Check it as-is + assertEquals(3, entry.getTags().size()); // Includes duplicate tag until re-loaded + assertEquals(true, entry.getTags().contains(TAG_1)); + assertEquals(false, entry.getTags().contains(TAG_2)); + assertEquals(true, entry.getTags().contains(TAG_3)); + + // Now load and re-check + entry = CALENDAR_SERVICE.getCalendarEntry(CALENDAR_SITE.getShortName(), entry.getSystemName()); + assertEquals(2, entry.getTags().size()); // Duplicate now gone assertEquals(true, entry.getTags().contains(TAG_1)); assertEquals(false, entry.getTags().contains(TAG_2)); assertEquals(true, entry.getTags().contains(TAG_3)); @@ -632,15 +655,18 @@ public class CalendarServiceImplTest // Now add some events to one site - CALENDAR_SERVICE.createCalendarEntry(CALENDAR_SITE.getShortName(), new CalendarEntryDTO( + NodeRef c1 = CALENDAR_SERVICE.createCalendarEntry(CALENDAR_SITE.getShortName(), new CalendarEntryDTO( "TitleA", "Description", "Location", new Date(1302431400), new Date(1302442200) - )); - CALENDAR_SERVICE.createCalendarEntry(CALENDAR_SITE.getShortName(), new CalendarEntryDTO( + )).getNodeRef(); + NodeRef c2 = CALENDAR_SERVICE.createCalendarEntry(CALENDAR_SITE.getShortName(), new CalendarEntryDTO( "TitleB", "Description", "Location", new Date(1302435000), new Date(1302435000) - )); - CALENDAR_SERVICE.createCalendarEntry(CALENDAR_SITE.getShortName(), new CalendarEntryDTO( + )).getNodeRef(); + NodeRef c3 = CALENDAR_SERVICE.createCalendarEntry(CALENDAR_SITE.getShortName(), new CalendarEntryDTO( "TitleC", "Description", "Location", new Date(1302431400), new Date(1302435000) - )); + )).getNodeRef(); + testNodesToTidy.add(c1); + testNodesToTidy.add(c2); + testNodesToTidy.add(c3); // Check @@ -649,20 +675,30 @@ public class CalendarServiceImplTest assertEquals(3, results.getPage().size()); // Should be date ordered, from then too - assertEquals("TitleA", results.getPage().get(0).getTitle()); - assertEquals("TitleC", results.getPage().get(1).getTitle()); + assertEquals("TitleC", results.getPage().get(0).getTitle()); // Same start as A, earlier end + assertEquals("TitleA", results.getPage().get(1).getTitle()); assertEquals("TitleB", results.getPage().get(2).getTitle()); // Add some to the other site, which the user isn't a member of AuthenticationUtil.setFullyAuthenticatedUser(ADMIN_USER); - CALENDAR_SERVICE.createCalendarEntry(ALTERNATE_CALENDAR_SITE.getShortName(), new CalendarEntryDTO( + NodeRef ca1 = CALENDAR_SERVICE.createCalendarEntry(ALTERNATE_CALENDAR_SITE.getShortName(), new CalendarEntryDTO( "PrivateTitleA", "Description", "Location", new Date(1302131400), new Date(1302135000) - )); - CALENDAR_SERVICE.createCalendarEntry(ALTERNATE_CALENDAR_SITE.getShortName(), new CalendarEntryDTO( + )).getNodeRef(); + NodeRef ca2 = CALENDAR_SERVICE.createCalendarEntry(ALTERNATE_CALENDAR_SITE.getShortName(), new CalendarEntryDTO( "PrivateTitleB", "Description", "Location", new Date(1302731400), new Date(1302472200) - )); + )).getNodeRef(); AuthenticationUtil.setFullyAuthenticatedUser(TEST_USER); + testNodesToTidy.add(ca1); + testNodesToTidy.add(ca2); + + // Our nodes are now, in start+end order: + // PrivateTitleA 1302131400 -> 1302135000 + // TitleC 1302431400 -> 1302435000 + // TitleA 1302431400 -> 1302442200 + // TitleB 1302435000 -> 1302435000 + // PrivateTitleB 1302731400 -> 1302472200 + // Check, they won't show up due to permissions results = CALENDAR_SERVICE.listCalendarEntries(new String[] { @@ -690,8 +726,8 @@ public class CalendarServiceImplTest // Should be date ordered, from then too assertEquals("PrivateTitleA", results.getPage().get(0).getTitle()); - assertEquals("TitleA", results.getPage().get(1).getTitle()); - assertEquals("TitleC", results.getPage().get(2).getTitle()); + assertEquals("TitleC", results.getPage().get(1).getTitle()); + assertEquals("TitleA", results.getPage().get(2).getTitle()); assertEquals("TitleB", results.getPage().get(3).getTitle()); assertEquals("PrivateTitleB", results.getPage().get(4).getTitle()); @@ -704,12 +740,22 @@ public class CalendarServiceImplTest new Date(1300031400), null, paging); assertEquals(5, results.getPage().size()); - // Date in the middle + // Date in the middle, several just finishing results = CALENDAR_SERVICE.listCalendarEntries(new String[] { CALENDAR_SITE.getShortName(), ALTERNATE_CALENDAR_SITE.getShortName()}, new Date(1302435000), null, paging); + assertEquals(4, results.getPage().size()); + assertEquals("TitleC", results.getPage().get(0).getTitle()); + assertEquals("TitleA", results.getPage().get(1).getTitle()); + assertEquals("TitleB", results.getPage().get(2).getTitle()); + assertEquals("PrivateTitleB", results.getPage().get(3).getTitle()); + + // Date in the middle, past the finish of many + results = CALENDAR_SERVICE.listCalendarEntries(new String[] { + CALENDAR_SITE.getShortName(), ALTERNATE_CALENDAR_SITE.getShortName()}, + new Date(1302441000), null, paging); assertEquals(2, results.getPage().size()); - assertEquals("TitleB", results.getPage().get(0).getTitle()); + assertEquals("TitleA", results.getPage().get(0).getTitle()); assertEquals("PrivateTitleB", results.getPage().get(1).getTitle()); // Date in the future @@ -727,16 +773,25 @@ public class CalendarServiceImplTest null, new Date(1300031400), paging); assertEquals(0, results.getPage().size()); - // Date in the middle + // Date in the middle, with some touching on the end date results = CALENDAR_SERVICE.listCalendarEntries(new String[] { CALENDAR_SITE.getShortName(), ALTERNATE_CALENDAR_SITE.getShortName()}, null, new Date(1302435000), paging); assertEquals(4, results.getPage().size()); assertEquals("PrivateTitleA", results.getPage().get(0).getTitle()); - assertEquals("TitleA", results.getPage().get(1).getTitle()); - assertEquals("TitleC", results.getPage().get(2).getTitle()); + assertEquals("TitleC", results.getPage().get(1).getTitle()); + assertEquals("TitleA", results.getPage().get(2).getTitle()); assertEquals("TitleB", results.getPage().get(3).getTitle()); + // Date in the middle, before the start date of several + results = CALENDAR_SERVICE.listCalendarEntries(new String[] { + CALENDAR_SITE.getShortName(), ALTERNATE_CALENDAR_SITE.getShortName()}, + null, new Date(1302432400), paging); + assertEquals(3, results.getPage().size()); + assertEquals("PrivateTitleA", results.getPage().get(0).getTitle()); + assertEquals("TitleC", results.getPage().get(1).getTitle()); + assertEquals("TitleA", results.getPage().get(2).getTitle()); + // Date in the future results = CALENDAR_SERVICE.listCalendarEntries(new String[] { CALENDAR_SITE.getShortName(), ALTERNATE_CALENDAR_SITE.getShortName()}, @@ -749,16 +804,16 @@ public class CalendarServiceImplTest CALENDAR_SITE.getShortName(), ALTERNATE_CALENDAR_SITE.getShortName()}, new Date(1302431400), new Date(1302432000), paging); assertEquals(2, results.getPage().size()); - assertEquals("TitleA", results.getPage().get(0).getTitle()); - assertEquals("TitleC", results.getPage().get(1).getTitle()); + assertEquals("TitleC", results.getPage().get(0).getTitle()); + assertEquals("TitleA", results.getPage().get(1).getTitle()); results = CALENDAR_SERVICE.listCalendarEntries(new String[] { CALENDAR_SITE.getShortName(), ALTERNATE_CALENDAR_SITE.getShortName()}, new Date(1302131400), new Date(1302432000), paging); assertEquals(3, results.getPage().size()); assertEquals("PrivateTitleA", results.getPage().get(0).getTitle()); - assertEquals("TitleA", results.getPage().get(1).getTitle()); - assertEquals("TitleC", results.getPage().get(2).getTitle()); + assertEquals("TitleC", results.getPage().get(1).getTitle()); + assertEquals("TitleA", results.getPage().get(2).getTitle()); // Filter on just one site, won't see from the other @@ -766,30 +821,150 @@ public class CalendarServiceImplTest CALENDAR_SITE.getShortName()}, new Date(1302131400), new Date(1302432000), paging); assertEquals(2, results.getPage().size()); - assertEquals("TitleA", results.getPage().get(0).getTitle()); - assertEquals("TitleC", results.getPage().get(1).getTitle()); + assertEquals("TitleC", results.getPage().get(0).getTitle()); + assertEquals("TitleA", results.getPage().get(1).getTitle()); results = CALENDAR_SERVICE.listCalendarEntries(new String[] { ALTERNATE_CALENDAR_SITE.getShortName()}, new Date(1302131400), new Date(1302432000), paging); assertEquals(1, results.getPage().size()); assertEquals("PrivateTitleA", results.getPage().get(0).getTitle()); - - - // Tidy - paging = new PagingRequest(10); - results = CALENDAR_SERVICE.listCalendarEntries(CALENDAR_SITE.getShortName(), paging); - for(CalendarEntry entry : results.getPage()) - { - testNodesToTidy.add(entry.getNodeRef()); - } - results = CALENDAR_SERVICE.listCalendarEntries(ALTERNATE_CALENDAR_SITE.getShortName(), paging); - for(CalendarEntry entry : results.getPage()) - { - testNodesToTidy.add(entry.getNodeRef()); - } } + + /** + * Ensure that the canned query returns the right entity objects + * for the underlying calendar entries. + * Checks both the low level filtering, and the DB fetching of the + * properties used in the filter + */ + @Test public void testCannedQueryEntityResults() throws Exception + { + PagingRequest paging = new PagingRequest(10); + NodeRef[] containers = new NodeRef[] { + SITE_SERVICE.getContainer(CALENDAR_SITE.getShortName(), CalendarServiceImpl.CALENDAR_COMPONENT), + SITE_SERVICE.getContainer(ALTERNATE_CALENDAR_SITE.getShortName(), CalendarServiceImpl.CALENDAR_COMPONENT), + }; + Date from = new Date(1302431400); + Date to = new Date(1302442200); + + + // To capture the low level results + final List full = new ArrayList(); + final List filtered = new ArrayList(); + GetCalendarEntriesCannedQueryTestHook hook = new GetCalendarEntriesCannedQueryTestHook() + { + @Override + public void notifyComplete(List fullList, + List filteredList) { + full.clear(); + filtered.clear(); + full.addAll(fullList); + filtered.addAll(filteredList); + } + }; + + + // With no entries, won't find anything + GetCalendarEntriesCannedQuery cq = (GetCalendarEntriesCannedQuery)CALENDAR_CQ_FACTORY.getCannedQuery( + containers, from, to, paging + ); + cq.setTestHook(hook); + cq.execute(); + + assertEquals(0, full.size()); + assertEquals(0, filtered.size()); + + + // Add some events, with a mixture of repeating and non + CalendarEntry c1 = CALENDAR_SERVICE.createCalendarEntry(CALENDAR_SITE.getShortName(), new CalendarEntryDTO( + "SiteNormal", "Description", "Location", new Date(1302431400), new Date(1302442200) + )); + CalendarEntry c2 = CALENDAR_SERVICE.createCalendarEntry(CALENDAR_SITE.getShortName(), new CalendarEntryDTO( + "SiteRepeating", "Description", "Location", new Date(1302435000), new Date(1302435000) + )); + CalendarEntry c3 = CALENDAR_SERVICE.createCalendarEntry(ALTERNATE_CALENDAR_SITE.getShortName(), new CalendarEntryDTO( + "AltSiteNormal", "Description", "Location", new Date(1302431400), new Date(1302435000) + )); + + + // Do a fetch that'll include all of them + cq = (GetCalendarEntriesCannedQuery)CALENDAR_CQ_FACTORY.getCannedQuery( + containers, from, to, paging + ); + cq.setTestHook(hook); + cq.execute(); + + assertEquals(3, full.size()); + assertEquals(3, filtered.size()); + + // Check they have the right details on them, and are correctly sorted + assertEquals(c3.getSystemName(), filtered.get(0).getName()); + assertEquals(ISO8601DateFormat.format(c3.getStart()), filtered.get(0).getFromDate()); + assertEquals(ISO8601DateFormat.format(c3.getEnd()), filtered.get(0).getToDate()); + assertEquals(c3.getRecurrenceRule(), filtered.get(0).getRecurrenceRule()); + assertEquals(null, filtered.get(0).getRecurrenceLastMeeting()); + + assertEquals(c1.getSystemName(), filtered.get(1).getName()); + assertEquals(ISO8601DateFormat.format(c1.getStart()), filtered.get(1).getFromDate()); + assertEquals(ISO8601DateFormat.format(c1.getEnd()), filtered.get(1).getToDate()); + assertEquals(c1.getRecurrenceRule(), filtered.get(1).getRecurrenceRule()); + assertEquals(null, filtered.get(1).getRecurrenceLastMeeting()); + + assertEquals(c2.getSystemName(), filtered.get(2).getName()); + assertEquals(ISO8601DateFormat.format(c2.getStart()), filtered.get(2).getFromDate()); + assertEquals(ISO8601DateFormat.format(c2.getEnd()), filtered.get(2).getToDate()); + assertEquals(c2.getRecurrenceRule(), filtered.get(2).getRecurrenceRule()); + assertEquals(null, filtered.get(2).getRecurrenceLastMeeting()); + + + // Now do one that'll only have some + from = new Date(1302431400-10); + to = new Date(1302431400+10); + cq = (GetCalendarEntriesCannedQuery)CALENDAR_CQ_FACTORY.getCannedQuery( + containers, from, to, paging + ); + cq.setTestHook(hook); + cq.execute(); + + assertEquals(3, full.size()); + assertEquals(2, filtered.size()); + + // Check the ordering and filtering + assertEquals(c3.getSystemName(), filtered.get(0).getName()); + assertEquals(c1.getSystemName(), filtered.get(1).getName()); + + + // Now make one repeating and check the correct info comes through + c3.setRecurrenceRule("FREQ=WEEKLY"); + c3.setLastRecurrence(new Date(1303431400)); + CALENDAR_SERVICE.updateCalendarEntry(c3); + + cq = (GetCalendarEntriesCannedQuery)CALENDAR_CQ_FACTORY.getCannedQuery( + containers, from, to, paging + ); + cq.setTestHook(hook); + cq.execute(); + assertEquals(3, full.size()); + assertEquals(2, filtered.size()); + + // Check the details + assertEquals(c3.getSystemName(), filtered.get(0).getName()); + assertEquals(ISO8601DateFormat.format(c3.getStart()), filtered.get(0).getFromDate()); + assertEquals(ISO8601DateFormat.format(c3.getEnd()), filtered.get(0).getToDate()); + assertEquals(ISO8601DateFormat.format(c3.getLastRecurrence()), filtered.get(0).getRecurrenceLastMeeting()); + assertEquals(c3.getRecurrenceRule(), filtered.get(0).getRecurrenceRule()); + + assertEquals(c1.getSystemName(), filtered.get(1).getName()); + assertEquals(ISO8601DateFormat.format(c1.getStart()), filtered.get(1).getFromDate()); + assertEquals(ISO8601DateFormat.format(c1.getEnd()), filtered.get(1).getToDate()); + assertEquals(c1.getRecurrenceRule(), filtered.get(1).getRecurrenceRule()); + assertEquals(null, filtered.get(1).getRecurrenceLastMeeting()); + } + + + // -------------------------------------------------------------------------------- + private static void createTestSites() throws Exception { final CalendarServiceImpl privateCalendarService = (CalendarServiceImpl)testContext.getBean("calendarService"); diff --git a/source/java/org/alfresco/repo/calendar/cannedqueries/CalendarEntity.java b/source/java/org/alfresco/repo/calendar/cannedqueries/CalendarEntity.java index a64c06a29f..e98fd3bc2b 100644 --- a/source/java/org/alfresco/repo/calendar/cannedqueries/CalendarEntity.java +++ b/source/java/org/alfresco/repo/calendar/cannedqueries/CalendarEntity.java @@ -39,6 +39,8 @@ public class CalendarEntity private String fromDate; private String toDate; + private String recurrenceRule; + private String recurrenceLastMeeting; // Supplemental query-related parameters private Long parentNodeId; @@ -46,6 +48,8 @@ public class CalendarEntity private Long contentTypeQNameId; private Long fromDateQNameId; private Long toDateQNameId; + private Long recurrenceRuleQNameId; + private Long recurrenceLastMeetingQNameId; /** * Default constructor @@ -55,13 +59,16 @@ public class CalendarEntity } public CalendarEntity(Long parentNodeId, Long nameQNameId, Long contentTypeQNameId, - Long fromDateQNameId, Long toDateQNameId) + Long fromDateQNameId, Long toDateQNameId, + Long recurrenceRuleQNameId, Long recurrenceLastMeetingQNameId) { this.parentNodeId = parentNodeId; this.nameQNameId = nameQNameId; this.contentTypeQNameId = contentTypeQNameId; this.fromDateQNameId = fromDateQNameId; this.toDateQNameId = toDateQNameId; + this.recurrenceRuleQNameId = recurrenceRuleQNameId; + this.recurrenceLastMeetingQNameId = recurrenceLastMeetingQNameId; } public Long getId() @@ -134,6 +141,33 @@ public class CalendarEntity this.toDate = toISO8061; } + /** + * SharePoint/Oulook rules string + */ + public String getRecurrenceRule() + { + return recurrenceRule; + } + + /** + * SharePoint/Oulook rules string + */ + public void setRecurrenceRule(String recurrenceRule) + { + this.recurrenceRule = recurrenceRule; + } + + // (ISO-8061) + public String getRecurrenceLastMeeting() + { + return recurrenceLastMeeting; + } + + public void setRecurrenceLastMeeting(String recurrenceLastMeetingISO8601) + { + this.recurrenceLastMeeting = recurrenceLastMeetingISO8601; + } + // Supplemental query-related parameters @@ -161,4 +195,14 @@ public class CalendarEntity { return toDateQNameId; } + + public Long getRecurrenceRuleQNameId() + { + return recurrenceRuleQNameId; + } + + public Long getRecurrenceLastMeetingQNameId() + { + return recurrenceLastMeetingQNameId; + } } diff --git a/source/java/org/alfresco/repo/calendar/cannedqueries/GetCalendarEntriesCannedQuery.java b/source/java/org/alfresco/repo/calendar/cannedqueries/GetCalendarEntriesCannedQuery.java index 1017106088..e08ae30769 100644 --- a/source/java/org/alfresco/repo/calendar/cannedqueries/GetCalendarEntriesCannedQuery.java +++ b/source/java/org/alfresco/repo/calendar/cannedqueries/GetCalendarEntriesCannedQuery.java @@ -33,6 +33,7 @@ import org.alfresco.repo.domain.query.CannedQueryDAO; import org.alfresco.repo.security.permissions.impl.acegi.AbstractCannedQueryPermissions; import org.alfresco.repo.security.permissions.impl.acegi.MethodSecurityBean; import org.alfresco.service.cmr.calendar.CalendarEntry; +import org.alfresco.service.cmr.calendar.CalendarRecurrenceHelper; import org.alfresco.service.cmr.calendar.CalendarService; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; @@ -59,6 +60,7 @@ public class GetCalendarEntriesCannedQuery extends AbstractCannedQueryPermission private final CannedQueryDAO cannedQueryDAO; private final TaggingService taggingService; private final NodeService nodeService; + private GetCalendarEntriesCannedQueryTestHook testHook; public GetCalendarEntriesCannedQuery( CannedQueryDAO cannedQueryDAO, @@ -101,6 +103,9 @@ public class GetCalendarEntriesCannedQuery extends AbstractCannedQueryPermission toDate = fromDate; } + String recurringRule = result.getRecurrenceRule(); + Date recurringLastDate = DefaultTypeConverter.INSTANCE.convert(Date.class, result.getRecurrenceLastMeeting()); + // Only return entries in the right period if(entriesFromDate != null) { @@ -112,13 +117,72 @@ public class GetCalendarEntriesCannedQuery extends AbstractCannedQueryPermission } if(entriesToDate != null) { - // Needs to start on or after the Filter End date + // Needs have started by the Filter To date if(fromDate == null || fromDate.after(entriesToDate)) { nextNodeIsAcceptable = false; } } + // Handle recurring events specially + if(recurringRule != null && !nextNodeIsAcceptable) + { + if(entriesToDate != null || recurringLastDate != null) + { + Date searchFrom = entriesFromDate; + if(searchFrom == null) + { + searchFrom = fromDate; + } + Date searchTo = entriesToDate; + if(searchTo == null) + { + searchTo = recurringLastDate; + } + + List dates = CalendarRecurrenceHelper.getRecurrencesOnOrAfter( + recurringRule, fromDate, toDate, recurringLastDate, + searchFrom, searchTo, false + ); + if(dates != null && dates.size() > 0) + { + // Do any of these fit? + for(Date date : dates) + { + if(entriesFromDate != null && entriesToDate != null) + { + // From and To date given, needs to sit between them + if(entriesFromDate.getTime() <= date.getTime() && + date.getTime() <= entriesToDate.getTime()) + { + nextNodeIsAcceptable = true; + break; + } + } + else if(entriesFromDate != null) + { + // From date but no end date, needs to be after the from + if(entriesFromDate.getTime() <= date.getTime()) + { + nextNodeIsAcceptable = true; + break; + } + } + else if(entriesToDate != null) + { + // End date but no start date, needs to be before the from + if(date.getTime() <= entriesToDate.getTime()) + { + nextNodeIsAcceptable = true; + break; + } + } + } + } + } + } + + // Did it make the cut if (nextNodeIsAcceptable) { filtered.add(result); @@ -155,6 +219,11 @@ public class GetCalendarEntriesCannedQuery extends AbstractCannedQueryPermission logger.debug("Base query: "+calendarEntries.size()+" in "+(System.currentTimeMillis()-start)+" msecs"); } + if(testHook != null) + { + testHook.notifyComplete(results, filtered); + } + return calendarEntries; } @@ -165,6 +234,11 @@ public class GetCalendarEntriesCannedQuery extends AbstractCannedQueryPermission return false; } + public void setTestHook(GetCalendarEntriesCannedQueryTestHook hook) + { + this.testHook = hook; + } + private class CalendarEntryImpl extends org.alfresco.repo.calendar.CalendarEntryImpl { private CalendarEntryImpl(CalendarEntity entity) diff --git a/source/java/org/alfresco/repo/calendar/cannedqueries/GetCalendarEntriesCannedQueryFactory.java b/source/java/org/alfresco/repo/calendar/cannedqueries/GetCalendarEntriesCannedQueryFactory.java index 3e3955473b..9907671c89 100644 --- a/source/java/org/alfresco/repo/calendar/cannedqueries/GetCalendarEntriesCannedQueryFactory.java +++ b/source/java/org/alfresco/repo/calendar/cannedqueries/GetCalendarEntriesCannedQueryFactory.java @@ -151,6 +151,8 @@ public class GetCalendarEntriesCannedQueryFactory extends AbstractCannedQueryFac getQNameId(CalendarModel.TYPE_EVENT), getQNameId(CalendarModel.PROP_FROM_DATE), getQNameId(CalendarModel.PROP_TO_DATE), + getQNameId(CalendarModel.PROP_RECURRENCE_RULE), + getQNameId(CalendarModel.PROP_RECURRENCE_LAST_MEETING), fromDate, toDate ); diff --git a/source/java/org/alfresco/repo/calendar/cannedqueries/GetCalendarEntriesCannedQueryParams.java b/source/java/org/alfresco/repo/calendar/cannedqueries/GetCalendarEntriesCannedQueryParams.java index 72c2f3cb55..2fc30833cc 100644 --- a/source/java/org/alfresco/repo/calendar/cannedqueries/GetCalendarEntriesCannedQueryParams.java +++ b/source/java/org/alfresco/repo/calendar/cannedqueries/GetCalendarEntriesCannedQueryParams.java @@ -37,10 +37,13 @@ public class GetCalendarEntriesCannedQueryParams extends CalendarEntity Long contentTypeQNameId, Long fromDateQNameId, Long toDateQNameId, + Long recurrenceRuleQNameId, + Long recurrenceLastMeetingQNameId, Date entriesFromDate, Date entriesToDate) { - super(null, nameQNameId, contentTypeQNameId, fromDateQNameId, toDateQNameId); + super(null, nameQNameId, contentTypeQNameId, fromDateQNameId, + toDateQNameId, recurrenceRuleQNameId, recurrenceLastMeetingQNameId); this.sitesContainerNodeIds = sitesContainerNodeIds; this.entriesFromDate = entriesFromDate; diff --git a/source/java/org/alfresco/repo/calendar/cannedqueries/GetCalendarEntriesCannedQueryTestHook.java b/source/java/org/alfresco/repo/calendar/cannedqueries/GetCalendarEntriesCannedQueryTestHook.java new file mode 100644 index 0000000000..798600ab97 --- /dev/null +++ b/source/java/org/alfresco/repo/calendar/cannedqueries/GetCalendarEntriesCannedQueryTestHook.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.calendar.cannedqueries; + +import java.util.List; + +/** + * This class provides a way for a unit test to check up on what + * the {@link GetCalendarEntriesCannedQuery} does + * + * @author Nick Burch + * @since 4.0 + */ +public interface GetCalendarEntriesCannedQueryTestHook +{ + void notifyComplete(List full, List filtered); +} diff --git a/source/java/org/alfresco/service/cmr/calendar/CalendarRecurrenceHelper.java b/source/java/org/alfresco/service/cmr/calendar/CalendarRecurrenceHelper.java index 2380a59485..9e3caabaa1 100644 --- a/source/java/org/alfresco/service/cmr/calendar/CalendarRecurrenceHelper.java +++ b/source/java/org/alfresco/service/cmr/calendar/CalendarRecurrenceHelper.java @@ -88,20 +88,28 @@ public class CalendarRecurrenceHelper */ public static Map extractRecurrenceRule(CalendarEntry entry) { - String recurrence = entry.getRecurrenceRule(); - if(recurrence == null) + return extractRecurrenceRule(entry.getRecurrenceRule()); + } + /** + * Returns the parsed calendar recurrence rule + * WARNING - Alfresco use only. Return type will likely shift to + * a real object in the near future + */ + private static Map extractRecurrenceRule(String recurrenceRule) + { + if(recurrenceRule == null) { return null; } // Turn the string into a useful map Map params = new HashMap(); - for(String rule : recurrence.split(";")) + for(String rule : recurrenceRule.split(";")) { String[] parts = rule.split("="); if(parts.length != 2) { - logger.warn("Invalid rule '" + rule + "' in recurrence: " + recurrence); + logger.warn("Invalid rule '" + rule + "' in recurrence: " + recurrenceRule); } else { @@ -123,8 +131,25 @@ public class CalendarRecurrenceHelper public static List getRecurrencesOnOrAfter(CalendarEntry entry, Date onOrAfter, Date until, boolean firstOnly) { - String recurrence = entry.getRecurrenceRule(); - if(recurrence == null) + return getRecurrencesOnOrAfter( + entry.getRecurrenceRule(), entry.getStart(), entry.getEnd(), + entry.getLastRecurrence(), onOrAfter, until, firstOnly + ); + } + + /** + * For the given Calendar Entry, return its subsequent Recurrence on or after + * the specified date, until the given limit. If it doesn't have any recurrences + * on or after the start date (either no recurrence rules, or the last recurrence + * date is before then), null will be returned. + * If requested, can stop after the first hit + * @return The next recurrence on or after the given date, or null if there aren't any + */ + public static List getRecurrencesOnOrAfter(String recurrenceRule, Date eventStart, + Date eventEnd, Date lastRecurrence, + Date onOrAfter, Date until, boolean firstOnly) + { + if(recurrenceRule == null) { // No recurrence return null; @@ -132,7 +157,6 @@ public class CalendarRecurrenceHelper // See if we're past the last recurrence date // Note - we rely on this being set for us, rather than checking the count - Date lastRecurrence = entry.getLastRecurrence(); if(lastRecurrence != null && lastRecurrence.before(onOrAfter)) { // Recurrence has stopped by this point @@ -168,7 +192,7 @@ public class CalendarRecurrenceHelper List dates = new ArrayList(); // Handle the different frequencies - Map params = extractRecurrenceRule(entry); + Map params = extractRecurrenceRule(recurrenceRule); if(params.containsKey("FREQ")) { String freq = params.get("FREQ"); @@ -187,7 +211,7 @@ public class CalendarRecurrenceHelper } Calendar currentDate = Calendar.getInstance(); - currentDate.setTime(entry.getStart()); + currentDate.setTime(eventStart); if ("DAILY".equals(freq)) { @@ -215,7 +239,7 @@ public class CalendarRecurrenceHelper } else { - logger.warn("No frequency found, possible invalid rule? " + recurrence); + logger.warn("No frequency found, possible invalid rule? " + recurrenceRule); return null; } } @@ -434,7 +458,7 @@ public class CalendarRecurrenceHelper currentDate.set(Calendar.DAY_OF_MONTH, dayOfMonth); } - while(currentDate.before(onOrAfter)) + while(currentDate.getTime().before(onOrAfter)) { currentDate.set(Calendar.YEAR, currentDate.get(Calendar.YEAR) + 1); currentDate.set(Calendar.MONTH, month); @@ -444,7 +468,7 @@ public class CalendarRecurrenceHelper { if(until != null) { - if(currentDate.after(until)) + if(currentDate.getTime().after(until)) { break; }