sub _parse_multipart_formdata {
my ($target, $temp_target, $input) = @_;
my ($name, $filename, $headers_done, $content_type, $boundary_found, $need_cr, $previous, $p_attachment, $encoding, $transfer_encoding);
+ my $data_start = 0;
+
+ # teach substr and length to use good ol' bytes, not 'em fancy characters
+ use bytes;
# We SHOULD honor encodings and transfer-encodings here, but as hard as I
# looked I couldn't find a reasonably recent webbrowser that makes use of
$ENV{'CONTENT_TYPE'} =~ /multipart\/form-data\s*;\s*boundary\s*=\s*(.+)$/;
my $boundary = '--' . $1;
+ my $index = 0;
+ my $line_length;
foreach my $line (split m/\n/, $input) {
- last if (($line eq "${boundary}--") || ($line eq "${boundary}--\r"));
+ $line_length = length $line;
+
+ if ($line =~ /^\Q$boundary\E(--)?\r?$/) {
+ my $last_boundary = $1;
+ my $data = substr $input, $data_start, $index - $data_start;
+ $data =~ s/\r?\n$//;
- if (($line eq $boundary) || ($line eq "$boundary\r")) {
- ${ $previous } =~ s|\r?\n$|| if $previous;
- ${ $previous } = Encode::decode($encoding, $$previous) if $previous && !$filename && !$transfer_encoding eq 'binary';
+ if ($previous && !$filename && $transfer_encoding && $transfer_encoding ne 'binary') {
+ ${ $previous } = Encode::decode($encoding, $data);
+ } else {
+ ${ $previous } = $data;
+ }
undef $previous;
undef $filename;
$need_cr = 0;
$encoding = $::lx_office_conf{system}->{dbcharset} || Common::DEFAULT_CHARSET;
$transfer_encoding = undef;
-
+ last if $last_boundary;
next;
}
if (!$line) {
$headers_done = 1;
+ $data_start = $index + $line_length + 1;
next;
}
next unless $previous;
- ${ $previous } .= "${line}\n";
+ } continue {
+ $index += $line_length + 1;
}
- ${ $previous } =~ s|\r?\n$|| if $previous;
-
$::lxdebug->leave_sub(2);
}
'Filter for customer variables' => 'Filter für benutzerdefinierte Kundenvariablen',
'Filter for item variables' => 'Filter für benutzerdefinierte Artikelvariablen',
'Finish' => 'Abschließen',
+ 'First 20 Lines' => 'Nur erste 20 Datensätze',
'Fix transaction' => 'Buchung korrigieren',
'Fix transactions' => 'Buchungen korrigieren',
'Folgekonto' => 'Folgekonto',
'From' => 'Von',
'From Date' => 'Von',
'Full Access' => 'Vollzugriff',
+ 'Full Preview' => 'Alles',
'Full access to all functions' => 'Vollzugriff auf alle Funktionen',
'Fwd' => 'Vorwärts',
'GL Transaction' => 'Dialogbuchung',
'Hardcopy' => 'Seite drucken',
'Has serial number' => 'Hat eine Serienummer',
'Heading' => 'Überschrift',
- 'Headings' => 'Überschriften',
'Help' => 'Hilfe',
'Help Template Variables' => 'Hilfe zu Dokumenten-Variablen',
'Help on column names' => 'Hilfe zu Spaltennamen',
'On Hand' => 'Auf Lager',
'On Order' => 'Ist bestellt',
'One or more Perl modules missing' => 'Ein oder mehr Perl-Module fehlen',
+ 'Only Warnings and Errors' => 'Nur Warnungen und Fehler',
'Only due follow-ups' => 'Nur fällige Wiedervorlagen',
'Only shown in item mode' => 'werden nur im Artikelmodus angezeigt',
'Oops. No valid action found to dispatch. Please report this case to the Lx-Office team.' => 'Ups. Es wurde keine gültige Funktion zum Aufrufen gefunden. Bitte berichten Sie diesen Fall den Lx-Office-Entwicklern.',
'Prepare bank transfer via SEPA XML' => 'Überweisung via SEPA XML vorbereiten',
'Prepayment' => 'Vorauszahlung',
'Preview' => 'Druckvorschau',
+ 'Preview Mode' => 'Vorschaumodus',
'Previous transdate text' => 'wurde gespeichert am',
'Previous transnumber text' => 'Letzte Buchung mit der Buchungsnummer',
'Price' => 'Preis',
use SL::BackgroundJob::ALL;
use SL::Form;
use SL::Helper::DateTime;
+use SL::InstanceConfiguration;
use SL::LXDebug;
use SL::LxOfficeConf;
use SL::Locale;
package main;
- $::lxdebug = LXDebug->new;
- $::locale = Locale->new($::lx_office_conf{system}->{language});
- $::form = Form->new;
- $::auth = SL::Auth->new;
- $::request = { cgi => CGI->new({}) };
+ $::lxdebug = LXDebug->new;
+ $::locale = Locale->new($::lx_office_conf{system}->{language});
+ $::form = Form->new;
+ $::auth = SL::Auth->new;
+ $::instance_conf = SL::InstanceConfiguration->new;
+ $::request = { cgi => CGI->new({}) };
die 'cannot reach auth db' unless $::auth->session_tables_present;
use Data::Dumper;
use SL::LxOfficeConf;
use SL::InstanceConfiguration;
-SL::LxOfficeConf->read;
sub _login {
my $login = shift;
}
sub login {
+ SL::LxOfficeConf->read;
+
my $login = shift || $::lx_office_conf{testing}{login} || 'demo';
_login($login);
}
--- /dev/null
+use strict;
+use utf8;
+
+use lib 't';
+use lib 'modules/fallback';
+BEGIN {
+ unshift @INC, 'modules/override';
+}
+
+use Support::TestSetup;
+use Test::More tests => 2;
+use Data::Dumper;
+require Test::Deep;
+use Encode;
+
+use SL::Request;
+
+Support::TestSetup::login();
+
+open my $fh, '<', 't/request/post_multipart_1' or die "can't load test";
+my $data = do { $/ = undef; <$fh> };
+
+my $t = {};
+my $tt = {};
+
+local $ENV{CONTENT_TYPE} = 'multipart/form-data; boundary=---------------------------23281168279961';
+SL::Request::_parse_multipart_formdata($t, $tt, $data);
+
+
+my $blob = Encode::encode('utf-8', qq|\x{feff}Stunde;Montag;Dienstag;Mittwoch;Donnerstag;Freitag
+1;Mathe;Deutsch;Englisch;Mathe;Kunst
+2;Sport;Französisch;Geschichte;Sport;Geschichte
+3;Sport;"Religion ev;kath";Kunst;;Kunst|);
+
+my $t_cmp = {
+ 'profile' => {
+ 'name' => undef,
+ 'type' => undef
+ },
+ 'quote_char' => undef,
+ 'file' => $blob,
+ 'custom_sep_char' => undef,
+ 'sep_char' => undef,
+ 'settings' => {
+ 'article_number_policy' => undef,
+ 'sellprice_places' => undef,
+ 'charset' => undef,
+ 'apply_buchungsgruppe' => undef,
+ 'full_preview' => undef,
+ 'parts_type' => undef,
+ 'default_unit' => undef,
+ 'default_buchungsgruppe' => undef,
+ 'duplicates' => undef,
+ 'numberformat' => undef,
+ 'sellprice_adjustment_type' => undef,
+ 'shoparticle_if_missing' => undef,
+ 'sellprice_adjustment' => undef
+ },
+ 'custom_escape_char' => undef,
+ 'action_test' => undef,
+ 'custom_quote_char' => undef,
+ 'escape_char' => undef,
+ 'action' => undef
+ };
+$t_cmp->{ATTACHMENTS}{file}{data} = \$t_cmp->{'file'};
+
+
+is_deeply $t, $t_cmp;
+
+is_deeply $tt,
+ {
+ 'profile' => {
+ 'name' => '',
+ 'type' =>'parts',
+ },
+ 'file' => undef,
+ 'quote_char' => 'quote',
+ 'custom_sep_char' => '',
+ 'sep_char' => 'semicolon',
+ 'settings' => {
+ 'article_number_policy' => 'update_prices',
+ 'sellprice_places' => 2,
+ 'charset' => 'UTF-8',
+ 'apply_buchungsgruppe' => 'all',
+ 'full_preview' => '0',
+ 'parts_type' => 'part',
+ 'default_unit' => 'g',
+ 'default_buchungsgruppe' => '815',
+ 'duplicates' => 'no_check',
+ 'numberformat' => '1.000,00',
+ 'sellprice_adjustment_type' => 'percent',
+ 'shoparticle_if_missing' => '0',
+ 'sellprice_adjustment' =>'0'
+ },
+ 'custom_escape_char' => '',
+ 'action_test' => 'Test und Vorschau',
+ 'ATTACHMENTS' => {
+ 'file' => {
+ 'filename' => 'from_wikipedia.csv'
+ }
+ },
+ 'custom_quote_char' => '',
+ 'escape_char' => 'quote',
+ 'action' => 'CsvImport/dispatch',
+ 'FILENAME' => 'from_wikipedia.csv'
+ };
+
--- /dev/null
+-----------------------------23281168279961
+Content-Disposition: form-data; name="action"
+
+CsvImport/dispatch
+-----------------------------23281168279961
+Content-Disposition: form-data; name="profile.type"
+
+parts
+-----------------------------23281168279961
+Content-Disposition: form-data; name="profile.name"
+
+
+-----------------------------23281168279961
+Content-Disposition: form-data; name="settings.numberformat"
+
+1.000,00
+-----------------------------23281168279961
+Content-Disposition: form-data; name="settings.charset"
+
+UTF-8
+-----------------------------23281168279961
+Content-Disposition: form-data; name="sep_char"
+
+semicolon
+-----------------------------23281168279961
+Content-Disposition: form-data; name="custom_sep_char"
+
+
+-----------------------------23281168279961
+Content-Disposition: form-data; name="quote_char"
+
+quote
+-----------------------------23281168279961
+Content-Disposition: form-data; name="custom_quote_char"
+
+
+-----------------------------23281168279961
+Content-Disposition: form-data; name="escape_char"
+
+quote
+-----------------------------23281168279961
+Content-Disposition: form-data; name="custom_escape_char"
+
+
+-----------------------------23281168279961
+Content-Disposition: form-data; name="settings.duplicates"
+
+no_check
+-----------------------------23281168279961
+Content-Disposition: form-data; name="settings.article_number_policy"
+
+update_prices
+-----------------------------23281168279961
+Content-Disposition: form-data; name="settings.sellprice_places"
+
+2
+-----------------------------23281168279961
+Content-Disposition: form-data; name="settings.sellprice_adjustment"
+
+0
+-----------------------------23281168279961
+Content-Disposition: form-data; name="settings.sellprice_adjustment_type"
+
+percent
+-----------------------------23281168279961
+Content-Disposition: form-data; name="settings.shoparticle_if_missing"
+
+0
+-----------------------------23281168279961
+Content-Disposition: form-data; name="settings.parts_type"
+
+part
+-----------------------------23281168279961
+Content-Disposition: form-data; name="settings.default_buchungsgruppe"
+
+815
+-----------------------------23281168279961
+Content-Disposition: form-data; name="settings.apply_buchungsgruppe"
+
+all
+-----------------------------23281168279961
+Content-Disposition: form-data; name="settings.default_unit"
+
+g
+-----------------------------23281168279961
+Content-Disposition: form-data; name="settings.full_preview"
+
+0
+-----------------------------23281168279961
+Content-Disposition: form-data; name="file"; filename="from_wikipedia.csv"
+Content-Type: text/comma-separated-values
+
+Stunde;Montag;Dienstag;Mittwoch;Donnerstag;Freitag
+1;Mathe;Deutsch;Englisch;Mathe;Kunst
+2;Sport;Französisch;Geschichte;Sport;Geschichte
+3;Sport;"Religion ev;kath";Kunst;;Kunst
+-----------------------------23281168279961
+Content-Disposition: form-data; name="action_test"
+
+Test und Vorschau
+-----------------------------23281168279961--
</tr>
[%- FOREACH row = SELF.data %]
+ [%- IF (SELF.profile.get('full_preview') == 2) || ((SELF.profile.get('full_preview') == 1) && (row.errors.size || row.information.size)) || ((SELF.profile.get('full_preview') == 0) && (loop.count < 21)) %]
<tr class="[% IF row.errors.size %]redrow[% ELSE %]listrow[% END %][% loop.count % 2 %]">
[%- FOREACH method = SELF.info_headers.methods %]
<td>[%- HTML.escape(row.info_data.$method) %]</td>
</td>
</tr>
[%- END %]
+ [%- END %]
</table>
[%- END %]
[%- INCLUDE 'csv_import/_form_customers_vendors.html' %]
[%- END %]
+ <tr>
+ <th align="right">[%- LxERP.t8('Preview Mode') %]:</th>
+ <td colspan="10">
+ [% L.radio_button_tag('settings.full_preview', value=2, checked=SELF.profile.get('full_preview')==2, label=LxERP.t8('Full Preview')) %]
+ [% L.radio_button_tag('settings.full_preview', value=1, checked=SELF.profile.get('full_preview')==1, label=LxERP.t8('Only Warnings and Errors')) %]
+ [% L.radio_button_tag('settings.full_preview', value=0, checked=!SELF.profile.get('full_preview'), label=LxERP.t8('First 20 Lines')) %]
+ </td>
+ </tr>
+
<tr>
<th align="right">[%- LxERP.t8('Import file') %]:</th>
<td colspan="10">[% L.input_tag('file', '', type => 'file', accept => '*') %]</td>