Module exchangelib.items
Sub-modules
exchangelib.items.base
exchangelib.items.calendar_item
exchangelib.items.contact
exchangelib.items.item
exchangelib.items.message
exchangelib.items.post
exchangelib.items.task
Classes
class AcceptItem (**kwargs)
-
Expand source code
class AcceptItem(BaseMeetingReplyItem): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/acceptitem""" ELEMENT_NAME = "AcceptItem"
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/acceptitem
Pick out optional 'account' and 'folder' kwargs, and pass the rest to the parent class.
:param kwargs: 'account' is optional but allows calling 'send()' and 'delete()' 'folder' is optional but allows calling 'save()'. If 'folder' has an account, and 'account' is not set, we use folder.account.
Ancestors
Class variables
var ELEMENT_NAME
Inherited members
class BaseItem (**kwargs)
-
Expand source code
class BaseItem(RegisterMixIn, metaclass=EWSMeta): """Base class for all other classes that implement EWS items.""" ID_ELEMENT_CLS = ItemId _id = IdElementField(field_uri="item:ItemId", value_cls=ID_ELEMENT_CLS) __slots__ = "account", "folder" def __init__(self, **kwargs): """Pick out optional 'account' and 'folder' kwargs, and pass the rest to the parent class. :param kwargs: 'account' is optional but allows calling 'send()' and 'delete()' 'folder' is optional but allows calling 'save()'. If 'folder' has an account, and 'account' is not set, we use folder.account. """ from ..account import Account from ..folders import BaseFolder self.account = kwargs.pop("account", None) if self.account is not None and not isinstance(self.account, Account): raise InvalidTypeError("account", self.account, Account) self.folder = kwargs.pop("folder", None) if self.folder is not None: if not isinstance(self.folder, BaseFolder): raise InvalidTypeError("folder", self.folder, BaseFolder) if self.folder.account is not None: if self.account is not None: # Make sure the account from kwargs matches the folder account if self.account != self.folder.account: raise ValueError("'account' does not match 'folder.account'") self.account = self.folder.account super().__init__(**kwargs) @classmethod def from_xml(cls, elem, account): item = super().from_xml(elem=elem, account=account) item.account = account return item
Base class for all other classes that implement EWS items.
Pick out optional 'account' and 'folder' kwargs, and pass the rest to the parent class.
:param kwargs: 'account' is optional but allows calling 'send()' and 'delete()' 'folder' is optional but allows calling 'save()'. If 'folder' has an account, and 'account' is not set, we use folder.account.
Ancestors
Subclasses
Class variables
var FIELDS
var ID_ELEMENT_CLS
-
'id' and 'changekey' are UUIDs generated by Exchange.
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/itemid
Static methods
def from_xml(elem, account)
Instance variables
var account
-
Expand source code
class BaseItem(RegisterMixIn, metaclass=EWSMeta): """Base class for all other classes that implement EWS items.""" ID_ELEMENT_CLS = ItemId _id = IdElementField(field_uri="item:ItemId", value_cls=ID_ELEMENT_CLS) __slots__ = "account", "folder" def __init__(self, **kwargs): """Pick out optional 'account' and 'folder' kwargs, and pass the rest to the parent class. :param kwargs: 'account' is optional but allows calling 'send()' and 'delete()' 'folder' is optional but allows calling 'save()'. If 'folder' has an account, and 'account' is not set, we use folder.account. """ from ..account import Account from ..folders import BaseFolder self.account = kwargs.pop("account", None) if self.account is not None and not isinstance(self.account, Account): raise InvalidTypeError("account", self.account, Account) self.folder = kwargs.pop("folder", None) if self.folder is not None: if not isinstance(self.folder, BaseFolder): raise InvalidTypeError("folder", self.folder, BaseFolder) if self.folder.account is not None: if self.account is not None: # Make sure the account from kwargs matches the folder account if self.account != self.folder.account: raise ValueError("'account' does not match 'folder.account'") self.account = self.folder.account super().__init__(**kwargs) @classmethod def from_xml(cls, elem, account): item = super().from_xml(elem=elem, account=account) item.account = account return item
var folder
-
Expand source code
class BaseItem(RegisterMixIn, metaclass=EWSMeta): """Base class for all other classes that implement EWS items.""" ID_ELEMENT_CLS = ItemId _id = IdElementField(field_uri="item:ItemId", value_cls=ID_ELEMENT_CLS) __slots__ = "account", "folder" def __init__(self, **kwargs): """Pick out optional 'account' and 'folder' kwargs, and pass the rest to the parent class. :param kwargs: 'account' is optional but allows calling 'send()' and 'delete()' 'folder' is optional but allows calling 'save()'. If 'folder' has an account, and 'account' is not set, we use folder.account. """ from ..account import Account from ..folders import BaseFolder self.account = kwargs.pop("account", None) if self.account is not None and not isinstance(self.account, Account): raise InvalidTypeError("account", self.account, Account) self.folder = kwargs.pop("folder", None) if self.folder is not None: if not isinstance(self.folder, BaseFolder): raise InvalidTypeError("folder", self.folder, BaseFolder) if self.folder.account is not None: if self.account is not None: # Make sure the account from kwargs matches the folder account if self.account != self.folder.account: raise ValueError("'account' does not match 'folder.account'") self.account = self.folder.account super().__init__(**kwargs) @classmethod def from_xml(cls, elem, account): item = super().from_xml(elem=elem, account=account) item.account = account return item
Inherited members
class BulkCreateResult (**kwargs)
-
Expand source code
class BulkCreateResult(BaseItem): """A dummy class to store return values from a CreateItem service call.""" attachments = AttachmentField(field_uri="item:Attachments") # ItemAttachment or FileAttachment def __init__(self, **kwargs): super().__init__(**kwargs) if self.attachments is None: self.attachments = []
A dummy class to store return values from a CreateItem service call.
Pick out optional 'account' and 'folder' kwargs, and pass the rest to the parent class.
:param kwargs: 'account' is optional but allows calling 'send()' and 'delete()' 'folder' is optional but allows calling 'save()'. If 'folder' has an account, and 'account' is not set, we use folder.account.
Ancestors
Class variables
var FIELDS
Instance variables
var attachments
Inherited members
class CalendarItem (**kwargs)
-
Expand source code
class CalendarItem(Item, AcceptDeclineMixIn): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/calendaritem""" ELEMENT_NAME = "CalendarItem" uid = TextField(field_uri="calendar:UID", is_required_after_save=True, is_searchable=False) recurrence_id = DateTimeField(field_uri="calendar:RecurrenceId", is_read_only=True) start = DateOrDateTimeField(field_uri="calendar:Start", is_required=True) end = DateOrDateTimeField(field_uri="calendar:End", is_required=True) original_start = DateTimeField(field_uri="calendar:OriginalStart", is_read_only=True) is_all_day = BooleanField(field_uri="calendar:IsAllDayEvent", is_required=True, default=False) legacy_free_busy_status = FreeBusyStatusField( field_uri="calendar:LegacyFreeBusyStatus", is_required=True, default="Busy" ) location = TextField(field_uri="calendar:Location") when = TextField(field_uri="calendar:When") is_meeting = BooleanField(field_uri="calendar:IsMeeting", is_read_only=True) is_cancelled = BooleanField(field_uri="calendar:IsCancelled", is_read_only=True) is_recurring = BooleanField(field_uri="calendar:IsRecurring", is_read_only=True) meeting_request_was_sent = BooleanField(field_uri="calendar:MeetingRequestWasSent", is_read_only=True) is_response_requested = BooleanField( field_uri="calendar:IsResponseRequested", default=None, is_required_after_save=True, is_searchable=False ) type = ChoiceField( field_uri="calendar:CalendarItemType", choices={Choice(c) for c in CALENDAR_ITEM_CHOICES}, is_read_only=True ) my_response_type = ChoiceField( field_uri="calendar:MyResponseType", choices={Choice(c) for c in Attendee.RESPONSE_TYPES}, is_read_only=True ) organizer = MailboxField(field_uri="calendar:Organizer", is_read_only=True) required_attendees = AttendeesField(field_uri="calendar:RequiredAttendees", is_searchable=False) optional_attendees = AttendeesField(field_uri="calendar:OptionalAttendees", is_searchable=False) resources = AttendeesField(field_uri="calendar:Resources", is_searchable=False) conflicting_meeting_count = IntegerField(field_uri="calendar:ConflictingMeetingCount", is_read_only=True) adjacent_meeting_count = IntegerField(field_uri="calendar:AdjacentMeetingCount", is_read_only=True) conflicting_meetings = EWSElementListField( field_uri="calendar:ConflictingMeetings", value_cls="CalendarItem", namespace=Item.NAMESPACE, is_read_only=True ) adjacent_meetings = EWSElementListField( field_uri="calendar:AdjacentMeetings", value_cls="CalendarItem", namespace=Item.NAMESPACE, is_read_only=True ) duration = CharField(field_uri="calendar:Duration", is_read_only=True) appointment_reply_time = DateTimeField(field_uri="calendar:AppointmentReplyTime", is_read_only=True) appointment_sequence_number = IntegerField(field_uri="calendar:AppointmentSequenceNumber", is_read_only=True) appointment_state = AppointmentStateField(field_uri="calendar:AppointmentState", is_read_only=True) recurrence = RecurrenceField(field_uri="calendar:Recurrence", is_searchable=False) first_occurrence = OccurrenceField( field_uri="calendar:FirstOccurrence", value_cls=FirstOccurrence, is_read_only=True ) last_occurrence = OccurrenceField(field_uri="calendar:LastOccurrence", value_cls=LastOccurrence, is_read_only=True) modified_occurrences = OccurrenceListField( field_uri="calendar:ModifiedOccurrences", value_cls=Occurrence, is_read_only=True ) deleted_occurrences = OccurrenceListField( field_uri="calendar:DeletedOccurrences", value_cls=DeletedOccurrence, is_read_only=True ) _meeting_timezone = TimeZoneField( field_uri="calendar:MeetingTimeZone", deprecated_from=EXCHANGE_2010, is_searchable=False ) _start_timezone = TimeZoneField( field_uri="calendar:StartTimeZone", supported_from=EXCHANGE_2010, is_searchable=False ) _end_timezone = TimeZoneField(field_uri="calendar:EndTimeZone", supported_from=EXCHANGE_2010, is_searchable=False) conference_type = EnumAsIntField( field_uri="calendar:ConferenceType", enum=CONFERENCE_TYPES, min=0, default=None, is_required_after_save=True ) allow_new_time_proposal = BooleanField( field_uri="calendar:AllowNewTimeProposal", default=None, is_required_after_save=True, is_searchable=False ) is_online_meeting = BooleanField(field_uri="calendar:IsOnlineMeeting", default=None, is_read_only=True) meeting_workspace_url = URIField(field_uri="calendar:MeetingWorkspaceUrl") net_show_url = URIField(field_uri="calendar:NetShowUrl") def occurrence(self, index): """Get an occurrence of a recurring master by index. No query is sent to the server to actually fetch the item. Call refresh() on the item to do so. Only call this method on a recurring master. :param index: The index, which is 1-based :return The occurrence """ return self.__class__( account=self.account, folder=self.folder, _id=OccurrenceItemId(id=self.id, changekey=self.changekey, instance_index=index), ) def recurring_master(self): """Get the recurring master of an occurrence. No query is sent to the server to actually fetch the item. Call refresh() on the item to do so. Only call this method on an occurrence of a recurring master. :return: The master occurrence """ return self.__class__( account=self.account, folder=self.folder, _id=RecurringMasterItemId(id=self.id, changekey=self.changekey), ) @classmethod def timezone_fields(cls): return tuple(f for f in cls.FIELDS if isinstance(f, TimeZoneField)) def clean_timezone_fields(self, version): # Sets proper values on the timezone fields if they are not already set if self.start is None: start_tz = None elif type(self.start) in (EWSDate, datetime.date): start_tz = self.account.default_timezone else: start_tz = self.start.tzinfo if self.end is None: end_tz = None elif type(self.end) in (EWSDate, datetime.date): end_tz = self.account.default_timezone else: end_tz = self.end.tzinfo if version.build < EXCHANGE_2010: if self._meeting_timezone is None: self._meeting_timezone = start_tz self._start_timezone = None self._end_timezone = None else: self._meeting_timezone = None if self._start_timezone is None: self._start_timezone = start_tz if self._end_timezone is None: self._end_timezone = end_tz def clean(self, version=None): super().clean(version=version) if self.start and self.end and self.end < self.start: raise ValueError(f"'end' must be greater than 'start' ({self.start} -> {self.end})") if version: self.clean_timezone_fields(version=version) def cancel(self, **kwargs): return CancelCalendarItem( account=self.account, reference_item_id=ReferenceItemId(id=self.id, changekey=self.changekey), **kwargs ).send() def _update_fieldnames(self): update_fields = super()._update_fieldnames() if self.type == OCCURRENCE: # Some CalendarItem fields cannot be updated when the item is an occurrence. The values are empty when we # receive them so would have been updated because they are set to None. update_fields.remove("recurrence") update_fields.remove("uid") return update_fields @classmethod def from_xml(cls, elem, account): item = super().from_xml(elem=elem, account=account) # EWS returns the start and end values as a datetime regardless of the is_all_day status. Convert to date if # applicable. if not item.is_all_day: return item for field_name in ("start", "end"): val = getattr(item, field_name) if val is None: continue # Return just the date part of the value. Subtract 1 day from the date if this is the end field. This is # the inverse of what we do in .to_xml(). Convert to the local timezone before getting the date. if field_name == "end": val -= datetime.timedelta(days=1) tz = getattr(item, f"_{field_name}_timezone") setattr(item, field_name, val.astimezone(tz).date()) return item def tz_field_for_field_name(self, field_name): meeting_tz_field, start_tz_field, end_tz_field = CalendarItem.timezone_fields() if self.account.version.build < EXCHANGE_2010: return meeting_tz_field if field_name == "start": return start_tz_field if field_name == "end": return end_tz_field raise ValueError("Unsupported field_name") def date_to_datetime(self, field_name): # EWS always expects a datetime. If we have a date value, then convert it to datetime in the local # timezone. Additionally, if this the end field, add 1 day to the date. We could add 12 hours to both # start and end values and let EWS apply its logic, but that seems hacky. value = getattr(self, field_name) tz = getattr(self, self.tz_field_for_field_name(field_name).name) value = EWSDateTime.combine(value, datetime.time(0, 0)).replace(tzinfo=tz) if field_name == "end": value += datetime.timedelta(days=1) return value def to_xml(self, version): # EWS has some special logic related to all-day start and end values. Non-midnight start values are pushed to # the previous midnight. Non-midnight end values are pushed to the following midnight. Midnight in this context # refers to midnight in the local timezone. See # # https://docs.microsoft.com/en-us/exchange/client-developer/exchange-web-services/how-to-create-all-day-events-by-using-ews-in-exchange # elem = super().to_xml(version=version) if not self.is_all_day: return elem for field_name in ("start", "end"): value = getattr(self, field_name) if value is None: continue if type(value) in (EWSDate, datetime.date): # EWS always expects a datetime value = self.date_to_datetime(field_name=field_name) # We already generated an XML element for this field, but it contains a plain date at this point, which # is invalid. Replace the value. field = self.get_field_by_fieldname(field_name) set_xml_value(elem.find(field.response_tag()), value) return elem
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/calendaritem
Pick out optional 'account' and 'folder' kwargs, and pass the rest to the parent class.
:param kwargs: 'account' is optional but allows calling 'send()' and 'delete()' 'folder' is optional but allows calling 'save()'. If 'folder' has an account, and 'account' is not set, we use folder.account.
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
Static methods
def from_xml(elem, account)
def timezone_fields()
Instance variables
var adjacent_meeting_count
var adjacent_meetings
var allow_new_time_proposal
var appointment_reply_time
var appointment_sequence_number
var appointment_state
var conference_type
var conflicting_meeting_count
var conflicting_meetings
var deleted_occurrences
var duration
var end
var first_occurrence
var is_all_day
var is_cancelled
var is_meeting
var is_online_meeting
var is_recurring
var is_response_requested
var last_occurrence
var legacy_free_busy_status
var location
var meeting_request_was_sent
var meeting_workspace_url
var modified_occurrences
var my_response_type
var net_show_url
var optional_attendees
var organizer
var original_start
var recurrence
var recurrence_id
var required_attendees
var resources
var start
var type
var uid
var when
Methods
def cancel(self, **kwargs)
-
Expand source code
def cancel(self, **kwargs): return CancelCalendarItem( account=self.account, reference_item_id=ReferenceItemId(id=self.id, changekey=self.changekey), **kwargs ).send()
def clean(self, version=None)
-
Expand source code
def clean(self, version=None): super().clean(version=version) if self.start and self.end and self.end < self.start: raise ValueError(f"'end' must be greater than 'start' ({self.start} -> {self.end})") if version: self.clean_timezone_fields(version=version)
def clean_timezone_fields(self, version)
-
Expand source code
def clean_timezone_fields(self, version): # Sets proper values on the timezone fields if they are not already set if self.start is None: start_tz = None elif type(self.start) in (EWSDate, datetime.date): start_tz = self.account.default_timezone else: start_tz = self.start.tzinfo if self.end is None: end_tz = None elif type(self.end) in (EWSDate, datetime.date): end_tz = self.account.default_timezone else: end_tz = self.end.tzinfo if version.build < EXCHANGE_2010: if self._meeting_timezone is None: self._meeting_timezone = start_tz self._start_timezone = None self._end_timezone = None else: self._meeting_timezone = None if self._start_timezone is None: self._start_timezone = start_tz if self._end_timezone is None: self._end_timezone = end_tz
def date_to_datetime(self, field_name)
-
Expand source code
def date_to_datetime(self, field_name): # EWS always expects a datetime. If we have a date value, then convert it to datetime in the local # timezone. Additionally, if this the end field, add 1 day to the date. We could add 12 hours to both # start and end values and let EWS apply its logic, but that seems hacky. value = getattr(self, field_name) tz = getattr(self, self.tz_field_for_field_name(field_name).name) value = EWSDateTime.combine(value, datetime.time(0, 0)).replace(tzinfo=tz) if field_name == "end": value += datetime.timedelta(days=1) return value
def occurrence(self, index)
-
Expand source code
def occurrence(self, index): """Get an occurrence of a recurring master by index. No query is sent to the server to actually fetch the item. Call refresh() on the item to do so. Only call this method on a recurring master. :param index: The index, which is 1-based :return The occurrence """ return self.__class__( account=self.account, folder=self.folder, _id=OccurrenceItemId(id=self.id, changekey=self.changekey, instance_index=index), )
Get an occurrence of a recurring master by index. No query is sent to the server to actually fetch the item. Call refresh() on the item to do so.
Only call this method on a recurring master.
:param index: The index, which is 1-based
:return The occurrence
def recurring_master(self)
-
Expand source code
def recurring_master(self): """Get the recurring master of an occurrence. No query is sent to the server to actually fetch the item. Call refresh() on the item to do so. Only call this method on an occurrence of a recurring master. :return: The master occurrence """ return self.__class__( account=self.account, folder=self.folder, _id=RecurringMasterItemId(id=self.id, changekey=self.changekey), )
Get the recurring master of an occurrence. No query is sent to the server to actually fetch the item. Call refresh() on the item to do so.
Only call this method on an occurrence of a recurring master.
:return: The master occurrence
def to_xml(self, version)
-
Expand source code
def to_xml(self, version): # EWS has some special logic related to all-day start and end values. Non-midnight start values are pushed to # the previous midnight. Non-midnight end values are pushed to the following midnight. Midnight in this context # refers to midnight in the local timezone. See # # https://docs.microsoft.com/en-us/exchange/client-developer/exchange-web-services/how-to-create-all-day-events-by-using-ews-in-exchange # elem = super().to_xml(version=version) if not self.is_all_day: return elem for field_name in ("start", "end"): value = getattr(self, field_name) if value is None: continue if type(value) in (EWSDate, datetime.date): # EWS always expects a datetime value = self.date_to_datetime(field_name=field_name) # We already generated an XML element for this field, but it contains a plain date at this point, which # is invalid. Replace the value. field = self.get_field_by_fieldname(field_name) set_xml_value(elem.find(field.response_tag()), value) return elem
def tz_field_for_field_name(self, field_name)
-
Expand source code
def tz_field_for_field_name(self, field_name): meeting_tz_field, start_tz_field, end_tz_field = CalendarItem.timezone_fields() if self.account.version.build < EXCHANGE_2010: return meeting_tz_field if field_name == "start": return start_tz_field if field_name == "end": return end_tz_field raise ValueError("Unsupported field_name")
Inherited members
class CancelCalendarItem (**kwargs)
-
Expand source code
class CancelCalendarItem(BaseReplyItem): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/cancelcalendaritem""" ELEMENT_NAME = "CancelCalendarItem" author_idx = BaseReplyItem.FIELDS.index_by_name("author") FIELDS = BaseReplyItem.FIELDS[:author_idx] + BaseReplyItem.FIELDS[author_idx + 1 :]
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/cancelcalendaritem
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
Inherited members
class Contact (**kwargs)
-
Expand source code
class Contact(Item): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/contact""" ELEMENT_NAME = "Contact" file_as = TextField(field_uri="contacts:FileAs") file_as_mapping = ChoiceField( field_uri="contacts:FileAsMapping", choices={ Choice("None"), Choice("LastCommaFirst"), Choice("FirstSpaceLast"), Choice("Company"), Choice("LastCommaFirstCompany"), Choice("CompanyLastFirst"), Choice("LastFirst"), Choice("LastFirstCompany"), Choice("CompanyLastCommaFirst"), Choice("LastFirstSuffix"), Choice("LastSpaceFirstCompany"), Choice("CompanyLastSpaceFirst"), Choice("LastSpaceFirst"), Choice("DisplayName"), Choice("FirstName"), Choice("LastFirstMiddleSuffix"), Choice("LastName"), Choice("Empty"), }, ) display_name = TextField(field_uri="contacts:DisplayName", is_required=True) given_name = CharField(field_uri="contacts:GivenName") initials = TextField(field_uri="contacts:Initials") middle_name = CharField(field_uri="contacts:MiddleName") nickname = TextField(field_uri="contacts:Nickname") complete_name = EWSElementField(field_uri="contacts:CompleteName", value_cls=CompleteName, is_read_only=True) company_name = TextField(field_uri="contacts:CompanyName") email_addresses = EmailAddressesField(field_uri="contacts:EmailAddress") physical_addresses = PhysicalAddressField(field_uri="contacts:PhysicalAddress") phone_numbers = PhoneNumberField(field_uri="contacts:PhoneNumber") assistant_name = TextField(field_uri="contacts:AssistantName") birthday = DateTimeBackedDateField(field_uri="contacts:Birthday", default_time=datetime.time(11, 59)) business_homepage = URIField(field_uri="contacts:BusinessHomePage") children = TextListField(field_uri="contacts:Children") companies = TextListField(field_uri="contacts:Companies", is_searchable=False) contact_source = ChoiceField( field_uri="contacts:ContactSource", choices={Choice("Store"), Choice("ActiveDirectory")}, is_read_only=True ) department = TextField(field_uri="contacts:Department") generation = TextField(field_uri="contacts:Generation") im_addresses = ImAddressField(field_uri="contacts:ImAddress") job_title = TextField(field_uri="contacts:JobTitle") manager = TextField(field_uri="contacts:Manager") mileage = TextField(field_uri="contacts:Mileage") office = TextField(field_uri="contacts:OfficeLocation") postal_address_index = ChoiceField( field_uri="contacts:PostalAddressIndex", choices={Choice("Business"), Choice("Home"), Choice("Other"), Choice("None")}, default="None", is_required_after_save=True, ) profession = TextField(field_uri="contacts:Profession") spouse_name = TextField(field_uri="contacts:SpouseName") surname = CharField(field_uri="contacts:Surname") wedding_anniversary = DateTimeBackedDateField( field_uri="contacts:WeddingAnniversary", default_time=datetime.time(11, 59) ) has_picture = BooleanField(field_uri="contacts:HasPicture", supported_from=EXCHANGE_2010, is_read_only=True) phonetic_full_name = TextField( field_uri="contacts:PhoneticFullName", supported_from=EXCHANGE_2010_SP2, is_read_only=True ) phonetic_first_name = TextField( field_uri="contacts:PhoneticFirstName", supported_from=EXCHANGE_2010_SP2, is_read_only=True ) phonetic_last_name = TextField( field_uri="contacts:PhoneticLastName", supported_from=EXCHANGE_2010_SP2, is_read_only=True ) email_alias = EmailAddressField(field_uri="contacts:Alias", is_read_only=True, supported_from=EXCHANGE_2010_SP2) # 'notes' is documented in MSDN but apparently unused. Writing to it raises ErrorInvalidPropertyRequest. OWA # put entries into the 'notes' form field into the 'body' field. notes = CharField(field_uri="contacts:Notes", supported_from=EXCHANGE_2010_SP2, is_read_only=True) # 'photo' is documented in MSDN but apparently unused. Writing to it raises ErrorInvalidPropertyRequest. OWA # adds photos as FileAttachments on the contact item (with 'is_contact_photo=True'), which automatically flips # the 'has_picture' field. photo = Base64Field(field_uri="contacts:Photo", supported_from=EXCHANGE_2010_SP2, is_read_only=True) user_smime_certificate = Base64Field( field_uri="contacts:UserSMIMECertificate", supported_from=EXCHANGE_2010_SP2, is_read_only=True ) ms_exchange_certificate = Base64Field( field_uri="contacts:MSExchangeCertificate", supported_from=EXCHANGE_2010_SP2, is_read_only=True ) directory_id = TextField(field_uri="contacts:DirectoryId", supported_from=EXCHANGE_2010_SP2, is_read_only=True) manager_mailbox = MailboxField( field_uri="contacts:ManagerMailbox", supported_from=EXCHANGE_2010_SP2, is_read_only=True ) direct_reports = MailboxListField( field_uri="contacts:DirectReports", supported_from=EXCHANGE_2010_SP2, is_read_only=True ) # O365 throws ErrorInternalServerError "[0x004f0102] MapiReplyToBlob" if UniqueBody is requested unique_body_idx = Item.FIELDS.index_by_name("unique_body") FIELDS = Item.FIELDS[:unique_body_idx] + Item.FIELDS[unique_body_idx + 1 :]
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/contact
Pick out optional 'account' and 'folder' kwargs, and pass the rest to the parent class.
:param kwargs: 'account' is optional but allows calling 'send()' and 'delete()' 'folder' is optional but allows calling 'save()'. If 'folder' has an account, and 'account' is not set, we use folder.account.
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
var unique_body_idx
Instance variables
var assistant_name
var birthday
var business_homepage
var children
var companies
var company_name
var complete_name
var contact_source
var department
var direct_reports
var directory_id
var display_name
var email_addresses
var email_alias
var file_as
var file_as_mapping
var generation
var given_name
var has_picture
var im_addresses
var initials
var job_title
var manager
var manager_mailbox
var middle_name
var mileage
var ms_exchange_certificate
var nickname
var notes
var office
var phone_numbers
var phonetic_first_name
var phonetic_full_name
var phonetic_last_name
var photo
var physical_addresses
var postal_address_index
var profession
var spouse_name
var surname
var user_smime_certificate
var wedding_anniversary
Inherited members
class DeclineItem (**kwargs)
-
Expand source code
class DeclineItem(BaseMeetingReplyItem): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/declineitem""" ELEMENT_NAME = "DeclineItem"
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/declineitem
Pick out optional 'account' and 'folder' kwargs, and pass the rest to the parent class.
:param kwargs: 'account' is optional but allows calling 'send()' and 'delete()' 'folder' is optional but allows calling 'save()'. If 'folder' has an account, and 'account' is not set, we use folder.account.
Ancestors
Class variables
var ELEMENT_NAME
Inherited members
class DistributionList (**kwargs)
-
Expand source code
class DistributionList(Item): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/distributionlist""" ELEMENT_NAME = "DistributionList" display_name = CharField(field_uri="contacts:DisplayName", is_required=True) file_as = CharField(field_uri="contacts:FileAs", is_read_only=True) contact_source = ChoiceField( field_uri="contacts:ContactSource", choices={Choice("Store"), Choice("ActiveDirectory")}, is_read_only=True ) members = MemberListField(field_uri="distributionlist:Members") # O365 throws ErrorInternalServerError "[0x004f0102] MapiReplyToBlob" if UniqueBody is requested unique_body_idx = Item.FIELDS.index_by_name("unique_body") FIELDS = Item.FIELDS[:unique_body_idx] + Item.FIELDS[unique_body_idx + 1 :]
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/distributionlist
Pick out optional 'account' and 'folder' kwargs, and pass the rest to the parent class.
:param kwargs: 'account' is optional but allows calling 'send()' and 'delete()' 'folder' is optional but allows calling 'save()'. If 'folder' has an account, and 'account' is not set, we use folder.account.
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
var unique_body_idx
Instance variables
var contact_source
var display_name
var file_as
var members
Inherited members
class ForwardItem (**kwargs)
-
Expand source code
class ForwardItem(BaseReplyItem): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/forwarditem""" ELEMENT_NAME = "ForwardItem"
Ancestors
Class variables
var ELEMENT_NAME
Inherited members
class Item (**kwargs)
-
Expand source code
class Item(BaseItem): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/item""" ELEMENT_NAME = "Item" mime_content = MimeContentField(field_uri="item:MimeContent", is_read_only_after_send=True) _id = BaseItem.FIELDS["_id"] parent_folder_id = EWSElementField(field_uri="item:ParentFolderId", value_cls=ParentFolderId, is_read_only=True) item_class = CharField(field_uri="item:ItemClass", is_read_only=True) subject = CharField(field_uri="item:Subject") sensitivity = ChoiceField( field_uri="item:Sensitivity", choices={Choice("Normal"), Choice("Personal"), Choice("Private"), Choice("Confidential")}, is_required=True, default="Normal", ) text_body = TextField(field_uri="item:TextBody", is_read_only=True, supported_from=EXCHANGE_2013) body = BodyField(field_uri="item:Body") # Accepts and returns Body or HTMLBody instances attachments = AttachmentField(field_uri="item:Attachments") # ItemAttachment or FileAttachment datetime_received = DateTimeField(field_uri="item:DateTimeReceived", is_read_only=True) size = IntegerField(field_uri="item:Size", is_read_only=True) # Item size in bytes categories = CharListField(field_uri="item:Categories") importance = ChoiceField( field_uri="item:Importance", choices={Choice("Low"), Choice("Normal"), Choice("High")}, is_required=True, default="Normal", ) in_reply_to = TextField(field_uri="item:InReplyTo") is_submitted = BooleanField(field_uri="item:IsSubmitted", is_read_only=True) is_draft = BooleanField(field_uri="item:IsDraft", is_read_only=True) is_from_me = BooleanField(field_uri="item:IsFromMe", is_read_only=True) is_resend = BooleanField(field_uri="item:IsResend", is_read_only=True) is_unmodified = BooleanField(field_uri="item:IsUnmodified", is_read_only=True) headers = MessageHeaderField(field_uri="item:InternetMessageHeaders", is_read_only=True) datetime_sent = DateTimeField(field_uri="item:DateTimeSent", is_read_only=True) datetime_created = DateTimeField(field_uri="item:DateTimeCreated", is_read_only=True) response_objects = EWSElementField( field_uri="item:ResponseObjects", value_cls=ResponseObjects, is_read_only=True, ) # Placeholder for ResponseObjects reminder_due_by = DateTimeField(field_uri="item:ReminderDueBy", is_required_after_save=True, is_searchable=False) reminder_is_set = BooleanField(field_uri="item:ReminderIsSet", is_required=True, default=False) reminder_minutes_before_start = IntegerField( field_uri="item:ReminderMinutesBeforeStart", is_required_after_save=True, min=0, default=0 ) display_cc = TextField(field_uri="item:DisplayCc", is_read_only=True) display_to = TextField(field_uri="item:DisplayTo", is_read_only=True) has_attachments = BooleanField(field_uri="item:HasAttachments", is_read_only=True) # ExtendedProperty fields go here culture = CultureField(field_uri="item:Culture", is_required_after_save=True, is_searchable=False) effective_rights = EffectiveRightsField(field_uri="item:EffectiveRights", is_read_only=True) last_modified_name = CharField(field_uri="item:LastModifiedName", is_read_only=True) last_modified_time = DateTimeField(field_uri="item:LastModifiedTime", is_read_only=True) is_associated = BooleanField(field_uri="item:IsAssociated", is_read_only=True, supported_from=EXCHANGE_2010) web_client_read_form_query_string = URIField( field_uri="item:WebClientReadFormQueryString", is_read_only=True, supported_from=EXCHANGE_2010 ) web_client_edit_form_query_string = URIField( field_uri="item:WebClientEditFormQueryString", is_read_only=True, supported_from=EXCHANGE_2010 ) conversation_id = EWSElementField( field_uri="item:ConversationId", value_cls=ConversationId, is_read_only=True, supported_from=EXCHANGE_2010 ) unique_body = BodyField(field_uri="item:UniqueBody", is_read_only=True, supported_from=EXCHANGE_2010) FIELDS = Fields() # Used to register extended properties INSERT_AFTER_FIELD = "has_attachments" def __init__(self, **kwargs): super().__init__(**kwargs) if self.attachments: for a in self.attachments: if a.parent_item: if a.parent_item is not self: raise ValueError(f"'parent_item' of attachment {a} must point to this item") else: a.parent_item = self self.attach(self.attachments) else: self.attachments = [] def save(self, update_fields=None, conflict_resolution=AUTO_RESOLVE, send_meeting_invitations=SEND_TO_NONE): from .task import Task if self.id: item_id, changekey = self._update( update_fieldnames=update_fields, message_disposition=SAVE_ONLY, conflict_resolution=conflict_resolution, send_meeting_invitations=send_meeting_invitations, ) if ( self.id != item_id and not isinstance(self._id, (OccurrenceItemId, RecurringMasterItemId)) and not isinstance(self, Task) ): # When we update an item with an OccurrenceItemId as ID, EWS returns the ID of the occurrence, so # the ID of this item changes. # # When we update certain fields on a task, the ID may change. A full description is available at # https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/updateitem-operation-task raise ValueError("'id' mismatch in returned update response") # Don't check that changekeys are different. No-op saves will sometimes leave the changekey intact self._id = self.ID_ELEMENT_CLS(item_id, changekey) else: if update_fields: raise ValueError("'update_fields' is only valid for updates") tmp_attachments = None if self.account and self.account.version.build < EXCHANGE_2013 and self.attachments: # At least some versions prior to Exchange 2013 can't save attachments immediately. You need to first # save, then attach. Store the attachment of this item temporarily and attach later. tmp_attachments, self.attachments = self.attachments, [] item = self._create(message_disposition=SAVE_ONLY, send_meeting_invitations=send_meeting_invitations) self._id = self.ID_ELEMENT_CLS(item.id, item.changekey) for old_att, new_att in zip(self.attachments, item.attachments): if old_att.attachment_id is not None: raise ValueError("Old 'attachment_id' is not empty") if new_att.attachment_id is None: raise ValueError("New 'attachment_id' is empty") old_att.attachment_id = new_att.attachment_id if tmp_attachments: # Exchange 2007 workaround. See above self.attach(tmp_attachments) return self @require_account def _create(self, message_disposition, send_meeting_invitations): # Return a BulkCreateResult because we want to return the ID of both the main item *and* attachments. In send # and send-and-save-copy mode, the server does not return an ID, so we just return True. from ..services import CreateItem return CreateItem(account=self.account).get( items=[self], folder=self.folder, message_disposition=message_disposition, send_meeting_invitations=send_meeting_invitations, ) def _update_fieldnames(self): from .contact import Contact, DistributionList # Return the list of fields we are allowed to update update_fieldnames = [] for f in self.supported_fields(version=self.account.version): if f.name == "attachments": # Attachments are handled separately after item creation continue if f.is_read_only: # These cannot be changed continue if (f.is_required or f.is_required_after_save) and ( getattr(self, f.name) is None or (f.is_list and not getattr(self, f.name)) ): # These are required and cannot be deleted continue if not self.is_draft and f.is_read_only_after_send: # These cannot be changed when the item is no longer a draft continue if f.name == "message_id" and f.is_read_only_after_send: # 'message_id' doesn't support updating, no matter the draft status continue if f.name == "mime_content" and isinstance(self, (Contact, DistributionList)): # Contact and DistributionList don't support updating mime_content, no matter the draft status continue update_fieldnames.append(f.name) return update_fieldnames @require_account def _update(self, update_fieldnames, message_disposition, conflict_resolution, send_meeting_invitations): from ..services import UpdateItem if not self.changekey: raise ValueError(f"{self.__class__.__name__} must have changekey") if not update_fieldnames: # The fields to update was not specified explicitly. Update all fields where update is possible update_fieldnames = self._update_fieldnames() return UpdateItem(account=self.account).get( items=[(self, update_fieldnames)], message_disposition=message_disposition, conflict_resolution=conflict_resolution, send_meeting_invitations_or_cancellations=send_meeting_invitations, suppress_read_receipts=True, expect_result=message_disposition != SEND_AND_SAVE_COPY, ) @require_id def refresh(self): # Updates the item based on fresh data from EWS from ..services import GetItem additional_fields = {FieldPath(field=f) for f in self.supported_fields(version=self.account.version)} res = GetItem(account=self.account).get(items=[self], additional_fields=additional_fields, shape=ID_ONLY) if self.id != res.id and not isinstance(self._id, (OccurrenceItemId, RecurringMasterItemId)): # When we refresh an item with an OccurrenceItemId as ID, EWS returns the ID of the occurrence, so # the ID of this item changes. raise ValueError("'id' mismatch in returned update response") for f in self.FIELDS: setattr(self, f.name, getattr(res, f.name)) # 'parent_item' should point to 'self', not 'fresh_item'. That way, 'fresh_item' can be garbage collected. for a in self.attachments: a.parent_item = self return self @require_id def copy(self, to_folder): from ..services import CopyItem # If 'to_folder' is a public folder or a folder in a different mailbox then None is returned return CopyItem(account=self.account).get( items=[self], to_folder=to_folder, expect_result=None, ) @require_id def move(self, to_folder): from ..services import MoveItem res = MoveItem(account=self.account).get( items=[self], to_folder=to_folder, expect_result=None, ) if res is None: # Assume 'to_folder' is a public folder or a folder in a different mailbox self._id = None return self._id = self.ID_ELEMENT_CLS(*res) self.folder = to_folder def move_to_trash( self, send_meeting_cancellations=SEND_TO_NONE, affected_task_occurrences=ALL_OCCURRENCES, suppress_read_receipts=True, ): # Delete and move to the trash folder. self._delete( delete_type=MOVE_TO_DELETED_ITEMS, send_meeting_cancellations=send_meeting_cancellations, affected_task_occurrences=affected_task_occurrences, suppress_read_receipts=suppress_read_receipts, ) self._id = None self.folder = self.account.trash def soft_delete( self, send_meeting_cancellations=SEND_TO_NONE, affected_task_occurrences=ALL_OCCURRENCES, suppress_read_receipts=True, ): # Delete and move to the dumpster, if it is enabled. self._delete( delete_type=SOFT_DELETE, send_meeting_cancellations=send_meeting_cancellations, affected_task_occurrences=affected_task_occurrences, suppress_read_receipts=suppress_read_receipts, ) self._id = None self.folder = self.account.recoverable_items_deletions def delete( self, send_meeting_cancellations=SEND_TO_NONE, affected_task_occurrences=ALL_OCCURRENCES, suppress_read_receipts=True, ): # Remove the item permanently. No copies are stored anywhere. self._delete( delete_type=HARD_DELETE, send_meeting_cancellations=send_meeting_cancellations, affected_task_occurrences=affected_task_occurrences, suppress_read_receipts=suppress_read_receipts, ) self._id, self.folder = None, None @require_id def _delete(self, delete_type, send_meeting_cancellations, affected_task_occurrences, suppress_read_receipts): from ..services import DeleteItem DeleteItem(account=self.account).get( items=[self], delete_type=delete_type, send_meeting_cancellations=send_meeting_cancellations, affected_task_occurrences=affected_task_occurrences, suppress_read_receipts=suppress_read_receipts, ) @require_id def archive(self, to_folder): from ..services import ArchiveItem return ArchiveItem(account=self.account).get(items=[self], to_folder=to_folder, expect_result=True) def attach(self, attachments): """Add an attachment, or a list of attachments, to this item. If the item has already been saved, the attachments will be created on the server immediately. If the item has not yet been saved, the attachments will be created on the server when the item is saved. Adding attachments to an existing item will update the changekey of the item. :param attachments: """ if not is_iterable(attachments, generators_allowed=True): attachments = [attachments] for a in attachments: if not a.parent_item: a.parent_item = self if self.id and not a.attachment_id: # Already saved object. Attach the attachment server-side now a.attach() if a not in self.attachments: self.attachments.append(a) def detach(self, attachments): """Remove an attachment, or a list of attachments, from this item. If the item has already been saved, the attachments will be deleted on the server immediately. If the item has not yet been saved, the attachments will simply not be created on the server the item is saved. Removing attachments from an existing item will update the changekey of the item. :param attachments: """ if not is_iterable(attachments, generators_allowed=True): attachments = [attachments] if attachments is self.attachments: # Don't remove from the same list we are iterating attachments = list(attachments) for a in attachments: if a.parent_item is not self: raise ValueError("Attachment does not belong to this item") if self.id: # Item is already created. Detach the attachment server-side now a.detach() if a in self.attachments: self.attachments.remove(a) @require_id def create_forward(self, subject, body, to_recipients, cc_recipients=None, bcc_recipients=None): from .message import ForwardItem return ForwardItem( account=self.account, reference_item_id=ReferenceItemId(id=self.id, changekey=self.changekey), subject=subject, new_body=body, to_recipients=to_recipients, cc_recipients=cc_recipients, bcc_recipients=bcc_recipients, ) def forward(self, subject, body, to_recipients, cc_recipients=None, bcc_recipients=None): return self.create_forward( subject, body, to_recipients, cc_recipients, bcc_recipients, ).send()
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/item
Pick out optional 'account' and 'folder' kwargs, and pass the rest to the parent class.
:param kwargs: 'account' is optional but allows calling 'send()' and 'delete()' 'folder' is optional but allows calling 'save()'. If 'folder' has an account, and 'account' is not set, we use folder.account.
Ancestors
Subclasses
- BaseMeetingItem
- CalendarItem
- exchangelib.items.calendar_item._Booking
- Contact
- DistributionList
- Message
- PostItem
- PostReplyItem
- Task
Class variables
var ELEMENT_NAME
var FIELDS
var INSERT_AFTER_FIELD
Instance variables
var attachments
var body
var categories
var conversation_id
var culture
var datetime_created
var datetime_received
var datetime_sent
var display_cc
var display_to
var effective_rights
var has_attachments
var headers
var importance
var in_reply_to
var is_associated
var is_draft
var is_from_me
var is_resend
var is_submitted
var is_unmodified
var item_class
var last_modified_name
var last_modified_time
var mime_content
var parent_folder_id
var reminder_due_by
var reminder_is_set
var reminder_minutes_before_start
var response_objects
var sensitivity
var size
var subject
var text_body
var unique_body
var web_client_edit_form_query_string
var web_client_read_form_query_string
Methods
def archive(self, to_folder)
-
Expand source code
@require_id def archive(self, to_folder): from ..services import ArchiveItem return ArchiveItem(account=self.account).get(items=[self], to_folder=to_folder, expect_result=True)
def attach(self, attachments)
-
Expand source code
def attach(self, attachments): """Add an attachment, or a list of attachments, to this item. If the item has already been saved, the attachments will be created on the server immediately. If the item has not yet been saved, the attachments will be created on the server when the item is saved. Adding attachments to an existing item will update the changekey of the item. :param attachments: """ if not is_iterable(attachments, generators_allowed=True): attachments = [attachments] for a in attachments: if not a.parent_item: a.parent_item = self if self.id and not a.attachment_id: # Already saved object. Attach the attachment server-side now a.attach() if a not in self.attachments: self.attachments.append(a)
Add an attachment, or a list of attachments, to this item. If the item has already been saved, the attachments will be created on the server immediately. If the item has not yet been saved, the attachments will be created on the server when the item is saved.
Adding attachments to an existing item will update the changekey of the item.
:param attachments:
def copy(self, to_folder)
-
Expand source code
@require_id def copy(self, to_folder): from ..services import CopyItem # If 'to_folder' is a public folder or a folder in a different mailbox then None is returned return CopyItem(account=self.account).get( items=[self], to_folder=to_folder, expect_result=None, )
def create_forward(self, subject, body, to_recipients, cc_recipients=None, bcc_recipients=None)
-
Expand source code
@require_id def create_forward(self, subject, body, to_recipients, cc_recipients=None, bcc_recipients=None): from .message import ForwardItem return ForwardItem( account=self.account, reference_item_id=ReferenceItemId(id=self.id, changekey=self.changekey), subject=subject, new_body=body, to_recipients=to_recipients, cc_recipients=cc_recipients, bcc_recipients=bcc_recipients, )
def delete(self,
send_meeting_cancellations='SendToNone',
affected_task_occurrences='AllOccurrences',
suppress_read_receipts=True)-
Expand source code
def delete( self, send_meeting_cancellations=SEND_TO_NONE, affected_task_occurrences=ALL_OCCURRENCES, suppress_read_receipts=True, ): # Remove the item permanently. No copies are stored anywhere. self._delete( delete_type=HARD_DELETE, send_meeting_cancellations=send_meeting_cancellations, affected_task_occurrences=affected_task_occurrences, suppress_read_receipts=suppress_read_receipts, ) self._id, self.folder = None, None
def detach(self, attachments)
-
Expand source code
def detach(self, attachments): """Remove an attachment, or a list of attachments, from this item. If the item has already been saved, the attachments will be deleted on the server immediately. If the item has not yet been saved, the attachments will simply not be created on the server the item is saved. Removing attachments from an existing item will update the changekey of the item. :param attachments: """ if not is_iterable(attachments, generators_allowed=True): attachments = [attachments] if attachments is self.attachments: # Don't remove from the same list we are iterating attachments = list(attachments) for a in attachments: if a.parent_item is not self: raise ValueError("Attachment does not belong to this item") if self.id: # Item is already created. Detach the attachment server-side now a.detach() if a in self.attachments: self.attachments.remove(a)
Remove an attachment, or a list of attachments, from this item. If the item has already been saved, the attachments will be deleted on the server immediately. If the item has not yet been saved, the attachments will simply not be created on the server the item is saved.
Removing attachments from an existing item will update the changekey of the item.
:param attachments:
def forward(self, subject, body, to_recipients, cc_recipients=None, bcc_recipients=None)
-
Expand source code
def forward(self, subject, body, to_recipients, cc_recipients=None, bcc_recipients=None): return self.create_forward( subject, body, to_recipients, cc_recipients, bcc_recipients, ).send()
def move(self, to_folder)
-
Expand source code
@require_id def move(self, to_folder): from ..services import MoveItem res = MoveItem(account=self.account).get( items=[self], to_folder=to_folder, expect_result=None, ) if res is None: # Assume 'to_folder' is a public folder or a folder in a different mailbox self._id = None return self._id = self.ID_ELEMENT_CLS(*res) self.folder = to_folder
def move_to_trash(self,
send_meeting_cancellations='SendToNone',
affected_task_occurrences='AllOccurrences',
suppress_read_receipts=True)-
Expand source code
def move_to_trash( self, send_meeting_cancellations=SEND_TO_NONE, affected_task_occurrences=ALL_OCCURRENCES, suppress_read_receipts=True, ): # Delete and move to the trash folder. self._delete( delete_type=MOVE_TO_DELETED_ITEMS, send_meeting_cancellations=send_meeting_cancellations, affected_task_occurrences=affected_task_occurrences, suppress_read_receipts=suppress_read_receipts, ) self._id = None self.folder = self.account.trash
def refresh(self)
-
Expand source code
@require_id def refresh(self): # Updates the item based on fresh data from EWS from ..services import GetItem additional_fields = {FieldPath(field=f) for f in self.supported_fields(version=self.account.version)} res = GetItem(account=self.account).get(items=[self], additional_fields=additional_fields, shape=ID_ONLY) if self.id != res.id and not isinstance(self._id, (OccurrenceItemId, RecurringMasterItemId)): # When we refresh an item with an OccurrenceItemId as ID, EWS returns the ID of the occurrence, so # the ID of this item changes. raise ValueError("'id' mismatch in returned update response") for f in self.FIELDS: setattr(self, f.name, getattr(res, f.name)) # 'parent_item' should point to 'self', not 'fresh_item'. That way, 'fresh_item' can be garbage collected. for a in self.attachments: a.parent_item = self return self
def save(self,
update_fields=None,
conflict_resolution='AutoResolve',
send_meeting_invitations='SendToNone')-
Expand source code
def save(self, update_fields=None, conflict_resolution=AUTO_RESOLVE, send_meeting_invitations=SEND_TO_NONE): from .task import Task if self.id: item_id, changekey = self._update( update_fieldnames=update_fields, message_disposition=SAVE_ONLY, conflict_resolution=conflict_resolution, send_meeting_invitations=send_meeting_invitations, ) if ( self.id != item_id and not isinstance(self._id, (OccurrenceItemId, RecurringMasterItemId)) and not isinstance(self, Task) ): # When we update an item with an OccurrenceItemId as ID, EWS returns the ID of the occurrence, so # the ID of this item changes. # # When we update certain fields on a task, the ID may change. A full description is available at # https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/updateitem-operation-task raise ValueError("'id' mismatch in returned update response") # Don't check that changekeys are different. No-op saves will sometimes leave the changekey intact self._id = self.ID_ELEMENT_CLS(item_id, changekey) else: if update_fields: raise ValueError("'update_fields' is only valid for updates") tmp_attachments = None if self.account and self.account.version.build < EXCHANGE_2013 and self.attachments: # At least some versions prior to Exchange 2013 can't save attachments immediately. You need to first # save, then attach. Store the attachment of this item temporarily and attach later. tmp_attachments, self.attachments = self.attachments, [] item = self._create(message_disposition=SAVE_ONLY, send_meeting_invitations=send_meeting_invitations) self._id = self.ID_ELEMENT_CLS(item.id, item.changekey) for old_att, new_att in zip(self.attachments, item.attachments): if old_att.attachment_id is not None: raise ValueError("Old 'attachment_id' is not empty") if new_att.attachment_id is None: raise ValueError("New 'attachment_id' is empty") old_att.attachment_id = new_att.attachment_id if tmp_attachments: # Exchange 2007 workaround. See above self.attach(tmp_attachments) return self
def soft_delete(self,
send_meeting_cancellations='SendToNone',
affected_task_occurrences='AllOccurrences',
suppress_read_receipts=True)-
Expand source code
def soft_delete( self, send_meeting_cancellations=SEND_TO_NONE, affected_task_occurrences=ALL_OCCURRENCES, suppress_read_receipts=True, ): # Delete and move to the dumpster, if it is enabled. self._delete( delete_type=SOFT_DELETE, send_meeting_cancellations=send_meeting_cancellations, affected_task_occurrences=affected_task_occurrences, suppress_read_receipts=suppress_read_receipts, ) self._id = None self.folder = self.account.recoverable_items_deletions
Inherited members
class MeetingCancellation (**kwargs)
-
Expand source code
class MeetingCancellation(BaseMeetingItem): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/meetingcancellation""" ELEMENT_NAME = "MeetingCancellation"
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/meetingcancellation
Pick out optional 'account' and 'folder' kwargs, and pass the rest to the parent class.
:param kwargs: 'account' is optional but allows calling 'send()' and 'delete()' 'folder' is optional but allows calling 'save()'. If 'folder' has an account, and 'account' is not set, we use folder.account.
Ancestors
Class variables
var ELEMENT_NAME
Inherited members
class MeetingRequest (**kwargs)
-
Expand source code
class MeetingRequest(BaseMeetingItem, AcceptDeclineMixIn): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/meetingrequest""" ELEMENT_NAME = "MeetingRequest" meeting_request_type = ChoiceField( field_uri="meetingRequest:MeetingRequestType", choices={ Choice("FullUpdate"), Choice("InformationalUpdate"), Choice("NewMeetingRequest"), Choice("None"), Choice("Outdated"), Choice("PrincipalWantsCopy"), Choice("SilentUpdate"), }, default="None", ) intended_free_busy_status = ChoiceField( field_uri="meetingRequest:IntendedFreeBusyStatus", choices={Choice("Free"), Choice("Tentative"), Choice("Busy"), Choice("OOF"), Choice("NoData")}, is_required=True, default="Busy", ) # This element also has some fields from CalendarItem start_idx = CalendarItem.FIELDS.index_by_name("start") is_response_requested_idx = CalendarItem.FIELDS.index_by_name("is_response_requested") FIELDS = ( BaseMeetingItem.FIELDS + CalendarItem.FIELDS[start_idx:is_response_requested_idx] + CalendarItem.FIELDS[is_response_requested_idx + 1 :] )
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/meetingrequest
Pick out optional 'account' and 'folder' kwargs, and pass the rest to the parent class.
:param kwargs: 'account' is optional but allows calling 'send()' and 'delete()' 'folder' is optional but allows calling 'save()'. If 'folder' has an account, and 'account' is not set, we use folder.account.
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
var is_response_requested_idx
var start_idx
Instance variables
var adjacent_meeting_count
var adjacent_meetings
var allow_new_time_proposal
var appointment_reply_time
var appointment_sequence_number
var appointment_state
var conference_type
var conflicting_meeting_count
var conflicting_meetings
var deleted_occurrences
var duration
var end
var first_occurrence
var intended_free_busy_status
var is_all_day
var is_cancelled
var is_meeting
var is_online_meeting
var is_recurring
var last_occurrence
var legacy_free_busy_status
var location
var meeting_request_type
var meeting_request_was_sent
var meeting_workspace_url
var modified_occurrences
var my_response_type
var net_show_url
var optional_attendees
var organizer
var original_start
var recurrence
var required_attendees
var resources
var start
var type
var when
Inherited members
class MeetingResponse (**kwargs)
-
Expand source code
class MeetingResponse(BaseMeetingItem): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/meetingresponse""" ELEMENT_NAME = "MeetingResponse" proposed_start = DateTimeField(field_uri="meeting:ProposedStart", supported_from=EXCHANGE_2013) proposed_end = DateTimeField(field_uri="meeting:ProposedEnd", supported_from=EXCHANGE_2013)
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/meetingresponse
Pick out optional 'account' and 'folder' kwargs, and pass the rest to the parent class.
:param kwargs: 'account' is optional but allows calling 'send()' and 'delete()' 'folder' is optional but allows calling 'save()'. If 'folder' has an account, and 'account' is not set, we use folder.account.
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
Instance variables
var proposed_end
var proposed_start
Inherited members
class Message (**kwargs)
-
Expand source code
class Message(Item): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/message-ex15websvcsotherref """ ELEMENT_NAME = "Message" sender = MailboxField(field_uri="message:Sender", is_read_only=True, is_read_only_after_send=True) to_recipients = MailboxListField( field_uri="message:ToRecipients", is_read_only_after_send=True, is_searchable=False ) cc_recipients = MailboxListField( field_uri="message:CcRecipients", is_read_only_after_send=True, is_searchable=False ) bcc_recipients = MailboxListField( field_uri="message:BccRecipients", is_read_only_after_send=True, is_searchable=False ) is_read_receipt_requested = BooleanField( field_uri="message:IsReadReceiptRequested", is_required=True, default=False, is_read_only_after_send=True ) is_delivery_receipt_requested = BooleanField( field_uri="message:IsDeliveryReceiptRequested", is_required=True, default=False, is_read_only_after_send=True ) conversation_index = Base64Field(field_uri="message:ConversationIndex", is_read_only=True) conversation_topic = CharField(field_uri="message:ConversationTopic", is_read_only=True) # Rename 'From' to 'author'. We can't use field name 'from' since it's a Python keyword. author = MailboxField(field_uri="message:From", is_read_only_after_send=True) message_id = TextField(field_uri="message:InternetMessageId", is_read_only_after_send=True) is_read = BooleanField(field_uri="message:IsRead", is_required=True, default=False) is_response_requested = BooleanField(field_uri="message:IsResponseRequested", default=False, is_required=True) references = TextField(field_uri="message:References") reply_to = MailboxListField(field_uri="message:ReplyTo", is_read_only_after_send=True, is_searchable=False) received_by = MailboxField(field_uri="message:ReceivedBy", is_read_only=True) received_representing = MailboxField(field_uri="message:ReceivedRepresenting", is_read_only=True) reminder_message_data = EWSElementField( field_uri="message:ReminderMessageData", value_cls=ReminderMessageData, supported_from=EXCHANGE_2013_SP1, is_read_only=True, ) @require_account def send( self, save_copy=True, copy_to_folder=None, conflict_resolution=AUTO_RESOLVE, send_meeting_invitations=SEND_TO_NONE, ): from ..services import SendItem # Only sends a message. The message can either be an existing draft stored in EWS or a new message that does # not yet exist in EWS. if copy_to_folder and not save_copy: raise AttributeError("'save_copy' must be True when 'copy_to_folder' is set") if save_copy and not copy_to_folder: copy_to_folder = self.account.sent # 'Sent' is default EWS behaviour if self.id: SendItem(account=self.account).get(items=[self], saved_item_folder=copy_to_folder) # The item will be deleted from the original folder self._id = None self.folder = copy_to_folder return None # New message if copy_to_folder: # This would better be done via send_and_save() but let's just support it here self.folder = copy_to_folder return self.send_and_save( conflict_resolution=conflict_resolution, send_meeting_invitations=send_meeting_invitations ) if self.account.version.build < EXCHANGE_2013 and self.attachments: # At least some versions prior to Exchange 2013 can't send attachments immediately. You need to first save, # then attach, then send. This is done in send_and_save(). send() will delete the item again. self.send_and_save( conflict_resolution=conflict_resolution, send_meeting_invitations=send_meeting_invitations ) return None self._create(message_disposition=SEND_ONLY, send_meeting_invitations=send_meeting_invitations) return None def send_and_save( self, update_fields=None, conflict_resolution=AUTO_RESOLVE, send_meeting_invitations=SEND_TO_NONE ): # Sends Message and saves a copy in the parent folder. Does not return an ItemId. if self.id: return self._update( update_fieldnames=update_fields, message_disposition=SEND_AND_SAVE_COPY, conflict_resolution=conflict_resolution, send_meeting_invitations=send_meeting_invitations, ) if self.account.version.build < EXCHANGE_2013 and self.attachments: # At least some versions prior to Exchange 2013 can't send-and-save attachments immediately. You need # to first save, then attach, then send. This is done in save(). self.save( update_fields=update_fields, conflict_resolution=conflict_resolution, send_meeting_invitations=send_meeting_invitations, ) return self.send( save_copy=False, conflict_resolution=conflict_resolution, send_meeting_invitations=send_meeting_invitations, ) return self._create(message_disposition=SEND_AND_SAVE_COPY, send_meeting_invitations=send_meeting_invitations) @require_id def create_reply(self, subject, body, to_recipients=None, cc_recipients=None, bcc_recipients=None, author=None): if not to_recipients: if not self.author: raise ValueError("'to_recipients' must be set when message has no 'author'") to_recipients = [self.author] return ReplyToItem( account=self.account, reference_item_id=ReferenceItemId(id=self.id, changekey=self.changekey), subject=subject, new_body=body, to_recipients=to_recipients, cc_recipients=cc_recipients, bcc_recipients=bcc_recipients, author=author, ) def reply(self, subject, body, to_recipients=None, cc_recipients=None, bcc_recipients=None, author=None): return self.create_reply(subject, body, to_recipients, cc_recipients, bcc_recipients, author).send() @require_id def create_reply_all(self, subject, body, author=None): me = MailboxField().clean(self.account.primary_smtp_address.lower()) to_recipients = set(self.to_recipients or []) to_recipients.discard(me) cc_recipients = set(self.cc_recipients or []) cc_recipients.discard(me) bcc_recipients = set(self.bcc_recipients or []) bcc_recipients.discard(me) if self.author: to_recipients.add(self.author) return ReplyAllToItem( account=self.account, reference_item_id=ReferenceItemId(id=self.id, changekey=self.changekey), subject=subject, new_body=body, to_recipients=list(to_recipients), cc_recipients=list(cc_recipients), bcc_recipients=list(bcc_recipients), author=author, ) def reply_all(self, subject, body, author=None): return self.create_reply_all(subject, body, author).send() def mark_as_junk(self, is_junk=True, move_item=True): """Mark or un-marks items as junk email. :param is_junk: If True, the sender will be added from the blocked sender list. Otherwise, the sender will be removed. :param move_item: If true, the item will be moved to the junk folder. :return: """ from ..services import MarkAsJunk res = MarkAsJunk(account=self.account).get( items=[self], is_junk=is_junk, move_item=move_item, expect_result=None ) if res is None: return self.folder = self.account.junk if is_junk else self.account.inbox self.id, self.changekey = res
Pick out optional 'account' and 'folder' kwargs, and pass the rest to the parent class.
:param kwargs: 'account' is optional but allows calling 'send()' and 'delete()' 'folder' is optional but allows calling 'save()'. If 'folder' has an account, and 'account' is not set, we use folder.account.
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
Instance variables
var bcc_recipients
var cc_recipients
var conversation_index
var conversation_topic
var is_delivery_receipt_requested
var is_read
var is_read_receipt_requested
var is_response_requested
var message_id
var received_by
var received_representing
var references
var reminder_message_data
var reply_to
var sender
var to_recipients
Methods
def create_reply(self,
subject,
body,
to_recipients=None,
cc_recipients=None,
bcc_recipients=None,
author=None)-
Expand source code
@require_id def create_reply(self, subject, body, to_recipients=None, cc_recipients=None, bcc_recipients=None, author=None): if not to_recipients: if not self.author: raise ValueError("'to_recipients' must be set when message has no 'author'") to_recipients = [self.author] return ReplyToItem( account=self.account, reference_item_id=ReferenceItemId(id=self.id, changekey=self.changekey), subject=subject, new_body=body, to_recipients=to_recipients, cc_recipients=cc_recipients, bcc_recipients=bcc_recipients, author=author, )
def create_reply_all(self, subject, body, author=None)
-
Expand source code
@require_id def create_reply_all(self, subject, body, author=None): me = MailboxField().clean(self.account.primary_smtp_address.lower()) to_recipients = set(self.to_recipients or []) to_recipients.discard(me) cc_recipients = set(self.cc_recipients or []) cc_recipients.discard(me) bcc_recipients = set(self.bcc_recipients or []) bcc_recipients.discard(me) if self.author: to_recipients.add(self.author) return ReplyAllToItem( account=self.account, reference_item_id=ReferenceItemId(id=self.id, changekey=self.changekey), subject=subject, new_body=body, to_recipients=list(to_recipients), cc_recipients=list(cc_recipients), bcc_recipients=list(bcc_recipients), author=author, )
def mark_as_junk(self, is_junk=True, move_item=True)
-
Expand source code
def mark_as_junk(self, is_junk=True, move_item=True): """Mark or un-marks items as junk email. :param is_junk: If True, the sender will be added from the blocked sender list. Otherwise, the sender will be removed. :param move_item: If true, the item will be moved to the junk folder. :return: """ from ..services import MarkAsJunk res = MarkAsJunk(account=self.account).get( items=[self], is_junk=is_junk, move_item=move_item, expect_result=None ) if res is None: return self.folder = self.account.junk if is_junk else self.account.inbox self.id, self.changekey = res
Mark or un-marks items as junk email.
:param is_junk: If True, the sender will be added from the blocked sender list. Otherwise, the sender will be removed. :param move_item: If true, the item will be moved to the junk folder. :return:
def reply(self,
subject,
body,
to_recipients=None,
cc_recipients=None,
bcc_recipients=None,
author=None)-
Expand source code
def reply(self, subject, body, to_recipients=None, cc_recipients=None, bcc_recipients=None, author=None): return self.create_reply(subject, body, to_recipients, cc_recipients, bcc_recipients, author).send()
def reply_all(self, subject, body, author=None)
-
Expand source code
def reply_all(self, subject, body, author=None): return self.create_reply_all(subject, body, author).send()
def send(self,
save_copy=True,
copy_to_folder=None,
conflict_resolution='AutoResolve',
send_meeting_invitations='SendToNone')-
Expand source code
@require_account def send( self, save_copy=True, copy_to_folder=None, conflict_resolution=AUTO_RESOLVE, send_meeting_invitations=SEND_TO_NONE, ): from ..services import SendItem # Only sends a message. The message can either be an existing draft stored in EWS or a new message that does # not yet exist in EWS. if copy_to_folder and not save_copy: raise AttributeError("'save_copy' must be True when 'copy_to_folder' is set") if save_copy and not copy_to_folder: copy_to_folder = self.account.sent # 'Sent' is default EWS behaviour if self.id: SendItem(account=self.account).get(items=[self], saved_item_folder=copy_to_folder) # The item will be deleted from the original folder self._id = None self.folder = copy_to_folder return None # New message if copy_to_folder: # This would better be done via send_and_save() but let's just support it here self.folder = copy_to_folder return self.send_and_save( conflict_resolution=conflict_resolution, send_meeting_invitations=send_meeting_invitations ) if self.account.version.build < EXCHANGE_2013 and self.attachments: # At least some versions prior to Exchange 2013 can't send attachments immediately. You need to first save, # then attach, then send. This is done in send_and_save(). send() will delete the item again. self.send_and_save( conflict_resolution=conflict_resolution, send_meeting_invitations=send_meeting_invitations ) return None self._create(message_disposition=SEND_ONLY, send_meeting_invitations=send_meeting_invitations) return None
def send_and_save(self,
update_fields=None,
conflict_resolution='AutoResolve',
send_meeting_invitations='SendToNone')-
Expand source code
def send_and_save( self, update_fields=None, conflict_resolution=AUTO_RESOLVE, send_meeting_invitations=SEND_TO_NONE ): # Sends Message and saves a copy in the parent folder. Does not return an ItemId. if self.id: return self._update( update_fieldnames=update_fields, message_disposition=SEND_AND_SAVE_COPY, conflict_resolution=conflict_resolution, send_meeting_invitations=send_meeting_invitations, ) if self.account.version.build < EXCHANGE_2013 and self.attachments: # At least some versions prior to Exchange 2013 can't send-and-save attachments immediately. You need # to first save, then attach, then send. This is done in save(). self.save( update_fields=update_fields, conflict_resolution=conflict_resolution, send_meeting_invitations=send_meeting_invitations, ) return self.send( save_copy=False, conflict_resolution=conflict_resolution, send_meeting_invitations=send_meeting_invitations, ) return self._create(message_disposition=SEND_AND_SAVE_COPY, send_meeting_invitations=send_meeting_invitations)
Inherited members
class Persona (**kwargs)
-
Expand source code
class Persona(IdChangeKeyMixIn): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/persona""" ELEMENT_NAME = "Persona" ID_ELEMENT_CLS = PersonaId _id = IdElementField(field_uri="persona:PersonaId", value_cls=ID_ELEMENT_CLS, namespace=TNS) persona_type = CharField(field_uri="persona:PersonaType") persona_object_type = TextField(field_uri="persona:PersonaObjectStatus") creation_time = DateTimeField(field_uri="persona:CreationTime") bodies = BodyContentAttributedValueField(field_uri="persona:Bodies") display_name_first_last_sort_key = TextField(field_uri="persona:DisplayNameFirstLastSortKey") display_name_last_first_sort_key = TextField(field_uri="persona:DisplayNameLastFirstSortKey") company_sort_key = TextField(field_uri="persona:CompanyNameSortKey") home_sort_key = TextField(field_uri="persona:HomeCitySortKey") work_city_sort_key = TextField(field_uri="persona:WorkCitySortKey") display_name_first_last_header = CharField(field_uri="persona:DisplayNameFirstLastHeader") display_name_last_first_header = CharField(field_uri="persona:DisplayNameLastFirstHeader") file_as_header = TextField(field_uri="persona:FileAsHeader") display_name = CharField(field_uri="persona:DisplayName") display_name_first_last = CharField(field_uri="persona:DisplayNameFirstLast") display_name_last_first = CharField(field_uri="persona:DisplayNameLastFirst") file_as = CharField(field_uri="persona:FileAs") file_as_id = TextField(field_uri="persona:FileAsId") display_name_prefix = CharField(field_uri="persona:DisplayNamePrefix") given_name = CharField(field_uri="persona:GivenName") middle_name = CharField(field_uri="persona:MiddleName") surname = CharField(field_uri="persona:Surname") generation = CharField(field_uri="persona:Generation") nickname = TextField(field_uri="persona:Nickname") yomi_company_name = TextField(field_uri="persona:YomiCompanyName") yomi_first_name = TextField(field_uri="persona:YomiFirstName") yomi_last_name = TextField(field_uri="persona:YomiLastName") title = CharField(field_uri="persona:Title") department = TextField(field_uri="persona:Department") company_name = CharField(field_uri="persona:CompanyName") email_address = EWSElementField(field_uri="persona:EmailAddress", value_cls=EmailAddress) email_addresses = EWSElementListField(field_uri="persona:EmailAddresses", value_cls=Address) PhoneNumber = PersonaPhoneNumberField(field_uri="persona:PhoneNumber") im_address = CharField(field_uri="persona:ImAddress") home_city = CharField(field_uri="persona:HomeCity") work_city = CharField(field_uri="persona:WorkCity") relevance_score = CharField(field_uri="persona:RelevanceScore") folder_ids = EWSElementListField(field_uri="persona:FolderIds", value_cls=FolderId) attributions = EWSElementListField(field_uri="persona:Attributions", value_cls=Attribution) display_names = StringAttributedValueField(field_uri="persona:DisplayNames") file_ases = StringAttributedValueField(field_uri="persona:FileAses") file_as_ids = StringAttributedValueField(field_uri="persona:FileAsIds") display_name_prefixes = StringAttributedValueField(field_uri="persona:DisplayNamePrefixes") given_names = StringAttributedValueField(field_uri="persona:GivenNames") middle_names = StringAttributedValueField(field_uri="persona:MiddleNames") surnames = StringAttributedValueField(field_uri="persona:Surnames") generations = StringAttributedValueField(field_uri="persona:Generations") nicknames = StringAttributedValueField(field_uri="persona:Nicknames") initials = StringAttributedValueField(field_uri="persona:Initials") yomi_company_names = StringAttributedValueField(field_uri="persona:YomiCompanyNames") yomi_first_names = StringAttributedValueField(field_uri="persona:YomiFirstNames") yomi_last_names = StringAttributedValueField(field_uri="persona:YomiLastNames") business_phone_numbers = PhoneNumberAttributedValueField(field_uri="persona:BusinessPhoneNumbers") business_phone_numbers2 = PhoneNumberAttributedValueField(field_uri="persona:BusinessPhoneNumbers2") home_phones = PhoneNumberAttributedValueField(field_uri="persona:HomePhones") home_phones2 = PhoneNumberAttributedValueField(field_uri="persona:HomePhones2") mobile_phones = PhoneNumberAttributedValueField(field_uri="persona:MobilePhones") mobile_phones2 = PhoneNumberAttributedValueField(field_uri="persona:MobilePhones2") assistant_phone_numbers = PhoneNumberAttributedValueField(field_uri="persona:AssistantPhoneNumbers") callback_phones = PhoneNumberAttributedValueField(field_uri="persona:CallbackPhones") car_phones = PhoneNumberAttributedValueField(field_uri="persona:CarPhones") home_faxes = PhoneNumberAttributedValueField(field_uri="persona:HomeFaxes") organization_main_phones = PhoneNumberAttributedValueField(field_uri="persona:OrganizationMainPhones") other_faxes = PhoneNumberAttributedValueField(field_uri="persona:OtherFaxes") other_telephones = PhoneNumberAttributedValueField(field_uri="persona:OtherTelephones") other_phones2 = PhoneNumberAttributedValueField(field_uri="persona:OtherPhones2") pagers = PhoneNumberAttributedValueField(field_uri="persona:Pagers") radio_phones = PhoneNumberAttributedValueField(field_uri="persona:RadioPhones") telex_numbers = PhoneNumberAttributedValueField(field_uri="persona:TelexNumbers") tty_tdd_phone_numbers = PhoneNumberAttributedValueField(field_uri="persona:TTYTDDPhoneNumbers") work_faxes = PhoneNumberAttributedValueField(field_uri="persona:WorkFaxes") emails1 = EmailAddressAttributedValueField(field_uri="persona:Emails1") emails2 = EmailAddressAttributedValueField(field_uri="persona:Emails2") emails3 = EmailAddressAttributedValueField(field_uri="persona:Emails3") business_home_pages = StringAttributedValueField(field_uri="persona:BusinessHomePages") personal_home_pages = StringAttributedValueField(field_uri="persona:PersonalHomePages") office_locations = StringAttributedValueField(field_uri="persona:OfficeLocations") im_addresses = StringAttributedValueField(field_uri="persona:ImAddresses") im_addresses2 = StringAttributedValueField(field_uri="persona:ImAddresses2") im_addresses3 = StringAttributedValueField(field_uri="persona:ImAddresses3") business_addresses = PostalAddressAttributedValueField(field_uri="persona:BusinessAddresses") home_addresses = PostalAddressAttributedValueField(field_uri="persona:HomeAddresses") other_addresses = PostalAddressAttributedValueField(field_uri="persona:OtherAddresses") titles = StringAttributedValueField(field_uri="persona:Titles") departments = StringAttributedValueField(field_uri="persona:Departments") company_names = StringAttributedValueField(field_uri="persona:CompanyNames") managers = StringAttributedValueField(field_uri="persona:Managers") assistant_names = StringAttributedValueField(field_uri="persona:AssistantNames") professions = StringAttributedValueField(field_uri="persona:Professions") spouse_names = StringAttributedValueField(field_uri="persona:SpouseNames") children = StringAttributedValueField(field_uri="persona:Children") schools = StringAttributedValueField(field_uri="persona:Schools") hobbies = StringAttributedValueField(field_uri="persona:Hobbies") wedding_anniversaries = StringAttributedValueField(field_uri="persona:WeddingAnniversaries") birthdays = StringAttributedValueField(field_uri="persona:Birthdays") locations = StringAttributedValueField(field_uri="persona:Locations") # This class has an additional field of type "ExtendedPropertyAttributedValueField" and # field_uri 'persona:ExtendedProperties'
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
var ID_ELEMENT_CLS
Instance variables
var PhoneNumber
var assistant_names
var assistant_phone_numbers
var attributions
var birthdays
var bodies
var business_addresses
var business_home_pages
var business_phone_numbers
var business_phone_numbers2
var callback_phones
var car_phones
var children
var company_name
var company_names
var company_sort_key
var creation_time
var department
var departments
var display_name
var display_name_first_last
var display_name_first_last_header
var display_name_first_last_sort_key
var display_name_last_first
var display_name_last_first_header
var display_name_last_first_sort_key
var display_name_prefix
var display_name_prefixes
var display_names
var email_address
var email_addresses
var emails1
var emails2
var emails3
var file_as
var file_as_header
var file_as_id
var file_as_ids
var file_ases
var folder_ids
var generation
var generations
var given_name
var given_names
var hobbies
var home_addresses
var home_city
var home_faxes
var home_phones
var home_phones2
var home_sort_key
var im_address
var im_addresses
var im_addresses2
var im_addresses3
var initials
var locations
var managers
var middle_name
var middle_names
var mobile_phones
var mobile_phones2
var nickname
var nicknames
var office_locations
var organization_main_phones
var other_addresses
var other_faxes
var other_phones2
var other_telephones
var pagers
var persona_object_type
var persona_type
var personal_home_pages
var professions
var radio_phones
var relevance_score
var schools
var spouse_names
var surname
var surnames
var telex_numbers
var title
var titles
var tty_tdd_phone_numbers
var wedding_anniversaries
var work_city
var work_city_sort_key
var work_faxes
var yomi_company_name
var yomi_company_names
var yomi_first_name
var yomi_first_names
var yomi_last_name
var yomi_last_names
Inherited members
class PostItem (**kwargs)
-
Expand source code
class PostItem(Item): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/postitem""" ELEMENT_NAME = "PostItem" conversation_index = Message.FIELDS["conversation_index"] conversation_topic = Message.FIELDS["conversation_topic"] author = Message.FIELDS["author"] message_id = Message.FIELDS["message_id"] is_read = Message.FIELDS["is_read"] posted_time = DateTimeField(field_uri="postitem:PostedTime", is_read_only=True) references = TextField(field_uri="message:References") sender = MailboxField(field_uri="message:Sender", is_read_only=True, is_read_only_after_send=True)
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/postitem
Pick out optional 'account' and 'folder' kwargs, and pass the rest to the parent class.
:param kwargs: 'account' is optional but allows calling 'send()' and 'delete()' 'folder' is optional but allows calling 'save()'. If 'folder' has an account, and 'account' is not set, we use folder.account.
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
Instance variables
var conversation_index
var conversation_topic
var is_read
var message_id
var posted_time
var references
var sender
Inherited members
class PostReplyItem (**kwargs)
-
Expand source code
class PostReplyItem(Item): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/postreplyitem""" ELEMENT_NAME = "PostReplyItem" # This element only has Item fields up to, and including, 'culture' # TDO: Plus all message fields new_body = BodyField(field_uri="NewBodyContent") # Accepts and returns Body or HTMLBody instances culture_idx = Item.FIELDS.index_by_name("culture") sender_idx = Message.FIELDS.index_by_name("sender") FIELDS = Item.FIELDS[: culture_idx + 1] + Message.FIELDS[sender_idx:]
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/postreplyitem
Pick out optional 'account' and 'folder' kwargs, and pass the rest to the parent class.
:param kwargs: 'account' is optional but allows calling 'send()' and 'delete()' 'folder' is optional but allows calling 'save()'. If 'folder' has an account, and 'account' is not set, we use folder.account.
Ancestors
Class variables
var ELEMENT_NAME
var FIELDS
var culture_idx
var sender_idx
Instance variables
var bcc_recipients
var cc_recipients
var conversation_index
var conversation_topic
var is_delivery_receipt_requested
var is_read
var is_read_receipt_requested
var is_response_requested
var message_id
var new_body
var received_by
var received_representing
var references
var reminder_message_data
var reply_to
var sender
var to_recipients
Inherited members
class RegisterMixIn (**kwargs)
-
Expand source code
class RegisterMixIn(IdChangeKeyMixIn, metaclass=EWSMeta): """Base class for classes that can change their list of supported fields dynamically.""" # This class implements dynamic fields on an element class, so we need to include __dict__ in __slots__ __slots__ = ("__dict__",) INSERT_AFTER_FIELD = None @classmethod def register(cls, attr_name, attr_cls): """Register a custom extended property in this item class so they can be accessed just like any other attribute :param attr_name: :param attr_cls: :return: """ if not cls.INSERT_AFTER_FIELD: raise ValueError(f"Class {cls} is missing INSERT_AFTER_FIELD value") try: cls.get_field_by_fieldname(attr_name) except InvalidField: pass else: raise ValueError(f"'attr_name' {attr_name!r} is already registered") if not issubclass(attr_cls, ExtendedProperty): raise TypeError(f"'attr_cls' {attr_cls!r} must be a subclass of type {ExtendedProperty}") # Check if class attributes are properly defined attr_cls.validate_cls() # ExtendedProperty is not a real field, but a placeholder in the fields list. See # https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/item # # Find the correct index for the new extended property, and insert. if attr_cls.is_array_type(): field = ExtendedPropertyListField(attr_name, value_cls=attr_cls) else: field = ExtendedPropertyField(attr_name, value_cls=attr_cls) cls.add_field(field, insert_after=cls.INSERT_AFTER_FIELD) @classmethod def deregister(cls, attr_name): """De-register an extended property that has been registered with register(). :param attr_name: :return: """ try: field = cls.get_field_by_fieldname(attr_name) except InvalidField: raise ValueError(f"{attr_name!r} is not registered") if not isinstance(field, ExtendedPropertyField): raise ValueError(f"{attr_name} is not registered as an ExtendedProperty") cls.remove_field(field)
Base class for classes that can change their list of supported fields dynamically.
Ancestors
Subclasses
Class variables
var INSERT_AFTER_FIELD
Static methods
def deregister(attr_name)
-
De-register an extended property that has been registered with register().
:param attr_name: :return:
def register(attr_name, attr_cls)
-
Register a custom extended property in this item class so they can be accessed just like any other attribute
:param attr_name: :param attr_cls: :return:
Inherited members
class ReplyAllToItem (**kwargs)
-
Expand source code
class ReplyAllToItem(BaseReplyItem): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/replyalltoitem""" ELEMENT_NAME = "ReplyAllToItem"
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/replyalltoitem
Ancestors
Class variables
var ELEMENT_NAME
Inherited members
class ReplyToItem (**kwargs)
-
Expand source code
class ReplyToItem(BaseReplyItem): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/replytoitem""" ELEMENT_NAME = "ReplyToItem"
Ancestors
Class variables
var ELEMENT_NAME
Inherited members
class Task (**kwargs)
-
Expand source code
class Task(Item): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/task""" ELEMENT_NAME = "Task" NOT_STARTED = "NotStarted" COMPLETED = "Completed" actual_work = IntegerField(field_uri="task:ActualWork", min=0) assigned_time = DateTimeField(field_uri="task:AssignedTime", is_read_only=True) billing_information = TextField(field_uri="task:BillingInformation") change_count = IntegerField(field_uri="task:ChangeCount", is_read_only=True, min=0) companies = TextListField(field_uri="task:Companies") # 'complete_date' can be set, but is ignored by the server, which sets it to now() complete_date = DateTimeField(field_uri="task:CompleteDate", is_read_only=True) contacts = TextListField(field_uri="task:Contacts") delegation_state = ChoiceField( field_uri="task:DelegationState", choices={ Choice("NoMatch"), Choice("OwnNew"), Choice("Owned"), Choice("Accepted"), Choice("Declined"), Choice("Max"), }, is_read_only=True, ) delegator = CharField(field_uri="task:Delegator", is_read_only=True) due_date = DateTimeBackedDateField(field_uri="task:DueDate") is_editable = BooleanField(field_uri="task:IsAssignmentEditable", is_read_only=True) is_complete = BooleanField(field_uri="task:IsComplete", is_read_only=True) is_recurring = BooleanField(field_uri="task:IsRecurring", is_read_only=True) is_team_task = BooleanField(field_uri="task:IsTeamTask", is_read_only=True) mileage = TextField(field_uri="task:Mileage") owner = CharField(field_uri="task:Owner", is_read_only=True) percent_complete = DecimalField( field_uri="task:PercentComplete", is_required=True, default=Decimal(0.0), min=Decimal(0), max=Decimal(100), is_searchable=False, ) recurrence = TaskRecurrenceField(field_uri="task:Recurrence", is_searchable=False) start_date = DateTimeBackedDateField(field_uri="task:StartDate") status = ChoiceField( field_uri="task:Status", choices={ Choice(NOT_STARTED), Choice("InProgress"), Choice(COMPLETED), Choice("WaitingOnOthers"), Choice("Deferred"), }, is_required=True, is_searchable=False, default=NOT_STARTED, ) status_description = CharField(field_uri="task:StatusDescription", is_read_only=True) total_work = IntegerField(field_uri="task:TotalWork", min=0) # O365 throws ErrorInternalServerError "[0x004f0102] MapiReplyToBlob" if UniqueBody is requested unique_body_idx = Item.FIELDS.index_by_name("unique_body") FIELDS = Item.FIELDS[:unique_body_idx] + Item.FIELDS[unique_body_idx + 1 :] def clean(self, version=None): super().clean(version=version) if self.due_date and self.start_date and self.due_date < self.start_date: log.warning( "'due_date' must be greater than 'start_date' (%s vs %s). Resetting 'due_date'", self.due_date, self.start_date, ) self.due_date = self.start_date if self.complete_date: if self.status != self.COMPLETED: log.warning( "'status' must be '%s' when 'complete_date' is set (%s). Resetting", self.COMPLETED, self.status ) self.status = self.COMPLETED now = datetime.datetime.now(tz=UTC) if (self.complete_date - now).total_seconds() > 120: # Reset complete_date values that are in the future # 'complete_date' can be set automatically by the server. Allow some grace between local and server time log.warning("'complete_date' must be in the past (%s vs %s). Resetting", self.complete_date, now) self.complete_date = now if self.start_date and self.complete_date.date() < self.start_date: log.warning( "'complete_date' must be greater than 'start_date' (%s vs %s). Resetting", self.complete_date, self.start_date, ) self.complete_date = EWSDateTime.combine(self.start_date, datetime.time(0, 0)).replace(tzinfo=UTC) if self.percent_complete is not None: if self.status == self.COMPLETED and self.percent_complete != Decimal(100): # percent_complete must be 100% if task is complete log.warning( "'percent_complete' must be 100 when 'status' is '%s' (%s). Resetting", self.COMPLETED, self.percent_complete, ) self.percent_complete = Decimal(100) elif self.status == self.NOT_STARTED and self.percent_complete != Decimal(0): # percent_complete must be 0% if task is not started log.warning( "'percent_complete' must be 0 when 'status' is '%s' (%s). Resetting", self.NOT_STARTED, self.percent_complete, ) self.percent_complete = Decimal(0) def complete(self): # A helper method to mark a task as complete on the server self.status = Task.COMPLETED self.percent_complete = Decimal(100) self.save()
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/task
Pick out optional 'account' and 'folder' kwargs, and pass the rest to the parent class.
:param kwargs: 'account' is optional but allows calling 'send()' and 'delete()' 'folder' is optional but allows calling 'save()'. If 'folder' has an account, and 'account' is not set, we use folder.account.
Ancestors
Class variables
var COMPLETED
var ELEMENT_NAME
var FIELDS
var NOT_STARTED
var unique_body_idx
Instance variables
var actual_work
var assigned_time
var billing_information
var change_count
var companies
var complete_date
var contacts
var delegation_state
var delegator
var due_date
var is_complete
var is_editable
var is_recurring
var is_team_task
var mileage
var owner
var percent_complete
var recurrence
var start_date
var status
var status_description
var total_work
Methods
def clean(self, version=None)
-
Expand source code
def clean(self, version=None): super().clean(version=version) if self.due_date and self.start_date and self.due_date < self.start_date: log.warning( "'due_date' must be greater than 'start_date' (%s vs %s). Resetting 'due_date'", self.due_date, self.start_date, ) self.due_date = self.start_date if self.complete_date: if self.status != self.COMPLETED: log.warning( "'status' must be '%s' when 'complete_date' is set (%s). Resetting", self.COMPLETED, self.status ) self.status = self.COMPLETED now = datetime.datetime.now(tz=UTC) if (self.complete_date - now).total_seconds() > 120: # Reset complete_date values that are in the future # 'complete_date' can be set automatically by the server. Allow some grace between local and server time log.warning("'complete_date' must be in the past (%s vs %s). Resetting", self.complete_date, now) self.complete_date = now if self.start_date and self.complete_date.date() < self.start_date: log.warning( "'complete_date' must be greater than 'start_date' (%s vs %s). Resetting", self.complete_date, self.start_date, ) self.complete_date = EWSDateTime.combine(self.start_date, datetime.time(0, 0)).replace(tzinfo=UTC) if self.percent_complete is not None: if self.status == self.COMPLETED and self.percent_complete != Decimal(100): # percent_complete must be 100% if task is complete log.warning( "'percent_complete' must be 100 when 'status' is '%s' (%s). Resetting", self.COMPLETED, self.percent_complete, ) self.percent_complete = Decimal(100) elif self.status == self.NOT_STARTED and self.percent_complete != Decimal(0): # percent_complete must be 0% if task is not started log.warning( "'percent_complete' must be 0 when 'status' is '%s' (%s). Resetting", self.NOT_STARTED, self.percent_complete, ) self.percent_complete = Decimal(0)
def complete(self)
-
Expand source code
def complete(self): # A helper method to mark a task as complete on the server self.status = Task.COMPLETED self.percent_complete = Decimal(100) self.save()
Inherited members
class TentativelyAcceptItem (**kwargs)
-
Expand source code
class TentativelyAcceptItem(BaseMeetingReplyItem): """MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/tentativelyacceptitem""" ELEMENT_NAME = "TentativelyAcceptItem"
Pick out optional 'account' and 'folder' kwargs, and pass the rest to the parent class.
:param kwargs: 'account' is optional but allows calling 'send()' and 'delete()' 'folder' is optional but allows calling 'save()'. If 'folder' has an account, and 'account' is not set, we use folder.account.
Ancestors
Class variables
var ELEMENT_NAME
Inherited members