[gnome-software/wip/rancell/reviews-3-18] Work on writing review UI / plugin



commit edf7e65ec0642606e6db7e1b799bb7ade990075e
Author: Robert Ancell <robert ancell canonical com>
Date:   Thu Jan 14 17:02:55 2016 +1300

    Work on writing review UI / plugin

 configure.ac                           |    1 +
 po/POTFILES.in                         |    2 +
 src/Makefile.am                        |    3 +
 src/gnome-software.gresource.xml       |    1 +
 src/gs-app-review-dialog.c             |   93 +++++++++++++
 src/gs-app-review-dialog.h             |   42 ++++++
 src/gs-app-review-dialog.ui            |  236 ++++++++++++++++++++++++++++++++
 src/gs-app-review.h                    |    5 -
 src/gs-app.c                           |   26 ++++-
 src/gs-app.h                           |    3 +
 src/gs-plugin-loader.c                 |    5 +
 src/gs-plugin-loader.h                 |    1 +
 src/gs-plugin.h                        |    4 +
 src/gs-shell-details.c                 |   53 +++++++
 src/plugins/Makefile.am                |    3 +-
 src/plugins/gs-plugin-ubuntu-reviews.c |  165 ++++++++++++++++++++++
 16 files changed, 636 insertions(+), 7 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 6324f62..13c0ac4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -68,6 +68,7 @@ PKG_CHECK_MODULES(GSETTINGS_DESKTOP_SCHEMAS, gsettings-desktop-schemas >= 3.11.5
 PKG_CHECK_MODULES(GNOME_DESKTOP, gnome-desktop-3.0 >= 3.17.92)
 PKG_CHECK_MODULES(POLKIT, polkit-gobject-1)
 PKG_CHECK_MODULES(JSON_GLIB, json-glib-1.0 >= 0.12)
+PKG_CHECK_MODULES(OAUTH, oauth)
 AC_PATH_PROG(APPSTREAM_UTIL, [appstream-util], [unfound])
 AC_ARG_ENABLE(man,
               [AS_HELP_STRING([--enable-man],
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 736f564..a083269 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -7,6 +7,8 @@ src/gnome-software-local-file.desktop.in
 src/gs-app-folder-dialog.c
 src/gs-application.c
 src/gs-app-addon-row.c
+src/gs-app-review-dialog.c
+[type: gettext/glade]src/gs-app-review-dialog.ui
 src/gs-app-review-row.c
 [type: gettext/glade]src/gs-app-review-row.ui
 src/gs-app.c
diff --git a/src/Makefile.am b/src/Makefile.am
index 65b37c8..05eddeb 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -35,6 +35,7 @@ UI_FILES =                                            \
        gnome-software.ui                               \
        gs-app-addon-row.ui                             \
        gs-app-row.ui                                   \
+       gs-app-review-dialog.ui                         \
        gs-app-review-row.ui                            \
        gs-first-run-dialog.ui                          \
        gs-history-dialog.ui                            \
@@ -123,6 +124,8 @@ gnome_software_SOURCES =                            \
        gs-category-tile.h                              \
        gs-app-review.c                                 \
        gs-app-review.h                                 \
+       gs-app-review-dialog.c                          \
+       gs-app-review-dialog.h                          \
        gs-app-review-row.c                             \
        gs-app-review-row.h                             \
        gs-app-tile.c                                   \
diff --git a/src/gnome-software.gresource.xml b/src/gnome-software.gresource.xml
index a15cf51..33faab2 100644
--- a/src/gnome-software.gresource.xml
+++ b/src/gnome-software.gresource.xml
@@ -10,6 +10,7 @@
   <file preprocess="xml-stripblanks">app-folder-dialog.ui</file>
   <file preprocess="xml-stripblanks">screenshot-image.ui</file>
   <file preprocess="xml-stripblanks">gs-app-addon-row.ui</file>
+  <file preprocess="xml-stripblanks">gs-app-review-dialog.ui</file>
   <file preprocess="xml-stripblanks">gs-app-review-row.ui</file>
   <file preprocess="xml-stripblanks">gs-app-row.ui</file>
   <file preprocess="xml-stripblanks">gs-first-run-dialog.ui</file>
diff --git a/src/gs-app-review-dialog.c b/src/gs-app-review-dialog.c
new file mode 100644
index 0000000..e145719
--- /dev/null
+++ b/src/gs-app-review-dialog.c
@@ -0,0 +1,93 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2015 Canonical Ltd.
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include "gs-app-review-dialog.h"
+#include "gs-star-widget.h"
+
+struct _GsAppReviewDialog
+{
+       GtkDialog        parent_instance;
+
+       GtkWidget       *star;
+       GtkWidget       *summary_entry;
+       GtkWidget       *text_view;
+       GtkWidget       *post_button;
+};
+
+G_DEFINE_TYPE (GsAppReviewDialog, gs_app_review_dialog, GTK_TYPE_DIALOG)
+
+gint
+gs_app_review_dialog_get_rating (GsAppReviewDialog *dialog)
+{
+       return gs_star_widget_get_rating (GS_STAR_WIDGET (dialog->star));
+}
+
+const gchar *
+gs_app_review_dialog_get_summary (GsAppReviewDialog *dialog)
+{
+       return gtk_entry_get_text (GTK_ENTRY (dialog->summary_entry));
+}
+
+gchar *
+gs_app_review_dialog_get_text (GsAppReviewDialog *dialog)
+{
+       GtkTextBuffer *buffer;
+       GtkTextIter start, end;
+
+       buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (dialog->text_view));
+       gtk_text_buffer_get_start_iter (buffer, &start);
+       gtk_text_buffer_get_end_iter (buffer, &end);
+       return gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
+}
+
+static void
+gs_app_review_dialog_init (GsAppReviewDialog *dialog)
+{
+       gtk_widget_init_template (GTK_WIDGET (dialog));
+}
+
+static void
+gs_app_review_dialog_class_init (GsAppReviewDialogClass *klass)
+{
+       GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+       gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/Software/gs-app-review-dialog.ui");
+
+       gtk_widget_class_bind_template_child (widget_class, GsAppReviewDialog, star);
+       gtk_widget_class_bind_template_child (widget_class, GsAppReviewDialog, summary_entry);
+       gtk_widget_class_bind_template_child (widget_class, GsAppReviewDialog, text_view);
+       gtk_widget_class_bind_template_child (widget_class, GsAppReviewDialog, post_button);
+}
+
+GtkWidget *
+gs_app_review_dialog_new (void)
+{
+       return GTK_WIDGET (g_object_new (GS_TYPE_APP_REVIEW_DIALOG,
+                                        "use-header-bar", TRUE,
+                                        NULL));
+}
+
+/* vim: set noexpandtab: */
diff --git a/src/gs-app-review-dialog.h b/src/gs-app-review-dialog.h
new file mode 100644
index 0000000..ff82a33
--- /dev/null
+++ b/src/gs-app-review-dialog.h
@@ -0,0 +1,42 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2015 Canonical Ltd.
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef GS_APP_REVIEW_DIALOG_H
+#define GS_APP_REVIEW_DIALOG_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GS_TYPE_APP_REVIEW_DIALOG (gs_app_review_dialog_get_type ())
+
+G_DECLARE_FINAL_TYPE (GsAppReviewDialog, gs_app_review_dialog, GS, APP_REVIEW_DIALOG, GtkDialog)
+
+GtkWidget      *gs_app_review_dialog_new               (void);
+gint            gs_app_review_dialog_get_rating        (GsAppReviewDialog      *dialog);
+const gchar    *gs_app_review_dialog_get_summary       (GsAppReviewDialog      *dialog);
+gchar          *gs_app_review_dialog_get_text          (GsAppReviewDialog      *dialog);
+
+G_END_DECLS
+
+#endif /* GS_APP_REVIEW_DIALOG_H */
+
+/* vim: set noexpandtab: */
diff --git a/src/gs-app-review-dialog.ui b/src/gs-app-review-dialog.ui
new file mode 100644
index 0000000..a53b2a6
--- /dev/null
+++ b/src/gs-app-review-dialog.ui
@@ -0,0 +1,236 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.18.3 -->
+<interface>
+  <requires lib="gtk+" version="3.10"/>
+  <template class="GsAppReviewDialog" parent="GtkDialog">
+    <property name="can_focus">False</property>
+    <property name="title" translatable="yes">Review</property>
+    <property name="modal">True</property>
+    <property name="default_width">600</property>
+    <property name="default_height">300</property>
+    <property name="destroy_with_parent">True</property>
+    <property name="type_hint">dialog</property>
+    <property name="use_header_bar">1</property>
+    <child internal-child="headerbar">
+      <object class="GtkHeaderBar">
+        <child>
+          <object class="GtkButton" id="post_button">
+            <property name="label" translatable="yes">_Post Review</property>
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
+            <property name="use_underline">True</property>
+          </object>
+          <packing>
+            <property name="pack-type">end</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+    <child internal-child="vbox">
+      <object class="GtkBox" id="dialog-vbox">
+        <property name="can_focus">False</property>
+        <property name="margin_start">6</property>
+        <property name="margin_end">6</property>
+        <property name="margin_top">6</property>
+        <property name="margin_bottom">6</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">9</property>
+        <child internal-child="action_area">
+          <object class="GtkButtonBox" id="dialog-action_area1">
+            <property name="can_focus">False</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkBox" id="box1">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="orientation">vertical</property>
+            <property name="spacing">12</property>
+            <child>
+              <object class="GtkBox" id="box4">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="orientation">vertical</property>
+                <property name="spacing">6</property>
+                <child>
+                  <object class="GtkLabel" id="label4">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label" translatable="yes">Rating</property>
+                    <property name="xalign">0</property>
+                    <attributes>
+                      <attribute name="weight" value="bold"/>
+                    </attributes>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="label6">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label" translatable="yes">Rate between one star (bad) and five stars 
(great)</property>
+                    <property name="wrap">True</property>
+                    <property name="xalign">0</property>
+                    <attributes>
+                      <attribute name="style" value="italic"/>
+                    </attributes>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GsStarWidget" id="star">
+                    <property name="visible">True</property>
+                    <property name="halign">start</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">2</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkBox" id="box2">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="orientation">vertical</property>
+                <property name="spacing">6</property>
+                <child>
+                  <object class="GtkLabel" id="label1">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label" translatable="yes">Summary</property>
+                    <property name="xalign">0</property>
+                    <attributes>
+                      <attribute name="weight" value="bold"/>
+                    </attributes>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="label2">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label" translatable="yes">A single link summarising your review, e.g. 
"Useful tool"</property>
+                    <property name="wrap">True</property>
+                    <property name="xalign">0</property>
+                    <attributes>
+                      <attribute name="style" value="italic"/>
+                    </attributes>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkEntry" id="summary_entry">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">3</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkBox" id="box3">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="orientation">vertical</property>
+                <property name="spacing">6</property>
+                <child>
+                  <object class="GtkLabel" id="label3">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label" translatable="yes">Review</property>
+                    <property name="xalign">0</property>
+                    <attributes>
+                      <attribute name="weight" value="bold"/>
+                    </attributes>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="label5">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label" translatable="yes">A few sentences describing how you find this 
application. e.g. "This application is great, it does does X very well. It would be improved if it could do 
Y."</property>
+                    <property name="wrap">True</property>
+                    <property name="xalign">0</property>
+                    <attributes>
+                      <attribute name="style" value="italic"/>
+                    </attributes>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkTextView" id="text_view">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                  </object>
+                  <packing>
+                    <property name="expand">True</property>
+                    <property name="fill">True</property>
+                    <property name="position">2</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
+                <property name="position">2</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">True</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/src/gs-app-review.h b/src/gs-app-review.h
index 2b1f1bd..0f3c4c7 100644
--- a/src/gs-app-review.h
+++ b/src/gs-app-review.h
@@ -57,11 +57,6 @@ GDateTime    *gs_app_review_get_date                 (GsAppReview    *review);
 void            gs_app_review_set_date                 (GsAppReview    *review,
                                                 GDateTime      *date);
 
-void            gs_app_review_set_is_useful                    (GsAppReview    *review,
-                                                gboolean       is_useful);
-
-void            gs_app_review_mark_innapropriate                       (GsAppReview    *review);
-
 G_END_DECLS
 
 #endif /* __GS_APP_REVIEW_H */
diff --git a/src/gs-app.c b/src/gs-app.c
index 795f8af..00cac08 100644
--- a/src/gs-app.c
+++ b/src/gs-app.c
@@ -82,7 +82,8 @@ struct _GsApp
        gint                     rating;
        gint                     rating_confidence;
        GsAppRatingKind          rating_kind;
-       GPtrArray               *reviews; /* on GsAppReview */
+       GsAppReview             *self_review;
+       GPtrArray               *reviews; /* of GsAppReview */
        guint64                  size;
        GsAppKind                kind;
        AsIdKind                 id_kind;
@@ -1596,6 +1597,29 @@ gs_app_set_rating_kind (GsApp *app, GsAppRatingKind rating_kind)
 }
 
 /**
+ * gs_app_get_self_review:
+ */
+GsAppReview *
+gs_app_get_self_review (GsApp *app)
+{
+       g_return_val_if_fail (GS_IS_APP (app), NULL);
+       return app->self_review;
+}
+
+/**
+ * gs_app_set_self_review:
+ */
+void
+gs_app_set_self_review (GsApp *app, GsAppReview *review)
+{
+       g_return_if_fail (GS_IS_APP (app));
+
+       g_clear_object (&app->self_review);
+       if (review != NULL)
+               app->self_review = g_object_ref (review);
+}
+
+/**
  * gs_app_get_reviews:
  */
 GPtrArray *
diff --git a/src/gs-app.h b/src/gs-app.h
index c9de8db..4f4d2de 100644
--- a/src/gs-app.h
+++ b/src/gs-app.h
@@ -205,6 +205,9 @@ void                 gs_app_set_rating_confidence   (GsApp          *app,
 GsAppRatingKind         gs_app_get_rating_kind         (GsApp          *app);
 void            gs_app_set_rating_kind         (GsApp          *app,
                                                 GsAppRatingKind rating_kind);
+GsAppReview    *gs_app_get_self_review         (GsApp          *app);
+void            gs_app_set_self_review         (GsApp          *app,
+                                                GsAppReview    *review);
 GPtrArray      *gs_app_get_reviews             (GsApp          *app);
 void            gs_app_add_review              (GsApp          *app,
                                                 GsAppReview    *review);
diff --git a/src/gs-plugin-loader.c b/src/gs-plugin-loader.c
index 84f35e9..4087c3d 100644
--- a/src/gs-plugin-loader.c
+++ b/src/gs-plugin-loader.c
@@ -2587,6 +2587,11 @@ gs_plugin_loader_app_action_async (GsPluginLoader *plugin_loader,
                state->state_success = AS_APP_STATE_UNKNOWN;
                state->state_failure = AS_APP_STATE_UNKNOWN;
                break;
+       case GS_PLUGIN_LOADER_ACTION_SET_REVIEW:
+               state->function_name = "gs_plugin_app_set_review";
+               state->state_success = AS_APP_STATE_UNKNOWN;
+               state->state_failure = AS_APP_STATE_UNKNOWN;
+               break;
        default:
                g_assert_not_reached ();
                break;
diff --git a/src/gs-plugin-loader.h b/src/gs-plugin-loader.h
index b823efd..3d90649 100644
--- a/src/gs-plugin-loader.h
+++ b/src/gs-plugin-loader.h
@@ -56,6 +56,7 @@ typedef enum {
        GS_PLUGIN_LOADER_ACTION_INSTALL,
        GS_PLUGIN_LOADER_ACTION_REMOVE,
        GS_PLUGIN_LOADER_ACTION_SET_RATING,
+       GS_PLUGIN_LOADER_ACTION_SET_REVIEW,
        GS_PLUGIN_LOADER_ACTION_LAST
 } GsPluginLoaderAction;
 
diff --git a/src/gs-plugin.h b/src/gs-plugin.h
index 9be0811..0122baf 100644
--- a/src/gs-plugin.h
+++ b/src/gs-plugin.h
@@ -247,6 +247,10 @@ gboolean    gs_plugin_app_set_rating               (GsPlugin       *plugin,
                                                         GsApp          *app,
                                                         GCancellable   *cancellable,
                                                         GError         **error);
+gboolean        gs_plugin_app_set_review               (GsPlugin       *plugin,
+                                                        GsApp          *app,
+                                                        GCancellable   *cancellable,
+                                                        GError         **error);
 gboolean        gs_plugin_refresh                      (GsPlugin       *plugin,
                                                         guint           cache_age,
                                                         GsPluginRefreshFlags flags,
diff --git a/src/gs-shell-details.c b/src/gs-shell-details.c
index 64b5b19..3f1f146 100644
--- a/src/gs-shell-details.c
+++ b/src/gs-shell-details.c
@@ -35,6 +35,7 @@
 #include "gs-screenshot-image.h"
 #include "gs-progress-button.h"
 #include "gs-star-widget.h"
+#include "gs-app-review-dialog.h"
 #include "gs-app-review-row.h"
 
 typedef enum {
@@ -1213,6 +1214,25 @@ gs_shell_details_app_set_ratings_cb (GObject *source,
        }
 }
 
+
+/**
+ * gs_shell_details_app_set_review_cb:
+ **/
+static void
+gs_shell_details_app_set_review_cb (GObject *source,
+                               GAsyncResult *res,
+                               gpointer user_data)
+{
+       GsPluginLoader *plugin_loader = GS_PLUGIN_LOADER (source);
+       GsShellDetails *self = GS_SHELL_DETAILS (user_data);
+       g_autoptr(GError) error = NULL;
+
+       if (!gs_plugin_loader_app_action_finish (plugin_loader, res, &error)) {
+               g_warning ("failed to set review %s: %s",
+                          gs_app_get_id (self->app), error->message);
+       }
+}
+
 /**
  * gs_shell_details_rating_changed_cb:
  **/
@@ -1221,6 +1241,38 @@ gs_shell_details_rating_changed_cb (GsStarWidget *star,
                                    guint rating,
                                    GsShellDetails *self)
 {
+       GtkWidget *dialog;
+       GtkResponseType response;
+
+       dialog = gs_app_review_dialog_new ();
+
+       gtk_window_set_transient_for (GTK_WINDOW (dialog), gs_shell_get_window (self->shell));
+       response = gtk_dialog_run (GTK_DIALOG (dialog));
+       if (response == GTK_RESPONSE_OK) {
+               g_autoptr(GsAppReview) review = NULL;
+               g_autoptr(GDateTime) now = NULL;
+               g_autofree gchar *text = NULL;
+
+               review = gs_app_review_new ();
+               gs_app_review_set_summary (review, gs_app_review_dialog_get_summary (GS_APP_REVIEW_DIALOG 
(dialog)));
+               text = gs_app_review_dialog_get_text (GS_APP_REVIEW_DIALOG (dialog));
+               gs_app_review_set_text (review, text);
+               gs_app_review_set_rating (review, gs_app_review_dialog_get_rating (GS_APP_REVIEW_DIALOG 
(dialog)));
+               gs_app_review_set_version (review, gs_app_get_version (self->app));
+               gs_app_review_set_reviewer (review, "Joe Bloggs"); // FIXME
+               now = g_date_time_new_now_local ();
+               gs_app_review_set_date (review, now);
+
+               /* call into the plugins to set the new value */
+               gs_app_set_self_review (self->app, review);
+               gs_plugin_loader_app_action_async (self->plugin_loader, self->app,
+                                                  GS_PLUGIN_LOADER_ACTION_SET_REVIEW,
+                                                  self->cancellable,
+                                                  gs_shell_details_app_set_review_cb,
+                                                  self);
+       }
+       gtk_widget_destroy (dialog);
+#if 0
        g_debug ("%s rating changed from %i%% to %i%%",
                 gs_app_get_id (self->app),
                 gs_app_get_rating (self->app),
@@ -1234,6 +1286,7 @@ gs_shell_details_rating_changed_cb (GsStarWidget *star,
                                           self->cancellable,
                                           gs_shell_details_app_set_ratings_cb,
                                           self);
+#endif
 }
 
 static void
diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am
index ba38d96..fb4bf67 100644
--- a/src/plugins/Makefile.am
+++ b/src/plugins/Makefile.am
@@ -12,6 +12,7 @@ AM_CPPFLAGS =                                         \
        $(FWUPD_CFLAGS)                                 \
        $(LIMBA_CFLAGS)                                 \
        $(JSON_GLIB_CFLAGS)                             \
+       $(OAUTH_CFLAGS)                                 \
        -DBINDIR=\"$(bindir)\"                          \
        -DDATADIR=\"$(datadir)\"                        \
        -DGS_MODULESETDIR=\"$(datadir)/gnome-software/modulesets.d\" \
@@ -133,7 +134,7 @@ libgs_plugin_local_ratings_la_LDFLAGS = -module -avoid-version
 libgs_plugin_local_ratings_la_CFLAGS = $(GS_PLUGIN_CFLAGS) $(WARN_CFLAGS)
 
 libgs_plugin_ubuntu_reviews_la_SOURCES = gs-plugin-ubuntu-reviews.c
-libgs_plugin_ubuntu_reviews_la_LIBADD = $(GS_PLUGIN_LIBS) $(SOUP_LIBS) $(SQLITE_LIBS) $(JSON_GLIB_LIBS)
+libgs_plugin_ubuntu_reviews_la_LIBADD = $(GS_PLUGIN_LIBS) $(SOUP_LIBS) $(SQLITE_LIBS) $(JSON_GLIB_LIBS) 
$(OAUTH_LIBS)
 libgs_plugin_ubuntu_reviews_la_LDFLAGS = -module -avoid-version
 libgs_plugin_ubuntu_reviews_la_CFLAGS = $(GS_PLUGIN_CFLAGS) $(WARNINGFLAGS_C)
 
diff --git a/src/plugins/gs-plugin-ubuntu-reviews.c b/src/plugins/gs-plugin-ubuntu-reviews.c
index 9d8e632..424c67d 100644
--- a/src/plugins/gs-plugin-ubuntu-reviews.c
+++ b/src/plugins/gs-plugin-ubuntu-reviews.c
@@ -24,6 +24,7 @@
 #include <math.h>
 #include <libsoup/soup.h>
 #include <json-glib/json-glib.h>
+#include <oauth.h>
 
 #include <gs-plugin.h>
 #include <gs-utils.h>
@@ -248,3 +249,167 @@ gs_plugin_refine (GsPlugin *plugin,
 
        return TRUE;
 }
+
+static void
+add_string_member (JsonBuilder *builder, const gchar *name, const gchar *value)
+{
+       json_builder_set_member_name (builder, name);
+       json_builder_add_string_value (builder, value);
+}
+
+static void
+add_int_member (JsonBuilder *builder, const gchar *name, gint64 value)
+{
+       json_builder_set_member_name (builder, name);
+       json_builder_add_int_value (builder, value);
+}
+
+static void
+sign_message (SoupMessage *message, OAuthMethod method,
+              const gchar *oauth_consumer_key, const gchar *oauth_consumer_secret,
+              const gchar *oauth_token, const gchar *oauth_token_secret)
+{
+       g_autofree gchar *url = NULL, *oauth_authorization_parameters = NULL, *authorization_text = NULL;
+       gchar **url_parameters = NULL;
+       int url_parameters_length;
+
+       url = soup_uri_to_string (soup_message_get_uri (message), FALSE);
+
+       url_parameters_length = oauth_split_url_parameters(url, &url_parameters);
+       oauth_sign_array2_process (&url_parameters_length, &url_parameters,
+                                  NULL,
+                                  method,
+                                  message->method,
+                                  oauth_consumer_key, oauth_consumer_secret,
+                                  oauth_token, oauth_token_secret);
+       oauth_authorization_parameters = oauth_serialize_url_sep (url_parameters_length, 1, url_parameters, 
", ", 6);
+       oauth_free_array (&url_parameters_length, &url_parameters);
+       authorization_text = g_strdup_printf ("OAuth realm=\"Ratings and Reviews\", %s", 
oauth_authorization_parameters);
+       soup_message_headers_append (message->request_headers, "Authorization", authorization_text);
+}
+
+static void
+set_request (SoupMessage *message, JsonBuilder *builder)
+{
+       JsonGenerator *generator = json_generator_new ();
+       json_generator_set_root (generator, json_builder_get_root (builder));
+       gsize length;
+       gchar *data = json_generator_to_data (generator, &length);
+       soup_message_set_request (message, "application/json", SOUP_MEMORY_TAKE, data, length);
+       g_object_unref (generator);
+}
+
+static gboolean
+set_package_review (GsPlugin *plugin,
+                    GsAppReview *review,
+                    const gchar *package_name,
+                    GError **error)
+{
+       g_autofree gchar *uri = NULL, *path = NULL;
+       g_autofree gchar *oauth_consumer_key = NULL, *oauth_consumer_secret = NULL, *oauth_token = NULL, 
*oauth_token_secret = NULL;
+       g_autoptr(GKeyFile) config = NULL;
+       gint rating, n_stars = 0;
+       g_autoptr(SoupMessage) msg = NULL;
+       JsonBuilder *builder;
+       guint status_code;
+
+       /* Ubuntu reviews require a summary and description - just make one up for now */
+       rating = gs_app_review_get_rating (review);
+       if (rating > 80)
+               n_stars = 5;
+       else if (rating > 60)
+               n_stars = 4;
+       else if (rating > 40)
+               n_stars = 3;
+       else if (rating > 20)
+               n_stars = 2;
+       else
+               n_stars = 1;
+
+       /* Load OAuth token */
+       // FIXME needs to integrate with GNOME Online Accounts / libaccounts
+       config = g_key_file_new ();
+       path = g_build_filename (g_get_user_config_dir (), "gnome-software", "ubuntu-one-credentials", NULL);
+       g_key_file_load_from_file (config, path, G_KEY_FILE_NONE, NULL);
+       oauth_consumer_key = g_key_file_get_string (config, "gnome-software", "consumer-key", NULL);
+       oauth_consumer_secret = g_key_file_get_string (config, "gnome-software", "consumer-secret", NULL);
+       oauth_token = g_key_file_get_string (config, "gnome-software", "token", NULL);
+       oauth_token_secret = g_key_file_get_string (config, "gnome-software", "token-secret", NULL);
+       if (!oauth_consumer_key || !oauth_consumer_secret || !oauth_token || !oauth_token_secret) {
+               g_set_error (error,
+                            GS_PLUGIN_ERROR,
+                            GS_PLUGIN_ERROR_FAILED,
+                            "%s", "No Ubuntu One OAuth tokens");
+               return FALSE;
+       }
+
+       uri = g_strdup_printf ("%s/api/1.0/reviews/", UBUNTU_REVIEWS_SERVER);
+       msg = soup_message_new (SOUP_METHOD_POST, uri);
+       builder = json_builder_new ();
+       json_builder_begin_object (builder);
+       add_string_member (builder, "package_name", package_name);
+       add_string_member (builder, "summary", gs_app_review_get_summary (review));
+       add_string_member (builder, "review_text", gs_app_review_get_text (review));
+       add_string_member (builder, "language", "en"); // FIXME
+       add_string_member (builder, "origin", "ubuntu"); // FIXME gs_app_get_origin (app));
+       add_string_member (builder, "distroseries", "xenial"); // FIXME
+       add_string_member (builder, "version", gs_app_review_get_version (review));
+       add_int_member (builder, "rating", n_stars);
+       add_string_member (builder, "arch_tag", "amd64"); // FIXME
+       json_builder_end_object (builder);
+       set_request (msg, builder);
+       g_object_unref (builder);
+       sign_message (msg, OA_HMAC, oauth_consumer_key, oauth_consumer_secret, oauth_token, 
oauth_token_secret);
+
+       status_code = soup_session_send_message (plugin->priv->session, msg);
+       if (status_code != SOUP_STATUS_OK) {
+               g_set_error (error,
+                            GS_PLUGIN_ERROR,
+                            GS_PLUGIN_ERROR_FAILED,
+                            "Failed to post review: %s",
+                            soup_status_get_phrase (status_code));
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+gboolean
+gs_plugin_app_set_review (GsPlugin *plugin,
+                         GsApp *app,
+                         GCancellable *cancellable,
+                         GError **error)
+{
+       GsAppReview *review;
+       GPtrArray *sources;
+       const gchar *package_name;
+       gboolean ret;
+       guint i;
+       g_autoptr(SoupMessage) msg = NULL;
+
+       review = gs_app_get_self_review (app);
+       g_return_val_if_fail (review != NULL, FALSE);
+
+       /* get the package name */
+       sources = gs_app_get_sources (app);
+       if (sources->len == 0) {
+               g_warning ("no package name for %s", gs_app_get_id (app));
+               return TRUE;
+       }
+
+       if (!setup_networking (plugin, error))
+               return FALSE;
+
+       /* set rating for each package */
+       for (i = 0; i < sources->len; i++) {
+               package_name = g_ptr_array_index (sources, i);
+               ret = set_package_review (plugin,
+                                         review,
+                                         package_name,
+                                         error);
+               if (!ret)
+                       return FALSE;
+       }
+
+       return TRUE;
+}


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