UNPKG

@jaricardodev/ews-javascript-api

Version:
394 lines (356 loc) 18.1 kB
import { ArgumentOutOfRangeException } from '../Exceptions/ArgumentException'; import { ArrayHelper, Convert, StringHelper } from '../ExtensionMethods'; import { CreateAttachmentException } from '../Exceptions/CreateAttachmentException'; import { CreateAttachmentResponse } from '../Core/Responses/CreateAttachmentResponse'; import { DeleteAttachmentException } from '../Exceptions/DeleteAttachmentException'; import { DeleteAttachmentResponse } from '../Core/Responses/DeleteAttachmentResponse'; import { EwsLogging } from "../Core/EwsLogging"; import { EwsUtilities } from "../Core/EwsUtilities"; import { ExchangeVersion } from '../Enumerations/ExchangeVersion'; import { FileAttachment } from "./FileAttachment"; import { ICustomUpdateSerializer } from "../Interfaces/ICustomXmlUpdateSerializer"; import { IOwnedProperty } from "../Interfaces/IOwnedProperty"; import { ISelfValidate } from "../Interfaces/ISelfValidate"; import { Item } from "../Core/ServiceObjects/Items/Item"; import { ItemAttachment } from "./ItemAttachment"; import { ItemAttachmentOf } from "./ItemAttachmentOf"; import { ItemInfo } from "../Core/ServiceObjects/Items/ItemInfo"; import { ServiceObject } from "../Core/ServiceObjects/ServiceObject"; import { ServiceResponseCollection } from '../Core/Responses/ServiceResponseCollection'; import { ServiceResult } from '../Enumerations/ServiceResult'; import { ServiceValidationException } from '../Exceptions/ServiceValidationException'; import { Strings } from "../Strings"; import { TypeContainer } from "../TypeContainer"; import { XmlElementNames } from "../Core/XmlElementNames"; import { Attachment } from "./Attachment"; import { ComplexPropertyCollection } from "./ComplexPropertyCollection"; /** * Represents an item's attachment collection. */ export class AttachmentCollection extends ComplexPropertyCollection<Attachment> implements IOwnedProperty { ___typeGenerics: string[] = ["ComplexProperty"]; /** * The item owner that owns this attachment collection */ private owner: Item = null; /** * @interface:IOwnedProperty The owner of this attachment collection. */ get Owner(): ServiceObject { return this.owner; }; set Owner(value) { EwsLogging.Assert( value != null && (value instanceof TypeContainer.Item), // instanceof Item), //info: can not check instanceof to avoid circular dependency in js. TypeContainer is workaround "AttachmentCollection.IOwnedProperty.set_Owner", "value is not a descendant of ItemBase"); this.owner = <Item>value; } /** * @internal Initializes a new instance of AttachmentCollection. */ constructor() { super(); } /** * Adds a file attachment to the collection. * * @param {string} fileName The name of the file representing the content of the attachment. * @return {FileAttachment} A FileAttachment instance. */ private AddFileAttachmentXXXXX(fileName: string): FileAttachment; /** * Adds a file attachment to the collection. * * @param {string} name The display name of the new attachment. * @param {string} fileName The name of the file representing the content of the attachment. * @return {FileAttachment} A FileAttachment instance. */ private AddFileAttachmentXXXXX(name: string, fileName: string): FileAttachment; //AddFileAttachmentXXXXX(name: string, contentStream: TextStreamBase /*System.IO.Stream*/): FileAttachment; //AddFileAttachmentXXXXX(name: string, content: number[] /* Byte[] */): FileAttachment; /** * Adds a file attachment to the collection. - isContent parameter is required to be true to be able to use bas64 content directly * * @param {string} name The display name of the new attachment. * @param {string} fileContent base64 ontent of the file representing the content of the attachment. * @param {boolean} isContent if true used as base64 content of file. * @return {FileAttachment} A FileAttachment instance. */ private AddFileAttachmentXXXXX(name: string, fileContent: string, isContent: boolean): FileAttachment; private AddFileAttachmentXXXXX(nameOrFileName: string, fileNameOrContent?: string, isContent: boolean = false): FileAttachment { let argsLength = arguments.length; if (argsLength == 1) { let name = nameOrFileName.split('\\').pop().split('/').pop(); return this.AddFileAttachment(name, fileNameOrContent); } if (argsLength === 2) { throw new Error("AttachmentCollection.ts - Can only use this method with base64 content"); let fileAttachment: FileAttachment = new FileAttachment(this.owner); fileAttachment.Name = 'name'; fileAttachment.FileName = fileNameOrContent; this.InternalAdd(fileAttachment); return fileAttachment; } if (argsLength == 3) { if (isContent === true) { let fileAttachment: FileAttachment = new FileAttachment(this.owner); fileAttachment.Name = 'name'; fileAttachment.Base64Content = fileNameOrContent; this.InternalAdd(fileAttachment); return fileAttachment; } else { return this.AddFileAttachment(nameOrFileName, fileNameOrContent); } } new Error("AttachmentCollection.ts - AddFileAttachment - incorrect count of parameters"); } /** * Adds a file attachment to the collection. - isContent parameter is required to be true to be able to use bas64 content directly * * @param {string} name The display name of the new attachment. * @param {string} fileContent base64 ontent of the file representing the content of the attachment. * @return {FileAttachment} A FileAttachment instance. */ AddFileAttachment(name: string, content: string): FileAttachment { let fileAttachment: FileAttachment = new FileAttachment(this.owner); fileAttachment.Name = name; fileAttachment.Base64Content = content; this.InternalAdd(fileAttachment); return fileAttachment; } /** * Adds an item attachment to the collection * * @type <TItem> The type of the item to attach. * * @param {any*} TItem Item type, not instance * @param {string} TItemElementName XML Element Name of the Item class * @return {ItemAttachmentOf<TItem>} An ItemAttachment instance. */ AddItemAttachment<TItem extends Item>(TItem: typeof Item, TItemElementName: string): ItemAttachmentOf<TItem> { if (typeof TItem.Attachable === 'undefined' || TItem.Attachable === false) { throw new Error( StringHelper.Format( "Items of type {0} are not supported as attachments.", TItem["name"])); //InvalidOperationException } let itemAttachment: ItemAttachmentOf<TItem> = new ItemAttachmentOf<TItem>(this.owner); //ref: //info: ItemAttachment can not be generic when same name non generic version exhist. TypeScript limitation itemAttachment.Item = <TItem>(new ItemInfo()).CreateItemFromItemClass(itemAttachment, TItemElementName, true); //todo: needs to implement Reflector metadata for Type to class creation map this.InternalAdd(<any><any>itemAttachment); return itemAttachment; } /** * Removes all attachments from this collection. */ Clear(): void { this.InternalClear(); } /** * @internal Disables the change log clearing mechanism. Attachment collections are saved separately from the items they belong to. */ ClearChangeLog(): void { /** Do nothing */ } /** * @internal Instantiate the appropriate attachment type depending on the current XML element name. * * @param {string} xmlElementName The XML element name from which to determine the type of attachment to create. * @return {Attachment} An Attachment instance. */ CreateComplexProperty(xmlElementName: string): Attachment { switch (xmlElementName) { case XmlElementNames.FileAttachment: return new FileAttachment(this.owner); case XmlElementNames.ItemAttachment: return <any><any>new ItemAttachment(this.owner); default: return null; } } //JsonDeserializationNotImplementedException CreateDefaultComplexProperty(): Attachment { EwsLogging.DebugLog("AttachmentCollection.ts - CreateDefaultComplexProperty : Not implemented."); return null; } /** * @internal Determines the name of the XML element associated with the complexProperty parameter. * * @param {Attachment} complexProperty The attachment object for which to determine the XML element name with. * @return {string} The XML element name associated with the complexProperty parameter. */ GetCollectionItemXmlElementName(complexProperty: Attachment): string { if (complexProperty instanceof FileAttachment) { return XmlElementNames.FileAttachment; } else { return XmlElementNames.ItemAttachment; } } /** * @internal Determines whether there are any unsaved attachment collection changes. * * @return {boolean} True if attachment adds or deletes haven't been processed yet. */ HasUnprocessedChanges(): boolean { for (let attachment of this.Items) { if (attachment.IsNew) { return true; } } // Any pending deletions? for (let attachment of this.RemovedItems) { if (!attachment.IsNew) { return true; } } // Recurse: process item attachments to check for new or deleted sub-attachments. for (let itemAttachment of ArrayHelper.OfType<Attachment, ItemAttachment>(this.Items, (attach) => { return attach instanceof ItemAttachment; })) { if (itemAttachment.Item != null) { if (itemAttachment.Item.Attachments.HasUnprocessedChanges()) { return true; } } } return false; } /** * Calls the CreateAttachment web method to create a list of attachments. * * @param {string} parentItemId The Id of the parent item of the new attachments. * @param {Attachment[]} attachments The attachments to create. */ private InternalCreateAttachments(parentItemId: string, attachments: Attachment[]): Promise<void> { return this.owner.Service.CreateAttachments(parentItemId, attachments) .then((responses: ServiceResponseCollection<CreateAttachmentResponse>) => { for (let response of responses.Responses) { // We remove all attachments that were successfully deleted from the change log. We should never // receive a warning from EWS, so we ignore them. if (response.Result != ServiceResult.Error) { this.RemoveFromChangeLog(response.Attachment); } } // TODO : Should we throw for warnings as well? if (responses.OverallResult == ServiceResult.Error) { throw new CreateAttachmentException(responses, Strings.AttachmentCreationFailed); } }); } /** * Calls the DeleteAttachment web method to delete a list of attachments. * * @param {Attachment[]} attachments The attachments to delete. */ private InternalDeleteAttachments(attachments: Attachment[]): Promise<void> { return this.owner.Service.DeleteAttachments(attachments) .then((responses: ServiceResponseCollection<DeleteAttachmentResponse>) => { for (let response of responses.Responses) { // We remove all attachments that were successfully deleted from the change log. We should never // receive a warning from EWS, so we ignore them. if (response.Result != ServiceResult.Error) { this.RemoveFromChangeLog(response.Attachment); } } // TODO : Should we throw for warnings as well? if (responses.OverallResult == ServiceResult.Error) { throw new DeleteAttachmentException(responses, Strings.AtLeastOneAttachmentCouldNotBeDeleted); } }); } /** * Removes the specified attachment. * * @param {Attachment} attachment The attachment to remove. * @return {boolean} True if the attachment was successfully removed from the collection, false otherwise. */ Remove(attachment: Attachment): boolean { EwsUtilities.ValidateParam(attachment, "attachment"); return this.InternalRemove(attachment); } /** * Removes the attachment at the specified index. * * @param {number} index Index of the attachment to remove. */ RemoveAt(index: number): void { if (index < 0 || index >= this.Count) { throw new ArgumentOutOfRangeException("index", Strings.IndexIsOutOfRange); } this.InternalRemoveAt(index); } /** * @internal Saves this collection by creating new attachment and deleting removed ones. */ Save(): Promise<void> { let attachments = []; // Retrieve a list of attachments that have to be deleted. for (let attachment of this.RemovedItems) { if (!attachment.IsNew) { attachments.push(attachment); } } // If any, delete them by calling the DeleteAttachment web method. //promise resolves with null to keep chaining return Promise.resolve(attachments.length > 0 ? this.InternalDeleteAttachments(attachments) : void 0) .then<void>(() => { attachments.splice(0); }) .then(() => { // Retrieve a list of attachments that have to be created. for (let attachment of this.Items) { if (attachment.IsNew) { attachments.push(attachment); } } // If there are any, create them by calling the CreateAttachment web method. if (attachments.length > 0) { let parentId = this.owner.IsAttachment ? this.owner.ParentAttachment.Id : this.owner.Id.UniqueId; return this.InternalCreateAttachments(parentId, attachments); } else { return Promise.resolve(); } }).then(() => { // Process all of the item attachments in this collection. let itemAttachments = ArrayHelper.OfType<Attachment, ItemAttachment>(attachments, (attachment) => attachment instanceof ItemAttachment); return itemAttachments.reduce((prev, curr, index) => { return prev.then<void>(() => { return curr.Item.Attachments.Save().then(() => { curr.Item.ClearChangeLog(); }); }); }, Promise.resolve()); }).then(() => { super.ClearChangeLog(); }); } /** * @internal Validates this instance. */ Validate(): void { // Validate all added attachments let contactPhotoFound: boolean = false; for (let attachmentIndex = 0; attachmentIndex < this.AddedItems.length; attachmentIndex++) { let attachment: Attachment = this.AddedItems[attachmentIndex]; if (attachment.IsNew) { // At the server side, only the last attachment with IsContactPhoto is kept, all other IsContactPhoto // attachments are removed. CreateAttachment will generate AttachmentId for each of such attachments (although // only the last one is valid). // // With E14 SP2 CreateItemWithAttachment, such request will only return 1 AttachmentId; but the client // expects to see all, so let us prevent such "invalid" request in the first place. // // The IsNew check is to still let CreateAttachmentRequest allow multiple IsContactPhoto attachments. // if (this.owner.IsNew && this.owner.Service.RequestedServerVersion >= ExchangeVersion.Exchange2010_SP2) { let fileAttachment: FileAttachment = <FileAttachment>attachment; if (fileAttachment instanceof Attachment && fileAttachment.IsContactPhoto) { if (contactPhotoFound) { throw new ServiceValidationException(Strings.MultipleContactPhotosInAttachment); } contactPhotoFound = true; } } attachment.Validate(attachmentIndex); } } } /** * @internal Validates and saves this instance. **Not in official EWS source, to workaround some promise errors with validate and save** */ ValidateAndSave(): Promise<void> { this.Validate(); return this.Save(); } }