invoice => $data->{invoice},
vars => $data->{time_period_vars},
attribute => $_,
- attribute_format => 'text'
+ attribute_format => ($_ eq 'email_body' ? 'html' : 'text')
);
}
$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),
use SL::DBUtils;
use SL::DB;
+use SL::HTML::Util;
sub unique_id {
my ($a, $b) = gettimeofday();
. $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;
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);
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 {
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'),
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;
$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);
$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}{<br>\n}g;
- $full_signature =~ s{\n}{<br>\n}g;
$mail->{message} .= $full_signature;
open(IN, "<", $self->{tmpfile})
return undef unless $body;
+ $body .= GenericTranslations->get(translation_type => "salutation_punctuation_mark", language_id => $self->{language_id});
+ $body = '<p>' . $::locale->quote_special_chars('HTML', $body) . '</p>';
+
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);
}
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
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{^<p>.*</p>$};
+
+ $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}{<br>}g;
+
+ push @paragraphs, $paragraph;
+ }
+
+ return '<p>' . join('</p><p>', @paragraphs) . '</p>';
+}
+
1;
__END__
Removes all HTML elements and tags from C<$html_content> and returns
the remaining plain text.
+=item C<plain_text_to_html $text>
+
+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
$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');
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 });
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;
$('#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();
};
'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',
'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',
'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)',
'Check Details' => '',
'Check connectivity' => '',
'Check for duplicates' => '',
- 'Check full signature' => '',
'Check on ap transaction' => '',
'Check on ar transaction' => '',
'Check on gl transaction' => '',
'Company name' => '',
'Company name and address' => '',
'Company settings' => '',
+ 'Company\'s email signature' => '',
'Compare to' => '',
'Complexities' => '',
'Complexity' => '',
'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)' => '',
--- /dev/null
+# @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 = <<SQL;
+ SELECT user_id, cfg_key, cfg_value
+ FROM auth.user_config
+ WHERE (cfg_key = 'signature')
+SQL
+
+ my $q_update = <<SQL;
+ UPDATE auth.user_config
+ SET cfg_value = ?
+ WHERE (user_id = ?)
+ AND (cfg_key = 'signature')
+SQL
+
+ my $h_fetch = $self->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;
--- /dev/null
+# @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 = <<SQL;
+ SELECT ${id_column}, ${column_to_convert}
+ FROM ${table}
+ ${condition}
+SQL
+
+ my $q_update = <<SQL;
+ UPDATE ${table}
+ SET ${column_to_convert} = ?
+ WHERE ${id_column} = ?
+SQL
+
+ my $h_fetch = $self->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', <<SQL);
+ translation_type IN (
+ 'preset_text_sales_quotation', 'preset_text_sales_order', 'preset_text_sales_delivery_order',
+ 'preset_text_invoice', 'preset_text_invoice_direct_debit', 'preset_text_request_quotation',
+ 'preset_text_purchase_order', 'preset_text_periodic_invoices_email_body'
+ )
+SQL
+
+ return 1;
+}
+
+1;
<tr valign="top">
<th align="right">[% LxERP.t8('Signature') %]</th>
- <td>[% L.textarea_tag("user.config_values.signature", props.signature, rows=3, cols=35) %]</td>
+ <td>[% L.textarea_tag("user.config_values.signature", props.signature, rows=3, cols=35, class="texteditor") %]</td>
</tr>
<tr>
[%- USE T8 %]
[%- USE LxERP %]
[%- USE HTML %]
-[%- USE L %]
+[%- USE L %][%- USE P -%]
<h1>[% title %]</h1>
</tr>
<tr>
- <th align="right">[% 'E-mail' | $T8 %]</th>
+ <th align="right">[% 'Email address' | $T8 %]</th>
<td><input name="email" size="30" value="[% HTML.escape(MYCONFIG.email) %]"></td>
</tr>
<tr valign="top">
- <th align="right">[% 'Signature' | $T8 %]</th>
- <td><textarea id="signature" name="signature" class="toggletextarea" rows="5" cols="50">[% HTML.escape(MYCONFIG.signature) %] </textarea>
- <span id="full_signature" class="toggletextarea"> <textarea readonly name="full_signature" rows="10" cols="50" >[% HTML.escape(full_signature) %]</textarea> </span>
- <a href="#" class="togglelink">[% 'Check full signature' | $T8 %]</a>
- <a href="#" id="edit_signature" class="togglelink">[% 'Edit user signature' | $T8 %]</a>
- </td> </tr>
+ <th align="right">[% 'Email signature' | $T8 %]</th>
+ <td>
+ [% P.textarea_tag("signature", MYCONFIG.signature, class="texteditor", rows="5", cols="50") %]
+ </td>
+ </tr>
+
+ <tr valign="top">
+ <th align="right">[% "Company's email signature" | $T8 %]</th>
+ <td>[% P.restricted_html(company_signature) %]</td>
+ </tr>
+
<tr>
<th align="right">[% 'Phone' | $T8 %]</th>
<td><input name="tel" size="14" value="[% HTML.escape(MYCONFIG.tel) %]"></td>
</div>
</div>
</form>
-
- <script type="text/javascript">
- <!--
-$(function() {
- $("#full_signature").toggle();
- $("#edit_signature").toggle();
- $('.togglelink').click(function() {
- $('.toggletextarea').toggle();
- $('.togglelink').toggle();
- return false;
- });
-});
- -->
- </script>
<tr>
<td align="right" valign="top">[% LxERP.t8("Signature") %]</td>
- <td valign="top">[% L.textarea_tag('defaults.signature', SELF.defaults.signature, style=style, rows=4) %]</td>
+ <td valign="top">[% L.textarea_tag('defaults.signature', SELF.defaults.signature, style=style, rows=4, class='texteditor') %]</td>
</tr>
<tr>
<th align="right" nowrap>[% LxERP.t8("Message") %]
<sup> [% 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") %]</sup>
</th>
- <td>[% L.textarea_tag("email_form.message", email_form.message, rows="15" cols="80" wrap="soft") %]</td>
+ <td>[% L.textarea_tag("email_form.message", email_form.message, rows="15", cols="80", class="texteditor") %]</td>
</tr>
[% IF INSTANCE_CONF.get_doc_storage %]
-[% USE HTML %][% USE L %][% USE LxERP %]
+[% USE HTML %][% USE L %][% USE LxERP %][%- USE P -%]
<h1>[% FORM.title %]</h1>
<tr class="listrow">
<th>[%- LxERP.t8("Body") %]</th>
- <td><pre>[% HTML.escape(SELF.entry.body) %]</pre></td>
+ <td>
+ [%- IF SELF.entry.headers.match('(?i)content-type:.*text/html') %]
+ [% P.restricted_html(SELF.entry.body) %]
+ [%- ELSE %]
+ <pre>[% HTML.escape(SELF.entry.body) %]</pre>
+ [%- END %]
+ </td>
</tr>
</table>
[%- END %]
</td>
<td>
- [%- IF mail_string.search('preset') %]
- <textarea name="translation__[% language.id %]__[% mail_string %]" rows="4" cols="60">[% HTML.escape(language.$mail_string) %]</textarea>
+ [%- IF mail_string.search('preset') && !mail_string.search('subject')%]
+ <textarea name="translation__[% language.id %]__[% mail_string %]" rows="4" cols="60" class="texteditor">[% HTML.escape(language.$mail_string) %]</textarea>
[%- ELSE %]
<input name="translation__[% language.id %]__[% mail_string %]" size="40" value="[% HTML.escape(language.$mail_string) %]">
[%- END %]
<tr>
<th align="right" valign="top">[%- LxERP.t8("Message") %]</th>
- <td valign="top">[% L.textarea_tag("email_body", config.email_body, disabled=!config.send_email, rows=8, style=style) %]</td>
+ <td valign="top">[% L.textarea_tag("email_body", config.email_body, disabled=!config.send_email, rows=8, style=style, class="texteditor") %]</td>
</tr>
</table>
</p>
$('#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);
}
-->
</script>