longdescription => 'html',
partnotes => 'html',
notes => 'html',
+ $::form->get_variable_content_types_for_cvars,
},
);
use SL::DBUtils;
use SL::MoreCommon qw(listify);
+use SL::Presenter::Text;
use SL::Util qw(trim);
use SL::DB;
} elsif ($config->{type} eq 'number') {
$config->{precision} = $1 if ($config->{options} =~ m/precision=(\d+)/i);
- } elsif ($config->{type} eq 'textfield') {
+ } elsif ($config->{type} =~ m{^(?:html|text)field$}) {
$config->{width} = 30;
$config->{height} = 5;
$config->{width} = $1 if ($config->{options} =~ m/width=(\d+)/i);
my $custom_variables = $self->get_configs(module => $params{module});
foreach my $cvar (@{ $custom_variables }) {
- if ($cvar->{type} eq 'textfield') {
+ if ($cvar->{type} =~ m{^(?:html|text)field}) {
$cvar->{width} = 30;
$cvar->{height} = 5;
my $value = $params{variables}->{"$params{name_prefix}cvar_$config->{name}$params{name_postfix}"};
- if (($config->{type} eq 'text') || ($config->{type} eq 'textfield') || ($config->{type} eq 'select')) {
+ if (any { $config->{type} eq $_ } qw(text textfield htmlfield select)) {
push @values, undef, undef, $value, undef;
} elsif (($config->{type} eq 'date') || ($config->{type} eq 'timestamp')) {
my (@sub_values, @sub_where, $not);
- if (($config->{type} eq 'text') || ($config->{type} eq 'textfield')) {
+ if (any { $config->{type} eq $_ } qw(text textfield htmlfield)) {
next unless ($params{filter}->{$name});
push @sub_where, qq|cvar.text_value ILIKE ?|;
: $cfg->{type} eq 'vendor' ? (SL::DB::Manager::Vendor->find_by(id => 1*$ref->{number_value}) || SL::DB::Vendor->new)->name
: $cfg->{type} eq 'part' ? (SL::DB::Manager::Part->find_by(id => 1*$ref->{number_value}) || SL::DB::Part->new)->partnumber
: $cfg->{type} eq 'bool' ? ($ref->{bool_value} ? $locale->text('Yes') : $locale->text('No'))
+ : $cfg->{type} eq 'htmlfield' ? SL::Presenter::Text::stripped_html($ref->{text_value})
: $ref->{text_value};
}
}
my %type_to_column = ( text => 'text_value',
textfield => 'text_value',
+ htmlfield => 'text_value',
select => 'text_value',
date => 'timestamp_value_as_date',
timestamp => 'timestamp_value_as_date',
my %type_to_column = ( text => 'text_value',
textfield => 'text_value',
+ htmlfield => 'text_value',
select => 'text_value',
date => 'timestamp_value_as_date',
timestamp => 'timestamp_value_as_date',
our %translations = (
text => t8('Free-form text'),
textfield => t8('Text field'),
+ htmlfield => t8('HTML field'),
number => t8('Number'),
date => t8('Date'),
timestamp => t8('Timestamp'),
part => t8('Part'),
);
-our @types = qw(text textfield number date bool select customer vendor part); # timestamp
+our @types = qw(text textfield htmlfield number date bool select customer vendor part); # timestamp
#
# actions
$self->{template_args} ||= {};
- $::request->{layout}->add_javascripts('kivi.CustomerVendor.js');
- $::request->{layout}->add_javascripts('kivi.File.js');
- $::request->{layout}->add_javascripts('kivi.CustomerVendorTurnover.js');
+ $::request->{layout}->add_javascripts("$_.js") for qw (kivi.CustomerVendor kivi.File kivi.CustomerVendorTurnover ckeditor/ckeditor ckeditor/adapters/jquery);
$self->_setup_form_action_bar;
}
longdescription => 'html',
partnotes => 'html',
notes => 'html',
+ $::form->get_variable_content_types_for_cvars,
},
);
1;
CVar->render_inputs(variables => $params{CUSTOM_VARIABLES}) if @{ $params{CUSTOM_VARIABLES} };
- $::request->layout->use_javascript('kivi.File.js');
+ $::request->layout->use_javascript("$_.js") for qw(kivi.File ckeditor/ckeditor ckeditor/adapters/jquery);
$self->setup_edit_action_bar(callback => $params{callback});
$self->render('project/form', %params);
$self->js
->hide('#basic_settings')
->after('#basic_settings', $html)
+ ->reinit_widgets
->render;
}
$::auth->assert('requirement_spec_edit');
$::request->{layout}->use_stylesheet("${_}.css") for qw(jquery.contextMenu requirement_spec);
- $::request->{layout}->use_javascript("${_}.js") for qw(jquery.jstree jquery/jquery.contextMenu jquery/jquery.hotkeys requirement_spec ckeditor/ckeditor ckeditor/adapters/jquery kivi.Part kivi.CustomerVendor);
+ $::request->{layout}->use_javascript("${_}.js") for qw(jquery.jstree jquery/jquery.contextMenu jquery/jquery.hotkeys requirement_spec ckeditor/ckeditor ckeditor/adapters/jquery kivi.Part kivi.CustomerVendor
+ ckeditor/ckeditor ckeditor/adapters/jquery);
$self->init_visible_section;
return 1;
return $self->timestamp_value(!defined($unparsed) ? undef : ref($unparsed) eq 'DateTime' ? $unparsed->clone : DateTime->from_kivitendo($unparsed));
}
- # text, textfield, select
+ # text, textfield, htmlfield and select
$self->text_value($unparsed);
}
return $self->timestamp_value ? $self->timestamp_value->clone->truncate(to => 'day') : undef;
}
- goto &text_value; # text, textfield and select
+ goto &text_value; # text, textfield, htmlfield and select
}
sub value_as_text {
return $object ? $object->displayable_name : '';
}
- goto &text_value; # text, textfield and select
+ goto &text_value; # text, textfield, htmlfield and select
}
sub is_valid {
use constant OPTION_DEFAULTS =>
{
MAXLENGTH => 75,
- WIDTH => 30,
- HEIGHT => 5,
+ WIDTH => 225,
+ HEIGHT => 90,
};
sub processed_options {
customer => 'number_value',
vendor => 'number_value',
part => 'number_value',
+ htmlfield => 'text_value',
text => 'text_value',
textfield => 'text_value',
select => 'text_value'
longdescription => 'html',
partnotes => 'html',
notes => 'html',
+ $print_form->get_variable_content_types_for_cvars,
},
);
use SL::DBUtils;
use SL::DB::AdditionalBillingAddress;
use SL::DB::Customer;
+use SL::DB::CustomVariableConfig;
use SL::DB::Default;
use SL::DB::PaymentTerm;
use SL::DB::Vendor;
}
sub get_variable_content_types {
- my %html_variables = (
- longdescription => 'html',
- partnotes => 'html',
- notes => 'html',
- orignotes => 'html',
- notes1 => 'html',
- notes2 => 'html',
- notes3 => 'html',
- notes4 => 'html',
- header_text => 'html',
- footer_text => 'html',
+ my ($self) = @_;
+
+ my %html_variables = (
+ longdescription => 'html',
+ partnotes => 'html',
+ notes => 'html',
+ orignotes => 'html',
+ notes1 => 'html',
+ notes2 => 'html',
+ notes3 => 'html',
+ notes4 => 'html',
+ header_text => 'html',
+ footer_text => 'html',
);
- return \%html_variables;
+
+ return {
+ %html_variables,
+ $self->get_variable_content_types_for_cvars,
+ };
+}
+
+sub get_variable_content_types_for_cvars {
+ my ($self) = @_;
+ my $html_configs = SL::DB::Manager::CustomVariableConfig->get_all(where => [ type => 'htmlfield' ]);
+ my %types;
+
+ if (@{ $html_configs }) {
+ my %prefix_by_module = (
+ Contacts => 'cp_cvar_',
+ CT => 'vc_cvar_',
+ IC => 'ic_cvar_',
+ Projects => 'project_cvar_',
+ ShipTo => 'shiptocvar_',
+ );
+
+ foreach my $cfg (@{ $html_configs }) {
+ my $prefix = $prefix_by_module{$cfg->module};
+ $types{$prefix . $cfg->name} = 'html' if $prefix;
+ }
+ }
+
+ return %types;
}
sub current_date {
if (!%stripper) {
%stripper = ( parser => HTML::Parser->new );
- $stripper{parser}->handler(text => sub { $stripper{text} .= $_[1]; });
+ $stripper{parser}->handler(text => sub { $stripper{text} .= ' ' . $_[1]; });
}
$stripper{text} = '';
$stripper{parser}->eof;
$stripper{text} =~ s{\&([^;]+);}{ $entities{$1} || "\&$1;" }eg;
+ $stripper{text} =~ s{^ +| +$}{}g;
+ $stripper{text} =~ s{ {2,}}{ }g;
return delete $stripper{text};
}
use strict;
use SL::Presenter::EscapedText qw(escape);
+use SL::HTML::Restrict;
+use SL::HTML::Util;
use Exporter qw(import);
-our @EXPORT_OK = qw(format_man_days simple_format truncate);
+our @EXPORT_OK = qw(format_man_days simple_format truncate restricted_html);
our %EXPORT_TAGS = (ALL => \@EXPORT_OK);
use Carp;
+my $html_cleaner;
+
sub truncate {
my ($text, %params) = @_;
escape($output);
}
+sub restricted_html {
+ my ($value) = @_;
+ $html_cleaner //= SL::HTML::Restrict->create;
+ return $html_cleaner->process($value);
+}
+
+sub stripped_html {
+ my ($value) = @_;
+ return SL::HTML::Util::strip($value);
+}
+
1;
__END__
one. Single newlines are converted to line breaks. Carriage returns
are removed.
+=item C<restricted_html $unsafe_html>
+
+Returns HTML code stripped from unwanted/unsupported content. This is
+done via the module L<SL::HTML::Restrict>.
+
+=item C<stripped_html $html>
+
+Returns the raw text with all HTML tags and comments stripped. This is
+done via L<SL::HTML::Util/strip>.
+
=back
=head1 BUGS
sub div_tag { return _call_presenter('div_tag', @_); }
sub radio_button_tag { return _call_presenter('radio_button_tag', @_); }
sub img_tag { return _call_presenter('img_tag', @_); }
+sub restricted_html { return _call_presenter('restricted_html', @_); }
+sub stripped_html { return _call_presenter('stripped_html', @_); }
sub _set_id_attribute {
my ($attributes, $name, $unique) = @_;
'Groups valid for this client' => 'Für Mandanten gültige Gruppen',
'HTML' => 'HTML',
'HTML Templates' => 'HTML-Vorlagen',
+ 'HTML field' => 'HTML-Feld',
'Handling of WebDAV' => 'Behandlung von WebDAV',
'Hardcopy' => 'Seite drucken',
'Has item type' => 'Hat Regeltypen',
'Text blocks back' => 'Textblöcke hinten',
'Text blocks front' => 'Textblöcke vorne',
'Text field' => 'Textfeld',
- 'Text field variables: \'WIDTH=w HEIGHT=h\' sets the width and height of the text field. They default to 30 and 5 respectively.' => 'Textfelder: \'WIDTH=w HEIGHT=h\' setzen die Breite und die Höhe des Textfeldes. Wenn nicht anders angegeben, so werden sie 30 Zeichen breit und fünf Zeichen hoch dargestellt.',
+ 'Text field and HTML field variables: \'WIDTH=w HEIGHT=h\' sets the width and height of the field in pixels. They default to 225 and 90 respectively.' => 'Textfelder und HTML-Felder: \'WIDTH=w HEIGHT=h\' setzen die Breite und die Höhe des Feldes in Pixeln. Wenn nicht anders angegeben, so werden sie 225 Pixel breit und 90 Pixel hoch dargestellt.',
'Text in CSV File' => 'Spalte in der CSV Datei',
'Text variables: \'MAXLENGTH=n\' sets the maximum entry length to \'n\'.' => 'Textzeilen: \'MAXLENGTH=n\' setzt eine Maximallänge von n Zeichen.',
- 'Text, text field and number variables: The default value will be used as-is.' => 'Textzeilen, Textfelder und Zahlenvariablen: Der Standardwert wird so wie er ist übernommen.',
+ 'Text, text field, HTML field and number variables: The default value will be used as-is.' => 'Textzeilen, Textfelder, HTML-Felder und Zahlenvariablen: Der Standardwert wird so wie er ist übernommen.',
'Texts for invoices' => 'Texte für Rechnungen',
'Texts for quotations & orders' => 'Texte für Angebote & Aufträge',
'That export does not exist.' => 'Dieser Export existiert nicht.',
'Groups valid for this client' => '',
'HTML' => '',
'HTML Templates' => '',
+ 'HTML field' => '',
'Handling of WebDAV' => '',
'Hardcopy' => '',
'Has item type' => '',
'Text blocks back' => '',
'Text blocks front' => '',
'Text field' => '',
- 'Text field variables: \'WIDTH=w HEIGHT=h\' sets the width and height of the text field. They default to 30 and 5 respectively.' => '',
+ 'Text field and HTML field variables: \'WIDTH=w HEIGHT=h\' sets the width and height of the field in pixels. They default to 225 and 90 respectively.' => '',
'Text in CSV File' => '',
'Text variables: \'MAXLENGTH=n\' sets the maximum entry length to \'n\'.' => '',
- 'Text, text field and number variables: The default value will be used as-is.' => '',
+ 'Text, text field, HTML field and number variables: The default value will be used as-is.' => '',
'Texts for invoices' => '',
'Texts for quotations & orders' => '',
'That export does not exist.' => '',
--- /dev/null
+# @tag: custom_variables_convert_width_height_to_pixels
+# @description: Benutzerdefinierte Variablen: Optionen »WIDTH« & »HEIGHT« nach Pixel konvertieren
+# @depends: release_3_5_8
+package SL::DBUpgrade2::custom_variables_convert_width_height_to_pixels;
+
+use strict;
+use utf8;
+
+use parent qw(SL::DBUpgrade2::Base);
+
+use SL::DBUtils;
+
+sub find_configs {
+ my ($self) = @_;
+
+ my $sql = <<SQL;
+ SELECT id, options
+ FROM custom_variable_configs
+ WHERE (COALESCE(options, '') ~ 'WIDTH=|HEIGHT=')
+ AND (type = 'textfield')
+SQL
+
+ return selectall_hashref_query($::form, $self->dbh, $sql);
+}
+
+sub fix_configs {
+ my ($self, $configs) = @_;
+
+ my $sql = <<SQL;
+ UPDATE custom_variable_configs
+ SET options = ?
+ WHERE id = ?
+SQL
+
+ my $update_h = prepare_query($::form, $self->dbh, $sql);
+
+ # Old defaults: 30 columns, 5 rows
+ # New defaults: 225px width, 90px height
+
+ foreach my $config (@{ $configs }) {
+ $config->{options} =~ s{WIDTH=(\d+)}{ int($1 * (225 / 30.0)) }eg;
+ $config->{options} =~ s{HEIGHT=(\d+)}{ int($1 * ( 90 / 5.0)) }eg;
+
+ $update_h->execute(@{$config}{qw(options id)}) || $self->db_error($sql);
+ }
+
+ $update_h->finish;
+}
+
+sub run {
+ my ($self) = @_;
+
+ my $configs = $self->find_configs;
+ $self->fix_configs($configs) if @{ $configs };
+
+ return 1;
+}
+
+1;
%\tableofcontents
+%%%% Deaktiviertes Beispiel, wie benutzerdefinierte Variablen ausgegeben werden können: %%%%
+%% \newpage
+%%
+%% \section{Benutzerdefinierte Variablen}
+%%
+%% %$ ( FOREACH cvar = rspec.cvars_by_config ) $
+%% Name: $ ( KiviLatex.filter(cvar.config.name) ) $
+%%
+%% Wert:% $ ( IF cvar.config.type == 'htmlfield' ) $
+%% $ ( KiviLatex.filter_html(cvar.value_as_text) ) $
+%% % $ ( ELSE ) $
+%% $ ( KiviLatex.filter(cvar.value_as_text) ) $
+%% % $ ( END ) $
+%%
+%% %$ ( END ) $
+%%%% ENDE Beispiel für benutzerdefinierte Variablen %%%%
+
%% Versionen
\newpage
[%- ELSIF var.type == 'textfield' %]
<textarea name="[% var_name %]" cols="[% HTML.escape(var.width) %]" rows="[% HTML.escape(var.height) %]">[% HTML.escape(var.value) %]</textarea>
+[%- ELSIF var.type == 'htmlfield' %]
+<textarea name="[% var_name %]" cols="[% HTML.escape(var.width) %]" rows="[% HTML.escape(var.height) %]" class="texteditor">[% HTML.escape(var.value) %]</textarea>
+
[%- ELSIF var.type == 'date' %]
[% L.date_tag(var_name, var.value) %]
[% render_cvar_tag_options.import(cols=cvar.var.width, rows=cvar.var.height);
L.textarea_tag(cvar_tag_name, cvar.value, render_cvar_tag_options) %]
+[%- ELSIF cvar.var.type == 'htmlfield' %]
+[% render_cvar_tag_options.import(cols=cvar.var.width, rows=cvar.var.height, class="texteditor");
+ L.textarea_tag(cvar_tag_name, L.restricted_html(cvar.value), render_cvar_tag_options) %]
+
[%- ELSIF cvar.var.type == 'date' %]
[%- L.date_tag(cvar_tag_name, cvar.value, render_cvar_tag_options) %]
[%- USE LxERP %]
[%- DEFAULT var_name = HTML.escape(cvar_name_prefix) _ HTML.escape(var.config.name) _ HTML.escape(cvar_name_postfix) %]
+[%- SET style_ = "width: " _ var.config.processed_options.WIDTH _ "px; height: " _ var.config.processed_options.HEIGHT _ "px" %]
[%- IF ( hide_non_editable && !var.config.is_flag('editable') ) %]
[% L.hidden_tag(var_name, var.value) %]
[%- ELSIF ( var.config .type == 'bool' ) %]
[% L.checkbox_tag(var_name, checked = var.value, for_submit = 1) %]
[%- ELSIF ( var.config .type == 'textfield' ) %]
- [% L.textarea_tag(var_name, var.value, cols = var.config.processed_options.WIDTH, rows = var.config.processed_options.HEIGHT) %]
+ [% L.textarea_tag(var_name, var.value, style=style_) %]
+[%- ELSIF ( var.config .type == 'htmlfield' ) %]
+ [% L.textarea_tag(var_name, L.restricted_html(var.value), class='texteditor', style=style_) %]
[%- ELSIF ( var.config.type == 'date' ) %]
[% L.date_tag(var_name, var.value) %]
[%- ELSIF ( var.config.type == 'timestamp' ) %]
(4) [% 'The default value depends on the variable type:' | $T8 %]
<br>
<ul>
- <li>[%- 'Text, text field and number variables: The default value will be used as-is.' | $T8 %]</li>
+ <li>[%- 'Text, text field, HTML field and number variables: The default value will be used as-is.' | $T8 %]</li>
<li>[%- 'Boolean variables: If the default value is non-empty then the checkbox will be checked by default and unchecked otherwise.' | $T8 %]</li>
<li>[%- 'Date and timestamp variables: If the default value equals \'NOW\' then the current date/current timestamp will be used. Otherwise the default value is copied as-is.' | $T8 %]</li>
</ul>
<br>
<ul>
<li>[%- 'Text variables: \'MAXLENGTH=n\' sets the maximum entry length to \'n\'.' | $T8 %]</li>
- <li>[%- 'Text field variables: \'WIDTH=w HEIGHT=h\' sets the width and height of the text field. They default to 30 and 5 respectively.' | $T8 %]</li>
+ <li>[%- 'Text field and HTML field variables: \'WIDTH=w HEIGHT=h\' sets the width and height of the field in pixels. They default to 225 and 90 respectively.' | $T8 %]</li>
<li>[%- 'Number variables: \'PRECISION=n\' forces numbers to be shown with exactly n decimal places.' | $T8 %]</li>
<li>[%- 'Selection fields: The option field must contain the available options for the selection. Options are separated by \'##\', for example \'Early##Normal##Late\'.' | $T8 %]</li>
</ul>
-[%- USE HTML -%][%- USE LxERP -%]
+[%- USE HTML -%][%- USE LxERP -%][%- USE L -%]
<div id="basic_settings" class="basic-settings-context-menu">
<h2>
[% IF SELF.requirement_spec.is_template %]
[% FOREACH var = cvars %]
<tr class="listrow">
<td>[% HTML.escape(var.config.description) %]</td>
- <td>[% HTML.escape(var.value_as_text) %]</td>
+ <td>
+ [%- IF var.config.type == 'htmlfield' -%]
+ [%- L.restricted_html(var.value_as_text) -%]
+ [%- ELSE -%]
+ [%- HTML.escape(var.value_as_text) -%]
+ [%- END -%]
+ </td>
</tr>
[% END %]