Author: adrianc
Date: Tue Nov 18 10:56:11 2008 New Revision: 718684 URL: http://svn.apache.org/viewvc?rev=718684&view=rev Log: A rudimentary iCalendar integration implementation. Work efforts can be published in iCalendar format. The published calendar is read-only. Run ant run-install, then navigate to: https://localhost:8443/iCalendar/CALENDAR_PUB_DEMO/MyCalendar.ics for a demonstration. I will continue to build this out as I have time. Added: ofbiz/trunk/applications/workeffort/src/org/ofbiz/workeffort/workeffort/ICalRecurConverter.java (with props) ofbiz/trunk/applications/workeffort/src/org/ofbiz/workeffort/workeffort/ICalServlet.java (with props) ofbiz/trunk/applications/workeffort/src/org/ofbiz/workeffort/workeffort/ICalendarWorker.java (with props) ofbiz/trunk/applications/workeffort/webapp/ical/ ofbiz/trunk/applications/workeffort/webapp/ical/WEB-INF/ ofbiz/trunk/applications/workeffort/webapp/ical/WEB-INF/web.xml (with props) ofbiz/trunk/framework/base/lib/ical4j-1.0-beta5.jar (with props) Modified: ofbiz/trunk/.classpath ofbiz/trunk/LICENSE ofbiz/trunk/applications/workeffort/build.xml ofbiz/trunk/applications/workeffort/data/WorkEffortTypeData.xml ofbiz/trunk/applications/workeffort/entitydef/entitymodel_view.xml ofbiz/trunk/applications/workeffort/ofbiz-component.xml ofbiz/trunk/specialpurpose/projectmgr/data/ProjectMgrDemoData.xml Modified: ofbiz/trunk/.classpath URL: http://svn.apache.org/viewvc/ofbiz/trunk/.classpath?rev=718684&r1=718683&r2=718684&view=diff ============================================================================== --- ofbiz/trunk/.classpath (original) +++ ofbiz/trunk/.classpath Tue Nov 18 10:56:11 2008 @@ -90,6 +90,7 @@ <classpathentry kind="lib" path="framework/geronimo/lib/geronimo-transaction-2.1.1.jar"/> <classpathentry kind="lib" path="framework/entity/lib/ofbiz-minerva.jar"/> <classpathentry kind="lib" path="framework/entity/lib/commons-dbcp-1.3-20080708-r674758.jar"/> + <classpathentry kind="lib" path="framework/base/lib/ical4j-1.0-beta5.jar"/> <classpathentry kind="lib" path="framework/base/lib/Tidy.jar"/> <classpathentry kind="lib" path="framework/base/lib/mx4j-remote-3.0.1.jar"/> <classpathentry kind="lib" path="framework/base/lib/mx4j-3.0.1.jar"/> Modified: ofbiz/trunk/LICENSE URL: http://svn.apache.org/viewvc/ofbiz/trunk/LICENSE?rev=718684&r1=718683&r2=718684&view=diff ============================================================================== --- ofbiz/trunk/LICENSE (original) +++ ofbiz/trunk/LICENSE Tue Nov 18 10:56:11 2008 @@ -420,21 +420,22 @@ ========================================================================= The following libraries distributed with Apache OFBiz are licensed under the BSD License: -ofbiz/trunk/framework/base/lib/javacc/javacc-4.1.jar -ofbiz/trunk/framework/base/lib/httpunit.jar ofbiz/trunk/framework/base/lib/freemarker-2.3.10.jar +ofbiz/trunk/framework/base/lib/httpunit.jar +ofbiz/trunk/framework/base/lib/ical4j-1.0-beta5.jar +ofbiz/trunk/framework/base/lib/javacc/javacc-4.1.jar +ofbiz/trunk/framework/base/lib/javolution-5.2.3.jar ofbiz/trunk/framework/base/lib/junitperf.jar ofbiz/trunk/framework/base/lib/scripting/antlr-2.7.6.jar ofbiz/trunk/framework/base/lib/scripting/asm-2.2.jar ofbiz/trunk/framework/base/lib/scripting/asm-analysis-2.2.jar ofbiz/trunk/framework/base/lib/scripting/asm-tree-2.2.jar ofbiz/trunk/framework/base/lib/scripting/asm-util-2.2.jar -ofbiz/trunk/specialpurpose/pos/lib/looks-2.0.2.jar -ofbiz/trunk/framework/base/lib/javolution-5.2.3.jar -ofbiz/trunk/framework/images/webapp/images/dojo/* -ofbiz/trunk/specialpurpose/ldap/lib/cas-server-core-3.3.jar ofbiz/trunk/framework/images/webapp/images/jsgantt.css ofbiz/trunk/framework/images/webapp/images/jsgantt.js +ofbiz/trunk/framework/images/webapp/images/dojo/* +ofbiz/trunk/specialpurpose/ldap/lib/cas-server-core-3.3.jar +ofbiz/trunk/specialpurpose/pos/lib/looks-2.0.2.jar ========================================================================= The BSD License Modified: ofbiz/trunk/applications/workeffort/build.xml URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/workeffort/build.xml?rev=718684&r1=718683&r2=718684&view=diff ============================================================================== --- ofbiz/trunk/applications/workeffort/build.xml (original) +++ ofbiz/trunk/applications/workeffort/build.xml Tue Nov 18 10:56:11 2008 @@ -35,6 +35,7 @@ <fileset dir="../../framework/base/lib/j2eespecs" includes="*.jar"/> <fileset dir="../../framework/base/lib/scripting" includes="*.jar"/> <fileset dir="../../framework/base/build/lib" includes="*.jar"/> + <fileset dir="../../framework/catalina/lib" includes="*.jar"/> <fileset dir="../../framework/entity/lib" includes="*.jar"/> <fileset dir="../../framework/entity/build/lib" includes="*.jar"/> <fileset dir="../../framework/security/build/lib" includes="*.jar"/> Modified: ofbiz/trunk/applications/workeffort/data/WorkEffortTypeData.xml URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/workeffort/data/WorkEffortTypeData.xml?rev=718684&r1=718683&r2=718684&view=diff ============================================================================== --- ofbiz/trunk/applications/workeffort/data/WorkEffortTypeData.xml (original) +++ ofbiz/trunk/applications/workeffort/data/WorkEffortTypeData.xml Tue Nov 18 10:56:11 2008 @@ -180,7 +180,8 @@ <WorkEffortType description="Business Travel" hasTable="N" parentTypeId="EVENT" workEffortTypeId="BUSINESS_TRAVEL"/> <WorkEffortType description="Meeting" hasTable="N" parentTypeId="EVENT" workEffortTypeId="MEETING"/> <WorkEffortType description="Personal Time Off" hasTable="N" parentTypeId="EVENT" workEffortTypeId="PERSONAL_TIMEOFF"/> - + <WorkEffortType description="Publish Properties" hasTable="N" workEffortTypeId="PUBLISH_PROPS"/> + <!-- Routing status, (workEffort Template) --> <StatusType description="Manufacturing Task and Routing status" hasTable="N" parentTypeId="WORK_EFFORT_STATUS" statusTypeId="ROUTING_STATUS"/> Modified: ofbiz/trunk/applications/workeffort/entitydef/entitymodel_view.xml URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/workeffort/entitydef/entitymodel_view.xml?rev=718684&r1=718683&r2=718684&view=diff ============================================================================== --- ofbiz/trunk/applications/workeffort/entitydef/entitymodel_view.xml (original) +++ ofbiz/trunk/applications/workeffort/entitydef/entitymodel_view.xml Tue Nov 18 10:56:11 2008 @@ -258,6 +258,30 @@ <key-map field-name="workEffortIdFrom" rel-field-name="workEffortId"/> </relation> </view-entity> + <view-entity entity-name="WorkEffortAssocToView" + package-name="org.ofbiz.workeffort.workeffort" + title="Work Effort Association To (Child) View"> + <member-entity entity-alias="WEA" entity-name="WorkEffortAssoc"/> + <member-entity entity-alias="WETO" entity-name="WorkEffort"/> + <alias-all entity-alias="WEA"/> + <alias-all entity-alias="WETO"/> + <view-link entity-alias="WEA" rel-entity-alias="WETO"> + <key-map field-name="workEffortIdTo" rel-field-name="workEffortId"/> + </view-link> + </view-entity> + <!-- + <view-entity entity-name="WorkEffortAssocFromView" + package-name="org.ofbiz.workeffort.workeffort" + title="Work Effort Association From (Parent) View"> + <member-entity entity-alias="WEA" entity-name="WorkEffortAssoc"/> + <member-entity entity-alias="WEFR" entity-name="WorkEffort"/> + <alias-all entity-alias="WEA"/> + <alias-all entity-alias="WEFR"/> + <view-link entity-alias="WEA" rel-entity-alias="WEFR"> + <key-map field-name="workEffortIdFrom" rel-field-name="workEffortId"/> + </view-link> + </view-entity> + --> <view-entity entity-name="WorkEffortNoteAndData" package-name="org.ofbiz.workeffort.workeffort" title="Work Effort Note And Note Data Entity"> Modified: ofbiz/trunk/applications/workeffort/ofbiz-component.xml URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/workeffort/ofbiz-component.xml?rev=718684&r1=718683&r2=718684&view=diff ============================================================================== --- ofbiz/trunk/applications/workeffort/ofbiz-component.xml (original) +++ ofbiz/trunk/applications/workeffort/ofbiz-component.xml Tue Nov 18 10:56:11 2008 @@ -43,4 +43,11 @@ location="webapp/workeffort" base-permission="OFBTOOLS,WORKEFFORTMGR" mount-point="/workeffort"/> + + <webapp name="ical" + title="iCalendar" + app-bar-display="false" + server="default-server" + location="webapp/ical" + mount-point="/iCalendar"/> </ofbiz-component> Added: ofbiz/trunk/applications/workeffort/src/org/ofbiz/workeffort/workeffort/ICalRecurConverter.java URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/workeffort/src/org/ofbiz/workeffort/workeffort/ICalRecurConverter.java?rev=718684&view=auto ============================================================================== --- ofbiz/trunk/applications/workeffort/src/org/ofbiz/workeffort/workeffort/ICalRecurConverter.java (added) +++ ofbiz/trunk/applications/workeffort/src/org/ofbiz/workeffort/workeffort/ICalRecurConverter.java Tue Nov 18 10:56:11 2008 @@ -0,0 +1,293 @@ +/******************************************************************************* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + *******************************************************************************/ + +package org.ofbiz.workeffort.workeffort; + +import java.util.List; +import java.util.Set; +import java.util.Stack; + +import javolution.util.FastList; +import javolution.util.FastSet; + +import net.fortuna.ical4j.model.*; +import net.fortuna.ical4j.model.property.*; + +import org.ofbiz.service.calendar.TemporalExpression; +import org.ofbiz.service.calendar.TemporalExpressions; +import org.ofbiz.service.calendar.TemporalExpressionVisitor; +import org.ofbiz.service.calendar.TemporalExpressions.*; + +/** Temporal Expression to iCalendar recurrence converter. The conversion results + * (or conversion success) are unpredictable since the OFBiz Temporal Expressions + * are more sophisticated than iCalendar recurrences. This class attempts to + * make a best attempt at conversion and throws <code>IllegalStateException</code> + * when conversion is not possible. + */ +public class ICalRecurConverter implements TemporalExpressionVisitor { + protected static final WeekDay dayOfWeekArray[] = {WeekDay.SU, WeekDay.MO, WeekDay.TU, WeekDay.WE, WeekDay.TH, WeekDay.FR, WeekDay.SA}; + protected DtStart dateStart = null; + protected List<DateListProperty> incDateList = FastList.newInstance(); + protected List<DateListProperty> exDateList = FastList.newInstance(); + protected List<RRule> incRuleList = FastList.newInstance(); + protected List<ExRule> exRuleList = FastList.newInstance(); + protected VisitorState state = new VisitorState(); + protected Stack<VisitorState> stateStack = new Stack<VisitorState>(); + + protected ICalRecurConverter() {} + + @SuppressWarnings("unchecked") + public static void convert(TemporalExpression expr, PropertyList eventProps) { + ICalRecurConverter converter = new ICalRecurConverter(); + expr.accept(converter); + DtStart dateStart = (DtStart) eventProps.getProperty(Property.DTSTART); + if (converter.dateStart != null) { + if (dateStart != null) { + eventProps.remove(dateStart); + } + dateStart = converter.dateStart; + eventProps.add(dateStart); + } + if (dateStart != null && converter.exRuleList.size() > 0) { + // iCalendar quirk - if exclusions exist, then the start date must be excluded also + ExDate exdate = new ExDate(); + exdate.getDates().add(dateStart.getDate()); + converter.exDateList.add(exdate); + } + eventProps.addAll(converter.incDateList); + eventProps.addAll(converter.incRuleList); + eventProps.addAll(converter.exDateList); + eventProps.addAll(converter.exRuleList); + } + + // ----- TemporalExpressionVisitor Implementation ----- // + + public void visit(Null expr) {} + + public void visit(Union expr) { + for (TemporalExpression childExpr : expr.getExpressionSet()) { + childExpr.accept(this); + } + } + + public void visit(Intersection expr) { + this.stateStack.push(this.state); + VisitorState newState = new VisitorState(); + newState.isExcluded = this.state.isExcluded; + newState.isIntersection = true; + this.state = newState; + for (TemporalExpression childExpr : expr.getExpressionSet()) { + childExpr.accept(this); + } + this.state = this.stateStack.pop(); + if (newState.inclRecurList.size() > 0) { + this.incRuleList.add(new RRule(this.consolidateRecurs(newState.inclRecurList))); + } + if (newState.exRecurList.size() > 0) { + this.exRuleList.add(new ExRule(this.consolidateRecurs(newState.exRecurList))); + } + } + + public void visit(Difference expr) { + VisitorState newState = new VisitorState(); + newState.isIntersection = this.state.isIntersection; + this.stateStack.push(this.state); + this.state = newState; + expr.getIncluded().accept(this); + newState.isExcluded = true; + expr.getExcluded().accept(this); + this.state = this.stateStack.pop(); + if (this.state.isIntersection) { + this.state.inclRecurList.addAll(newState.inclRecurList); + this.state.exRecurList.addAll(newState.exRecurList); + } + } + + public void visit(TemporalExpressions.DateRange expr) { + if (this.state.isExcluded) { + throw new IllegalStateException("iCalendar does not support excluded date ranges"); + } + org.ofbiz.base.util.DateRange range = expr.getDateRange(); + PeriodList periodList = new PeriodList(); + periodList.add(new Period(new DateTime(range.start()), new DateTime(range.end()))); + this.incDateList.add(new RDate(periodList)); + } + + @SuppressWarnings("unchecked") + public void visit(TimeOfDayRange expr) { + // TODO: this needs a better conversion + int startHr = expr.getStartHours(); + int endHr = expr.getEndHours(); + NumberList hourList = new NumberList(); + hourList.add(startHr); + while (startHr != endHr) { + startHr++; + if (startHr == 24) { + startHr = 0; + } + hourList.add(startHr); + } + Recur recur = new Recur(Recur.HOURLY, 0); + recur.getHourList().addAll(hourList); + this.state.addRecur(recur); + } + + @SuppressWarnings("unchecked") + public void visit(TemporalExpressions.DayOfWeekRange expr) { + int startDay = expr.getStartDay(); + int endDay = expr.getEndDay(); + WeekDayList dayList = new WeekDayList(); + dayList.add(dayOfWeekArray[startDay - 1]); + while (startDay != endDay) { + startDay++; + if (startDay > java.util.Calendar.SATURDAY) { + startDay = java.util.Calendar.SUNDAY; + } + dayList.add(dayOfWeekArray[startDay - 1]); + } + Recur recur = new Recur(Recur.DAILY, 0); + recur.getDayList().addAll(dayList); + this.state.addRecur(recur); + } + + @SuppressWarnings("unchecked") + public void visit(TemporalExpressions.MonthRange expr) { + int startMonth = expr.getStartMonth(); + int endMonth = expr.getEndMonth(); + java.util.Calendar cal = java.util.Calendar.getInstance(); + int maxMonth = cal.getActualMaximum(java.util.Calendar.MONTH); + NumberList monthList = new NumberList(); + monthList.add(startMonth + 1); + while (startMonth != endMonth) { + startMonth++; + if (startMonth > maxMonth) { + startMonth = java.util.Calendar.JANUARY; + } + monthList.add(startMonth + 1); + } + Recur recur = new Recur(Recur.MONTHLY, 0); + recur.getMonthList().addAll(monthList); + this.state.addRecur(recur); + } + + @SuppressWarnings("unchecked") + public void visit(TemporalExpressions.DayOfMonthRange expr) { + int startDay = expr.getStartDay(); + int endDay = expr.getEndDay(); + NumberList dayList = new NumberList(); + dayList.add(startDay); + while (startDay != endDay) { + startDay++; + dayList.add(startDay); + } + Recur recur = new Recur(Recur.DAILY, 0); + recur.getMonthDayList().addAll(dayList); + this.state.addRecur(recur); + } + + public void visit(TemporalExpressions.DayInMonth expr) { + Recur recur = new Recur(Recur.MONTHLY, 0); + recur.getDayList().add(new WeekDay(dayOfWeekArray[expr.getDayOfWeek() - 1], expr.getOccurrence())); + this.state.addRecur(recur); + } + + public void visit(TemporalExpressions.Frequency expr) { + if (this.dateStart == null) { + this.dateStart = new DtStart(new net.fortuna.ical4j.model.Date(expr.getStartDate())); + } + int freqCount = expr.getFreqCount(); + int freqType = expr.getFreqType(); + switch (freqType) { + case java.util.Calendar.SECOND: + this.state.addRecur((new Recur(Recur.SECONDLY, freqCount))); + case java.util.Calendar.MINUTE: + this.state.addRecur((new Recur(Recur.MINUTELY, freqCount))); + case java.util.Calendar.HOUR: + this.state.addRecur((new Recur(Recur.HOURLY, freqCount))); + case java.util.Calendar.DAY_OF_MONTH: + this.state.addRecur((new Recur(Recur.DAILY, freqCount))); + case java.util.Calendar.MONTH: + this.state.addRecur((new Recur(Recur.MONTHLY, freqCount))); + case java.util.Calendar.YEAR: + this.state.addRecur((new Recur(Recur.YEARLY, freqCount))); + } + } + + @SuppressWarnings("unchecked") + protected Recur consolidateRecurs(List<Recur> recurList) { + // Try to consolidate a list of Recur instances into one instance + Set<Integer> monthList = FastSet.newInstance(); + Set<Integer> monthDayList = FastSet.newInstance(); + Set<WeekDay> weekDayList = FastSet.newInstance(); + Set<Integer> hourList = FastSet.newInstance(); + String freq = null; + int freqCount = 0; + for (Recur recur : recurList) { + monthList.addAll(recur.getMonthList()); + monthDayList.addAll(recur.getMonthDayList()); + weekDayList.addAll(recur.getDayList()); + hourList.addAll(recur.getHourList()); + if (recur.getInterval() != 0 && freq == null) { + freq = recur.getFrequency(); + freqCount = recur.getInterval(); + } + } + if (freq == null && monthList.size() > 0) { + freq = Recur.MONTHLY; + } else if (freq == null && (monthDayList.size() > 0 || weekDayList.size() > 0)) { + freq = Recur.DAILY; + } else if (freq == null && hourList.size() > 0) { + freq = Recur.HOURLY; + } + if (freq == null) { + throw new IllegalStateException("Unable to convert intersection"); + } + Recur newRecur = new Recur(freq, 0); + if (freqCount != 0) { + newRecur.setInterval(freqCount); + } + newRecur.getMonthList().addAll(monthList); + newRecur.getMonthDayList().addAll(monthDayList); + newRecur.getDayList().addAll(weekDayList); + newRecur.getHourList().addAll(hourList); + return newRecur; + } + + protected class VisitorState { + public boolean isExcluded = false; + public boolean isIntersection = false; + public List<Recur> inclRecurList = FastList.newInstance(); + public List<Recur> exRecurList = FastList.newInstance(); + public void addRecur(Recur recur) { + if (this.isIntersection) { + if (this.isExcluded) { + this.exRecurList.add(recur); + } else { + this.inclRecurList.add(recur); + } + } else { + if (this.isExcluded) { + exRuleList.add(new ExRule(recur)); + } else { + incRuleList.add(new RRule(recur)); + } + } + } + } +} Propchange: ofbiz/trunk/applications/workeffort/src/org/ofbiz/workeffort/workeffort/ICalRecurConverter.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: ofbiz/trunk/applications/workeffort/src/org/ofbiz/workeffort/workeffort/ICalRecurConverter.java ------------------------------------------------------------------------------ svn:keywords = "Date Rev Author URL Id" Propchange: ofbiz/trunk/applications/workeffort/src/org/ofbiz/workeffort/workeffort/ICalRecurConverter.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Added: ofbiz/trunk/applications/workeffort/src/org/ofbiz/workeffort/workeffort/ICalServlet.java URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/workeffort/src/org/ofbiz/workeffort/workeffort/ICalServlet.java?rev=718684&view=auto ============================================================================== --- ofbiz/trunk/applications/workeffort/src/org/ofbiz/workeffort/workeffort/ICalServlet.java (added) +++ ofbiz/trunk/applications/workeffort/src/org/ofbiz/workeffort/workeffort/ICalServlet.java Tue Nov 18 10:56:11 2008 @@ -0,0 +1,109 @@ +/******************************************************************************* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + *******************************************************************************/ + +package org.ofbiz.workeffort.workeffort; + +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.Writer; + +import javax.servlet.http.HttpServlet; +import javax.servlet.ServletException; +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +import net.fortuna.ical4j.model.Calendar; + +import org.ofbiz.base.util.Debug; +import org.ofbiz.base.util.UtilJ2eeCompat; +import org.ofbiz.base.util.UtilValidate; +import org.ofbiz.entity.GenericDelegator; + +@SuppressWarnings("serial") +public class ICalServlet extends HttpServlet { + public static final String module = ICalServlet.class.getName(); + + /** Initialize this servlet. */ + public void init() throws ServletException { + super.init(); + if (Debug.infoOn()) { + Debug.logInfo("[ICalServlet.init] Loading iCalendar Servlet mounted on path " + this.getServletContext().getRealPath("/"), module); + } + } + + public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + String path = req.getPathInfo(); + if (UtilValidate.isEmpty(path)) { + path = "/"; + } + String workEffortId = path.substring(1); + if (workEffortId.contains("/")) { + workEffortId = workEffortId.substring(0, workEffortId.indexOf("/")); + } + if (workEffortId.length() < 1) { + resp.setStatus(HttpServletResponse.SC_BAD_REQUEST); + return; + } + if (Debug.infoOn()) { + Debug.logInfo("[ICalServlet.doGet] workEffortId = " + workEffortId, module); + } + GenericDelegator delegator = null; + HttpSession session = req.getSession(); + String delegatorName = (String) session.getAttribute("delegatorName"); + if (UtilValidate.isNotEmpty(delegatorName)) { + delegator = GenericDelegator.getGenericDelegator(delegatorName); + } + if (delegator == null) { + delegator = (GenericDelegator) this.getServletContext().getAttribute("delegator"); + } + if (delegator == null) { + Debug.logError("[ICalServlet.doGet] ERROR: delegator not found in ServletContext", module); + resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + return; + } + Calendar calendar = null; + try { + calendar = ICalendarWorker.getICalendar(delegator, workEffortId); + } catch (Exception e) { + Debug.logError("[ICalServlet.doGet] Error while getting iCalendar: " + e, module); + resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + return; + } + if (calendar == null) { + resp.setStatus(HttpServletResponse.SC_NOT_FOUND); + return; + } + resp.setContentType("text/calendar"); + resp.setStatus(HttpServletResponse.SC_OK); + Writer writer = null; + if (UtilJ2eeCompat.useOutputStreamNotWriter(this.getServletContext())) { + ServletOutputStream ros = resp.getOutputStream(); + writer = new OutputStreamWriter(ros, "UTF-8"); + } else { + writer = resp.getWriter(); + } + writer.write(calendar.toString()); + writer.close(); + if (Debug.infoOn()) { + Debug.logInfo("[ICalServlet.doGet] finished request", module); + } + } +}; Propchange: ofbiz/trunk/applications/workeffort/src/org/ofbiz/workeffort/workeffort/ICalServlet.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: ofbiz/trunk/applications/workeffort/src/org/ofbiz/workeffort/workeffort/ICalServlet.java ------------------------------------------------------------------------------ svn:keywords = "Date Rev Author URL Id" Propchange: ofbiz/trunk/applications/workeffort/src/org/ofbiz/workeffort/workeffort/ICalServlet.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Added: ofbiz/trunk/applications/workeffort/src/org/ofbiz/workeffort/workeffort/ICalendarWorker.java URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/workeffort/src/org/ofbiz/workeffort/workeffort/ICalendarWorker.java?rev=718684&view=auto ============================================================================== --- ofbiz/trunk/applications/workeffort/src/org/ofbiz/workeffort/workeffort/ICalendarWorker.java (added) +++ ofbiz/trunk/applications/workeffort/src/org/ofbiz/workeffort/workeffort/ICalendarWorker.java Tue Nov 18 10:56:11 2008 @@ -0,0 +1,175 @@ +/******************************************************************************* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + *******************************************************************************/ + +package org.ofbiz.workeffort.workeffort; + +import java.util.List; +import java.util.Map; +import java.util.TimeZone; + +import javolution.util.FastList; + +import net.fortuna.ical4j.model.*; +import net.fortuna.ical4j.model.component.*; +import net.fortuna.ical4j.model.parameter.*; +import net.fortuna.ical4j.model.property.*; + +import org.ofbiz.base.util.DateRange; +import org.ofbiz.base.util.Debug; +import org.ofbiz.base.util.TimeDuration; +import org.ofbiz.base.util.UtilMisc; +import org.ofbiz.base.util.UtilValidate; +import org.ofbiz.entity.GenericDelegator; +import org.ofbiz.entity.GenericEntityException; +import org.ofbiz.entity.GenericValue; +import org.ofbiz.entity.condition.EntityCondition; +import org.ofbiz.entity.condition.EntityConditionList; +import org.ofbiz.entity.condition.EntityExpr; +import org.ofbiz.entity.condition.EntityOperator; +import org.ofbiz.entity.util.EntityUtil; +import org.ofbiz.service.calendar.TemporalExpression; +import org.ofbiz.service.calendar.TemporalExpressionWorker; + +/** iCalendar worker class. */ +public class ICalendarWorker { + public static final String module = ICalendarWorker.class.getName(); + protected static ProdId prodId = new ProdId("-//Apache Open For Business//Work Effort Calendar//EN"); + protected static Map<String, Status> statusMap = UtilMisc.toMap("CAL_TENTATIVE", Status.VEVENT_TENTATIVE, + "CAL_CONFIRMED", Status.VEVENT_CONFIRMED, "CAL_CANCELLED", Status.VEVENT_CANCELLED); + protected static String workEffortIdPropName = "X-ORG-OFBIZ-WORKEFFORT-ID"; + + public static net.fortuna.ical4j.model.Calendar getICalendar(GenericDelegator delegator, String workEffortId) throws GenericEntityException { + GenericValue calendarProperties = delegator.findByPrimaryKey("WorkEffort", UtilMisc.toMap("workEffortId", workEffortId)); + if (calendarProperties == null || !"PUBLISH_PROPS".equals(calendarProperties.get("workEffortTypeId"))) { + return null; + } + net.fortuna.ical4j.model.Calendar calendar = makeCalendar(calendarProperties); + ComponentList components = calendar.getComponents(); + List<GenericValue> workEfforts = getRelatedWorkEfforts(calendarProperties); + for (GenericValue workEffort : workEfforts) { + components.add(makeEvent(workEffort)); + } + return calendar; + } + + public static List<GenericValue> getRelatedWorkEfforts(GenericValue workEffort) throws GenericEntityException { + GenericDelegator delegator = workEffort.getDelegator(); + String workEffortId = workEffort.getString("workEffortId"); + List<GenericValue> relatedParties = EntityUtil.filterByDate(delegator.findList("WorkEffortPartyAssignment", EntityCondition.makeCondition("workEffortId", EntityOperator.EQUALS, workEffortId), null, null, null, false)); + List<GenericValue> relatedFixedAssets = EntityUtil.filterByDate(delegator.findList("WorkEffortFixedAssetAssign", EntityCondition.makeCondition("workEffortId", EntityOperator.EQUALS, workEffortId), null, null, null, false)); + List<GenericValue> workEfforts = FastList.newInstance(); + List<EntityCondition> conditionList = UtilMisc.<EntityCondition>toList( + EntityCondition.makeCondition("scopeEnumId", EntityOperator.EQUALS, "WES_PUBLIC"), + EntityCondition.makeCondition("workEffortTypeId", EntityOperator.NOT_EQUAL, "PUBLISH_PROPS")); + EntityExpr variableExpr = EntityCondition.makeCondition("partyId", EntityOperator.EQUALS, ""); + conditionList.add(variableExpr); + EntityCondition workEffortCond = EntityCondition.makeCondition(conditionList); + for (GenericValue partyValue : relatedParties) { + variableExpr.init("partyId", EntityOperator.EQUALS, partyValue.get("partyId")); + workEfforts.addAll(delegator.findList("WorkEffortAndPartyAssign", workEffortCond, null, null, null, false)); + } + for (GenericValue fixedAssetValue : relatedFixedAssets) { + variableExpr.init("fixedAssetId", EntityOperator.EQUALS, fixedAssetValue.get("fixedAssetId")); + workEfforts.addAll(delegator.findList("WorkEffortAndPartyAssign", workEffortCond, null, null, null, false)); + } + workEfforts.addAll(EntityUtil.filterByDate(delegator.findList("WorkEffortAssocToView", EntityCondition.makeCondition("workEffortIdFrom", EntityOperator.EQUALS, workEffortId), null, null, null, false))); + return WorkEffortWorker.removeDuplicateWorkEfforts(workEfforts); + } + + public static VEvent makeEvent(GenericValue workEffort) throws GenericEntityException { + GenericDelegator delegator = workEffort.getDelegator(); + String workEffortId = workEffort.getString("workEffortId"); + PropertyList eventProps = new PropertyList(); + eventProps.add(new DtStamp()); // iCalendar object created date/time + if (workEffort.getTimestamp("createdDate") != null) { + eventProps.add(new Created(new DateTime(workEffort.getTimestamp("createdDate")))); + } + if (workEffort.getTimestamp("lastModifiedDate") != null) { + eventProps.add(new LastModified(new DateTime(workEffort.getTimestamp("lastModifiedDate")))); + } + eventProps.add(new XProperty(workEffortIdPropName, workEffort.getString("workEffortId"))); + eventProps.add(new Summary(workEffort.getString("workEffortName"))); + Status eventStatus = statusMap.get(workEffort.getString("currentStatusId")); + if (eventStatus != null) { + eventProps.add(statusMap.get(workEffort.getString("currentStatusId"))); + } + Double durationMillis = workEffort.getDouble("estimatedMilliSeconds"); + if (durationMillis != null) { + TimeDuration duration = TimeDuration.fromLong(durationMillis.longValue()); + eventProps.add(new Duration(new Dur(duration.days(), duration.hours(), duration.minutes(), duration.seconds()))); + } + List<GenericValue> relatedParties = EntityUtil.filterByDate(delegator.findList("WorkEffortPartyAssignView", EntityCondition.makeCondition("workEffortId", EntityOperator.EQUALS, workEffortId), null, null, null, false)); + for (GenericValue partyValue : relatedParties) { + ParameterList paramList = new ParameterList(); + String partyName = partyValue.getString("groupName"); + if (UtilValidate.isEmpty(partyName)) { + partyName = partyValue.getString("firstName") + " " + partyValue.getString("lastName"); + } + paramList.add(new Cn(partyName)); + // paramList.add(new XParameter(partyIdPropName, partyValue.getString("partyId"))); + try { + if ("CAL_ORGANIZER~CAL_OWNER".contains(partyValue.getString("roleTypeId"))) { + eventProps.add(new Organizer(paramList, "")); + } else { + eventProps.add(new Attendee(paramList, "")); + } + } catch (Exception e) {} + } + DateRange range = new DateRange(workEffort.getTimestamp("estimatedStartDate"), workEffort.getTimestamp("estimatedCompletionDate")); + eventProps.add(new DtStart(new DateTime(range.start()))); + if (UtilValidate.isNotEmpty(workEffort.getString("tempExprId"))) { + TemporalExpression tempExpr = TemporalExpressionWorker.getTemporalExpression(delegator, workEffort.getString("tempExprId")); + if (tempExpr != null) { + try { + ICalRecurConverter.convert(tempExpr, eventProps); + } catch (Exception e) { + eventProps.add(new Description("Error while converting recurrence: " + e)); + eventProps.add(new DtStart()); + eventProps.add(new DtEnd()); + return new VEvent(eventProps); + } + } + } else { + eventProps.add(new DtEnd(new DateTime(range.end()))); + } + if (workEffort.getString("description") != null) { + eventProps.add(new Description(workEffort.getString("description"))); + } + return new VEvent(eventProps); + } + + public static net.fortuna.ical4j.model.Calendar makeCalendar(GenericValue workEffort) throws GenericEntityException { + net.fortuna.ical4j.model.Calendar calendar = new net.fortuna.ical4j.model.Calendar(); + PropertyList propList = calendar.getProperties(); + propList.add(prodId); + propList.add(Version.VERSION_2_0); + propList.add(CalScale.GREGORIAN); + if (workEffort.get("description") != null) { + propList.add(new Description(workEffort.getString("description"))); + } else { + propList.add(new Description(workEffort.getString("workEffortName"))); + } + // TODO: Get time zone from publish properties value + java.util.TimeZone tz = java.util.TimeZone.getDefault(); + TimeZoneRegistry registry = TimeZoneRegistryFactory.getInstance().createRegistry(); + net.fortuna.ical4j.model.TimeZone timezone = registry.getTimeZone(tz.getID()); + calendar.getComponents().add(timezone.getVTimeZone()); + return calendar; + } +} Propchange: ofbiz/trunk/applications/workeffort/src/org/ofbiz/workeffort/workeffort/ICalendarWorker.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: ofbiz/trunk/applications/workeffort/src/org/ofbiz/workeffort/workeffort/ICalendarWorker.java ------------------------------------------------------------------------------ svn:keywords = "Date Rev Author URL Id" Propchange: ofbiz/trunk/applications/workeffort/src/org/ofbiz/workeffort/workeffort/ICalendarWorker.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Added: ofbiz/trunk/applications/workeffort/webapp/ical/WEB-INF/web.xml URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/workeffort/webapp/ical/WEB-INF/web.xml?rev=718684&view=auto ============================================================================== --- ofbiz/trunk/applications/workeffort/webapp/ical/WEB-INF/web.xml (added) +++ ofbiz/trunk/applications/workeffort/webapp/ical/WEB-INF/web.xml Tue Nov 18 10:56:11 2008 @@ -0,0 +1,98 @@ +<?xml version="1.0"?> +<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"> + +<!-- +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you under the Apache License, Version 2.0 (the +"License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied. See the License for the +specific language governing permissions and limitations +under the License. +--> + +<web-app> + <display-name>Open For Business - iCalendar Server</display-name> + <description>iCalendar Server Module of the Open For Business Project</description> + + <context-param> + <param-name>entityDelegatorName</param-name> + <param-value>default</param-value> + <description>The Name of the Entity Delegator to use, defined in entityengine.xml</description> + </context-param> + <context-param> + <param-name>localDispatcherName</param-name> + <param-value>ical</param-value> + <description>A unique name used to identify/recognize the local dispatcher for the Service Engine</description> + </context-param> + <context-param> + <param-name>serviceReaderUrls</param-name> + <param-value>/WEB-INF/services.xml</param-value> + <description>Configuration File(s) For The Service Dispatcher</description> + </context-param> + <context-param> + <param-name>scriptLocationPath</param-name> + <param-value>/WEB-INF/bsh</param-value> + <description>BeanShell Script Location</description> + </context-param> + + <filter> + <filter-name>ContextFilter</filter-name> + <display-name>ContextFilter</display-name> + <filter-class>org.ofbiz.webapp.control.ContextFilter</filter-class> + <init-param> + <param-name>disableContextSecurity</param-name> + <param-value>N</param-value> + </init-param> + <init-param> + <param-name>allowedPaths</param-name> + <param-value>/control:/select:/index.html:/index.jsp:/default.html:/default.jsp:/images:/includes/maincss.css</param-value> + </init-param> + <init-param> + <param-name>errorCode</param-name> + <param-value>403</param-value> + </init-param> + <init-param> + <param-name>redirectPath</param-name> + <param-value>/control/main</param-value> + </init-param> + </filter> + <filter-mapping> + <filter-name>ContextFilter</filter-name> + <url-pattern>/*</url-pattern> + </filter-mapping> + + <listener><listener-class>org.ofbiz.webapp.control.ControlEventListener</listener-class></listener> + <listener><listener-class>org.ofbiz.webapp.control.LoginEventListener</listener-class></listener> + <!-- NOTE: not all app servers support mounting implementations of the HttpSessionActivationListener interface --> + <!-- <listener><listener-class>org.ofbiz.webapp.control.ControlActivationEventListener</listener-class></listener> --> + + <servlet> + <servlet-name>iCalendarServlet</servlet-name> + <display-name>iCalendarServlet</display-name> + <description>Main Control Servlet</description> + <servlet-class>org.ofbiz.workeffort.workeffort.ICalServlet</servlet-class> + <load-on-startup>1</load-on-startup> + </servlet> + <servlet-mapping> + <servlet-name>iCalendarServlet</servlet-name> + <url-pattern>/*</url-pattern> + </servlet-mapping> + + <session-config> + <session-timeout>60</session-timeout> <!-- in minutes --> + </session-config> + + <welcome-file-list> + <welcome-file>index.jsp</welcome-file> + </welcome-file-list> +</web-app> Propchange: ofbiz/trunk/applications/workeffort/webapp/ical/WEB-INF/web.xml ------------------------------------------------------------------------------ svn:eol-style = native Propchange: ofbiz/trunk/applications/workeffort/webapp/ical/WEB-INF/web.xml ------------------------------------------------------------------------------ svn:keywords = "Date Rev Author URL Id" Propchange: ofbiz/trunk/applications/workeffort/webapp/ical/WEB-INF/web.xml ------------------------------------------------------------------------------ svn:mime-type = text/plain Added: ofbiz/trunk/framework/base/lib/ical4j-1.0-beta5.jar URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/base/lib/ical4j-1.0-beta5.jar?rev=718684&view=auto ============================================================================== Binary file - no diff available. Propchange: ofbiz/trunk/framework/base/lib/ical4j-1.0-beta5.jar ------------------------------------------------------------------------------ svn:mime-type = application/octet-stream Modified: ofbiz/trunk/specialpurpose/projectmgr/data/ProjectMgrDemoData.xml URL: http://svn.apache.org/viewvc/ofbiz/trunk/specialpurpose/projectmgr/data/ProjectMgrDemoData.xml?rev=718684&r1=718683&r2=718684&view=diff ============================================================================== --- ofbiz/trunk/specialpurpose/projectmgr/data/ProjectMgrDemoData.xml (original) +++ ofbiz/trunk/specialpurpose/projectmgr/data/ProjectMgrDemoData.xml Tue Nov 18 10:56:11 2008 @@ -158,4 +158,8 @@ <WorkEffortPartyAssignment workEffortId="STAFF_MTG" partyId="DemoEmployee2" statusId="PRTYASGN_ASSIGNED" roleTypeId="CAL_ATTENDEE" availabilityStatusId="WEPA_AV_BUSY" fromDate="2008-01-01 00:00:00.0"/> <WorkEffortPartyAssignment workEffortId="STAFF_MTG" partyId="DemoEmployee3" statusId="PRTYASGN_ASSIGNED" roleTypeId="CAL_ATTENDEE" availabilityStatusId="WEPA_AV_BUSY" fromDate="2008-01-01 00:00:00.0"/> + <!-- Publish the staff meeting calendar event --> + <WorkEffort workEffortId="CALENDAR_PUB_DEMO" workEffortTypeId="PUBLISH_PROPS" currentStatusId="CAL_CANCELLED" scopeEnumId="WES_PUBLIC" description="Demo Project 1 Customer 1" workEffortName="iCalendar Publish Demonstration"/> + <WorkEffortAssoc workEffortIdFrom="CALENDAR_PUB_DEMO" workEffortIdTo="STAFF_MTG" workEffortAssocTypeId="WORK_EFF_DEPENDENCY" fromDate="2008-01-01 00:00:00.0"/> + </entity-engine-xml> |
Free forum by Nabble | Edit this page |