Remove QSF

git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@19099 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
Geert Janssens 2010-05-04 14:15:38 +00:00
parent 36a9a2e592
commit 70f08de64a
51 changed files with 60 additions and 9269 deletions

View File

@ -550,11 +550,8 @@ if test x$QOF_XML_DIR = x; then
QOF_LIBS='${top_builddir}/src/libqof/qof/libgnc-qof.la'
QOF_VERSION="internal"
QOF_PREFIX="internal"
QOF_XML_DIR='${pkgdatadir}/xml/qsf'
LIBQOF_LIBRARY_VERSION=1:4:0
LIBQOF_BACKEND_QSF_LIBRARY_VERSION=0:3:0
AC_SUBST(LIBQOF_LIBRARY_VERSION)
AC_SUBST(LIBQOF_BACKEND_QSF_LIBRARY_VERSION)
AC_DEFINE(HAVE_LIBQOF,,[We will use the internal QOF code])
HAVE_LIBQOF="use_internal"
fi
@ -566,7 +563,6 @@ AC_SUBST(QOF_LIBS)
AS_SCRUB_INCLUDE(QOF_PREFIX)
AC_SUBST(QOF_PREFIX)
AC_SUBST(QOF_LIB_DIR)
AC_SUBST(QOF_XML_DIR)
AC_DEFINE(QOF_DISABLE_DEPRECATED,1, [Don't use deprecated qof functions])
@ -1449,12 +1445,9 @@ AC_CONFIG_FILES(po/Makefile.in
src/import-export/hbci/test/Makefile
src/libqof/Makefile
src/libqof/qof/Makefile
src/libqof/backend/Makefile
src/libqof/backend/file/Makefile
src/optional/Makefile
src/optional/python-bindings/Makefile
src/optional/python-bindings/tests/Makefile
src/optional/xsl/Makefile
src/pixmaps/Makefile
src/quotes/Makefile
src/register/Makefile

View File

@ -56,7 +56,6 @@ AM_CFLAGS = \
-I${top_srcdir}/src/test-core \
-I${top_srcdir}/src/engine \
-I${top_srcdir}/src/engine/test-core \
-I${top_srcdir}/src/backend/qsf \
-I${top_srcdir}/src/libqof/qof \
-DTEST_MYSQL_URL=\"${TEST_MYSQL_URL}\" \
-DTEST_PGSQL_URL=\"${TEST_PGSQL_URL}\" \

View File

@ -92,9 +92,6 @@ typedef enum
GNC_BOOK_XML1_FILE,
GNC_BOOK_XML2_FILE,
GNC_BOOK_XML2_FILE_NO_ENCODING,
QSF_GNC_OBJECT,
QSF_OBJECT,
QSF_MAP,
} QofBookFileType;
/* ================================================================= */

View File

@ -237,7 +237,6 @@ AM_CPPFLAGS = \
-I${top_srcdir}/src/engine \
-I${top_srcdir}/src/engine/test-core \
-I${top_srcdir}/src/backend/xml \
-I${top_srcdir}/src/backend/qsf \
-I${top_srcdir}/src/libqof/qof \
${LIBXML2_CFLAGS} \
${GLIB_CFLAGS} \

View File

@ -14,7 +14,7 @@ GNC_TEST_DEPS = \
--library-dir ${top_builddir}/src/core-utils \
--library-dir ${top_builddir}/src/gnc-module \
--library-dir ${top_builddir}/src/engine \
--library-dir ${top_builddir}/src/backend\sql
--library-dir ${top_builddir}/src/backend/sql
TESTS_ENVIRONMENT = \
GNC_ACCOUNT_PATH=${top_srcdir}/accounts/C \

View File

@ -6,6 +6,7 @@ AM_CPPFLAGS = \
-I${top_srcdir}/src/engine \
-I${top_srcdir}/src/business/business-core \
-I${top_srcdir}/src/libqof/qof \
-I${top_srcdir}/src/backend/xml \
${GUILE_INCS} \
${GLIB_CFLAGS}
@ -15,6 +16,7 @@ LDADD = \
${top_builddir}/src/engine/libgncmod-engine.la \
../libgncmod-business-core.la \
${top_builddir}/src/libqof/qof/libgnc-qof.la \
${top_builddir}/src/backend/xml/libgncmod-backend-xml.la \
${GLIB_LIBS}
TESTS = \
@ -36,7 +38,8 @@ GNC_TEST_DEPS = \
--library-dir ${top_builddir}/src/core-utils \
--library-dir ${top_builddir}/src/gnc-module \
--library-dir ${top_builddir}/src/engine \
--library-dir ${top_builddir}/src/business/business-core
--library-dir ${top_builddir}/src/business/business-core \
--library-dir ${top_builddir}/src/backend/xml
TESTS_ENVIRONMENT = \
$(shell ${top_srcdir}/src/gnc-test-env --no-exports ${GNC_TEST_DEPS})

View File

@ -33,6 +33,11 @@
#include "gncJobP.h"
#include "test-stuff.h"
#include "gnc-backend-xml.h"
#define FILE_NAME "xml:///tmp/testbook.gnucash"
#define GNC_LIB_NAME "gncmod-backend-xml"
static int count = 0;
static void
@ -60,14 +65,14 @@ test_customer (void)
session = qof_session_new();
be = NULL;
qof_session_begin(session, QOF_STDOUT, FALSE, FALSE);
qof_session_begin(session, FILE_NAME, FALSE, FALSE);
book = qof_session_get_book(session);
be = qof_book_get_backend(book);
/* The book *must* have a backend to pass the test of the 'dirty' flag
so use a session to use the default QSF. However, until the SQL backend can be used,
so use a session to use the default XML. However, until the SQL backend can be used,
entities remain dirty until the session is saved or closed. */
do_test (be != NULL, "qsf backend could not be set");
do_test (be != NULL, "xml backend could not be set");
/* Test creation/destruction */
{
@ -210,6 +215,7 @@ int
main (int argc, char **argv)
{
qof_init();
qof_load_backend_library ("../../../backend/xml/.libs/", GNC_LIB_NAME);
do_test (cashobjects_register(), "Cannot register cash objects");
do_test (gncInvoiceRegister(), "Cannot register GncInvoice");
do_test (gncJobRegister (), "Cannot register GncJob");

View File

@ -33,6 +33,11 @@
#include "gncInvoiceP.h"
#include "test-stuff.h"
#include "gnc-backend-xml.h"
#define FILE_NAME "xml:///tmp/testbook.gnucash"
#define GNC_LIB_NAME "gncmod-backend-xml"
static int count = 0;
static void
@ -66,12 +71,13 @@ test_employee (void)
GncEmployee *employee;
session = qof_session_new();
qof_session_begin(session, QOF_STDOUT, FALSE, FALSE);
qof_session_begin(session, FILE_NAME, FALSE, FALSE);
book = qof_session_get_book(session);
be = qof_book_get_backend (book);
/* The book *must* have a backend to pass the test of the 'dirty' flag */
/* See the README file for details */
be = qof_book_get_backend (book);
do_test (be != NULL, "xml backend could not be set");
/* Test creation/destruction */
{
@ -220,6 +226,7 @@ int
main (int argc, char **argv)
{
qof_init();
qof_load_backend_library ("../../../backend/xml/.libs/", GNC_LIB_NAME);
do_test (gncInvoiceRegister(), "Cannot register GncInvoice");
do_test (gncJobRegister (), "Cannot register GncJob");
do_test (gncCustomerRegister(), "Cannot register GncCustomer");

View File

@ -33,6 +33,11 @@
#include "gncOwner.h"
#include "test-stuff.h"
#include "gnc-backend-xml.h"
#define FILE_NAME "xml:///tmp/testbook.gnucash"
#define GNC_LIB_NAME "gncmod-backend-xml"
static int count = 0;
static void
@ -69,13 +74,13 @@ test_job (void)
session = qof_session_new();
be = NULL;
qof_session_begin(session, QOF_STDOUT, FALSE, FALSE);
qof_session_begin(session, FILE_NAME, FALSE, FALSE);
book = qof_session_get_book (session);
be = qof_book_get_backend(book);
/* The book *must* have a backend to pass the test of the 'dirty' flag */
/* See the README file for details */
do_test (be != NULL, "qsf backend could not be set");
do_test (be != NULL, "xml backend could not be set");
/* Test creation/destruction */
{
@ -241,6 +246,7 @@ int
main (int argc, char **argv)
{
qof_init();
qof_load_backend_library ("../../../backend/xml/.libs/", GNC_LIB_NAME);
do_test (gncInvoiceRegister(), "Cannot register GncInvoice");
do_test (gncJobRegister (), "Cannot register GncJob");
do_test (gncCustomerRegister(), "Cannot register GncCustomer");

View File

@ -32,6 +32,11 @@
#include "gncVendorP.h"
#include "test-stuff.h"
#include "gnc-backend-xml.h"
#define FILE_NAME "xml:///tmp/testbook.gnucash"
#define GNC_LIB_NAME "gncmod-backend-xml"
static int count = 0;
static void
@ -68,13 +73,13 @@ test_vendor (void)
session = qof_session_new();
be = NULL;
qof_session_begin(session, QOF_STDOUT, FALSE, FALSE);
qof_session_begin(session, FILE_NAME, FALSE, FALSE);
book = qof_session_get_book (session);
be = qof_book_get_backend(book);
/* The book *must* have a backend to pass the test of the 'dirty' flag */
/* See the README file for details */
do_test (be != NULL, "qsf backend could not be set");
do_test (be != NULL, "xml backend could not be set");
/* Test creation/destruction */
{
@ -222,6 +227,7 @@ int
main (int argc, char **argv)
{
qof_init();
qof_load_backend_library ("../../../backend/xml/.libs/", GNC_LIB_NAME);
do_test (gncInvoiceRegister(), "Cannot register GncInvoice");
do_test (gncJobRegister (), "Cannot register GncJob");
do_test (gncCustomerRegister(), "Cannot register GncCustomer");

View File

@ -101,18 +101,6 @@ static void gnc_plugin_business_cmd_billing_terms (GtkAction *action,
static void gnc_plugin_business_cmd_bills_due_reminder (GtkAction *action,
GncMainWindowActionData *data);
static void gnc_plugin_business_cmd_export_invoice (GtkAction *action,
GncMainWindowActionData *data);
static void gnc_plugin_business_cmd_export_customer (GtkAction *action,
GncMainWindowActionData *data);
static void gnc_plugin_business_cmd_export_vendor (GtkAction *action,
GncMainWindowActionData *data);
static void gnc_plugin_business_cmd_export_employee (GtkAction *action,
GncMainWindowActionData *data);
static void gnc_plugin_business_cmd_test_search (GtkAction *action,
GncMainWindowActionData *data);
@ -128,8 +116,6 @@ static void gnc_plugin_business_cmd_test_reload_receivable_report (GtkAction *ac
static void gnc_plugin_business_cmd_test_init_data (GtkAction *action,
GncMainWindowActionData *data);
/*static void gnc_plugin_business_cmd_export_report (GtkAction *action,
GncMainWindowActionData *data);*/
#define PLUGIN_ACTIONS_NAME "gnc-plugin-business-actions"
#define PLUGIN_UI_FILENAME "gnc-plugin-business-ui.xml"
@ -268,26 +254,6 @@ static GtkActionEntry gnc_plugin_actions [] =
G_CALLBACK (gnc_plugin_business_cmd_bills_due_reminder)
},
{ "ExportMenuAction", NULL, N_("E_xport"), NULL, NULL, NULL },
{
"QSFInvoiceAction", NULL, N_("QSF _Invoice..."), NULL,
N_("Export one or more invoices to QSF"),
G_CALLBACK (gnc_plugin_business_cmd_export_invoice)
},
{
"QSFCustomerAction", NULL, N_("QSF _Customer..."), NULL,
N_("Export one or more customers to QSF"),
G_CALLBACK (gnc_plugin_business_cmd_export_customer)
},
{
"QSFVendorAction", NULL, N_("QSF _Vendor..."), NULL,
N_("Export one or more vendors to QSF"),
G_CALLBACK (gnc_plugin_business_cmd_export_vendor)
},
{
"QSFEmployeeAction", NULL, N_("QSF _Employee..."), NULL,
N_("Export one or more employees to QSF"),
G_CALLBACK (gnc_plugin_business_cmd_export_employee)
},
/* Extensions Menu */
{ "BusinessTestAction", NULL, N_("_Business"), NULL, NULL, NULL },
@ -768,149 +734,6 @@ gnc_plugin_business_cmd_bills_due_reminder (GtkAction *action,
gnc_invoice_remind_bills_due();
}
/**************************************************************
* QSF export routines
**************************************************************/
static void
gnc_plugin_business_cmd_export_invoice (GtkAction *action, GncMainWindowActionData *mw)
{
QofSession *current_session, *chart_session;
QofBook *book;
QofCollection *coll;
gchar *filename;
gboolean success;
current_session = gnc_get_current_session();
book = qof_session_get_book(current_session);
chart_session = qof_session_new();
success = FALSE;
filename = gnc_file_dialog(_("Export Invoices to XML"), NULL,
NULL, GNC_FILE_DIALOG_EXPORT);
if (filename)
{
gchar* url = g_strdup_printf( "qsf:%s", filename );
qof_session_begin(chart_session, url, TRUE, TRUE);
coll = qof_book_get_collection(book, GNC_ID_INVOICE);
success = qof_instance_copy_coll_r(chart_session, coll);
/* Need to get the GList of GncEntry's - KVP */
coll = qof_book_get_collection(book, GNC_ID_CUSTOMER);
success = qof_instance_copy_coll_r(chart_session, coll);
if (success)
{
qof_session_save(chart_session, NULL);
}
g_free(url);
}
show_session_error(qof_session_get_error(chart_session), filename,
GNC_FILE_DIALOG_EXPORT);
g_free(filename);
qof_session_end(chart_session);
gnc_set_current_session(current_session);
}
static void
gnc_plugin_business_cmd_export_customer (GtkAction *action, GncMainWindowActionData *mw)
{
QofSession *current_session, *chart_session;
QofBook *book;
QofCollection *coll;
gchar *filename;
gboolean success;
current_session = gnc_get_current_session();
book = qof_session_get_book(current_session);
chart_session = qof_session_new();
success = FALSE;
filename = gnc_file_dialog(_("Export Customers to XML"), NULL,
NULL, GNC_FILE_DIALOG_EXPORT);
if (filename)
{
gchar* url = g_strdup_printf( "qsf:%s", filename );
qof_session_begin(chart_session, url, TRUE, TRUE);
coll = qof_book_get_collection(book, GNC_ID_CUSTOMER);
success = qof_instance_copy_coll_r(chart_session, coll);
if (success)
{
qof_session_save(chart_session, NULL);
}
g_free(url);
}
show_session_error(qof_session_get_error(chart_session), filename,
GNC_FILE_DIALOG_EXPORT);
qof_session_end(chart_session);
g_free(filename);
gnc_set_current_session(current_session);
}
static void
gnc_plugin_business_cmd_export_vendor (GtkAction *action, GncMainWindowActionData *mw)
{
QofSession *current_session, *chart_session;
QofBook *book;
QofCollection *coll;
gchar *filename;
gboolean success;
current_session = gnc_get_current_session();
book = qof_session_get_book(current_session);
chart_session = qof_session_new();
success = FALSE;
filename = gnc_file_dialog(_("Export Vendors to XML"), NULL,
NULL, GNC_FILE_DIALOG_EXPORT);
if (filename)
{
gchar* url = g_strdup_printf( "qsf:%s", filename );
qof_session_begin(chart_session, url, TRUE, TRUE);
coll = qof_book_get_collection(book, GNC_ID_VENDOR);
success = qof_instance_copy_coll_r(chart_session, coll);
if (success)
{
qof_session_save(chart_session, NULL);
}
g_free(url);
}
show_session_error(qof_session_get_error(chart_session), filename,
GNC_FILE_DIALOG_EXPORT);
qof_session_end(chart_session);
g_free(filename);
gnc_set_current_session(current_session);
}
static void
gnc_plugin_business_cmd_export_employee (GtkAction *action, GncMainWindowActionData *mw)
{
QofSession *current_session, *chart_session;
QofBook *book;
QofCollection *coll;
gchar *filename;
gboolean success;
current_session = gnc_get_current_session();
book = qof_session_get_book(current_session);
chart_session = qof_session_new();
success = FALSE;
filename = gnc_file_dialog(_("Export Employees to XML"), NULL,
NULL, GNC_FILE_DIALOG_EXPORT);
if (filename)
{
gchar* url = g_strdup_printf( "qsf:%s", filename );
qof_session_begin(chart_session, url, TRUE, TRUE);
coll = qof_book_get_collection(book, GNC_ID_EMPLOYEE);
success = qof_instance_copy_coll_r(chart_session, coll);
if (success)
{
qof_session_save(chart_session, NULL);
}
g_free(url);
}
show_session_error(qof_session_get_error(chart_session), filename,
GNC_FILE_DIALOG_EXPORT);
qof_session_end(chart_session);
g_free(filename);
gnc_set_current_session(current_session);
}
static void
gnc_plugin_business_cmd_test_search (GtkAction *action,
GncMainWindowActionData *data)

View File

@ -47,8 +47,8 @@
#define OBJ_RELATIVE "family"
#define OBJ_LIST "descendents"
/* set to TRUE to get QSF XML output
* requires QSF available (i.e. make install) */
/* set to TRUE to get XML output on stdout
* requires a stdout capable backend available (i.e. make install) */
static gboolean debug = FALSE;
/* simple object structure */
@ -1168,7 +1168,8 @@ test_recursion (QofSession *original, guint counter)
copy = qof_session_new();
if (debug)
{
qof_session_begin(copy, QOF_STDOUT, TRUE, FALSE);
/* FIXME XML backend can't handle STDOUT
* qof_session_begin(copy, QOF_STDOUT, TRUE, FALSE); */
}
/* TODO: implement QOF_TYPE_CHOICE testing. */
qof_instance_copy_coll_r(copy, grand_coll);
@ -1189,8 +1190,9 @@ test_recursion (QofSession *original, guint counter)
do_test((f == c.collect), "Number of children in descendents does not match");
if (counter == 4 && debug == TRUE)
{
qof_session_save(copy, NULL);
qof_session_save(original, NULL);
/* FIXME XML backend can't handle STDOUT
* qof_session_save(copy, NULL);
qof_session_save(original, NULL); */
}
qof_session_end(copy);
copy = NULL;
@ -1211,7 +1213,8 @@ main (int argc, const char *argv[])
original = qof_session_new();
if (debug)
{
qof_session_begin(original, QOF_STDOUT, TRUE, FALSE);
/* FIXME XML backend can't handle STDOUT
* qof_session_begin(original, QOF_STDOUT, TRUE, FALSE); */
}
create_data(original, (counter % 5));
test_recursion(original, (counter % 5));

View File

@ -332,79 +332,6 @@ show_session_error (QofBackendError io_error,
gnc_error_dialog (parent, fmt, newfile);
break;
/* QSF additions */
case ERR_QSF_INVALID_OBJ:
fmt = _("Invalid QSF Object file! The QSF object file %s failed to"
" validate against the QSF object schema. The XML structure of"
" the file is either not well-formed or contains illegal data.");
gnc_error_dialog(parent, fmt, newfile);
break;
case ERR_QSF_INVALID_MAP:
fmt = _("Invalid QSF Map file! The QSF map file %s failed to validate"
" against the QSF map schema. The XML structure of the file"
" is either not well-formed or contains illegal data.");
gnc_error_dialog(parent, fmt, newfile);
break;
case ERR_QSF_BAD_QOF_VERSION:
fmt = _("The QSF Map file %s was written for a different version of"
" QOF. It may need to be modified to work with your current"
" QOF installation.");
gnc_error_dialog(parent, fmt, newfile);
break;
case ERR_QSF_BAD_MAP:
fmt = _("The selected QSF map %s contains unusable data. "
"This is usually because not all the required parameters for "
"the defined objects have calculations described in the map.");
gnc_error_dialog(parent, fmt, newfile);
break;
case ERR_QSF_BAD_OBJ_GUID:
fmt = _("The selected QSF object file %s contains one or more invalid "
"GUIDs. The file cannot be processed - please check the source "
"of the file and try again.");
gnc_error_dialog(parent, fmt, newfile);
break;
case ERR_QSF_NO_MAP:
fmt = _("The selected QSF Object file %s requires a map but it was "
"not provided.");
gnc_error_dialog(parent, fmt, newfile);
break;
case ERR_QSF_WRONG_MAP:
fmt = _("Wrong QSF map selected. The selected map %s validates but was "
"written for different QOF objects. The list of objects defined "
"in this map does not include all the objects described in "
"the current QSF object file.");
gnc_error_dialog(parent, fmt, newfile);
break;
case ERR_QSF_MAP_NOT_OBJ:
fmt = _("The selected file %s is a QSF map and cannot be "
"opened as a QSF object.");
gnc_error_dialog(parent, fmt, newfile);
break;
case ERR_QSF_OVERFLOW:
fmt = _("When converting XML strings into numbers, an overflow "
"has been detected. The QSF object file %s contains invalid "
"data in a field that is meant to hold a number.");
gnc_error_dialog(parent, fmt, newfile);
break;
case ERR_QSF_OPEN_NOT_MERGE:
fmt = _("The QSF object file %s is valid and contains GnuCash "
"objects. However, GnuCash cannot open the file directly because "
"the data needs to be merged into an existing GnuCash data book. "
"Please open a GnuCash file or create a new one, then import "
"this QSF object file so that the data can be merged into the "
"main data book.");
gnc_error_dialog(parent, fmt, newfile);
break;
case ERR_FILEIO_FILE_BAD_READ:
fmt = _("There was an error reading the file. "
"Do you want to continue?");
@ -776,10 +703,6 @@ gnc_post_file_open (const char * filename)
gnc_file_new ();
}
}
if (ERR_QSF_OPEN_NOT_MERGE == io_err)
{
uh_oh = TRUE;
}
/* if the database doesn't exist, ask the user ... */
else if ((ERR_BACKEND_NO_SUCH_DB == io_err) ||
(ERR_SQL_DB_TOO_OLD == io_err))

View File

@ -24,7 +24,6 @@ libgnc_gnome_la_LIBADD = \
libgnc_gnome_la_SOURCES = \
swig-gnome.c \
dialog-chart-export.c \
dialog-commodities.c \
dialog-fincalc.c \
dialog-find-transactions.c \
@ -39,7 +38,6 @@ libgnc_gnome_la_SOURCES = \
dialog-tax-info.c \
druid-acct-period.c \
druid-hierarchy.c \
druid-merge.c \
druid-loan.c \
druid-stock-split.c \
gnc-plugin-account-tree.c \
@ -66,7 +64,6 @@ gnomeapp_DATA = $(gnomeapp_in_files:.desktop.in=.desktop)
appicondir = $(datadir)/pixmaps
noinst_HEADERS = \
dialog-chart-export.h \
dialog-fincalc.h \
dialog-find-transactions.h \
dialog-new-user.h \
@ -77,7 +74,6 @@ noinst_HEADERS = \
dialog-sx-since-last-run.h \
druid-acct-period.h \
druid-hierarchy.h \
druid-merge.h \
druid-loan.h \
druid-stock-split.h \
gnc-plugin-account-tree.h \

View File

@ -1,300 +0,0 @@
/***************************************************************************
* dialog-chart-export.c
*
* Sun Feb 27 14:19:12 2005
* Copyright 2005 Neil Williams
* linux@codehelp.co.uk
* Copyright (c) 2006 David Hampton <hampton@employees.org>
****************************************************************************/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
#include "config.h"
#include <gtk/gtk.h>
#include <glib/gi18n.h>
#include <time.h>
#include "qof.h"
#include "AccountP.h"
#include "Transaction.h"
#include "dialog-chart-export.h"
#include "gnc-ui-util.h"
#include "dialog-utils.h"
#include "gnc-engine.h"
#include "gnc-file.h"
#include "gnc-ui.h"
#include "gnc-session.h"
#define EQUITY_ACCOUNT_NAME _("Opening Balances")
#define OPENING_BALANCE_DESC _("Opening Balance")
void chart_export_response_cb (GtkDialog *dialog, gint response, gpointer user_data);
typedef struct chart_data_s
{
GladeXML *xml;
GtkWidget *dialog;
GtkWidget *calendar;
time_t chart_time_t;
QofSession *chart_session;
Account *equity_account;
GList *param_ref_list;
} chart_data;
static void
chart_collection_cb(QofInstance *ent, gpointer user_data)
{
chart_data *data;
Account *acc;
gboolean success;
const GncGUID *guid;
QofCollection *copy_coll;
QofBook *book;
g_return_if_fail(user_data != NULL);
data = (chart_data*)user_data;
acc = (Account*)ent;
if (0 == safe_strcmp(EQUITY_ACCOUNT_NAME, xaccAccountGetName(acc))
&& (xaccAccountGetType(acc) == ACCT_TYPE_EQUITY))
{
success = qof_instance_copy_to_session(data->chart_session, ent);
if (!success)
{
return;
}
guid = qof_entity_get_guid(ent);
book = qof_session_get_book(data->chart_session);
copy_coll = qof_book_get_collection(book, GNC_ID_ACCOUNT);
data->equity_account = (Account*)qof_collection_lookup_entity(copy_coll, guid);
return;
}
}
static void
chart_reference_cb(QofInstance *ent, gpointer user_data)
{
QofInstanceReference *reference;
QofParam *ref_param;
chart_data *data;
g_return_if_fail(user_data != NULL);
data = (chart_data*)user_data;
while (data->param_ref_list != NULL)
{
ref_param = data->param_ref_list->data;
reference = qof_instance_get_reference_from(ent, ref_param);
qof_session_update_reference_list(data->chart_session, reference);
data->param_ref_list = data->param_ref_list->next;
}
}
static void
chart_entity_cb(QofInstance *ent, gpointer user_data)
{
chart_data *data;
Account *acc_ent, *equity_account;
Transaction *trans;
Split *split;
gnc_numeric balance;
QofBook *book;
QofCollection *coll;
const GncGUID *guid;
time_t trans_time;
GList *ref;
QofInstanceReference *ent_ref;
g_return_if_fail(user_data != NULL);
data = (chart_data*)user_data;
trans_time = data->chart_time_t;
data->param_ref_list = NULL;
guid = qof_entity_get_guid(ent);
acc_ent = (Account*)ent;
ref = NULL;
equity_account = data->equity_account;
g_return_if_fail(equity_account != NULL);
balance = xaccAccountGetBalanceAsOfDate(acc_ent, data->chart_time_t);
qof_instance_copy_to_session(data->chart_session, ent);
book = qof_session_get_book(data->chart_session);
coll = qof_book_get_collection(book, GNC_ID_ACCOUNT);
acc_ent = (Account*)qof_collection_lookup_entity(coll, guid);
if (xaccAccountGetCommodity(acc_ent) == NULL)
{
xaccAccountSetCommodity(acc_ent, gnc_default_currency());
}
/* can't use gnc_account_create_opening_balance directly - the partial
QofBook doesn't have an AccountGroup that is used to locate the Equity Account. */
xaccAccountBeginEdit (acc_ent);
xaccAccountBeginEdit (equity_account);
trans = xaccMallocTransaction (book);
xaccTransBeginEdit (trans);
xaccTransSetCurrency (trans, xaccAccountGetCommodity (acc_ent));
xaccTransSetDateSecs (trans, trans_time);
xaccTransSetDateEnteredSecs (trans, trans_time);
xaccTransSetDescription (trans, OPENING_BALANCE_DESC);
/* User account split */
split = xaccMallocSplit (book);
xaccTransAppendSplit (trans, split);
xaccAccountInsertSplit (acc_ent, split);
xaccSplitSetAmount (split, balance);
xaccSplitSetValue (split, balance);
ref = qof_class_get_referenceList(GNC_ID_SPLIT);
while (ref != NULL)
{
ent_ref = qof_instance_get_reference_from(QOF_INSTANCE(split), ref->data);
qof_session_update_reference_list(data->chart_session, ent_ref);
ref = g_list_next(ref);
}
g_list_free(ref);
balance = gnc_numeric_neg (balance);
/* Equity account split */
split = xaccMallocSplit (book);
xaccTransAppendSplit (trans, split);
xaccAccountInsertSplit (equity_account, split);
xaccSplitSetAmount (split, balance);
xaccSplitSetValue (split, balance);
xaccTransCommitEdit (trans);
xaccAccountCommitEdit (equity_account);
xaccAccountCommitEdit (acc_ent);
ref = qof_class_get_referenceList(GNC_ID_TRANS);
while (ref != NULL)
{
ent_ref = qof_instance_get_reference_from(QOF_INSTANCE(trans), ref->data);
qof_session_update_reference_list(data->chart_session, ent_ref);
ref = g_list_next(ref);
}
g_list_free(ref);
ref = qof_class_get_referenceList(GNC_ID_SPLIT);
while (ref != NULL)
{
ent_ref = qof_instance_get_reference_from(QOF_INSTANCE(split), ref->data);
qof_session_update_reference_list(data->chart_session, ent_ref);
ref = g_list_next(ref);
}
g_list_free(ref);
}
void
gnc_main_window_chart_export(void)
{
GladeXML *xml;
chart_data *data;
xml = gnc_glade_xml_new ("chart-export.glade", "chart-export");
data = g_new0(chart_data, 1);
data->xml = xml;
data->dialog = glade_xml_get_widget(xml, "chart-export");
data->calendar = glade_xml_get_widget(xml, "chart-calendar");
glade_xml_signal_autoconnect_full(xml,
gnc_glade_autoconnect_full_func,
data);
gtk_widget_show(data->dialog);
}
static void
on_dateok_clicked (chart_data *data)
{
guint year, month, day;
struct tm *chart_tm;
gchar *filename;
QofSession *current_session, *chart_session;
QofBook *book;
QofCollection *coll;
data->chart_time_t = time(NULL);
chart_tm = gmtime(&data->chart_time_t);
/* set today - calendar will omit any zero/NULL values */
year = chart_tm->tm_year + 1900;
month = chart_tm->tm_mon + 1;
day = chart_tm->tm_mday;
gtk_calendar_get_date(GTK_CALENDAR(data->calendar),
&year, &month, &day);
if ((year + 1900) != chart_tm->tm_year)
{
chart_tm->tm_year = year - 1900;
}
if (month != chart_tm->tm_mon)
{
chart_tm->tm_mon = month;
}
if (day != chart_tm->tm_yday)
{
chart_tm->tm_mday = day;
}
data->chart_time_t = mktime(chart_tm);
current_session = gnc_get_current_session();
book = qof_session_get_book(current_session);
chart_session = qof_session_new();
filename = gnc_file_dialog(_("Export Chart of Accounts to QSF XML"),
NULL, NULL, GNC_FILE_DIALOG_EXPORT);
if (filename)
{
gnc_set_busy_cursor(NULL, TRUE);
qof_event_suspend();
qof_session_begin(chart_session, filename, TRUE, TRUE);
data->chart_session = chart_session;
data->equity_account = NULL;
coll = qof_book_get_collection(book, GNC_ID_ACCOUNT);
qof_collection_foreach(coll, chart_collection_cb, data);
if (data->equity_account == NULL)
{
data->equity_account = xaccMallocAccount (qof_session_get_book(chart_session));
xaccAccountBeginEdit (data->equity_account);
xaccAccountSetName (data->equity_account, EQUITY_ACCOUNT_NAME);
xaccAccountSetDescription(data->equity_account, EQUITY_ACCOUNT_NAME);
xaccAccountSetType (data->equity_account, ACCT_TYPE_EQUITY);
xaccAccountSetCommodity (data->equity_account, gnc_default_currency());
}
qof_object_foreach(GNC_ID_ACCOUNT, book, chart_entity_cb, data);
data->param_ref_list = qof_class_get_referenceList(GNC_ID_TRANS);
qof_object_foreach(GNC_ID_TRANS, book, chart_reference_cb, data);
g_list_free(data->param_ref_list);
data->param_ref_list = qof_class_get_referenceList(GNC_ID_SPLIT);
qof_object_foreach(GNC_ID_SPLIT, book, chart_reference_cb, data);
g_list_free(data->param_ref_list);
qof_session_save(chart_session, NULL);
show_session_error(qof_session_get_error(chart_session),
filename, GNC_FILE_DIALOG_EXPORT);
qof_event_resume();
gnc_unset_busy_cursor(NULL);
}
qof_session_end(chart_session);
gnc_set_current_session(current_session);
}
void
chart_export_response_cb (GtkDialog *dialog, gint response, gpointer user_data)
{
chart_data *data;
data = (chart_data*)user_data;
switch (response)
{
case GTK_RESPONSE_OK:
gtk_widget_hide(data->dialog);
on_dateok_clicked(data);
break;
default:
/* do nothing */
break;
}
gtk_widget_destroy(data->dialog);
g_object_unref(data->xml);
g_free(data);
}

View File

@ -1,75 +0,0 @@
/***************************************************************************
* dialog-chart-export.h
*
* Sun Feb 27 14:19:21 2005
* Copyright 2005 Neil Williams
* linux@codehelp.co.uk
****************************************************************************/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
#ifndef _DIALOG_CHART_EXPORT_H
#define _DIALOG_CHART_EXPORT_H
/** @addtogroup Import_Export
@{
*/
/** @addtogroup ChartExport Export a chart of accounts.
Remember to use qof_instance_copy routines
like ::qof_instance_copy_to_session so that the QofBook is marked
as \a partial.
\par guidelines Guidelines for partial book export
-# When exporting GnuCash accounts into QSF, remember that there is no
AccountGroup in the partial book, not even a root. Some account functions
that you have used in full books will \b not work within a partial book.
-# Take special care with the book pointer. It is \b very easy to use a
QofBook* pointer to a book in a different QofSession. Certain API functions
also make assumptions about the current book - \b check carefully.
-# Remember that just because the function does not use books or AccountGroup
itself, it does \b not follow that other functions called by the routine
are also suitable. You may have to reimplement the body of certain functions.
-# Commodities are \b not supported. Most Account functions will use the
commodity of the account, so be sure to set at least the ::gnc_default_currency().
@{
*/
/** @file dialog-chart-export.h
@brief Chart Export - Routines to export Chart of Accounts to file
@author Copyright (C) 2005 Neil Williams <linux@codehelp.co.uk>
*/
/** \brief Export the Chart of Accounts to QSF
Write out the Chart of Accounts \b with balances as of a
specific date, as QSF.
The function iterates over each account in the current book and
gets the balance as of the specified date. The account is copied to
the export session, setting the export session book as partial.
The function then looks up the new entity in the export session
book and sets the opening balance of the copied account.
*/
void
gnc_main_window_chart_export(void);
/** @} */
/** @} */
#endif /* _DIALOG_CHART_EXPORT_H */

View File

@ -34,7 +34,6 @@
#include "dialog-new-user.h"
#include "dialog-utils.h"
#include "druid-hierarchy.h"
#include "druid-merge.h"
#include "druid-utils.h"
#include "gnc-amount-edit.h"
#include "gnc-currency-edit.h"

View File

@ -1,400 +0,0 @@
/********************************************************************\
* druid-merge.c -- account hierarchy merge functionality *
* Copyright (C) 2001 Gnumatic, Inc. *
* Copyright (C) 2004 Neil Williams <linux@codehelp.co.uk> *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License as *
* published by the Free Software Foundation; either version 2 of *
* the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License*
* along with this program; if not, contact: *
* *
* Free Software Foundation Voice: +1-617-542-5942 *
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
* Boston, MA 02110-1301, USA gnu@gnu.org *
\********************************************************************/
#include "config.h"
#include <gnome.h>
#include <glib/gi18n.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "dialog-utils.h"
#include "druid-merge.h"
#include "druid-utils.h"
#include "gnc-component-manager.h"
#include "gnc-gui-query.h"
#include "qof.h"
#include "druid-hierarchy.h"
#include "gnc-ui-util.h"
#include "Account.h"
GtkWidget *druid_hierarchy_window = NULL;
static GtkWidget *qsf_import_merge_window = NULL;
QofSession *previous_session = NULL;
gint count = 0;
QofBookMergeData *mergeData = NULL;
QofSession *merge_session = NULL;
QofBook *mergeBook = NULL;
QofBook *targetBook = NULL;
static QofLogModule log_module = GNC_QSF_IMPORT;
void collision_rule_loop (QofBookMergeData*, QofBookMergeRule*, guint );
static GtkWidget*
merge_get_widget (const char *name)
{
if (!qsf_import_merge_window) return NULL;
return gnc_glade_lookup_widget (qsf_import_merge_window, name);
}
static void
delete_merge_window (void)
{
if (!qsf_import_merge_window) return;
gtk_widget_destroy (qsf_import_merge_window);
qsf_import_merge_window = NULL;
}
static void
qof_book_merge_destroy_cb (GtkObject *obj, gpointer user_data)
{
}
static gboolean
on_import_start_page_next(GnomeDruidPage *gnomedruidpage,
gpointer arg1,
gpointer user_data)
{
return FALSE;
}
static void
on_MergeUpdate_clicked (GtkButton *button,
gpointer user_data)
{
GtkLabel *output;
g_return_if_fail(mergeData != NULL);
ENTER (" ");
mergeData = qof_book_merge_update_result(mergeData, MERGE_UPDATE);
count = 0;
qof_book_merge_rule_foreach(mergeData, collision_rule_loop, MERGE_REPORT);
if (count == 0)
{
output = GTK_LABEL(merge_get_widget("OutPut"));
gtk_label_set_text(output, _("No conflicts to be resolved."));
gtk_widget_show(GTK_WIDGET(output));
}
LEAVE (" ");
}
static void
on_MergeDuplicate_clicked (GtkButton *button,
gpointer user_data)
{
QofBookMergeRule *currentRule;
GtkLabel *output;
g_return_if_fail(mergeData != NULL);
ENTER (" ");
currentRule = mergeData->currentRule;
if (currentRule->mergeAbsolute == FALSE)
{
mergeData = qof_book_merge_update_result(mergeData, MERGE_DUPLICATE);
count = 0;
}
if (currentRule->mergeAbsolute == TRUE)
{
mergeData = qof_book_merge_update_result(mergeData, MERGE_ABSOLUTE);
count = 0;
}
qof_book_merge_rule_foreach(mergeData, collision_rule_loop, MERGE_REPORT);
if (count == 0)
{
output = GTK_LABEL(merge_get_widget("OutPut"));
gtk_label_set_text(output, _("No conflicts to be resolved."));
gtk_widget_show(GTK_WIDGET(output));
}
LEAVE (" ");
}
static void
on_MergeNew_clicked (GtkButton *button,
gpointer user_data)
{
QofBookMergeRule *currentRule;
GtkLabel *output;
g_return_if_fail(mergeData != NULL);
currentRule = mergeData->currentRule;
g_return_if_fail(currentRule != NULL);
ENTER (" ");
if (currentRule->mergeAbsolute == FALSE)
{
mergeData = qof_book_merge_update_result(mergeData, MERGE_NEW);
}
count = 0;
qof_book_merge_rule_foreach(mergeData, collision_rule_loop, MERGE_REPORT);
if (count == 0)
{
output = GTK_LABEL(merge_get_widget("OutPut"));
gtk_label_set_text(output, _("No conflicts to be resolved."));
gtk_widget_show(GTK_WIDGET(output));
}
LEAVE (" ");
}
static gboolean
on_import_next (GnomeDruidPage *gnomedruidpage,
gpointer arg1,
gpointer user_data)
{
GtkWidget *top;
gchar *message;
if (count > 0)
{
message = g_strdup_printf(_("Error: Please resolve all %d "
"conflicts before trying to commit the data."), count);
top = gtk_widget_get_toplevel (GTK_WIDGET (gnomedruidpage));
gnc_error_dialog(top, "%s", message);
g_free(message);
return TRUE;
}
return FALSE;
}
static void
on_merge_cancel (GnomeDruid *gnomedruid, gpointer user_data)
{
g_return_if_fail(mergeBook != NULL);
gnc_suspend_gui_refresh ();
delete_merge_window();
qof_book_destroy(mergeBook);
qof_session_end(merge_session);
gnc_resume_gui_refresh ();
}
void currency_transfer_cb ( QofInstance* ent, gpointer user_data)
{
if (!ent) return;
if (xaccAccountGetCommodity((Account*)ent) == NULL)
{
xaccAccountSetCommodity((Account*)ent, gnc_default_currency());
}
}
/* If the account has no parent, shove it into the top level under the root. */
void reference_parent_cb ( QofInstance* ent, gpointer user_data)
{
Account *root;
if (!ent) return;
if (gnc_account_get_parent((Account*)ent) == NULL)
{
root = gnc_book_get_root_account(targetBook);
gnc_account_append_child(root, (Account*)ent);
}
}
static void
on_merge_finish (GnomeDruidPage *gnomedruidpage,
gpointer arg1,
gpointer user_data)
{
gint result;
GtkWidget *top;
const char *message;
ENTER (" ");
g_return_if_fail(mergeData != NULL);
gnc_suspend_gui_refresh ();
result = qof_book_merge_commit(mergeData);
if (result != 0)
{
message = g_strdup_printf(_("Error: the Commit operation failed, error code %d."), result);
top = gtk_widget_get_toplevel (GTK_WIDGET (gnomedruidpage));
gnc_error_dialog(top, "%s", message);
}
g_return_if_fail(result == 0);
delete_merge_window ();
qof_object_foreach(GNC_ID_ACCOUNT, targetBook, currency_transfer_cb, NULL);
qof_object_foreach(GNC_ID_ACCOUNT, targetBook, reference_parent_cb, NULL);
qof_book_destroy(mergeBook);
qof_session_end(merge_session);
gnc_resume_gui_refresh ();
LEAVE (" ");
}
static void
on_merge_prepare (GnomeDruidPage *gnomedruidpage,
gpointer arg1,
gpointer user_data)
{
GtkLabel *progress, *output;
gnc_suspend_gui_refresh ();
ENTER (" ");
progress = GTK_LABEL (merge_get_widget("ResultsBox"));
/* blank out old data */
gtk_label_set_text(progress, "");
g_return_if_fail(mergeBook || targetBook);
mergeData = qof_book_merge_init(mergeBook, targetBook);
g_return_if_fail(mergeData != NULL);
count = 0;
qof_book_merge_rule_foreach(mergeData, collision_rule_loop, MERGE_REPORT);
if (count == 0)
{
output = GTK_LABEL(merge_get_widget("OutPut"));
gtk_label_set_text(output, _("No conflicts to be resolved."));
gtk_widget_show(GTK_WIDGET(output));
}
gnc_resume_gui_refresh ();
LEAVE (" ");
}
static GtkWidget *
gnc_create_import_druid ( void )
{
GtkWidget *dialog, *druid, *start_page;
GladeXML *xml;
xml = gnc_glade_xml_new ("merge.glade", "Merge Druid");
dialog = glade_xml_get_widget (xml, "Merge Druid");
druid = glade_xml_get_widget (xml, "merge_druid");
gnc_druid_set_colors (GNOME_DRUID (druid));
start_page = glade_xml_get_widget (xml, "start_page");
gtk_widget_show (start_page);
gtk_widget_show (glade_xml_get_widget (xml, "MergeDruidFinishPage"));
glade_xml_signal_connect(xml, "on_start_page_next",
G_CALLBACK (on_import_start_page_next));
glade_xml_signal_connect(xml, "on_qof_book_merge_prepare",
G_CALLBACK (on_merge_prepare));
glade_xml_signal_connect(xml, "on_qof_book_merge_next",
G_CALLBACK (on_import_next));
glade_xml_signal_connect (xml, "on_finish",
G_CALLBACK (on_merge_finish));
glade_xml_signal_connect (xml, "on_cancel",
G_CALLBACK (on_merge_cancel));
glade_xml_signal_connect (xml, "on_MergeUpdate_clicked",
G_CALLBACK (on_MergeUpdate_clicked));
glade_xml_signal_connect (xml, "on_MergeDuplicate_clicked",
G_CALLBACK (on_MergeDuplicate_clicked));
glade_xml_signal_connect (xml, "on_MergeNew_clicked",
G_CALLBACK (on_MergeNew_clicked));
g_signal_connect (dialog, "destroy",
G_CALLBACK(qof_book_merge_destroy_cb), NULL);
return dialog;
}
void collision_rule_loop(QofBookMergeData *mergeData, QofBookMergeRule *rule,
guint remainder)
{
GSList *user_reports;
QofParam *one_param;
gchar *importstring, *targetstring;
GtkLabel *output;
gchar *buffer, *buffer2, *buffer3;
g_return_if_fail(rule != NULL);
buffer = "";
/* there is a rule awaiting resolution, don't print any more */
if (count > 0)
{
return;
}
ENTER (" remainder=%d", remainder);
gnc_suspend_gui_refresh ();
user_reports = rule->mergeParam;
mergeData->currentRule = rule;
output = GTK_LABEL(merge_get_widget("OutPut"));
gtk_label_set_text(output, buffer);
gtk_widget_show(GTK_WIDGET(output));
gnc_resume_gui_refresh ();
count = 1; /* user display text counts from 1, not zero */
importstring = targetstring = NULL;
gnc_suspend_gui_refresh ();
/* Translators: %i is the number of conflicts. This is a
ngettext(3) message. */
buffer2 = g_strdup_printf(ngettext("%i conflict needs to be resolved.",
"%i conflicts need to be resolved.",
remainder),
remainder);
/* Translators: %i is the number of values. This is a
ngettext(3) message. */
buffer3 = g_strdup_printf(ngettext("%i parameter value for this \"%s\" object.",
"%i parameter values for this \"%s\" object.",
g_slist_length(user_reports)),
g_slist_length(user_reports), rule->targetEnt->e_type);
buffer = g_strconcat("\n", buffer2, "\n", "\n", buffer3, "\n", NULL);
g_free(buffer2);
g_free(buffer3);
while (user_reports != NULL)
{
one_param = user_reports->data;
/* FIXME: each g_strdup_printf as well as g_strconcat
will allocate a new string; all of these need to be
freed later. Currently this causes a lot of memory
leaks. */
buffer = g_strconcat(buffer, g_strdup_printf(_("%i: Parameter name: %s "),
count, one_param->param_name), NULL);
importstring = qof_book_merge_param_as_string(one_param, rule->importEnt);
buffer = g_strconcat(buffer,
g_strdup_printf(_("Import data : %s "), importstring), NULL);
targetstring = qof_book_merge_param_as_string(one_param, rule->targetEnt);
buffer = g_strconcat(buffer,
g_strdup_printf(_("Original data : %s\n"), targetstring), NULL);
user_reports = g_slist_next(user_reports);
count++;
}
gtk_label_set_text(output, buffer);
gtk_widget_show(GTK_WIDGET(output));
gnc_resume_gui_refresh ();
g_free(buffer);
g_free(importstring);
g_free(targetstring);
LEAVE (" ");
}
void
gnc_ui_qsf_import_merge_druid (QofSession *original, QofSession *import)
{
if (qsf_import_merge_window)
{
return;
}
qof_event_suspend ();
qsf_import_merge_window = gnc_create_import_druid();
g_return_if_fail(qsf_import_merge_window != NULL);
previous_session = original;
targetBook = qof_session_get_book(previous_session);
merge_session = import;
mergeBook = qof_session_get_book(merge_session);
gtk_widget_show(qsf_import_merge_window);
}

View File

@ -1,90 +0,0 @@
/********************************************************************\
* druid-merge.h -- account hierarchy merge functionality *
* Copyright (C) 2001 Gnumatic, Inc. *
* Copyright (C) 2004 Neil Williams <linux@codehelp.co.uk> *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License as *
* published by the Free Software Foundation; either version 2 of *
* the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License*
* along with this program; if not, contact: *
* *
* Free Software Foundation Voice: +1-617-542-5942 *
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
* Boston, MA 02110-1301, USA gnu@gnu.org *
\********************************************************************/
#ifndef DRUID_MERGE_H
#define DRUID_MERGE_H
#include "qof.h"
/** @addtogroup GUI
@{ */
/** @addtogroup NewHierarchy Merging a new account tree into an existing file
<b>Collision handling principles.</b>\n
\n
This druid builds a second ::QofBook in memory using ::QofSession and
populates the book with accounts created using the usual New Account Tree
code. The druid then uses ::qof_book_merge_init to begin the merge
of the new book (created with QofSession) with the existing QofBook
(loaded by the user), with user intervention and collision handling.
-# Always check for a ::GncGUID first and compare. qof_book_merge only accepts valid ::QofBook
data and therefore ALL objects in the import book will include valid GncGUID's.
-# If the original import data did not contain a GncGUID (e.g. an external non-GnuCash source)
the GncGUID values will have been created by QofSession and will not match any existing
GncGUID's in the target book so objects that do not have a GncGUID match cannot be assumed to
be ::MERGE_NEW - parameter values must be checked.
- If a GncGUID match exists, set QofBookMergeRule::mergeAbsolute to \a TRUE.
-# If ALL parameters in the import object match the target object with the same \a GncGUID,
set ::QofBookMergeResult to \a MERGE_ABSOLUTE.
-# If any parameters differ, set ::MERGE_UPDATE.
- If the import object \a GncGUID does not match an existing object,
mergeAbsolute is unchanged from the default \a FALSE
The parameter values of the object are compared to other objects of the same
type in the target book.
-# If the same data exists in the target book with a different GncGUID, the object
is tagged as DUPLICATE.
-# If the data has changed, the object is tagged as REPORT.
-# If the data does not match, the object is tagged as NEW
More information is at http://code.neil.williamsleesmill.me.uk/
Each foreach function uses g_return_if_fail checks to protect the target book. If
any essential data is missing, the loop returns without changing the target book.
Note that this will not set or return an error value. However, g_return is only
used for critical errors that arise from programming errors, not for invalid import data
which should be cleaned up before creating the import QofBook.
Only ::qof_book_merge_init, ::qof_book_merge_update_result and ::qof_book_merge_commit return
any error values to the calling process.
@{ */
/** @file druid-merge.h
@brief API for merging two \c QofBook* structures with collision handling
@author Copyright (c) 2004 Neil Williams <linux@codehelp.co.uk>
*/
void gnc_ui_qsf_import_merge_druid(QofSession *original, QofSession *import);
#define GNC_QSF_IMPORT "gnc-qsf-import-druid"
/** \brief gncCommodity is not QOF enabled, need to set a default commodity before the merge */
void currency_transfer_cb ( QofInstance* ent, gpointer user_data);
/** \brief workaround for AccountGroup not being fully QOF enabled. Eh? */
void reference_parent_cb ( QofInstance* ent, gpointer user_data);
/** @} */
/** @} */
#endif

View File

@ -4,11 +4,9 @@ glade_DATA = \
acctperiod.glade \
autoclear.glade \
budget.glade \
chart-export.glade \
commodities.glade \
fincalc.glade \
lots.glade \
merge.glade \
newuser.glade \
price.glade \
print.glade \

View File

@ -1,245 +0,0 @@
<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
<glade-interface>
<widget class="GtkDialog" id="chart-export">
<property name="border_width">6</property>
<property name="visible">True</property>
<property name="title" translatable="yes">Export Chart of Accounts</property>
<property name="type">GTK_WINDOW_TOPLEVEL</property>
<property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
<property name="modal">False</property>
<property name="resizable">True</property>
<property name="destroy_with_parent">False</property>
<property name="decorated">True</property>
<property name="skip_taskbar_hint">False</property>
<property name="skip_pager_hint">False</property>
<property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
<property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
<property name="focus_on_map">True</property>
<property name="has_separator">False</property>
<signal name="response" handler="chart_export_response_cb" last_modification_time="Mon, 23 Jan 2006 04:43:46 GMT"/>
<child internal-child="vbox">
<widget class="GtkVBox" id="dialog-vbox1">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">12</property>
<child internal-child="action_area">
<widget class="GtkHButtonBox" id="dialog-action_area1">
<property name="visible">True</property>
<property name="layout_style">GTK_BUTTONBOX_END</property>
<child>
<widget class="GtkButton" id="cancelbutton1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="label">gtk-cancel</property>
<property name="use_stock">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<property name="response_id">-6</property>
</widget>
</child>
<child>
<widget class="GtkButton" id="dateok">
<property name="visible">True</property>
<property name="tooltip" translatable="yes">Click to choose the filename and location.</property>
<property name="can_default">True</property>
<property name="has_default">True</property>
<property name="can_focus">True</property>
<property name="has_focus">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<property name="response_id">-5</property>
<child>
<widget class="GtkAlignment" id="alignment2">
<property name="visible">True</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xscale">0</property>
<property name="yscale">0</property>
<property name="top_padding">0</property>
<property name="bottom_padding">0</property>
<property name="left_padding">0</property>
<property name="right_padding">0</property>
<child>
<widget class="GtkHBox" id="hbox2">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">2</property>
<child>
<widget class="GtkImage" id="image2">
<property name="visible">True</property>
<property name="stock">gtk-convert</property>
<property name="icon_size">4</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label4">
<property name="visible">True</property>
<property name="label">_Export</property>
<property name="use_underline">True</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
</widget>
</child>
</widget>
</child>
</widget>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack_type">GTK_PACK_END</property>
</packing>
</child>
<child>
<widget class="GtkVBox" id="vbox1">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">6</property>
<child>
<widget class="GtkLabel" id="chartlabel">
<property name="width_request">304</property>
<property name="height_request">56</property>
<property name="visible">True</property>
<property name="label" translatable="yes">You can export the Chart of Accounts to a QSF XML file that can be imported into another GnuCash file or used in other programs.</property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_FILL</property>
<property name="wrap">True</property>
<property name="selectable">False</property>
<property name="xalign">0.5</property>
<property name="yalign">0.519999980927</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="chartnote">
<property name="width_request">304</property>
<property name="height_request">56</property>
<property name="visible">True</property>
<property name="label" translatable="yes">The Chart of Accounts includes the balance for each account, on the date specified. Note that future dates are not supported.</property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_FILL</property>
<property name="wrap">True</property>
<property name="selectable">False</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="processlabel">
<property name="width_request">296</property>
<property name="height_request">48</property>
<property name="visible">True</property>
<property name="label" translatable="yes">Select the date to use and click Export to choose the filename and location. Future dates are not supported.</property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_FILL</property>
<property name="wrap">True</property>
<property name="selectable">False</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkCalendar" id="chart-calendar">
<property name="width_request">215</property>
<property name="height_request">180</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="display_options">GTK_CALENDAR_SHOW_HEADING|GTK_CALENDAR_SHOW_DAY_NAMES</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">True</property>
</packing>
</child>
</widget>
</child>
</widget>
</glade-interface>

View File

@ -1,296 +0,0 @@
<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
<glade-interface>
<requires lib="gnome"/>
<widget class="GtkWindow" id="Merge Druid">
<property name="visible">True</property>
<property name="title" translatable="yes">QSF Data Import Setup</property>
<property name="type">GTK_WINDOW_TOPLEVEL</property>
<property name="window_position">GTK_WIN_POS_CENTER</property>
<property name="modal">False</property>
<property name="resizable">True</property>
<property name="destroy_with_parent">False</property>
<property name="decorated">True</property>
<property name="skip_taskbar_hint">False</property>
<property name="skip_pager_hint">False</property>
<property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
<property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
<property name="focus_on_map">True</property>
<child>
<widget class="GnomeDruid" id="merge_druid">
<property name="border_width">4</property>
<property name="visible">True</property>
<property name="show_help">False</property>
<signal name="cancel" handler="on_cancel"/>
<child>
<widget class="GnomeDruidPageEdge" id="start_page">
<property name="visible">True</property>
<property name="position">GNOME_EDGE_START</property>
<property name="title" translatable="yes">QSF Data Import Setup</property>
<property name="text" translatable="yes">This assistant will merge your QSF data into the currently open GnuCash file.
You will be asked how to proceed if some QSF data clashes with the your existing GnuCash data file.
There is NO way to undo this operation! Please ensure you have a backup of your file BEFORE continuing! You will be given the option to cancel the merge at all stages until the final merge operation. Once you click Finish, the new QSF data will be committed to your current data file.
There is currently no currency or price support in the merge operation, the new data will inherit any default currency or you can change the currency after the merge is complete.
Your QSF data is ready to import
Click 'Cancel' if you do not wish to merge your QSF data now.</property>
<property name="title_color">#ffffffffffff</property>
<property name="text_color">#000000000000</property>
<property name="background_color">#191919197070</property>
<property name="logo_background_color">#ffffffffffff</property>
<property name="textbox_color">#ffffffffffff</property>
<signal name="next" handler="on_start_page_next"/>
</widget>
</child>
<child>
<widget class="GnomeDruidPageStandard" id="qof_book_merge">
<property name="visible">True</property>
<property name="title" translatable="yes">Please resolve any conflicts in the merge</property>
<property name="title_foreground">#ffffffffffff</property>
<property name="background">#191919197070</property>
<property name="logo_background">#ffffffffffff</property>
<signal name="prepare" handler="on_qof_book_merge_prepare"/>
<signal name="next" handler="on_qof_book_merge_next"/>
<child internal-child="vbox">
<widget class="GtkVBox" id="druid-vbox6">
<property name="border_width">16</property>
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">0</property>
<child>
<widget class="GtkHBox" id="hbox105">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">0</property>
<child>
<widget class="GtkVBox" id="vbox121">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">0</property>
<child>
<widget class="GtkLabel" id="ResultsBox">
<property name="visible">True</property>
<property name="label"></property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_CENTER</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkHBox" id="hbox106">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">0</property>
<child>
<widget class="GtkScrolledWindow" id="scrolledwindow27">
<property name="visible">True</property>
<property name="hscrollbar_policy">GTK_POLICY_NEVER</property>
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="shadow_type">GTK_SHADOW_NONE</property>
<property name="window_placement">GTK_CORNER_TOP_LEFT</property>
<child>
<widget class="GtkViewport" id="viewport1">
<property name="border_width">5</property>
<property name="height_request">51</property>
<property name="visible">True</property>
<property name="tooltip" translatable="yes">Shows how your QSF data conflicts with existing data in your file.</property>
<property name="shadow_type">GTK_SHADOW_IN</property>
<child>
<widget class="GtkLabel" id="OutPut">
<property name="height_request">50</property>
<property name="visible">True</property>
<property name="label" translatable="yes"></property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="mnemonic_widget">MergeUpdate</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
</child>
</widget>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">True</property>
</packing>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">True</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="Static">
<property name="visible">True</property>
<property name="label" translatable="yes">You have three choices for each collision:
1. The import object can be allowed to overwrite the target - use this to update your existing book.
2. The import object can be ignored - use this if the import is a duplicate of an object in the existing book.
3. The import object can be created as a new object in the existing book.</property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_FILL</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="MergeUpdate">
<property name="border_width">5</property>
<property name="visible">True</property>
<property name="tooltip" translatable="yes">overwrite the original with the import data</property>
<property name="can_focus">True</property>
<property name="has_focus">True</property>
<property name="label" translatable="yes">1. Update your existing book with the import data</property>
<property name="use_underline">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<signal name="clicked" handler="on_MergeUpdate_clicked"/>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="MergeDuplicate">
<property name="border_width">5</property>
<property name="visible">True</property>
<property name="tooltip" translatable="yes">ignore the import, leave the original untouched</property>
<property name="can_focus">True</property>
<property name="label" translatable="yes">2. Ignore the import data, leave original unchanged</property>
<property name="use_underline">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<signal name="clicked" handler="on_MergeDuplicate_clicked"/>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="MergeNew">
<property name="border_width">5</property>
<property name="visible">True</property>
<property name="tooltip" translatable="yes">Add the import as a new object, leave original in place</property>
<property name="can_focus">True</property>
<property name="label" translatable="yes">3. Import the data as a NEW object</property>
<property name="use_underline">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<signal name="clicked" handler="on_MergeNew_clicked"/>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">True</property>
</packing>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">True</property>
</packing>
</child>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GnomeDruidPageEdge" id="MergeDruidFinishPage">
<property name="visible">True</property>
<property name="position">GNOME_EDGE_FINISH</property>
<property name="title" translatable="yes">Commit QSF Import Data to data file</property>
<property name="text" translatable="yes">Press `Apply' to merge your QSF data into the current GnuCash file.
Press `Back' to review your selections.
Press `Cancel' to close this dialog without changing your current data file.
REMEMBER: There is no way to undo this final operation! Make sure you have a backup before clicking 'Apply'.</property>
<property name="title_color">#ffffffffffff</property>
<property name="text_color">#000000000000</property>
<property name="background_color">#191919197070</property>
<property name="logo_background_color">#ffffffffffff</property>
<property name="textbox_color">#ffffffffffff</property>
<signal name="finish" handler="on_finish"/>
</widget>
</child>
</widget>
</child>
</widget>
</glade-interface>

View File

@ -40,7 +40,6 @@
#include "gnc-ui-util.h"
#include "dialog-book-close.h"
#include "dialog-chart-export.h"
#include "dialog-file-access.h"
#include "dialog-fincalc.h"
#include "dialog-find-transactions.h"
@ -48,7 +47,6 @@
#include "dialog-totd.h"
#include "druid-acct-period.h"
#include "druid-loan.h"
#include "druid-merge.h"
#include "gnc-engine.h"
#include "gnc-file.h"
#include "gnc-gui-query.h"
@ -72,9 +70,7 @@ static void gnc_main_window_cmd_file_new (GtkAction *action, GncMainWindowAction
static void gnc_main_window_cmd_file_open (GtkAction *action, GncMainWindowActionData *data);
static void gnc_main_window_cmd_file_save (GtkAction *action, GncMainWindowActionData *data);
static void gnc_main_window_cmd_file_save_as (GtkAction *action, GncMainWindowActionData *data);
static void gnc_main_window_cmd_file_qsf_import (GtkAction *action, GncMainWindowActionData *data);
static void gnc_main_window_cmd_file_export_accounts (GtkAction *action, GncMainWindowActionData *data);
static void gnc_main_window_cmd_file_chart_export (GtkAction *action, GncMainWindowActionData *data);
static void gnc_main_window_cmd_edit_tax_options (GtkAction *action, GncMainWindowActionData *data);
static void gnc_main_window_cmd_actions_mortgage_loan (GtkAction *action, GncMainWindowActionData *data);
static void gnc_main_window_cmd_actions_scheduled_transaction_editor (GtkAction *action, GncMainWindowActionData *data);
@ -119,26 +115,12 @@ static GtkActionEntry gnc_plugin_actions [] =
NULL,
G_CALLBACK (gnc_main_window_cmd_file_save_as)
},
#ifdef QSF_IMPORT_WORKS
{
"FileImportQSFAction", GTK_STOCK_CONVERT,
N_("_QSF Import"), NULL,
N_("Import a QSF object file"),
G_CALLBACK (gnc_main_window_cmd_file_qsf_import)
},
#endif // QSF_IMPORT_WORKS
{
"FileExportAccountsAction", GTK_STOCK_CONVERT,
N_("Export _Accounts"), NULL,
N_("Export the account hierarchy to a new GnuCash datafile"),
G_CALLBACK (gnc_main_window_cmd_file_export_accounts)
},
{
"FileExportChartAction", GTK_STOCK_CONVERT,
N_("Export _Chart of Accounts to QSF"), NULL,
N_("Export the chart of accounts for a date with balances as QSF"),
G_CALLBACK (gnc_main_window_cmd_file_chart_export)
},
/* Edit menu */
@ -439,62 +421,6 @@ gnc_main_window_cmd_file_save_as (GtkAction *action, GncMainWindowActionData *da
/* FIXME GNOME 2 Port (update the title etc.) */
}
static void
qsf_file_select_ok(GtkWidget *w, GtkFileSelection *fs )
{
QofSession *qsf_session, *first_session;
const gchar *filename, *message, *error_message;
QofBook *original;
QofBackendError err;
ENTER (" ");
qof_event_suspend();
filename = gtk_file_selection_get_filename(GTK_FILE_SELECTION (fs));
gtk_widget_destroy((GtkWidget*) fs);
first_session = gnc_get_current_session();
original = qof_session_get_book(first_session);
qsf_session = qof_session_new();
qof_session_begin(qsf_session, filename, TRUE, FALSE);
qof_session_load(qsf_session, NULL);
err = qof_session_get_error(qsf_session);
if (err != ERR_BACKEND_NO_ERR)
{
error_message = qof_session_get_error_message(qsf_session);
if (!error_message)
error_message = "";
/* Translators: %d is the error number; %s is the error message as
text. */
message = g_strdup_printf(_("Error: Loading failed, error code %d - %s."), err, error_message);
PERR("%s", message);
qof_session_destroy(qsf_session);
qof_event_resume();
gnc_error_dialog(gnc_ui_get_toplevel(), "%s", message);
LEAVE (" ");
return;
}
qof_event_resume();
gnc_ui_qsf_import_merge_druid(first_session, qsf_session);
LEAVE (" ");
}
static void
gnc_main_window_cmd_file_qsf_import (GtkAction *action, GncMainWindowActionData *data)
{
GtkWidget *file_select;
g_return_if_fail (data != NULL);
gnc_window_set_progressbar_window(GNC_WINDOW(data->window));
file_select = gtk_file_selection_new(_("Select the QSF file to import into GnuCash"));
g_signal_connect (G_OBJECT (GTK_FILE_SELECTION (file_select)->ok_button),
"clicked", G_CALLBACK (qsf_file_select_ok), (gpointer) file_select);
g_signal_connect_swapped (G_OBJECT (GTK_FILE_SELECTION (file_select)->cancel_button),
"clicked", G_CALLBACK (gtk_widget_destroy), G_OBJECT (file_select));
gtk_widget_show (file_select);
gnc_window_set_progressbar_window(NULL);
}
static void
gnc_main_window_cmd_file_export_accounts (GtkAction *action, GncMainWindowActionData *data)
{
@ -507,18 +433,6 @@ gnc_main_window_cmd_file_export_accounts (GtkAction *action, GncMainWindowAction
/* gnc_refresh_main_window_info (); */
}
static void
gnc_main_window_cmd_file_chart_export (GtkAction *action, GncMainWindowActionData *data)
{
g_return_if_fail (data != NULL);
gnc_window_set_progressbar_window (GNC_WINDOW(data->window));
gnc_main_window_chart_export();
gnc_window_set_progressbar_window (NULL);
/* FIXME GNOME 2 Port (update the title etc.) */
/* gnc_refresh_main_window_info (); */
}
static void
gnc_main_window_cmd_edit_tax_options (GtkAction *action, GncMainWindowActionData *data)
{

View File

@ -1,2 +1,2 @@
SUBDIRS = . qof backend
SUBDIRS = . qof

View File

@ -1,2 +0,0 @@
SUBDIRS = file

View File

@ -1,46 +0,0 @@
SUBDIRS = .
pkglib_LTLIBRARIES = libgncqof-backend-qsf.la
AM_CPPFLAGS = \
-I.. -I../.. \
-I$(top_srcdir)/lib/libc \
-DLOCALE_DIR=\""$(datadir)/locale"\" \
-I${top_srcdir}/src/libqof/qof \
${LIBXML2_CFLAGS} \
${GLIB_CFLAGS}
libgncqof_backend_qsf_la_SOURCES = \
qsf-backend.c \
qsf-xml-map.c \
qsf-xml.c
libgncqof_backend_qsf_la_LDFLAGS = -module -avoid-version
libgncqof_backend_qsf_la_LIBADD = \
${top_builddir}/src/libqof/qof/libgnc-qof.la \
${GLIB_LIBS} \
${REGEX_LIBS} \
${LIBXML2_LIBS} \
$(top_builddir)/lib/libc/libc-missing.la
qsfschemadir = $(QOF_XML_DIR)
qsfschema_DATA = \
qsf-object.xsd.xml \
qsf-map.xsd.xml \
pilot-qsf-GnuCashInvoice.xml \
pilot-qsf-gncCustomer.xml
EXTRA_DIST = \
$(qsfschema_DATA) \
qsf-dir.h.in \
qof-backend-qsf.h \
qsf-xml.h
qsf-dir.h: qsf-dir.h.in Makefile
rm -f $@.tmp
sed < $< > $@.tmp \
-e 's#@-QSF_SCHEMA_DIR-@#${QOF_XML_DIR}#g'
mv $@.tmp $@
BUILT_SOURCES = qsf-dir.h
CLEANFILES = qsf-dir.h

View File

@ -1,122 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- this map does NOT post invoices automatically -->
<!-- maps use the same sequence of parameter types as other QSF -->
<!-- Trans:desc can be set by expenses or datebook -->
<qsf-map
xmlns="http://qof.sourceforge.net/">
<definition qof_version="3">
<define e_type="pilot_expenses"/>
<define e_type="pilot_datebook"/>
<define e_type="pilot_address"/>
<define e_type="gncInvoice"/>
<define e_type="Trans"/>
<define e_type="gncEntry"/>
<default name="mileage_rate" type="numeric" value="30/100"/>
<default name="use_weekday_descriptor" type="boolean" value="true"/>
<default name="use_discount" type="boolean" value="false"/>
<default name="tax_included" type="boolean" value="false"/>
<default name="post_account" type="string" value="Assets:Current Assets:CD account"/>
<default name="expenses_account" type="string" value="Income:Other Income"/>
<default name="datebook_account" type="string" value="Income:Locum Income"/>
<default name="tax_included" type="enum" value="1">GNC_TAXINCLUDED_YES</default>
<default name="tax_included" type="enum" value="2">GNC_TAXINCLUDED_NO</default>
<default name="tax_included" type="enum" value="3">GNC_TAXINCLUDED_USEGLOBAL</default>
<default name="amount_type" type="enum" value="1">GNC_AMT_TYPE_VALUE</default>
<default name="amount_type" type="enum" value="2">GNC_AMT_TYPE_PERCENT</default>
</definition>
<object type="gncEntry">
<calculate type="string" value="desc">
<if boolean="use_weekday_descriptor">
<set format="%A">expense_date</set>
</if>
<else type="pilot_expenses">
<set>expense_vendor</set>
</else>
<else type="pilot_datebook">
<set>description</set>
</else>
</calculate>
<calculate type="string" value="action">
<if type="pilot_expenses">
<set>Material</set>
</if>
<else type="pilot_datebook">
<set>Hours</set>
</else>
</calculate>
<calculate type="string" value="notes">
<if type="pilot-expenses">
<set>expense_note</set>
</if>
<else type="pilot_datebook">
<set>note</set>
</else>
</calculate>
<calculate type="guid" value="bill-to"/>
<calculate type="boolean" value="invoice-taxable"/>
<calculate type="boolean" value="bill-taxable"/>
<calculate type="boolean" value="billable?"/>
<calculate type="boolean" value="bill-tax-included"/>
<calculate type="boolean" value="invoice-tax-included">
<set>tax_included</set>
</calculate>
<calculate type="numeric" value="iprice">
<if type="string" value="expense_type">
<equals type="string" value="Mileage">
<set>mileage_rate</set>
</equals>
</if>
</calculate>
<calculate type="numeric" value="bprice"/>
<calculate type="numeric" value="qty">
<if type="pilot_datebook">
<set>end_time-start_time</set>
</if>
<else type="pilot_expenses">
<set>expense_amount</set>
</else>
</calculate>
<calculate type="numeric" value="invoice-discount">
<set>0/1</set>
</calculate>
<calculate type="date" value="date-entered">
<set>qsf_time_now</set>
</calculate>
<calculate type="date" value="date">
<set>qsf_enquiry_date</set>
</calculate>
<calculate type="gint32" value="discount-type">
<set option="amount_type_enum">GNC_AMT_TYPE_PERCENT</set>
</calculate>
<calculate type="gint32" value="discount-method">
<set>2</set>
</calculate>
<calculate type="gint32" value="bill-payment-type"/>
</object>
<object type="Trans">
<calculate type="string" value="num"/>
<calculate type="string" value="desc"/>
<calculate type="date" value="date-entered"/>
<calculate type="date" value="date-posted"/>
<calculate type="date" value="date-due"/>
<calculate type="string" value="notes"/>
</object>
<object type="gncInvoice">
<calculate type="string" value="id"/>
<calculate type="string" value="billing_id"/>
<calculate type="string" value="notes"/>
<calculate type="guid" value="invoice_owner"/>
<calculate type="guid" value="account"/>
<calculate type="guid" value="posted_txn"/>
<calculate type="guid" value="posted_lot"/>
<calculate type="guid" value="terms"/>
<calculate type="guid" value="bill-to"/>
<calculate type="boolean" value="active">
<set>true</set>
</calculate>
<calculate type="date" value="date_opened">
<set>qsf_enquiry_date</set>
</calculate>
<calculate type="date" value="date_posted"/>
</object>
</qsf-map>

View File

@ -1,103 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<qsf-map xmlns="http://qof.sourceforge.net/">
<definition qof_version="3">
<define e_type="pilot_address"/>
<define e_type="gncBillTerm"/>
<define e_type="gncCustomer"/>
<define e_type="gncAddress" foreach="true"/>
</definition>
<object type="gncCustomer">
<calculate type="string" value="id"/>
<calculate type="string" value="notes">
<set>entryNote</set>
</calculate>
<calculate type="string" value="name">
<set>entryCompany</set>
</calculate>
<calculate type="guid" value="guid"/>
<calculate type="guid" value="addr">
<set>guid</set>
</calculate>
<calculate type="guid" value="customer_terms"/>
<calculate type="guid" value="shipaddr"/>
<calculate type="boolean" value="active">
<set>true</set>
</calculate>
<calculate type="boolean" value="tax table override"/>
<calculate type="numeric" value="amount of discount"/>
<calculate type="numeric" value="amount of credit"/>
</object>
<object type="gncAddress">
<calculate type="string" value="city">
<set>entryZip</set>
</calculate>
<calculate type="string" value="street">
<set>entryAddress</set>
</calculate>
<calculate type="string" value="fax">
<set>entryPhone2</set>
</calculate>
<calculate type="string" value="number"/>
<calculate type="string" value="name">
<set>entryFirstname</set>
<set>entryLastname</set>
</calculate>
<calculate type="string" value="email">
<set>entryPhone5</set>
</calculate>
<calculate type="string" value="locality">
<set>entryState</set>
</calculate>
<calculate type="string" value="phone">
<set>entryPhone1</set>
</calculate>
<calculate type="guid" value="guid"/>
<calculate type="guid" value="owner">
<set>guid</set>
</calculate>
</object>
<object type="gncBillTerm">
<calculate type="string" value="description"/>
<calculate type="string" value="name"/>
<calculate type="string" value="bill type"/>
<calculate type="guid" value="guid"/>
<calculate type="numeric" value="amount of discount"/>
<calculate type="gint32" value="cut off"/>
<calculate type="gint32" value="number of days due"/>
<calculate type="gint32" value="number of discounted days"/>
</object>
<object type="pilot_address">
<calculate type="string" value="entryCity">
<set object="gncAddress">city</set>
</calculate>
<calculate type="string" value="entryCustom4"/>
<calculate type="string" value="entryPhone1">
<set object="gncAddress">phone</set>
</calculate>
<calculate type="string" value="entryZip"/>
<calculate type="string" value="entryLastname">
<set object="gncAddress">name</set>
</calculate>
<calculate type="string" value="entryPhone2"/>
<calculate type="string" value="entryNote"/>
<calculate type="string" value="category"/>
<calculate type="string" value="entryFirstname"/>
<calculate type="string" value="entryPhone3"/>
<calculate type="string" value="entryTitle"/>
<calculate type="string" value="entryPhone4"/>
<calculate type="string" value="entryCompany">
<set object="gncCustomer">name</set>
</calculate>
<calculate type="string" value="entryPhone5"/>
<calculate type="string" value="entryState">
<set object="gncAddress">locality</set>
</calculate>
<calculate type="string" value="entryCustom1"/>
<calculate type="string" value="entryAddress">
<set object="gncAddress">number</set>
</calculate>
<calculate type="string" value="entryCustom2"/>
<calculate type="string" value="entryCountry"/>
<calculate type="string" valuee="entryCustom3"/>
</object>
</qsf-map>

View File

@ -1,204 +0,0 @@
/***************************************************************************
* qof-backend-qsf.h
*
* Sat Jun 11 19:34:36 2005
* Copyright 2005 Neil Williams
* linux@codehelp.co.uk
****************************************************************************/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/** @addtogroup Backend
@{ */
/** @addtogroup QSF QOF Serialisation Format
This is the public interface of the qof-backend-qsf library.
QSF - QOF Serialization Format is an XML serialization format
i.e. it lays out a QOF object in a series of XML tags. The format will
consist of two component formats:
qof-qsf for the QSF object data and
qsf-map to map sets of QSF objects between QOF applications.
Maps exist to allow complex conversions between objects where object parameters
need to be calculated, combined or processed using conditionals. Some QSF objects
can be converted using XSL or other standard tools. For more information on maps,
see http://code.neil.williamsleesmill.me.uk/map.html
QSF object files will contain user data and are to be exported from QOF applications
under user control or they can be hand-edited. QSF maps contain application data and
can be created by application developers from application source code. Tools may be
created later to generate maps interactively but maps require application support as
well as an understanding of QOF objects in the import and output applications and are
intended to remain within the scope of application developers rather than users.
A QSF file written by one QOF application will need an appropriate QSF map before the
data can be accessed by a different application using QOF. Any QSF objects that are
not defined in the map will be ignored. QSF files written and accessed by the same
application can use maps if necessary or can simply import the QSF data as a whole.
Unless specifically mentioned otherwise, all defined strings are case-sensitive.
Full documentation of this format is at:
http://code.neil.williamsleesmill.me.uk/qsf.html
QSF itself is now being built into the QOF library for use with pilot-link to allow
Palm objects to be described in QOF, written to XML as QSF and imported directly into
GnuCash and other QOF-compliant applications. As a generic format, it does not depend
on any pre-defined objects. Instead, QSF is a simple container for all QOF objects.
QSF grew from the qof_book_merge code base and uses the qof_book_merge code that is now
part of QOF. Any QofBook generated by QSF still needs to be merged into the existing
application data using qof_book_merge. See ::BookMerge.
QSF can be used as an export or offline storage format for QOF applications (although
long term storage may be best performed using separate (non-XML) methods, depending
on the application).
QSF is designed to cope with partial QofBooks at the QofObject level. There is no
requirement for specific objects to always be defined, as long as each QOF object
specified is fully defined, no orphan or missing parameters are allowed. Part of the
handling for partial books requires a storage mechanism for references to entities
that are not within reach of the current book. This requires a little extra coding
in the QSF QofBackend to contain the reference data so that when the book is
written out, the reference can be included. When the file is imported back in, a
little extra code then rebuilds those references during the merge.
Copying entites from an existing QofBook using the qof_instance_copy routines will
automatically create the reference table. If your QOF objects use references to other
entities, books that are created manually also need to create a reference table.
Work is continuing on supporting QSF in GnuCash and QOF. QSF is a very open format -
the majority of the work will be in standardising object types and creating maps that
convert between objects. Applications that read QSF should ignore any objects that do
not match the available maps and warn the user about missing data.
Anyone is free to create their own QSF objects, subject to the GNU GPL. It is intended
that QSF can be used as the flexible, open and free format for QOF data - providing
all that is lacking from a typical CSV export with all the validation benefits of XML
and the complex data handling of QOF. The QSF object and map formats remain under the
GNU GPL licence and QSF is free software.
\todo
- Adding more map support, some parts of the map are still not coded. equals,
variables and the conditional logic may not be up to the task of the
datebook repetition calculations.
QSF is in three sections:
- QSF Backend : a QofBackend for file:/ QSF objects and maps.
qsf-backend.c
- QSF Object : Input, export and validation of QSF object files.
qsf-xml.c
- QSF Map : Validation, processing and conversion routines.
qsf-xml-map.c
To work with QSF, your QOF objects must have:
- a create: function in the QofObject definition
- a foreach: function in the QofObject definition
- QofParam params[] registered with QOF using
qof_class_register and containing all necessary parameters
to reconstruct this object without any further information.
- Logical distinction between those parameters that should be
set (have a QofAccessFunc and QofSetterFunc) and those that
should only be calculated (only a QofAccessFunc).
If you begin your QSF session with ::QOF_STDOUT as the book_id,
QSF will write to STDOUT - usually a terminal. This is used by QOF
applications to provide data streaming. If you don't want terminal
output, take care to check the path given to
::qof_session_begin - don't try to change it later!
The XML is validated against the QSF object schema before being
written (to file or stdout).
Check the QofBackendError - don't assume the file is OK.
@{ */
/** @file qof-backend-qsf.h
@brief QSF API - Backend, maps, objects and configuration.
@author Copyright (C) 2004-2005 Neil Williams <linux@codehelp.co.uk>
*/
#ifndef _QOF_BACKEND_QSF_H
#define _QOF_BACKEND_QSF_H
#include <gmodule.h>
#include "qoflog.h"
#include "qofbackend.h"
/** \brief Describe this backend to the application.
Sets QSF Backend Version 0.2, access method = file:
The version number only changes if:
-# The map schema is modified, or
-# The object schema is modified, or
-# The QofBackendProvider struct is modified in QOF to
support new members and QSF can support the new function, or
-# The QofBackendOption settings are modified.
v0.2 introduces the QSF_MAP_FILES QofBackendOption.
Initialises the backend and provides access to the
functions that will load and save the data. Initialises
default values for the QofBackendOption KvpFrame.
*/
G_MODULE_EXPORT void
qof_backend_module_init(void);
/** \name Supported backend configurations
@{
*/
/** \brief compression level
\b Type: gint (KVP_TYPE_GINT64)
Use GINT_TO_POINTER() to set a integer value between 0 and 9.
*/
#define QSF_COMPRESS "compression_level"
/** \brief selected QSF maps
\b Type: GList* (KVP_TYPE_GLIST) of const char* (QOF_TYPE_STRING) values.
Defaults to the pre-installed QSF map(s) but may be overridden
by the application to pass full path(s) of user selected
QSF map file(s).
If you override the list, it is advisable to only specify the single
map file for this QSF object to reduce the amount of error checking
required within the backend. Simply reset the QofBackendOption before
another file is to be opened. It is up to the application to decide how
to offer multiple map selections to the user.
*/
#define QSF_MAP_FILES "selected_map_files"
/** \brief Encoding string.
*
* Defaults of UTF-8. */
#define QSF_ENCODING "encoding_string"
/** @} */
/** @} */
/** @} */
#endif /* _QOF_BACKEND_QSF_H */

File diff suppressed because it is too large Load Diff

View File

@ -1,27 +0,0 @@
/***************************************************************************
* qsf-xml.h.in
*
* Mon Dec 20 20:27:48 2004
* Copyright 2004 Neil Williams
* linux@codehelp.co.uk
****************************************************************************/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
#define QSF_SCHEMA_DIR "@-QSF_SCHEMA_DIR-@"

View File

@ -1,117 +0,0 @@
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://qof.sourceforge.net/"
xmlns:qsf-map="http://qof.sourceforge.net/">
<xsd:annotation>
<xsd:documentation xml:lang="en">
Query Object Framework Serialization Format (QSF)
Copyright 2004 Neil Williams linux@codehelp.co.uk
QSF is part of QOF.
QOF is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 2 of the License, or (at your
option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
</xsd:documentation>
</xsd:annotation>
<xsd:element name="qsf-map" qsf-map:type="map-type"/>
<xsd:complexType name="map-type">
<xsd:sequence>
<xsd:element name="definition" qsf-map:type="qsfdefinition"/>
<xsd:element name="object" qsf-map:type="mapobject" minOccurs="1" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="qsfdefinition">
<xsd:sequence>
<xsd:element name="define" qsf-map:type="qsfdefine" minOccurs="2" maxOccurs="unbounded"/>
<xsd:element name="default" qsf-map:type="qsfdefault" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="variable" qsf-map:type="qsfvariable" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attribute name="qof_version" type="xsd:positiveInteger"/>
</xsd:complexType>
<xsd:complexType name="qsfdefine">
<xsd:simpleContent>
<xsd:extension base="xsd:string">
<xsd:attribute name="e_type" type="xsd:string"/>
<xsd:attribute name="foreach" type="xsd:string" use="optional"/>
</xsd:extension>
</xsd:simpleContent>
</xsd:complexType>
<xsd:complexType name="qsfdefault">
<xsd:simpleContent>
<xsd:extension base="xsd:string">
<xsd:attribute name="name" type="xsd:string"/>
<xsd:attribute name="type" type="xsd:string"/>
<xsd:attribute name="value" type="xsd:string"/>
</xsd:extension>
</xsd:simpleContent>
</xsd:complexType>
<xsd:complexType name="qsfvariable">
<xsd:simpleContent>
<xsd:extension base="xsd:string">
<xsd:attribute name="name" type="xsd:string"/>
<xsd:attribute name="type" type="xsd:string"/>
<xsd:attribute name="value" type="xsd:string"/>
</xsd:extension>
</xsd:simpleContent>
</xsd:complexType>
<xsd:complexType name="mapobject">
<xsd:sequence>
<xsd:element name="calculate" qsf-map:type="qsfcalculate" minOccurs="1" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attribute name="type" type="xsd:string"/>
<xsd:attribute name="value" type="xsd:string"/>
</xsd:complexType>
<xsd:complexType name="qsfcalculate">
<xsd:sequence>
<xsd:element name="set" qsf-map:type="qsf_set" minOccurs="0" maxOccurs="1"/>
<xsd:element name="if" qsf-map:type="qsf_if" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="equals" qsf-map:type="qsf_equal" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="else" qsf-map:type="qsf_else" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attribute name="type" type="xsd:string"/>
<xsd:attribute name="value" type="xsd:string"/>
</xsd:complexType>
<xsd:complexType name="qsf_set">
<xsd:simpleContent>
<xsd:extension base="xsd:string">
<xsd:attribute name="option" type="xsd:string" use="optional"/>
<xsd:attribute name="format" type="xsd:string" use="optional"/>
<xsd:attribute name="object" type="xsd:string" use="optional"/>
</xsd:extension>
</xsd:simpleContent>
</xsd:complexType>
<xsd:complexType name="qsf_if">
<xsd:sequence>
<xsd:element name="set" qsf-map:type="qsf_set" minOccurs="0" maxOccurs="1"/>
<xsd:element name="equals" qsf-map:type="qsf_equal" minOccurs="0" maxOccurs="1"/>
</xsd:sequence>
<xsd:attribute name="boolean" type="xsd:string" use="optional"/>
<xsd:attribute name="type" type="xsd:string" use="optional"/>
<xsd:attribute name="value" type="xsd:string" use="optional"/>
<xsd:attribute name="string" type="xsd:string" use="optional"/>
</xsd:complexType>
<xsd:complexType name="qsf_else">
<xsd:sequence>
<xsd:element name="set" qsf-map:type="qsf_set" minOccurs="0" maxOccurs="1"/>
<xsd:element name="equals" qsf-map:type="qsf_equal" minOccurs="0" maxOccurs="1"/>
</xsd:sequence>
<xsd:attribute name="type" type="xsd:string"/>
<xsd:attribute name="value" type="xsd:string"/>
</xsd:complexType>
<xsd:complexType name="qsf_equal">
<xsd:sequence>
<xsd:element name="set" qsf-map:type="qsf_set" minOccurs="0" maxOccurs="1"/>
<xsd:element name="equals" qsf-map:type="qsf_equal" minOccurs="0" maxOccurs="1"/>
</xsd:sequence>
<xsd:attribute name="type" type="xsd:string"/>
<xsd:attribute name="value" type="xsd:string"/>
</xsd:complexType>
</xsd:schema>

View File

@ -1,154 +0,0 @@
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://qof.sourceforge.net/"
xmlns:qof-qsf="http://qof.sourceforge.net/"
elementFormDefault="qualified"
attributeFormDefault="unqualified" xml:lang="en-GB">
<xsd:annotation>
<xsd:documentation xml:lang="en">
Query Object Framework Serialization Format (QSF)
Copyright 2004-5 Neil Williams linux@codehelp.co.uk
QSF is part of QOF.
QOF is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 2 of the License, or (at your
option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
</xsd:documentation>
</xsd:annotation>
<xsd:element name="qof-qsf" type="qof-qsf:qsftype"/>
<xsd:complexType name="qsftype">
<xsd:sequence>
<xsd:element name="book" type="qof-qsf:qofbook" minOccurs="1" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="qofbook">
<xsd:sequence>
<xsd:element name="book-guid" type="xsd:string" minOccurs="1" maxOccurs="1"/>
<xsd:element name="object" type="qof-qsf:qsfobject" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attribute name="count" type="xsd:positiveInteger" use="optional"/>
</xsd:complexType>
<xsd:complexType name="qsfobject">
<xsd:sequence>
<xsd:element name="string" minOccurs="0" maxOccurs="unbounded">
<xsd:complexType>
<xsd:simpleContent>
<xsd:extension base="xsd:string">
<xsd:attribute name="type" type="xsd:string" use="required"/>
</xsd:extension>
</xsd:simpleContent>
</xsd:complexType>
</xsd:element>
<xsd:element name="guid" minOccurs="0" maxOccurs="unbounded">
<xsd:complexType>
<xsd:simpleContent>
<xsd:extension base="xsd:hexBinary">
<xsd:attribute name="type" type="xsd:string" use="required"/>
</xsd:extension>
</xsd:simpleContent>
</xsd:complexType>
</xsd:element>
<xsd:element name="boolean" minOccurs="0" maxOccurs="unbounded">
<xsd:complexType>
<xsd:simpleContent>
<xsd:extension base="xsd:boolean">
<xsd:attribute name="type" type="xsd:string" use="required"/>
</xsd:extension>
</xsd:simpleContent>
</xsd:complexType>
</xsd:element>
<xsd:element name="numeric" minOccurs="0" maxOccurs="unbounded">
<xsd:complexType>
<xsd:simpleContent>
<xsd:extension base="xsd:string">
<xsd:attribute name="type" type="xsd:string" use="required"/>
</xsd:extension>
</xsd:simpleContent>
</xsd:complexType>
</xsd:element>
<xsd:element name="date" minOccurs="0" maxOccurs="unbounded" >
<xsd:complexType>
<xsd:simpleContent>
<xsd:extension base="xsd:dateTime">
<xsd:attribute name="type" type="xsd:string" use="required"/>
</xsd:extension>
</xsd:simpleContent>
</xsd:complexType>
</xsd:element>
<xsd:element name="gint32" minOccurs="0" maxOccurs="unbounded">
<xsd:complexType>
<xsd:simpleContent>
<xsd:extension base="xsd:int">
<xsd:attribute name="type" type="xsd:string" use="required"/>
</xsd:extension>
</xsd:simpleContent>
</xsd:complexType>
</xsd:element>
<xsd:element name="gint64" minOccurs="0" maxOccurs="unbounded">
<xsd:complexType>
<xsd:simpleContent>
<xsd:extension base="xsd:long">
<xsd:attribute name="type" type="xsd:string" use="required"/>
</xsd:extension>
</xsd:simpleContent>
</xsd:complexType>
</xsd:element>
<xsd:element name="double" minOccurs="0" maxOccurs="unbounded">
<xsd:complexType>
<xsd:simpleContent>
<xsd:extension base="xsd:double">
<xsd:attribute name="type" type="xsd:string" use="required"/>
</xsd:extension>
</xsd:simpleContent>
</xsd:complexType>
</xsd:element>
<xsd:element name="character" minOccurs="0" maxOccurs="unbounded">
<xsd:complexType>
<xsd:simpleContent>
<xsd:extension base="xsd:string">
<xsd:attribute name="type" type="xsd:string" use="required"/>
</xsd:extension>
</xsd:simpleContent>
</xsd:complexType>
</xsd:element>
<xsd:element name="kvp" minOccurs="0" maxOccurs="unbounded">
<xsd:complexType>
<xsd:simpleContent>
<xsd:extension base="xsd:string">
<xsd:attribute name="type" type="xsd:string" use="required"/>
<xsd:attribute name="path" type="xsd:string" use="required"/>
<xsd:attribute name="value" type="xsd:string" use="required"/>
</xsd:extension>
</xsd:simpleContent>
</xsd:complexType>
</xsd:element>
<xsd:element name="collection" minOccurs="0" maxOccurs="unbounded">
<xsd:complexType>
<xsd:simpleContent>
<xsd:extension base="xsd:hexBinary">
<xsd:attribute name="type" type="xsd:string" use="required"/>
</xsd:extension>
</xsd:simpleContent>
</xsd:complexType>
</xsd:element>
<xsd:element name="choice" minOccurs="0" maxOccurs="unbounded">
<xsd:complexType>
<xsd:simpleContent>
<xsd:extension base="xsd:hexBinary">
<xsd:attribute name="type" type="xsd:string" use="required"/>
<xsd:attribute name="name" type="xsd:string" use="required"/>
</xsd:extension>
</xsd:simpleContent>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
<xsd:attribute name="type" type="xsd:string" use="required"/>
<xsd:attribute name="count" type="xsd:positiveInteger" use="optional"/>
</xsd:complexType>
</xsd:schema>

View File

@ -1,901 +0,0 @@
/***************************************************************************
* qsf-xml-map.c
*
* Sat Jan 1 07:31:55 2005
* Copyright 2005-2006 Neil Williams
* linux@codehelp.co.uk
****************************************************************************/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "config.h"
#include <glib.h>
#include <libxml/xmlversion.h>
#include <libxml/xmlmemory.h>
#include <libxml/tree.h>
#include <libxml/parser.h>
#include <libxml/xmlschemas.h>
#include "qof.h"
#include "qof-backend-qsf.h"
#include "qsf-xml.h"
#include "qsf-dir.h"
#ifndef HAVE_STRPTIME
#include "strptime.h"
#endif
static QofLogModule log_module = QOF_MOD_QSF;
static void
qsf_date_default_handler(const gchar *default_name, GHashTable *qsf_default_hash,
xmlNodePtr parent_tag, xmlNodePtr import_node, xmlNsPtr ns)
{
xmlNodePtr output_parent;
time_t *qsf_time;
gchar date_as_string[QSF_DATE_LENGTH];
output_parent = xmlAddChild(parent_tag, xmlNewNode(ns,
xmlGetProp(import_node, BAD_CAST QSF_OBJECT_TYPE)));
xmlNewProp(output_parent, BAD_CAST QSF_OBJECT_TYPE,
xmlGetProp(import_node, BAD_CAST MAP_VALUE_ATTR));
qsf_time = (time_t*)g_hash_table_lookup(qsf_default_hash, default_name);
qof_strftime(date_as_string, QSF_DATE_LENGTH, QSF_XSD_TIME, gmtime(qsf_time));
xmlNodeAddContent(output_parent, BAD_CAST date_as_string);
}
static void
qsf_string_default_handler(const gchar *default_name, GHashTable *qsf_default_hash,
xmlNodePtr parent_tag, xmlNodePtr import_node, xmlNsPtr ns)
{
xmlNodePtr node;
xmlChar *output;
node = xmlAddChild(parent_tag,
xmlNewNode(ns, xmlGetProp(import_node, BAD_CAST QSF_OBJECT_TYPE)));
xmlNewProp(node, BAD_CAST QSF_OBJECT_TYPE,
xmlGetProp(import_node, BAD_CAST MAP_VALUE_ATTR));
output = (xmlChar *)g_hash_table_lookup(qsf_default_hash, default_name);
xmlNodeAddContent(node, output);
}
static void
qsf_map_validation_handler(xmlNodePtr child, xmlNsPtr ns, qsf_validator *valid)
{
xmlChar *qof_version, *match;
GString *buff;
xmlNodePtr child_node;
xmlChar *obj_type;
if (qsf_is_element(child, ns, MAP_DEFINITION_TAG))
{
qof_version = xmlGetProp(child, BAD_CAST MAP_QOF_VERSION);
buff = g_string_new(" ");
g_string_printf(buff, "%i", QSF_QOF_VERSION);
if (xmlStrcmp(qof_version, BAD_CAST buff->str) != 0)
{
valid->error_state = ERR_QSF_BAD_QOF_VERSION;
return;
}
for (child_node = child->children; child_node != NULL;
child_node = child_node->next)
{
if (qsf_is_element(child_node, ns, MAP_DEFINE_TAG))
{
g_hash_table_insert(valid->validation_table,
xmlGetProp(child_node, BAD_CAST MAP_E_TYPE),
xmlNodeGetContent(child_node));
}
}
}
if (qsf_is_element(child, ns, MAP_OBJECT_TAG))
{
match = NULL;
obj_type = xmlGetProp(child, BAD_CAST MAP_TYPE_ATTR);
match = BAD_CAST g_hash_table_lookup( valid->validation_table, obj_type);
if (match)
{
valid->map_calculated_count++;
if (TRUE == qof_class_is_registered((QofIdTypeConst) obj_type))
{
valid->qof_registered_count++;
PINFO (" %s is to be calculated", obj_type);
}
else
{
PINFO (" %s to be mapped", obj_type);
}
}
}
}
gboolean is_qsf_object_with_map_be(gchar *map_file, qsf_param *params)
{
xmlDocPtr doc, map_doc;
gint valid_count, calc_count;
struct qsf_node_iterate iter;
xmlNodePtr map_root, object_root;
xmlNsPtr map_ns;
qsf_validator valid;
gchar *path;
gchar *map_path;
g_return_val_if_fail((params != NULL), FALSE);
PINFO (" mapfile=%s", map_file);
path = g_strdup(params->filepath);
map_path = g_strdup_printf("%s/%s", QSF_SCHEMA_DIR, map_file);
if (path == NULL)
{
qof_backend_set_error(params->be, ERR_FILEIO_FILE_NOT_FOUND);
return FALSE;
}
doc = xmlParseFile(path);
if (doc == NULL)
{
qof_backend_set_error(params->be, ERR_FILEIO_PARSE_ERROR);
return FALSE;
}
if (TRUE != qsf_is_valid(QSF_SCHEMA_DIR, QSF_OBJECT_SCHEMA, doc))
{
qof_backend_set_error(params->be, ERR_QSF_INVALID_OBJ);
return FALSE;
}
object_root = xmlDocGetRootElement(doc);
if (map_path == NULL)
{
qof_backend_set_error(params->be, ERR_FILEIO_FILE_NOT_FOUND);
return FALSE;
}
valid.validation_table = g_hash_table_new(g_str_hash, g_str_equal);
map_doc = xmlParseFile(map_path);
if (map_doc == NULL)
{
qof_backend_set_error(params->be, ERR_FILEIO_PARSE_ERROR);
return FALSE;
}
if (TRUE != qsf_is_valid(QSF_SCHEMA_DIR, QSF_MAP_SCHEMA, map_doc))
{
qof_backend_set_error(params->be, ERR_QSF_INVALID_MAP);
return FALSE;
}
map_root = xmlDocGetRootElement(map_doc);
valid.map_calculated_count = 0;
valid.valid_object_count = 0;
valid.qof_registered_count = 0;
valid.error_state = ERR_BACKEND_NO_ERR;
map_ns = map_root->ns;
iter.ns = object_root->ns;
qsf_valid_foreach(object_root, qsf_object_validation_handler, &iter, &valid);
iter.ns = map_ns;
qsf_valid_foreach(map_root, qsf_map_validation_handler, &iter, &valid);
if (valid.error_state != ERR_BACKEND_NO_ERR)
{
qof_backend_set_error(params->be, valid.error_state);
g_hash_table_destroy(valid.validation_table);
return FALSE;
}
/* check all counted objects are valid */
/* Should be:
the same number of valid object calculations as there are defined in the map.
And the number of calculations must match the number of unregistered
objects plus the number of registered objects defined. */
valid_count = g_hash_table_size(valid.validation_table) - valid.map_calculated_count;
calc_count = valid.map_calculated_count -
(valid.valid_object_count + valid.qof_registered_count);
if (valid_count == 0 && calc_count == 0)
{
g_hash_table_destroy(valid.validation_table);
qof_backend_get_error(params->be);
return TRUE;
}
qof_backend_set_error(params->be, ERR_QSF_WRONG_MAP);
/* the object is OK, only the map is wrong. */
PINFO (" Map is wrong. map:%d object:%d reg:%d size:%d result:%d",
valid.map_calculated_count, valid.valid_object_count,
valid.qof_registered_count,
g_hash_table_size(valid.validation_table), valid_count);
if (valid_count != 0)
{
PINFO (" size - map != 0. actual: %d.", valid_count);
}
if (calc_count != 0)
{
PINFO (" map - (object + registered) != 0. Actual: %d.", calc_count);
}
g_hash_table_destroy(valid.validation_table);
return TRUE;
}
gboolean is_qsf_object_with_map(const gchar *path, gchar *map_file)
{
xmlDocPtr doc, map_doc;
gint valid_count;
struct qsf_node_iterate iter;
xmlNodePtr map_root, object_root;
xmlNsPtr map_ns;
qsf_validator valid;
gchar *map_path;
map_path = g_strdup_printf("%s/%s", QSF_SCHEMA_DIR, map_file);
if (path == NULL)
{
return FALSE;
}
doc = xmlParseFile(path);
if (doc == NULL)
{
return FALSE;
}
if (TRUE != qsf_is_valid(QSF_SCHEMA_DIR, QSF_OBJECT_SCHEMA, doc))
{
return FALSE;
}
object_root = xmlDocGetRootElement(doc);
if (map_path == NULL)
{
return FALSE;
}
valid.validation_table = g_hash_table_new(g_str_hash, g_str_equal);
map_doc = xmlParseFile(map_path);
if (map_doc == NULL)
{
return FALSE;
}
if (TRUE != qsf_is_valid(QSF_SCHEMA_DIR, QSF_MAP_SCHEMA, map_doc))
{
return FALSE;
}
map_root = xmlDocGetRootElement(map_doc);
valid.map_calculated_count = 0;
valid.valid_object_count = 0;
valid.error_state = ERR_BACKEND_NO_ERR;
map_ns = map_root->ns;
iter.ns = map_ns;
qsf_valid_foreach(map_root, qsf_map_validation_handler, &iter, &valid);
iter.ns = object_root->ns;
qsf_valid_foreach(object_root, qsf_object_validation_handler, &iter, &valid);
if (valid.error_state != ERR_BACKEND_NO_ERR)
{
g_hash_table_destroy(valid.validation_table);
return FALSE;
}
valid_count = 0 - g_hash_table_size(valid.validation_table);
valid_count += valid.map_calculated_count;
valid_count += valid.valid_object_count;
g_hash_table_destroy(valid.validation_table);
if (valid_count == 0)
{
return TRUE;
}
return FALSE;
}
gboolean is_qsf_map_be(qsf_param *params)
{
xmlDocPtr doc;
struct qsf_node_iterate iter;
qsf_validator valid;
xmlNodePtr map_root;
xmlNsPtr map_ns;
gchar *path;
g_return_val_if_fail((params != NULL), FALSE);
qof_backend_get_error(params->be);
path = g_strdup(params->filepath);
if (path == NULL)
{
qof_backend_set_error(params->be, ERR_FILEIO_FILE_NOT_FOUND);
return FALSE;
}
doc = xmlParseFile(path);
if (doc == NULL)
{
qof_backend_set_error(params->be, ERR_FILEIO_PARSE_ERROR);
return FALSE;
}
if (TRUE != qsf_is_valid(QSF_SCHEMA_DIR, QSF_MAP_SCHEMA, doc))
{
qof_backend_set_error(params->be, ERR_QSF_INVALID_MAP);
return FALSE;
}
map_root = xmlDocGetRootElement(doc);
map_ns = map_root->ns;
iter.ns = map_ns;
valid.validation_table = g_hash_table_new(g_str_hash, g_str_equal);
valid.error_state = ERR_BACKEND_NO_ERR;
qsf_valid_foreach(map_root, qsf_map_validation_handler, &iter, &valid);
if (valid.error_state != ERR_BACKEND_NO_ERR)
{
qof_backend_set_error(params->be, valid.error_state);
g_hash_table_destroy(valid.validation_table);
return FALSE;
}
qof_backend_get_error(params->be);
g_hash_table_destroy(valid.validation_table);
return TRUE;
}
gboolean is_qsf_map(const gchar *path)
{
xmlDocPtr doc;
struct qsf_node_iterate iter;
qsf_validator valid;
xmlNodePtr map_root;
xmlNsPtr map_ns;
g_return_val_if_fail((path != NULL), FALSE);
if (path == NULL)
{
return FALSE;
}
doc = xmlParseFile(path);
if (doc == NULL)
{
return FALSE;
}
if (TRUE != qsf_is_valid(QSF_SCHEMA_DIR, QSF_MAP_SCHEMA, doc))
{
xmlFreeDoc(doc);
return FALSE;
}
map_root = xmlDocGetRootElement(doc);
map_ns = map_root->ns;
iter.ns = map_ns;
valid.error_state = ERR_BACKEND_NO_ERR;
valid.validation_table = g_hash_table_new(g_str_hash, g_str_equal);
qsf_valid_foreach(map_root, qsf_map_validation_handler, &iter, &valid);
if (valid.error_state != ERR_BACKEND_NO_ERR)
{
g_hash_table_destroy(valid.validation_table);
return FALSE;
}
g_hash_table_destroy(valid.validation_table);
return TRUE;
}
static void
qsf_map_default_handler(xmlNodePtr child, xmlNsPtr ns, qsf_param *params )
{
xmlChar *qsf_enum;
gchar* iterate;
g_return_if_fail(params->qsf_define_hash != NULL);
iterate = NULL;
if (qsf_is_element(child, ns, MAP_DEFINE_TAG))
{
iterate = (gchar*)xmlGetProp(child, BAD_CAST MAP_ITERATE_ATTR);
if (qof_util_bool_to_int(iterate) == 1)
{
params->qof_foreach = (QofIdType)xmlGetProp(child, BAD_CAST MAP_E_TYPE);
}
if (NULL == g_hash_table_lookup(params->qsf_define_hash,
xmlGetProp(child, BAD_CAST MAP_E_TYPE)))
{
g_hash_table_insert(params->qsf_define_hash,
xmlGetProp(child, BAD_CAST MAP_E_TYPE), params->child_node);
}
else
{
qof_backend_set_error(params->be, ERR_QSF_BAD_MAP);
PERR (" ERR_QSF_BAD_MAP set");
return;
}
}
if (qsf_is_element(child, ns, MAP_DEFAULT_TAG))
{
if (qsf_strings_equal(xmlGetProp(child, BAD_CAST MAP_TYPE_ATTR), MAP_ENUM_TYPE))
{
qsf_enum = xmlNodeGetContent(child);
/** Use content to discriminate enums in QOF */
PERR (" enum todo incomplete");
/** \todo FIXME: the default enum value is not used
implemented properly or fully handled.
*/
if (NULL == g_hash_table_lookup(params->qsf_default_hash,
xmlNodeGetContent(child)))
{
g_hash_table_insert(params->qsf_default_hash,
xmlNodeGetContent(child), child);
}
else
{
qof_backend_set_error(params->be, ERR_QSF_BAD_MAP);
PERR (" ERR_QSF_BAD_MAP set");
return;
}
}
/** Non-enum defaults */
else
{
if (NULL == g_hash_table_lookup(params->qsf_default_hash,
xmlGetProp(child, BAD_CAST MAP_NAME_ATTR)))
{
g_hash_table_insert(params->qsf_default_hash,
xmlGetProp(child, BAD_CAST MAP_NAME_ATTR), child);
}
else
/* if(0 != xmlHashAddEntry(params->default_map,
xmlGetProp(child_node, MAP_NAME_ATTR), child_node))*/
{
qof_backend_set_error(params->be, ERR_QSF_BAD_MAP);
PERR (" ERR_QSF_BAD_MAP set");
return;
}
}
}
}
static void
qsf_map_top_node_handler(xmlNodePtr child, xmlNsPtr ns, qsf_param *params)
{
xmlChar *qof_version;
GString *buff;
struct qsf_node_iterate iter;
if (!params->qsf_define_hash) return;
if (!params->qsf_default_hash) return;
ENTER (" child=%s", child->name);
if (qsf_is_element(child, ns, MAP_DEFINITION_TAG))
{
qof_version = xmlGetProp(child, BAD_CAST MAP_QOF_VERSION);
buff = g_string_new(" ");
g_string_printf(buff, "%i", QSF_QOF_VERSION);
if (xmlStrcmp(qof_version, BAD_CAST buff->str) != 0)
{
qof_backend_set_error(params->be, ERR_QSF_BAD_QOF_VERSION);
LEAVE (" ERR_QSF_BAD_QOF_VERSION set");
return;
}
iter.ns = ns;
qsf_node_foreach(child, qsf_map_default_handler, &iter, params);
}
LEAVE (" ");
}
static char*
qsf_else_set_value(xmlNodePtr parent, GHashTable *default_hash,
gchar *content, xmlNsPtr map_ns)
{
xmlNodePtr cur_node;
content = NULL;
for (cur_node = parent->children; cur_node != NULL; cur_node = cur_node->next)
{
if (qsf_is_element(cur_node, map_ns, QSF_CONDITIONAL_SET))
{
content = (gchar*)xmlNodeGetContent(cur_node);
return content;
}
}
return NULL;
}
/* Handles the set tag in the map.
This function will be overhauled once inside QOF
QOF hook required for "Lookup in the receiving application"
*/
static gchar*
qsf_set_handler(xmlNodePtr parent, GHashTable *default_hash,
gchar *content, qsf_param *params)
{
xmlNodePtr cur_node, lookup_node;
ENTER (" lookup problem");
content = NULL;
for (cur_node = parent->children; cur_node != NULL; cur_node = cur_node->next)
{
if (qsf_is_element(cur_node, params->map_ns, QSF_CONDITIONAL_SET))
{
content = (gchar*)xmlGetProp(cur_node, BAD_CAST QSF_OPTION);
if (qsf_strings_equal(xmlGetProp(cur_node, BAD_CAST QSF_OPTION), "qsf_lookup_string"))
{
lookup_node = (xmlNodePtr) g_hash_table_lookup(default_hash,
xmlNodeGetContent(cur_node));
content = (gchar*)xmlGetProp(lookup_node, BAD_CAST MAP_VALUE_ATTR);
/** \todo FIXME: do the lookup. type is defined by output object. */
/* Find by name, get GncGUID, return GncGUID as string. */
g_message("Lookup %s in the receiving application\n", content );
LEAVE (" todo");
return content;
}
if (content)
{
lookup_node = (xmlNodePtr) g_hash_table_lookup(default_hash,
xmlNodeGetContent(cur_node));
content = (gchar*)xmlGetProp(lookup_node, BAD_CAST "value");
return content;
}
content = (gchar*)xmlGetProp(parent, BAD_CAST "boolean");
if (!content)
{
/** \todo Check qsf_parameter_hash arguments */
lookup_node = (xmlNodePtr) g_hash_table_lookup(params->qsf_parameter_hash,
xmlGetProp(parent->parent, BAD_CAST MAP_TYPE_ATTR));
if (lookup_node)
{
return (gchar*)xmlNodeGetContent(lookup_node);
}
LEAVE (" check arguments");
return (gchar*)xmlNodeGetContent(cur_node);
}
}
}
LEAVE (" null");
return NULL;
}
static void
qsf_calculate_else(xmlNodePtr param_node, xmlNodePtr child, qsf_param *params)
{
xmlNodePtr export_node;
xmlChar *output_content, *object_data;
if (qsf_is_element(param_node, params->map_ns, QSF_CONDITIONAL_ELSE))
{
if (params->boolean_calculation_done == 0)
{
output_content = object_data = NULL;
output_content = BAD_CAST qsf_set_handler(param_node,
params->qsf_default_hash, (gchar*)output_content, params);
if (output_content == NULL)
{
output_content = xmlGetProp(param_node, BAD_CAST MAP_TYPE_ATTR);
object_data = BAD_CAST qsf_else_set_value(param_node, params->qsf_default_hash,
(gchar*)output_content, params->map_ns);
output_content = BAD_CAST xmlGetProp( (xmlNodePtr) g_hash_table_lookup(
params->qsf_default_hash, object_data), BAD_CAST MAP_VALUE_ATTR);
}
if (object_data != NULL)
{
export_node = (xmlNodePtr) g_hash_table_lookup(
params->qsf_parameter_hash,
xmlGetProp(params->child_node, BAD_CAST QSF_OBJECT_TYPE));
object_data = xmlNodeGetContent(export_node);
}
if (output_content != NULL)
{
object_data = output_content;
}
export_node = xmlAddChild(params->lister, xmlNewNode(params->qsf_ns,
xmlGetProp(child, BAD_CAST QSF_OBJECT_TYPE)));
xmlNewProp(export_node, BAD_CAST QSF_OBJECT_TYPE,
xmlGetProp(child, BAD_CAST MAP_VALUE_ATTR));
xmlNodeAddContent(export_node, object_data);
params->boolean_calculation_done = 1;
}
}
}
static void
qsf_set_format_value(xmlChar *format, gchar *qsf_time_now_as_string,
xmlNodePtr cur_node, qsf_param *params)
{
gint result;
xmlChar *content;
time_t *output;
struct tm *tmp;
time_t tester;
xmlNodePtr kl;
regex_t reg;
/** Comments retained - this behaves a little strangely */
result = 0;
if (format == NULL)
{
return;
}
ENTER (" ");
content = xmlNodeGetContent(cur_node);
output = (time_t*) g_hash_table_lookup(params->qsf_default_hash, content);
if (!output)
{
/** No default time set, use the object time */
/** fill the time structs with temp data */
tester = time(NULL);
tmp = gmtime(&tester);
/** Lookup the object value to read */
/** \todo qsf_parameter_hash check correct arguments */
kl = (xmlNodePtr) g_hash_table_lookup(params->qsf_parameter_hash, content);
if (!kl)
{
LEAVE (" no suitable date set.");
return;
}
/** Read the object value as a dateTime */
strptime((char*)xmlNodeGetContent(kl), QSF_XSD_TIME, tmp);
if (!tmp)
{
LEAVE (" empty date field in QSF object.\n");
return;
}
tester = mktime(tmp);
output = &tester;
}
result = regcomp(&reg, "%[a-zA-Z]", REG_EXTENDED | REG_NOSUB);
result = regexec(&reg, (gchar*)format, (size_t)0, NULL, 0);
if (result == REG_NOMATCH)
{
format = BAD_CAST "%F";
}
regfree(&reg);
/** QSF_DATE_LENGTH preset for all internal and QSF_XSD_TIME string formats.
*/
qof_strftime(qsf_time_now_as_string, QSF_DATE_LENGTH, (char*)format, gmtime(output));
LEAVE (" ok");
}
static void
qsf_boolean_set_value(xmlNodePtr parent, qsf_param *params,
gchar *content, xmlNsPtr map_ns)
{
xmlNodePtr cur_node;
xmlChar *boolean_name;
boolean_name = NULL;
for (cur_node = parent->children; cur_node != NULL; cur_node = cur_node->next)
{
if (qsf_is_element(cur_node, map_ns, QSF_CONDITIONAL_SET))
{
boolean_name = xmlGetProp(cur_node, BAD_CAST QSF_FORMATTING_OPTION);
qsf_set_format_value(boolean_name, content, cur_node, params);
}
}
}
static void
qsf_calculate_conditional(xmlNodePtr param_node, xmlNodePtr child, qsf_param *params)
{
xmlNodePtr export_node;
xmlChar *output_content;
output_content = NULL;
if (qsf_is_element(param_node, params->map_ns, QSF_CONDITIONAL))
{
if (params->boolean_calculation_done == 0)
{
/* set handler */
output_content = BAD_CAST qsf_set_handler(param_node, params->qsf_default_hash,
(gchar*)output_content, params);
/* If the 'if' contains a boolean that has a default value */
if (output_content == NULL)
{
if (NULL != xmlGetProp(param_node, BAD_CAST QSF_BOOLEAN_DEFAULT))
{
output_content = xmlGetProp( (xmlNodePtr) g_hash_table_lookup(
params->qsf_default_hash, xmlGetProp(param_node,
BAD_CAST QSF_BOOLEAN_DEFAULT) ), BAD_CAST MAP_VALUE_ATTR);
}
/* Is the default set to true? */
if ( 0 == qsf_compare_tag_strings(output_content, QSF_XML_BOOLEAN_TEST))
{
qsf_boolean_set_value(param_node, params, (gchar*)output_content, params->map_ns);
export_node = xmlAddChild(params->lister, xmlNewNode(params->qsf_ns,
xmlGetProp(child, BAD_CAST QSF_OBJECT_TYPE)));
xmlNewProp(export_node, BAD_CAST QSF_OBJECT_TYPE,
xmlGetProp(child, BAD_CAST MAP_VALUE_ATTR));
xmlNodeAddContent(export_node, output_content);
params->boolean_calculation_done = 1;
}
}
}
}
}
static void
qsf_add_object_tag(qsf_param *params, gint count)
{
xmlNodePtr extra_node;
GString *str;
xmlChar *property;
str = g_string_new (" ");
g_string_printf(str, "%i", count);
extra_node = NULL;
extra_node = xmlAddChild(params->output_node,
xmlNewNode(params->qsf_ns, BAD_CAST QSF_OBJECT_TAG));
xmlNewProp(extra_node, BAD_CAST QSF_OBJECT_TYPE,
xmlGetProp(params->convert_node, BAD_CAST QSF_OBJECT_TYPE));
property = xmlCharStrdup(str->str);
xmlNewProp(extra_node, BAD_CAST QSF_OBJECT_COUNT, property);
params->lister = extra_node;
}
static gint
identify_source_func(gconstpointer qsf_object, gconstpointer map)
{
return safe_strcmp(((qsf_objects*)qsf_object)->object_type, (QofIdType)map);
}
static void
qsf_map_calculate_output(xmlNodePtr param_node, xmlNodePtr child, qsf_param *params)
{
xmlNodePtr export_node;
xmlChar *output_content;
xmlNodePtr node;
GList *source;
DEBUG (" %s", xmlNodeGetContent(param_node));
output_content = xmlNodeGetContent(param_node);
/* source refers to the source object that provides the data */
source = g_list_find_custom(params->qsf_object_list,
BAD_CAST xmlGetProp(param_node, BAD_CAST MAP_OBJECT_ATTR),
identify_source_func);
if (!source) return;
params->object_set = source->data;
node = g_hash_table_lookup(params->object_set->parameters, output_content);
export_node = xmlAddChild(params->lister, xmlNewNode(params->qsf_ns,
xmlGetProp(child, BAD_CAST QSF_OBJECT_TYPE)));
xmlNewProp(export_node, BAD_CAST QSF_OBJECT_TYPE,
xmlGetProp(child, BAD_CAST MAP_VALUE_ATTR));
if (node)
{
xmlNodeAddContent(export_node, xmlNodeGetContent(node));
}
}
static void
qsf_map_object_handler(xmlNodePtr child, xmlNsPtr ns, qsf_param *params)
{
xmlNodePtr param_node;
xmlNsPtr map_ns, qsf_ns;
gint result;
map_ns = ns;
qsf_ns = params->qsf_ns;
param_node = NULL;
result = 0;
if (child == NULL)
{
return;
}
if (ns == NULL)
{
return;
}
params->boolean_calculation_done = 0;
if (qsf_is_element(child, map_ns, MAP_CALCULATE_TAG))
{
params->boolean_calculation_done = 0;
/* read the child nodes to prepare the calculation. */
for (param_node = child->children; param_node != NULL;
param_node = param_node->next)
{
if (qsf_is_element(param_node, map_ns, QSF_CONDITIONAL_SET))
{
/* Map the pre-defined defaults */
if (0 == qsf_compare_tag_strings(
xmlNodeGetContent(param_node), "qsf_enquiry_date"))
{
qsf_string_default_handler("qsf_enquiry_date",
params->qsf_default_hash, params->lister, child, qsf_ns);
}
if (0 == qsf_compare_tag_strings(
xmlNodeGetContent(param_node), "qsf_time_now"))
{
qsf_date_default_handler("qsf_time_now",
params->qsf_default_hash, params->lister, child, qsf_ns);
}
if (0 == qsf_compare_tag_strings(
xmlNodeGetContent(param_node), "qsf_time_string"))
{
qsf_string_default_handler("qsf_time_string",
params->qsf_default_hash, params->lister, child, qsf_ns);
}
qsf_map_calculate_output(param_node, child, params);
}
qsf_calculate_conditional( param_node, child, params);
qsf_calculate_else(param_node, child, params);
}
/* calculate_map currently not in use */
/* ensure uniqueness of the key before re-instating */
/* result = xmlHashAddEntry2(calculate_map,
xmlGetProp(child_node, MAP_TYPE_ATTR),
xmlGetProp(child_node, MAP_VALUE_ATTR), child_node);
if(result != 0) {
printf("add entry to calculate hash failed. %s\t%s\t%s.\n",
xmlGetProp(child_node, MAP_TYPE_ATTR),
xmlGetProp(child_node, MAP_VALUE_ATTR), child_node->name);
return;
}
is_qsf_object_with_map(path, map_path);
*/
}
}
static void
iterator_cb(xmlNodePtr child, xmlNsPtr ns, qsf_param *params)
{
gchar *object_name;
/* count the number of iterators in the QSF file */
if (qsf_is_element(child, ns, QSF_OBJECT_TAG))
{
object_name = (gchar *)xmlGetProp(child, BAD_CAST QSF_OBJECT_TYPE);
if (0 == safe_strcmp(object_name, params->qof_foreach))
{
params->foreach_limit++;
}
}
}
xmlDocPtr
qsf_object_convert(xmlDocPtr mapDoc, xmlNodePtr qsf_root, qsf_param *params)
{
/* mapDoc : map document. qsf_root: incoming QSF root node. */
struct qsf_node_iterate iter;
xmlDocPtr output_doc;
xmlNode *cur_node;
xmlNode *map_root, *output_root;
g_return_val_if_fail((mapDoc && qsf_root && params), NULL);
ENTER (" root=%s", qsf_root->name);
/* prepare the intermediary document */
iter.ns = params->qsf_ns;
output_doc = xmlNewDoc(BAD_CAST QSF_XML_VERSION);
output_root = xmlNewNode(NULL, BAD_CAST QSF_ROOT_TAG);
xmlDocSetRootElement(output_doc, output_root);
xmlSetNs(output_root, params->qsf_ns);
params->output_node = xmlNewChild(output_root, params->qsf_ns,
BAD_CAST QSF_BOOK_TAG, NULL);
xmlNewProp(params->output_node, BAD_CAST QSF_BOOK_COUNT, BAD_CAST "1");
/* parse the incoming QSF */
qsf_book_node_handler(qsf_root->children->next, params->qsf_ns, params);
/* parse the map and calculate the values */
map_root = xmlDocGetRootElement(mapDoc);
params->foreach_limit = 0;
iter.ns = params->map_ns;
qsf_node_foreach(map_root, qsf_map_top_node_handler, &iter, params);
/* identify the entities of iterator type. */
iter.ns = params->qsf_ns;
qsf_node_foreach(qsf_root->children->next, iterator_cb, &iter, params);
params->count = 0;
for (cur_node = map_root->children; cur_node != NULL; cur_node = cur_node->next)
{
params->convert_node = cur_node;
if (qsf_is_element(cur_node, params->map_ns, MAP_OBJECT_TAG))
{
gint i;
params->lister = NULL;
/* cur_node describes the target object */
if (!qof_class_is_registered(
(QofIdTypeConst)xmlGetProp(cur_node,
BAD_CAST MAP_TYPE_ATTR)))
{
continue;
}
qsf_add_object_tag(params, params->count);
params->count++;
iter.ns = params->map_ns;
for (i = 0; i < params->foreach_limit; i++)
{
qsf_node_foreach(cur_node, qsf_map_object_handler, &iter, params);
params->qsf_object_list = g_list_next(params->qsf_object_list);
qsf_add_object_tag(params, params->count);
params->count++;
}
}
}
params->file_type = OUR_QSF_OBJ;
/* use for debugging */
/* xmlSaveFormatFileEnc("-", output_doc, "UTF-8", 1);*/
LEAVE (" ");
return output_doc;
}

View File

@ -1,413 +0,0 @@
/***************************************************************************
* qsf-xml.c
*
* Fri Nov 26 19:29:47 2004
* Copyright 2004,2005,2006 Neil Williams <linux@codehelp.co.uk>
*
****************************************************************************/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "config.h"
#include <glib.h>
#include <libxml/xmlversion.h>
#include <libxml/xmlmemory.h>
#include <libxml/tree.h>
#include <libxml/parser.h>
#include <libxml/xmlschemas.h>
#include "qof.h"
#include "qof-backend-qsf.h"
#include "qsf-dir.h"
#include "qsf-xml.h"
static QofLogModule log_module = QOF_MOD_QSF;
gint
qsf_compare_tag_strings(const xmlChar *node_name, gchar *tag_name)
{
return xmlStrcmp(node_name, (const xmlChar *)tag_name);
}
gint
qsf_strings_equal(const xmlChar *node_name, gchar *tag_name)
{
if (0 == qsf_compare_tag_strings(node_name, tag_name))
{
return 1;
}
return 0;
}
gint
qsf_is_element(xmlNodePtr a, xmlNsPtr ns, gchar *c)
{
g_return_val_if_fail(a != NULL, 0);
g_return_val_if_fail(ns != NULL, 0);
g_return_val_if_fail(c != NULL, 0);
if ((a->ns == ns) && (a->type == XML_ELEMENT_NODE) &&
qsf_strings_equal(a->name, c))
{
return 1;
}
return 0;
}
gint
qsf_check_tag(qsf_param *params, gchar *qof_type)
{
return qsf_is_element(params->child_node, params->qsf_ns, qof_type);
}
gboolean
qsf_is_valid(const gchar *schema_dir, const gchar* schema_filename, xmlDocPtr doc)
{
xmlSchemaParserCtxtPtr qsf_schema_file;
xmlSchemaPtr qsf_schema;
xmlSchemaValidCtxtPtr qsf_context;
gchar *schema_path;
gint result;
g_return_val_if_fail(doc || schema_filename, FALSE);
schema_path = g_strdup_printf("%s/%s", schema_dir, schema_filename);
qsf_schema_file = xmlSchemaNewParserCtxt(schema_path);
qsf_schema = xmlSchemaParse(qsf_schema_file);
qsf_context = xmlSchemaNewValidCtxt(qsf_schema);
result = xmlSchemaValidateDoc(qsf_context, doc);
xmlSchemaFreeParserCtxt(qsf_schema_file);
xmlSchemaFreeValidCtxt(qsf_context);
xmlSchemaFree(qsf_schema);
g_free(schema_path);
if (result == 0)
{
return TRUE;
}
return FALSE;
}
void
qsf_valid_foreach(xmlNodePtr parent, qsf_validCB cb,
struct qsf_node_iterate *iter, qsf_validator *valid)
{
xmlNodePtr cur_node;
iter->v_fcn = &cb;
for (cur_node = parent->children; cur_node != NULL; cur_node = cur_node->next)
{
cb(cur_node, iter->ns, valid);
}
}
void
qsf_node_foreach(xmlNodePtr parent, qsf_nodeCB cb,
struct qsf_node_iterate *iter, qsf_param *params)
{
xmlNodePtr cur_node;
g_return_if_fail(iter->ns);
iter->fcn = &cb;
for (cur_node = parent->children; cur_node != NULL; cur_node = cur_node->next)
{
cb(cur_node, iter->ns, params);
}
}
void
qsf_object_validation_handler(xmlNodePtr child, xmlNsPtr ns, qsf_validator *valid)
{
xmlNodePtr cur_node;
xmlChar *object_declaration;
guint count;
count = 0;
for (cur_node = child->children; cur_node != NULL;
cur_node = cur_node->next)
{
if (qsf_is_element(cur_node, ns, QSF_OBJECT_TAG))
{
object_declaration = xmlGetProp(cur_node, BAD_CAST QSF_OBJECT_TYPE);
count = g_hash_table_size(valid->validation_table);
g_hash_table_insert(valid->validation_table, object_declaration, xmlNodeGetContent(cur_node));
if (g_hash_table_size(valid->validation_table) > count)
{
valid->valid_object_count++;
if (TRUE == qof_class_is_registered((QofIdTypeConst) object_declaration))
{
valid->qof_registered_count++;
}
}
}
}
}
gboolean is_our_qsf_object(const gchar *path)
{
xmlDocPtr doc;
struct qsf_node_iterate iter;
xmlNodePtr object_root;
qsf_validator valid;
gint table_count;
g_return_val_if_fail((path != NULL), FALSE);
doc = xmlParseFile(path);
if (doc == NULL)
{
return FALSE;
}
if (TRUE != qsf_is_valid(QSF_SCHEMA_DIR, QSF_OBJECT_SCHEMA, doc))
{
PINFO (" validation failed %s %s %s", QSF_SCHEMA_DIR,
QSF_OBJECT_SCHEMA, path);
xmlFreeDoc(doc);
return FALSE;
}
object_root = xmlDocGetRootElement(doc);
/* check that all objects in the file are already registered in QOF */
valid.validation_table = g_hash_table_new(g_str_hash, g_str_equal);
valid.qof_registered_count = 0;
valid.valid_object_count = 0;
iter.ns = object_root->ns;
qsf_valid_foreach(object_root, qsf_object_validation_handler, &iter, &valid);
table_count = g_hash_table_size(valid.validation_table);
g_hash_table_destroy(valid.validation_table);
if (table_count == valid.qof_registered_count)
{
return TRUE;
}
return FALSE;
}
gboolean is_qsf_object(const gchar *path)
{
xmlDocPtr doc;
g_return_val_if_fail((path != NULL), FALSE);
if (path == NULL)
{
return FALSE;
}
doc = xmlParseFile(path);
if (doc == NULL)
{
return FALSE;
}
if (TRUE != qsf_is_valid(QSF_SCHEMA_DIR, QSF_OBJECT_SCHEMA, doc))
{
xmlFreeDoc(doc);
return FALSE;
}
xmlFreeDoc(doc);
/* Note cannot test against a map here, so if the file is valid QSF,
accept it and work out the details later. */
return TRUE;
}
gboolean is_our_qsf_object_be(qsf_param *params)
{
xmlDocPtr doc;
struct qsf_node_iterate iter;
xmlNodePtr object_root;
qsf_validator valid;
gint table_count;
gchar *path;
g_return_val_if_fail((params != NULL), FALSE);
path = g_strdup(params->filepath);
if (path == NULL)
{
qof_backend_set_error(params->be, ERR_FILEIO_FILE_NOT_FOUND);
return FALSE;
}
if (params->file_type != QSF_UNDEF)
{
return FALSE;
}
doc = xmlParseFile(path);
if (doc == NULL)
{
qof_backend_set_error(params->be, ERR_FILEIO_PARSE_ERROR);
return FALSE;
}
if (TRUE != qsf_is_valid(QSF_SCHEMA_DIR, QSF_OBJECT_SCHEMA, doc))
{
qof_backend_set_error(params->be, ERR_QSF_INVALID_OBJ);
return FALSE;
}
params->file_type = IS_QSF_OBJ;
object_root = xmlDocGetRootElement(doc);
valid.validation_table = g_hash_table_new(g_str_hash, g_str_equal);
valid.qof_registered_count = 0;
iter.ns = object_root->ns;
qsf_valid_foreach(object_root, qsf_object_validation_handler, &iter, &valid);
table_count = g_hash_table_size(valid.validation_table);
if (table_count == valid.qof_registered_count)
{
g_hash_table_destroy(valid.validation_table);
qof_backend_set_error(params->be, ERR_BACKEND_NO_ERR);
return TRUE;
}
g_hash_table_destroy(valid.validation_table);
qof_backend_set_error(params->be, ERR_QSF_NO_MAP);
return FALSE;
}
gboolean is_qsf_object_be(qsf_param *params)
{
gboolean result;
xmlDocPtr doc;
GList *maps;
gchar *path;
g_return_val_if_fail((params != NULL), FALSE);
path = g_strdup(params->filepath);
if (path == NULL)
{
qof_backend_set_error(params->be, ERR_FILEIO_FILE_NOT_FOUND);
return FALSE;
}
/* skip validation if is_our_qsf_object has already been called. */
if (ERR_QSF_INVALID_OBJ == qof_backend_get_error(params->be))
{
return FALSE;
}
if (params->file_type == QSF_UNDEF)
{
doc = xmlParseFile(path);
if (doc == NULL)
{
qof_backend_set_error(params->be, ERR_FILEIO_PARSE_ERROR);
return FALSE;
}
if (TRUE != qsf_is_valid(QSF_SCHEMA_DIR, QSF_OBJECT_SCHEMA, doc))
{
qof_backend_set_error(params->be, ERR_QSF_INVALID_OBJ);
return FALSE;
}
}
result = FALSE;
/* retrieve list of maps from config frame. */
for (maps = params->map_files; maps; maps = maps->next)
{
QofBackendError err;
result = is_qsf_object_with_map_be(maps->data, params);
err = qof_backend_get_error(params->be);
if ((err == ERR_BACKEND_NO_ERR) && result)
{
params->map_path = maps->data;
PINFO ("map chosen = %s", params->map_path);
break;
}
/* pop the error back on the stack. */
else
{
qof_backend_set_error(params->be, err);
}
}
return result;
}
static void
qsf_supported_data_types(gpointer type, gpointer user_data)
{
qsf_param *params;
g_return_if_fail(user_data != NULL);
g_return_if_fail(type != NULL);
params = (qsf_param*) user_data;
if (qsf_is_element(params->param_node, params->qsf_ns, (gchar*)type))
{
g_hash_table_insert(params->qsf_parameter_hash,
xmlGetProp(params->param_node, BAD_CAST QSF_OBJECT_TYPE), params->param_node);
}
}
static void
qsf_parameter_handler(xmlNodePtr child, xmlNsPtr qsf_ns, qsf_param *params)
{
params->param_node = child;
g_slist_foreach(params->supported_types, qsf_supported_data_types, params);
}
void
qsf_object_node_handler(xmlNodePtr child, xmlNsPtr qsf_ns, qsf_param *params)
{
struct qsf_node_iterate iter;
qsf_objects *object_set;
gchar *tail, *object_count_s;
gint64 c;
g_return_if_fail(child != NULL);
g_return_if_fail(qsf_ns != NULL);
params->qsf_ns = qsf_ns;
if (qsf_is_element(child, qsf_ns, QSF_OBJECT_TAG))
{
params->qsf_parameter_hash = NULL;
c = 0;
object_set = g_new(qsf_objects, 1);
params->object_set = object_set;
object_set->object_count = 0;
object_set->parameters = g_hash_table_new(g_str_hash, g_str_equal);
object_set->object_type = g_strdup((gchar*)xmlGetProp(child,
BAD_CAST QSF_OBJECT_TYPE));
object_count_s = g_strdup((gchar*)xmlGetProp(child,
BAD_CAST QSF_OBJECT_COUNT));
c = (gint64)strtol(object_count_s, &tail, 0);
object_set->object_count = (gint)c;
g_free(object_count_s);
params->qsf_object_list = g_list_prepend(params->qsf_object_list, object_set);
iter.ns = qsf_ns;
params->qsf_parameter_hash = object_set->parameters;
qsf_node_foreach(child, qsf_parameter_handler, &iter, params);
}
}
void
qsf_book_node_handler(xmlNodePtr child, xmlNsPtr ns, qsf_param *params)
{
gchar *book_count_s, *tail;
gint book_count;
xmlNodePtr child_node;
struct qsf_node_iterate iter;
gchar *buffer;
GncGUID book_guid;
g_return_if_fail(child);
g_return_if_fail(params);
ENTER (" child=%s", child->name);
if (qsf_is_element(child, ns, QSF_BOOK_TAG))
{
book_count_s = (gchar*)xmlGetProp(child, BAD_CAST QSF_BOOK_COUNT);
if (book_count_s)
{
book_count = (gint)strtol(book_count_s, &tail, 0);
/* More than one book not currently supported. */
g_return_if_fail(book_count == 1);
}
iter.ns = ns;
child_node = child->children->next;
if (qsf_is_element(child_node, ns, QSF_BOOK_GUID))
{
DEBUG (" trying to set book GncGUID");
buffer = g_strdup((gchar*)xmlNodeGetContent(child_node));
g_return_if_fail(TRUE == string_to_guid(buffer, &book_guid));
qof_instance_set_guid(QOF_INSTANCE(params->book), &book_guid);
xmlNewChild(params->output_node, params->qsf_ns,
BAD_CAST QSF_BOOK_GUID, BAD_CAST buffer);
g_free(buffer);
}
qsf_node_foreach(child, qsf_object_node_handler, &iter, params);
}
LEAVE (" ");
}

View File

@ -1,702 +0,0 @@
/***************************************************************************
* qsf-xml.h
*
* Fri Nov 26 19:29:47 2004
* Copyright 2004,2005,2006 Neil Williams <linux@codehelp.co.uk>
*
****************************************************************************/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef QSF_XML_H
#define QSF_XML_H
/** @file qsf-xml.h
@brief Private QSF header - not for use by applications
@author Copyright (C) 2004-2005 Neil Williams <linux@codehelp.co.uk>
*/
#include <stdio.h>
#include <stdlib.h>
#include <regex.h>
#include <time.h>
#include "qof.h"
#include <libintl.h>
#define _(String) dgettext (GETTEXT_PACKAGE, String)
typedef enum
{
QSF_UNDEF = 0, /**< Initial undefined value. */
IS_QSF_MAP, /**< A QSF map */
IS_QSF_OBJ, /**< A QSF object without a map - it may or may not need one. */
HAVE_QSF_MAP, /**< A QSF object with the map it needs. */
OUR_QSF_OBJ, /**< A QSF object that can be loaded without a map. */
} qsf_type;
/** \brief Holds a description of the QofObject.
Used when converting QOF objects from another application. The incoming,
\b unknown, objects need to be stored prior to conversion. This allows
many-to-many conversions where an invoice can receive data from an incoming
expense AND datebook and use data from an incoming contacts object to lookup
the customer for the invoice.
*/
typedef struct qsf_object_set
{
GHashTable *parameters;
QofIdType object_type;
gint object_count;
} qsf_objects;
#define QSF_QOF_VERSION QOF_OBJECT_VERSION /**< QOF Version check.
Make sure the same version of QOF is in use in both applications.
*/
/** @name QSF Object XML
@{ */
#define QSF_ROOT_TAG "qof-qsf" /**< The top level root tag */
#define QSF_DEFAULT_NS "http://qof.sourceforge.net/" /**< Default namespace for QSF root tag
The map namespace is not included as maps are not currently written out by QOF.
*/
#define QSF_DATE_LENGTH MAX_DATE_LENGTH /**< Max length of QSF_XSD_TIME.
MAX_DATE_LENGTH itself is defined in gnc-date.h */
#define QSF_BOOK_TAG "book" /**< First level child: book tag - the ::QofBook. */
#define QSF_BOOK_GUID "book-guid" /**< QOF GncGUID tag for the QofBook
described by this QSF object file */
#define QSF_BOOK_COUNT "count" /**< Sequential counter of each book in this file */
#define QSF_OBJECT_TAG "object" /**< Second level child: object tag */
#define QSF_OBJECT_TYPE "type" /**< QSF parameter name for object type specifiers */
#define QSF_OBJECT_COUNT "count" /**< Sequential counter for each QSF object
in this file */
#define QSF_XML_VERSION "1.0" /**< The current XML version. */
/** @} */
/** @name Representing KVP as XML
&lt;kvp type="kvp" path="/from-sched-xaction" value="guid"&gt;c858b9a3235723b55bc1179f0e8c1322&lt;/kvp&gt;
A kvp type KVP parameter located at $path containing a GncGUID $value.
The relevance of type="kvp" won't be evident in GnuCash, they all use "kvp".
A non-GnuCash example helps:
&lt;kvp type="pilot_addr_kvp" path="/user/name" value="guid"&gt;c858b9a3235723b55bc1179f0e8c1322&lt;/kvp&gt;
A pilot_addr_kvp type KVP parameter located at /user/name containing a guid value.
@{ */
#define QSF_OBJECT_KVP "path" /**< The path to this KVP value in the entity frame. */
#define QSF_OBJECT_VALUE "value" /**< The KVP Value. */
/** @} */
/** @name QSF Map XML
@{ */
#define MAP_ROOT_TAG "qsf-map" /**< Top level root tag for QSF Maps */
#define MAP_DEFINITION_TAG "definition" /**< Second level container for defined objects
Attributes: qof_version - Taken from the QOF_OBJECT_VERSION macro in QOF,
At the time of QSF development, QOF_OBJECT_VERSION is defined as 3. All
QSF maps and QSF objects must use the same qof_version which in turn must
match the QOF_OBJECT_VERSION for the QOF library in use by the calling process.
No text content allowed.
*/
#define MAP_DEFINE_TAG "define" /**< defines each object supported by this QSF map
Attributes: e_type Copied directly from the QofObject definition.
Content: The full QofObject description for the defined QOF object.
*/
#define MAP_ITERATE_ATTR "foreach" /**< Dictate which object type is the basis
for iteration in a hierarchical object set. */
#define MAP_DEFAULT_TAG "default" /**< User editable defaults for data not
available within the available QSF objects.
Some defaults will relate to how to format descriptive dates, whether discount
should be considered, which account to use for certain QSF data from applications
that do not use accounts.
Some defaults are pre-defined and cannot be over-written:
- qsf_time_now
- qsf_time_string
Attributes (All are mandatory):
\a name The text name for this default. Certain pre-defined defaults exist but
user- or map-defined defaults can have any unique text name. Spaces are \b NOT allowed,
use undersccores instead. The value of name must not duplicate any existing default,
define, object or parameter unless the special type, enum, is used.
\a type QOF_TYPE - must be one of the recognised QOF data types for the
qof_version in use or the special type, enum.
\a value Text representation of the required value. For numeric, use the format
[0-9]?/[0-9]?
\attention Using boolean defaults
A boolean default is not output in the QSF directly, instead the value is
used in the calculations to modify certain values. If the boolean default
is set to true, the if statement containing the boolean name will be evaluated.
If the boolean default is set to false, the corresponding else will be evaluted.
Make sure your calculations contain an appropriate else statement so that the
boolean value can be adjusted without invalidating the map!
QSF deals with partial QofBooks - each object is fully described but the
book does not have to contain any specific object types or have any
particular structure. To merge partial books into usual QofBook data
sources, the map must deal with entities that need to be referenced in
the target QofBook but which simply do not exist in the QofBook used to generate
the QSF. e.g. pilot-link knows nothing of Accounts yet when QSF creates
a gncInvoice from qof-datebook, gncInvoice needs to know the GncGUID of
certain accounts in the target QofBook. This is handled in the map
by specifying the name of the account as a default for that map. When imported,
the QSF QofBackend looks up the object required using the name of
the parameter to obtain the parameter type. This is the only situation
where QSF converts between QOF data types. A string description of the
required object is converted to the GncGUID for that specific entity. The
map cannot contain the GncGUID as it is generic and used by multiple users.
\attention Using enumerators
- enum types are the only defaults that are allowed to use the same name value
more than once.
- enum types are used to increase the readability of a QSF map.
- The enum name acts to group the enum values together - in a similar fashion to
radio buttons in HTML forms.
- enum types are used only where the QOF object itself uses an enum type.
e.g. the tax_included enum type allows maps to use the full name of the enum
value GNC_TAXINCLUDED_YES, instead of the cryptic digit value, 1.
*/
#define MAP_OBJECT_TAG "object" /**< Contains all the calculations to make one
object from others.
Note that creating an object for the import application can involve using data
from more than one QSF object, as well as defaults and lookups in the import
application itself. Conditionals, simple arithmetic and date/time formatting
options are also available.
*/
#define MAP_CALCULATE_TAG "calculate" /**< One calculation for every parameter
that needs to be set.
QSF follows the same rule as qof_book_merge. Only if a getter and a setter
function are defined for a parameter is it available to QSF. If a ::QofAccessFunc
and ::QofSetterFunc are both defined for any QofObject parameter, that parameter
\b MUST be calculated in any map that defines that object.
*/
#define MAP_QOF_VERSION "qof_version" /**< This is the QOF_OBJECT_VERSION from QOF.
QSF maps may need to be updated if QOF itself is upgraded. This setting is coded
into QOF and maps for one version cannot necessarily be used by other versions.
At the first release of QSF, QOF_OBJECT_VERSION = 3.
*/
#define MAP_NAME_ATTR "name" /**< The name of the default setting.
Use this name to refer to the value of this default in the map calculations.
Make sure that the type of this default matches the type of the parameter being
set by the parent calculation!
*/
#define MAP_TYPE_ATTR "type" /**< QSF will NOT convert between QOF types.
QSF will allow a conditional to use a parameter of one type to determine the
value from a parameter of another type, but the final value assigned \b MUST be
of the same type as the parent calculation.
*/
#define MAP_VALUE_ATTR "value" /**< The value of the tag, used in defaults and
calculations.
The value of a default is a string representation of the value to be inserted into
the calculation where the default is used.
The value of a calculation is the name of the parameter that will be set by that
calculation.
*/
#define MAP_OBJECT_ATTR "object" /**< The object to use to provide the data
being set using the map.*/
#define MAP_E_TYPE "e_type" /**< Validates the objects defined in the map
The e_type will be used to match incoming QSF objects with the relevant QSF map.
The value of the e_type must be the value of the e_type for that object in the
originating QOF application. The define tag must contain the value of the description
of the same object in the same originating QOF application.
*/
/** \todo enum is an attempt to make enumerator values descriptive in the maps
and QSF (possibly). Not working yet. */
#define MAP_ENUM_TYPE "enum"
/** \brief A specific boolean default for this map.
*/
#define QSF_BOOLEAN_DEFAULT "boolean"
#define QSF_CONDITIONAL "if" /**< child of calculate.
Conditionals can reference objects as if within the original application. In operation,
the map is overlaid across both sets of defined objects, an import object in the source
application and an output object for the destination object. The current import and output
QSF objects are therefore always available to the map.
Conditionals can reference parameter as well as object values.
*/
#define QSF_CONDITIONAL_SET "set" /**< Assignment statement
Map assignments can use the native values within the output object. The output object
must support setting the relevant parameter using the value exactly as given in the map
because the relevant set() function will be called using this value. This may reduce the
readability of the map but the relevant application could also be modified to support
a more readable set function.
*/
#define QSF_CONDITIONAL_ELSE "else" /**< Alternative
if(){} else{} is also supported. Nesting of conditionals causes problems for
validating the final map against any sensible XML Schema and a map that does not
validate will be rejected. When editing conditionals in a QSF map, ALWAYS
validate the map using xmllint. If necessary, define a variable at the foot of
the definitions block, using a similar syntax to a default, then use that
variable in another conditional
\a variable \a name="my_rate" \a type="numeric" \a value="0/1"
The syntax for xmllint is:
\a xmllint \a --schema \a &lt;schema file&gt; \a &lt;qsf-file&gt;
Use the qsf-object.xsd.xml schema for objects and qsf-map.xsd.xml for map files.
e.g. xmllint --schema qsf-object.xsd.xml --noout qof-qsf.xml
*/
#define QSF_OPTION "option" /**< enum operator
Not implemented yet - may need to change once work starts.
Theoretically, option will specify when an enumerator value is in use -
it is quite possible that it will be unnecessary.
*/
#define QSF_FORMATTING_OPTION "format" /**< How to format dates/times
When the QSF map uses a date/time value as a \b string, the formatting
can be adjusted to personal preference. \a format will only be evaluated
if the calculated parameter is a QOF_TYPE_STRING - any format attributes
on other data types will be ignored.
*/
/** @} */
#define QSF_XSD_TIME QOF_UTC_DATE_FORMAT /**< xsd:dateTime format in coordinated
universal time, UTC.
You can reproduce the string from the GNU/Linux command line using the date utility:
date -u +%Y-%m-%dT%H:%M:%SZ
2004-12-12T23:39:11Z
The datestring must be timezone independent and include all specified fields.
Remember to use gmtime() NOT localtime()!. From the command line, use the
-u switch with the date command: date -u
To generate a timestamp based on a real time, use the qsf_time_now and
qsf_time_string defaults.
qsf_time_now : Format: QOF_TYPE_DATE. The current time taken from the moment
the default is read into a QSF object at runtime.
qsf_time_string : Format: QOF_TYPE_STRING. The current timestamp taken from the
moment the default is read into a QSF object at runtime. This form is used when
the output parameter needs a formatted date string, not an actual date object.
The format is determined by the optional format attribute of the set tag which
takes the same operators as the GNU C Library for strftime() and output may therefore
be dependent on the locale of the calling process - \b take \b care. Default value
is %F, used when qsf_time_string is set without the format attribute.
Both defaults use UTC.
*/
#define QSF_XML_BOOLEAN_TEST "true" /**< needs to be lowercase for XML validation */
#define QSF_OBJECT_SCHEMA "qsf-object.xsd.xml" /**< Name of the QSF Object Schema. */
#define QSF_MAP_SCHEMA "qsf-map.xsd.xml" /**< Name of the QSF Map Schema. */
/** \brief QSF Parameters
This struct is a catch-all for all parameters required
for various stages of the process. There are lots of elements
here that will finally be removed.
*/
typedef struct qsf_metadata
{
qsf_type file_type; /**< what type of file is being handled */
qsf_objects *object_set; /**< current object set for qsf_object_list. */
gint count; /**< sequential counter for each object in the book */
GList *qsf_object_list; /**< list of qsf_objects */
GSList *qsf_sequence; /**< Parameter list sorted into QSF order */
GList *referenceList; /**< Table of references, ::QofInstanceReference. */
GHashTable *qsf_parameter_hash; /**< Hashtable of parameters for each object */
GHashTable *qsf_calculate_hash, *qsf_default_hash, *qsf_define_hash;
GSList *supported_types; /**< The list of QOF types currently supported, in QSF order. */
xmlDocPtr input_doc; /**< Pointer to the input xml document(s). */
xmlDocPtr output_doc; /**< Pointer to the output xml document(s). */
xmlNodePtr child_node; /**< The current child_node. */
xmlNodePtr convert_node; /**< Node in the converted object */
xmlNodePtr param_node; /**< Node for parameter data. */
xmlNodePtr output_node; /**< Node in the output document. */
xmlNodePtr output_root; /**< Root node of the output document. */
xmlNodePtr book_node; /**< Node for the book. */
xmlNodePtr lister; /**< Comparison node for map defaults. */
xmlNsPtr qsf_ns, map_ns; /**< Separate namespaces for QSF objects and QSF maps. */
const gchar *qof_type; /**< Holds details of the QOF_TYPE */
QofIdType qof_obj_type; /**< current QofObject type (e_type) for the parameters. */
QofIdType qof_foreach; /**< How to iterate over hierarchical entities. */
gint foreach_limit; /**< How many iterations are found in the QSF */
QofInstance *qsf_ent; /**< Current entity in the book. */
QofBackend *be; /**< the current QofBackend for this operation. */
gboolean knowntype; /**< detect references by comparing with known QOF types. */
QofParam *qof_param; /**< used by kvp to handle the frame hash table */
QofBook *book; /**< the current QofBook.
Theoretically, QSF can handle multiple QofBooks - currently limited to 1.
*/
gint boolean_calculation_done; /**< simple trip once this boolean is complete. */
gchar *filepath; /**< Path to the QSF file. */
gchar *map_path; /**< Path to best match map, if any. */
gchar* full_kvp_path; /**< Full path for each KvpValue written out. */
gint64 use_gz_level; /**< Default compression level. */
GList *map_files; /**< List of selected map files for this session.
Defaults to the pre-installed QSF maps, currently: pilot-qsf-GnuCashInvoice.xml
*/
const gchar *encoding; /**< Backend encoding option - defaults to UTF-8. */
} qsf_param;
/** \brief Validation metadata
The validation is a separate parse with separate data.
This is used to determine which backend should load the data.
*/
typedef struct qsf_validates
{
QofBackendError error_state;
const gchar *object_path;
const gchar *map_path;
GHashTable *validation_table;
gint valid_object_count;
gint map_calculated_count;
gint qof_registered_count;
} qsf_validator;
/** \brief shorthand function
This may look repetitive but each one is used separately
as well as in a block.
*/
gint
qsf_compare_tag_strings(const xmlChar *node_name, gchar *tag_name);
/** \brief shorthand function
This may look repetitive but each one is used separately
as well as in a block.
*/
gint
qsf_strings_equal(const xmlChar *node_name, gchar *tag_name);
/** \brief shorthand function
This may look repetitive but each one is used separately
as well as in a block.
*/
gint
qsf_is_element(xmlNodePtr a, xmlNsPtr ns, gchar *c);
/** \brief shorthand function
This may look repetitive but each one is used separately
as well as in a block.
*/
gint
qsf_check_tag(qsf_param *params, gchar *qof_type);
/** \brief Checks all incoming objects for QOF registration.
Sums all existing objects in the QSF and counts the number of those
objects that are also registered with QOF in the host application.
*/
void
qsf_object_validation_handler(xmlNodePtr child, xmlNsPtr ns, qsf_validator *valid);
/** \brief Compares an xmlDoc in memory against the schema file.
@param schema_dir set at compile time to $prefix/share/qsf/
@param schema_filename Either the QSF Object Schema or the QSF Map Schema.
@param doc The xmlDoc read from the file using libxml2.
Ensure that you call the right schema_filename for the doc in question!
Incorrect validation will result in output to the terminal window.
@return TRUE if the doc validates against the assigned schema, otherwise FALSE.
*/
gboolean
qsf_is_valid(const gchar *schema_dir, const gchar* schema_filename, xmlDocPtr doc);
/** \brief Prepare the default list of maps.
Prepend the default maps to the supplied GList.
The GList remains the property of the caller.
*/
GList** qsf_map_prepare_list(GList **maps);
/** \name Why two sets of functions and typedefs?
These functions are in pairs, one to use in an existing session and one to use
when deciding which backend should be selected for a new session.
- When there is an existing QofSession, the qsf_param
context will be available, so set error codes in the backend.
Use the *_be functions.
- When just determining the type of file, qsf_param is
not necessary and no backend is available (it has not been selected yet).
Use the twin function. e.g. in ::qsf_file_type()
@{
*/
/** \brief map and qsf object callback
This callback cannot do both the map and the validation tasks
because validation sometimes needs to be done without qsf_params.
e.g. when selecting which backend should be used for a particular
data source where two or more backends share the same access_method.
*/
typedef void (* qsf_nodeCB)(xmlNodePtr, xmlNsPtr, qsf_param*);
/** \brief validator callback
\todo The need for separate metadata means a separate callback typedef
is needed for the validator, but this should be fixed to only need one.
*/
typedef void (* qsf_validCB)(xmlNodePtr, xmlNsPtr, qsf_validator*);
/** \brief One iterator, two typedefs
\todo resolve the two callbacks in ::qsf_node_iterate into one.
*/
struct qsf_node_iterate
{
qsf_nodeCB *fcn;
qsf_validCB *v_fcn;
xmlNsPtr ns;
};
/** \brief Validate a QSF file and identify a suitable QSF map
@param params Pointer to qsf_param context
These functions are in pairs. When called from within a QofSession, the qsf_param
context will be available. When just determining the type of file, qsf_param is
not necessary. Use the *_be functions from within the QofBackend and the
corresponding function in other code.
The file is validated against the QSF object schema, qsf-object.xsd.xml and
each object described in the file is checked to find out if a suitable QSF
map exists. Map files are accepted if all objects described in the QSF object
file are defined in the QSF map.
@return TRUE if the file validates and a QSF map can be found,
otherwise FALSE.
*/
gboolean is_qsf_object_be(qsf_param *params);
/** \brief Validate a QSF file and identify a suitable QSF map
@param path Absolute or relative path to the file to be validated.
These functions are in pairs. When called from within a QofSession, the qsf_param
context will be available. When just determining the type of file, qsf_param is
not necessary. Use the *_be functions from within the QofBackend and the
corresponding function in other code.
The file is validated against the QSF object schema, qsf-object.xsd.xml and
each object described in the file is checked to find out if a suitable QSF
map exists. Map files are accepted if all objects described in the QSF object
file are defined in the QSF map.
@return TRUE if the file validates and a QSF map can be found,
otherwise FALSE.
*/
gboolean is_qsf_object(const gchar *path);
/** \brief Validate a QSF file and determine type.
@param params Pointer to qsf_param context
The file is validated against the QSF object schema, qsf-object.xsd.xml and
each object described in the file is checked to see if it is registered
with QOF within the QOF environment of the calling process.
Files that pass the test can be imported into the QOF appliction without the need
for a QSF map.
@return TRUE if the file validates and all objects pass,
otherwise FALSE.
*/
gboolean is_our_qsf_object_be(qsf_param *params);
/** \brief Validate a QSF file.
@param path Absolute or relative path to the file to be validated
The file is validated against the QSF object schema, qsf-object.xsd.xml and
each object described in the file is checked to see if it is registered
with QOF within the QOF environment of the calling process.
Files that pass the test can be imported into the QOF appliction without the need
for a QSF map.
@return TRUE if the file validates and all objects pass,
otherwise FALSE.
*/
gboolean is_our_qsf_object(const gchar *path);
/** \brief Validate a QSF map file.
@param params Pointer to qsf_param context
The file is validated aginst the QSF map schema, qsf-map.xsd.xsml. This
function is called by ::is_qsf_object. If called directly, the map file
is validated and closed with a QofBackend error. QSF maps do not contain
user data and are used to import QSF object files.
@return TRUE if the map validates, otherwise FALSE.
*/
gboolean is_qsf_map_be(qsf_param *params);
/** \brief Validate a QSF map file.
@param path Absolute or relative path to the file to be validated
These functions are in pairs. When called from within a QofSession, the qsf_param
context will be available. When just determining the type of file, qsf_param is
not necessary. Use the *_be functions from within the QofBackend and the
corresponding function in other code.
The file is validated aginst the QSF map schema, qsf-map.xsd.xsml. This
function is called by ::is_qsf_object. If called directly, the map file
is validated and closed, no data is retrieved. QSF maps do not contain
user data but are used to import QSF object files from other applications.
@return TRUE if the map validates, otherwise FALSE.
*/
gboolean is_qsf_map(const gchar *path);
/** \brief Validate a QSF file and a selected QSF map
@param map_path Absolute or relative path to the selected QSF map file
@param params Pointer to qsf_param context
The file is validated against the QSF object schema, qsf-object.xsd.xml and
each object described in the file is checked to find out if the supplied QSF
map is suitable. Map files are accepted if all objects described in the QSF object
file are defined in the QSF map.
This backend twin also sets QofBackendError codes.
@return TRUE if the file validates and the supplied QSF map is usable,
otherwise FALSE.
*/
gboolean is_qsf_object_with_map_be(gchar *map_path, qsf_param *params);
/** \brief Validate a QSF file and a selected QSF map
@param path Absolute or relative path to the selected QSF map file
@param map_file Name of the map file
The file is validated against the QSF object schema, qsf-object.xsd.xml and
each object described in the file is checked to find out if the supplied QSF
map is suitable. Map files are accepted if all objects described in the QSF object
file are defined in the QSF map.
@return TRUE if the file validates and the supplied QSF map is usable,
otherwise FALSE.
*/
gboolean is_qsf_object_with_map(const gchar *path, gchar *map_file);
/** @} */
/** \brief Book and book-guid node handler.
Reads the book count="" attribute (currently only 1 QofBook is supported per QSF object file)
Sets the book-guid as the GncGUID of the current QofBackend QofBook in qsf_param.
Calls the next handler, qsf_object_node_handler, with the child of the book tag.
*/
void qsf_book_node_handler(xmlNodePtr child, xmlNsPtr qsf_ns, qsf_param *params);
/** \brief Convert a string value into KvpValue
Partner to ::kvp_value_to_string. Given the type of KvpValue
required, attempts to convert the string into that type of
value.
@param content A string representation of the value, ideally as
output by kvp_value_to_string.
@param type KvpValueType of the intended KvpValue
@return KvpValue* or NULL on failure.
*/
KvpValue*
string_to_kvp_value(const gchar *content, KvpValueType type);
/** Validate the children of the parent node.
\note Slightly different to qsf_node_foreach because
the validation can be run without qsf_param being
initialized.
*/
void
qsf_valid_foreach(xmlNodePtr parent, qsf_validCB cb,
struct qsf_node_iterate *iter, qsf_validator *valid);
/** Iterate over the children of the parent node.
Only iterates over the immediate children of the parent -
this function is \b not recursive.
*/
void
qsf_node_foreach(xmlNodePtr parent, qsf_nodeCB cb,
struct qsf_node_iterate *iter, qsf_param *params);
/** \brief Convert between QSF objects
This is the main workhorse of the conversion between QSF objects using
maps.
@param mapDoc The map document, parsed by libxml2.
@param qsf_root The top node of the QSF object to be converted using the map.
@param params The QSF backend parameters.
Each calculation in the map is performed over the child nodes of the
object tree. A new xmlDoc is created and this is made available to QOF to
be loaded into the book.
*/
xmlDocPtr
qsf_object_convert(xmlDocPtr mapDoc, xmlNodePtr qsf_root, qsf_param *params);
/** Despite the name, this function handles the QSF object book tag
AND the object tags.
Used to parse object and map files.
*/
void
qsf_object_node_handler(xmlNodePtr child, xmlNsPtr qsf_ns, qsf_param *params);
/** @} */
/** @} */
#endif /* QSF_XML_H */

View File

@ -97,9 +97,4 @@
#include "qofbookmerge.h"
#include "qofreference.h"
/** allow easy logging of QSF debug messages */
#define QOF_MOD_QSF "qof.backend.qsf"
/** allow easy loading of the QSF backend */
#define QSF_BACKEND_LIB "gncqof-backend-qsf"
#endif /* QOF_H_ */

View File

@ -75,37 +75,13 @@ typedef enum
another user has deleted the object */
ERR_BACKEND_MISC, /**< undetermined error */
/* QSF add-ons */
ERR_QSF_INVALID_OBJ, /**< The QSF object failed to validate against the QSF object schema */
ERR_QSF_INVALID_MAP, /**< The QSF map failed to validate against the QSF map schema */
ERR_QSF_BAD_OBJ_GUID, /**< The QSF object contains one or more invalid GUIDs. */
ERR_QSF_BAD_QOF_VERSION, /**< QSF map or object doesn't match the current QOF_OBJECT_VERSION. */
ERR_QSF_BAD_MAP, /**< The selected map validates but is unusable.
This is usually because not all the required parameters for the defined objects
have calculations described in the map.
*/
ERR_QSF_NO_MAP, /**< The QSF object file was loaded without a map
The QSF Object file requires a map but it was not provided.
*/
ERR_QSF_WRONG_MAP, /**< The selected map validates but is for different objects.
The list of objects defined in this map does not include all the objects described in
the current QSF object file.
*/
ERR_QSF_MAP_NOT_OBJ, /**< Selected file is a QSF map and cannot be opened as a QSF object */
ERR_QSF_OVERFLOW, /**< EOVERFLOW - generated by strtol or strtoll.
ERR_QOF_OVERFLOW, /**< EOVERFLOW - generated by strtol or strtoll.
When converting XML strings into numbers, an overflow has been detected. The XML file
contains invalid data in a field that is meant to hold a signed long integer or signed long long
integer.
*/
ERR_QSF_OPEN_NOT_MERGE, /** QSF files cannot be opened alone. The data must be merged.
This error is more of a warning that can be ignored by any routine
that uses qof_book_merge on the new session.
*/
/* fileio errors */
ERR_FILEIO_FILE_BAD_READ = 1000, /**< read failed or file prematurely truncated */
ERR_FILEIO_FILE_EMPTY, /**< file exists, is readable, but is empty */

View File

@ -54,8 +54,8 @@ really exist by itself:
\verbatim
QOF_TYPE_CHOICE<QOF_X, QOF_Y, QOF_Z>
\endverbatim
It holds a single entity of type X, Y, or Z for the purposes of QOF
or ::QSF. For querying the object it queries as if it's an X, Y, or Z.
It holds a single entity of type X, Y, or Z for the purposes of QOF.
For querying the object it queries as if it's an X, Y, or Z.
Each choice type has it's own definition of the allowable objects -
each of which need to be registered as normal. Objects can declare

View File

@ -1011,7 +1011,6 @@ Remember: Use the libdir from the current build environment
and use JUST the module name without .so - .so is not portable! */
struct backend_providers backend_list[] =
{
{ QOF_LIB_DIR, QSF_BACKEND_LIB },
#ifdef HAVE_DWI
{ QOF_LIB_DIR, "libqof_backend_dwi"},
#endif

View File

@ -120,10 +120,9 @@ void qof_session_swap_data (QofSession *session_1, QofSession *session_2);
/** The qof_session_begin () method begins a new session.
* It takes as an argument the book id. The book id must be a string
* in the form of a URI/URL. The access method specified depends
* on the loaded backends. In the absence of a customised backend,
* only QSF XML would be accepted). Paths may be relative or absolute.
* If the path is relative; that is, if the argument is "file:somefile.xml"
* then the current working directory is assumed. Customised backends can
* on the loaded backends. Paths may be relative or absolute.
* If the path is relative; that is, if the argument is "file://somefile.xml"
* then the current working directory is assumed. Customized backends can
* choose to search other, application-specific, directories as well.
*
* The 'ignore_lock' argument, if set to TRUE, will cause this routine

View File

@ -655,17 +655,6 @@ qof_sql_insertCB(const QofParam *param, const gchar *insert_string, QofSqlQuery
LEAVE (" string to guid failed for %s", insert_string);
return;
}
/* reference_type = xmlGetProp(node, QSF_OBJECT_TYPE);
if(0 == safe_strcmp(QOF_PARAM_GUID, reference_type))
{
qof_instance_set_guid(qsf_ent, cm_guid);
}
else {
reference = qof_instance_get_reference_from(qsf_ent, cm_param);
if(reference) {
params->referenceList = g_list_append(params->referenceList, reference);
}
}*/
}
if (safe_strcmp(param->param_type, QOF_TYPE_INT32) == 0)
{
@ -686,7 +675,7 @@ qof_sql_insertCB(const QofParam *param, const gchar *insert_string, QofSqlQuery
book = qof_instance_get_book((QofInstance*)ent);
backend = qof_book_get_backend(book);
qof_backend_set_error(backend, ERR_QSF_OVERFLOW);
qof_backend_set_error(backend, ERR_QOF_OVERFLOW);
}
}
if (safe_strcmp(param->param_type, QOF_TYPE_INT64) == 0)
@ -708,7 +697,7 @@ qof_sql_insertCB(const QofParam *param, const gchar *insert_string, QofSqlQuery
book = qof_instance_get_book((QofInstance*)ent);
backend = qof_book_get_backend(book);
qof_backend_set_error(backend, ERR_QSF_OVERFLOW);
qof_backend_set_error(backend, ERR_QOF_OVERFLOW);
}
}
if (safe_strcmp(param->param_type, QOF_TYPE_DOUBLE) == 0)

View File

@ -413,7 +413,6 @@ qof_util_param_as_string(QofInstance *ent, QofParam *param)
{
boolean_getter = (gboolean (*)(QofInstance*, QofParam*)) param->param_getfcn;
param_boolean = boolean_getter(ent, param);
/* Boolean values need to be lowercase for QSF validation. */
if (param_boolean == TRUE)
{
param_string = g_strdup("true");

View File

@ -1,2 +1,2 @@
SUBDIRS = xsl ${PYTHON_DIR}
DIST_SUBDIRS = xsl python-bindings
SUBDIRS = ${PYTHON_DIR}
DIST_SUBDIRS = python-bindings

View File

@ -1,13 +0,0 @@
xsldir = ${datadir}/xml/gnucash/xsl
xsl_DATA = \
README \
date-time.xsl \
gnucash-std.xsl \
string.xsl \
gnucash-gnccustomer-vcard2.xsl \
vcard-gnccustomer.pl
EXTRA_DIST = \
${xsl_DATA}

View File

@ -1,98 +0,0 @@
GnuCash, XSL and QSF.
====================
If you need to convert gnucash QSF data into other formats, take a look at the example
stylesheets installed with gnucash. You are welcome to contribute new or amended
stylesheets - just post them on the QOF-devel mailing list.
http://lists.sourceforge.net/lists/listinfo/qof-devel
Depending on your package manager, the XSL stylesheets should be installed in
/usr/share/xml/gnucash/xsl/, or on Fink /sw/share/xml/gnucash/xsl/.
Current stylesheets are works in progress, but include:
· gnucash-gnccustomer-vcard2.xsl
This stylesheet converts QSF export gnucash customer data into a brief Vcard,
suitable for upload to Kaddressbook. Each VCard is written into a separate .vcf
file, named after the contact described in the pilot_address records. Spaces are
replaced with underscores. Specify the '-o dir/' option to xsltproc to output all
vcards into a directory.
· Others
Stylesheets for ICS, vcal and possibly LDAP are planned. Any plain text, XML or HTML
format can be generated, theoretically. If you have a request for an XSL stylesheet
or if you have a stylesheet you would like to make available to others, mention it
on the QOF-devel mailing list.
Also included is a perl script:
· vcard-gnccustomer.pl
The script uses the Text::vCard::Addressbook CPAN module to parse VCard files,
including those from other applications, into QSF XML that could be imported into
gnucash and merged into your gnucash customer list. With a few tweaks, this could
also be configured to create QSF XML suitable for gnucash vendor or employee records.
You are free to copy and modify these stylesheets to your own requirements, including
translations and customised formats. Depending on your package manager, this is often
best done by copying the installed file to a local directory before modifying it.
GnuCash does not reference these stylesheets directly. If your modifications could be
useful to others, please contribute them to gnucash via the QOF-devel mailing list or
gnucash-devel mailing list. Submitted stylesheets should be licenced under the GNU GPL.
LOCALISATION (l10N) OF STYLESHEET OUTPUT.
========================================
Later HTML stylesheets will support providing translatable strings and user-specific
encodings via external parameters. This can make the command line very long so is best
performed using a script. Each descriptive word in the output is configured as a string
parameter for the stylesheet and can be replaced with a translated version. HTML output
supports setting the HTML language (as would be specified in the <html> lang attribute)
and the encoding (as would be specified in the <meta> charset value in the content
attribute). If you use these stylesheets via a scripting language - like bash, PHP or
Perl - you could automate the translation by passing values obtained from a normal get-
text PO file. Copy the translatable strings into your script file as normal variables
then mark up those variables for translation by gettext. When the script is called, get-
text will assign the translated values to the variables and your script can simply echo
those values to the calls to the XSL parser routines.
It is important that the HTML language and the encoding match each other AND the
expected content of the HTML output generated from the gnucash QSF data.
When providing translated strings, the same constraints apply as if you were using get-
text and a normal PO file: the context and format of the translation should match the
intention expressed in the default value of the parameter. If the default is plural, the
translation should be plural. If the default is capitalised, the translation should be
capitalised - subject to grammatical rules for that language - even if the parameter
name itself is not capitalised.
Each stylesheet specifies the translatable strings in a block near the top of the file,
marked as for the attention of translators. Common settings, like the HTML language
parameter and the encoding support, are in the gnucash-std.xsl stylesheet and are
available in all HTML stylesheets by using the string parameters html_lang and encoding.
For more information, please ask on the gnucash-devel mailing list, the QOF-devel mailing
list or see the pilot-qof source: http://pilot-qof.sourceforge.net/
EXAMPLES
========
Convert a QSF XML file containing gncCustomer records, into individual VCard .vcf files
in the vcards/ directory - which must already exist. Files are named according to either
the company or name of the gncAddress record.
$ xsltproc -o vcards/ gnucash-gnccustomer-vcard2.xsl addresses.xml
This example is based on an "in-progress" QSF invoice stylesheet for gnucash that is based
on the existing pilot-qof HTML invoice stylesheet. It overrides the default mileage-rate
(0.30 currency units per unit distance) and the default hourly-rate (20 currency units per
hour) provided by the stylesheet with user specific values. The example is here just to
indicate how --stringparam would be used.
$ xsltproc --stringparam mileage-rate 0.45 --stringparam hourly-rate 21 -o invoices/
pilot-qof-invoice-xhtml.xsl invoice.xml

File diff suppressed because it is too large Load Diff

View File

@ -1,74 +0,0 @@
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:qof-qsf="http://qof.sourceforge.net/" xmlns:str="http://xsltsl.org/string" version="1.1">
<xsl:import href="string.xsl"/>
<!-- our own gnucash standard routines -->
<xsl:import href="gnucash-std.xsl"/>
<xsl:output method="text"/>
<!-- Representing QSF gncAddress data in a 2.1 extended VCard
This stylesheet converts the GnuCash output from Export Customers
into a simple Vcard, suitable for import into KAdddressbook. For other
VCard support (e.g. for mobile phones), see other vcard XSL stylesheets in the
gnucash collection.
Each VCard is written into a separate .vcf file, named after the
contact described in the gncAddress records. Spaces are replaced with
underscores. Specify the '-o dir/' option to xsltproc to output all vcards
into a directory.
-->
<!-- Licence
This file is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-->
<xsl:template match="/*/qof-qsf:book/qof-qsf:object[@type='gncAddress']">
<xsl:text>TEL;TYPE=WORK:</xsl:text>
<xsl:value-of select="qof-qsf:string[@type='phone']"/>
<xsl:text>&#10;TEL;TYPE=FAX:</xsl:text>
<xsl:value-of select="qof-qsf:string[@type='fax']"/>
<xsl:text>&#10;EMAIL:</xsl:text>
<xsl:value-of select="qof-qsf:string[@type='email']"/>
<xsl:text>&#10;ADR;TYPE=work:;;</xsl:text>
<xsl:value-of select="qof-qsf:string[@type='number']"/>
<xsl:text>;</xsl:text>
<xsl:value-of select="qof-qsf:string[@type='street']"/>
<xsl:text>;</xsl:text>
<xsl:value-of select="qof-qsf:string[@type='locality']"/>
<xsl:text>;</xsl:text>
<xsl:value-of select="qof-qsf:string[@type='city']"/>
<xsl:text>&#10;N:</xsl:text>
<xsl:value-of select="qof-qsf:string[@type='name']"/>
<xsl:text>&#10;</xsl:text>
</xsl:template>
<xsl:template match="/">
<xsl:for-each select="/*/qof-qsf:book/qof-qsf:object[@type='gncCustomer']">
<xsl:variable name="contactName">
<xsl:value-of select="qof-qsf:string[@type='name']"/>
</xsl:variable>
<xsl:variable name="card_title">
<xsl:call-template name="get_chunk_name">
<xsl:with-param name="entryName" select="$contactName"/>
</xsl:call-template>
</xsl:variable>
<!-- chunking support -->
<xsl:document href="{$card_title}.vcf" method="text">
<xsl:text>BEGIN:VCARD&#10;VERSION:2.1&#10;</xsl:text>
<xsl:call-template name="locate_child">
<xsl:with-param name="customer_object" select="."/>
</xsl:call-template>
<xsl:text>END:VCARD&#10;</xsl:text>
</xsl:document>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>

View File

@ -1,140 +0,0 @@
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:qof-qsf="http://qof.sourceforge.net/" xmlns:html="http://www.w3.org/1999/xhtml" xmlns:dt="http://xsltsl.org/date-time" xmlns:str="http://xsltsl.org/string" version="1.0" exclude-result-prefixes="qof-qsf html dt str">
<xsl:import href="date-time.xsl"/>
<xsl:import href="string.xsl"/>
<!--This stylesheet contains standard templates for QOF QSF.-->
<!--This is only a working prototype -->
<!-- Licence
This file is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-->
<!-- Translation support via external parameters -->
<xsl:param name="html_lang">en</xsl:param>
<xsl:param name="encoding">iso-8859-15</xsl:param>
<!-- Convert gnc_numeric notation to an XSL number -->
<xsl:template name="numeric_to_double">
<xsl:param name="numeric_string"/>
<xsl:variable name="before" select="substring-before($numeric_string, '/')"/>
<xsl:variable name="after" select="substring-after($numeric_string, '/')"/>
<xsl:variable name="numeric" select="$before div $after"/>
<xsl:value-of select="number($numeric)"/>
</xsl:template>
<xsl:template name="get_chunk_name">
<xsl:param name="entryName"/>
<xsl:param name="entryCompany"/>
<xsl:variable name="result">
<xsl:choose>
<xsl:when test="$entryName = ''">
<xsl:call-template name="str:subst">
<xsl:with-param name="text" select="$entryCompany"/>
<xsl:with-param name="replace">
<xsl:text> </xsl:text>
</xsl:with-param>
<xsl:with-param name="with">
<xsl:text>_</xsl:text>
</xsl:with-param>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="temp">
<xsl:call-template name="str:subst">
<xsl:with-param name="text" select="$entryName"/>
<xsl:with-param name="replace">
<xsl:text> </xsl:text>
</xsl:with-param>
<xsl:with-param name="with">
<xsl:text>_</xsl:text>
</xsl:with-param>
</xsl:call-template>
</xsl:variable>
<xsl:call-template name="str:subst">
<xsl:with-param name="text" select="$temp"/>
<xsl:with-param name="replace">
<xsl:text>;</xsl:text>
</xsl:with-param>
<xsl:with-param name="with">
<xsl:text>_</xsl:text>
</xsl:with-param>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:value-of select="$result"/>
</xsl:template>
<xsl:template name="prepare_address_div">
<xsl:param name="address_object" select="."/>
<xsl:comment>this address panel can be located precisely using CSS</xsl:comment>
<xsl:text>&#10;</xsl:text>
<div class="address">
<h2>Customer address</h2>
<p class="address_para">
<xsl:text>&#10;</xsl:text>
<b>
<xsl:value-of select="qof-qsf:string[@type='entryCompany']"/>
</b>
<br/>
<xsl:text>&#10;</xsl:text>
<xsl:value-of select="qof-qsf:string[@type='entryAddress']"/>
<br/>
<xsl:text>&#10;</xsl:text>
<xsl:value-of select="qof-qsf:string[@type='entryCity']"/>
<br/>
<xsl:text>&#10;</xsl:text>
<xsl:value-of select="qof-qsf:string[@type='entryState']"/>
<br/>
<xsl:text>&#10;</xsl:text>
<xsl:value-of select="qof-qsf:string[@type='entryZip']"/>
<br/>
<xsl:text>&#10;</xsl:text>
<xsl:value-of select="qof-qsf:string[@type='entryCountry']"/>
<br/>
<br/>
<xsl:text>&#10;</xsl:text>
<xsl:value-of select="qof-qsf:string[@type='entryPhone1']"/>
</p>
</div>
</xsl:template>
<xsl:template name="vcard_safe">
<xsl:param name="address_string"/>
<xsl:variable name="safe_string">
<xsl:call-template name="str:subst">
<xsl:with-param name="text" select="$entryCompany"/>
<xsl:with-param name="replace">
<xsl:text>,</xsl:text>
</xsl:with-param>
<xsl:with-param name="with">
<xsl:text>\,</xsl:text>
</xsl:with-param>
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="$safe_string"/>
</xsl:template>
<!-- hierarchy support -->
<xsl:template name="locate_child">
<xsl:param name="customer_object"/>
<xsl:variable name="cust_addr_guid">
<xsl:value-of select="qof-qsf:guid[@type='addr']"/>
</xsl:variable>
<xsl:for-each select="/*/qof-qsf:book/qof-qsf:object[@type='gncAddress']">
<xsl:variable name="addr_guid">
<xsl:value-of select="qof-qsf:guid[@type='guid']"/>
</xsl:variable>
<xsl:if test="$addr_guid = $cust_addr_guid">
<xsl:variable name="set" select="."/>
<xsl:apply-templates select="$set"/>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>

File diff suppressed because one or more lines are too long

View File

@ -1,103 +0,0 @@
#!/usr/bin/perl -w
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
use Text::vCard::Addressbook;
use strict;
use vars qw($address_book $hex_string $num $c $d $e $i $vcard $names $name $fullname $email $address $addresses $phones $phone $filename $usage $encoding);
############### USER CONFIGURATION ####################
# Change to the encoding for you locale if your VCF data
# contains accented characters etc.
$encoding = 'UTF-8';
############### END CONFIGURATION ####################
# About
#
# This is a very simple file and should be simple to customise.
# A similar file is included in pilot-qof. Currently, the script
# needs to be told where to find the VCF file to transform and
# expects the user to redirect output to a file:
# $ perl vcard-gnccustomer.pl contact.vcf > contact.xml
$usage = $#ARGV + 1;
if($usage < 1)
{
print ("Error: please specify a .vcf file.\n\n");
exit;
}
$filename = $ARGV[0];
$address_book = Text::vCard::Addressbook->new({
'source_file' => $filename, });
# generate a temporary GUID to relate the address to the customer
$hex_string = '';
for($d = 1;$d < 5;$d++)
{
for($c = 1; $c < 8; $c++)
{
$num = (int(rand(99)) + 1) * 10**$c;
}
$hex_string .= sprintf("%x", $num);
}
print "<?xml version=\"1.0\" encoding=\"$encoding\"?>\n";
print "<qof-qsf xmlns=\"http://qof.sourceforge.net/\">\n";
print " <book count=\"1\">\n";
print " <book-guid/>\n";
print " <object type=\"gncAddress\" count=\"1\">\n";
foreach $vcard ($address_book->vcards()) {
$names = $vcard->get({ 'node_type' => 'name' });
foreach $name (@{$names}){
if($name->given()) {
$fullname = $name->given() . " " . $name->family();
}
else {
$fullname = $name->family();
}
&wrap_in_qsf($fullname, "name");
}
$phones = $vcard->get('tel');
$i = 0;
foreach $phone (@{$phones}){
if($i == 0) {
&wrap_in_qsf($phone->value(), "phone");
}
if($i == 1) {
&wrap_in_qsf($phone->value(), "fax");
}
$i++;
}
$email = $vcard->get('email');
foreach $e (@{$email}){ &wrap_in_qsf($e->value(), "email"); }
$addresses = $vcard->get({ 'node_type' => 'addresses' });
foreach $address (@{$addresses}) {
&wrap_in_qsf($address->street(), "street");
&wrap_in_qsf($address->city(), "city");
&wrap_in_qsf($address->region(), "locality");
}
print " <guid type=\"guid\">$hex_string</guid>\n";
}
print " </object>\n";
print " <object type=\"gncCustomer\" count=\"1\">\n";
&wrap_in_qsf($fullname, "name");
print " <boolean type=\"active\">true</boolean\n";
print " <guid type=\"addr\">$hex_string</guid>\n";
print " </book>\n";
print "</qof-qsf>\n\n";
sub wrap_in_qsf()
{
if($_[0]) { print " <string type=\"$_[1]\">$_[0]</string>\n"; }
else { print " <string type=\"$_[1]\"/>\n"; }
};