Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision 72f19f83

Von Moritz Bunkus vor mehr als 9 Jahren hinzugefügt

  • ID 72f19f83681b222d1606d75c90ceedc43bb545f9
  • Vorgänger 7e601869
  • Nachfolger 358a7497

E-Mail-Journal: Journal anzeigen, Eintrag anzeigen, Anhänge herunterladen

Unterschiede anzeigen:

SL/Controller/EmailJournal.pm
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;
SL/DB/EmailJournal.pm
use SL::DB::MetaSetup::EmailJournal;
use SL::DB::Manager::EmailJournal;
use SL::DB::Helper::AttrSorted;
__PACKAGE__->meta->add_relationship(
attachments => {
......
__PACKAGE__->meta->initialize;
__PACKAGE__->attr_sorted('attachments');
1;
SL/DB/Manager/EmailJournal.pm
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;
css/email_journal.css
/* 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;
}
locale/de/all
'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',
......
'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)',
......
'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.',
......
'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',
......
'Receipt, payment, reconciliation' => 'Zahlungseingang, Zahlungsausgang, Kontenabgleich',
'Receipts' => 'Zahlungseingänge',
'Receivables' => 'Forderungen',
'Recipients' => 'EmpfängerInnen',
'Reconcile' => 'Abgleichen',
'Reconciliation' => 'Kontenabgleich',
'Reconciliation with bank' => 'Kontenabgleich mit Bank',
......
'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',
......
'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',
......
'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:',
......
'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: ',
......
'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.',
menus/user/00-erp.yaml
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
templates/webpages/email_journal/_filter.html
[%- USE L %][%- USE LxERP %][%- USE HTML %]
<form action="controller.pl" method="post">
<div class="filter_toggle">
<a href="#" onClick="javascript:$('.filter_toggle').toggle()">[% LxERP.t8('Show Filter') %]</a>
[% IF SELF.filter_summary %]([% LxERP.t8("Current filter") %]: [% SELF.filter_summary %])[% END %]
</div>
<div class="filter_toggle" style="display:none">
<a href="#" onClick="javascript:$('.filter_toggle').toggle()">[% LxERP.t8('Hide Filter') %]</a>
<table id="filter_table">
<tr>
<th align="right">[% LxERP.t8("From") %]</th>
<td>[% L.input_tag("filter.from:substr::ilike", filter.from_substr__ilike, size = 20) %]</td>
</tr>
<tr>
<th align="right">[% LxERP.t8("Recipients") %]</th>
<td>[% L.input_tag("filter.recipients:substr::ilike", filter.recipients_substr__ilike, size = 20) %]</td>
</tr>
<tr>
<th align="right">[% LxERP.t8("Sent on") %]</th>
<td>
[% L.date_tag("filter.sent_on:date::ge", filter.sent_on_date__ge) %]
[% LxERP.t8("To Date") %]
[% L.date_tag("filter.sent_on:date::le", filter.sent_on_date__le) %]
</td>
</tr>
<tr>
<th align="right">[% LxERP.t8("Status") %]</th>
<td>[% L.select_tag("filter.status:eq_ignore_empty", [ [ "", "" ], [ "failed", LxERP.t8("failed") ], [ "ok", LxERP.t8("succeeded") ] ], default=filter.status_eq_ignore_empty) %]</td>
</tr>
</table>
[% L.hidden_tag("action", "EmailJournal/dispatch") %]
[% L.hidden_tag("sort_by", FORM.sort_by) %]
[% L.hidden_tag("sort_dir", FORM.sort_dir) %]
[% L.hidden_tag("page", FORM.page) %]
[% L.submit_tag("action_list", LxERP.t8("Continue"))%]
<a href="#" onClick="javascript:$('#filter_table input,#filter_table select').val("");">[% LxERP.t8("Reset") %]</a>
</div>
</form>
templates/webpages/email_journal/list.html
[% USE HTML %][% USE L %][% USE LxERP %]
<h1>[% FORM.title %]</h1>
[%- INCLUDE 'common/flash.html' %]
[%- PROCESS 'email_journal/_filter.html' filter=SELF.models.filtered.laundered %]
[% IF !ENTRIES.size %]
<p>
[%- LxERP.t8('There are no entries that match the filter.') %]
</p>
[%- ELSE %]
<table id="email_journal_list" width="100%">
<thead>
<tr class="listheading">
[% IF SELF.can_view_all %]
<th>[% L.sortable_table_header("sender") %]</th>
[% END %]
<th>[% L.sortable_table_header("from") %]</th>
<th>[% L.sortable_table_header("recipients") %]</th>
<th>[% L.sortable_table_header("subject") %]</th>
<th>[% L.sortable_table_header("sent_on") %]</th>
<th>[% L.sortable_table_header("status") %]</th>
<th>[% L.sortable_table_header("extended_status") %]</th>
</tr>
</thead>
<tbody>
[%- FOREACH entry = ENTRIES %]
<tr class="listrow[% IF entry.status != 'ok' %]_error[% END %]" id="email_journal_id_[% entry.id %]">
[% IF SELF.can_view_all %]
<td>
[% IF entry.sender %]
[% HTML.escape(entry.sender.name) %]
[% ELSE %]
[% LxERP.t8("kivitendo") %]
[% END %]
</td>
[% END %]
<td>
<a href="[% SELF.url_for(action => 'show', id => entry.id, back_to => SELF.get_callback) %]">
[%- HTML.escape(entry.from) %]
</a>
</td>
<td>[%- HTML.escape(entry.recipients) %]</td>
<td>
<a href="[% SELF.url_for(action => 'show', id => entry.id, back_to => SELF.get_callback) %]">
[%- HTML.escape(entry.subject) %]
</a>
</td>
<td>[%- HTML.escape(entry.sent_on.to_lxoffice('precision' => 'second')) %]</td>
<td>
[%- IF entry.status == 'ok' %]
[%- LxERP.t8('succeeded') %]
[% ELSE %]
[%- LxERP.t8('failed') %]
[%- END %]
</td>
<td>[%- HTML.escape(entry.extended_status) %]</td>
</tr>
[%- END %]
</tbody>
</table>
[%- END %]
[% L.paginate_controls %]
templates/webpages/email_journal/show.html
[% USE HTML %][% USE L %][% USE LxERP %]
<h1>[% FORM.title %]</h1>
[%- INCLUDE 'common/flash.html' %]
<table id="email_journal_details" class="email_journal_details">
<tbody>
<tr class="listrow">
<th>[%- LxERP.t8("From") %]</th>
<td>[%- HTML.escape(SELF.entry.from) %]</td>
</tr>
<tr class="listrow">
<th>[%- LxERP.t8("Recipients") %]</th>
<td>[%- HTML.escape(SELF.entry.recipients) %]</td>
</tr>
<tr class="listrow">
<th>[%- LxERP.t8("Subject") %]</th>
<td>[%- HTML.escape(SELF.entry.subject) %]</td>
</tr>
<tr class="listrow">
<th>[%- LxERP.t8("Sent on") %]</th>
<td>[%- HTML.escape(SELF.entry.sent_on.to_lxoffice("precision" => "second")) %]</td>
</tr>
<tr class="listrow">
<th>[%- LxERP.t8("Status") %]</th>
<td>
[%- IF SELF.entry.status == "ok" %]
[%- LxERP.t8("succeeded") %]
[%- ELSE %]
[%- LxERP.t8("failed") %]
[%- END %]
</td>
</tr>
<tr class="listrow">
<th>[%- LxERP.t8("Extended status") %]</th>
<td><pre>[%- HTML.escape(SELF.entry.extended_status) %]</pre></td>
</tr>
<tr class="listrow">
<th>[%- LxERP.t8("Headers") %]</th>
<td><pre>[% HTML.escape(SELF.entry.headers) %]</pre></td>
</tr>
<tr class="listrow">
<th>[%- LxERP.t8("Body") %]</th>
<td><pre>[% HTML.escape(SELF.entry.body) %]</pre></td>
</tr>
</table>
[% SET attachments = SELF.entry.attachments_sorted %]
[% IF attachments.size %]
<h2>[% LxERP.t8("Attachments") %]</h2>
<table id="email_journal_details" class="email_journal_details">
<thead>
<tr>
<th>[% LxERP.t8("Attachment name") %]</th>
<th>[% LxERP.t8("MIME type") %]</th>
<th>[% LxERP.t8("Size") %]</th>
</tr>
</thead>
<tbody>
[% FOREACH attachment = attachments %]
<tr class="listrow">
<td>[% L.link(SELF.url_for(action="download_attachment", id=attachment.id), attachment.name) %]</td>
<td>[% HTML.escape(attachment.mime_type) %]</td>
<td>[% HTML.escape(LxERP.format_amount(attachment.content.length, 0)) %]</td>
</tr>
[% END %]
</tbody>
</table>
[% END %]
<p>
<a href="[% back_to %]">[%- LxERP.t8("Back") %]</a>
</p>

Auch abrufbar als: Unified diff