diff options
22 files changed, 228 insertions, 328 deletions
diff --git a/.gitignore b/.gitignore index 5287b1ea0..18f2d0a17 100644 --- a/.gitignore +++ b/.gitignore @@ -14,7 +14,6 @@ /skins/custom /graphs /data -/assets /localconfig /localconfig.* /index.html @@ -2,6 +2,27 @@ <FilesMatch (\.pm|\.pl|\.tmpl|\.swf|localconfig.*|cpanfile)$> deny from all </FilesMatch> +<IfModule mod_expires.c> +<IfModule mod_headers.c> +<IfModule mod_env.c> + <FilesMatch (\.js|\.css)$> + ExpiresActive On + # According to RFC 2616, "1 year in the future" means "never expire". + # We change the name of the file's URL whenever its modification date + # changes, so browsers can cache any individual JS or CSS URL forever. + # However, since all JS and CSS URLs involve a ? in them (for the changing + # name) we have to explicitly set an Expires header or browsers won't + # *ever* cache them. + ExpiresDefault "now plus 1 years" + Header append Cache-Control "public" + </FilesMatch> + + # This lets Bugzilla know that we are properly sending Cache-Control + # and Expires headers for CSS and JS files. + SetEnv BZ_CACHE_CONTROL 1 +</IfModule> +</IfModule> +</IfModule> # Allow ZeroClipboard for access to the clipboard <Files "ZeroClipboard.swf"> diff --git a/Bugzilla.pm b/Bugzilla.pm index 55dc93e98..33a658323 100644 --- a/Bugzilla.pm +++ b/Bugzilla.pm @@ -36,7 +36,6 @@ use Bugzilla::Flag; use Bugzilla::Hook; use Bugzilla::Install::Localconfig qw(read_localconfig); use Bugzilla::Install::Util qw(init_console include_languages); -use Bugzilla::Install::AssetManager; use Bugzilla::Memcached; use Bugzilla::Template; use Bugzilla::Token; @@ -255,11 +254,6 @@ sub extensions { return $cache->{extensions}; } -sub asset_manager { - state $asset_manager = Bugzilla::Install::AssetManager->new; - return $asset_manager; -} - sub cgi { return $_[0]->request_cache->{cgi} ||= new Bugzilla::CGI(); } diff --git a/Bugzilla/CGI.pm b/Bugzilla/CGI.pm index 65e36a24f..2402e997b 100644 --- a/Bugzilla/CGI.pm +++ b/Bugzilla/CGI.pm @@ -441,20 +441,12 @@ sub header { %headers = ('-type' => shift(@_)); } else { - # we always want headers to be in lower case, to avoid - # for instance -Cache_Control vs. -cache_control. - my %tmp = @_; - foreach my $key (keys %tmp) { - my $lc_key = lc $key; - warn "duplicate header: $key" if exists $headers{$lc_key}; - $headers{$lc_key} = $tmp{$key}; - } + %headers = @_; } if ($self->{'_content_disp'}) { $headers{'-content_disposition'} = $self->{'_content_disp'}; } - $headers{'-cache_control'} //= 'no-cache, no-store, must-revalidate'; if (!$user->id && $user->authorizer->can_login && !$self->cookie('Bugzilla_login_request_cookie')) diff --git a/Bugzilla/Constants.pm b/Bugzilla/Constants.pm index 4cb838f94..9c8f39b4a 100644 --- a/Bugzilla/Constants.pm +++ b/Bugzilla/Constants.pm @@ -26,6 +26,8 @@ use Memoize; bz_locations + CONCATENATE_ASSETS + IS_NULL NOT_NULL @@ -217,6 +219,11 @@ use constant REST_DOC => "http://www.bugzilla.org/docs/tip/en/html/api/"; use constant REMOTE_FILE => 'http://updates.bugzilla.org/bugzilla-update.xml'; use constant LOCAL_FILE => 'bugzilla-update.xml'; # Relative to datadir. +# When true CSS and JavaScript assets will be concatanted and minified at +# run-time, to reduce the number of requests required to render a page. +# Setting this to a false value can help debugging. +use constant CONCATENATE_ASSETS => 1; + # These are unique values that are unlikely to match a string or a number, # to be used in criteria for match() functions and other things. They start # and end with spaces because most Bugzilla stuff has trim() called on it, @@ -695,7 +702,7 @@ sub _bz_locations { # The script should really generate these graphs directly... 'webdotdir' => "$datadir/webdot", 'extensionsdir' => "$libpath/extensions", - 'assetsdir' => "$libpath/assets", + 'assetsdir' => "$datadir/assets", # error_reports store error/warnings destined for sentry 'error_reports' => "$libpath/error_reports", }; diff --git a/Bugzilla/Install/AssetManager.pm b/Bugzilla/Install/AssetManager.pm deleted file mode 100644 index 073e012cd..000000000 --- a/Bugzilla/Install/AssetManager.pm +++ /dev/null @@ -1,218 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. -# -# This Source Code Form is "Incompatible With Secondary Licenses", as -# defined by the Mozilla Public License, v. 2.0. - -package Bugzilla::Install::AssetManager; - -use 5.10.1; -use strict; -use warnings; - -use Moo; -use MooX::StrictConstructor; -use Type::Utils; -use Types::Standard qw(Bool Str ArrayRef); - -use Digest::SHA (); -use File::Copy qw(cp); -use File::Find qw(find); -use File::Basename qw(dirname); -use File::Spec; -use JSON::XS (); -use MIME::Base64 qw(encode_base64); -use File::Slurp; -use List::MoreUtils qw(any all); -use Carp; - -use Bugzilla::Constants qw(bz_locations); - -our $VERSION = 1; - -my $SHA_VERSION = '224'; - -my $ABSOLUTE_DIR = declare as Str, where { File::Spec->file_name_is_absolute($_) && -d $_ } -message {"must be an absolute path to a directory"}; - -has 'base_dir' => ( is => 'lazy', isa => $ABSOLUTE_DIR ); -has 'asset_dir' => ( is => 'lazy', isa => $ABSOLUTE_DIR ); -has 'source_dirs' => ( is => 'lazy' ); -has 'state' => ( is => 'lazy' ); -has 'state_file' => ( is => 'lazy' ); -has 'json' => ( is => 'lazy' ); - -sub asset_file { - my ( $self, $file, $relative_to ) = @_; - $relative_to //= $self->base_dir; - my $asset_file = $self->state->{asset_map}->{$file} - or return $file; - - return File::Spec->abs2rel( File::Spec->catfile( $self->asset_dir, $asset_file ), $relative_to ); -} - -sub asset_files { - my ( $self, @files ) = @_; - - return \@files; -} - -sub asset_sri { - my ( $self, $asset_file ) = @_; - my ($hex) = $asset_file =~ m!([[:xdigit:]]+)\.\w+$!; - my $data = pack "H*", $hex; - return "sha$SHA_VERSION-" . encode_base64( $data, "" ); -} - -sub compile_file { - my ( $self, $file ) = @_; - return unless -f $file; - my $base_dir = $self->base_dir; - my $asset_dir = $self->asset_dir; - my $asset_map = $self->state->{asset_map}; - - my $key = File::Spec->abs2rel( $file, $base_dir ); - return if $asset_map->{$key}; - - if ( $file =~ /\.(jpe?g|png|gif|ico|woff|js)$/i ) { - my $ext = $1; - my $digest = $self->_digest_file($file) or die "invalid digest for $file"; - my $asset_file = File::Spec->catfile( $asset_dir, "$digest.$ext" ); - cp( $file, $asset_file ) or die "failed to copy $file to $asset_file: $!"; - if ( $digest eq $self->_digest_file($asset_file) ) { - $asset_map->{$key} = File::Spec->abs2rel( $asset_file, $asset_dir ); - } - else { - die "failed to write $asset_file"; - } - } - elsif ( $file =~ /\.css$/ ) { - my $content = read_file($file); - - $content =~ s{(?<!=)url\(([^\)]+)\)}{$self->_css_url_rewrite($1, $file)}eig; - my $digest = $self->_digest_string($content); - my $asset_file = File::Spec->catfile( $asset_dir, "$digest.css" ); - write_file( $asset_file, $content ) or die "failed to write $asset_file: $!"; - if ( $digest eq $self->_digest_file($asset_file) ) { - $asset_map->{$key} = File::Spec->abs2rel( $asset_file, $asset_dir ); - } - else { - die "failed to write $asset_file"; - } - } -} - -sub compile_all { - my ($self) = @_; - my $asset_map = $self->state->{asset_map} = {}; - - my $wanted = sub { - $self->compile_file($File::Find::name); - }; - - find( { wanted => $wanted, no_chdir => 1 }, @{ $self->source_dirs } ); - - $self->_save_state(); -} - -sub _css_url_rewrite { - my ( $self, $url, $file ) = @_; - my $dir = dirname($file); - - # rewrite relative urls as the unified stylesheet lives in a different - # directory from the source - $url =~ s/(^['"]|['"]$)//g; - if ( $url =~ m!^(/|data:)! ) { - return 'url(' . $url . ')'; - } - else { - my $url_file = File::Spec->rel2abs( $url, $dir ); - my $ref_file = File::Spec->abs2rel( $url_file, $self->base_dir ); - $self->compile_file($url_file); - return sprintf( "url(%s)", $self->asset_file( $ref_file, $self->asset_dir ) ); - } -} - -sub _new_digest { Digest::SHA->new($SHA_VERSION) } - -sub _digest_file { - my ( $self, $file ) = @_; - my $digest = $self->_new_digest; - $digest->addfile( $file, "b" ); - return $digest->hexdigest; -} - -sub _digest_string { - my ( $self, $string ) = @_; - my $digest = $self->_new_digest; - $digest->add($string); - return $digest->hexdigest; -} - -sub _build_base_dir { - Cwd::realpath( File::Spec->rel2abs( bz_locations->{cgi_path} ) ); -} - -sub _build_asset_dir { - my ($self) = @_; - my $dir - = Cwd::realpath( File::Spec->rel2abs( bz_locations->{assetsdir} ) ); - - if ( $dir && -d $dir ) { - my $version_dir = File::Spec->catdir( $dir, "v" . $self->VERSION ); - unless ( -d $version_dir ) { - mkdir $version_dir or die "mkdir $version_dir failed: $!"; - } - return $version_dir; - } - else { - return $dir; - } -} - -sub _build_source_dirs { - my ($self) = @_; - my $base = $self->base_dir; - my $ext_dir = "$base/extensions"; - opendir my $ext_dir_handle, $ext_dir or die "unable to open $ext_dir: $!"; - my @dirs = grep { -d $_ } map {"$ext_dir/$_/web"} grep { !/^\.\.?$/ } readdir $ext_dir_handle; - closedir $ext_dir_handle; - - return [ "$base/images", "$base/skins", "$base/js", grep { -d $_ } @dirs ]; -} - -sub _build_state_file { - my ($self) = @_; - return $self->asset_dir . "/state.json"; -} - -sub _build_state { - my ($self) = @_; - my $state; - if ( open my $fh, '<:bytes', $self->state_file ) { - local $/ = undef; - my $json = <$fh>; - close $fh; - $state = $self->json->decode($json); - } - else { - $state = {}; - } - - $state->{asset_map} //= {}; - - return $state; -} - -sub _build_json { JSON::XS->new->canonical->utf8 } - -sub _save_state { - my ($self) = @_; - open my $fh, '>:bytes', $self->state_file - or die "unable to write state file: $!"; - print $fh $self->json->encode( $self->state ); - close $fh; -} - -1; diff --git a/Bugzilla/Install/Filesystem.pm b/Bugzilla/Install/Filesystem.pm index 162e324f7..22ec34a95 100644 --- a/Bugzilla/Install/Filesystem.pm +++ b/Bugzilla/Install/Filesystem.pm @@ -31,7 +31,6 @@ use File::Path; use File::Basename; use File::Copy qw(move); use File::Spec; -use File::stat; use Cwd (); use File::Slurp; use IO::File; @@ -82,16 +81,12 @@ EOT use constant HT_ASSETS_DIR => <<'EOT'; # Allow access to .css and js files -<FilesMatch state\.json$> - Deny from all +<FilesMatch \.(css|js)$> + Allow from all </FilesMatch> -FileETag None -Header set Cache-Control "public, immutable, max-age=31536000" -Header set Content-Security-Policy "default-src 'none';" - # And no directory listings, either. -Options -Indexes +Deny from all EOT use constant INDEX_HTML => <<'EOT'; @@ -349,7 +344,7 @@ sub FILESYSTEM { $attachdir => DIR_CGI_WRITE, $graphsdir => DIR_CGI_WRITE | DIR_ALSO_WS_SERVE, $webdotdir => DIR_CGI_WRITE | DIR_ALSO_WS_SERVE, - $assetsdir => DIR_WS_SERVE, + $assetsdir => DIR_CGI_WRITE | DIR_ALSO_WS_SERVE, $template_cache => DIR_CGI_WRITE, $error_reports => DIR_CGI_WRITE, # Directories that contain content served directly by the web server. @@ -451,13 +446,8 @@ sub FILESYSTEM { "$webdotdir/.htaccess" => { perms => WS_SERVE, contents => HT_WEBDOT_DIR }, "$assetsdir/.htaccess" => { perms => WS_SERVE, - contents => HT_ASSETS_DIR }, + contents => HT_ASSETS_DIR }, ); - my $mtime = stat(__FILE__)->mtime; - foreach my $file (keys %htaccess) { - my $file_stat = stat($file); - $htaccess{$file}{overwrite} = $file_stat && $mtime > $file_stat->mtime; - } Bugzilla::Hook::process('install_filesystem', { files => \%files, @@ -571,6 +561,7 @@ sub update_filesystem { _remove_empty_css_files(); _convert_single_file_skins(); + _remove_dynamic_assets(); } sub _css_url_fix { @@ -636,6 +627,27 @@ sub _convert_single_file_skins { } } +# delete all automatically generated css/js files to force recreation at the +# next request. +sub _remove_dynamic_assets { + my @files = ( + glob(bz_locations()->{assetsdir} . '/*.css'), + glob(bz_locations()->{assetsdir} . '/*.js'), + ); + foreach my $file (@files) { + unlink($file); + } + + # remove old skins/assets directory + my $old_path = bz_locations()->{skinsdir} . '/assets'; + if (-d $old_path) { + foreach my $file (glob("$old_path/*.css")) { + unlink($file); + } + rmdir($old_path); + } +} + sub create_htaccess { _create_files(%{FILESYSTEM()->{htaccess}}); diff --git a/Bugzilla/Template.pm b/Bugzilla/Template.pm index 663e467c6..5bef599d4 100644 --- a/Bugzilla/Template.pm +++ b/Bugzilla/Template.pm @@ -401,13 +401,7 @@ sub css_files { unshift @requested_css, "skins/yui.css" unless $no_yui; - my @css_sets = map { _css_link_set($_) } sort { - my $first_a = $a =~ m!js/jquery!; - my $first_b = $b =~ m!js/jquery!; - return -1 if $first_a && !$first_b; - return 1 if !$first_a && $first_b; - return 0; - } @requested_css; + my @css_sets = map { _css_link_set($_) } @requested_css; my %by_type = (standard => [], skin => [], custom => []); foreach my $set (@css_sets) { @@ -416,13 +410,18 @@ sub css_files { } } + # build unified + $by_type{unified_standard_skin} = _concatenate_css($by_type{standard}, + $by_type{skin}); + $by_type{unified_custom} = _concatenate_css($by_type{custom}); + return \%by_type; } sub _css_link_set { my ($file_name) = @_; - my %set = (standard => $file_name); + my %set = (standard => mtime_filter($file_name)); # We use (?:^|/) to allow Extensions to use the skins system if they want. if ($file_name !~ m{(?:^|/)skins/standard/}) { @@ -433,19 +432,127 @@ sub _css_link_set { my $cgi_path = bz_locations()->{'cgi_path'}; my $skin_file_name = $file_name; $skin_file_name =~ s{(?:^|/)skins/standard/}{skins/contrib/$skin/}; - if (-f "$cgi_path/$skin_file_name") { - $set{skin} = $skin_file_name; + if (my $mtime = _mtime("$cgi_path/$skin_file_name")) { + $set{skin} = mtime_filter($skin_file_name, $mtime); } my $custom_file_name = $file_name; $custom_file_name =~ s{(?:^|/)skins/standard/}{skins/custom/}; - if (-f "$cgi_path/$custom_file_name") { - $set{custom} = $custom_file_name; + if (my $custom_mtime = _mtime("$cgi_path/$custom_file_name")) { + $set{custom} = mtime_filter($custom_file_name, $custom_mtime); } return \%set; } +sub _concatenate_css { + my @sources = map { @$_ } @_; + return unless @sources; + + my %files = + map { + (my $file = $_) =~ s/(^[^\?]+)\?.+/$1/; + $_ => $file; + } @sources; + + my $cgi_path = bz_locations()->{cgi_path}; + my $skins_path = bz_locations()->{assetsdir}; + + # build minified files + my @minified; + foreach my $source (@sources) { + next unless -e "$cgi_path/$files{$source}"; + my $file = $skins_path . '/' . md5_hex($source) . '.css'; + if (!-e $file) { + my $content = read_file("$cgi_path/$files{$source}"); + + # minify + $content =~ s{/\*.*?\*/}{}sg; # comments + $content =~ s{(^\s+|\s+$)}{}mg; # leading/trailing whitespace + $content =~ s{\n}{}g; # single line + + # rewrite urls + $content =~ s{url\(([^\)]+)\)}{_css_url_rewrite($source, $1)}eig; + + write_file($file, "/* $files{$source} */\n" . $content . "\n"); + } + push @minified, $file; + } + + # concat files + my $file = $skins_path . '/' . md5_hex(join(' ', @sources)) . '.css'; + if (!-e $file) { + my $content = ''; + foreach my $source (@minified) { + $content .= read_file($source); + } + write_file($file, $content); + } + + $file =~ s/^\Q$cgi_path\E\///o; + return mtime_filter($file); +} + +sub _css_url_rewrite { + my ($source, $url) = @_; + # rewrite relative urls as the unified stylesheet lives in a different + # directory from the source + $url =~ s/(^['"]|['"]$)//g; + if (substr($url, 0, 1) eq '/' || substr($url, 0, 5) eq 'data:') { + return 'url(' . $url . ')'; + } + return 'url(../../' . dirname($source) . '/' . $url . ')'; +} + +sub _concatenate_js { + return @_ unless CONCATENATE_ASSETS; + my ($sources) = @_; + return [] unless $sources; + $sources = ref($sources) ? $sources : [ $sources ]; + + my %files = + map { + (my $file = $_) =~ s/(^[^\?]+)\?.+/$1/; + $_ => $file; + } @$sources; + + my $cgi_path = bz_locations()->{cgi_path}; + my $skins_path = bz_locations()->{assetsdir}; + + # build minified files + my @minified; + foreach my $source (@$sources) { + next unless -e "$cgi_path/$files{$source}"; + my $file = $skins_path . '/' . md5_hex($source) . '.js'; + if (!-e $file) { + my $content = read_file("$cgi_path/$files{$source}"); + + # minimal minification + $content =~ s#/\*.*?\*/##sg; # block comments + $content =~ s#(^ +| +$)##gm; # leading/trailing spaces + $content =~ s#^//.+$##gm; # single line comments + $content =~ s#\n{2,}#\n#g; # blank lines + $content =~ s#(^\s+|\s+$)##g; # whitespace at the start/end of file + + write_file($file, "/* $files{$source} */\n" . $content . "\n"); + } + push @minified, $file; + } + + # concat files + my $file = $skins_path . '/' . md5_hex(join(' ', @$sources)) . '.js'; + if (!-e $file) { + my $content = ''; + foreach my $source (@minified) { + $content .= read_file($source); + } + write_file($file, $content); + } + + $file =~ s/^\Q$cgi_path\E\///o; + return [ $file ]; +} + # YUI dependency resolution sub yui_resolve_deps { my ($yui, $yui_deps) = @_; @@ -793,7 +900,7 @@ sub create { html_light => \&Bugzilla::Util::html_light_quote, email => \&Bugzilla::Util::email_filter, - + mtime => \&mtime_filter, # iCalendar contentline filter @@ -891,14 +998,6 @@ sub create { } }, - asset_file => sub { - return Bugzilla->asset_manager->asset_file($_[0]); - }, - - asset_files => sub { - return Bugzilla->asset_manager->asset_files(@{ $_[0] }); - }, - json_encode => sub { return encode_json($_[0]); }, @@ -992,6 +1091,7 @@ sub create { 'css_files' => \&css_files, yui_resolve_deps => \&yui_resolve_deps, + concatenate_js => \&_concatenate_js, # Whether or not keywords are enabled, in this Bugzilla. 'use_keywords' => sub { return Bugzilla::Keyword->any_exist; }, diff --git a/checksetup.pl b/checksetup.pl index b7a852e0f..d9ce9e8a9 100755 --- a/checksetup.pl +++ b/checksetup.pl @@ -52,7 +52,6 @@ GetOptions(\%switch, 'help|h|?', 'no-templates|t', 'verbose|v|no-silent', 'cpanm:s', 'check-modules', 'make-admin=s', 'reset-password=s', 'version|V', - 'no-assets', 'default-localconfig', 'no-database', 'no-permissions|p'); @@ -194,11 +193,6 @@ my %old_params = $switch{'no-database'} ? () : update_params(); Bugzilla::Template::precompile_templates(!$silent) unless $switch{'no-templates'}; -unless ($switch{'no-assets'}) { - say "Compiling assets..." unless $silent; - Bugzilla->asset_manager->compile_all; -} - ########################################################################### # Set proper rights (--CHMOD--) ########################################################################### diff --git a/extensions/BugModal/template/en/default/bug_modal/edit.html.tmpl b/extensions/BugModal/template/en/default/bug_modal/edit.html.tmpl index 18faf19cc..5ece78805 100644 --- a/extensions/BugModal/template/en/default/bug_modal/edit.html.tmpl +++ b/extensions/BugModal/template/en/default/bug_modal/edit.html.tmpl @@ -298,7 +298,7 @@ <button type="button" id="mode-btn" class="major"> <span id="mode-btn-readonly" title="Enable editing fields for [% terms.bug %] metadata">Edit [% terms.Bug %]</span> <span id="mode-btn-loading"> - <img id="edit-throbber" src="[% asset_file('extensions/BugModal/web/throbber.gif') FILTER html %]" width="16" height="11"> + <img id="edit-throbber" src="extensions/BugModal/web/throbber.gif" width="16" height="11"> Fetching </span> </button> @@ -402,7 +402,7 @@ %] <button id="product-search" type="button" class="minor">Search</button> <button id="product-search-cancel" type="button" class="minor" style="display:none">X</button> - <img id="product-throbber" src="[% asset_file('extensions/BugModal/web/throbber.gif') FILTER html %]" + <img id="product-throbber" src="extensions/BugModal/web/throbber.gif" width="16" height="11" style="display:none"> <img id="product-search-error" class="tt" src="extensions/BugModal/web/error.png" width="16" height="16" style="display:none"> diff --git a/extensions/BugmailFilter/template/en/default/account/prefs/bugmail_filter.html.tmpl b/extensions/BugmailFilter/template/en/default/account/prefs/bugmail_filter.html.tmpl index e09a16e39..e7e0ed749 100644 --- a/extensions/BugmailFilter/template/en/default/account/prefs/bugmail_filter.html.tmpl +++ b/extensions/BugmailFilter/template/en/default/account/prefs/bugmail_filter.html.tmpl @@ -6,10 +6,10 @@ # defined by the Mozilla Public License, v. 2.0. #%] -<link href="[% asset_file("extensions/BugmailFilter/web/style/bugmail-filter.css") FILTER html %]" +<link href="[% "extensions/BugmailFilter/web/style/bugmail-filter.css" FILTER mtime %]" rel="stylesheet" type="text/css"> <script type="text/javascript" - src="[% asset_file("extensions/BugmailFilter/web/js/bugmail-filter.js") FILTER html %]"></script> + src="[% "extensions/BugmailFilter/web/js/bugmail-filter.js" FILTER mtime %]"></script> [% SET selectable_products = user.get_selectable_products %] [% SET dont_show_button = 1 %] @@ -26,7 +26,7 @@ var cpts = new Array(); [% n = n + 1 %] [% END %] </script> -<script type="text/javascript" src="[% asset_file('js/productform.js') FILTER html %]"> +<script type="text/javascript" src="[% 'js/productform.js' FILTER mtime FILTER html %]"> </script> <hr> diff --git a/extensions/ComponentWatching/template/en/default/account/prefs/component_watch.html.tmpl b/extensions/ComponentWatching/template/en/default/account/prefs/component_watch.html.tmpl index 8252cdb9e..5e27c1247 100644 --- a/extensions/ComponentWatching/template/en/default/account/prefs/component_watch.html.tmpl +++ b/extensions/ComponentWatching/template/en/default/account/prefs/component_watch.html.tmpl @@ -44,7 +44,7 @@ var watch_users = new Array(); [% END %] [% END %] </script> -<script type="text/javascript" src="[% asset_file('js/productform.js') FILTER html %]"> +<script type="text/javascript" src="[% 'js/productform.js' FILTER mtime FILTER html %]"> </script> <script> diff --git a/extensions/OpenGraph/template/en/default/hook/global/header-start.html.tmpl b/extensions/OpenGraph/template/en/default/hook/global/header-start.html.tmpl index 112118280..10f501965 100644 --- a/extensions/OpenGraph/template/en/default/hook/global/header-start.html.tmpl +++ b/extensions/OpenGraph/template/en/default/hook/global/header-start.html.tmpl @@ -8,6 +8,6 @@ [% USE Bugzilla %] <meta property="og:type" content="website"> -<meta property="og:image" content='[% urlbase FILTER none %][% asset_file("extensions/OpenGraph/web/moz-social-bw-rgb-32x32.png") FILTER html %]'> +<meta property="og:image" content="[% urlbase FILTER none %]extensions/OpenGraph/web/moz-social-bw-rgb-32x32.png"> <meta property="og:title" content="[% title FILTER none %]"> <meta property="og:url" content="[% Bugzilla.cgi.self_url FILTER html %]"> diff --git a/extensions/ProdCompSearch/template/en/default/prodcompsearch/form.html.tmpl b/extensions/ProdCompSearch/template/en/default/prodcompsearch/form.html.tmpl index bbd0cfc40..c232f677d 100644 --- a/extensions/ProdCompSearch/template/en/default/prodcompsearch/form.html.tmpl +++ b/extensions/ProdCompSearch/template/en/default/prodcompsearch/form.html.tmpl @@ -33,7 +33,7 @@ <div class="pcs-header"> [% input_label FILTER none %] <img id="[% id FILTER html %]-throbber" - src="[% asset_file('extensions/ProdCompSearch/web/images/throbber.gif') FILTER html %]" + src="extensions/ProdCompSearch/web/images/throbber.gif" style="display:none" width="16" height="11"> <span class="pcs-message" id="[% id FILTER html %]-no_results" style="display:none"> No components found diff --git a/template/en/default/attachment/diff-header.html.tmpl b/template/en/default/attachment/diff-header.html.tmpl index 3e56ab2f4..b407b4f3a 100644 --- a/template/en/default/attachment/diff-header.html.tmpl +++ b/template/en/default/attachment/diff-header.html.tmpl @@ -57,9 +57,9 @@ Interdiff of #[% oldid %] and #[% newid %] for [% terms.bug %] #[% bugid %] [% ELSE %] <html> <head> - <link href="[% asset_file('skins/standard/attachment.css') FILTER html %]" + <link href="[% 'skins/standard/attachment.css' FILTER mtime %]" rel="stylesheet" type="text/css"> - <script src="[% asset_file('js/attachment.js') FILTER html %]" + <script src="[% 'js/attachment.js' FILTER mtime %]" type="text/javascript"></script> </head> <body onload="[% onload FILTER html %]"> diff --git a/template/en/default/bug/comments.html.tmpl b/template/en/default/bug/comments.html.tmpl index eb7d240b6..2f8658c9c 100644 --- a/template/en/default/bug/comments.html.tmpl +++ b/template/en/default/bug/comments.html.tmpl @@ -22,7 +22,8 @@ [% PROCESS bug/time.html.tmpl %] -<script src="[% asset_file('js/comments.js') FILTER html %]" type="text/javascript"></script> +<script src="[% 'js/comments.js' FILTER mtime %]" type="text/javascript"> +</script> <script type="text/javascript"> <!-- diff --git a/template/en/default/flag/list.html.tmpl b/template/en/default/flag/list.html.tmpl index 09113bbf3..55184c47b 100644 --- a/template/en/default/flag/list.html.tmpl +++ b/template/en/default/flag/list.html.tmpl @@ -30,7 +30,7 @@ [% DEFAULT flag_table_id = "flags" %] -<script src="[% asset_file('js/flag.js') FILTER html %]" type="text/javascript"></script> +<script src="[% 'js/flag.js' FILTER mtime %]" type="text/javascript"></script> <table id="[% flag_table_id FILTER html %]"> [% UNLESS flag_no_header %] diff --git a/template/en/default/global/header.html.tmpl b/template/en/default/global/header.html.tmpl index 57088ac2a..107c69069 100644 --- a/template/en/default/global/header.html.tmpl +++ b/template/en/default/global/header.html.tmpl @@ -52,6 +52,7 @@ style_urls = [] no_yui = 0 jquery = [] + jquery_css = [] generate_api_token = 0 %] @@ -72,7 +73,7 @@ "js/jquery/ui/jquery-ui-structure-min.css", "js/jquery/ui/jquery-ui-theme-min.css", ] %] -[% CALL style_urls.import(jq_css_urls) %] +[% style_urls.import(jquery_css, jq_css_urls) FILTER null %] [%# Add our required jQuery plugins %] [% jquery.push("cookie", "devbridgeAutocomplete") %] @@ -136,25 +137,30 @@ [% PROCESS 'global/setting-descs.none.tmpl' %] [% SET css_sets = css_files(style_urls.unique, no_yui) %] - [% SET css_standard_and_skin = css_sets.standard %] - [% CALL css_standard_and_skin.import(css_sets.skin) %] - [% IF style %] - [% FOREACH asset_url = asset_files(css_standard_and_skin) %] + [% IF constants.CONCATENATE_ASSETS %] + [% PROCESS format_css_link asset_url = css_sets.unified_standard_skin %] + [% ELSE %] + [% FOREACH asset_url = css_sets.standard %] + [% PROCESS format_css_link %] + [% END %] + [% FOREACH asset_url = css_sets.skin %] [% PROCESS format_css_link %] [% END %] + [% END %] + [% IF style %] <style type="text/css"> [% style %] </style> + [% END %] - [% FOREACH asset_url = asset_files(css_sets.custom) %] - [% PROCESS format_css_link %] - [% END %] - [% ELSE %] - [% SET css_all = [] %] - [% CALL css_all.import(css_standard_and_skin, css_sets.custom) %] - [% FOREACH asset_url = asset_files(css_all) %] - [% PROCESS format_css_link %] + [% IF css_sets.unified_custom %] + [% IF constants.CONCATENATE_ASSETS %] + [% PROCESS format_css_link asset_url = css_sets.unified_custom %] + [% ELSE %] + [% FOREACH asset_url = css_sets.custom %] + [% PROCESS format_css_link %] + [% END %] [% END %] [% END %] @@ -164,6 +170,10 @@ [% END %] [% starting_js_urls.push('js/global.js') %] + [% FOREACH asset_url = concatenate_js(starting_js_urls) %] + [% PROCESS format_js_link %] + [% END %] + [% inline_javascript = BLOCK %] [% IF NOT no_yui %] YAHOO.namespace('bugzilla'); @@ -178,26 +188,14 @@ [% javascript %] [% END %] [% END %] - [% IF inline_javascript.search("\\S") %] - [% FOREACH asset_url = asset_files(starting_js_urls) %] - [% PROCESS format_js_link %] - [% END %] - <script [% script_nonce FILTER none %]> [% inline_javascript FILTER none %] </script> + [% END %] - [% FOREACH asset_url = asset_files(javascript_urls) %] - [% PROCESS format_js_link %] - [% END %] - [% ELSE %] - [% SET js_all = [] %] - [% CALL js_all.import(starting_js_urls) %] - [% CALL js_all.import(javascript_urls) %] - [% FOREACH asset_url = asset_files(js_all) %] - [% PROCESS format_js_link %] - [% END %] + [% FOREACH asset_url = concatenate_js(javascript_urls) %] + [% PROCESS format_js_link %] [% END %] [%# this puts the live bookmark up on firefox for the Atom feed %] @@ -312,7 +310,7 @@ </td> <td id="moz_tab"> <a href="https://www.mozilla.org/" title="Mozilla - Home of the Mozilla Project"> - <img src="[% asset_file('images/tabzilla.png') FILTER html %]" border="0" height="42" width="154"></a> + <img src="images/tabzilla.png" border="0" height="42" width="154"></a> </td> </tr> </table> @@ -398,9 +396,9 @@ [% END %] [% BLOCK format_css_link %] - <link data-file="[% asset_url FILTER html %]" href="[% asset_file(asset_url) FILTER html %]" rel="stylesheet" type="text/css"> -[%+ END %] + <link href="[% asset_url FILTER html %]" rel="stylesheet" type="text/css"> +[% END %] [% BLOCK format_js_link %] - <script [% script_nonce FILTER none %] data-file="[% asset_url FILTER html %]" type="text/javascript" src="[% asset_file(asset_url) FILTER html %]"></script> -[%+ END %] + <script [% script_nonce FILTER none %] type="text/javascript" src="[% asset_url FILTER mtime FILTER html %]"></script> +[% END %] diff --git a/template/en/default/list/list-simple.html.tmpl b/template/en/default/list/list-simple.html.tmpl index 4c186e207..f4c3549ed 100644 --- a/template/en/default/list/list-simple.html.tmpl +++ b/template/en/default/list/list-simple.html.tmpl @@ -39,7 +39,7 @@ <head> <title>[% title FILTER html %]</title> <base href="[% urlbase FILTER html %]"> - <link href="[% asset_file('skins/standard/buglist.css') FILTER html %]" + <link href="[% 'skins/standard/buglist.css' FILTER mtime %]" rel="stylesheet" type="text/css"> </head> diff --git a/template/en/default/reports/duplicates-simple.html.tmpl b/template/en/default/reports/duplicates-simple.html.tmpl index 88f14b910..a08522790 100644 --- a/template/en/default/reports/duplicates-simple.html.tmpl +++ b/template/en/default/reports/duplicates-simple.html.tmpl @@ -39,9 +39,9 @@ <head> <title>[% title FILTER html %]</title> - <link href="[% asset_file('skins/standard/global.css') FILTER html %]" + <link href="[% 'skins/standard/global.css' FILTER mtime %]" rel="stylesheet" type="text/css"> - <link href="[% asset_file('skins/standard/duplicates.css') FILTER html %]" + <link href="[% 'skins/standard/duplicates.css' FILTER mtime %]" rel="stylesheet" type="text/css"> </head> diff --git a/template/en/default/rest.html.tmpl b/template/en/default/rest.html.tmpl index bc9c5b6fd..0b8321dd1 100644 --- a/template/en/default/rest.html.tmpl +++ b/template/en/default/rest.html.tmpl @@ -10,7 +10,7 @@ <html> <head> <title>Bugzilla::REST::API</title> - <link href="[% urlbase FILTER none %][% asset_file('skins/standard/global.css') FILTER html %]" + <link href="[% urlbase FILTER none %][% 'skins/standard/global.css' FILTER mtime %]" rel="stylesheet" type="text/css"> </head> <body> diff --git a/template/en/default/search/boolean-charts.html.tmpl b/template/en/default/search/boolean-charts.html.tmpl index e3b149969..3fb1f8eae 100644 --- a/template/en/default/search/boolean-charts.html.tmpl +++ b/template/en/default/search/boolean-charts.html.tmpl @@ -76,8 +76,8 @@ TUI_alternates['custom_search_advanced'] = "Show Advanced Features"; TUI_hide_default('custom_search_advanced'); </script> - <script type="text/javascript" src="[% asset_file('js/custom-search.js') FILTER html %]"></script> - <script type="text/javascript" src="[% asset_file('js/history.js/native.history.js') FILTER html %]"></script> + <script type="text/javascript" src="[% 'js/custom-search.js' FILTER mtime %]"></script> + <script type="text/javascript" src="[% 'js/history.js/native.history.js' FILTER mtime %]"></script> <script type="text/javascript"> redirect_html4_browsers(); [%# These are alternative labels for the AND and OR options in and_all_select %] |