MT#5879 Store invoice templates on save.

mr3.3.1
Andreas Granig 12 years ago
parent a88724c0c4
commit fdde82379b

@ -3,6 +3,7 @@ use Sipwise::Base;
use namespace::sweep;
BEGIN { extends 'Catalyst::Controller'; }
use NGCP::Panel::Utils::InvoiceTemplate;
use NGCP::Panel::Form::Invoice::TemplateAdmin;
use NGCP::Panel::Form::Invoice::TemplateReseller;
@ -260,7 +261,7 @@ sub edit_content :Chained('base') :PathPart('editcontent') :Args(0) {
}
sub get_content_ajax :Chained('base') :PathPart('editcontent/get/ajax') :Args(0) {
my ($self, $c, @args) = @_;
my ($self, $c) = @_;
my $tmpl = $c->stash->{tmpl};
my $content;
@ -283,10 +284,59 @@ sub get_content_ajax :Chained('base') :PathPart('editcontent/get/ajax') :Args(0)
}
}
# some part of the chain doesn't like content being encoded as utf8 at that poing
# already; decode here, and umlauts etc will be fine througout the chain.
use utf8;
utf8::decode($content);
$c->response->content_type('text/html');
$c->response->body($content);
}
sub set_content_ajax :Chained('base') :PathPart('editcontent/set/ajax') :Args(0) {
my ($self, $c, @args) = @_;
my $tmpl = $c->stash->{tmpl};
my $content = $c->request->body_parameters->{template};
unless($content) {
NGCP::Panel::Utils::Message->error(
c => $c,
error => 'empty svg file not allowed',
desc => $c->log('Attempted to save an empty invoice template'),
);
return;
}
NGCP::Panel::Utils::InvoiceTemplate::sanitize_svg(\$content);
try {
$tmpl->update({
base64_saved => $content,
base64_previewed => undef,
});
} catch($e) {
NGCP::Panel::Utils::Message->error(
c => $c,
error => $e,
desc => $c->loc('Failed to store invoice template'),
);
return;
}
$c->flash(messages => [{type => 'success', text => $c->loc('Invoice template successfully saved')}]);
$c->response->content_type('application/json');
$c->response->body('');
$c->detach($c->view('JSON'));
}
sub messages_ajax :Chained('template_list') :PathPart('messages') :Args(0) {
my ($self, $c) = @_;
$c->stash(
messages => $c->flash->{messages},
template => 'helpers/ajax_messages.tt',
);
$c->detach($c->view('TT'));
}

@ -9,20 +9,8 @@ use File::Temp qw/tempfile tempdir/;
use File::Path qw/mkpath/;
use XML::XPath;
sub getDefaultInvoiceTemplate{
my (%in) = @_;
#in future may be we will store root default in Db too, but now it is convenient to edit template as file
my $result = $in{c}->view('SVG')->getTemplateContent($in{c}, 'invoice/invoice_template_'.$in{type}.'.tt');
#$in{c}->log->debug("result=$result;");
if( $result && exists $in{result} ){
${$in{result}} = $result;
}
return \$result;
}
sub convertSvg2Pdf{
my($c,$svg_ref,$in,$out) = @_;
sub svg2pdf {
my ($c,$svg_ref,$in,$out) = @_;
my $svg = $$svg_ref;
my(@pages) = $svg=~/(<svg.*?(?:\/svg>))/sig;
no warnings 'uninitialized';
@ -116,4 +104,21 @@ sub preprocessInvoiceTemplateSvg{
##print "\n\n2.\n\n\n\nsvg=".$out->{tt_string_prepared}.";";
}
1;
sub sanitize_svg {
my ($svg_ref) = @_;
my $xp = XML::XPath->new($$svg_ref);
my $s = $xp->find('//script');
foreach my $node($s->get_nodelist) {
if($node->getAttribute('display')) {
$node->getParentNode->removeChild($node);
}
}
$$svg_ref = ($xp->findnodes('/'))[0]->toString();
return 1;
}
1;
# vim: set tabstop=4 expandtab:

@ -65,49 +65,18 @@ function fetchSvgToEditor( data ) {
setSvgStringToEditor( httpResponse );
});
}
//public
function fetchInvoiceTemplateData( data, noshowform ){
//params spec: tt_type=[svg|html]/tt_viewmode[parsed|raw]/tt_sourcestate[saved|previewed|default]/tt_output_type[svg|pdf|html|json|svgzip|pdfzip|htmlzip]/tt_id
//tt_output_type=svg really outputs text/html mimetype. But it will be couple of <svg> tags (<svg> per page).
data.tt_output_type = 'json';
var q = uriForAction( data, 'template' );
//alert('fetchInvoiceTemplateData: q='+q+';');
function fetchInvoiceTemplateData(data) {
var q = uriForAction(data, 'template');
var queryObj = {
url: q,
type: 'POST',
type: 'GET',
};
//if (!$.browser.msie) {
//msie prompts to save
queryObj.dataType = "json";
//}
queryObj.contentType = 'application/x-www-form-urlencoded;charset=utf-8';
//alert('QQQ');
$.ajax( queryObj ).done( function( templatedata ){
//alert(templatedata.aaData);
//alert(templatedata.aaData);
//if ($.browser.msie) {
//alert(templatedata);
//templatedata = jQuery.parseJSON(templatedata);
//alert(templatedata);
//}
if(templatedata && templatedata.aaData){
if( templatedata.aaData.template ){
setSvgStringToEditor( templatedata.aaData.template.raw );
setSvgStringToPreview( templatedata.aaData.template.parsed, '', data );
}
// $('#load_previewed_control').css('visibility', 'visible' );
// $('#load_saved_control').css('visibility', 'visible' );
if( templatedata.aaData.form ){
$('form[name=template_editor]').loadJSON(templatedata.aaData.form);
//$('#load_previewed_control').css('display', 'inline' );
$('#load_saved_control').css('display', 'inline' );
}
if( !noshowform ){
$('#template_editor_form').css('visibility','visible');
}
}
$.ajax(queryObj).done(function(templatedata) {
setSvgStringToEditor(templatedata);
});
}
function clearTemplateForm(data){
$('#template_editor_form').css('visibility','hidden');
//$('#load_previewed_control').css('display', 'none' );
@ -118,6 +87,7 @@ function clearTemplateForm(data){
data.tt_sourcestate = 'default';
fetchInvoiceTemplateData(data, 1);//1 = no show form again, just clear it up to default state
}
function savePreviewed( data, callback ){
var svgString = getSvgString();
var q = uriForAction( data, 'template_previewed' );
@ -166,8 +136,6 @@ function savePreviewedAndShowParsed( data ){
function saveTemplate( data, callback ) {
var svgString = getSvgString();
data.tt_sourcestate='saved';
data.tt_output_type = 'json';
var q = uriForAction( data, 'template_saved' );
q=formToUri(q);
//alert('saveTemplate: q='+q+';');

@ -1,50 +0,0 @@
[% USE JSON.Escape( pretty => 0 ) %]
[%-
#USE Dumper;
#we can't use DEFAULT directive - it treats false the same as undefined, which doesn't allow to keep default filled values and switch them off by customization
MACRO clearHelper BLOCK;
FOREACH identifier in [ 'messages', 'length_change', 'dt_columns', 'close_target', 'create_flag', 'form_object', 'ajax_uri' ];
helper.${identifier} = '';
END;
END;
MACRO initHelper(var,value) BLOCK;
# Dumper.dump(helper);
identifier = "${helper.identifier}" _ "_" _ var;
UNLESS helper.${var}.defined; helper.${var} = helper.${var} || value || ${"${identifier}"} || ${var}; END;
END;
MACRO initDtColumns BLOCK;
FOR col in helper.dt_columns;
IF ! col.accessor;
col.accessor = col.name.replace('\.','_');
END;
END;
END;
MACRO initHelperAuto BLOCK;
FOREACH identifier in [ 'messages', 'length_change', 'dt_columns', 'close_target', 'create_flag' ];
initHelper(identifier);
END;
#;
initHelper('form_object',form);
initHelper('ajax_uri', c.uri_for( c.controller.action_for('ajax') ));
IF ! helper.ajax_uri.search('[\?&]dt_columns=');
initDtColumns();
helper.dt_columns_json = helper.dt_columns.json() | url;
helper.ajax_uri = helper.ajax_uri _ '?dt_columns=' _ helper.dt_columns_json;
END;
END;
IF !no_auto_helper;
clearHelper();
initHelperAuto();
#here we can generate couple of variants, selection of proper variant will be exact view responsibility
UNLESS c.user.read_only;
initHelper('dt_buttons',[
{ name = c.loc('Edit'), uri = "/${helper.identifier}'+full[\"id\"]+'/edit", class = 'btn-small btn-primary', icon = 'icon-edit' },
] );
initHelper('top_buttons',[
{ name = c.loc("Create ${helper.name_single}"), uri = c.uri_for("/${helper.identifier}/create"), icon = 'icon-star' },
] );
END;
END;
-%]

@ -4,19 +4,8 @@
write_access = 1;
%]
[% PROCESS 'helpers/datatables_vars.tt' no_auto_helper = 1 -%]
[%PROCESS 'helpers/modal.tt' -%]
[%
mf_helper = {
ajax_load => 1,
ajax_list_refresh => 'template',
}
%]
[% modal_script( m = mf_helper ) %]
[% site_config.title = c.loc('Invoice template [_1]', tmpl.name ) -%]
<script type="text/javascript" src="/js/libs/svg-edit/embedapi.js"></script>
<script type="text/javascript" src="/js/background.js"></script>
<script type="text/javascript" src="/js/jquery.loadJSON.js"></script>
@ -32,9 +21,6 @@
</span>
</div>
[% IF reseller.first.status != "active" -%]
[%messages.unshift( c.loc('Reseller is <b>[_1]</b>', reseller.first.status) ); %]
[% END -%]
<div class="row" id="messages">
[%PROCESS 'helpers/ajax_messages.tt' -%]
</div>

@ -1,4 +1,3 @@
[%# USE FillInForm %]
<script>
function formToUri(q){
return q+'?'+$('form[id=template_editor]').serialize();
@ -6,51 +5,34 @@ function formToUri(q){
function formSerialize(){
return $('form[id=template_editor]').serialize();
}
function getTtIdVal(){
return $('form[id=template_editor] :input[name=tt_id]').val();
}
</script>
<form name="template_editor" id="template_editor" action="[% c.uri_for_action('/invoicetemplate/get_content_ajax', tmpl.id) -%]" class="form-horizontal" enctype="multipart/form-data" method="post">
<input type="hidden" name="tt_id" value="[% tt_id %]">
<span>
<a class="btn btn-primary btn-small" onclick="
saveTemplate({},
refreshMessagesAjax );void(0);"><i class="icon-download-alt"></i> [% c.loc('Save SVG')%]</a>
</span>
<span>
<a class="btn btn-primary btn-small" href="javascript:
var previewWindow = window.open('','_blank');
previewWindow.blur;
saveTemplate({
provider_id:'[%provider.id%]',
tt_id: getTtIdVal(),
},
saveTemplate({},
function(httpResponse, q, data) {
previewWindow.location.href = uriForAction({
tt_sourcestate: 'saved',
tt_output_type : 'pdf',
tt_id: getTtIdVal(),
provider_id: '[%provider.id%]',});
previewWindow.location.href = uriForAction();
previewWindow.focus;
}
);
void(0);">[% c.loc('Preview')%] <i class="icon-edit"></i></a>
void(0);"><i class="icon-eye-open"></i> [% c.loc('Preview as PDF')%]</a>
</span>
<span id="load_saved_control">
<a class="btn btn-secondary btn-small" data-confirm="[%c.loc('Overwrite changes');%]" href="javascript:fetchInvoiceTemplateData({
tt_sourcestate: 'saved',
tt_id: getTtIdVal(),
provider_id: '[%provider.id%]',
});void(0);" cancel-hide="1">[% c.loc('Discard Changes')%] <i class="icon-edit"></i></a>
</span>
<span>
<a class="btn btn-primary btn-small" onclick="
//alert('tt_id='+getTtIdVal()+';');
saveTemplate({
provider_id: '[%provider.id%]',
tt_id: getTtIdVal(),
}, refreshMessagesAjax );void(0);">[% c.loc('Save template')%] <i class="icon-disk"></i></a>
<a class="btn btn-secondary btn-small" data-confirm="[%c.loc('Discard');%]" href="javascript:fetchInvoiceTemplateData({});void(0);" cancel-hide="1"><i class="icon-trash"></i> [% c.loc('Discard Changes')%]</a>
</span>
<div class="ngcp-separator"></div>
<span>
<a class="btn btn-primary btn-small" onclick="
<a class="btn btn-tertiary btn-small" onclick="
var divId = '#template_variables_help';
$(divId).draggable({
handle: '.modal-header',
@ -58,7 +40,7 @@ function getTtIdVal(){
}).css('display','block').find($('.mod_close')).click(function(event) {
$(divId).css('display','none');
});
void(0);">[% c.loc('Show template variables')%] <i class="icon-info-sign"></i></a>
void(0);"><i class="icon-info-sign"></i> [% c.loc('Show Variables')%]</a>
</span>
<style>
#template_variables_help{
@ -118,61 +100,13 @@ END;
</div>
</div>
[%IF viewmode == 'development' %]
<div class="ngcp-separator"></div>
<span>
<a class="btn btn-secondary btn-small" data-confirm="[%c.loc('Overwrite changes');%]" href="javascript:fetchInvoiceTemplateData({
tt_sourcestate: 'default',
tt_id: getTtIdVal(),
provider_id: '[%provider.id%]',
});void(0);" cancel-hide="1">[% c.loc('Load default')%] <i class="icon-edit"></i></a>
</span>
<span id="load_previewed_control" style="display:none;visibility:hidden;">
<a class="btn btn-secondary btn-small" data-confirm="[%c.loc('Overwrite changes');%]" href="javascript:fetchInvoiceTemplateData({
tt_sourcestate: 'previewed',
tt_id: getTtIdVal(),
provider_id: '[%provider.id%]',
});void(0);" cancel-hide="1">[% c.loc('Load previewed')%] <i class="icon-edit"></i></a>
</span>
<div class="ngcp-separator"></div>
<span>
<a class="btn btn-primary btn-small" href="javascript:window.open(uriForAction({
tt_sourcestate: 'previewed',
tt_output_type : 'svg',
tt_id: getTtIdVal(),
provider_id: '[%provider.id%]',
}),'_blank');void(0);">[% c.loc('Previewed SVG')%] <i class="icon-edit"></i></a>
</span>
<span>
<a class="btn btn-primary btn-small" href="javascript:window.open(uriForAction({
tt_sourcestate: 'saved',
tt_output_type : 'svg',
tt_id: getTtIdVal(),
provider_id: '[%provider.id%]',
}),'_blank');void(0);">[% c.loc('Saved SVG')%] <i class="icon-edit"></i></a>
</span>
<span>
<a class="btn btn-primary btn-small" href="javascript:window.open(uriForAction({
tt_sourcestate: 'saved',
tt_output_type : 'pdf',
tt_id: getTtIdVal(),
provider_id: '[%provider.id%]',
}),'_blank');void(0);">[% c.loc('Saved PDF')%] <i class="icon-edit"></i></a>
</span>
[%END%]
[%initial = 'saved'%]
<div class="ngcp-separator"></div>
<iframe
type="text/html"
src="/js/libs/svg-edit/svg-editor.htm" id="svgedit" onload="
init_embed();
fetchSvgToEditor({
tt_viewmode: 'raw',
tt_sourcestate: '[%initial%]',
provider_id: '[%provider.id%]',
tt_id: getTtIdVal(),
});" width="1300px" height="1500px" style="border-width:0px;"></iframe>
src="/js/libs/svg-edit/svg-editor.htm"
id="svgedit"
onload="init_embed();fetchSvgToEditor({});"
width="1024px" height="1500px" style="border-width:0px;"></iframe>
</div>
</form>

@ -1,20 +1,17 @@
<script type="text/javascript">
var uriForAction = function( data, type ){
var q_template;
console.log("type=" + type);
switch(type){
case 'template_previewed':
q_template = '[%# c.uri_for_action("/invoice/template_view", ['provider_id']) -%]'+'/svg/parsed/previewed/svg/tt_id';
break;
case 'template_saved':
q_template = '[%# c.uri_for_action("/invoice/template_view", ['provider_id']) -%]'+'/svg/parsed/saved/tt_output_type/tt_id';
break;
case 'template_list':
q_template = '[%# c.uri_for_action("/invoice/template_list", ['provider_id']) -%]';
q_template = '[% c.uri_for_action("/invoicetemplate/set_content_ajax", [tmpl.id]) -%]';
break;
case 'messages':
q_template = '[%# c.uri_for_action("/invoice/messages") -%]';
q_template = '[% c.uri_for_action("/invoicetemplate/messages_ajax") -%]';
break;
case 'template':
default:
q_template = '[% c.uri_for_action("/invoicetemplate/get_content_ajax", [tmpl.id]) -%]';
break;

Loading…
Cancel
Save