---
configure.ac | 5 +
libreport.spec.in | 16 ++
po/POTFILES.in | 28 ++--
src/Makefile.am | 2 +-
src/gtk-helpers/Makefile.am | 5 +-
src/gtk-helpers/config_dialog.c | 197 ++++++++++++++++++++++
src/gtk-helpers/event_config_dialog.c | 276 ++++++++++++-------------------
src/gtk-helpers/internal_libreport_gtk.h | 20 ++-
src/gtk-helpers/secrets.c | 4 +-
src/gtk-helpers/workflow_config_dialog.c | 117 +++++++++++++
src/gui-wizard-gtk/Makefile.am | 1 +
src/gui-wizard-gtk/main.c | 2 +
src/gui-wizard-gtk/wizard.c | 120 ++++++++++++--
src/gui-wizard-gtk/wizard.glade | 62 +++++++
src/gui-wizard-gtk/wizard.h | 2 +-
src/include/Makefile.am | 1 +
src/include/config_item_info.h | 2 +-
src/include/file_obj.h | 2 +
src/include/internal_libreport.h | 3 +
src/include/workflow.h | 51 ++++++
src/lib/Makefile.am | 3 +
src/lib/config_item_info.c | 4 +-
src/lib/report_event.conf | 1 +
src/lib/workflow.c | 144 ++++++++++++++++
src/lib/workflow_xml_parser.c | 167 +++++++++++++++++++
src/workflows/Makefile.am | 16 ++
src/workflows/report_Fedora.xml.in | 13 ++
src/workflows/report_Upload.xml.in | 11 ++
src/workflows/report_fedora.conf | 3 +
29 files changed, 1071 insertions(+), 207 deletions(-)
create mode 100644 src/gtk-helpers/config_dialog.c
create mode 100644 src/gtk-helpers/workflow_config_dialog.c
create mode 100644 src/include/workflow.h
create mode 100644 src/lib/workflow.c
create mode 100644 src/lib/workflow_xml_parser.c
create mode 100644 src/workflows/Makefile.am
create mode 100644 src/workflows/report_Fedora.xml.in
create mode 100644 src/workflows/report_Upload.xml.in
create mode 100644 src/workflows/report_fedora.conf
diff --git a/configure.ac b/configure.ac
index 07b3cf8..bed5366 100644
--- a/configure.ac
+++ b/configure.ac
@@ -176,6 +176,8 @@ PLUGINS_CONF_DIR='${sysconfdir}/${PACKAGE_NAME}/plugins'
REPORT_PLUGINS_CONF_DIR='${sysconfdir}/libreport/plugins'
EVENTS_DIR='${sysconfdir}/${PACKAGE_NAME}/events'
EVENTS_CONF_DIR='${sysconfdir}/${PACKAGE_NAME}/events.d'
+WORKFLOWS_DIR='${sysconfdir}/${PACKAGE_NAME}/workflows'
+WORKFLOWS_CONF_DIR='${sysconfdir}/${PACKAGE_NAME}/workflows.d'
PLUGINS_LIB_DIR='${libdir}/${PACKAGE_NAME}'
LIBEXEC_DIR='${libexecdir}'
@@ -195,6 +197,8 @@ AC_SUBST(EVENTS_DIR)
AC_SUBST(PLUGINS_LIB_DIR)
AC_SUBST(DEBUG_DUMPS_DIR)
AC_SUBST(LIBEXEC_DIR)
+AC_SUBST(WORKFLOWS_DIR)
+AC_SUBST(WORKFLOWS_CONF_DIR)
# Initialize the test suite.
AC_CONFIG_TESTDIR(tests)
@@ -222,6 +226,7 @@ AC_CONFIG_FILES([
src/client-python/Makefile
po/Makefile.in
doc/Makefile
+ src/workflows/Makefile
])
# src/plugins/Makefile
diff --git a/libreport.spec.in b/libreport.spec.in
index cda8f23..78f690e 100644
--- a/libreport.spec.in
+++ b/libreport.spec.in
@@ -217,6 +217,15 @@ Obsoletes: report-config-scp < 0:0.23-1
%description plugin-reportuploader
Plugin to report bugs into anonymous FTP site associated with ticketing system.
+%package fedora
+Summary: Default configuration for reporting bugs via Fedora infrastructure
+Group: Applications/File
+
+%description fedora
+Default configuration for reporting bugs via Fedora infrastructure
+used to easy configure the reporting process for Fedora sytems. Just
+install this package and you're done.
+
%prep
%setup -q
@@ -291,6 +300,7 @@ gtk-update-icon-cache %{_datadir}/icons/hicolor &>/dev/null ||
:
%{_includedir}/libreport/run_event.h
%{_includedir}/libreport/file_obj.h
%{_includedir}/libreport/config_item_info.h
+%{_includedir}/libreport/workflow.h
# Private api headers:
%{_includedir}/libreport/internal_abrt_dbus.h
%{_includedir}/libreport/internal_libreport.h
@@ -393,6 +403,12 @@ gtk-update-icon-cache %{_datadir}/icons/hicolor &>/dev/null ||
:
%{_bindir}/reporter-upload
%{_sysconfdir}/libreport/events/report_Uploader.xml
%config(noreplace) %{_sysconfdir}/libreport/events.d/uploader_event.conf
+%{_sysconfdir}/libreport/workflows/report_Upload.xml
+
+%files fedora
+%defattr(-,root,root,-)
+%{_sysconfdir}/libreport/workflows/report_Fedora.xml
+%config(noreplace) %{_sysconfdir}/libreport/workflows.d/report_fedora.conf
%changelog
* Wed Nov 14 2012 Jakub Filak <jfilak(a)redhat.com> 2.0.19-1
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 668c526..2dfe09e 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -3,31 +3,27 @@
# Please keep this file sorted alphabetically.
src/cli/cli.c
src/cli/cli-report.c
-src/client-python/__init__.py
src/client-python/debuginfo.py
+src/client-python/__init__.py
+src/gtk-helpers/config_dialog.c
src/gtk-helpers/event_config_dialog.c
src/gtk-helpers/secrets.c
+src/gtk-helpers/workflow_config_dialog.c
src/gui-wizard-gtk/main.c
src/gui-wizard-gtk/wizard.c
src/gui-wizard-gtk/wizard.glade
src/include/internal_libreport.h
src/lib/abrt_sock.c
+src/lib/client.c
src/lib/create_dump_dir.c
+src/lib/curl.c
src/lib/event_config.c
src/lib/parse_options.c
-src/lib/curl.c
-src/lib/client.c
-src/lib/run_event.c
src/lib/problem_data.c
+src/lib/run_event.c
src/plugins/abrt_rh_support.c
-src/plugins/report.c
src/plugins/report_Bugzilla.xml.in
-src/plugins/report_Kerneloops.xml.in
-src/plugins/report_Logger.xml.in
-src/plugins/report_Mailx.xml.in
-src/plugins/report_RHTSupport.xml.in
-src/plugins/report_Uploader.xml.in
-src/plugins/report_uReport.xml.in
+src/plugins/report.c
src/plugins/reporter-bugzilla.c
src/plugins/reporter-kerneloops.c
src/plugins/reporter-mailx.c
@@ -35,6 +31,14 @@ src/plugins/reporter-print.c
src/plugins/reporter-rhtsupport.c
src/plugins/reporter-rhtsupport-parse.c
src/plugins/reporter-upload.c
+src/plugins/report_Kerneloops.xml.in
+src/plugins/report_Logger.xml.in
+src/plugins/report_Mailx.xml.in
+src/plugins/report_RHTSupport.xml.in
+src/plugins/report_Uploader.xml.in
+src/plugins/report_uReport.xml.in
src/plugins/rhbz.c
-src/report-newt/report-newt.c
src/plugins/ureport.c
+src/report-newt/report-newt.c
+src/workflows/report_Fedora.xml.in
+src/workflows/report_Upload.xml.in
diff --git a/src/Makefile.am b/src/Makefile.am
index 68bd1c0..c15928c 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -8,4 +8,4 @@ if BUILD_NEWT
sub_dirs += report-newt
endif
-SUBDIRS = include lib plugins report-python cli client-python $(sub_dirs)
+SUBDIRS = include lib plugins report-python cli client-python workflows $(sub_dirs)
diff --git a/src/gtk-helpers/Makefile.am b/src/gtk-helpers/Makefile.am
index a821b5d..0d3f9d8 100644
--- a/src/gtk-helpers/Makefile.am
+++ b/src/gtk-helpers/Makefile.am
@@ -12,7 +12,9 @@ libreport_gtk_la_SOURCES = \
event_config_dialog.c \
secrets.c \
hyperlinks.c \
- autowrapped_label.c
+ autowrapped_label.c \
+ workflow_config_dialog.c \
+ config_dialog.c
libreport_gtk_la_CPPFLAGS = \
-I$(srcdir)/../include \
@@ -20,6 +22,7 @@ libreport_gtk_la_CPPFLAGS = \
$(GTK_CFLAGS) \
$(GLIB_CFLAGS) \
$(GIO_CFLAGS) \
+ -DWORKFLOWS_DIR=\"$(WORKFLOWS_DIR)\" \
-D_GNU_SOURCE
libreport_gtk_la_LDFLAGS = \
-version-info 0:1:0
diff --git a/src/gtk-helpers/config_dialog.c b/src/gtk-helpers/config_dialog.c
new file mode 100644
index 0000000..f997b50
--- /dev/null
+++ b/src/gtk-helpers/config_dialog.c
@@ -0,0 +1,197 @@
+/*
+ Copyright (C) 2011 ABRT Team
+ Copyright (C) 2011 RedHat inc.
+
+ 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.
+*/
+
+#include <gtk/gtk.h>
+#include "internal_libreport_gtk.h"
+
+enum
+{
+ COLUMN_UINAME,
+ COLUMN_NAME,
+ CONFIG_DIALOG,
+ NUM_COLUMNS
+};
+
+enum
+{
+ TYPE_STR,
+ TYPE_POINTER
+};
+
+GtkListStore *new_conf_liststore(void)
+{
+ /* Create data store for the list and attach it
+ * COLUMN_EVENT_UINAME -> name+description
+ * COLUMN_EVENT_NAME -> event name so we can retrieve it from the row
+ */
+ return gtk_list_store_new(NUM_COLUMNS,
+ G_TYPE_STRING, /* Event name + description */
+ G_TYPE_STRING, /* event name */
+ G_TYPE_POINTER
+ );
+}
+
+static const void *get_column_value_from_row(GtkTreeView *treeview, int column, int
type)
+{
+ GtkTreeSelection *selection = gtk_tree_view_get_selection(treeview);
+ const void *retval = NULL;
+ if (selection)
+ {
+ GtkTreeIter iter;
+ GtkTreeModel *store = gtk_tree_view_get_model(treeview);
+ if (gtk_tree_selection_get_selected(selection, &store, &iter) == TRUE)
+ {
+ GValue value = { 0 };
+ gtk_tree_model_get_value(store, &iter, column, &value);
+ switch(type){
+ case TYPE_STR:
+ retval = g_value_get_string(&value);
+ break;
+ case TYPE_POINTER:
+ retval = g_value_get_pointer(&value);
+ }
+ }
+ }
+ return retval;
+}
+
+static void on_row_changed_cb(GtkTreeView *treeview, gpointer user_data)
+{
+ VERB1 log("activated row: '%s'", (const
char*)get_column_value_from_row(treeview, COLUMN_NAME, TYPE_STR));
+
+ const void *dialog = get_column_value_from_row(treeview, CONFIG_DIALOG,
TYPE_POINTER);
+ gtk_widget_set_sensitive(GTK_WIDGET(user_data), dialog != NULL);
+}
+
+static void on_configure_button_cb(GtkWidget *button, gpointer user_data)
+{
+ GtkTreeView *tv = (GtkTreeView *)user_data;
+ const void * dialog = get_column_value_from_row(tv, CONFIG_DIALOG, TYPE_POINTER);
+
+ if (dialog != NULL)
+ {
+ int result = gtk_dialog_run(GTK_DIALOG(dialog));
+ if (result == GTK_RESPONSE_APPLY)
+ {
+ //TODO: saving!!!
+ g_print("apply\n");
+ }
+ //else if (result == GTK_RESPONSE_CANCEL)
+ // log("log");
+ gtk_widget_hide(GTK_WIDGET(dialog));
+ }
+}
+
+static void on_close_list_cb(GtkWidget *button, gpointer user_data)
+{
+ GtkWidget *window = (GtkWidget *)user_data;
+ gtk_widget_destroy(window);
+}
+
+void add_item_to_config_liststore(gpointer dialog, gpointer inf, gpointer user_data)
+{
+ GtkListStore *list_store = (GtkListStore *)user_data;
+ config_item_info_t *info = (config_item_info_t *)inf;
+
+ VERB1 log("adding '%s' to workflow list\n", info->screen_name);
+ char *label;
+ if (ci_get_screen_name(info) != NULL && ci_get_description(info) != NULL)
+ label = xasprintf("<b>%s</b>\n%s",ci_get_screen_name(info),
ci_get_description(info));
+ else
+ //if event has no xml description
+ label = xasprintf("<b>%s</b>\nNo description available",
ci_get_name(info));
+
+ GtkTreeIter iter;
+ gtk_list_store_append(list_store, &iter);
+ gtk_list_store_set(list_store, &iter,
+ COLUMN_UINAME, label,
+ COLUMN_NAME, ci_get_name(info),
+ CONFIG_DIALOG, dialog,
+ -1);
+ free(label);
+}
+
+GtkWidget *create_config_list_dialog(const char *column_label,
+ GHashTable *items,
+ GtkWindow *dialog,
+ GHFunc item_to_config_info,
+ GCallback on_config_cb,
+ GCallback on_row_change)
+{
+ GtkWidget *main_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
+ GtkWidget *scroll = gtk_scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
+ GTK_POLICY_NEVER,
+ GTK_POLICY_AUTOMATIC);
+ /* workflow list treeview */
+ GtkWidget *tv = gtk_tree_view_new();
+ /* column with workflow name and description */
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+
+ /* add column to tree view */
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes(column_label,
+ renderer,
+ "markup",
+ COLUMN_UINAME,
+ NULL);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ g_object_set(G_OBJECT(renderer), "wrap-mode", PANGO_WRAP_WORD, NULL);
+ g_object_set(G_OBJECT(renderer), "wrap-width", 440, NULL);
+ gtk_tree_view_column_set_sort_column_id(column, COLUMN_NAME);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(tv), column);
+ /* "Please draw rows in alternating colors": */
+ gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(tv), TRUE);
+ // TODO: gtk_tree_view_set_headers_visible(FALSE)? We have only one column anyway...
+
+ /* Create data store for the list and attach it
+ * COLUMN_UINAME -> name+description
+ * COLUMN_NAME -> workflow name so we can retrieve it from the row
+ */
+ GtkListStore *list_store = new_conf_liststore();
+ gtk_tree_view_set_model(GTK_TREE_VIEW(tv), GTK_TREE_MODEL(list_store));
+
+ g_hash_table_foreach(items,
+ item_to_config_info,
+ list_store);
+//TODO: can unref workflows_list_store? treeview holds one ref.
+
+ /* Double click/Enter handler */
+ //g_signal_connect(workflows_tv, "row-activated",
G_CALLBACK(on_workflow_row_activated_cb), NULL);
+
+ gtk_container_add(GTK_CONTAINER(scroll), tv);
+
+ GtkWidget *configure_btn = gtk_button_new_with_mnemonic(_("C_onfigure"));
+ gtk_widget_set_sensitive(configure_btn, false);
+ g_signal_connect(configure_btn, "clicked",
G_CALLBACK(on_configure_button_cb), tv);
+ g_signal_connect(tv, "cursor-changed", G_CALLBACK(on_row_changed_cb),
configure_btn);
+
+ GtkWidget *close_btn = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
+ g_signal_connect(close_btn, "clicked", G_CALLBACK(on_close_list_cb),
dialog);
+
+ GtkWidget *btnbox = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
+ gtk_box_pack_end(GTK_BOX(btnbox), close_btn, false, false, 0);
+ gtk_box_pack_end(GTK_BOX(btnbox), configure_btn, false, false, 0);
+
+ gtk_box_pack_start(GTK_BOX(main_vbox), scroll, true, true, 10);
+ gtk_box_pack_start(GTK_BOX(main_vbox), btnbox, false, false, 0);
+
+ return main_vbox;
+}
diff --git a/src/gtk-helpers/event_config_dialog.c
b/src/gtk-helpers/event_config_dialog.c
index f135c9a..01527ec 100644
--- a/src/gtk-helpers/event_config_dialog.c
+++ b/src/gtk-helpers/event_config_dialog.c
@@ -177,80 +177,6 @@ static void add_option_to_table(gpointer data, gpointer user_data)
free(option_label);
}
-static void on_close_event_list_cb(GtkWidget *button, gpointer user_data)
-{
- GtkWidget *window = (GtkWidget *)user_data;
- gtk_widget_destroy(window);
-}
-
-static char *get_event_name_from_row(GtkTreeView *treeview)
-{
- GtkTreeSelection *selection = gtk_tree_view_get_selection(treeview);
- char *event_name = NULL;
- if (selection)
- {
- GtkTreeIter iter;
- GtkTreeModel *store = gtk_tree_view_get_model(treeview);
- if (gtk_tree_selection_get_selected(selection, &store, &iter) == TRUE)
- {
- GValue value = { 0 };
- gtk_tree_model_get_value(store, &iter, COLUMN_EVENT_NAME, &value);
- event_name = (char *)g_value_get_string(&value);
- }
- }
- return event_name;
-}
-
-static void on_configure_event_cb(GtkWidget *button, gpointer user_data)
-{
- GtkTreeView *events_tv = (GtkTreeView *)user_data;
- char *event_name = get_event_name_from_row(events_tv);
- if (event_name != NULL)
- show_event_config_dialog(event_name, NULL);
- //else
- // error_msg(_("Please select a plugin from the list to edit its
options."));
-}
-
-static void on_event_row_activated_cb(GtkTreeView *treeview, GtkTreePath *path,
GtkTreeViewColumn *column, gpointer user_data)
-{
- char *event_name = get_event_name_from_row(treeview);
- event_config_t *ec = get_event_config(event_name);
- if (ec->options != NULL) //We need to have some options to show
- show_event_config_dialog(event_name, NULL);
-}
-
-static void on_event_row_changed_cb(GtkTreeView *treeview, gpointer user_data)
-{
- const char *event_name = get_event_name_from_row(treeview);
-
- if (event_name)
- {
- event_config_t *ec = get_event_config(event_name);
- gtk_widget_set_sensitive(GTK_WIDGET(user_data), ec->options != NULL);
- }
-}
-
-static void add_event_to_liststore(gpointer key, gpointer value, gpointer user_data)
-{
- GtkListStore *events_list_store = (GtkListStore *)user_data;
- event_config_t *ec = (event_config_t *)value;
-
- char *event_label;
- if (ec_get_screen_name(ec) != NULL && ec_get_description(ec) != NULL)
- event_label = xasprintf("<b>%s</b>\n%s",
ec_get_screen_name(ec), ec_get_description(ec));
- else
- //if event has no xml description
- event_label = xasprintf("<b>%s</b>\nNo description
available", key);
-
- GtkTreeIter iter;
- gtk_list_store_append(events_list_store, &iter);
- gtk_list_store_set(events_list_store, &iter,
- COLUMN_EVENT_UINAME, event_label,
- COLUMN_EVENT_NAME, key,
- -1);
- free(event_label);
-}
-
static void save_value_from_widget(gpointer data, gpointer user_data)
{
option_widget_t *ow = (option_widget_t *)data;
@@ -283,43 +209,12 @@ static void dehydrate_config_dialog()
g_list_foreach(option_widget_list, &save_value_from_widget, NULL);
}
-int show_event_config_dialog(const char *event_name, GtkWindow *parent)
+GtkWidget *create_event_config_dialog_content(event_config_t *event, GtkWidget *content)
{
- if (option_widget_list != NULL)
- {
- g_list_free(option_widget_list);
- option_widget_list = NULL;
- }
-
- event_config_t *event = get_event_config(event_name);
-
- GtkWindow *parent_window = parent ? parent : g_event_list_window;
-
- GtkWidget *dialog = gtk_dialog_new_with_buttons(
- /*title:*/ ec_get_screen_name(event) ? ec_get_screen_name(event)
: event_name,
- parent_window,
- GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
- GTK_STOCK_CANCEL,
- GTK_RESPONSE_CANCEL,
- GTK_STOCK_OK,
- GTK_RESPONSE_APPLY,
- NULL);
+ if (content == NULL)
+ content = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
- /* Allow resize?
- * W/o resize, e.g. upload configuration hint looks awfully
- * line wrapped.
- * With resize, there are some somewhat not nice effects:
- * for one, opening an expander will enlarge window,
- * but won't contract it back when expander is closed.
- */
- gtk_window_set_resizable(GTK_WINDOW(dialog), true);
- gtk_window_set_default_size(GTK_WINDOW(dialog), 450, -1);
-
- if (parent_window != NULL)
- {
- gtk_window_set_icon_name(GTK_WINDOW(dialog),
- gtk_window_get_icon_name(parent_window));
- }
+ //event_config_t *event = get_event_config(event_name);
GtkWidget *option_table = gtk_grid_new();
gtk_grid_set_row_homogeneous(GTK_GRID(option_table), FALSE);
@@ -359,7 +254,6 @@ int show_event_config_dialog(const char *event_name, GtkWindow
*parent)
g_signal_connect(pass_store_cb, "toggled",
G_CALLBACK(on_show_pass_store_cb), NULL);
}
- GtkWidget *content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
gtk_box_pack_start(GTK_BOX(content), option_table, false, false, 20);
/* add the adv_option_table to the dialog only if there is some adv option */
@@ -379,6 +273,106 @@ int show_event_config_dialog(const char *event_name, GtkWindow
*parent)
gtk_box_pack_start(GTK_BOX(content), keyring_warn_lbl, false, false, 0);
}
+ gtk_widget_show_all(content); //make it all visible
+
+ return content;
+}
+
+GtkWidget *create_config_dialog(const char *event_name, GtkWindow *parent)
+{
+ if (option_widget_list != NULL)
+ {
+ g_list_free(option_widget_list);
+ option_widget_list = NULL;
+ }
+
+ event_config_t *event = get_event_config(event_name);
+
+ if(!ec_is_configurable(event))
+ return NULL;
+
+ GtkWindow *parent_window = parent ? parent : g_event_list_window;
+
+ GtkWidget *dialog = gtk_dialog_new_with_buttons(
+ /*title:*/ec_get_screen_name(event) ? ec_get_screen_name(event) :
event_name,
+ parent_window,
+ GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_STOCK_CANCEL,
+ GTK_RESPONSE_CANCEL,
+ GTK_STOCK_OK,
+ GTK_RESPONSE_APPLY,
+ NULL);
+
+ /* Allow resize?
+ * W/o resize, e.g. upload configuration hint looks awfully
+ * line wrapped.
+ * With resize, there are some somewhat not nice effects:
+ * for one, opening an expander will enlarge window,
+ * but won't contract it back when expander is closed.
+ */
+ gtk_window_set_resizable(GTK_WINDOW(dialog), true);
+ gtk_window_set_default_size(GTK_WINDOW(dialog), 450, -1);
+
+ if (parent_window != NULL)
+ {
+ gtk_window_set_icon_name(GTK_WINDOW(dialog),
+ gtk_window_get_icon_name(parent_window));
+ }
+
+ GtkWidget *content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
+ content = create_event_config_dialog_content(event, content);
+
+ return dialog;
+}
+
+static void add_event_to_liststore(gpointer key, gpointer value, gpointer user_data)
+{
+ config_item_info_t *info = ec_get_config_info((event_config_t *)value);
+ GtkWidget *dialog = create_config_dialog(key, NULL);
+ add_item_to_config_liststore(dialog, info, user_data);
+}
+
+int show_event_config_dialog(const char *event_name, GtkWindow *parent)
+{
+ if (option_widget_list != NULL)
+ {
+ g_list_free(option_widget_list);
+ option_widget_list = NULL;
+ }
+
+ event_config_t *event = get_event_config(event_name);
+
+ GtkWindow *parent_window = parent ? parent : g_event_list_window;
+
+ GtkWidget *dialog = gtk_dialog_new_with_buttons(
+ /*title:*/ec_get_screen_name(event) ? ec_get_screen_name(event) :
event_name,
+ parent_window,
+ GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_STOCK_CANCEL,
+ GTK_RESPONSE_CANCEL,
+ GTK_STOCK_OK,
+ GTK_RESPONSE_APPLY,
+ NULL);
+
+ /* Allow resize?
+ * W/o resize, e.g. upload configuration hint looks awfully
+ * line wrapped.
+ * With resize, there are some somewhat not nice effects:
+ * for one, opening an expander will enlarge window,
+ * but won't contract it back when expander is closed.
+ */
+ gtk_window_set_resizable(GTK_WINDOW(dialog), true);
+ gtk_window_set_default_size(GTK_WINDOW(dialog), 450, -1);
+
+ if (parent_window != NULL)
+ {
+ gtk_window_set_icon_name(GTK_WINDOW(dialog),
+ gtk_window_get_icon_name(parent_window));
+ }
+
+ GtkWidget *content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
+ content = create_event_config_dialog_content(event, content);
+
gtk_widget_show_all(content);
int result = gtk_dialog_run(GTK_DIALOG(dialog));
@@ -421,67 +415,7 @@ void show_events_list_dialog(GtkWindow *parent)
gtk_window_get_icon_name(parent));
}
- GtkWidget *main_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
- GtkWidget *events_scroll = gtk_scrolled_window_new(NULL, NULL);
- gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(events_scroll),
- GTK_POLICY_NEVER,
- GTK_POLICY_AUTOMATIC);
- /* event list treeview */
- GtkWidget *events_tv = gtk_tree_view_new();
- /* column with event name and description */
- GtkCellRenderer *renderer;
- GtkTreeViewColumn *column;
-
- /* add column to tree view */
- renderer = gtk_cell_renderer_text_new();
- column = gtk_tree_view_column_new_with_attributes(_("Event"),
- renderer,
- "markup",
- COLUMN_EVENT_UINAME,
- NULL);
- gtk_tree_view_column_set_resizable(column, TRUE);
- g_object_set(G_OBJECT(renderer), "wrap-mode", PANGO_WRAP_WORD, NULL);
- g_object_set(G_OBJECT(renderer), "wrap-width", 440, NULL);
- gtk_tree_view_column_set_sort_column_id(column, COLUMN_EVENT_NAME);
- gtk_tree_view_append_column(GTK_TREE_VIEW(events_tv), column);
- /* "Please draw rows in alternating colors": */
- gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(events_tv), TRUE);
- // TODO: gtk_tree_view_set_headers_visible(FALSE)? We have only one column anyway...
-
- /* Create data store for the list and attach it
- * COLUMN_EVENT_UINAME -> name+description
- * COLUMN_EVENT_NAME -> event name so we can retrieve it from the row
- */
- GtkListStore *events_list_store = gtk_list_store_new(NUM_COLUMNS,
- G_TYPE_STRING, /* Event name +
description */
- G_TYPE_STRING /* event name */
- );
- gtk_tree_view_set_model(GTK_TREE_VIEW(events_tv),
GTK_TREE_MODEL(events_list_store));
-
- g_hash_table_foreach(g_event_config_list,
- &add_event_to_liststore,
- events_list_store);
-//TODO: can unref events_list_store? treeview holds one ref.
-
- /* Double click/Enter handler */
- g_signal_connect(events_tv, "row-activated",
G_CALLBACK(on_event_row_activated_cb), NULL);
-
- gtk_container_add(GTK_CONTAINER(events_scroll), events_tv);
-
- GtkWidget *configure_event_btn = gtk_button_new_with_mnemonic(_("Configure
E_vent"));
- gtk_widget_set_sensitive(configure_event_btn, false);
- g_signal_connect(configure_event_btn, "clicked",
G_CALLBACK(on_configure_event_cb), events_tv);
- g_signal_connect(events_tv, "cursor-changed",
G_CALLBACK(on_event_row_changed_cb), configure_event_btn);
-
- GtkWidget *close_btn = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
- g_signal_connect(close_btn, "clicked", G_CALLBACK(on_close_event_list_cb),
g_event_list_window);
-
- GtkWidget *btnbox = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
- gtk_box_pack_end(GTK_BOX(btnbox), close_btn, false, false, 0);
- gtk_box_pack_end(GTK_BOX(btnbox), configure_event_btn, false, false, 0);
-
- gtk_box_pack_start(GTK_BOX(main_vbox), events_scroll, true, true, 10);
- gtk_box_pack_start(GTK_BOX(main_vbox), btnbox, false, false, 0);
+ GtkWidget *main_vbox = create_config_list_dialog(_("Event"),
g_event_config_list, GTK_WINDOW(event_list_window), &add_event_to_liststore, NULL,
NULL);
gtk_container_add(GTK_CONTAINER(event_list_window), main_vbox);
diff --git a/src/gtk-helpers/internal_libreport_gtk.h
b/src/gtk-helpers/internal_libreport_gtk.h
index 5cb965c..5e631cb 100644
--- a/src/gtk-helpers/internal_libreport_gtk.h
+++ b/src/gtk-helpers/internal_libreport_gtk.h
@@ -37,7 +37,7 @@ void show_events_list_dialog(GtkWindow *parent);
bool is_event_config_user_storage_available();
#define load_single_event_config_data_from_user_storage
libreport_load_single_event_config_data_from_user_storage
-void load_single_event_config_data_from_user_storage(const char *event_name,
event_config_t *config);
+void load_single_event_config_data_from_user_storage(event_config_t *config);
#define load_event_config_data_from_user_storage
libreport_load_event_config_data_from_user_storage
void load_event_config_data_from_user_storage(GHashTable *event_config_list);
@@ -50,6 +50,24 @@ void save_event_config_data_to_user_storage(const char *event_name,
#define show_event_config_dialog libreport_show_event_config_dialog
int show_event_config_dialog(const char *event_name, GtkWindow *parent);
+#define create_event_config_dialog_content libreport_create_event_config_dialog_content
+GtkWidget *create_event_config_dialog_content(event_config_t *event, GtkWidget
*content);
+
+#define show_workflow_list_dialog libreport_show_workflow_list_dialog
+void show_workflow_list_dialog(GtkWindow *parent);
+
+#define add_item_to_config_liststore libreport_add_item_to_config_liststore
+void add_item_to_config_liststore(gpointer key, gpointer value, gpointer user_data);
+
+#define create_config_list_dialog libreport_create_config_list_dialog
+GtkWidget *create_config_list_dialog(const char *column_label,
+ GHashTable *items,
+ GtkWindow *dialog,
+ GHFunc item_to_config_info,
+ GCallback on_config_cb,
+ GCallback on_row_change);
+GtkListStore *new_conf_liststore(void);
+
char * tag_url(const char* line, const char* prefix);
#define url_token libreport_url_token
diff --git a/src/gtk-helpers/secrets.c b/src/gtk-helpers/secrets.c
index 468c508..9de2284 100644
--- a/src/gtk-helpers/secrets.c
+++ b/src/gtk-helpers/secrets.c
@@ -1378,7 +1378,7 @@ bool is_event_config_user_storage_available()
* @param name Event name
* @param config Event config
*/
-void load_single_event_config_data_from_user_storage(const char *event_name,
event_config_t *config)
+void load_single_event_config_data_from_user_storage(event_config_t *config)
{
GHashTable *tmp = g_hash_table_new_full(
/*hash_func*/ g_str_hash,
@@ -1386,7 +1386,7 @@ void load_single_event_config_data_from_user_storage(const char
*event_name, eve
/*key_destroy_func:*/ g_free,
/*value_destroy_func:*/ NULL);
- g_hash_table_insert(tmp, xstrdup(event_name), config);
+ g_hash_table_insert(tmp, xstrdup(ec_get_name(config)), config);
load_event_config_data_from_user_storage(tmp);
diff --git a/src/gtk-helpers/workflow_config_dialog.c
b/src/gtk-helpers/workflow_config_dialog.c
new file mode 100644
index 0000000..a044c3a
--- /dev/null
+++ b/src/gtk-helpers/workflow_config_dialog.c
@@ -0,0 +1,117 @@
+/*
+ Copyright (C) 2011 ABRT Team
+ Copyright (C) 2011 RedHat inc.
+
+ 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.
+*/
+#include <gtk/gtk.h>
+#include "internal_libreport_gtk.h"
+
+enum
+{
+ COLUMN_WORKFLOW_UINAME,
+ COLUMN_WORKFLOW_NAME,
+ NUM_COLUMNS
+};
+
+static GtkWindow *g_parent_window;
+
+static void create_event_config_dialog_content_cb(event_config_t *ec, gpointer content)
+{
+ create_event_config_dialog_content(ec, (GtkWidget *)content);
+}
+
+GtkWidget *create_workflow_config_dialog(const char *workflow_name, GtkWindow *parent)
+{
+ workflow_t *workflow = get_workflow(workflow_name);
+ GList *events = workflow->events;
+
+ GtkWindow *parent_window = parent ? parent : g_parent_window;
+
+ GtkWidget *dialog = gtk_dialog_new_with_buttons(
+ /*title:*/ wf_get_screen_name(workflow) ?
wf_get_screen_name(workflow) : workflow_name,
+ parent_window,
+ GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_STOCK_CANCEL,
+ GTK_RESPONSE_CANCEL,
+ GTK_STOCK_OK,
+ GTK_RESPONSE_APPLY,
+ NULL);
+
+ gtk_window_set_resizable(GTK_WINDOW(dialog), true);
+ gtk_window_set_default_size(GTK_WINDOW(dialog), 450, -1);
+
+ if (parent_window != NULL)
+ {
+ gtk_window_set_icon_name(GTK_WINDOW(dialog),
+ gtk_window_get_icon_name(parent_window));
+ }
+
+ GtkWidget *content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
+
+ g_list_foreach(events, (GFunc)create_event_config_dialog_content_cb, content);
+
+ return dialog;
+}
+
+static void add_workflow_to_liststore(gpointer key, gpointer value, gpointer user_data)
+{
+ config_item_info_t *info = workflow_get_config_info((workflow_t *)value);
+ GtkWidget *dialog = create_workflow_config_dialog(key, g_parent_window);
+ add_item_to_config_liststore(dialog, info, user_data);
+}
+
+static void load_single_event_config_foreach(event_config_t *ec, gpointer user_data)
+{
+ load_single_event_config_data_from_user_storage(ec);
+}
+
+static void load_events_foreach_workflow(const char *name, workflow_t *workflow, gpointer
user_data)
+{
+ g_list_foreach(wf_get_event_list(workflow), (GFunc)load_single_event_config_foreach,
NULL);
+}
+
+void show_workflow_list_dialog(GtkWindow *parent)
+{
+ g_parent_window = parent;
+ //g_verbose = 3;
+ if (g_workflow_list == NULL)
+ {
+ VERB1 log("workflow list is empty - reloading");
+ load_workflow_config_data(WORKFLOWS_DIR);
+ }
+
+ g_hash_table_foreach(g_workflow_list, (GHFunc)load_events_foreach_workflow, NULL);
+
+ GtkWindow *workflow_list_window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
+ gtk_window_set_title(workflow_list_window, _("Workflow Configuration"));
+ gtk_window_set_default_size(workflow_list_window, 450, 400);
+ gtk_window_set_position(workflow_list_window, parent ? GTK_WIN_POS_CENTER_ON_PARENT :
GTK_WIN_POS_CENTER);
+ if (parent != NULL)
+ {
+ gtk_window_set_transient_for(workflow_list_window, parent);
+ // modal = parent window can't steal focus
+ gtk_window_set_modal(workflow_list_window, true);
+ gtk_window_set_icon_name(workflow_list_window,
+ gtk_window_get_icon_name(parent));
+ }
+
+ GtkWidget *main_vbox = create_config_list_dialog(_("Workflow"),
g_workflow_list, workflow_list_window, add_workflow_to_liststore, NULL, NULL);
+
+ gtk_container_add(GTK_CONTAINER(workflow_list_window), main_vbox);
+
+ gtk_widget_show_all(GTK_WIDGET(workflow_list_window));
+
+}
diff --git a/src/gui-wizard-gtk/Makefile.am b/src/gui-wizard-gtk/Makefile.am
index ecd1887..46c1d65 100644
--- a/src/gui-wizard-gtk/Makefile.am
+++ b/src/gui-wizard-gtk/Makefile.am
@@ -19,6 +19,7 @@ report_gtk_CFLAGS = \
-DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" \
-DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \
-DICON_DIR=\"${datadir}/abrt/icons/hicolor/48x48/status\" \
+ -DWORKFLOWS_DIR=\"$(WORKFLOWS_DIR)\" \
$(GLIB_CFLAGS) \
$(GTK_CFLAGS) \
-D_GNU_SOURCE
diff --git a/src/gui-wizard-gtk/main.c b/src/gui-wizard-gtk/main.c
index 08ff14d..03f8fd2 100644
--- a/src/gui-wizard-gtk/main.c
+++ b/src/gui-wizard-gtk/main.c
@@ -144,6 +144,8 @@ int main(int argc, char **argv)
load_event_config_data_from_user_storage(g_event_config_list);
load_user_settings("report-gtk");
+ load_workflow_config_data(WORKFLOWS_DIR);
+
problem_data_reload_from_dump_dir();
create_assistant(expert_mode);
diff --git a/src/gui-wizard-gtk/wizard.c b/src/gui-wizard-gtk/wizard.c
index 54202e9..50dee85 100644
--- a/src/gui-wizard-gtk/wizard.c
+++ b/src/gui-wizard-gtk/wizard.c
@@ -64,6 +64,7 @@ static GtkWidget *g_btn_close;
static GtkWidget *g_btn_next;
static GtkBox *g_box_events;
+static GtkBox *g_box_workflows;
/* List of event_gui_data's */
static GList *g_list_events;
static GtkLabel *g_lbl_event_log;
@@ -104,6 +105,9 @@ static GtkImage *g_img_process_ok;
static GtkWidget *g_top_most_window;
+static void add_workflow_buttons(GtkBox *box, GHashTable *workflows, GCallback func);
+static void set_auto_event_chain(GtkButton *button, gpointer user_data);
+
typedef struct
{
int page; //which tab in notepad
@@ -972,6 +976,7 @@ static char *missing_items_in_comma_list(const char *input_item_list)
}
return result;
}
+
static event_gui_data_t *add_event_buttons(GtkBox *box,
GList **p_event_list,
char *event_name,
@@ -1318,13 +1323,24 @@ void update_gui_state_from_problem_data(int flags)
load_text_to_text_view(g_tv_comment, FILENAME_COMMENT);
- /* Update event radio buttons */
- event_gui_data_t *active_button = add_event_buttons(
- g_box_events,
- &g_list_events,
- g_events,
- G_CALLBACK(event_rb_was_toggled)
- );
+ add_workflow_buttons(g_box_workflows, g_workflow_list,
+ G_CALLBACK(set_auto_event_chain));
+
+ /* Update event radio buttons
+ * show them only in expert mode
+ */
+ event_gui_data_t *active_button = NULL;
+ if (g_expert_mode == true)
+ {
+ //this widget doesn't react to show_all, so we need to "force" it
+ gtk_widget_show(GTK_WIDGET(g_box_events));
+ active_button = add_event_buttons(
+ g_box_events,
+ &g_list_events,
+ g_events,
+ G_CALLBACK(event_rb_was_toggled)
+ );
+ }
if (flags & UPDATE_SELECTED_EVENT && g_expert_mode)
{
@@ -1337,7 +1353,6 @@ void update_gui_state_from_problem_data(int flags)
}
VERB2 log("g_event_selected='%s'", g_event_selected);
}
-
/* We can't just do gtk_widget_show_all once in main:
* We created new widgets (buttons). Need to make them visible.
*/
@@ -1505,8 +1520,11 @@ static bool is_processing_finished()
return !g_expert_mode && !g_auto_event_list;
}
-static void update_gui_on_finished_reporting()
+static void hide_next_step_button()
{
+ if(gtk_widget_get_visible(GTK_WIDGET(g_btn_next)) == false)
+ return
+
/* replace 'Forward' with 'Close' button */
/* 1. hide next button */
gtk_widget_hide(g_btn_next);
@@ -1514,6 +1532,15 @@ static void update_gui_on_finished_reporting()
gtk_box_reorder_child(g_box_buttons, g_btn_close, 3);
}
+static void show_next_step_button()
+{
+ if(gtk_widget_get_visible(GTK_WIDGET(g_btn_next)) == true)
+ return
+
+ gtk_box_reorder_child(g_box_buttons, g_btn_close, 0);
+ gtk_widget_show(g_btn_next);
+}
+
static void terminate_event_chain()
{
if (g_expert_mode)
@@ -1525,7 +1552,7 @@ static void terminate_event_chain()
g_list_free_full(g_auto_event_list, free);
g_auto_event_list = NULL;
- update_gui_on_finished_reporting();
+ hide_next_step_button();
}
static void update_command_run_log(const char* message, struct analyze_event_data *evd)
@@ -1788,7 +1815,7 @@ static gboolean consume_cmd_output(GIOChannel *source, GIOCondition
condition, g
kill(getppid(), SIGCHLD);
if (is_processing_finished())
- update_gui_on_finished_reporting();
+ hide_next_step_button();
else if (retval == 0 && !g_verbose && !g_expert_mode)
on_next_btn_cb(GTK_WIDGET(g_btn_next), NULL);
@@ -2342,6 +2369,9 @@ static void on_page_prepare(GtkNotebook *assistant, GtkWidget *page,
gpointer us
save_items_from_notepad();
save_text_from_text_view(g_tv_comment, FILENAME_COMMENT);
+ //some pages hide it, so restore it to it's default
+ show_next_step_button();
+
if (pages[PAGENO_SUMMARY].page_widget == page)
{
if (!g_expert_mode)
@@ -2400,6 +2430,60 @@ static void on_page_prepare(GtkNotebook *assistant, GtkWidget
*page, gpointer us
setup_and_start_event_run(g_event_selected);
}
}
+
+ if(pages[PAGENO_EVENT_SELECTOR].page_widget == page)
+ {
+ if (!g_expert_mode && !g_auto_event_list)
+ hide_next_step_button();
+ }
+}
+
+//static void event_rb_was_toggled(GtkButton *button, gpointer user_data)
+static void set_auto_event_chain(GtkButton *button, gpointer user_data)
+{
+ //event is selected, so make sure the expert mode is disabled
+ g_expert_mode = false;
+
+ workflow_t *w = (workflow_t *)user_data;
+ config_item_info_t *info = workflow_get_config_info(w);
+ VERB1 log("selected workflow '%s'", info->screen_name);
+
+ GList *wf_event_list = wf_get_event_list(w);
+ while(wf_event_list)
+ {
+ g_auto_event_list = g_list_append(g_auto_event_list,
xstrdup(ec_get_name(wf_event_list->data)));
+ load_single_event_config_data_from_user_storage((event_config_t
*)wf_event_list->data);
+
+ wf_event_list = g_list_next(wf_event_list);
+ }
+
+ gint current_page_no = gtk_notebook_get_current_page(g_assistant);
+ gint next_page_no = select_next_page_no(current_page_no, NULL);
+
+ /* if pageno is not change 'switch-page' signal is not emitted */
+ if (current_page_no == next_page_no)
+ on_page_prepare(g_assistant, gtk_notebook_get_nth_page(g_assistant,
next_page_no), NULL);
+ else
+ gtk_notebook_set_current_page(g_assistant, next_page_no);
+}
+
+static void add_workflow_buttons(GtkBox *box, GHashTable *workflows, GCallback func)
+{
+ GList *keys = g_hash_table_get_keys(g_workflow_list);
+ while(keys)
+ {
+ workflow_t *w = g_hash_table_lookup(g_workflow_list, keys->data);
+ char *btn_label = xasprintf("<b>%s</b>\n%s",
wf_get_screen_name(w), wf_get_description(w));
+ GtkWidget *button = gtk_button_new_with_label(btn_label);
+ GList *children = gtk_container_get_children(GTK_CONTAINER(button));
+ GtkWidget *label = (GtkWidget *)children->data;
+ gtk_label_set_use_markup(GTK_LABEL(label), true);
+ free(btn_label);
+ g_signal_connect(button, "clicked", func, w);
+ gtk_box_pack_start(box, button, true, false, 2);
+ keys = g_list_next(keys);
+ }
+
}
static char *setup_next_processed_event(GList **events_list)
@@ -2416,7 +2500,7 @@ static char *setup_next_processed_event(GList **events_list)
* so at least hide the spinner, because we're obviously finished
*/
gtk_widget_hide(GTK_WIDGET(g_spinner_event_log));
- update_gui_on_finished_reporting();
+ hide_next_step_button();
return NULL;
}
@@ -2451,6 +2535,11 @@ static gint select_next_page_no(gint current_page_no, gpointer
data)
if (pages[PAGENO_EVENT_SELECTOR].page_widget == page)
{
+ if (!g_expert_mode && (g_auto_event_list == NULL))
+ {
+ return current_page_no; //stay here and let user select the workflow
+ }
+
if (!g_expert_mode)
{
/* (note: this frees and sets to NULL g_event_selected) */
@@ -2883,6 +2972,7 @@ static void add_pages(void)
/* Set pointers to objects we might need to work with */
g_lbl_cd_reason = GTK_LABEL( gtk_builder_get_object(g_builder,
"lbl_cd_reason"));
g_box_events = GTK_BOX( gtk_builder_get_object(g_builder,
"vb_events"));
+ g_box_workflows = GTK_BOX( gtk_builder_get_object(g_builder,
"vb_workflows"));
g_lbl_event_log = GTK_LABEL( gtk_builder_get_object(g_builder,
"lbl_event_log"));
g_tv_event_log = GTK_TEXT_VIEW( gtk_builder_get_object(g_builder,
"tv_event_log"));
g_tv_comment = GTK_TEXT_VIEW( gtk_builder_get_object(g_builder,
"tv_comment"));
@@ -2991,7 +3081,7 @@ static void init_page(page_obj_t *page, const char *name, const char
*title)
static void init_pages(void)
{
init_page(&pages[0], PAGE_SUMMARY , _("Problem description")
);
- init_page(&pages[1], PAGE_EVENT_SELECTOR , _("Select operation")
);
+ init_page(&pages[1], PAGE_EVENT_SELECTOR , _("Select how to report this
problem"));
init_page(&pages[2], PAGE_EDIT_COMMENT,_("Provide additional
information"));
init_page(&pages[3], PAGE_EDIT_ELEMENTS , _("Review the data")
);
init_page(&pages[4], PAGE_REVIEW_DATA , _("Confirm data to
report"));
@@ -3007,11 +3097,11 @@ static void assistant_quit_cb(void *obj, void *data)
gtk_main_quit();
}
-void create_assistant(void)
+void create_assistant(bool expert_mode)
{
g_loaded_texts = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
- g_expert_mode = !g_auto_event_list;
+ g_expert_mode = expert_mode;
g_monospace_font = pango_font_description_from_string("monospace");
g_builder = gtk_builder_new();
diff --git a/src/gui-wizard-gtk/wizard.glade b/src/gui-wizard-gtk/wizard.glade
index 1ed1b6d..625cd7c 100644
--- a/src/gui-wizard-gtk/wizard.glade
+++ b/src/gui-wizard-gtk/wizard.glade
@@ -183,9 +183,65 @@
<property name="border_width">10</property>
<property name="spacing">3</property>
<child>
+ <object class="GtkBox" id="vb_workflows_aligned">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkAlignment" id="alignment2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="vb_workflows">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkAlignment" id="alignment1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
<object class="GtkVBox" id="vb_events">
<property name="visible">True</property>
<property name="can_focus">False</property>
+ <property name="no_show_all">True</property>
<child>
<placeholder/>
</child>
@@ -658,4 +714,10 @@
</object>
</child>
</object>
+ <object class="GtkSizeGroup" id="sizegroup1">
+ <widgets>
+ <widget name="alignment2"/>
+ <widget name="alignment1"/>
+ </widgets>
+ </object>
</interface>
diff --git a/src/gui-wizard-gtk/wizard.h b/src/gui-wizard-gtk/wizard.h
index d2caaa6..a6d8b5d 100644
--- a/src/gui-wizard-gtk/wizard.h
+++ b/src/gui-wizard-gtk/wizard.h
@@ -21,7 +21,7 @@
#include "internal_libreport_gtk.h"
-void create_assistant(void);
+void create_assistant(bool expert_mode);
enum
{
diff --git a/src/include/Makefile.am b/src/include/Makefile.am
index a9af359..a4dc133 100644
--- a/src/include/Makefile.am
+++ b/src/include/Makefile.am
@@ -7,6 +7,7 @@ libreport_include_HEADERS = \
report.h \
run_event.h \
libreport_curl.h \
+ workflow.h \
\
config_item_info.h \
file_obj.h \
diff --git a/src/include/config_item_info.h b/src/include/config_item_info.h
index dee6bc2..6473dc3 100644
--- a/src/include/config_item_info.h
+++ b/src/include/config_item_info.h
@@ -29,7 +29,7 @@ typedef struct
} config_item_info_t;
-config_item_info_t *new_config_info();
+config_item_info_t *new_config_info(void);
void free_config_info(config_item_info_t *info);
void ci_set_screen_name(config_item_info_t *ci, const char *screen_name);
diff --git a/src/include/file_obj.h b/src/include/file_obj.h
index 9cb293f..08823af 100644
--- a/src/include/file_obj.h
+++ b/src/include/file_obj.h
@@ -24,6 +24,8 @@ typedef struct file_obj
* e.g:
* if fullpath is: /foo/bar/report_Bugzilla.xml
* then filename is: report_Bugzilla
+ * in case of symlink the filename is created from the symlink name
+ * and the fullpath is the symlink target
*/
char *filename;
char *fullpath; //the full path with extension
diff --git a/src/include/internal_libreport.h b/src/include/internal_libreport.h
index d22bcbe..4f85b2a 100644
--- a/src/include/internal_libreport.h
+++ b/src/include/internal_libreport.h
@@ -93,6 +93,7 @@ int vdprintf(int d, const char *format, va_list ap);
#include "problem_data.h"
#include "report.h"
#include "run_event.h"
+#include "workflow.h"
#include "file_obj.h"
#ifdef __cplusplus
@@ -642,6 +643,8 @@ void free_file_list(GList *filelist);
file_obj_t *new_file_obj(const char* fullpath, const char* filename);
#define free_file_obj libreport_free_file_obj
void free_file_obj(file_obj_t *f);
+#define load_workflow_config_data libreport_load_workflow_config_data
+void load_workflow_config_data(const char* path);
/* Connect to abrtd over unix domain socket, issue DELETE command */
int delete_dump_dir_possibly_using_abrtd(const char *dump_dir_name);
diff --git a/src/include/workflow.h b/src/include/workflow.h
new file mode 100644
index 0000000..dc2768c
--- /dev/null
+++ b/src/include/workflow.h
@@ -0,0 +1,51 @@
+/*
+ Copyright (C) 2011 ABRT team
+ Copyright (C) 2010 RedHat Inc
+
+ 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.
+*/
+#ifndef LIBREPORT_WORKFLOW_H
+#define LIBREPORT_WORKFLOW_H
+
+#include <glib.h>
+#include "event_config.h"
+#include "config_item_info.h"
+
+typedef struct
+{
+ config_item_info_t *info;
+
+ GList *events; //list of event_option_t
+} workflow_t;
+
+extern GHashTable *g_workflow_list;
+
+workflow_t *new_workflow(void);
+workflow_t *get_workflow(const char *name);
+void free_workflow(workflow_t *w);
+
+void load_workflow_description_from_file(workflow_t *w, const char *filename);
+config_item_info_t *workflow_get_config_info(workflow_t *w);
+const char *wf_get_name(workflow_t *w);
+GList *wf_get_event_list(workflow_t *w);
+const char *wf_get_screen_name(workflow_t *w);
+const char *wf_get_description(workflow_t *w);
+const char *wf_get_long_desc(workflow_t *w);
+
+void wf_set_name(workflow_t *w, const char* name);
+void wf_set_description(workflow_t *w, const char* description);
+void wf_set_long_desc(workflow_t *w, const char* long_desc);
+
+#endif
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
index 0a7a819..dad9f89 100644
--- a/src/lib/Makefile.am
+++ b/src/lib/Makefile.am
@@ -50,6 +50,8 @@ libreport_la_SOURCES = \
utf8.c \
file_list.c \
file_obj.c \
+ workflow.c \
+ workflow_xml_parser.c \
config_item_info.c
libreport_la_CPPFLAGS = \
-I$(srcdir)/../include \
@@ -60,6 +62,7 @@ libreport_la_CPPFLAGS = \
-DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \
-DCONF_DIR=\"$(CONF_DIR)\" \
-DEVENTS_DIR=\"$(EVENTS_DIR)\" \
+ -DWORKFLOWS_DIR=\"$(WORKFLOWS_DIR)\" \
-DBIN_DIR=\"$(bindir)\" \
$(GLIB_CFLAGS) \
-D_GNU_SOURCE
diff --git a/src/lib/config_item_info.c b/src/lib/config_item_info.c
index 1635e72..f4af30d 100644
--- a/src/lib/config_item_info.c
+++ b/src/lib/config_item_info.c
@@ -21,9 +21,9 @@
//#include "config_item_info.h"
-config_item_info_t *new_config_info()
+config_item_info_t *new_config_info(void)
{
- config_item_info_t *info = (config_item_info_t
*)xzalloc(sizeof(config_item_info_t));
+ config_item_info_t *info = (config_item_info_t *)xzalloc(sizeof(*info));
return info;
}
diff --git a/src/lib/report_event.conf b/src/lib/report_event.conf
index f5c9407..c21bb86 100644
--- a/src/lib/report_event.conf
+++ b/src/lib/report_event.conf
@@ -43,6 +43,7 @@
# and processed. This process is repeated until the end of this file.
include events.d/*.conf
+include workflows.d/*.conf
# For testing purposes only
#EVENT=report_Dummy analyzer=libreport echo "Hello world" >>
/tmp/libreport.log
diff --git a/src/lib/workflow.c b/src/lib/workflow.c
new file mode 100644
index 0000000..715f044
--- /dev/null
+++ b/src/lib/workflow.c
@@ -0,0 +1,144 @@
+/*
+ Copyright (C) 2011 ABRT team
+ Copyright (C) 2010 RedHat Inc
+
+ 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.
+*/
+
+#include "event_config.h"
+#include "workflow.h"
+#include "internal_libreport.h"
+
+GHashTable *g_workflow_list;
+
+workflow_t *new_workflow(void)
+{
+ workflow_t *w = xzalloc(sizeof(*w));
+ w->info = new_config_info();
+ return w;
+}
+
+void free_workflow(workflow_t *w)
+{
+ if (!w)
+ return;
+
+ free_config_info(w->info);
+ g_list_free_full(w->events, (GDestroyNotify)free_event_config);
+ free(w);
+}
+
+static void free_workflow_cb(const char *name, workflow_t *w, gpointer user_data)
+{
+ free_workflow(w);
+}
+
+void free_workflow_list(GHashTable *wl)
+{
+ g_hash_table_foreach(wl, (GHFunc)free_workflow_cb, NULL);
+ g_hash_table_destroy(wl);
+}
+
+workflow_t *get_workflow(const char *name)
+{
+ if (!g_workflow_list)
+ return NULL;
+ /* @@ FIXME: SYMLINKS@!!!
+ if (g_event_config_symlinks)
+ {
+ char *link = g_hash_table_lookup(g_event_config_symlinks, name);
+ if (link)
+ name = link;
+ }
+ */
+ return g_hash_table_lookup(g_workflow_list, name);
+}
+
+void load_workflow_config_data(const char* path)
+{
+ if (g_workflow_list == NULL)
+ {
+ g_workflow_list = g_hash_table_new_full(
+ g_str_hash,
+ g_str_equal,
+ g_free,
+ (GDestroyNotify) free_workflow
+ );
+ }
+
+ GList *workflow_files = get_file_list(path, "xml");
+ while(workflow_files)
+ {
+ file_obj_t *file = (file_obj_t *)workflow_files->data;
+
+ workflow_t *workflow = get_workflow(file->filename);
+ bool nw_workflow = (!workflow);
+ if (nw_workflow)
+ workflow = new_workflow();
+
+ load_workflow_description_from_file(workflow, file->fullpath);
+
+ if (nw_workflow)
+ g_hash_table_replace(g_workflow_list, xstrdup(file->filename), workflow);
+
+ workflow_files = g_list_next(workflow_files);
+ }
+ free_file_list(workflow_files);
+}
+
+config_item_info_t *workflow_get_config_info(workflow_t *w)
+{
+ return w->info;
+}
+
+GList *wf_get_event_list(workflow_t *w)
+{
+ return w->events;
+}
+
+const char *wf_get_name(workflow_t *w)
+{
+ return ci_get_name(workflow_get_config_info(w));
+}
+
+const char *wf_get_screen_name(workflow_t *w)
+{
+ return ci_get_screen_name(workflow_get_config_info(w));
+}
+
+const char *wf_get_description(workflow_t *w)
+{
+ return ci_get_description(workflow_get_config_info(w));
+}
+
+const char *wf_get_long_desc(workflow_t *w)
+{
+ return ci_get_long_desc(workflow_get_config_info(w));
+}
+
+void wf_set_name(workflow_t *w, const char* name)
+{
+ ci_set_name(workflow_get_config_info(w), name);
+}
+
+void wf_set_description(workflow_t *w, const char* description)
+{
+ ci_set_description(workflow_get_config_info(w), description);
+}
+
+void wf_set_long_desc(workflow_t *w, const char* long_desc)
+{
+ ci_set_long_desc(workflow_get_config_info(w), long_desc);
+}
diff --git a/src/lib/workflow_xml_parser.c b/src/lib/workflow_xml_parser.c
new file mode 100644
index 0000000..66d4f97
--- /dev/null
+++ b/src/lib/workflow_xml_parser.c
@@ -0,0 +1,167 @@
+/*
+ Copyright (C) 2011 ABRT Team
+ Copyright (C) 2011 RedHat inc.
+
+ 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.
+*/
+#include "workflow.h"
+#include "internal_libreport.h"
+
+//workflow elements
+#define WORKFLOW_ELEMENT "workflow"
+#define EVENTS_ELEMENT "events"
+#define EVENT_ELEMENT "event"
+#define DESCRIPTION_ELEMENT "description"
+#define NAME_ELEMENT "name"
+
+struct my_parse_data
+{
+ workflow_t *workflow;
+ const char *cur_locale;
+ char *attribute_lang;
+ bool in_event_list;
+};
+
+static void start_element(GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ gpointer user_data,
+ GError **error)
+{
+ //log("start: %s", element_name);
+
+ struct my_parse_data *parse_data = user_data;
+ if (strcmp(element_name, EVENTS_ELEMENT) == 0)
+ {
+ parse_data->in_event_list = true;
+ }
+}
+
+// Called for close tags </foo>
+static void end_element(GMarkupParseContext *context,
+ const gchar *element_name,
+ gpointer user_data,
+ GError **error)
+{
+ struct my_parse_data *parse_data = user_data;
+
+ free(parse_data->attribute_lang);
+ parse_data->attribute_lang = NULL;
+
+ if (strcmp(element_name, EVENTS_ELEMENT) == 0)
+ {
+ parse_data->in_event_list = false;
+ }
+}
+
+// Called for character data
+// text is not nul-terminated
+static void text(GMarkupParseContext *context,
+ const gchar *text,
+ gsize text_len,
+ gpointer user_data,
+ GError **error)
+{
+ struct my_parse_data *parse_data = user_data;
+ workflow_t *workflow = parse_data->workflow;
+
+ const gchar *inner_element = g_markup_parse_context_get_element(context);
+
+ if(parse_data->in_event_list && strcmp(inner_element, EVENT_ELEMENT) ==
0)
+ {
+ event_config_t *ec = new_event_config();
+ char *subevent_filename = xasprintf(EVENTS_DIR"/%s.xml", text);
+
+ load_event_description_from_file(ec, subevent_filename);
+ if (ec_get_screen_name(ec))
+ {
+ workflow->events = g_list_append(workflow->events, ec);
+ VERB2 log("added to ev list: '%s'",
ec_get_screen_name(ec));
+ ec_set_name(ec, text);
+ }
+ else
+ free_event_config(ec);
+
+ free(subevent_filename);
+ }
+
+ if(strcmp(inner_element, NAME_ELEMENT) == 0)
+ {
+ workflow->info->screen_name = xstrdup(text);
+ }
+
+ if(strcmp(inner_element, DESCRIPTION_ELEMENT) == 0)
+ {
+ workflow->info->description = xstrdup(text);
+ }
+}
+
+ // Called for strings that should be re-saved verbatim in this same
+ // position, but are not otherwise interpretable. At the moment
+ // this includes comments and processing instructions.
+ // text is not nul-terminated
+static void passthrough(GMarkupParseContext *context,
+ const gchar *passthrough_text,
+ gsize text_len,
+ gpointer user_data,
+ GError **error)
+{
+ VERB3 log("passthrough");
+}
+
+// Called on error, including one set by other
+// methods in the vtable. The GError should not be freed.
+static void error(GMarkupParseContext *context,
+ GError *error,
+ gpointer user_data)
+{
+ error_msg("error in XML parsing");
+}
+
+void load_workflow_description_from_file(workflow_t *workflow, const char* filename)
+{
+ VERB1 log("loading workflow: '%s'", filename);
+ struct my_parse_data parse_data = { workflow, NULL, NULL, 0};
+ parse_data.cur_locale = setlocale(LC_ALL, NULL);
+
+ GMarkupParser parser;
+ memset(&parser, 0, sizeof(parser)); /* just in case */
+ parser.start_element = &start_element;
+ parser.end_element = &end_element;
+ parser.text = &text;
+ parser.passthrough = &passthrough;
+ parser.error = &error;
+
+ GMarkupParseContext *context = g_markup_parse_context_new(
+ &parser, G_MARKUP_TREAT_CDATA_AS_TEXT,
+ &parse_data, /*GDestroyNotify:*/ NULL);
+
+ FILE* fin = fopen(filename, "r");
+ if (fin != NULL)
+ {
+ size_t read_bytes;
+ char buff[1024];
+ while ((read_bytes = fread(buff, 1, 1024, fin)) != 0)
+ {
+ g_markup_parse_context_parse(context, buff, read_bytes, NULL);
+ }
+ fclose(fin);
+ }
+
+ g_markup_parse_context_free(context);
+
+ free(parse_data.attribute_lang); /* just in case */
+}
diff --git a/src/workflows/Makefile.am b/src/workflows/Makefile.am
new file mode 100644
index 0000000..3cd0797
--- /dev/null
+++ b/src/workflows/Makefile.am
@@ -0,0 +1,16 @@
+workflowsdir = $(WORKFLOWS_DIR)
+
+dist_workflows_DATA = \
+ report_Fedora.xml \
+ report_Upload.xml
+
+workflowsconfdir = $(WORKFLOWS_CONF_DIR)
+
+dist_workflowsconf_DATA =\
+ report_fedora.conf
+
+@INTLTOOL_XML_RULE@
+
+EXTRA_DIST = \
+ report_Fedora.xml.in \
+ report_Upload.xml.in
diff --git a/src/workflows/report_Fedora.xml.in b/src/workflows/report_Fedora.xml.in
new file mode 100644
index 0000000..f9df177
--- /dev/null
+++ b/src/workflows/report_Fedora.xml.in
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<workflow>
+ <_name>Report to Fedora</_name>
+ <_description>Process the report using the Fedora
infrastructure</_description>
+
+ <events>
+ <event>report_uReport</event>
+ <event>collect_*</event>
+ <event>analyze_CCpp</event>
+ <event>report_Bugzilla</event>
+ <event>post_report</event>
+ </events>
+</workflow>
diff --git a/src/workflows/report_Upload.xml.in b/src/workflows/report_Upload.xml.in
new file mode 100644
index 0000000..3965f99
--- /dev/null
+++ b/src/workflows/report_Upload.xml.in
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<workflow>
+ <_name>Upload the problem data to a server</_name>
+ <_description>Analyze the problem locally and upload the data via scp or
ftp</_description>
+
+ <events>
+ <event>collect_*</event>
+ <event>analyze_CCpp</event>
+ <event>report_Uploader</event>
+ </events>
+</workflow>
\ No newline at end of file
diff --git a/src/workflows/report_fedora.conf b/src/workflows/report_fedora.conf
new file mode 100644
index 0000000..9dd86ce
--- /dev/null
+++ b/src/workflows/report_fedora.conf
@@ -0,0 +1,3 @@
+EVENT=report_Fedora analyzer=CCpp
+# this is just a meta event which consists of other events
+# the list is defined in the xml file
--
1.8.0