From 41ac1f153e2ab03c80f08206ce61825324d3c51d Mon Sep 17 00:00:00 2001 From: Paul Driver Date: Wed, 5 Jan 2011 12:34:55 -0600 Subject: [PATCH] Template Preview button --- docs/changelog/7.x.x.txt | 1 + lib/WebGUI/Asset/Template.pm | 253 +++++++++++++++---- lib/WebGUI/Asset/Template/TemplateToolkit.pm | 6 +- lib/WebGUI/i18n/English/Asset_Template.pm | 49 +++- t/Asset/Template.t | 30 ++- 5 files changed, 283 insertions(+), 56 deletions(-) diff --git a/docs/changelog/7.x.x.txt b/docs/changelog/7.x.x.txt index 11ed462a1..b1a409a37 100644 --- a/docs/changelog/7.x.x.txt +++ b/docs/changelog/7.x.x.txt @@ -1,4 +1,5 @@ 7.10.7 + - Added a Preview button the the Template asset's edit page - fixed #12009: Export JS errors in IE7 - added #11968: use the language override in the registration form (Jukka Raimovaara / Mentalhouse Oy) - Changed Carousel to use TinyMCE with WebGUI plugins diff --git a/lib/WebGUI/Asset/Template.pm b/lib/WebGUI/Asset/Template.pm index d5e0c9102..8c969a71f 100644 --- a/lib/WebGUI/Asset/Template.pm +++ b/lib/WebGUI/Asset/Template.pm @@ -25,7 +25,8 @@ use List::MoreUtils qw{ any }; use Tie::IxHash; use Clone qw/clone/; use HTML::Packer; -use JSON qw{ to_json }; +use JSON qw{ to_json from_json }; +use Try::Tiny; =head1 NAME @@ -284,60 +285,134 @@ Returns the TabForm object that will be used in generating the edit page for thi sub getEditForm { my $self = shift; + my $session = $self->session; + + my ( $db, $url, $style, $form, $config ) + = $session->quick(qw( db url style form config )); + my $tabform = $self->SUPER::getEditForm(); - my $i18n = WebGUI::International->new($self->session, 'Asset_Template'); + my $i18n = WebGUI::International->new($session, 'Asset_Template'); + + my ( $properties, $meta, $display ) = + map { $tabform->getTab($_) } qw( properties meta display ); + + my $returnUrl = $form->get('returnUrl'); $tabform->hidden({ name=>"returnUrl", - value=>$self->session->form->get("returnUrl") + value=>$returnUrl, }); if ($self->getValue("namespace") eq "") { - my $namespaces = $self->session->dbSlave->buildHashRef("select distinct(namespace) from template order by namespace"); - $tabform->getTab("properties")->combo( + my $namespaces = $db->buildHashRef("select distinct(namespace) from template order by namespace"); + $properties->combo( -name=>"namespace", -options=>$namespaces, -label=>$i18n->get('namespace'), -hoverHelp=>$i18n->get('namespace description'), - -value=>[$self->session->form->get("namespace")] + -value=>[$form->get("namespace")] ); } else { - $tabform->getTab("meta")->readOnly( + $meta->readOnly( -label=>$i18n->get('namespace'), -hoverHelp=>$i18n->get('namespace description'), -value=>$self->getValue("namespace") - ); - $tabform->getTab("meta")->hidden( + ); + $meta->hidden( -name=>"namespace", -value=>$self->getValue("namespace") ); } - $tabform->getTab("display")->yesNo( + $display->yesNo( -name=>"showInForms", -value=>$self->getValue("showInForms"), -label=>$i18n->get('show in forms'), -hoverHelp=>$i18n->get('show in forms description'), ); - $tabform->getTab("properties")->codearea( + $properties->codearea( -name=>"template", -label=>$i18n->get('assetName'), -hoverHelp=>$i18n->get('template description'), -syntax => "html", -value=>$self->getValue("template") ); - $tabform->getTab('properties')->yesNo( - name => "usePacked", - label => $i18n->get('usePacked label'), - hoverHelp => $i18n->get('usePacked description'), - value => $self->getValue("usePacked"), - ); - if($self->session->config->get("templateParsers")){ - my @temparray = @{$self->session->config->get("templateParsers")}; + $properties->raw(qq( + + + ${\ $i18n->get('Preview') } + + + + + + + )); + my $cform = WebGUI::HTMLForm->new($session); + $cform->yesNo( + id => 'previewRaw', + name => 'previewRaw', + label => $i18n->get('Plain Text?'), + hoverHelp => $i18n->get('Plain Text hoverHelp'), + ); + $cform->text( + id => 'previewFetchUrl', + label => $i18n->get('URL'), + hoverHelp => $i18n->get('URL hoverHelp'), + defaultValue => $returnUrl, + ); + $cform->button( + id => 'previewFetch', + label => $i18n->get('Fetch Variables'), + hoverHelp => $i18n->get('Fetch Variables hoverHelp'), + value => $i18n->get('Fetch'), + ); + $cform->codearea( + id => 'previewVars', + label => $i18n->get('Variables'), + hoverHelp => $i18n->get('Variables hoverHelp'), + ); + + $cform->hidden(id => 'previewId', value => $self->getId); + $cform->hidden(id => 'previewGateway', value => $url->gateway); + $properties->raw(qq( + + + +
+
${\ $i18n->get('Configure Preview') }
+ ${\ $cform->printRowsOnly }
+
+ +
+
+ + + )); + + $properties->yesNo( + name => "usePacked", + label => $i18n->get('usePacked label'), + hoverHelp => $i18n->get('usePacked description'), + value => $self->getValue("usePacked"), + ); + + $style->setScript($url->extras($_)) for qw( + yui/build/json/json-min.js + yui/build/container/container-min.js + templatePreview.js + ); + + if($config->get("templateParsers")){ + my @temparray = @{$config->get("templateParsers")}; tie my %parsers, 'Tie::IxHash'; while(my $a = shift @temparray){ - $parsers{$a} = $self->getParser($self->session, $a)->getName(); + $parsers{$a} = $self->getParser($session, $a)->getName(); } my $value = [$self->getValue("parser")]; - $value = \[$self->session->config->get("defaultTemplateParser")] if(!$self->getValue("parser")); - $tabform->getTab("properties")->selectBox( + $value = \[$config->get("defaultTemplateParser")] if(!$self->getValue("parser")); + $properties->selectBox( -name=>"parser", -options=>\%parsers, -value=>$value, @@ -346,36 +421,36 @@ sub getEditForm { ); } - $tabform->getTab('properties')->jsonTable( - name => 'attachmentsJson', - value => $self->get('attachmentsJson'), - label => $i18n->get("attachment display label"), - fields => [ - { - type => "text", - name => "url", - label => $i18n->get('attachment header url'), - size => '48', - }, - { - type => "select", - name => "type", - label => $i18n->get('attachment header type'), - options => [ - stylesheet => $i18n->get('css label'), - headScript => $i18n->get('js head label'), - bodyScript => $i18n->get('js body label'), - ], - }, - ], - ); + $properties->jsonTable( + name => 'attachmentsJson', + value => $self->get('attachmentsJson'), + label => $i18n->get("attachment display label"), + fields => [ + { + type => "text", + name => "url", + label => $i18n->get('attachment header url'), + size => '48', + }, + { + type => "select", + name => "type", + label => $i18n->get('attachment header type'), + options => [ + stylesheet => $i18n->get('css label'), + headScript => $i18n->get('js head label'), + bodyScript => $i18n->get('js body label'), + ], + }, + ], + ); - $tabform->getTab('properties')->image( - name => 'storageIdExample', - value => $self->getValue('storageIdExample'), - label => $i18n->get('field storageIdExample'), - hoverHelp => $i18n->get('field storageIdExample description'), - ); + $properties->image( + name => 'storageIdExample', + value => $self->getValue('storageIdExample'), + label => $i18n->get('field storageIdExample'), + hoverHelp => $i18n->get('field storageIdExample description'), + ); return $tabform; } @@ -623,6 +698,44 @@ sub process { return to_json( $vars ); } + my $extra = ''; + if ($self->canEdit) { + # Used for debugging and the template test renderer. + + # WARNING: Please do not rely on this behavior. It's a bit of a hack, + # and should not be considered part of the core API. Eventually, we + # will have introspectable template objects so that you can more + # easily (and efficiently) get this kind of information. + + # If the first value for the 'X-Webgui-Template-Variables' header is + # our assetId, then in addition to processing the template, we'll add + # a json representation of our template variables. The headers + # "X-Webgui-Template-Variables-Start" and + # "X-Webgui-Template-Variables-End" will contain the delimiters for + # the start and end of this content so that the user agent (who had to + # have stuck the header in in the first place) can parse it out. The + # delimiters will make the whole thing look like an xml comment () just in case. + + # We would just send the vars in the header, but different webservers + # have different limits on header field size and it's impossible to + # say whether our data will fit inside them or not. + my $r = $session->request; + my $head = 'X-Webgui-Template-Variables'; + if ($self->getId eq $r->headers_in->{$head}) { + if (my $json = eval { to_json($vars) }) { + my @chr = ('0'..'9', 'a'..'z', 'A'..'Z'); + my $rnd = join('', map { $chr[int(rand($#chr))] } (1..32)); + my $out = $r->headers_out; + my $st = ""; + $out->{"$head-Start"} = $st; + $out->{"$head-End"} = $end; + $extra = $st . $json . $end; + } + } + } + $self->prepare unless ($self->{_prepared}); my $parser = $self->getParser($session, $self->get("parser")); my $template = $self->get('usePacked') @@ -636,7 +749,7 @@ sub process { my $i18n = WebGUI::International->new($session, 'Asset_Template'); $output = sprintf $i18n->get('template error').$e->error, $self->getUrl, $self->getId; } - return $output; + return $output . $extra; } @@ -1174,6 +1287,42 @@ sub www_styleWizard { #------------------------------------------------------------------- +=head2 www_preview + +Rendes this template with the given variables (posted as JSON) + +=cut + +sub www_preview { + my $self = shift; + my $session = $self->session; + return $session->privilege->insufficient unless $self->canEdit; + + my $form = $session->form; + my $http = $session->http; + + try { + my $output = $self->processRaw( + $session, + $form->get('template'), + from_json($form->get('variables')), + $form->get('parser'), + ); + if ($form->get('plainText')) { + $http->setMimeType('text/plain'); + } + elsif ($output !~ //) { + $output = $session->style->userStyle($output); + } + return $output; + } catch { + $http->setMimeType('text/plain'); + $_[0]; + } +} + +#------------------------------------------------------------------- + =head2 www_view Override the default behavior. When a template is viewed, it redirects you diff --git a/lib/WebGUI/Asset/Template/TemplateToolkit.pm b/lib/WebGUI/Asset/Template/TemplateToolkit.pm index 3474dedda..55c41250d 100644 --- a/lib/WebGUI/Asset/Template/TemplateToolkit.pm +++ b/lib/WebGUI/Asset/Template/TemplateToolkit.pm @@ -81,7 +81,11 @@ sub process { POST_CHOMP => 1, # cleanup whitespace EVAL_PERL => 0, # evaluate Perl code blocks }); - $t->process( \$template, _rewriteVars($vars),\$output) || $self->session->errorHandler->error($t->error()); + unless ($t->process( \$template, _rewriteVars($vars),\$output)) { + my $e = $t->error; + $self->session->log->error($e); + die $e; + } }; if ($@) { WebGUI::Error::Template->throw( error => $@ ); diff --git a/lib/WebGUI/i18n/English/Asset_Template.pm b/lib/WebGUI/i18n/English/Asset_Template.pm index 1e4d3bc7b..5aa1ef3b9 100644 --- a/lib/WebGUI/i18n/English/Asset_Template.pm +++ b/lib/WebGUI/i18n/English/Asset_Template.pm @@ -399,7 +399,54 @@ Any scratch variables will be available in the template with this syntax:
message => 'An example image to show what the template looks like before the user selects it', lastUpdated => 0, }, - + 'Configure' => { + message => 'Configure', + lastUpdated => 1294247160, + }, + 'Fetch Variables' => { + message => 'Fetch Variables', + lastUpdated => 1294165643, + }, + 'Fetch Variables hoverHelp' => { + message => 'Try to guess variables from a url that uses this template.', + lastUpdated => 1294165643, + }, + 'Fetch' => { + message => 'Fetch', + lastUpdated => 1294165643, + }, + 'URL' => { + message => 'URL', + lastUpdated => 1294165643, + }, + 'URL hoverHelp' => { + message => 'URL used by the fetch button.', + lastUpdated => 1294165643, + }, + 'Plain Text?' => { + message => 'Preview as Plain Text?', + lastUpdated => 1294165643, + }, + 'Plain Text hoverHelp' => { + message => 'If you mark yes, you will get a plain-text response (useful for seeing the raw output of a template). Otherwise, the output will be rendered as html.', + lastUpdated => 1294165643, + }, + 'Preview' => { + message => 'Preview', + lastUpdated => 1294247388, + }, + 'Variables' => { + message => 'Variables', + lastUpdated => 1294165651, + }, + 'Variables hoverHelp' => { + message => 'Variables used by the render button (in JSON).', + lastUpdated => 1294165652, + }, + 'Configure Preview' => { + message => 'Configure Preview', + lastUpdated => 1294251507, + }, }; 1; diff --git a/t/Asset/Template.t b/t/Asset/Template.t index da42347c4..9fe94e201 100644 --- a/t/Asset/Template.t +++ b/t/Asset/Template.t @@ -16,7 +16,7 @@ use WebGUI::Test; use WebGUI::Session; use WebGUI::Asset::Template; use Exception::Class; -use Test::More tests => 57; # increment this value for each test you create +use Test::More tests => 58; # increment this value for each test you create use Test::Deep; use Data::Dumper; use Test::Exception; @@ -51,7 +51,10 @@ ok($output =~ m/true/, "process() - conditionals"); ok($output =~ m/\b(?:XY){5}\b/, "process() - loops"); # See if template listens the Accept header -$session->request->headers_in->{Accept} = 'application/json'; +my $request = $session->request; +my $in = $request->headers_in; +my $out = $request->headers_out; +$in->{Accept} = 'application/json'; my $json = $template->process(\%var); my $andNowItsAPerlHashRef = eval { from_json( $json ) }; @@ -61,6 +64,29 @@ cmp_deeply( \%var, $andNowItsAPerlHashRef, 'Accept = json, The correct JSON is r # Done, so remove the json Accept header. delete $session->request->headers_in->{Accept}; +# Testing the stuff-your-variables-into-the-body-with-delimiters header +my $oldUser = $session->user; + +# log in as admin so we pass canEdit +$session->user({ userId => 3 }); +my $hname = 'X-Webgui-Template-Variables'; +$in->{$hname} = $template->getId; + +# processRaw sets some session variables (including username), so we need to +# re-do it. +WebGUI::Asset::Template->processRaw($session,$tmplText,\%var); +my $output = $template->process(\%var); +delete $in->{$hname}; +my $start = delete $out->{"$hname-Start"}; +my $end = delete $out->{"$hname-End"}; +my ($json) = $output =~ /\Q$start\E(.*)\Q$end\E/; +$andNowItsAPerlHashRef = eval { from_json( $json ) }; +cmp_deeply( $andNowItsAPerlHashRef, \%var, "$hname: json returned correctly" ) + or diag "output: $output"; + +$session->user({ user => $oldUser }); + +# done testing the header stuff my $newList = WebGUI::Asset::Template->getList($session, 'WebGUI Test Template'); ok(exists $newList->{$template->getId}, 'Uncommitted template exists returned from getList');