1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
2  *
3  * Copyright (C) 2016 Canonical Ltd.
4  * Copyright (C) 2016 Richard Hughes <richard@hughsie.com>
5  *
6  * SPDX-License-Identifier: LGPL-2.1+
7  */
8 
9 /**
10  * SECTION:as-review
11  * @title: AsReview
12  * @include: appstream-glib.h
13  * @stability: Stable
14  * @short_description: An application user review
15  *
16  * This object represents a user-submitted application review.
17  *
18  * Since: 0.6.1
19  **/
20 
21 #include "config.h"
22 
23 #include "as-review-private.h"
24 #include "as-node-private.h"
25 #include "as-ref-string.h"
26 #include "as-utils-private.h"
27 #include "as-yaml.h"
28 
29 typedef struct
30 {
31 	AsReviewFlags		 flags;
32 	AsRefString		*id;
33 	AsRefString		*summary;
34 	AsRefString		*description;
35 	AsRefString		*locale;
36 	gint			 priority;
37 	gint			 rating;
38 	AsRefString		*version;
39 	AsRefString		*reviewer_id;
40 	AsRefString		*reviewer_name;
41 	GDateTime		*date;
42 	GHashTable		*metadata;	/* AsRefString : AsRefString */
43 } AsReviewPrivate;
44 
45 enum {
46 	PROP_0,
47 	PROP_ID,
48 	PROP_SUMMARY,
49 	PROP_DESCRIPTION,
50 	PROP_LOCALE,
51 	PROP_RATING,
52 	PROP_VERSION,
53 	PROP_REVIEWER_ID,
54 	PROP_REVIEWER_NAME,
55 	PROP_DATE,
56 	PROP_FLAGS,
57 	PROP_LAST
58 };
59 
G_DEFINE_TYPE_WITH_PRIVATE(AsReview,as_review,G_TYPE_OBJECT)60 G_DEFINE_TYPE_WITH_PRIVATE (AsReview, as_review, G_TYPE_OBJECT)
61 
62 #define GET_PRIVATE(o) (as_review_get_instance_private (o))
63 
64 static void
65 as_review_finalize (GObject *object)
66 {
67 	AsReview *review = AS_REVIEW (object);
68 	AsReviewPrivate *priv = GET_PRIVATE (review);
69 
70 	if (priv->id != NULL)
71 		as_ref_string_unref (priv->id);
72 	if (priv->summary != NULL)
73 		as_ref_string_unref (priv->summary);
74 	if (priv->description != NULL)
75 		as_ref_string_unref (priv->description);
76 	if (priv->locale != NULL)
77 		as_ref_string_unref (priv->locale);
78 	if (priv->version != NULL)
79 		as_ref_string_unref (priv->version);
80 	if (priv->reviewer_id != NULL)
81 		as_ref_string_unref (priv->reviewer_id);
82 	if (priv->reviewer_name != NULL)
83 		as_ref_string_unref (priv->reviewer_name);
84 	g_hash_table_unref (priv->metadata);
85 	if (priv->date != NULL)
86 		g_date_time_unref (priv->date);
87 
88 	G_OBJECT_CLASS (as_review_parent_class)->finalize (object);
89 }
90 
91 static void
as_review_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)92 as_review_get_property (GObject *object, guint prop_id,
93 			GValue *value, GParamSpec *pspec)
94 {
95 	AsReview *review = AS_REVIEW (object);
96 	AsReviewPrivate *priv = GET_PRIVATE (review);
97 
98 	switch (prop_id) {
99 	case PROP_ID:
100 		g_value_set_string (value, priv->id);
101 		break;
102 	case PROP_SUMMARY:
103 		g_value_set_string (value, priv->summary);
104 		break;
105 	case PROP_DESCRIPTION:
106 		g_value_set_string (value, priv->description);
107 		break;
108 	case PROP_LOCALE:
109 		g_value_set_string (value, priv->locale);
110 		break;
111 	case PROP_RATING:
112 		g_value_set_int (value, priv->rating);
113 		break;
114 	case PROP_FLAGS:
115 		g_value_set_uint64 (value, priv->flags);
116 		break;
117 	case PROP_VERSION:
118 		g_value_set_string (value, priv->version);
119 		break;
120 	case PROP_REVIEWER_ID:
121 		g_value_set_string (value, priv->reviewer_id);
122 		break;
123 	case PROP_REVIEWER_NAME:
124 		g_value_set_string (value, priv->reviewer_name);
125 		break;
126 	case PROP_DATE:
127 		g_value_set_object (value, priv->date);
128 		break;
129 	default:
130 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
131 		break;
132 	}
133 }
134 
135 static void
as_review_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)136 as_review_set_property (GObject *object, guint prop_id,
137 			const GValue *value, GParamSpec *pspec)
138 {
139 	AsReview *review = AS_REVIEW (object);
140 
141 	switch (prop_id) {
142 	case PROP_ID:
143 		as_review_set_id (review, g_value_get_string (value));
144 		break;
145 	case PROP_SUMMARY:
146 		as_review_set_summary (review, g_value_get_string (value));
147 		break;
148 	case PROP_DESCRIPTION:
149 		as_review_set_description (review, g_value_get_string (value));
150 		break;
151 	case PROP_LOCALE:
152 		as_review_set_locale (review, g_value_get_string (value));
153 		break;
154 	case PROP_RATING:
155 		as_review_set_rating (review, g_value_get_int (value));
156 		break;
157 	case PROP_FLAGS:
158 		as_review_set_flags (review, g_value_get_uint64 (value));
159 		break;
160 	case PROP_VERSION:
161 		as_review_set_version (review, g_value_get_string (value));
162 		break;
163 	case PROP_REVIEWER_ID:
164 		as_review_set_reviewer_id (review, g_value_get_string (value));
165 		break;
166 	case PROP_REVIEWER_NAME:
167 		as_review_set_reviewer_name (review, g_value_get_string (value));
168 		break;
169 	case PROP_DATE:
170 		as_review_set_date (review, g_value_get_object (value));
171 		break;
172 	default:
173 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
174 		break;
175 	}
176 }
177 
178 static void
as_review_class_init(AsReviewClass * klass)179 as_review_class_init (AsReviewClass *klass)
180 {
181 	GParamSpec *pspec;
182 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
183 	object_class->finalize = as_review_finalize;
184 	object_class->get_property = as_review_get_property;
185 	object_class->set_property = as_review_set_property;
186 
187 	/**
188 	 * AsReview:id:
189 	 *
190 	 * Since: 0.6.1
191 	 **/
192 	pspec = g_param_spec_string ("id", NULL, NULL,
193 				     NULL,
194 				     G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
195 	g_object_class_install_property (object_class, PROP_ID, pspec);
196 
197 	/**
198 	 * AsReview:summary:
199 	 *
200 	 * Since: 0.6.1
201 	 **/
202 	pspec = g_param_spec_string ("summary", NULL, NULL,
203 				     NULL,
204 				     G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
205 	g_object_class_install_property (object_class, PROP_SUMMARY, pspec);
206 
207 	/**
208 	 * AsReview:description:
209 	 *
210 	 * Since: 0.6.1
211 	 **/
212 	pspec = g_param_spec_string ("description", NULL, NULL,
213 				     NULL,
214 				     G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
215 	g_object_class_install_property (object_class, PROP_DESCRIPTION, pspec);
216 
217 	/**
218 	 * AsReview:locale:
219 	 *
220 	 * Since: 0.6.1
221 	 **/
222 	pspec = g_param_spec_string ("locale", NULL, NULL,
223 				     NULL,
224 				     G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
225 	g_object_class_install_property (object_class, PROP_LOCALE, pspec);
226 
227 	/**
228 	 * AsReview:rating:
229 	 *
230 	 * Since: 0.6.1
231 	 **/
232 	pspec = g_param_spec_int ("rating", NULL, NULL,
233 				  -1, 100, 0,
234 				  G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
235 	g_object_class_install_property (object_class, PROP_RATING, pspec);
236 
237 	/**
238 	 * AsReview:flags:
239 	 *
240 	 * Since: 0.6.1
241 	 **/
242 	pspec = g_param_spec_uint64 ("flags", NULL, NULL,
243 				     AS_REVIEW_FLAG_NONE,
244 				     AS_REVIEW_FLAG_LAST,
245 				     AS_REVIEW_FLAG_NONE,
246 				     G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
247 	g_object_class_install_property (object_class, PROP_FLAGS, pspec);
248 
249 	/**
250 	 * AsReview:version:
251 	 *
252 	 * Since: 0.6.1
253 	 **/
254 	pspec = g_param_spec_string ("version", NULL, NULL,
255 				     NULL,
256 				     G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
257 	g_object_class_install_property (object_class, PROP_VERSION, pspec);
258 
259 	/**
260 	 * AsReview:reviewer-id:
261 	 *
262 	 * Since: 0.6.1
263 	 **/
264 	pspec = g_param_spec_string ("reviewer-id", NULL, NULL,
265 				     NULL,
266 				     G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
267 	g_object_class_install_property (object_class, PROP_REVIEWER_ID, pspec);
268 
269 	/**
270 	 * AsReview:reviewer-name:
271 	 *
272 	 * Since: 0.6.1
273 	 **/
274 	pspec = g_param_spec_string ("reviewer-name", NULL, NULL,
275 				     NULL,
276 				     G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
277 	g_object_class_install_property (object_class, PROP_REVIEWER_NAME, pspec);
278 
279 	/**
280 	 * AsReview:date:
281 	 *
282 	 * Since: 0.6.1
283 	 **/
284 	pspec = g_param_spec_object ("date", NULL, NULL,
285 				     AS_TYPE_REVIEW,
286 				     G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
287 	g_object_class_install_property (object_class, PROP_DATE, pspec);
288 }
289 
290 static void
as_review_init(AsReview * review)291 as_review_init (AsReview *review)
292 {
293 	AsReviewPrivate *priv = GET_PRIVATE (review);
294 	priv->metadata = g_hash_table_new_full (g_str_hash, g_str_equal,
295 						(GDestroyNotify) as_ref_string_unref,
296 						(GDestroyNotify) as_ref_string_unref);
297 }
298 
299 /**
300  * as_review_get_priority:
301  * @review: a #AsReview
302  *
303  * This allows the UI to sort reviews into the correct order.
304  * Higher numbers indicate a more important or relevant review.
305  *
306  * Returns: the review priority, or 0 for unset.
307  *
308  * Since: 0.6.1
309  **/
310 gint
as_review_get_priority(AsReview * review)311 as_review_get_priority (AsReview *review)
312 {
313 	AsReviewPrivate *priv = GET_PRIVATE (review);
314 	g_return_val_if_fail (AS_IS_REVIEW (review), 0);
315 	return priv->priority;
316 }
317 
318 /**
319  * as_review_set_priority:
320  * @review: a #AsReview
321  * @priority: a priority value
322  *
323  * Sets the priority for the review, where positive numbers indicate
324  * a better review for the specific user.
325  *
326  * Since: 0.6.1
327  **/
328 void
as_review_set_priority(AsReview * review,gint priority)329 as_review_set_priority (AsReview *review, gint priority)
330 {
331 	AsReviewPrivate *priv = GET_PRIVATE (review);
332 	g_return_if_fail (AS_IS_REVIEW (review));
333 	priv->priority = priority;
334 }
335 
336 /**
337  * as_review_get_id:
338  * @review: a #AsReview
339  *
340  * Gets the review id.
341  *
342  * Returns: the review identifier, e.g. "deadbeef"
343  *
344  * Since: 0.6.1
345  **/
346 const gchar *
as_review_get_id(AsReview * review)347 as_review_get_id (AsReview *review)
348 {
349 	AsReviewPrivate *priv = GET_PRIVATE (review);
350 	g_return_val_if_fail (AS_IS_REVIEW (review), NULL);
351 	return priv->id;
352 }
353 
354 /**
355  * as_review_get_summary:
356  * @review: a #AsReview
357  *
358  * Gets the review summary.
359  *
360  * Returns: the one-line summary, e.g. "Awesome application"
361  *
362  * Since: 0.6.1
363  **/
364 const gchar *
as_review_get_summary(AsReview * review)365 as_review_get_summary (AsReview *review)
366 {
367 	AsReviewPrivate *priv = GET_PRIVATE (review);
368 	g_return_val_if_fail (AS_IS_REVIEW (review), NULL);
369 	return priv->summary;
370 }
371 
372 /**
373  * as_review_set_id:
374  * @review: a #AsReview
375  * @id: review identifier, e.g. "deadbeef"
376  *
377  * Sets the review identifier that is unique to each review.
378  *
379  * Since: 0.6.1
380  **/
381 void
as_review_set_id(AsReview * review,const gchar * id)382 as_review_set_id (AsReview *review, const gchar *id)
383 {
384 	AsReviewPrivate *priv = GET_PRIVATE (review);
385 	g_return_if_fail (AS_IS_REVIEW (review));
386 	as_ref_string_assign_safe (&priv->id, id);
387 }
388 
389 /**
390  * as_review_set_summary:
391  * @review: a #AsReview
392  * @summary: a one-line summary, e.g. "Awesome application"
393  *
394  * Sets the one-line summary that may be displayed in bold.
395  *
396  * Since: 0.6.1
397  **/
398 void
as_review_set_summary(AsReview * review,const gchar * summary)399 as_review_set_summary (AsReview *review, const gchar *summary)
400 {
401 	AsReviewPrivate *priv = GET_PRIVATE (review);
402 	g_return_if_fail (AS_IS_REVIEW (review));
403 	as_ref_string_assign_safe (&priv->summary, summary);
404 }
405 
406 /**
407  * as_review_get_description:
408  * @review: a #AsReview
409  *
410  * Gets the multi-line review text that forms the body of the review.
411  *
412  * Returns: the string, or %NULL
413  *
414  * Since: 0.6.1
415  **/
416 const gchar *
as_review_get_description(AsReview * review)417 as_review_get_description (AsReview *review)
418 {
419 	AsReviewPrivate *priv = GET_PRIVATE (review);
420 	g_return_val_if_fail (AS_IS_REVIEW (review), NULL);
421 	return priv->description;
422 }
423 
424 /**
425  * as_review_set_description:
426  * @review: a #AsReview
427  * @description: multi-line description
428  *
429  * Sets the multi-line review text that forms the body of the review.
430  *
431  * Since: 0.6.1
432  **/
433 void
as_review_set_description(AsReview * review,const gchar * description)434 as_review_set_description (AsReview *review, const gchar *description)
435 {
436 	AsReviewPrivate *priv = GET_PRIVATE (review);
437 	g_return_if_fail (AS_IS_REVIEW (review));
438 	as_ref_string_assign_safe (&priv->description, description);
439 }
440 
441 /**
442  * as_review_get_locale:
443  * @review: a #AsReview
444  *
445  * Gets the locale for the review.
446  *
447  * Returns: the string, or %NULL
448  *
449  * Since: 0.6.1
450  **/
451 const gchar *
as_review_get_locale(AsReview * review)452 as_review_get_locale (AsReview *review)
453 {
454 	AsReviewPrivate *priv = GET_PRIVATE (review);
455 	g_return_val_if_fail (AS_IS_REVIEW (review), NULL);
456 	return priv->locale;
457 }
458 
459 /**
460  * as_review_set_locale:
461  * @review: a #AsReview
462  * @locale: locale, e.g. "en_GB"
463  *
464  * Sets the locale for the review.
465  *
466  * Since: 0.6.1
467  **/
468 void
as_review_set_locale(AsReview * review,const gchar * locale)469 as_review_set_locale (AsReview *review, const gchar *locale)
470 {
471 	AsReviewPrivate *priv = GET_PRIVATE (review);
472 	g_return_if_fail (AS_IS_REVIEW (review));
473 	as_ref_string_assign_safe (&priv->locale, locale);
474 }
475 
476 /**
477  * as_review_get_rating:
478  * @review: a #AsReview
479  *
480  * Gets the star rating of the review, where 100 is 5 stars.
481  *
482  * Returns: integer as a percentage, or 0 for unset
483  *
484  * Since: 0.6.1
485  **/
486 gint
as_review_get_rating(AsReview * review)487 as_review_get_rating (AsReview *review)
488 {
489 	AsReviewPrivate *priv = GET_PRIVATE (review);
490 	g_return_val_if_fail (AS_IS_REVIEW (review), 0);
491 	return priv->rating;
492 }
493 
494 /**
495  * as_review_set_rating:
496  * @review: a #AsReview
497  * @rating: a integer as a percentage, or 0 for unset
498  *
499  * Sets the star rating of the review, where 100 is 5 stars..
500  *
501  * Since: 0.6.1
502  **/
503 void
as_review_set_rating(AsReview * review,gint rating)504 as_review_set_rating (AsReview *review, gint rating)
505 {
506 	AsReviewPrivate *priv = GET_PRIVATE (review);
507 	g_return_if_fail (AS_IS_REVIEW (review));
508 	priv->rating = rating;
509 }
510 
511 /**
512  * as_review_get_flags:
513  * @review: a #AsReview
514  *
515  * Gets any flags set on the review, for example if the user has already
516  * voted on the review or if the user wrote the review themselves.
517  *
518  * Returns: a #AsReviewFlags, e.g. %AS_REVIEW_FLAG_SELF
519  *
520  * Since: 0.6.1
521  **/
522 AsReviewFlags
as_review_get_flags(AsReview * review)523 as_review_get_flags (AsReview *review)
524 {
525 	AsReviewPrivate *priv = GET_PRIVATE (review);
526 	g_return_val_if_fail (AS_IS_REVIEW (review), AS_REVIEW_FLAG_NONE);
527 	return priv->flags;
528 }
529 
530 /**
531  * as_review_set_flags:
532  * @review: a #AsReview
533  * @flags: a #AsReviewFlags, e.g. %AS_REVIEW_FLAG_SELF
534  *
535  * Gets any flags set on the review, for example if the user has already
536  * voted on the review or if the user wrote the review themselves.
537  *
538  * Since: 0.6.1
539  **/
540 void
as_review_set_flags(AsReview * review,AsReviewFlags flags)541 as_review_set_flags (AsReview *review, AsReviewFlags flags)
542 {
543 	AsReviewPrivate *priv = GET_PRIVATE (review);
544 	g_return_if_fail (AS_IS_REVIEW (review));
545 	priv->flags = flags;
546 }
547 
548 /**
549  * as_review_add_flags:
550  * @review: a #AsReview
551  * @flags: a #AsReviewFlags, e.g. %AS_REVIEW_FLAG_SELF
552  *
553  * Adds flags to an existing review without replacing the other flags.
554  *
555  * Since: 0.6.1
556  **/
557 void
as_review_add_flags(AsReview * review,AsReviewFlags flags)558 as_review_add_flags (AsReview *review, AsReviewFlags flags)
559 {
560 	AsReviewPrivate *priv = GET_PRIVATE (review);
561 	g_return_if_fail (AS_IS_REVIEW (review));
562 	priv->flags |= flags;
563 }
564 
565 /**
566  * as_review_get_reviewer_id:
567  * @review: a #AsReview
568  *
569  * Gets the name of the reviewer.
570  *
571  * Returns: the reviewer ID, e.g. "deadbeef", or %NULL
572  *
573  * Since: 0.6.1
574  **/
575 const gchar *
as_review_get_reviewer_id(AsReview * review)576 as_review_get_reviewer_id (AsReview *review)
577 {
578 	AsReviewPrivate *priv = GET_PRIVATE (review);
579 	g_return_val_if_fail (AS_IS_REVIEW (review), NULL);
580 	return priv->reviewer_id;
581 }
582 
583 /**
584  * as_review_get_reviewer_name:
585  * @review: a #AsReview
586  *
587  * Gets the name of the reviewer.
588  *
589  * Returns: the reviewer name, e.g. "David Smith", or %NULL
590  *
591  * Since: 0.6.1
592  **/
593 const gchar *
as_review_get_reviewer_name(AsReview * review)594 as_review_get_reviewer_name (AsReview *review)
595 {
596 	AsReviewPrivate *priv = GET_PRIVATE (review);
597 	g_return_val_if_fail (AS_IS_REVIEW (review), NULL);
598 	return priv->reviewer_name;
599 }
600 
601 /**
602  * as_review_set_reviewer_id:
603  * @review: a #AsReview
604  * @reviewer_id: the reviewer ID, e.g. "deadbeef"
605  *
606  * Sets the name of the reviewer, which can be left unset.
607  *
608  * Since: 0.6.1
609  **/
610 void
as_review_set_reviewer_id(AsReview * review,const gchar * reviewer_id)611 as_review_set_reviewer_id (AsReview *review, const gchar *reviewer_id)
612 {
613 	AsReviewPrivate *priv = GET_PRIVATE (review);
614 	g_return_if_fail (AS_IS_REVIEW (review));
615 	as_ref_string_assign_safe (&priv->reviewer_id, reviewer_id);
616 }
617 
618 /**
619  * as_review_set_reviewer_name:
620  * @review: a #AsReview
621  * @reviewer_name: the reviewer name, e.g. "David Smith"
622  *
623  * Sets the name of the reviewer, which can be left unset.
624  *
625  * Since: 0.6.1
626  **/
627 void
as_review_set_reviewer_name(AsReview * review,const gchar * reviewer_name)628 as_review_set_reviewer_name (AsReview *review, const gchar *reviewer_name)
629 {
630 	AsReviewPrivate *priv = GET_PRIVATE (review);
631 	g_return_if_fail (AS_IS_REVIEW (review));
632 	as_ref_string_assign_safe (&priv->reviewer_name, reviewer_name);
633 }
634 
635 /**
636  * as_review_set_version:
637  * @review: a #AsReview
638  * @version: a version string, e.g. "0.1.2"
639  *
640  * Sets the version string for the application being reviewed.
641  *
642  * Since: 0.6.1
643  **/
644 void
as_review_set_version(AsReview * review,const gchar * version)645 as_review_set_version (AsReview *review, const gchar *version)
646 {
647 	AsReviewPrivate *priv = GET_PRIVATE (review);
648 	g_return_if_fail (AS_IS_REVIEW (review));
649 	as_ref_string_assign_safe (&priv->version, version);
650 }
651 
652 /**
653  * as_review_get_version:
654  * @review: a #AsReview
655  *
656  * Gets the version string for the application being reviewed..
657  *
658  * Returns: the version string, e.g. "0.1.2", or %NULL for unset
659  *
660  * Since: 0.6.1
661  **/
662 const gchar *
as_review_get_version(AsReview * review)663 as_review_get_version (AsReview *review)
664 {
665 	AsReviewPrivate *priv = GET_PRIVATE (review);
666 	g_return_val_if_fail (AS_IS_REVIEW (review), NULL);
667 	return priv->version;
668 }
669 
670 /**
671  * as_review_get_date:
672  * @review: a #AsReview
673  *
674  * Gets the date the review was originally submitted.
675  *
676  * Returns: (transfer none): the #GDateTime, or %NULL for unset
677  *
678  * Since: 0.6.1
679  **/
680 GDateTime *
as_review_get_date(AsReview * review)681 as_review_get_date (AsReview *review)
682 {
683 	AsReviewPrivate *priv = GET_PRIVATE (review);
684 	g_return_val_if_fail (AS_IS_REVIEW (review), NULL);
685 	return priv->date;
686 }
687 
688 /**
689  * as_review_set_date:
690  * @review: a #AsReview
691  * @date: a #GDateTime, or %NULL
692  *
693  * Sets the date the review was originally submitted.
694  *
695  * Since: 0.6.1
696  **/
697 void
as_review_set_date(AsReview * review,GDateTime * date)698 as_review_set_date (AsReview *review, GDateTime *date)
699 {
700 	AsReviewPrivate *priv = GET_PRIVATE (review);
701 	g_return_if_fail (AS_IS_REVIEW (review));
702 	g_clear_pointer (&priv->date, g_date_time_unref);
703 	if (date != NULL)
704 		priv->date = g_date_time_ref (date);
705 }
706 
707 /**
708  * as_review_get_metadata_item:
709  * @review: a #AsReview
710  * @key: a string
711  *
712  * Gets some metadata from a review object.
713  * It is left for the the plugin to use this method as required, but a
714  * typical use would be to retrieve some secure authentication token.
715  *
716  * Returns: A string value, or %NULL for not found
717  *
718  * Since: 0.6.1
719  **/
720 const gchar *
as_review_get_metadata_item(AsReview * review,const gchar * key)721 as_review_get_metadata_item (AsReview *review, const gchar *key)
722 {
723 	AsReviewPrivate *priv = GET_PRIVATE (review);
724 	g_return_val_if_fail (AS_IS_REVIEW (review), NULL);
725 	g_return_val_if_fail (key != NULL, NULL);
726 	return g_hash_table_lookup (priv->metadata, key);
727 }
728 
729 /**
730  * as_review_add_metadata:
731  * @review: a #AsReview
732  * @key: a string
733  * @value: a string
734  *
735  * Adds metadata to the review object.
736  * It is left for the the plugin to use this method as required, but a
737  * typical use would be to store some secure authentication token.
738  *
739  * Since: 0.6.1
740  **/
741 void
as_review_add_metadata(AsReview * review,const gchar * key,const gchar * value)742 as_review_add_metadata (AsReview *review, const gchar *key, const gchar *value)
743 {
744 	AsReviewPrivate *priv = GET_PRIVATE (review);
745 	g_return_if_fail (AS_IS_REVIEW (review));
746 	g_hash_table_insert (priv->metadata,
747 			     as_ref_string_new (key),
748 			     as_ref_string_new (value));
749 }
750 
751 /**
752  * as_review_equal:
753  * @review1: a #AsReview instance.
754  * @review2: a #AsReview instance.
755  *
756  * Checks if two reviews are the same.
757  *
758  * Returns: %TRUE for success
759  *
760  * Since: 0.6.1
761  **/
762 gboolean
as_review_equal(AsReview * review1,AsReview * review2)763 as_review_equal (AsReview *review1, AsReview *review2)
764 {
765 	AsReviewPrivate *priv1 = GET_PRIVATE (review1);
766 	AsReviewPrivate *priv2 = GET_PRIVATE (review2);
767 
768 	/* trivial */
769 	if (review1 == review2)
770 		return TRUE;
771 
772 	/* check for equality */
773 	if (!g_date_time_equal (priv1->date, priv2->date))
774 		return FALSE;
775 	if (priv1->priority != priv2->priority)
776 		return FALSE;
777 	if (priv1->rating != priv2->rating)
778 		return FALSE;
779 	if (g_strcmp0 (priv1->id, priv2->id) != 0)
780 		return FALSE;
781 	if (g_strcmp0 (priv1->summary, priv2->summary) != 0)
782 		return FALSE;
783 	if (g_strcmp0 (priv1->description, priv2->description) != 0)
784 		return FALSE;
785 	if (g_strcmp0 (priv1->locale, priv2->locale) != 0)
786 		return FALSE;
787 	if (g_strcmp0 (priv1->version, priv2->version) != 0)
788 		return FALSE;
789 
790 	/* success */
791 	return TRUE;
792 }
793 
794 /**
795  * as_review_node_insert: (skip)
796  * @review: a #AsReview instance.
797  * @parent: the parent #GNode to use..
798  * @ctx: the #AsNodeContext
799  *
800  * Inserts the review into the DOM tree.
801  *
802  * Returns: (transfer none): A populated #GNode
803  *
804  * Since: 0.6.1
805  **/
806 GNode *
as_review_node_insert(AsReview * review,GNode * parent,AsNodeContext * ctx)807 as_review_node_insert (AsReview *review, GNode *parent, AsNodeContext *ctx)
808 {
809 	AsReviewPrivate *priv = GET_PRIVATE (review);
810 	GNode *n;
811 
812 	n = as_node_insert (parent, "review", NULL,
813 			    AS_NODE_INSERT_FLAG_NONE,
814 			    NULL);
815 	if (priv->id != NULL)
816 		as_node_add_attribute (n, "id", priv->id);
817 	if (priv->priority != 0) {
818 		g_autofree gchar *str = g_strdup_printf ("%i", priv->priority);
819 		as_node_insert (n, "priority", str,
820 				AS_NODE_INSERT_FLAG_NONE,
821 				NULL);
822 	}
823 	if (priv->rating != 0) {
824 		g_autofree gchar *str = g_strdup_printf ("%i", priv->rating);
825 		as_node_add_attribute (n, "rating", str);
826 	}
827 	if (priv->date != NULL) {
828 		g_autofree gchar *str = g_date_time_format (priv->date, "%F");
829 		as_node_add_attribute (n, "date", str);
830 	}
831 	if (priv->summary != NULL) {
832 		as_node_insert (n, "summary", priv->summary,
833 				AS_NODE_INSERT_FLAG_NONE,
834 				NULL);
835 	}
836 	if (priv->description != NULL) {
837 		as_node_insert (n, "description", priv->description,
838 				AS_NODE_INSERT_FLAG_PRE_ESCAPED,
839 				NULL);
840 	}
841 	if (priv->version != NULL) {
842 		as_node_insert (n, "version", priv->version,
843 				AS_NODE_INSERT_FLAG_NONE,
844 				NULL);
845 	}
846 	if (priv->reviewer_id != NULL) {
847 		as_node_insert (n, "reviewer_id", priv->reviewer_id,
848 				AS_NODE_INSERT_FLAG_NONE,
849 				NULL);
850 	}
851 	if (priv->reviewer_name != NULL) {
852 		as_node_insert (n, "reviewer_name", priv->reviewer_name,
853 				AS_NODE_INSERT_FLAG_NONE,
854 				NULL);
855 	}
856 	if (priv->locale != NULL) {
857 		as_node_insert (n, "lang", priv->locale,
858 				AS_NODE_INSERT_FLAG_NONE,
859 				NULL);
860 	}
861 
862 	/* <metadata> */
863 	if (g_hash_table_size (priv->metadata) > 0) {
864 		AsNode *node_tmp;
865 		node_tmp = as_node_insert (n, "metadata", NULL, 0, NULL);
866 		as_node_insert_hash (node_tmp, "value", "key", priv->metadata, FALSE);
867 	}
868 
869 	return n;
870 }
871 
872 /**
873  * as_review_node_parse:
874  * @review: a #AsReview instance.
875  * @node: a #GNode.
876  * @ctx: a #AsNodeContext.
877  * @error: A #GError or %NULL.
878  *
879  * Populates the object from a DOM node.
880  *
881  * Returns: %TRUE for success
882  *
883  * Since: 0.6.1
884  **/
885 gboolean
as_review_node_parse(AsReview * review,GNode * node,AsNodeContext * ctx,GError ** error)886 as_review_node_parse (AsReview *review, GNode *node,
887 		     AsNodeContext *ctx, GError **error)
888 {
889 	AsReviewPrivate *priv = GET_PRIVATE (review);
890 	AsNode *c;
891 	const gchar *tmp;
892 	gint itmp;
893 
894 	g_return_val_if_fail (AS_IS_REVIEW (review), FALSE);
895 
896 	itmp = as_node_get_attribute_as_int (node, "rating");
897 	if (itmp != G_MAXINT)
898 		as_review_set_rating (review, itmp);
899 	tmp = as_node_get_attribute (node, "date");
900 	if (tmp != NULL) {
901 		g_autoptr(GDateTime) dt = as_utils_iso8601_to_datetime (tmp);
902 		if (dt != NULL)
903 			as_review_set_date (review, dt);
904 	}
905 	tmp = as_node_get_attribute (node, "id");
906 	if (tmp != NULL)
907 		as_review_set_id (review, tmp);
908 	for (c = node->children; c != NULL; c = c->next) {
909 		if (as_node_get_tag (c) == AS_TAG_SUMMARY) {
910 			as_review_set_summary (review, as_node_get_data (c));
911 			continue;
912 		}
913 		if (as_node_get_tag (c) == AS_TAG_PRIORITY) {
914 			gint64 prio = g_ascii_strtoll (as_node_get_data (c),
915 						       NULL, 10);
916 			as_review_set_priority (review, (gint) prio);
917 			continue;
918 		}
919 		if (as_node_get_tag (c) == AS_TAG_DESCRIPTION) {
920 			g_autoptr(GString) xml = NULL;
921 			xml = as_node_to_xml (c->children, AS_NODE_TO_XML_FLAG_INCLUDE_SIBLINGS);
922 			as_review_set_description (review, xml->str);
923 			continue;
924 		}
925 		if (as_node_get_tag (c) == AS_TAG_VERSION) {
926 			as_review_set_version (review, as_node_get_data (c));
927 			continue;
928 		}
929 		if (as_node_get_tag (c) == AS_TAG_REVIEWER_ID) {
930 			as_review_set_reviewer_id (review, as_node_get_data (c));
931 			continue;
932 		}
933 		if (as_node_get_tag (c) == AS_TAG_REVIEWER_NAME) {
934 			as_review_set_reviewer_name (review, as_node_get_data (c));
935 			continue;
936 		}
937 		if (as_node_get_tag (c) == AS_TAG_LANG) {
938 			as_review_set_locale (review, as_node_get_data (c));
939 			continue;
940 		}
941 		if (as_node_get_tag (c) == AS_TAG_METADATA) {
942 			AsNode *c2;
943 			for (c2 = c->children; c2 != NULL; c2 = c2->next) {
944 				AsRefString *key;
945 				AsRefString *value;
946 				if (as_node_get_tag (c2) != AS_TAG_VALUE)
947 					continue;
948 				key = as_node_get_attribute_as_refstr (c2, "key");
949 				value = as_node_get_data_as_refstr (c2);
950 				if (value == NULL) {
951 					g_hash_table_insert (priv->metadata,
952 							     as_ref_string_ref (key),
953 							     as_ref_string_new_static (""));
954 				} else {
955 					g_hash_table_insert (priv->metadata,
956 							     as_ref_string_ref (key),
957 							     as_ref_string_ref (value));
958 				}
959 			}
960 			continue;
961 		}
962 	}
963 	return TRUE;
964 }
965 
966 /**
967  * as_review_node_parse_dep11:
968  * @review: a #AsReview instance.
969  * @node: a #GNode.
970  * @ctx: a #AsNodeContext.
971  * @error: A #GError or %NULL.
972  *
973  * Populates the object from a DEP-11 node.
974  *
975  * Returns: %TRUE for success
976  *
977  * Since: 0.6.1
978  **/
979 gboolean
as_review_node_parse_dep11(AsReview * im,GNode * node,AsNodeContext * ctx,GError ** error)980 as_review_node_parse_dep11 (AsReview *im, GNode *node,
981 			   AsNodeContext *ctx, GError **error)
982 {
983 	return TRUE;
984 }
985 
986 /**
987  * as_review_new:
988  *
989  * Creates a new #AsReview.
990  *
991  * Returns: (transfer full): a #AsReview
992  *
993  * Since: 0.6.1
994  **/
995 AsReview *
as_review_new(void)996 as_review_new (void)
997 {
998 	AsReview *review;
999 	review = g_object_new (AS_TYPE_REVIEW, NULL);
1000 	return AS_REVIEW (review);
1001 }
1002