--- 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@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