summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Hughes <richard@hughsie.com>2016-07-19 15:48:08 +0100
committerRichard Hughes <richard@hughsie.com>2016-07-20 11:37:52 +0100
commit90a9dda7c47a1b67dd17ef78ee77ebed5bb2c740 (patch)
tree95a59bda212e266a8dedf33de22ed024f5aa831b
parentaccf6cd614f1be1dec2963f72f0a419a48de69f9 (diff)
downloadappstream-glib-wip/hughsie/AsReview.tar.gz
This allows us to represent an end-user application review.
-rw-r--r--libappstream-glib/Makefile.am5
-rw-r--r--libappstream-glib/appstream-glib.h1
-rw-r--r--libappstream-glib/as-app.c90
-rw-r--r--libappstream-glib/as-app.h6
-rw-r--r--libappstream-glib/as-review-private.h48
-rw-r--r--libappstream-glib/as-review.c1063
-rw-r--r--libappstream-glib/as-review.h124
-rw-r--r--libappstream-glib/as-self-test.c69
-rw-r--r--libappstream-glib/as-tag.c5
-rw-r--r--libappstream-glib/as-tag.gperf5
-rw-r--r--libappstream-glib/as-tag.h10
11 files changed, 1425 insertions, 1 deletions
diff --git a/libappstream-glib/Makefile.am b/libappstream-glib/Makefile.am
index 0aaeee7..348dbc9 100644
--- a/libappstream-glib/Makefile.am
+++ b/libappstream-glib/Makefile.am
@@ -83,6 +83,7 @@ libappstream_glib_include_HEADERS = \
as-profile.h \
as-provide.h \
as-release.h \
+ as-review.h \
as-screenshot.h \
as-store.h \
as-tag.h \
@@ -123,6 +124,8 @@ libappstream_glib_la_SOURCES = \
as-provide-private.h \
as-release.c \
as-release-private.h \
+ as-review.c \
+ as-review-private.h \
as-resources.c \
as-resources.h \
as-screenshot.c \
@@ -217,6 +220,8 @@ introspection_sources = \
as-provide.h \
as-release.c \
as-release.h \
+ as-review.c \
+ as-review.h \
as-screenshot.c \
as-screenshot.h \
as-store.c \
diff --git a/libappstream-glib/appstream-glib.h b/libappstream-glib/appstream-glib.h
index ab5e631..55962dd 100644
--- a/libappstream-glib/appstream-glib.h
+++ b/libappstream-glib/appstream-glib.h
@@ -39,6 +39,7 @@
#include <as-profile.h>
#include <as-provide.h>
#include <as-release.h>
+#include <as-review.h>
#include <as-screenshot.h>
#include <as-store.h>
#include <as-tag.h>
diff --git a/libappstream-glib/as-app.c b/libappstream-glib/as-app.c
index dbb4f91..0765d54 100644
--- a/libappstream-glib/as-app.c
+++ b/libappstream-glib/as-app.c
@@ -45,6 +45,7 @@
#include "as-node-private.h"
#include "as-provide-private.h"
#include "as-release-private.h"
+#include "as-review-private.h"
#include "as-screenshot-private.h"
#include "as-stemmer.h"
#include "as-tag.h"
@@ -78,6 +79,7 @@ typedef struct
GPtrArray *releases; /* of AsRelease */
GPtrArray *provides; /* of AsProvide */
GPtrArray *screenshots; /* of AsScreenshot */
+ GPtrArray *reviews; /* of AsReview */
GPtrArray *content_ratings; /* of AsContentRating */
GPtrArray *icons; /* of AsIcon */
GPtrArray *bundles; /* of AsBundle */
@@ -391,6 +393,7 @@ as_app_finalize (GObject *object)
g_ptr_array_unref (priv->releases);
g_ptr_array_unref (priv->provides);
g_ptr_array_unref (priv->screenshots);
+ g_ptr_array_unref (priv->reviews);
g_ptr_array_unref (priv->icons);
g_ptr_array_unref (priv->bundles);
g_ptr_array_unref (priv->translations);
@@ -419,6 +422,7 @@ as_app_init (AsApp *app)
priv->releases = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
priv->provides = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
priv->screenshots = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
+ priv->reviews = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
priv->icons = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
priv->bundles = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
priv->translations = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
@@ -841,6 +845,23 @@ as_app_get_screenshots (AsApp *app)
}
/**
+ * as_app_get_reviews:
+ * @app: a #AsApp instance.
+ *
+ * Gets any reviews the application has defined.
+ *
+ * Returns: (element-type AsScreenshot) (transfer none): an array
+ *
+ * Since: 0.5.18
+ **/
+GPtrArray *
+as_app_get_reviews (AsApp *app)
+{
+ AsAppPrivate *priv = GET_PRIVATE (app);
+ return priv->reviews;
+}
+
+/**
* as_app_get_content_ratings:
* @app: a #AsApp instance.
*
@@ -2550,6 +2571,33 @@ as_app_add_screenshot (AsApp *app, AsScreenshot *screenshot)
}
/**
+ * as_app_add_review:
+ * @app: a #AsApp instance.
+ * @review: a #AsReview instance.
+ *
+ * Adds a review to an application.
+ *
+ * Since: 0.5.18
+ **/
+void
+as_app_add_review (AsApp *app, AsReview *review)
+{
+ AsAppPrivate *priv = GET_PRIVATE (app);
+ AsReview *review_tmp;
+ guint i;
+
+ /* handle untrusted */
+ if ((priv->trust_flags & AS_APP_TRUST_FLAG_CHECK_DUPLICATES) > 0) {
+ for (i = 0; i < priv->reviews->len; i++) {
+ review_tmp = g_ptr_array_index (priv->reviews, i);
+ if (as_review_equal (review_tmp, review))
+ return;
+ }
+ }
+ g_ptr_array_add (priv->reviews, g_object_ref (review));
+}
+
+/**
* as_app_add_content_rating:
* @app: a #AsApp instance.
* @content_rating: a #AsContentRating instance.
@@ -2997,6 +3045,7 @@ as_app_subsume_private (AsApp *app, AsApp *donor, AsAppSubsumeFlags flags)
AsAppPrivate *papp = GET_PRIVATE (app);
AsBundle *bundle;
AsTranslation *translation;
+ AsReview *review;
AsScreenshot *ss;
AsProvide *pr;
const gchar *tmp;
@@ -3086,6 +3135,12 @@ as_app_subsume_private (AsApp *app, AsApp *donor, AsAppSubsumeFlags flags)
as_app_add_screenshot (app, ss);
}
+ /* reviews */
+ for (i = 0; i < priv->reviews->len; i++) {
+ review = g_ptr_array_index (priv->reviews, i);
+ as_app_add_review (app, review);
+ }
+
/* content_ratings */
for (i = 0; i < priv->content_ratings->len; i++) {
AsContentRating *content_rating;
@@ -3546,6 +3601,16 @@ as_app_node_insert (AsApp *app, GNode *parent, AsNodeContext *ctx)
}
}
+ /* <reviews> */
+ if (priv->reviews->len > 0) {
+ AsReview *review;
+ node_tmp = as_node_insert (node_app, "reviews", NULL, 0, NULL);
+ for (i = 0; i < priv->reviews->len; i++) {
+ review = g_ptr_array_index (priv->reviews, i);
+ as_review_node_insert (review, node_tmp, ctx);
+ }
+ }
+
/* <content_ratings> */
if (priv->content_ratings->len > 0) {
for (i = 0; i < priv->content_ratings->len; i++) {
@@ -3923,6 +3988,21 @@ as_app_node_parse_child (AsApp *app, GNode *n, AsAppParseFlags flags,
}
break;
+ /* <reviews> */
+ case AS_TAG_REVIEWS:
+ if (!(flags & AS_APP_PARSE_FLAG_APPEND_DATA))
+ g_ptr_array_set_size (priv->reviews, 0);
+ for (c = n->children; c != NULL; c = c->next) {
+ g_autoptr(AsReview) review = NULL;
+ if (as_node_get_tag (c) != AS_TAG_REVIEW)
+ continue;
+ review = as_review_new ();
+ if (!as_review_node_parse (review, c, ctx, error))
+ return FALSE;
+ as_app_add_review (app, review);
+ }
+ break;
+
/* <content_ratings> */
case AS_TAG_CONTENT_RATING:
{
@@ -4328,6 +4408,16 @@ as_app_node_parse_dep11 (AsApp *app, GNode *node,
}
continue;
}
+ if (g_strcmp0 (tmp, "Reviews") == 0) {
+ for (c = n->children; c != NULL; c = c->next) {
+ g_autoptr(AsReview) review = NULL;
+ review = as_review_new ();
+ if (!as_review_node_parse_dep11 (review, c, ctx, error))
+ return FALSE;
+ as_app_add_review (app, review);
+ }
+ continue;
+ }
if (g_strcmp0 (tmp, "Extends") == 0) {
for (c = n->children; c != NULL; c = c->next)
as_app_add_extends (app, as_yaml_node_get_key (c));
diff --git a/libappstream-glib/as-app.h b/libappstream-glib/as-app.h
index f027924..d642333 100644
--- a/libappstream-glib/as-app.h
+++ b/libappstream-glib/as-app.h
@@ -1,6 +1,6 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
*
- * Copyright (C) 2014-2015 Richard Hughes <richard@hughsie.com>
+ * Copyright (C) 2014-2016 Richard Hughes <richard@hughsie.com>
*
* Licensed under the GNU Lesser General Public License Version 2.1
*
@@ -34,6 +34,7 @@
#include "as-provide.h"
#include "as-release.h"
#include "as-screenshot.h"
+#include "as-review.h"
#include "as-content-rating.h"
#include "as-translation.h"
@@ -305,6 +306,7 @@ GPtrArray *as_app_get_architectures (AsApp *app);
GPtrArray *as_app_get_releases (AsApp *app);
GPtrArray *as_app_get_provides (AsApp *app);
GPtrArray *as_app_get_screenshots (AsApp *app);
+GPtrArray *as_app_get_reviews (AsApp *app);
GPtrArray *as_app_get_content_ratings (AsApp *app);
GPtrArray *as_app_get_icons (AsApp *app);
GPtrArray *as_app_get_bundles (AsApp *app);
@@ -420,6 +422,8 @@ void as_app_add_provide (AsApp *app,
AsProvide *provide);
void as_app_add_screenshot (AsApp *app,
AsScreenshot *screenshot);
+void as_app_add_review (AsApp *app,
+ AsReview *review);
void as_app_add_content_rating (AsApp *app,
AsContentRating *content_rating);
void as_app_add_icon (AsApp *app,
diff --git a/libappstream-glib/as-review-private.h b/libappstream-glib/as-review-private.h
new file mode 100644
index 0000000..a8322ab
--- /dev/null
+++ b/libappstream-glib/as-review-private.h
@@ -0,0 +1,48 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2016 Richard Hughes <richard@hughsie.com>
+ *
+ * Licensed under the GNU Lesser General Public License Version 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#if !defined (__APPSTREAM_GLIB_PRIVATE_H) && !defined (AS_COMPILATION)
+#error "Only <appstream-glib.h> can be included directly."
+#endif
+
+#ifndef __AS_REVIEW_PRIVATE_H
+#define __AS_REVIEW_PRIVATE_H
+
+#include "as-review.h"
+#include "as-node-private.h"
+
+G_BEGIN_DECLS
+
+GNode *as_review_node_insert (AsReview *review,
+ GNode *parent,
+ AsNodeContext *ctx);
+gboolean as_review_node_parse (AsReview *review,
+ GNode *node,
+ AsNodeContext *ctx,
+ GError **error);
+gboolean as_review_node_parse_dep11 (AsReview *review,
+ GNode *node,
+ AsNodeContext *ctx,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* __AS_REVIEW_PRIVATE_H */
diff --git a/libappstream-glib/as-review.c b/libappstream-glib/as-review.c
new file mode 100644
index 0000000..dd9840e
--- /dev/null
+++ b/libappstream-glib/as-review.c
@@ -0,0 +1,1063 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2016 Canonical Ltd.
+ * Copyright (C) 2016 Richard Hughes <richard@hughsie.com>
+ *
+ * Licensed under the GNU Lesser General Public License Version 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * SECTION:as-review
+ * @title: AsReview
+ * @include: appstream-glib.h
+ * @stability: Stable
+ * @short_description: An application user review
+ *
+ * This object represents a user-submitted application review.
+ *
+ * Since: 0.5.18
+ **/
+
+#include "config.h"
+
+#include "as-review-private.h"
+#include "as-node-private.h"
+#include "as-utils-private.h"
+#include "as-yaml.h"
+
+typedef struct
+{
+ AsReviewFlags flags;
+ gchar *id;
+ gchar *summary;
+ gchar *description;
+ gchar *locale;
+ gint karma;
+ gint score;
+ gint rating;
+ gchar *version;
+ gchar *reviewer_id;
+ gchar *reviewer_name;
+ GDateTime *date;
+ GHashTable *metadata;
+} AsReviewPrivate;
+
+enum {
+ PROP_0,
+ PROP_ID,
+ PROP_KARMA,
+ PROP_SUMMARY,
+ PROP_DESCRIPTION,
+ PROP_LOCALE,
+ PROP_RATING,
+ PROP_VERSION,
+ PROP_REVIEWER_ID,
+ PROP_REVIEWER_NAME,
+ PROP_DATE,
+ PROP_FLAGS,
+ PROP_LAST
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (AsReview, as_review, G_TYPE_OBJECT)
+
+#define GET_PRIVATE(o) (as_review_get_instance_private (o))
+
+static void
+as_review_finalize (GObject *object)
+{
+ AsReview *review = AS_REVIEW (object);
+ AsReviewPrivate *priv = GET_PRIVATE (review);
+
+ g_free (priv->id);
+ g_free (priv->summary);
+ g_free (priv->description);
+ g_free (priv->locale);
+ g_free (priv->reviewer_id);
+ g_free (priv->reviewer_name);
+ g_hash_table_unref (priv->metadata);
+ if (priv->date != NULL)
+ g_date_time_unref (priv->date);
+
+ G_OBJECT_CLASS (as_review_parent_class)->finalize (object);
+}
+
+static void
+as_review_get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ AsReview *review = AS_REVIEW (object);
+ AsReviewPrivate *priv = GET_PRIVATE (review);
+
+ switch (prop_id) {
+ case PROP_KARMA:
+ g_value_set_int (value, priv->karma);
+ break;
+ case PROP_ID:
+ g_value_set_string (value, priv->id);
+ break;
+ case PROP_SUMMARY:
+ g_value_set_string (value, priv->summary);
+ break;
+ case PROP_DESCRIPTION:
+ g_value_set_string (value, priv->description);
+ break;
+ case PROP_LOCALE:
+ g_value_set_string (value, priv->locale);
+ break;
+ case PROP_RATING:
+ g_value_set_int (value, priv->rating);
+ break;
+ case PROP_FLAGS:
+ g_value_set_uint64 (value, priv->flags);
+ break;
+ case PROP_VERSION:
+ g_value_set_string (value, priv->version);
+ break;
+ case PROP_REVIEWER_ID:
+ g_value_set_string (value, priv->reviewer_id);
+ break;
+ case PROP_REVIEWER_NAME:
+ g_value_set_string (value, priv->reviewer_name);
+ break;
+ case PROP_DATE:
+ g_value_set_object (value, priv->date);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+as_review_set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ AsReview *review = AS_REVIEW (object);
+
+ switch (prop_id) {
+ case PROP_KARMA:
+ as_review_set_karma (review, g_value_get_int (value));
+ break;
+ case PROP_ID:
+ as_review_set_id (review, g_value_get_string (value));
+ break;
+ case PROP_SUMMARY:
+ as_review_set_summary (review, g_value_get_string (value));
+ break;
+ case PROP_DESCRIPTION:
+ as_review_set_description (review, g_value_get_string (value));
+ break;
+ case PROP_LOCALE:
+ as_review_set_locale (review, g_value_get_string (value));
+ break;
+ case PROP_RATING:
+ as_review_set_rating (review, g_value_get_int (value));
+ break;
+ case PROP_FLAGS:
+ as_review_set_flags (review, g_value_get_uint64 (value));
+ break;
+ case PROP_VERSION:
+ as_review_set_version (review, g_value_get_string (value));
+ break;
+ case PROP_REVIEWER_ID:
+ as_review_set_reviewer_id (review, g_value_get_string (value));
+ break;
+ case PROP_REVIEWER_NAME:
+ as_review_set_reviewer_name (review, g_value_get_string (value));
+ break;
+ case PROP_DATE:
+ as_review_set_date (review, g_value_get_object (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+as_review_class_init (AsReviewClass *klass)
+{
+ GParamSpec *pspec;
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ object_class->finalize = as_review_finalize;
+ object_class->get_property = as_review_get_property;
+ object_class->set_property = as_review_set_property;
+
+ /**
+ * AsReview:karma:
+ *
+ * Since: 0.5.18
+ **/
+ pspec = g_param_spec_int ("karma", NULL, NULL,
+ G_MININT, G_MAXINT, 0,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
+ g_object_class_install_property (object_class, PROP_KARMA, pspec);
+
+ /**
+ * AsReview:id:
+ *
+ * Since: 0.5.18
+ **/
+ pspec = g_param_spec_string ("id", NULL, NULL,
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
+ g_object_class_install_property (object_class, PROP_ID, pspec);
+
+ /**
+ * AsReview:summary:
+ *
+ * Since: 0.5.18
+ **/
+ pspec = g_param_spec_string ("summary", NULL, NULL,
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
+ g_object_class_install_property (object_class, PROP_SUMMARY, pspec);
+
+ /**
+ * AsReview:description:
+ *
+ * Since: 0.5.18
+ **/
+ pspec = g_param_spec_string ("description", NULL, NULL,
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
+ g_object_class_install_property (object_class, PROP_DESCRIPTION, pspec);
+
+ /**
+ * AsReview:locale:
+ *
+ * Since: 0.5.18
+ **/
+ pspec = g_param_spec_string ("locale", NULL, NULL,
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
+ g_object_class_install_property (object_class, PROP_LOCALE, pspec);
+
+ /**
+ * AsReview:rating:
+ *
+ * Since: 0.5.18
+ **/
+ pspec = g_param_spec_int ("rating", NULL, NULL,
+ -1, 100, 0,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
+ g_object_class_install_property (object_class, PROP_RATING, pspec);
+
+ /**
+ * AsReview:flags:
+ *
+ * Since: 0.5.18
+ **/
+ pspec = g_param_spec_uint64 ("flags", NULL, NULL,
+ AS_REVIEW_FLAG_NONE,
+ AS_REVIEW_FLAG_LAST,
+ AS_REVIEW_FLAG_NONE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
+ g_object_class_install_property (object_class, PROP_FLAGS, pspec);
+
+ /**
+ * AsReview:version:
+ *
+ * Since: 0.5.18
+ **/
+ pspec = g_param_spec_string ("version", NULL, NULL,
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
+ g_object_class_install_property (object_class, PROP_VERSION, pspec);
+
+ /**
+ * AsReview:reviewer-id:
+ *
+ * Since: 0.5.18
+ **/
+ pspec = g_param_spec_string ("reviewer-id", NULL, NULL,
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
+ g_object_class_install_property (object_class, PROP_REVIEWER_ID, pspec);
+
+ /**
+ * AsReview:reviewer-name:
+ *
+ * Since: 0.5.18
+ **/
+ pspec = g_param_spec_string ("reviewer-name", NULL, NULL,
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
+ g_object_class_install_property (object_class, PROP_REVIEWER_NAME, pspec);
+
+ /**
+ * AsReview:date:
+ *
+ * Since: 0.5.18
+ **/
+ pspec = g_param_spec_object ("date", NULL, NULL,
+ AS_TYPE_REVIEW,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
+ g_object_class_install_property (object_class, PROP_DATE, pspec);
+}
+
+static void
+as_review_init (AsReview *review)
+{
+ AsReviewPrivate *priv = GET_PRIVATE (review);
+ priv->metadata = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, g_free);
+}
+
+/**
+ * as_review_get_karma:
+ * @review: a #AsReview
+ *
+ * Gets the karma for the review, where positive numbers indicate
+ * more positive feedback for the review.
+ *
+ * Returns: the karma value, or 0 for unset.
+ *
+ * Since: 0.5.18
+ **/
+gint
+as_review_get_karma (AsReview *review)
+{
+ AsReviewPrivate *priv = GET_PRIVATE (review);
+ g_return_val_if_fail (AS_IS_REVIEW (review), 0);
+ return priv->karma;
+}
+
+/**
+ * as_review_set_karma:
+ * @review: a #AsReview
+ * @karma: a karma value
+ *
+ * Sets the karma for the review, where positive numbers indicate
+ * more positive feedback for the review.
+ *
+ * Karma can be positive or negative, or 0 for unset.
+ *
+ * Since: 0.5.18
+ **/
+void
+as_review_set_karma (AsReview *review, gint karma)
+{
+ AsReviewPrivate *priv = GET_PRIVATE (review);
+ g_return_if_fail (AS_IS_REVIEW (review));
+ priv->karma = karma;
+}
+
+/**
+ * as_review_get_score:
+ * @review: a #AsReview
+ *
+ * This allows the UI to sort reviews into the correct order.
+ * Higher numbers indicate a more important or relevant review.
+ *
+ * Returns: the review score, or 0 for unset.
+ *
+ * Since: 0.5.18
+ **/
+gint
+as_review_get_score (AsReview *review)
+{
+ AsReviewPrivate *priv = GET_PRIVATE (review);
+ g_return_val_if_fail (AS_IS_REVIEW (review), 0);
+ return priv->score;
+}
+
+/**
+ * as_review_set_score:
+ * @review: a #AsReview
+ * @score: a score value
+ *
+ * Sets the score for the review, where positive numbers indicate
+ * a better review for the specific user.
+ *
+ * Since: 0.5.18
+ **/
+void
+as_review_set_score (AsReview *review, gint score)
+{
+ AsReviewPrivate *priv = GET_PRIVATE (review);
+ g_return_if_fail (AS_IS_REVIEW (review));
+ priv->score = score;
+}
+
+/**
+ * as_review_get_id:
+ * @review: a #AsReview
+ *
+ * Gets the review id.
+ *
+ * Returns: the review identifier, e.g. "deadbeef"
+ *
+ * Since: 0.5.18
+ **/
+const gchar *
+as_review_get_id (AsReview *review)
+{
+ AsReviewPrivate *priv = GET_PRIVATE (review);
+ g_return_val_if_fail (AS_IS_REVIEW (review), NULL);
+ return priv->id;
+}
+
+/**
+ * as_review_get_summary:
+ * @review: a #AsReview
+ *
+ * Gets the review summary.
+ *
+ * Returns: the one-line summary, e.g. "Awesome application"
+ *
+ * Since: 0.5.18
+ **/
+const gchar *
+as_review_get_summary (AsReview *review)
+{
+ AsReviewPrivate *priv = GET_PRIVATE (review);
+ g_return_val_if_fail (AS_IS_REVIEW (review), NULL);
+ return priv->summary;
+}
+
+/**
+ * as_review_set_id:
+ * @review: a #AsReview
+ * @id: review identifier, e.g. "deadbeef"
+ *
+ * Sets the review identifier that is unique to each review.
+ *
+ * Since: 0.5.18
+ **/
+void
+as_review_set_id (AsReview *review, const gchar *id)
+{
+ AsReviewPrivate *priv = GET_PRIVATE (review);
+ g_return_if_fail (AS_IS_REVIEW (review));
+ g_free (priv->id);
+ priv->id = g_strdup (id);
+}
+
+/**
+ * as_review_set_summary:
+ * @review: a #AsReview
+ * @summary: a one-line summary, e.g. "Awesome application"
+ *
+ * Sets the one-line summary that may be displayed in bold.
+ *
+ * Since: 0.5.18
+ **/
+void
+as_review_set_summary (AsReview *review, const gchar *summary)
+{
+ AsReviewPrivate *priv = GET_PRIVATE (review);
+ g_return_if_fail (AS_IS_REVIEW (review));
+ g_free (priv->summary);
+ priv->summary = g_strdup (summary);
+}
+
+/**
+ * as_review_get_description:
+ * @review: a #AsReview
+ *
+ * Gets the multi-line review text that forms the body of the review.
+ *
+ * Returns: the string, or %NULL
+ *
+ * Since: 0.5.18
+ **/
+const gchar *
+as_review_get_description (AsReview *review)
+{
+ AsReviewPrivate *priv = GET_PRIVATE (review);
+ g_return_val_if_fail (AS_IS_REVIEW (review), NULL);
+ return priv->description;
+}
+
+/**
+ * as_review_set_description:
+ * @review: a #AsReview
+ * @description: multi-line description
+ *
+ * Sets the multi-line review text that forms the body of the review.
+ *
+ * Since: 0.5.18
+ **/
+void
+as_review_set_description (AsReview *review, const gchar *description)
+{
+ AsReviewPrivate *priv = GET_PRIVATE (review);
+ g_return_if_fail (AS_IS_REVIEW (review));
+ g_free (priv->description);
+ priv->description = g_strdup (description);
+}
+
+/**
+ * as_review_get_locale:
+ * @review: a #AsReview
+ *
+ * Gets the locale for the review.
+ *
+ * Returns: the string, or %NULL
+ *
+ * Since: 0.5.18
+ **/
+const gchar *
+as_review_get_locale (AsReview *review)
+{
+ AsReviewPrivate *priv = GET_PRIVATE (review);
+ g_return_val_if_fail (AS_IS_REVIEW (review), NULL);
+ return priv->locale;
+}
+
+/**
+ * as_review_set_locale:
+ * @review: a #AsReview
+ * @locale: locale, e.g. "en_GB"
+ *
+ * Sets the locale for the review.
+ *
+ * Since: 0.5.18
+ **/
+void
+as_review_set_locale (AsReview *review, const gchar *locale)
+{
+ AsReviewPrivate *priv = GET_PRIVATE (review);
+ g_return_if_fail (AS_IS_REVIEW (review));
+ g_free (priv->locale);
+ priv->locale = g_strdup (locale);
+}
+
+/**
+ * as_review_get_rating:
+ * @review: a #AsReview
+ *
+ * Gets the star rating of the review, where 100 is 5 stars.
+ *
+ * Returns: integer as a percentage, or 0 for unset
+ *
+ * Since: 0.5.18
+ **/
+gint
+as_review_get_rating (AsReview *review)
+{
+ AsReviewPrivate *priv = GET_PRIVATE (review);
+ g_return_val_if_fail (AS_IS_REVIEW (review), 0);
+ return priv->rating;
+}
+
+/**
+ * as_review_set_rating:
+ * @review: a #AsReview
+ * @rating: a integer as a percentage, or 0 for unset
+ *
+ * Sets the star rating of the review, where 100 is 5 stars..
+ *
+ * Since: 0.5.18
+ **/
+void
+as_review_set_rating (AsReview *review, gint rating)
+{
+ AsReviewPrivate *priv = GET_PRIVATE (review);
+ g_return_if_fail (AS_IS_REVIEW (review));
+ priv->rating = rating;
+}
+
+/**
+ * as_review_get_flags:
+ * @review: a #AsReview
+ *
+ * Gets any flags set on the review, for example if the user has already
+ * voted on the review or if the user wrote the review themselves.
+ *
+ * Returns: a #AsReviewFlags, e.g. %AS_REVIEW_FLAG_SELF
+ *
+ * Since: 0.5.18
+ **/
+AsReviewFlags
+as_review_get_flags (AsReview *review)
+{
+ AsReviewPrivate *priv = GET_PRIVATE (review);
+ g_return_val_if_fail (AS_IS_REVIEW (review), 0);
+ return priv->flags;
+}
+
+/**
+ * as_review_set_flags:
+ * @review: a #AsReview
+ * @flags: a #AsReviewFlags, e.g. %AS_REVIEW_FLAG_SELF
+ *
+ * Gets any flags set on the review, for example if the user has already
+ * voted on the review or if the user wrote the review themselves.
+ *
+ * Since: 0.5.18
+ **/
+void
+as_review_set_flags (AsReview *review, AsReviewFlags flags)
+{
+ AsReviewPrivate *priv = GET_PRIVATE (review);
+ g_return_if_fail (AS_IS_REVIEW (review));
+ priv->flags = flags;
+}
+
+/**
+ * as_review_add_flags:
+ * @review: a #AsReview
+ * @flags: a #AsReviewFlags, e.g. %AS_REVIEW_FLAG_SELF
+ *
+ * Adds flags to an existing review without replacing the other flags.
+ *
+ * Since: 0.5.18
+ **/
+void
+as_review_add_flags (AsReview *review, AsReviewFlags flags)
+{
+ AsReviewPrivate *priv = GET_PRIVATE (review);
+ g_return_if_fail (AS_IS_REVIEW (review));
+ priv->flags |= flags;
+}
+
+/**
+ * as_review_get_reviewer_id:
+ * @review: a #AsReview
+ *
+ * Gets the name of the reviewer.
+ *
+ * Returns: the reviewer ID, e.g. "deadbeef", or %NULL
+ *
+ * Since: 0.5.18
+ **/
+const gchar *
+as_review_get_reviewer_id (AsReview *review)
+{
+ AsReviewPrivate *priv = GET_PRIVATE (review);
+ g_return_val_if_fail (AS_IS_REVIEW (review), NULL);
+ return priv->reviewer_id;
+}
+
+/**
+ * as_review_get_reviewer_name:
+ * @review: a #AsReview
+ *
+ * Gets the name of the reviewer.
+ *
+ * Returns: the reviewer name, e.g. "David Smith", or %NULL
+ *
+ * Since: 0.5.18
+ **/
+const gchar *
+as_review_get_reviewer_name (AsReview *review)
+{
+ AsReviewPrivate *priv = GET_PRIVATE (review);
+ g_return_val_if_fail (AS_IS_REVIEW (review), NULL);
+ return priv->reviewer_name;
+}
+
+/**
+ * as_review_set_reviewer_id:
+ * @review: a #AsReview
+ * @reviewer_id: the reviewer ID, e.g. "deadbeef"
+ *
+ * Sets the name of the reviewer, which can be left unset.
+ *
+ * Since: 0.5.18
+ **/
+void
+as_review_set_reviewer_id (AsReview *review, const gchar *reviewer_id)
+{
+ AsReviewPrivate *priv = GET_PRIVATE (review);
+ g_return_if_fail (AS_IS_REVIEW (review));
+ g_free (priv->reviewer_id);
+ priv->reviewer_id = g_strdup (reviewer_id);
+}
+
+/**
+ * as_review_set_reviewer_name:
+ * @review: a #AsReview
+ * @reviewer_name: the reviewer name, e.g. "David Smith"
+ *
+ * Sets the name of the reviewer, which can be left unset.
+ *
+ * Since: 0.5.18
+ **/
+void
+as_review_set_reviewer_name (AsReview *review, const gchar *reviewer_name)
+{
+ AsReviewPrivate *priv = GET_PRIVATE (review);
+ g_return_if_fail (AS_IS_REVIEW (review));
+ g_free (priv->reviewer_name);
+ priv->reviewer_name = g_strdup (reviewer_name);
+}
+
+/**
+ * as_review_set_version:
+ * @review: a #AsReview
+ * @version: a version string, e.g. "0.1.2"
+ *
+ * Sets the version string for the application being reviewed.
+ *
+ * Since: 0.5.18
+ **/
+void
+as_review_set_version (AsReview *review, const gchar *version)
+{
+ AsReviewPrivate *priv = GET_PRIVATE (review);
+ g_return_if_fail (AS_IS_REVIEW (review));
+ g_free (priv->version);
+ priv->version = g_strdup (version);
+}
+
+/**
+ * as_review_get_version:
+ * @review: a #AsReview
+ *
+ * Gets the version string for the application being reviewed..
+ *
+ * Returns: the version string, e.g. "0.1.2", or %NULL for unset
+ *
+ * Since: 0.5.18
+ **/
+const gchar *
+as_review_get_version (AsReview *review)
+{
+ AsReviewPrivate *priv = GET_PRIVATE (review);
+ g_return_val_if_fail (AS_IS_REVIEW (review), NULL);
+ return priv->version;
+}
+
+/**
+ * as_review_get_date:
+ * @review: a #AsReview
+ *
+ * Gets the date the review was originally submitted.
+ *
+ * Returns: (transfer none): the #GDateTime, or %NULL for unset
+ *
+ * Since: 0.5.18
+ **/
+GDateTime *
+as_review_get_date (AsReview *review)
+{
+ AsReviewPrivate *priv = GET_PRIVATE (review);
+ g_return_val_if_fail (AS_IS_REVIEW (review), NULL);
+ return priv->date;
+}
+
+/**
+ * as_review_set_date:
+ * @review: a #AsReview
+ * @date: a #GDateTime, or %NULL
+ *
+ * Sets the date the review was originally submitted.
+ *
+ * Since: 0.5.18
+ **/
+void
+as_review_set_date (AsReview *review, GDateTime *date)
+{
+ AsReviewPrivate *priv = GET_PRIVATE (review);
+ g_return_if_fail (AS_IS_REVIEW (review));
+ g_clear_pointer (&priv->date, g_date_time_unref);
+ if (date != NULL)
+ priv->date = g_date_time_ref (date);
+}
+
+/**
+ * as_review_get_metadata_item:
+ * @review: a #AsReview
+ * @key: a string
+ *
+ * Gets some metadata from a review object.
+ * It is left for the the plugin to use this method as required, but a
+ * typical use would be to retrieve some secure authentication token.
+ *
+ * Returns: A string value, or %NULL for not found
+ *
+ * Since: 0.5.18
+ **/
+const gchar *
+as_review_get_metadata_item (AsReview *review, const gchar *key)
+{
+ AsReviewPrivate *priv = GET_PRIVATE (review);
+ g_return_val_if_fail (AS_IS_REVIEW (review), NULL);
+ g_return_val_if_fail (key != NULL, NULL);
+ return g_hash_table_lookup (priv->metadata, key);
+}
+
+/**
+ * as_review_add_metadata:
+ * @review: a #AsReview
+ * @key: a string
+ * @value: a string
+ *
+ * Adds metadata to the review object.
+ * It is left for the the plugin to use this method as required, but a
+ * typical use would be to store some secure authentication token.
+ *
+ * Since: 0.5.18
+ **/
+void
+as_review_add_metadata (AsReview *review, const gchar *key, const gchar *value)
+{
+ AsReviewPrivate *priv = GET_PRIVATE (review);
+ g_return_if_fail (AS_IS_REVIEW (review));
+ g_hash_table_insert (priv->metadata, g_strdup (key), g_strdup (value));
+}
+
+/**
+ * as_review_equal:
+ * @review1: a #AsReview instance.
+ * @review2: a #AsReview instance.
+ *
+ * Checks if two reviews are the same.
+ *
+ * Returns: %TRUE for success
+ *
+ * Since: 0.5.18
+ **/
+gboolean
+as_review_equal (AsReview *review1, AsReview *review2)
+{
+ AsReviewPrivate *priv1 = GET_PRIVATE (review1);
+ AsReviewPrivate *priv2 = GET_PRIVATE (review2);
+
+ /* trivial */
+ if (review1 == review2)
+ return TRUE;
+
+ /* check for equality */
+ if (!g_date_time_equal (priv1->date, priv2->date))
+ return FALSE;
+ if (priv1->karma != priv2->karma)
+ return FALSE;
+ if (priv1->score != priv2->score)
+ return FALSE;
+ if (priv1->rating != priv2->rating)
+ return FALSE;
+ if (g_strcmp0 (priv1->id, priv2->id) != 0)
+ return FALSE;
+ if (g_strcmp0 (priv1->summary, priv2->summary) != 0)
+ return FALSE;
+ if (g_strcmp0 (priv1->description, priv2->description) != 0)
+ return FALSE;
+ if (g_strcmp0 (priv1->locale, priv2->locale) != 0)
+ return FALSE;
+ if (g_strcmp0 (priv1->version, priv2->version) != 0)
+ return FALSE;
+
+ /* success */
+ return TRUE;
+}
+
+/**
+ * as_review_node_insert: (skip)
+ * @review: a #AsReview instance.
+ * @parent: the parent #GNode to use..
+ * @ctx: the #AsNodeContext
+ *
+ * Inserts the review into the DOM tree.
+ *
+ * Returns: (transfer none): A populated #GNode
+ *
+ * Since: 0.5.18
+ **/
+GNode *
+as_review_node_insert (AsReview *review, GNode *parent, AsNodeContext *ctx)
+{
+ AsReviewPrivate *priv = GET_PRIVATE (review);
+ GNode *n;
+
+ n = as_node_insert (parent, "review", NULL,
+ AS_NODE_INSERT_FLAG_NONE,
+ NULL);
+ if (priv->id != NULL)
+ as_node_add_attribute (n, "id", priv->id);
+ if (priv->karma != 0) {
+ g_autofree gchar *str = g_strdup_printf ("%i", priv->karma);
+ as_node_add_attribute (n, "karma", str);
+ }
+ if (priv->score != 0) {
+ g_autofree gchar *str = g_strdup_printf ("%i", priv->score);
+ as_node_add_attribute (n, "score", str);
+ }
+ if (priv->rating != 0) {
+ g_autofree gchar *str = g_strdup_printf ("%i", priv->rating);
+ as_node_add_attribute (n, "rating", str);
+ }
+ if (priv->date != NULL) {
+ g_autofree gchar *str = g_date_time_format (priv->date, "%F");
+ as_node_add_attribute (n, "date", str);
+ }
+ if (priv->summary != NULL) {
+ as_node_insert (n, "summary", priv->summary,
+ AS_NODE_INSERT_FLAG_NONE,
+ NULL);
+ }
+ if (priv->description != NULL) {
+ as_node_insert (n, "description", priv->description,
+ AS_NODE_INSERT_FLAG_PRE_ESCAPED,
+ NULL);
+ }
+ if (priv->version != NULL) {
+ as_node_insert (n, "version", priv->version,
+ AS_NODE_INSERT_FLAG_NONE,
+ NULL);
+ }
+ if (priv->reviewer_id != NULL) {
+ as_node_insert (n, "reviewer_id", priv->reviewer_id,
+ AS_NODE_INSERT_FLAG_NONE,
+ NULL);
+ }
+ if (priv->reviewer_name != NULL) {
+ as_node_insert (n, "reviewer_name", priv->reviewer_name,
+ AS_NODE_INSERT_FLAG_NONE,
+ NULL);
+ }
+ if (priv->locale != NULL) {
+ as_node_insert (n, "lang", priv->locale,
+ AS_NODE_INSERT_FLAG_NONE,
+ NULL);
+ }
+
+ /* <metadata> */
+ if (g_hash_table_size (priv->metadata) > 0) {
+ AsNode *node_tmp;
+ node_tmp = as_node_insert (n, "metadata", NULL, 0, NULL);
+ as_node_insert_hash (node_tmp, "value", "key", priv->metadata, FALSE);
+ }
+
+ return n;
+}
+
+/**
+ * as_review_node_parse:
+ * @review: a #AsReview instance.
+ * @node: a #GNode.
+ * @ctx: a #AsNodeContext.
+ * @error: A #GError or %NULL.
+ *
+ * Populates the object from a DOM node.
+ *
+ * Returns: %TRUE for success
+ *
+ * Since: 0.5.18
+ **/
+gboolean
+as_review_node_parse (AsReview *review, GNode *node,
+ AsNodeContext *ctx, GError **error)
+{
+ AsReviewPrivate *priv = GET_PRIVATE (review);
+ AsNode *c;
+ const gchar *tmp;
+ gint itmp;
+
+ itmp = as_node_get_attribute_as_int (node, "karma");
+ if (itmp != G_MAXINT)
+ as_review_set_karma (review, itmp);
+ itmp = as_node_get_attribute_as_int (node, "score");
+ if (itmp != G_MAXINT)
+ as_review_set_score (review, itmp);
+ itmp = as_node_get_attribute_as_int (node, "rating");
+ if (itmp != G_MAXINT)
+ as_review_set_rating (review, itmp);
+ tmp = as_node_get_attribute (node, "date");
+ if (tmp != NULL) {
+ g_autoptr(GDateTime) dt = as_utils_iso8601_to_datetime (tmp);
+ if (dt != NULL)
+ as_review_set_date (review, dt);
+ }
+ tmp = as_node_get_attribute (node, "id");
+ if (tmp != NULL)
+ as_review_set_id (review, tmp);
+ for (c = node->children; c != NULL; c = c->next) {
+ if (as_node_get_tag (c) == AS_TAG_SUMMARY) {
+ as_review_set_summary (review, as_node_get_data (c));
+ continue;
+ }
+ if (as_node_get_tag (c) == AS_TAG_DESCRIPTION) {
+ g_autoptr(GString) xml = NULL;
+ xml = as_node_to_xml (c->children, AS_NODE_TO_XML_FLAG_INCLUDE_SIBLINGS);
+ as_review_set_description (review, xml->str);
+ continue;
+ }
+ if (as_node_get_tag (c) == AS_TAG_VERSION) {
+ as_review_set_version (review, as_node_get_data (c));
+ continue;
+ }
+ if (as_node_get_tag (c) == AS_TAG_REVIEWER_ID) {
+ as_review_set_reviewer_id (review, as_node_get_data (c));
+ continue;
+ }
+ if (as_node_get_tag (c) == AS_TAG_REVIEWER_NAME) {
+ as_review_set_reviewer_name (review, as_node_get_data (c));
+ continue;
+ }
+ if (as_node_get_tag (c) == AS_TAG_LANG) {
+ as_review_set_locale (review, as_node_get_data (c));
+ continue;
+ }
+ if (as_node_get_tag (c) == AS_TAG_METADATA) {
+ AsNode *c2;
+ gchar *taken;
+ for (c2 = c->children; c2 != NULL; c2 = c2->next) {
+ gchar *key;
+ if (as_node_get_tag (c2) != AS_TAG_VALUE)
+ continue;
+ key = as_node_take_attribute (c2, "key");
+ taken = as_node_take_data (c2);
+ if (taken == NULL)
+ taken = g_strdup ("");
+ g_hash_table_insert (priv->metadata, key, taken);
+ }
+ continue;
+ }
+ }
+ return TRUE;
+}
+
+/**
+ * as_review_node_parse_dep11:
+ * @review: a #AsReview instance.
+ * @node: a #GNode.
+ * @ctx: a #AsNodeContext.
+ * @error: A #GError or %NULL.
+ *
+ * Populates the object from a DEP-11 node.
+ *
+ * Returns: %TRUE for success
+ *
+ * Since: 0.5.18
+ **/
+gboolean
+as_review_node_parse_dep11 (AsReview *im, GNode *node,
+ AsNodeContext *ctx, GError **error)
+{
+ return TRUE;
+}
+
+/**
+ * as_review_new:
+ *
+ * Creates a new #AsReview.
+ *
+ * Returns: (transfer full): a #AsReview
+ *
+ * Since: 0.5.18
+ **/
+AsReview *
+as_review_new (void)
+{
+ AsReview *review;
+ review = g_object_new (AS_TYPE_REVIEW, NULL);
+ return AS_REVIEW (review);
+}
diff --git a/libappstream-glib/as-review.h b/libappstream-glib/as-review.h
new file mode 100644
index 0000000..3b3b5f9
--- /dev/null
+++ b/libappstream-glib/as-review.h
@@ -0,0 +1,124 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2016 Canonical Ltd.
+ * Copyright (C) 2016 Richard Hughes <richard@hughsie.com>
+ *
+ * Licensed under the GNU Lesser General Public License Version 2.1
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#if !defined (__APPSTREAM_GLIB_H) && !defined (AS_COMPILATION)
+#error "Only <appstream-glib.h> can be included directly."
+#endif
+
+#ifndef __AS_REVIEW_H
+#define __AS_REVIEW_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define AS_TYPE_REVIEW (as_review_get_type ())
+G_DECLARE_DERIVABLE_TYPE (AsReview, as_review, AS, REVIEW, GObject)
+
+struct _AsReviewClass
+{
+ GObjectClass parent_class;
+ /*< private >*/
+ void (*_as_reserved1) (void);
+ void (*_as_reserved2) (void);
+ void (*_as_reserved3) (void);
+ void (*_as_reserved4) (void);
+ void (*_as_reserved5) (void);
+ void (*_as_reserved6) (void);
+ void (*_as_reserved7) (void);
+ void (*_as_reserved8) (void);
+};
+
+/**
+ * AsReviewFlags:
+ * @AS_REVIEW_FLAG_NONE: No special flags set
+ * @AS_REVIEW_FLAG_SELF: The user wrote the review themselves
+ * @AS_REVIEW_FLAG_VOTED: The user voted on the review
+ *
+ * The flags for the review.
+ *
+ * Since: 0.5.18
+ **/
+typedef enum {
+ AS_REVIEW_FLAG_NONE = 0,
+ AS_REVIEW_FLAG_SELF = 1 << 0,
+ AS_REVIEW_FLAG_VOTED = 1 << 1,
+ /*< private >*/
+ AS_REVIEW_FLAG_LAST
+} AsReviewFlags;
+
+AsReview *as_review_new (void);
+
+/* getters */
+gint as_review_get_karma (AsReview *review);
+gint as_review_get_score (AsReview *review);
+const gchar *as_review_get_id (AsReview *review);
+const gchar *as_review_get_summary (AsReview *review);
+const gchar *as_review_get_description (AsReview *review);
+const gchar *as_review_get_locale (AsReview *review);
+gint as_review_get_rating (AsReview *review);
+const gchar *as_review_get_version (AsReview *review);
+const gchar *as_review_get_reviewer_id (AsReview *review);
+const gchar *as_review_get_reviewer_name (AsReview *review);
+GDateTime *as_review_get_date (AsReview *review);
+AsReviewFlags as_review_get_flags (AsReview *review);
+const gchar *as_review_get_metadata_item (AsReview *review,
+ const gchar *key);
+
+/* setters */
+void as_review_set_karma (AsReview *review,
+ gint karma);
+void as_review_set_score (AsReview *review,
+ gint score);
+void as_review_set_id (AsReview *review,
+ const gchar *id);
+void as_review_set_summary (AsReview *review,
+ const gchar *summary);
+void as_review_set_description (AsReview *review,
+ const gchar *description);
+void as_review_set_locale (AsReview *review,
+ const gchar *locale);
+void as_review_set_rating (AsReview *review,
+ gint rating);
+void as_review_set_version (AsReview *review,
+ const gchar *version);
+void as_review_set_reviewer_id (AsReview *review,
+ const gchar *reviewer_id);
+void as_review_set_reviewer_name (AsReview *review,
+ const gchar *reviewer_name);
+void as_review_set_date (AsReview *review,
+ GDateTime *date);
+void as_review_set_flags (AsReview *review,
+ AsReviewFlags flags);
+void as_review_add_flags (AsReview *review,
+ AsReviewFlags flags);
+void as_review_add_metadata (AsReview *review,
+ const gchar *key,
+ const gchar *value);
+
+/* helpers */
+gboolean as_review_equal (AsReview *review1,
+ AsReview *review2);
+
+G_END_DECLS
+
+#endif /* __AS_REVIEW_H */
diff --git a/libappstream-glib/as-self-test.c b/libappstream-glib/as-self-test.c
index c5e404b..8ac8cbf 100644
--- a/libappstream-glib/as-self-test.c
+++ b/libappstream-glib/as-self-test.c
@@ -35,6 +35,7 @@
#include "as-enums.h"
#include "as-icon-private.h"
#include "as-image-private.h"
+#include "as-review-private.h"
#include "as-inf.h"
#include "as-markup.h"
#include "as-monitor.h"
@@ -1173,6 +1174,68 @@ as_test_image_func (void)
}
static void
+as_test_review_func (void)
+{
+ GError *error = NULL;
+ AsNode *n;
+ AsNode *root;
+ GString *xml;
+ const gchar *src =
+ "<review date=\"2016-09-15\" rating=\"80\" score=\"5\" karma=\"-1\" id=\"17\">\n"
+ "<summary>Hello world</summary>\n"
+ "<description><p>Mighty Fine</p></description>\n"
+ "<version>1.2.3</version>\n"
+ "<reviewer_id>deadbeef</reviewer_id>\n"
+ "<reviewer_name>Richard Hughes</reviewer_name>\n"
+ "<lang>en_GB</lang>\n"
+ "<metadata>\n"
+ "<value key=\"foo\">bar</value>\n"
+ "</metadata>\n"
+ "</review>\n";
+ gboolean ret;
+ g_autofree AsNodeContext *ctx = NULL;
+ g_autoptr(AsReview) review = NULL;
+
+ review = as_review_new ();
+
+ /* to object */
+ root = as_node_from_xml (src, 0, &error);
+ g_assert_no_error (error);
+ g_assert (root != NULL);
+ n = as_node_find (root, "review");
+ g_assert (n != NULL);
+ ctx = as_node_context_new ();
+ ret = as_review_node_parse (review, n, ctx, &error);
+ g_assert_no_error (error);
+ g_assert (ret);
+ as_node_unref (root);
+
+ /* verify */
+ g_assert_cmpint (as_review_get_karma (review), ==, -1);
+ g_assert_cmpint (as_review_get_score (review), ==, 5);
+ g_assert (as_review_get_date (review) != NULL);
+ g_assert_cmpstr (as_review_get_id (review), ==, "17");
+ g_assert_cmpstr (as_review_get_version (review), ==, "1.2.3");
+ g_assert_cmpstr (as_review_get_reviewer_id (review), ==, "deadbeef");
+ g_assert_cmpstr (as_review_get_reviewer_name (review), ==, "Richard Hughes");
+ g_assert_cmpstr (as_review_get_summary (review), ==, "Hello world");
+ g_assert_cmpstr (as_review_get_locale (review), ==, "en_GB");
+ g_assert_cmpstr (as_review_get_description (review), ==, "<p>Mighty Fine</p>");
+ g_assert_cmpstr (as_review_get_metadata_item (review, "foo"), ==, "bar");
+
+ /* back to node */
+ root = as_node_new ();
+ as_node_context_set_version (ctx, 0.4);
+ n = as_review_node_insert (review, root, ctx);
+ xml = as_node_to_xml (n, AS_NODE_TO_XML_FLAG_FORMAT_MULTILINE);
+ ret = as_test_compare_lines (xml->str, src, &error);
+ g_assert_no_error (error);
+ g_assert (ret);
+ g_string_free (xml, TRUE);
+ as_node_unref (root);
+}
+
+static void
as_test_bundle_func (void)
{
GError *error = NULL;
@@ -1430,6 +1493,11 @@ as_test_app_func (void)
"<image type=\"thumbnail\">http://b.png</image>\n"
"</screenshot>\n"
"</screenshots>\n"
+ "<reviews>\n"
+ "<review date=\"2016-09-15\">\n"
+ "<summary>Hello world</summary>\n"
+ "</review>\n"
+ "</reviews>\n"
"<content_rating type=\"oars-1.0\">\n"
"<content_attribute id=\"drugs-alcohol\">moderate</content_attribute>\n"
"</content_rating>\n"
@@ -4858,6 +4926,7 @@ main (int argc, char **argv)
g_test_add_func ("/AppStream/icon", as_test_icon_func);
g_test_add_func ("/AppStream/icon{embedded}", as_test_icon_embedded_func);
g_test_add_func ("/AppStream/bundle", as_test_bundle_func);
+ g_test_add_func ("/AppStream/review", as_test_review_func);
g_test_add_func ("/AppStream/translation", as_test_translation_func);
g_test_add_func ("/AppStream/image", as_test_image_func);
g_test_add_func ("/AppStream/image{resize}", as_test_image_resize_func);
diff --git a/libappstream-glib/as-tag.c b/libappstream-glib/as-tag.c
index e802dcb..a431f1b 100644
--- a/libappstream-glib/as-tag.c
+++ b/libappstream-glib/as-tag.c
@@ -194,6 +194,11 @@ as_tag_to_string (AsTag tag)
"translation",
"content_rating",
"content_attribute",
+ "version",
+ "reviews",
+ "review",
+ "reviewer_name",
+ "reviewer_id",
NULL };
if (tag > AS_TAG_LAST)
tag = AS_TAG_LAST;
diff --git a/libappstream-glib/as-tag.gperf b/libappstream-glib/as-tag.gperf
index 7e13f72..5508e65 100644
--- a/libappstream-glib/as-tag.gperf
+++ b/libappstream-glib/as-tag.gperf
@@ -58,3 +58,8 @@ size, AS_TAG_SIZE
translation, AS_TAG_TRANSLATION
content_rating, AS_TAG_CONTENT_RATING
content_attribute, AS_TAG_CONTENT_ATTRIBUTE
+version, AS_TAG_VERSION
+reviews, AS_TAG_REVIEWS
+review, AS_TAG_REVIEW
+reviewer_name, AS_TAG_REVIEWER_NAME
+reviewer_id, AS_TAG_REVIEWER_ID
diff --git a/libappstream-glib/as-tag.h b/libappstream-glib/as-tag.h
index 8579745..f4eab76 100644
--- a/libappstream-glib/as-tag.h
+++ b/libappstream-glib/as-tag.h
@@ -83,6 +83,11 @@ G_BEGIN_DECLS
* @AS_TAG_TRANSLATION: `translation`
* @AS_TAG_CONTENT_RATING: `content_rating`
* @AS_TAG_CONTENT_ATTRIBUTE: `content_attribute`
+ * @AS_TAG_VERSION: `version`
+ * @AS_TAG_REVIEWS: `reviews`
+ * @AS_TAG_REVIEW: `review`
+ * @AS_TAG_REVIEWER_NAME: `reviewer_name`
+ * @AS_TAG_REVIEWER_ID: `reviewer_id`
*
* The tag type.
**/
@@ -138,6 +143,11 @@ typedef enum {
AS_TAG_TRANSLATION, /* Since: 0.5.8 */
AS_TAG_CONTENT_RATING, /* Since: 0.5.12 */
AS_TAG_CONTENT_ATTRIBUTE, /* Since: 0.5.12 */
+ AS_TAG_VERSION, /* Since: 0.5.18 */
+ AS_TAG_REVIEWS, /* Since: 0.5.18 */
+ AS_TAG_REVIEW, /* Since: 0.5.18 */
+ AS_TAG_REVIEWER_NAME, /* Since: 0.5.18 */
+ AS_TAG_REVIEWER_ID, /* Since: 0.5.18 */
/*< private >*/
AS_TAG_LAST
} AsTag;