use of org.bedework.calfacade.BwRecurrenceInstance in project bw-calendar-engine by Bedework.
the class CoreEvents method doRecurrence.
/* Retrieves the overides for a recurring event and if required,
* retrieves the instances.
*
* The overrides we retrieve are optionally limited by date.
*
* The CalDAV spec requires that we retrieve all overrides which fall within
* the given date range AND all instances in that date range including
* overriden instances that WOULD have fallen in that range had they not been
* overriden.
*
* Thus we need to search both overrides and instances - unless no date range
* is given in which case all overrides will appear along with the instances.
*
* If the calendars parameter is non-null, as it usually will be for a call
* from getEvents, we limit the result to instances that appear within that
* set of calendars. This handles the case of an overriden instance moved to a
* different calendar, for example the trash.
*/
@SuppressWarnings("unchecked")
private void doRecurrence(final CoreEventInfo cei, final RecurringRetrievalMode recurRetrieval) throws CalFacadeException {
final BwEvent master = cei.getEvent();
final Set<String> overrides = new HashSet<>();
final CurrentAccess ca = cei.getCurrentAccess();
// Always fetch all overrides
final Collection<BwEventAnnotation> ovs = dao.eventQuery(BwEventAnnotation.class, null, null, null, master, // overrides
true, // recurRetrieval);
null);
if (ovs != null) {
for (final BwEventAnnotation override : ovs) {
final CoreEventInfo ocei = makeOverrideProxy(override, ca);
cei.addOverride(ocei);
overrides.add(ocei.getEvent().getRecurrenceId());
}
}
if ((recurRetrieval == null) || (recurRetrieval.mode != Rmode.expanded)) {
return;
}
/* Create a list of all instance date/times before overrides. */
final int maxYears;
final int maxInstances;
maxYears = getAuthprops().getMaxYears();
maxInstances = getAuthprops().getMaxInstances();
final RecurPeriods rp = RecurUtil.getPeriods(master, maxYears, maxInstances);
if (rp.instances.isEmpty()) {
// No instances for an alleged recurring event.
return;
// throw new CalFacadeException(CalFacadeException.noRecurrenceInstances);
}
final String stzid = master.getDtstart().getTzid();
final boolean dateOnly = master.getDtstart().getDateType();
/* Emit all instances that aren't overridden. */
final TreeSet<CoreEventInfo> ceis = new TreeSet<>();
for (final Period p : rp.instances) {
String dtval = p.getStart().toString();
if (dateOnly) {
dtval = dtval.substring(0, 8);
}
final BwDateTime rstart = BwDateTime.makeBwDateTime(dateOnly, dtval, stzid);
if (overrides.contains(rstart.getDate())) {
// Overrides built separately - skip this instance.
continue;
}
final String recurrenceId = rstart.getDate();
dtval = p.getEnd().toString();
if (dateOnly) {
dtval = dtval.substring(0, 8);
}
final BwDateTime rend = BwDateTime.makeBwDateTime(dateOnly, dtval, stzid);
final BwRecurrenceInstance inst = new BwRecurrenceInstance(rstart, rend, recurrenceId, master, null);
final CoreEventInfo instcei = makeInstanceProxy(inst, ca);
if (instcei != null) {
// if (debug) {
// debugMsg("Ev: " + proxy);
// }
ceis.add(instcei);
}
}
cei.setInstances(ceis);
}
use of org.bedework.calfacade.BwRecurrenceInstance in project bw-calendar-engine by Bedework.
the class CoreEvents method updateEvent.
@Override
public UpdateEventResult updateEvent(final EventInfo ei) throws CalFacadeException {
final BwEvent val = ei.getEvent();
final Collection<BwEventProxy> overrides = ei.getOverrideProxies();
final Collection<BwEventProxy> deletedOverrides = ei.getDeletedOverrideProxies(cb.getPrincipalInfo().getPrincipal().getPrincipalRef());
final UpdateEventResult ue = new UpdateEventResult();
if (!ac.checkAccess(val, privWrite, true).getAccessAllowed()) {
// XXX Is this correct?
try {
ac.checkAccess(val, privWriteContent, false);
} catch (final CalFacadeException cfe) {
throwException(cfe);
}
}
BwEventProxy proxy = null;
if (val instanceof BwEventProxy) {
proxy = (BwEventProxy) val;
}
final BwCalendar col = getCollection(val.getColPath());
final boolean shared = col.getPublick() || col.getShared();
if ((proxy != null) && (proxy.getRef().getOverride())) {
final BwEventAnnotation ann = proxy.getRef();
final BwEvent mstr = ann.getMaster();
if (!proxy.getUid().equals(mstr.getUid())) {
throwException("org.bedework.cannot.overrideuid");
}
if (!proxy.getName().equals(mstr.getName())) {
throwException("org.bedework.cannot.overridename");
}
} else {
/* The guid must not exist in the same calendar. We assign a guid if
* one wasn't assigned already. However, the event may have come with a guid
* (caldav, import, etc) so we need to check here.
*
* It also ensures our guid allocation is working OK
*/
final CollectionInfo collInf = col.getCollectionInfo();
if (collInf.uniqueKey) {
String name = calendarGuidExists(val, false, false);
if (name == null) {
name = calendarGuidExists(val, true, false);
}
if (name != null) {
throwException(CalFacadeException.duplicateGuid, name);
}
}
/* Similarly for event names which must be unique within a collection
*/
if (calendarNameExists(val, false, false) || calendarNameExists(val, true, false)) {
throwException(new CalFacadeDupNameException(val.getName()));
}
}
if (!(val instanceof BwEventProxy)) {
dao.update(val);
final Collection<BwDbentity<?>> deleted = val.getDeletedEntities();
if (deleted != null) {
for (final BwDbentity ent : deleted) {
dao.delete(ent);
}
deleted.clear();
}
if (val.testRecurring()) {
if (!Util.isEmpty(overrides)) {
for (final BwEventProxy pxy : overrides) {
final BwEventAnnotation ann = pxy.getRef();
boolean updated = false;
if ((ann.getRecurring() != null) && ann.getRecurring()) {
// be safe
ann.setRecurring(false);
updated = true;
}
if (ann.getTombstoned() == null) {
// be safe
ann.setTombstoned(false);
updated = true;
}
if (ann.unsaved()) {
dao.save(ann);
} else if (updated) {
updateProxy(new BwEventProxy(ann));
}
}
}
updateRecurrences(ei, ue, overrides, shared);
}
if (!val.testRecurring() || (Util.isEmpty(overrides) && Util.isEmpty(deletedOverrides))) {
notify(SysEvent.SysCode.ENTITY_UPDATED, val, shared);
indexEntity(ei);
return ue;
}
if (!Util.isEmpty(overrides)) {
updateOverrides: for (final BwEventProxy pxy : overrides) {
final BwEventAnnotation ann = pxy.getRef();
if (ue.deleted != null) {
for (final BwRecurrenceInstance ri : ue.deleted) {
if (ri.getRecurrenceId().equals(ann.getRecurrenceId())) {
continue updateOverrides;
}
}
}
if (ue.added != null) {
for (final BwRecurrenceInstance ri : ue.added) {
if (ri.getRecurrenceId().equals(ann.getRecurrenceId())) {
continue updateOverrides;
}
}
}
// be safe
ann.setRecurring(false);
if (ann.getTombstoned() == null) {
// be safe
ann.setTombstoned(false);
}
if (!ann.unsaved()) {
updateProxy(new BwEventProxy(ann));
} else {
dao.save(ann);
/* See if there is an instance for this override
*/
BwRecurrenceInstance ri = dao.getInstance(val, ann.getRecurrenceId());
if (ri == null) {
final BwDateTime rid = BwDateTime.fromUTC(ann.getRecurrenceId().length() == 8, ann.getRecurrenceId());
final Dur dur = new Dur(val.getDuration());
final BwDateTime end = rid.addDur(dur);
ri = new BwRecurrenceInstance();
ri.setDtstart(rid);
ri.setDtend(end);
ri.setRecurrenceId(rid.getDate());
ri.setMaster(val);
ri.setOverride(ann);
dao.save(ri);
} else {
ri.setOverride(ann);
dao.update(ri);
}
}
notifyInstanceChange(SysEvent.SysCode.ENTITY_UPDATED, val, shared, ann.getRecurrenceId());
}
}
if (!Util.isEmpty(deletedOverrides)) {
final Collection<String> rids = new ArrayList<>();
for (final BwEventProxy pxy : deletedOverrides) {
rids.add(pxy.getRecurrenceId());
}
removeInstances(val, rids, ue, deletedOverrides, shared);
}
} else {
if (proxy.getChangeFlag()) {
updateProxy(proxy);
}
}
notify(SysEvent.SysCode.ENTITY_UPDATED, val, shared);
if (proxy != null) {
if (ei.getRetrievedEvent() == null) {
warn("No retrieved event for indexer");
} else {
final EventInfo rei = ei.getRetrievedEvent();
rei.addOverride(ei);
indexEntity(rei);
}
} else {
indexEntity(ei);
}
return ue;
}
use of org.bedework.calfacade.BwRecurrenceInstance in project bw-calendar-engine by Bedework.
the class CoreEvents method addEvent.
@Override
public UpdateEventResult addEvent(final EventInfo ei, final boolean scheduling, final boolean rollbackOnError) throws CalFacadeException {
final BwEvent val = ei.getEvent();
final Collection<BwEventProxy> overrides = ei.getOverrideProxies();
final long startTime = System.currentTimeMillis();
RecuridTable recurids = null;
final UpdateEventResult uer = new UpdateEventResult();
uer.addedUpdated = true;
final BwCalendar cal = getEntityCollection(val.getColPath(), privBind, scheduling, false);
/* Indicate if we want sharing notifications of changes */
final boolean shared = cal.getPublick() || cal.getShared();
final CollectionInfo collInf = cal.getCollectionInfo();
if (!Util.isEmpty(overrides)) {
if (!val.testRecurring()) {
throwException(CalFacadeException.overridesForNonRecurring);
}
recurids = new RecuridTable(overrides);
}
if (val.getUid() == null) {
throwException(CalFacadeException.noEventGuid);
}
if (val.getName() == null) {
throwException(CalFacadeException.noEventName);
}
/* The guid must not exist in the same calendar. We assign a guid if
* one wasn't assigned already. However, the event may have come with a guid
* (caldav, import, etc) so we need to check here.
*
* It also ensures our guid allocation is working OK
*/
if (collInf.uniqueKey) {
String name = calendarGuidExists(val, false, true);
if (name == null) {
name = calendarGuidExists(val, true, true);
}
if (name != null) {
throwException(CalFacadeException.duplicateGuid, name);
}
}
/* Similarly for event names which must be unique within a collection.
* Note that a duplicate name is essentially overwriting an event with a
* new uid - also disallowed.
*/
if ((val.getEntityType() != IcalDefs.entityTypeAvailable) && (calendarNameExists(val, false, true) || calendarNameExists(val, true, true))) {
throwException(CalFacadeException.duplicateName, val.getName());
}
setupDependentEntities(val);
/* Remove any tombstoned event in the collection with same uid */
deleteTombstoned(val.getColPath(), val.getUid());
/* If it's a recurring event see what we can do to optimize searching
* and retrieval
*/
if ((val instanceof BwEventAnnotation) || !val.getRecurring()) {
dao.save(val);
if (!getForRestore()) {
notify(SysEvent.SysCode.ENTITY_ADDED, val, shared);
}
stat(StatsEvent.createTime, startTime);
indexEntity(ei);
return uer;
}
/* Get all the times for this event. - this could be a problem. Need to
limit the number. Should we do this in chunks, stepping through the
whole period?
*/
final RecurPeriods rp = RecurUtil.getPeriods(val, getAuthprops().getMaxYears(), getAuthprops().getMaxInstances());
if (rp.instances.isEmpty()) {
// No instances for an alleged recurring event.
if (rollbackOnError) {
throwException(CalFacadeException.noRecurrenceInstances, val.getUid());
}
uer.addedUpdated = false;
uer.errorCode = CalFacadeException.noRecurrenceInstances;
stat(StatsEvent.createTime, startTime);
indexEntity(ei);
return uer;
}
/* We can save the master at this point */
dao.save(val);
final String stzid = val.getDtstart().getTzid();
final TimeZone stz = null;
/* try {
if (stzid != null) {
stz = Timezones.getTz(stzid);
}
val.setLatestDate(Timezones.getUtc(rp.rangeEnd.toString(),
stzid));
} catch (Throwable t) {
throwException(new CalFacadeException(t));
} */
int maxInstances = getAuthprops().getMaxInstances();
final boolean dateOnly = val.getDtstart().getDateType();
/* There appears to be a bug in ical4j in which the first instance gets
* duplicated. Rather than change that code and run the risk of breaking
* all recurrences I'll just look for that duplicate.
*/
String firstRecurrenceId = null;
for (final Period p : rp.instances) {
String dtval = p.getStart().toString();
if (dateOnly) {
dtval = dtval.substring(0, 8);
}
final BwDateTime rstart = BwDateTime.makeBwDateTime(dateOnly, dtval, stzid);
final DateTime edt = p.getEnd();
if (!dateOnly && (stz != null)) {
edt.setTimeZone(stz);
}
dtval = edt.toString();
if (dateOnly) {
dtval = dtval.substring(0, 8);
}
final BwDateTime rend = BwDateTime.makeBwDateTime(dateOnly, dtval, stzid);
final BwRecurrenceInstance ri = new BwRecurrenceInstance();
ri.setDtstart(rstart);
ri.setDtend(rend);
ri.setRecurrenceId(ri.getDtstart().getDate());
ri.setMaster(val);
if (firstRecurrenceId == null) {
firstRecurrenceId = ri.getRecurrenceId();
} else if (firstRecurrenceId.equals(ri.getRecurrenceId())) {
// Skip it
if (debug) {
debugMsg("Skipping duplicate recurid " + firstRecurrenceId);
}
continue;
}
if (recurids != null) {
/* See if we have a recurrence */
final String rid = ri.getRecurrenceId();
final BwEventProxy ov = recurids.get(rid);
if (ov != null) {
if (debug) {
debugMsg("Add override with recurid " + rid);
}
setupDependentEntities(ov);
addOverride(ov, ri);
recurids.remove(rid);
}
}
dao.save(ri);
maxInstances--;
if (maxInstances == 0) {
// That's all you're getting from me
break;
}
}
if ((recurids != null) && (recurids.size() != 0)) {
/* We removed all the valid overrides - we are left with those
* with recurrence ids that don't match.
*/
if (rollbackOnError) {
throwException(CalFacadeException.invalidOverride);
}
uer.failedOverrides = recurids.values();
}
if (!getForRestore()) {
notify(SysEvent.SysCode.ENTITY_ADDED, val, shared);
}
indexEntity(ei);
stat(StatsEvent.createTime, startTime);
return uer;
}
use of org.bedework.calfacade.BwRecurrenceInstance in project bw-calendar-engine by Bedework.
the class CoreEvents method removeInstance.
/* Remove instances identified by the Collection of recurrence ids
*/
private void removeInstance(final BwEvent master, final UpdateEventResult uc, final Collection<BwEventProxy> overrides, final String rid, final boolean shared) throws CalFacadeException {
notifyInstanceChange(SysEvent.SysCode.ENTITY_DELETED, master, shared, rid);
if (overrides != null) {
for (final BwEventProxy pr : overrides) {
if (pr.getRecurrenceId() == null) {
throw new NullPointerException();
}
if (pr.getRecurrenceId().equals(rid)) {
// This one is being deleted
overrides.remove(pr);
break;
}
}
}
final BwRecurrenceInstance inst = dao.getInstance(master, rid);
if (inst != null) {
dao.delete(inst);
uc.addDeleted(inst);
}
}
use of org.bedework.calfacade.BwRecurrenceInstance in project bw-calendar-engine by Bedework.
the class CoreEvents method addInstances.
/* Add instances identified by the Collection of recurrence ids
*/
private void addInstances(final BwEvent master, final UpdateEventResult uc, final Collection<BwEventProxy> overrides, final Collection rids, final boolean shared) throws CalFacadeException {
final Dur dur = new Dur(master.getDuration());
for (final Object rid : rids) {
final BwDateTime start = (BwDateTime) rid;
final BwDateTime end = start.addDur(dur);
final BwRecurrenceInstance ri = new BwRecurrenceInstance();
ri.setDtstart(start);
ri.setDtend(end);
ri.setRecurrenceId(start.getDate());
ri.setMaster(master);
if (!Util.isEmpty(overrides)) {
for (final BwEventProxy pxy : overrides) {
final BwEventAnnotation ann = pxy.getRef();
if (!ann.getRecurrenceId().equals(ri.getRecurrenceId())) {
continue;
}
// be safe
ann.setRecurring(false);
if (ann.getTombstoned() == null) {
// be safe
ann.setTombstoned(false);
}
if (!ann.unsaved()) {
updateProxy(new BwEventProxy(ann));
} else {
dao.save(ann);
}
ri.setOverride(ann);
break;
}
}
dao.save(ri);
notifyInstanceChange(SysEvent.SysCode.ENTITY_ADDED, master, shared, start.getDate());
uc.addAdded(ri);
}
}
Aggregations