Moved chart loading into ag_chart_load_from_file(), where it was intended to be
This commit is contained in:
		
							
								
								
									
										179
									
								
								src/ag-app.c
									
									
									
									
									
								
							
							
						
						
									
										179
									
								
								src/ag-app.c
									
									
									
									
									
								
							| @@ -1,20 +1,10 @@ | |||||||
| #include <glib/gi18n.h> | #include <glib/gi18n.h> | ||||||
| #include <libxml/parser.h> |  | ||||||
| #include <libxml/xpath.h> |  | ||||||
| #include <errno.h> |  | ||||||
|  |  | ||||||
| #include "ag-app.h" | #include "ag-app.h" | ||||||
| #include "ag-window.h" | #include "ag-window.h" | ||||||
| #include "ag-chart.h" | #include "ag-chart.h" | ||||||
| #include "config.h" | #include "config.h" | ||||||
| #include "astrognome.h" | #include "astrognome.h" | ||||||
|  |  | ||||||
| typedef enum { |  | ||||||
|     XML_CONVERT_STRING, |  | ||||||
|     XML_CONVERT_DOUBLE, |  | ||||||
|     XML_CONVERT_INT |  | ||||||
| } XmlConvertType; |  | ||||||
|  |  | ||||||
| struct _AgAppPrivate { | struct _AgAppPrivate { | ||||||
| }; | }; | ||||||
|  |  | ||||||
| @@ -114,182 +104,17 @@ quit_cb(GSimpleAction *action, GVariant *parameter, gpointer user_data) | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| GVariant * |  | ||||||
| get_by_xpath(xmlXPathContextPtr ctx, const gchar *xpath, XmlConvertType type) |  | ||||||
| { |  | ||||||
|     xmlXPathObjectPtr xpathObj; |  | ||||||
|     const gchar *text; |  | ||||||
|     char *endptr; |  | ||||||
|     GVariant *ret = NULL; |  | ||||||
|     gdouble d; |  | ||||||
|     gint i; |  | ||||||
|  |  | ||||||
|     if ((xpathObj = xmlXPathEvalExpression((const xmlChar *)xpath, ctx)) == NULL) { |  | ||||||
|         // TODO: Warn with a popup or similar way |  | ||||||
|         g_warning("Could not initialize XPath"); |  | ||||||
|  |  | ||||||
|         return NULL; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (xpathObj->nodesetval == NULL) { |  | ||||||
|         // TODO: Warn with a popup or similar way |  | ||||||
|         g_warning("Required element not found. This is not a valid save file!"); |  | ||||||
|         xmlXPathFreeObject(xpathObj); |  | ||||||
|  |  | ||||||
|         return NULL; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (xpathObj->nodesetval->nodeNr > 1) { |  | ||||||
|         // TODO: Warn with a popup or similar way |  | ||||||
|         g_warning("Too many elements. This is not a valid save file!"); |  | ||||||
|         xmlXPathFreeObject(xpathObj); |  | ||||||
|  |  | ||||||
|         return NULL; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     text = (const gchar *)xpathObj->nodesetval->nodeTab[0]->content; |  | ||||||
|  |  | ||||||
|     switch (type) { |  | ||||||
|         case XML_CONVERT_STRING: |  | ||||||
|             ret = g_variant_new_string(text); |  | ||||||
|  |  | ||||||
|             break; |  | ||||||
|  |  | ||||||
|         case XML_CONVERT_DOUBLE: |  | ||||||
|             d = g_ascii_strtod(text, &endptr); |  | ||||||
|  |  | ||||||
|             if ((*endptr != 0) || (errno != 0)) { |  | ||||||
|                 ret = NULL; |  | ||||||
|             } else { |  | ||||||
|                 ret = g_variant_new_double(d); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             break; |  | ||||||
|  |  | ||||||
|         case XML_CONVERT_INT: |  | ||||||
|             i = strtol(text, &endptr, 10); |  | ||||||
|  |  | ||||||
|             if ((*endptr != 0) || (errno != 0)) { |  | ||||||
|                 ret = NULL; |  | ||||||
|             } else { |  | ||||||
|                 ret = g_variant_new_int32(i); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             break; |  | ||||||
|  |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     xmlXPathFreeObject(xpathObj); |  | ||||||
|  |  | ||||||
|     return ret; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void | static void | ||||||
| ag_app_open_chart(AgApp *app, GFile *file) | ag_app_open_chart(AgApp *app, GFile *file) | ||||||
| { | { | ||||||
|     GError *err = NULL; |  | ||||||
|     gchar *uri, |  | ||||||
|           *xml; |  | ||||||
|     guint length; |  | ||||||
|     xmlDocPtr doc; |  | ||||||
|     xmlXPathContextPtr xpathCtx; |  | ||||||
|     GVariant *chart_name, |  | ||||||
|              *country, |  | ||||||
|              *city, |  | ||||||
|              *longitude, |  | ||||||
|              *latitude, |  | ||||||
|              *altitude, |  | ||||||
|              *year, |  | ||||||
|              *month, |  | ||||||
|              *day, |  | ||||||
|              *hour, |  | ||||||
|              *minute, |  | ||||||
|              *second, |  | ||||||
|              *timezone; |  | ||||||
|     GtkWidget *window; |     GtkWidget *window; | ||||||
|     AgChart *chart; |     AgChart *chart; | ||||||
|     GsweTimestamp *timestamp; |     GError *err = NULL; | ||||||
|  |  | ||||||
|     uri = g_file_get_uri(file); |  | ||||||
|  |  | ||||||
|     if (!g_file_load_contents(file, NULL, &xml, &length, NULL, &err)) { |  | ||||||
|         // TODO: Warn with a popup or similar way |  | ||||||
|         g_warning("Could not open file '%s': %s", uri, err->message); |  | ||||||
|         g_clear_error(&err); |  | ||||||
|         g_free(uri); |  | ||||||
|  |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if ((doc = xmlReadMemory(xml, length, "chart.xml", NULL, 0)) == NULL) { |  | ||||||
|         // TODO: Warn with a popup or similar way |  | ||||||
|         g_warning("Saved chart is corrupt (or not a saved chart at all)"); |  | ||||||
|         g_free(xml); |  | ||||||
|         g_free(uri); |  | ||||||
|  |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if ((xpathCtx = xmlXPathNewContext(doc)) == NULL) { |  | ||||||
|         // TODO: Warn with a popup or similar way |  | ||||||
|         g_warning("Could not initialize XPath"); |  | ||||||
|         xmlFreeDoc(doc); |  | ||||||
|         g_free(xml); |  | ||||||
|         g_free(uri); |  | ||||||
|  |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     chart_name = get_by_xpath(xpathCtx, "/chartinfo/data/name/text()", XML_CONVERT_STRING); |  | ||||||
|     country = get_by_xpath(xpathCtx, "/chartinfo/data/place/country/text()", XML_CONVERT_STRING); |  | ||||||
|     city = get_by_xpath(xpathCtx, "/chartinfo/data/place/city/text()", XML_CONVERT_STRING); |  | ||||||
|     longitude = get_by_xpath(xpathCtx, "/chartinfo/data/place/longitude/text()", XML_CONVERT_DOUBLE); |  | ||||||
|     latitude = get_by_xpath(xpathCtx, "/chartinfo/data/place/latitude/text()", XML_CONVERT_DOUBLE); |  | ||||||
|     altitude = get_by_xpath(xpathCtx, "/chartinfo/data/place/altitude/text()", XML_CONVERT_DOUBLE); |  | ||||||
|     year = get_by_xpath(xpathCtx, "/chartinfo/data/time/year/text()", XML_CONVERT_INT); |  | ||||||
|     month = get_by_xpath(xpathCtx, "/chartinfo/data/time/month/text()", XML_CONVERT_INT); |  | ||||||
|     day = get_by_xpath(xpathCtx, "/chartinfo/data/time/day/text()", XML_CONVERT_INT); |  | ||||||
|     hour = get_by_xpath(xpathCtx, "/chartinfo/data/time/hour/text()", XML_CONVERT_INT); |  | ||||||
|     minute = get_by_xpath(xpathCtx, "/chartinfo/data/time/minute/text()", XML_CONVERT_INT); |  | ||||||
|     second = get_by_xpath(xpathCtx, "/chartinfo/data/time/second/text()", XML_CONVERT_INT); |  | ||||||
|     timezone = get_by_xpath(xpathCtx, "/chartinfo/data/time/timezone/text()", XML_CONVERT_DOUBLE); |  | ||||||
|  |  | ||||||
|  |     chart = ag_chart_load_from_file(file, &err); | ||||||
|     window = ag_app_create_window(app); |     window = ag_app_create_window(app); | ||||||
|     timestamp = gswe_timestamp_new_from_gregorian_full( |  | ||||||
|             g_variant_get_int32(year), |  | ||||||
|             g_variant_get_int32(month), |  | ||||||
|             g_variant_get_int32(day), |  | ||||||
|             g_variant_get_int32(hour), |  | ||||||
|             g_variant_get_int32(minute), |  | ||||||
|             g_variant_get_int32(second), |  | ||||||
|             0, |  | ||||||
|             g_variant_get_double(timezone) |  | ||||||
|         ); |  | ||||||
|     // TODO: Make house system configurable (and saveable) |  | ||||||
|     chart = ag_chart_new_full(timestamp, g_variant_get_double(longitude), g_variant_get_double(latitude), g_variant_get_double(altitude), GSWE_HOUSE_SYSTEM_PLACIDUS); |  | ||||||
|     ag_chart_set_name(chart, g_variant_get_string(chart_name, NULL)); |  | ||||||
|     ag_chart_set_country(chart, g_variant_get_string(country, NULL)); |  | ||||||
|     ag_chart_set_city(chart, g_variant_get_string(city, NULL)); |  | ||||||
|     ag_window_set_chart(AG_WINDOW(window), chart); |     ag_window_set_chart(AG_WINDOW(window), chart); | ||||||
|     ag_window_update_from_chart(AG_WINDOW(window)); |     ag_window_update_from_chart(AG_WINDOW(window)); | ||||||
|  |  | ||||||
|     g_variant_unref(chart_name); |  | ||||||
|     g_variant_unref(country); |  | ||||||
|     g_variant_unref(city); |  | ||||||
|     g_variant_unref(longitude); |  | ||||||
|     g_variant_unref(latitude); |  | ||||||
|     g_variant_unref(altitude); |  | ||||||
|     g_variant_unref(year); |  | ||||||
|     g_variant_unref(month); |  | ||||||
|     g_variant_unref(day); |  | ||||||
|     g_variant_unref(hour); |  | ||||||
|     g_variant_unref(minute); |  | ||||||
|     g_variant_unref(second); |  | ||||||
|  |  | ||||||
|     g_free(xml); |  | ||||||
|     g_free(uri); |  | ||||||
|     xmlXPathFreeContext(xpathCtx); |  | ||||||
|     xmlFreeDoc(doc); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
|   | |||||||
							
								
								
									
										183
									
								
								src/ag-chart.c
									
									
									
									
									
								
							
							
						
						
									
										183
									
								
								src/ag-chart.c
									
									
									
									
									
								
							| @@ -1,3 +1,7 @@ | |||||||
|  | #include <errno.h> | ||||||
|  | #include <gio/gio.h> | ||||||
|  | #include <libxml/parser.h> | ||||||
|  | #include <libxml/xpath.h> | ||||||
| #include <swe-glib.h> | #include <swe-glib.h> | ||||||
|  |  | ||||||
| #include "ag-chart.h" | #include "ag-chart.h" | ||||||
| @@ -16,6 +20,12 @@ enum { | |||||||
|     PROP_CITY |     PROP_CITY | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | typedef enum { | ||||||
|  |     XML_CONVERT_STRING, | ||||||
|  |     XML_CONVERT_DOUBLE, | ||||||
|  |     XML_CONVERT_INT | ||||||
|  | } XmlConvertType; | ||||||
|  |  | ||||||
| G_DEFINE_QUARK(ag-chart-error-quark, ag_chart_error); | G_DEFINE_QUARK(ag-chart-error-quark, ag_chart_error); | ||||||
|  |  | ||||||
| G_DEFINE_TYPE(AgChart, ag_chart, GSWE_TYPE_MOMENT); | G_DEFINE_TYPE(AgChart, ag_chart, GSWE_TYPE_MOMENT); | ||||||
| @@ -188,3 +198,176 @@ ag_chart_get_city(AgChart *chart) | |||||||
|     return g_strdup(chart->priv->city); |     return g_strdup(chart->priv->city); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static GVariant * | ||||||
|  | get_by_xpath(xmlXPathContextPtr xpath_context, const gchar *uri, const gchar *xpath, XmlConvertType type, GError **err) | ||||||
|  | { | ||||||
|  |     xmlXPathObjectPtr xpathObj; | ||||||
|  |     const gchar *text; | ||||||
|  |     char *endptr; | ||||||
|  |     GVariant *ret = NULL; | ||||||
|  |     gdouble d; | ||||||
|  |     gint i; | ||||||
|  |  | ||||||
|  |     if ((xpathObj = xmlXPathEvalExpression((const xmlChar *)xpath, xpath_context)) == NULL) { | ||||||
|  |         g_set_error(err, AG_CHART_ERROR, AG_CHART_ERROR_LIBXML, "File '%s' could not be parsed due to internal XML error.", uri); | ||||||
|  |  | ||||||
|  |         return NULL; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (xpathObj->nodesetval == NULL) { | ||||||
|  |         g_set_error(err, AG_CHART_ERROR, AG_CHART_ERROR_CORRUPT_FILE, "File '%s' doesn't look like a valid saved chart.", uri); | ||||||
|  |         xmlXPathFreeObject(xpathObj); | ||||||
|  |  | ||||||
|  |         return NULL; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (xpathObj->nodesetval->nodeNr > 1) { | ||||||
|  |         g_set_error(err, AG_CHART_ERROR, AG_CHART_ERROR_CORRUPT_FILE, "File '%s' doesn't look like a valid saved chart.", uri); | ||||||
|  |         xmlXPathFreeObject(xpathObj); | ||||||
|  |  | ||||||
|  |         return NULL; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     text = (const gchar *)xpathObj->nodesetval->nodeTab[0]->content; | ||||||
|  |  | ||||||
|  |     switch (type) { | ||||||
|  |         case XML_CONVERT_STRING: | ||||||
|  |             ret = g_variant_new_string(text); | ||||||
|  |  | ||||||
|  |             break; | ||||||
|  |  | ||||||
|  |         case XML_CONVERT_DOUBLE: | ||||||
|  |             d = g_ascii_strtod(text, &endptr); | ||||||
|  |  | ||||||
|  |             if ((*endptr != 0) || (errno != 0)) { | ||||||
|  |                 g_set_error(err, AG_CHART_ERROR, AG_CHART_ERROR_CORRUPT_FILE, "File '%s' doesn't look like a valid saved chart.", uri); | ||||||
|  |                 ret = NULL; | ||||||
|  |             } else { | ||||||
|  |                 ret = g_variant_new_double(d); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             break; | ||||||
|  |  | ||||||
|  |         case XML_CONVERT_INT: | ||||||
|  |             i = strtol(text, &endptr, 10); | ||||||
|  |  | ||||||
|  |             if ((*endptr != 0) || (errno != 0)) { | ||||||
|  |                 g_set_error(err, AG_CHART_ERROR, AG_CHART_ERROR_CORRUPT_FILE, "File '%s' doesn't look like a valid saved chart.", uri); | ||||||
|  |                 ret = NULL; | ||||||
|  |             } else { | ||||||
|  |                 ret = g_variant_new_int32(i); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             break; | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     xmlXPathFreeObject(xpathObj); | ||||||
|  |  | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | AgChart * | ||||||
|  | ag_chart_load_from_file(GFile *file, GError **err) | ||||||
|  | { | ||||||
|  |     AgChart *chart = NULL; | ||||||
|  |     gchar *uri, | ||||||
|  |           *xml = NULL; | ||||||
|  |     guint length; | ||||||
|  |     xmlDocPtr doc; | ||||||
|  |     xmlXPathContextPtr xpath_context; | ||||||
|  |     GVariant *chart_name, | ||||||
|  |              *country, | ||||||
|  |              *city, | ||||||
|  |              *longitude, | ||||||
|  |              *latitude, | ||||||
|  |              *altitude, | ||||||
|  |              *year, | ||||||
|  |              *month, | ||||||
|  |              *day, | ||||||
|  |              *hour, | ||||||
|  |              *minute, | ||||||
|  |              *second, | ||||||
|  |              *timezone; | ||||||
|  |     GsweTimestamp *timestamp; | ||||||
|  |  | ||||||
|  |     uri = g_file_get_uri(file); | ||||||
|  |  | ||||||
|  |     if (!g_file_load_contents(file, NULL, &xml, &length, NULL, err)) { | ||||||
|  |         g_free(uri); | ||||||
|  |  | ||||||
|  |         return NULL; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if ((doc = xmlReadMemory(xml, length, "chart.xml", NULL, 0)) == NULL) { | ||||||
|  |         g_set_error(err, AG_CHART_ERROR, AG_CHART_ERROR_CORRUPT_FILE, "File '%s' can not be read. Maybe it is corrupt, or not a save file at all", uri); | ||||||
|  |         g_free(xml); | ||||||
|  |         g_free(uri); | ||||||
|  |  | ||||||
|  |         return NULL; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if ((xpath_context = xmlXPathNewContext(doc)) == NULL) { | ||||||
|  |         g_set_error(err, AG_CHART_ERROR, AG_CHART_ERROR_LIBXML, "File '%s' could not be loaded due to internal LibXML error", uri); | ||||||
|  |         xmlFreeDoc(doc); | ||||||
|  |         g_free(xml); | ||||||
|  |         g_free(uri); | ||||||
|  |  | ||||||
|  |         return NULL; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     chart_name = get_by_xpath(xpath_context, uri, "/chartinfo/data/name/text()", XML_CONVERT_STRING, err); | ||||||
|  |     country = get_by_xpath(xpath_context, uri, "/chartinfo/data/place/country/text()", XML_CONVERT_STRING, err); | ||||||
|  |     city = get_by_xpath(xpath_context, uri, "/chartinfo/data/place/city/text()", XML_CONVERT_STRING, err); | ||||||
|  |     longitude = get_by_xpath(xpath_context, uri, "/chartinfo/data/place/longitude/text()", XML_CONVERT_DOUBLE, err); | ||||||
|  |     latitude = get_by_xpath(xpath_context, uri, "/chartinfo/data/place/latitude/text()", XML_CONVERT_DOUBLE, err); | ||||||
|  |     altitude = get_by_xpath(xpath_context, uri, "/chartinfo/data/place/altitude/text()", XML_CONVERT_DOUBLE, err); | ||||||
|  |     year = get_by_xpath(xpath_context, uri, "/chartinfo/data/time/year/text()", XML_CONVERT_INT, err); | ||||||
|  |     month = get_by_xpath(xpath_context, uri, "/chartinfo/data/time/month/text()", XML_CONVERT_INT, err); | ||||||
|  |     day = get_by_xpath(xpath_context, uri, "/chartinfo/data/time/day/text()", XML_CONVERT_INT, err); | ||||||
|  |     hour = get_by_xpath(xpath_context, uri, "/chartinfo/data/time/hour/text()", XML_CONVERT_INT, err); | ||||||
|  |     minute = get_by_xpath(xpath_context, uri, "/chartinfo/data/time/minute/text()", XML_CONVERT_INT, err); | ||||||
|  |     second = get_by_xpath(xpath_context, uri, "/chartinfo/data/time/second/text()", XML_CONVERT_INT, err); | ||||||
|  |     timezone = get_by_xpath(xpath_context, uri, "/chartinfo/data/time/timezone/text()", XML_CONVERT_DOUBLE, err); | ||||||
|  |  | ||||||
|  |     timestamp = gswe_timestamp_new_from_gregorian_full( | ||||||
|  |             g_variant_get_int32(year), | ||||||
|  |             g_variant_get_int32(month), | ||||||
|  |             g_variant_get_int32(day), | ||||||
|  |             g_variant_get_int32(hour), | ||||||
|  |             g_variant_get_int32(minute), | ||||||
|  |             g_variant_get_int32(second), | ||||||
|  |             0, | ||||||
|  |             g_variant_get_double(timezone) | ||||||
|  |         ); | ||||||
|  |     g_variant_unref(year); | ||||||
|  |     g_variant_unref(month); | ||||||
|  |     g_variant_unref(day); | ||||||
|  |     g_variant_unref(hour); | ||||||
|  |     g_variant_unref(minute); | ||||||
|  |     g_variant_unref(second); | ||||||
|  |     g_variant_unref(timezone); | ||||||
|  |  | ||||||
|  |     // TODO: Make house system configurable (and saveable) | ||||||
|  |     chart = ag_chart_new_full(timestamp, g_variant_get_double(longitude), g_variant_get_double(latitude), g_variant_get_double(altitude), GSWE_HOUSE_SYSTEM_PLACIDUS); | ||||||
|  |     g_variant_unref(longitude); | ||||||
|  |     g_variant_unref(latitude); | ||||||
|  |     g_variant_unref(altitude); | ||||||
|  |  | ||||||
|  |     ag_chart_set_name(chart, g_variant_get_string(chart_name, NULL)); | ||||||
|  |     g_variant_unref(chart_name); | ||||||
|  |  | ||||||
|  |     ag_chart_set_country(chart, g_variant_get_string(country, NULL)); | ||||||
|  |     g_variant_unref(country); | ||||||
|  |  | ||||||
|  |     ag_chart_set_city(chart, g_variant_get_string(city, NULL)); | ||||||
|  |     g_variant_unref(city); | ||||||
|  |  | ||||||
|  |     g_free(xml); | ||||||
|  |     g_free(uri); | ||||||
|  |     xmlXPathFreeContext(xpath_context); | ||||||
|  |     xmlFreeDoc(doc); | ||||||
|  |  | ||||||
|  |     return chart; | ||||||
|  | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -33,8 +33,8 @@ struct _AgChartClass { | |||||||
|  |  | ||||||
| GType ag_chart_get_type(void) G_GNUC_CONST; | GType ag_chart_get_type(void) G_GNUC_CONST; | ||||||
| AgChart *ag_chart_new_full(GsweTimestamp *timestamp, gdouble longitude, gdouble latitude, gdouble altitude, GsweHouseSystem house_system); | AgChart *ag_chart_new_full(GsweTimestamp *timestamp, gdouble longitude, gdouble latitude, gdouble altitude, GsweHouseSystem house_system); | ||||||
|  | AgChart *ag_chart_load_from_file(GFile *file, GError **err); | ||||||
|  |  | ||||||
| void ag_chart_load_from_file(const gchar *path, GError **err); |  | ||||||
| void ag_chart_save_to_file(const gchar *path, GError **err); | void ag_chart_save_to_file(const gchar *path, GError **err); | ||||||
|  |  | ||||||
| void ag_chart_set_name(AgChart *chart, const gchar *name); | void ag_chart_set_name(AgChart *chart, const gchar *name); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user