Мы используем Office JS для разработки надстройки для календаря Outlook. Для той же встречи идентификаторы EWS организатора и участников не совпадает. По-видимому, мне нужен ICalUid
от Exchange, который является общим для организатора и участников, а также для разных событий для одного и того же предмета, именно то, что нам нужно. Но Office JS не предоставляет ни такого свойства, ни метода для его получения (только для .NET).
Но Office JS предоставляет Office.context.mailbox.makeEwsRequestAsync
, с помощью которого я могу делать запросы SOAP к серверу Exchange, используя XML.
Что такое XML-код EWS SOAP для получения ICalUid
из идентификатора EWS?
Это работает? Просто заменить FieldURI
на ICalUid
?
function getSubjectRequest(id) {
// Return a GetItem operation request for the subject of the specified item.
var result =
'<?xml version = "1.0" encoding = "utf-8"?>' +
'<soap:Envelope xmlns:xsi = "https://www.w3.org/2001/XMLSchema-instance"' +
' xmlns:xsd = "https://www.w3.org/2001/XMLSchema"' +
' xmlns:soap = "http://schemas.xmlsoap.org/soap/envelope/"' +
' xmlns:t = "http://schemas.microsoft.com/exchange/services/2006/types">' +
' <soap:Header>' +
' <RequestServerVersion Version = "Exchange2013" xmlns = "http://schemas.microsoft.com/exchange/services/2006/types" soap:mustUnderstand = "0" />' +
' </soap:Header>' +
' <soap:Body>' +
' <GetItem xmlns = "http://schemas.microsoft.com/exchange/services/2006/messages">' +
' <ItemShape>' +
' <t:BaseShape>IdOnly</t:BaseShape>' +
' <t:AdditionalProperties>' +
' <t:FieldURI FieldURI = "item:ICalUid"/>' +
' </t:AdditionalProperties>' +
' </ItemShape>' +
' <ItemIds><t:ItemId Id = "' + id + '"/></ItemIds>' +
' </GetItem>' +
' </soap:Body>' +
'</soap:Envelope>';
return result;
}
function sendRequest() {
// Create a local variable that contains the mailbox.
var mailbox = Office.context.mailbox;
mailbox.makeEwsRequestAsync(getSubjectRequest(mailbox.item.itemId), callback);
}
function callback(asyncResult) {
var result = asyncResult.value;
var context = asyncResult.context;
// Process the returned response here.
}
Для IcalUid вам понадобится
<t:FieldURI FieldURI = "calendar:UID"/>
Имейте в виду, что ICalUid не является свойством для поиска, поэтому вы не сможете найти встречи на основе этого свойства, если вам это нужно, а затем посмотрите на использование свойства Extended Goid, такого как João, связанного в комментарии.
Просто для подтверждения: является ли ICalUid
общим (точно одинаковым) как для Организатора, так и для всех Участников, когда я использую соответствующие идентификаторы EWS в качестве входных данных?
в чем разница между ICalUid
и расширенным свойством Goid?
Goid — это свойство Exchange, которое используется Outlook для уникальной идентификации встреч в календаре, поэтому оно будет одинаковым для всех экземпляров, см. docs.microsoft.com/en-us/openspecs/exchange_server_protocols/…. Он также устанавливается на приглашения, принятия и т. д. ICalUid — это свойство rfc, установленное на встрече, см. icalendar.org/iCalendar-RFC-5545/3-8-4-7-unique-identifier.html
извините, я ничего не понимаю в запросах мыла к EWS, я пытаюсь разработать офисную надстройку js. Не могли бы вы опубликовать в своем ответе полный XML-код для получения ICalUid
из ItemId? Я сделал, как вы сказали (просто заменив эту строку), и в ответ я снова получаю ItemId. Что поставить в BaseShape
? Идентификаторы?
Для тех, кто придет после меня, и за то, что не потерял 3 дня на этом, как я, вот как я добился
Эта функция получает идентификатор ewsId как для Организатора, так и для Участника, поскольку у них разные способы его получения.
// Event Outlook ID is available when
// a) Outlook event already existed in user's calendar or
// b) it was already saved by the user in the current session
function getEventOutlookUid (callback) {
if (typeof Office.context.mailbox.item.getItemIdAsync === 'function') { // is Organizer
Office.context.mailbox.item.getItemIdAsync(function (result) {
if (result.status === Office.AsyncResultStatus.Succeeded) {
callback(null, result.value)
} else {
console.warn(`EventOutlookUid unavailable: ${result.error.message}. Probably just a new event`)
callback(null, null)
}
})
} else if (Office.context.mailbox.item.itemId) { // is Attendee
callback(null, Office.context.mailbox.item.itemId)
} else {
callback(Error('Neither Office.context.mailbox.item.getItemIdAsync nor Office.context.mailbox.item.itemId could get Outlook Item UID'))
}
}
Эта функция получает дополнительные идентификаторы, анализируя XML из запроса SOAP. Вам нужен jQuery, так как с ним очень легко анализировать XML.
function getExtendedIds (callback) {
getEventOutlookUid((err, eventOutlookUid) => {
if (err) {
console.error('Error fetching Outlook UID ' + err.message)
callback(Error(err))
} else {
const soapRequest = generateCalendarUidSoapRequest(eventOutlookUid)
if (validateXML(soapRequest)) {
Office.context.mailbox.makeEwsRequestAsync(soapRequest, function (result) {
if (result.status === Office.AsyncResultStatus.Succeeded) {
// console.info(prettifyXml(result.value))
const res = $.parseXML(result.value)
const changeKey = res.getElementsByTagName('t:ItemId')[0].getAttribute('ChangeKey')
const UID = res.getElementsByTagName('t:UID')[0].textContent
const GlobalObjectId = res.getElementsByTagName('t:GlobalObjectId')[0].textContent
const ConversationId = res.getElementsByTagName('t:ConversationId')[0].getAttribute('Id')
callback(null, { ewsId: eventOutlookUid, changeKey, UID, GlobalObjectId, ConversationId })
}
})
} else {
callback(Error('Invalid XML request'))
}
}
})
}
Эта функция генерирует запрос XML SOAP для получения всех возможных идентификаторов.
function generateCalendarUidSoapRequest (itemId) {
const request = '<soap:Envelope xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xmlns:m = "http://schemas.microsoft.com/exchange/services/2006/messages" xmlns:t = "http://schemas.microsoft.com/exchange/services/2006/types" xmlns:soap = "http://schemas.xmlsoap.org/soap/envelope/">' +
' <soap:Header><t:RequestServerVersion Version = "Exchange2013" /></soap:Header>' +
' <soap:Body>' +
' <m:GetItem>' +
' <m:ItemShape>' +
' <t:BaseShape>AllProperties</t:BaseShape>' +
' </m:ItemShape >' +
' <t:AdditionalProperties>' +
' <t:FieldURI FieldURI = "calendar:UID"/>' +
' <t:ExtendedFieldURI DistinguishedPropertySetId = "Meeting" PropertyId = "3" PropertyType = "Binary" />' +
' </t:AdditionalProperties>' +
' <m:ItemIds>' +
' <t:ItemId Id = "' + itemId + '" />' +
' </m:ItemIds>' +
' </m:GetItem>' +
' </soap:Body>' +
'</soap:Envelope>'
return request
}
Это вспомогательные функции для предварительной обработки и проверки XML.
function prettifyXml (sourceXml) {
const xmlDoc = new DOMParser().parseFromString(sourceXml, 'application/xml')
const xsltDoc = new DOMParser().parseFromString([
// describes how we want to modify the XML - indent everything
'<xsl:stylesheet xmlns:xsl = "http://www.w3.org/1999/XSL/Transform">',
' <xsl:strip-space elements = "*"/>',
' <xsl:template match = "para[content-style][not(text())]">', // change to just text() to strip space in text nodes
' <xsl:value-of select = "normalize-space(.)"/>',
' </xsl:template>',
' <xsl:template match = "node()|@*">',
' <xsl:copy><xsl:apply-templates select = "node()|@*"/></xsl:copy>',
' </xsl:template>',
' <xsl:output indent = "yes"/>',
'</xsl:stylesheet>'
].join('\n'), 'application/xml')
const xsltProcessor = new XSLTProcessor()
xsltProcessor.importStylesheet(xsltDoc)
const resultDoc = xsltProcessor.transformToDocument(xmlDoc)
const resultXml = new XMLSerializer().serializeToString(resultDoc)
return resultXml
}
function validateXML (xmlString) {
const domParser = new DOMParser()
const dom = domParser.parseFromString(xmlString, 'text/xml')
// print the name of the root element or error message
return dom.documentElement.nodeName !== 'parsererror'
}
Просто добавьте все эти функции в область/модуль, а затем получите дополнительные идентификаторы с помощью
getExtendedIds((err, res) => {
if (!err) {
console.info(res)
}
})
У вас будет объект с { ewsId, changeKey, GlobalObjectId, ConversationId, UID }