Author: adrianc
Date: Wed Jun 24 06:58:54 2009 New Revision: 787927 URL: http://svn.apache.org/viewvc?rev=787927&view=rev Log: Improved Work Effort iCalendar support: 1. Improved work effort field-to-iCalendar property mapping. 2. Work efforts can be updated from 3rd party calendar clients. 3. The publish point scope controls calendar access 4. Some iCalendar WebDAV servlet operations use services - so the iCalendar integration can be modified or enhanced. TODO: I'm not an expert on SSL - the request handler needs more work to enforce SSL. Allow third party calendar programs to create new work efforts. Implement a subset of CalDAV. Authentication is done with URL parameters - that needs to be improved. I replaced the work effort aatribute services I set up earlier. The attributes were too limited. I will create a Wiki page soon to explain all of this. Many thanks to David Jones for his advice, and to Hans Bakker for his calendar demo data. Added: ofbiz/trunk/applications/workeffort/src/org/ofbiz/workeffort/workeffort/ICalConverter.java (with props) ofbiz/trunk/applications/workeffort/src/org/ofbiz/workeffort/workeffort/ICalHandlerFactory.java (with props) Removed: ofbiz/trunk/applications/workeffort/src/org/ofbiz/workeffort/workeffort/ICalServlet.java Modified: ofbiz/trunk/applications/workeffort/data/WorkEffortDemoData.xml ofbiz/trunk/applications/workeffort/entitydef/entitymodel.xml ofbiz/trunk/applications/workeffort/script/org/ofbiz/workeffort/permission/WorkEffortPermissionServices.xml ofbiz/trunk/applications/workeffort/script/org/ofbiz/workeffort/workeffort/WorkEffortSimpleServices.xml ofbiz/trunk/applications/workeffort/servicedef/services.xml ofbiz/trunk/applications/workeffort/src/org/ofbiz/workeffort/workeffort/ICalWorker.java ofbiz/trunk/applications/workeffort/webapp/ical/WEB-INF/web.xml Modified: ofbiz/trunk/applications/workeffort/data/WorkEffortDemoData.xml URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/workeffort/data/WorkEffortDemoData.xml?rev=787927&r1=787926&r2=787927&view=diff ============================================================================== --- ofbiz/trunk/applications/workeffort/data/WorkEffortDemoData.xml (original) +++ ofbiz/trunk/applications/workeffort/data/WorkEffortDemoData.xml Wed Jun 24 06:58:54 2009 @@ -47,7 +47,7 @@ <WorkEffortAssoc workEffortIdFrom="CALENDAR_PUB_DEMO" workEffortIdTo="PrivateDemoEmployee1" workEffortAssocTypeId="WORK_EFF_DEPENDENCY" fromDate="2008-01-01 00:00:00.0"/> <WorkEffortPartyAssignment workEffortId="PrivateDemoEmployee1" partyId="DemoEmployee1" statusId="PRTYASGN_ASSIGNED" roleTypeId="CAL_OWNER" availabilityStatusId="WEPA_AV_BUSY" fromDate="2008-01-01 00:00:00.0"/> <!-- public event --> - <WorkEffort workEffortId="PublicEvent" workEffortTypeId="MEETING" currentStatusId="CAL_TENTATIVE" lastStatusUpdate="2008-01-01 00:00:00.0" scopeEnumId="WES_PUBLIC" workEffortName="The general company party june 17" description="General Party" estimatedStartDate="2009-06-17 19:00:00.0" estimatedCompletionDate="2009-06-17 23:00:00.0"/> + <WorkEffort workEffortId="PublicEvent" workEffortTypeId="MEETING" currentStatusId="CAL_TENTATIVE" lastStatusUpdate="2008-01-01 00:00:00.0" scopeEnumId="WES_PUBLIC" workEffortName="The general company party june 17" description="General Party" locationDesc="Tom's Banquet Hall" estimatedStartDate="2009-06-17 19:00:00.0" estimatedCompletionDate="2009-06-17 23:00:00.0"/> <WorkEffortAssoc workEffortIdFrom="CALENDAR_PUB_DEMO" workEffortIdTo="PublicEvent" workEffortAssocTypeId="WORK_EFF_DEPENDENCY" fromDate="2008-01-01 00:00:00.0"/> </entity-engine-xml> Modified: ofbiz/trunk/applications/workeffort/entitydef/entitymodel.xml URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/workeffort/entitydef/entitymodel.xml?rev=787927&r1=787926&r2=787927&view=diff ============================================================================== --- ofbiz/trunk/applications/workeffort/entitydef/entitymodel.xml (original) +++ ofbiz/trunk/applications/workeffort/entitydef/entitymodel.xml Wed Jun 24 06:58:54 2009 @@ -578,6 +578,18 @@ <key-map field-name="parentTypeId" rel-field-name="workEffortGoodStdTypeId"/> </relation> </entity> + <entity entity-name="WorkEffortIcalData" + package-name="org.ofbiz.workeffort.workeffort" + title="Work Effort iCalendar Data"> + <field name="workEffortId" type="id-ne"></field> + <field name="icalData" type="very-long"> + <description>iCalender Data</description> + </field> + <prim-key field="workEffortId"/> + <relation type="one" fk-name="WKEFF_ICAL_DATA" rel-entity-name="WorkEffort"> + <key-map field-name="workEffortId"/> + </relation> + </entity> <entity entity-name="WorkEffortInventoryAssign" package-name="org.ofbiz.workeffort.workeffort" title="Work Effort Inventory Assignment Entity"> Modified: ofbiz/trunk/applications/workeffort/script/org/ofbiz/workeffort/permission/WorkEffortPermissionServices.xml URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/workeffort/script/org/ofbiz/workeffort/permission/WorkEffortPermissionServices.xml?rev=787927&r1=787926&r2=787927&view=diff ============================================================================== --- ofbiz/trunk/applications/workeffort/script/org/ofbiz/workeffort/permission/WorkEffortPermissionServices.xml (original) +++ ofbiz/trunk/applications/workeffort/script/org/ofbiz/workeffort/permission/WorkEffortPermissionServices.xml Wed Jun 24 06:58:54 2009 @@ -303,4 +303,80 @@ </while> </simple-method> + <simple-method method-name="workEffortICalendarPermission" short-description="Check iCalendar Permission"> + <!-- Note: the iCalendar servlet will call the permission service under two conditions - to grant + VIEW access to a calendar that isn't PUBLIC, or to grant UPDATE access to a work effort --> + <log level="verbose" message="workEffortICalendarPermission invoked for workEffortId ${parameters.workEffortId}, + user login partyId = ${userLogin.partyId}"/> + <call-simple-method method-name="workEffortManagerPermission"/> + <if-compare field="hasPermission" value="true" operator="equals"> + <set field="hasPermission" value="false" type="Boolean"/> + <set field="workEffortId" from-field="parameters.workEffortId"/> + <entity-one value-field="workEffort" entity-name="WorkEffort"/> + <if-not-empty field="workEffort"> + <entity-condition list="partyAssignments" entity-name="WorkEffortPartyAssignment" filter-by-date="true"> + <condition-list combine="and"> + <condition-expr field-name="workEffortId" from-field="workEffortId"/> + <condition-expr field-name="partyId" from-field="userLogin.partyId"/> + <condition-list combine="or"> + <condition-expr field-name="roleTypeId" value="CAL_OWNER"/> + <condition-expr field-name="roleTypeId" value="CAL_ORGANIZER"/> + <condition-expr field-name="roleTypeId" value="CAL_DELEGATE"/> + </condition-list> + </condition-list> + </entity-condition> + <set field="isDelegate" value="false"/> + <set field="isOwner" value="false"/> + <set field="isOrganizer" value="false"/> + <iterate list="partyAssignments" entry="partyAssignment"> + <if-compare field="partyAssignment.roleTypeId" operator="equals" value="CAL_OWNER"> + <set field="isDelegate" value="true"/> + <set field="isOwner" value="true"/> + <set field="isOrganizer" value="true"/> + <else> + <if-compare field="partyAssignment.roleTypeId" operator="equals" value="CAL_ORGANIZER"> + <set field="isOrganizer" value="true"/> + <else> + <if-compare field="partyAssignment.roleTypeId" operator="equals" value="CAL_DELEGATE"> + <set field="isDelegate" value="true"/> + </if-compare> + </else> + </if-compare> + </else> + </if-compare> + </iterate> + <if-compare value="PUBLISH_PROPS" field="workEffort.workEffortTypeId" operator="equals"> + <log level="verbose" message="Checking publish properties permission, isOwner = ${isOwner}, isDelegate = ${isDelegate}"/> + <if> + <condition> + <or> + <and> + <!-- The calendar is private and the user is the calendar owner --> + <if-compare field="workEffort.scopeEnumId" operator="equals" value="WES_PRIVATE"/> + <if-compare field="isOwner" operator="equals" value="true"/> + </and> + <and> + <!-- The calendar is confidential and the user is a delegate of the calendar --> + <if-compare field="workEffort.scopeEnumId" operator="equals" value="WES_CONFIDENTIAL"/> + <if-compare field="isDelegate" operator="equals" value="true"/> + </and> + </or> + </condition> + <then> + <set field="hasPermission" value="true" type="Boolean"/> + </then> + </if> + <else> + <!-- RFC 2445 3.5 Only an ORGANIZER can update a VEVENT or VTODO. --> + <log level="verbose" message="Checking work effort update permission, isOrganizer = ${isOrganizer}"/> + <if-compare field="isOrganizer" operator="equals" value="true"> + <set field="hasPermission" value="true" type="Boolean"/> + </if-compare> + </else> + </if-compare> + </if-not-empty> + <field-to-result field="hasPermission"/> + </if-compare> + </simple-method> + </simple-methods> Modified: ofbiz/trunk/applications/workeffort/script/org/ofbiz/workeffort/workeffort/WorkEffortSimpleServices.xml URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/workeffort/script/org/ofbiz/workeffort/workeffort/WorkEffortSimpleServices.xml?rev=787927&r1=787926&r2=787927&view=diff ============================================================================== --- ofbiz/trunk/applications/workeffort/script/org/ofbiz/workeffort/workeffort/WorkEffortSimpleServices.xml (original) +++ ofbiz/trunk/applications/workeffort/script/org/ofbiz/workeffort/workeffort/WorkEffortSimpleServices.xml Wed Jun 24 06:58:54 2009 @@ -1472,4 +1472,66 @@ <store-value value-field="lookedUpValue"/> </simple-method> + <!-- iCalendar services --> + + <simple-method method-name="getICalWorkEfforts" short-description="Get All Work Efforts Related To An iCalendar Publish Point"> + <!-- Servlet already confirmed workEffortId is a valid publish point --> + <set field="workEffortId" from-field="parameters.workEffortId"/> + <entity-condition list="assignedParties" entity-name="WorkEffortPartyAssignment" filter-by-date="true"> + <condition-list combine="and"> + <condition-expr field-name="workEffortId" from-field="workEffortId"/> + <!-- DELEGATE has special meaning in publish properties - it's + another party in an ORGANIZER role, NOT a party whose work efforts + are included in a calendar. We need another role to define calendar + members. --> + <condition-expr field-name="roleTypeId" value="CAL_DELEGATE" operator="not-equals"/> + </condition-list> + </entity-condition> + <iterate entry="assignedParty" list="assignedParties"> + <entity-condition list="resultList" entity-name="WorkEffortAndPartyAssign" filter-by-date="true"> + <condition-list combine="and"> + <condition-expr field-name="scopeEnumId" value="WES_PUBLIC"/> + <condition-expr field-name="workEffortTypeId" value="PUBLISH_PROPS" operator="not-equals"/> + <condition-expr field-name="partyId" from-field="assignedParty.partyId"/> + </condition-list> + </entity-condition> + <list-to-list list="resultList" to-list="workEfforts"/> + </iterate> + <entity-and list="assignedFixedAssets" entity-name="WorkEffortFixedAssetAssign" filter-by-date="true"> + <field-map field-name="workEffortId" from-field="workEffortId"/> + </entity-and> + <iterate entry="assignedFixedAsset" list="assignedFixedAssets"> + <entity-condition list="resultList" entity-name="WorkEffortAndFixedAssetAssign" filter-by-date="true"> + <condition-list combine="and"> + <condition-expr field-name="scopeEnumId" value="WES_PUBLIC"/> + <condition-expr field-name="workEffortTypeId" value="PUBLISH_PROPS" operator="not-equals"/> + <condition-expr field-name="fixedAssetId" from-field="assignedFixedAsset.fixedAssetId"/> + </condition-list> + </entity-condition> + <list-to-list list="resultList" to-list="workEfforts"/> + </iterate> + <entity-and list="resultList" entity-name="WorkEffortAssocToView" filter-by-date="true"> + <field-map field-name="workEffortIdFrom" from-field="workEffortId"/> + </entity-and> + <list-to-list list="resultList" to-list="workEfforts"/> + <field-to-result field="workEfforts"/> + </simple-method> + + <simple-method method-name="getPartyICalUri" short-description="Get The Party iCalendar URI"> + <!-- RFC 2445 4.8.4.1 and 4.8.4.3 Value must be a URI (4.3.3) --> + <!-- For now we just look for a primary email address. This could be expanded later. --> + <set field="partyId" from-field="parameters.partyId"/> + <entity-condition list="emailAddresses" entity-name="PartyContactWithPurpose"> + <condition-list combine="and"> + <condition-expr field-name="partyId" from-field="partyId"/> + <condition-expr field-name="contactMechPurposeTypeId" value="PRIMARY_EMAIL"/> + <condition-expr field-name="purposeThruDate" from-field="null"/> + </condition-list> + </entity-condition> + <if-compare field="util:size(emailAddresses)" operator="not-equals" value="0" type="Integer"> + <set field="iCalUri" value="MAILTO:${emailAddresses[0].infoString}"/> + <field-to-result field="iCalUri"/> + </if-compare> + </simple-method> + </simple-methods> Modified: ofbiz/trunk/applications/workeffort/servicedef/services.xml URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/workeffort/servicedef/services.xml?rev=787927&r1=787926&r2=787927&view=diff ============================================================================== --- ofbiz/trunk/applications/workeffort/servicedef/services.xml (original) +++ ofbiz/trunk/applications/workeffort/servicedef/services.xml Wed Jun 24 06:58:54 2009 @@ -662,24 +662,44 @@ <auto-attributes mode="IN" include="pk" optional="false"/> </service> - <!-- WorkEffort Attribute Services --> - <service name="createWorkEffortAttribute" default-entity-name="WorkEffortAttribute" engine="entity-auto" invoke="create" auth="true"> - <description>Create a WorkEffort Attribute</description> - <permission-service service-name="workEffortGenericPermission" main-action="CREATE"/> + <!-- WorkEffort iCalendar Services --> + <service name="createWorkEffortICalData" default-entity-name="WorkEffortIcalData" engine="entity-auto" invoke="create" auth="true"> + <description>Create WorkEffort iCalendar Data</description> + <permission-service service-name="workEffortICalendarPermission" main-action="CREATE"/> <auto-attributes include="pk" mode="IN" optional="false"/> <auto-attributes include="nonpk" mode="IN" optional="true"/> </service> - <service name="updateWorkEffortAttribute" default-entity-name="WorkEffortAttribute" engine="entity-auto" invoke="update" auth="true"> - <description>Update a WorkEffort Attribute</description> - <permission-service service-name="workEffortGenericPermission" main-action="UPDATE"/> + <service name="updateWorkEffortICalData" default-entity-name="WorkEffortIcalData" engine="entity-auto" invoke="update" auth="true"> + <description>Update WorkEffort iCalendar Data</description> + <permission-service service-name="workEffortICalendarPermission" main-action="UPDATE"/> <auto-attributes include="pk" mode="IN" optional="false"/> <auto-attributes include="nonpk" mode="IN" optional="true"/> </service> - <service name="deleteWorkEffortAttribute" default-entity-name="WorkEffortAttribute" engine="entity-auto" invoke="delete" auth="true"> - <description>Delete a WorkEffort Attribute</description> - <permission-service service-name="workEffortGenericPermission" main-action="DELETE"/> + <service name="deleteWorkEffortICalData" default-entity-name="WorkEffortIcalData" engine="entity-auto" invoke="delete" auth="true"> + <description>Delete WorkEffort iCalendar Data</description> + <permission-service service-name="workEffortICalendarPermission" main-action="DELETE"/> <auto-attributes include="pk" mode="IN" optional="false"/> </service> + <service name="workEffortICalendarPermission" engine="simple" + location="component://workeffort/script/org/ofbiz/workeffort/permission/WorkEffortPermissionServices.xml" invoke="workEffortICalendarPermission"> + <description>iCalendar Permission Check</description> + <implements service="permissionInterface"/> + <attribute type="String" mode="IN" name="workEffortId" optional="false"/> + </service> + <service name="getICalWorkEfforts" engine="simple" + location="component://workeffort/script/org/ofbiz/workeffort/workeffort/WorkEffortSimpleServices.xml" invoke="getICalWorkEfforts"> + <description>Get iCalendar Work Efforts</description> + <!-- No permission checking - the servlet handles that --> + <attribute type="String" mode="IN" name="workEffortId" optional="false"/> + <attribute type="List" mode="OUT" name="workEfforts"/> + </service> + <service name="getPartyICalUri" engine="simple" + location="component://workeffort/script/org/ofbiz/workeffort/workeffort/WorkEffortSimpleServices.xml" invoke="getPartyICalUri"> + <description>Get Party iCalendar URI</description> + <!-- No permission checking - the servlet handles that --> + <attribute type="String" mode="IN" name="partyId" optional="false"/> + <attribute type="String" mode="OUT" name="iCalUri" optional="true"/> + </service> <!-- WorkEffort Event Reminder Services --> <service name="createWorkEffortEventReminder" default-entity-name="WorkEffortEventReminder" engine="entity-auto" invoke="create" auth="true"> Added: ofbiz/trunk/applications/workeffort/src/org/ofbiz/workeffort/workeffort/ICalConverter.java URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/workeffort/src/org/ofbiz/workeffort/workeffort/ICalConverter.java?rev=787927&view=auto ============================================================================== --- ofbiz/trunk/applications/workeffort/src/org/ofbiz/workeffort/workeffort/ICalConverter.java (added) +++ ofbiz/trunk/applications/workeffort/src/org/ofbiz/workeffort/workeffort/ICalConverter.java Wed Jun 24 06:58:54 2009 @@ -0,0 +1,878 @@ +/******************************************************************************* + * 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.InputStream; +import java.io.IOException; +import java.io.StringReader; +import java.net.URISyntaxException; +import java.sql.Timestamp; +import java.text.ParseException; +import java.util.Date; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; + +import javolution.util.FastList; +import javolution.util.FastMap; +import javolution.util.FastSet; + +import net.fortuna.ical4j.data.CalendarBuilder; +import net.fortuna.ical4j.data.ParserException; +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.ObjectType; +import org.ofbiz.base.util.TimeDuration; +import org.ofbiz.base.util.UtilMisc; +import org.ofbiz.base.util.UtilProperties; +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.EntityExpr; +import org.ofbiz.entity.condition.EntityOperator; +import org.ofbiz.entity.util.EntityUtil; +import org.ofbiz.service.GenericServiceException; +import org.ofbiz.service.LocalDispatcher; +import org.ofbiz.service.ModelParam; +import org.ofbiz.service.ModelService; +import org.ofbiz.service.ServiceUtil; +import org.ofbiz.service.calendar.TemporalExpression; +import org.ofbiz.service.calendar.TemporalExpressionWorker; + +/** iCalendar converter class. This class uses the <a href="http://ical4j.sourceforge.net/index.html"> + * iCal4J</a> library. + */ +public class ICalConverter { + + protected static final String module = ICalConverter.class.getName(); + protected static final String contactMechIdXParamName = "X-ORG-APACHE-OFBIZ-CONTACT-MECH-ID"; + protected static final String partyIdXParamName = "X-ORG-APACHE-OFBIZ-PARTY-ID"; + protected static final ProdId prodId = new ProdId("-//Apache Open For Business//Work Effort Calendar//EN"); + protected static final String workEffortIdParamName = "X-ORG-APACHE-OFBIZ-WORKEFFORT-ID"; + protected static final String uidPrefix = "ORG-APACHE-OFBIZ-WE-"; + protected static final String workEffortIdXPropName = "X-ORG-APACHE-OFBIZ-WORKEFFORT-ID"; + protected static final String reminderXPropName = "X-ORG-APACHE-OFBIZ-REMINDER-ID"; + protected static final Map<String, String> fromStatusMap = UtilMisc.toMap("TENTATIVE", "CAL_TENTATIVE", + "CONFIRMED", "CAL_CONFIRMED", "CANCELLED", "CAL_CANCELLED", "NEEDS-ACTION", "CAL_NEEDS_ACTION", + "COMPLETED", "CAL_COMPLETED", "IN-PROCESS", "CAL_ACCEPTED"); + protected static final Map<String, Status> toStatusMap = UtilMisc.toMap("CAL_TENTATIVE", Status.VEVENT_TENTATIVE, + "CAL_CONFIRMED", Status.VEVENT_CONFIRMED, "CAL_CANCELLED", Status.VEVENT_CANCELLED, + "CAL_NEEDS_ACTION", Status.VTODO_NEEDS_ACTION, "CAL_COMPLETED", Status.VTODO_COMPLETED, + "CAL_ACCEPTED", Status.VTODO_IN_PROCESS); + protected static final Map<String, PartStat> toPartStatusMap = UtilMisc.toMap( + "PRTYASGN_OFFERED", PartStat.TENTATIVE, "PRTYASGN_ASSIGNED", PartStat.ACCEPTED); + protected static final Map<String, String> fromPartStatusMap = UtilMisc.toMap( + "TENTATIVE", "PRTYASGN_OFFERED", "ACCEPTED", "PRTYASGN_ASSIGNED"); + + protected static VAlarm createAlarm(GenericValue workEffortEventReminder) { + VAlarm alarm = null; + Timestamp reminderStamp = workEffortEventReminder.getTimestamp("reminderDateTime"); + if (reminderStamp != null) { + alarm = new VAlarm(new DateTime(reminderStamp)); + } else { + long reminderOffset = workEffortEventReminder.get("reminderOffset") == null ? 0 : workEffortEventReminder.getLong("reminderOffset").longValue(); + TimeDuration duration = TimeDuration.fromLong(reminderOffset); + alarm = new VAlarm(new Dur(duration.days(), duration.hours(), duration.minutes(), duration.seconds())); + } + return alarm; + } + + protected static Attendee createAttendee(GenericValue partyValue, Map<String, Object> context) { + Attendee attendee = new Attendee(); + loadPartyAssignment(attendee, partyValue, context); + return attendee; + } + + protected static Organizer createOrganizer(GenericValue partyValue, Map<String, Object> context) { + Organizer organizer = new Organizer(); + loadPartyAssignment(organizer, partyValue, context); + return organizer; + } + + protected static String fromClazz(PropertyList propertyList) { + Clazz iCalObj = (Clazz) propertyList.getProperty(Clazz.CLASS); + if (iCalObj == null) { + return null; + } + return "WES_".concat(iCalObj.getValue()); + } + + protected static Timestamp fromCompleted(PropertyList propertyList) { + Completed iCalObj = (Completed) propertyList.getProperty(Completed.COMPLETED); + if (iCalObj == null) { + return null; + } + Date date = iCalObj.getDate(); + return new Timestamp(date.getTime()); + } + + protected static String fromDescription(PropertyList propertyList) { + Description iCalObj = (Description) propertyList.getProperty(Description.DESCRIPTION); + if (iCalObj == null) { + return null; + } + return iCalObj.getValue(); + } + + protected static Timestamp fromDtEnd(PropertyList propertyList) { + DtEnd iCalObj = (DtEnd) propertyList.getProperty(DtEnd.DTEND); + if (iCalObj == null) { + return null; + } + Date date = iCalObj.getDate(); + return new Timestamp(date.getTime()); + } + + protected static Timestamp fromDtStart(PropertyList propertyList) { + DtStart iCalObj = (DtStart) propertyList.getProperty(DtStart.DTSTART); + if (iCalObj == null) { + return null; + } + Date date = iCalObj.getDate(); + return new Timestamp(date.getTime()); + } + + protected static Double fromDuration(PropertyList propertyList) { + Duration iCalObj = (Duration) propertyList.getProperty(Duration.DURATION); + if (iCalObj == null) { + return null; + } + Dur dur = iCalObj.getDuration(); + TimeDuration td = new TimeDuration(0, 0, (dur.getWeeks() * 7) + dur.getDays(), dur.getHours(), dur.getMinutes(), dur.getSeconds(), 0); + return new Double(TimeDuration.toLong(td)); + } + + protected static Timestamp fromLastModified(PropertyList propertyList) { + LastModified iCalObj = (LastModified) propertyList.getProperty(LastModified.LAST_MODIFIED); + if (iCalObj == null) { + return null; + } + Date date = iCalObj.getDate(); + return new Timestamp(date.getTime()); + } + + protected static String fromLocation(PropertyList propertyList) { + Location iCalObj = (Location) propertyList.getProperty(Location.LOCATION); + if (iCalObj == null) { + return null; + } + return iCalObj.getValue(); + } + + protected static String fromParticipationStatus(Parameter status) { + if (status == null) { + return null; + } + return fromPartStatusMap.get(status.getValue()); + } + + protected static Long fromPercentComplete(PropertyList propertyList) { + PercentComplete iCalObj = (PercentComplete) propertyList.getProperty(PercentComplete.PERCENT_COMPLETE); + if (iCalObj == null) { + return null; + } + return new Long((long)iCalObj.getPercentage()); + } + + protected static Double fromPriority(PropertyList propertyList) { + Priority iCalObj = (Priority) propertyList.getProperty(Priority.PRIORITY); + if (iCalObj == null) { + return null; + } + return new Double(iCalObj.getLevel()); + } + + protected static String fromStatus(PropertyList propertyList) { + Status iCalObj = (Status) propertyList.getProperty(Status.STATUS); + if (iCalObj == null) { + return null; + } + return fromStatusMap.get(iCalObj.getValue()); + } + + protected static String fromSummary(PropertyList propertyList) { + Summary iCalObj = (Summary) propertyList.getProperty(Summary.SUMMARY); + if (iCalObj == null) { + return null; + } + return iCalObj.getValue(); + } + + protected static String fromXParameter(ParameterList parameterList, String parameterName) { + if (parameterName == null) { + return null; + } + Parameter parameter = parameterList.getParameter(parameterName); + if (parameter != null) { + return parameter.getValue(); + } + return null; + } + + protected static String fromXProperty(PropertyList propertyList, String propertyName) { + if (propertyName == null) { + return null; + } + Property property = propertyList.getProperty(propertyName); + if (property != null) { + return property.getValue(); + } + return null; + } + + @SuppressWarnings("unchecked") + protected static void getAlarms(GenericValue workEffort, ComponentList alarms) throws GenericEntityException { + Description description = null; + if (workEffort.get("description") != null) { + description = new Description(workEffort.getString("description")); + } else { + description = new Description(workEffort.getString("workEffortName")); + } + Summary summary = new Summary(UtilProperties.getMessage("WorkEffortUiLabels", "WorkEffortEventReminder", Locale.getDefault())); + GenericDelegator delegator = workEffort.getDelegator(); + String workEffortId = workEffort.getString("workEffortId"); + List<GenericValue> reminderList = delegator.findList("WorkEffortEventReminder", EntityCondition.makeCondition("workEffortId", EntityOperator.EQUALS, workEffort.get("workEffortId")), null, null, null, false); + for (GenericValue reminder : reminderList) { + String reminderId = workEffortId + "-" + reminder.getString("sequenceId"); + VAlarm alarm = null; + PropertyList alarmProps = null; + boolean newAlarm = true; + Iterator<VAlarm> i = alarms.iterator(); + while (i.hasNext()) { + alarm = i.next(); + Property xProperty = alarm.getProperty(reminderXPropName); + if (xProperty != null && reminderId.equals(xProperty.getValue())) { + newAlarm = false; + alarmProps = alarm.getProperties(); + // TODO: Write update code. For now, just re-create + alarmProps.clear(); + break; + } + } + if (newAlarm) { + alarm = createAlarm(reminder); + alarms.add(alarm); + alarmProps = alarm.getProperties(); + alarmProps.add(new XProperty(reminderXPropName, reminderId)); + } + GenericValue contactMech = reminder.getRelatedOne("ContactMech"); + if (contactMech != null && "EMAIL_ADDRESS".equals(contactMech.get("contactMechTypeId"))) { + try { + alarmProps.add(new Attendee(contactMech.getString("infoString"))); + alarmProps.add(Action.EMAIL); + alarmProps.add(summary); + alarmProps.add(description); + } catch (URISyntaxException e) { + alarmProps.add(Action.DISPLAY); + alarmProps.add(new Description("Error encountered while creating iCalendar: " + e)); + } + } else { + alarmProps.add(Action.DISPLAY); + alarmProps.add(description); + } + if (Debug.verboseOn()) { + try { + alarm.validate(true); + Debug.logVerbose("iCalendar alarm passes validation", module); + } catch (ValidationException e) { + Debug.logVerbose("iCalendar alarm fails validation: " + e, module); + } + } + } + } + + protected static void getPartyPrimaryEmailAddress(Property property, GenericValue partyAssign, Map<String, Object> context) { + Map<String, ? extends Object> serviceMap = UtilMisc.toMap("partyId", partyAssign.get("partyId")); + Map<String, Object> resultMap = invokeService("getPartyICalUri", serviceMap, context); + String iCalUri = (String) resultMap.get("iCalUri"); + if (iCalUri != null) { + try { + property.setValue(iCalUri); + } catch (Exception e) { + Debug.logError(e, "Error while setting party URI: ", module); + } + } + } + + @SuppressWarnings("unchecked") + protected static List<GenericValue> getRelatedWorkEfforts(GenericValue workEffort, Map<String, Object> context) throws GenericEntityException { + Map<String, ? extends Object> serviceMap = UtilMisc.toMap("workEffortId", workEffort.getString("workEffortId")); + Map<String, Object> resultMap = invokeService("getICalWorkEfforts", serviceMap, context); + List<GenericValue> workEfforts = (List) resultMap.get("workEfforts"); + if (workEfforts != null) { + return WorkEffortWorker.removeDuplicateWorkEfforts(workEfforts); + } + return null; + } + + protected static Map<String, Object> invokeService(String serviceName, Map<String, ? extends Object> serviceMap, Map<String, Object> context) { + LocalDispatcher dispatcher = (LocalDispatcher) context.get("dispatcher"); + Map<String, Object> localMap = FastMap.newInstance(); + try { + ModelService modelService = null; + modelService = dispatcher.getDispatchContext().getModelService(serviceName); + for (ModelParam modelParam: modelService.getInModelParamList()) { + if (serviceMap.containsKey(modelParam.name)) { + Object value = serviceMap.get(modelParam.name); + if (UtilValidate.isNotEmpty(modelParam.type)) { + value = ObjectType.simpleTypeConvert(value, modelParam.type, null, null, null, true); + } + localMap.put(modelParam.name, value); + } + } + } catch (Exception e) { + String errMsg = "Error while creating service Map for service " + serviceName + ": "; + Debug.logError(e, errMsg, module); + return ServiceUtil.returnError(errMsg + e); + } + if (context.get("userLogin") != null) { + localMap.put("userLogin", context.get("userLogin")); + } + localMap.put("locale", context.get("locale")); + try { + return dispatcher.runSync(serviceName, localMap); + } catch (GenericServiceException e) { + String errMsg = "Error while invoking service " + serviceName + ": "; + Debug.logError(e, errMsg, module); + return ServiceUtil.returnError(errMsg + e); + } + } + + protected static void loadPartyAssignment(Property property, GenericValue partyAssign, Map<String, Object> context) { + getPartyPrimaryEmailAddress(property, partyAssign, context); + if (UtilValidate.isEmpty(property.getValue())) { + try { + // RFC 2445 4.8.4.1 and 4.8.4.3 Value must be a URL + property.setValue("MAILTO:[hidden email]"); + } catch (Exception e) { + Debug.logError(e, "Error while setting Property value: ", module); + } + } + ParameterList parameterList = property.getParameters(); + if (partyAssign != null) { + replaceParameter(parameterList, toXParameter(partyIdXParamName, partyAssign.getString("partyId"))); + replaceParameter(parameterList, new Cn(makePartyName(partyAssign))); + replaceParameter(parameterList, toParticipationStatus(partyAssign.getString("assignmentStatusId"))); + } + } + + @SuppressWarnings("unchecked") + protected static void loadRelatedParties(List<GenericValue> relatedParties, PropertyList componentProps, Map<String, Object> context) { + PropertyList attendees = componentProps.getProperties("ATTENDEE"); + for (GenericValue partyValue : relatedParties) { + if ("CAL_ORGANIZER~CAL_OWNER".contains(partyValue.getString("roleTypeId"))) { + // RFC 2445 4.6.1, 4.6.2, and 4.6.3 ORGANIZER can appear only once + replaceProperty(componentProps, createOrganizer(partyValue, context)); + } else { + String partyId = partyValue.getString("partyId"); + boolean newAttendee = true; + Attendee attendee = null; + Iterator<Attendee> i = attendees.iterator(); + while (i.hasNext()) { + attendee = i.next(); + Parameter xParameter = attendee.getParameter(partyIdXParamName); + if (xParameter != null && partyId.equals(xParameter.getValue())) { + loadPartyAssignment(attendee, partyValue, context); + newAttendee = false; + break; + } + } + if (newAttendee) { + attendee = createAttendee(partyValue, context); + componentProps.add(attendee); + } + } + } + } + + protected static void loadWorkEffort(PropertyList componentProps, GenericValue workEffort) { + replaceProperty(componentProps, new DtStamp()); // iCalendar object created date/time + replaceProperty(componentProps, toClazz(workEffort.getString("scopeEnumId"))); + replaceProperty(componentProps, toCreated(workEffort.getTimestamp("createdDate"))); + replaceProperty(componentProps, toDescription(workEffort.getString("description"))); + replaceProperty(componentProps, toDtStart(workEffort.getTimestamp("estimatedStartDate"))); + replaceProperty(componentProps, toLastModified(workEffort.getTimestamp("lastModifiedDate"))); + replaceProperty(componentProps, toPriority(workEffort.getLong("priority"))); + replaceProperty(componentProps, toLocation(workEffort.getString("locationDesc"))); + replaceProperty(componentProps, toStatus(workEffort.getString("currentStatusId"))); + replaceProperty(componentProps, toSummary(workEffort.getString("workEffortName"))); + replaceProperty(componentProps, toUid(workEffort.getString("workEffortId"))); + replaceProperty(componentProps, toXProperty(workEffortIdXPropName, workEffort.getString("workEffortId"))); + } + + protected static String makePartyName(GenericValue partyAssign) { + String partyName = partyAssign.getString("groupName"); + if (UtilValidate.isEmpty(partyName)) { + partyName = partyAssign.getString("firstName") + " " + partyAssign.getString("lastName"); + } + return partyName; + } + + protected static void replaceParameter(ParameterList parameterList, Parameter parameter) { + if (parameter == null) { + return; + } + Parameter existingParam = parameterList.getParameter(parameter.getName()); + if (existingParam != null) { + parameterList.remove(existingParam); + } + parameterList.add(parameter); + } + + protected static void replaceProperty(PropertyList propertyList, Property property) { + if (property == null) { + return; + } + Property existingProp = propertyList.getProperty(property.getName()); + if (existingProp != null) { + propertyList.remove(existingProp); + } + propertyList.add(property); + } + + protected static void setMapElement(Map<String, Object> map, String key, Object value) { + if (map == null || key == null || value == null) { + return; + } + map.put(key, value); + } + + protected static boolean isCalendarPublished(GenericValue publishProperties) { + if (publishProperties == null || !"PUBLISH_PROPS".equals(publishProperties.get("workEffortTypeId"))) { + return false; + } + DateRange range = new DateRange(publishProperties.getTimestamp("actualStartDate"), publishProperties.getTimestamp("actualCompletionDate")); + return range.includesDate(new Date()); + } + + @SuppressWarnings("unchecked") + public static void storeCalendar(InputStream is, Map<String, Object> context) throws IOException, ParserException, GenericEntityException, GenericServiceException { + CalendarBuilder builder = new CalendarBuilder(); + Calendar calendar = null; + try { + calendar = builder.build(is); + } catch (IOException e) { + Debug.logError(e, "Error while updating calendar: ", module); + throw e; + } finally { + if (is != null) { + is.close(); + } + } + Debug.logInfo("Processing calendar:\r\n" + calendar, module); + String workEffortId = fromXProperty(calendar.getProperties(), workEffortIdXPropName); + if (workEffortId == null) { + // TODO: Create new publish point + Debug.logWarning("Warning: Not an OFBiz calendar: \r\n" + calendar, module); + return; + } + if (!workEffortId.equals(context.get("workEffortId"))) { + Debug.logWarning("Spoof attempt: received calendar workEffortId " + workEffortId + + " on URL workEffortId " + context.get("workEffortId"), module); + return; + } + Map<String, ? extends Object> serviceMap = UtilMisc.toMap("workEffortId", workEffortId, "icalData", calendar.toString()); + GenericDelegator delegator = (GenericDelegator) context.get("delegator"); + GenericValue publishProperties = delegator.findOne("WorkEffort", UtilMisc.toMap("workEffortId", workEffortId), false); + if (!isCalendarPublished(publishProperties)) { + Debug.logInfo("WorkEffort calendar is not published: " + workEffortId, module); + return; + } + GenericValue iCalData = publishProperties.getRelatedOne("WorkEffortIcalData"); + if (iCalData == null) { + invokeService("createWorkEffortICalData", serviceMap, context); + } else { + invokeService("updateWorkEffortICalData", serviceMap, context); + } + List<GenericValue> workEfforts = getRelatedWorkEfforts(publishProperties, context); + if (workEfforts == null || workEfforts.size() == 0) { + return; + } + // Security issue: make sure only related work efforts get updated + Set validWorkEfforts = FastSet.newInstance(); + for (GenericValue workEffort : workEfforts) { + validWorkEfforts.add(workEffort.getString("workEffortId")); + } + List<Component> components = calendar.getComponents(); + for (Component component : components) { + if (Component.VEVENT.equals(component.getName()) || Component.VTODO.equals(component.getName())) { + workEffortId = fromXProperty(component.getProperties(), workEffortIdXPropName); + if (workEffortId != null) { + if (validWorkEfforts.contains(workEffortId)) { + storeWorkEffort(component, context); + } else { + Debug.logWarning("Spoof attempt: unrelated workEffortId " + workEffortId + + " on URL workEffortId " + context.get("workEffortId"), module); + continue; + } + } + } + } + } + + protected static boolean hasPermission(String workEffortId, String action, Map<String, Object> context) { + if (context.get("userLogin") == null) { + return false; + } + Map<String, ? extends Object> serviceMap = UtilMisc.toMap("workEffortId", workEffortId, "mainAction", action); + Map<String, Object> serviceResult = invokeService("workEffortICalendarPermission", serviceMap, context); + Boolean hasPermission = (Boolean) serviceResult.get("hasPermission"); + if (hasPermission != null) { + return hasPermission.booleanValue(); + } else { + return false; + } + } + + /** Returns a calendar derived from a Work Effort calendar publish point. + * + * @param delegator + * @param workEffortId ID of a work effort with <code>workEffortTypeId</code> equal to + * <code>PUBLISH_PROPS</code>. + * @return An iCalendar as a <code>String</code>, or <code>null</code> + * if <code>workEffortId</code> is invalid. + * @throws GenericEntityException + */ + public static String getICalendar(String workEffortId, Map<String, Object> context) throws GenericEntityException { + GenericDelegator delegator = (GenericDelegator) context.get("delegator"); + GenericValue publishProperties = delegator.findOne("WorkEffort", UtilMisc.toMap("workEffortId", workEffortId), false); + if (!isCalendarPublished(publishProperties)) { + Debug.logInfo("WorkEffort calendar is not published: " + workEffortId, module); + return null; + } + if (!"WES_PUBLIC".equals(publishProperties.get("scopeEnumId")) && !hasPermission(workEffortId, "VIEW", context)) { + return null; + } + Calendar calendar = makeCalendar(publishProperties, context); + ComponentList components = calendar.getComponents(); + List<GenericValue> workEfforts = getRelatedWorkEfforts(publishProperties, context); + if (workEfforts != null) { + for (GenericValue workEffort : workEfforts) { + toCalendarComponent(components, workEffort, context); + } + } + if (Debug.verboseOn()) { + try { + calendar.validate(true); + Debug.logVerbose("iCalendar passes validation", module); + } catch (ValidationException e) { + Debug.logVerbose("iCalendar fails validation: " + e, module); + } + } + // TODO: Remove this before commit + try { + calendar.validate(true); + Debug.logInfo("iCalendar passes validation", module); + } catch (ValidationException e) { + Debug.logInfo("iCalendar fails validation: " + e, module); + } + return calendar.toString(); + } + + protected static Calendar makeCalendar(GenericValue workEffort, Map<String, Object> context) throws GenericEntityException { + String iCalData = null; + GenericValue iCalValue = workEffort.getRelatedOne("WorkEffortIcalData"); + if (iCalValue != null) { + iCalData = iCalValue.getString("icalData"); + } + boolean newCalendar = true; + Calendar calendar = null; + if (iCalData == null) { + Debug.logVerbose("iCalendar Data not found, creating new Calendar", module); + calendar = new Calendar(); + } else { + Debug.logVerbose("iCalendar Data found, using saved Calendar", module); + StringReader reader = new StringReader(iCalData); + CalendarBuilder builder = new CalendarBuilder(); + try { + calendar = builder.build(reader); + newCalendar = false; + } catch (Exception e) { + Debug.logError(e, "Error while parsing saved iCalendar, creating new iCalendar: ", module); + calendar = new Calendar(); + } + } + PropertyList propList = calendar.getProperties(); + replaceProperty(propList, prodId); + replaceProperty(propList, new XProperty(workEffortIdXPropName, workEffort.getString("workEffortId"))); + if (newCalendar) { + propList.add(Version.VERSION_2_0); + propList.add(CalScale.GREGORIAN); + // 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; + } + + protected static void storeWorkEffort(Component component, Map<String, Object> context) throws GenericEntityException, GenericServiceException { + PropertyList propertyList = component.getProperties(); + String workEffortId = fromXProperty(propertyList, workEffortIdXPropName); + GenericDelegator delegator = (GenericDelegator) context.get("delegator"); + GenericValue workEffort = delegator.findOne("WorkEffort", UtilMisc.toMap("workEffortId", workEffortId), false); + if (workEffort == null) { + return; + } + if (!hasPermission(workEffortId, "UPDATE", context)) { + return; + } + Map<String, Object> serviceMap = FastMap.newInstance(); + serviceMap.put("workEffortId", workEffortId); + setMapElement(serviceMap, "scopeEnumId", fromClazz(propertyList)); + setMapElement(serviceMap, "description", fromDescription(propertyList)); + setMapElement(serviceMap, "estimatedStartDate", fromDtStart(propertyList)); + setMapElement(serviceMap, "estimatedMilliSeconds", fromDuration(propertyList)); + setMapElement(serviceMap, "lastModifiedDate", fromLastModified(propertyList)); + setMapElement(serviceMap, "locationDesc", fromLocation(propertyList)); + setMapElement(serviceMap, "priority", fromPriority(propertyList)); + setMapElement(serviceMap, "currentStatusId", fromStatus(propertyList)); + setMapElement(serviceMap, "workEffortName", fromSummary(propertyList)); + if ("VTODO".equals(component.getName())) { + setMapElement(serviceMap, "actualCompletionDate", fromCompleted(propertyList)); + setMapElement(serviceMap, "percentComplete", fromPercentComplete(propertyList)); + } else { + setMapElement(serviceMap, "estimatedCompletionDate", fromDtEnd(propertyList)); + } + invokeService("updateWorkEffort", serviceMap, context); + } + + @SuppressWarnings("unchecked") + protected static void toCalendarComponent(ComponentList components, GenericValue workEffort, Map<String, Object> context) throws GenericEntityException { + GenericDelegator delegator = workEffort.getDelegator(); + String workEffortId = workEffort.getString("workEffortId"); + context.put("workEffortId", workEffortId); + String workEffortTypeId = workEffort.getString("workEffortTypeId"); + GenericValue typeValue = delegator.findOne("WorkEffortType", UtilMisc.toMap("workEffortTypeId", workEffortTypeId), true); + boolean isTask = false; + boolean newComponent = true; + ComponentList resultList = null; + ComponentList alarms = null; + Component result = null; + if ("TASK".equals(workEffortTypeId) || (typeValue != null && "TASK".equals(typeValue.get("parentTypeId")))) { + isTask = true; + resultList = components.getComponents("VTODO"); + } else if ("EVENT".equals(workEffortTypeId) || (typeValue != null && "EVENT".equals(typeValue.get("parentTypeId")))){ + resultList = components.getComponents("VEVENT"); + } else { + return; + } + Iterator<Component> i = resultList.iterator(); + while (i.hasNext()) { + result = i.next(); + Property xProperty = result.getProperty(workEffortIdXPropName); + if (xProperty != null && workEffortId.equals(xProperty.getValue())) { + newComponent = false; + break; + } + } + if (isTask) { + VToDo toDo = null; + if (newComponent) { + toDo = new VToDo(); + result = toDo; + } else { + toDo = (VToDo) result; + } + alarms = toDo.getAlarms(); + } else { + VEvent event = null; + if (newComponent) { + event = new VEvent(); + result = event; + } else { + event = (VEvent) result; + } + alarms = event.getAlarms(); + } + if (newComponent) { + components.add(result); + } + PropertyList componentProps = result.getProperties(); + loadWorkEffort(componentProps, workEffort); + if (isTask) { + replaceProperty(componentProps, toCompleted(workEffort.getTimestamp("actualCompletionDate"))); + replaceProperty(componentProps, toPercentComplete(workEffort.getLong("percentComplete"))); + } else { + replaceProperty(componentProps, toDtEnd(workEffort.getTimestamp("estimatedCompletionDate"))); + } + if (workEffort.get("estimatedCompletionDate") == null) { + replaceProperty(componentProps, toDuration(workEffort.getDouble("estimatedMilliSeconds"))); + } + List<GenericValue> relatedParties = EntityUtil.filterByDate(delegator.findList("WorkEffortPartyAssignView", EntityCondition.makeCondition("workEffortId", EntityOperator.EQUALS, workEffortId), null, null, null, true)); + if (relatedParties.size() > 0) { + loadRelatedParties(relatedParties, componentProps, context); + } + if (newComponent) { + DateRange range = new DateRange(workEffort.getTimestamp("estimatedStartDate"), workEffort.getTimestamp("estimatedCompletionDate")); + if (UtilValidate.isNotEmpty(workEffort.getString("tempExprId"))) { + TemporalExpression tempExpr = TemporalExpressionWorker.getTemporalExpression(delegator, workEffort.getString("tempExprId")); + if (tempExpr != null) { + try { + ICalRecurConverter.convert(tempExpr, componentProps); + } catch (Exception e) { + replaceProperty(componentProps, new Description("Error while converting recurrence: " + e)); + } + } + } else { + componentProps.add(new DtEnd(new DateTime(range.end()))); + } + getAlarms(workEffort, alarms); + } + if (Debug.verboseOn()) { + try { + result.validate(true); + Debug.logVerbose("iCalendar component passes validation", module); + } catch (ValidationException e) { + Debug.logVerbose(e, "iCalendar component fails validation: ", module); + } + } + } + + protected static Clazz toClazz(String javaObj) { + if (javaObj == null) { + return null; + } + return new Clazz(javaObj.replace("WES_", "")); + } + + protected static Completed toCompleted(Timestamp javaObj) { + if (javaObj == null) { + return null; + } + return new Completed(new DateTime(javaObj)); + } + + protected static Created toCreated(Timestamp javaObj) { + if (javaObj == null) { + return null; + } + return new Created(new DateTime(javaObj)); + } + + protected static Description toDescription(String javaObj) { + if (javaObj == null) { + return null; + } + return new Description(javaObj); + } + + protected static DtEnd toDtEnd(Timestamp javaObj) { + if (javaObj == null) { + return null; + } + return new DtEnd(new DateTime(javaObj)); + } + + protected static DtStart toDtStart(Timestamp javaObj) { + if (javaObj == null) { + return null; + } + return new DtStart(new DateTime(javaObj)); + } + + protected static Duration toDuration(Double javaObj) { + if (javaObj == null) { + return null; + } + TimeDuration duration = TimeDuration.fromLong(javaObj.longValue()); + return new Duration(new Dur(duration.days(), duration.hours(), duration.minutes(), duration.seconds())); + } + + protected static LastModified toLastModified(Timestamp javaObj) { + if (javaObj == null) { + return null; + } + return new LastModified(new DateTime(javaObj)); + } + + protected static Location toLocation(String javaObj) { + if (javaObj == null) { + return null; + } + return new Location(javaObj); + } + + protected static PartStat toParticipationStatus(String statusId) { + if (statusId == null) { + return null; + } + return toPartStatusMap.get(statusId); + } + + protected static PercentComplete toPercentComplete(Long javaObj) { + if (javaObj == null) { + return null; + } + return new PercentComplete(javaObj.intValue()); + } + + protected static Priority toPriority(Long javaObj) { + if (javaObj == null) { + return null; + } + return new Priority(javaObj.intValue()); + } + + protected static Status toStatus(String javaObj) { + if (javaObj == null) { + return null; + } + return toStatusMap.get(javaObj); + } + + protected static Summary toSummary(String javaObj) { + if (javaObj == null) { + return null; + } + return new Summary(javaObj); + } + + protected static Uid toUid(String javaObj) { + if (javaObj == null) { + return null; + } + return new Uid(uidPrefix.concat(javaObj)); + } + + protected static XParameter toXParameter(String name, String value) { + if (name == null || value == null) { + return null; + } + return new XParameter(name, value); + } + + protected static XProperty toXProperty(String name, String value) { + if (name == null || value == null) { + return null; + } + return new XProperty(name, value); + } +} Propchange: ofbiz/trunk/applications/workeffort/src/org/ofbiz/workeffort/workeffort/ICalConverter.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: ofbiz/trunk/applications/workeffort/src/org/ofbiz/workeffort/workeffort/ICalConverter.java ------------------------------------------------------------------------------ svn:keywords = "Date Rev Author URL Id" Propchange: ofbiz/trunk/applications/workeffort/src/org/ofbiz/workeffort/workeffort/ICalConverter.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Added: ofbiz/trunk/applications/workeffort/src/org/ofbiz/workeffort/workeffort/ICalHandlerFactory.java URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/workeffort/src/org/ofbiz/workeffort/workeffort/ICalHandlerFactory.java?rev=787927&view=auto ============================================================================== --- ofbiz/trunk/applications/workeffort/src/org/ofbiz/workeffort/workeffort/ICalHandlerFactory.java (added) +++ ofbiz/trunk/applications/workeffort/src/org/ofbiz/workeffort/workeffort/ICalHandlerFactory.java Wed Jun 24 06:58:54 2009 @@ -0,0 +1,110 @@ +/******************************************************************************* + * 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.util.Map; + +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import javolution.util.FastMap; + +import org.ofbiz.base.util.Debug; +import org.ofbiz.webapp.webdav.RequestHandler; +import org.ofbiz.webapp.webdav.RequestHandlerFactory; + +/** WebDAV request handler factory for iCalendar. This class is a simple connector + * between the WebDAV servlet and the <code>ICalWorker</code> class. + */ +@SuppressWarnings("serial") +public class ICalHandlerFactory implements RequestHandlerFactory { + + public static final String module = ICalHandlerFactory.class.getName(); + + protected final Map<String, RequestHandler> handlerMap; + protected final RequestHandler invalidMethodHandler = new InvalidMethodHandler(); + protected final RequestHandler doNothingHandler = new DoNothingHandler(); + + public ICalHandlerFactory() { + this.handlerMap = FastMap.newInstance(); + this.handlerMap.put("COPY", doNothingHandler); + this.handlerMap.put("DELETE", doNothingHandler); + this.handlerMap.put("GET", new GetHandler()); + this.handlerMap.put("HEAD", doNothingHandler); + this.handlerMap.put("LOCK", doNothingHandler); + this.handlerMap.put("MKCOL", doNothingHandler); + this.handlerMap.put("MOVE", doNothingHandler); + this.handlerMap.put("POST", doNothingHandler); + this.handlerMap.put("PROPFIND", new PropFindHandler()); + this.handlerMap.put("PROPPATCH", doNothingHandler); + this.handlerMap.put("PUT", new PutHandler()); + this.handlerMap.put("UNLOCK", doNothingHandler); + } + + public RequestHandler getHandler(String method) { + RequestHandler handler = this.handlerMap.get(method); + if (handler == null) { + return invalidMethodHandler; + } + return handler; + } + + protected static class InvalidMethodHandler implements RequestHandler { + public void handleRequest(HttpServletRequest request, HttpServletResponse response, ServletContext context) throws ServletException, IOException { + Debug.logInfo("[InvalidMethodHandler] method = " + request.getMethod(), module); + response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED); + } + } + + protected static class DoNothingHandler implements RequestHandler { + public void handleRequest(HttpServletRequest request, HttpServletResponse response, ServletContext context) throws ServletException, IOException { + Debug.logInfo("[DoNothingHandler] method = " + request.getMethod(), module); + response.setStatus(HttpServletResponse.SC_OK); + } + } + + protected static class GetHandler implements RequestHandler { + public void handleRequest(HttpServletRequest request, HttpServletResponse response, ServletContext context) throws ServletException, IOException { + Debug.logInfo("[GetHandler] starting request", module); + ICalWorker.handleGetRequest(request, response, context); + Debug.logInfo("[GetHandler] finished request", module); + } + } + + protected static class PutHandler implements RequestHandler { + public void handleRequest(HttpServletRequest request, HttpServletResponse response, ServletContext context) throws ServletException, IOException { + Debug.logInfo("[PutHandler] starting request", module); + ICalWorker.handlePutRequest(request, response, context); + Debug.logInfo("[PutHandler] finished request", module); + } + } + + protected static class PropFindHandler implements RequestHandler { + public void handleRequest(HttpServletRequest request, HttpServletResponse response, ServletContext context) throws ServletException, IOException { + Debug.logInfo("[PropFindHandler] starting request", module); + ICalWorker.handlePropFindRequest(request, response, context); + Debug.logInfo("[PropFindHandler] finished request", module); + } + } + +}; Propchange: ofbiz/trunk/applications/workeffort/src/org/ofbiz/workeffort/workeffort/ICalHandlerFactory.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: ofbiz/trunk/applications/workeffort/src/org/ofbiz/workeffort/workeffort/ICalHandlerFactory.java ------------------------------------------------------------------------------ svn:keywords = "Date Rev Author URL Id" Propchange: ofbiz/trunk/applications/workeffort/src/org/ofbiz/workeffort/workeffort/ICalHandlerFactory.java ------------------------------------------------------------------------------ svn:mime-type = text/plain |
Free forum by Nabble | Edit this page |