MT#5879 Separated Model DB actions. Time spent with FormHandler and Catalyst::Adaptor.

ipeshinskaya/InvoiceTemplate5
Irina Peshinskaya 12 years ago committed by Victor Seva
parent 7c3a045ddc
commit 4f19965240

@ -17,6 +17,8 @@ use NGCP::Panel::Form::Customer::PbxFieldDevice;
use NGCP::Panel::Form::Customer::PbxFieldDeviceEdit; use NGCP::Panel::Form::Customer::PbxFieldDeviceEdit;
use NGCP::Panel::Form::Customer::PbxFieldDeviceSync; use NGCP::Panel::Form::Customer::PbxFieldDeviceSync;
use NGCP::Panel::Form::Customer::InvoiceTemplate; use NGCP::Panel::Form::Customer::InvoiceTemplate;
use NGCP::Panel::Model::DB::InvoiceTemplate;
use NGCP::Panel::Utils::Message; use NGCP::Panel::Utils::Message;
use NGCP::Panel::Utils::Navigation; use NGCP::Panel::Utils::Navigation;
use NGCP::Panel::Utils::DateTime; use NGCP::Panel::Utils::DateTime;
@ -839,25 +841,36 @@ sub calls :Chained('base') :PathPart('calls') :Args(0) {
$c->stash(zonecalls_rs => [1..100] ); $c->stash(zonecalls_rs => [1..100] );
} }
$c->stash(template => 'customer/calls.tt'); $c->stash(template => 'customer/calls.tt');
#$c->stash(zonecalls_rs => $c->stash{zonecalls_rs});
#$c->detach($c->view('SVG'));
# return;
#$c->response->body(JSON::to_json({ methods => $allowed_methods })."\n");
#$c->stash(template => 'customer/calls_svg.tt');
#$c->stash(close_target => $c->uri_for_action("/customer/details", [$c->stash->{contract}->id]));
#$c->stash(template => 'customer/calls.tt');
# $c->stash(contract => $contract_rs->first);
} }
sub calls_svg :Chained('base') :PathPart('calls/template') :Args { sub calls_svg :Chained('base') :PathPart('calls/template') :Args {
my ($self, $c, $in); my ($self, $c) = @_;
($self,$c,@$in{qw/tt_type tt_viewmode tt_sourcestate/}) = @_; #$c->log->debug($c->model('DB'));
#return;
#my $db = NGCP::Panel::Model::DB::InvoiceTemplate->new();
#$c->log->debug($db);
#my $contract_id = $in->{contract_id} = ; #my $contract_id = $in->{contract_id} = ;
no warnings 'uninitialized'; no warnings 'uninitialized';
my($validator,$backend,$in); my($validator,$db,$in);
#input #input
(undef,undef,@$in{qw/tt_type tt_viewmode tt_sourcestate tt_output_type tt_id/}) = @_ ; (undef,undef,@$in{qw/contract_id tt_type tt_viewmode tt_sourcestate tt_id/}) = ( $c->stash->{contract}->id, @_ );
$in->{contract_id} = $c->stash->{contract}->id;
$in->{tt_string} = $c->request->body_parameters->{template} || ''; $in->{tt_string} = $c->request->body_parameters->{template} || '';
#output #output
my $out={}; my $tt_string;
#input checking & simple preprocessing #input checking & simple preprocessing
$validator = NGCP::Panel::Form::Customer::InvoiceTemplate->new; $validator = NGCP::Panel::Form::Customer::InvoiceTemplate->new;
@ -867,45 +880,37 @@ sub calls_svg :Chained('base') :PathPart('calls/template') :Args {
#storage #storage
#pass scheme here is ugly, and should be moved somehow to DB::Base #pass scheme here is ugly, and should be moved somehow to DB::Base
$backend = NGCP::Panel::Model::DB::InvoiceTemplate->new( schema => $c->model('DB') ); $db = NGCP::Panel::Model::DB::InvoiceTemplate->new( schema => $c->model('DB') );
#really, we don't need a form here at all #really, we don't need a form here at all
#just use as already implemented fields checking and defaults applying #just use as already implemented fields checking and defaults applying
#$validator->setup_form( $validator->setup_form(
$validator->process(
posted => 1, posted => 1,
params => $in, params => $in,
); );
#$validator->validate_form();
#die();
#$c->view('SVG');
#handle request
# my $tt_viewmode //= '';
# my $tt_state //= 'saved';
# my $tt_type //= 'svg';
my $invoicetemplate = $c->request->body_parameters->{template} || '';
$c->log->debug("validated=".$validator->validated.";\n");
my $in_validated = $validator->fif;
#dirty hack
$in = $in_validated;
#$c->log->debug("1.invoicetemplate is empty=".($invoicetemplate?0:1).";viewbox=".($invoicetemplate !~/^<svg.*?viewbox.*?>/is ).";\n"); #really this is for code for field default in validator. The question is how to pass DB model to validator (formhandler) - ideologically correctly?
$c->log->debug("1.invoicetemplate is empty=".($invoicetemplate?0:1).";viewbox=".($invoicetemplate !~/^<svg.*?viewbox.*?>/is ).";\n"); $tt_string = $in->{tt_string};
my $form = NGCP::Panel::Form::Customer::InvoiceTemplate->new; if(!$tt_string){
$form->process( #here we also may be better should contact model, not DB directly. Will return to this separation later
posted => 1, #at the end - we can figure out rather basic controller behaviour
params => $in, $tt_string = $db->getCustomerInvoiceTemplate( %$in );
action => $c->uri_for_action("/customer/calls_svg", [$c->stash->{contract}->id]),
);
$form->validate();
if(!$invoicetemplate){
#getCustomerActiveInvoiceTemplateFromDB.
} }
#we need to get default to 1) sanitize (if in->tt_string) or 2)if not in->tt_string and no customer->tt_string
if($in->{tt_string} || !$tt_string_customer || $tt_string_force_default ){ if(!$tt_string){
#getDefault
NGCP::Panel::Utils::InvoiceTemplate::getDefaultInvoiceTemplate( c => $c, result => \$tt_string );
try{ try{
#Utils... mmm - if it were model - there would be no necessity in utils using #Utils... mmm - maybe model?
NGCP::Panel::Utils::InvoiceTemplate::getDefaultInvoiceTemplate( c => $c, type => $in->{tt_type}, result => \$tt_string_default ); NGCP::Panel::Utils::InvoiceTemplate::getDefaultInvoiceTemplate( c => $c, result => \$tt_string );
} catch($e) { } catch($e) {
NGCP::Panel::Utils::Message->error( NGCP::Panel::Utils::Message->error(
c => $c, c => $c,
@ -913,140 +918,49 @@ sub calls_svg :Chained('base') :PathPart('calls/template') :Args {
desc => $c->loc('There is no one invoice template in the system.'), desc => $c->loc('There is no one invoice template in the system.'),
); );
} }
if($in->{tt_string} && !$tt_string_force_default){ }#else{
#sanitize # #here we have invoice content - of customer or default, so no checking of tt_string is necessary
my $tt_string_sanitized = $in->{tt_string}; #if($tt_string){
$tt_string_sanitized =~s/<script.*?\/script>//gs; #sub candidate, preSaveCustomTemplate
my $tokens_re = qr/\[%(.*?)%\]/; #if it is presaving, making it for default isn't necessary, default should contain it
my $token_shape_re = qr/\s+/; #but while let it be here
my %tokens_valid = map{$_=~s/$token_shape_re//sg; $_ => 1;} ($tt_string_default=~/$tokens_re/sg); ##moved to correct place - to js, generating svg
foreach( $tt_string_sanitized=~/$tokens_re/sg ){ ##### $c->log->debug("3.tt_string is empty=".($tt_string?0:1).";viewbox=".($tt_string !~/^<svg.*?viewbox.*?>/is).";\n");
my $token_shape=$_; ##### if( $tt_string !~/^<svg.*?viewbox.*?>/is ){
$token_shape=~s/$token_shape_re//sg; ##### (my ($width)) = ($tt_string =~/^<svg.*?width.*?(\d+).*?>/is );
if(! exists $tokens_valid{$token_shape}){ ##### $c->log->debug("width=$width;\n");
$c->log->debug('Not allowed token in invoice template:'.$_.";\n"); ##### (my ($height)) = ($tt_string =~/^<svg.*?height.*?(\d+).*?>/is );
$tt_string_sanitized=~s/(?:\[%)+\s*\Q$_\E\s*(?:%\])+//g; ##### my($replaced) = $tt_string =~s/^<svg(.*?(?:width.*?height|height.*width).*?\d+.*? )/<svg $1 viewBox="0 0 $width $height" /i;
} ##### $c->log->debug("replaced=$replaced;\n");
} ##### #storeToDB
#/sanitize - to sub, later ##### }
#}
#irka::loglong(Dumper($tt_string_sanitized));
$backend->storeCustomerInvoiceTemplate(
%$in,
tt_string_sanitized => \$tt_string_sanitized,
);
$out->{tt_string} = $tt_string_sanitized;
}elsif(!$tt_string_customer || $tt_string_force_default){
$out->{tt_string} = $tt_string_default;
$c->log->debug("apply default;");
}
}else{#we have customer template, we don't have dynamic template string, we weren't requested to show default
$out->{tt_string} = $tt_string_customer;
}
#/model logic
#prepare response #prepare response
$c->response->content_type('image/svg+xml'); $c->response->content_type('image/svg+xml');
$c->log->debug("tt_viewmode=".$in->{tt_viewmode}.";\n"); #$c->log->debug("tt_viewmode=".$in->{tt_viewmode}.";\n");
$c->log->debug("tt_viewmode=;\n");
if($in->{tt_viewmode} eq 'raw'){ if($in->{tt_viewmode} eq 'raw'){
#$c->stash->{VIEW_NO_TT_PROCESS} = 1; #$c->stash->{VIEW_NO_TT_PROCESS} = 1;
$c->response->body($out->{tt_string}); $c->response->body($tt_string);
return; return;
}else{ }else{
my $contacts = $c->model('DB')->resultset('contacts')->search({ id => $in->{contract_id} }); my $contacts = $c->model('DB')->resultset('contacts')->search({ id => $in->{contract_id} });
$c->stash( provider => $contacts->first );
#some preprocessing should be done only before showing. So, there will be: #some preprocessing should be done only before showing. So, there will be:
#preSaveCustomTemplate prerpocessing #preSaveCustomTemplate prerpocessing
#preShowCustomTemplate prerpocessing #preShowCustomTemplate prerpocessing
{ {
#preShowInvoice #preShowInvoice
#also to model #also to model
$out->{tt_string}=~s/(?:{\s*)?<!--{|}-->(?:\s*})?//gs; $tt_string =~s/(?:{\s*)?<!--{|}-->(?:\s*})?//gs;
} }
if( ($in->{tt_output_type} eq 'svg') || ( $in->{tt_output_type} eq 'html') ){ $c->stash( provider => $contacts->first );
#$c->response->content_type('image/svg+xml'); #use irka;
$c->stash( template => \$out->{tt_string} ); #irka::loglong(\$tt_string);
$c->detach( $c->view('SVG') ); $c->stash( template => \$tt_string );
}elsif($in->{tt_output_type} eq 'pdf'){ $c->log->debug("before_detach;\n");
$c->response->content_type('application/pdf'); $c->detach($c->view('SVG'));
my $svg = $c->view('SVG')->getTemplateProcessed($c,\$out->{tt_string}, $c->stash );
my(@pages) = $svg=~/(<svg.*?(?:\/svg>))/sig;
#$c->log->debug($svg);
#my $kit = PDF::WebKit->new(\$svg, page_size => 'A4');
#push @{ $kit->stylesheets }, "/path/to/css/file";
# Get an inline PDF
#$out->{tt_string} = $kit->to_pdf;
#$c->response->body($out->{tt_string});
my ($tempdirbase,$tempdir );
use File::Temp qw/tempfile tempdir/;
#my($fh, $tempfilename) = tempfile();
$tempdirbase = join('/',File::Spec->tmpdir,@$in{qw/contract_id tt_type tt_sourcestate/}, $out->{tt_id});
use File::Path qw( mkpath );
! -e $tempdirbase and mkpath( $tempdirbase, 0, 0777 );
$tempdir = tempdir( DIR => $tempdirbase , CLEANUP => 1 );
$c->log->debug("tempdirbase=$tempdirbase; tempdir=$tempdir;");
#try{
#} catch($e){
# NGCP::Panel::Utils::Message->error(
# c => $c,
# error => "Can't create temporary directory at: $tempdirbase;" ,
# desc => $c->loc("Can't create temporary directory."),
# );
#}
my $pagenum = 1;
my @pagefiles;
foreach my $page (@pages){
my $fh;
my $pagefile = "$tempdir/$pagenum.svg";
push @pagefiles, $pagefile;
open($fh,">",$pagefile);
#try{
#} catch($e){
# NGCP::Panel::Utils::Message->error(
# c => $c,
# error => "Can't create temporary page file at: $tempdirbase/$page.svg;" ,
# desc => $c->loc("Can't create temporary file."),
# );
#}
print $fh $page;
close $fh;
$pagenum++;
}
#$fh->unlink_on_destroy( 0 );
#my $filename = "/tmp/bbb.svg";
#open my $fh, ">$filename";
#binmode $fh;
#print $fh $svg;
#close $fh;
#my $cmd = "/tmp/wkhtmltox/bin/wkhtmltopdf $filename - ";
my $cmd = "rsvg-convert -f pdf ".join(" ", @pagefiles);
$c->log->debug($cmd);
#`chmod ugo+rwx $filename`;
#binmode(STDOUT);
#binmode(STDIN);
#$out->{tt_string} = `$cmd`;
{
#$cmd = "fc-list";
open B, "$cmd |";
binmode B;
local $/ = undef;
$out->{tt_string} = <B>;
close B;
}
$c->response->body($out->{tt_string});
return;
#$out->{tt_string} = `cat $filename `;
}
} }
} }

@ -4,45 +4,48 @@ use HTML::FormHandler::Moose;
extends 'HTML::FormHandler'; extends 'HTML::FormHandler';
use Moose::Util::TypeConstraints; use Moose::Util::TypeConstraints;
enum 'TemplateType' => [ qw/svg/ ];#html enum 'TemplateType' => [ qw/svg html/ ];#html
enum 'TemplateViewMode' => [ qw/raw parsed/ ]; enum 'TemplateViewMode' => [ qw/raw parsed/ ];
enum 'TemplateSourceState' => [ qw/saved previewed/ ]; enum 'TemplateSourceState' => [ qw/saved previewed/ ];
no Moose::Util::TypeConstraints; no Moose::Util::TypeConstraints;
#while use only for validation, no rendering is necessary has '+use_fields_for_input_without_param' => ( default => 1 );
#use HTML::FormHandler::Widget::Block::Bootstrap;
#looks as often repeated
#has '+widget_wrapper' => ( default => 'Bootstrap' );
#sub build_form_element_class { [qw/form-horizontal/] }
#Attempt to use Moose to validation
#has 'sort_order' => (
# is => 'ro',
# isa => enum([qw[ ascending descending ]]),
#);
has_field 'tt_type' => ( has_field 'tt_type' => (
# is => 'rw',
# isa => enum([qw[ svg ]]),#html
type => 'Text', type => 'Text',
required => 0, required => 1,
#apply => [ 'enum' ], default => 'svg',
#apply => [ { check => [qw/svg/] } ], #apply => [ qw/svg/ ],
apply => [ 'TemplateType' ], apply => [ 'TemplateType' ],
); );
has_field 'tt_viewmode' => ( has_field 'tt_viewmode' => (
type => 'Text', type => 'Text',
required => 0, required => 0,
apply => [ 'TemplateViewMode' ],
#check => [ qw/raw parsed/ ],
default => 'parsed',
); );
has_field 'tt_sourcestate' => ( has_field 'tt_sourcestate' => (
type => 'Text', type => 'Text',
required => 1, required => 1,
default => 'saved',
apply => [ 'TemplateSourceState' ],
#check => [ qw/saved previewed/ ],
); );
has_field 'tt_string' => (
type => 'Text',
#default => \&
#apply => [ { check => \&validate_tt_string } ],
required => 0,
);
sub validate_tt_string{
#here could be following: take default from file and get all variables and validate variables from customer string
};
1; 1;
=head1 NAME =head1 NAME
@ -51,7 +54,7 @@ NGCP::Panel::Form::InvoiceTemplate
=head1 DESCRIPTION =head1 DESCRIPTION
Form to modify a invoice template. Form to modify an invoice template.
=head1 METHODS =head1 METHODS

@ -4,10 +4,9 @@ use base NGCP::Panel::Model::DB::Base;
sub getCustomerInvoiceTemplate{ sub getCustomerInvoiceTemplate{
my $self = shift; my $self = shift;
my (%params) = @_; my (%params) = @_;
my ($contract_id,$tt_sourcestate,$tt_type) = @params{qw/contract_id tt_sourcestate tt_type/}; my ($contract_id,$tt_sourcestate,$tt_type) = @params{qw/contract_id tt_sourcestate/};
my $result = ''; my $result = '';
my $tt_id = '';
#my $tt_record = $self->resultset('invoice_template')->search({ #my $tt_record = $self->resultset('invoice_template')->search({
my $tt_record = $self->schema->resultset('invoice_template')->search({ my $tt_record = $self->schema->resultset('invoice_template')->search({
@ -20,62 +19,12 @@ sub getCustomerInvoiceTemplate{
#here we will rely on form checking and defaults #here we will rely on form checking and defaults
#if('saved' eq $tt_sourcestate){ #if('saved' eq $tt_sourcestate){
if( $tt_record ){ if( $tt_record ){
$result = $tt_record->get_column( 'base64_'.$tt_sourcestate ); $result = \$tt_record->get_column( 'base64_'.$tt_sourcestate );
$tt_id = $tt_record->get_column( 'id' );
} }
if( $result && exists $params{result} ){ if( $result && exists $params{result} ){
${$params{result}} = $result; ${$params{result}} = $result;
} }
return ( $tt_id,\$result, $tt_record );#sgorila hata, gori i saray return $result;
}
sub storeCustomerInvoiceTemplate{
my $self = shift;
my (%params) = @_;
my ($contract_id,$tt_sourcestate,$tt_type, $tt_string, $tt_id) = @params{qw/contract_id tt_sourcestate tt_type tt_string_sanitized tt_id/};
#my $tt_record = $self->resultset('invoice_template')->search({
$self->schema->txn_do(sub {
#reseller_id and is_active aren't unique key, because is_active can kepp some 0 values for one reseller, we shouldn't keep active and inactive in one table
# $self->schema->resultset('invoice_template')->update_or_create({
# reseller_id => $contract_id,
# type => $tt_type,
# is_active => 1,
# 'base64_'.$tt_sourcestate => $$tt_string,
# });
my $tt_record = $self->schema->resultset('invoice_template')->search({
reseller_id => $contract_id,
type => $tt_type,
is_active => 1,
})->first;
#here may be base64 decoding
# $self->schema->resultset('ivoice_template')->search({
# reseller_id => $contract_id,
# type => $tt_type,
# })->update_all({
# is_active => 0,
# });
if( !$tt_record ){
$self->schema->resultset('invoice_template')->create({
reseller_id => $contract_id,
type => $tt_type,
is_active => 1,
'base64_'.$tt_sourcestate => $$tt_string,
});
}else{
$self->schema->resultset('invoice_template')->update({
reseller_id => $contract_id,
type => $tt_type,
is_active => 1,
'base64_'.$tt_sourcestate => $$tt_string,
id => $tt_record->get_column( 'id' ),
},
{ key => 'id' });
}
});
} }
1; 1;

@ -3,17 +3,11 @@ use strict;
use warnings; use warnings;
use Moose; use Moose;
use Sipwise::Base; use Sipwise::Base;
use DBIx::Class::Exception;
use NGCP::Panel::Utils::DateTime;
#use NGCP::Panel::Utils::DateTime;
sub getDefault{
my %params = @_;
sub getDefaultInvoiceTemplate{ sub getDefaultInvoiceTemplate{
my (%in) = @_; my (%in) = @_;
#in future may be we will store root default in Db too, but now it is convenient to edit template as file #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}, 'customer/calls_'.$in{type}.'.tt'); my $result = $in{c}->view('SVG')->getTemplateContent($in{c}, 'customer/calls_svg.tt');
#$in{c}->log->debug("result=$result;"); #$in{c}->log->debug("result=$result;");
@ -23,24 +17,4 @@ sub getDefaultInvoiceTemplate{
return \$result; return \$result;
} }
sub getCustomerTemplate{
my %params = @_;
my $c = $params{c};
my $contract_id = $params{contract_id} || $c->stash->{contract}->id;
my $tt_state = $params{tt_state} || 'saved';
my $result;
my $template_record = $c->model('DB')->resultset('invoice_template')->search({
reseller_id => $contract_id,
is_active => 1,
})->first;
#here may be base64 decoding
if('saved' eq $tt_state){
$result = \$template_record->get_column('base64_'.$tt_state);
}
return $result;
}
1; 1;

@ -38,7 +38,9 @@ sub process
} }
return 1; return 1;
} }
sub getTemplate{ #method is necessary to apply APP specific template configurations, e.g. path, tt file extensions etc
#may be moved to main view class, but as on template_invoice experiments stage it would be ok to leave it in separated class
sub getTemplateContent{
my ( $self, $c, $template ) = @_; my ( $self, $c, $template ) = @_;
if(defined $template){ if(defined $template){
$c->log->debug("getTemplate: template=$template;"); $c->log->debug("getTemplate: template=$template;");

Loading…
Cancel
Save