From 72f19f83681b222d1606d75c90ceedc43bb545f9 Mon Sep 17 00:00:00 2001 From: Moritz Bunkus Date: Thu, 24 Sep 2015 11:42:15 +0200 Subject: [PATCH] =?utf8?q?E-Mail-Journal:=20Journal=20anzeigen,=20Eintrag?= =?utf8?q?=20anzeigen,=20Anh=C3=A4nge=20herunterladen?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- SL/Controller/EmailJournal.pm | 124 ++++++++++++++++++ SL/DB/EmailJournal.pm | 3 + SL/DB/Manager/EmailJournal.pm | 13 ++ css/email_journal.css | 13 ++ locale/de/all | 12 ++ menus/user/00-erp.yaml | 7 + templates/webpages/email_journal/_filter.html | 43 ++++++ templates/webpages/email_journal/list.html | 68 ++++++++++ templates/webpages/email_journal/show.html | 83 ++++++++++++ 9 files changed, 366 insertions(+) create mode 100644 SL/Controller/EmailJournal.pm create mode 100644 css/email_journal.css create mode 100644 templates/webpages/email_journal/_filter.html create mode 100644 templates/webpages/email_journal/list.html create mode 100644 templates/webpages/email_journal/show.html diff --git a/SL/Controller/EmailJournal.pm b/SL/Controller/EmailJournal.pm new file mode 100644 index 000000000..7c40a0de7 --- /dev/null +++ b/SL/Controller/EmailJournal.pm @@ -0,0 +1,124 @@ +package SL::Controller::EmailJournal; + +use strict; + +use parent qw(SL::Controller::Base); + +use SL::Controller::Helper::GetModels; +use SL::DB::Employee; +use SL::DB::EmailJournal; +use SL::DB::EmailJournalAttachment; +use SL::Helper::Flash; +use SL::Locale::String; +use SL::System::TaskServer; + +use Rose::Object::MakeMethods::Generic +( + scalar => [ qw(entry) ], + 'scalar --get_set_init' => [ qw(models can_view_all filter_summary) ], +); + +__PACKAGE__->run_before('add_stylesheet'); + +# +# actions +# + +sub action_list { + my ($self) = @_; + + $self->render('email_journal/list', + title => $::locale->text('Email journal'), + ENTRIES => $self->models->get, + MODELS => $self->models); +} + +sub action_show { + my ($self) = @_; + + my $back_to = $::form->{back_to} || $self->url_for(action => 'list'); + + $self->entry(SL::DB::EmailJournal->new(id => $::form->{id})->load); + + if (!$self->can_view_all && ($self->entry->sender_id != SL::DB::Manager::Emplyee->current->id)) { + $::form->error(t8('You do not have permission to access this entry.')); + } + + $self->render('email_journal/show', + title => $::locale->text('View sent email'), + back_to => $back_to); +} + +sub action_download_attachment { + my ($self) = @_; + + my $attachment = SL::DB::EmailJournalAttachment->new(id => $::form->{id})->load; + + if (!$self->can_view_all && ($attachment->email_journal->sender_id != SL::DB::Manager::Emplyee->current->id)) { + $::form->error(t8('You do not have permission to access this entry.')); + } + + $self->send_file(\$attachment->content, name => $attachment->name, type => $attachment->mime_type); +} + +# +# filters +# + +sub add_stylesheet { + $::request->{layout}->use_stylesheet('email_journal.css'); +} + +# +# helpers +# + +sub init_can_view_all { $::auth->assert('admin', 1) } + +sub init_models { + my ($self) = @_; + + my @where; + push @where, (sender_id => SL::DB::Manager::Employee->current->id) if !$self->can_view_all; + + SL::Controller::Helper::GetModels->new( + controller => $self, + query => \@where, + with_objects => [ 'sender' ], + sorted => { + sender => t8('Sender'), + from => t8('From'), + recipients => t8('Recipients'), + subject => t8('Subject'), + sent_on => t8('Sent on'), + status => t8('Status'), + extended_status => t8('Extended status'), + }, + ); +} + +sub init_filter_summary { + my ($self) = @_; + + my $filter = $::form->{filter} || {}; + my @filters = ( + [ "from:substr::ilike", $::locale->text('From') ], + [ "recipients:substr::ilike", $::locale->text('Recipients') ], + [ "sent_on:date::ge", $::locale->text('Sent on') . " " . $::locale->text('From Date') ], + [ "sent_on:date::le", $::locale->text('Sent on') . " " . $::locale->text('To Date') ], + ); + + my @filter_strings = grep { $_ } + map { $filter->{ $_->[0] } ? $_->[1] . ' ' . $filter->{ $_->[0] } : undef } + @filters; + + my %status = ( + failed => $::locale->text('failed'), + ok => $::locale->text('succeeded'), + ); + push @filter_strings, $status{ $filter->{'status:eq_ignore_empty'} } if $filter->{'status:eq_ignore_empty'}; + + return join ', ', @filter_strings; +} + +1; diff --git a/SL/DB/EmailJournal.pm b/SL/DB/EmailJournal.pm index 6875b5249..9920d59d7 100644 --- a/SL/DB/EmailJournal.pm +++ b/SL/DB/EmailJournal.pm @@ -4,6 +4,7 @@ use strict; use SL::DB::MetaSetup::EmailJournal; use SL::DB::Manager::EmailJournal; +use SL::DB::Helper::AttrSorted; __PACKAGE__->meta->add_relationship( attachments => { @@ -15,4 +16,6 @@ __PACKAGE__->meta->add_relationship( __PACKAGE__->meta->initialize; +__PACKAGE__->attr_sorted('attachments'); + 1; diff --git a/SL/DB/Manager/EmailJournal.pm b/SL/DB/Manager/EmailJournal.pm index d387a7bab..b5584872c 100644 --- a/SL/DB/Manager/EmailJournal.pm +++ b/SL/DB/Manager/EmailJournal.pm @@ -4,8 +4,21 @@ use strict; use parent qw(SL::DB::Helper::Manager); +use SL::DB::Helper::Paginated; +use SL::DB::Helper::Sorted; + sub object_class { 'SL::DB::EmailJournal' } __PACKAGE__->make_manager_methods; +sub _sort_spec { + return ( + default => [ 'sent_on', 1 ], + columns => { + SIMPLE => 'ALL', + sender => 'sender.name', + }, + ); +} + 1; diff --git a/css/email_journal.css b/css/email_journal.css new file mode 100644 index 000000000..5e5bf8f03 --- /dev/null +++ b/css/email_journal.css @@ -0,0 +1,13 @@ +/* E-Mail-Journal */ +.email_journal_details tbody pre { + margin: 0px; +} + +.email_journal_details tbody th { + text-align: right; + vertical-align: top; +} + +.email_journal_details tbody td { + vertical-align: top; +} diff --git a/locale/de/all b/locale/de/all index 74ca6749b..d2884bd62 100755 --- a/locale/de/all +++ b/locale/de/all @@ -302,6 +302,7 @@ $self->{texts} = { 'Attach PDF:' => 'PDF anhängen', 'Attachment' => 'als Anhang', 'Attachment name' => 'Name des Anhangs', + 'Attachments' => 'Dateianhänge', 'Attempt to call an undefined sub named \'%s\'' => 'Es wurde versucht, eine nicht definierte Unterfunktion namens \'%s\' aufzurufen.', 'Audit Control' => 'Bücherkontrolle', 'Aug' => 'Aug', @@ -1074,6 +1075,7 @@ $self->{texts} = { 'Editable' => 'Bearbeitbar', 'Either there are no open invoices, or you have already initiated bank transfers with the open amounts for those that are still open.' => 'Entweder gibt es keine offenen Rechnungen, oder es wurden bereits Überweisungen über die offenen Beträge aller offenen Rechnungen erstellt.', 'Element disabled' => 'Element deaktiviert', + 'Email journal' => 'E-Mail-Journal', 'Employee' => 'Bearbeiter', 'Employee #1 saved!' => 'Benutzer #1 gespeichert!', 'Employee (database ID)' => 'Bearbeiter (Datenbank-ID)', @@ -1184,6 +1186,7 @@ $self->{texts} = { 'Export date to' => 'Exportdatum bis', 'Extend automatically by n months' => 'Automatische Verlängerung um x Monate', 'Extended' => 'Gesamt', + 'Extended status' => 'Erweiterter Status', 'Extension Of Time' => 'Dauerfristverlängerung', 'Factor' => 'Faktor', 'Factor missing!' => 'Der Faktor fehlt.', @@ -1292,6 +1295,7 @@ $self->{texts} = { 'Hardcopy' => 'Seite drucken', 'Has item type' => 'Hat Regeltypen', 'Has serial number' => 'Hat eine Serienummer', + 'Headers' => 'Kopfzeilen', 'Heading' => 'Überschrift', 'Help Template Variables' => 'Hilfe zu Dokumenten-Variablen', 'Help on column names' => 'Hilfe zu Spaltennamen', @@ -2125,6 +2129,7 @@ $self->{texts} = { 'Receipt, payment, reconciliation' => 'Zahlungseingang, Zahlungsausgang, Kontenabgleich', 'Receipts' => 'Zahlungseingänge', 'Receivables' => 'Forderungen', + 'Recipients' => 'EmpfängerInnen', 'Reconcile' => 'Abgleichen', 'Reconciliation' => 'Kontenabgleich', 'Reconciliation with bank' => 'Kontenabgleich mit Bank', @@ -2344,6 +2349,9 @@ $self->{texts} = { 'Sellprice for price group \'#1\'' => 'Verkaufspreis für Preisgruppe \'#1\'', 'Sellprice significant places' => 'Verkaufspreis: Nachkommastellen', 'Semicolon' => 'Semikolon', + 'Send PDF to support contract\'s contact person' => 'PDFs an Ansprechpersonen der Wartungsverträge schicken', + 'Sender' => 'AbsenderIn', + 'Sent on' => 'Verschickt am', 'Sep' => 'Sep', 'Separator' => 'Trennzeichen', 'Separator chararacter' => 'Feldtrennzeichen', @@ -2428,6 +2436,7 @@ $self->{texts} = { 'Since bin is not enforced in the parts data, please specify a bin where goods without a specified bin will be put.' => 'Da Lagerplätze kein Pflichtfeld sind, geben Sie bitte einen Lagerplatz an, in dem Waren ohne spezifizierten Lagerplatz eingelagert werden sollen.', 'Single quotes' => 'Einfache Anführungszeichen', 'Single values in item mode, cumulated values in invoice mode' => 'Einzelwerte im Artikelmodus, kumulierte Werte im Rechnungsmodus', + 'Size' => 'Größe', 'Sketch' => 'Skizze', 'Skip' => 'Überspringen', 'Skip entry' => 'Eintrag überspringen', @@ -2874,6 +2883,7 @@ $self->{texts} = { 'There are invalid transactions in your database.' => 'Sie haben ungültige Buchungen in Ihrer Datenbank.', 'There are invoices which could not be paid by bank transaction #1 (Account number: #2, bank code: #3)!' => 'Einige Rechnungen konnten nicht durch die Bankbewegung #1 (Kontonummer: #2, Bankleitzahl: #3) bezahlt werden!', 'There are no entries in the background job history.' => 'Es gibt keine Einträge im Hintergrund-Job-Verlauf.', + 'There are no entries that match the filter.' => 'Es gibt keine Einträge, auf die der Filter zutrifft.', 'There are no items in stock.' => 'Dieser Artikel ist nicht eingelagert.', 'There are no items on your TODO list at the moment.' => 'Ihre Aufgabenliste enthält momentan keine Einträge.', 'There are several options you can handle this problem, please select one:' => 'Bitte wählen Sie eine der folgenden Optionen, um mit dem Problem umzugehen:', @@ -3131,6 +3141,7 @@ $self->{texts} = { 'View background job execution result' => 'Verlauf der Hintergrund-Job-Ausführungen anzeigen', 'View background job history' => 'Hintergrund-Job-Verlauf anzeigen', 'View background jobs' => 'Hintergrund-Jobs anzeigen', + 'View sent email' => 'Verschickte E-Mail anzeigen', 'View warehouse content' => 'Lagerbestand ansehen', 'View/edit all employees sales documents' => 'Bearbeiten/ansehen der Verkaufsdokumente aller Mitarbeiter', 'Von Konto: ' => 'von Konto: ', @@ -3196,6 +3207,7 @@ $self->{texts} = { 'You cannot create an invoice for delivery orders from different vendors.' => 'Sie können keine Rechnung aus Lieferscheinen von verschiedenen Lieferanten erstellen.', 'You cannot modify individual assigments from additional articles to line items.' => 'Eine individuelle Zuordnung der zusätzlichen Artikel zu Positionen kann nicht vorgenommen werden.', 'You cannot paste function blocks or sub function blocks if there is no section.' => 'Sie können keine Funktionsblöcke oder Unterfunktionsblöcke einfügen, wenn es noch keinen Abschnitt gibt.', + 'You do not have permission to access this entry.' => 'Sie verfügen nicht über die Berechtigung, auf diesen Eintrag zuzugreifen.', 'You do not have the permissions to access this function.' => 'Sie verfügen nicht über die notwendigen Rechte, um auf diese Funktion zuzugreifen.', 'You have entered or selected the following shipping address for this customer:' => 'Sie haben die folgende Lieferadresse eingegeben oder ausgewählt:', 'You have never worked with currencies.' => 'Sie haben noch nie mit Währungen gearbeitet.', diff --git a/menus/user/00-erp.yaml b/menus/user/00-erp.yaml index 64d3d7175..9f025e1e4 100644 --- a/menus/user/00-erp.yaml +++ b/menus/user/00-erp.yaml @@ -966,6 +966,13 @@ module: fu.pl params: action: search +- parent: productivity_reports + id: productivity_reports_email_journal + name: Email journal + order: 200 + module: controller.pl + params: + action: EmailJournal/list - id: system name: System icon: system diff --git a/templates/webpages/email_journal/_filter.html b/templates/webpages/email_journal/_filter.html new file mode 100644 index 000000000..282fd0980 --- /dev/null +++ b/templates/webpages/email_journal/_filter.html @@ -0,0 +1,43 @@ +[%- USE L %][%- USE LxERP %][%- USE HTML %] +
+
+ [% LxERP.t8('Show Filter') %] + [% IF SELF.filter_summary %]([% LxERP.t8("Current filter") %]: [% SELF.filter_summary %])[% END %] +
+ + + +
diff --git a/templates/webpages/email_journal/list.html b/templates/webpages/email_journal/list.html new file mode 100644 index 000000000..f169aed18 --- /dev/null +++ b/templates/webpages/email_journal/list.html @@ -0,0 +1,68 @@ +[% USE HTML %][% USE L %][% USE LxERP %] + +

[% FORM.title %]

+ +[%- INCLUDE 'common/flash.html' %] + +[%- PROCESS 'email_journal/_filter.html' filter=SELF.models.filtered.laundered %] + +[% IF !ENTRIES.size %] +

+ [%- LxERP.t8('There are no entries that match the filter.') %] +

+ +[%- ELSE %] + + + + [% IF SELF.can_view_all %] + + [% END %] + + + + + + + + + + + [%- FOREACH entry = ENTRIES %] + + [% IF SELF.can_view_all %] + + [% END %] + + + + + + + + [%- END %] + +
[% L.sortable_table_header("sender") %][% L.sortable_table_header("from") %][% L.sortable_table_header("recipients") %][% L.sortable_table_header("subject") %][% L.sortable_table_header("sent_on") %][% L.sortable_table_header("status") %][% L.sortable_table_header("extended_status") %]
+ [% IF entry.sender %] + [% HTML.escape(entry.sender.name) %] + [% ELSE %] + [% LxERP.t8("kivitendo") %] + [% END %] + + + [%- HTML.escape(entry.from) %] + + [%- HTML.escape(entry.recipients) %] + + [%- HTML.escape(entry.subject) %] + + [%- HTML.escape(entry.sent_on.to_lxoffice('precision' => 'second')) %] + [%- IF entry.status == 'ok' %] + [%- LxERP.t8('succeeded') %] + [% ELSE %] + [%- LxERP.t8('failed') %] + [%- END %] + [%- HTML.escape(entry.extended_status) %]
+[%- END %] + +[% L.paginate_controls %] diff --git a/templates/webpages/email_journal/show.html b/templates/webpages/email_journal/show.html new file mode 100644 index 000000000..67d76219f --- /dev/null +++ b/templates/webpages/email_journal/show.html @@ -0,0 +1,83 @@ +[% USE HTML %][% USE L %][% USE LxERP %] + +

[% FORM.title %]

+ +[%- INCLUDE 'common/flash.html' %] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
[%- LxERP.t8("From") %][%- HTML.escape(SELF.entry.from) %]
[%- LxERP.t8("Recipients") %][%- HTML.escape(SELF.entry.recipients) %]
[%- LxERP.t8("Subject") %][%- HTML.escape(SELF.entry.subject) %]
[%- LxERP.t8("Sent on") %][%- HTML.escape(SELF.entry.sent_on.to_lxoffice("precision" => "second")) %]
[%- LxERP.t8("Status") %] + [%- IF SELF.entry.status == "ok" %] + [%- LxERP.t8("succeeded") %] + [%- ELSE %] + [%- LxERP.t8("failed") %] + [%- END %] +
[%- LxERP.t8("Extended status") %]
[%- HTML.escape(SELF.entry.extended_status) %]
[%- LxERP.t8("Headers") %]
[% HTML.escape(SELF.entry.headers) %]
[%- LxERP.t8("Body") %]
[% HTML.escape(SELF.entry.body) %]
+ + [% SET attachments = SELF.entry.attachments_sorted %] + [% IF attachments.size %] +

[% LxERP.t8("Attachments") %]

+ + + + + + + + + + + + [% FOREACH attachment = attachments %] + + + + + + [% END %] + +
[% LxERP.t8("Attachment name") %][% LxERP.t8("MIME type") %][% LxERP.t8("Size") %]
[% L.link(SELF.url_for(action="download_attachment", id=attachment.id), attachment.name) %][% HTML.escape(attachment.mime_type) %][% HTML.escape(LxERP.format_amount(attachment.content.length, 0)) %]
+ [% END %] + +

+ [%- LxERP.t8("Back") %] +

-- 2.20.1