1
0
mirror of https://github.com/moparisthebest/davmail synced 2024-12-13 03:02:22 -05:00

Caldav: implement task percent complete and status over WebDav

git-svn-id: http://svn.code.sf.net/p/davmail/code/trunk@1764 3d1905a2-6b24-0410-a738-b14d5a86fcbd
This commit is contained in:
mguessan 2011-08-07 09:57:03 +00:00
parent 4d79c262ca
commit e9c9aad7b9
4 changed files with 96 additions and 35 deletions

View File

@ -2146,7 +2146,7 @@ public abstract class ExchangeSession {
fixICS(itemBody.getBytes("UTF-8"), false); fixICS(itemBody.getBytes("UTF-8"), false);
// fix task item name // fix task item name
if (vCalendar.isTodo() && this.itemName.endsWith(".ics")) { if (vCalendar.isTodo() && this.itemName.endsWith(".ics")) {
this.itemName = itemName.substring(0, itemName.length() - 3)+".EML"; this.itemName = itemName.substring(0, itemName.length() - 3)+"EML";
} }
} }

View File

@ -82,6 +82,23 @@ public class DavExchangeSession extends ExchangeSession {
WELL_KNOWN_FOLDERS.add(Field.getPropertyName("outbox")); WELL_KNOWN_FOLDERS.add(Field.getPropertyName("outbox"));
} }
static final Map<String, String> vTodoToTaskStatusMap = new HashMap<String, String>();
static final Map<String, String> taskTovTodoStatusMap = new HashMap<String, String>();
static {
//taskTovTodoStatusMap.put("0", null);
taskTovTodoStatusMap.put("1", "IN-PROCESS");
taskTovTodoStatusMap.put("2", "COMPLETED");
taskTovTodoStatusMap.put("3", "NEEDS-ACTION");
taskTovTodoStatusMap.put("4", "CANCELLED");
//vTodoToTaskStatusMap.put(null, "0");
vTodoToTaskStatusMap.put("IN-PROCESS", "1");
vTodoToTaskStatusMap.put("COMPLETED", "2");
vTodoToTaskStatusMap.put("NEEDS-ACTION", "3");
vTodoToTaskStatusMap.put("CANCELLED", "4");
}
/** /**
* Various standard mail boxes Urls * Various standard mail boxes Urls
*/ */
@ -1349,34 +1366,36 @@ public class DavExchangeSession extends ExchangeSession {
byte[] result = null; byte[] result = null;
// experimental: build VCALENDAR from properties // experimental: build VCALENDAR from properties
DavPropertyNameSet davPropertyNameSet = new DavPropertyNameSet();
davPropertyNameSet.add(Field.getPropertyName("method"));
davPropertyNameSet.add(Field.getPropertyName("created"));
davPropertyNameSet.add(Field.getPropertyName("calendarlastmodified"));
davPropertyNameSet.add(Field.getPropertyName("dtstamp"));
davPropertyNameSet.add(Field.getPropertyName("calendaruid"));
davPropertyNameSet.add(Field.getPropertyName("subject"));
davPropertyNameSet.add(Field.getPropertyName("dtstart"));
davPropertyNameSet.add(Field.getPropertyName("dtend"));
davPropertyNameSet.add(Field.getPropertyName("transparent"));
davPropertyNameSet.add(Field.getPropertyName("organizer"));
davPropertyNameSet.add(Field.getPropertyName("to"));
davPropertyNameSet.add(Field.getPropertyName("description"));
davPropertyNameSet.add(Field.getPropertyName("rrule"));
davPropertyNameSet.add(Field.getPropertyName("exdate"));
davPropertyNameSet.add(Field.getPropertyName("sensitivity"));
davPropertyNameSet.add(Field.getPropertyName("alldayevent"));
davPropertyNameSet.add(Field.getPropertyName("busystatus"));
davPropertyNameSet.add(Field.getPropertyName("reminderset"));
davPropertyNameSet.add(Field.getPropertyName("reminderdelta"));
// task
davPropertyNameSet.add(Field.getPropertyName("importance"));
davPropertyNameSet.add(Field.getPropertyName("uid"));
PropFindMethod propFindMethod = new PropFindMethod(permanentUrl, davPropertyNameSet, 0);
try { try {
MultiStatusResponse[] responses = DavGatewayHttpClientFacade.executeMethod(httpClient, propFindMethod); //MultiStatusResponse[] responses = DavGatewayHttpClientFacade.executeMethod(httpClient, propFindMethod);
Set<String> eventProperties = new HashSet<String>();
eventProperties.add("method");
eventProperties.add("created");
eventProperties.add("calendarlastmodified");
eventProperties.add("dtstamp");
eventProperties.add("calendaruid");
eventProperties.add("subject");
eventProperties.add("dtstart");
eventProperties.add("dtend");
eventProperties.add("transparent");
eventProperties.add("organizer");
eventProperties.add("to");
eventProperties.add("description");
eventProperties.add("rrule");
eventProperties.add("exdate");
eventProperties.add("sensitivity");
eventProperties.add("alldayevent");
eventProperties.add("busystatus");
eventProperties.add("reminderset");
eventProperties.add("reminderdelta");
// task
eventProperties.add("importance");
eventProperties.add("uid");
eventProperties.add("percentcomplete");
MultiStatusResponse[] responses = searchItems(folderPath, eventProperties, DavExchangeSession.this.isEqualTo("urlcompname", convertItemNameToEML(itemName)), FolderQueryTraversal.Shallow, 1);
if (responses.length == 0) { if (responses.length == 0) {
throw new HttpNotFoundException(permanentUrl + " not found"); throw new HttpNotFoundException(permanentUrl + " not found");
} }
@ -1398,6 +1417,9 @@ public class DavExchangeSession extends ExchangeSession {
vEvent.setPropertyValue("PRIORITY", convertPriorityFromExchange(getPropertyIfExists(davPropertySet, "importance"))); vEvent.setPropertyValue("PRIORITY", convertPriorityFromExchange(getPropertyIfExists(davPropertySet, "importance")));
if (instancetype == null) { if (instancetype == null) {
vEvent.type = "VTODO"; vEvent.type = "VTODO";
vEvent.setPropertyValue("PERCENT-COMPLETE", String.valueOf(getDoublePropertyIfExists(davPropertySet, "percentcomplete")*100));
vEvent.setPropertyValue("STATUS", taskTovTodoStatusMap.get(getPropertyIfExists(davPropertySet, "taskstatus")));
} else { } else {
vEvent.type = "VEVENT"; vEvent.type = "VEVENT";
// check mandatory dtstart value // check mandatory dtstart value
@ -1485,8 +1507,6 @@ public class DavExchangeSession extends ExchangeSession {
} catch (IOException e) { } catch (IOException e) {
LOGGER.warn("Unable to rebuild event content: " + e.getMessage(), e); LOGGER.warn("Unable to rebuild event content: " + e.getMessage(), e);
throw buildHttpException(e); throw buildHttpException(e);
} finally {
propFindMethod.releaseConnection();
} }
return result; return result;
@ -1545,6 +1565,12 @@ public class DavExchangeSession extends ExchangeSession {
propertyValues.add(Field.createPropertyValue("subject", vCalendar.getFirstVeventPropertyValue("SUMMARY"))); propertyValues.add(Field.createPropertyValue("subject", vCalendar.getFirstVeventPropertyValue("SUMMARY")));
propertyValues.add(Field.createPropertyValue("description", vCalendar.getFirstVeventPropertyValue("DESCRIPTION"))); propertyValues.add(Field.createPropertyValue("description", vCalendar.getFirstVeventPropertyValue("DESCRIPTION")));
propertyValues.add(Field.createPropertyValue("importance", convertPriorityToExchange(vCalendar.getFirstVeventPropertyValue("PRIORITY")))); propertyValues.add(Field.createPropertyValue("importance", convertPriorityToExchange(vCalendar.getFirstVeventPropertyValue("PRIORITY"))));
String percentComplete = vCalendar.getFirstVeventPropertyValue("PERCENT-COMPLETE");
if (percentComplete == null) {
percentComplete = "0";
}
propertyValues.add(Field.createPropertyValue("percentcomplete", String.valueOf(Double.parseDouble(percentComplete) / 100)));
propertyValues.add(Field.createPropertyValue("taskstatus", vTodoToTaskStatusMap.get(vCalendar.getFirstVeventPropertyValue("STATUS"))));
ExchangePropPatchMethod propPatchMethod = new ExchangePropPatchMethod(encodedHref, propertyValues); ExchangePropPatchMethod propPatchMethod = new ExchangePropPatchMethod(encodedHref, propertyValues);
propPatchMethod.setRequestHeader("Translate", "f"); propPatchMethod.setRequestHeader("Translate", "f");
@ -1859,6 +1885,15 @@ public class DavExchangeSession extends ExchangeSession {
} }
} }
protected double getDoublePropertyIfExists(DavPropertySet properties, String alias) {
DavProperty property = properties.get(Field.getResponsePropertyName(alias));
if (property == null) {
return 0;
} else {
return Double.parseDouble((String) property.getValue());
}
}
protected byte[] getBinaryPropertyIfExists(DavPropertySet properties, String alias) { protected byte[] getBinaryPropertyIfExists(DavPropertySet properties, String alias) {
byte[] property = null; byte[] property = null;
String base64Property = getPropertyIfExists(properties, alias); String base64Property = getPropertyIfExists(properties, alias);
@ -1990,7 +2025,12 @@ public class DavExchangeSession extends ExchangeSession {
protected MultiStatusResponse[] searchItems(String folderPath, Set<String> attributes, Condition condition, protected MultiStatusResponse[] searchItems(String folderPath, Set<String> attributes, Condition condition,
FolderQueryTraversal folderQueryTraversal, int maxCount) throws IOException { FolderQueryTraversal folderQueryTraversal, int maxCount) throws IOException {
String folderUrl = getFolderPath(folderPath); String folderUrl;
if (folderPath.startsWith("http")) {
folderUrl = folderPath;
} else {
folderUrl = getFolderPath(folderPath);
}
StringBuilder searchRequest = new StringBuilder(); StringBuilder searchRequest = new StringBuilder();
searchRequest.append("SELECT ") searchRequest.append("SELECT ")
.append(Field.getRequestPropertyString("permanenturl")); .append(Field.getRequestPropertyString("permanenturl"));
@ -2037,15 +2077,19 @@ public class DavExchangeSession extends ExchangeSession {
String itemPath = getFolderPath(folderPath) + '/' + emlItemName; String itemPath = getFolderPath(folderPath) + '/' + emlItemName;
MultiStatusResponse[] responses = null; MultiStatusResponse[] responses = null;
try { try {
responses = DavGatewayHttpClientFacade.executePropFindMethod(httpClient, URIUtil.encodePath(itemPath), 0, EVENT_REQUEST_PROPERTIES_NAME_SET); try {
if (responses.length == 0 && isMainCalendar(folderPath)) { responses = DavGatewayHttpClientFacade.executePropFindMethod(httpClient, URIUtil.encodePath(itemPath), 0, EVENT_REQUEST_PROPERTIES_NAME_SET);
} catch (HttpNotFoundException e) {
// ignore
}
if (responses == null || responses.length == 0 && isMainCalendar(folderPath)) {
if (itemName.endsWith(".ics")) { if (itemName.endsWith(".ics")) {
itemName = itemName.substring(0, itemName.length() - 3) + "EML"; itemName = itemName.substring(0, itemName.length() - 3) + "EML";
} }
// look for item in tasks folder // look for item in tasks folder
responses = DavGatewayHttpClientFacade.executePropFindMethod(httpClient, URIUtil.encodePath(getFolderPath(TASKS) + '/' + emlItemName), 0, EVENT_REQUEST_PROPERTIES_NAME_SET); responses = DavGatewayHttpClientFacade.executePropFindMethod(httpClient, URIUtil.encodePath(getFolderPath(TASKS) + '/' + emlItemName), 0, EVENT_REQUEST_PROPERTIES_NAME_SET);
} }
if (responses.length == 0) { if (responses == null || responses.length == 0) {
throw new HttpNotFoundException(itemPath + " not found"); throw new HttpNotFoundException(itemPath + " not found");
} }
} catch (HttpNotFoundException e) { } catch (HttpNotFoundException e) {

View File

@ -122,10 +122,14 @@ public class ExchangePropPatchMethod extends PostMethod {
writer.write(':'); writer.write(':');
writer.write(propertyValue.getName()); writer.write(propertyValue.getName());
if (propertyType != null) { if (propertyType != null) {
String typeString = propertyType.toString().toLowerCase();
if ("integer".equals(typeString)) {
typeString = "int";
}
writer.write(' '); writer.write(' ');
writer.write(nameSpaceMap.get(TYPE_NAMESPACE)); writer.write(nameSpaceMap.get(TYPE_NAMESPACE));
writer.write(":dt=\""); writer.write(":dt=\"");
writer.write(propertyType.toString().toLowerCase()); writer.write(typeString);
writer.write("\""); writer.write("\"");
} }
writer.write('>'); writer.write('>');

View File

@ -78,6 +78,7 @@ public class Field {
propertyTypeMap.put(PropertyType.SystemTime, "0040"); // PT_SYSTIME propertyTypeMap.put(PropertyType.SystemTime, "0040"); // PT_SYSTIME
propertyTypeMap.put(PropertyType.String, "001f"); // 001f is PT_UNICODE_STRING, 001E is PT_STRING propertyTypeMap.put(PropertyType.String, "001f"); // 001f is PT_UNICODE_STRING, 001E is PT_STRING
propertyTypeMap.put(PropertyType.Binary, "0102"); // PT_BINARY propertyTypeMap.put(PropertyType.Binary, "0102"); // PT_BINARY
propertyTypeMap.put(PropertyType.Double, "0005"); // PT_DOUBLE
} }
@SuppressWarnings({"UnusedDeclaration"}) @SuppressWarnings({"UnusedDeclaration"})
@ -325,6 +326,8 @@ public class Field {
// task // task
createField(URN_SCHEMAS_MAILHEADER, "importance");//PS_INTERNET_HEADERS/importance createField(URN_SCHEMAS_MAILHEADER, "importance");//PS_INTERNET_HEADERS/importance
createField("percentcomplete", DistinguishedPropertySetType.Task, 0x8102, "percentcomplete", PropertyType.Double);
createField("taskstatus", DistinguishedPropertySetType.Task, 0x8101, "taskstatus", PropertyType.Integer);
} }
protected static String toHexString(int propertyTag) { protected static String toHexString(int propertyTag) {
@ -362,11 +365,15 @@ public class Field {
if (propertySetType == DistinguishedPropertySetType.Address) { if (propertySetType == DistinguishedPropertySetType.Address) {
// Address namespace expects integer names // Address namespace expects integer names
name = String.valueOf(propertyTag); name = String.valueOf(propertyTag);
updateAlias = "_x0030_x" + toHexString(propertyTag);
} else if (propertySetType == DistinguishedPropertySetType.Task) {
name = "0x" + toHexString(propertyTag);
updateAlias = "0x0000" + toHexString(propertyTag);
} else { } else {
// Common namespace expects hex names // Common namespace expects hex names
name = "0x" + toHexString(propertyTag); name = "0x" + toHexString(propertyTag);
updateAlias = "_x0030_x" + toHexString(propertyTag);
} }
updateAlias = "_x0030_x" + toHexString(propertyTag);
Field field = new Field(alias, Namespace.getNamespace(SCHEMAS_MAPI_ID.getURI() + Field field = new Field(alias, Namespace.getNamespace(SCHEMAS_MAPI_ID.getURI() +
'{' + distinguishedPropertySetMap.get(propertySetType) + "}/"), name, propertyType, responseAlias, null, updateAlias); '{' + distinguishedPropertySetMap.get(propertySetType) + "}/"), name, propertyType, responseAlias, null, updateAlias);
fieldMap.put(field.alias, field); fieldMap.put(field.alias, field);
@ -397,6 +404,7 @@ public class Field {
protected final boolean isIntValue; protected final boolean isIntValue;
protected final boolean isMultivalued; protected final boolean isMultivalued;
protected final boolean isBooleanValue; protected final boolean isBooleanValue;
protected final boolean isFloatValue;
/** /**
* Create field for namespace and name, use name as alias. * Create field for namespace and name, use name as alias.
@ -443,6 +451,7 @@ public class Field {
isMultivalued = propertyType != null && propertyType.toString().endsWith("Array"); isMultivalued = propertyType != null && propertyType.toString().endsWith("Array");
isIntValue = propertyType == PropertyType.Long || propertyType == PropertyType.Integer || propertyType == PropertyType.Short; isIntValue = propertyType == PropertyType.Long || propertyType == PropertyType.Integer || propertyType == PropertyType.Short;
isBooleanValue = propertyType == PropertyType.Boolean; isBooleanValue = propertyType == PropertyType.Boolean;
isFloatValue = propertyType == PropertyType.Float || propertyType == PropertyType.Double;
this.uri = namespace.getURI() + name; this.uri = namespace.getURI() + name;
if (responseAlias == null) { if (responseAlias == null) {
@ -567,6 +576,10 @@ public class Field {
} else { } else {
throw new RuntimeException("Invalid value for " + field.alias + ": " + value); throw new RuntimeException("Invalid value for " + field.alias + ": " + value);
} }
} else if (field.isFloatValue) {
return new PropertyValue(davPropertyName.getNamespace().getURI(), davPropertyName.getName(), StringUtil.xmlEncode(value), PropertyType.Float);
} else if (field.isIntValue) {
return new PropertyValue(field.updatePropertyName.getNamespace().getURI(), field.updatePropertyName.getName(), StringUtil.xmlEncode(value), PropertyType.Integer);
} else { } else {
return new PropertyValue(davPropertyName.getNamespace().getURI(), davPropertyName.getName(), StringUtil.xmlEncode(value)); return new PropertyValue(davPropertyName.getNamespace().getURI(), davPropertyName.getName(), StringUtil.xmlEncode(value));
} }