384 lines
12 KiB
C
384 lines
12 KiB
C
/*
|
|
* This program 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 of the License, or (at your option) version 3.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with the program; if not, see <http://www.gnu.org/licenses/>
|
|
*
|
|
*
|
|
* Authors:
|
|
* Philip Van Hoof <pvanhoof@gnome.org>
|
|
*
|
|
* Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
|
|
*
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include <string.h>
|
|
#include <glib/gi18n.h>
|
|
|
|
#include <libxml/xmlmemory.h>
|
|
#include <libxml/parser.h>
|
|
#include <libxml/tree.h>
|
|
#include <libxml/xmlIO.h>
|
|
#include <libxml/xpath.h>
|
|
|
|
#include "format-handler.h"
|
|
|
|
static void add_string_to_rdf (xmlNodePtr node,
|
|
const gchar *tag,
|
|
const gchar *value);
|
|
|
|
/* Use { */
|
|
|
|
/* #include <calendar/gui/calendar-config-keys.h> */
|
|
/* #include <calendar/gui/calendar-config.h> */
|
|
|
|
/* } or { */
|
|
#define CALENDAR_CONFIG_PREFIX "/apps/evolution/calendar"
|
|
#define CALENDAR_CONFIG_TIMEZONE CALENDAR_CONFIG_PREFIX "/display/timezone"
|
|
|
|
static gchar *
|
|
calendar_config_get_timezone (void)
|
|
{
|
|
GSettings *settings;
|
|
gchar *retval = NULL;
|
|
|
|
settings = g_settings_new ("org.gnome.evolution.calendar");
|
|
retval = g_settings_get_string (settings, "timezone");
|
|
if (!retval)
|
|
retval = g_strdup ("UTC");
|
|
|
|
return retval;
|
|
}
|
|
/* } */
|
|
|
|
enum { /* XML helper enum */
|
|
ECALCOMPONENTTEXT,
|
|
ECALCOMPONENTATTENDEE,
|
|
CONSTCHAR
|
|
};
|
|
|
|
static void
|
|
display_error_message (GtkWidget *parent,
|
|
GError *error)
|
|
{
|
|
GtkWidget *dialog;
|
|
|
|
dialog = gtk_message_dialog_new (GTK_WINDOW (parent), 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
|
|
"%s", error->message);
|
|
gtk_dialog_run (GTK_DIALOG (dialog));
|
|
gtk_widget_destroy (dialog);
|
|
}
|
|
|
|
/* Some helpers for the xml stuff */
|
|
static void
|
|
add_list_to_rdf (xmlNodePtr node,
|
|
const gchar *tag,
|
|
GSList *list_in,
|
|
gint type)
|
|
{
|
|
if (list_in) {
|
|
GSList *list = list_in;
|
|
|
|
while (list) {
|
|
const gchar *str = NULL;
|
|
|
|
switch (type) {
|
|
case ECALCOMPONENTATTENDEE:
|
|
str = ((ECalComponentAttendee *) list->data)->value;
|
|
break;
|
|
case ECALCOMPONENTTEXT:
|
|
str = ((ECalComponentText *) list->data)->value;
|
|
break;
|
|
case CONSTCHAR:
|
|
default:
|
|
str = list->data;
|
|
break;
|
|
}
|
|
|
|
add_string_to_rdf (node, tag, str);
|
|
|
|
list = g_slist_next (list);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
add_nummeric_to_rdf (xmlNodePtr node,
|
|
const gchar *tag,
|
|
gint *nummeric)
|
|
{
|
|
if (nummeric) {
|
|
gchar *value = g_strdup_printf ("%d", *nummeric);
|
|
xmlNodePtr cur_node = xmlNewChild (node, NULL, (guchar *) tag, (guchar *) value);
|
|
xmlSetProp (cur_node, (const guchar *)"rdf:datatype", (const guchar *)"http://www.w3.org/2001/XMLSchema#integer");
|
|
g_free (value);
|
|
}
|
|
}
|
|
|
|
static void
|
|
add_time_to_rdf (xmlNodePtr node,
|
|
const gchar *tag,
|
|
icaltimetype *time)
|
|
{
|
|
if (time) {
|
|
xmlNodePtr cur_node = NULL;
|
|
struct tm mytm = icaltimetype_to_tm (time);
|
|
gchar *str = (gchar *) g_malloc (sizeof (gchar) * 200);
|
|
gchar *tmp = NULL;
|
|
gchar *timezone;
|
|
/*
|
|
* Translator: the %FT%T is the thirth argument for a strftime function.
|
|
* It lets you define the formatting of the date in the rdf-file.
|
|
* Also check out http://www.w3.org/2002/12/cal/tzd
|
|
* */
|
|
e_utf8_strftime (str, 200, _("%FT%T"), &mytm);
|
|
|
|
cur_node = xmlNewChild (node, NULL, (guchar *) tag, (guchar *) str);
|
|
|
|
/* Not sure about this property */
|
|
timezone = calendar_config_get_timezone ();
|
|
tmp = g_strdup_printf ("http://www.w3.org/2002/12/cal/tzd/%s#tz", timezone);
|
|
xmlSetProp (cur_node, (const guchar *)"rdf:datatype", (guchar *)tmp);
|
|
g_free (tmp);
|
|
g_free (timezone);
|
|
g_free (str);
|
|
}
|
|
}
|
|
|
|
static void
|
|
add_string_to_rdf (xmlNodePtr node,
|
|
const gchar *tag,
|
|
const gchar *value)
|
|
{
|
|
if (value) {
|
|
xmlNodePtr cur_node = NULL;
|
|
cur_node = xmlNewChild (node, NULL, (guchar *) tag, (guchar *) value);
|
|
xmlSetProp (cur_node, (const guchar *)"rdf:datatype", (const guchar *)"http://www.w3.org/2001/XMLSchema#string");
|
|
}
|
|
}
|
|
|
|
static void
|
|
do_save_calendar_rdf (FormatHandler *handler,
|
|
ESourceSelector *selector,
|
|
ECalClientSourceType type,
|
|
gchar *dest_uri)
|
|
{
|
|
|
|
/*
|
|
* According to some documentation about CSV, newlines 'are' allowed
|
|
* in CSV-files. But you 'do' have to put the value between quotes.
|
|
* The helper 'string_needsquotes' will check for that
|
|
*
|
|
* http://www.creativyst.com/Doc/Articles/CSV/CSV01.htm
|
|
* http://www.creativyst.com/cgi-bin/Prod/15/eg/csv2xml.pl
|
|
*/
|
|
|
|
ESource *primary_source;
|
|
ECalClient *source_client;
|
|
GError *error = NULL;
|
|
GSList *objects = NULL;
|
|
gchar *temp = NULL;
|
|
GOutputStream *stream;
|
|
|
|
if (!dest_uri)
|
|
return;
|
|
|
|
/* open source client */
|
|
primary_source = e_source_selector_ref_primary_selection (selector);
|
|
source_client = e_cal_client_new (primary_source, type, &error);
|
|
g_object_unref (primary_source);
|
|
|
|
if (!source_client || !e_client_open_sync (E_CLIENT (source_client), TRUE, NULL, &error)) {
|
|
display_error_message (gtk_widget_get_toplevel (GTK_WIDGET (selector)), error);
|
|
if (source_client)
|
|
g_object_unref (source_client);
|
|
g_error_free (error);
|
|
return;
|
|
}
|
|
|
|
stream = open_for_writing (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (selector))), dest_uri, &error);
|
|
|
|
if (stream && e_cal_client_get_object_list_as_comps_sync (source_client, "#t", &objects, NULL, NULL)) {
|
|
GSList *iter;
|
|
|
|
xmlBufferPtr buffer = xmlBufferCreate ();
|
|
xmlDocPtr doc = xmlNewDoc((xmlChar *) "1.0");
|
|
xmlNodePtr fnode;
|
|
|
|
doc->children = xmlNewDocNode (doc, NULL, (const guchar *)"rdf:RDF", NULL);
|
|
xmlSetProp (doc->children, (const guchar *)"xmlns:rdf", (const guchar *)"http://www.w3.org/1999/02/22-rdf-syntax-ns#");
|
|
xmlSetProp (doc->children, (const guchar *)"xmlns", (const guchar *)"http://www.w3.org/2002/12/cal/ical#");
|
|
|
|
fnode = xmlNewChild (doc->children, NULL, (const guchar *)"Vcalendar", NULL);
|
|
|
|
/* Should Evolution publicise these? */
|
|
xmlSetProp (fnode, (const guchar *)"xmlns:x-wr", (const guchar *)"http://www.w3.org/2002/12/cal/prod/Apple_Comp_628d9d8459c556fa#");
|
|
xmlSetProp (fnode, (const guchar *)"xmlns:x-lic", (const guchar *)"http://www.w3.org/2002/12/cal/prod/Apple_Comp_628d9d8459c556fa#");
|
|
|
|
/* Not sure if it's correct like this */
|
|
xmlNewChild (fnode, NULL, (const guchar *)"prodid", (const guchar *)"-//" PACKAGE_STRING "//iCal 1.0//EN");
|
|
|
|
/* Assuming GREGORIAN is the only supported calendar scale */
|
|
xmlNewChild (fnode, NULL, (const guchar *)"calscale", (const guchar *)"GREGORIAN");
|
|
|
|
temp = calendar_config_get_timezone ();
|
|
xmlNewChild (fnode, NULL, (const guchar *)"x-wr:timezone", (guchar *)temp);
|
|
g_free (temp);
|
|
|
|
xmlNewChild (fnode, NULL, (const guchar *)"method", (const guchar *)"PUBLISH");
|
|
|
|
xmlNewChild (fnode, NULL, (const guchar *)"x-wr:relcalid", (guchar *)e_source_get_uid (primary_source));
|
|
|
|
xmlNewChild (fnode, NULL, (const guchar *)"x-wr:calname", (guchar *)e_source_get_display_name (primary_source));
|
|
|
|
/* Version of this RDF-format */
|
|
xmlNewChild (fnode, NULL, (const guchar *)"version", (const guchar *)"2.0");
|
|
|
|
for (iter = objects; iter; iter = iter->next) {
|
|
ECalComponent *comp = iter->data;
|
|
const gchar *temp_constchar;
|
|
gchar *tmp_str = NULL;
|
|
GSList *temp_list;
|
|
ECalComponentDateTime temp_dt;
|
|
struct icaltimetype *temp_time;
|
|
gint *temp_int;
|
|
ECalComponentText temp_comptext;
|
|
xmlNodePtr c_node = xmlNewChild (fnode, NULL, (const guchar *)"component", NULL);
|
|
xmlNodePtr node = xmlNewChild (c_node, NULL, (const guchar *)"Vevent", NULL);
|
|
|
|
/* Getting the stuff */
|
|
e_cal_component_get_uid (comp, &temp_constchar);
|
|
tmp_str = g_strdup_printf ("#%s", temp_constchar);
|
|
xmlSetProp (node, (const guchar *)"about", (guchar *)tmp_str);
|
|
g_free (tmp_str);
|
|
add_string_to_rdf (node, "uid",temp_constchar);
|
|
|
|
e_cal_component_get_summary (comp, &temp_comptext);
|
|
add_string_to_rdf (node, "summary", temp_comptext.value);
|
|
|
|
e_cal_component_get_description_list (comp, &temp_list);
|
|
add_list_to_rdf (node, "description", temp_list, ECALCOMPONENTTEXT);
|
|
if (temp_list)
|
|
e_cal_component_free_text_list (temp_list);
|
|
|
|
e_cal_component_get_categories_list (comp, &temp_list);
|
|
add_list_to_rdf (node, "categories", temp_list, CONSTCHAR);
|
|
if (temp_list)
|
|
e_cal_component_free_categories_list (temp_list);
|
|
|
|
e_cal_component_get_comment_list (comp, &temp_list);
|
|
add_list_to_rdf (node, "comment", temp_list, ECALCOMPONENTTEXT);
|
|
|
|
if (temp_list)
|
|
e_cal_component_free_text_list (temp_list);
|
|
|
|
e_cal_component_get_completed (comp, &temp_time);
|
|
add_time_to_rdf (node, "completed", temp_time);
|
|
if (temp_time)
|
|
e_cal_component_free_icaltimetype (temp_time);
|
|
|
|
e_cal_component_get_created (comp, &temp_time);
|
|
add_time_to_rdf (node, "created", temp_time);
|
|
if (temp_time)
|
|
e_cal_component_free_icaltimetype (temp_time);
|
|
|
|
e_cal_component_get_contact_list (comp, &temp_list);
|
|
add_list_to_rdf (node, "contact", temp_list, ECALCOMPONENTTEXT);
|
|
if (temp_list)
|
|
e_cal_component_free_text_list (temp_list);
|
|
|
|
e_cal_component_get_dtstart (comp, &temp_dt);
|
|
add_time_to_rdf (node, "dtstart", temp_dt.value ? temp_dt.value : NULL);
|
|
e_cal_component_free_datetime (&temp_dt);
|
|
|
|
e_cal_component_get_dtend (comp, &temp_dt);
|
|
add_time_to_rdf (node, "dtend", temp_dt.value ? temp_dt.value : NULL);
|
|
e_cal_component_free_datetime (&temp_dt);
|
|
|
|
e_cal_component_get_due (comp, &temp_dt);
|
|
add_time_to_rdf (node, "due", temp_dt.value ? temp_dt.value : NULL);
|
|
e_cal_component_free_datetime (&temp_dt);
|
|
|
|
e_cal_component_get_percent (comp, &temp_int);
|
|
add_nummeric_to_rdf (node, "percentComplete", temp_int);
|
|
|
|
e_cal_component_get_priority (comp, &temp_int);
|
|
add_nummeric_to_rdf (node, "priority", temp_int);
|
|
|
|
e_cal_component_get_url (comp, &temp_constchar);
|
|
add_string_to_rdf (node, "URL", temp_constchar);
|
|
|
|
if (e_cal_component_has_attendees (comp)) {
|
|
e_cal_component_get_attendee_list (comp, &temp_list);
|
|
add_list_to_rdf (node, "attendee", temp_list, ECALCOMPONENTATTENDEE);
|
|
if (temp_list)
|
|
e_cal_component_free_attendee_list (temp_list);
|
|
}
|
|
|
|
e_cal_component_get_location (comp, &temp_constchar);
|
|
add_string_to_rdf (node, "location", temp_constchar);
|
|
|
|
e_cal_component_get_last_modified (comp, &temp_time);
|
|
add_time_to_rdf (node, "lastModified",temp_time);
|
|
|
|
/* Important note!
|
|
* The documentation is not requiring this!
|
|
*
|
|
* if (temp_time) e_cal_component_free_icaltimetype (temp_time);
|
|
*
|
|
* Please uncomment and fix documentation if untrue
|
|
* http://www.gnome.org/projects/evolution/developer-doc/libecal/ECalComponent.html
|
|
* #e-cal-component-get-last-modified
|
|
*/
|
|
}
|
|
|
|
/* I used a buffer rather than xmlDocDump: I want gio support */
|
|
xmlNodeDump (buffer, doc, doc->children, 2, 1);
|
|
|
|
g_output_stream_write_all (stream, xmlBufferContent (buffer), xmlBufferLength (buffer), NULL, NULL, &error);
|
|
g_output_stream_close (stream, NULL, NULL);
|
|
|
|
e_cal_client_free_ecalcomp_slist (objects);
|
|
|
|
xmlBufferFree (buffer);
|
|
xmlFreeDoc (doc);
|
|
}
|
|
|
|
if (stream)
|
|
g_object_unref (stream);
|
|
|
|
g_object_unref (source_client);
|
|
|
|
if (error) {
|
|
display_error_message (gtk_widget_get_toplevel (GTK_WIDGET (selector)), error);
|
|
g_error_free (error);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
FormatHandler *rdf_format_handler_new (void)
|
|
{
|
|
FormatHandler *handler = g_new (FormatHandler, 1);
|
|
|
|
handler->isdefault = FALSE;
|
|
handler->combo_label = _("RDF (.rdf)");
|
|
handler->filename_ext = ".rdf";
|
|
handler->options_widget = NULL;
|
|
handler->save = do_save_calendar_rdf;
|
|
|
|
return handler;
|
|
}
|