Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision 0bf4e031

Von Werner Hahn vor fast 8 Jahren hinzugefügt

  • ID 0bf4e03107774434939b9268eff6c065509da0fc
  • Vorgänger f2eba9e7
  • Nachfolger ed8a2661

Shopmodul: Bilder hochladen

Unterschiede anzeigen:

SL/Controller/File.pm
__PACKAGE__->run_before('check_object_params', only => [ qw(list ajax_delete ajax_importdialog ajax_import ajax_unimport ajax_upload ajax_files_uploaded) ]);
my %file_types = (
'sales_quotation' => { gen => 1, gltype => '', dir => 'SalesQuotation', model => 'Order', right => 'import_ar' },
'sales_order' => { gen => 1, gltype => '', dir => 'SalesOrder', model => 'Order', right => 'import_ar' },
'sales_delivery_order' => { gen => 1, gltype => '', dir => 'SalesDeliveryOrder', model => 'DeliveryOrder', right => 'import_ar' },
'invoice' => { gen => 1, gltype => 'ar', dir => 'SalesInvoice', model => 'Invoice', right => 'import_ar' },
'credit_note' => { gen => 1, gltype => '', dir => 'CreditNote', model => 'Invoice', right => 'import_ar' },
'request_quotation' => { gen => 3, gltype => '', dir => 'RequestForQuotation', model => 'Order', right => 'import_ap' },
'purchase_order' => { gen => 3, gltype => '', dir => 'PurchaseOrder', model => 'Order', right => 'import_ap' },
'purchase_delivery_order' => { gen => 3, gltype => '', dir => 'PurchaseDeliveryOrder', model => 'DeliveryOrder', right => 'import_ap' },
'purchase_invoice' => { gen => 2, gltype => 'ap', dir => 'PurchaseInvoice', model => 'PurchaseInvoice', right => 'import_ap' },
'vendor' => { gen => 0, gltype => '', dir => 'Vendor', model => 'Vendor', right => 'xx' },
'customer' => { gen => 1, gltype => '', dir => 'Customer', model => 'Customer', right => 'xx' },
'part' => { gen => 0, gltype => '', dir => 'Part', model => 'Part', right => 'xx' },
'gl_transaction' => { gen => 2, gltype => 'gl', dir => 'GeneralLedger', model => 'GLTransaction', right => 'import_ap' },
'draft' => { gen => 0, gltype => '', dir => 'Draft', model => 'Draft', right => 'xx' },
'csv_customer' => { gen => 1, gltype => '', dir => 'Reports', model => 'Customer', right => 'xx' },
'csv_vendor' => { gen => 1, gltype => '', dir => 'Reports', model => 'Vendor', right => 'xx' },
'sales_quotation' => { gen => 1, gltype => '', dir =>'SalesQuotation', model => 'Order', right => 'import_ar' },
'sales_order' => { gen => 1, gltype => '', dir =>'SalesOrder', model => 'Order', right => 'import_ar' },
'sales_delivery_order' => { gen => 1, gltype => '', dir =>'SalesDeliveryOrder', model => 'DeliveryOrder', right => 'import_ar' },
'invoice' => { gen => 1, gltype => 'ar', dir =>'SalesInvoice', model => 'Invoice', right => 'import_ar' },
'credit_note' => { gen => 1, gltype => '', dir =>'CreditNote', model => 'Invoice', right => 'import_ar' },
'request_quotation' => { gen => 3, gltype => '', dir =>'RequestForQuotation', model => 'Order', right => 'import_ap' },
'purchase_order' => { gen => 3, gltype => '', dir =>'PurchaseOrder', model => 'Order', right => 'import_ap' },
'purchase_delivery_order' => { gen => 3, gltype => '', dir =>'PurchaseDeliveryOrder',model => 'DeliveryOrder', right => 'import_ap' },
'purchase_invoice' => { gen => 2, gltype => 'ap', dir =>'PurchaseInvoice', model => 'PurchaseInvoice',right => 'import_ap' },
'vendor' => { gen => 0, gltype => '', dir =>'Vendor', model => 'Vendor', right => 'xx' },
'customer' => { gen => 1, gltype => '', dir =>'Customer', model => 'Customer', right => 'xx' },
'part' => { gen => 0, gltype => '', dir =>'Part', model => 'Part', right => 'xx' },
'gl_transaction' => { gen => 2, gltype => 'gl', dir =>'GeneralLedger', model => 'GLTransaction', right => 'import_ap' },
'draft' => { gen => 0, gltype => '', dir =>'Draft', model => 'Draft', right => 'xx' },
'csv_customer' => { gen => 1, gltype => '', dir =>'Reports', model => 'Customer', right => 'xx' },
'csv_vendor' => { gen => 1, gltype => '', dir =>'Reports', model => 'Vendor', right => 'xx' },
'shop_image' => { gen => 0, gltype => '', dir =>'ShopImages', model => 'Part', right => 'xx' },
);
#--- 4 locale ---#
......
source => $source,
file_type => $self->file_type,
file_name => $basefile,
);
if ($existobj) {
push @existing, $existobj->id.'_'.$sfile->file_name;
} else {
my $fileobj = SL::File->save(object_id => $self->object_id,
object_type => $self->object_type,
mime_type => $mime_type,
source => $source,
file_type => $self->file_type,
file_name => $basefile,
## two possibilities: which is better ? content or sessionfile ??
#file_contents => ${$upfiles[$idx]->{data}},
file_path => $sfile->file_name
);
if ($existobj) {
push @existing, $existobj->id.'_'.$sfile->file_name;
} else {
my $fileobj = SL::File->save(object_id => $self->object_id,
object_type => $self->object_type,
mime_type => $mime_type,
source => $source,
file_type => $self->file_type,
file_name => $basefile,
title => $::form->{title},
description => $::form->{description},
## two possibilities: what is better ? content or sessionfile ??
file_contents => ${$upfiles[$idx]->{data}},
file_path => $sfile->file_name
);
unlink($sfile->file_name);
}
1;
} or do {
$self->js->flash( 'error', t8('internal error (see details)'))
->flash_detail('error', $@)->render;
return;
}
}
}
$self->existing(\@existing);
$self->_do_list(1);
}
sub action_download {
my ($self) = @_;
my ($id,$version) = split /_/, $::form->{id};
my $file = SL::File->get(id => $id );
$file->version($version) if $version;
my $ref = $file->get_content;
);
unlink($sfile->file_name);
}
SL/Controller/Helper/ThumbnailCreator.pm
);
sub file_create_thumbnail {
my ($self) = @_;
croak "No picture set yet" if !$self->file_content;
my $image = GD::Image->new($self->file_content);
my ($thumb) = @_;
croak "No picture set yet" if !$thumb->{content};
$main::lxdebug->dump(0, 'WH: CTHUMB ', $thumb);
my $image = GD::Image->new($thumb->{content});
my ($width, $height) = $image->getBounds;
my $max_dim = 64;
my $curr_max = max $width, $height, 1;
......
$thumbnail->copyResized($image, 0, 0, 0, 0, $new_width, $new_height, $width, $height);
$self->thumbnail_img_content($thumbnail->png);
$self->thumbnail_img_content_type('image/png');
$self->thumbnail_img_width($new_width);
$self->thumbnail_img_height($new_height);
return 1;
$thumb->{thumbnail_img_content} = $thumbnail->png;
$thumb->{thumbnail_img_content_type} = "image/png";
$thumb->{thumbnail_img_width} = $new_width;
$thumb->{thumbnail_img_height} = $new_height;
#$self->thumbnail_img_content($thumbnail->png);
#$self->thumbnail_img_content_type('image/png');
#$self->thumbnail_img_width($new_width);
#$self->thumbnail_img_height($new_height);
return $thumb;
}
......
}
sub file_probe_type {
my ($self) = @_;
return (t8("No file uploaded yet")) if !$self->file_content;
my $mime_type = File::MimeInfo::Magic::magic($self->file_content);
my $info = Image::Info::image_info(\$self->{file_content});
my ($content) = @_;
#$main::lxdebug->dump(0, 'WH: FPT Content', $content);
return (t8("No file uploaded yet")) if !$content;
#my $mime_type = File::MimeInfo::Magic::magic($content);
#$main::lxdebug->dump(0, 'WH: MIME ', $mime_type);
my $info = Image::Info::image_info(\$content);
#$main::lxdebug->dump(0, 'WH: INFO', $info);
if (!$info || $info->{error} || !$info->{file_media_type} || !$supported_mime_types{ $info->{file_media_type} }) {
$::lxdebug->warn("Image::Info error: " . $info->{error}) if $info && $info->{error};
return (t8('Unsupported image type (supported types: #1)', join(' ', sort keys %supported_mime_types)));
}
$self->file_content_type($info->{file_media_type});
$self->files_img_width($info->{width});
$self->files_img_height($info->{height});
$self->files_mtime(DateTime->now_local);
my $thumbnail;
$thumbnail->{file_content_type} = $info->{file_media_type};
$thumbnail->{file_image_width} = $info->{width};
$thumbnail->{file_image_height} = $info->{height};
$thumbnail->{content} = $content;
#$self->file_content_type($info->{file_media_type});
#$self->files_img_width($info->{width});
#$self->files_img_height($info->{height});
#$self->files_mtime(DateTime->now_local);
$self->file_create_thumbnail;
$thumbnail = &file_create_thumbnail($thumbnail);
return ();
return $thumbnail;
}
sub file_update_type_and_dimensions {
SL/Controller/ShopPart.pm
use SL::Locale::String qw(t8);
use SL::DB::ShopPart;
use SL::DB::File;
use SL::DB::ShopImage;
use SL::Controller::FileUploader;
use SL::DB::Default;
use SL::Helper::Flash;
......
sub action_show_files {
my ($self) = @_;
$main::lxdebug->message(0, "WH:ShowFiles ");
$main::lxdebug->dump(0, 'WH:FORM ',$::form);
$main::lxdebug->dump(0, 'WH:FORM ',$::form->{part});
my $images = SL::DB::Manager::File->get_all_sorted( where => [ trans_id => $::form->{id}, modul => $::form->{modul}, file_content_type => { like => 'image/%' } ], sort_by => 'position' );
#my $images = SL::DB::Manager::File->get_all_sorted( where => [ trans_id => $::form->{id}, modul => $::form->{modul}, file_content_type => { like => 'image/%' } ], sort_by => 'position' );
#my $images = SL::DB::Manager::ShopImage->get_all_sorted( where => [ partnumber => $::form->{part}{partnumber}, ], with_object => 'file', sort_by => 'position' );
my $images = SL::DB::Manager::ShopImage->get_all( where => [ 'files.object_id' => $::form->{id}, ], with_objects => 'file', sort_by => 'position' );
$main::lxdebug->dump(0, 'WH:ShowFiles1 ',$images);
$self->render('shop_part/_list_images', { header => 0 }, IMAGES => $images);
}
......
my $categories = $shop->connector->get_categories;
$main::lxdebug->dump(0, 'WH:KAT ',$categories);
$self->js
->run(
'kivi.shop_part.shop_part_dialog',
......
sub action_list_articles {
my ($self) = @_;
my %filter = ($::form->{filter} ? parse_filter($::form->{filter}) : query => [ transferred => 0 ]);
my %filter = ($::form->{filter} ? parse_filter($::form->{filter}) : query => [ transferred => 0 ]);
my $transferred = $::form->{filter}->{transferred_eq_ignore_empty} ne '' ? $::form->{filter}->{transferred_eq_ignore_empty} : '';
my $sort_by = $::form->{sort_by} ? $::form->{sort_by} : 'part.partnumber';
my $sort_by = $::form->{sort_by} ? $::form->{sort_by} : 'part.partnumber';
$sort_by .=$::form->{sort_dir} ? ' DESC' : ' ASC';
my $articles = SL::DB::Manager::ShopPart->get_all(where => [ 'shop.obsolete' => 0 ],with_objects => [ 'part','shop' ], sort_by => $sort_by );
......
#TODO Price must be formatted. Translations for $price_grp_str
my $price;
if ($price_src_str eq "master_data") {
my $part = SL::DB::Manager::Part->get_all( where => [id => $self->shop_part->part_id], with_objects => ['prices'],limit => 1)->[0];
$price = $part->$price_src_id;
my $part = SL::DB::Manager::Part->get_all( where => [id => $self->shop_part->part_id], with_objects => ['prices'],limit => 1)->[0];
$price = $part->$price_src_id;
$price_src_str = $price_src_id;
}else{
my $part = SL::DB::Manager::Part->get_all( where => [id => $self->shop_part->part_id, 'prices.'.pricegroup_id => $price_src_id], with_objects => ['prices'],limit => 1)->[0];
my $pricegrp = SL::DB::Manager::Pricegroup->find_by( id => $price_src_id )->pricegroup;
$price = $part->prices->[0]->price;
}else{
my $part = SL::DB::Manager::Part->get_all( where => [id => $self->shop_part->part_id, 'prices.'.pricegroup_id => $price_src_id], with_objects => ['prices'],limit => 1)->[0];
my $pricegrp = SL::DB::Manager::Pricegroup->find_by( id => $price_src_id )->pricegroup;
$price = $part->prices->[0]->price;
$price_src_str = $pricegrp;
}
return($price,$price_src_str);
SL/DB/Helper/Mappings.pm
schema_info => 'schema_info',
shipto => 'shipto',
shops => 'shop',
shop_images => 'shop_image',
shop_orders => 'shop_order',
shop_order_items => 'shop_order_item',
shop_parts => 'shop_part',
SL/DB/Manager/ShopImage.pm
# This file has been auto-generated only because it didn't exist.
# Feel free to modify it at will; it will not be overwritten automatically.
package SL::DB::Manager::ShopImage;
use strict;
use parent qw(SL::DB::Helper::Manager);
use SL::DB::Helper::Sorted;
sub object_class { 'SL::DB::ShopImage' }
__PACKAGE__->make_manager_methods;
1;
SL/DB/MetaSetup/Default.pm
doc_storage_for_attachments => { type => 'text', default => 'Filesystem' },
doc_storage_for_documents => { type => 'text', default => 'Filesystem' },
doc_storage_for_images => { type => 'text', default => 'Filesystem' },
doc_storage_for_shopimages => { type => 'text', default => 'Filesystem' },
doc_webdav => { type => 'boolean', default => 'false' },
dunning_ar => { type => 'integer' },
dunning_ar_amount_fee => { type => 'integer' },
SL/DB/MetaSetup/ShopImage.pm
# This file has been auto-generated. Do not modify it; it will be overwritten
# by rose_auto_create_model.pl automatically.
package SL::DB::ShopImage;
use strict;
use parent qw(SL::DB::Object);
__PACKAGE__->meta->table('shop_images');
__PACKAGE__->meta->columns(
file_id => { type => 'integer' },
id => { type => 'serial', not_null => 1 },
itime => { type => 'timestamp', default => 'now()' },
mtime => { type => 'timestamp' },
org_file_height => { type => 'integer' },
org_file_width => { type => 'integer' },
position => { type => 'integer' },
thumbnail_content => { type => 'bytea' },
thumbnail_content_type => { type => 'text' },
);
__PACKAGE__->meta->primary_key_columns([ 'id' ]);
__PACKAGE__->meta->allow_inline_column_values(1);
__PACKAGE__->meta->foreign_keys(
file => {
class => 'SL::DB::File',
key_columns => { file_id => 'id' },
},
);
1;
;
SL/DB/ShopImage.pm
# This file has been auto-generated only because it didn't exist.
# Feel free to modify it at will; it will not be overwritten automatically.
package SL::DB::ShopImage;
use strict;
use SL::DB::MetaSetup::ShopImage;
use SL::DB::Manager::ShopImage;
__PACKAGE__->meta->initialize;
1;
SL/File.pm
use SL::File::Backend;
use SL::File::Object;
use SL::DB::History;
use SL::DB::ShopImage;
use SL::DB::File;
use SL::Helper::UserPreferences;
use SL::Controller::Helper::ThumbnailCreator qw(file_probe_type);
use SL::JSON;
use constant RENAME_OK => 0;
......
sub _save {
my ($self, %params) = @_;
$main::lxdebug->dump(0, 'WH: PARAMS', \%params);
my $file = $params{dbfile};
my $exists = 0;
......
$file->mtime(DateTime->now_local);
$file->save;
#ShopImage
if($file->object_type eq "shop_image"){
my $image_content = $params{file_contents};
my $thumbnail = file_probe_type($image_content);
$main::lxdebug->dump(0, 'WH: THUMB ',$thumbnail);
my $shopimage = SL::DB::ShopImage->new();
$shopimage->assign_attributes(
file_id => $file->id,
thumbnail_content => $thumbnail->{thumbnail_img_content},
org_file_height => $thumbnail->{file_image_height},
org_file_width => $thumbnail->{file_image_width},
thumbnail_content_type => $thumbnail->{thumbnail_img_content_type},
);
$shopimage->save;
}
if ($params{file_type} eq 'document' && $params{source} ne 'created') {
SL::DB::History->new(
addition => 'IMPORT',
SL/File/Backend/Webdav.pm
assembly => 'erzeugnisse',
letter => 'briefe',
general_ledger => 'dialogbuchungen',
gl_transaction => 'dialogbuchungen',
accounts_payable => 'kreditorenbuchungen',
shop_image => 'shopbilder',
);
my %type_to_model = (
......
assembly => 'Part',
letter => 'Letter',
general_ledger => 'GLTransaction',
gl_transaction => 'GLTransaction',
accounts_payable => 'GLTransaction',
shop_image => 'Part',
);
my %model_to_number = (
......
PurchaseInvoice => 'invnumber',
Part => 'partnumber',
Letter => 'letternumber',
GLTransaction => 'reference'
GLTransaction => 'reference',
ShopImage => 'partnumber',
);
sub webdav_path {
js/kivi.shop_part.js
$('.ui-dialog-titlebar button.ui-dialog-titlebar-close').prop('disabled', '')
};
ns.imageUpload = function(id,type,filetype,upload_title,gl) {
kivi.popup_dialog({ url: 'controller.pl',
data: { action: 'File/ajax_upload',
file_type: filetype,
object_type: type,
object_id: id,
is_global: gl
},
id: 'files_upload',
dialog: { title: upload_title, width: 650, height: 240 } });
return true;
}
ns.setup = function() {
kivi.shop_part.massUploadInitialize();
kivi.submit_ajax_form('controller.pl?action=ShopPart/mass_upload','[name=shop_parts]');
sql/Pg-upgrade2/shopimages.sql
-- @tag:shopimages
-- @description: Tabelle für Shopbilder und zusätzliche Konfiguration und valid_type für Filemanagement
-- @charset: UTF-8
-- @depends: release_3_4_1 files shop_parts
-- @ignore: 0
CREATE TABLE shop_images(
id SERIAL PRIMARY KEY,
file_id INTEGER REFERENCES files(id) ON DELETE CASCADE,
position INTEGER,
thumbnail_content BYTEA,
thumbnail_width INTEGER,
thumbnail_height INTEGER,
thumbnail_content_type TEXT,
itime TIMESTAMP DEFAULT now(),
mtime TIMESTAMP
);
CREATE TRIGGER mtime_shop_images BEFORE UPDATE ON shop_images FOR EACH ROW EXECUTE PROCEDURE set_mtime();
ALTER TABLE defaults ADD COLUMN doc_storage_for_shopimages text default 'Filesystem';
ALTER TABLE files
DROP CONSTRAINT valid_type;
ALTER TABLE files
ADD CONSTRAINT valid_type CHECK (
(object_type = 'credit_note' ) OR (object_type = 'invoice' ) OR (object_type = 'sales_order' )
OR (object_type = 'sales_quotation' ) OR (object_type = 'sales_delivery_order' ) OR (object_type = 'request_quotation' )
OR (object_type = 'purchase_order' ) OR (object_type = 'purchase_delivery_order' ) OR (object_type = 'purchase_invoice' )
OR (object_type = 'vendor' ) OR (object_type = 'customer' ) OR (object_type = 'part' )
OR (object_type = 'gl_transaction' ) OR (object_type = 'dunning' ) OR (object_type = 'dunning1' )
OR (object_type = 'dunning2' ) OR (object_type = 'dunning3' ) OR (object_type = 'draft' )
OR (object_type = 'statement' ) OR (object_type = 'shop_image' )
);
sql/Pg-upgrade2/shopimages_2.sql
-- @tag:shopimages_2
-- @description: Umbennung der Spalten für Weite und Breite in die Weite und Breite des orginal Bildes
-- @charset: UTF-8
-- @depends: release_3_4_1 files shop_parts shopimages
-- @ignore: 0
ALTER TABLE shop_images RENAME thumbnail_width TO org_file_width;
ALTER TABLE shop_images RENAME thumbnail_height TO org_file_height;
templates/webpages/client_config/_features.html
onchange="return checkavailable_filebackend(this);") %]</td>
<td>[% LxERP.t8('Use this storage backend for uploaded images') %]</td>
</tr>
<tr>
<td align="right">[% LxERP.t8('Storage Type for shopimages') %]</td>
<td>[% L.select_tag('defaults.doc_storage_for_shopimages',
[ [ 'None', LxERP.t8('None') ], [ 'Filesystem', LxERP.t8('Files') ],[ 'Webdav', LxERP.t8('WebDAV') ],[ 'ExtDMS', LxERP.t8('ext.DMS') ],[ 'DB', LxERP.t8('Database') ] ],
default = SELF.defaults.doc_storage_for_shopimages,
onchange="return checkavailable_filebackend(this);") %]</td>
<td>[% LxERP.t8('Use this storage backend for uploaded images') %]</td>
</tr>
<tr>
<td align="right">[% LxERP.t8('Delete printfiles') %]</td>
<td>[% L.yes_no_tag('defaults.doc_delete_printfiles', SELF.defaults.doc_delete_printfiles) %]</td>
templates/webpages/file/upload_dialog.html
[%- USE L -%][%- USE LxERP -%][%- USE JavaScript -%]
xx
<form method="post" id="upload_form" enctype="multipart/form-data" action="controller.pl">
[% SET multiple = 'true' %]
[% IF SELF.object_type == 'shop_image' %][% multiple = 'false' %][% END %]
<table>
<tr>
<td>[%- LxERP.t8("Filename") %]:</td><td>
<input type="file" name="uploadfiles[]" multiple="true" id="upload_files" size="45" accept="[% SELF.accept_types %]" onchange="kivi.File.allow_upload_submit();"></td>
<input type="file" name="uploadfiles[]" multiple="[% multiple %]" id="upload_files" size="45" accept="[% SELF.accept_types %]" onchange="kivi.File.allow_upload_submit();"></td>
</tr>
[% IF SELF.object_type == 'shop_image' %]
<tr>
<td>[% LxERP.t8("Title") %]</td>
<td>[% L.input_tag("title",'') %]</td>
</tr>
<tr>
<td>[% LxERP.t8("Description") %]</td>
<td>[% L.input_tag("description",'') %]</td>
</tr>
[% END %]
</table>
<p>
templates/webpages/shop_part/_list_images.html
</thead>
<tbody>
[%- FOREACH img = IMAGES %]
[% # Dumper.dump_html(img) %]
<tr class="listrow" id="image_id_[% img.id %]">
<td><img src="image/updown.png" alt="[%- LxERP.t8('reorder item') %]" class="dragdrop"></td>
<td width="70px"><img src="data:[% img.thumbnail_img_content_type %];base64,[% img.thumbnail_img_content.encode_base64 %]" alt="[% img.title %]"></td>
<td>[% HTML.escape(img.title) %]</td>
<td>[% HTML.escape(img.description) %]</td>
<td>[% HTML.escape(img.filename) %]</td>
<td>[% HTML.escape(img.files_img_width) _ ' x ' _ HTML.escape(img.files_img_height) %]</td>
<td width="70px"><img src="data:[% img.thumbnail_content_type %];base64,[% img.thumbnail_content.encode_base64 %]" alt="[% img.file.title %]"></td>
<td>[% HTML.escape(img.file.title) %]</td>
<td>[% HTML.escape(img.file.description) %]</td>
<td>[% HTML.escape(img.file.file_name) %]</td>
<td>[% HTML.escape(img.org_file_width) _ ' x ' _ HTML.escape(img.org_file_height) %]</td>
<td>[% L.button_tag("kivi.FileUploader.delete_file(" _ img.id _ ", 'ShopPart/ajax_delete_file')", LxERP.t8('Delete'), confirm=LxERP.t8("Are you sure?")) %] [% L.button_tag("kivi.FileUploader.add_file(" _ img.id _ "," _ FORM.id _ ",'shop_part','ShopPart/ajax_update_file','jpg|png|tif|gif')", LxERP.t8('Edit')) %] </td>
</tr>
[% END %]
......
[% L.sortable_element('#images_list tbody', url=SELF.url_for(action='reorder'), with='image_id') %]
<p>
[% L.button_tag("kivi.FileUploader.add_file(''," _ FORM.id _ ",'shop_part','ShopPart/ajax_upload_file','jpg|png|tif|gif')", LxERP.t8('Fileupload')) %]
[% L.button_tag("kivi.shop_part.imageUpload(" _ FORM.id _ ",'shop_image','image','Upload shopimage',0);", LxERP.t8('Upload shopimage') ) %]
</p>

Auch abrufbar als: Unified diff