[gnome-online-accounts] Add Todoist



commit ba9cc7bf2af77b4131f8b8e5ae5fe70f895026cb
Author: Ekta Nandwani <mailnandwaniekta gmail com>
Date:   Fri Jun 16 18:15:55 2017 +0200

    Add Todoist
    
    Bump minimum GLib version to 2.52.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=780138

 README                              |    6 +
 configure.ac                        |   29 +++-
 po/POTFILES.in                      |    1 +
 src/goabackend/Makefile.am          |    1 +
 src/goabackend/goaprovider.c        |    4 +
 src/goabackend/goatodoistprovider.c |  340 +++++++++++++++++++++++++++++++++++
 src/goabackend/goatodoistprovider.h |   37 ++++
 7 files changed, 417 insertions(+), 1 deletions(-)
---
diff --git a/README b/README
index a168c79..a75ddf3 100644
--- a/README
+++ b/README
@@ -70,6 +70,12 @@ useful:
 http://www.slideshare.net/KuoE0/pocket-authentication-with-oauth-on-firefox-os
 
 
+Todoist
+-------
+
+OAuth 2.0: https://developer.todoist.com/
+
+
 Windows Live
 ------------
 
diff --git a/configure.ac b/configure.ac
index ac15d46..6bc0061 100644
--- a/configure.ac
+++ b/configure.ac
@@ -85,7 +85,7 @@ GTK_DOC_CHECK([1.3])
 # Libraries
 #
 
-PKG_CHECK_MODULES(GLIB, [glib-2.0 gio-2.0 gio-unix-2.0 >= 2.44])
+PKG_CHECK_MODULES(GLIB, [glib-2.0 gio-2.0 gio-unix-2.0 >= 2.52])
 AC_SUBST(GLIB_CFLAGS)
 AC_SUBST(GLIB_LIBS)
 
@@ -315,6 +315,32 @@ if test "$enable_facebook" != "no"; then
   AC_DEFINE(GOA_FACEBOOK_ENABLED, 1, [Enable Facebook data provider])
 fi
 
+# Todoist
+AC_DEFINE(GOA_TODOIST_NAME, ["todoist"], [ProviderType and extension point name])
+AC_ARG_ENABLE([todoist],
+              [AS_HELP_STRING([--enable-todoist], [Enable Todoist provider])],
+              [],
+              [enable_todoist=no])
+AC_ARG_WITH(todoist-client-id,
+            [AS_HELP_STRING([--with-todoist-client-id], [Todoist OAuth 2.0 client id])],
+            [],
+            [])
+AC_ARG_WITH(todoist-client-secret,
+            [AS_HELP_STRING([--with-todoist-client-secret], [Todoist client secret])],
+            [],
+            [])
+if test "$with_todoist_client_id" = ""; then
+  with_todoist_client_id="2e41575c1dd74f98ad7cfb178e1ba3dd"
+fi
+if test "$with_todoist_client_secret" = ""; then
+  with_todoist_client_secret="3e59831ffdd64633918d19aa0a1efd2b"
+fi
+AC_DEFINE_UNQUOTED(GOA_TODOIST_CLIENT_ID, ["$with_todoist_client_id"], [Todoist OAuth 2.0 client id])
+AC_DEFINE_UNQUOTED(GOA_TODOIST_CLIENT_SECRET, ["$with_todoist_client_secret"], [Todoist client secret])
+if test "$enable_todoist" != "no"; then
+  AC_DEFINE(GOA_TODOIST_ENABLED, 1, [Enable Todoist data provider])
+fi
+
 # Windows Live
 AC_DEFINE(GOA_WINDOWS_LIVE_NAME, ["windows_live"], [ProviderType and extension point name])
 AC_ARG_ENABLE([windows-live],
@@ -569,6 +595,7 @@ echo "
        ownCloud provider:              ${enable_owncloud}
        Kerberos provider:              ${enable_kerberos}
        Facebook provider:              ${enable_facebook} (OAuth 2.0, id:${with_facebook_client_id})
+       Todoist provider:               ${enable_todoist} (OAuth 2.0, id:${with_todoist_client_id} 
secret:${with_todoist_client_secret})
        Windows Live provider:          ${enable_windows_live} (OAuth 2.0, id:${with_windows_live_client_id})
         Telepathy provider:             ${enable_telepathy}
         Pocket provider:                ${enable_pocket} (id:${with_pocket_client_id})
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 7250033..e5a35ca 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -21,6 +21,7 @@ src/goabackend/goapocketprovider.c
 src/goabackend/goaprovider.c
 src/goabackend/goasmtpauth.c
 src/goabackend/goatelepathyprovider.c
+src/goabackend/goatodoistprovider.c
 src/goabackend/goautils.c
 src/goabackend/goawebview.c
 src/goabackend/goawindowsliveprovider.c
diff --git a/src/goabackend/Makefile.am b/src/goabackend/Makefile.am
index 7062d77..909c55c 100644
--- a/src/goabackend/Makefile.am
+++ b/src/goabackend/Makefile.am
@@ -93,6 +93,7 @@ libgoa_backend_1_0_la_SOURCES =                                               \
        goawindowsliveprovider.h        goawindowsliveprovider.c        \
        goapocketprovider.h             goapocketprovider.c             \
        goalastfmprovider.h             goalastfmprovider.c             \
+       goatodoistprovider.h            goatodoistprovider.c            \
        goaobjectskeletonutils.h        goaobjectskeletonutils.c        \
        goautils.h                      goautils.c                      \
        goawebview.h                    goawebview.c                    \
diff --git a/src/goabackend/goaprovider.c b/src/goabackend/goaprovider.c
index e199f95..1a4374c 100644
--- a/src/goabackend/goaprovider.c
+++ b/src/goabackend/goaprovider.c
@@ -35,6 +35,7 @@
 #include "goapocketprovider.h"
 #include "goamediaserverprovider.h"
 #include "goalastfmprovider.h"
+#include "goatodoistprovider.h"
 
 #ifdef GOA_KERBEROS_ENABLED
 #include "goakerberosprovider.h"
@@ -998,6 +999,9 @@ static struct
 #ifdef GOA_LASTFM_ENABLED
   { GOA_LASTFM_NAME, goa_lastfm_provider_get_type },
 #endif
+#ifdef GOA_TODOIST_ENABLED
+  { GOA_TODOIST_NAME, goa_todoist_provider_get_type },
+#endif
 #ifdef GOA_IMAP_SMTP_ENABLED
   { GOA_IMAP_SMTP_NAME, goa_imap_smtp_provider_get_type },
 #endif
diff --git a/src/goabackend/goatodoistprovider.c b/src/goabackend/goatodoistprovider.c
new file mode 100644
index 0000000..747ea4b
--- /dev/null
+++ b/src/goabackend/goatodoistprovider.c
@@ -0,0 +1,340 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright © 2017 Ekta Nandwani
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+#include <glib/gi18n-lib.h>
+
+#include <rest/rest-proxy.h>
+#include <json-glib/json-glib.h>
+
+#include "goaprovider.h"
+#include "goaprovider-priv.h"
+#include "goaoauth2provider.h"
+#include "goaoauth2provider-priv.h"
+#include "goatodoistprovider.h"
+#include "goaobjectskeletonutils.h"
+
+
+struct _GoaTodoistProvider
+{
+  GoaOAuth2Provider parent_instance;
+};
+
+G_DEFINE_TYPE_WITH_CODE (GoaTodoistProvider, goa_todoist_provider, GOA_TYPE_OAUTH2_PROVIDER,
+                         goa_provider_ensure_extension_points_registered ();
+                         g_io_extension_point_implement (GOA_PROVIDER_EXTENSION_POINT_NAME,
+                                                         g_define_type_id,
+                                                         GOA_TODOIST_NAME,
+                                                         0));
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static const gchar *
+get_provider_type (GoaProvider *provider)
+{
+  return GOA_TODOIST_NAME;
+}
+
+static gchar *
+get_provider_name (GoaProvider *provider,
+                   GoaObject   *object)
+{
+  return g_strdup (_("Todoist"));
+}
+
+static GoaProviderGroup
+get_provider_group (GoaProvider *provider)
+{
+  return GOA_PROVIDER_GROUP_BRANDED;
+}
+
+static GoaProviderFeatures
+get_provider_features (GoaProvider *provider)
+{
+  return GOA_PROVIDER_FEATURE_TODO;
+}
+
+static const gchar *
+get_authorization_uri (GoaOAuth2Provider *oauth2_provider)
+{
+  return "https://todoist.com/oauth/authorize";;
+}
+
+static const gchar *
+get_scope (GoaOAuth2Provider *oauth2_provider)
+{
+  return "data:read_write,data:delete,task:add";
+}
+
+static const gchar *
+get_redirect_uri (GoaOAuth2Provider *oauth2_provider)
+{
+  return "https://localhost";;
+}
+
+static const gchar *
+get_client_id (GoaOAuth2Provider *oauth2_provider)
+{
+  return GOA_TODOIST_CLIENT_ID;
+}
+
+static const gchar *
+get_client_secret (GoaOAuth2Provider *oauth2_provider)
+{
+  return GOA_TODOIST_CLIENT_SECRET;
+}
+
+static const gchar *
+get_token_uri (GoaOAuth2Provider *oauth_provider)
+{
+  return "https://todoist.com/oauth/access_token";;
+}
+
+static gchar *
+build_authorization_uri (GoaOAuth2Provider  *oauth2_provider,
+                         const gchar        *authorization_uri,
+                         const gchar        *escaped_redirect_uri,
+                         const gchar        *escaped_client_id,
+                         const gchar        *escaped_scope)
+{
+  gchar *state;
+  gchar *uri;
+
+  state = g_uuid_string_random ();
+  uri = g_strdup_printf ("%s"
+                         "?response_type=token"
+                         "&client_id=%s"
+                         "&scope=%s"
+                         "&state=%s",
+                         authorization_uri,
+                         escaped_client_id,
+                         escaped_scope,
+                         state);
+
+  g_free (state);
+  return uri;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static gboolean
+is_identity_node (GoaOAuth2Provider *oauth2_provider, WebKitDOMHTMLInputElement *element)
+{
+  gboolean ret = FALSE;
+  gchar *element_type = NULL;
+  gchar *id = NULL;
+  gchar *name = NULL;
+
+  g_object_get (element, "type", &element_type, NULL);
+  if (g_strcmp0 (element_type, "email") != 0)
+    goto out;
+
+  id = webkit_dom_element_get_id (WEBKIT_DOM_ELEMENT (element));
+  if (g_strcmp0 (id, "email") != 0)
+    goto out;
+
+  ret = TRUE;
+
+ out:
+  g_free (element_type);
+  g_free (id);
+  g_free (name);
+  return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static gchar *
+get_identity_sync (GoaOAuth2Provider  *oauth2_provider,
+                   const gchar        *access_token,
+                   gchar             **out_presentation_identity,
+                   GCancellable       *cancellable,
+                   GError            **error)
+{
+  GError *identity_error = NULL;
+  RestProxy *proxy = NULL;
+  RestProxyCall *call = NULL;
+  JsonObject *json_object;
+  JsonObject *json_object_user;
+  JsonParser *parser = NULL;
+  gchar *email = NULL;
+  gchar *ret = NULL;
+
+  proxy = rest_proxy_new ("https://todoist.com/API/v7/sync";, FALSE);
+  call = rest_proxy_new_call (proxy);
+  rest_proxy_call_set_method (call, "GET");
+  rest_proxy_call_add_param (call, "token", access_token);
+  rest_proxy_call_add_param (call, "sync_token", "*");
+  rest_proxy_call_add_param (call, "resource_types", "[\"user\"]");
+
+  if (!rest_proxy_call_sync (call, error))
+    goto out;
+  if (rest_proxy_call_get_status_code (call) != 200)
+    {
+      g_set_error (error,
+                   GOA_ERROR,
+                   GOA_ERROR_FAILED,
+                   _("Expected status 200 when requesting your identity, instead got status %d (%s)"),
+                   rest_proxy_call_get_status_code (call),
+                   rest_proxy_call_get_status_message (call));
+      goto out;
+    }
+
+  parser = json_parser_new ();
+  if (!json_parser_load_from_data (parser,
+                                   rest_proxy_call_get_payload (call),
+                                   rest_proxy_call_get_payload_length (call),
+                                   &identity_error))
+    {
+      g_warning ("json_parser_load_from_data() failed: %s (%s, %d)",
+                 identity_error->message,
+                 g_quark_to_string (identity_error->domain),
+                 identity_error->code);
+      g_set_error (error,
+                   GOA_ERROR,
+                   GOA_ERROR_FAILED,
+                   _("Could not parse response"));
+      goto out;
+    }
+
+  json_object = json_node_get_object (json_parser_get_root (parser));
+  json_object_user = json_object_get_object_member (json_object, "user");
+  if (json_object_user == NULL)
+    {
+      g_warning ("Did not find user in JSON data");
+      g_set_error (error,
+                   GOA_ERROR,
+                   GOA_ERROR_FAILED,
+                   _("Could not parse response"));
+      goto out;
+    }
+
+  email = g_strdup (json_object_get_string_member (json_object_user, "email"));
+  if (email == NULL)
+    {
+      g_warning ("Did not find email in JSON data");
+      g_set_error (error,
+                   GOA_ERROR,
+                   GOA_ERROR_FAILED,
+                   _("Could not parse response"));
+      goto out;
+    }
+
+  ret = email;
+  email = NULL;
+  if (out_presentation_identity != NULL)
+    *out_presentation_identity = g_strdup (ret);
+
+ out:
+  g_clear_object (&parser);
+  g_clear_error (&identity_error);
+  g_clear_object (&call);
+  g_clear_object (&proxy);
+  g_free (email);
+  return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static gboolean
+build_object (GoaProvider         *provider,
+              GoaObjectSkeleton   *object,
+              GKeyFile            *key_file,
+              const gchar         *group,
+              GDBusConnection     *connection,
+              gboolean             just_added,
+              GError             **error)
+{
+  GoaAccount *account = NULL;
+  gboolean todo_enabled;
+  gboolean ret = FALSE;
+
+  /* Chain up */
+  if (!GOA_PROVIDER_CLASS (goa_todoist_provider_parent_class)->build_object (provider,
+                                                                             object,
+                                                                             key_file,
+                                                                             group,
+                                                                             connection,
+                                                                             just_added,
+                                                                             error))
+    goto out;
+
+  account = goa_object_get_account (GOA_OBJECT (object));
+
+  /* Todo */
+  todo_enabled = g_key_file_get_boolean (key_file, group, "TodoEnabled", NULL);
+  goa_object_skeleton_attach_todo (object, todo_enabled);
+
+  if (just_added)
+    {
+      goa_account_set_todo_disabled (account, !todo_enabled);
+
+      g_signal_connect (account,
+                        "notify::todo-disabled",
+                        G_CALLBACK (goa_util_account_notify_property_cb),
+                        "TodoEnabled");
+    }
+
+  ret = TRUE;
+
+ out:
+  g_clear_object (&account);
+  return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+add_account_key_values (GoaOAuth2Provider *oauth2_provider,
+                        GVariantBuilder   *builder)
+{
+  g_variant_builder_add (builder, "{ss}", "TodoEnabled", "true");
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+goa_todoist_provider_init (GoaTodoistProvider *self)
+{
+}
+
+static void
+goa_todoist_provider_class_init (GoaTodoistProviderClass *klass)
+{
+  GoaProviderClass *provider_class;
+  GoaOAuth2ProviderClass *oauth2_class;
+
+  provider_class = GOA_PROVIDER_CLASS (klass);
+  provider_class->get_provider_type          = get_provider_type;
+  provider_class->get_provider_name          = get_provider_name;
+  provider_class->get_provider_group         = get_provider_group;
+  provider_class->get_provider_features      = get_provider_features;
+  provider_class->build_object               = build_object;
+
+  oauth2_class = GOA_OAUTH2_PROVIDER_CLASS (klass);
+  oauth2_class->get_authorization_uri     = get_authorization_uri;
+  oauth2_class->build_authorization_uri   = build_authorization_uri;
+  oauth2_class->get_redirect_uri          = get_redirect_uri;
+  oauth2_class->get_client_id             = get_client_id;
+  oauth2_class->get_token_uri             = get_token_uri;
+  oauth2_class->get_client_secret         = get_client_secret;
+  oauth2_class->get_scope                 = get_scope;
+  oauth2_class->get_identity_sync         = get_identity_sync;
+  oauth2_class->is_identity_node          = is_identity_node;
+  oauth2_class->add_account_key_values    = add_account_key_values;
+}
diff --git a/src/goabackend/goatodoistprovider.h b/src/goabackend/goatodoistprovider.h
new file mode 100644
index 0000000..d618947
--- /dev/null
+++ b/src/goabackend/goatodoistprovider.h
@@ -0,0 +1,37 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright © 2017 Ekta Nandwani
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GOA_BACKEND_INSIDE_GOA_BACKEND_H__) && !defined (GOA_BACKEND_COMPILATION)
+#error "Only <goabackend/goabackend.h> can be included directly."
+#endif
+
+#ifndef __GOA_TODOIST_PROVIDER_H__
+#define __GOA_TODOIST_PROVIDER_H__
+
+#include <glib-object.h>
+
+#include "goaoauth2provider-priv.h"
+
+G_BEGIN_DECLS
+
+#define GOA_TYPE_TODOIST_PROVIDER   (goa_todoist_provider_get_type ())
+G_DECLARE_FINAL_TYPE (GoaTodoistProvider, goa_todoist_provider, GOA, TODOIST_PROVIDER, GoaOAuth2Provider);
+
+G_END_DECLS
+
+#endif /* __GOA_TODOIST_PROVIDER_H__ */
\ No newline at end of file


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