~ubuntu-branches/ubuntu/natty/libgdata/natty-updates

« back to all changes in this revision

Viewing changes to gdata/services/documents/gdata-documents-service.c

  • Committer: Bazaar Package Importer
  • Author(s): Robert Ancell
  • Date: 2011-01-05 11:09:00 UTC
  • mfrom: (1.1.6 upstream)
  • Revision ID: james.westby@ubuntu.com-20110105110900-gkjnbslnr18s45us
Tags: 0.8.0-0ubuntu1
* New upstream release
* debian/control:
  - Use gir1.2 packages
  - Use standards version 3.9.1
  - Add Vcs-Bzr link
  - Rename libgdata10 to libgdata11
* debian/rules:
  - Drop simple-patchsys.mk
* debian/source:
  - Use source version 3.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
34
34
 * <ulink type="http" url="http://groups.google.com/group/Google-Docs-Data-APIs/browse_thread/thread/bfc50e94e303a29a?pli=1">
35
35
 * online explanation about the problem</ulink>.
36
36
 *
 
37
 * <example>
 
38
 *      <title>Uploading a Document from Disk</title>
 
39
 *      <programlisting>
 
40
 *      GDataDocumentsService *service;
 
41
 *      GDataDocumentsDocument *document, *uploaded_document;
 
42
 *      GFile *document_file;
 
43
 *      GDataDocumentsFolder *destination_folder;
 
44
 *      GFileInfo *file_info;
 
45
 *      const gchar *slug, *content_type;
 
46
 *      GFileInputStream *file_stream;
 
47
 *      GDataUploadStream *upload_stream;
 
48
 *      GError *error = NULL;
 
49
 *
 
50
 *      /<!-- -->* Create a service *<!-- -->/
 
51
 *      service = create_documents_service ();
 
52
 *
 
53
 *      /<!-- -->* Get the document file to upload and the folder to upload it into *<!-- -->/
 
54
 *      document_file = g_file_new_for_path ("document.odt");
 
55
 *      destination_folder = query_user_for_destination_folder (service);
 
56
 *
 
57
 *      /<!-- -->* Get the file's display name and content type *<!-- -->/
 
58
 *      file_info = g_file_query_info (document_file, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME "," G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
 
59
 *                                     G_FILE_QUERY_INFO_NONE, NULL, &error);
 
60
 *
 
61
 *      if (error != NULL) {
 
62
 *              g_error ("Error getting document file information: %s", error->message);
 
63
 *              g_error_free (error);
 
64
 *              g_object_unref (destination_folder);
 
65
 *              g_object_unref (document_file);
 
66
 *              g_object_unref (service);
 
67
 *              return;
 
68
 *      }
 
69
 *
 
70
 *      slug = g_file_info_get_display_name (file_info);
 
71
 *      content_type = g_file_info_get_content_type (file_info);
 
72
 *
 
73
 *      /<!-- -->* Get an input stream for the file *<!-- -->/
 
74
 *      file_stream = g_file_read (document_file, NULL, &error);
 
75
 *
 
76
 *      g_object_unref (document_file);
 
77
 *
 
78
 *      if (error != NULL) {
 
79
 *              g_error ("Error getting document file stream: %s", error->message);
 
80
 *              g_error_free (error);
 
81
 *              g_object_unref (file_info);
 
82
 *              g_object_unref (destination_folder);
 
83
 *              g_object_unref (service);
 
84
 *              return;
 
85
 *      }
 
86
 *
 
87
 *      /<!-- -->* Create the document metadata to upload *<!-- -->/
 
88
 *      document = gdata_documents_text_new (NULL);
 
89
 *      gdata_entry_set_title (GDATA_ENTRY (document), "Document Title");
 
90
 *
 
91
 *      /<!-- -->* Get an upload stream for the document *<!-- -->/
 
92
 *      upload_stream = gdata_documents_service_upload_document (service, document, slug, content_type, destination_folder, NULL, &error);
 
93
 *
 
94
 *      g_object_unref (document);
 
95
 *      g_object_unref (file_info);
 
96
 *      g_object_unref (destination_folder);
 
97
 *
 
98
 *      if (error != NULL) {
 
99
 *              g_error ("Error getting upload stream: %s", error->message);
 
100
 *              g_error_free (error);
 
101
 *              g_object_unref (file_stream);
 
102
 *              g_object_unref (service);
 
103
 *              return;
 
104
 *      }
 
105
 *
 
106
 *      /<!-- -->* Upload the document. This is a blocking operation, and should normally be done asynchronously. *<!-- -->/
 
107
 *      g_output_stream_splice (G_OUTPUT_STREAM (upload_stream), G_INPUT_STREAM (file_stream),
 
108
 *                              G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET, NULL, &error);
 
109
 *
 
110
 *      g_object_unref (file_stream);
 
111
 *
 
112
 *      if (error != NULL) {
 
113
 *              g_error ("Error splicing streams: %s", error->message);
 
114
 *              g_error_free (error);
 
115
 *              g_object_unref (upload_stream);
 
116
 *              g_object_unref (service);
 
117
 *              return;
 
118
 *      }
 
119
 *
 
120
 *      /<!-- -->* Finish off the upload by parsing the returned updated document metadata entry *<!-- -->/
 
121
 *      uploaded_document = gdata_documents_service_finish_upload (service, upload_stream, &error);
 
122
 *
 
123
 *      g_object_unref (upload_stream);
 
124
 *      g_object_unref (service);
 
125
 *
 
126
 *      if (error != NULL) {
 
127
 *              g_error ("Error uploading document: %s", error->message);
 
128
 *              g_error_free (error);
 
129
 *              return;
 
130
 *      }
 
131
 *
 
132
 *      /<!-- -->* Do something with the uploaded document *<!-- -->/
 
133
 *
 
134
 *      g_object_unref (uploaded_document);
 
135
 *      </programlisting>
 
136
 * </example>
 
137
 *
 
138
 * The Documents service can be manipulated using batch operations, too. See the
 
139
 * <ulink type="http" url="http://code.google.com/apis/documents/docs/2.0/developers_guide_protocol.html#ACLBatch">online documentation on batch
 
140
 * operations</ulink> for more information.
 
141
 *
 
142
 * <example>
 
143
 *      <title>Performing a Batch Operation on Documents</title>
 
144
 *      <programlisting>
 
145
 *      GDataDocumentsService *service;
 
146
 *      GDataBatchOperation *operation;
 
147
 *      GDataFeed *feed;
 
148
 *      GDataLink *batch_link;
 
149
 *      GList *i;
 
150
 *      GError *error = NULL;
 
151
 *
 
152
 *      /<!-- -->* Create a service *<!-- -->/
 
153
 *      service = create_documents_service ();
 
154
 *
 
155
 *      /<!-- -->* Create the batch operation; this requires that we have done a query first so that we can get the batch link *<!-- -->/
 
156
 *      feed = do_some_query (service);
 
157
 *      batch_link = gdata_feed_look_up_link (feed, GDATA_LINK_BATCH);
 
158
 *      operation = gdata_batchable_create_operation (GDATA_BATCHABLE (service), gdata_link_get_uri (batch_link));
 
159
 *      g_object_unref (feed);
 
160
 *
 
161
 *      gdata_batch_operation_add_query (operation, presentation_entry_id_to_query, GDATA_TYPE_DOCUMENTS_PRESENTATION,
 
162
 *                                       (GDataBatchOperationCallback) batch_query_cb, user_data);
 
163
 *      gdata_batch_operation_add_insertion (operation, new_entry, (GDataBatchOperationCallback) batch_insertion_cb, user_data);
 
164
 *      gdata_batch_operation_add_update (operation, old_entry, (GDataBatchOperationCallback) batch_update_cb, user_data);
 
165
 *      gdata_batch_operation_add_deletion (operation, entry_to_delete, (GDataBatchOperationCallback) batch_deletion_cb, user_data);
 
166
 *
 
167
 *      /<!-- -->* Run the batch operation and handle the results in the various callbacks *<!-- -->/
 
168
 *      gdata_test_batch_operation_run (operation, NULL, &error);
 
169
 *
 
170
 *      g_object_unref (operation);
 
171
 *      g_object_unref (service);
 
172
 *
 
173
 *      if (error != NULL) {
 
174
 *              g_error ("Error running batch operation: %s", error->message);
 
175
 *              g_error_free (error);
 
176
 *              return;
 
177
 *      }
 
178
 *
 
179
 *      static void
 
180
 *      batch_query_cb (guint operation_id, GDataBatchOperationType operation_type, GDataEntry *entry, GError *error, gpointer user_data)
 
181
 *      {
 
182
 *              /<!-- -->* operation_type == GDATA_BATCH_OPERATION_QUERY *<!-- -->/
 
183
 *              /<!-- -->* Reference and do something with the returned entry. *<!-- -->/
 
184
 *      }
 
185
 *
 
186
 *      static void
 
187
 *      batch_insertion_cb (guint operation_id, GDataBatchOperationType operation_type, GDataEntry *entry, GError *error, gpointer user_data)
 
188
 *      {
 
189
 *              /<!-- -->* operation_type == GDATA_BATCH_OPERATION_INSERTION *<!-- -->/
 
190
 *              /<!-- -->* Reference and do something with the returned entry. *<!-- -->/
 
191
 *      }
 
192
 *
 
193
 *      static void
 
194
 *      batch_update_cb (guint operation_id, GDataBatchOperationType operation_type, GDataEntry *entry, GError *error, gpointer user_data)
 
195
 *      {
 
196
 *              /<!-- -->* operation_type == GDATA_BATCH_OPERATION_UPDATE *<!-- -->/
 
197
 *              /<!-- -->* Reference and do something with the returned entry. *<!-- -->/
 
198
 *      }
 
199
 *
 
200
 *      static void
 
201
 *      batch_deletion_cb (guint operation_id, GDataBatchOperationType operation_type, GDataEntry *entry, GError *error, gpointer user_data)
 
202
 *      {
 
203
 *              /<!-- -->* operation_type == GDATA_BATCH_OPERATION_DELETION, entry == NULL *<!-- -->/
 
204
 *      }
 
205
 *      </programlisting>
 
206
 * </example>
 
207
 *
37
208
 * Since: 0.4.0
38
209
 **/
39
210
 
62
233
static void gdata_documents_service_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec);
63
234
static void notify_authenticated_cb (GObject *service, GParamSpec *pspec, GObject *self);
64
235
static void notify_proxy_uri_cb (GObject *service, GParamSpec *pspec, GObject *self);
65
 
static GDataDocumentsEntry *upload_update_document (GDataDocumentsService *self, GDataDocumentsEntry *document, GFile *document_file,
66
 
                                                    const gchar *method, const gchar *upload_uri, GCancellable *cancellable, GError **error);
67
236
 
68
237
struct _GDataDocumentsServicePrivate {
69
238
        GDataService *spreadsheet_service;
291
460
        gdata_service_set_proxy_uri (GDATA_DOCUMENTS_SERVICE (self)->priv->spreadsheet_service, proxy_uri);
292
461
}
293
462
 
294
 
static GDataDocumentsEntry *
295
 
upload_update_document (GDataDocumentsService *self, GDataDocumentsEntry *document, GFile *document_file, const gchar *method,
296
 
                        const gchar *upload_uri, GCancellable *cancellable, GError **error)
 
463
static GDataUploadStream *
 
464
upload_update_document (GDataDocumentsService *self, GDataDocumentsDocument *document, const gchar *slug, const gchar *content_type,
 
465
                        const gchar *method, const gchar *upload_uri, GCancellable *cancellable)
297
466
{
298
 
        GDataDocumentsEntry *new_entry;
299
 
        GOutputStream *output_stream;
300
 
        GInputStream *input_stream;
301
 
        const gchar *slug = NULL, *content_type = NULL, *response_body;
302
 
        gssize response_length;
303
 
        GFileInfo *file_info = NULL;
304
 
        GType new_document_type = G_TYPE_INVALID;
305
 
        GError *child_error = NULL;
306
 
 
307
 
        /* Get some information about the file we're uploading */
308
 
        if (document_file != NULL) {
309
 
                /* Get the slug and content type */
310
 
                file_info = g_file_query_info (document_file, "standard::display-name,standard::content-type", G_FILE_QUERY_INFO_NONE, NULL, error);
311
 
                if (file_info == NULL)
312
 
                        return NULL;
313
 
 
314
 
                slug = g_file_info_get_display_name (file_info);
315
 
                content_type = g_file_info_get_content_type (file_info);
316
 
 
317
 
                /* Corrects a bug on spreadsheet content types handling
318
 
                 * The content type for ODF spreadsheets is "application/vnd.oasis.opendocument.spreadsheet" for my ODF spreadsheet;
319
 
                 * but Google Documents' spreadsheet service is waiting for "application/x-vnd.oasis.opendocument.spreadsheet"
320
 
                 * and nothing else.
321
 
                 * Bug filed with Google: http://code.google.com/p/gdata-issues/issues/detail?id=1127 */
322
 
                if (strcmp (content_type, "application/vnd.oasis.opendocument.spreadsheet") == 0)
323
 
                        content_type = "application/x-vnd.oasis.opendocument.spreadsheet";
324
 
 
325
 
                if (document == NULL) {
326
 
                        /* Get the document type of the document which is being uploaded */
327
 
                        if (strcmp (content_type, "application/x-vnd.oasis.opendocument.spreadsheet") == 0 ||
328
 
                            strcmp (content_type, "text/tab-separated-values") == 0 ||
329
 
                            strcmp (content_type, "application/x-vnd.oasis.opendocument.spreadsheet") == 0 ||
330
 
                            strcmp (content_type, "application/vnd.ms-excel") == 0) {
331
 
                                new_document_type = GDATA_TYPE_DOCUMENTS_SPREADSHEET;
332
 
                        } else if (strcmp (content_type, "application/msword") == 0 ||
333
 
                                   strcmp (content_type, "application/vnd.oasis.opendocument.text") == 0 ||
334
 
                                   strcmp (content_type, "application/rtf") == 0 ||
335
 
                                   strcmp (content_type, "text/html") == 0 ||
336
 
                                   strcmp (content_type, "application/vnd.sun.xml.writer") == 0 ||
337
 
                                   strcmp (content_type, "text/plain") == 0) {
338
 
                                new_document_type = GDATA_TYPE_DOCUMENTS_TEXT;
339
 
                        } else if (strcmp (content_type, "application/vnd.ms-powerpoint") == 0) {
340
 
                                new_document_type = GDATA_TYPE_DOCUMENTS_PRESENTATION;
341
 
                        } else {
342
 
                                g_set_error_literal (error, GDATA_DOCUMENTS_SERVICE_ERROR, GDATA_DOCUMENTS_SERVICE_ERROR_INVALID_CONTENT_TYPE,
343
 
                                                     _("The supplied document had an invalid content type."));
344
 
                                if (file_info != NULL)
345
 
                                        g_object_unref (file_info);
346
 
                                return NULL;
347
 
                        }
348
 
                }
349
 
        }
350
 
 
351
 
        /* Determine the type of the document we're uploading */
352
 
        if (document != NULL)
353
 
                new_document_type = G_OBJECT_TYPE (document);
 
467
        /* Corrects a bug on spreadsheet content types handling
 
468
         * The content type for ODF spreadsheets is "application/vnd.oasis.opendocument.spreadsheet" for my ODF spreadsheet;
 
469
         * but Google Documents' spreadsheet service is waiting for "application/x-vnd.oasis.opendocument.spreadsheet"
 
470
         * and nothing else.
 
471
         * Bug filed with Google: http://code.google.com/p/gdata-issues/issues/detail?id=1127 */
 
472
        if (strcmp (content_type, "application/vnd.oasis.opendocument.spreadsheet") == 0)
 
473
                content_type = "application/x-vnd.oasis.opendocument.spreadsheet";
354
474
 
355
475
        /* We need streaming file I/O: GDataUploadStream */
356
 
        output_stream = gdata_upload_stream_new (GDATA_SERVICE (self), method, upload_uri, GDATA_ENTRY (document), slug, content_type);
357
 
 
358
 
        if (file_info != NULL)
359
 
                g_object_unref (file_info);
360
 
        if (output_stream == NULL)
361
 
                return NULL;
362
 
 
363
 
        /* Open the document file for reading and pipe it to the upload stream */
364
 
        input_stream = G_INPUT_STREAM (g_file_read (document_file, cancellable, error));
365
 
        if (input_stream == NULL) {
366
 
                g_object_unref (output_stream);
367
 
                return NULL;
368
 
        }
369
 
 
370
 
        g_output_stream_splice (output_stream, input_stream, G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
371
 
                                cancellable, &child_error);
372
 
 
373
 
        g_object_unref (input_stream);
374
 
        if (child_error != NULL) {
375
 
                g_object_unref (output_stream);
376
 
                g_propagate_error (error, child_error);
377
 
                return NULL;
378
 
        }
379
 
 
380
 
        /* Get and parse the response from the server */
381
 
        response_body = gdata_upload_stream_get_response (GDATA_UPLOAD_STREAM (output_stream), &response_length);
382
 
        g_assert (response_body != NULL && response_length > 0);
383
 
 
384
 
        new_entry = GDATA_DOCUMENTS_ENTRY (gdata_parsable_new_from_xml (new_document_type, response_body, (gint) response_length, error));
385
 
        g_object_unref (output_stream);
386
 
 
387
 
        return new_entry;
 
476
        return GDATA_UPLOAD_STREAM (gdata_upload_stream_new (GDATA_SERVICE (self), method, upload_uri, GDATA_ENTRY (document), slug, content_type,
 
477
                                                             cancellable));
388
478
}
389
479
 
390
480
/**
391
481
 * gdata_documents_service_upload_document:
392
482
 * @self: an authenticated #GDataDocumentsService
393
 
 * @document: (allow-none): the #GDataDocumentsEntry to insert, or %NULL
394
 
 * @document_file: (allow-none): the document to upload, or %NULL
 
483
 * @document: (allow-none): the #GDataDocumentsDocument to insert, or %NULL
 
484
 * @slug: the filename to give to the uploaded document
 
485
 * @content_type: the content type of the uploaded data
395
486
 * @folder: (allow-none): the folder to which the document should be uploaded, or %NULL
396
 
 * @cancellable: optional #GCancellable object, or %NULL
 
487
 * @cancellable: (allow-none): a #GCancellable for the entire upload stream, or %NULL
397
488
 * @error: a #GError, or %NULL
398
489
 *
399
 
 * Uploads a document to Google Documents, using the properties from @document and the document file pointed to by @document_file.
400
 
 *
401
 
 * If @document is %NULL, only the document file will be uploaded. The new document entry will be named after the document file's name,
402
 
 * and will have default metadata.
403
 
 *
404
 
 * If @document_file is %NULL, only the document metadata will be uploaded. A blank document file will be created with the name
405
 
 * <literal>new document</literal> and the specified metadata. @document and @document_file cannot both be %NULL, but can both have values.
406
 
 *
407
 
 * The updated @document_entry will be returned on success, containing updated metadata.
408
 
 *
409
 
 * If there is a problem reading @document_file, an error from g_file_load_contents() or g_file_query_info() will be returned. Other errors from
410
 
 * #GDataServiceError can be returned for other exceptional conditions, as determined by the server.
411
 
 *
412
 
 * Return value: (transfer full): an updated #GDataDocumentsEntry, or %NULL; unref with g_object_unref()
413
 
 *
414
 
 * Since: 0.4.0
 
490
 * Uploads a document to Google Documents, using the properties from @document and the document data written to the resulting #GDataUploadStream. If
 
491
 * the document data does not need to be provided at the moment, just the metadata, use gdata_service_insert_entry() instead (e.g. in the case of
 
492
 * creating a new, empty file to be edited at a later date).
 
493
 *
 
494
 * If @document is %NULL, only the document data will be uploaded. The new document entry will be named using @slug, and will have default metadata.
 
495
 *
 
496
 * The stream returned by this function should be written to using the standard #GOutputStream methods, asychronously or synchronously. Once the stream
 
497
 * is closed (using g_output_stream_close()), gdata_documents_service_finish_upload() should be called on it to parse and return the updated
 
498
 * #GDataDocumentsDocument for the document. This must be done, as @document isn't updated in-place.
 
499
 *
 
500
 * In order to cancel the upload, a #GCancellable passed in to @cancellable must be cancelled using g_cancellable_cancel(). Cancelling the individual
 
501
 * #GOutputStream operations on the #GDataUploadStream will not cancel the entire upload; merely the write or close operation in question. See the
 
502
 * #GDataUploadStream:cancellable for more details.
 
503
 *
 
504
 * Any upload errors will be thrown by the stream methods, and may come from the #GDataServiceError domain.
 
505
 *
 
506
 * Return value: (transfer full): a #GDataUploadStream to write the document data to, or %NULL; unref with g_object_unref()
 
507
 *
 
508
 * Since: 0.8.0
415
509
 **/
416
 
GDataDocumentsEntry *
417
 
gdata_documents_service_upload_document (GDataDocumentsService *self, GDataDocumentsEntry *document, GFile *document_file,
 
510
GDataUploadStream *
 
511
gdata_documents_service_upload_document (GDataDocumentsService *self, GDataDocumentsDocument *document, const gchar *slug, const gchar *content_type,
418
512
                                         GDataDocumentsFolder *folder, GCancellable *cancellable, GError **error)
419
513
{
420
 
        GDataDocumentsEntry *new_document;
 
514
        GDataUploadStream *upload_stream;
421
515
        gchar *upload_uri;
422
516
 
423
517
        g_return_val_if_fail (GDATA_IS_DOCUMENTS_SERVICE (self), NULL);
424
 
        g_return_val_if_fail (document == NULL || GDATA_IS_DOCUMENTS_ENTRY (document), NULL);
425
 
        g_return_val_if_fail (document_file == NULL || G_IS_FILE (document_file), NULL);
426
 
        g_return_val_if_fail (document != NULL || document_file != NULL, NULL);
 
518
        g_return_val_if_fail (document == NULL || GDATA_IS_DOCUMENTS_DOCUMENT (document), NULL);
 
519
        g_return_val_if_fail (slug != NULL && *slug != '\0', NULL);
 
520
        g_return_val_if_fail (content_type != NULL && *content_type != '\0', NULL);
427
521
        g_return_val_if_fail (folder == NULL || GDATA_IS_DOCUMENTS_FOLDER (folder), NULL);
428
522
        g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
429
523
        g_return_val_if_fail (error == NULL || *error == NULL, NULL);
441
535
        }
442
536
 
443
537
        upload_uri = gdata_documents_service_get_upload_uri (folder);
444
 
 
445
 
        if (document_file == NULL) {
446
 
                new_document = GDATA_DOCUMENTS_ENTRY (gdata_service_insert_entry (GDATA_SERVICE (self), upload_uri, GDATA_ENTRY (document),
447
 
                                                                                  cancellable, error));
448
 
        } else {
449
 
                new_document = upload_update_document (self, document, document_file, SOUP_METHOD_POST, upload_uri, cancellable, error);
450
 
        }
 
538
        upload_stream = upload_update_document (self, document, slug, content_type, SOUP_METHOD_POST, upload_uri, cancellable);
451
539
        g_free (upload_uri);
452
540
 
453
 
        return new_document;
 
541
        return upload_stream;
454
542
}
455
543
 
456
544
/**
457
545
 * gdata_documents_service_update_document:
458
546
 * @self: a #GDataDocumentsService
459
 
 * @document: the #GDataDocumentsEntry to update
460
 
 * @document_file: (allow-none): the local document file containing the new data, or %NULL
461
 
 * @cancellable: optional #GCancellable object, or %NULL
 
547
 * @document: the #GDataDocumentsDocument to update
 
548
 * @slug: the filename to give to the uploaded document
 
549
 * @content_type: the content type of the uploaded data
 
550
 * @cancellable: (allow-none): a #GCancellable for the entire upload stream, or %NULL
462
551
 * @error: a #GError, or %NULL
463
552
 *
464
 
 * Update the document using the properties from @document and the document file pointed to by @document_file. If the document file does not
465
 
 * need to be changed, @document_file can be %NULL.
466
 
 *
467
 
 * If there is a problem reading @document_file, an error from g_file_load_contents() or g_file_query_info() will be returned. Other errors from
468
 
 * #GDataServiceError can be returned for other exceptional conditions, as determined by the server.
469
 
 *
470
 
 * For more details, see gdata_service_insert_entry().
471
 
 *
472
 
 * Return value: (transfer full): an updated #GDataDocumentsEntry, or %NULL; unref with g_object_unref()
473
 
 *
474
 
 * Since: 0.4.0
 
553
 * Update the document using the properties from @document and the document data written to the resulting #GDataUploadStream. If the document data does
 
554
 * not need to be changed, just the metadata, use gdata_service_update_entry() instead.
 
555
 *
 
556
 * The stream returned by this function should be written to using the standard #GOutputStream methods, asychronously or synchronously. Once the stream
 
557
 * is closed (using g_output_stream_close()), gdata_documents_service_finish_upload() should be called on it to parse and return the updated
 
558
 * #GDataDocumentsDocument for the document. This must be done, as @document isn't updated in-place.
 
559
 *
 
560
 * In order to cancel the update, a #GCancellable passed in to @cancellable must be cancelled using g_cancellable_cancel(). Cancelling the individual
 
561
 * #GOutputStream operations on the #GDataUploadStream will not cancel the entire update; merely the write or close operation in question. See the
 
562
 * #GDataUploadStream:cancellable for more details.
 
563
 *
 
564
 * Any upload errors will be thrown by the stream methods, and may come from the #GDataServiceError domain.
 
565
 *
 
566
 * For more information, see gdata_service_update_entry().
 
567
 *
 
568
 * Return value: (transfer full): a #GDataUploadStream to write the document data to; unref with g_object_unref()
 
569
 *
 
570
 * Since: 0.8.0
475
571
 **/
476
 
GDataDocumentsEntry *
477
 
gdata_documents_service_update_document (GDataDocumentsService *self, GDataDocumentsEntry *document, GFile *document_file,
 
572
GDataUploadStream *
 
573
gdata_documents_service_update_document (GDataDocumentsService *self, GDataDocumentsDocument *document, const gchar *slug, const gchar *content_type,
478
574
                                         GCancellable *cancellable, GError **error)
479
575
{
480
576
        GDataLink *update_link;
481
577
 
482
578
        g_return_val_if_fail (GDATA_IS_DOCUMENTS_SERVICE (self), NULL);
483
 
        g_return_val_if_fail (GDATA_IS_DOCUMENTS_ENTRY (document), NULL);
484
 
        g_return_val_if_fail (document_file == NULL || G_IS_FILE (document_file), NULL);
 
579
        g_return_val_if_fail (GDATA_IS_DOCUMENTS_DOCUMENT (document), NULL);
 
580
        g_return_val_if_fail (slug != NULL && *slug != '\0', NULL);
 
581
        g_return_val_if_fail (content_type != NULL && *content_type != '\0', NULL);
485
582
        g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
486
583
        g_return_val_if_fail (error == NULL || *error == NULL, NULL);
487
584
 
491
588
                return NULL;
492
589
        }
493
590
 
494
 
        if (document_file == NULL)
495
 
                return GDATA_DOCUMENTS_ENTRY (gdata_service_update_entry (GDATA_SERVICE (self), GDATA_ENTRY (document), cancellable, error));
496
 
 
497
591
        update_link = gdata_entry_look_up_link (GDATA_ENTRY (document), GDATA_LINK_EDIT_MEDIA);
498
592
        g_assert (update_link != NULL);
499
593
 
500
 
        return upload_update_document (self, document, document_file, SOUP_METHOD_PUT, gdata_link_get_uri (update_link), cancellable, error);
501
 
}
502
 
 
503
 
/**
504
 
 * gdata_documents_service_move_document_to_folder:
 
594
        return upload_update_document (self, document, slug, content_type, SOUP_METHOD_PUT, gdata_link_get_uri (update_link), cancellable);
 
595
}
 
596
 
 
597
/**
 
598
 * gdata_documents_service_finish_upload:
 
599
 * @self: a #GDataDocumentsService
 
600
 * @upload_stream: the #GDataUploadStream from the operation
 
601
 * @error: a #GError, or %NULL
 
602
 *
 
603
 * Finish off a document upload or update operation started by gdata_documents_service_upload_document() or gdata_documents_service_update_document(),
 
604
 * parsing the result and returning the new or updated #GDataDocumentsDocument.
 
605
 *
 
606
 * If an error occurred during the upload or update operation, it will have been returned during the operation (e.g. by g_output_stream_splice() or one
 
607
 * of the other stream methods). In such a case, %NULL will be returned but @error will remain unset. @error is only set in the case that the server
 
608
 * indicates that the operation was successful, but an error is encountered in parsing the result sent by the server.
 
609
 *
 
610
 * In the case that no #GDataDocumentsDocument was passed (to gdata_documents_service_upload_document() or gdata_documents_service_update_document())
 
611
 * when starting the operation, %GDATA_DOCUMENTS_SERVICE_ERROR_INVALID_CONTENT_TYPE will be thrown in @error if the content type of the uploaded data
 
612
 * could not be mapped to a document type with which to interpret the response from the server.
 
613
 *
 
614
 * Return value: (transfer full): the new or updated #GDataDocumentsDocument, or %NULL; unref with g_object_unref()
 
615
 *
 
616
 * Since: 0.8.0
 
617
 */
 
618
GDataDocumentsDocument *
 
619
gdata_documents_service_finish_upload (GDataDocumentsService *self, GDataUploadStream *upload_stream, GError **error)
 
620
{
 
621
        const gchar *response_body, *content_type;
 
622
        gssize response_length;
 
623
        GDataEntry *entry;
 
624
        GType new_document_type = G_TYPE_INVALID;
 
625
 
 
626
        /* Determine the type of the document we've uploaded */
 
627
        entry = gdata_upload_stream_get_entry (upload_stream);
 
628
        content_type = gdata_upload_stream_get_content_type (upload_stream);
 
629
 
 
630
        if (entry != NULL) {
 
631
                new_document_type = G_OBJECT_TYPE (entry);
 
632
        } else if (strcmp (content_type, "application/x-vnd.oasis.opendocument.spreadsheet") == 0 ||
 
633
                   strcmp (content_type, "text/tab-separated-values") == 0 ||
 
634
                   strcmp (content_type, "application/x-vnd.oasis.opendocument.spreadsheet") == 0 ||
 
635
                   strcmp (content_type, "application/vnd.ms-excel") == 0) {
 
636
                new_document_type = GDATA_TYPE_DOCUMENTS_SPREADSHEET;
 
637
        } else if (strcmp (content_type, "application/msword") == 0 ||
 
638
                   strcmp (content_type, "application/vnd.oasis.opendocument.text") == 0 ||
 
639
                   strcmp (content_type, "application/rtf") == 0 ||
 
640
                   strcmp (content_type, "text/html") == 0 ||
 
641
                   strcmp (content_type, "application/vnd.sun.xml.writer") == 0 ||
 
642
                   strcmp (content_type, "text/plain") == 0) {
 
643
                new_document_type = GDATA_TYPE_DOCUMENTS_TEXT;
 
644
        } else if (strcmp (content_type, "application/vnd.ms-powerpoint") == 0) {
 
645
                new_document_type = GDATA_TYPE_DOCUMENTS_PRESENTATION;
 
646
        }
 
647
 
 
648
        if (g_type_is_a (new_document_type, GDATA_TYPE_DOCUMENTS_DOCUMENT) == FALSE) {
 
649
                g_set_error (error, GDATA_DOCUMENTS_SERVICE_ERROR, GDATA_DOCUMENTS_SERVICE_ERROR_INVALID_CONTENT_TYPE,
 
650
                             _("The content type of the supplied document ('%s') could not be recognized."), content_type);
 
651
                return NULL;
 
652
        }
 
653
 
 
654
        /* Get and parse the response from the server */
 
655
        response_body = gdata_upload_stream_get_response (upload_stream, &response_length);
 
656
        if (response_body == NULL || response_length == 0)
 
657
                return NULL;
 
658
 
 
659
        return GDATA_DOCUMENTS_DOCUMENT (gdata_parsable_new_from_xml (new_document_type, response_body, (gint) response_length, error));
 
660
}
 
661
 
 
662
/**
 
663
 * gdata_documents_service_add_entry_to_folder:
505
664
 * @self: an authenticated #GDataDocumentsService
506
 
 * @document: the #GDataDocumentsEntry to move
507
 
 * @folder: the #GDataDocumentsFolder to move @document into
 
665
 * @entry: the #GDataDocumentsEntry to move
 
666
 * @folder: the #GDataDocumentsFolder to move @entry into
508
667
 * @cancellable: optional #GCancellable object, or %NULL
509
668
 * @error: a #GError, or %NULL
510
669
 *
511
 
 * Move the given @document to the specified @folder. If the document is already in another folder, it will be added to
512
 
 * the new folder, but will also remain in any previous folders.
 
670
 * Add the given @entry to the specified @folder, and return an updated #GDataDocumentsEntry for @entry. If the @entry is already in another folder, it
 
671
 * will be added to the new folder, but will also remain  in its other folders. Note that @entry can be either a #GDataDocumentsDocument or a
 
672
 * #GDataDocumentsFolder.
513
673
 *
514
 
 * Errors from #GDataServiceError can be returned for other exceptional conditions, as determined by the server.
 
674
 * Errors from #GDataServiceError can be returned for exceptional conditions, as determined by the server.
515
675
 *
516
676
 * Return value: (transfer full): an updated #GDataDocumentsEntry, or %NULL; unref with g_object_unref()
517
677
 *
518
 
 * Since: 0.4.0
 
678
 * Since: 0.8.0
519
679
 **/
520
680
GDataDocumentsEntry *
521
 
gdata_documents_service_move_document_to_folder (GDataDocumentsService *self, GDataDocumentsEntry *document, GDataDocumentsFolder *folder,
522
 
                                                 GCancellable *cancellable, GError **error)
 
681
gdata_documents_service_add_entry_to_folder (GDataDocumentsService *self, GDataDocumentsEntry *entry, GDataDocumentsFolder *folder,
 
682
                                             GCancellable *cancellable, GError **error)
523
683
{
524
 
        GDataDocumentsEntry *new_document;
 
684
        GDataDocumentsEntry *new_entry;
525
685
        gchar *uri, *upload_data;
526
686
        const gchar *folder_id;
527
687
        SoupMessage *message;
528
688
        guint status;
529
689
 
530
690
        g_return_val_if_fail (GDATA_IS_DOCUMENTS_SERVICE (self), NULL);
531
 
        g_return_val_if_fail (GDATA_IS_DOCUMENTS_ENTRY (document), NULL);
 
691
        g_return_val_if_fail (GDATA_IS_DOCUMENTS_ENTRY (entry), NULL);
532
692
        g_return_val_if_fail (GDATA_IS_DOCUMENTS_FOLDER (folder), NULL);
533
693
        g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
534
694
        g_return_val_if_fail (error == NULL || *error == NULL, NULL);
535
695
 
536
696
        if (gdata_service_is_authenticated (GDATA_SERVICE (self)) == FALSE) {
537
697
                g_set_error_literal (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_AUTHENTICATION_REQUIRED,
538
 
                                     _("You must be authenticated to move documents."));
 
698
                                     _("You must be authenticated to move documents and folders."));
539
699
                return NULL;
540
700
        }
541
701
 
547
707
        g_free (uri);
548
708
 
549
709
        /* Append the data */
550
 
        upload_data = gdata_parsable_get_xml (GDATA_PARSABLE (document));
 
710
        upload_data = gdata_parsable_get_xml (GDATA_PARSABLE (entry));
551
711
        soup_message_set_request (message, "application/atom+xml", SOUP_MEMORY_TAKE, upload_data, strlen (upload_data));
552
712
 
553
713
        /* Send the message */
567
727
                return NULL;
568
728
        }
569
729
 
570
 
        /* Parse the XML; and update the document */
 
730
        /* Parse the XML; and update the entry */
571
731
        g_assert (message->response_body->data != NULL);
572
 
        new_document = GDATA_DOCUMENTS_ENTRY (gdata_parsable_new_from_xml (G_OBJECT_TYPE (document), message->response_body->data,
573
 
                                                                           message->response_body->length, error));
 
732
        new_entry = GDATA_DOCUMENTS_ENTRY (gdata_parsable_new_from_xml (G_OBJECT_TYPE (entry), message->response_body->data,
 
733
                                                                        message->response_body->length, error));
574
734
        g_object_unref (message);
575
735
 
576
 
        return new_document;
577
 
}
578
 
 
579
 
/**
580
 
 * gdata_documents_service_remove_document_from_folder:
581
 
 * @self: a #GDataDocumentsService
582
 
 * @document: the #GDataDocumentsEntry to remove
583
 
 * @folder: the #GDataDocumentsFolder from which we should remove @document
584
 
 * @cancellable: optional #GCancellable object, or %NULL
585
 
 * @error: a #GError, or %NULL
586
 
 *
587
 
 * Remove the #GDataDocumentsEntry @document from the GDataDocumentsFolder @folder, and updates the document entry @document.
588
 
 *
589
 
 * Errors from #GDataServiceError can be returned for other exceptional conditions, as determined by the server.
590
 
 *
591
 
 * Return value: (transfer full): an updated #GDataDocumentsEntry, or %NULL; unref with g_object_unref()
592
 
 *
593
 
 * Since: 0.4.0
594
 
 **/
595
 
GDataDocumentsEntry *
596
 
gdata_documents_service_remove_document_from_folder (GDataDocumentsService *self, GDataDocumentsEntry *document, GDataDocumentsFolder *folder,
597
 
                                                     GCancellable *cancellable, GError **error)
598
 
{
599
 
        const gchar *folder_id, *document_id;
 
736
        return new_entry;
 
737
}
 
738
 
 
739
typedef struct {
 
740
        GDataDocumentsEntry *entry;
 
741
        GDataDocumentsFolder *folder;
 
742
} AddEntryToFolderData;
 
743
 
 
744
static void
 
745
add_entry_to_folder_data_free (AddEntryToFolderData *data)
 
746
{
 
747
        g_object_unref (data->entry);
 
748
        g_object_unref (data->folder);
 
749
        g_slice_free (AddEntryToFolderData, data);
 
750
}
 
751
 
 
752
static void
 
753
add_entry_to_folder_thread (GSimpleAsyncResult *result, GDataDocumentsService *service, GCancellable *cancellable)
 
754
{
 
755
        GDataDocumentsEntry *updated_entry;
 
756
        AddEntryToFolderData *data;
 
757
        GError *error = NULL;
 
758
 
 
759
        data = g_simple_async_result_get_op_res_gpointer (result);
 
760
 
 
761
        /* Add the entry to the folder and return */
 
762
        updated_entry = gdata_documents_service_add_entry_to_folder (service, data->entry, data->folder, cancellable, &error);
 
763
        if (error != NULL) {
 
764
                g_simple_async_result_set_from_error (result, error);
 
765
                g_error_free (error);
 
766
                return;
 
767
        }
 
768
 
 
769
        /* Return the updated entry */
 
770
        g_simple_async_result_set_op_res_gpointer (result, updated_entry, (GDestroyNotify) g_object_unref);
 
771
}
 
772
 
 
773
/**
 
774
 * gdata_documents_service_add_entry_to_folder_async:
 
775
 * @self: a #GDataDocumentsService
 
776
 * @entry: the #GDataDocumentsEntry to add to @folder
 
777
 * @folder: the #GDataDocumentsFolder to add @entry to
 
778
 * @cancellable: optional #GCancellable object, or %NULL
 
779
 * @callback: a #GAsyncReadyCallback to call when the operation is finished, or %NULL
 
780
 * @user_data: (closure): data to pass to the @callback function
 
781
 *
 
782
 * Add the given @entry to the specified @folder. @self, @entry and @folder are all reffed when this function is called, so can safely be unreffed
 
783
 * after this function returns.
 
784
 *
 
785
 * For more details, see gdata_documents_service_add_entry_to_folder(), which is the synchronous version of this function.
 
786
 *
 
787
 * When the operation is finished, @callback will be called. You can then call gdata_documents_service_add_entry_to_folder_finish() to get the results
 
788
 * of the operation.
 
789
 *
 
790
 * Since: 0.8.0
 
791
 **/
 
792
void
 
793
gdata_documents_service_add_entry_to_folder_async (GDataDocumentsService *self, GDataDocumentsEntry *entry, GDataDocumentsFolder *folder,
 
794
                                                   GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
 
795
{
 
796
        GSimpleAsyncResult *result;
 
797
        AddEntryToFolderData *data;
 
798
 
 
799
        g_return_if_fail (GDATA_IS_DOCUMENTS_SERVICE (self));
 
800
        g_return_if_fail (GDATA_IS_DOCUMENTS_ENTRY (entry));
 
801
        g_return_if_fail (GDATA_IS_DOCUMENTS_FOLDER (folder));
 
802
        g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
 
803
 
 
804
        data = g_slice_new (AddEntryToFolderData);
 
805
        data->entry = g_object_ref (entry);
 
806
        data->folder = g_object_ref (folder);
 
807
 
 
808
        result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, gdata_documents_service_add_entry_to_folder_async);
 
809
        g_simple_async_result_set_op_res_gpointer (result, data, (GDestroyNotify) add_entry_to_folder_data_free);
 
810
        g_simple_async_result_run_in_thread (result, (GSimpleAsyncThreadFunc) add_entry_to_folder_thread, G_PRIORITY_DEFAULT, cancellable);
 
811
        g_object_unref (result);
 
812
}
 
813
 
 
814
/**
 
815
 * gdata_documents_service_add_entry_to_folder_finish:
 
816
 * @self: a #GDataDocumentsService
 
817
 * @async_result: a #GAsyncResult
 
818
 * @error: a #GError, or %NULL
 
819
 *
 
820
 * Finish an asynchronous operation to add a #GDataDocumentsEntry to a folder started with gdata_documents_service_add_entry_to_folder_async().
 
821
 *
 
822
 * Return value: (transfer full): an updated #GDataDocumentsEntry, or %NULL; unref with g_object_unref()
 
823
 *
 
824
 * Since: 0.8.0
 
825
 **/
 
826
GDataDocumentsEntry *
 
827
gdata_documents_service_add_entry_to_folder_finish (GDataDocumentsService *self, GAsyncResult *async_result, GError **error)
 
828
{
 
829
        GSimpleAsyncResult *result = G_SIMPLE_ASYNC_RESULT (async_result);
 
830
        GDataDocumentsEntry *entry;
 
831
 
 
832
        g_return_val_if_fail (GDATA_IS_DOCUMENTS_SERVICE (self), NULL);
 
833
        g_return_val_if_fail (G_IS_ASYNC_RESULT (async_result), NULL);
 
834
        g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
 
835
 
 
836
        g_warn_if_fail (g_simple_async_result_get_source_tag (result) == gdata_documents_service_add_entry_to_folder_async);
 
837
 
 
838
        if (g_simple_async_result_propagate_error (result, error) == TRUE)
 
839
                return NULL;
 
840
 
 
841
        entry = g_simple_async_result_get_op_res_gpointer (result);
 
842
        if (entry != NULL)
 
843
                return g_object_ref (entry);
 
844
 
 
845
        g_assert_not_reached ();
 
846
}
 
847
 
 
848
/**
 
849
 * gdata_documents_service_remove_entry_from_folder:
 
850
 * @self: a #GDataDocumentsService
 
851
 * @entry: the #GDataDocumentsEntry to remove
 
852
 * @folder: the #GDataDocumentsFolder from which we should remove @entry
 
853
 * @cancellable: optional #GCancellable object, or %NULL
 
854
 * @error: a #GError, or %NULL
 
855
 *
 
856
 * Remove the given @entry from @folder, and return an updated #GDataDocumentsEntry for @entry. @entry will remain a member of any other folders it's
 
857
 * currently in. Note that @entry can be either a #GDataDocumentsDocument or a #GDataDocumentsFolder.
 
858
 *
 
859
 * Errors from #GDataServiceError can be returned for exceptional conditions, as determined by the server.
 
860
 *
 
861
 * Return value: (transfer full): an updated #GDataDocumentsEntry, or %NULL; unref with g_object_unref()
 
862
 *
 
863
 * Since: 0.8.0
 
864
 **/
 
865
GDataDocumentsEntry *
 
866
gdata_documents_service_remove_entry_from_folder (GDataDocumentsService *self, GDataDocumentsEntry *entry, GDataDocumentsFolder *folder,
 
867
                                                  GCancellable *cancellable, GError **error)
 
868
{
 
869
        const gchar *folder_id, *entry_id;
600
870
        SoupMessage *message;
601
871
        guint status;
602
872
        gchar *uri;
603
873
 
604
874
        g_return_val_if_fail (GDATA_IS_DOCUMENTS_SERVICE (self), NULL);
605
 
        g_return_val_if_fail (GDATA_IS_DOCUMENTS_ENTRY (document), NULL);
 
875
        g_return_val_if_fail (GDATA_IS_DOCUMENTS_ENTRY (entry), NULL);
606
876
        g_return_val_if_fail (GDATA_IS_DOCUMENTS_FOLDER (folder), NULL);
607
877
        g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
608
878
        g_return_val_if_fail (error == NULL || *error == NULL, NULL);
609
879
 
610
880
        if (gdata_service_is_authenticated (GDATA_SERVICE (self)) == FALSE) {
611
881
                g_set_error_literal (error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_AUTHENTICATION_REQUIRED,
612
 
                                     _("You must be authenticated to move documents."));
 
882
                                     _("You must be authenticated to move documents and folders."));
613
883
                return NULL;
614
884
        }
615
885
 
616
886
        /* Get the document ID */
617
887
        folder_id = gdata_documents_entry_get_document_id (GDATA_DOCUMENTS_ENTRY (folder));
618
 
        document_id = gdata_documents_entry_get_document_id (GDATA_DOCUMENTS_ENTRY (document));
 
888
        entry_id = gdata_documents_entry_get_document_id (entry);
619
889
        g_assert (folder_id != NULL);
620
 
        g_assert (document_id != NULL);
 
890
        g_assert (entry_id != NULL);
621
891
 
622
 
        if (GDATA_IS_DOCUMENTS_PRESENTATION (document)) {
 
892
        if (GDATA_IS_DOCUMENTS_PRESENTATION (entry)) {
623
893
                uri = _gdata_service_build_uri (FALSE, "http://docs.google.com/feeds/folders/private/full/folder%%3A%s/presentation%%3A%s",
624
 
                                                folder_id, document_id);
625
 
        } else if (GDATA_IS_DOCUMENTS_SPREADSHEET (document)) {
 
894
                                                folder_id, entry_id);
 
895
        } else if (GDATA_IS_DOCUMENTS_SPREADSHEET (entry)) {
626
896
                uri = _gdata_service_build_uri (FALSE, "http://docs.google.com/feeds/folders/private/full/folder%%3A%s/spreadsheet%%3A%s",
627
 
                                                folder_id, document_id);
628
 
        } else if (GDATA_IS_DOCUMENTS_TEXT (document)) {
 
897
                                                folder_id, entry_id);
 
898
        } else if (GDATA_IS_DOCUMENTS_TEXT (entry)) {
629
899
                uri = _gdata_service_build_uri (FALSE, "http://docs.google.com/feeds/folders/private/full/folder%%3A%s/document%%3A%s",
630
 
                                                folder_id, document_id);
631
 
        } else if (GDATA_IS_DOCUMENTS_FOLDER (document)) {
 
900
                                                folder_id, entry_id);
 
901
        } else if (GDATA_IS_DOCUMENTS_FOLDER (entry)) {
632
902
                uri = _gdata_service_build_uri (FALSE, "http://docs.google.com/feeds/folders/private/full/folder%%3A%s/folder%%3A%s",
633
 
                                                folder_id, document_id);
 
903
                                                folder_id, entry_id);
634
904
        } else {
635
905
                g_assert_not_reached ();
636
906
        }
637
907
 
638
 
        message = _gdata_service_build_message (GDATA_SERVICE (self), SOUP_METHOD_DELETE, uri, gdata_entry_get_etag (GDATA_ENTRY (document)), TRUE);
 
908
        message = _gdata_service_build_message (GDATA_SERVICE (self), SOUP_METHOD_DELETE, uri, gdata_entry_get_etag (GDATA_ENTRY (entry)), TRUE);
639
909
        g_free (uri);
640
910
 
641
911
        /* Send the message */
659
929
 
660
930
        /* Google's servers don't return an updated copy of the entry, so we have to query for it again.
661
931
         * See: http://code.google.com/p/gdata-issues/issues/detail?id=1380 */
662
 
        return GDATA_DOCUMENTS_ENTRY (gdata_service_query_single_entry (GDATA_SERVICE (self), gdata_entry_get_id (GDATA_ENTRY (document)), NULL,
663
 
                                                                        G_OBJECT_TYPE (document), cancellable, error));
 
932
        return GDATA_DOCUMENTS_ENTRY (gdata_service_query_single_entry (GDATA_SERVICE (self), gdata_entry_get_id (GDATA_ENTRY (entry)), NULL,
 
933
                                                                        G_OBJECT_TYPE (entry), cancellable, error));
 
934
}
 
935
 
 
936
typedef struct {
 
937
        GDataDocumentsEntry *entry;
 
938
        GDataDocumentsFolder *folder;
 
939
} RemoveEntryFromFolderData;
 
940
 
 
941
static void
 
942
remove_entry_from_folder_data_free (RemoveEntryFromFolderData *data)
 
943
{
 
944
        g_object_unref (data->entry);
 
945
        g_object_unref (data->folder);
 
946
        g_slice_free (RemoveEntryFromFolderData, data);
 
947
}
 
948
 
 
949
static void
 
950
remove_entry_from_folder_thread (GSimpleAsyncResult *result, GDataDocumentsService *service, GCancellable *cancellable)
 
951
{
 
952
        GDataDocumentsEntry *updated_entry;
 
953
        RemoveEntryFromFolderData *data;
 
954
        GError *error = NULL;
 
955
 
 
956
        data = g_simple_async_result_get_op_res_gpointer (result);
 
957
 
 
958
        /* Remove the entry from the folder and return */
 
959
        updated_entry = gdata_documents_service_remove_entry_from_folder (service, data->entry, data->folder, cancellable, &error);
 
960
        if (error != NULL) {
 
961
                g_simple_async_result_set_from_error (result, error);
 
962
                g_error_free (error);
 
963
                return;
 
964
        }
 
965
 
 
966
        /* Return the updated entry */
 
967
        g_simple_async_result_set_op_res_gpointer (result, updated_entry, (GDestroyNotify) g_object_unref);
 
968
}
 
969
 
 
970
/**
 
971
 * gdata_documents_service_remove_entry_from_folder_async:
 
972
 * @self: a #GDataDocumentsService
 
973
 * @entry: the #GDataDocumentsEntry to remove from @folder
 
974
 * @folder: the #GDataDocumentsFolder to remove @entry from
 
975
 * @cancellable: optional #GCancellable object, or %NULL
 
976
 * @callback: a #GAsyncReadyCallback to call when the operation is finished, or %NULL
 
977
 * @user_data: (closure): data to pass to the @callback function
 
978
 *
 
979
 * Remove the given @entry from the specified @folder. @self, @entry and @folder are all reffed when this function is called, so can safely be unreffed
 
980
 * after this function returns.
 
981
 *
 
982
 * For more details, see gdata_documents_service_remove_entry_from_folder(), which is the synchronous version of this function.
 
983
 *
 
984
 * When the operation is finished, @callback will be called. You can then call gdata_documents_service_remove_entry_from_folder_finish() to get the
 
985
 * results of the operation.
 
986
 *
 
987
 * Since: 0.8.0
 
988
 **/
 
989
void
 
990
gdata_documents_service_remove_entry_from_folder_async (GDataDocumentsService *self, GDataDocumentsEntry *entry, GDataDocumentsFolder *folder,
 
991
                                                        GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
 
992
{
 
993
        GSimpleAsyncResult *result;
 
994
        RemoveEntryFromFolderData *data;
 
995
 
 
996
        g_return_if_fail (GDATA_IS_DOCUMENTS_SERVICE (self));
 
997
        g_return_if_fail (GDATA_IS_DOCUMENTS_ENTRY (entry));
 
998
        g_return_if_fail (GDATA_IS_DOCUMENTS_FOLDER (folder));
 
999
        g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
 
1000
 
 
1001
        data = g_slice_new (RemoveEntryFromFolderData);
 
1002
        data->entry = g_object_ref (entry);
 
1003
        data->folder = g_object_ref (folder);
 
1004
 
 
1005
        result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, gdata_documents_service_remove_entry_from_folder_async);
 
1006
        g_simple_async_result_set_op_res_gpointer (result, data, (GDestroyNotify) remove_entry_from_folder_data_free);
 
1007
        g_simple_async_result_run_in_thread (result, (GSimpleAsyncThreadFunc) remove_entry_from_folder_thread, G_PRIORITY_DEFAULT, cancellable);
 
1008
        g_object_unref (result);
 
1009
}
 
1010
 
 
1011
/**
 
1012
 * gdata_documents_service_remove_entry_from_folder_finish:
 
1013
 * @self: a #GDataDocumentsService
 
1014
 * @async_result: a #GAsyncResult
 
1015
 * @error: a #GError, or %NULL
 
1016
 *
 
1017
 * Finish an asynchronous operation to remove a #GDataDocumentsEntry from a folder started with
 
1018
 * gdata_documents_service_remove_entry_from_folder_async().
 
1019
 *
 
1020
 * Return value: (transfer full): an updated #GDataDocumentsEntry, or %NULL; unref with g_object_unref()
 
1021
 *
 
1022
 * Since: 0.8.0
 
1023
 **/
 
1024
GDataDocumentsEntry *
 
1025
gdata_documents_service_remove_entry_from_folder_finish (GDataDocumentsService *self, GAsyncResult *async_result, GError **error)
 
1026
{
 
1027
        GSimpleAsyncResult *result = G_SIMPLE_ASYNC_RESULT (async_result);
 
1028
        GDataDocumentsEntry *entry;
 
1029
 
 
1030
        g_return_val_if_fail (GDATA_IS_DOCUMENTS_SERVICE (self), NULL);
 
1031
        g_return_val_if_fail (G_IS_ASYNC_RESULT (async_result), NULL);
 
1032
        g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
 
1033
 
 
1034
        g_warn_if_fail (g_simple_async_result_get_source_tag (result) == gdata_documents_service_remove_entry_from_folder_async);
 
1035
 
 
1036
        if (g_simple_async_result_propagate_error (result, error) == TRUE)
 
1037
                return NULL;
 
1038
 
 
1039
        entry = g_simple_async_result_get_op_res_gpointer (result);
 
1040
        if (entry != NULL)
 
1041
                return g_object_ref (entry);
 
1042
 
 
1043
        g_assert_not_reached ();
664
1044
}
665
1045
 
666
1046
/**