From fa04ab1288fac056e0fc431d59c8a8e1b454f0ea Mon Sep 17 00:00:00 2001 From: Patrick Donelan Date: Tue, 3 Mar 2009 10:58:32 +1100 Subject: [PATCH 01/38] Added Build.pm macro, which will be completely re-written into FilePump --- lib/WebGUI/Macro/Build.pm | 478 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 478 insertions(+) create mode 100644 lib/WebGUI/Macro/Build.pm diff --git a/lib/WebGUI/Macro/Build.pm b/lib/WebGUI/Macro/Build.pm new file mode 100644 index 000000000..834d57863 --- /dev/null +++ b/lib/WebGUI/Macro/Build.pm @@ -0,0 +1,478 @@ +package WebGUI::Macro::Build; + +=head1 LEGAL + + ------------------------------------------------------------------- + (c) Patrick Donelan + ------------------------------------------------------------------- + http://patspam.com pat@patspam.com + ------------------------------------------------------------------- + +=cut + +use strict; +use Readonly; +use File::Assets; +use CSS::Minifier::XS; # implicit +use JavaScript::Minifier::XS; # implicit +use File::Slurp qw(read_file write_file); +use Digest::SHA1 qw(sha1_hex); +use JSON; + +Readonly my $STATIC => 'static'; # Source files come from uploads/$STATIC +Readonly my $MINIFIED => 'minified'; # Build dir is uploads/$MINIFIED +Readonly my $ASSETS => 'assets'; # Built JS/CSS files are called $ASSETS.js/css + +=head1 NAME + +Package WebGUI::Macro::Minify + +=head2 DESCRIPTION + +Build tool for maximising YSlow score. CSS and JS are minified into files called assets.css and assets.js in the build dir. + +=head3 FOLDERS + +Build dir lives under /data/domains/site.com/uploads/$STATIC (so that we don't clash with any existing urls). +Normally this would be a symlink to your custom static folder. +You can create a "frozen" folder inside $STATIC and modify site.com.modproxy.conf to give it a far-future expiry: + + ExpiresActive On + ExpiresDefault "access plus 10 years" + +Any files in this folder should be revved. + +You will need to manually build mod_expires and modify modproxy.conf to load the module: + LoadModule expires_module modules/mod_expires.so + +$MINIFIED folder gets created automatically (it lives under /uploads too). +You should also give it a far-future expiry: + + ExpiresActive On + ExpiresDefault "access plus 10 years" + + +To further maximise your YSlow score, you should make sure modproxy.conf and modperl.conf both contain: + LoadModule deflate_module modules/mod_deflate.so + AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css text/javascript application/x-javascript application/x-shockwave-flash application/javascript + FileETag none + +=head3 MODES + +Modes can be specified either as first argument to macro, or via url as ?build=mode. +Expects unmixed args, e.g. only css, only js or all generic assets (e.g. images). +Assets are symlinked to build dir. They can be specified as file globs (e.g. 'subfolder/*.png') +or as entire dirs (e.g. 'subfolder/subsubfolder'). +Digest cached in wg settings table (see get_digest_field_name). +Description of modes: + +=head4 default mode +js: re-use previously built $ASSETS.js and cached digest +css: re-use previously built $ASSETS.css and cached digest +images: re-use symlinked images + +=head4 debug mode +js: concat into $ASSETS.js and update digest +css: use original css +images: symlink images + +=head4 min mode +js: minify into $ASSETS.js and update digest +css: minify into $ASSETS.css, rewrite CSS image urls and update digest +images: symlink images + +=head3 JS Widget Best Practices + +Each widget should be in a sub-dir with js, css and images +CSS should use local relative path to images +Images should use unique prefix so that they don't clash with other images when copied to minified folder + +=cut + +#------------------------------------------------------------------- +sub process { + my ( $session, @args ) = @_; + + if ( !@args ) { + $session->log->warn('Minify: no args, skipping'); + return; + } + + my ( $mode, $type, @files ) = process_macro_args( $session, @args ); + + if ( $type eq 'js' ) { + return handle_js( $session, $mode, @files ); + } + elsif ( $type eq 'css' ) { + return handle_css( $session, $mode, @files ); + } + else { + return handle_assets( $session, $mode, @files ); + } +} + +#------------------------------------------------------------------- + +=head2 process_macro_args +Figures out what to do based on the macro args, form params etc.. +=cut + +sub process_macro_args { + my ( $session, @args ) = @_; + + # Trim whitespace from Macro arguments + map {s/^\s+|\s$//g} @args; + + # Get mode and remove any mode-related arguments from @args.. + my $form_param = $session->form->param('build') || q{}; + my $mode = 'default'; + foreach my $valid_mode (qw(min debug)) { + if ( $args[0] eq $valid_mode ) { + $mode = $valid_mode; + shift @args; + } + elsif ( $form_param eq $valid_mode ) { + $mode = $valid_mode; + } + } + my $type = get_type(@args); + + $session->log->debug( "Minify: $mode mode for $type on " . @args . ' files' ); + + return ( $mode, $type, @args ); +} + +#------------------------------------------------------------------- + +=head2 get_type +Guesses file type (css or js). Anything else (e.g. images) is returns undef. +=cut + +sub get_type { + my @files = @_; + foreach my $file (@files) { + return 'css' if $file =~ /\.css$/i; + return 'js' if $file =~ /\.js$/i; + } + return; +} + +#------------------------------------------------------------------- + +=head2 get_digest_field_name ($type) +Generates the field name used to store the cached digest in the settings table +=cut + +sub get_digest_field_name { + my ($type) = @_; + return "minify_digest_$type"; +} + +#------------------------------------------------------------------- + +=head2 get_digest +Pulls a previously stored digest out of the wg db +=cut + +sub get_digest { + my ( $session, $type ) = @_; + my $digest = $session->db->quickScalar( 'select value from settings where name = ?', + [ get_digest_field_name($type) ] ); + return $digest; +} + +#------------------------------------------------------------------- + +=head2 handle_assets +Assets are symlinked to build dir. They can be specified as file globs (e.g. 'subfolder/*.png') +or as entire dirs (e.g. 'subfolder/subsubfolder'). +=cut + +sub handle_assets { + my ( $session, $mode, @files ) = @_; + + # Check if we need to do anything extra for mode.. + if ( $mode ne 'default' ) { + + # N.B. Not needed for CSS images in debug mode, but possibly for js and other things + link_assets( $session, @files ); + } + + return; # no output needed for assets +} + +#------------------------------------------------------------------- + +=head2 link_assets (@files) +Symlink files to the build dir +=cut + +sub link_assets { + my ( $session, @files ) = @_; + my $base_dir = $session->config->get('uploadsPath'); + my $build_dir = "$base_dir/$MINIFIED"; + + my $counter = 0; + foreach my $file (@files) { + $file =~ s{^[/.]*}{}; # disallow absolute paths + $file =~ s{\.\.}{}g; # disallow tree-traversal + if ( $file =~ /\*/ ) { + $session->log->debug("Processing file glob: $file"); + while (my $asset = <$base_dir/$STATIC/$file>) { # probably a security hole + my $src = $asset; + $asset =~ s{.*/}{}; # remove subdirs from path + my $dest = "$build_dir/$asset"; + create_link( $session, $src, $dest ); + $counter++; + } + } + else { + $session->log->debug("Processing asset: $file"); + my $src = "$base_dir/$STATIC/$file"; + $file =~ s{.*/}{}; # remove subdirs from path + my $dest = "$build_dir/$file"; + create_link( $session, $src, $dest ); + $counter++; + } + } + $session->log->debug("Minify: linked $counter assets to: $build_dir"); + return; +} + +#------------------------------------------------------------------- + +=head2 create_link ($src, $dest) +Create a single symlink from $src to $dest +=cut + +sub create_link { + my ( $session, $src, $dest ) = @_; + if ( -e $dest && !-l $dest ) { + $session->log->error("Destination file exists but is not a symlink: $dest"); + return; + } + else { + remove( $session, $dest ); + + if ( symlink $src, $dest ) { + $session->log->debug("Symlinked $src to $dest"); + return 1; + } + else { + $session->log->error("Unable to symlink $src to $dest: $!"); + return; + } + } +} + +#------------------------------------------------------------------- + +=head2 handle_css ($mode, @files) +In debug mode, write out link tags to original (untouched) css files. +In min mode, minify css to $ASSETS.css, rewrite CSS image urls and update digest. +In default mode, pluck the digest out of the db and do no work. + +This call to the macro should be placed in the HEAD of the document. +=cut + +sub handle_css { + my ( $session, $mode, @files ) = @_; + + # Check if we need to minify.. + if ( $mode eq 'min' ) { + minify( $session, $mode, 'css', @files ); + } + + my $base_uri = $session->config->get('uploadsURL'); + + my $output = q{}; + if ( $mode eq 'debug' ) { + + # Use original css in debug mode + foreach my $file (@files) { + my $original_file_uri = "$base_uri/$STATIC/$file"; + $output .= qq~\n~; + } + } + else { + + # Use minified css in both 'min' and 'default' modes + my $digest = get_digest( $session, 'css' ); + my $asset_uri = "$base_uri/$MINIFIED/$ASSETS.css?digest=$digest"; + $output = qq~~; + } + return $output; +} + +#---------------------------------------------------------------------------- + +=head2 handle_js + +In debug mode, concat js files into $ASSETS.js and update the digest. +In min mode, minify js files into $ASSETS.js and update the digest. +In default mode, pluck the digest out of the db and do no work. + +This call to the macro should be placed in the bottom of the BODY of the document + +=cut + +sub handle_js { + my ( $session, $mode, @files ) = @_; + + # Check if we need to minify + if ( $mode ne 'default' ) { # Minify in both 'min' and 'debug' modes + minify( $session, $mode, 'js', @files ); + } + + my $base_uri = $session->config->get('uploadsURL'); + my $digest = get_digest( $session, 'js' ); + my $asset_uri = "$base_uri/$MINIFIED/$ASSETS.js?digest=$digest"; + + return qq~~; +} + +#---------------------------------------------------------------------------- + +=head2 minify +Minify js or css. Valid modes are 'debug' or 'min': +In min mode: +* js: minify into $ASSETS.js +* css: minify into $ASSETS.css +In debug mode: +* js: concat into $ASSETS.js +* css: invalid mode +=cut + +sub minify { + my ( $session, $mode, $type, @files ) = @_; + + # Only handle js and css + if ( $type ne 'js' && $type ne 'css' ) { + $session->log->error('Invalid type, skipping'); + return; + } + + # Valid modes for js: 'debug' and 'min' + if ( $type eq 'js' && $mode ne 'debug' && $mode ne 'min' ) { + $session->log->error("Invalid mode for $type: $mode"); + return; + } + + # Valid modes for css: 'min' + if ( $type eq 'css' && $mode ne 'min' ) { + $session->log->error("Invalid mode for $type: $mode"); + return; + } + + my $base_dir = $session->config->get('uploadsPath'); + my $base_uri = $session->config->get('uploadsURL'); + my $output_path = "$MINIFIED/$ASSETS"; + my $asset_path = "$base_dir/$output_path.$type"; + + if ( !@files ) { + $session->log->error('Minify: No files to process, skipping'); + return; + } + + $session->log->debug("minify $mode mode for $type"); + + # Start with a clean slate + remove( $session, $asset_path ); + + my $concat = concat( $base_dir, @files ); + my $digest = sha1_hex($concat); + update_digest( $session, $digest, $type ); + + if ( $mode eq 'debug' ) { # only applies to js + write_file( $asset_path, $concat ); + } + else { + my $assets = File::Assets->new( + base => { + dir => $base_dir, + uri => $base_uri, + }, + minify => 'xs', + ); + + # Built files go here (if we build at all) + $assets->set_output_path($output_path); + + # Process the files.. + foreach my $file (@files) { + $assets->include("$STATIC/$file"); + } + $assets->export(); + } + + if ( $type eq 'css' ) { + rewrite_image_urls( $session, $asset_path, $digest ); + } + + $session->log->debug( "Minify: ${mode}'d " . @files . " assets to: $asset_path ($digest)" ); + + return; +} + +#---------------------------------------------------------------------------- + +=head2 rewrite_image_urls ( $session, $asset_path, $digest ) +Append digest query string to the end of all relative CSS image urls in the CSS file specified at $asset_path +=cut + +sub rewrite_image_urls { + my ( $session, $asset_path, $digest ) = @_; + $session->log->debug('Adding digest to CSS image urls'); + my $content = read_file($asset_path) or $session->log->warn("rewrite_image_urls unable to read $asset_path: $!"); + $content =~ s{url\(([^/][^)]*)\)}{url($1?digest=$digest)}ig; + write_file( $asset_path, $content ) or $session->log->warn("rewrite_image_urls unable to write $asset_path: $!"); + return; +} + +#---------------------------------------------------------------------------- + +=head2 remove ($session, $file) +Unlink file if it exists +=cut + +sub remove { + my ( $session, $file ) = @_; + if ( -e $file ) { + $session->log->debug("Removing file: $file"); + unlink $file or $session->log->warn("Error removing $file: $!"); + } + return; +} + +#---------------------------------------------------------------------------- + +=head2 update_digest ($session, $digest, $type) +Update the cached digest in the db +=cut + +sub update_digest { + my ( $session, $digest, $type ) = @_; + my $field_name = get_digest_field_name($type); + $session->db->write( 'delete from settings where name = ?', [$field_name] ); + $session->db->write( 'insert into settings (name, value) values (?,?)', [ $field_name, $digest ] ); + $session->log->debug("Minify: Set digest: $digest"); + return; +} + +#---------------------------------------------------------------------------- + +=head2 concat ( $base_dir, @files) +Concatenate the specified @files +=cut + +sub concat { + my ( $base_dir, @files ) = @_; + my @output; + foreach my $file (@files) { + my $slurped = read_file("$base_dir/$STATIC/$file"); + push @output, $slurped; + } + return join( "\n", @output ) . "\n"; +} + +1; From 3f10db18467c13dc6553ae5e5b8fe360675be600 Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Tue, 28 Apr 2009 18:58:28 -0700 Subject: [PATCH 02/38] syncing with patspam's filepump branch. --- .../default-shopping-cart-template.wgpkg | Bin 2352 -> 0 bytes ...t-webgui-account-activation-template.wgpkg | Bin 823 -> 0 bytes ...root_import_survey_default-questions.wgpkg | Bin 2666 -> 2628 bytes ...ot_import_survey_default-survey-edit.wgpkg | Bin 1670 -> 1593 bytes www/extras/VendorPayout/vendorPayout.js | 10 +++++----- 5 files changed, 5 insertions(+), 5 deletions(-) delete mode 100644 docs/upgrades/packages-7.7.0/default-shopping-cart-template.wgpkg delete mode 100644 docs/upgrades/packages-7.7.0/root_import_auth_webgui_create_default-webgui-account-activation-template.wgpkg diff --git a/docs/upgrades/packages-7.7.0/default-shopping-cart-template.wgpkg b/docs/upgrades/packages-7.7.0/default-shopping-cart-template.wgpkg deleted file mode 100644 index ff866b14683bf3f2dc4df77a73006c64e801cdff..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2352 zcmV-03D5Q)iwFP!000001MOP-PaC-w=I8w@MnSqNQUeJgL>ah|SSYvX_CZ_NYWHRr zIWtb;GGk9|50I|vlIbTCH}k z+uGRZ^vowHKCO**yS?9P?|0k1&PLE~wY$BI0A6|ePc9_l;982fB=r9FV3P1rlQjR{ zU(?_EruMd7{_gDQ@B733+JB*idPnbi6lSw2dLnBus6m*U&cgvRVvD{YknVn$ge$hDxo&$fbay!Nembf|{85a^tpcVICFA9V?=1DNrAiM)q`Hoqnet7KQ< zpn^vz^qN{(1HYIMs$x4o|I)=!u!O{cAju81hIS=aR#_E!3I=9#R_C%h*c^A%kC)a% z=^6(#4d7m;d+0Bo)Kcr)&h|D=GdZgUe%?MbXDTwpF|9T)i-B&`A!XFB{55RbnptK& zbn^~!p1k<&lqC2PzPO&>jG*bOo|3>S#Zl6P&XbBAS2^2XvdPxLRsjDGR22u5H95{; zCH!ju!TUN%AKy!zt-hQ3!F~NKo66&Q>U9|Bt9kSwRK4nd5_c=uT7Mq`uYffos!sh-TzqSPxh-X(oHYTA@fhWaY*s58syI_6UCI;j zn#3`t`lYu2{G2n%6ho|&P{la1v-nj^WSwgk`V|>Ilwj zKLWmKWNr|5LOUQeINY(X8a@gfrqGoFX#!?m)n*@7^c8|DH7d(v##6D zAj4Rl9p^$3BZyXPklV3ST{-1N(nVgFJ%B|{Bc_})>J5OpH~_v{4}e#GJEO2zQ6veY zD0!iltd|P6t%ItHx?sjhgx8Y%nj;8@oUJ7cI}=P}90yDF+wxctL${xTjG6Nmc#xV} z;F#qCXNu4$JurEf*veqSVA0mgxy^QnuPsWw>nSSj>gxT;$<@^WHV2@>_&V?-Q@*E3 zekGkbPT5V7&g(|dOF2fzWRi0nUl5rcfCC-Q=%n5(L_UHw93@4Muwr#6ibZL`92YcP&B_!S#S-BREhqz6^ZudPOLX_- z69*g15=(FY{G55DV0#=whiggbEiI@c5O_gb-ZBmqNeNu&^?{#0((7k;Fe9(p%M7T| z5GMn47F4-%u9cvlcZD@?W!H&z09$_v-6pXIeNeET57p_Y#`Zo_3_vU~d7oSaGN@0L zfZP|B|GdL2D@p>u0xb#P=olz;Jw8Qgy2~JKXdzJbu)L^sYIK80;x*~ei#?%=8XapF zj-1?JtFUgGpG$zbkf^jSLhtK3!Eqib{2!$%)Cq&yQau9dPud4vTP+~b)?71mqqXE; zBNHu!NC_oro~lj7Q3}VsQs*|X?4q^aXZt)oulaNj19%)eJ2oId31=$wxL)Uqu=HW}d>rE@!jSW5##|X$>u{-s4lY_%ieZ@C1 zf-P;)S4MLoC<5Ot${4tE#)P>*@+#XtqbLP#t2re!2)h+1>mQ`vFm|11u-QyYYyNV# z&V&oiYUxU{DPyp^H8=u3-boFeVooF+-=1=op6iq3 z`jJfnJ=ET3Hp;v0mcHk9H{7L0&DY*R%(a@&c=VskvoJhTr%+*dF?WQduQHFPh)$N> zgT7@k856kSRL4T?-A=F5>+g5G%Tm}>C&rq-ZG3TJPOr8~pX{O2PXwDw(Ox-3ggS~+ z>zvVFj)-fLy<-w=NBUHt4oiw@uJ@OEV3}z(zuerM!oG*YzSg^{)n{{`;I>n%*za_^ zrFl5jhZ|Wvumr>~e0}lu%{-(83M#sGa8@pkiLl_*C*y}fkM;Y#e!tt>_3n>3z99k@ z=M#7NTm)mcbeo8CxL0)P;++WsYq>q6s>lr*-jM1L>Nw26ps2-;g0obLX~bulJa=k6 zrzD!#!_AN4tl2-pA9D2mRdV?u{W+cDw;xI+unV3EIQytiT~EuQYUnHYMaiLW z?yFd|`u*K@yYJbzaNe52=DgXduorIPE diff --git a/docs/upgrades/packages-7.7.0/root_import_auth_webgui_create_default-webgui-account-activation-template.wgpkg b/docs/upgrades/packages-7.7.0/root_import_auth_webgui_create_default-webgui-account-activation-template.wgpkg deleted file mode 100644 index a24ae78afc6d5b9cf0e71ccd72689030c74fe883..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 823 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 Y;GZP$B!MRhe8m#@FHr}0x&Sl)05O6F>;M1& diff --git a/docs/upgrades/packages-7.7.0/root_import_survey_default-survey-edit.wgpkg b/docs/upgrades/packages-7.7.0/root_import_survey_default-survey-edit.wgpkg index 844aafa6e30eb799a5af9d04be33f3991a791dea..1b2f5d2bece02739acac5e2ae9d93709292311e7 100644 GIT binary patch literal 1593 zcmV-92FCdxiwFP!000001MOH_a~d}k_H%xP$^%Yj4C1z?VB(D9OU%@vNo*(S)J;b0 z3Q*XUHj)-X(){-xNxSSC?53Tdy9`TCLXhyq%p|t={%(`7c^N zwViss?s@xOquKO!5PaA7cMvSu{*x$8B#0Ihtzr`0%kW2ZP!b-aocp~O=>J8q($@2L zvtz#Nt?DNw_=ZFU!2MP$_j?ux&i!>uyzPIl)~M|uu;%ukZTY{tebG0;;oBqdLP=>M z$(WW<7ybUno*jy%h^f?!Dl=-QnVBI``t(aIq^7}^b{ZEHV(Od)0p+Xa8B3AG{ovH_w3ch+aGFMOm|3qk0i_RbxXGPf zL^VMZt>X&)oUqx`o6@o1nsQya0^T&|on<(NTDS{0Whu%+o}VDnH+p4pEAx znGWgUJ34%E-A5n#03jb9V58{{z&Gb$%Ej5Ey4K&dN!s#MT#5GVnSDTj#mVW;TYVC-T@r5O#){!v|! zi4av^MA*21z=q<7RN;wG8vKo)kwgoi#v#?TJWR9}ylkSDFi7EYKr~Szkv=V(^Qat( z@fe09%9FCBihZSJ1xXxIqr8yiM6-~YJ*)h(KPYp9JOm(Asbt<&zGSh!6)>O3jJRP4 z110@C!)A$$mtSy1n#eiqVYm9fvdERi`KOK14Kpg*92fs=gW1xVc=@#rHYOynd2IvS z#U*SYCOo&nzyvoouWf+4q`~fI8w^|~V)wHR1}?jCG1dQuO}at-yG&o%QiEVK*}Vd5 z6mjLnmf<1UqG;PZ+$ppfF8Oh@zS;F~TJ+rTo~;~ws6rKN(+JL#iaBK~aLC#MBlSH& z9BLJq!GfNK^Wo!+NMujtJ`_(S#DaTiu$`0JMMuD9*?{grz2z#4D)3t=l3JbSo-3bB zVMGqmpH7?`?Sx<>q^MF*QB^^nDhZ(e_>}|pjfN>phL%nAc{0^EoL_35RP$DH>t_=Cx>6ABfM;Qws)5m$2x2DuBlS1@^ICs1-tQy44Q zxwy(|YC}E^s*DKvgiS&n=FJA<6Dpa$mf@Kc(JOE!cO6NgiGd24rrlyGzd5McTZHWX zAV;*F-|>X-@v<*0^kX)1Ok-i&9+y4cu8qHL_bTgrmTjbIKVOP~jTrQgu2E|4x4pX8 zDD;|XGxe5H*HE@|VQjZz7oNCo54ns$r;u*>G}u$(fzZIlw;hmUmD8Cr^NqPzDRz^& zgu>JknvG5;Uu7(j*lx8%V(71D#U9wyE?1@l`>}Mf>EEQCcaOok-SexL7whI@>O%O_ z%n!FZccxN{SloiQi~h5;PN&`Jc-*5 literal 1670 zcmV;126_1(iwFP!000001MOICa~d}g_UHVHD<5z&<3nH|6nt^UP3*)I+jU~QNv3W* z;!c2qPP#}s2ubtbyOOvUz;4RVo;QE$QvmTdl)Dvc$mh7%l)$ldb3M>odR=f;`$|Kw-2ZWPaE z*M7HZAC=&1vH>_gZnX-(8wn;3ockM%W~aU6|5mfv-17bv6gTDn+M2-IFq^LrzzY?n zg~UTr@p|5`zwOweM2dt+O^Gt2cAA+PN`f_sewvxlO0!}o8v?^hAAU}R1nPlZ!&Am` zGGv*y{3}V?XH<`j+xYOfA!TK~OU;5=B?pi76-YN)dS+Eh|DkYl&YH99`n! z>O!m((ep(w-dvp>g_=cILy`EO-khZ3=J`P(J(bKdmqKVnV`Cdqshp4*ipT(`O#AMy zPa>*|byU5iQAD_1+$fKqOQD6HB@T&_p+hOsEy|E<(c4`aP41$lA$!-_C6c~E6K5@f@Aj3LU)E^vU6ZSyt%MYJlYG8Ta_`%-ht9HMm%F~ zCO%XO#AOqV1$`0Cysuu2<&g4Tea}m96j44jpWm{H0kW*}h50z6eC$cWfWMsqVMR1p zy&}^aNiu+09}Z1)sE4zZqJBzQga!bRa!)^|yap;h+5;9Smlmu+ip5SLHKrV%IJRGF zl{FRpk0jK{d1W4_CN9@1Wh5p&T1L=wUw4MNyMdNxRV2oh&P znAursGdvQa7K#`dXJFWBLPiuC38lgR=qXOM0BXXBCe?nbwcu3~&6q;!S0k*k5~&PH z)tppSCWb>8jtNhzk|_FxR28HwBL)H?tEr}pn%%5=dVE&phI1bndA?uxS%s1&dLei| z;t6)c5E4p;w}w#88|7bce3~fQhsd=||GALDU{rkCh+Vs=GV-YWXEWk*r=t98GlEQT zAoJJ^dl#dyt(Z{Vj0h9z$UHW~-X#%oug!>Xxry9sGa_8ZqjLKHsi1u0`g{4mwBbh4 zx^jMru`$P$852^hzlm*P&Ub;&7?q}w*a0ObXf@pEA7jSz}e1zWiXCy#H=WFL;? zd0~=ALqsPYjbK)c)!4_3EmST+PG$m|g2gw}?2ZqVe7lDdZF%AXn>jsGDB}giFby%B zTT7EY@5D)~)oPidwhlx~LKprA`9HHJTNhxDLoo*i(kReyRNhRm^lYJW3>9X@8Gkzs z&NZCdm@_QvaIJy=yy&Wd6-%{btJ~ahRi>#Q@xJ$a#swByW$3_DL@6jeYoJ#RM^K#o z%&pLCUFQCDkn-?}$&ytu?N0k{F=#HQFr=ij)V5G8@^k+XMc~tJp%U# Q{Ff2<1E@p;Hvk*}0Q#*jJ^%m! diff --git a/www/extras/VendorPayout/vendorPayout.js b/www/extras/VendorPayout/vendorPayout.js index 3690c58a1..8b01d3359 100644 --- a/www/extras/VendorPayout/vendorPayout.js +++ b/www/extras/VendorPayout/vendorPayout.js @@ -19,7 +19,7 @@ WebGUI.VendorPayout = function ( containerId ) { // Submit button this.submitPayoutsButton = new YAHOO.widget.Button({ label: 'Submit Scheduled Payouts', container: this.buttonDiv }); this.submitPayoutsButton.on( 'click', function () { - YAHOO.util.Connect.asyncRequest( 'GET', '?shop=vendor;method=submitScheduledPayouts', { + YAHOO.util.Connect.asyncRequest( 'GET', '/?shop=vendor;method=submitScheduledPayouts', { success: obj.initialize, scope: obj } ); @@ -30,7 +30,7 @@ WebGUI.VendorPayout = function ( containerId ) { this.container.appendChild( this.payoutDetails ); - this.itemBaseUrl = '?shop=vendor;method=payoutDataAsJSON;'; + this.itemBaseUrl = '/?shop=vendor;method=payoutDataAsJSON;'; // Initialise tables this.initialize(); @@ -56,7 +56,7 @@ WebGUI.VendorPayout.prototype.initVendorList = function () { ]; // setup data source - var url = '?shop=vendor;method=vendorTotalsAsJSON;'; + var url = '/?shop=vendor;method=vendorTotalsAsJSON;'; this.vendorDataSource = new YAHOO.util.DataSource( url ); this.vendorDataSource.responseType = YAHOO.util.DataSource.TYPE_JSON; this.vendorDataSource.responseSchema = { @@ -186,7 +186,7 @@ WebGUI.VendorPayout.prototype.initPayoutDetails = function () { }; var status = record.getData( 'vendorPayoutStatus' ) === 'NotPaid' ? 'Scheduled' : 'NotPaid'; - var url = '?shop=vendor;method=setPayoutStatus' + ';itemId=' + record.getData( 'itemId' ) + ';status=' + status; + var url = '/?shop=vendor;method=setPayoutStatus' + ';itemId=' + record.getData( 'itemId' ) + ';status=' + status; YAHOO.util.Connect.asyncRequest( 'post', url, callback ); } ); } @@ -204,7 +204,7 @@ WebGUI.VendorPayout.prototype.initButtons = function () { } var postdata = itemIds.join('&'); - var url = '?shop=vendor&method=setPayoutStatus&status=' + status; + var url = '/?shop=vendor&method=setPayoutStatus&status=' + status; var callback = { success: function (o) { this.refreshItemDataTable(); From 821ac7fd10c6e5c5681395055f5de264fa07efbd Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Tue, 28 Apr 2009 21:01:38 -0700 Subject: [PATCH 03/38] Adding install script and base module with i18n. --- lib/WebGUI/FilePump/Bundle.pm | 486 ++++++++++++++++++++++++++++ lib/WebGUI/i18n/English/FilePump.pm | 27 ++ sbin/installFilePump.pl | 198 ++++++++++++ 3 files changed, 711 insertions(+) create mode 100644 lib/WebGUI/FilePump/Bundle.pm create mode 100644 lib/WebGUI/i18n/English/FilePump.pm create mode 100644 sbin/installFilePump.pl diff --git a/lib/WebGUI/FilePump/Bundle.pm b/lib/WebGUI/FilePump/Bundle.pm new file mode 100644 index 000000000..cef4ee01d --- /dev/null +++ b/lib/WebGUI/FilePump/Bundle.pm @@ -0,0 +1,486 @@ +package FilePump::Bundle; + +use base qw/WebGUI::Crud/; +use WebGUI::International; + +#------------------------------------------------------------------- + +=head2 addCssFile ( $uri ) + +Adds a CSS file to the bundle. Returns 1 if the add was successful. +Otherwise, returns 0 and an error message as to why it was not successful. + +=head3 $uri + +A URI to the new file to add. + +=cut + +sub addCssFile { + my ($self, $uri) = @_; + return 0, 'No URI' unless $uri; + $self->setCollateral( + 'cssFiles', + 'fileId', + 'new', + { + uri => $uri, + lastModified => 0, + }, + ); +} + +#------------------------------------------------------------------- + +=head2 addJsFile ( $uri ) + +Adds a javascript file to the bundle. Returns 1 if the add was successful. +Otherwise, returns 0 and an error message as to why it was not successful. + +=head3 $uri + +A URI to the new file to add. + +=cut + +sub addJsFile { + my ($self, $uri) = @_; + return 0, 'No URI' unless $uri; + $self->setCollateral( + 'jsFiles', + 'fileId', + 'new', + { + uri => $uri, + lastModified => 0, + }, + ); +} + +#------------------------------------------------------------------- + +=head2 addOtherFile ( $uri ) + +Adds an Other file to the bundle. Returns 1 if the add was successful. +Otherwise, returns 0 and an error message as to why it was not successful. + +=head3 $uri + +A URI to the new file to add. + +=cut + +sub addOtherFile { + my ($self, $uri) = @_; + return 0, 'No URI' unless $uri; + $self->setCollateral( + 'otherFiles', + 'fileId', + 'new', + { + uri => $uri, + lastModified => 0, + }, + ); +} + +#------------------------------------------------------------------- + +=head2 crud_definition + +WebGUI::Crud definition for this class. + +=head3 tableName + +filePumpBundle + +=head3 tableKey + +bundleId + +=head3 sequenceKey + +None. Bundles have no sequence amongst themselves. + +=head3 properties + +=head4 bundleName + +The name of a bundle + +=head4 lastBuild + +The date the bundle was last built + +=head4 jsFiles, cssFiles, otherFiles + +JSON blobs with files attached to the bundle. js = javascript, css = Cascading Style Sheets, other +means anything else. + +=cut + +sub crud_definition { + my ($class, $session) = @_; + my $definition = $class->SUPER::crud_definition($session); + my $i18n = WebGUI::International->new($session, 'FilePump'); + $definition->{tableName} = 'filePumpBundle'; + $definition->{tableKey} = 'bundleId'; + $definition->{sequenceKey} = ''; + my $properties = $definition->{properties}; + $properties->{bucketName} = { + fieldName => 'text', + defaultValue => $i18n->get('new bundle'), + }; + $properties->{lastBuild} = { + fieldName => 'integer', + defaultValue => 0, + }; + $properties->{jsFiles} = { + fieldName => 'textarea', + defaultValue => 0, + serialize => 1, + }; + $properties->{cssFiles} = { + fieldName => 'textarea', + defaultValue => 0, + serialize => 1, + }; + $properties->{otherFiles} = { + fieldName => 'textarea', + defaultValue => 0, + serialize => 1, + }; +} + +#------------------------------------------------------------------- + +=head2 deleteCollateral ( tableName, keyName, keyValue ) + +Deletes a row of collateral data. + +=head3 tableName + +The name of the table you wish to delete the data from. + +=head3 keyName + +The name of a key in the collateral hash. Typically a unique identifier for a given +"row" of collateral data. + +=head3 keyValue + +Along with keyName, determines which "row" of collateral data to delete. + +=cut + +sub deleteCollateral { + my $self = shift; + my $tableName = shift; + my $keyName = shift; + my $keyValue = shift; + my $table = $self->getAllCollateral($tableName); + my $index = $self->getCollateralDataIndex($table, $keyName, $keyValue); + return if $index == -1; + splice @{ $table }, $index, 1; + $self->setAllCollateral($tableName); +} + +#------------------------------------------------------------------- + +=head2 getAllCollateral ( tableName ) + +Returns an array reference to the translated JSON data for the +requested collateral table. + +=head3 tableName + +The name of the table you wish to retrieve the data from. + +=cut + +sub getAllCollateral { + my $self = shift; + my $tableName = shift; + return $self->{_collateral}->{$tableName} if exists $self->{_collateral}->{$tableName}; + my $json = $self->get($tableName); + my $table; + if ($json) { + $table = from_json($json); + } + else { + $table = []; + } + $self->{_collateral}->{$tableName} = $table; + return $table; +} + + +#------------------------------------------------------------------- + +=head2 getCollateral ( tableName, keyName, keyValue ) + +Returns a hash reference containing one row of collateral data from a particular +table. + +=head3 tableName + +The name of the table you wish to retrieve the data from. + +=head3 keyName + +The name of a key in the collateral hash. Typically a unique identifier for a given +"row" of collateral data. + +=head3 keyValue + +Along with keyName, determines which "row" of collateral data to delete. +If this is equal to "new", then an empty hashRef will be returned to avoid +strict errors in the caller. If the requested data does not exist in the +collateral array, it also returns an empty hashRef. + +=cut + +sub getCollateral { + my $self = shift; + my $tableName = shift; + my $keyName = shift; + my $keyValue = shift; + if ($keyValue eq "new" || $keyValue eq "") { + return {}; + } + my $table = $self->getAllCollateral($tableName); + my $index = $self->getCollateralDataIndex($table, $keyName, $keyValue); + return {} if $index == -1; + my %copy = %{ $table->[$index] }; + return \%copy; +} + + +#------------------------------------------------------------------- + +=head2 getCollateralDataIndex ( table, keyName, keyValue ) + +Returns the index in a set of collateral where an element of the +data (keyName) has a certain value (keyValue). If the criteria +are not found, returns -1. + +=head3 table + +The collateral data to search + +=head3 keyName + +The name of a key in the collateral hash. + +=head3 keyValue + +The value that keyName should have to meet the criteria. + +=cut + +sub getCollateralDataIndex { + my $self = shift; + my $table = shift; + my $keyName = shift; + my $keyValue = shift; + for (my $index=0; $index <= $#{ $table }; $index++) { + return $index + if (exists $table->[$index]->{$keyName} and $table->[$index]->{$keyName} eq $keyValue ); + } + return -1; +} + +#------------------------------------------------------------------- + +=head2 moveCollateralDown ( tableName, keyName, keyValue ) + +Moves a collateral data item down one position. If called on the last element of the +collateral array then it does nothing. + +=head3 tableName + +A string indicating the table that contains the collateral data. + +=head3 keyName + +The name of a key in the collateral hash. Typically a unique identifier for a given +"row" of collateral data. + +=head3 keyValue + +Along with keyName, determines which "row" of collateral data to move. + +=cut + +sub moveCollateralDown { + my $self = shift; + my $tableName = shift; + my $keyName = shift; + my $keyValue = shift; + + my $table = $self->getAllCollateral($tableName); + my $index = $self->getCollateralDataIndex($table, $keyName, $keyValue); + return if $index == -1; + return unless (abs($index) < $#{$table}); + @{ $table }[$index,$index+1] = @{ $table }[$index+1,$index]; + $self->setAllCollateral($tableName); +} + + +#------------------------------------------------------------------- + +=head2 moveCollateralUp ( tableName, keyName, keyValue ) + +Moves a collateral data item up one position. If called on the first element of the +collateral array then it does nothing. + +=head3 tableName + +A string indicating the table that contains the collateral data. + +=head3 keyName + +The name of a key in the collateral hash. Typically a unique identifier for a given +"row" of collateral data. + +=head3 keyValue + +Along with keyName, determines which "row" of collateral data to move. + +=cut + +sub moveCollateralUp { + my $self = shift; + my $tableName = shift; + my $keyName = shift; + my $keyValue = shift; + + my $table = $self->getAllCollateral($tableName); + my $index = $self->getCollateralDataIndex($table, $keyName, $keyValue); + return if $index == -1; + return unless $index && (abs($index) <= $#{$table}); + @{ $table }[$index-1,$index] = @{ $table }[$index,$index-1]; + $self->setAllCollateral($tableName); +} + +#------------------------------------------------------------------- + +=head2 reorderCollateral ( tableName,keyName [,setName,setValue] ) + +Resequences collateral data. Typically useful after deleting a collateral item to remove the gap created by the deletion. + +=head3 tableName + +The name of the table to resequence. + +=head3 keyName + +The key column name used to determine which data needs sorting within the table. + +=head3 setName + +Defaults to "assetId". This is used to define which data set to reorder. + +=head3 setValue + +Used to define which data set to reorder. Defaults to the value of setName (default "assetId", see above) in the wobject properties. + +=cut + +sub reorderCollateral { + my $self = shift; + my $table = shift; + my $keyName = shift; + my $setName = shift || "assetId"; + my $setValue = shift || $self->get($setName); + my $i = 1; + my $sth = $self->session->db->read("select $keyName from $table where $setName=? order by sequenceNumber", [$setValue]); + my $sth2 = $self->session->db->prepare("update $table set sequenceNumber=? where $setName=? and $keyName=?"); + while (my ($id) = $sth->array) { + $sth2->execute([$i, $setValue, $id]); + $i++; + } + $sth2->finish; + $sth->finish; +} + + +#----------------------------------------------------------------- + +=head2 setAllCollateral ( tableName ) + +Update the db from the object cache. + +=head3 tableName + +The name of the table to insert the data. + +=cut + +sub setAllCollateral { + my $self = shift; + my $tableName = shift; + my $json = to_json($self->{_collateral}->{$tableName}); + $self->update({ $tableName => $json }); + return; +} + +#----------------------------------------------------------------- + +=head2 setCollateral ( tableName, keyName, keyValue, properties ) + +Performs and insert/update of collateral data for any wobject's collateral data. +Returns the id of the data that was set, even if a new row was added to the +data. + +=head3 tableName + +The name of the table to insert the data. + +=head3 keyName + +The name of a key in the collateral hash. Typically a unique identifier for a given +"row" of collateral data. + +=head3 keyValue + +Along with keyName, determines which "row" of collateral data to set. +The index of the collateral data to set. If the keyValue = "new", then a +new entry will be appended to the end of the collateral array. Otherwise, +the appropriate entry will be overwritten with the new data. + +=head3 properties + +A hash reference containing the name/value pairs to be inserted into the collateral, using +the criteria mentioned above. + +=cut + +sub setCollateral { + my $self = shift; + my $tableName = shift; + my $keyName = shift; + my $keyValue = shift; + my $properties = shift; + ##Note, since this returns a reference, it is actually updating + ##the object cache directly. + my $table = $self->getAllCollateral($tableName); + if ($keyValue eq 'new' || $keyValue eq '') { + if (! exists $properties->{$keyName} + or $properties->{$keyName} eq 'new' + or $properties->{$keyName} eq '') { + $properties->{$keyName} = $self->session->id->generate; + } + push @{ $table }, $properties; + $self->setAllCollateral($tableName); + return $properties->{$keyName}; + } + my $index = $self->getCollateralDataIndex($table, $keyName, $keyValue); + return if $index == -1; + $table->[$index] = $properties; + $self->setAllCollateral($tableName); + return $keyValue; +} + + +1; diff --git a/lib/WebGUI/i18n/English/FilePump.pm b/lib/WebGUI/i18n/English/FilePump.pm new file mode 100644 index 000000000..d2285307a --- /dev/null +++ b/lib/WebGUI/i18n/English/FilePump.pm @@ -0,0 +1,27 @@ +package WebGUI::i18n::English::FilePump; + +use strict; + +our $I18N = { + 'bundle name' => { + message => q|Bundle name|, + lastUpdated => 1131394070, + context => q|Label for the name of a bundle (group, clump) of files.| + }, + + 'bundle name help' => { + message => q|A unique, human readable name for this bundle. Bundle names must be unique.|, + lastUpdated => 1131394072, + context => q|Hover help for bundle name.| + }, + + 'new bundle' => { + message => q|New bundle|, + lastUpdated => 1131394072, + context => q|Hover help for bundle name.| + }, + +}; + +1; +#vim:ft=perl diff --git a/sbin/installFilePump.pl b/sbin/installFilePump.pl new file mode 100644 index 000000000..4e0cca383 --- /dev/null +++ b/sbin/installFilePump.pl @@ -0,0 +1,198 @@ +#!/usr/bin/env perl + +#------------------------------------------------------------------- +# Copyright 2009 SDH Consulting Group +#------------------------------------------------------------------- + +$|++; # disable output buffering +our ($webguiRoot, $configFile, $help, $man); + +BEGIN { + $webguiRoot = ".."; + unshift (@INC, $webguiRoot."/lib"); +} + +use strict; +use Pod::Usage; +use Getopt::Long; +use WebGUI::Session; +use WebGUI::ProfileField; + +# Get parameters here, including $help +GetOptions( + 'configFile=s' => \$configFile, + 'help' => \$help, + 'man' => \$man, +); + +pod2usage( verbose => 1 ) if $help; +pod2usage( verbose => 2 ) if $man; +pod2usage( msg => "Must specify a config file!" ) unless $configFile; + +my $session = start( $webguiRoot, $configFile ); + +installFilePumpHandler($session); + +# Do your work here +finish($session); + +#---------------------------------------------------------------------------- +# Your sub here + +#---------------------------------------------------------------------------- +sub installFilePumpHandler { + my $session = shift; + print "\tAdding FilePump content handler... \n" unless $quiet; + ##Content Handler + my $contentHandlers = $session->config->get('contentHandlers'); + if (! isIn('WebGUI::Content::FilePump', @{ $contentHandlers }) ) { + my @newHandlers = (); + foreach my $handler (@{ $contentHandlers }) { + push @newHandlers, $handler; + push @newHandlers, 'WebGUI::Content::FilePump' if + $handler eq 'WebGUI::Content::AssetHistory'; + } + $session->config->set('contentHandlers', \@newHandlers); + } + ##Admin Console + $session->config->addToHash('adminConsole', 'filePump', { + "icon" => "filePump.gif", + "groupSetting" => "groupIdFilePump", + "uiLevel" => 5, + "url" => "^PageUrl(\"\",op=filePump);", + "title" => "^International(filePump,FilePump);" + }); + ##Setting for custom group + $session->setting->add('groupIdFilePump', 12); + print "Done.\n" unless $quiet; +} + +#---------------------------------------------------------------------------- +sub start { + my $webguiRoot = shift; + my $configFile = shift; + my $session = WebGUI::Session->open($webguiRoot,$configFile); + $session->user({userId=>3}); + + ## If your script is adding or changing content you need these lines, otherwise leave them commented + # + # my $versionTag = WebGUI::VersionTag->getWorking($session); + # $versionTag->set({name => 'Name Your Tag'}); + # + ## + + return $session; +} + +#---------------------------------------------------------------------------- +sub finish { + my $session = shift; + + ## If your script is adding or changing content you need these lines, otherwise leave them commented + # + # my $versionTag = WebGUI::VersionTag->getWorking($session); + # $versionTag->commit; + ## + my $versionTag = WebGUI::VersionTag->getWorking($session); + $versionTag->commit; + + $session->var->end; + $session->close; +} + +#------------------------------------------------- +sub updateTemplates { + my $session = shift; + my $packageDir = "message_center_packages"; + return undef unless (-d $packageDir); + print "\tUpdating packages.\n"; + opendir(DIR,$packageDir); + my @files = readdir(DIR); + closedir(DIR); + my $newFolder = undef; + foreach my $file (@files) { + next unless ($file =~ /\.wgpkg$/); + # Fix the filename to include a path + $file = $packageDir . "/" . $file; + addPackage( $session, $file ); + } +} + +sub addPackage { + my $session = shift; + my $file = shift; + + # Make a storage location for the package + my $storage = WebGUI::Storage->createTemp( $session ); + $storage->addFileFromFilesystem( $file ); + + # Import the package into the import node + my $package = WebGUI::Asset->getImportNode($session)->importPackage( $storage ); + + # Make the package not a package anymore + $package->update({ isPackage => 0 }); + + # Set the default flag for templates added + my $assetIds + = $package->getLineage( ['self','descendants'], { + includeOnlyClasses => [ 'WebGUI::Asset::Template' ], + } ); + for my $assetId ( @{ $assetIds } ) { + my $asset = WebGUI::Asset->newByDynamicClass( $session, $assetId ); + if ( !$asset ) { + print "Couldn't instantiate asset with ID '$assetId'. Please check package '$file' for corruption.\n"; + next; + } + $asset->update( { isDefault => 1 } ); + } + + return; +} + + +__END__ + + +=head1 NAME + +utility - A template for WebGUI utility scripts + +=head1 SYNOPSIS + + utility --configFile config.conf ... + + utility --help + +=head1 DESCRIPTION + +This WebGUI utility script helps you... + +=head1 ARGUMENTS + +=head1 OPTIONS + +=over + +=item B<--configFile config.conf> + +The WebGUI config file to use. Only the file name needs to be specified, +since it will be looked up inside WebGUI's configuration directory. +This parameter is required. + +=item B<--help> + +Shows a short summary and usage + +=item B<--man> + +Shows this document + +=back + +=head1 AUTHOR + +Copyright 2009 SDH Consulting Group + +=cut + +#vim:ft=perl From 08ea05486e34997f8fd668f6f5179e45444593e8 Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Thu, 30 Apr 2009 10:39:01 -0700 Subject: [PATCH 04/38] Add code to install the FilePump table. --- sbin/installFilePump.pl | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/sbin/installFilePump.pl b/sbin/installFilePump.pl index 4e0cca383..00156e838 100644 --- a/sbin/installFilePump.pl +++ b/sbin/installFilePump.pl @@ -16,7 +16,10 @@ use strict; use Pod::Usage; use Getopt::Long; use WebGUI::Session; -use WebGUI::ProfileField; +use WebGUI::Utility; +use WebGUI::FilePump::Bundle; + +my $quiet = 0; # Get parameters here, including $help GetOptions( @@ -32,6 +35,7 @@ pod2usage( msg => "Must specify a config file!" ) unless $configFile; my $session = start( $webguiRoot, $configFile ); installFilePumpHandler($session); +installFilePumpTable($session); # Do your work here finish($session); @@ -67,6 +71,15 @@ sub installFilePumpHandler { print "Done.\n" unless $quiet; } +#---------------------------------------------------------------------------- +sub installFilePumpTable { + my $session = shift; + print "\tAdding FilePump database table via CRUD... \n" unless $quiet; + ##Content Handler + WebGUI::FilePump::Bundle->crud_createTable($session); + print "Done.\n" unless $quiet; +} + #---------------------------------------------------------------------------- sub start { my $webguiRoot = shift; From c92c3867479429fe39de5ab2f774ef40a87e6062 Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Thu, 30 Apr 2009 10:39:16 -0700 Subject: [PATCH 05/38] Collateral handling code. --- lib/WebGUI/FilePump/Bundle.pm | 179 ++++++++++++++++++++++------------ 1 file changed, 119 insertions(+), 60 deletions(-) diff --git a/lib/WebGUI/FilePump/Bundle.pm b/lib/WebGUI/FilePump/Bundle.pm index cef4ee01d..ce73c269b 100644 --- a/lib/WebGUI/FilePump/Bundle.pm +++ b/lib/WebGUI/FilePump/Bundle.pm @@ -1,80 +1,38 @@ -package FilePump::Bundle; +package WebGUI::FilePump::Bundle; use base qw/WebGUI::Crud/; use WebGUI::International; +use WebGUI::Utility; +use URI; #------------------------------------------------------------------- -=head2 addCssFile ( $uri ) +=head2 addFile ( $type, $uri ) -Adds a CSS file to the bundle. Returns 1 if the add was successful. +Adds a file of the requested type to the bundle. Returns 1 if the add was successful. Otherwise, returns 0 and an error message as to why it was not successful. +=head3 $type + +If $type is JS, it adds it to the javascript part of the bundle. If it is +CSS, it adds it to the CSS part of the bundle. OTHER is used for all other +types of files. + =head3 $uri A URI to the new file to add. =cut -sub addCssFile { - my ($self, $uri) = @_; +sub addFile { + my ($self, $type, $uri) = @_; return 0, 'No URI' unless $uri; + return 0, 'Illegal type' unless WebGUI::Utility::isIn($type, 'JS', 'CSS', 'OTHER'); + my $collateralType = $type eq 'JS' ? 'jsFiles' + : $type eq 'CSS' ? 'cssFiles' + : 'OTHER'; $self->setCollateral( - 'cssFiles', - 'fileId', - 'new', - { - uri => $uri, - lastModified => 0, - }, - ); -} - -#------------------------------------------------------------------- - -=head2 addJsFile ( $uri ) - -Adds a javascript file to the bundle. Returns 1 if the add was successful. -Otherwise, returns 0 and an error message as to why it was not successful. - -=head3 $uri - -A URI to the new file to add. - -=cut - -sub addJsFile { - my ($self, $uri) = @_; - return 0, 'No URI' unless $uri; - $self->setCollateral( - 'jsFiles', - 'fileId', - 'new', - { - uri => $uri, - lastModified => 0, - }, - ); -} - -#------------------------------------------------------------------- - -=head2 addOtherFile ( $uri ) - -Adds an Other file to the bundle. Returns 1 if the add was successful. -Otherwise, returns 0 and an error message as to why it was not successful. - -=head3 $uri - -A URI to the new file to add. - -=cut - -sub addOtherFile { - my ($self, $uri) = @_; - return 0, 'No URI' unless $uri; - $self->setCollateral( - 'otherFiles', + $collateralType, 'fileId', 'new', { @@ -82,6 +40,7 @@ sub addOtherFile { lastModified => 0, }, ); + return 1; } #------------------------------------------------------------------- @@ -150,6 +109,7 @@ sub crud_definition { defaultValue => 0, serialize => 1, }; + return $definition; } #------------------------------------------------------------------- @@ -187,6 +147,39 @@ sub deleteCollateral { #------------------------------------------------------------------- +=head2 deleteFile ( $type, $fileId ) + +Deletes a file of the requested type from the bundle. + +=head3 $type + +If $type is JS, it deletes it from the javascript part of the bundle. If it is +CSS, it deletes it from the CSS part of the bundle. OTHER is used for all other +types of files. + +=head3 $fileId + +The unique collateral GUID to delete from the bundle. + +=cut + +sub deleteFile { + my ($self, $type, $fileId) = @_; + return 0, 'No fileId' unless $fileId; + return 0, 'Illegal type' unless WebGUI::Utility::isIn($type, 'JS', 'CSS', 'OTHER'); + my $collateralType = $type eq 'JS' ? 'jsFiles' + : $type eq 'CSS' ? 'cssFiles' + : 'OTHER'; + $self->deleteCollateral( + $collateralType, + 'fileId', + $fileId, + ); + return 1; +} + +#------------------------------------------------------------------- + =head2 getAllCollateral ( tableName ) Returns an array reference to the translated JSON data for the @@ -365,6 +358,72 @@ sub moveCollateralUp { #------------------------------------------------------------------- +=head2 moveFileDown ( $type, $fileId ) + +Moves the requested file down in the ordered collateral. + +=head3 $type + +If $type is JS, it moves a file in the javascript part of the bundle. If it is +CSS, it moves a file in the CSS part of the bundle. OTHER is used for all other +types of files. + +=head3 $fileId + +The unique collateral GUID to move in the bundle. + +=cut + +sub moveFileDown { + my ($self, $type, $fileId) = @_; + return 0, 'No fileId' unless $fileId; + return 0, 'Illegal type' unless WebGUI::Utility::isIn($type, 'JS', 'CSS', 'OTHER'); + my $collateralType = $type eq 'JS' ? 'jsFiles' + : $type eq 'CSS' ? 'cssFiles' + : 'OTHER'; + $self->moveCollateralDown( + $collateralType, + 'fileId', + $fileId, + ); + return 1; +} + +#------------------------------------------------------------------- + +=head2 moveFileUp ( $type, $fileId ) + +Moves the requested file up in the ordered collateral. + +=head3 $type + +If $type is JS, it moves a file in the javascript part of the bundle. If it is +CSS, it moves a file in the CSS part of the bundle. OTHER is used for all other +types of files. + +=head3 $fileId + +The unique collateral GUID to move in the bundle. + +=cut + +sub moveFileUp { + my ($self, $type, $fileId) = @_; + return 0, 'No fileId' unless $fileId; + return 0, 'Illegal type' unless WebGUI::Utility::isIn($type, 'JS', 'CSS', 'OTHER'); + my $collateralType = $type eq 'JS' ? 'jsFiles' + : $type eq 'CSS' ? 'cssFiles' + : 'OTHER'; + $self->moveCollateralUp( + $collateralType, + 'fileId', + $fileId, + ); + return 1; +} + +#------------------------------------------------------------------- + =head2 reorderCollateral ( tableName,keyName [,setName,setValue] ) Resequences collateral data. Typically useful after deleting a collateral item to remove the gap created by the deletion. From 3a6802945c434b384e1067896843cf38cd9595e0 Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Thu, 30 Apr 2009 15:32:32 -0700 Subject: [PATCH 06/38] Add tracking of out of date bundles. Added a db column, lastModifed, and a class method to fetch the set of out of date bundles, based on comparison of lastModified and lastBuilt. --- lib/WebGUI/FilePump/Bundle.pm | 84 +++++++++++++++++------------------ 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/lib/WebGUI/FilePump/Bundle.pm b/lib/WebGUI/FilePump/Bundle.pm index ce73c269b..7b6d03a49 100644 --- a/lib/WebGUI/FilePump/Bundle.pm +++ b/lib/WebGUI/FilePump/Bundle.pm @@ -40,6 +40,7 @@ sub addFile { lastModified => 0, }, ); + $self->update({lastModified => time()}); return 1; } @@ -69,7 +70,13 @@ The name of a bundle =head4 lastBuild -The date the bundle was last built +The date the bundle was last built. This is used to generate the name of the bundled files +for this bundle. + +=head4 lastModified + +The date the bundle was last modified. With this, and the lastBuild date, you can determine +which bundles need to be rebuilt. =head4 jsFiles, cssFiles, otherFiles @@ -90,6 +97,10 @@ sub crud_definition { fieldName => 'text', defaultValue => $i18n->get('new bundle'), }; + $properties->{lastModified} = { + fieldName => 'integer', + defaultValue => 0, + }; $properties->{lastBuild} = { fieldName => 'integer', defaultValue => 0, @@ -175,6 +186,7 @@ sub deleteFile { 'fileId', $fileId, ); + $self->update({lastModified => time()}); return 1; } @@ -285,6 +297,33 @@ sub getCollateralDataIndex { #------------------------------------------------------------------- +=head2 getOutOfDateBundles ( $session ) + +This is a class method. It returns an array reference of WebGUI::FilePump::Bundle +objects that need to be rebuilt. + +=head3 $session + +A WebGUI::Session object. + +=cut + +sub getOutOfDateBundles { + my ($class, $session) = @_; + my $oldBundles = []; + my $oldBundleIterator = $class->getAllIterator({ + constraints => [ + 'lastBuild < lastModified' => [], + ], + }); + while (my $bundle = $oldBundleIterator->()) { + push @{ $oldBundles }, $bundle; + } + return $oldBundles; +} + +#------------------------------------------------------------------- + =head2 moveCollateralDown ( tableName, keyName, keyValue ) Moves a collateral data item down one position. If called on the last element of the @@ -386,6 +425,7 @@ sub moveFileDown { 'fileId', $fileId, ); + $self->update({lastModified => time()}); return 1; } @@ -419,50 +459,10 @@ sub moveFileUp { 'fileId', $fileId, ); + $self->update({lastModified => time()}); return 1; } -#------------------------------------------------------------------- - -=head2 reorderCollateral ( tableName,keyName [,setName,setValue] ) - -Resequences collateral data. Typically useful after deleting a collateral item to remove the gap created by the deletion. - -=head3 tableName - -The name of the table to resequence. - -=head3 keyName - -The key column name used to determine which data needs sorting within the table. - -=head3 setName - -Defaults to "assetId". This is used to define which data set to reorder. - -=head3 setValue - -Used to define which data set to reorder. Defaults to the value of setName (default "assetId", see above) in the wobject properties. - -=cut - -sub reorderCollateral { - my $self = shift; - my $table = shift; - my $keyName = shift; - my $setName = shift || "assetId"; - my $setValue = shift || $self->get($setName); - my $i = 1; - my $sth = $self->session->db->read("select $keyName from $table where $setName=? order by sequenceNumber", [$setValue]); - my $sth2 = $self->session->db->prepare("update $table set sequenceNumber=? where $setName=? and $keyName=?"); - while (my ($id) = $sth->array) { - $sth2->execute([$i, $setValue, $id]); - $i++; - } - $sth2->finish; - $sth->finish; -} - #----------------------------------------------------------------- From 907ce09355b31571634e9cea62c9338b9ecbd750 Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Thu, 30 Apr 2009 16:42:31 -0700 Subject: [PATCH 07/38] More utility methods, and a few bug fixes. Use the correct parameter name for other files. Stub in the build method. Extend the delete method to handle cleaning up the build directory. Add a method to get a Path::Class::Dir object for the build directory. --- lib/WebGUI/FilePump/Bundle.pm | 81 ++++++++++++++++++++++++++++++++--- 1 file changed, 76 insertions(+), 5 deletions(-) diff --git a/lib/WebGUI/FilePump/Bundle.pm b/lib/WebGUI/FilePump/Bundle.pm index 7b6d03a49..23e569938 100644 --- a/lib/WebGUI/FilePump/Bundle.pm +++ b/lib/WebGUI/FilePump/Bundle.pm @@ -4,6 +4,7 @@ use base qw/WebGUI::Crud/; use WebGUI::International; use WebGUI::Utility; use URI; +use Path::Class::Dir; #------------------------------------------------------------------- @@ -20,7 +21,8 @@ types of files. =head3 $uri -A URI to the new file to add. +A URI to the new file to add. If the URI already exists in that part of the bundle, +it will return 0 and an error message. =cut @@ -30,7 +32,10 @@ sub addFile { return 0, 'Illegal type' unless WebGUI::Utility::isIn($type, 'JS', 'CSS', 'OTHER'); my $collateralType = $type eq 'JS' ? 'jsFiles' : $type eq 'CSS' ? 'cssFiles' - : 'OTHER'; + : 'otherFiles'; + my $files = $self->getAllCollateral($collateralType); + my $uriExists = $self->getCollateralDataIndex($files, 'uri', $uri) == -1; + return 0, 'Duplicate URI' if $uriExists; $self->setCollateral( $collateralType, 'fileId', @@ -46,6 +51,38 @@ sub addFile { #------------------------------------------------------------------- +=head2 build ( ) + +build goes through and fetches all files referenced in all URIs stored for +this bundle. It downloads them, stores their modification time for future +checks, and then does special processing, depending on the type of file. + +Javascript files are concatenated together in order, and minimized. The +resulting data is stored in the filepump area under the uploads directory +with the name bundleName.timestamp/bundleName.js + +CSS files are handled likewise, except that the name is bundleName.timestamp/bundleName.css. + +Other files are copied from their current location into the timestamped bundle directory. + +Older timestamped build directories are removed. + +If the build is successful, it will return 1. Otherwise, if problems +occur during the build, then the old build directory is not affected and +the method returns 0, along with an error message. + +=cut + +sub build { + my ($self) = @_; + my $lastBuild = time(); + my $originalBuild = $self->get('lastBuild'); + $self->update({lastBuild => $lastBuild}); + return 1; +} + +#------------------------------------------------------------------- + =head2 crud_definition WebGUI::Crud definition for this class. @@ -125,6 +162,22 @@ sub crud_definition { #------------------------------------------------------------------- +=head2 delete ( ) + +Extend the method from WebGUI::Crud to handle deleting the locally stored +files. + +=cut + +sub delete { + my ($self) = @_; + my $bundleDir = $self->getPathClassDir(); + $bundleDir->rmtree(); + return $self->SUPER::delete; +} + +#------------------------------------------------------------------- + =head2 deleteCollateral ( tableName, keyName, keyValue ) Deletes a row of collateral data. @@ -180,7 +233,7 @@ sub deleteFile { return 0, 'Illegal type' unless WebGUI::Utility::isIn($type, 'JS', 'CSS', 'OTHER'); my $collateralType = $type eq 'JS' ? 'jsFiles' : $type eq 'CSS' ? 'cssFiles' - : 'OTHER'; + : 'otherFiles'; $self->deleteCollateral( $collateralType, 'fileId', @@ -297,6 +350,24 @@ sub getCollateralDataIndex { #------------------------------------------------------------------- +=head2 getPathClassDir ( ) + +Returns a Path::Class::Dir object to the last build directory +for this bundle. + +=cut + +sub getPathClassDir { + my ($self) = @_; + return Path::Class::Dir->new( + $self->session->get('uploadsPath'), + 'filepump', + $self->get('bundleName') . $self->get('lastBuild') + ); +} + +#------------------------------------------------------------------- + =head2 getOutOfDateBundles ( $session ) This is a class method. It returns an array reference of WebGUI::FilePump::Bundle @@ -419,7 +490,7 @@ sub moveFileDown { return 0, 'Illegal type' unless WebGUI::Utility::isIn($type, 'JS', 'CSS', 'OTHER'); my $collateralType = $type eq 'JS' ? 'jsFiles' : $type eq 'CSS' ? 'cssFiles' - : 'OTHER'; + : 'otherFiles'; $self->moveCollateralDown( $collateralType, 'fileId', @@ -453,7 +524,7 @@ sub moveFileUp { return 0, 'Illegal type' unless WebGUI::Utility::isIn($type, 'JS', 'CSS', 'OTHER'); my $collateralType = $type eq 'JS' ? 'jsFiles' : $type eq 'CSS' ? 'cssFiles' - : 'OTHER'; + : 'otherFiles'; $self->moveCollateralUp( $collateralType, 'fileId', From dc9347d7cd3fdd3003d83de8fe785edb83b80307 Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Wed, 6 May 2009 14:06:28 -0700 Subject: [PATCH 08/38] Copying over fixed Crud from HEAD. --- lib/WebGUI/Crud.pm | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/lib/WebGUI/Crud.pm b/lib/WebGUI/Crud.pm index c75268496..94009180a 100644 --- a/lib/WebGUI/Crud.pm +++ b/lib/WebGUI/Crud.pm @@ -20,11 +20,11 @@ use strict; use Class::InsideOut qw(readonly private id register); use JSON; use Tie::IxHash; +use Clone qw/clone/; use WebGUI::DateTime; use WebGUI::Exception; use WebGUI::Utility; - private objectData => my %objectData; readonly session => my %session; @@ -438,8 +438,14 @@ sub crud_updateTable { my $fieldType = $control->getDatabaseFieldType; my $isKey = $properties->{$property}{isQueryKey}; my $defaultValue = $properties->{$property}{defaultValue}; + if ($properties->{$property}{serialize}) { + $defaultValue = JSON->new->canonical->encode($defaultValue); + } my $notNullClause = ($isKey || $defaultValue ne "") ? "not null" : ""; - my $defaultClause = "default ".$dbh->quote($defaultValue) if ($defaultValue ne ""); + my $defaultClause = ''; + if ($fieldType !~ /(?:text|blob)$/i) { + $defaultClause = "default ".$dbh->quote($defaultValue) if ($defaultValue ne ""); + } if (exists $tableFields{$property}) { my $changed = 0; @@ -549,12 +555,11 @@ sub get { # return a specific property if (defined $name) { - return $objectData{id $self}{$name}; + return clone $objectData{id $self}{$name}; } # return a copy of all properties - my %copy = %{$objectData{id $self}}; - return \%copy; + return clone $objectData{id $self}; } #------------------------------------------------------------------- @@ -920,9 +925,11 @@ B As part of it's validation mechanisms, update() will delete any elem sub update { my ($self, $data) = @_; + my $session = $self->session; # validate incoming data - my $properties = $self->crud_getProperties($self->session); + my $properties = $self->crud_getProperties($session); + my $dbData = { $self->crud_getTableKey($session) => $self->getId }; foreach my $property (keys %{$data}) { # don't save fields that aren't part of our definition @@ -937,20 +944,23 @@ sub update { } # serialize if needed - if ($properties->{$property}{serialize} && $data->{property} ne "") { - $data->{property} = JSON->new->canonical->encode($data->{property}); + if ($properties->{$property}{serialize} && $data->{$property} ne "") { + $dbData->{$property} = JSON->new->canonical->encode($data->{$property}); } + else { + $dbData->{$property} = $data->{$property}; + } } # set last updated - $data->{lastUpdated} ||= WebGUI::DateTime->new($self->session, time())->toDatabase; + $data->{lastUpdated} ||= WebGUI::DateTime->new($session, time())->toDatabase; # update memory my $refId = id $self; %{$objectData{$refId}} = (%{$objectData{$refId}}, %{$data}); # update the database - $self->session->db->setRow($self->crud_getTableName($self->session), $self->crud_getTableKey($self->session), $objectData{$refId}); + $session->db->setRow($self->crud_getTableName($session), $self->crud_getTableKey($session), $dbData); return 1; } From 627ee82c762ac442f0c73ef70ce5d2fa6f26646d Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Wed, 6 May 2009 14:31:01 -0700 Subject: [PATCH 09/38] Added a test, found many bugs which were fixed. --- t/FilePump/Bundle.t | 109 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 t/FilePump/Bundle.t diff --git a/t/FilePump/Bundle.t b/t/FilePump/Bundle.t new file mode 100644 index 000000000..ed363c91d --- /dev/null +++ b/t/FilePump/Bundle.t @@ -0,0 +1,109 @@ +# vim:syntax=perl +#------------------------------------------------------------------- +# 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 +#------------------------------------------------------------------ + +# Write a little about what this script tests. +# +# + +use FindBin; +use strict; +use lib "$FindBin::Bin/../lib"; +use Test::More; +use Test::Deep; +use Data::Dumper; +use WebGUI::Test; # Must use this before any other WebGUI modules +use WebGUI::Session; + +#---------------------------------------------------------------------------- +# Init +my $session = WebGUI::Test->session; + +#---------------------------------------------------------------------------- +# Tests + +my $tests = 8; # Increment this number for each test you create +plan tests => 1 + $tests; # 1 for the use_ok + +#---------------------------------------------------------------------------- +# put your tests here + +my $loaded = use_ok('WebGUI::FilePump::Bundle'); + +SKIP: { + +skip 'Unable to load module WebGUI::FilePump::Bundle', $tests unless $loaded; + +my $bundle = WebGUI::FilePump::Bundle->create($session); +isa_ok($bundle, 'WebGUI::FilePump::Bundle'); +isa_ok($bundle, 'WebGUI::Crud'); + +################################################################### +# +# addFile +# +################################################################### + +cmp_deeply( + [ $bundle->addFile() ], + [ 0, 'Illegal type' ], + 'addFile, checking error for no type' +); + +cmp_deeply( + [ $bundle->addFile('BAD_TYPE', ) ], + [ 0, 'Illegal type' ], + '... checking error for bad type of file to add' +); + +cmp_deeply( + [ $bundle->addFile('JS', ) ], + [ 0, 'No URI' ], + '... checking error for no uri' +); + +$bundle->setCollateral( + 'jsFiles', + 'fileId', + 'new', + { + uri => 'mysite', + lastUpdated => 0, + } +); + +is( + $bundle->addFile('JS', 'http://mysite.com/script.js'), + 1, + '... adding a JS file' +); + +is( + $bundle->addFile('CSS', 'http://mysite.com/script.js'), + 1, + '... okay to add a duplicate to another type' +); + +cmp_deeply( + [ $bundle->addFile('JS', 'http://mysite.com/script.js') ], + [ 0, 'Duplicate URI' ], + '... checking error message for duplicate URI' +); + +$bundle->delete; + +} + +#---------------------------------------------------------------------------- +# Cleanup +END { + $session->db->write('delete from filePumpBundle'); +} +#vim:ft=perl From 73fe4db1800649a781b76f0fc8f2f53dad275fb6 Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Thu, 7 May 2009 16:51:21 -0700 Subject: [PATCH 10/38] Add {CSS,JavaScript}::Minifier::XS requirements. --- sbin/testEnvironment.pl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sbin/testEnvironment.pl b/sbin/testEnvironment.pl index a7f54f7b8..7a3bec936 100755 --- a/sbin/testEnvironment.pl +++ b/sbin/testEnvironment.pl @@ -123,6 +123,8 @@ checkModule("Module::Find", "0.06" ); checkModule("Class::C3", "0.19" ); checkModule("Params::Validate", "0.81" ); checkModule("Clone", "0.31" ); +checkModule("CSS::Minifier::XS", "0.03" ); +checkModule("JavaScript::Minifier::XS", "0.05" ); failAndExit("Required modules are missing, running no more checks.") if $missingModule; From e36d550e1904629517cb87d83e10f59e732081b2 Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Thu, 7 May 2009 17:26:12 -0700 Subject: [PATCH 11/38] Add a whole raft of tests, many bug fixes. Removed unused parts of the original JSON collateral code, like getAllCollateral, etc. --- lib/WebGUI/FilePump/Bundle.pm | 103 ++++++++------------------- t/FilePump/Bundle.t | 130 +++++++++++++++++++++++++++++++--- 2 files changed, 149 insertions(+), 84 deletions(-) diff --git a/lib/WebGUI/FilePump/Bundle.pm b/lib/WebGUI/FilePump/Bundle.pm index 23e569938..4feacb554 100644 --- a/lib/WebGUI/FilePump/Bundle.pm +++ b/lib/WebGUI/FilePump/Bundle.pm @@ -5,6 +5,8 @@ use WebGUI::International; use WebGUI::Utility; use URI; use Path::Class::Dir; +use CSS::Minifier::XS; +use JavaScript::Minifier::XS; #------------------------------------------------------------------- @@ -28,13 +30,13 @@ it will return 0 and an error message. sub addFile { my ($self, $type, $uri) = @_; - return 0, 'No URI' unless $uri; return 0, 'Illegal type' unless WebGUI::Utility::isIn($type, 'JS', 'CSS', 'OTHER'); + return 0, 'No URI' unless $uri; my $collateralType = $type eq 'JS' ? 'jsFiles' : $type eq 'CSS' ? 'cssFiles' : 'otherFiles'; - my $files = $self->getAllCollateral($collateralType); - my $uriExists = $self->getCollateralDataIndex($files, 'uri', $uri) == -1; + my $files = $self->get($collateralType); + my $uriExists = $self->getCollateralDataIndex($files, 'uri', $uri) != -1 ? 1 : 0; return 0, 'Duplicate URI' if $uriExists; $self->setCollateral( $collateralType, @@ -144,17 +146,17 @@ sub crud_definition { }; $properties->{jsFiles} = { fieldName => 'textarea', - defaultValue => 0, + defaultValue => [], serialize => 1, }; $properties->{cssFiles} = { fieldName => 'textarea', - defaultValue => 0, + defaultValue => [], serialize => 1, }; $properties->{otherFiles} = { fieldName => 'textarea', - defaultValue => 0, + defaultValue => [], serialize => 1, }; return $definition; @@ -202,11 +204,11 @@ sub deleteCollateral { my $tableName = shift; my $keyName = shift; my $keyValue = shift; - my $table = $self->getAllCollateral($tableName); + my $table = $self->get($tableName); my $index = $self->getCollateralDataIndex($table, $keyName, $keyValue); return if $index == -1; splice @{ $table }, $index, 1; - $self->setAllCollateral($tableName); + $self->update({ $tableName => $table }); } #------------------------------------------------------------------- @@ -229,8 +231,8 @@ The unique collateral GUID to delete from the bundle. sub deleteFile { my ($self, $type, $fileId) = @_; - return 0, 'No fileId' unless $fileId; return 0, 'Illegal type' unless WebGUI::Utility::isIn($type, 'JS', 'CSS', 'OTHER'); + return 0, 'No fileId' unless $fileId; my $collateralType = $type eq 'JS' ? 'jsFiles' : $type eq 'CSS' ? 'cssFiles' : 'otherFiles'; @@ -243,36 +245,6 @@ sub deleteFile { return 1; } -#------------------------------------------------------------------- - -=head2 getAllCollateral ( tableName ) - -Returns an array reference to the translated JSON data for the -requested collateral table. - -=head3 tableName - -The name of the table you wish to retrieve the data from. - -=cut - -sub getAllCollateral { - my $self = shift; - my $tableName = shift; - return $self->{_collateral}->{$tableName} if exists $self->{_collateral}->{$tableName}; - my $json = $self->get($tableName); - my $table; - if ($json) { - $table = from_json($json); - } - else { - $table = []; - } - $self->{_collateral}->{$tableName} = $table; - return $table; -} - - #------------------------------------------------------------------- =head2 getCollateral ( tableName, keyName, keyValue ) @@ -306,7 +278,7 @@ sub getCollateral { if ($keyValue eq "new" || $keyValue eq "") { return {}; } - my $table = $self->getAllCollateral($tableName); + my $table = $self->get($tableName); my $index = $self->getCollateralDataIndex($table, $keyName, $keyValue); return {} if $index == -1; my %copy = %{ $table->[$index] }; @@ -343,7 +315,7 @@ sub getCollateralDataIndex { my $keyValue = shift; for (my $index=0; $index <= $#{ $table }; $index++) { return $index - if (exists $table->[$index]->{$keyName} and $table->[$index]->{$keyName} eq $keyValue ); + if (exists($table->[$index]->{$keyName}) && ($table->[$index]->{$keyName} eq $keyValue )); } return -1; } @@ -360,7 +332,7 @@ for this bundle. sub getPathClassDir { my ($self) = @_; return Path::Class::Dir->new( - $self->session->get('uploadsPath'), + $self->session->config->get('uploadsPath'), 'filepump', $self->get('bundleName') . $self->get('lastBuild') ); @@ -398,7 +370,8 @@ sub getOutOfDateBundles { =head2 moveCollateralDown ( tableName, keyName, keyValue ) Moves a collateral data item down one position. If called on the last element of the -collateral array then it does nothing. +collateral array then it does nothing. Returns 1 if the move is successful. Returns +undef or the empty array otherwise. =head3 tableName @@ -421,12 +394,13 @@ sub moveCollateralDown { my $keyName = shift; my $keyValue = shift; - my $table = $self->getAllCollateral($tableName); + my $table = $self->get($tableName); my $index = $self->getCollateralDataIndex($table, $keyName, $keyValue); return if $index == -1; return unless (abs($index) < $#{$table}); @{ $table }[$index,$index+1] = @{ $table }[$index+1,$index]; - $self->setAllCollateral($tableName); + $self->update({ $tableName => $table }); + return 1; } @@ -435,7 +409,9 @@ sub moveCollateralDown { =head2 moveCollateralUp ( tableName, keyName, keyValue ) Moves a collateral data item up one position. If called on the first element of the -collateral array then it does nothing. +collateral array then it does nothing. Returns 1 if the move is successful. Returns +undef or the empty array otherwise. + =head3 tableName @@ -458,12 +434,13 @@ sub moveCollateralUp { my $keyName = shift; my $keyValue = shift; - my $table = $self->getAllCollateral($tableName); + my $table = $self->get($tableName); my $index = $self->getCollateralDataIndex($table, $keyName, $keyValue); return if $index == -1; return unless $index && (abs($index) <= $#{$table}); @{ $table }[$index-1,$index] = @{ $table }[$index,$index-1]; - $self->setAllCollateral($tableName); + $self->update({ $tableName => $table }); + return 1; } #------------------------------------------------------------------- @@ -486,8 +463,8 @@ The unique collateral GUID to move in the bundle. sub moveFileDown { my ($self, $type, $fileId) = @_; - return 0, 'No fileId' unless $fileId; return 0, 'Illegal type' unless WebGUI::Utility::isIn($type, 'JS', 'CSS', 'OTHER'); + return 0, 'No fileId' unless $fileId; my $collateralType = $type eq 'JS' ? 'jsFiles' : $type eq 'CSS' ? 'cssFiles' : 'otherFiles'; @@ -520,8 +497,8 @@ The unique collateral GUID to move in the bundle. sub moveFileUp { my ($self, $type, $fileId) = @_; - return 0, 'No fileId' unless $fileId; return 0, 'Illegal type' unless WebGUI::Utility::isIn($type, 'JS', 'CSS', 'OTHER'); + return 0, 'No fileId' unless $fileId; my $collateralType = $type eq 'JS' ? 'jsFiles' : $type eq 'CSS' ? 'cssFiles' : 'otherFiles'; @@ -535,26 +512,6 @@ sub moveFileUp { } -#----------------------------------------------------------------- - -=head2 setAllCollateral ( tableName ) - -Update the db from the object cache. - -=head3 tableName - -The name of the table to insert the data. - -=cut - -sub setAllCollateral { - my $self = shift; - my $tableName = shift; - my $json = to_json($self->{_collateral}->{$tableName}); - $self->update({ $tableName => $json }); - return; -} - #----------------------------------------------------------------- =head2 setCollateral ( tableName, keyName, keyValue, properties ) @@ -594,7 +551,7 @@ sub setCollateral { my $properties = shift; ##Note, since this returns a reference, it is actually updating ##the object cache directly. - my $table = $self->getAllCollateral($tableName); + my $table = $self->get($tableName); if ($keyValue eq 'new' || $keyValue eq '') { if (! exists $properties->{$keyName} or $properties->{$keyName} eq 'new' @@ -602,13 +559,13 @@ sub setCollateral { $properties->{$keyName} = $self->session->id->generate; } push @{ $table }, $properties; - $self->setAllCollateral($tableName); + $self->update({$tableName => $table}); return $properties->{$keyName}; } my $index = $self->getCollateralDataIndex($table, $keyName, $keyValue); return if $index == -1; $table->[$index] = $properties; - $self->setAllCollateral($tableName); + $self->update({ $tableName => $table }); return $keyValue; } diff --git a/t/FilePump/Bundle.t b/t/FilePump/Bundle.t index ed363c91d..159abbfd6 100644 --- a/t/FilePump/Bundle.t +++ b/t/FilePump/Bundle.t @@ -29,7 +29,7 @@ my $session = WebGUI::Test->session; #---------------------------------------------------------------------------- # Tests -my $tests = 8; # Increment this number for each test you create +my $tests = 23; # Increment this number for each test you create plan tests => 1 + $tests; # 1 for the use_ok #---------------------------------------------------------------------------- @@ -69,16 +69,6 @@ cmp_deeply( '... checking error for no uri' ); -$bundle->setCollateral( - 'jsFiles', - 'fileId', - 'new', - { - uri => 'mysite', - lastUpdated => 0, - } -); - is( $bundle->addFile('JS', 'http://mysite.com/script.js'), 1, @@ -97,6 +87,124 @@ cmp_deeply( '... checking error message for duplicate URI' ); +$bundle->addFile('JS', 'http://mysite.com/helloworld.js'); +$bundle->addFile('JS', 'file:/data/domains/mysite.com/www/uploads/XX/YY/XXYYZZ/graviticEnergyDrive.js'); + +my @fileUris = map { $_->{uri} } @{ $bundle->get('jsFiles') }; +cmp_deeply( + [ @fileUris ], + [qw{ + http://mysite.com/script.js + http://mysite.com/helloworld.js + file:/data/domains/mysite.com/www/uploads/XX/YY/XXYYZZ/graviticEnergyDrive.js + }], + '... checking actual jsFiles data structure contents' +); + +################################################################### +# +# moveFile{Up,Down} +# +################################################################### + +cmp_deeply( + [ $bundle->moveFileUp() ], + [ 0, 'Illegal type' ], + 'moveFileUp: checking error for no type' +); + +cmp_deeply( + [ $bundle->moveFileUp('BEER') ], + [ 0, 'Illegal type' ], + '... checking error for bad type' +); + +cmp_deeply( + [ $bundle->moveFileUp('JS', ) ], + [ 0, 'No fileId' ], + '... checking error for no fileId' +); + +cmp_deeply( + [ $bundle->moveFileDown() ], + [ 0, 'Illegal type' ], + 'moveFileDown: checking error for no type' +); + +cmp_deeply( + [ $bundle->moveFileDown('BEER') ], + [ 0, 'Illegal type' ], + '... checking error for bad type' +); + +cmp_deeply( + [ $bundle->moveFileDown('JS', ) ], + [ 0, 'No fileId' ], + '... checking error for no fileId' +); + +my @fileIds = map { $_->{fileId} } @{ $bundle->get('jsFiles') }; + +ok($bundle->moveFileDown('JS', $fileIds[0]), 'moveFileDown returns 1 for a successful move'); +@fileUris = map { $_->{uri} } @{ $bundle->get('jsFiles') }; +cmp_deeply( + [ @fileUris ], + [qw{ + http://mysite.com/helloworld.js + http://mysite.com/script.js + file:/data/domains/mysite.com/www/uploads/XX/YY/XXYYZZ/graviticEnergyDrive.js + }], + '... checking the actual order of js files' +); + +ok($bundle->moveFileUp('JS', $fileIds[2]), 'moveFileUp returns 1 for a successful move'); +@fileUris = map { $_->{uri} } @{ $bundle->get('jsFiles') }; +cmp_deeply( + [ @fileUris ], + [qw{ + http://mysite.com/helloworld.js + file:/data/domains/mysite.com/www/uploads/XX/YY/XXYYZZ/graviticEnergyDrive.js + http://mysite.com/script.js + }], + '... checking the actual order of js files' +); + +################################################################### +# +# deleteFile +# +################################################################### + +cmp_deeply( + [ $bundle->deleteFile() ], + [ 0, 'Illegal type' ], + 'deleteFile: checking error for no type' +); + +cmp_deeply( + [ $bundle->deleteFile('BEER') ], + [ 0, 'Illegal type' ], + '... checking error for bad type' +); + +cmp_deeply( + [ $bundle->deleteFile('JS', ) ], + [ 0, 'No fileId' ], + '... checking error for no fileId' +); + +@fileIds = map { $_->{fileId} } @{ $bundle->get('jsFiles') }; +$bundle->deleteFile('JS', $fileIds[1]); +@fileUris = map { $_->{uri} } @{ $bundle->get('jsFiles') }; +cmp_deeply( + [ @fileUris ], + [qw{ + http://mysite.com/helloworld.js + http://mysite.com/script.js + }], + '... checking the actual deletion of js files' +); + $bundle->delete; } From 9574533f3f9f683d94863b39b82fcb2092907e49 Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Thu, 7 May 2009 17:30:57 -0700 Subject: [PATCH 12/38] Fix the bundle name property. --- lib/WebGUI/FilePump/Bundle.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/WebGUI/FilePump/Bundle.pm b/lib/WebGUI/FilePump/Bundle.pm index 4feacb554..05714ef9c 100644 --- a/lib/WebGUI/FilePump/Bundle.pm +++ b/lib/WebGUI/FilePump/Bundle.pm @@ -132,7 +132,7 @@ sub crud_definition { $definition->{tableKey} = 'bundleId'; $definition->{sequenceKey} = ''; my $properties = $definition->{properties}; - $properties->{bucketName} = { + $properties->{bundleName} = { fieldName => 'text', defaultValue => $i18n->get('new bundle'), }; From 7f19d874c828538e934bde568524bb3c85550087 Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Fri, 8 May 2009 10:46:19 -0700 Subject: [PATCH 13/38] Tests for lastModified on add, delete, move. --- t/FilePump/Bundle.t | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/t/FilePump/Bundle.t b/t/FilePump/Bundle.t index 159abbfd6..ac8f45a38 100644 --- a/t/FilePump/Bundle.t +++ b/t/FilePump/Bundle.t @@ -22,6 +22,8 @@ use Data::Dumper; use WebGUI::Test; # Must use this before any other WebGUI modules use WebGUI::Session; +my $startTime = time(); + #---------------------------------------------------------------------------- # Init my $session = WebGUI::Test->session; @@ -29,7 +31,7 @@ my $session = WebGUI::Test->session; #---------------------------------------------------------------------------- # Tests -my $tests = 23; # Increment this number for each test you create +my $tests = 28; # Increment this number for each test you create plan tests => 1 + $tests; # 1 for the use_ok #---------------------------------------------------------------------------- @@ -45,6 +47,8 @@ my $bundle = WebGUI::FilePump::Bundle->create($session); isa_ok($bundle, 'WebGUI::FilePump::Bundle'); isa_ok($bundle, 'WebGUI::Crud'); +is($bundle->get('lastModified'), 0, 'by default, lastModified is 0'); + ################################################################### # # addFile @@ -74,6 +78,7 @@ is( 1, '... adding a JS file' ); +cmp_ok($bundle->get('lastModified'), '>=', $startTime, '... updates lastModified'); is( $bundle->addFile('CSS', 'http://mysite.com/script.js'), @@ -145,6 +150,7 @@ cmp_deeply( my @fileIds = map { $_->{fileId} } @{ $bundle->get('jsFiles') }; +$bundle->update({lastModified => 0}); ok($bundle->moveFileDown('JS', $fileIds[0]), 'moveFileDown returns 1 for a successful move'); @fileUris = map { $_->{uri} } @{ $bundle->get('jsFiles') }; cmp_deeply( @@ -156,7 +162,9 @@ cmp_deeply( }], '... checking the actual order of js files' ); +cmp_ok($bundle->get('lastModified'), '>=', $startTime, '... updates lastModified'); +$bundle->update({lastModified => 0}); ok($bundle->moveFileUp('JS', $fileIds[2]), 'moveFileUp returns 1 for a successful move'); @fileUris = map { $_->{uri} } @{ $bundle->get('jsFiles') }; cmp_deeply( @@ -168,6 +176,7 @@ cmp_deeply( }], '... checking the actual order of js files' ); +cmp_ok($bundle->get('lastModified'), '>=', $startTime, '... updates lastModified'); ################################################################### # @@ -194,6 +203,7 @@ cmp_deeply( ); @fileIds = map { $_->{fileId} } @{ $bundle->get('jsFiles') }; +$bundle->update({lastModified => 0}); $bundle->deleteFile('JS', $fileIds[1]); @fileUris = map { $_->{uri} } @{ $bundle->get('jsFiles') }; cmp_deeply( @@ -204,6 +214,7 @@ cmp_deeply( }], '... checking the actual deletion of js files' ); +cmp_ok($bundle->get('lastModified'), '>=', $startTime, '... updates lastModified'); $bundle->delete; From b8711513a6782e1463c11ecb9067fc247d75f56f Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Fri, 8 May 2009 17:13:56 -0700 Subject: [PATCH 14/38] First cut at fetch methods for file, URL and asset. --- lib/WebGUI/FilePump/Bundle.pm | 90 +++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/lib/WebGUI/FilePump/Bundle.pm b/lib/WebGUI/FilePump/Bundle.pm index 05714ef9c..5c9232c4c 100644 --- a/lib/WebGUI/FilePump/Bundle.pm +++ b/lib/WebGUI/FilePump/Bundle.pm @@ -7,6 +7,7 @@ use URI; use Path::Class::Dir; use CSS::Minifier::XS; use JavaScript::Minifier::XS; +use LWP; #------------------------------------------------------------------- @@ -79,6 +80,9 @@ sub build { my ($self) = @_; my $lastBuild = time(); my $originalBuild = $self->get('lastBuild'); + + ##Whole lot of building + $self->update({lastBuild => $lastBuild}); return 1; } @@ -247,6 +251,92 @@ sub deleteFile { #------------------------------------------------------------------- +=head2 fetchAsset ( $uri ) + +Fetches a bundle file from a WebGUI Asset (probably a snippet) in this site. Returns a hashref +with the content and date that it was lastUpdated. If the Asset cannot be found with that URL, +it returns an empty hashref. + +=head3 $uri + +A valid asset URI. + +=cut + +sub fetchAsset { + my ($self, $uri ) = @_; + my $url = URI->new($uri)->opaque; + my $asset = WebGUI::Asset->newByUrl($self->session, $url); + return {} unless $asset; + ##Check for a snippet, or snippet subclass? + my $guts = { + lastModified => $asset->get('lastModified'), + content => $asset->view(1), + }; + return $guts; +} + +#------------------------------------------------------------------- + +=head2 fetchFile ( $uri ) + +Fetches a bundle file from the local filesystem. Returns a hashref +with the content and date that it was last updated. If there is any problem +with getting the file, it returns an empty hashref. + + +=head3 $uri + +A valid filesystem URI. + +=cut + +sub fetchFile { + my ($self, $uri ) = @_; + + my $guts = { + }; + return $guts; +} + +#------------------------------------------------------------------- + +=head2 fetchHttp ( $uri ) + +Fetches a bundle file from the web. Returns a hashref with the content +and date that it was last updated. If there is any problem with making +the request, it returns an empty hashref. + +=head3 $uri + +A valid web URI. + +=cut + +sub fetchHttp { + my ($self, $uri ) = @_; + + # Set up LWP + my $userAgent = LWP::UserAgent->new; + $userAgent->env_proxy; + $userAgent->agent("WebGUI"); + + # Create a request and stuff the uri in it + my $request = HTTP::Request->new( GET => $uri ); + my $response = $userAgent->request($request); + + if (! $response->is_success) { + return {}; + } + my $guts = { + lastModified => $response->header('lastModified'), + content => $response->content(), + }; + return $guts; +} + +#------------------------------------------------------------------- + =head2 getCollateral ( tableName, keyName, keyValue ) Returns a hash reference containing one row of collateral data from a particular From cc6537b99b3631dcbffb212a6a11c6f7651bccef Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Wed, 13 May 2009 10:02:19 -0700 Subject: [PATCH 15/38] checkin test cleanup and comments --- t/FilePump/Bundle.t | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/t/FilePump/Bundle.t b/t/FilePump/Bundle.t index ac8f45a38..953bf0d4c 100644 --- a/t/FilePump/Bundle.t +++ b/t/FilePump/Bundle.t @@ -19,6 +19,7 @@ use lib "$FindBin::Bin/../lib"; use Test::More; use Test::Deep; use Data::Dumper; +use URI; use WebGUI::Test; # Must use this before any other WebGUI modules use WebGUI::Session; @@ -216,6 +217,18 @@ cmp_deeply( ); cmp_ok($bundle->get('lastModified'), '>=', $startTime, '... updates lastModified'); +################################################################### +# +# fetchAsset +# +################################################################### + +################################################################### +# +# delete +# +################################################################### + $bundle->delete; } From ac77f6f1da27317ff38ca861bff0f86a405cd674 Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Thu, 14 May 2009 22:18:52 -0700 Subject: [PATCH 16/38] Add tagsToRollback to WebGUI::Test --- t/lib/WebGUI/Test.pm | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/t/lib/WebGUI/Test.pm b/t/lib/WebGUI/Test.pm index 1136ed2e3..37216aa4d 100644 --- a/t/lib/WebGUI/Test.pm +++ b/t/lib/WebGUI/Test.pm @@ -55,6 +55,7 @@ my $originalSetting; my @groupsToDelete; my @usersToDelete; my @storagesToDelete; +my @tagsToRollback; BEGIN { @@ -157,6 +158,9 @@ END { $stor->delete; } } + foreach my $tag (@tagsToRollback) { + $tag->rollback; + } if ($ENV{WEBGUI_TEST_DEBUG}) { $Test->diag('Sessions: '.$SESSION->db->quickScalar('select count(*) from userSession')); $Test->diag('Scratch : '.$SESSION->db->quickScalar('select count(*) from userSessionScratch')); @@ -423,6 +427,21 @@ sub storagesToDelete { #---------------------------------------------------------------------------- +=head2 tagsToRollback ( $tag ) + +Push a list of version tags to rollback at the end of the test. + +This is a class method. + +=cut + +sub tagsToRollback { + my $class = shift; + push @tagsToRollback, @_; +} + +#---------------------------------------------------------------------------- + =head2 usersToDelete ( $user, [$user, ...] ) Push a list of user objects onto the stack of groups to be automatically deleted From aef2fd3a75894ad9264aeebf865e4fcc61b8478e Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Thu, 14 May 2009 22:19:44 -0700 Subject: [PATCH 17/38] fetch, fetch* with some tests. Refactor getPathClassDir, add build. --- lib/WebGUI/FilePump/Bundle.pm | 154 ++++++++++++++++++++++++++++++---- t/FilePump/Bundle.t | 64 +++++++++++++- 2 files changed, 198 insertions(+), 20 deletions(-) diff --git a/lib/WebGUI/FilePump/Bundle.pm b/lib/WebGUI/FilePump/Bundle.pm index 5c9232c4c..f1b796868 100644 --- a/lib/WebGUI/FilePump/Bundle.pm +++ b/lib/WebGUI/FilePump/Bundle.pm @@ -4,7 +4,7 @@ use base qw/WebGUI::Crud/; use WebGUI::International; use WebGUI::Utility; use URI; -use Path::Class::Dir; +use Path::Class; use CSS::Minifier::XS; use JavaScript::Minifier::XS; use LWP; @@ -82,8 +82,56 @@ sub build { my $originalBuild = $self->get('lastBuild'); ##Whole lot of building + my $error = undef; - $self->update({lastBuild => $lastBuild}); + ##JavaScript first + my $jsFiles = $self->get('jsFiles'); + my $concatenatedJS = ''; + JSFILE: foreach my $jsFile (@{ $jsFiles }) { + my $uri = $jsFile->{uri}; + my $results = $self->fetch($uri); + if (! $results->{content}) { + $error = $uri; + last JSFILE; + } + $concatenatedJS .= $results->{content}; + $jsFile->{lastModified} = $results->{lastModified}; + } + return (0, $error) if ($error); + + ##CSS next + my $cssFiles = $self->get('cssFiles'); + my $concatenatedCSS = ''; + CSSFILE: foreach my $cssFile (@{ $cssFiles }) { + my $uri = $cssFile->{uri}; + my $results = $self->fetch($uri); + if (! $results->{content}) { + $error = $uri; + last CSSFILE; + } + $concatenatedCSS .= $results->{content}; + $cssFile->{lastModified} = $results->{lastModified}; + } + + return (0, $error) if ($error); + + ##Create the new build directory + + ##Minimize files, and write them out. + + my $minimizedJS = JS::Minimizer::XS::minimize($concatenatedJS); + undef $concatenatedJS; + + my $minimizedCSS = CSS::Minimizer::XS::minimize($concatenatedCSS); + undef $concatenatedCSS; + + ##Delete the old build directory and update myself with the new data. + $self->deleteBuild(); + $self->update({ + jsFiles => $jsFiles, + cssFiles => $cssFiles, + lastBuild => $lastBuild, + }); return 1; } @@ -176,10 +224,23 @@ files. =cut sub delete { + my ($self) = @_; + $self->deleteBuild; + return $self->SUPER::delete; +} + +#------------------------------------------------------------------- + +=head2 deleteBuild ( ) + +Delete the build as specified by the Bundle's current lastBuild timestamp; + +=cut + +sub deleteBuild { my ($self) = @_; my $bundleDir = $self->getPathClassDir(); $bundleDir->rmtree(); - return $self->SUPER::delete; } #------------------------------------------------------------------- @@ -251,28 +312,74 @@ sub deleteFile { #------------------------------------------------------------------- -=head2 fetchAsset ( $uri ) +=head2 fetch ( $uri ) -Fetches a bundle file from a WebGUI Asset (probably a snippet) in this site. Returns a hashref -with the content and date that it was lastUpdated. If the Asset cannot be found with that URL, -it returns an empty hashref. +Based on the scheme of the URI, dispatch the URI to the correct method +to handle it. Returns the results of the method. =head3 $uri -A valid asset URI. +A uri, of the form accepted by URI. + +=cut + +sub fetch { + my ($self, $uri ) = @_; + my $guts = {}; + my $urio = URI->new($uri); + my $scheme = $urio->scheme; + if ($scheme eq 'http' or $scheme eq 'https') { + $guts = $self->fetchHttp($urio); + } + elsif ($scheme eq 'asset') { + $guts = $self->fetchAsset($urio); + } + elsif ($scheme eq 'file') { + $guts = $self->fetchFile($urio); + } + return $guts; +} + +#------------------------------------------------------------------- + +=head2 fetchAsset ( $uri ) + +Fetches a bundle file from a WebGUI Asset (probably a snippet) in this site. +If the Asset cannot be found with that URL, it returns an empty hashref. +Depending on the type of Asset fetched, there will be different fields. Every +kind of asset will have the lastModified field. + +Snippet assets will have a content field with the contents of the Snippet inside +of it. + +File assets will have a content field with the contents of the file. + +Any other kind of asset will return an empty content field. + +=head3 $uri + +A URI object. =cut sub fetchAsset { my ($self, $uri ) = @_; - my $url = URI->new($uri)->opaque; + + my $url = $uri->opaque; + $url =~ s{^/+}{}; my $asset = WebGUI::Asset->newByUrl($self->session, $url); return {} unless $asset; ##Check for a snippet, or snippet subclass? my $guts = { lastModified => $asset->get('lastModified'), - content => $asset->view(1), + content => '', }; + if ($asset->isa('WebGUI::Asset::Snippet')) { + $guts->{content} = $asset->view(1); + } + elsif ($asset->isa('WebGUI::Asset::File')) { + $guts->{content} = $asset->getStorageLocation->getFileContentsAsScalar($asset->get('filename')); + } return $guts; } @@ -284,18 +391,24 @@ Fetches a bundle file from the local filesystem. Returns a hashref with the content and date that it was last updated. If there is any problem with getting the file, it returns an empty hashref. - =head3 $uri -A valid filesystem URI. +A URI object. =cut sub fetchFile { my ($self, $uri ) = @_; - + my $filepath = $uri->path; + return {} unless (-e $filepath && -r _); + my @stats = stat(_); # recycle stat data from file tests. + open my $file, '<', $filepath or return {}; + local $/; my $guts = { + lastModified => $stats[9], + content => <$file>, }; + close $file; return $guts; } @@ -309,7 +422,7 @@ the request, it returns an empty hashref. =head3 $uri -A valid web URI. +A URI object. =cut @@ -353,7 +466,7 @@ The name of a key in the collateral hash. Typically a unique identifier for a g =head3 keyValue -Along with keyName, determines which "row" of collateral data to delete. +Along with keyName, determines which "row" of collateral data to get. If this is equal to "new", then an empty hashRef will be returned to avoid strict errors in the caller. If the requested data does not exist in the collateral array, it also returns an empty hashRef. @@ -412,19 +525,24 @@ sub getCollateralDataIndex { #------------------------------------------------------------------- -=head2 getPathClassDir ( ) +=head2 getPathClassDir ( $otherBuild ) Returns a Path::Class::Dir object to the last build directory for this bundle. +=head3 $otherBuild + +Another time stamp to use instead of the lastModified timestamp. + =cut sub getPathClassDir { - my ($self) = @_; + my ($self, $lastBuild) = @_; + $lastBuild ||= $self->get('lastBuild'); return Path::Class::Dir->new( $self->session->config->get('uploadsPath'), 'filepump', - $self->get('bundleName') . $self->get('lastBuild') + $self->get('bundleName') . $lastBuild ); } diff --git a/t/FilePump/Bundle.t b/t/FilePump/Bundle.t index 953bf0d4c..d684c9ff4 100644 --- a/t/FilePump/Bundle.t +++ b/t/FilePump/Bundle.t @@ -32,7 +32,7 @@ my $session = WebGUI::Test->session; #---------------------------------------------------------------------------- # Tests -my $tests = 28; # Increment this number for each test you create +my $tests = 31; # Increment this number for each test you create plan tests => 1 + $tests; # 1 for the use_ok #---------------------------------------------------------------------------- @@ -219,7 +219,67 @@ cmp_ok($bundle->get('lastModified'), '>=', $startTime, '... updates lastModified ################################################################### # -# fetchAsset +# fetch +# +################################################################### + +my $root = WebGUI::Asset->getRoot($session); + +my $snippet = $root->addChild({ + className => 'WebGUI::Asset::Snippet', + url => 'filePumpSnippet', + snippet => 'Pump a Snippet', +}); + +my $fileAsset = $root->addChild({ + className => 'WebGUI::Asset::File', + url => 'filePumpFileAsset', + filename => 'pumpfile', +}); + +$fileAsset->getStorageLocation->addFileFromScalar('pumpfile', 'Pump up the jam'); + +my $snippetTag = WebGUI::VersionTag->getWorking($session); +WebGUI::Test->tagsToRollback($snippetTag); +$snippetTag->commit; + +my $guts; +$guts = $bundle->fetchAsset(URI->new('asset://filePumpSnippet')); +cmp_deeply( + $guts, + { + content => 'Pump a Snippet', + lastModified => re('^\d+$'), + }, + 'fetchAsset: retrieved a snippet' +); + +$guts = $bundle->fetchAsset(URI->new('asset://filePumpFileAsset')); +cmp_deeply( + $guts, + { + content => 'Pump up the jam', + lastModified => re('^\d+$'), + }, + 'fetchAsset: retrieved a file asset' +); + +my $path = $fileAsset->getStorageLocation->getPath($fileAsset->get('filename')); +my $urilet = URI->new('file:'.$path); + +$guts = $bundle->fetchFile($urilet); +cmp_deeply( + $guts, + { + content => 'Pump up the jam', + lastModified => re('^\d+$'), + }, + 'fetchFile: retrieved a file from the filesystem' +); + +################################################################### +# +# build # ################################################################### From 68378665be58b2dfa78267a904716ffb8378f230 Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Fri, 15 May 2009 09:54:08 -0700 Subject: [PATCH 18/38] getPathClassDir tests --- t/FilePump/Bundle.t | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/t/FilePump/Bundle.t b/t/FilePump/Bundle.t index d684c9ff4..7d0c1748c 100644 --- a/t/FilePump/Bundle.t +++ b/t/FilePump/Bundle.t @@ -32,7 +32,7 @@ my $session = WebGUI::Test->session; #---------------------------------------------------------------------------- # Tests -my $tests = 31; # Increment this number for each test you create +my $tests = 35; # Increment this number for each test you create plan tests => 1 + $tests; # 1 for the use_ok #---------------------------------------------------------------------------- @@ -277,6 +277,30 @@ cmp_deeply( 'fetchFile: retrieved a file from the filesystem' ); +################################################################### +# +# getPathClassDir +# +################################################################### + +my $dir = $bundle->getPathClassDir(); +isa_ok($dir, 'Path::Class::Dir'); +my $timestampDir = $dir->dir_list(-1, 1); +cmp_deeply( + [ split /\./, $timestampDir ], + [ 'new-bundle', 0 ], + '... directory has correct name and timestamp' +); + +$dir = $bundle->getPathClassDir(997966800); +isa_ok($dir, 'Path::Class::Dir'); +$timestampDir = $dir->dir_list(-1, 1); +cmp_deeply( + [ split /\./, $timestampDir ], + [ 'new-bundle', 997966800 ], + '... directory has correct name and timestamp when timestamp is specified' +); + ################################################################### # # build From 2db7f98169bcdbc2f81ea26a68bfed2a93d38046 Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Fri, 15 May 2009 09:54:22 -0700 Subject: [PATCH 19/38] Create the bundle directory, with error handling. --- lib/WebGUI/FilePump/Bundle.pm | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/WebGUI/FilePump/Bundle.pm b/lib/WebGUI/FilePump/Bundle.pm index f1b796868..174ddcbf1 100644 --- a/lib/WebGUI/FilePump/Bundle.pm +++ b/lib/WebGUI/FilePump/Bundle.pm @@ -78,7 +78,7 @@ the method returns 0, along with an error message. sub build { my ($self) = @_; - my $lastBuild = time(); + my $newBuild = time(); my $originalBuild = $self->get('lastBuild'); ##Whole lot of building @@ -116,6 +116,14 @@ sub build { return (0, $error) if ($error); ##Create the new build directory + my $newDir = $self->getPathClassBuild($newBuild); + my $mkpathErrors; + my $dirsCreated = $newDir->mkpath({ errors => $mkpathErrors }); + if (! $dirsCreated) { + $newDir->rmtree; + my $errorMessages = join "\n", @{ $mkpathErrors }; + return (0, $errorMessages); + } ##Minimize files, and write them out. @@ -130,7 +138,7 @@ sub build { $self->update({ jsFiles => $jsFiles, cssFiles => $cssFiles, - lastBuild => $lastBuild, + lastBuild => $newBuild, }); return 1; } @@ -542,7 +550,7 @@ sub getPathClassDir { return Path::Class::Dir->new( $self->session->config->get('uploadsPath'), 'filepump', - $self->get('bundleName') . $lastBuild + $self->session->url->urlize($self->get('bundleName')) . '.' . $lastBuild ); } From 285b62212ed735411c411bb003d4c3654664edcf Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Fri, 15 May 2009 14:12:36 -0700 Subject: [PATCH 20/38] build code, with tests. build works! --- lib/WebGUI/FilePump/Bundle.pm | 55 ++++++++++++++++++++++++++++++++--- t/FilePump/Bundle.t | 46 +++++++++++++++++++++++++++-- 2 files changed, 94 insertions(+), 7 deletions(-) diff --git a/lib/WebGUI/FilePump/Bundle.pm b/lib/WebGUI/FilePump/Bundle.pm index 174ddcbf1..8cb10c2c9 100644 --- a/lib/WebGUI/FilePump/Bundle.pm +++ b/lib/WebGUI/FilePump/Bundle.pm @@ -116,7 +116,7 @@ sub build { return (0, $error) if ($error); ##Create the new build directory - my $newDir = $self->getPathClassBuild($newBuild); + my $newDir = $self->getPathClassDir($newBuild); my $mkpathErrors; my $dirsCreated = $newDir->mkpath({ errors => $mkpathErrors }); if (! $dirsCreated) { @@ -127,12 +127,22 @@ sub build { ##Minimize files, and write them out. - my $minimizedJS = JS::Minimizer::XS::minimize($concatenatedJS); + my $minimizedJS = JavaScript::Minifier::XS::minify($concatenatedJS); undef $concatenatedJS; - my $minimizedCSS = CSS::Minimizer::XS::minimize($concatenatedCSS); + my $minimizedCSS = CSS::Minifier::XS::minify($concatenatedCSS); undef $concatenatedCSS; + my $flatJsFile = $newDir->file($self->bundleUrl . '.js'); + my $jsFH = $flatJsFile->open('>'); + print $jsFH $minimizedJS; + close $jsFH; + + my $flatCssFile = $newDir->file($self->bundleUrl . '.css'); + my $cssFH = $flatCssFile->open('>'); + print $cssFH $minimizedCSS; + close $cssFH; + ##Delete the old build directory and update myself with the new data. $self->deleteBuild(); $self->update({ @@ -286,6 +296,30 @@ sub deleteCollateral { #------------------------------------------------------------------- +=head2 deleteFiles ( $type ) + +Deletes all files of the requested type. + +=head3 $type + +If $type is JS, it deletes it from the javascript part of the bundle. If it is +CSS, it deletes it from the CSS part of the bundle. OTHER is used for all other +types of files. + +=cut + +sub deleteFiles { + my ($self, $type) = @_; + return 0, 'Illegal type' unless WebGUI::Utility::isIn($type, 'JS', 'CSS', 'OTHER'); + my $collateralType = $type eq 'JS' ? 'jsFiles' + : $type eq 'CSS' ? 'cssFiles' + : 'otherFiles'; + $self->update({$collateralType => []}); + return 1; +} + +#------------------------------------------------------------------- + =head2 deleteFile ( $type, $fileId ) Deletes a file of the requested type from the bundle. @@ -458,6 +492,19 @@ sub fetchHttp { #------------------------------------------------------------------- +=head2 bundleUrl ( ) + +Returns a urlized version of the bundle name, safe for URLs and filenames. + +=cut + +sub bundleUrl { + my ($self) = @_; + return $self->session->url->urlize($self->get('bundleName')); +} + +#------------------------------------------------------------------- + =head2 getCollateral ( tableName, keyName, keyValue ) Returns a hash reference containing one row of collateral data from a particular @@ -550,7 +597,7 @@ sub getPathClassDir { return Path::Class::Dir->new( $self->session->config->get('uploadsPath'), 'filepump', - $self->session->url->urlize($self->get('bundleName')) . '.' . $lastBuild + $self->bundleUrl . '.' . $lastBuild ); } diff --git a/t/FilePump/Bundle.t b/t/FilePump/Bundle.t index 7d0c1748c..fa6bcfaf1 100644 --- a/t/FilePump/Bundle.t +++ b/t/FilePump/Bundle.t @@ -24,6 +24,7 @@ use WebGUI::Test; # Must use this before any other WebGUI modules use WebGUI::Session; my $startTime = time(); +my $wgBday = 997966800; #---------------------------------------------------------------------------- # Init @@ -32,7 +33,7 @@ my $session = WebGUI::Test->session; #---------------------------------------------------------------------------- # Tests -my $tests = 35; # Increment this number for each test you create +my $tests = 45; # Increment this number for each test you create plan tests => 1 + $tests; # 1 for the use_ok #---------------------------------------------------------------------------- @@ -292,21 +293,60 @@ cmp_deeply( '... directory has correct name and timestamp' ); -$dir = $bundle->getPathClassDir(997966800); +$dir = $bundle->getPathClassDir($wgBday); isa_ok($dir, 'Path::Class::Dir'); $timestampDir = $dir->dir_list(-1, 1); cmp_deeply( [ split /\./, $timestampDir ], - [ 'new-bundle', 997966800 ], + [ 'new-bundle', $wgBday ], '... directory has correct name and timestamp when timestamp is specified' ); +################################################################### +# +# deleteFiles +# +################################################################### + +$bundle->deleteFiles('JS'); +$bundle->deleteFiles('CSS'); + +cmp_deeply($bundle->get('jsFiles'), [], ' deleteFiles deleted all JS URIs'); +cmp_deeply($bundle->get('cssFiles'), [], ' ... deleted all CSS URIs'); + ################################################################### # # build # ################################################################### +my $oldBuildDir = $bundle->getPathClassDir($wgBday); +$oldBuildDir->mkpath; + +ok(-e $oldBuildDir->stringify && -d _, 'No problems creating old build directory'); +$bundle->update({lastBuild => $wgBday}); + +$snippet->update({snippet => qq|\n\nfunction doNothing()\n{ var foo = 'bar';} |}); + +$fileAsset->getStorageLocation->deleteFile('pumpfile'); +$fileAsset->getStorageLocation->addFileFromScalar('pumpfile.css', qq| body {\npadding: 0px;}\n\n|); +$fileAsset->update({filename => 'pumpfile.css'}); + +$bundle->addFile('JS', 'asset://filePumpSnippet'); +$bundle->addFile('CSS', 'asset://filePumpFileAsset'); +my ($buildFlag, $error) = $bundle->build(); +ok($buildFlag, 'build returns true when there are no errors'); +isnt($bundle->get('lastBuild'), $wgBday, '... lastBuild time updated'); + +my $buildDir = $bundle->getPathClassDir(); +isnt($buildDir->stringify, $oldBuildDir->stringify, '... build directory did actually change'); +ok(-e $buildDir->stringify && -d _, '... new build directory created'); +ok(!-e $oldBuildDir->stringify && !-d _, '... old build directory deleted'); +my $jsFile = $buildDir->file($bundle->bundleUrl . '.js'); +my $cssFile = $buildDir->file($bundle->bundleUrl . '.css'); +ok(-e $jsFile->stringify && -f _ && -s _, '... minified JS file built, not empty'); +ok(-e $cssFile->stringify && -f _ && -s _, '... minified CSS file built, not empty'); + ################################################################### # # delete From a4cb3c2aac2d2cfd384f57d7d426a13fe2c0ae85 Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Fri, 15 May 2009 16:42:21 -0700 Subject: [PATCH 21/38] FilePump content handler. --- lib/WebGUI/Content/FilePump.pm | 49 ++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 lib/WebGUI/Content/FilePump.pm diff --git a/lib/WebGUI/Content/FilePump.pm b/lib/WebGUI/Content/FilePump.pm new file mode 100644 index 000000000..0433e958b --- /dev/null +++ b/lib/WebGUI/Content/FilePump.pm @@ -0,0 +1,49 @@ +package WebGUI::Content::FilePump; + +use strict; +use WebGUI::AdminConsole; +use WebGUI::Exception; +use WebGUI::FilePump::Admin; + +=head1 NAME + +Package WebGUI::Content::FilePump + +=head1 DESCRIPTION + +Handle all requests for building and editing FilePump bundles + +=head1 SYNOPSIS + + use WebGUI::Content::FilePump; + my $output = WebGUI::Content::FilePump::handler($session); + +=head1 SUBROUTINES + +These subroutines are available from this package: + +=cut + +#------------------------------------------------------------------- + +=head2 handler ( session ) + +The content handler for this package. + +=cut + +sub handler { + my ($session) = @_; + my $output = undef; + return undef unless $session->form->get('op') eq 'filePump'; + my $function = "www_".$session->form->get('func'); + if ($function ne "www_" && (my $sub = WebGUI::FilePump::Admin->can($function))) { + $output = $sub->($session); + } + else { + WebGUI::Error::MethodNotFound->throw(error=>"Couldn't call non-existant method $function inside FilePump", method=>$function); + } + return $output; +} + +1; From fe221e87ab0f1c9132cdc8909028a8a67a819788 Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Fri, 15 May 2009 18:39:02 -0700 Subject: [PATCH 22/38] Adding FilePump admin module. --- lib/WebGUI/FilePump/Admin.pm | 178 +++++++++++++++++++++++++++++++++++ 1 file changed, 178 insertions(+) create mode 100644 lib/WebGUI/FilePump/Admin.pm diff --git a/lib/WebGUI/FilePump/Admin.pm b/lib/WebGUI/FilePump/Admin.pm new file mode 100644 index 000000000..5bd1417f0 --- /dev/null +++ b/lib/WebGUI/FilePump/Admin.pm @@ -0,0 +1,178 @@ +package WebGUI::FilePump::Admin; + +use strict; +use Tie::IxHash; +use WebGUI::AdminConsole; +use WebGUI::HTMLForm; +use WebGUI::International; +use WebGUI::Pluggable; +use WebGUI::Utility; + +=head1 NAME + +Package WebGUI::FilePump::Admin + +=head1 DESCRIPTION + +Web interface for making, building, and editing FilePump bundles. + +=cut + +#---------------------------------------------------------------------------- + +=head2 canView ( session [, user] ) + +Returns true if the user can administrate this operation. user defaults to +the current user. + +=cut + +sub canView { + my $session = shift; + my $user = shift || $session->user; + return $user->isInGroup( 3 ); +} + +#------------------------------------------------------------------- + +=head2 www_deleteBundle ( ) + +Deletes a bundle, identified by the form variable, bundleId. + +=cut + +sub www_deleteBundle { + my $session = shift; + return $session->privilege->insufficient() unless canView($session); + my $bundle = WebGUI::FilePump::Bundle->new($session, $session->form->get("bundleId")); + if (defined $bundle) { + $bundle->delete; + } + return www_manage($session); +} + +#------------------------------------------------------------------ + +=head2 www_demoteFile ( session ) + +Moves a bundle file down one position. The kind of file is set by the form variable filetype, +the id of the bundle is bundleId, and the id of the file to move is fileId. + +=head3 session + +A reference to the current session. + +=cut + +sub www_demoteFile { + my $session = shift; + return $session->privilege->insufficient() unless canView($session); + my $bundle = WebGUI::FilePump::Bundle->new($session, $session->form->get("bundleId")); + if (defined $bundle) { + } + return www_manage($session); +} + +#------------------------------------------------------------------- + +=head2 www_editBundle ( ) + +Displays a form to add or edit a bundle. + +=cut + +sub www_editBundle { + my ($session, $error) = @_; + return $session->privilege->insufficient() unless canView($session); + + if ($error) { + $error = qq|
$error
\n|; + } + ##Make a PassiveAnalytics rule to use to populate the form. + my $bundleId = $session->form->get('bundleId'); + my $bundle; + if ($bundleId) { + $bundle = WebGUI::FilePump::Bundle->new($session, $bundle); + } + else { + ##We need a temporary rule so that we can call dynamicForm, below + $bundleId = 'new'; + $bundle = WebGUI::FilePump::Bundle->create($session, {}); + } + + ##Build the form + my $form = WebGUI::HTMLForm->new($session); + $form->hidden( name=>"op", value=>"filePump"); + $form->hidden( name=>"func", value=>"filePumpSave"); + $form->hidden( name=>"bundleId", value=>$bundleId); + $form->dynamicForm([WebGUI::FilePump::Bundle->crud_definition($session)], 'properties', $bundle); + $form->submit; + + my $i18n = WebGUI::International->new($session, 'PassiveAnalytics'); + my $ac = WebGUI::AdminConsole->new($session,'passiveAnalytics'); + $ac->addSubmenuItem($session->url->page("op=passiveAnalytics;func=editRuleflow"), $i18n->get("manage ruleset")); + if ($ruleId eq 'new') { + $rule->delete; + } + return $ac->render($error.$form->print,$i18n->get('Edit Rule')); +} + +#------------------------------------------------------------------- + +=head2 www_editBundleSave ( ) + +Saves the results of www_editBundle(). + +=cut + +sub www_editBundleSave { + my $session = shift; + my $form = $session->form; + return $session->privilege->insufficient() unless canView($session); + return www_manage($session); +} + +#------------------------------------------------------------------ + +=head2 www_promoteFile ( session ) + +Moves a bundle file up one position. The kind of file is set by the form variable filetype, +the id of the bundle is bundleId, and the id of the file to move is fileId. + +=head3 session + +A reference to the current session. + +=cut + +sub www_promoteFile { + my $session = shift; + return $session->privilege->insufficient() unless canView($session); + my $bundle = WebGUI::FilePump::Bundle->new($session, $session->form->get("bundleId")); + if (defined $bundle) { + } + return www_manage($session); +} + +#------------------------------------------------------------------- + +=head2 www_manage ( session ) + +Display a list of available bundles. Provide ways to add, edit and delete them. + +=head3 $session + +A WebGUI session object. + +=cut + +sub www_manage { + my $session = shift; + return $session->privilege->insufficient() unless canView($session); + my $ac = WebGUI::AdminConsole->new($session,'passiveAnalytics'); + $ac->addSubmenuItem($session->url->page('op=filePump;func=add'), $i18n->get('add a bundle')); + return $ac->render($error.$f->print.$addmenu.$steps, 'Passive Analytics'); +} + + +1; From 3da92cdd842ad6cb37ee62c207d4c1fcea03e8d6 Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Fri, 15 May 2009 18:39:41 -0700 Subject: [PATCH 23/38] Test that the build directory was deleted. --- t/FilePump/Bundle.t | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/t/FilePump/Bundle.t b/t/FilePump/Bundle.t index fa6bcfaf1..2e7536ec8 100644 --- a/t/FilePump/Bundle.t +++ b/t/FilePump/Bundle.t @@ -33,7 +33,7 @@ my $session = WebGUI::Test->session; #---------------------------------------------------------------------------- # Tests -my $tests = 45; # Increment this number for each test you create +my $tests = 46; # Increment this number for each test you create plan tests => 1 + $tests; # 1 for the use_ok #---------------------------------------------------------------------------- @@ -354,6 +354,7 @@ ok(-e $cssFile->stringify && -f _ && -s _, '... minified CSS file built, not emp ################################################################### $bundle->delete; +ok(!-e $buildDir->stringify && !-d _, 'delete deletes the current build directory deleted'); } From 4753442b0c935a494dbb2d037f89015c3a91f34d Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Fri, 15 May 2009 18:40:10 -0700 Subject: [PATCH 24/38] Add the FilePump settings admin group. --- lib/WebGUI/Operation/Settings.pm | 1 + sbin/installFilePump.pl | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/lib/WebGUI/Operation/Settings.pm b/lib/WebGUI/Operation/Settings.pm index 41677cc83..606f97313 100644 --- a/lib/WebGUI/Operation/Settings.pm +++ b/lib/WebGUI/Operation/Settings.pm @@ -482,6 +482,7 @@ sub definition { groupIdAdminCache groupIdAdminCron groupIdAdminDatabaseLink + groupIdAdminFilePump groupIdAdminGraphics groupIdAdminGroup groupIdAdminGroupAdmin diff --git a/sbin/installFilePump.pl b/sbin/installFilePump.pl index 00156e838..1fd653390 100644 --- a/sbin/installFilePump.pl +++ b/sbin/installFilePump.pl @@ -36,6 +36,7 @@ my $session = start( $webguiRoot, $configFile ); installFilePumpHandler($session); installFilePumpTable($session); +installFilePumpAdminGroup($session); # Do your work here finish($session); @@ -43,6 +44,18 @@ finish($session); #---------------------------------------------------------------------------- # Your sub here +#---------------------------------------------------------------------------- +sub installFileAdminGroup { + my $session = shift; + print "\tAdding FilePump admin group setting... \n" unless $quiet; + ##Content Handler + if (! $session->setting->has('groupIdAdminFilePump')) { + $session->setting->add('groupIdAdminFilePump','8'); + print "\tAdded FilePump admin group ... \n" unless $quiet; + } + print "Done.\n" unless $quiet; +} + #---------------------------------------------------------------------------- sub installFilePumpHandler { my $session = shift; From a03bab37ea0eb9c56726d3f0fbacc4616c32c5fb Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Fri, 15 May 2009 21:29:43 -0700 Subject: [PATCH 25/38] Reduce the install code. --- sbin/installFilePump.pl | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/sbin/installFilePump.pl b/sbin/installFilePump.pl index 1fd653390..5cd04539a 100644 --- a/sbin/installFilePump.pl +++ b/sbin/installFilePump.pl @@ -45,14 +45,14 @@ finish($session); # Your sub here #---------------------------------------------------------------------------- -sub installFileAdminGroup { +sub installFilePumpAdminGroup { my $session = shift; print "\tAdding FilePump admin group setting... \n" unless $quiet; ##Content Handler - if (! $session->setting->has('groupIdAdminFilePump')) { + #if (! $session->setting->has('groupIdAdminFilePump')) { $session->setting->add('groupIdAdminFilePump','8'); print "\tAdded FilePump admin group ... \n" unless $quiet; - } + #} print "Done.\n" unless $quiet; } @@ -62,15 +62,8 @@ sub installFilePumpHandler { print "\tAdding FilePump content handler... \n" unless $quiet; ##Content Handler my $contentHandlers = $session->config->get('contentHandlers'); - if (! isIn('WebGUI::Content::FilePump', @{ $contentHandlers }) ) { - my @newHandlers = (); - foreach my $handler (@{ $contentHandlers }) { - push @newHandlers, $handler; - push @newHandlers, 'WebGUI::Content::FilePump' if - $handler eq 'WebGUI::Content::AssetHistory'; - } - $session->config->set('contentHandlers', \@newHandlers); - } + $session->config->addToArray('contentHandlers', 'WebGUI::Content::FilePump'); + ##Admin Console $session->config->addToHash('adminConsole', 'filePump', { "icon" => "filePump.gif", @@ -88,7 +81,6 @@ sub installFilePumpHandler { sub installFilePumpTable { my $session = shift; print "\tAdding FilePump database table via CRUD... \n" unless $quiet; - ##Content Handler WebGUI::FilePump::Bundle->crud_createTable($session); print "Done.\n" unless $quiet; } From bff9c617ac143e60dc0e0f57bed06928f3b133d0 Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Fri, 15 May 2009 21:30:21 -0700 Subject: [PATCH 26/38] Fix the content handler. --- lib/WebGUI/Content/FilePump.pm | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/WebGUI/Content/FilePump.pm b/lib/WebGUI/Content/FilePump.pm index 0433e958b..937a07d66 100644 --- a/lib/WebGUI/Content/FilePump.pm +++ b/lib/WebGUI/Content/FilePump.pm @@ -36,12 +36,15 @@ sub handler { my ($session) = @_; my $output = undef; return undef unless $session->form->get('op') eq 'filePump'; - my $function = "www_".$session->form->get('func'); - if ($function ne "www_" && (my $sub = WebGUI::FilePump::Admin->can($function))) { + my $method = $session->form->get( 'method' ) + ? 'www_' . $session->form->get( 'method' ) + : 'www_manage' + ; + if ($method ne "www_" && (my $sub = WebGUI::FilePump::Admin->can($method))) { $output = $sub->($session); } else { - WebGUI::Error::MethodNotFound->throw(error=>"Couldn't call non-existant method $function inside FilePump", method=>$function); + WebGUI::Error::MethodNotFound->throw(error=>"Couldn't call non-existant method $method inside FilePump", method=>$method); } return $output; } From e1329192b98b7004e837b6cbbb4f2bd65adeb1a5 Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Fri, 15 May 2009 21:30:51 -0700 Subject: [PATCH 27/38] labels for filepump group in settings. --- lib/WebGUI/i18n/English/WebGUI.pm | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/WebGUI/i18n/English/WebGUI.pm b/lib/WebGUI/i18n/English/WebGUI.pm index a03ff435a..e8f6e7f93 100644 --- a/lib/WebGUI/i18n/English/WebGUI.pm +++ b/lib/WebGUI/i18n/English/WebGUI.pm @@ -3795,6 +3795,15 @@ LongTruncOk=1

lastUpdated => 0, }, + 'settings groupIdAdminFilePump label' => { + message => q{File Pump}, + lastUpdated => 0, + }, + 'settings groupIdAdminFilePump hoverHelp' => { + message => q{Group to access and manage File Pump bundles.}, + lastUpdated => 0, + }, + 'settings groupIdAdminGroupAdmin label' => { message => q{Groups (limited)}, From ecb3e7d394440ba6e7cba567ab90ff018e1746e3 Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Fri, 15 May 2009 21:31:20 -0700 Subject: [PATCH 28/38] Admin code for manage, and i18n. --- lib/WebGUI/FilePump/Admin.pm | 29 +++++++++++++++++++++-------- lib/WebGUI/i18n/English/FilePump.pm | 18 ++++++++++++++++++ 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/lib/WebGUI/FilePump/Admin.pm b/lib/WebGUI/FilePump/Admin.pm index 5bd1417f0..ba01a7c08 100644 --- a/lib/WebGUI/FilePump/Admin.pm +++ b/lib/WebGUI/FilePump/Admin.pm @@ -7,6 +7,7 @@ use WebGUI::HTMLForm; use WebGUI::International; use WebGUI::Pluggable; use WebGUI::Utility; +use WebGUI::FilePump::Bundle; =head1 NAME @@ -108,13 +109,12 @@ sub www_editBundle { $form->dynamicForm([WebGUI::FilePump::Bundle->crud_definition($session)], 'properties', $bundle); $form->submit; - my $i18n = WebGUI::International->new($session, 'PassiveAnalytics'); - my $ac = WebGUI::AdminConsole->new($session,'passiveAnalytics'); - $ac->addSubmenuItem($session->url->page("op=passiveAnalytics;func=editRuleflow"), $i18n->get("manage ruleset")); - if ($ruleId eq 'new') { - $rule->delete; + my $i18n = WebGUI::International->new($session, 'FilePump'); + my $ac = WebGUI::AdminConsole->new($session,'filePump'); + if ($bundleId eq 'new') { + $bundle->delete; } - return $ac->render($error.$form->print,$i18n->get('Edit Rule')); + return $ac->render($error.$form->print,$i18n->get('Edit Bundle')); } #------------------------------------------------------------------- @@ -169,9 +169,22 @@ A WebGUI session object. sub www_manage { my $session = shift; return $session->privilege->insufficient() unless canView($session); - my $ac = WebGUI::AdminConsole->new($session,'passiveAnalytics'); + my $i18n = WebGUI::International->new($session, 'FilePump'); + my $error = shift; + my $rows = ''; + my $getABundle = WebGUI::FilePump::Bundle->getAllIterator($session,{ orderBy => 'bundleName' } ); + while (my $bundle = $getABundle->()) { + $rows .= sprintf ' %s', $bundle->get('bundleName'); + } + my $output = sprintf <get('bundle name'); + + +%s +
 %s
+EOHTML + my $ac = WebGUI::AdminConsole->new($session,'filePump'); $ac->addSubmenuItem($session->url->page('op=filePump;func=add'), $i18n->get('add a bundle')); - return $ac->render($error.$f->print.$addmenu.$steps, 'Passive Analytics'); + return $ac->render($error.$output, 'File Pump'); } diff --git a/lib/WebGUI/i18n/English/FilePump.pm b/lib/WebGUI/i18n/English/FilePump.pm index d2285307a..2c9cb4a2b 100644 --- a/lib/WebGUI/i18n/English/FilePump.pm +++ b/lib/WebGUI/i18n/English/FilePump.pm @@ -21,6 +21,24 @@ our $I18N = { context => q|Hover help for bundle name.| }, + 'File Pump' => { + message => q|File Pump|, + lastUpdated => 1242439269, + context => q|File Pump is a system for pushing out lots of files at once.| + }, + + 'add a bundle' => { + message => q|Add a Bundle|, + lastUpdated => 1242439269, + context => q|Admin console label. Bundle is a loose set of similar, but not identical objects. Similar to pile.| + }, + + 'Edit Bundle' => { + message => q|Edit Bundle|, + lastUpdated => 1242439269, + context => q|Admin console label. Bundle is a loose set of similar, but not identical objects. Similar to pile.| + }, + }; 1; From c49d53ce88a05a64913b3e2e1872c1d7f909c0c2 Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Fri, 15 May 2009 22:23:36 -0700 Subject: [PATCH 29/38] Add bundles, and display list of bundles. --- lib/WebGUI/Content/FilePump.pm | 8 +- lib/WebGUI/FilePump/Admin.pm | 125 ++++++++++++++-------------- lib/WebGUI/i18n/English/FilePump.pm | 4 +- 3 files changed, 70 insertions(+), 67 deletions(-) diff --git a/lib/WebGUI/Content/FilePump.pm b/lib/WebGUI/Content/FilePump.pm index 937a07d66..5964bfef0 100644 --- a/lib/WebGUI/Content/FilePump.pm +++ b/lib/WebGUI/Content/FilePump.pm @@ -36,15 +36,15 @@ sub handler { my ($session) = @_; my $output = undef; return undef unless $session->form->get('op') eq 'filePump'; - my $method = $session->form->get( 'method' ) - ? 'www_' . $session->form->get( 'method' ) + my $func = $session->form->get( 'func' ) + ? 'www_' . $session->form->get( 'func' ) : 'www_manage' ; - if ($method ne "www_" && (my $sub = WebGUI::FilePump::Admin->can($method))) { + if ($func ne "www_" && (my $sub = WebGUI::FilePump::Admin->can($func))) { $output = $sub->($session); } else { - WebGUI::Error::MethodNotFound->throw(error=>"Couldn't call non-existant method $method inside FilePump", method=>$method); + WebGUI::Error::MethodNotFound->throw(error=>"Couldn't call non-existant function $func inside FilePump", method=>$func); } return $output; } diff --git a/lib/WebGUI/FilePump/Admin.pm b/lib/WebGUI/FilePump/Admin.pm index ba01a7c08..55828d957 100644 --- a/lib/WebGUI/FilePump/Admin.pm +++ b/lib/WebGUI/FilePump/Admin.pm @@ -31,7 +31,68 @@ the current user. sub canView { my $session = shift; my $user = shift || $session->user; - return $user->isInGroup( 3 ); + return $user->isInGroup( $session->setting->get('groupIdAdminFilePump') ); +} + +#------------------------------------------------------------------- + +=head2 www_addBundle ( ) + +Displays a form to add a bundle. + +Note, we do not allow bundle names to be edited. This is why. The directory to a bundle is based on +the bundle name, and the time stamp of the last build. If you change the name, then you have a few +options. + +1) You delete the build directory with the old name, which will break every macro which references it. + +2) You leave it there, which means that they accumulate with time since they can't every be deleted because +you don't know the old name. + +In short, this really means that instead of an option to edit the name, it needs a copy function. When you +copy the bundle, it asks you what you want for a new name, and it is supplied by the user at that time. + +=cut + +sub www_addBundle { + my ($session) = @_; + return $session->privilege->insufficient() unless canView($session); + + ##Build the form + my $i18n = WebGUI::International->new($session, 'FilePump'); + my $form = WebGUI::HTMLForm->new($session); + $form->hidden( name=>"op", value=>"filePump"); + $form->hidden( name=>"func", value=>"addBundleSave"); + $form->text( + name => 'bundleName', + defaultValue => $i18n->get('new bundle'), + label => $i18n->get('bundle name'), + hoverHelp => $i18n->get('bundle name help'), + ); + $form->submit; + + my $ac = WebGUI::AdminConsole->new($session,'filePump'); + return $ac->render($form->print, $i18n->get('Add Bundle')); +} + +#------------------------------------------------------------------- + +=head2 www_addBundleSave ( ) + +Saves the results of www_addBundle(). + +=cut + +sub www_addBundleSave { + my $session = shift; + return $session->privilege->insufficient() unless canView($session); + my $form = $session->form; + my $bundleName = $form->get('bundleName'); + my $bundle = WebGUI::FilePump::Bundle->create($session, { + bundleName => $bundleName, + lastModified => time(), + }); + return www_manage($session); } #------------------------------------------------------------------- @@ -74,64 +135,6 @@ sub www_demoteFile { return www_manage($session); } -#------------------------------------------------------------------- - -=head2 www_editBundle ( ) - -Displays a form to add or edit a bundle. - -=cut - -sub www_editBundle { - my ($session, $error) = @_; - return $session->privilege->insufficient() unless canView($session); - - if ($error) { - $error = qq|
$error
\n|; - } - ##Make a PassiveAnalytics rule to use to populate the form. - my $bundleId = $session->form->get('bundleId'); - my $bundle; - if ($bundleId) { - $bundle = WebGUI::FilePump::Bundle->new($session, $bundle); - } - else { - ##We need a temporary rule so that we can call dynamicForm, below - $bundleId = 'new'; - $bundle = WebGUI::FilePump::Bundle->create($session, {}); - } - - ##Build the form - my $form = WebGUI::HTMLForm->new($session); - $form->hidden( name=>"op", value=>"filePump"); - $form->hidden( name=>"func", value=>"filePumpSave"); - $form->hidden( name=>"bundleId", value=>$bundleId); - $form->dynamicForm([WebGUI::FilePump::Bundle->crud_definition($session)], 'properties', $bundle); - $form->submit; - - my $i18n = WebGUI::International->new($session, 'FilePump'); - my $ac = WebGUI::AdminConsole->new($session,'filePump'); - if ($bundleId eq 'new') { - $bundle->delete; - } - return $ac->render($error.$form->print,$i18n->get('Edit Bundle')); -} - -#------------------------------------------------------------------- - -=head2 www_editBundleSave ( ) - -Saves the results of www_editBundle(). - -=cut - -sub www_editBundleSave { - my $session = shift; - my $form = $session->form; - return $session->privilege->insufficient() unless canView($session); - return www_manage($session); -} - #------------------------------------------------------------------ =head2 www_promoteFile ( session ) @@ -176,14 +179,14 @@ sub www_manage { while (my $bundle = $getABundle->()) { $rows .= sprintf ' %s', $bundle->get('bundleName'); } - my $output = sprintf <get('bundle name'); + my $output = sprintf <get('bundle name'), $rows; %s
 %s
EOHTML my $ac = WebGUI::AdminConsole->new($session,'filePump'); - $ac->addSubmenuItem($session->url->page('op=filePump;func=add'), $i18n->get('add a bundle')); + $ac->addSubmenuItem($session->url->page('op=filePump;func=addBundle'), $i18n->get('add a bundle')); return $ac->render($error.$output, 'File Pump'); } diff --git a/lib/WebGUI/i18n/English/FilePump.pm b/lib/WebGUI/i18n/English/FilePump.pm index 2c9cb4a2b..067a87ef4 100644 --- a/lib/WebGUI/i18n/English/FilePump.pm +++ b/lib/WebGUI/i18n/English/FilePump.pm @@ -33,8 +33,8 @@ our $I18N = { context => q|Admin console label. Bundle is a loose set of similar, but not identical objects. Similar to pile.| }, - 'Edit Bundle' => { - message => q|Edit Bundle|, + 'Add Bundle' => { + message => q|Add Bundle|, lastUpdated => 1242439269, context => q|Admin console label. Bundle is a loose set of similar, but not identical objects. Similar to pile.| }, From ae57ce3e0f7306c7d99ff680122f10b3d204c054 Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Sat, 16 May 2009 09:40:28 -0700 Subject: [PATCH 30/38] Deleting bundles. --- lib/WebGUI/FilePump/Admin.pm | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/WebGUI/FilePump/Admin.pm b/lib/WebGUI/FilePump/Admin.pm index 55828d957..43a1434e8 100644 --- a/lib/WebGUI/FilePump/Admin.pm +++ b/lib/WebGUI/FilePump/Admin.pm @@ -177,7 +177,10 @@ sub www_manage { my $rows = ''; my $getABundle = WebGUI::FilePump::Bundle->getAllIterator($session,{ orderBy => 'bundleName' } ); while (my $bundle = $getABundle->()) { - $rows .= sprintf ' %s', $bundle->get('bundleName'); + $rows .= sprintf '%s%s', + $session->icon->delete('op=filePump;func=deleteBundle;bundleId='.$bundle->getId), + $bundle->get('bundleName') + ; } my $output = sprintf <get('bundle name'), $rows; From 18d0a9e36f6e845fdfb3e82ebe50218a5eb1c25f Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Sat, 16 May 2009 09:48:44 -0700 Subject: [PATCH 31/38] Admin Console setup. Graphics and i18n. --- etc/WebGUI.conf.original | 7 +++++++ sbin/installFilePump.pl | 6 +++--- www/extras/adminConsole/filePump.png | Bin 0 -> 3481 bytes www/extras/adminConsole/small/filePump.png | Bin 0 -> 863 bytes 4 files changed, 10 insertions(+), 3 deletions(-) create mode 100644 www/extras/adminConsole/filePump.png create mode 100644 www/extras/adminConsole/small/filePump.png diff --git a/etc/WebGUI.conf.original b/etc/WebGUI.conf.original index cd71fcc2d..6f0599350 100644 --- a/etc/WebGUI.conf.original +++ b/etc/WebGUI.conf.original @@ -393,6 +393,13 @@ "url" : "^PageUrl(\"\",op=manageCache);", "title" : "^International(manage cache,WebGUI);", "groupSetting" : "groupIdAdminCache" + }, + "filePump" : { + "icon" : "filePump.png", + "uiLevel" : 5, + "url" : "^PageUrl(\"\",op=filePump);", + "title" : "^International(File Pump,FilePump);", + "groupSetting" : "groupIdAdminFilePump" } }, diff --git a/sbin/installFilePump.pl b/sbin/installFilePump.pl index 5cd04539a..650e570ef 100644 --- a/sbin/installFilePump.pl +++ b/sbin/installFilePump.pl @@ -66,11 +66,11 @@ sub installFilePumpHandler { ##Admin Console $session->config->addToHash('adminConsole', 'filePump', { - "icon" => "filePump.gif", - "groupSetting" => "groupIdFilePump", + "icon" => "filePump.png", + "groupSetting" => "groupIdAdminFilePump", "uiLevel" => 5, "url" => "^PageUrl(\"\",op=filePump);", - "title" => "^International(filePump,FilePump);" + "title" => "^International(File Pump,FilePump);" }); ##Setting for custom group $session->setting->add('groupIdFilePump', 12); diff --git a/www/extras/adminConsole/filePump.png b/www/extras/adminConsole/filePump.png new file mode 100644 index 0000000000000000000000000000000000000000..8b30677bc1150cb08bfce29244eaee52f61569e0 GIT binary patch literal 3481 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 Hu0mjfR8paq literal 0 HcmV?d00001 diff --git a/www/extras/adminConsole/small/filePump.png b/www/extras/adminConsole/small/filePump.png new file mode 100644 index 0000000000000000000000000000000000000000..466cc2e82b5cd3f91b1710395ff049b51fab911d GIT binary patch literal 863 zcmV-l1EBngP)2YMZ7%dgAamN5TwutUj`{v5Rr=b646Hyd?-!OwiHV#K1iWj zMIoZZ;Dw3N1WYCql8L!YCYiHc`=s%<;9uvk_gddxd+ogqr4%1oTY9nhy`pbLNs(JL zDtaioCi0$D6Q6Q36gAvKn0jGT%3mC*Nor2fp4(?j%B&O7f9!O zKwH`+|Cd>|W2-=D!n0keJsp~f1r zL$A5>*G;ysb5UJk(G-QgSC4qrU$w__y6q3ZlmHAv6Ii-_t5b4HzVlYIsD~~MKKs1n$#p3cdFj6OH(#Co zm0j1KGWli{Tc3iAE6d|7KvB}C72{)yU`51P{Sk#iGnEUS{cFqiW*E&5IHen9e((jM zFg4esf`my{c0@@-tDFit80eQE*_6NB0nAK}QF0QPOV;ChUIXM3t0H7_(!4}tj|Tj~ zNbVk)E|8M6eYR;QOwg~plIsW85{$+%y|M)j@koyhlgx<83o}V;b8AQ0v5V~=KBf)guM}$=>PxAWP#%$Z z6Hh2C@5y2^+sD;I>sc79qqV8Eqo%rcpMI6bN6)z}T`T?v(QXp;QOYa9C8toBz<%DP phH?X43&K@DdD7?G=TqJaFaVLW6E2$YT}S``002ovPDHLkV1j4(k$wOG literal 0 HcmV?d00001 From 15321d19ae8b2f2ae97eea8b3da56dc018969703 Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Sat, 16 May 2009 11:35:49 -0700 Subject: [PATCH 32/38] More code for editBundle --- lib/WebGUI/FilePump/Admin.pm | 98 +++++++++++++++++++++++++++-- lib/WebGUI/i18n/English/FilePump.pm | 31 +++++++++ 2 files changed, 125 insertions(+), 4 deletions(-) diff --git a/lib/WebGUI/FilePump/Admin.pm b/lib/WebGUI/FilePump/Admin.pm index 43a1434e8..0d3dce743 100644 --- a/lib/WebGUI/FilePump/Admin.pm +++ b/lib/WebGUI/FilePump/Admin.pm @@ -115,6 +115,29 @@ sub www_deleteBundle { #------------------------------------------------------------------ +=head2 www_deleteFile ( session ) + +Deletes a file from it's bundle. The kind of file is set by the form variable filetype, +the id of the bundle is bundleId, and the id of the file to move is fileId. + +=head3 session + +A reference to the current session. + +=cut + +sub www_deleteFile { + my $session = shift; + return $session->privilege->insufficient() unless canView($session); + my $form = $session->form; + my $bundle = WebGUI::FilePump::Bundle->new($session, $form->get("bundleId")); + return www_editBundle($session) unless $bundle; + $bundle->deleteFile($form->get('fileType'), $form->get('fileId')); + return www_editBundle($session); +} + +#------------------------------------------------------------------ + =head2 www_demoteFile ( session ) Moves a bundle file down one position. The kind of file is set by the form variable filetype, @@ -137,6 +160,68 @@ sub www_demoteFile { #------------------------------------------------------------------ +=head2 www_editBundle ( session ) + +Interface for managing URIs in a bundle, given by the form param bundleId. Add, delete, +promote and demote are supported for all three file types. + +=head3 session + +A reference to the current session. + +=cut + +sub www_editBundle { + my ($session, $error) = @_; + return $session->privilege->insufficient() unless canView($session); + my $bundle = WebGUI::FilePump::Bundle->new($session, $session->form->get("bundleId")); + my $i18n = WebGUI::International->new($session, 'FilePump'); + if (!defined $bundle) { + return www_addBundle($session); + } + my $tableStub = <%s +
+ +%s +
 URI%s
+

%s

+EOTABLE + my $output = ''; + my $bundleId = $bundle->getId; + my $dt = $session->datetime; + my $lastModifiedi18n = $i18n->get('last modified'); + foreach my $fileType (qw/jsFiles cssFiles/) { + my $type = $fileType eq 'jsFiles' ? 'JS' + : $fileType eq 'cssFiles' ? 'CSS' + : 'OTHER'; + my $rows = ''; + my $form = ''; + my $files = $bundle->get($fileType); + foreach my $file (@{ $files }) { + my $urlFrag = 'bundleId='.$bundleId.'fileType='.$type.'fileId='.$file->{fileId}; + $rows .= sprintf '%s%s%s', + $session->icon->delete( 'op=filePump;func=deleteFile;' . $urlFrag). + $session->icon->moveUp( 'op=filePump;func=promoteFile;' . $urlFrag). + $session->icon->moveDown( 'op=filePump;func=demoteFile;' . $urlFrag) , + $file->{uri}, + $dt->epochToHuman($file->{lastModified}) + ; + } + $output .= sprintf $tableStub, $i18n->get($fileType), $lastModifiedi18n, $rows, $form; + $output .= WebGUI::Form::text($session, { + name => 'uri', + }); + } + + my $ac = WebGUI::AdminConsole->new($session,'filePump'); + $ac->addSubmenuItem($session->url->page('op=filePump;'), $i18n->get('list bundles')); + $ac->addSubmenuItem($session->url->page('op=filePump;func=addBundle'), $i18n->get('add a bundle')); + return $ac->render($error.$output, 'File Pump'); +} + +#------------------------------------------------------------------ + =head2 www_promoteFile ( session ) Moves a bundle file up one position. The kind of file is set by the form variable filetype, @@ -175,16 +260,21 @@ sub www_manage { my $i18n = WebGUI::International->new($session, 'FilePump'); my $error = shift; my $rows = ''; + my $dt = $session->datetime; + my $url = $session->url; my $getABundle = WebGUI::FilePump::Bundle->getAllIterator($session,{ orderBy => 'bundleName' } ); while (my $bundle = $getABundle->()) { - $rows .= sprintf '%s%s', + $rows .= sprintf '%s%s%s%s', $session->icon->delete('op=filePump;func=deleteBundle;bundleId='.$bundle->getId), - $bundle->get('bundleName') + $url->gateway($url->getRequestedUrl,'op=filePump;func=editBundle;bundleId='.$bundle->getId), + $bundle->get('bundleName'), + $dt->epochToHuman($bundle->get('lastModified')), + $dt->epochToHuman($bundle->get('lastBuild')), ; } - my $output = sprintf <get('bundle name'), $rows; + my $output = sprintf <get('bundle name'), $i18n->get('last modified'), $i18n->get('last build'), $rows; - + %s
 %s
 %s%s%s
EOHTML diff --git a/lib/WebGUI/i18n/English/FilePump.pm b/lib/WebGUI/i18n/English/FilePump.pm index 067a87ef4..9cbf780ac 100644 --- a/lib/WebGUI/i18n/English/FilePump.pm +++ b/lib/WebGUI/i18n/English/FilePump.pm @@ -3,12 +3,25 @@ package WebGUI::i18n::English::FilePump; use strict; our $I18N = { + 'bundle name' => { message => q|Bundle name|, lastUpdated => 1131394070, context => q|Label for the name of a bundle (group, clump) of files.| }, + 'last build' => { + message => q|Last Build|, + lastUpdated => 1242493652, + context => q|The time the bundle was built last| + }, + + 'last modified' => { + message => q|Last Modified|, + lastUpdated => 1242493669, + context => q|The time the bundle was last modified.| + }, + 'bundle name help' => { message => q|A unique, human readable name for this bundle. Bundle names must be unique.|, lastUpdated => 1131394072, @@ -39,6 +52,24 @@ our $I18N = { context => q|Admin console label. Bundle is a loose set of similar, but not identical objects. Similar to pile.| }, + 'list bundles' => { + message => q|List Bundles|, + lastUpdated => 1242495011, + context => q|Admin console label. Bundle is a loose set of similar, but not identical objects. Similar to pile.| + }, + + 'jsFiles' => { + message => q|JavaScript|, + lastUpdated => 1242495011, + context => q|Edit bundle label.| + }, + + 'cssFiles' => { + message => q|CSS|, + lastUpdated => 1242495011, + context => q|Edit bundle label.| + }, + }; 1; From 8a81d5817f2e4bc1f9974ac60b18cd3c159870ad Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Sat, 16 May 2009 13:01:10 -0700 Subject: [PATCH 33/38] Add files to a bundle. --- lib/WebGUI/FilePump/Admin.pm | 59 +++++++++++++++++++++++++++++++----- 1 file changed, 52 insertions(+), 7 deletions(-) diff --git a/lib/WebGUI/FilePump/Admin.pm b/lib/WebGUI/FilePump/Admin.pm index 0d3dce743..a7da16a2d 100644 --- a/lib/WebGUI/FilePump/Admin.pm +++ b/lib/WebGUI/FilePump/Admin.pm @@ -97,6 +97,44 @@ sub www_addBundleSave { #------------------------------------------------------------------- +=head2 www_addFile ( ) + +Processes the form to add a file to a bundle. + +Form variables used: + +=item uri + +The URI to add. + +=item type + +The type of file to add. + +=item bundleId + +The GUID of the bundle to add a file to. + +=cut + +sub www_addFile { + my ($session) = @_; + return $session->privilege->insufficient() unless canView($session); + my $form = $session->form; + my $bundleId = $form->get('bundleId'); + my $bundle = WebGUI::FilePump::Bundle->new($session, $bundleId); + return www_editBundle($session) unless $bundle; + + my $type = $form->get('type'); + my $uri = $form->get('uri'); + + my (undef, $error) = $bundle->addFile($type, $uri); + + return www_editBundle($session, $error); +} + +#------------------------------------------------------------------- + =head2 www_deleteBundle ( ) Deletes a bundle, identified by the form variable, bundleId. @@ -190,13 +228,23 @@ EOTABLE my $output = ''; my $bundleId = $bundle->getId; my $dt = $session->datetime; + my $url = $session->url; my $lastModifiedi18n = $i18n->get('last modified'); foreach my $fileType (qw/jsFiles cssFiles/) { my $type = $fileType eq 'jsFiles' ? 'JS' : $fileType eq 'cssFiles' ? 'CSS' : 'OTHER'; + my $form = WebGUI::Form::formHeader($session, { + action => $url->gateway($url->getRequestedUrl,'op=filePump;func=addFile;type='.$type.';bundleId='.$bundleId), + }) + . WebGUI::Form::text($session, { + name => 'uri', + }) + . WebGUI::Form::submit($session) + . WebGUI::Form::formFooter() + ; + my $rows = ''; - my $form = ''; my $files = $bundle->get($fileType); foreach my $file (@{ $files }) { my $urlFrag = 'bundleId='.$bundleId.'fileType='.$type.'fileId='.$file->{fileId}; @@ -205,13 +253,10 @@ EOTABLE $session->icon->moveUp( 'op=filePump;func=promoteFile;' . $urlFrag). $session->icon->moveDown( 'op=filePump;func=demoteFile;' . $urlFrag) , $file->{uri}, - $dt->epochToHuman($file->{lastModified}) + $file->{lastModified} ? $dt->epochToHuman($file->{lastModified}) : ' ' ; } $output .= sprintf $tableStub, $i18n->get($fileType), $lastModifiedi18n, $rows, $form; - $output .= WebGUI::Form::text($session, { - name => 'uri', - }); } my $ac = WebGUI::AdminConsole->new($session,'filePump'); @@ -268,8 +313,8 @@ sub www_manage { $session->icon->delete('op=filePump;func=deleteBundle;bundleId='.$bundle->getId), $url->gateway($url->getRequestedUrl,'op=filePump;func=editBundle;bundleId='.$bundle->getId), $bundle->get('bundleName'), - $dt->epochToHuman($bundle->get('lastModified')), - $dt->epochToHuman($bundle->get('lastBuild')), + $bundle->get('lastModified') ? $dt->epochToHuman($bundle->get('lastModified')) : ' ', + $bundle->get('lastBuild') ? $dt->epochToHuman($bundle->get('lastBuild')) : ' ', ; } my $output = sprintf <get('bundle name'), $i18n->get('last modified'), $i18n->get('last build'), $rows; From 6314228b59df7500ffb0afd8f6166c1ae414a8a5 Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Sat, 16 May 2009 14:24:21 -0700 Subject: [PATCH 34/38] Promote and demote files inside of a bundle. --- lib/WebGUI/FilePump/Admin.pm | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/lib/WebGUI/FilePump/Admin.pm b/lib/WebGUI/FilePump/Admin.pm index a7da16a2d..b9e1a9e10 100644 --- a/lib/WebGUI/FilePump/Admin.pm +++ b/lib/WebGUI/FilePump/Admin.pm @@ -178,7 +178,7 @@ sub www_deleteFile { =head2 www_demoteFile ( session ) -Moves a bundle file down one position. The kind of file is set by the form variable filetype, +Moves a bundle file down one position. The kind of file is set by the form variable fileType, the id of the bundle is bundleId, and the id of the file to move is fileId. =head3 session @@ -190,10 +190,13 @@ A reference to the current session. sub www_demoteFile { my $session = shift; return $session->privilege->insufficient() unless canView($session); - my $bundle = WebGUI::FilePump::Bundle->new($session, $session->form->get("bundleId")); - if (defined $bundle) { - } - return www_manage($session); + my $form = $session->form; + my $bundle = WebGUI::FilePump::Bundle->new($session, $form->get("bundleId")); + return www_editBundle($session) unless $bundle; + my $type = $form->get('fileType'); + my $fileId = $form->get('fileId'); + $bundle->moveFileDown($type, $fileId); + return www_editBundle($session); } #------------------------------------------------------------------ @@ -247,7 +250,7 @@ EOTABLE my $rows = ''; my $files = $bundle->get($fileType); foreach my $file (@{ $files }) { - my $urlFrag = 'bundleId='.$bundleId.'fileType='.$type.'fileId='.$file->{fileId}; + my $urlFrag = 'bundleId='.$bundleId.';fileType='.$type.';fileId='.$file->{fileId}; $rows .= sprintf '%s%s%s', $session->icon->delete( 'op=filePump;func=deleteFile;' . $urlFrag). $session->icon->moveUp( 'op=filePump;func=promoteFile;' . $urlFrag). @@ -269,7 +272,7 @@ EOTABLE =head2 www_promoteFile ( session ) -Moves a bundle file up one position. The kind of file is set by the form variable filetype, +Moves a bundle file up one position. The kind of file is set by the form variable fileType, the id of the bundle is bundleId, and the id of the file to move is fileId. =head3 session @@ -281,10 +284,13 @@ A reference to the current session. sub www_promoteFile { my $session = shift; return $session->privilege->insufficient() unless canView($session); + my $form = $session->form; my $bundle = WebGUI::FilePump::Bundle->new($session, $session->form->get("bundleId")); - if (defined $bundle) { - } - return www_manage($session); + return www_editBundle($session) unless $bundle; + my $type = $form->get('fileType'); + my $fileId = $form->get('fileId'); + $bundle->moveFileUp($type, $fileId); + return www_editBundle($session); } #------------------------------------------------------------------- From 61c7c7a46e24819ed3e159a33adaba53cb7eae27 Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Sat, 16 May 2009 14:42:22 -0700 Subject: [PATCH 35/38] Build bundle UI via edit bundle admin console. --- lib/WebGUI/FilePump/Admin.pm | 31 +++++++++++++++++++++++++---- lib/WebGUI/i18n/English/FilePump.pm | 12 +++++++++++ 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/lib/WebGUI/FilePump/Admin.pm b/lib/WebGUI/FilePump/Admin.pm index b9e1a9e10..8bcf90e24 100644 --- a/lib/WebGUI/FilePump/Admin.pm +++ b/lib/WebGUI/FilePump/Admin.pm @@ -135,6 +135,27 @@ sub www_addFile { #------------------------------------------------------------------- +=head2 www_buildBundle ( ) + +Builds a bundle, identified by the form variable, bundleId. + +=cut + +sub www_buildBundle { + my $session = shift; + return $session->privilege->insufficient() unless canView($session); + my $bundle = WebGUI::FilePump::Bundle->new($session, $session->form->get("bundleId")); + return www_editBundle($session) unless $bundle; + my ($code, $error) = $bundle->build; + if ($error) { + my $i18n = WebGUI::International->new($session, 'FilePump'); + $error = sprintf $i18n->get('build error'), $error; + } + return www_editBundle($session, $error); +} + +#------------------------------------------------------------------- + =head2 www_deleteBundle ( ) Deletes a bundle, identified by the form variable, bundleId. @@ -215,10 +236,12 @@ A reference to the current session. sub www_editBundle { my ($session, $error) = @_; return $session->privilege->insufficient() unless canView($session); - my $bundle = WebGUI::FilePump::Bundle->new($session, $session->form->get("bundleId")); + my $bundleId = $session->form->get("bundleId"); + my $bundle = WebGUI::FilePump::Bundle->new($session, $bundleId); + return www_addBundle($session) unless $bundle; my $i18n = WebGUI::International->new($session, 'FilePump'); - if (!defined $bundle) { - return www_addBundle($session); + if ($error) { + $error = qq|
$error
\n|; } my $tableStub = <%s @@ -229,7 +252,6 @@ sub www_editBundle {

%s

EOTABLE my $output = ''; - my $bundleId = $bundle->getId; my $dt = $session->datetime; my $url = $session->url; my $lastModifiedi18n = $i18n->get('last modified'); @@ -265,6 +287,7 @@ EOTABLE my $ac = WebGUI::AdminConsole->new($session,'filePump'); $ac->addSubmenuItem($session->url->page('op=filePump;'), $i18n->get('list bundles')); $ac->addSubmenuItem($session->url->page('op=filePump;func=addBundle'), $i18n->get('add a bundle')); + $ac->addSubmenuItem($session->url->page('op=filePump;func=buildBundle;bundleId='.$bundleId), $i18n->get('build this bundle')); return $ac->render($error.$output, 'File Pump'); } diff --git a/lib/WebGUI/i18n/English/FilePump.pm b/lib/WebGUI/i18n/English/FilePump.pm index 9cbf780ac..46f6db28b 100644 --- a/lib/WebGUI/i18n/English/FilePump.pm +++ b/lib/WebGUI/i18n/English/FilePump.pm @@ -70,6 +70,18 @@ our $I18N = { context => q|Edit bundle label.| }, + 'build this bundle' => { + message => q|Build this bundle|, + lastUpdated => 1242495011, + context => q|Edit bundle label.| + }, + + 'build error' => { + message => q|Problem fetching this URI: %s|, + lastUpdated => 1242495011, + context => q|Edit bundle error label.| + }, + }; 1; From 76e4b99eb5dd671921e15cd962a541bca250f0d0 Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Sat, 16 May 2009 14:52:50 -0700 Subject: [PATCH 36/38] Fix bad property keys in crud_definition. --- lib/WebGUI/FilePump/Bundle.pm | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/WebGUI/FilePump/Bundle.pm b/lib/WebGUI/FilePump/Bundle.pm index 8cb10c2c9..ccad6d204 100644 --- a/lib/WebGUI/FilePump/Bundle.pm +++ b/lib/WebGUI/FilePump/Bundle.pm @@ -203,29 +203,29 @@ sub crud_definition { $definition->{sequenceKey} = ''; my $properties = $definition->{properties}; $properties->{bundleName} = { - fieldName => 'text', + fieldType => 'text', defaultValue => $i18n->get('new bundle'), }; $properties->{lastModified} = { - fieldName => 'integer', + fieldType => 'integer', defaultValue => 0, }; $properties->{lastBuild} = { - fieldName => 'integer', + fieldType => 'integer', defaultValue => 0, }; $properties->{jsFiles} = { - fieldName => 'textarea', + fieldType => 'textarea', defaultValue => [], serialize => 1, }; $properties->{cssFiles} = { - fieldName => 'textarea', + fieldType => 'textarea', defaultValue => [], serialize => 1, }; $properties->{otherFiles} = { - fieldName => 'textarea', + fieldType => 'textarea', defaultValue => [], serialize => 1, }; From 9cafcf3a61c425546f8b083b6c1f35effe2d8737 Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Sat, 16 May 2009 16:17:43 -0700 Subject: [PATCH 37/38] UI improvements. Show build label in list of bundles if lastModified > lastBuild. Display label "Not yet" if bundle has never been modified, or built. --- lib/WebGUI/FilePump/Admin.pm | 14 ++++++++++++-- lib/WebGUI/i18n/English/FilePump.pm | 12 ++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/lib/WebGUI/FilePump/Admin.pm b/lib/WebGUI/FilePump/Admin.pm index 8bcf90e24..de90f2adf 100644 --- a/lib/WebGUI/FilePump/Admin.pm +++ b/lib/WebGUI/FilePump/Admin.pm @@ -264,6 +264,7 @@ EOTABLE }) . WebGUI::Form::text($session, { name => 'uri', + size => 45, }) . WebGUI::Form::submit($session) . WebGUI::Form::formFooter() @@ -337,13 +338,22 @@ sub www_manage { my $dt = $session->datetime; my $url = $session->url; my $getABundle = WebGUI::FilePump::Bundle->getAllIterator($session,{ orderBy => 'bundleName' } ); + my $notYet = $i18n->get('not yet'); while (my $bundle = $getABundle->()) { + my $lastModified = $bundle->get('lastModified'); + my $lastBuild = $bundle->get('lastBuild'); + my $build = ''; + if ($lastModified > $lastBuild) { + $build = sprintf q| (%s)|, + $url->gateway($url->getRequestedUrl,'op=filePump;func=buildBundle;bundleId='.$bundle->getId), + $i18n->get('build'); + } $rows .= sprintf '%s%s%s%s', $session->icon->delete('op=filePump;func=deleteBundle;bundleId='.$bundle->getId), $url->gateway($url->getRequestedUrl,'op=filePump;func=editBundle;bundleId='.$bundle->getId), $bundle->get('bundleName'), - $bundle->get('lastModified') ? $dt->epochToHuman($bundle->get('lastModified')) : ' ', - $bundle->get('lastBuild') ? $dt->epochToHuman($bundle->get('lastBuild')) : ' ', + $bundle->get('lastModified') ? $dt->epochToHuman($lastModified) : $notYet, + $bundle->get('lastBuild') ? $dt->epochToHuman($lastBuild).$build : $notYet, ; } my $output = sprintf <get('bundle name'), $i18n->get('last modified'), $i18n->get('last build'), $rows; diff --git a/lib/WebGUI/i18n/English/FilePump.pm b/lib/WebGUI/i18n/English/FilePump.pm index 46f6db28b..5eceaa852 100644 --- a/lib/WebGUI/i18n/English/FilePump.pm +++ b/lib/WebGUI/i18n/English/FilePump.pm @@ -76,12 +76,24 @@ our $I18N = { context => q|Edit bundle label.| }, + 'build' => { + message => q|Build|, + lastUpdated => 1242495011, + context => q|List bundles label. Meaning to construct. The short version of Build this bundle.| + }, + 'build error' => { message => q|Problem fetching this URI: %s|, lastUpdated => 1242495011, context => q|Edit bundle error label.| }, + 'not yet' => { + message => q|Not yet|, + lastUpdated => 1242515308, + context => q|Meaning that something has not been done at this time. Before the first time.| + }, + }; 1; From 46cedb1a9096e8dac35c5103256a43b7856adba0 Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Sat, 16 May 2009 17:37:17 -0700 Subject: [PATCH 38/38] Use the correct HTTP header for lastModified --- lib/WebGUI/FilePump/Bundle.pm | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/WebGUI/FilePump/Bundle.pm b/lib/WebGUI/FilePump/Bundle.pm index ccad6d204..456fc7f8c 100644 --- a/lib/WebGUI/FilePump/Bundle.pm +++ b/lib/WebGUI/FilePump/Bundle.pm @@ -8,6 +8,7 @@ use Path::Class; use CSS::Minifier::XS; use JavaScript::Minifier::XS; use LWP; +use Data::Dumper; #------------------------------------------------------------------- @@ -484,8 +485,8 @@ sub fetchHttp { return {}; } my $guts = { - lastModified => $response->header('lastModified'), - content => $response->content(), + content => $response->content, + lastModified => $response->header('last-modified'), }; return $guts; }