/skins/custom
/graphs
/data
-/assets
/localconfig
/localconfig.*
/index.html
<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">
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;
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();
}
%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'))
bz_locations
+ CONCATENATE_ASSETS
+
IS_NULL
NOT_NULL
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,
# 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",
};
+++ /dev/null
-# 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;
use File::Basename;
use File::Copy qw(move);
use File::Spec;
-use File::stat;
use Cwd ();
use File::Slurp;
use IO::File;
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';
$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.
"$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,
_remove_empty_css_files();
_convert_single_file_skins();
+ _remove_dynamic_assets();
}
sub _css_url_fix {
}
}
+# 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}});
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) {
}
}
+ # 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/}) {
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) = @_;
html_light => \&Bugzilla::Util::html_light_quote,
email => \&Bugzilla::Util::email_filter,
-
+
mtime => \&mtime_filter,
# iCalendar contentline filter
}
},
- 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]);
},
'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; },
'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');
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--)
###########################################################################
<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>
%]
<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">
# 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 %]
[% 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>
[% 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>
[% 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 %]">
<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
[% 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 %]">
[% 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">
<!--
[% 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 %]
style_urls = []
no_yui = 0
jquery = []
+ jquery_css = []
generate_api_token = 0
%]
"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") %]
[% 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 %]
[% 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');
[% 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 %]
</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>
[% 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 %]
<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>
<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>
<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>
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 %]