[evolution-ews] Microsoft365: Use 'To-Do' API for tasks



commit abe3dbf032f4fa544147b82379d7162de06953e2
Author: Milan Crha <mcrha redhat com>
Date:   Mon Oct 3 17:47:35 2022 +0200

    Microsoft365: Use 'To-Do' API for tasks
    
    It [1] is an official API now, the previous one was a Beta API.
    
    [1] https://learn.microsoft.com/en-us/graph/api/resources/todo-overview?view=graph-rest-1.0

 .../calendar/e-cal-backend-m365-utils.c            |   35 +-
 src/Microsoft365/calendar/e-cal-backend-m365.c     |   12 +-
 src/Microsoft365/common/e-m365-connection.c        | 1164 +++++++++++---------
 src/Microsoft365/common/e-m365-connection.h        |  191 ++--
 src/Microsoft365/common/e-m365-json-utils.c        |  287 +++--
 src/Microsoft365/common/e-m365-json-utils.h        |   94 +-
 src/Microsoft365/registry/e-m365-backend.c         |   52 +-
 7 files changed, 1033 insertions(+), 802 deletions(-)
---
diff --git a/src/Microsoft365/calendar/e-cal-backend-m365-utils.c 
b/src/Microsoft365/calendar/e-cal-backend-m365-utils.c
index 7dacc34d..6829a656 100644
--- a/src/Microsoft365/calendar/e-cal-backend-m365-utils.c
+++ b/src/Microsoft365/calendar/e-cal-backend-m365-utils.c
@@ -507,7 +507,7 @@ ecb_m365_get_subject (EM365Connection *cnc,
                subject = e_m365_event_get_subject (m365_object);
                break;
        case I_CAL_VTODO_COMPONENT:
-               subject = e_m365_task_get_subject (m365_object);
+               subject = e_m365_task_get_title (m365_object);
                break;
        default:
                g_warn_if_reached ();
@@ -539,7 +539,7 @@ ecb_m365_add_subject (EM365Connection *cnc,
                        e_m365_event_add_subject (builder, new_value ? new_value : "");
                        break;
                case I_CAL_VTODO_COMPONENT:
-                       e_m365_task_add_subject (builder, new_value ? new_value : "");
+                       e_m365_task_add_title (builder, new_value ? new_value : "");
                        break;
                default:
                        g_warn_if_reached ();
@@ -627,8 +627,7 @@ ecb_m365_get_sensitivity (EM365Connection *cnc,
                value = e_m365_event_get_sensitivity (m365_object);
                break;
        case I_CAL_VTODO_COMPONENT:
-               value = e_m365_task_get_sensitivity (m365_object);
-               break;
+               return;
        default:
                g_warn_if_reached ();
                return;
@@ -658,6 +657,9 @@ ecb_m365_add_sensitivity (EM365Connection *cnc,
        ICalProperty_Class new_value = I_CAL_CLASS_NONE, old_value = I_CAL_CLASS_NONE;
        ICalProperty *prop;
 
+       if (i_cal_component_isa (new_comp) == I_CAL_VTODO_COMPONENT)
+               return;
+
        prop = i_cal_component_get_first_property (new_comp, prop_kind);
 
        if (prop) {
@@ -687,7 +689,6 @@ ecb_m365_add_sensitivity (EM365Connection *cnc,
                        e_m365_event_add_sensitivity (builder, value);
                        break;
                case I_CAL_VTODO_COMPONENT:
-                       e_m365_task_add_sensitivity (builder, value);
                        break;
                default:
                        g_warn_if_reached ();
@@ -1862,8 +1863,8 @@ ecb_m365_get_reminder (EM365Connection *cnc,
 
                                alarm = e_cal_component_alarm_new ();
                                e_cal_component_alarm_set_action (alarm, E_CAL_COMPONENT_ALARM_DISPLAY);
-                               e_cal_component_alarm_take_summary (alarm, e_cal_component_text_new 
(e_m365_task_get_subject (m365_object), NULL));
-                               e_cal_component_alarm_take_description (alarm, e_cal_component_text_new 
(e_m365_task_get_subject (m365_object), NULL));
+                               e_cal_component_alarm_take_summary (alarm, e_cal_component_text_new 
(e_m365_task_get_title (m365_object), NULL));
+                               e_cal_component_alarm_take_description (alarm, e_cal_component_text_new 
(e_m365_task_get_title (m365_object), NULL));
                                e_cal_component_alarm_take_trigger (alarm, trigger);
 
                                i_cal_component_take_component (inout_comp, 
e_cal_component_alarm_get_as_component (alarm));
@@ -2091,17 +2092,7 @@ ecb_m365_get_attachments (EM365Connection *cnc,
                }
                break;
        case I_CAL_VTODO_COMPONENT:
-               if (!e_m365_task_get_has_attachments (m365_object))
-                       return TRUE;
-
-               id = e_m365_task_get_id (m365_object);
-
-               if (!e_m365_connection_list_task_attachments_sync (cnc, NULL,
-                       group_id, folder_id, id, "id,name,contentType,contentBytes",
-                       &attachments, cancellable, error)) {
-                       return FALSE;
-               }
-               break;
+               return TRUE;
        default:
                g_warn_if_reached ();
                return FALSE;
@@ -2276,9 +2267,11 @@ ecb_m365_add_attachments (EM365Connection *cnc,
                delete_attachment_func = e_m365_connection_delete_event_attachment_sync;
                break;
        case I_CAL_VTODO_COMPONENT:
-               add_attachment_func = e_m365_connection_add_task_attachment_sync;
-               delete_attachment_func = e_m365_connection_delete_task_attachment_sync;
-               break;
+               if (!e_cal_util_component_has_property (new_comp, I_CAL_ATTACH_PROPERTY))
+                       return TRUE;
+               g_set_error (error, E_CLIENT_ERROR, E_CLIENT_ERROR_NOT_SUPPORTED,
+                       _("Microsoft 365 task cannot have attachments."));
+               return FALSE;
        default:
                g_warn_if_reached ();
                return FALSE;
diff --git a/src/Microsoft365/calendar/e-cal-backend-m365.c b/src/Microsoft365/calendar/e-cal-backend-m365.c
index 4802be90..9cad82ba 100644
--- a/src/Microsoft365/calendar/e-cal-backend-m365.c
+++ b/src/Microsoft365/calendar/e-cal-backend-m365.c
@@ -439,6 +439,7 @@ ecb_m365_get_changes_sync (ECalMetaBackend *meta_backend,
                                      GError **error);
        const gchar *(* get_id_func) (JsonObject *item);
        const gchar *(* get_change_key_func) (JsonObject *item);
+       const gchar *select_props = "id,changeKey";
 
        g_return_val_if_fail (E_IS_CAL_BACKEND_M365 (meta_backend), FALSE);
        g_return_val_if_fail (out_new_sync_tag != NULL, FALSE);
@@ -456,7 +457,8 @@ ecb_m365_get_changes_sync (ECalMetaBackend *meta_backend,
        case I_CAL_VTODO_COMPONENT:
                list_items_func = e_m365_connection_list_tasks_sync;
                get_id_func = e_m365_task_get_id;
-               get_change_key_func = e_m365_task_get_change_key;
+               get_change_key_func = e_m365_task_get_last_modified_as_string;
+               select_props = NULL;
                break;
        default:
                g_warn_if_reached ();
@@ -474,10 +476,10 @@ ecb_m365_get_changes_sync (ECalMetaBackend *meta_backend,
 
        LOCK (cbm365);
 
-       full_read = !e_cache_get_count (E_CACHE (cal_cache), E_CACHE_INCLUDE_DELETED, cancellable, NULL);
+       full_read = !select_props || !e_cache_get_count (E_CACHE (cal_cache), E_CACHE_INCLUDE_DELETED, 
cancellable, NULL);
 
        success = list_items_func (cbm365->priv->cnc, NULL, cbm365->priv->group_id, cbm365->priv->folder_id, 
NULL,
-               full_read ? NULL : "id,changeKey", &items, cancellable, error);
+               full_read ? NULL : select_props, &items, cancellable, error);
 
        if (success) {
                GSList *new_ids = NULL; /* const gchar *, borrowed from 'items' objects */
@@ -584,7 +586,7 @@ ecb_m365_load_component_sync (ECalMetaBackend *meta_backend,
        case I_CAL_VTODO_COMPONENT:
                success = e_m365_connection_get_task_sync (cbm365->priv->cnc, NULL, cbm365->priv->group_id,
                        cbm365->priv->folder_id, uid, NULL, NULL, &item, cancellable, error);
-               get_change_key_func = e_m365_task_get_change_key;
+               get_change_key_func = e_m365_task_get_last_modified_as_string;
                break;
        default:
                success = FALSE;
@@ -679,7 +681,7 @@ ecb_m365_save_component_sync (ECalMetaBackend *meta_backend,
                create_item_func = e_m365_connection_create_task_sync;
                update_item_func = e_m365_connection_update_task_sync;
                get_id_func = e_m365_task_get_id;
-               get_change_key_func = e_m365_task_get_change_key;
+               get_change_key_func = e_m365_task_get_last_modified_as_string;
                break;
        default:
                g_warn_if_reached ();
diff --git a/src/Microsoft365/common/e-m365-connection.c b/src/Microsoft365/common/e-m365-connection.c
index 2d62fbf8..cb7c6093 100644
--- a/src/Microsoft365/common/e-m365-connection.c
+++ b/src/Microsoft365/common/e-m365-connection.c
@@ -19,6 +19,7 @@
 #define LOCK(x) g_rec_mutex_lock (&(x->priv->property_lock))
 #define UNLOCK(x) g_rec_mutex_unlock (&(x->priv->property_lock))
 
+#define M365_HOSTNAME "graph.microsoft.com"
 #define M365_RETRY_IO_ERROR_SECONDS 3
 #define X_EVO_M365_DATA "X-EVO-M365-DATA"
 
@@ -1289,7 +1290,7 @@ e_m365_connection_authenticate_sync (EM365Connection *cnc,
                if (!folder_id || !*folder_id)
                        folder_id = "tasks";
 
-               success = e_m365_connection_get_task_folder_sync (cnc, user_override, group_id, folder_id, 
"name", &object, cancellable, error);
+               success = e_m365_connection_get_task_list_sync (cnc, user_override, folder_id, &object, 
cancellable, error);
                break;
        }
 
@@ -1372,7 +1373,7 @@ e_m365_connection_construct_uri (EM365Connection *cnc,
 
        /* https://graph.microsoft.com/v1.0/users/XUSERX/mailFolders */
 
-       g_string_append (uri, "https://graph.microsoft.com";);
+       g_string_append (uri, "https://"; M365_HOSTNAME);
 
        switch (api_version) {
        case E_M365_API_V1_0:
@@ -1663,7 +1664,12 @@ e_m365_connection_batch_request_internal_sync (EM365Connection *cnc,
                        continue;
                }
 
-               use_uri = uri;
+               /* Only the path with the query can be used in the batch request */
+               use_uri = strstr (uri, M365_HOSTNAME);
+               if (use_uri)
+                       use_uri = strchr (use_uri, '/');
+               if (!use_uri)
+                       use_uri = uri;
 
                /* The 'url' is without the API part, it is derived from the main request */
                if (g_str_has_prefix (use_uri, "/v1.0/") ||
@@ -4607,14 +4613,14 @@ e_m365_connection_get_schedule_sync (EM365Connection *cnc,
        return success;
 }
 
-/* https://docs.microsoft.com/en-us/graph/api/outlookuser-list-taskgroups?view=graph-rest-beta&tabs=http */
+/* https://learn.microsoft.com/en-us/graph/api/todo-list-lists?view=graph-rest-1.0&tabs=http */
 
 gboolean
-e_m365_connection_list_task_groups_sync (EM365Connection *cnc,
-                                                const gchar *user_override, /* for which user, NULL to use 
the account user */
-                                                GSList **out_groups, /* EM365TaskGroup * - the returned 
outlookTaskGroup objects */
-                                                GCancellable *cancellable,
-                                                GError **error)
+e_m365_connection_list_task_lists_sync (EM365Connection *cnc,
+                                       const gchar *user_override, /* for which user, NULL to use the 
account user */
+                                       GSList **out_task_lists, /* EM365TaskList * - the returned 
todoTaskList objects */
+                                       GCancellable *cancellable,
+                                       GError **error)
 {
        EM365ResponseData rd;
        SoupMessage *message;
@@ -4622,10 +4628,13 @@ e_m365_connection_list_task_groups_sync (EM365Connection *cnc,
        gboolean success;
 
        g_return_val_if_fail (E_IS_M365_CONNECTION (cnc), FALSE);
-       g_return_val_if_fail (out_groups != NULL, FALSE);
+       g_return_val_if_fail (out_task_lists != NULL, FALSE);
 
-       uri = e_m365_connection_construct_uri (cnc, TRUE, user_override, E_M365_API_BETA, NULL,
-               "outlook", "taskGroups", NULL, NULL);
+       uri = e_m365_connection_construct_uri (cnc, TRUE, user_override, E_M365_API_V1_0, NULL,
+               "todo",
+               "lists",
+               NULL,
+               NULL);
 
        message = m365_connection_new_soup_message (SOUP_METHOD_GET, uri, CSM_DEFAULT, error);
 
@@ -4639,7 +4648,7 @@ e_m365_connection_list_task_groups_sync (EM365Connection *cnc,
 
        memset (&rd, 0, sizeof (EM365ResponseData));
 
-       rd.out_items = out_groups;
+       rd.out_items = out_task_lists;
 
        success = m365_connection_send_request_sync (cnc, message, e_m365_read_valued_response_cb, NULL, &rd, 
cancellable, error);
 
@@ -4648,27 +4657,29 @@ e_m365_connection_list_task_groups_sync (EM365Connection *cnc,
        return success;
 }
 
-/* https://docs.microsoft.com/en-us/graph/api/outlookuser-post-taskgroups?view=graph-rest-beta&tabs=http */
+/* https://learn.microsoft.com/en-us/graph/api/todo-post-lists?view=graph-rest-1.0&tabs=http */
 
 gboolean
-e_m365_connection_create_task_group_sync (EM365Connection *cnc,
-                                         const gchar *user_override, /* for which user, NULL to use the 
account user */
-                                         const gchar *name,
-                                         EM365TaskGroup **out_created_group,
-                                         GCancellable *cancellable,
-                                         GError **error)
+e_m365_connection_create_task_list_sync (EM365Connection *cnc,
+                                        const gchar *user_override, /* for which user, NULL to use the 
account user */
+                                        JsonBuilder *task_list,
+                                        EM365TaskList **out_created_task_list,
+                                        GCancellable *cancellable,
+                                        GError **error)
 {
        SoupMessage *message;
-       JsonBuilder *builder;
        gboolean success;
        gchar *uri;
 
        g_return_val_if_fail (E_IS_M365_CONNECTION (cnc), FALSE);
-       g_return_val_if_fail (name != NULL, FALSE);
-       g_return_val_if_fail (out_created_group != NULL, FALSE);
+       g_return_val_if_fail (task_list != NULL, FALSE);
+       g_return_val_if_fail (out_created_task_list != NULL, FALSE);
 
-       uri = e_m365_connection_construct_uri (cnc, TRUE, user_override, E_M365_API_BETA, NULL,
-               "outlook", "taskGroups", NULL, NULL);
+       uri = e_m365_connection_construct_uri (cnc, TRUE, user_override, E_M365_API_V1_0, NULL,
+               "todo",
+               "lists",
+               NULL,
+               NULL);
 
        message = m365_connection_new_soup_message (SOUP_METHOD_POST, uri, CSM_DEFAULT, error);
 
@@ -4680,45 +4691,37 @@ e_m365_connection_create_task_group_sync (EM365Connection *cnc,
 
        g_free (uri);
 
-       builder = json_builder_new_immutable ();
-
-       e_m365_json_begin_object_member (builder, NULL);
-       e_m365_json_add_string_member (builder, "name", name);
-       e_m365_json_end_object_member (builder);
-
-       e_m365_connection_set_json_body (message, builder);
-
-       g_object_unref (builder);
+       e_m365_connection_set_json_body (message, task_list);
 
-       success = m365_connection_send_request_sync (cnc, message, e_m365_read_json_object_response_cb, NULL, 
out_created_group, cancellable, error);
+       success = m365_connection_send_request_sync (cnc, message, e_m365_read_json_object_response_cb, NULL, 
out_created_task_list, cancellable, error);
 
        g_clear_object (&message);
 
        return success;
 }
 
-/* https://docs.microsoft.com/en-us/graph/api/outlooktaskgroup-get?view=graph-rest-beta&tabs=http */
+/* https://learn.microsoft.com/en-us/graph/api/todotasklist-get?view=graph-rest-1.0&tabs=http */
 
 gboolean
-e_m365_connection_get_task_group_sync (EM365Connection *cnc,
-                                      const gchar *user_override, /* for which user, NULL to use the account 
user */
-                                      const gchar *group_id,
-                                      EM365TaskGroup **out_group,
-                                      GCancellable *cancellable,
-                                      GError **error)
+e_m365_connection_get_task_list_sync (EM365Connection *cnc,
+                                     const gchar *user_override, /* for which user, NULL to use the account 
user */
+                                     const gchar *task_list_id,
+                                     EM365TaskList **out_task_list,
+                                     GCancellable *cancellable,
+                                     GError **error)
 {
        SoupMessage *message;
-       gboolean success;
        gchar *uri;
+       gboolean success;
 
        g_return_val_if_fail (E_IS_M365_CONNECTION (cnc), FALSE);
-       g_return_val_if_fail (group_id != NULL, FALSE);
-       g_return_val_if_fail (out_group != NULL, FALSE);
+       g_return_val_if_fail (task_list_id != NULL, FALSE);
+       g_return_val_if_fail (out_task_list != NULL, FALSE);
 
-       uri = e_m365_connection_construct_uri (cnc, TRUE, user_override, E_M365_API_BETA, NULL,
-               "outlook",
-               "taskGroups",
-               group_id,
+       uri = e_m365_connection_construct_uri (cnc, TRUE, user_override, E_M365_API_V1_0, NULL,
+               "todo",
+               "lists",
+               task_list_id,
                NULL);
 
        message = m365_connection_new_soup_message (SOUP_METHOD_GET, uri, CSM_DEFAULT, error);
@@ -4731,22 +4734,22 @@ e_m365_connection_get_task_group_sync (EM365Connection *cnc,
 
        g_free (uri);
 
-       success = m365_connection_send_request_sync (cnc, message, e_m365_read_json_object_response_cb, NULL, 
out_group, cancellable, error);
+       success = m365_connection_send_request_sync (cnc, message, e_m365_read_json_object_response_cb, NULL, 
out_task_list, cancellable, error);
 
        g_clear_object (&message);
 
        return success;
 }
 
-/* https://docs.microsoft.com/en-us/graph/api/outlooktaskgroup-update?view=graph-rest-beta&tabs=http */
+/* https://learn.microsoft.com/en-us/graph/api/todotasklist-update?view=graph-rest-1.0&tabs=http */
 
 gboolean
-e_m365_connection_update_task_group_sync (EM365Connection *cnc,
-                                         const gchar *user_override, /* for which user, NULL to use the 
account user */
-                                         const gchar *group_id,
-                                         const gchar *name,
-                                         GCancellable *cancellable,
-                                         GError **error)
+e_m365_connection_update_task_list_sync (EM365Connection *cnc,
+                                        const gchar *user_override, /* for which user, NULL to use the 
account user */
+                                        const gchar *task_list_id,
+                                        const gchar *display_name,
+                                        GCancellable *cancellable,
+                                        GError **error)
 {
        SoupMessage *message;
        JsonBuilder *builder;
@@ -4754,14 +4757,13 @@ e_m365_connection_update_task_group_sync (EM365Connection *cnc,
        gchar *uri;
 
        g_return_val_if_fail (E_IS_M365_CONNECTION (cnc), FALSE);
-       g_return_val_if_fail (group_id != NULL, FALSE);
-       g_return_val_if_fail (name != NULL, FALSE);
+       g_return_val_if_fail (task_list_id != NULL, FALSE);
+       g_return_val_if_fail (display_name != NULL, FALSE);
 
-       uri = e_m365_connection_construct_uri (cnc, TRUE, user_override, E_M365_API_BETA, NULL,
-               "outlook",
-               "taskGroups",
-               group_id,
-               NULL,
+       uri = e_m365_connection_construct_uri (cnc, TRUE, user_override, E_M365_API_V1_0, NULL,
+               "todo",
+               "lists",
+               task_list_id,
                NULL);
 
        message = m365_connection_new_soup_message ("PATCH", uri, CSM_DISABLE_RESPONSE, error);
@@ -4777,7 +4779,7 @@ e_m365_connection_update_task_group_sync (EM365Connection *cnc,
        builder = json_builder_new_immutable ();
 
        e_m365_json_begin_object_member (builder, NULL);
-       e_m365_json_add_string_member (builder, "name", name);
+       e_m365_json_add_string_member (builder, "displayName", display_name);
        e_m365_json_end_object_member (builder);
 
        e_m365_connection_set_json_body (message, builder);
@@ -4791,24 +4793,27 @@ e_m365_connection_update_task_group_sync (EM365Connection *cnc,
        return success;
 }
 
-/* https://docs.microsoft.com/en-us/graph/api/outlooktaskfolder-delete?view=graph-rest-beta&tabs=http */
+/* https://learn.microsoft.com/en-us/graph/api/todotasklist-delete?view=graph-rest-1.0&tabs=http */
 
 gboolean
-e_m365_connection_delete_task_group_sync (EM365Connection *cnc,
-                                         const gchar *user_override, /* for which user, NULL to use the 
account user */
-                                         const gchar *group_id,
-                                         GCancellable *cancellable,
-                                         GError **error)
+e_m365_connection_delete_task_list_sync (EM365Connection *cnc,
+                                        const gchar *user_override, /* for which user, NULL to use the 
account user */
+                                        const gchar *task_list_id,
+                                        GCancellable *cancellable,
+                                        GError **error)
 {
        SoupMessage *message;
        gboolean success;
        gchar *uri;
 
        g_return_val_if_fail (E_IS_M365_CONNECTION (cnc), FALSE);
-       g_return_val_if_fail (group_id != NULL, FALSE);
+       g_return_val_if_fail (task_list_id != NULL, FALSE);
 
-       uri = e_m365_connection_construct_uri (cnc, TRUE, user_override, E_M365_API_BETA, NULL,
-               "outlook", "taskGroups", group_id, NULL);
+       uri = e_m365_connection_construct_uri (cnc, TRUE, user_override, E_M365_API_V1_0, NULL,
+               "todo",
+               "lists",
+               task_list_id,
+               NULL);
 
        message = m365_connection_new_soup_message (SOUP_METHOD_DELETE, uri, CSM_DEFAULT, error);
 
@@ -4827,46 +4832,64 @@ e_m365_connection_delete_task_group_sync (EM365Connection *cnc,
        return success;
 }
 
-/* https://docs.microsoft.com/en-us/graph/api/outlookuser-list-taskfolders?view=graph-rest-beta&tabs=http */
+/* https://learn.microsoft.com/en-us/graph/api/todotasklist-delta?view=graph-rest-1.0 */
 
 gboolean
-e_m365_connection_list_task_folders_sync (EM365Connection *cnc,
-                                         const gchar *user_override, /* for which user, NULL to use the 
account user */
-                                         const gchar *group_id, /* nullable, task group id for group task 
folders */
-                                         const gchar *select, /* properties to select, nullable */
-                                         GSList **out_folders, /* EM365TaskFolder * - the returned 
outlookTaskFolder objects */
-                                         GCancellable *cancellable,
-                                         GError **error)
+e_m365_connection_get_task_lists_delta_sync (EM365Connection *cnc,
+                                            const gchar *user_override, /* for which user, NULL to use the 
account user */
+                                            const gchar *delta_link, /* previous delta link */
+                                            guint max_page_size, /* 0 for default by the server */
+                                            EM365ConnectionJsonFunc func, /* function to call with each 
result set */
+                                            gpointer func_user_data, /* user data passed into the 'func' */
+                                            gchar **out_delta_link,
+                                            GCancellable *cancellable,
+                                            GError **error)
 {
        EM365ResponseData rd;
-       SoupMessage *message;
-       gchar *uri;
+       SoupMessage *message = NULL;
        gboolean success;
 
        g_return_val_if_fail (E_IS_M365_CONNECTION (cnc), FALSE);
-       g_return_val_if_fail (out_folders != NULL, FALSE);
+       g_return_val_if_fail (out_delta_link != NULL, FALSE);
+       g_return_val_if_fail (func != NULL, FALSE);
 
-       uri = e_m365_connection_construct_uri (cnc, TRUE, user_override, E_M365_API_BETA, NULL,
-               "outlook",
-               group_id ? "taskGroups" : "taskFolders",
-               group_id,
-               "", group_id ? "taskFolders" : NULL,
-               "$select", select,
-               NULL);
+       if (delta_link) {
+               message = m365_connection_new_soup_message (SOUP_METHOD_GET, delta_link, CSM_DEFAULT, NULL);
+       } else {
+               gchar *uri;
 
-       message = m365_connection_new_soup_message (SOUP_METHOD_GET, uri, CSM_DEFAULT, error);
+               uri = e_m365_connection_construct_uri (cnc, TRUE, user_override, E_M365_API_V1_0, NULL,
+                       "todo",
+                       "lists",
+                       "delta",
+                       NULL);
 
-       if (!message) {
-               g_free (uri);
+               message = m365_connection_new_soup_message (SOUP_METHOD_GET, uri, CSM_DEFAULT, error);
 
-               return FALSE;
+               if (!message) {
+                       g_free (uri);
+
+                       return FALSE;
+               }
+
+               g_free (uri);
        }
 
-       g_free (uri);
+       if (max_page_size > 0) {
+               gchar *prefer_value;
+
+               prefer_value = g_strdup_printf ("odata.maxpagesize=%u", max_page_size);
+
+               soup_message_headers_append (soup_message_get_request_headers (message), "Prefer", 
prefer_value);
+
+               g_free (prefer_value);
+       }
 
        memset (&rd, 0, sizeof (EM365ResponseData));
 
-       rd.out_items = out_folders;
+       rd.json_func = func;
+       rd.func_user_data = func_user_data;
+       rd.out_delta_link = out_delta_link;
 
        success = m365_connection_send_request_sync (cnc, message, e_m365_read_valued_response_cb, NULL, &rd, 
cancellable, error);
 
@@ -4875,33 +4898,37 @@ e_m365_connection_list_task_folders_sync (EM365Connection *cnc,
        return success;
 }
 
-/* https://docs.microsoft.com/en-us/graph/api/outlookuser-post-taskfolders?view=graph-rest-beta&tabs=http */
+/* https://learn.microsoft.com/en-us/graph/api/todotasklist-list-tasks?view=graph-rest-1.0&tabs=http */
 
 gboolean
-e_m365_connection_create_task_folder_sync (EM365Connection *cnc,
-                                          const gchar *user_override, /* for which user, NULL to use the 
account user */
-                                          const gchar *group_id, /* nullable, then the default group is used 
*/
-                                          JsonBuilder *task_folder,
-                                          EM365TaskFolder **out_created_folder,
-                                          GCancellable *cancellable,
-                                          GError **error)
+e_m365_connection_list_tasks_sync (EM365Connection *cnc,
+                                  const gchar *user_override, /* for which user, NULL to use the account 
user */
+                                  const gchar *group_id, /* unused, always NULL */
+                                  const gchar *task_list_id,
+                                  const gchar *prefer_outlook_timezone, /* nullable - then UTC, otherwise 
that zone for the returned times */
+                                  const gchar *select, /* nullable - properties to select */
+                                  GSList **out_tasks, /* EM365Task * - the returned task objects */
+                                  GCancellable *cancellable,
+                                  GError **error)
 {
+       EM365ResponseData rd;
        SoupMessage *message;
-       gboolean success;
        gchar *uri;
+       gboolean success;
 
        g_return_val_if_fail (E_IS_M365_CONNECTION (cnc), FALSE);
-       g_return_val_if_fail (task_folder != NULL, FALSE);
-       g_return_val_if_fail (out_created_folder != NULL, FALSE);
+       g_return_val_if_fail (task_list_id != NULL, FALSE);
+       g_return_val_if_fail (out_tasks != NULL, FALSE);
 
-       uri = e_m365_connection_construct_uri (cnc, TRUE, user_override, E_M365_API_BETA, NULL,
-               "outlook",
-               group_id ? "taskGroups" : "taskFolders",
-               group_id,
-               "", "taskFolders",
+       uri = e_m365_connection_construct_uri (cnc, TRUE, user_override, E_M365_API_V1_0, NULL,
+               "todo",
+               "lists",
+               task_list_id,
+               "", "tasks",
+               "$select", select,
                NULL);
 
-       message = m365_connection_new_soup_message (SOUP_METHOD_POST, uri, CSM_DEFAULT, error);
+       message = m365_connection_new_soup_message (SOUP_METHOD_GET, uri, CSM_DEFAULT, error);
 
        if (!message) {
                g_free (uri);
@@ -4911,54 +4938,49 @@ e_m365_connection_create_task_folder_sync (EM365Connection *cnc,
 
        g_free (uri);
 
-       e_m365_connection_set_json_body (message, task_folder);
+       m365_connection_prefer_outlook_timezone (message, prefer_outlook_timezone);
+       soup_message_headers_append (soup_message_get_request_headers (message), "Prefer", 
"outlook.body-content-type=\"text\"");
+
+       memset (&rd, 0, sizeof (EM365ResponseData));
+
+       rd.out_items = out_tasks;
 
-       success = m365_connection_send_request_sync (cnc, message, e_m365_read_json_object_response_cb, NULL, 
out_created_folder, cancellable, error);
+       success = m365_connection_send_request_sync (cnc, message, e_m365_read_valued_response_cb, NULL, &rd, 
cancellable, error);
 
        g_clear_object (&message);
 
        return success;
 }
 
-/* https://docs.microsoft.com/en-us/graph/api/outlooktaskfolder-get?view=graph-rest-beta&tabs=http */
+/* https://learn.microsoft.com/en-us/graph/api/todotasklist-post-tasks?view=graph-rest-1.0&tabs=http */
 
 gboolean
-e_m365_connection_get_task_folder_sync (EM365Connection *cnc,
-                                       const gchar *user_override, /* for which user, NULL to use the 
account user */
-                                       const gchar *group_id, /* nullable - then the default group is used */
-                                       const gchar *task_folder_id,
-                                       const gchar *select, /* nullable - properties to select */
-                                       EM365TaskFolder **out_task_folder,
-                                       GCancellable *cancellable,
-                                       GError **error)
+e_m365_connection_create_task_sync (EM365Connection *cnc,
+                                   const gchar *user_override, /* for which user, NULL to use the account 
user */
+                                   const gchar *group_id, /* unused, always NULL */
+                                   const gchar *task_list_id,
+                                   JsonBuilder *task,
+                                   EM365Task **out_created_task,
+                                   GCancellable *cancellable,
+                                   GError **error)
 {
        SoupMessage *message;
-       gchar *uri;
        gboolean success;
+       gchar *uri;
 
        g_return_val_if_fail (E_IS_M365_CONNECTION (cnc), FALSE);
-       g_return_val_if_fail (task_folder_id != NULL, FALSE);
-       g_return_val_if_fail (out_task_folder != NULL, FALSE);
+       g_return_val_if_fail (task_list_id != NULL, FALSE);
+       g_return_val_if_fail (task != NULL, FALSE);
+       g_return_val_if_fail (out_created_task != NULL, FALSE);
 
-       if (group_id) {
-               uri = e_m365_connection_construct_uri (cnc, TRUE, user_override, E_M365_API_BETA, NULL,
-                       "outlook",
-                       "taskGroups",
-                       group_id,
-                       "", "taskFolders",
-                       "", task_folder_id,
-                       "$select", select,
-                       NULL);
-       } else {
-               uri = e_m365_connection_construct_uri (cnc, TRUE, user_override, E_M365_API_BETA, NULL,
-                       "outlook",
-                       "taskFolders",
-                       task_folder_id,
-                       "$select", select,
-                       NULL);
-       }
+       uri = e_m365_connection_construct_uri (cnc, TRUE, user_override, E_M365_API_V1_0, NULL,
+               "todo",
+               "lists",
+               task_list_id,
+               "", "tasks",
+               NULL);
 
-       message = m365_connection_new_soup_message (SOUP_METHOD_GET, uri, CSM_DEFAULT, error);
+       message = m365_connection_new_soup_message (SOUP_METHOD_POST, uri, CSM_DEFAULT, error);
 
        if (!message) {
                g_free (uri);
@@ -4968,303 +4990,85 @@ e_m365_connection_get_task_folder_sync (EM365Connection *cnc,
 
        g_free (uri);
 
-       success = m365_connection_send_request_sync (cnc, message, e_m365_read_json_object_response_cb, NULL, 
out_task_folder, cancellable, error);
+       e_m365_connection_set_json_body (message, task);
+
+       success = m365_connection_send_request_sync (cnc, message, e_m365_read_json_object_response_cb, NULL, 
out_created_task, cancellable, error);
 
        g_clear_object (&message);
 
        return success;
 }
 
-/* https://docs.microsoft.com/en-us/graph/api/outlooktaskfolder-update?view=graph-rest-beta&tabs=http */
+/* https://learn.microsoft.com/en-us/graph/api/todotask-get?view=graph-rest-1.0&tabs=http */
 
-gboolean
-e_m365_connection_update_task_folder_sync (EM365Connection *cnc,
-                                          const gchar *user_override, /* for which user, NULL to use the 
account user */
-                                          const gchar *group_id, /* nullable - then the default group is 
used */
-                                          const gchar *task_folder_id,
-                                          const gchar *name,
-                                          GCancellable *cancellable,
-                                          GError **error)
+SoupMessage *
+e_m365_connection_prepare_get_task (EM365Connection *cnc,
+                                   const gchar *user_override, /* for which user, NULL to use the account 
user */
+                                   const gchar *group_id, /* unused, always NULL */
+                                   const gchar *task_list_id,
+                                   const gchar *task_id,
+                                   const gchar *prefer_outlook_timezone, /* nullable - then UTC, otherwise 
that zone for the returned times */
+                                   const gchar *select, /* nullable - properties to select */
+                                   GError **error)
 {
        SoupMessage *message;
-       JsonBuilder *builder;
-       gboolean success;
        gchar *uri;
 
-       g_return_val_if_fail (E_IS_M365_CONNECTION (cnc), FALSE);
-       g_return_val_if_fail (task_folder_id != NULL, FALSE);
-       g_return_val_if_fail (name != NULL, FALSE);
+       g_return_val_if_fail (E_IS_M365_CONNECTION (cnc), NULL);
+       g_return_val_if_fail (task_list_id != NULL, NULL);
+       g_return_val_if_fail (task_id != NULL, NULL);
 
-       if (group_id) {
-               uri = e_m365_connection_construct_uri (cnc, TRUE, user_override, E_M365_API_BETA, NULL,
-                       "outlook",
-                       "taskGroups",
-                       group_id,
-                       "", "taskFolders",
-                       "", task_folder_id,
-                       NULL);
-       } else {
-               uri = e_m365_connection_construct_uri (cnc, TRUE, user_override, E_M365_API_BETA, NULL,
-                       "outlook",
-                       "taskFolders",
-                       task_folder_id,
-                       NULL);
-       }
+       uri = e_m365_connection_construct_uri (cnc, TRUE, user_override, E_M365_API_V1_0, NULL,
+               "todo",
+               "lists",
+               task_list_id,
+               "", "tasks",
+               "", task_id,
+               "$select", select,
+               NULL);
 
-       message = m365_connection_new_soup_message ("PATCH", uri, CSM_DISABLE_RESPONSE, error);
+       message = m365_connection_new_soup_message (SOUP_METHOD_GET, uri, CSM_DEFAULT, error);
 
        if (!message) {
                g_free (uri);
 
-               return FALSE;
+               return NULL;
        }
 
        g_free (uri);
 
-       builder = json_builder_new_immutable ();
-
-       e_m365_json_begin_object_member (builder, NULL);
-       e_m365_json_add_string_member (builder, "name", name);
-       e_m365_json_end_object_member (builder);
-
-       e_m365_connection_set_json_body (message, builder);
-
-       g_object_unref (builder);
-
-       success = m365_connection_send_request_sync (cnc, message, NULL, e_m365_read_no_response_cb, NULL, 
cancellable, error);
-
-       g_clear_object (&message);
+       m365_connection_prefer_outlook_timezone (message, prefer_outlook_timezone);
+       soup_message_headers_append (soup_message_get_request_headers (message), "Prefer", 
"outlook.body-content-type=\"text\"");
 
-       return success;
+       return message;
 }
 
-/* https://docs.microsoft.com/en-us/graph/api/outlooktaskfolder-delete?view=graph-rest-beta&tabs=http */
-
 gboolean
-e_m365_connection_delete_task_folder_sync (EM365Connection *cnc,
-                                          const gchar *user_override, /* for which user, NULL to use the 
account user */
-                                          const gchar *group_id, /* nullable - then the default group is 
used */
-                                          const gchar *task_folder_id,
-                                          GCancellable *cancellable,
-                                          GError **error)
+e_m365_connection_get_task_sync (EM365Connection *cnc,
+                                const gchar *user_override, /* for which user, NULL to use the account user 
*/
+                                const gchar *group_id, /* unused, always NULL */
+                                const gchar *task_list_id,
+                                const gchar *task_id,
+                                const gchar *prefer_outlook_timezone, /* nullable - then UTC, otherwise that 
zone for the returned times */
+                                const gchar *select, /* nullable - properties to select */
+                                EM365Task **out_task,
+                                GCancellable *cancellable,
+                                GError **error)
 {
        SoupMessage *message;
        gboolean success;
-       gchar *uri;
 
        g_return_val_if_fail (E_IS_M365_CONNECTION (cnc), FALSE);
-       g_return_val_if_fail (task_folder_id != NULL, FALSE);
-
-       if (group_id) {
-               uri = e_m365_connection_construct_uri (cnc, TRUE, user_override, E_M365_API_BETA, NULL,
-                       "outlook",
-                       "taskGroups",
-                       group_id,
-                       "", "taskFolders",
-                       "", task_folder_id,
-                       NULL);
-       } else {
-               uri = e_m365_connection_construct_uri (cnc, TRUE, user_override, E_M365_API_BETA, NULL,
-                       "outlook",
-                       "taskFolders",
-                       task_folder_id,
-                       NULL);
-       }
-
-       message = m365_connection_new_soup_message (SOUP_METHOD_DELETE, uri, CSM_DEFAULT, error);
+       g_return_val_if_fail (task_list_id != NULL, FALSE);
+       g_return_val_if_fail (task_id != NULL, FALSE);
+       g_return_val_if_fail (out_task != NULL, FALSE);
 
-       if (!message) {
-               g_free (uri);
+       message = e_m365_connection_prepare_get_task (cnc, user_override, group_id, task_list_id, task_id, 
prefer_outlook_timezone, select, error);
 
+       if (!message)
                return FALSE;
-       }
 
-       g_free (uri);
-
-       success = m365_connection_send_request_sync (cnc, message, NULL, e_m365_read_no_response_cb, NULL, 
cancellable, error);
-
-       g_clear_object (&message);
-
-       return success;
-}
-
-/* https://docs.microsoft.com/en-us/graph/api/outlooktaskfolder-list-tasks?view=graph-rest-beta&tabs=http */
-
-gboolean
-e_m365_connection_list_tasks_sync (EM365Connection *cnc,
-                                  const gchar *user_override, /* for which user, NULL to use the account 
user */
-                                  const gchar *group_id, /* nullable, task group id for group task folders */
-                                  const gchar *task_folder_id,
-                                  const gchar *prefer_outlook_timezone, /* nullable - then UTC, otherwise 
that zone for the returned times */
-                                  const gchar *select, /* nullable - properties to select */
-                                  GSList **out_tasks, /* EM365Task * - the returned task objects */
-                                  GCancellable *cancellable,
-                                  GError **error)
-{
-       EM365ResponseData rd;
-       SoupMessage *message;
-       gchar *uri;
-       gboolean success;
-
-       g_return_val_if_fail (E_IS_M365_CONNECTION (cnc), FALSE);
-       g_return_val_if_fail (task_folder_id != NULL, FALSE);
-       g_return_val_if_fail (out_tasks != NULL, FALSE);
-
-       uri = e_m365_connection_construct_uri (cnc, TRUE, user_override, E_M365_API_BETA, NULL,
-               "outlook",
-               group_id ? "taskGroups" : "taskFolders",
-               group_id,
-               "", group_id ? "taskFolders" : NULL,
-               "", task_folder_id,
-               "", "tasks",
-               "$select", select,
-               NULL);
-
-       message = m365_connection_new_soup_message (SOUP_METHOD_GET, uri, CSM_DEFAULT, error);
-
-       if (!message) {
-               g_free (uri);
-
-               return FALSE;
-       }
-
-       g_free (uri);
-
-       m365_connection_prefer_outlook_timezone (message, prefer_outlook_timezone);
-       soup_message_headers_append (soup_message_get_request_headers (message), "Prefer", 
"outlook.body-content-type=\"text\"");
-
-       memset (&rd, 0, sizeof (EM365ResponseData));
-
-       rd.out_items = out_tasks;
-
-       success = m365_connection_send_request_sync (cnc, message, e_m365_read_valued_response_cb, NULL, &rd, 
cancellable, error);
-
-       g_clear_object (&message);
-
-       return success;
-}
-
-/* https://docs.microsoft.com/en-us/graph/api/outlookuser-post-tasks?view=graph-rest-beta&tabs=csharp */
-
-gboolean
-e_m365_connection_create_task_sync (EM365Connection *cnc,
-                                   const gchar *user_override, /* for which user, NULL to use the account 
user */
-                                   const gchar *group_id, /* nullable, then the default group is used */
-                                   const gchar *task_folder_id,
-                                   JsonBuilder *task,
-                                   EM365Task **out_created_task,
-                                   GCancellable *cancellable,
-                                   GError **error)
-{
-       SoupMessage *message;
-       gboolean success;
-       gchar *uri;
-
-       g_return_val_if_fail (E_IS_M365_CONNECTION (cnc), FALSE);
-       g_return_val_if_fail (task_folder_id != NULL, FALSE);
-       g_return_val_if_fail (task != NULL, FALSE);
-       g_return_val_if_fail (out_created_task != NULL, FALSE);
-
-       uri = e_m365_connection_construct_uri (cnc, TRUE, user_override, E_M365_API_BETA, NULL,
-               "outlook",
-               group_id ? "taskGroups" : "taskFolders",
-               group_id,
-               "", group_id ? "taskFolders" : NULL,
-               "", task_folder_id,
-               "", "tasks",
-               NULL);
-
-       message = m365_connection_new_soup_message (SOUP_METHOD_POST, uri, CSM_DEFAULT, error);
-
-       if (!message) {
-               g_free (uri);
-
-               return FALSE;
-       }
-
-       g_free (uri);
-
-       e_m365_connection_set_json_body (message, task);
-
-       success = m365_connection_send_request_sync (cnc, message, e_m365_read_json_object_response_cb, NULL, 
out_created_task, cancellable, error);
-
-       g_clear_object (&message);
-
-       return success;
-}
-
-/* https://docs.microsoft.com/en-us/graph/api/outlooktask-get?view=graph-rest-beta&tabs=http */
-
-SoupMessage *
-e_m365_connection_prepare_get_task (EM365Connection *cnc,
-                                   const gchar *user_override, /* for which user, NULL to use the account 
user */
-                                   const gchar *group_id, /* nullable, then the default group is used */
-                                   const gchar *task_folder_id,
-                                   const gchar *task_id,
-                                   const gchar *prefer_outlook_timezone, /* nullable - then UTC, otherwise 
that zone for the returned times */
-                                   const gchar *select, /* nullable - properties to select */
-                                   GError **error)
-{
-       SoupMessage *message;
-       gchar *uri;
-
-       g_return_val_if_fail (E_IS_M365_CONNECTION (cnc), NULL);
-       g_return_val_if_fail (task_folder_id != NULL, NULL);
-       g_return_val_if_fail (task_id != NULL, NULL);
-
-       uri = e_m365_connection_construct_uri (cnc, TRUE, user_override, E_M365_API_BETA, NULL,
-               "outlook",
-               group_id ? "taskGroups" : "taskFolders",
-               group_id,
-               "", group_id ? "taskFolders" : NULL,
-               "", task_folder_id,
-               "", "tasks",
-               "", task_id,
-               "$select", select,
-               NULL);
-
-       message = m365_connection_new_soup_message (SOUP_METHOD_GET, uri, CSM_DEFAULT, error);
-
-       if (!message) {
-               g_free (uri);
-
-               return NULL;
-       }
-
-       g_free (uri);
-
-       m365_connection_prefer_outlook_timezone (message, prefer_outlook_timezone);
-       soup_message_headers_append (soup_message_get_request_headers (message), "Prefer", 
"outlook.body-content-type=\"text\"");
-
-       return message;
-}
-
-gboolean
-e_m365_connection_get_task_sync (EM365Connection *cnc,
-                                const gchar *user_override, /* for which user, NULL to use the account user 
*/
-                                const gchar *group_id, /* nullable, then the default group is used */
-                                const gchar *task_folder_id,
-                                const gchar *task_id,
-                                const gchar *prefer_outlook_timezone, /* nullable - then UTC, otherwise that 
zone for the returned times */
-                                const gchar *select, /* nullable - properties to select */
-                                EM365Task **out_task,
-                                GCancellable *cancellable,
-                                GError **error)
-{
-       SoupMessage *message;
-       gboolean success;
-
-       g_return_val_if_fail (E_IS_M365_CONNECTION (cnc), FALSE);
-       g_return_val_if_fail (task_folder_id != NULL, FALSE);
-       g_return_val_if_fail (task_id != NULL, FALSE);
-       g_return_val_if_fail (out_task != NULL, FALSE);
-
-       message = e_m365_connection_prepare_get_task (cnc, user_override, group_id, task_folder_id, task_id, 
prefer_outlook_timezone, select, error);
-
-       if (!message)
-               return FALSE;
-
-       success = m365_connection_send_request_sync (cnc, message, e_m365_read_json_object_response_cb, NULL, 
out_task, cancellable, error);
+       success = m365_connection_send_request_sync (cnc, message, e_m365_read_json_object_response_cb, NULL, 
out_task, cancellable, error);
 
        g_clear_object (&message);
 
@@ -5274,8 +5078,8 @@ e_m365_connection_get_task_sync (EM365Connection *cnc,
 gboolean
 e_m365_connection_get_tasks_sync (EM365Connection *cnc,
                                  const gchar *user_override, /* for which user, NULL to use the account user 
*/
-                                 const gchar *group_id, /* nullable, then the default group is used */
-                                 const gchar *task_folder_id,
+                                 const gchar *group_id, /* unused, always NULL */
+                                 const gchar *task_list_id,
                                  const GSList *task_ids, /* const gchar * */
                                  const gchar *prefer_outlook_timezone, /* nullable - then UTC, otherwise 
that zone for the returned times */
                                  const gchar *select, /* nullable - properties to select */
@@ -5286,7 +5090,7 @@ e_m365_connection_get_tasks_sync (EM365Connection *cnc,
        gboolean success = TRUE;
 
        g_return_val_if_fail (E_IS_M365_CONNECTION (cnc), FALSE);
-       g_return_val_if_fail (task_folder_id != NULL, FALSE);
+       g_return_val_if_fail (task_list_id != NULL, FALSE);
        g_return_val_if_fail (task_ids != NULL, FALSE);
        g_return_val_if_fail (out_tasks != NULL, FALSE);
 
@@ -5302,7 +5106,7 @@ e_m365_connection_get_tasks_sync (EM365Connection *cnc,
                        const gchar *id = link->data;
                        SoupMessage *message;
 
-                       message = e_m365_connection_prepare_get_task (cnc, user_override, group_id, 
task_folder_id, id, prefer_outlook_timezone, select, error);
+                       message = e_m365_connection_prepare_get_task (cnc, user_override, group_id, 
task_list_id, id, prefer_outlook_timezone, select, error);
 
                        if (!message) {
                                success = FALSE;
@@ -5320,7 +5124,7 @@ e_m365_connection_get_tasks_sync (EM365Connection *cnc,
                                        if (success)
                                                *out_tasks = g_slist_prepend (*out_tasks, task);
                                } else {
-                                       success = e_m365_connection_batch_request_sync (cnc, E_M365_API_BETA, 
requests, cancellable, error);
+                                       success = e_m365_connection_batch_request_sync (cnc, E_M365_API_V1_0, 
requests, cancellable, error);
 
                                        if (success) {
                                                guint ii;
@@ -5363,7 +5167,7 @@ e_m365_connection_get_tasks_sync (EM365Connection *cnc,
        } else {
                SoupMessage *message;
 
-               message = e_m365_connection_prepare_get_task (cnc, user_override, group_id, task_folder_id, 
task_ids->data, prefer_outlook_timezone, select, error);
+               message = e_m365_connection_prepare_get_task (cnc, user_override, group_id, task_list_id, 
task_ids->data, prefer_outlook_timezone, select, error);
 
                if (message) {
                        EM365Task *task = NULL;
@@ -5384,13 +5188,13 @@ e_m365_connection_get_tasks_sync (EM365Connection *cnc,
        return success;
 }
 
-/* https://docs.microsoft.com/en-us/graph/api/outlooktask-update?view=graph-rest-beta&tabs=http */
+/* https://learn.microsoft.com/en-us/graph/api/todotask-update?view=graph-rest-1.0&tabs=http */
 
 gboolean
 e_m365_connection_update_task_sync (EM365Connection *cnc,
                                    const gchar *user_override, /* for which user, NULL to use the account 
user */
-                                   const gchar *group_id, /* nullable - then the default group is used */
-                                   const gchar *task_folder_id,
+                                   const gchar *group_id, /* unused, always NULL */
+                                   const gchar *task_list_id,
                                    const gchar *task_id,
                                    JsonBuilder *task,
                                    GCancellable *cancellable,
@@ -5401,16 +5205,14 @@ e_m365_connection_update_task_sync (EM365Connection *cnc,
        gchar *uri;
 
        g_return_val_if_fail (E_IS_M365_CONNECTION (cnc), FALSE);
-       g_return_val_if_fail (task_folder_id != NULL, FALSE);
+       g_return_val_if_fail (task_list_id != NULL, FALSE);
        g_return_val_if_fail (task_id != NULL, FALSE);
        g_return_val_if_fail (task != NULL, FALSE);
 
-       uri = e_m365_connection_construct_uri (cnc, TRUE, user_override, E_M365_API_BETA, NULL,
-               "outlook",
-               group_id ? "taskGroups" : "taskFolders",
-               group_id,
-               "", group_id ? "taskFolders" : NULL,
-               "", task_folder_id,
+       uri = e_m365_connection_construct_uri (cnc, TRUE, user_override, E_M365_API_V1_0, NULL,
+               "todo",
+               "lists",
+               task_list_id,
                "", "tasks",
                "", task_id,
                NULL);
@@ -5434,13 +5236,13 @@ e_m365_connection_update_task_sync (EM365Connection *cnc,
        return success;
 }
 
-/* https://docs.microsoft.com/en-us/graph/api/outlooktask-delete?view=graph-rest-beta&tabs=http */
+/* https://learn.microsoft.com/en-us/graph/api/todotask-delete?view=graph-rest-1.0&tabs=http */
 
 gboolean
 e_m365_connection_delete_task_sync (EM365Connection *cnc,
                                    const gchar *user_override, /* for which user, NULL to use the account 
user */
-                                   const gchar *group_id, /* nullable - then the default group is used */
-                                   const gchar *task_folder_id,
+                                   const gchar *group_id, /* unused, always NULL */
+                                   const gchar *task_list_id,
                                    const gchar *task_id,
                                    GCancellable *cancellable,
                                    GError **error)
@@ -5450,15 +5252,13 @@ e_m365_connection_delete_task_sync (EM365Connection *cnc,
        gchar *uri;
 
        g_return_val_if_fail (E_IS_M365_CONNECTION (cnc), FALSE);
-       g_return_val_if_fail (task_folder_id != NULL, FALSE);
+       g_return_val_if_fail (task_list_id != NULL, FALSE);
        g_return_val_if_fail (task_id != NULL, FALSE);
 
-       uri = e_m365_connection_construct_uri (cnc, TRUE, user_override, E_M365_API_BETA, NULL,
-               "outlook",
-               group_id ? "taskGroups" : "taskFolders",
-               group_id,
-               "", group_id ? "taskFolders" : NULL,
-               "", task_folder_id,
+       uri = e_m365_connection_construct_uri (cnc, TRUE, user_override, E_M365_API_V1_0, NULL,
+               "todo",
+               "lists",
+               task_list_id,
                "", "tasks",
                "", task_id,
                NULL);
@@ -5480,37 +5280,310 @@ e_m365_connection_delete_task_sync (EM365Connection *cnc,
        return success;
 }
 
-/* https://docs.microsoft.com/en-us/graph/api/outlooktask-complete?view=graph-rest-beta */
+/* https://learn.microsoft.com/en-us/graph/api/todotask-delta?view=graph-rest-1.0 */
 
 gboolean
-e_m365_connection_complete_task_sync (EM365Connection *cnc,
-                                     const gchar *user_override, /* for which user, NULL to use the account 
user */
-                                     const gchar *group_id, /* nullable - then the default group is used */
-                                     const gchar *task_folder_id,
-                                     const gchar *task_id,
-                                     GCancellable *cancellable,
-                                     GError **error)
+e_m365_connection_get_tasks_delta_sync (EM365Connection *cnc,
+                                       const gchar *user_override, /* for which user, NULL to use the 
account user */
+                                       const gchar *task_list_id,
+                                       const gchar *delta_link, /* previous delta link */
+                                       guint max_page_size, /* 0 for default by the server */
+                                       EM365ConnectionJsonFunc func, /* function to call with each result 
set */
+                                       gpointer func_user_data, /* user data passed into the 'func' */
+                                       gchar **out_delta_link,
+                                       GCancellable *cancellable,
+                                       GError **error)
+{
+       EM365ResponseData rd;
+       SoupMessage *message = NULL;
+       gboolean success;
+
+       g_return_val_if_fail (E_IS_M365_CONNECTION (cnc), FALSE);
+       g_return_val_if_fail (task_list_id != NULL, FALSE);
+       g_return_val_if_fail (out_delta_link != NULL, FALSE);
+       g_return_val_if_fail (func != NULL, FALSE);
+
+       if (delta_link) {
+               message = m365_connection_new_soup_message (SOUP_METHOD_GET, delta_link, CSM_DEFAULT, NULL);
+       } else {
+               gchar *uri;
+
+               uri = e_m365_connection_construct_uri (cnc, TRUE, user_override, E_M365_API_V1_0, NULL,
+                       "todo",
+                       "lists",
+                       "task_list_id",
+                       "", "tasks",
+                       "", "delta",
+                       NULL);
+
+               message = m365_connection_new_soup_message (SOUP_METHOD_GET, uri, CSM_DEFAULT, error);
+
+               if (!message) {
+                       g_free (uri);
+
+                       return FALSE;
+               }
+
+               g_free (uri);
+       }
+
+       if (max_page_size > 0) {
+               gchar *prefer_value;
+
+               prefer_value = g_strdup_printf ("odata.maxpagesize=%u", max_page_size);
+
+               soup_message_headers_append (soup_message_get_request_headers (message), "Prefer", 
prefer_value);
+
+               g_free (prefer_value);
+       }
+
+       memset (&rd, 0, sizeof (EM365ResponseData));
+
+       rd.json_func = func;
+       rd.func_user_data = func_user_data;
+       rd.out_delta_link = out_delta_link;
+
+       success = m365_connection_send_request_sync (cnc, message, e_m365_read_valued_response_cb, NULL, &rd, 
cancellable, error);
+
+       g_clear_object (&message);
+
+       return success;
+}
+
+/* https://learn.microsoft.com/en-us/graph/api/todotask-list-checklistitems?view=graph-rest-1.0&tabs=http */
+
+gboolean
+e_m365_connection_list_checklist_items_sync (EM365Connection *cnc,
+                                            const gchar *user_override, /* for which user, NULL to use the 
account user */
+                                            const gchar *task_list_id,
+                                            const gchar *task_id,
+                                            const gchar *select, /* nullable - properties to select */
+                                            GSList **out_items, /* EM365ChecklistItem * */
+                                            GCancellable *cancellable,
+                                            GError **error)
+{
+       EM365ResponseData rd;
+       SoupMessage *message;
+       gchar *uri;
+       gboolean success;
+
+       g_return_val_if_fail (E_IS_M365_CONNECTION (cnc), FALSE);
+       g_return_val_if_fail (task_list_id != NULL, FALSE);
+       g_return_val_if_fail (task_id != NULL, FALSE);
+       g_return_val_if_fail (out_items != NULL, FALSE);
+
+       uri = e_m365_connection_construct_uri (cnc, TRUE, user_override, E_M365_API_V1_0, NULL,
+               "todo",
+               "lists",
+               task_list_id,
+               "", "tasks",
+               "", task_id,
+               "", "checklistItems",
+               "$select", select,
+               NULL);
+
+       message = m365_connection_new_soup_message (SOUP_METHOD_GET, uri, CSM_DEFAULT, error);
+
+       if (!message) {
+               g_free (uri);
+
+               return FALSE;
+       }
+
+       g_free (uri);
+
+       memset (&rd, 0, sizeof (EM365ResponseData));
+
+       rd.out_items = out_items;
+
+       success = m365_connection_send_request_sync (cnc, message, e_m365_read_valued_response_cb, NULL, &rd, 
cancellable, error);
+
+       g_clear_object (&message);
+
+       return success;
+}
+
+/* https://learn.microsoft.com/en-us/graph/api/checklistitem-get?view=graph-rest-1.0&tabs=http */
+
+gboolean
+e_m365_connection_get_checklist_item_sync (EM365Connection *cnc,
+                                          const gchar *user_override, /* for which user, NULL to use the 
account user */
+                                          const gchar *task_list_id,
+                                          const gchar *task_id,
+                                          const gchar *item_id,
+                                          EM365ChecklistItem **out_item, /* nullable */
+                                          GCancellable *cancellable,
+                                          GError **error)
+{
+       SoupMessage *message;
+       gchar *uri;
+       gboolean success;
+
+       g_return_val_if_fail (E_IS_M365_CONNECTION (cnc), FALSE);
+       g_return_val_if_fail (task_list_id != NULL, FALSE);
+       g_return_val_if_fail (task_id != NULL, FALSE);
+       g_return_val_if_fail (item_id != NULL, FALSE);
+       g_return_val_if_fail (out_item != NULL, FALSE);
+
+       uri = e_m365_connection_construct_uri (cnc, TRUE, user_override, E_M365_API_V1_0, NULL,
+               "todo",
+               "lists",
+               task_list_id,
+               "", "tasks",
+               "", task_id,
+               "", "checklistItems",
+               "", item_id,
+               NULL);
+
+       message = m365_connection_new_soup_message (SOUP_METHOD_GET, uri, CSM_DEFAULT, error);
+
+       if (!message) {
+               g_free (uri);
+
+               return FALSE;
+       }
+
+       g_free (uri);
+
+       success = m365_connection_send_request_sync (cnc, message, e_m365_read_json_object_response_cb, NULL, 
out_item, cancellable, error);
+
+       g_clear_object (&message);
+
+       return success;
+}
+
+/* https://learn.microsoft.com/en-us/graph/api/todotask-post-checklistitems?view=graph-rest-1.0&tabs=http */
+
+gboolean
+e_m365_connection_create_checklist_item_sync (EM365Connection *cnc,
+                                             const gchar *user_override, /* for which user, NULL to use the 
account user */
+                                             const gchar *task_list_id,
+                                             const gchar *task_id,
+                                             JsonBuilder *in_item,
+                                             EM365ChecklistItem **out_item, /* nullable */
+                                             GCancellable *cancellable,
+                                             GError **error)
 {
        SoupMessage *message;
        gboolean success;
        gchar *uri;
 
        g_return_val_if_fail (E_IS_M365_CONNECTION (cnc), FALSE);
-       g_return_val_if_fail (task_folder_id != NULL, FALSE);
+       g_return_val_if_fail (task_list_id != NULL, FALSE);
        g_return_val_if_fail (task_id != NULL, FALSE);
+       g_return_val_if_fail (in_item != NULL, FALSE);
 
-       uri = e_m365_connection_construct_uri (cnc, TRUE, user_override, E_M365_API_BETA, NULL,
-               "outlook",
-               group_id ? "taskGroups" : "taskFolders",
-               group_id,
-               "", group_id ? "taskFolders" : NULL,
-               "", task_folder_id,
+       uri = e_m365_connection_construct_uri (cnc, TRUE, user_override, E_M365_API_V1_0, NULL,
+               "todo",
+               "lists",
+               task_list_id,
                "", "tasks",
                "", task_id,
-               "", "complete",
+               "", "checklistItems",
                NULL);
 
-       message = m365_connection_new_soup_message (SOUP_METHOD_POST, uri, CSM_DISABLE_RESPONSE, error);
+       message = m365_connection_new_soup_message (SOUP_METHOD_POST, uri, out_item ? CSM_DEFAULT : 
CSM_DISABLE_RESPONSE, error);
+
+       if (!message) {
+               g_free (uri);
+
+               return FALSE;
+       }
+
+       g_free (uri);
+
+       e_m365_connection_set_json_body (message, in_item);
+
+       success = m365_connection_send_request_sync (cnc, message, out_item ? 
e_m365_read_json_object_response_cb : NULL,
+               out_item ? NULL : e_m365_read_no_response_cb, out_item, cancellable, error);
+
+       g_clear_object (&message);
+
+       return success;
+}
+
+/* https://learn.microsoft.com/en-us/graph/api/checklistitem-update?view=graph-rest-1.0&tabs=http */
+
+gboolean
+e_m365_connection_update_checklist_item_sync (EM365Connection *cnc,
+                                             const gchar *user_override, /* for which user, NULL to use the 
account user */
+                                             const gchar *task_list_id,
+                                             const gchar *task_id,
+                                             const gchar *item_id,
+                                             JsonBuilder *in_item,
+                                             GCancellable *cancellable,
+                                             GError **error)
+{
+       SoupMessage *message;
+       gboolean success;
+       gchar *uri;
+
+       g_return_val_if_fail (E_IS_M365_CONNECTION (cnc), FALSE);
+       g_return_val_if_fail (task_list_id != NULL, FALSE);
+       g_return_val_if_fail (task_id != NULL, FALSE);
+       g_return_val_if_fail (item_id != NULL, FALSE);
+       g_return_val_if_fail (in_item != NULL, FALSE);
+
+       uri = e_m365_connection_construct_uri (cnc, TRUE, user_override, E_M365_API_V1_0, NULL,
+               "todo",
+               "lists",
+               task_list_id,
+               "", "tasks",
+               "", task_id,
+               "", "checklistItems",
+               "", item_id,
+               NULL);
+
+       message = m365_connection_new_soup_message ("PATCH", uri, CSM_DISABLE_RESPONSE, error);
+
+       if (!message) {
+               g_free (uri);
+
+               return FALSE;
+       }
+
+       g_free (uri);
+
+       e_m365_connection_set_json_body (message, in_item);
+
+       success = m365_connection_send_request_sync (cnc, message, NULL, e_m365_read_no_response_cb, NULL, 
cancellable, error);
+
+       g_clear_object (&message);
+
+       return success;
+}
+
+/* https://learn.microsoft.com/en-us/graph/api/checklistitem-delete?view=graph-rest-1.0&tabs=http */
+
+gboolean
+e_m365_connection_delete_checklist_item_sync (EM365Connection *cnc,
+                                             const gchar *user_override, /* for which user, NULL to use the 
account user */
+                                             const gchar *task_list_id,
+                                             const gchar *task_id,
+                                             const gchar *item_id,
+                                             GCancellable *cancellable,
+                                             GError **error)
+{
+       SoupMessage *message;
+       gboolean success;
+       gchar *uri;
+
+       g_return_val_if_fail (E_IS_M365_CONNECTION (cnc), FALSE);
+       g_return_val_if_fail (task_list_id != NULL, FALSE);
+       g_return_val_if_fail (task_id != NULL, FALSE);
+       g_return_val_if_fail (item_id != NULL, FALSE);
+
+       uri = e_m365_connection_construct_uri (cnc, TRUE, user_override, E_M365_API_V1_0, NULL,
+               "todo",
+               "lists",
+               task_list_id,
+               "", "tasks",
+               "", task_id,
+               "", "checklistItems",
+               "", item_id,
+               NULL);
+
+       message = m365_connection_new_soup_message (SOUP_METHOD_DELETE, uri, CSM_DEFAULT, error);
 
        if (!message) {
                g_free (uri);
@@ -5527,16 +5600,15 @@ e_m365_connection_complete_task_sync (EM365Connection *cnc,
        return success;
 }
 
-/* https://docs.microsoft.com/en-us/graph/api/outlooktask-list-attachments?view=graph-rest-beta&tabs=http */
+/* https://learn.microsoft.com/en-us/graph/api/todotask-list-linkedresources?view=graph-rest-1.0&tabs=http */
 
 gboolean
-e_m365_connection_list_task_attachments_sync (EM365Connection *cnc,
+e_m365_connection_list_linked_resources_sync (EM365Connection *cnc,
                                              const gchar *user_override, /* for which user, NULL to use the 
account user */
-                                             const gchar *group_id, /* nullable, then the default group is 
used */
-                                             const gchar *task_folder_id,
+                                             const gchar *task_list_id,
                                              const gchar *task_id,
                                              const gchar *select, /* nullable - properties to select */
-                                             GSList **out_attachments, /* EM365Attachment * */
+                                             GSList **out_resources, /* EM365LinkedResource * */
                                              GCancellable *cancellable,
                                              GError **error)
 {
@@ -5546,19 +5618,17 @@ e_m365_connection_list_task_attachments_sync (EM365Connection *cnc,
        gboolean success;
 
        g_return_val_if_fail (E_IS_M365_CONNECTION (cnc), FALSE);
-       g_return_val_if_fail (task_folder_id != NULL, FALSE);
+       g_return_val_if_fail (task_list_id != NULL, FALSE);
        g_return_val_if_fail (task_id != NULL, FALSE);
-       g_return_val_if_fail (out_attachments != NULL, FALSE);
+       g_return_val_if_fail (out_resources != NULL, FALSE);
 
-       uri = e_m365_connection_construct_uri (cnc, TRUE, user_override, E_M365_API_BETA, NULL,
-               "outlook",
-               group_id ? "taskGroups" : "taskFolders",
-               group_id,
-               "", group_id ? "taskFolders" : NULL,
-               "", task_folder_id,
+       uri = e_m365_connection_construct_uri (cnc, TRUE, user_override, E_M365_API_V1_0, NULL,
+               "todo",
+               "lists",
+               task_list_id,
                "", "tasks",
                "", task_id,
-               "", "attachments",
+               "", "linkedResources",
                "$select", select,
                NULL);
 
@@ -5574,7 +5644,7 @@ e_m365_connection_list_task_attachments_sync (EM365Connection *cnc,
 
        memset (&rd, 0, sizeof (EM365ResponseData));
 
-       rd.out_items = out_attachments;
+       rd.out_items = out_resources;
 
        success = m365_connection_send_request_sync (cnc, message, e_m365_read_valued_response_cb, NULL, &rd, 
cancellable, error);
 
@@ -5583,41 +5653,36 @@ e_m365_connection_list_task_attachments_sync (EM365Connection *cnc,
        return success;
 }
 
-/* https://docs.microsoft.com/en-us/graph/api/attachment-get?view=graph-rest-beta&tabs=http */
+/* https://learn.microsoft.com/en-us/graph/api/linkedresource-get?view=graph-rest-1.0&tabs=http */
 
 gboolean
-e_m365_connection_get_task_attachment_sync (EM365Connection *cnc,
+e_m365_connection_get_linked_resource_sync (EM365Connection *cnc,
                                            const gchar *user_override, /* for which user, NULL to use the 
account user */
-                                           const gchar *group_id, /* nullable, then the default group is 
used */
-                                           const gchar *task_folder_id,
+                                           const gchar *task_list_id,
                                            const gchar *task_id,
-                                           const gchar *attachment_id,
-                                           EM365ConnectionRawDataFunc func,
-                                           gpointer func_user_data,
+                                           const gchar *resource_id,
+                                           EM365LinkedResource **out_resource, /* nullable */
                                            GCancellable *cancellable,
                                            GError **error)
 {
        SoupMessage *message;
-       gboolean success;
        gchar *uri;
+       gboolean success;
 
        g_return_val_if_fail (E_IS_M365_CONNECTION (cnc), FALSE);
-       g_return_val_if_fail (task_folder_id != NULL, FALSE);
+       g_return_val_if_fail (task_list_id != NULL, FALSE);
        g_return_val_if_fail (task_id != NULL, FALSE);
-       g_return_val_if_fail (attachment_id != NULL, FALSE);
-       g_return_val_if_fail (func != NULL, FALSE);
+       g_return_val_if_fail (resource_id != NULL, FALSE);
+       g_return_val_if_fail (out_resource != NULL, FALSE);
 
-       uri = e_m365_connection_construct_uri (cnc, TRUE, user_override, E_M365_API_BETA, NULL,
-               "outlook",
-               group_id ? "taskGroups" : "taskFolders",
-               group_id,
-               "", group_id ? "taskFolders" : NULL,
-               "", task_folder_id,
+       uri = e_m365_connection_construct_uri (cnc, TRUE, user_override, E_M365_API_V1_0, NULL,
+               "todo",
+               "lists",
+               task_list_id,
                "", "tasks",
                "", task_id,
-               "", "attachments",
-               "", attachment_id,
-               "", "$value",
+               "", "linkedResources",
+               "", resource_id,
                NULL);
 
        message = m365_connection_new_soup_message (SOUP_METHOD_GET, uri, CSM_DEFAULT, error);
@@ -5630,47 +5695,44 @@ e_m365_connection_get_task_attachment_sync (EM365Connection *cnc,
 
        g_free (uri);
 
-       success = m365_connection_send_request_sync (cnc, message, NULL, func, func_user_data, cancellable, 
error);
+       success = m365_connection_send_request_sync (cnc, message, e_m365_read_json_object_response_cb, NULL, 
out_resource, cancellable, error);
 
        g_clear_object (&message);
 
        return success;
 }
 
-/* https://docs.microsoft.com/en-us/graph/api/outlooktask-post-attachments?view=graph-rest-beta&tabs=http */
+/* https://learn.microsoft.com/en-us/graph/api/todotask-post-linkedresources?view=graph-rest-1.0&tabs=http */
 
 gboolean
-e_m365_connection_add_task_attachment_sync (EM365Connection *cnc,
-                                           const gchar *user_override, /* for which user, NULL to use the 
account user */
-                                           const gchar *group_id, /* nullable, then the default group is 
used */
-                                           const gchar *task_folder_id,
-                                           const gchar *task_id,
-                                           JsonBuilder *in_attachment,
-                                           EM365Attachment **out_attachment, /* nullable */
-                                           GCancellable *cancellable,
-                                           GError **error)
+e_m365_connection_create_linked_resource_sync (EM365Connection *cnc,
+                                              const gchar *user_override, /* for which user, NULL to use the 
account user */
+                                              const gchar *task_list_id,
+                                              const gchar *task_id,
+                                              JsonBuilder *in_resource,
+                                              EM365LinkedResource **out_resource, /* nullable */
+                                              GCancellable *cancellable,
+                                              GError **error)
 {
        SoupMessage *message;
        gboolean success;
        gchar *uri;
 
        g_return_val_if_fail (E_IS_M365_CONNECTION (cnc), FALSE);
-       g_return_val_if_fail (task_folder_id != NULL, FALSE);
+       g_return_val_if_fail (task_list_id != NULL, FALSE);
        g_return_val_if_fail (task_id != NULL, FALSE);
-       g_return_val_if_fail (in_attachment != NULL, FALSE);
+       g_return_val_if_fail (in_resource != NULL, FALSE);
 
-       uri = e_m365_connection_construct_uri (cnc, TRUE, user_override, E_M365_API_BETA, NULL,
-               "outlook",
-               group_id ? "taskGroups" : "taskFolders",
-               group_id,
-               "", group_id ? "taskFolders" : NULL,
-               "", task_folder_id,
+       uri = e_m365_connection_construct_uri (cnc, TRUE, user_override, E_M365_API_V1_0, NULL,
+               "todo",
+               "lists",
+               task_list_id,
                "", "tasks",
                "", task_id,
-               "", "attachments",
+               "", "linkedResources",
                NULL);
 
-       message = m365_connection_new_soup_message (SOUP_METHOD_POST, uri, out_attachment ? CSM_DEFAULT : 
CSM_DISABLE_RESPONSE, error);
+       message = m365_connection_new_soup_message (SOUP_METHOD_POST, uri, out_resource ? CSM_DEFAULT : 
CSM_DISABLE_RESPONSE, error);
 
        if (!message) {
                g_free (uri);
@@ -5680,25 +5742,75 @@ e_m365_connection_add_task_attachment_sync (EM365Connection *cnc,
 
        g_free (uri);
 
-       e_m365_connection_set_json_body (message, in_attachment);
+       e_m365_connection_set_json_body (message, in_resource);
 
-       success = m365_connection_send_request_sync (cnc, message, out_attachment ? 
e_m365_read_json_object_response_cb : NULL,
-               out_attachment ? NULL : e_m365_read_no_response_cb, out_attachment, cancellable, error);
+       success = m365_connection_send_request_sync (cnc, message, out_resource ? 
e_m365_read_json_object_response_cb : NULL,
+               out_resource ? NULL : e_m365_read_no_response_cb, out_resource, cancellable, error);
 
        g_clear_object (&message);
 
        return success;
 }
 
-/* https://docs.microsoft.com/en-us/graph/api/attachment-delete?view=graph-rest-beta&tabs=http */
+/* https://learn.microsoft.com/en-us/graph/api/linkedresource-update?view=graph-rest-1.0&tabs=http */
 
 gboolean
-e_m365_connection_delete_task_attachment_sync (EM365Connection *cnc,
+e_m365_connection_update_linked_resource_sync (EM365Connection *cnc,
+                                             const gchar *user_override, /* for which user, NULL to use the 
account user */
+                                             const gchar *task_list_id,
+                                             const gchar *task_id,
+                                             const gchar *resource_id,
+                                             JsonBuilder *in_resource,
+                                             GCancellable *cancellable,
+                                             GError **error)
+{
+       SoupMessage *message;
+       gboolean success;
+       gchar *uri;
+
+       g_return_val_if_fail (E_IS_M365_CONNECTION (cnc), FALSE);
+       g_return_val_if_fail (task_list_id != NULL, FALSE);
+       g_return_val_if_fail (task_id != NULL, FALSE);
+       g_return_val_if_fail (resource_id != NULL, FALSE);
+       g_return_val_if_fail (in_resource != NULL, FALSE);
+
+       uri = e_m365_connection_construct_uri (cnc, TRUE, user_override, E_M365_API_V1_0, NULL,
+               "todo",
+               "lists",
+               task_list_id,
+               "", "tasks",
+               "", task_id,
+               "", "linkedResources",
+               "", resource_id,
+               NULL);
+
+       message = m365_connection_new_soup_message ("PATCH", uri, CSM_DISABLE_RESPONSE, error);
+
+       if (!message) {
+               g_free (uri);
+
+               return FALSE;
+       }
+
+       g_free (uri);
+
+       e_m365_connection_set_json_body (message, in_resource);
+
+       success = m365_connection_send_request_sync (cnc, message, NULL, e_m365_read_no_response_cb, NULL, 
cancellable, error);
+
+       g_clear_object (&message);
+
+       return success;
+}
+
+/* https://learn.microsoft.com/en-us/graph/api/linkedresource-delete?view=graph-rest-1.0&tabs=http */
+
+gboolean
+e_m365_connection_delete_linked_resource_sync (EM365Connection *cnc,
                                               const gchar *user_override, /* for which user, NULL to use the 
account user */
-                                              const gchar *group_id, /* nullable, then the default group is 
used */
-                                              const gchar *task_folder_id,
+                                              const gchar *task_list_id,
                                               const gchar *task_id,
-                                              const gchar *attachment_id,
+                                              const gchar *resource_id,
                                               GCancellable *cancellable,
                                               GError **error)
 {
@@ -5707,20 +5819,18 @@ e_m365_connection_delete_task_attachment_sync (EM365Connection *cnc,
        gchar *uri;
 
        g_return_val_if_fail (E_IS_M365_CONNECTION (cnc), FALSE);
-       g_return_val_if_fail (task_folder_id != NULL, FALSE);
+       g_return_val_if_fail (task_list_id != NULL, FALSE);
        g_return_val_if_fail (task_id != NULL, FALSE);
-       g_return_val_if_fail (attachment_id != NULL, FALSE);
+       g_return_val_if_fail (resource_id != NULL, FALSE);
 
-       uri = e_m365_connection_construct_uri (cnc, TRUE, user_override, E_M365_API_BETA, NULL,
-               "outlook",
-               group_id ? "taskGroups" : "taskFolders",
-               group_id,
-               "", group_id ? "taskFolders" : NULL,
-               "", task_folder_id,
+       uri = e_m365_connection_construct_uri (cnc, TRUE, user_override, E_M365_API_V1_0, NULL,
+               "todo",
+               "lists",
+               task_list_id,
                "", "tasks",
                "", task_id,
-               "", "attachments",
-               "", attachment_id,
+               "", "linkedResources",
+               "", resource_id,
                NULL);
 
        message = m365_connection_new_soup_message (SOUP_METHOD_DELETE, uri, CSM_DEFAULT, error);
diff --git a/src/Microsoft365/common/e-m365-connection.h b/src/Microsoft365/common/e-m365-connection.h
index 89cee114..33fb831c 100644
--- a/src/Microsoft365/common/e-m365-connection.h
+++ b/src/Microsoft365/common/e-m365-connection.h
@@ -561,84 +561,54 @@ gboolean  e_m365_connection_get_schedule_sync
                                                 GSList **out_infos, /* EM365ScheduleInformation * */
                                                 GCancellable *cancellable,
                                                 GError **error);
-gboolean       e_m365_connection_list_task_groups_sync
+gboolean       e_m365_connection_list_task_lists_sync
                                                (EM365Connection *cnc,
                                                 const gchar *user_override, /* for which user, NULL to use 
the account user */
-                                                GSList **out_groups, /* EM365TaskGroup * - the returned 
outlookTaskGroup objects */
+                                                GSList **out_task_lists, /* EM365TaskList * - the returned 
todoTaskList objects */
                                                 GCancellable *cancellable,
                                                 GError **error);
-gboolean       e_m365_connection_create_task_group_sync
+gboolean       e_m365_connection_get_task_list_sync
                                                (EM365Connection *cnc,
                                                 const gchar *user_override, /* for which user, NULL to use 
the account user */
-                                                const gchar *name,
-                                                EM365TaskGroup **out_created_group,
-                                                GCancellable *cancellable,
-                                                GError **error);
-gboolean       e_m365_connection_get_task_group_sync
-                                               (EM365Connection *cnc,
-                                                const gchar *user_override, /* for which user, NULL to use 
the account user */
-                                                const gchar *group_id,
-                                                EM365TaskGroup **out_group,
-                                                GCancellable *cancellable,
-                                                GError **error);
-gboolean       e_m365_connection_update_task_group_sync
-                                               (EM365Connection *cnc,
-                                                const gchar *user_override, /* for which user, NULL to use 
the account user */
-                                                const gchar *group_id,
-                                                const gchar *name,
-                                                GCancellable *cancellable,
-                                                GError **error);
-gboolean       e_m365_connection_delete_task_group_sync
-                                               (EM365Connection *cnc,
-                                                const gchar *user_override, /* for which user, NULL to use 
the account user */
-                                                const gchar *group_id,
+                                                const gchar *task_list_id,
+                                                EM365TaskList **out_task_list,
                                                 GCancellable *cancellable,
                                                 GError **error);
-gboolean       e_m365_connection_list_task_folders_sync
+gboolean       e_m365_connection_create_task_list_sync
                                                (EM365Connection *cnc,
                                                 const gchar *user_override, /* for which user, NULL to use 
the account user */
-                                                const gchar *group_id, /* nullable, task group id for group 
task folders */
-                                                const gchar *select, /* properties to select, nullable */
-                                                GSList **out_folders, /* EM365TaskFolder * - the returned 
outlookTaskFolder objects */
+                                                JsonBuilder *task_list,
+                                                EM365TaskList **out_created_task_list,
                                                 GCancellable *cancellable,
                                                 GError **error);
-gboolean       e_m365_connection_get_task_folder_sync
+gboolean       e_m365_connection_update_task_list_sync
                                                (EM365Connection *cnc,
                                                 const gchar *user_override, /* for which user, NULL to use 
the account user */
-                                                const gchar *group_id, /* nullable - then the default group 
is used */
-                                                const gchar *task_folder_id,
-                                                const gchar *select, /* nullable - properties to select */
-                                                EM365TaskFolder **out_task_folder,
-                                                GCancellable *cancellable,
-                                                GError **error);
-gboolean       e_m365_connection_create_task_folder_sync
-                                               (EM365Connection *cnc,
-                                                const gchar *user_override, /* for which user, NULL to use 
the account user */
-                                                const gchar *group_id, /* nullable, then the default group 
is used */
-                                                JsonBuilder *task_folder,
-                                                EM365TaskFolder **out_created_folder,
+                                                const gchar *task_list_id,
+                                                const gchar *display_name,
                                                 GCancellable *cancellable,
                                                 GError **error);
-gboolean       e_m365_connection_update_task_folder_sync
+gboolean       e_m365_connection_delete_task_list_sync
                                                (EM365Connection *cnc,
                                                 const gchar *user_override, /* for which user, NULL to use 
the account user */
-                                                const gchar *group_id, /* nullable - then the default group 
is used */
-                                                const gchar *task_folder_id,
-                                                const gchar *name,
+                                                const gchar *task_list_id,
                                                 GCancellable *cancellable,
                                                 GError **error);
-gboolean       e_m365_connection_delete_task_folder_sync
+gboolean       e_m365_connection_get_task_lists_delta_sync
                                                (EM365Connection *cnc,
                                                 const gchar *user_override, /* for which user, NULL to use 
the account user */
-                                                const gchar *group_id, /* nullable - then the default group 
is used */
-                                                const gchar *task_folder_id,
+                                                const gchar *delta_link, /* previous delta link */
+                                                guint max_page_size, /* 0 for default by the server */
+                                                EM365ConnectionJsonFunc func, /* function to call with each 
result set */
+                                                gpointer func_user_data, /* user data passed into the 'func' 
*/
+                                                gchar **out_delta_link,
                                                 GCancellable *cancellable,
                                                 GError **error);
 gboolean       e_m365_connection_list_tasks_sync
                                                (EM365Connection *cnc,
                                                 const gchar *user_override, /* for which user, NULL to use 
the account user */
-                                                const gchar *group_id, /* nullable, task group id for group 
task folders */
-                                                const gchar *task_folder_id,
+                                                const gchar *group_id, /* unused, always NULL */
+                                                const gchar *task_list_id,
                                                 const gchar *prefer_outlook_timezone, /* nullable - then 
UTC, otherwise that zone for the returned times */
                                                 const gchar *select, /* nullable - properties to select */
                                                 GSList **out_tasks, /* EM365Task * - the returned task 
objects */
@@ -647,8 +617,8 @@ gboolean    e_m365_connection_list_tasks_sync
 gboolean       e_m365_connection_create_task_sync
                                                (EM365Connection *cnc,
                                                 const gchar *user_override, /* for which user, NULL to use 
the account user */
-                                                const gchar *group_id, /* nullable, then the default group 
is used */
-                                                const gchar *task_folder_id,
+                                                const gchar *group_id, /* unused, always NULL */
+                                                const gchar *task_list_id,
                                                 JsonBuilder *task,
                                                 EM365Task **out_created_task,
                                                 GCancellable *cancellable,
@@ -656,16 +626,16 @@ gboolean  e_m365_connection_create_task_sync
 SoupMessage *  e_m365_connection_prepare_get_task
                                                (EM365Connection *cnc,
                                                 const gchar *user_override, /* for which user, NULL to use 
the account user */
-                                                const gchar *group_id, /* nullable, then the default group 
is used */
-                                                const gchar *task_folder_id,
+                                                const gchar *group_id, /* unused, always NULL */
+                                                const gchar *task_list_id,
                                                 const gchar *task_id,
                                                 const gchar *prefer_outlook_timezone, /* nullable - then 
UTC, otherwise that zone for the returned times */
                                                 const gchar *select, /* nullable - properties to select */
                                                 GError **error);
 gboolean       e_m365_connection_get_task_sync (EM365Connection *cnc,
                                                 const gchar *user_override, /* for which user, NULL to use 
the account user */
-                                                const gchar *group_id, /* nullable, then the default group 
is used */
-                                                const gchar *task_folder_id,
+                                                const gchar *group_id, /* unused, always NULL */
+                                                const gchar *task_list_id,
                                                 const gchar *task_id,
                                                 const gchar *prefer_outlook_timezone, /* nullable - then 
UTC, otherwise that zone for the returned times */
                                                 const gchar *select, /* nullable - properties to select */
@@ -674,8 +644,8 @@ gboolean    e_m365_connection_get_task_sync (EM365Connection *cnc,
                                                 GError **error);
 gboolean       e_m365_connection_get_tasks_sync(EM365Connection *cnc,
                                                 const gchar *user_override, /* for which user, NULL to use 
the account user */
-                                                const gchar *group_id, /* nullable, then the default group 
is used */
-                                                const gchar *task_folder_id,
+                                                const gchar *group_id, /* unused, always NULL */
+                                                const gchar *task_list_id,
                                                 const GSList *task_ids, /* const gchar * */
                                                 const gchar *prefer_outlook_timezone, /* nullable - then 
UTC, otherwise that zone for the returned times */
                                                 const gchar *select, /* nullable - properties to select */
@@ -685,8 +655,8 @@ gboolean    e_m365_connection_get_tasks_sync(EM365Connection *cnc,
 gboolean       e_m365_connection_update_task_sync
                                                (EM365Connection *cnc,
                                                 const gchar *user_override, /* for which user, NULL to use 
the account user */
-                                                const gchar *group_id, /* nullable - then the default group 
is used */
-                                                const gchar *task_folder_id,
+                                                const gchar *group_id, /* unused, always NULL */
+                                                const gchar *task_list_id,
                                                 const gchar *task_id,
                                                 JsonBuilder *task,
                                                 GCancellable *cancellable,
@@ -694,57 +664,108 @@ gboolean e_m365_connection_update_task_sync
 gboolean       e_m365_connection_delete_task_sync
                                                (EM365Connection *cnc,
                                                 const gchar *user_override, /* for which user, NULL to use 
the account user */
-                                                const gchar *group_id, /* nullable - then the default group 
is used */
-                                                const gchar *task_folder_id,
+                                                const gchar *group_id, /* unused, always NULL */
+                                                const gchar *task_list_id,
                                                 const gchar *task_id,
                                                 GCancellable *cancellable,
                                                 GError **error);
-gboolean       e_m365_connection_complete_task_sync
+gboolean       e_m365_connection_get_tasks_delta_sync
                                                (EM365Connection *cnc,
                                                 const gchar *user_override, /* for which user, NULL to use 
the account user */
-                                                const gchar *group_id, /* nullable - then the default group 
is used */
-                                                const gchar *task_folder_id,
+                                                const gchar *task_list_id,
+                                                const gchar *delta_link, /* previous delta link */
+                                                guint max_page_size, /* 0 for default by the server */
+                                                EM365ConnectionJsonFunc func, /* function to call with each 
result set */
+                                                gpointer func_user_data, /* user data passed into the 'func' 
*/
+                                                gchar **out_delta_link,
+                                                GCancellable *cancellable,
+                                                GError **error);
+gboolean       e_m365_connection_list_checklist_items_sync
+                                               (EM365Connection *cnc,
+                                                const gchar *user_override, /* for which user, NULL to use 
the account user */
+                                                const gchar *task_list_id,
                                                 const gchar *task_id,
+                                                const gchar *select, /* nullable - properties to select */
+                                                GSList **out_items, /* EM365ChecklistItem * */
                                                 GCancellable *cancellable,
                                                 GError **error);
-gboolean       e_m365_connection_list_task_attachments_sync
+gboolean       e_m365_connection_get_checklist_item_sync
                                                (EM365Connection *cnc,
                                                 const gchar *user_override, /* for which user, NULL to use 
the account user */
-                                                const gchar *group_id, /* nullable, then the default group 
is used */
-                                                const gchar *task_folder_id,
+                                                const gchar *task_list_id,
+                                                const gchar *task_id,
+                                                const gchar *item_id,
+                                                EM365ChecklistItem **out_item, /* nullable */
+                                                GCancellable *cancellable,
+                                                GError **error);
+gboolean       e_m365_connection_create_checklist_item_sync
+                                               (EM365Connection *cnc,
+                                                const gchar *user_override, /* for which user, NULL to use 
the account user */
+                                                const gchar *task_list_id,
+                                                const gchar *task_id,
+                                                JsonBuilder *in_item,
+                                                EM365ChecklistItem **out_item, /* nullable */
+                                                GCancellable *cancellable,
+                                                GError **error);
+gboolean       e_m365_connection_update_checklist_item_sync
+                                               (EM365Connection *cnc,
+                                                const gchar *user_override, /* for which user, NULL to use 
the account user */
+                                                const gchar *task_list_id,
+                                                const gchar *task_id,
+                                                const gchar *item_id,
+                                                JsonBuilder *in_item,
+                                                GCancellable *cancellable,
+                                                GError **error);
+gboolean       e_m365_connection_delete_checklist_item_sync
+                                               (EM365Connection *cnc,
+                                                const gchar *user_override, /* for which user, NULL to use 
the account user */
+                                                const gchar *task_list_id,
+                                                const gchar *task_id,
+                                                const gchar *item_id,
+                                                GCancellable *cancellable,
+                                                GError **error);
+gboolean       e_m365_connection_list_linked_resources_sync
+                                               (EM365Connection *cnc,
+                                                const gchar *user_override, /* for which user, NULL to use 
the account user */
+                                                const gchar *task_list_id,
                                                 const gchar *task_id,
                                                 const gchar *select, /* nullable - properties to select */
-                                                GSList **out_attachments, /* EM365Attachment * */
+                                                GSList **out_resources, /* EM365LinkedResource * */
                                                 GCancellable *cancellable,
                                                 GError **error);
-gboolean       e_m365_connection_get_task_attachment_sync
+gboolean       e_m365_connection_get_linked_resource_sync
                                                (EM365Connection *cnc,
                                                 const gchar *user_override, /* for which user, NULL to use 
the account user */
-                                                const gchar *group_id, /* nullable, then the default group 
is used */
-                                                const gchar *task_folder_id,
+                                                const gchar *task_list_id,
                                                 const gchar *task_id,
-                                                const gchar *attachment_id,
-                                                EM365ConnectionRawDataFunc func,
-                                                gpointer func_user_data,
+                                                const gchar *resource_id,
+                                                EM365LinkedResource **out_resource, /* nullable */
                                                 GCancellable *cancellable,
                                                 GError **error);
-gboolean       e_m365_connection_add_task_attachment_sync
+gboolean       e_m365_connection_create_linked_resource_sync
                                                (EM365Connection *cnc,
                                                 const gchar *user_override, /* for which user, NULL to use 
the account user */
-                                                const gchar *group_id, /* nullable, then the default group 
is used */
-                                                const gchar *task_folder_id,
+                                                const gchar *task_list_id,
                                                 const gchar *task_id,
-                                                JsonBuilder *in_attachment,
-                                                EM365Attachment **out_attachment, /* nullable */
+                                                JsonBuilder *in_resource,
+                                                EM365LinkedResource **out_resource, /* nullable */
                                                 GCancellable *cancellable,
                                                 GError **error);
-gboolean       e_m365_connection_delete_task_attachment_sync
+gboolean       e_m365_connection_update_linked_resource_sync
                                                (EM365Connection *cnc,
                                                 const gchar *user_override, /* for which user, NULL to use 
the account user */
-                                                const gchar *group_id, /* nullable, then the default group 
is used */
-                                                const gchar *task_folder_id,
+                                                const gchar *task_list_id,
                                                 const gchar *task_id,
-                                                const gchar *attachment_id,
+                                                const gchar *resource_id,
+                                                JsonBuilder *in_resource,
+                                                GCancellable *cancellable,
+                                                GError **error);
+gboolean       e_m365_connection_delete_linked_resource_sync
+                                               (EM365Connection *cnc,
+                                                const gchar *user_override, /* for which user, NULL to use 
the account user */
+                                                const gchar *task_list_id,
+                                                const gchar *task_id,
+                                                const gchar *resource_id,
                                                 GCancellable *cancellable,
                                                 GError **error);
 
diff --git a/src/Microsoft365/common/e-m365-json-utils.c b/src/Microsoft365/common/e-m365-json-utils.c
index 9af7659b..8e554012 100644
--- a/src/Microsoft365/common/e-m365-json-utils.c
+++ b/src/Microsoft365/common/e-m365-json-utils.c
@@ -253,6 +253,13 @@ static MapData week_index_map[] = {
        { "last",       E_M365_WEEK_INDEX_LAST }
 };
 
+static MapData task_list_kind_map[] = {
+       { "none",               E_M365_TASK_LIST_KIND_NONE },
+       { "defaultList",        E_M365_TASK_LIST_KIND_DEFAULT_LIST },
+       { "flaggedEmails",      E_M365_TASK_LIST_KIND_FLAGGED_EMAILS },
+       { "unknownFutureValue", E_M365_TASK_LIST_KIND_UNKNOWN_FUTURE_VALUE }
+};
+
 const gchar *
 e_m365_calendar_color_to_rgb (EM365CalendarColorType color)
 {
@@ -3570,71 +3577,49 @@ e_m365_schedule_information_get_working_hours (EM365ScheduleInformation *schinfo
        return e_m365_json_get_object_member (schinfo, "workingHours");
 }
 
-/* https://docs.microsoft.com/en-us/graph/api/resources/outlooktaskfolder?view=graph-rest-beta */
+/* https://learn.microsoft.com/en-us/graph/api/resources/todotasklist?view=graph-rest-1.0 */
 
 const gchar *
-e_m365_task_folder_get_id (EM365TaskFolder *folder)
+e_m365_task_list_get_id (EM365TaskList *list)
 {
-       return e_m365_json_get_string_member (folder, "id", NULL);
+       return e_m365_json_get_string_member (list, "id", NULL);
 }
 
 const gchar *
-e_m365_task_folder_get_change_key (EM365TaskFolder *folder)
+e_m365_task_list_get_display_name (EM365TaskList *list)
 {
-       return e_m365_json_get_string_member (folder, "changeKey", NULL);
+       return e_m365_json_get_string_member (list, "displayName", NULL);
 }
 
-const gchar *
-e_m365_task_folder_get_parent_group_key (EM365TaskFolder *folder)
-{
-       return e_m365_json_get_string_member (folder, "parentGroupKey", NULL);
-}
-
-const gchar *
-e_m365_task_folder_get_name (EM365TaskFolder *folder)
+void
+e_m365_task_list_add_display_name (JsonBuilder *builder,
+                                  const gchar *display_name)
 {
-       return e_m365_json_get_string_member (folder, "name", NULL);
+       e_m365_json_add_string_member (builder, "displayName", display_name);
 }
 
 gboolean
-e_m365_task_folder_get_is_default_folder (EM365TaskFolder *folder)
+e_m365_task_list_get_is_owner (EM365TaskList *list)
 {
-       return e_m365_json_get_boolean_member (folder, "isDefaultFolder", FALSE);
+       return e_m365_json_get_boolean_member (list, "isOwner", FALSE);
 }
 
-/* https://docs.microsoft.com/en-us/graph/api/resources/outlooktaskgroup?view=graph-rest-beta */
-
-const gchar *
-e_m365_task_group_get_id (EM365TaskGroup *group)
-{
-       return e_m365_json_get_string_member (group, "id", NULL);
-}
-
-const gchar *
-e_m365_task_group_get_change_key (EM365TaskGroup *group)
-{
-       return e_m365_json_get_string_member (group, "changeKey", NULL);
-}
-
-const gchar *
-e_m365_task_group_get_group_key (EM365TaskGroup *group)
-{
-       return e_m365_json_get_string_member (group, "groupKey", NULL);
-}
-
-const gchar *
-e_m365_task_group_get_name (EM365TaskGroup *group)
+gboolean
+e_m365_task_list_get_is_shared (EM365TaskList *list)
 {
-       return e_m365_json_get_string_member (group, "name", NULL);
+       return e_m365_json_get_boolean_member (list, "isShared", FALSE);
 }
 
-gboolean
-e_m365_task_group_get_is_default_group (EM365TaskGroup *group)
+EM365TaskListKind
+e_m365_task_list_get_kind (EM365TaskList *list)
 {
-       return e_m365_json_get_boolean_member (group, "isDefaultGroup", FALSE);
+       return m365_json_utils_get_json_as_enum (list, "wellknownListName",
+               task_list_kind_map, G_N_ELEMENTS (task_list_kind_map),
+               E_M365_TASK_LIST_KIND_NOT_SET,
+               E_M365_TASK_LIST_KIND_UNKNOWN);
 }
 
-/* https://docs.microsoft.com/en-us/graph/api/resources/outlooktask?view=graph-rest-beta */
+/* https://learn.microsoft.com/en-us/graph/api/resources/todotask?view=graph-rest-1.0 */
 
 const gchar *
 e_m365_task_get_id (EM365Task *task)
@@ -3642,22 +3627,11 @@ e_m365_task_get_id (EM365Task *task)
        return e_m365_json_get_string_member (task, "id", NULL);
 }
 
-const gchar *
-e_m365_task_get_change_key (EM365Task *task)
-{
-       return e_m365_json_get_string_member (task, "changeKey", NULL);
-}
-
-const gchar *
-e_m365_task_get_parent_folder_id (EM365Task *task)
-{
-       return e_m365_json_get_string_member (task, "parentFolderId", NULL);
-}
-
-const gchar *
-e_m365_task_get_assigned_to (EM365Task *task)
+void
+e_m365_task_add_id (JsonBuilder *builder,
+                   const gchar *value)
 {
-       return e_m365_json_get_string_member (task, "assignedTo", NULL);
+       e_m365_json_add_string_member (builder, "id", value);
 }
 
 EM365ItemBody *
@@ -3674,6 +3648,19 @@ e_m365_task_add_body (JsonBuilder *builder,
        e_m365_add_item_body (builder, "body", content_type, content);
 }
 
+time_t
+e_m365_task_get_body_last_modified_date_time (EM365Task *task)
+{
+       return e_m365_get_date_time_offset_member (task, "bodyLastModifiedDateTime", NULL);
+}
+
+void
+e_m365_task_add_boady_last_modified_date_time (JsonBuilder *builder,
+                                              time_t value)
+{
+       return e_m365_add_date_time_offset_member (builder, "bodyLastModifiedDateTime", value);
+}
+
 JsonArray * /* const gchar * */
 e_m365_task_get_categories (EM365Task *task)
 {
@@ -3721,6 +3708,13 @@ e_m365_task_get_created_date_time (EM365Task *task)
        return e_m365_get_date_time_offset_member (task, "createdDateTime", NULL);
 }
 
+void
+e_m365_task_add_created_date_time (JsonBuilder *builder,
+                                  time_t value)
+{
+       return e_m365_add_date_time_offset_member (builder, "createdDateTime", value);
+}
+
 EM365DateTimeWithZone *
 e_m365_task_get_due_date_time (EM365Task *task)
 {
@@ -3735,12 +3729,6 @@ e_m365_task_add_due_date_time (JsonBuilder *builder,
        e_m365_add_date_time (builder, "dueDateTime", date_time, zone);
 }
 
-gboolean
-e_m365_task_get_has_attachments (EM365Task *task)
-{
-       return e_m365_json_get_boolean_member (task, "hasAttachments", FALSE);
-}
-
 EM365ImportanceType
 e_m365_task_get_importance (EM365Task *task)
 {
@@ -3780,16 +3768,16 @@ e_m365_task_get_last_modified_date_time (EM365Task *task)
 }
 
 const gchar *
-e_m365_task_get_owner (EM365Task *task)
+e_m365_task_get_last_modified_as_string (EM365Task *task)
 {
-       return e_m365_json_get_string_member (task, "owner", NULL);
+       return e_m365_json_get_string_member (task, "lastModifiedDateTime", NULL);
 }
 
 void
-e_m365_task_add_owner (JsonBuilder *builder,
-                      const gchar *value)
+e_m365_task_add_last_modified_date_time (JsonBuilder *builder,
+                                        time_t value)
 {
-       e_m365_json_add_string_member (builder, "owner", value);
+       return e_m365_add_date_time_offset_member (builder, "lastModifiedDateTime", value);
 }
 
 EM365PatternedRecurrence *
@@ -3830,23 +3818,13 @@ e_m365_task_add_reminder_date_time (JsonBuilder *builder,
        e_m365_add_date_time (builder, "reminderDateTime", date_time, zone);
 }
 
-EM365SensitivityType
-e_m365_task_get_sensitivity (EM365Task *task)
-{
-       return m365_json_utils_get_json_as_enum (task, "sensitivity",
-               sensitivity_map, G_N_ELEMENTS (sensitivity_map),
-               E_M365_SENSITIVITY_NOT_SET,
-               E_M365_SENSITIVITY_UNKNOWN);
-}
-
-void
-e_m365_task_add_sensitivity (JsonBuilder *builder,
-                            EM365SensitivityType value)
+EM365StatusType
+e_m365_task_get_status (EM365Task *task)
 {
-       m365_json_utils_add_enum_as_json (builder, "sensitivity", value,
-               sensitivity_map, G_N_ELEMENTS (sensitivity_map),
-               E_M365_SENSITIVITY_NOT_SET,
-               E_M365_SENSITIVITY_UNKNOWN);
+       return m365_json_utils_get_json_as_enum (task, "status",
+               status_map, G_N_ELEMENTS (status_map),
+               E_M365_STATUS_NOT_SET,
+               E_M365_STATUS_UNKNOWN);
 }
 
 EM365DateTimeWithZone *
@@ -3863,15 +3841,6 @@ e_m365_task_add_start_date_time (JsonBuilder *builder,
        e_m365_add_date_time (builder, "startDateTime", date_time, zone);
 }
 
-EM365StatusType
-e_m365_task_get_status (EM365Task *task)
-{
-       return m365_json_utils_get_json_as_enum (task, "status",
-               status_map, G_N_ELEMENTS (status_map),
-               E_M365_STATUS_NOT_SET,
-               E_M365_STATUS_UNKNOWN);
-}
-
 void
 e_m365_task_add_status (JsonBuilder *builder,
                        EM365StatusType value)
@@ -3883,14 +3852,134 @@ e_m365_task_add_status (JsonBuilder *builder,
 }
 
 const gchar *
-e_m365_task_get_subject (EM365Task *task)
+e_m365_task_get_title (EM365Task *task)
 {
-       return e_m365_json_get_string_member (task, "subject", NULL);
+       return e_m365_json_get_string_member (task, "title", NULL);
 }
 
 void
-e_m365_task_add_subject (JsonBuilder *builder,
-                        const gchar *value)
+e_m365_task_add_title (JsonBuilder *builder,
+                      const gchar *value)
+{
+       e_m365_json_add_string_member (builder, "title", value);
+}
+
+/* https://learn.microsoft.com/en-us/graph/api/resources/checklistitem?view=graph-rest-1.0 */
+
+const gchar *
+e_m365_checklist_item_get_id (EM365ChecklistItem *item)
+{
+       return e_m365_json_get_string_member (item, "id", NULL);
+}
+
+time_t
+e_m365_checklist_item_get_checked_date_time (EM365ChecklistItem *item)
+{
+       return e_m365_get_date_time_offset_member (item, "checkedDateTime", NULL);
+}
+
+void
+e_m365_checklist_item_add_checked_date_time (JsonBuilder *builder,
+                                            time_t value)
+{
+       e_m365_add_date_time_offset_member (builder, "checkedDateTime", value);
+}
+
+time_t
+e_m365_checklist_item_get_created_date_time (EM365ChecklistItem *item)
+{
+       return e_m365_get_date_time_offset_member (item, "createdDateTime", NULL);
+}
+
+void
+e_m365_checklist_item_add_created_date_time (JsonBuilder *builder,
+                                            time_t value)
+{
+       e_m365_add_date_time_offset_member (builder, "createdDateTime", value);
+}
+
+const gchar *
+e_m365_checklist_item_get_display_name (EM365ChecklistItem *item)
+{
+       return e_m365_json_get_string_member (item, "displayName", NULL);
+}
+
+void
+e_m365_checklist_item_add_display_name (JsonBuilder *builder,
+                                       const gchar *value)
+{
+       e_m365_json_add_string_member (builder, "displayName", value);
+}
+
+gboolean
+e_m365_checklist_item_get_is_checked (EM365ChecklistItem *item)
+{
+       return e_m365_json_get_boolean_member (item, "isChecked", FALSE);
+}
+
+void
+e_m365_checklist_item_add_is_checked (JsonBuilder *builder,
+                                     gboolean value)
+{
+       e_m365_json_add_boolean_member (builder, "isChecked", value);
+}
+
+/* https://learn.microsoft.com/en-us/graph/api/resources/linkedresource?view=graph-rest-1.0 */
+
+const gchar *
+e_m365_linked_resource_get_id (EM365LinkedResource *resource)
+{
+       return e_m365_json_get_string_member (resource, "id", NULL);
+}
+
+const gchar *
+e_m365_linked_resource_get_application_name (EM365LinkedResource *resource)
+{
+       return e_m365_json_get_string_member (resource, "applicationName", NULL);
+}
+
+void
+e_m365_linked_resource_add_application_name (JsonBuilder *builder,
+                                            const gchar *value)
+{
+       e_m365_json_add_string_member (builder, "applicationName", value);
+}
+
+const gchar *
+e_m365_linked_resource_get_display_name (EM365LinkedResource *resource)
+{
+       return e_m365_json_get_string_member (resource, "displayName", NULL);
+}
+
+void
+e_m365_linked_resource_add_display_name (JsonBuilder *builder,
+                                        const gchar *value)
+{
+       e_m365_json_add_string_member (builder, "displayName", value);
+}
+
+const gchar *
+e_m365_linked_resource_get_external_id (EM365LinkedResource *resource)
+{
+       return e_m365_json_get_string_member (resource, "externalId", NULL);
+}
+
+void
+e_m365_linked_resource_add_external_id (JsonBuilder *builder,
+                                       const gchar *value)
+{
+       e_m365_json_add_string_member (builder, "externalId", value);
+}
+
+const gchar *
+e_m365_linked_resource_get_web_url (EM365LinkedResource *resource)
+{
+       return e_m365_json_get_string_member (resource, "webUrl", NULL);
+}
+
+void
+e_m365_linked_resource_add_web_url (JsonBuilder *builder,
+                                   const gchar *value)
 {
-       e_m365_json_add_string_member (builder, "subject", value);
+       e_m365_json_add_string_member (builder, "webUrl", value);
 }
diff --git a/src/Microsoft365/common/e-m365-json-utils.h b/src/Microsoft365/common/e-m365-json-utils.h
index 3f99996e..7efad851 100644
--- a/src/Microsoft365/common/e-m365-json-utils.h
+++ b/src/Microsoft365/common/e-m365-json-utils.h
@@ -18,6 +18,7 @@ G_BEGIN_DECLS
 #define EM365Calendar                  JsonObject
 #define EM365CalendarGroup             JsonObject
 #define EM365Category                  JsonObject
+#define EM365ChecklistItem             JsonObject
 #define EM365Contact                   JsonObject
 #define EM365Date                      gint
 #define EM365DateTimeWithZone          JsonObject
@@ -28,6 +29,7 @@ G_BEGIN_DECLS
 #define EM365FreeBusyError             JsonObject
 #define EM365InternetMessageHeader     JsonObject
 #define EM365ItemBody                  JsonObject
+#define EM365LinkedResource            JsonObject
 #define EM365Location                  JsonObject
 #define EM365MailFolder                        JsonObject
 #define EM365MailMessage               JsonObject
@@ -43,8 +45,7 @@ G_BEGIN_DECLS
 #define EM365ScheduleInformation       JsonObject
 #define EM365ScheduleItem              JsonObject
 #define EM365Task                      JsonObject
-#define EM365TaskFolder                        JsonObject
-#define EM365TaskGroup                 JsonObject
+#define EM365TaskList                  JsonObject
 #define EM365TimeOfDay                 gint64
 #define EM365WorkingHours              JsonObject
 
@@ -238,6 +239,15 @@ typedef enum _EM365WeekIndexType {
        E_M365_WEEK_INDEX_LAST
 } EM365WeekIndexType;
 
+typedef enum _EM365TaskListKind {
+       E_M365_TASK_LIST_KIND_NOT_SET,
+       E_M365_TASK_LIST_KIND_UNKNOWN,
+       E_M365_TASK_LIST_KIND_NONE,
+       E_M365_TASK_LIST_KIND_DEFAULT_LIST,
+       E_M365_TASK_LIST_KIND_FLAGGED_EMAILS,
+       E_M365_TASK_LIST_KIND_UNKNOWN_FUTURE_VALUE
+} EM365TaskListKind;
+
 const gchar *  e_m365_calendar_color_to_rgb            (EM365CalendarColorType color);
 EM365CalendarColorType
                e_m365_rgb_to_calendar_color            (const gchar *rgb);
@@ -979,26 +989,27 @@ EM365WorkingHours *
                e_m365_schedule_information_get_working_hours
                                                        (EM365ScheduleInformation *schinfo);
 
-const gchar *  e_m365_task_folder_get_id               (EM365TaskFolder *folder);
-const gchar *  e_m365_task_folder_get_change_key       (EM365TaskFolder *folder);
-const gchar *  e_m365_task_folder_get_parent_group_key (EM365TaskFolder *folder);
-const gchar *  e_m365_task_folder_get_name             (EM365TaskFolder *folder);
-gboolean       e_m365_task_folder_get_is_default_folder(EM365TaskFolder *folder);
-
-const gchar *  e_m365_task_group_get_id                (EM365TaskGroup *group);
-const gchar *  e_m365_task_group_get_change_key        (EM365TaskGroup *group);
-const gchar *  e_m365_task_group_get_group_key         (EM365TaskGroup *group);
-const gchar *  e_m365_task_group_get_name              (EM365TaskGroup *group);
-gboolean       e_m365_task_group_get_is_default_group  (EM365TaskGroup *group);
+const gchar *  e_m365_task_list_get_id                 (EM365TaskList *list);
+const gchar *  e_m365_task_list_get_display_name       (EM365TaskList *list);
+void           e_m365_task_list_add_display_name       (JsonBuilder *builder,
+                                                        const gchar *display_name);
+gboolean       e_m365_task_list_get_is_owner           (EM365TaskList *list);
+gboolean       e_m365_task_list_get_is_shared          (EM365TaskList *list);
+EM365TaskListKind
+               e_m365_task_list_get_kind               (EM365TaskList *list);
 
 const gchar *  e_m365_task_get_id                      (EM365Task *task);
-const gchar *  e_m365_task_get_change_key              (EM365Task *task);
-const gchar *  e_m365_task_get_parent_folder_id        (EM365Task *task);
-const gchar *  e_m365_task_get_assigned_to             (EM365Task *task);
+void           e_m365_task_add_id                      (JsonBuilder *builder,
+                                                        const gchar *value);
 EM365ItemBody *        e_m365_task_get_body                    (EM365Task *task);
 void           e_m365_task_add_body                    (JsonBuilder *builder,
                                                         EM365ItemBodyContentTypeType content_type,
                                                         const gchar *content);
+time_t         e_m365_task_get_body_last_modified_date_time
+                                                       (EM365Task *task);
+void           e_m365_task_add_boady_last_modified_date_time
+                                                       (JsonBuilder *builder,
+                                                        time_t value);
 JsonArray *    e_m365_task_get_categories              (EM365Task *task); /* const gchar * */
 void           e_m365_task_begin_categories            (JsonBuilder *builder);
 void           e_m365_task_end_categories              (JsonBuilder *builder);
@@ -1010,12 +1021,13 @@ void            e_m365_task_add_completed_date_time     (JsonBuilder *builder,
                                                         time_t date_time,
                                                         const gchar *zone);
 time_t         e_m365_task_get_created_date_time       (EM365Task *task);
+void           e_m365_task_add_created_date_time       (JsonBuilder *builder,
+                                                        time_t value);
 EM365DateTimeWithZone *
                e_m365_task_get_due_date_time           (EM365Task *task);
 void           e_m365_task_add_due_date_time           (JsonBuilder *builder,
                                                         time_t date_time,
                                                         const gchar *zone);
-gboolean       e_m365_task_get_has_attachments         (EM365Task *task);
 EM365ImportanceType
                e_m365_task_get_importance              (EM365Task *task);
 void           e_m365_task_add_importance              (JsonBuilder *builder,
@@ -1024,9 +1036,9 @@ gboolean  e_m365_task_get_is_reminder_on          (EM365Task *task);
 void           e_m365_task_add_is_reminder_on          (JsonBuilder *builder,
                                                         gboolean value);
 time_t         e_m365_task_get_last_modified_date_time (EM365Task *task);
-const gchar *  e_m365_task_get_owner                   (EM365Task *task);
-void           e_m365_task_add_owner                   (JsonBuilder *builder,
-                                                        const gchar *value);
+const gchar *  e_m365_task_get_last_modified_as_string (EM365Task *task);
+void           e_m365_task_add_last_modified_date_time (JsonBuilder *builder,
+                                                        time_t value);
 EM365PatternedRecurrence *
                e_m365_task_get_recurrence              (EM365Task *task);
 void           e_m365_task_begin_recurrence            (JsonBuilder *builder);
@@ -1037,10 +1049,6 @@ EM365DateTimeWithZone *
 void           e_m365_task_add_reminder_date_time      (JsonBuilder *builder,
                                                         time_t date_time,
                                                         const gchar *zone);
-EM365SensitivityType
-               e_m365_task_get_sensitivity             (EM365Task *task);
-void           e_m365_task_add_sensitivity             (JsonBuilder *builder,
-                                                        EM365SensitivityType value);
 EM365DateTimeWithZone *
                e_m365_task_get_start_date_time         (EM365Task *task);
 void           e_m365_task_add_start_date_time         (JsonBuilder *builder,
@@ -1049,8 +1057,42 @@ void             e_m365_task_add_start_date_time         (JsonBuilder *builder,
 EM365StatusType        e_m365_task_get_status                  (EM365Task *task);
 void           e_m365_task_add_status                  (JsonBuilder *builder,
                                                         EM365StatusType value);
-const gchar *  e_m365_task_get_subject                 (EM365Task *task);
-void           e_m365_task_add_subject                 (JsonBuilder *builder,
+const gchar *  e_m365_task_get_title                   (EM365Task *task);
+void           e_m365_task_add_title                   (JsonBuilder *builder,
+                                                        const gchar *value);
+
+const gchar *  e_m365_checklist_item_get_id            (EM365ChecklistItem *item);
+time_t         e_m365_checklist_item_get_checked_date_time
+                                                       (EM365ChecklistItem *item);
+void           e_m365_checklist_item_add_checked_date_time
+                                                       (JsonBuilder *builder,
+                                                        time_t value);
+time_t         e_m365_checklist_item_get_created_date_time
+                                                       (EM365ChecklistItem *item);
+void           e_m365_checklist_item_add_created_date_time
+                                                       (JsonBuilder *builder,
+                                                        time_t value);
+const gchar *  e_m365_checklist_item_get_display_name  (EM365ChecklistItem *item);
+void           e_m365_checklist_item_add_display_name  (JsonBuilder *builder,
+                                                        const gchar *value);
+gboolean       e_m365_checklist_item_get_is_checked    (EM365ChecklistItem *item);
+void           e_m365_checklist_item_add_is_checked    (JsonBuilder *builder,
+                                                        gboolean value);
+
+const gchar *  e_m365_linked_resource_get_id           (EM365LinkedResource *resource);
+const gchar *  e_m365_linked_resource_get_application_name
+                                                       (EM365LinkedResource *resource);
+void           e_m365_linked_resource_add_application_name
+                                                       (JsonBuilder *builder,
+                                                        const gchar *value);
+const gchar *  e_m365_linked_resource_get_display_name (EM365LinkedResource *resource);
+void           e_m365_linked_resource_add_display_name (JsonBuilder *builder,
+                                                        const gchar *value);
+const gchar *  e_m365_linked_resource_get_external_id  (EM365LinkedResource *resource);
+void           e_m365_linked_resource_add_external_id  (JsonBuilder *builder,
+                                                        const gchar *value);
+const gchar *  e_m365_linked_resource_get_web_url      (EM365LinkedResource *resource);
+void           e_m365_linked_resource_add_web_url      (JsonBuilder *builder,
                                                         const gchar *value);
 
 G_END_DECLS
diff --git a/src/Microsoft365/registry/e-m365-backend.c b/src/Microsoft365/registry/e-m365-backend.c
index 261d5cf7..932548be 100644
--- a/src/Microsoft365/registry/e-m365-backend.c
+++ b/src/Microsoft365/registry/e-m365-backend.c
@@ -433,11 +433,6 @@ m365_backend_sync_calendar_folders_sync (EM365Backend *m365_backend,
        g_clear_error (&error);
 }
 
-/* Tasks are in the beta stage (as of 2020-07-31) and even one can get groups of tasks, getting
-   information about a task folder in the group fails with an error NavigationNotSupported:
-   "Recursive navigation is not allowed after property 'TaskFolders' according to the entity schema."
-   The server returns similar error when trying to get a single task, which makes it unusable. */
-#if 0
 static void
 m365_backend_sync_task_folders_sync (EM365Backend *m365_backend,
                                     EM365Connection *cnc,
@@ -446,47 +441,29 @@ m365_backend_sync_task_folders_sync (EM365Backend *m365_backend,
        const gchar *extension_name = E_SOURCE_EXTENSION_TASK_LIST;
        GHashTable *known_ids; /* gchar *id ~> NULL */
        gboolean success = FALSE;
-       GSList *groups = NULL, *link;
+       GSList *task_lists = NULL, *link;
        GError *error = NULL;
 
        known_ids = m365_backend_get_known_folder_ids (m365_backend, extension_name, FALSE);
 
-       if (e_m365_connection_list_task_groups_sync (cnc, NULL, &groups, cancellable, &error) && groups) {
-               success = TRUE;
-
-               for (link = groups; link && success; link = g_slist_next (link)) {
-                       EM365TaskGroup *group = link->data;
-                       GSList *task_folders = NULL;
+       if (e_m365_connection_list_task_lists_sync (cnc, NULL, &task_lists, cancellable, &error)) {
+               for (link = task_lists; link; link = g_slist_next (link)) {
+                       EM365TaskList *task_list = link->data;
 
-                       if (!group)
+                       if (!task_list || !e_m365_task_list_get_id (task_list))
                                continue;
 
-                       if (e_m365_connection_list_task_folders_sync (cnc, NULL, e_m365_task_group_get_id 
(group), NULL, &task_folders, cancellable, &error)) {
-                               GSList *tlink;
-
-                               for (tlink = task_folders; tlink; tlink = g_slist_next (tlink)) {
-                                       EM365TaskFolder *task_folder = tlink->data;
-
-                                       if (!task_folder || !e_m365_task_folder_get_id (task_folder))
-                                               continue;
-
-                                       m365_backend_update_resource (m365_backend, extension_name,
-                                               e_m365_task_folder_get_id (task_folder),
-                                               e_m365_task_group_get_id (group),
-                                               e_m365_task_folder_get_name (task_folder),
-                                               e_m365_task_folder_get_is_default_folder (task_folder),
-                                               NULL);
-
-                                       g_hash_table_remove (known_ids, e_m365_task_folder_get_id 
(task_folder));
-                               }
+                       m365_backend_update_resource (m365_backend, extension_name,
+                               e_m365_task_list_get_id (task_list),
+                               NULL,
+                               e_m365_task_list_get_display_name (task_list),
+                               e_m365_task_list_get_kind (task_list) == E_M365_TASK_LIST_KIND_DEFAULT_LIST,
+                               NULL);
 
-                               g_slist_free_full (task_folders, (GDestroyNotify) json_object_unref);
-                       } else {
-                               success = FALSE;
-                       }
+                       g_hash_table_remove (known_ids, e_m365_task_list_get_id (task_list));
                }
 
-               g_slist_free_full (groups, (GDestroyNotify) json_object_unref);
+               g_slist_free_full (task_lists, (GDestroyNotify) json_object_unref);
        }
 
        if (success)
@@ -495,7 +472,6 @@ m365_backend_sync_task_folders_sync (EM365Backend *m365_backend,
        g_hash_table_destroy (known_ids);
        g_clear_error (&error);
 }
-#endif
 
 static void
 m365_backend_sync_folders_thread (GTask *task,
@@ -520,9 +496,7 @@ m365_backend_sync_folders_thread (GTask *task,
 
        if (e_source_collection_get_calendar_enabled (collection_extension)) {
                m365_backend_sync_calendar_folders_sync (m365_backend, cnc, cancellable);
-#if 0
                m365_backend_sync_task_folders_sync (m365_backend, cnc, cancellable);
-#endif
        }
 
        e_collection_backend_thaw_populate (E_COLLECTION_BACKEND (m365_backend));


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]