diff --git a/docs/upgrades/upgrade_7.9.8-7.9.9.pl b/docs/upgrades/upgrade_7.9.8-7.9.9.pl index 548cf5576..60f6ed5ab 100644 --- a/docs/upgrades/upgrade_7.9.8-7.9.9.pl +++ b/docs/upgrades/upgrade_7.9.8-7.9.9.pl @@ -31,6 +31,7 @@ my $quiet; # this line required my $session = start(); # this line required # upgrade functions go here +migrateAttachmentsToJson( $session ); finish($session); # this line required @@ -44,6 +45,40 @@ finish($session); # this line required # print "DONE!\n" unless $quiet; #} +#---------------------------------------------------------------------------- +# Move Template attachments to JSON collateral +sub migrateAttachmentsToJson { + my $session = shift; + print "\tMoving template attachments to JSON... " unless $quiet; + # and here's our code + $session->db->write( + "ALTER TABLE template ADD attachmentsJson LONGTEXT" + ); + + my $attach; # hashref (template) of hashrefs (revisionDate) + # of arrayrefs (attachments) of hashrefs (attachment) + my $sth = $session->db->read( "SELECT * FROM template_attachments" ); + while ( my $row = $sth->hashRef ) { + push @{ $attach->{ $row->{templateId} }{ $row->{revisionDate} } }, { + url => $row->{url}, + type => $row->{type}, + }; + } + + for my $templateId ( keys %{ $attach } ) { + for my $revisionDate ( keys %{ $attach->{$templateId} } ) { + my $data = $attach->{$templateId}{$revisionDate}; + my $asset = WebGUI::Asset->newByDynamicClass( $session, $templateId, $revisionDate ); + $asset->update({ attachmentsJson => JSON->new->encode( $data ) }); + } + } + + $session->db->write( + "DROP TABLE template_attachments" + ); + + print "DONE!\n" unless $quiet; +} # -------------- DO NOT EDIT BELOW THIS LINE -------------------------------- diff --git a/lib/WebGUI/Asset/Template.pm b/lib/WebGUI/Asset/Template.pm index a55394f0e..a016bdaf5 100644 --- a/lib/WebGUI/Asset/Template.pm +++ b/lib/WebGUI/Asset/Template.pm @@ -207,12 +207,18 @@ If defined, will limit the attachments to this type; e.g., passing sub getAttachments { my ( $self, $type ) = @_; - if ( !$self->{_attachments} ) { - $self->{_attachments} = JSON->new->decode( $self->get('attachmentsJson') ); + + return [] if !$self->get('attachmentsJson'); + + my $attachments = JSON->new->decode( $self->get('attachmentsJson') ); + + # We want it all and we want it now + if ( !$type ) { + return $attachments; } my $output = []; - for my $attach ( @{$self->{_attachments}} ) { + for my $attach ( @{$attachments} ) { if ( $attach->{type} eq $type ) { push @{$output}, $attach; } @@ -293,7 +299,7 @@ sub getEditForm { ); } - $properties->jsonTable( + $tabform->getTab('properties')->jsonTable( name => 'attachmentsJson', value => $self->get('attachmentsJson'), label => $i18n->get("attachments display label"), @@ -317,7 +323,7 @@ sub getEditForm { ], ); - $properties->image( + $tabform->getTab('properties')->image( name => 'storageIdExample', value => $self->getValue('storageIdExample'), label => $i18n->get('field storageIdExample'), @@ -588,6 +594,7 @@ Extends the master class to handle template parsers, namespaces and template att sub processPropertiesFromFormPost { my $self = shift; + my $session = $self->session; $self->SUPER::processPropertiesFromFormPost; # TODO: Perhaps add a way to check template syntax before it blows stuff up? my %data; @@ -610,7 +617,7 @@ sub processPropertiesFromFormPost { } ### Template attachments - $self->update({ attachmentsJson => $f->process( 'attachmentsJson', 'JsonTable' ), }); + $self->update({ attachmentsJson => $session->form->process( 'attachmentsJson', 'JsonTable' ), }); return; } @@ -650,70 +657,6 @@ sub processRaw { #------------------------------------------------------------------- -=head2 purge ( ) - -Extend the master to purge attachments in all revisions. - -=cut - -sub purge { - my $self = shift; - $self->session->db->write('delete from template_attachments where templateId=?', [$self->getId]); - return $self->SUPER::purge(@_); -} - -#------------------------------------------------------------------- - -=head2 purgeRevision ( ) - -Extend the master purgeRevision to purge attachments - -=cut - -sub purgeRevision { - my $self = shift; - $self->removeAttachments; - return $self->SUPER::purgeRevision(@_); -} - -#------------------------------------------------------------------- - -=head2 removeAttachments ( urls ) - -Removes attachments. C is an arrayref of URLs to remove. If C -is not defined, will remove all attachments for this revision. - -=cut - -sub removeAttachments { - my ($self, $urls) = @_; - - my $db = $self->session->db; - my $dbh = $db->dbh; - my $rmsql = qq{ - DELETE FROM - template_attachments - WHERE - templateId = ? - AND revisionDate = ? - }; - - if ( $urls && @{$urls} ) { - my $in = join(',', map { $dbh->quote($_) } @{$urls}); - $rmsql .= qq{ - AND url IN ($in) - }; - } - - my @params = ( - $self->getId, - $self->get('revisionDate'), - ); - $db->write($rmsql, \@params); -} - -#------------------------------------------------------------------- - =head2 update Override update from Asset.pm to handle backwards compatibility with the old diff --git a/lib/WebGUI/Form/JsonTable.pm b/lib/WebGUI/Form/JsonTable.pm new file mode 100644 index 000000000..33ac0cf6a --- /dev/null +++ b/lib/WebGUI/Form/JsonTable.pm @@ -0,0 +1,195 @@ +package WebGUI::Form::JsonTable; + +=head1 LEGAL + + ------------------------------------------------------------------- + WebGUI is Copyright 2001-2009 Plain Black Corporation. + ------------------------------------------------------------------- + Please read the legal notices (docs/legal.txt) and the license + (docs/license.txt) that came with this distribution before using + this software. + ------------------------------------------------------------------- + http://www.plainblack.com info@plainblack.com + ------------------------------------------------------------------- + +=cut + +use strict; +use base 'WebGUI::Form::Control'; +use WebGUI::International; +use JSON; + +=head1 NAME + +Package WebGUI::Form::JsonTable + +=head1 DESCRIPTION + +Creates a table to edit a JSON blob + +=head1 SEE ALSO + +This is a subclass of WebGUI::Form::Control. + +=head1 METHODS + +=cut + +#------------------------------------------------------------------- + +=head2 definition ( [ additionalTerms ] ) + +See the super class for additional details. + +=head3 additionalTerms + +The following additional parameters have been added via this sub class. + +=head4 fields + +An array of hashrefs defining the fields in this JsonTable. + + { + type => "text", # One of "text", "select", or "readonly" + name => "name", # The name of the field + label => "Name", # an i18n label + options => [ option => "label", ... ] # Options for select fields + } + + +=cut + +sub definition { + my $class = shift; + my $session = shift; + my $definition = shift || []; + push @{$definition}, { + fields => { + defaultValue => [], + }, + }; + return $class->SUPER::definition($session, $definition); +} + +#------------------------------------------------------------------- + +=head2 getName ( session ) + +Returns the name of the form control. + +=cut + +sub getName { + my ($class, $session) = @_; + return WebGUI::International->new($session, "Form_JsonTable")->get("topicName"); +} + +#------------------------------------------------------------------- + +=head2 getValue ( value ) + +Get the value of the field. Substitute id fields with GUIDs. + +=cut + +sub getValue { + my ( $self, $value ) = @_; + $value ||= $self->SUPER::getValue; + + $value = JSON->new->decode( $value ); + + for my $row ( @{$value} ) { + for my $field ( @{$self->get('fields')} ) { + if ( $field->{type} eq 'id' && $row->{ $field->{name} } eq "new" ) { + $row->{ $field->{name} } = $self->session->id->generate; + } + } + } + + return JSON->new->encode( $value ); +} + +#------------------------------------------------------------------- + +=head2 toHtml ( ) + +Renders an input tag of type text. + +=cut + +sub toHtml { + my $self = shift; + my $session = $self->session; + my ( $url, $style ) = $session->quick(qw( url style )); + my $value = $self->fixMacros($self->fixQuotes($self->fixSpecialCharacters($self->getOriginalValue))); + my $output = ''; + + # Table headers + $output .= ''; + for my $field ( @{ $self->get('fields') } ) { + $output .= ''; + } + $output .= ''; # Extra column for buttons + + # Buttons to add rows in the table footer + my $cols = scalar @{ $self->get('fields') } + 1; # Extra column for buttons + $output .= '' + ; + + # Build a hidden row to copy for new rows + $output .= ''; + for my $field ( @{ $self->get('fields') } ) { + my $fieldName = join "_", $self->get('name'), $field->{name}; + # Drawing using raw HTML to sanitize field HTML and allow future merging with DataTable + my $fieldHtml; + + if ( $field->{type} eq "text" ) { + $fieldHtml = ''; + } + elsif ( $field->{type} eq "select" ) { + $fieldHtml = ''; + } + elsif ( $field->{type} eq "id" ) { + $fieldHtml .= ''; + } + else { # Readonly or unknown + $fieldHtml = ''; + } + + $output .= ''; + } + + $output .= '
' . $field->{label} . ' 
' + . '' + . '
'; + + # Build the existing rows + $output .= ''; + + # Existing rows are entirely built in javascript from the JSON in the hidden field + $style->setScript( + $url->extras('yui/build/yahoo-dom-event/yahoo-dom-event.js'), + { type => 'text/javascript' }, + ); + $style->setScript( + $url->extras('yui/build/json/json-min.js'), + { type => 'text/javascript' }, + ); + $output .= sprintf '', + $url->extras('yui-webgui/build/form/jsontable.js'); + $output .= ''; + + return $output; +} + +1; +#vim:ft=perl diff --git a/t/Asset/Template.t b/t/Asset/Template.t index 3f3c0f05b..bfbc0ebb3 100644 --- a/t/Asset/Template.t +++ b/t/Asset/Template.t @@ -18,6 +18,7 @@ use WebGUI::Asset::Template; use Exception::Class; use Test::More tests => 44; # increment this value for each test you create use Test::Deep; +use Data::Dumper; use JSON qw{ from_json }; my $session = WebGUI::Test->session; @@ -82,13 +83,13 @@ ok(!$template3->get('headBlock'), 'headBlock is empty'); is($template3->get('extraHeadTags'), 'tag1 tag2 tag3', 'extraHeadTags contains headBlock info'); my @atts = ( - {type => 'headScript', sequence => 1, url => 'bar'}, - {type => 'headScript', sequence => 0, url => 'foo'}, - {type => 'stylesheet', sequence => 0, url => 'style'}, - {type => 'bodyScript', sequence => 0, url => 'body'}, + {type => 'headScript', url => 'foo'}, + {type => 'headScript', url => 'bar'}, + {type => 'stylesheet', url => 'style'}, + {type => 'bodyScript', url => 'body'}, ); -$template3->addAttachments(\@atts); +$template3->update({ attachmentsJson => JSON->new->encode( \@atts ) }); my $att3 = $template3->getAttachments('headScript'); is($att3->[0]->{url}, 'foo', 'has foo'); is($att3->[1]->{url}, 'bar', 'has bar'); @@ -104,12 +105,12 @@ ok(exists $session->style->{_javascript}->{$_}, "$_ in style") for qw(foo bar bo #sleep 1; my $template3dup = $template3->duplicate; -my @atts3dup = map { delete @{ $_ }{qw/attachId templateId revisionDate/}; $_; } @{ $template3dup->getAttachments }; +my @atts3dup = @{ $template3dup->getAttachments }; cmp_bag( [@atts3dup], [@atts], 'attachments are duplicated' -); +) or diag( Dumper \@atts3dup ); my $template3rev = $template3->addRevision(); my $att4 = $template3rev->getAttachments('headScript'); @@ -117,7 +118,9 @@ is($att4->[0]->{url}, 'foo', 'rev has foo'); is($att4->[1]->{url}, 'bar', 'rev has bar'); is(@$att4, 2, 'rev is proper size'); -$template3rev->addAttachments([{type => 'headScript', sequence => 2, url => 'baz'}]); +$template3rev->update({ + attachmentsJson => JSON->new->encode([ @atts, {type => 'headScript', url => 'baz'} ]), +}); $att4 = $template3rev->getAttachments('headScript'); $att3 = $template3->getAttachments('headScript'); is($att3->[0]->{url}, 'foo', 'original still has foo'); @@ -126,13 +129,9 @@ is(@$att3, 2, 'original does not have new thing'); is($att4->[0]->{url}, 'foo', 'rev still has foo'); is($att4->[1]->{url}, 'bar', 'rev still has bar'); -is($att4->[2]->{url}, 'baz', 'rev does have new thing'); +is($att4->[2]->{url}, 'baz', 'rev does have new thing') or diag( $template3rev->get('attachmentsJson') ); is(@$att4, 3, 'rev is proper size'); -##This is a non-test. Duplicate URLs will not cause the test to blow-up with -##an untrappable error. -$template3rev->addAttachments([{ type => 'headScript', sequence => 3, url => 'baz'}]); - $template3rev->purgeRevision(); ## Check how templates in the trash and clipboard are handled.