File: CHANGELOG.md

package info (click to toggle)
python-exchangelib 3.2.0-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, sid
  • size: 1,496 kB
  • sloc: python: 18,149; makefile: 3
file content (749 lines) | stat: -rw-r--r-- 29,874 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
Change Log
==========

HEAD
----


3.2.0
-----
-   Remove use of `ThreadPool` objects. Threads were used to implement async HTTP requests, but
    were creating massive memory leaks. Async requests should be reimplemented using a real async
    HTTP request package, so this is just an emergency fix. This also lowers the default
    `Protocol.SESSION_POOLSIZE` to 1 because no internal code is running multi-threaded anymore.
-   All-day calendar items (created as `CalendarItem(is_all_day=True, ...)`) now accept `EWSDate`
    instances for the `start` and `end` values. Similarly, all-day calendar items fetched from
    the server now return `start` and `end` values as `EWSDate` instances. In this case, start
    and end values are inclusive; a one-day event starts and ends on the same `EWSDate` value.
-   Add support for `RecurringMasterItemId` and `OccurrenceItemId` elements that allow to request
    the master recurrence from a `CalendarItem` occurrence, and to request a specific occurrence
    from a `CalendarItem` master recurrence. `CalendarItem.master_recurrence()` and 
    `CalendarItem.occurrence(some_occurrence_index)` methods were added to aid this traversal.
    `some_occurrence_index` in the last method specifies which item in the list of occurrences to
    target; `CalendarItem.occurrence(3)` gets the third occurrence in the recurrence.
-   Change `Contact.birthday` and `Contact.wedding_anniversary` from `EWSDateTime` to `EWSDate`
    fields. EWS still expects and sends datetime values but has started to reset the time part to
    11:59. Dates are a better match for these two fields anyway.
-   Remove support for `len(some_queryset)`. It had the nasty side-effect of forcing
    `list(some_queryset)` to run the query twice, once for pre-allocating the list via the result
    of `len(some_queryset)`, and then once more to fetch the results. All occurrences of
    `len(some_queryset)` can be replaced with `some_queryset.count()`. Unfortunately, there is
    no way to keep backwards-compatibility for this feature.
-   Added `Account.identity`, an attribute to contain extra information for impersonation. Setting
    `Account.identity.upn` or `Account.identity.sid` removes the need for an AD lookup on every request.
    `upn` will often be the same as `primary_smtp_address`, but it is not guaranteed. If you have
    access to your organization's AD servers, you can look up these values once and add them to your
    `Account` object to improve performance of the following requests.
-   Added support for CBA authentication


3.1.1
-----
-   The `max_wait` argument to `FaultTolerance` changed semantics. Previously, it triggered when
    the delay until the next attempt would exceed this value. It now triggers after the given
    timespan since the *first* request attempt.
-   Fixed a bug when pagination is combined with `max_items` (#710)
-   Other minor bug fixes


3.1.0
-----
-   Removed the legacy autodiscover implementation.
-   Added `QuerySet.depth()` to configure item traversal of querysets. Default is `Shallow` except
    for the `CommonViews` folder where default is `Associated`.
-   Updating credentials on `Account.protocol` after getting an `UnauthorizedError` now works.


3.0.0
-----
-   The new Autodiscover implementation added in 2.2.0 is now default. To switch back to the old
    implementation, set the environment variable `EXCHANGELIB_AUTODISCOVER_VERSION=legacy`.
-   Removed support for Python 2


2.2.0
-----
-   Added support for specifying a separate retry policy for the autodiscover service endpoint
    selection. Set via the `exchangelib.autodiscover.legacy.INITIAL_RETRY_POLICY` module variable
    for the the old autodiscover implementation, and via the
    `exchangelib.autodiscover.Autodiscovery.INITIAL_RETRY_POLICY` class variable for the new one.
-   Support the authorization code OAuth 2.0 grant type (see issue #698)
-   Removed the `RootOfHierarchy.permission_set` field. It was causing too many failures in the wild.
-   The full autodiscover response containing all contents of the reponse is now available as `Account.ad_response`.
-   Added a new Autodiscover implementation that is closer to the specification and easier to debug. To switch
    to the new implementation, set the environment variable `EXCHANGELIB_AUTODISCOVER_VERSION=new`. The old
    one is still the default if the variable is not set, or set to `EXCHANGELIB_AUTODISCOVER_VERSION=legacy`.
-   The `Item.mime_content` field was switched back from a string type to a `bytes` type. It turns out trying
    to decode the data was an error (see issue #709).


2.1.1
-----
-   Bugfix release.


2.1.0
-----
-   Added support for OAuth 2.0 authentication
-   Fixed a bug in `RelativeMonthlyPattern` and `RelativeYearlyPattern` where the `weekdays` field was thought to
    be a list, but is in fact a single value. Renamed the field to `weekday` to reflect the change.
-   Added support for archiving items to the archive mailbox, if the account has one.
-   Added support for getting delegate information on an Account, as `Account.delegates`.
-   Added support for the `ConvertId` service. Available as `Protocol.convert_ids()`.


2.0.1
-----
-   Fixed a bug where version 2.x could not open autodiscover cache files generated by
    version 1.x packages.


2.0.0
-----
-   `Item.mime_content` is now a text field instead of a binary field. Encoding and
    decoding is done automatically.
-   The `Item.item_id`, `Folder.folder_id` and `Occurrence.item_id` fields that were renamed
    to just `id` in 1.12.0, have now been removed.
-   The `Persona.persona_id` field was replaced with `Persona.id` and `Persona.changekey`, to
    align with the `Item` and `Folder` classes.
-   In addition to bulk deleting via a QuerySet (`qs.delete()`), it is now possible to also
    bulk send, move and copy items in a QuerySet (via `qs.send()`, `qs.move()` and `qs.copy()`,
    respectively).
-   SSPI support was added but dependencies are not installed by default since it only works
    in Win32 environments. Install as `pip install exchangelib[sspi]` to get SSPI support.
    Install with `pip install exchangelib[complete]` to get both Kerberos and SSPI auth.
-   The custom `extern_id` field is no longer registered by default. If you require this field,
    register it manually as part of your setup code on the item types you need:

    ```python
    from exchangelib import CalendarItem, Message, Contact, Task
    from exchangelib.extended_properties import ExternId

    CalendarItem.register('extern_id', ExternId)
    Message.register('extern_id', ExternId)
    Contact.register('extern_id', ExternId)
    Task.register('extern_id', ExternId)
    ```
-   The `ServiceAccount` class has been removed. If you want fault tolerance, set it in a
    `Configuration` object:

    ```python
    from exchangelib import Configuration, Credentials, FaultTolerance
    c = Credentials('foo', 'bar')
    config = Configuration(credentials=c, retry_policy=FaultTolerance())
    ```
-   It is now possible to use Kerberos and SSPI auth without providing a dummy
    `Credentials('', '')` object.
-   The `has_ssl` argument of `Configuration` was removed. If you want to connect to a
    plain HTTP endpoint, pass the full URL in the `service_endpoint` argument.
-   We no longer look in `types.xsd` for a hint of which API version the server is running. Instead,
    we query the service directly, starting with the latest version first.


1.12.5
------
-   Bugfix release.


1.12.4
------
- Fix bug that left out parts of the folder hierarchy when traversing `account.root`.
- Fix bug that did not properly find all attachments if an item has a mix of item
  and file attachments.


1.12.3
------
-   Add support for reading and writing `PermissionSet` field on folders.
-   Add support for Exchange 2019 build IDs.


1.12.2
------
-   Add `Protocol.expand_dl()` to get members of a distribution list.


1.12.1
------
-   Lower the session pool size automatically in response to ErrorServerBusy and
    ErrorTooManyObjectsOpened errors from the server.
-   Unusual slicing and indexing (e.g. `inbox.all()[9000]` and `inbox.all()[9000:9001]`)
    is now efficient.
-   Downloading large attachments is now more memory-efficient. We can now stream the file
    content without ever storing the full file content in memory, using the new
    `Attachment.fp` context manager.

1.12.0
------

-   Add a MAINFEST.in to ensure the LICENSE file gets included + CHANGELOG.md
    and README.md to sdist tarball
-   Renamed `Item.item_id`, `Folder.folder_id` and `Occurrence.item_id` to just
    `Item.id`, `Folder.id` and `Occurrence.id`, respectively. This removes
    redundancy in the naming and provides consistency. For all classes that
    have an ID, the ID can now be accessed using the `id` attribute. Backwards
    compatibility and deprecation warnings were added.
-   Support folder traversal without creating a full cache of the folder
    hierarchy first, using the `some_folder // 'sub_folder' // 'leaf'`
    (double-slash) syntax.
-   Fix a bug in traversal of public and archive folders. These folder
    hierarchies are now fully supported.
-   Fix a bug where the timezone of a calendar item changed when the item was
    fetched and then saved.
-   Kerberos support is now optional and Kerberos dependencies are not
    installed by default. Install as `pip install exchangelib[kerberos]` to get
    Kerberos support.

1.11.4
------

-   Improve back off handling when receiving `ErrorServerBusy` error messages
    from the server
-   Fixed bug where `Account.root` and its children would point to the root
    folder of the connecting account instead of the target account when
    connecting to other accounts.

1.11.3
------

-   Add experimental Kerberos support. This adds the `pykerberos` package,
    which needs the following system packages to be installed on Ubuntu/Debian
    systems: `apt-get install build-essential libssl-dev libffi-dev python-dev libkrb5-dev`.

1.11.2
------

-   Bugfix release

1.11.1
------

-   Bugfix release

1.11.0
------

-   Added `cancel` to `CalendarItem` and `CancelCalendarItem` class to
    allow cancelling meetings that were set up
-   Added `accept`, `decline` and `tentatively_accept` to `CalendarItem`
    as wrapper methods
-   Added `accept`, `decline` and `tentatively_accept` to
    `MeetingRequest` to respond to incoming invitations
-   Added `BaseMeetingItem` (inheriting from `Item`) being used as base
    for MeetingCancellation, MeetingMessage, MeetingRequest and
    MeetingResponse
-   Added `AssociatedCalendarItemId` (property),
    `AssociatedCalendarItemIdField` and `ReferenceItemIdField`
-   Added `PostReplyItem`
-   Removed `Folder.get_folder_by_name()` which has been deprecated
    since version `1.10.2`.
-   Added `Item.copy(to_folder=some_folder)` method which copies an item
    to the given folder and returns the ID of the new item.
-   We now respect the back off value of an `ErrorServerBusy`
    server error.
-   Added support for fetching free/busy availability information ofr a
    list of accounts.
-   Added `Message.reply()`, `Message.reply_all()`, and
    `Message.forward()` methods.
-   The full search API now works on single folders *and* collections of
    folders, e.g. `some_folder.glob('foo*').filter()`,
    `some_folder.children.filter()` and `some_folder.walk().filter()`.
-   Deprecated `EWSService.CHUNKSIZE` in favor of a per-request
    chunk\_size available on `Account.bulk_foo()` methods.
-   Support searching the GAL and other contact folders using
    `some_contact_folder.people()`.
-   Deprecated the `page_size` argument for `QuerySet.iterator()` because it
    was inconsistent with other API methods. You can still set the page size
    of a queryset like this:

    ```python
    qs = a.inbox.filter(...).iterator()
    qs.page_size = 123
    for item in items:
        print(item)
    ```

1.10.7
------

-   Added support for registering extended properties on folders.
-   Added support for creating, updating, deleting and emptying folders.

1.10.6
------

-   Added support for getting and setting `Account.oof_settings` using
    the new `OofSettings` class.
-   Added snake\_case named shortcuts to all distinguished folders on
    the `Account` model. E.g. `Account.search_folders`.

1.10.5
------

-   Bugfix release

1.10.4
------

-   Added support for most item fields. The remaining ones are mentioned
    in issue \#203.

1.10.3
------

-   Added an `exchangelib.util.PrettyXmlHandler` log handler which will
    pretty-print and highlight XML requests and responses.

1.10.2
------

-   Greatly improved folder navigation. See the 'Folders' section in the
    README
-   Added deprecation warnings for `Account.folders` and
    `Folder.get_folder_by_name()`

1.10.1
------

-   Bugfix release

1.10.0
------

-   Removed the `verify_ssl` argument to `Account`, `discover` and
    `Configuration`. If you need to disable TLS verification, register a
    custom `HTTPAdapter` class. A sample adapter class is provided for
    convenience:

    ```python
    from exchangelib.protocol import BaseProtocol, NoVerifyHTTPAdapter
    BaseProtocol.HTTP_ADAPTER_CLS = NoVerifyHTTPAdapter
    ```

1.9.6
-----

-   Support new Office365 build numbers

1.9.5
-----

-   Added support for the `effective_rights`field on items and folders.
-   Added support for custom `requests` transport adapters, to allow
    proxy support, custom TLS validation etc.
-   Default value for the `affected_task_occurrences` argument to
    `Item.move_to_trash()`, `Item.soft_delete()` and `Item.delete()` was
    changed to `'AllOccurrences'` as a less surprising default when
    working with simple tasks.
-   Added `Task.complete()` helper method to mark tasks as complete.

1.9.4
-----

-   Added minimal support for the `PostItem` item type
-   Added support for the `DistributionList` item type
-   Added support for receiving naive datetimes from the server. They
    will be localized using the new `default_timezone` attribute on
    `Account`
-   Added experimental support for recurring calendar items. See
    examples in issue \#37.

1.9.3
-----

-   Improved support for `filter()`, `.only()`, `.order_by()` etc. on
    indexed properties. It is now possible to specify labels and
    subfields, e.g.
    `.filter(phone_numbers=PhoneNumber(label='CarPhone', phone_number='123'))`
    `.filter(phone_numbers__CarPhone='123')`,
    `.filter(physical_addresses__Home__street='Elm St. 123')`,
    .only('physical\_addresses\_\_Home\_\_street')\` etc.
-   Improved performance of `.order_by()` when sorting on
    multiple fields.
-   Implemented QueryString search. You can now filter using an EWS
    QueryString, e.g. `filter('subject:XXX')`

1.9.2
-----

-   Added `EWSTimeZone.localzone()` to get the local timezone
-   Support `some_folder.get(item_id=..., changekey=...)` as a shortcut
    to get a single item when you know the ID and changekey.
-   Support attachments on Exchange 2007

1.9.1
-----

-   Fixed XML generation for Exchange 2010 and other picky server
    versions
-   Fixed timezone localization for `EWSTimeZone` created from a static
    timezone

1.9.0
-----

-   Expand support for `ExtendedProperty` to include all
    possible attributes. This required renaming the `property_id`
    attribute to `property_set_id`.
-   When using the `Credentials` class, `UnauthorizedError` is now
    raised if the credentials are wrong.
-   Add a new `version` attribute to `Configuration`, to force the
    server version if version guessing does not work. Accepts a
    `exchangelib.version.Version` object.
-   Rework bulk operations `Account.bulk_foo()` and `Account.fetch()` to
    return some exceptions unraised, if it is deemed the exception does
    not apply to all items. This means that e.g. `fetch()` can return a
    mix of `` `Item `` and `ErrorItemNotFound` instances, if only some
    of the requested `ItemId` were valid. Other exceptions will be
    raised immediately, e.g. `ErrorNonExistentMailbox` because the
    exception applies to all items. It is the responsibility of the
    caller to check the type of the returned values.
-   The `Folder` class has new attributes `total_count`, `unread_count`
    and `child_folder_count`, and a `refresh()` method to update
    these values.
-   The argument to `Account.upload()` was renamed from `upload_data` to
    just `data`
-   Support for using a string search expression for `Folder.filter()`
    was removed. It was a cool idea but using QuerySet chaining and `Q`
    objects is even cooler and provides the same functionality,
    and more.
-   Add support for `reminder_due_by` and
    `reminder_minutes_before_start` fields on `Item` objects. Submitted
    by `@vikipha`.
-   Added a new `ServiceAccount` class which is like `Credentials` but
    does what `is_service_account` did before. If you need
    fault-tolerane and used `Credentials(..., is_service_account=True)`
    before, use `ServiceAccount` now. This also disables fault-tolerance
    for the `Credentials` class, which is in line with what most
    users expected.
-   Added an optional `update_fields` attribute to `save()` to specify
    only some fields to be updated.
-   Code in in `folders.py` has been split into multiple files, and some
    classes will have new import locaions. The most commonly used
    classes have a shortcut in \_\_init\_\_.py
-   Added support for the `exists` lookup in filters, e.g.
    `my_folder.filter(categories__exists=True|False)` to filter on the
    existence of that field on items in the folder.
-   When filtering, `foo__in=value` now requires the value to be a list,
    and `foo__contains` requires the value to be a list if the field
    itself is a list, e.g. `categories__contains=['a', 'b']`.
-   Added support for fields and enum entries that are only supported in
    some EWS versions
-   Added a new field `Item.text_body` which is a read-only version of
    HTML body content, where HTML tags are stripped by the server. Only
    supported from Exchange 2013 and up.
-   Added a new choice `WorkingElsewhere` to the
    `CalendarItem.legacy_free_busy_status` enum. Only supported from
    Exchange 2013 and up.

1.8.1
-----

-   Fix completely botched `Message.from` field renaming in 1.8.0
-   Improve performance of QuerySet slicing and indexing. For example,
    `account.inbox.all()[10]` and `account.inbox.all()[:10]` now only
    fetch 10 items from the server even though `account.inbox.all()`
    could contain thousands of messages.

1.8.0
-----

-   Renamed `Message.from` field to `Message.author`. `from` is a Python
    keyword so `from` could only be accessed as
    `Getattr(my_essage, 'from')` which is just stupid.
-   Make `EWSTimeZone` Windows timezone name translation more robust
-   Add read-only `Message.message_id` which holds the Internet Message
    Id
-   Memory and speed improvements when sorting querysets using
    `order_by()` on a single field.
-   Allow setting `Mailbox` and `Attendee`-type attributes as plain
    strings, e.g.:

    ```python
    calendar_item.organizer =  'anne@example.com'
    calendar_item.required_attendees =  ['john@example.com', 'bill@example.com']

    message.to_recipients =  ['john@example.com', 'anne@example.com']
    ```

1.7.6
-----

-   Bugfix release

1.7.5
-----

-   `Account.fetch()` and `Folder.fetch()` are now generators. They will
    do nothing before being evaluated.
-   Added optional `page_size` attribute to `QuerySet.iterator()` to
    specify the number of items to return per HTTP request for large
    query results. Default `page_size` is 100.
-   Many minor changes to make queries less greedy and return earlier

1.7.4
-----

-   Add Python2 support

1.7.3
-----

-   Implement attachments support. It's now possible to create, delete
    and get attachments connected to any item type:

    ```python
    from exchangelib.folders import FileAttachment, ItemAttachment

    # Process attachments on existing items
    for item in my_folder.all():
        for attachment in item.attachments:
            local_path = os.path.join('/tmp', attachment.name)
            with open(local_path, 'wb') as f:
                f.write(attachment.content)
                print('Saved attachment to', local_path)

    # Create a new item with an attachment
    item = Message(...)
    binary_file_content = 'Hello from unicode æøå'.encode('utf-8')  # Or read from file, BytesIO etc.
    my_file = FileAttachment(name='my_file.txt', content=binary_file_content)
    item.attach(my_file)
    my_calendar_item = CalendarItem(...)
    my_appointment = ItemAttachment(name='my_appointment', item=my_calendar_item)
    item.attach(my_appointment)
    item.save()

    # Add an attachment on an existing item
    my_other_file = FileAttachment(name='my_other_file.txt', content=binary_file_content)
    item.attach(my_other_file)

    # Remove the attachment again
    item.detach(my_file)
    ```

    Be aware that adding and deleting attachments from items that are
    already created in Exchange (items that have an `item_id`) will
    update the `changekey` of the item.

-   Implement `Item.headers` which contains custom Internet
    message headers. Primarily useful for `Message` objects. Read-only
    for now.

1.7.2
-----

-   Implement the `Contact.physical_addresses` attribute. This is a list
    of `exchangelib.folders.PhysicalAddress` items.
-   Implement the `CalendarItem.is_all_day` boolean to create
    all-day appointments.
-   Implement `my_folder.export()` and `my_folder.upload()`. Thanks to
    @SamCB!
-   Fixed `Account.folders` for non-distinguished folders
-   Added `Folder.get_folder_by_name()` to make it easier to get
    sub-folders by name.
-   Implement `CalendarView` searches as
    `my_calendar.view(start=..., end=...)`. A view differs from a normal
    `filter()` in that a view expands recurring items and returns
    recurring item occurrences that are valid in the time span of
    the view.
-   Persistent storage location for autodiscover cache is now platform
    independent
-   Implemented custom extended properties. To add support for your own
    custom property, subclass `exchangelib.folders.ExtendedProperty` and
    call `register()` on the item class you want to use the extended
    property with. When you have registered your extended property, you
    can use it exactly like you would use any other attribute on this
    item type. If you change your mind, you can remove the extended
    property again with `deregister()`:

    ```python
    class LunchMenu(ExtendedProperty):
        property_id = '12345678-1234-1234-1234-123456781234'
        property_name = 'Catering from the cafeteria'
        property_type = 'String'

    CalendarItem.register('lunch_menu', LunchMenu)
    item = CalendarItem(..., lunch_menu='Foie gras et consommé de légumes')
    item.save()
    CalendarItem.deregister('lunch_menu')
    ```

-   Fixed a bug on folder items where an existing HTML body would be
    converted to text when calling `save()`. When creating or updating
    an item body, you can use the two new helper classes
    `exchangelib.Body` and `exchangelib.HTMLBody` to specify if your
    body should be saved as HTML or text. E.g.:

    ```python
    item = CalendarItem(...)
    # Plain-text body
    item.body = Body('Hello UNIX-beard pine user!')
    # Also plain-text body, works as before
    item.body = 'Hello UNIX-beard pine user!'
    # Exchange will see this as an HTML body and display nicely in clients
    item.body = HTMLBody('<html><body>Hello happy <blink>OWA user!</blink></body></html>')
    item.save()
    ```

1.7.1
-----

-   Fix bug where fetching items from a folder that can contain multiple
    item types (e.g. the Deleted Items folder) would only return one
    item type.
-   Added `Item.move(to_folder=...)` that moves an item to another
    folder, and `Item.refresh()` that updates the Item with data
    from EWS.
-   Support reverse sort on individual fields in `order_by()`, e.g.
    `my_folder.all().order_by('subject', '-start')`
-   `Account.bulk_create()` was added to create items that don't need a
    folder, e.g. `Message.send()`
-   `Account.fetch()` was added to fetch items without knowing the
    containing folder.
-   Implemented `SendItem` service to send existing messages.
-   `Folder.bulk_delete()` was moved to `Account.bulk_delete()`
-   `Folder.bulk_update()` was moved to `Account.bulk_update()` and
    changed to expect a list of `(Item, fieldnames)` tuples where Item
    is e.g. a `Message` instance and `fieldnames` is a list of
    attributes names that need updating. E.g.:

    ```python
    items = []
    for i in range(4):
        item = Message(subject='Test %s' % i)
        items.append(item)
    account.sent.bulk_create(items=items)

    item_changes = []
    for i, item in enumerate(items):
        item.subject = 'Changed subject' % i
        item_changes.append(item, ['subject'])
    account.bulk_update(items=item_changes)
    ```

1.7.0
-----

-   Added the `is_service_account` flag to `Credentials`.
    `is_service_account=False` disables the fault-tolerant error
    handling policy and enables immediate failures.
-   `Configuration` now expects a single `credentials` attribute instead
    of separate `username` and `password` attributes.
-   Added support for distinguished folders `Account.trash`,
    `Account.drafts`, `Account.outbox`, `Account.sent` and
    `Account.junk`.
-   Renamed `Folder.find_items()` to `Folder.filter()`
-   Renamed `Folder.add_items()` to `Folder.bulk_create()`
-   Renamed `Folder.update_items()` to `Folder.bulk_update()`
-   Renamed `Folder.delete_items()` to `Folder.bulk_delete()`
-   Renamed `Folder.get_items()` to `Folder.fetch()`
-   Made various policies for message saving, meeting invitation
    sending, conflict resolution, task occurrences and deletion
    available on `bulk_create()`, `bulk_update()` and `bulk_delete()`.
-   Added convenience methods `Item.save()`, `Item.delete()`,
    `Item.soft_delete()`, `Item.move_to_trash()`, and methods
    `Message.send()` and `Message.send_and_save()` that are specific to
    `Message` objects. These methods make it easier to create, update
    and delete single items.
-   Removed `fetch(.., with_extra=True)` in favor of the more
    fine-grained `fetch(.., only_fields=[...])`
-   Added a `QuerySet` class that supports QuerySet-returning methods
    `filter()`, `exclude()`, `only()`, `order_by()`,
    `reverse()``values()` and `values_list()` that all allow
    for chaining. `QuerySet` also has methods `iterator()`, `get()`,
    `count()`, `exists()` and `delete()`. All these methods behave like
    their counterparts in Django.

1.6.2
-----

-   Use of `my_folder.with_extra_fields = True` to get the extra fields
    in `Item.EXTRA_ITEM_FIELDS` is deprecated (it was a kludge anyway).
    Instead, use `my_folder.get_items(ids, with_extra=[True, False])`.
    The default was also changed to `True`, to avoid head-scratching
    with newcomers.

1.6.1
-----

-   Simplify `Q` objects and `Restriction.from_source()` by using Item
    attribute names in expressions and kwargs instead of EWS
    FieldURI values. Change `Folder.find_items()` to accept either a
    search expression, or a list of `Q` objects just like Django
    `filter()` does. E.g.:

    ```python
    ids = account.calendar.find_items(
          "start < '2016-01-02T03:04:05T' and end > '2016-01-01T03:04:05T' and categories in ('foo', 'bar')",
          shape=IdOnly
    )

    q1, q2 = (Q(subject__iexact='foo') | Q(subject__contains='bar')), ~Q(subject__startswith='baz')
    ids = account.calendar.find_items(q1, q2, shape=IdOnly)
    ```

1.6.0
-----

-   Complete rewrite of `Folder.find_items()`. The old `start`, `end`,
    `subject` and `categories` args are deprecated in favor of a Django
    QuerySet filter() syntax. The supported lookup types are `__gt`,
    `__lt`, `__gte`, `__lte`, `__range`, `__in`, `__exact`, `__iexact`,
    `__contains`, `__icontains`, `__contains`, `__icontains`,
    `__startswith`, `__istartswith`, plus an additional `__not` which
    translates to `!=`. Additionally, *all* fields on the item are now
    supported in `Folder.find_items()`.

    **WARNING**: This change is backwards-incompatible! Old uses of
    `Folder.find_items()` like this:

    ```python
    ids = account.calendar.find_items(
        start=tz.localize(EWSDateTime(year, month, day)),
        end=tz.localize(EWSDateTime(year, month, day + 1)),
        categories=['foo', 'bar'],
    )
    ```

    must be rewritten like this:

    ```python
    ids = account.calendar.find_items(
        start__lt=tz.localize(EWSDateTime(year, month, day + 1)),
        end__gt=tz.localize(EWSDateTime(year, month, day)),
        categories__contains=['foo', 'bar'],
    )
    ```

    failing to do so will most likely result in empty or wrong results.

-   Added a `exchangelib.restrictions.Q` class much like Django Q
    objects that can be used to create even more complex filtering. Q
    objects must be passed directly to `exchangelib.services.FindItem`.

1.3.6
-----

-   Don't require sequence arguments to `Folder.*_items()` methods to
    support `len()` (e.g. generators and `map` instances are
    now supported)
-   Allow empty sequences as argument to `Folder.*_items()` methods

1.3.4
-----

-   Add support for `required_attendees`, `optional_attendees` and
    `resources` attribute on `folders.CalendarItem`. These are
    implemented with a new `folders.Attendee` class.

1.3.3
-----

-   Add support for `organizer` attribute on `CalendarItem`. Implemented
    with a new `folders.Mailbox` class.

1.2
---

-   Initial import