From: Jan Büren Date: Thu, 10 Feb 2022 11:58:58 +0000 (+0100) Subject: Merge pull request #34 from kivitendo/warengruppen_pflichtfeld X-Git-Tag: kivitendo-mebil_0.1-0~10^2~2^2~219 X-Git-Url: http://wagnertech.de/gitweb/gitweb.cgi/mfinanz.git/commitdiff_plain/670a6e6cb58ccaef212150406b66a955295fe074?hp=f3bad0c2bf4ac1a731ee7f1d8da2cccde5f3f10c Merge pull request #34 from kivitendo/warengruppen_pflichtfeld Mandantenkonfiguration - Warengruppe als Pflichtfeld --- diff --git a/SL/BackgroundJob/CreatePeriodicInvoices.pm b/SL/BackgroundJob/CreatePeriodicInvoices.pm index f41135387..4e3206317 100644 --- a/SL/BackgroundJob/CreatePeriodicInvoices.pm +++ b/SL/BackgroundJob/CreatePeriodicInvoices.pm @@ -473,7 +473,7 @@ sub _email_invoice { invoice => $data->{invoice}, vars => $data->{time_period_vars}, attribute => $_, - attribute_format => 'text' + attribute_format => ($_ eq 'email_body' ? 'html' : 'text') ); } @@ -489,6 +489,8 @@ sub _email_invoice { $mail->{bcc} = $global_bcc; $mail->{subject} = $data->{config}->email_subject; $mail->{message} = $data->{config}->email_body; + $mail->{message} .= SL::DB::Default->get->signature; + $mail->{content_type} = 'text/html'; $mail->{attachments} = [{ path => $pdf_file_name, name => sprintf('%s %s.pdf', $label, $data->{invoice}->invnumber), diff --git a/SL/Common.pm b/SL/Common.pm index 15f8f7b93..36635fdbc 100644 --- a/SL/Common.pm +++ b/SL/Common.pm @@ -25,6 +25,7 @@ use Encode qw(decode); use SL::DBUtils; use SL::DB; +use SL::HTML::Util; sub unique_id { my ($a, $b) = gettimeofday(); @@ -423,7 +424,7 @@ sub save_email_status { . $main::locale->text('To (email)') . ": $form->{email}\n" . "${cc}${bcc}" . $main::locale->text('Subject') . ": $form->{subject}\n\n" - . $main::locale->text('Message') . ": $form->{message}"; + . $main::locale->text('Message') . ": " . SL::HTML::Util->strip($form->{message}); $intnotes =~ s|\r||g; diff --git a/SL/Controller/Admin.pm b/SL/Controller/Admin.pm index e6200896c..c22fdca74 100644 --- a/SL/Controller/Admin.pm +++ b/SL/Controller/Admin.pm @@ -592,6 +592,13 @@ sub use_multiselect_js { return $self; } +sub use_ckeditor_js { + my ($self) = @_; + + $::request->{layout}->use_javascript("${_}.js") for qw(ckeditor/ckeditor ckeditor/adapters/jquery); + return $self; +} + sub login_form { my ($self, %params) = @_; $::request->layout(SL::Layout::AdminLogin->new); @@ -601,7 +608,7 @@ sub login_form { sub edit_user_form { my ($self, %params) = @_; - $self->use_multiselect_js->render('admin/edit_user', %params); + $self->use_multiselect_js->use_ckeditor_js->render('admin/edit_user', %params); } sub edit_client_form { diff --git a/SL/Controller/ClientConfig.pm b/SL/Controller/ClientConfig.pm index 12125122e..bc0f28701 100644 --- a/SL/Controller/ClientConfig.pm +++ b/SL/Controller/ClientConfig.pm @@ -246,7 +246,7 @@ sub check_auth { sub edit_form { my ($self) = @_; - $::request->layout->use_javascript("${_}.js") for qw(jquery.selectboxes jquery.multiselect2side kivi.File); + $::request->layout->use_javascript("${_}.js") for qw(jquery.selectboxes jquery.multiselect2side kivi.File ckeditor/ckeditor ckeditor/adapters/jquery); $self->setup_edit_form_action_bar; $self->render('client_config/form', title => t8('Client Configuration'), diff --git a/SL/Controller/Order.pm b/SL/Controller/Order.pm index f24f5cf54..64ad14d23 100644 --- a/SL/Controller/Order.pm +++ b/SL/Controller/Order.pm @@ -4,6 +4,7 @@ use strict; use parent qw(SL::Controller::Base); use SL::Helper::Flash qw(flash_later); +use SL::HTML::Util; use SL::Presenter::Tag qw(select_tag hidden_tag div_tag); use SL::Locale::String qw(t8); use SL::SessionFile::Random; @@ -522,7 +523,7 @@ sub action_send_email { $intnotes .= t8('Cc') . ": " . $::form->{cc} . "\n" if $::form->{cc}; $intnotes .= t8('Bcc') . ": " . $::form->{bcc} . "\n" if $::form->{bcc}; $intnotes .= t8('Subject') . ": " . $::form->{subject} . "\n\n"; - $intnotes .= t8('Message') . ": " . $::form->{message}; + $intnotes .= t8('Message') . ": " . SL::HTML::Util->strip($::form->{message}); $self->order->update_attributes(intnotes => $intnotes); diff --git a/SL/Form.pm b/SL/Form.pm index 229ad2c4d..9c05c3353 100644 --- a/SL/Form.pm +++ b/SL/Form.pm @@ -952,17 +952,15 @@ sub send_email { $mail->{to} = $self->{EMAIL_RECIPIENT} ? $self->{EMAIL_RECIPIENT} : $self->{email}; $mail->{from} = qq|"$myconfig->{name}" <$myconfig->{email}>|; $mail->{fileid} = time() . '.' . $$ . '.'; + $mail->{content_type} = "text/html"; my $full_signature = $self->create_email_signature(); - $full_signature =~ s/\r//g; $mail->{attachments} = []; my @attfiles; # if we send html or plain text inline if (($self->{format} eq 'html') && ($self->{sendmode} eq 'inline')) { - $mail->{content_type} = "text/html"; $mail->{message} =~ s/\r//g; $mail->{message} =~ s{\n}{
\n}g; - $full_signature =~ s{\n}{
\n}g; $mail->{message} .= $full_signature; open(IN, "<", $self->{tmpfile}) @@ -1234,10 +1232,12 @@ sub generate_email_body { return undef unless $body; + $body .= GenericTranslations->get(translation_type => "salutation_punctuation_mark", language_id => $self->{language_id}); + $body = '

' . $::locale->quote_special_chars('HTML', $body) . '

'; + my $translation_type = $params{translation_type} // "preset_text_$self->{formname}"; my $main_body = GenericTranslations->get(translation_type => $translation_type, language_id => $self->{language_id}); $main_body = GenericTranslations->get(translation_type => $params{fallback_translation_type}, language_id => $self->{language_id}) if !$main_body && $params{fallback_translation_type}; - $body .= GenericTranslations->get(translation_type => "salutation_punctuation_mark", language_id => $self->{language_id}) . "\n\n"; $body .= $main_body; $body = $main::locale->unquote_special_chars('HTML', $body); @@ -3440,19 +3440,11 @@ sub reformat_numbers { } sub create_email_signature { - my $client_signature = $::instance_conf->get_signature; my $user_signature = $::myconfig{signature}; - my $signature = ''; - if ( $client_signature or $user_signature ) { - $signature = "\n\n-- \n"; - $signature .= $user_signature . "\n" if $user_signature; - $signature .= $client_signature . "\n" if $client_signature; - }; - return $signature; - -}; + return join '', grep { $_ } ($user_signature, $client_signature); +} sub calculate_tax { # this function calculates the net amount and tax for the lines in ar, ap and diff --git a/SL/HTML/Util.pm b/SL/HTML/Util.pm index a54425d83..07031869f 100644 --- a/SL/HTML/Util.pm +++ b/SL/HTML/Util.pm @@ -41,6 +41,31 @@ sub strip { return delete $stripper{text}; } +sub plain_text_to_html { + my ($class_or_text) = @_; + + my $text = !ref($class_or_text) && (($class_or_text // '') eq 'SL::HTML::Util') ? $_[1] : $class_or_text; + + return $text if $text =~ m{^

.*

$}; + + $text =~ s{\r+}{}g; + $text =~ s{^[[:space:]]+|[[:space:]]+$}{}g; + + return '' if $text eq ''; + + my @paragraphs; + + foreach my $paragraph (split m{\n{2,}}, $text) { + no warnings 'once'; + $paragraph = $::locale->quote_special_chars('HTML', $paragraph); + $paragraph =~ s{\n}{
}g; + + push @paragraphs, $paragraph; + } + + return '

' . join('

', @paragraphs) . '

'; +} + 1; __END__ @@ -65,6 +90,12 @@ SL::HTML::Util - Utility functions dealing with HTML Removes all HTML elements and tags from C<$html_content> and returns the remaining plain text. +=item C + +Converts a plain text to HTML: paragraphs will be recognized by empty +lines; remaining newlines will be converted into forced line breaks; +the rest will be HTML escaped. + =back =head1 BUGS diff --git a/bin/mozilla/am.pl b/bin/mozilla/am.pl index 0b35fc7d9..e24fae6c5 100644 --- a/bin/mozilla/am.pl +++ b/bin/mozilla/am.pl @@ -669,14 +669,14 @@ sub config { $myconfig{show_form_details} = 1 unless (defined($myconfig{show_form_details})); $form->{CAN_CHANGE_PASSWORD} = $main::auth->can_change_password(); $form->{todo_cfg} = { TODO->get_user_config('login' => $::myconfig{login}) }; - - $::request->{layout}->use_javascript("jquery.multiselect2side.js"); $form->{title} = $locale->text('Edit Preferences for #1', $::myconfig{login}); + $::request->{layout}->use_javascript("${_}.js") for qw(jquery.multiselect2side ckeditor/ckeditor ckeditor/adapters/jquery); + setup_am_config_action_bar(); $form->header(); - $form->{full_signature} = $form->create_email_signature(); + $form->{company_signature} = SL::DB::Default->get->signature; print $form->parse_html_template('am/config'); diff --git a/bin/mozilla/generictranslations.pl b/bin/mozilla/generictranslations.pl index 4e6d26328..de7d924bd 100644 --- a/bin/mozilla/generictranslations.pl +++ b/bin/mozilla/generictranslations.pl @@ -178,6 +178,7 @@ sub edit_email_strings { setup_generictranslations_edit_email_strings_action_bar(); $form->{title} = $locale->text('Edit preset email strings'); + $::request->{layout}->use_javascript(map { "${_}.js" } qw(ckeditor/ckeditor ckeditor/adapters/jquery)); $form->header(); print $form->parse_html_template('generictranslations/edit_email_strings',{ 'MAIL_STRINGS' => \%mail_strings }); diff --git a/js/kivi.SalesPurchase.js b/js/kivi.SalesPurchase.js index e8a1ba241..771351cf5 100644 --- a/js/kivi.SalesPurchase.js +++ b/js/kivi.SalesPurchase.js @@ -241,9 +241,17 @@ namespace('kivi.SalesPurchase', function(ns) { if (!kivi.SalesPurchase.check_required_email_fields()) return false; + // ckeditor gets de-initialized when removing the children from + // the DOM. Therefore we have to manually preserve its content + // over the children's relocation. + + var message = $('#email_form_message').val(); + $('#send_email_dialog').children().remove().appendTo('#email_inputs'); $('#send_email_dialog').dialog('close'); + $('#email_form_message').val(message); + kivi.submit_form_with_action('#form', $('#form').data('send-email-action')); return true; @@ -255,6 +263,8 @@ namespace('kivi.SalesPurchase', function(ns) { $('#print_options').children().remove().appendTo('#email_form_print_options'); + kivi.reinit_widgets(); + var to_focus = $('#email_form_to').val() === '' ? 'to' : 'subject'; $('#email_form_' + to_focus).focus(); }; diff --git a/locale/de/all b/locale/de/all index 0807feb38..1f8d087e3 100755 --- a/locale/de/all +++ b/locale/de/all @@ -649,7 +649,6 @@ $self->{texts} = { 'Check Details' => 'Bitte Angaben überprüfen', 'Check connectivity' => 'Verbindungstest', 'Check for duplicates' => 'Dublettencheck', - 'Check full signature' => 'Volle Signatur prüfen', 'Check on ap transaction' => 'Prüfen bei Kreditorenbuchung', 'Check on ar transaction' => 'Prüfen bei Debitorenbuchung', 'Check on gl transaction' => 'Prüfen bei Dialogbuchung', @@ -698,6 +697,7 @@ $self->{texts} = { 'Company name' => 'Firmenname', 'Company name and address' => 'Firmenname und -adresse', 'Company settings' => 'Firmeneinstellungen', + 'Company\'s email signature' => 'Firmen-E-Mail-Signatur', 'Compare to' => 'Gegenüberstellen zu', 'Complexities' => 'Komplexitätsgrade', 'Complexity' => 'Komplexität', @@ -1328,14 +1328,15 @@ $self->{texts} = { 'Edit time recordings of all staff members' => 'Zeiterfassungseinträge aller Mitarbeiter bearbeiten', 'Edit title' => 'Titiel bearbeiten', 'Edit units' => 'Einheiten bearbeiten', - 'Edit user signature' => 'Benutzersignatur bearbeiten', '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' => 'E-Mail', + 'Email address' => 'E-Mail-Adresse', 'Email journal' => 'E-Mail-Journal', 'Email of the delivery order recipient' => 'E-Mail des Lieferscheinempfängers', 'Email of the invoice recipient' => 'E-Mail des Rechnungsempfängers', + 'Email signature' => 'E-Mail-Signatur', 'Employee' => 'Bearbeiter', 'Employee #1 saved!' => 'Benutzer #1 gespeichert!', 'Employee (database ID)' => 'Bearbeiter (Datenbank-ID)', diff --git a/locale/en/all b/locale/en/all index 8ae48e1a8..aabb43ca3 100644 --- a/locale/en/all +++ b/locale/en/all @@ -649,7 +649,6 @@ $self->{texts} = { 'Check Details' => '', 'Check connectivity' => '', 'Check for duplicates' => '', - 'Check full signature' => '', 'Check on ap transaction' => '', 'Check on ar transaction' => '', 'Check on gl transaction' => '', @@ -698,6 +697,7 @@ $self->{texts} = { 'Company name' => '', 'Company name and address' => '', 'Company settings' => '', + 'Company\'s email signature' => '', 'Compare to' => '', 'Complexities' => '', 'Complexity' => '', @@ -1328,14 +1328,15 @@ $self->{texts} = { 'Edit time recordings of all staff members' => '', 'Edit title' => '', 'Edit units' => '', - 'Edit user signature' => '', 'Editable' => '', 'Either there are no open invoices, or you have already initiated bank transfers with the open amounts for those that are still open.' => '', 'Element disabled' => '', 'Email' => '', + 'Email address' => '', 'Email journal' => '', 'Email of the delivery order recipient' => '', 'Email of the invoice recipient' => '', + 'Email signature' => '', 'Employee' => '', 'Employee #1 saved!' => '', 'Employee (database ID)' => '', diff --git a/sql/Pg-upgrade2-auth/convert_columns_to_html_for_sending_html_emails.pl b/sql/Pg-upgrade2-auth/convert_columns_to_html_for_sending_html_emails.pl new file mode 100644 index 000000000..f8ffbe63f --- /dev/null +++ b/sql/Pg-upgrade2-auth/convert_columns_to_html_for_sending_html_emails.pl @@ -0,0 +1,46 @@ +# @tag: convert_columns_to_html_for_sending_html_emails +# @description: Versand von E-Mails in HTML: mehrere Text-Spalten nach HTML umwandeln +# @depends: release_3_5_8 +package SL::DBUpgrade2::Auth::convert_columns_to_html_for_sending_html_emails; + +use strict; +use utf8; + +use parent qw(SL::DBUpgrade2::Base); + +use SL::HTML::Util; + +sub run { + my ($self) = @_; + + my $q_fetch = <dbh->prepare($q_fetch); + $h_fetch->execute || $::form->dberror($q_fetch); + + my $h_update = $self->dbh->prepare($q_update); + + while (my $entry = $h_fetch->fetchrow_hashref) { + $entry->{cfg_value} //= ''; + my $new_value = SL::HTML::Util->plain_text_to_html($entry->{cfg_value}); + + next if $entry->{cfg_value} eq $new_value; + + $h_update->execute($new_value, $entry->{user_id}) || $::form->dberror($q_update); + } + + return 1; +} + +1; diff --git a/sql/Pg-upgrade2/convert_columns_to_html_for_sending_html_emails.pl b/sql/Pg-upgrade2/convert_columns_to_html_for_sending_html_emails.pl new file mode 100644 index 000000000..168ea459f --- /dev/null +++ b/sql/Pg-upgrade2/convert_columns_to_html_for_sending_html_emails.pl @@ -0,0 +1,62 @@ +# @tag: convert_columns_to_html_for_sending_html_emails +# @description: Versand von E-Mails in HTML: mehrere Text-Spalten nach HTML umwandeln +# @depends: release_3_5_8 +package SL::DBUpgrade2::convert_columns_to_html_for_sending_html_emails; + +use strict; +use utf8; + +use parent qw(SL::DBUpgrade2::Base); + +use SL::HTML::Util; + +sub convert_column { + my ($self, $table, $id_column, $column_to_convert, $condition) = @_; + + $condition = $condition ? "WHERE $condition" : ""; + + my $q_fetch = <dbh->prepare($q_fetch); + $h_fetch->execute || $::form->dberror($q_fetch); + + my $h_update = $self->dbh->prepare($q_update); + + while (my $entry = $h_fetch->fetchrow_hashref) { + $entry->{$column_to_convert} //= ''; + my $new_value = SL::HTML::Util->plain_text_to_html($entry->{$column_to_convert}); + + next if $entry->{$column_to_convert} eq $new_value; + + $h_update->execute($new_value, $entry->{id}) || $::form->dberror($q_update); + } +} + +sub run { + my ($self) = @_; + + $self->convert_column('defaults', 'id', 'signature'); + $self->convert_column('employee', 'id', 'deleted_signature'); + $self->convert_column('periodic_invoices_configs', 'id', 'email_body'); + $self->convert_column('generic_translations', 'id', 'translation', < [% LxERP.t8('Signature') %] - [% L.textarea_tag("user.config_values.signature", props.signature, rows=3, cols=35) %] + [% L.textarea_tag("user.config_values.signature", props.signature, rows=3, cols=35, class="texteditor") %] diff --git a/templates/webpages/am/config.html b/templates/webpages/am/config.html index 6901cba8c..4a8d64d52 100644 --- a/templates/webpages/am/config.html +++ b/templates/webpages/am/config.html @@ -1,7 +1,7 @@ [%- USE T8 %] [%- USE LxERP %] [%- USE HTML %] -[%- USE L %] +[%- USE L %][%- USE P -%]

[% title %]

@@ -34,17 +34,22 @@ - [% 'E-mail' | $T8 %] + [% 'Email address' | $T8 %] - [% 'Signature' | $T8 %] - - - [% 'Check full signature' | $T8 %] - [% 'Edit user signature' | $T8 %] - + [% 'Email signature' | $T8 %] + + [% P.textarea_tag("signature", MYCONFIG.signature, class="texteditor", rows="5", cols="50") %] + + + + + [% "Company's email signature" | $T8 %] + [% P.restricted_html(company_signature) %] + + [% 'Phone' | $T8 %] @@ -333,17 +338,3 @@ - - diff --git a/templates/webpages/client_config/_miscellaneous.html b/templates/webpages/client_config/_miscellaneous.html index 06067676e..752bcd816 100644 --- a/templates/webpages/client_config/_miscellaneous.html +++ b/templates/webpages/client_config/_miscellaneous.html @@ -36,7 +36,7 @@ [% LxERP.t8("Signature") %] - [% L.textarea_tag('defaults.signature', SELF.defaults.signature, style=style, rows=4) %] + [% L.textarea_tag('defaults.signature', SELF.defaults.signature, style=style, rows=4, class='texteditor') %] diff --git a/templates/webpages/common/_send_email_dialog.html b/templates/webpages/common/_send_email_dialog.html index b039b253f..23cc1a9f7 100644 --- a/templates/webpages/common/_send_email_dialog.html +++ b/templates/webpages/common/_send_email_dialog.html @@ -68,7 +68,7 @@ [% LxERP.t8("Message") %] [% L.link("generictranslations.pl?action=edit_email_strings", "1)", title=LxERP.t8('Tired of copying always nice phrases for this message? Click here to use the new preset message option!'), target="_blank") %] - [% L.textarea_tag("email_form.message", email_form.message, rows="15" cols="80" wrap="soft") %] + [% L.textarea_tag("email_form.message", email_form.message, rows="15", cols="80", class="texteditor") %] [% IF INSTANCE_CONF.get_doc_storage %] diff --git a/templates/webpages/email_journal/show.html b/templates/webpages/email_journal/show.html index 20444a9c8..b484bc76c 100644 --- a/templates/webpages/email_journal/show.html +++ b/templates/webpages/email_journal/show.html @@ -1,4 +1,4 @@ -[% USE HTML %][% USE L %][% USE LxERP %] +[% USE HTML %][% USE L %][% USE LxERP %][%- USE P -%]

[% FORM.title %]

@@ -49,7 +49,13 @@ [%- LxERP.t8("Body") %] -
[% HTML.escape(SELF.entry.body) %]
+ + [%- IF SELF.entry.headers.match('(?i)content-type:.*text/html') %] + [% P.restricted_html(SELF.entry.body) %] + [%- ELSE %] +
[% HTML.escape(SELF.entry.body) %]
+ [%- END %] + diff --git a/templates/webpages/generictranslations/edit_email_strings.html b/templates/webpages/generictranslations/edit_email_strings.html index 2db858dad..a25cc7e41 100644 --- a/templates/webpages/generictranslations/edit_email_strings.html +++ b/templates/webpages/generictranslations/edit_email_strings.html @@ -28,8 +28,8 @@ [%- END %] - [%- IF mail_string.search('preset') %] - + [%- IF mail_string.search('preset') && !mail_string.search('subject')%] + [%- ELSE %] [%- END %] diff --git a/templates/webpages/is/form_header.html b/templates/webpages/is/form_header.html index 8f307fde2..5b78c3ec5 100644 --- a/templates/webpages/is/form_header.html +++ b/templates/webpages/is/form_header.html @@ -57,6 +57,7 @@ [% P.customer_vendor.picker("customer_id", customer_id, type="customer", style="width: 250px", class="initial_focus", onchange="\$('#update_button').click()") %] [% L.button_tag("show_vc_details('customer')", LxERP.t8('Details (one letter abbreviation)')) %] + [% P.link_tag('controller.pl?action=CustomerVendor/edit&db=customer&id=' _ customer_id, LxERP.t8('Edit'), target="_blank", title=LxERP.t8('Open in new window')) %] [% L.hidden_tag("previous_customer_id", customer_id) %] [% L.hidden_tag("customer_pricegroup_id", customer_pricegroup_id) %] diff --git a/templates/webpages/oe/edit_periodic_invoices_config.html b/templates/webpages/oe/edit_periodic_invoices_config.html index f9802bfc4..d7ee7263c 100644 --- a/templates/webpages/oe/edit_periodic_invoices_config.html +++ b/templates/webpages/oe/edit_periodic_invoices_config.html @@ -141,7 +141,7 @@ [%- LxERP.t8("Message") %] - [% L.textarea_tag("email_body", config.email_body, disabled=!config.send_email, rows=8, style=style) %] + [% L.textarea_tag("email_body", config.email_body, disabled=!config.send_email, rows=8, style=style, class="texteditor") %]

@@ -168,7 +168,7 @@ $('#email_recipient_address').prop('disabled', disabled); $('#email_sender').prop('disabled', disabled); $('#email_subject').prop('disabled', disabled); - $('#email_body').prop('disabled', disabled); + $('#email_body').data('ckeditorInstance').setReadOnly(disabled); } -->