template attachments (scripts and stylesheets)

This commit is contained in:
Paul Driver 2009-05-01 23:03:36 +00:00
parent 676b124697
commit f4df3b1280
6 changed files with 664 additions and 36 deletions

View file

@ -19,9 +19,17 @@ use base 'WebGUI::Asset';
use WebGUI::International;
use WebGUI::Asset::Template::HTMLTemplate;
use WebGUI::Utility;
use WebGUI::Form;
use Tie::IxHash;
use Clone qw/clone/;
use HTML::Packer;
tie my %attachmentTypeNames, 'Tie::IxHash' => (
stylesheet => 'CSS Stylesheet',
headScript => 'Javascript (head)',
bodyScript => 'Javascript (body)',
);
=head1 NAME
Package WebGUI::Asset::Template
@ -43,6 +51,52 @@ These methods are available from this class:
#-------------------------------------------------------------------
=head2 addAttachments ( attachments )
Adds attachments to this template. Attachments is an arrayref of hashrefs,
where each hashref should have at least url, type, and sequence as keys.
=cut
sub addAttachments {
my ($self, $attachments) = @_;
my $db = $self->session->db;
my $sql = q{
INSERT INTO template_attachments
(templateId, revisionDate, url, type, sequence)
VALUES
(?, ?, ?, ?, ?)
};
foreach my $a (@$attachments) {
my @params = (
$self->getId,
$self->get('revisionDate'),
@{$a}{qw(url type sequence)}
);
$db->write($sql, \@params);
}
}
#-------------------------------------------------------------------
=head2 addRevision ( )
Override the master addRevision to copy attachments
=cut
sub addRevision {
my $self = shift;
my $object = $self->SUPER::addRevision(@_);
$object->addAttachments($self->getAttachments);
return $object;
}
#-------------------------------------------------------------------
=head2 definition ( session, definition )
Defines the properties of this asset.
@ -146,6 +200,39 @@ sub duplicate {
#-------------------------------------------------------------------
=head2 removeAttachments ( urls )
Removes the specified attachments (specified by their urls, NOT by hashrefs,
since urls are unique to a particular revision) from this template. urls
should be an arrayref of strings.
=cut
sub removeAttachments {
my ($self, $urls) = @_;
return unless @$urls;
my $db = $self->session->db;
my $dbh = $db->dbh;
my $in = join(',', map { $dbh->quote($_) } @$urls);
my $rmsql = qq{
DELETE FROM
template_attachments
WHERE
templateId = ?
AND revisionDate = ?
AND url IN ($in)
};
my @params = (
$self->getId,
$self->get('revisionDate'),
);
$db->write($rmsql, \@params);
}
#-------------------------------------------------------------------
=head2 packTemplate ( template )
Pack the template into a minified version for faster downloads.
@ -185,11 +272,76 @@ sub processPropertiesFromFormPost {
$needsUpdate = 1;
$data{extraHeadTags} = '';
}
my $f = $self->session->form;
my $p = $f->paramsHashRef;
my @nums = grep {$_} map { my ($i) = /^attachmentUrl(\d+)$/; $i } keys %$p;
my @add;
foreach my $n (@nums) {
my ($index, $type, $url) =
map { $f->process('attachment' . $_ . $n) }
qw(Index Type Url);
push(
@add, {
sequence => $index,
url => $url,
type => $type,
}
);
}
my @remove = $f->process('removeAttachment', 'list');
$data{removeAttachments} = \@remove;
$data{attachments} = \@add;
$needsUpdate = 1 if @remove || @add;
$self->update(\%data) if $needsUpdate;
}
#-------------------------------------------------------------------
=head2 getAttachments ( [type] )
Returns an arrayref of hashrefs representing all attachments for this template
of the specified type (link, bodyScript, headScript).
=head3 type
If defined, will limit the attachments to this type; e.g., passing
'stylesheet' will return only stylesheets.
=cut
sub getAttachments {
my ( $self, $type ) = @_;
my @params = ($self->getId, $self->get('revisionDate'));
my $typeString;
if ($type) {
$typeString = 'AND type = ?';
push(@params, $type);
}
my $sql = qq{
SELECT
*
FROM
template_attachments
WHERE
templateId = ?
AND revisionDate = ?
$typeString
ORDER BY
type, sequence ASC
};
return $self->session->db->buildArrayRefOfHashRefs($sql, \@params);
}
#-------------------------------------------------------------------
=head2 getEditForm ( )
Returns the TabForm object that will be used in generating the edit page for this asset.
@ -230,11 +382,11 @@ sub getEditForm {
-label=>$i18n->get('show in forms'),
-hoverHelp=>$i18n->get('show in forms description'),
);
$tabform->getTab("properties")->codearea(
$tabform->getTab("properties")->codearea(
-name=>"template",
-label=>$i18n->get('assetName'),
-hoverHelp=>$i18n->get('template description'),
-syntax => "html",
-syntax => "html",
-value=>$self->getValue("template")
);
$tabform->getTab('properties')->yesNo(
@ -257,8 +409,67 @@ sub getEditForm {
-value=>$value,
-label=>$i18n->get('parser'),
-hoverHelp=>$i18n->get('parser description'),
);
);
}
my $session = $self->session;
my @headers = map { '<th>' . $i18n->get("attachment header $_") . '</th>' }
qw(index type url remove);
my $table = '<table id="attachmentDisplay">';
$table .= "<thead><tr>@headers</tr></thead><tbody id='addAnchor'>";
foreach my $a ( @{ $self->getAttachments } ) {
my ($seq, $type, $url) = @{$a}{qw(sequence type url)};
# escape macros in the url so they don't get processed
$url =~ s/\^/&#94;/g;
my $del = WebGUI::Form::checkbox(
$session, {
name => 'removeAttachment',
value => $url,
extras => 'class="id"',
}
);
my @data = (
"<td class='index'>$seq</td>",
"<td class='type'>$type</td>",
"<td class='url'>$url</td>",
"<td>$del</td>",
);
$table .= "<tr class='existingAttachment'>@data</tr>";
}
$table .= '</tbody></table>';
my $properties = $tabform->getTab('properties');
my $label = $i18n->get('attachment display label');
$properties->raw("<tr><td>$label</td><td>$table</td></tr>");
my @data = map { "<td>$_</td>" } (
WebGUI::Form::integer(
$session, { size => '2', id => 'addBoxIndex' }
),
WebGUI::Form::selectBox(
$session, { options => \%attachmentTypeNames, id => 'addBoxType' }
),
WebGUI::Form::text($session, { id => 'addBoxUrl', size => 40 }),
WebGUI::Form::button(
$session, {
value => $i18n->get('attachment add button'),
extras => 'onclick="addClick()"'
}
),
);
my ($style, $url) = $self->session->quick(qw(style url));
$style->setScript($url->extras('yui/build/yahoo/yahoo-min.js'));
$style->setScript($url->extras('yui/build/json/json-min.js'));
$style->setScript($url->extras('yui/build/dom/dom-min.js'));
pop(@headers);
my $scriptUrl = $url->extras('templateAttachments.js');
$table = "<table id='addBox'><tr>@headers</tr><tr>@data</tr></table>";
$table .= qq(<script type="text/javascript" src="$scriptUrl"></script>);
$label = $i18n->get('attachment add field label');
$properties->raw("<tr><td>$label</td><td>$table</td></tr>");
return $tabform;
}
@ -370,15 +581,42 @@ A hash reference containing template variables to be processed for the head bloc
sub prepare {
my $self = shift;
my $vars = shift;
my $vars = shift;
$self->{_prepared} = 1;
my $templateHeadersSent = $self->session->stow->get("templateHeadersSent") || [];
my @sent = @{$templateHeadersSent};
unless (isIn($self->getId, @sent)) { # don't send head block if we've already sent it for this template
$self->session->style->setRawHeadTags($self->getParser($self->session, $self->get('parser'))->process($self->getExtraHeadTags, $vars));
}
push(@sent, $self->getId);
$self->session->stow->set("templateHeadersSent", \@sent);
my $sent = $self->session->stow->get('templateHeadersSent');
unless ($sent) {
$self->session->stow->set('templateHeadersSent', $sent = []);
}
my $id = $self->getId;
# don't send head block if we've already sent it for this template
return if isIn($id, @$sent);
my $session = $self->session;
my ($db, $style) = $session->quick(qw(db style));
my $parser = $self->getParser($session, $self->get('parser'));
my $headBlock = $parser->process($self->getExtraHeadTags, $vars);
$style->setRawHeadTags($headBlock);
foreach my $sheet ( @{ $self->getAttachments('stylesheet') } ) {
my %props = ( type => 'text/css', rel => 'stylesheet' );
$style->setLink($sheet->{url}, \%props);
}
my $doScripts = sub {
my ($type, $body) = @_;
foreach my $script ( @{ $self->getAttachments($type) } ) {
my %props = ( type => 'text/javascript' );
$style->setScript($script->{url}, \%props, $body);
}
};
$doScripts->('headScript');
$doScripts->('bodyScript', 1);
push(@$sent, $id);
}
@ -445,6 +683,27 @@ sub processRaw {
return $class->getParser($session,$parser)->process($template, $vars);
}
#-------------------------------------------------------------------
=head2 purgeRevision ( )
Override the master purgeRevision to purge attachments
=cut
sub purgeRevision {
my $self = shift;
my $db = $self->session->db;
my $sql = q{
DELETE FROM
template_attachments
WHERE
templateId = ?
AND revisionDate = ?
};
$db->write($sql, [$self->getId, $self->get('revisionDate')]);
return $self->SUPER::purgeRevision(@_);
}
#-------------------------------------------------------------------
@ -456,16 +715,27 @@ packages that contain headBlocks.
This method is deprecated and will be removed in the future. Don't plan
on this being here.
Note, we are also using this now to update template attachments. So maybe it
will stay.
=cut
sub update {
my $self = shift;
my $requestedProperties = shift;
my $properties = clone($requestedProperties);
my $attachments = delete $properties->{attachments} || [];
my $removeAttachments = delete $properties->{removeAttachments} || [];
$self->removeAttachments($removeAttachments);
$self->addAttachments($attachments);
if (exists $properties->{headBlock}) {
$properties->{extraHeadTags} .= $properties->{headBlock};
delete $properties->{headBlock};
}
$self->SUPER::update($properties);
}

View file

@ -67,6 +67,30 @@ sub DESTROY {
undef $self;
}
#-------------------------------------------------------------------
sub _generateAdditionalTags {
my $var = shift;
return sub {
my $self = shift;
my $tags = $self->{$var};
delete $self->{$var};
WebGUI::Macro::process($self->session,\$tags);
return $tags;
};
}
#-------------------------------------------------------------------
=head2 generateAdditionalBodyTags ( )
Creates tags that were set using setScript (if inBody was true) and setRawBodyTags.
Macros are processed in the tags if processed by this method.
=cut
BEGIN { *generateAdditionalBodyTags = _generateAdditionalTags('_rawBody') }
#-------------------------------------------------------------------
@ -77,15 +101,7 @@ Macros are processed in the tags if processed by this method.
=cut
sub generateAdditionalHeadTags {
my $self = shift;
my $tags = $self->{_raw};
delete $self->{_raw};
WebGUI::Macro::process($self->session,\$tags);
return $tags;
}
BEGIN { *generateAdditionalHeadTags = _generateAdditionalTags('_raw') }
#-------------------------------------------------------------------
@ -196,10 +212,16 @@ if ($self->session->user->isRegistered || $self->session->setting->get("preventP
} else {
$var{'head.tags'} .= '<meta http-equiv="Cache-Control" content="must-revalidate" />'
}
# Removing the newlines will probably annoy people.
# Perhaps turn it off under debug mode?
$var{'head.tags'} =~ s/\n//g;
# head.tags = head_attachments . body_attachments
# keeping head.tags for backwards compatibility
$var{'head_attachments'} = $var{'head.tags'};
$var{'head.tags'} .= ($var{'body_attachments'} = '<!--morebody-->');
my $style = WebGUI::Asset::Template->new($self->session,$templateId);
my $output;
if (defined $style) {
@ -216,8 +238,10 @@ if ($self->session->user->isRegistered || $self->session->setting->get("preventP
}
WebGUI::Macro::process($self->session,\$output);
$self->sent(1);
my $macroHeadTags = $self->generateAdditionalHeadTags();
$output =~ s/\<\!--morehead--\>/$macroHeadTags/;
my $macroHeadTags = $self->generateAdditionalHeadTags();
my $macroBodyTags = $self->generateAdditionalBodyTags();
$output =~ s/\<\!--morehead--\>/$macroHeadTags/;
$output =~ s/\<\!--morebody--\>/$macroBodyTags/;
return $output;
}
@ -339,7 +363,33 @@ sub setMeta {
$self->setRawHeadTags($tag);
}
#-------------------------------------------------------------------
sub _setRawTags {
my $var = shift;
return sub {
my $self = shift;
my $tags = shift;
if ($self->sent) {
$self->session->output->print($tags);
}
else {
$self->{$var} .= $tags;
}
};
}
#-------------------------------------------------------------------
=head2 setRawBodyTags ( tags )
Does exactly the same thing as setRawHeadTags, except that the tags will be
appended to a seperate variable (to be output after the body if the style
template supports it) instead.
=cut
BEGIN { *setRawBodyTags = _setRawTags('_rawBody') }
#-------------------------------------------------------------------
@ -357,21 +407,11 @@ A raw string containing tags. This is just a raw string so you must actually pas
=cut
sub setRawHeadTags {
my $self = shift;
my $tags = shift;
if ($self->sent) {
$self->session->output->print($tags);
}
else {
$self->{_raw} .= $tags;
}
}
BEGIN { *setRawHeadTags = _setRawTags('_raw') }
#-------------------------------------------------------------------
=head2 setScript ( url, params )
=head2 setScript ( url, params, [inBody] )
Sets a <script> tag into the <head> of this rendered page for this
page view. This is typically used for dynamically adding references
@ -388,12 +428,18 @@ The URL to your script.
A hash reference containing the additional parameters to include in the script tag, such as "type" and "language".
=head3 inBody
Optional, defaults to false. If true, the script will be added to the
body_attachments variable instead of to head_attachments.
=cut
sub setScript {
my $self = shift;
my $url = shift;
my $params = shift;
my $inBody = shift;
return undef if ($self->{_javascript}{$url});
my $tag = '<script src="'.$url.'"';
foreach my $name (keys %{$params}) {
@ -401,7 +447,12 @@ sub setScript {
}
$tag .= '></script>'."\n";
$self->{_javascript}{$url} = 1;
$self->setRawHeadTags($tag);
if ($inBody) {
$self->setRawBodyTags($tag);
}
else {
$self->setRawHeadTags($tag);
}
}
#-------------------------------------------------------------------

View file

@ -298,6 +298,48 @@ Any scratch variables will be available in the template with this syntax:<br/>
context => q{Label for URL to make a duplicate and open the duplicate's edit screen},
},
'attachment header index' => {
message => 'Index',
lastUpdated => 1241192473,
context => q|header for the sequence number column for attachments|,
},
'attachment header type' => {
message => 'Type',
lastUpdated => 1241192473,
context => q|header for the attachment types column|,
},
'attachment header url' => {
message => 'Url',
lastUpdated => 1241192473,
context => q|header for the url column for attachments|,
},
'attachment header remove' => {
message => 'Remove',
lastUpdated => 1241192473,
context => q|header for the remove button column for attachments|,
},
'attachment display label' => {
message => 'Attachments',
lastUpdated => 1241192473,
context => q|field label for displaying existing attachments|,
},
'attachment add field label' => {
message => 'Add Attachments',
lastUpdated => 1241192473,
context => q|field label for adding new attachments|,
},
'attachment add button' => {
message => 'Add',
lastUpdated => 1241192473,
context => q|button text for adding a new attachment|,
},
'usePacked label' => {
message => q{Use Packed Template},
lastUpdated => 0,