org.alfresco.repo.web.scripts.calendar.AbstractCalendarListingWebScript.java Source code

Java tutorial

Introduction

Here is the source code for org.alfresco.repo.web.scripts.calendar.AbstractCalendarListingWebScript.java

Source

/*
 * #%L
 * Alfresco Remote API
 * %%
 * Copyright (C) 2005 - 2016 Alfresco Software Limited
 * %%
 * This file is part of the Alfresco software. 
 * If the software was purchased under a paid Alfresco license, the terms of 
 * the paid license agreement will prevail.  Otherwise, the software is 
 * provided under the following open source license terms:
 * 
 * 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 <http://www.gnu.org/licenses/>.
 * #L%
 */
package org.alfresco.repo.web.scripts.calendar;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.alfresco.repo.calendar.CalendarModel;
import org.alfresco.service.cmr.calendar.CalendarEntry;
import org.alfresco.service.cmr.calendar.CalendarRecurrenceHelper;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.ISO8601DateFormat;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;

/**
 * This class provides functionality common across the webscripts
 *  which list events.
 * 
 * @author Nick Burch
 * @since 4.0
 */
public abstract class AbstractCalendarListingWebScript extends AbstractCalendarWebScript {
    protected static final String RESULT_EVENT = "event";
    protected static final String RESULT_NAME = "name";
    protected static final String RESULT_TITLE = "title";
    protected static final String RESULT_START = "start";
    protected static final String RESULT_END = "end";

    /**
     * Returns a Comparator for (re-)sorting events, typically used after
     *  expanding out recurring instances.
     */
    protected static Comparator<Map<String, Object>> getEventDetailsSorter() {
        return new Comparator<Map<String, Object>>() {
            public int compare(Map<String, Object> resultA, Map<String, Object> resultB) {
                DateTimeFormatter fmtNoTz = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSS");
                DateTimeFormatter fmtTz = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZZ");

                String startA = (String) resultA.get(RESULT_START);
                String startB = (String) resultB.get(RESULT_START);

                startA = startA.replace("Z", "+00:00");
                startB = startB.replace("Z", "+00:00");

                //check and parse iso8601 date without time zone (All day events are stripped of time zone)
                DateTime sa = startA.length() > 23 ? fmtTz.parseDateTime(startA) : fmtNoTz.parseDateTime(startA);
                DateTime sb = startB.length() > 23 ? fmtTz.parseDateTime(startB) : fmtNoTz.parseDateTime(startB);

                int cmp = sa.compareTo(sb);
                if (cmp == 0) {
                    String endA = (String) resultA.get(RESULT_END);
                    String endB = (String) resultB.get(RESULT_END);

                    DateTime ea = endA.length() > 23 ? fmtTz.parseDateTime(endA) : fmtNoTz.parseDateTime(endA);
                    DateTime eb = endB.length() > 23 ? fmtTz.parseDateTime(endB) : fmtNoTz.parseDateTime(endB);

                    cmp = ea.compareTo(eb);
                    if (cmp == 0) {
                        String nameA = (String) resultA.get(RESULT_NAME);
                        String nameB = (String) resultB.get(RESULT_NAME);
                        return nameA.compareTo(nameB);
                    }
                    return cmp;
                }
                return cmp;
            }
        };
    }

    /**
     * Do what's needed for recurring events.
     * 
     * @return If dates have been tweaked, and a sort may be required 
     */
    protected boolean handleRecurring(CalendarEntry entry, Map<String, Object> entryResult,
            List<Map<String, Object>> allResults, Date from, Date until, boolean repeatingFirstOnly) {
        if (entry.getRecurrenceRule() == null) {
            // Nothing to do
            return false;
        }

        // If no date is given, start looking for occurrences from the event itself
        if (from == null) {
            from = entry.getStart();
        }

        // Do we need to limit ourselves?
        // Should we limit ourselves?
        if (!repeatingFirstOnly) {
            if (until == null) {
                // If no end date was given, only allow repeating instances 
                // for next 60 days, to keep the list sane
                // (It's normally only used for a month view anyway)
                Calendar c = Calendar.getInstance();
                c.setTime(from);
                c.add(Calendar.DATE, 60);
                until = c.getTime();
            }
        }

        // How long is it?
        long duration = entry.getEnd().getTime() - entry.getStart().getTime();

        // if some instances were deleted from series ignore them
        Set<QName> childNodeTypeQNames = new HashSet<QName>();
        childNodeTypeQNames.add(CalendarModel.TYPE_IGNORE_EVENT);
        List<ChildAssociationRef> ignoreEventList = nodeService.getChildAssocs(entry.getNodeRef(),
                childNodeTypeQNames);
        Set<Date> ignoredDates = new HashSet<Date>();
        for (ChildAssociationRef ignoreEvent : ignoreEventList) {
            NodeRef nodeRef = ignoreEvent.getChildRef();
            Date ignoredDate = (Date) nodeService.getProperty(nodeRef, CalendarModel.PROP_IGNORE_EVENT_DATE);
            ignoredDates.add(ignoredDate);
        }

        // Get it's recurring instances
        List<Date> dates = CalendarRecurrenceHelper.getRecurrencesOnOrAfter(entry, from, until, repeatingFirstOnly,
                ignoredDates);
        if (dates == null) {
            dates = new ArrayList<Date>();
        }

        // Add on the original event time itself if needed
        if (entry.getStart().getTime() >= from.getTime()) {
            if (dates.size() == 0 || dates.get(0).getTime() != entry.getStart().getTime()) {
                // Original event is after the start time, and not on the recurring list
                dates.add(0, entry.getStart());
            }
        }

        // If we got no dates, then no recurrences in the period so zap
        if (dates.size() == 0) {
            allResults.remove(entryResult);
            return false; // Remains sorted despite delete
        }

        // if some instances were updated
        SimpleDateFormat fmt = new SimpleDateFormat("yyyyMMdd");
        childNodeTypeQNames = new HashSet<QName>();
        childNodeTypeQNames.add(CalendarModel.TYPE_UPDATED_EVENT);
        List<ChildAssociationRef> updatedEventList = nodeService.getChildAssocs(entry.getNodeRef(),
                childNodeTypeQNames);
        Map<String, Object> updatedDates = new HashMap<String, Object>();
        for (ChildAssociationRef updatedEvent : updatedEventList) {
            NodeRef nodeRef = updatedEvent.getChildRef();
            Date updatedDate = (Date) nodeService.getProperty(nodeRef, CalendarModel.PROP_UPDATED_EVENT_DATE);
            Date newStart = (Date) nodeService.getProperty(nodeRef, CalendarModel.PROP_UPDATED_START);
            Date newEnd = (Date) nodeService.getProperty(nodeRef, CalendarModel.PROP_UPDATED_END);
            String newWhere = (String) nodeService.getProperty(nodeRef, CalendarModel.PROP_UPDATED_WHERE);
            String newWhat = (String) nodeService.getProperty(nodeRef, CalendarModel.PROP_UPDATED_WHAT);
            updatedDates.put(fmt.format(updatedDate), new Date[] { newStart, newEnd });
            updatedDates.put(fmt.format(updatedDate).toString() + "where", newWhere);
            updatedDates.put(fmt.format(updatedDate).toString() + "what", newWhat);
        }

        // first occurrence can be edited as separate event
        Date liveEntry = dates.get(0);

        // If first result only, alter title and finish
        if (repeatingFirstOnly) {
            entryResult.put(RESULT_TITLE, entry.getTitle() + " (Repeating)");

            updateRepeating(entry, updatedDates, entryResult, duration, fmt, liveEntry);
            return true; // Date has been changed
        } else {
            // Otherwise generate one entry per extra date
            for (int i = 1; i < dates.size(); i++) {
                // Clone the properties
                Map<String, Object> newResult = new HashMap<String, Object>(entryResult);

                Date extra = dates.get(i);

                updateRepeating(entry, updatedDates, newResult, duration, fmt, extra);

                // Save as a new event
                allResults.add(newResult);
            }

            updateRepeating(entry, updatedDates, entryResult, duration, fmt, liveEntry);
        }

        // TODO Skip ignored instances

        // New dates have been added
        return true;
    }

    private void updateRepeatingStartEnd(Date newStart, long duration, Map<String, Object> result) {
        Date newEnd = new Date(newStart.getTime() + duration);
        result.put(RESULT_START, ISO8601DateFormat.format(newStart));
        result.put(RESULT_END, ISO8601DateFormat.format(newEnd));
        String legacyDateFormat = "yyyy-MM-dd";
        SimpleDateFormat ldf = new SimpleDateFormat(legacyDateFormat);
        String legacyTimeFormat = "HH:mm";
        SimpleDateFormat ltf = new SimpleDateFormat(legacyTimeFormat);
        result.put("legacyDateFrom", ldf.format(newStart));
        result.put("legacyTimeFrom", ltf.format(newStart));
        result.put("legacyDateTo", ldf.format(newEnd));
        result.put("legacyTimeTo", ltf.format(newEnd));
    }

    private void updateRepeating(CalendarEntry entry, Map<String, Object> updatedDates,
            Map<String, Object> entryResult, long duration, SimpleDateFormat fmt, Date date) {
        if (updatedDates.keySet().contains(fmt.format(date))) {
            // there is day that was edited
            Date[] newValues = (Date[]) updatedDates.get(fmt.format(date));
            long newDuration = newValues[1].getTime() - newValues[0].getTime();

            entryResult.put(RESULT_TITLE, (String) updatedDates.get(fmt.format(date).toString() + "what"));
            entryResult.put("where", (String) updatedDates.get(fmt.format(date).toString() + "where"));

            updateRepeatingStartEnd(newValues[0], newDuration, entryResult);
        } else {
            // Update entry
            updateRepeatingStartEnd(date, duration, entryResult);
        }
    }
}