diff options
author | Philip Withnall <philip@tecnocode.co.uk> | 2019-07-30 15:46:04 +0000 |
---|---|---|
committer | Philip Withnall <philip@tecnocode.co.uk> | 2019-07-30 15:46:04 +0000 |
commit | 5780a533aaebcee1f56a778ebdf8b70d32ba393e (patch) | |
tree | dde2935200c02b9b7087b20468fa759cd9ee322a /gdata/services | |
parent | f07d87f511a381b7c67b218b4255932208d49180 (diff) | |
parent | b4243d78b9f1df9a0d9d6af7b0ffeaa100b79b0a (diff) | |
download | libgdata-5780a533aaebcee1f56a778ebdf8b70d32ba393e.tar.gz |
Merge branch 'wip/app-properties/api' into 'master'
Drive v2 Properties API
See merge request GNOME/libgdata!7
Diffstat (limited to 'gdata/services')
-rw-r--r-- | gdata/services/documents/gdata-documents-entry.c | 265 | ||||
-rw-r--r-- | gdata/services/documents/gdata-documents-entry.h | 6 | ||||
-rw-r--r-- | gdata/services/documents/gdata-documents-property.c | 472 | ||||
-rw-r--r-- | gdata/services/documents/gdata-documents-property.h | 106 | ||||
-rw-r--r-- | gdata/services/documents/gdata-documents-service.c | 14 | ||||
-rw-r--r-- | gdata/services/documents/meson.build | 2 |
6 files changed, 864 insertions, 1 deletions
diff --git a/gdata/services/documents/gdata-documents-entry.c b/gdata/services/documents/gdata-documents-entry.c index 535c82eb..65bfff02 100644 --- a/gdata/services/documents/gdata-documents-entry.c +++ b/gdata/services/documents/gdata-documents-entry.c @@ -98,6 +98,7 @@ #include <glib.h> #include <glib/gi18n-lib.h> +#include "gdata-comparable.h" #include "gdata-documents-entry.h" #include "gdata-documents-entry-private.h" #include "gdata-parser.h" @@ -133,6 +134,7 @@ struct _GDataDocumentsEntryPrivate { GDataAuthor *last_modified_by; goffset quota_used; /* bytes */ goffset file_size; /* bytes */ + GList *properties; /* GDataDocumentsProperty */ }; enum { @@ -384,6 +386,9 @@ gdata_entry_dispose (GObject *object) g_object_unref (priv->last_modified_by); priv->last_modified_by = NULL; + g_list_free_full (priv->properties, g_object_unref); + priv->properties = NULL; + /* Chain up to the parent class */ G_OBJECT_CLASS (gdata_documents_entry_parent_class)->dispose (object); } @@ -459,6 +464,69 @@ gdata_documents_entry_set_property (GObject *object, guint property_id, const GV } static void +get_key_value_and_visibility (JsonReader *reader, gchar **out_key, gchar **out_value, gchar **out_visibility, GError **error) +{ + g_autoptr(GError) child_error = NULL; + g_autofree gchar *key = NULL; + g_autofree gchar *value = NULL; + g_autofree gchar *visibility = NULL; + gboolean success = FALSE; + guint i, members; + + for (i = 0, members = (guint) json_reader_count_members (reader); i < members; i++) { + json_reader_read_element (reader, i); + + if (gdata_parser_string_from_json_member (reader, "key", P_REQUIRED | P_NON_EMPTY, &key, &success, &child_error) == TRUE) { + if (!success && child_error != NULL) { + g_propagate_prefixed_error (error, g_steal_pointer (&child_error), + /* Translators: the parameter is an error message */ + _("Error parsing JSON: %s"), + "Failed to find ‘key’."); + json_reader_end_element (reader); + return; + } + } + + if (gdata_parser_string_from_json_member (reader, "visibility", P_REQUIRED | P_NON_EMPTY, &visibility, &success, &child_error) == TRUE) { + if (!success && child_error != NULL) { + g_propagate_prefixed_error (error, g_steal_pointer (&child_error), + /* Translators: the parameter is an error message */ + _("Error parsing JSON: %s"), + "Failed to find ‘visibility’."); + json_reader_end_element (reader); + return; + } + } + + /* A Property can have a value field to be an empty string */ + if (gdata_parser_string_from_json_member (reader, "value", P_DEFAULT, &value, &success, &child_error) == TRUE) { + if (!success && child_error != NULL) { + g_propagate_prefixed_error (error, g_steal_pointer (&child_error), + /* Translators: the parameter is an error message */ + _("Error parsing JSON: %s"), + "Failed to find ‘value’."); + json_reader_end_element (reader); + return; + } + } + + json_reader_end_element (reader); + } + + if (out_key != NULL) { + *out_key = g_steal_pointer (&key); + } + + if (out_visibility != NULL) { + *out_visibility = g_steal_pointer (&visibility); + } + + if (out_value != NULL) { + *out_value = g_steal_pointer (&value); + } +} + +static void get_kind_email_and_name (JsonReader *reader, gchar **out_kind, gchar **out_email, gchar **out_name, GError **error) { GError *child_error = NULL; @@ -807,6 +875,66 @@ parse_json (GDataParsable *parsable, JsonReader *reader, gpointer user_data, GEr } return success; + } else if (g_strcmp0 (json_reader_get_member_name (reader), "properties") == 0) { + guint i, elements; + + if (!json_reader_is_array (reader)) { + g_set_error (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_PROTOCOL_ERROR, + /* Translators: the parameter is an error message */ + _("Error parsing JSON: %s"), + "JSON node ‘properties’ is not an array."); + return FALSE; + } + + /* Loop through the properties array. */ + for (i = 0, elements = (guint) json_reader_count_elements (reader); success && i < elements; i++) { + g_autofree gchar *key = NULL; + g_autofree gchar *value = NULL; + g_autofree gchar *visibility = NULL; + g_autoptr(GDataDocumentsProperty) property = NULL; + + json_reader_read_element (reader, i); + + if (!json_reader_is_object (reader)) { + g_set_error (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_PROTOCOL_ERROR, + /* Translators: the parameter is an error message */ + _("Error parsing JSON: %s"), + "JSON node inside ‘properties’ is not an object."); + success = FALSE; + goto continue_properties; + } + + get_key_value_and_visibility (reader, &key, &value, &visibility, &child_error); + + if (child_error != NULL) { + g_propagate_error (error, child_error); + success = FALSE; + goto continue_properties; + } + + if (g_strcmp0 (visibility, GDATA_DOCUMENTS_PROPERTY_VISIBILITY_PUBLIC) && + g_strcmp0 (visibility, GDATA_DOCUMENTS_PROPERTY_VISIBILITY_PRIVATE)) { + g_set_error (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_PROTOCOL_ERROR, + /* Translators: the parameter is the invalid value of visibility variable */ + _("Invalid visibility: unrecognised value ‘%s’"), + visibility); + success = FALSE; + goto continue_properties; + } + + g_assert (key != NULL); + + property = gdata_documents_property_new (key); + gdata_documents_property_set_visibility (property, visibility); + gdata_documents_property_set_value (property, value); + + gdata_documents_entry_add_documents_property (GDATA_DOCUMENTS_ENTRY (parsable), property); + + continue_properties: + json_reader_end_element (reader); + } + + return success; } return GDATA_PARSABLE_CLASS (gdata_documents_entry_parent_class)->parse_json (parsable, reader, user_data, error); @@ -842,7 +970,7 @@ static void get_json (GDataParsable *parsable, JsonBuilder *builder) { GList *i; - GList *parent_folders_list; + GList *parent_folders_list, *documents_properties_list; const gchar *mime_type; GDATA_PARSABLE_CLASS (gdata_documents_entry_parent_class)->get_json (parsable, builder); @@ -880,6 +1008,42 @@ get_json (GDataParsable *parsable, JsonBuilder *builder) g_list_free (parent_folders_list); + /* Set all the properties */ + json_builder_set_member_name (builder, "properties"); + json_builder_begin_array (builder); + + documents_properties_list = gdata_documents_entry_get_document_properties (GDATA_DOCUMENTS_ENTRY (parsable)); + for (i = documents_properties_list; i != NULL; i = i->next) { + GDataDocumentsProperty *property = GDATA_DOCUMENTS_PROPERTY (i->data); + const gchar *key = NULL; + const gchar *value = NULL; + const gchar *visibility = NULL; + + if (property != NULL) { + key = gdata_documents_property_get_key (property); + value = gdata_documents_property_get_value (property); + visibility = gdata_documents_property_get_visibility (property); + + json_builder_begin_object (builder); + + json_builder_set_member_name (builder, "key"); + json_builder_add_string_value (builder, key); + + json_builder_set_member_name (builder, "visibility"); + json_builder_add_string_value (builder, visibility); + + json_builder_set_member_name (builder, "value"); + if (value == NULL) { + json_builder_add_null_value (builder); + } else { + json_builder_add_string_value (builder, value); + } + + json_builder_end_object (builder); + } + } + + json_builder_end_array (builder); } static void @@ -1156,3 +1320,102 @@ gdata_documents_entry_is_deleted (GDataDocumentsEntry *self) g_return_val_if_fail (GDATA_IS_DOCUMENTS_ENTRY (self), FALSE); return self->priv->is_deleted; } + + +/** + * gdata_documents_entry_get_document_properties: + * @self: a #GDataDocumentsEntry + * + * Gets a list of the #GDataDocumentsPropertys for this entry. + * + * Return value: (transfer none) (element-type GDataDocumentsProperty): a #GList of pointers to #GDataDocumentsPropertys + * + * Since: 0.18.0 + */ +GList * +gdata_documents_entry_get_document_properties (GDataDocumentsEntry *self) +{ + g_return_val_if_fail (GDATA_IS_DOCUMENTS_ENTRY (self), NULL); + return self->priv->properties; +} + +/** + * gdata_documents_entry_add_documents_property: + * @self: a #GDataDocumentsEntry + * @property: a #GDataDocumentsProperty + * + * Inserts/updates @property on "properties" list in @self. Since, a GDataDocumentsProperty is uniquely identified by #GDataDocumentsProperty:key and #GDataDocumentsProperty:visibility, if no such property exists in the "properties" list, then a @property will be appended to the list after incrementing the reference count. + * + * In case that there already exists a @property in "properties", the #GDataDocumentsProperty inside the list will be updated to @property. Note that #GDataDocumentsProperty:value has no role in determining the uniqueness of a #GDataDocumentsProperty. + * + * The changes made by this function will be local only and you need to explicity update @self by calling gdata_service_update_entry(). + * + * Return value: %TRUE if the @property doesn't exist in the "properties" list, or that #GDataDocumentsProperty:value of @property has been set to %NULL by a call to gdata_documents_entry_remove_documents_property(). %FALSE if @property exists in "properties" list, or in the case that @self or @property aren't of proper types. + * + * Since: 0.18.0 + */ +gboolean +gdata_documents_entry_add_documents_property (GDataDocumentsEntry *self, GDataDocumentsProperty *property) { + GList *l = NULL; + gboolean ret_val = TRUE; + + g_return_val_if_fail (GDATA_IS_DOCUMENTS_ENTRY (self), FALSE); + g_return_val_if_fail (GDATA_IS_DOCUMENTS_PROPERTY (property), FALSE); + + l = g_list_find_custom (self->priv->properties, property, (GCompareFunc) gdata_comparable_compare); + + if (l == NULL) { + self->priv->properties = g_list_prepend (self->priv->properties, g_object_ref (property)); + } else { + GDataDocumentsProperty *_prop = GDATA_DOCUMENTS_PROPERTY (l->data); + if (gdata_documents_property_get_value (_prop) != NULL) + ret_val = FALSE; + gdata_documents_property_set_value (_prop, gdata_documents_property_get_value (property)); + } + + return ret_val; +} + +/** + * gdata_documents_entry_remove_documents_property: + * @self: a #GDataDocumentsEntry + * @property: a #GDataDocumentsProperty + * + * The property specified by @property will be removed from the "properties" list on @self. + * + * Only #GDataDocumentsProperty:key and #GDataDocumentsProperty:visibility will be used to find @property in "properties" list. #GDataDocumentsProperty:value has no role in determining the uniqueness of a #GDataDocumentsProperty. + * + * The changes made by this function will be local only and you need to explicity update @self by calling gdata_service_update_entry(). + * + * Return value: %TRUE if @property has been successfully removed from "properties" list on @self, %FALSE otherwise. + * + * Since: 0.18.0 + */ +gboolean +gdata_documents_entry_remove_documents_property (GDataDocumentsEntry *self, GDataDocumentsProperty *property) { + GList *l; + + g_return_val_if_fail (GDATA_IS_DOCUMENTS_ENTRY (self), FALSE); + g_return_val_if_fail (GDATA_IS_DOCUMENTS_PROPERTY (property), FALSE); + + l = g_list_find_custom (self->priv->properties, property, (GCompareFunc) gdata_comparable_compare); + + if (l == NULL) { + return FALSE; + } + + /* Google Drive has this weird behaviour that setting the properties + * array to empty with file/update method doesn't empties the array. + * For eg. Suppose we initially did an update with properties array + * with value [1, 2, 3]. If we again update the properties array with + * [1, 5, 6], the final resulting properties array will be [1, 2, 3, 5, + * 6] meaning that the values get appended on subsequent updates. + * + * The way to counter this is to set the "value" field in the Property + * Resource to NULL, which removes that Property Resource from the + * properties array. + */ + gdata_documents_property_set_value ((GDataDocumentsProperty *) l->data, NULL); + + return TRUE; +} diff --git a/gdata/services/documents/gdata-documents-entry.h b/gdata/services/documents/gdata-documents-entry.h index 07e05abf..558b91d2 100644 --- a/gdata/services/documents/gdata-documents-entry.h +++ b/gdata/services/documents/gdata-documents-entry.h @@ -26,6 +26,7 @@ #include <gdata/gdata-entry.h> #include <gdata/gdata-service.h> #include <gdata/atom/gdata-author.h> +#include <gdata/services/documents/gdata-documents-property.h> G_BEGIN_DECLS @@ -119,6 +120,11 @@ goffset gdata_documents_entry_get_file_size (GDataDocumentsEntry *self) G_GNUC_P gboolean gdata_documents_entry_is_deleted (GDataDocumentsEntry *self) G_GNUC_PURE; +GList *gdata_documents_entry_get_document_properties (GDataDocumentsEntry *self); + +gboolean gdata_documents_entry_add_documents_property (GDataDocumentsEntry *self, GDataDocumentsProperty *property); +gboolean gdata_documents_entry_remove_documents_property (GDataDocumentsEntry *self, GDataDocumentsProperty *property); + G_END_DECLS #endif /* !GDATA_DOCUMENTS_ENTRY_H */ diff --git a/gdata/services/documents/gdata-documents-property.c b/gdata/services/documents/gdata-documents-property.c new file mode 100644 index 00000000..f57744a2 --- /dev/null +++ b/gdata/services/documents/gdata-documents-property.c @@ -0,0 +1,472 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* + * GData Client + * Copyright (C) Mayank Sharma <mayank8019@gmail.com> + * + * GData Client 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.1 of the License, or (at your option) any later version. + * + * GData Client 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 GData Client. If not, see <http://www.gnu.org/licenses/>. + */ + +/** + * SECTION:gdata-documents-property + * @short_description: GData Documents property object + * @stability: Unstable + * @include: gdata/gdata-property.h + * + * #GDataDocumentsProperty is a subclass of #GDataParsable and represents a Google Drive Property Resource on a file object. + * + * It allows applications to store additional metadata on a file, such as tags, IDs from other data stores, viewing preferences etc. Properties can be used to share metadata between applications, for example, in a workflow application. + * + * Each #GDataDocumentsProperty is characterized by a key-value pair (where value is optional, and takes empty string "" by default) and a visibility parameter. The visibility can take values "PUBLIC" for public properties and "PRIVATE" for private properties (default). Private properties are accessible only by the application which set them, but public properties can be read/written by other applications as well. + * + * Since: 0.18.0 + */ + +#include <glib.h> +#include <gdata/gdata-parsable.h> +#include <json-glib/json-glib.h> + +#include "gdata-documents-property.h" +#include "gdata-parser.h" +#include "gdata-comparable.h" + +static void gdata_documents_property_comparable_init (GDataComparableIface *iface); +static void gdata_documents_property_finalize (GObject *object); +static void gdata_documents_property_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); +static void gdata_documents_property_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); + +static void _gdata_documents_property_set_key (GDataDocumentsProperty *self, const gchar *key); +static void _gdata_documents_property_set_etag (GDataDocumentsProperty *self, const gchar *etag); + +static gboolean parse_json (GDataParsable *parsable, JsonReader *reader, gpointer user_data, GError **error); +static void get_json (GDataParsable *parsable, JsonBuilder *builder); + +struct _GDataDocumentsPropertyPrivate { + gchar *key; + gchar *etag; + gchar *value; /* default - empty string ("") */ + gchar *visibility; /* default - "PRIVATE" */ +}; + +enum { + PROP_KEY = 1, + PROP_ETAG, + PROP_VALUE, + PROP_VISIBILITY +}; + +G_DEFINE_TYPE_WITH_CODE (GDataDocumentsProperty, gdata_documents_property, GDATA_TYPE_PARSABLE, + G_IMPLEMENT_INTERFACE (GDATA_TYPE_COMPARABLE, gdata_documents_property_comparable_init)) + +static void +gdata_documents_property_class_init (GDataDocumentsPropertyClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GDataParsableClass *parsable_class = GDATA_PARSABLE_CLASS (klass); + + g_type_class_add_private (klass, sizeof (GDataDocumentsPropertyPrivate)); + + gobject_class->set_property = gdata_documents_property_set_property; + gobject_class->get_property = gdata_documents_property_get_property; + gobject_class->finalize = gdata_documents_property_finalize; + + parsable_class->parse_json = parse_json; + parsable_class->get_json = get_json; + parsable_class->element_name = "property"; + + /** + * GDataDocumentsProperty:key: + * + * The key of this property. + * + * For more information, see the <ulink type="http" url="https://developers.google.com/drive/api/v2/reference/properties">Properties Resource</ulink> + * + * Since: 0.18.0 + */ + g_object_class_install_property (gobject_class, PROP_KEY, + g_param_spec_string ("key", + "Key", "The key of this property.", + NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + /** + * GDataDocumentsProperty:etag: + * + * ETag of the property. + * + * For more information, see the <ulink type="http" url="https://developers.google.com/drive/api/v2/reference/properties">Properties Resource</ulink> + * + * Since: 0.18.0 + */ + g_object_class_install_property (gobject_class, PROP_ETAG, + g_param_spec_string ("etag", + "ETag", "ETag of the property.", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + + /** + * GDataDocumentsProperty:value: + * + * The value of this property. By default, it takes the an empty string (""). + * + * For more information, see the <ulink type="http" url="https://developers.google.com/drive/api/v2/reference/properties">Properties Resource</ulink> + * + * Since: 0.18.0 + */ + g_object_class_install_property (gobject_class, PROP_VALUE, + g_param_spec_string ("value", + "Value", "The value of this property.", + NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + /** + * GDataDocumentsProperty:visibility: + * + * The visibility status of this property. The default value of + * visibility is PRIVATE on a Drive Properties Resource object, + * hence #GDataDocumentsProperty:visibility is %GDATA_DOCUMENTS_PROPERTY_VISIBILITY_PRIVATE + * by default. A private property restricts its visibility to only the app which created it. + * + * For more information, see the <ulink type="http" url="https://developers.google.com/drive/api/v2/reference/properties">Properties Resource</ulink> + * + * Since: 0.18.0 + */ + g_object_class_install_property (gobject_class, PROP_VISIBILITY, + g_param_spec_string ("visibility", + "Visibility", "The visibility of this property.", + NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); +} + +static gint +compare_with (GDataComparable *self, GDataComparable *other) +{ + + GDataDocumentsPropertyPrivate *a = ((GDataDocumentsProperty*) self)->priv, *b = ((GDataDocumentsProperty*) other)->priv; + + if (g_strcmp0 (a->key, b->key) == 0 && g_strcmp0 (a->visibility, b->visibility) == 0) + return 0; + return 1; +} + +static gboolean +parse_json (GDataParsable *parsable, JsonReader *reader, gpointer user_data, GError **error) +{ + gchar *output_val = NULL; + gboolean success = TRUE, is_key_parsed = FALSE; + + if (gdata_parser_string_from_json_member (reader, "key", P_DEFAULT, &output_val, &success, error) == TRUE) { + if (success && output_val != NULL && output_val[0] != '\0') { + _gdata_documents_property_set_key (GDATA_DOCUMENTS_PROPERTY (parsable), output_val); + } + + is_key_parsed = TRUE; + } else if (gdata_parser_string_from_json_member (reader, "etag", P_DEFAULT, &output_val, &success, error) == TRUE) { + if (success && output_val != NULL && output_val[0] != '\0') { + _gdata_documents_property_set_etag (GDATA_DOCUMENTS_PROPERTY (parsable), output_val); + } + + is_key_parsed = TRUE; + } else if (gdata_parser_string_from_json_member (reader, "value", P_DEFAULT, &output_val, &success, error) == TRUE) { + + /* A Property can have a value field to be an empty string, but + * never NULL */ + if (success && output_val != NULL) { + gdata_documents_property_set_value (GDATA_DOCUMENTS_PROPERTY (parsable), output_val); + } + + is_key_parsed = TRUE; + } else if (gdata_parser_string_from_json_member (reader, "visibility", P_REQUIRED | P_NON_EMPTY, &output_val, &success, error) == TRUE) { + gdata_documents_property_set_visibility (GDATA_DOCUMENTS_PROPERTY (parsable), output_val); + + is_key_parsed = TRUE; + } + + if (is_key_parsed) { + g_free (output_val); + return success; + } + + /* Chain up to the parent class */ + return GDATA_PARSABLE_CLASS (gdata_documents_property_parent_class)->parse_json (parsable, reader, user_data, error); +} + +static void +get_json (GDataParsable *parsable, JsonBuilder *builder) +{ + + GDataDocumentsPropertyPrivate *priv = GDATA_DOCUMENTS_PROPERTY (parsable)->priv; + + /* Add all the Property specific JSON members */ + g_assert (priv->key != NULL); + json_builder_set_member_name (builder, "key"); + json_builder_add_string_value (builder, priv->key); + + if (gdata_documents_property_get_etag (GDATA_DOCUMENTS_PROPERTY (parsable)) != NULL) { + json_builder_set_member_name (builder, "etag"); + json_builder_add_string_value (builder, priv->etag); + } + + /* Setting the "value" field of a Property Resource deletes that Property Resource */ + json_builder_set_member_name (builder, "value"); + json_builder_add_string_value (builder, priv->value); + + json_builder_set_member_name (builder, "visibility"); + json_builder_add_string_value (builder, priv->visibility); +} + +static void +gdata_documents_property_comparable_init (GDataComparableIface *iface) +{ + iface->compare_with = compare_with; +} + +static void +gdata_documents_property_init (GDataDocumentsProperty *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GDATA_TYPE_DOCUMENTS_PROPERTY, GDataDocumentsPropertyPrivate); + + /* Google Drive sets the default value of a Property Resource to be an empty string (""), + * and visibility is %GDATA_DOCUMENTS_PROPERTY_VISIBILITY_PRIVATE by default */ + self->priv->value = g_strdup (""); + self->priv->visibility = g_strdup (GDATA_DOCUMENTS_PROPERTY_VISIBILITY_PRIVATE); +} + +static void +gdata_documents_property_finalize (GObject *object) +{ + GDataDocumentsPropertyPrivate *priv = GDATA_DOCUMENTS_PROPERTY (object)->priv; + + g_free (priv->key); + g_free (priv->etag); + g_free (priv->value); + g_free (priv->visibility); + + /* Chain up to the parent class */ + G_OBJECT_CLASS (gdata_documents_property_parent_class)->finalize (object); +} + +static void +gdata_documents_property_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) +{ + GDataDocumentsPropertyPrivate *priv = GDATA_DOCUMENTS_PROPERTY (object)->priv; + + switch (property_id) { + case PROP_KEY: + g_value_set_string (value, priv->key); + break; + case PROP_ETAG: + g_value_set_string (value, priv->etag); + break; + case PROP_VALUE: + g_value_set_string (value, priv->value); + break; + case PROP_VISIBILITY: + g_value_set_string (value, priv->visibility); + break; + default: + /* We don't have any other property... */ + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gdata_documents_property_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) +{ + GDataDocumentsProperty *self = GDATA_DOCUMENTS_PROPERTY (object); + + switch (property_id) { + case PROP_KEY: + _gdata_documents_property_set_key (self, g_value_get_string (value)); + break; + case PROP_ETAG: + _gdata_documents_property_set_etag (self, g_value_get_string (value)); + break; + case PROP_VALUE: + gdata_documents_property_set_value (self, g_value_get_string (value)); + break; + case PROP_VISIBILITY: + gdata_documents_property_set_visibility (self, g_value_get_string (value)); + break; + default: + /* We don't have any other property... */ + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +/** + * gdata_documents_property_new: + * @key: the property's key + * + * Creates a new #GDataEntry with the given ID and default properties. + * + * Return value: (transfer full): a new #GDataDocumentsProperty; unref with g_object_unref() + * + * Since: 0.18.0 + */ +GDataDocumentsProperty * +gdata_documents_property_new (const gchar *key) +{ + /* GDataDocumentsProperty must have a non NULL key at initilization time, + * rest of the properties can be NULL or take their default values. */ + g_return_val_if_fail (key != NULL && key[0] != '\0', NULL); + + return GDATA_DOCUMENTS_PROPERTY (g_object_new (GDATA_TYPE_DOCUMENTS_PROPERTY, "key", key, NULL)); +} + +/** + * gdata_documents_property_get_key: + * @self: a #GDataDocumentsProperty + * + * Returns the key of the property. This will never be %NULL or an empty string (""). + * + * Return value: (transfer none): the property's key + * + * Since: 0.18.0 + */ +const gchar * +gdata_documents_property_get_key (GDataDocumentsProperty *self) +{ + g_return_val_if_fail (GDATA_IS_DOCUMENTS_PROPERTY (self), NULL); + return self->priv->key; +} + +static void +_gdata_documents_property_set_key (GDataDocumentsProperty *self, const gchar *key) +{ + /* This is a READ-ONLY PROPERTY */ + g_return_if_fail (GDATA_IS_DOCUMENTS_PROPERTY (self)); + g_return_if_fail (key != NULL && *key != '\0'); + + g_free (self->priv->key); + self->priv->key = g_strdup (key); + + g_object_notify (G_OBJECT (self), "key"); +} + +/** + * gdata_documents_property_get_etag: + * @self: a #GDataDocumentsProperty + * + * Returns the ETag of the property. + * + * Return value: (transfer none): the property's ETag. The ETag will never be empty; it's either %NULL or a valid ETag. + * + * Since: 0.18.0 + */ +const gchar * +gdata_documents_property_get_etag (GDataDocumentsProperty *self) +{ + g_return_val_if_fail (GDATA_IS_DOCUMENTS_PROPERTY (self), NULL); + return self->priv->etag; +} + +static void +_gdata_documents_property_set_etag (GDataDocumentsProperty *self, const gchar *etag) +{ + g_return_if_fail (GDATA_IS_DOCUMENTS_PROPERTY (self)); + + if (g_strcmp0 (self->priv->etag, etag) != 0) { + g_free (self->priv->etag); + self->priv->etag = g_strdup (etag); + g_object_notify (G_OBJECT (self), "etag"); + } +} + +/** + * gdata_documents_property_get_value: + * @self: a #GDataDocumentsProperty + * + * Returns the value of the property. + * + * In the case that this value is %NULL, the Property Resource corresponding to @self will be deleted from the properties array on a file's metadata, whereas in the case that it's empty string (""), it will be set as it is. + * + * Return value: (nullable): the property's value. This can be %NULL or empty. + * + * Since: 0.18.0 + */ +const gchar * +gdata_documents_property_get_value (GDataDocumentsProperty *self) +{ + g_return_val_if_fail (GDATA_IS_DOCUMENTS_PROPERTY (self), NULL); + return self->priv->value; +} + +/** + * gdata_documents_property_set_value: + * @self: a #GDataDocumentsProperty + * @value: (allow-none): the new value of the property + * + * Sets #GDataDocumentsProperty:value to @value, corresponding to the key. + * + * In the case that @value is %NULL, the Property Resource corresponding to @self will be deleted from the properties array on a file's metadata, whereas in the case that it's empty string (""), it will be set as it is. + * + * Since: 0.18.0 + */ +void +gdata_documents_property_set_value (GDataDocumentsProperty *self, const gchar *value) +{ + g_return_if_fail (GDATA_IS_DOCUMENTS_PROPERTY (self)); + + if (g_strcmp0 (self->priv->value, value) != 0) { + g_free (self->priv->value); + self->priv->value = g_strdup (value); + g_object_notify (G_OBJECT (self), "value"); + } +} + +/** + * gdata_documents_property_get_visibility: + * @self: a #GDataDocumentsProperty + * + * Returns the visibility status of the property. + * + * Return value: %GDATA_DOCUMENTS_PROPERTY_VISIBILITY_PUBLIC if the #GDataDocumentsProperty is publicly visible to other + * apps, %GDATA_DOCUMENTS_PROPERTY_VISIBILITY_PRIVATE if the #GDataDocumentsProperty is restricted to the application which + * created it. + * + * Since: 0.18.0 + */ +const gchar * +gdata_documents_property_get_visibility (GDataDocumentsProperty *self) +{ + g_return_val_if_fail (GDATA_IS_DOCUMENTS_PROPERTY (self), NULL); + return self->priv->visibility; +} + +/** + * gdata_documents_property_set_visibility: + * @self: a #GDataDocumentsProperty + * @visibility: the new visibility status of the property + * + * Sets #GDataDocumentsProperty:visibility to %GDATA_DOCUMENTS_PROPERTY_VISIBILITY_PUBLIC for + * public properties and %GDATA_DOCUMENTS_PROPERTY_VISIBILITY_PRIVATE for + * private properties (default). + * + * Since: 0.18.0 + */ +void +gdata_documents_property_set_visibility (GDataDocumentsProperty *self, const gchar *visibility) +{ + g_return_if_fail (GDATA_IS_DOCUMENTS_PROPERTY (self)); + g_return_if_fail (g_strcmp0 (visibility, GDATA_DOCUMENTS_PROPERTY_VISIBILITY_PUBLIC) == 0 || + g_strcmp0 (visibility, GDATA_DOCUMENTS_PROPERTY_VISIBILITY_PRIVATE) == 0); + + if (g_strcmp0 (self->priv->visibility, visibility) != 0) { + g_free (self->priv->visibility); + self->priv->visibility = g_strdup (visibility); + g_object_notify (G_OBJECT (self), "visibility"); + } +} diff --git a/gdata/services/documents/gdata-documents-property.h b/gdata/services/documents/gdata-documents-property.h new file mode 100644 index 00000000..84f8d644 --- /dev/null +++ b/gdata/services/documents/gdata-documents-property.h @@ -0,0 +1,106 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* + * GData Client + * Copyright (C) Mayank Sharma <mayank8019@gmail.com> + * + * GData Client 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.1 of the License, or (at your option) any later version. + * + * GData Client 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 GData Client. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef GDATA_DOCUMENTS_PROPERTY_H +#define GDATA_DOCUMENTS_PROPERTY_H + +#include <glib.h> +#include <glib-object.h> + +#include <gdata/gdata-parsable.h> + +G_BEGIN_DECLS + +/** + * GDATA_DOCUMENTS_PROPERTY_VISIBILITY_PUBLIC: + * + * The #GDataDocumentsProperty having the visibility set to TRUE corresponds to having the visibility property + * on a Drive Property Resource + * set to "PUBLIC". This makes the Property Resource visible to other apps. + * + * Since: 0.18.0 + */ +#define GDATA_DOCUMENTS_PROPERTY_VISIBILITY_PUBLIC "PUBLIC" + +/** + * GDATA_DOCUMENTS_PROPERTY_VISIBILITY_PRIVATE: + * + * The #GDataDocumentsProperty having the visibility set to FALSE (default) corresponds to having the visibility property on a Drive Property Resource + * set to "PRIVATE". This makes the Property Resource accessible only by the app that created it. + * + * Since: 0.18.0 + */ +#define GDATA_DOCUMENTS_PROPERTY_VISIBILITY_PRIVATE "PRIVATE" + +#define GDATA_TYPE_DOCUMENTS_PROPERTY (gdata_documents_property_get_type ()) +#define GDATA_DOCUMENTS_PROPERTY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDATA_TYPE_DOCUMENTS_PROPERTY, GDataDocumentsProperty)) +#define GDATA_DOCUMENTS_PROPERTY_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GDATA_TYPE_DOCUMENTS_PROPERTY, GDataDocumentsPropertyClass)) +#define GDATA_IS_DOCUMENTS_PROPERTY(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDATA_TYPE_DOCUMENTS_PROPERTY)) +#define GDATA_IS_DOCUMENTS_PROPERTY_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GDATA_TYPE_DOCUMENTS_PROPERTY)) +#define GDATA_DOCUMENTS_PROPERTY_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDATA_TYPE_DOCUMENTS_PROPERTY, GDataDocumentsPropertyClass)) + +typedef struct _GDataDocumentsPropertyPrivate GDataDocumentsPropertyPrivate; + +/** + * GDataDocumentsProperty: + * + * All the fields in the #GDataDocumentsProperty structure are private and should never be accessed directly. + * + * Since: 0.18.0 + */ +typedef struct { + GDataParsable parent; + GDataDocumentsPropertyPrivate *priv; +} GDataDocumentsProperty; + +/** + * GDataDocumentsPropertyClass: + * + * All the fields in the #GDataDocumentsPropertyClass structure are private and should never be accessed directly. + * + * Since: 0.18.0 + */ +typedef struct { + /*< private >*/ + GDataParsableClass parent; + + /*< private >*/ + /* Padding for future expansion */ + void (*_g_reserved0) (void); + void (*_g_reserved1) (void); +} GDataDocumentsPropertyClass; + +GType gdata_documents_property_get_type (void) G_GNUC_CONST; +G_DEFINE_AUTOPTR_CLEANUP_FUNC (GDataDocumentsProperty, g_object_unref) + +GDataDocumentsProperty *gdata_documents_property_new (const gchar *key) G_GNUC_WARN_UNUSED_RESULT G_GNUC_MALLOC; + +const gchar *gdata_documents_property_get_key (GDataDocumentsProperty *self) G_GNUC_PURE; + +const gchar *gdata_documents_property_get_etag (GDataDocumentsProperty *self) G_GNUC_PURE; + +const gchar *gdata_documents_property_get_value (GDataDocumentsProperty *self) G_GNUC_PURE; +void gdata_documents_property_set_value (GDataDocumentsProperty *self, const gchar *value); + +const gchar *gdata_documents_property_get_visibility (GDataDocumentsProperty *self) G_GNUC_PURE; +void gdata_documents_property_set_visibility (GDataDocumentsProperty *self, const gchar *visibility); + +G_END_DECLS + +#endif /* !GDATA_DOCUMENTS_PROPERTY_H */ diff --git a/gdata/services/documents/gdata-documents-service.c b/gdata/services/documents/gdata-documents-service.c index 524f93f6..f0c919ee 100644 --- a/gdata/services/documents/gdata-documents-service.c +++ b/gdata/services/documents/gdata-documents-service.c @@ -258,6 +258,7 @@ #include <libsoup/soup.h> #include <string.h> +#include "gdata-documents-property.h" #include "gdata-documents-service.h" #include "gdata-documents-utils.h" #include "gdata-batchable.h" @@ -1193,6 +1194,7 @@ gdata_documents_service_add_entry_to_folder (GDataDocumentsService *self, GDataD gchar *uri; SoupMessage *message; guint status; + GList *l; g_return_val_if_fail (GDATA_IS_DOCUMENTS_SERVICE (self), NULL); g_return_val_if_fail (GDATA_IS_DOCUMENTS_ENTRY (entry), NULL); @@ -1226,6 +1228,18 @@ gdata_documents_service_add_entry_to_folder (GDataDocumentsService *self, GDataD gdata_documents_utils_add_content_type (local_entry, content_type); add_folder_link_to_entry (local_entry, folder); + for (l = gdata_documents_entry_get_document_properties (entry); l != NULL; l = l->next) { + GDataDocumentsProperty *old_prop; + g_autoptr(GDataDocumentsProperty) new_prop = NULL; + + old_prop = GDATA_DOCUMENTS_PROPERTY (l->data); + + new_prop = gdata_documents_property_new (gdata_documents_property_get_key (old_prop)); + gdata_documents_property_set_value (new_prop, gdata_documents_property_get_value (old_prop)); + gdata_documents_property_set_visibility (new_prop, gdata_documents_property_get_visibility (old_prop)); + gdata_documents_entry_add_documents_property (local_entry, new_prop); + } + message = _gdata_service_build_message (GDATA_SERVICE (self), get_documents_authorization_domain (), SOUP_METHOD_POST, uri, NULL, FALSE); g_free (uri); diff --git a/gdata/services/documents/meson.build b/gdata/services/documents/meson.build index 6ff2bec1..a0984839 100644 --- a/gdata/services/documents/meson.build +++ b/gdata/services/documents/meson.build @@ -12,6 +12,7 @@ headers = files( 'gdata-documents-metadata.h', 'gdata-documents-pdf.h', 'gdata-documents-presentation.h', + 'gdata-documents-property.h', 'gdata-documents-query.h', 'gdata-documents-service.h', 'gdata-documents-spreadsheet.h', @@ -36,6 +37,7 @@ sources += files( 'gdata-documents-metadata.c', 'gdata-documents-pdf.c', 'gdata-documents-presentation.c', + 'gdata-documents-property.c', 'gdata-documents-query.c', 'gdata-documents-service.c', 'gdata-documents-spreadsheet.c', |