From 1be3901b67867477f82c462aaa2f4fca5eebe01e Mon Sep 17 00:00:00 2001 From: Marek Kasik Date: Mon, 19 Feb 2018 01:02:32 +0100 Subject: [PATCH] Printers: Add authentication dialog for print jobs This commit adds popup dialog to jobs dialog which can be accessed if there is a print job which needs authentication. Clicking "Authenticate" button will reveal this dialog where user can enter credential info for the actual printer and clicking the "Authenticate" in it will authenticate all print jobs of this printer which needs authentication info and will send them for printing. https://bugzilla.gnome.org/show_bug.cgi?id=758170 --- panels/printers/jobs-dialog.ui | 273 ++++++++++++++++++++++++- panels/printers/pp-jobs-dialog.c | 330 ++++++++++++++++++++++++++++--- 2 files changed, 579 insertions(+), 24 deletions(-) diff --git a/panels/printers/jobs-dialog.ui b/panels/printers/jobs-dialog.ui index 579b58f0e..0542538ad 100644 --- a/panels/printers/jobs-dialog.ui +++ b/panels/printers/jobs-dialog.ui @@ -1,7 +1,148 @@ - + - + + + False + start + start + + + True + False + 30 + 30 + 20 + 20 + vertical + 20 + + + True + False + 5 + 5 + Enter credentials to print from %s. + + + False + True + 0 + + + + + True + False + 10 + 15 + + + True + False + start + Domain + 1 + + + 0 + 0 + + + + + True + False + start + Username + 1 + + + 0 + 1 + + + + + True + False + start + Password + 1 + + + 0 + 2 + + + + + True + True + True + + + 1 + 0 + + + + + True + True + True + + + 1 + 1 + + + + + True + True + True + False + * + password + + + 1 + 2 + + + + + False + True + 1 + + + + + A_uthenticate + True + True + True + True + end + + + False + True + 2 + + + + + + + + + + + + 600 500 @@ -39,9 +180,125 @@ + vertical + + + False + + + False + False + 0 + + + + + True + False + 0 + vertical + 12 + + + False + 12 + 6 + end + 2 + + + True + True + False + end + authentication_popover + + + True + False + 5 + + + True + False + True + _Authenticate + + + False + True + 0 + + + + + True + False + down + + + False + True + 1 + + + + + + + True + True + 0 + + + + + False + False + end + 0 + + + + + False + 16 + 12 + + + True + False + start + False + 2 Jobs Require Authentication + + + + + + False + True + 0 + + + + + False + False + 0 + + + + + False + True + 0 + + True + False True @@ -93,23 +350,35 @@ True + False No Active Printer Jobs + False + True 1 no-jobs-page + 1 + + False + True + 2 + + + + diff --git a/panels/printers/pp-jobs-dialog.c b/panels/printers/pp-jobs-dialog.c index 6971256c4..7b2993114 100644 --- a/panels/printers/pp-jobs-dialog.c +++ b/panels/printers/pp-jobs-dialog.c @@ -58,9 +58,141 @@ struct _PpJobsDialog { gchar *printer_name; + gchar **actual_auth_info_required; + GCancellable *get_jobs_cancellable; }; +static gboolean +is_info_required (PpJobsDialog *dialog, + const gchar *info) +{ + gboolean required = FALSE; + gint i; + + if (dialog != NULL && dialog->actual_auth_info_required != NULL) + { + for (i = 0; dialog->actual_auth_info_required[i] != NULL; i++) + { + if (g_strcmp0 (dialog->actual_auth_info_required[i], info) == 0) + { + required = TRUE; + break; + } + } + } + + return required; +} + +static gboolean +is_domain_required (PpJobsDialog *dialog) +{ + return is_info_required (dialog, "domain"); +} + +static gboolean +is_username_required (PpJobsDialog *dialog) +{ + return is_info_required (dialog, "username"); +} + +static gboolean +is_password_required (PpJobsDialog *dialog) +{ + return is_info_required (dialog, "password"); +} + +static gboolean +auth_popup_filled (PpJobsDialog *dialog) +{ + gboolean domain_required; + gboolean username_required; + gboolean password_required; + guint16 domain_length; + guint16 username_length; + guint16 password_length; + + domain_required = is_domain_required (dialog); + username_required = is_username_required (dialog); + password_required = is_password_required (dialog); + + domain_length = gtk_entry_get_text_length (GTK_ENTRY (gtk_builder_get_object (dialog->builder, "domain-entry"))); + username_length = gtk_entry_get_text_length (GTK_ENTRY (gtk_builder_get_object (dialog->builder, "username-entry"))); + password_length = gtk_entry_get_text_length (GTK_ENTRY (gtk_builder_get_object (dialog->builder, "password-entry"))); + + return (!domain_required || domain_length > 0) && + (!username_required || username_length > 0) && + (!password_required || password_length > 0); +} + +static void +auth_entries_changed (GtkEntry *entry, + PpJobsDialog *dialog) +{ + GtkWidget *widget; + + widget = GTK_WIDGET (gtk_builder_get_object (dialog->builder, "authenticate-button")); + gtk_widget_set_sensitive (widget, auth_popup_filled (dialog)); +} + +static void +auth_entries_activated (GtkEntry *entry, + PpJobsDialog *dialog) +{ + GtkWidget *widget; + + widget = GTK_WIDGET (gtk_builder_get_object (dialog->builder, "authenticate-button")); + if (auth_popup_filled (dialog)) + gtk_button_clicked (GTK_BUTTON (widget)); +} + +static void +authenticate_popover_update (PpJobsDialog *dialog) +{ + GtkWidget *widget; + gboolean domain_required; + gboolean username_required; + gboolean password_required; + + domain_required = is_domain_required (dialog); + username_required = is_username_required (dialog); + password_required = is_password_required (dialog); + + widget = GTK_WIDGET (gtk_builder_get_object (GTK_BUILDER (dialog->builder), "domain-label")); + gtk_widget_set_visible (widget, domain_required); + widget = GTK_WIDGET (gtk_builder_get_object (GTK_BUILDER (dialog->builder), "domain-entry")); + gtk_widget_set_visible (widget, domain_required); + if (domain_required) + { + gtk_entry_set_text (GTK_ENTRY (widget), ""); + gtk_entry_grab_focus_without_selecting (GTK_ENTRY (widget)); + } + + widget = GTK_WIDGET (gtk_builder_get_object (GTK_BUILDER (dialog->builder), "username-label")); + gtk_widget_set_visible (widget, username_required); + widget = GTK_WIDGET (gtk_builder_get_object (GTK_BUILDER (dialog->builder), "username-entry")); + gtk_widget_set_visible (widget, username_required); + if (username_required) + { + gtk_entry_set_text (GTK_ENTRY (widget), cupsUser ()); + gtk_entry_grab_focus_without_selecting (GTK_ENTRY (widget)); + } + + widget = GTK_WIDGET (gtk_builder_get_object (GTK_BUILDER (dialog->builder), "password-label")); + gtk_widget_set_visible (widget, password_required); + widget = GTK_WIDGET (gtk_builder_get_object (GTK_BUILDER (dialog->builder), "password-entry")); + gtk_widget_set_visible (widget, password_required); + if (password_required) + { + gtk_entry_set_text (GTK_ENTRY (widget), ""); + gtk_entry_grab_focus_without_selecting (GTK_ENTRY (widget)); + } + + widget = GTK_WIDGET (gtk_builder_get_object (GTK_BUILDER (dialog->builder), "authenticate-button")); + gtk_widget_set_sensitive (widget, FALSE); +} + static void job_stop_cb (GtkButton *button, PpJob *job) @@ -88,14 +220,19 @@ static GtkWidget * create_listbox_row (gpointer item, gpointer user_data) { - PpJob *job = (PpJob *)item; - GtkWidget *box; - GtkWidget *widget; - gchar *title; - gchar *state_string = NULL; - gint job_state; + GtkWidget *widget; + GtkWidget *box; + PpJob *job = (PpJob *)item; + gchar **auth_info_required; + gchar *title; + gchar *state_string = NULL; + gint job_state; - g_object_get (job, "title", &title, "state", &job_state, NULL); + g_object_get (job, + "title", &title, + "state", &job_state, + "auth-info-required", &auth_info_required, + NULL); switch (job_state) { @@ -104,8 +241,16 @@ create_listbox_row (gpointer item, state_string = g_strdup (C_("print job", "Pending")); break; case IPP_JOB_HELD: - /* Translators: Job's state (job is held for printing) */ - state_string = g_strdup (C_("print job", "Paused")); + if (auth_info_required == NULL) + { + /* Translators: Job's state (job is held for printing) */ + state_string = g_strdup (C_("print job", "Paused")); + } + else + { + /* Translators: Job's state (job needs authentication to proceed further) */ + state_string = g_strdup_printf ("%s", C_("print job", "Authentication required")); + } break; case IPP_JOB_PROCESSING: /* Translators: Job's state (job is currently printing) */ @@ -137,14 +282,17 @@ create_listbox_row (gpointer item, gtk_widget_set_halign (widget, GTK_ALIGN_START); gtk_box_pack_start (GTK_BOX (box), widget, TRUE, TRUE, 10); - widget = gtk_label_new (state_string); + widget = gtk_label_new (NULL); + gtk_label_set_markup (GTK_LABEL (widget), state_string); gtk_widget_set_halign (widget, GTK_ALIGN_END); gtk_widget_set_margin_end (widget, 64); + gtk_widget_set_margin_start (widget, 64); gtk_box_pack_start (GTK_BOX (box), widget, FALSE, FALSE, 10); widget = gtk_button_new_from_icon_name (job_state == IPP_JOB_HELD ? "media-playback-start-symbolic" : "media-playback-pause-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR); g_signal_connect (widget, "clicked", G_CALLBACK (job_pause_cb), item); + gtk_widget_set_sensitive (widget, auth_info_required == NULL); gtk_box_pack_start (GTK_BOX (box), widget, FALSE, FALSE, 4); widget = gtk_button_new_from_icon_name ("edit-delete-symbolic", @@ -162,13 +310,18 @@ update_jobs_list_cb (GObject *source_object, GAsyncResult *result, gpointer user_data) { - PpJobsDialog *dialog = user_data; - PpPrinter *printer = PP_PRINTER (source_object); - GtkWidget *clear_all_button; - GtkStack *stack; - GError *error = NULL; - GList *jobs, *l; - gint num_of_jobs; + PpJobsDialog *dialog = user_data; + PpPrinter *printer = PP_PRINTER (source_object); + GtkWidget *clear_all_button; + GtkWidget *infobar; + GtkWidget *label; + GtkStack *stack; + GError *error = NULL; + GList *jobs, *l; + PpJob *job; + gchar **auth_info_required = NULL; + gchar *text; + gint num_of_jobs, num_of_auth_jobs = 0; g_list_store_remove_all (dialog->store); @@ -201,9 +354,45 @@ update_jobs_list_cb (GObject *source_object, for (l = jobs; l != NULL; l = l->next) { - g_list_store_append (dialog->store, l->data); + job = PP_JOB (l->data); + + g_list_store_append (dialog->store, job); + + g_object_get (G_OBJECT (job), + "auth-info-required", &auth_info_required, + NULL); + if (auth_info_required != NULL) + { + num_of_auth_jobs++; + + if (dialog->actual_auth_info_required == NULL) + dialog->actual_auth_info_required = auth_info_required; + else + g_strfreev (auth_info_required); + + auth_info_required = NULL; + } } + infobar = GTK_WIDGET (gtk_builder_get_object (GTK_BUILDER (dialog->builder), "authentication-infobar")); + if (num_of_auth_jobs > 0) + { + label = GTK_WIDGET (gtk_builder_get_object (GTK_BUILDER (dialog->builder), "authenticate-jobs-label")); + + /* Translators: This label shows how many jobs of this printer needs to be authenticated to be printed. */ + text = g_strdup_printf (ngettext ("%u Job Requires Authentication", "%u Jobs Require Authentication", num_of_auth_jobs), num_of_auth_jobs); + gtk_label_set_text (GTK_LABEL (label), text); + g_free (text); + + gtk_widget_show (infobar); + } + else + { + gtk_widget_hide (infobar); + } + + authenticate_popover_update (dialog); + g_list_free (jobs); g_clear_object (&dialog->get_jobs_cancellable); } @@ -265,6 +454,78 @@ on_clear_all_button_clicked (GtkButton *button, } } +static void +pp_job_authenticate_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + PpJobsDialog *dialog = user_data; + gboolean result; + GError *error = NULL; + PpJob *job = PP_JOB (source_object); + + result = pp_job_authenticate_finish (job, res, &error); + if (result) + { + pp_jobs_dialog_update (dialog); + } + else if (error != NULL) + { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + { + g_warning ("Could not authenticate job: %s", error->message); + } + + g_error_free (error); + } +} + +static void +authenticate_button_clicked (GtkWidget *button, + gpointer user_data) +{ + PpJobsDialog *dialog = user_data; + GtkWidget *widget; + PpJob *job; + gchar **auth_info_required = NULL; + gchar **auth_info; + guint num_items; + gint i; + + auth_info = g_new0 (gchar *, g_strv_length (dialog->actual_auth_info_required) + 1); + for (i = 0; dialog->actual_auth_info_required[i] != NULL; i++) + { + if (g_strcmp0 (dialog->actual_auth_info_required[i], "domain") == 0) + widget = GTK_WIDGET (gtk_builder_get_object (GTK_BUILDER (dialog->builder), "domain-entry")); + else if (g_strcmp0 (dialog->actual_auth_info_required[i], "username") == 0) + widget = GTK_WIDGET (gtk_builder_get_object (GTK_BUILDER (dialog->builder), "username-entry")); + else if (g_strcmp0 (dialog->actual_auth_info_required[i], "password") == 0) + widget = GTK_WIDGET (gtk_builder_get_object (GTK_BUILDER (dialog->builder), "password-entry")); + else + widget = NULL; + + if (widget != NULL) + auth_info[i] = g_strdup (gtk_entry_get_text (GTK_ENTRY (widget))); + } + + num_items = g_list_model_get_n_items (G_LIST_MODEL (dialog->store)); + for (i = 0; i < num_items; i++) + { + job = PP_JOB (g_list_model_get_item (G_LIST_MODEL (dialog->store), i)); + + g_object_get (job, "auth-info-required", &auth_info_required, NULL); + if (auth_info_required != NULL) + { + pp_job_authenticate_async (job, auth_info, NULL, pp_job_authenticate_cb, dialog); + + g_strfreev (auth_info_required); + auth_info_required = NULL; + } + } + + g_strfreev (auth_info); +} + PpJobsDialog * pp_jobs_dialog_new (GtkWindow *parent, UserResponseCallback user_callback, @@ -272,9 +533,10 @@ pp_jobs_dialog_new (GtkWindow *parent, gchar *printer_name) { PpJobsDialog *dialog; - GtkButton *clear_all_button; + GtkWidget *widget; GError *error = NULL; - gchar *objects[] = { "jobs-dialog", NULL }; + gchar *objects[] = { "jobs-dialog", "authentication_popover", NULL }; + gchar *text; guint builder_result; gchar *title; @@ -298,19 +560,41 @@ pp_jobs_dialog_new (GtkWindow *parent, dialog->user_callback = user_callback; dialog->user_data = user_data; dialog->printer_name = g_strdup (printer_name); + dialog->actual_auth_info_required = NULL; /* connect signals */ g_signal_connect (dialog->dialog, "delete-event", G_CALLBACK (gtk_widget_hide_on_delete), NULL); g_signal_connect (dialog->dialog, "response", G_CALLBACK (jobs_dialog_response_cb), dialog); - clear_all_button = GTK_BUTTON (gtk_builder_get_object (dialog->builder, "jobs-clear-all-button")); - g_signal_connect (clear_all_button, "clicked", G_CALLBACK (on_clear_all_button_clicked), dialog); + widget = GTK_WIDGET (gtk_builder_get_object (dialog->builder, "jobs-clear-all-button")); + g_signal_connect (widget, "clicked", G_CALLBACK (on_clear_all_button_clicked), dialog); + + widget = GTK_WIDGET (gtk_builder_get_object (dialog->builder, "authenticate-button")); + g_signal_connect (widget, "clicked", G_CALLBACK (authenticate_button_clicked), dialog); + + widget = GTK_WIDGET (gtk_builder_get_object (dialog->builder, "domain-entry")); + g_signal_connect (widget, "changed", G_CALLBACK (auth_entries_changed), dialog); + g_signal_connect (widget, "activate", G_CALLBACK (auth_entries_activated), dialog); + + widget = GTK_WIDGET (gtk_builder_get_object (dialog->builder, "username-entry")); + g_signal_connect (widget, "changed", G_CALLBACK (auth_entries_changed), dialog); + g_signal_connect (widget, "activate", G_CALLBACK (auth_entries_activated), dialog); + + widget = GTK_WIDGET (gtk_builder_get_object (dialog->builder, "password-entry")); + g_signal_connect (widget, "changed", G_CALLBACK (auth_entries_changed), dialog); + g_signal_connect (widget, "activate", G_CALLBACK (auth_entries_activated), dialog); /* Translators: This is the printer name for which we are showing the active jobs */ title = g_strdup_printf (C_("Printer jobs dialog title", "%s — Active Jobs"), printer_name); gtk_window_set_title (GTK_WINDOW (dialog->dialog), title); g_free (title); + /* Translators: The printer needs authentication info to print. */ + text = g_strdup_printf (_("Enter credentials to print from %s."), printer_name); + widget = GTK_WIDGET (gtk_builder_get_object (GTK_BUILDER (dialog->builder), "authentication-label")); + gtk_label_set_text (GTK_LABEL (widget), text); + g_free (text); + dialog->listbox = GTK_LIST_BOX (gtk_builder_get_object (dialog->builder, "jobs-listbox")); gtk_list_box_set_header_func (dialog->listbox, cc_list_box_update_header_func, NULL, NULL); @@ -357,6 +641,8 @@ pp_jobs_dialog_free (PpJobsDialog *dialog) gtk_widget_destroy (GTK_WIDGET (dialog->dialog)); dialog->dialog = NULL; + g_strfreev (dialog->actual_auth_info_required); + g_clear_object (&dialog->builder); g_free (dialog->printer_name); g_free (dialog);