<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">
AddType image/x-icon .ico
AddType application/font-woff .woff
+AddType application/font-woff2 .woff2
Redirect permanent /queryhelp.cgi https://bugzilla.mozilla.org/query.cgi?format=advanced&help=1
Redirect permanent /bug_status.html https://bugzilla.mozilla.org/page.cgi?id=fields.html
# heartbeat.cgi returns 200 if the DB and memcached are both working, and 500 otherwise.
RewriteRule ^__heartbeat__$ heartbeat.cgi [L]
+RewriteRule ^static/v\d{4}\d{2}\d{2}\.\d+/(.+\.(?:js|css|woff2?|png|jpe?g|gif|ico|svg)) $1 [NC,E=IMMUTABLE:1,L]
+Header set Cache-Control "public, max-age=31536000" env=REDIRECT_IMMUTABLE
+
RewriteRule ^robots\.txt$ robots.cgi [L]
# New single page interface for filing bugs
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,
_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}});
# Header Generation #
#####################
-# Returns the last modification time of a file, as an integer number of
-# seconds since the epoch.
-sub _mtime { return (stat($_[0]))[9] }
-
-sub mtime_filter {
- my ($file_url, $mtime) = @_;
- # This environment var is set in the .htaccess if we have mod_headers
- # and mod_expires installed, to make sure that JS and CSS with "?"
- # after them will still be cached by clients.
- return $file_url if !$ENV{BZ_CACHE_CONTROL};
- if (!$mtime) {
- my $cgi_path = bz_locations()->{'cgi_path'};
- my $file_path = "$cgi_path/$file_url";
- $mtime = _mtime($file_path);
- }
- return "$file_url?$mtime";
+sub version_filter {
+ my ($file_url) = @_;
+ return "static/v" . Bugzilla->VERSION . "/$file_url";
}
# Set up the skin CSS cascade:
}
}
- # 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 => mtime_filter($file_name));
+ my %set = (standard => version_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 (my $mtime = _mtime("$cgi_path/$skin_file_name")) {
- $set{skin} = mtime_filter($skin_file_name, $mtime);
+ if (-f "$cgi_path/$skin_file_name") {
+ $set{skin} = version_filter($skin_file_name);
}
my $custom_file_name = $file_name;
$custom_file_name =~ s{(?:^|/)skins/standard/}{skins/custom/};
- if (my $custom_mtime = _mtime("$cgi_path/$custom_file_name")) {
- $set{custom} = mtime_filter($custom_file_name, $custom_mtime);
+ if (-f "$cgi_path/$custom_file_name") {
+ $set{custom} = version_filter($custom_file_name);
}
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) = @_;
email => \&Bugzilla::Util::email_filter,
- mtime => \&mtime_filter,
+ version => \&version_filter,
# iCalendar contentline filter
ics => [ sub {
'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; },
<div id="header-external-links" class="dropdown first">
<button type="button" id="header-external-menu-button" class="dropdown-button minor" title="Mozilla"
aria-label="Mozilla" aria-expanded="false" aria-haspopup="true" aria-controls="header-external-menu">
- <img src="extensions/BMO/web/images/moz-fav-bw-rgb.svg" width="32" height="32" alt="">
+ <img src="[% 'extensions/BMO/web/images/moz-fav-bw-rgb.svg' FILTER version %]" width="32" height="32" alt="">
</button>
<ul class="dropdown-content right" id="header-external-menu" role="menu" style="display:none;">
<li role="presentation">
# defined by the Mozilla Public License, v. 2.0.
#%]
-<link href="[% "extensions/BugmailFilter/web/style/bugmail-filter.css" FILTER mtime %]"
+<link href="[% "extensions/BugmailFilter/web/style/bugmail-filter.css" FILTER version %]"
rel="stylesheet" type="text/css">
<script type="text/javascript"
- src="[% "extensions/BugmailFilter/web/js/bugmail-filter.js" FILTER mtime %]"></script>
+ src="[% "extensions/BugmailFilter/web/js/bugmail-filter.js" FILTER version %]"></script>
[% SET selectable_products = user.get_selectable_products %]
[% SET dont_show_button = 1 %]
[% n = n + 1 %]
[% END %]
</script>
-<script type="text/javascript" src="[% 'js/productform.js' FILTER mtime FILTER html %]">
+<script type="text/javascript" src="[% 'js/productform.js' FILTER version FILTER html %]">
</script>
<hr>
[% END %]
[% END %]
</script>
-<script type="text/javascript" src="[% 'js/productform.js' FILTER mtime FILTER html %]">
+<script type="text/javascript" src="[% 'js/productform.js' FILTER version FILTER html %]">
</script>
<script>
[% IF attachid %]
Attachment #[% attachid %]: [% description FILTER html %]
[% ELSE %]
- Diff Between
+ Diff Between
#[% oldid %]: <a href="[% PROCESS diffurl id=oldid %]">[% old_desc FILTER html %]</a>
- and
+ and
#[% newid %]: <a href="[% PROCESS diffurl id=newid %]">[% new_desc FILTER html %]</a>
[% END %]
for <a href="show_bug.cgi?id=[% bugid %]">[% terms.bug %] #[% bugid %]</a>
[% ELSE %]
<html>
<head>
- <link href="[% 'skins/standard/attachment.css' FILTER mtime %]"
+ <link href="[% 'skins/standard/attachment.css' FILTER version %]"
rel="stylesheet" type="text/css">
- <script src="[% 'js/attachment.js' FILTER mtime %]"
+ <script src="[% 'js/attachment.js' FILTER version %]"
type="text/javascript"></script>
</head>
<body onload="[% onload FILTER html %]">
[% END %]
-
+
[%# If we have attachid, we are in diff, otherwise we're in interdiff %]
[% IF attachid %]
[%# HEADER %]
|
[% END %]
[% END %]
-
+
[%# Collapse / Expand %]
<a href="#"
onmouseover="lastStatus = window.status; window.status='Collapse All'; return true"
onmouseout="window.status = lastStatus; return true"
- onclick="return collapse_all()">Collapse All</a> |
+ onclick="return collapse_all()">Collapse All</a> |
<a href="#"
onmouseover="lastStatus = window.status; window.status='Expand All'; return true"
onmouseout="window.status = lastStatus; return true"
[%# only happens for normal viewing, not interdiff %]
| <span style='font-weight: bold'>Context:</span>
[% IF context == "patch" %]
- (<strong>Patch</strong> /
+ (<strong>Patch</strong> /
[% ELSE %]
- (<a href="[% PROCESS diffurl id=attachid %]&headers=[% headers FILTER uri %]">Patch</a> /
+ (<a href="[% PROCESS diffurl id=attachid %]&headers=[% headers FILTER uri %]">Patch</a> /
[% END %]
[% IF context == "file" %]
<strong>File</strong> /
[% ELSE %]
- <a href="[% PROCESS diffurl id=attachid %]&headers=[% headers FILTER uri %]&context=file">File</a> /
+ <a href="[% PROCESS diffurl id=attachid %]&headers=[% headers FILTER uri %]&context=file">File</a> /
[% END %]
[% IF context == "patch" || context == "file" %]
[% ELSE %]
<br><br>
[% END %]
-
+
[%# Restore Stuff %]
<form name="checkboxform" action="attachment.cgi">
<input type="checkbox" name="restore_indicator" style="display: none">
[% PROCESS bug/time.html.tmpl %]
-<script src="[% 'js/comments.js' FILTER mtime %]" type="text/javascript">
+<script src="[% 'js/comments.js' FILTER version %]" type="text/javascript">
</script>
<script type="text/javascript">
[% IF user.is_insider %]
if (document.getElementById('isprivate_' + real_id).checked) {
document.getElementById('newcommentprivacy').checked = 'checked';
- updateCommentTagControl(document.getElementById('newcommentprivacy'), 'comment');
+ updateCommentTagControl(document.getElementById('newcommentprivacy'), 'comment');
}
[% END %]
[% Hook.process("comment_banner") %]
-<!-- This auto-sizes the comments and positions the collapse/expand links
+<!-- This auto-sizes the comments and positions the collapse/expand links
to the right. -->
<table class="bz_comment_table" cellpadding="0" cellspacing="0"><tr>
<td>
[% IF count >= start_at %]
[% PROCESS a_comment %]
[% END %]
-
+
[% count = count + increment %]
[% END %]
[%# Note: this template is used in multiple places; if you use this hook,
# make sure you are aware of this fact.
- #%]
+ #%]
[% Hook.process("aftercomments") %]
</td>
[% END %]
<span role="heading" aria-level="2" class="bz_comment_number">
- <a
+ <a
href="show_bug.cgi?id=[% bug.bug_id %]#c[% comment.count %]">
[%- comment_label FILTER html %]</a>
</span>
[% IF user.is_timetracker &&
(comment.work_time > 0 || comment.work_time < 0) %]
<br>
- Additional hours worked:
+ Additional hours worked:
[% PROCESS formattimeunit time_unit=comment.work_time %]
[% END %]
[%# We list flags by looping twice over the flag types relevant for the bug.
# In the first loop, we display existing flags and then, for active types,
- # we display UI for adding new flags. In the second loop, we display UI
+ # we display UI for adding new flags. In the second loop, we display UI
# for adding additional new flags for those types for which a flag already
# exists but which are multiplicable (can have multiple flags of the type
# on a single bug/attachment).
[% DEFAULT flag_table_id = "flags" %]
-<script src="[% 'js/flag.js' FILTER mtime %]" type="text/javascript"></script>
+<script src="[% 'js/flag.js' FILTER version %]" type="text/javascript"></script>
<table id="[% flag_table_id FILTER html %]">
[% UNLESS flag_no_header %]
[% PROCESS 'global/setting-descs.none.tmpl' %]
[% SET css_sets = css_files(style_urls.unique, no_yui) %]
- [% 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 %]
+ [% FOREACH asset_url = css_sets.standard %]
+ [% PROCESS format_css_link %]
+ [% END %]
+ [% FOREACH asset_url = css_sets.skin %]
+ [% PROCESS format_css_link %]
[% END %]
[% IF style %]
</style>
[% END %]
- [% 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 %]
+ [% FOREACH asset_url = css_sets.custom %]
+ [% PROCESS format_css_link %]
[% END %]
[%# jQuery Plugins %]
[% END %]
[% starting_js_urls.push('js/global.js', 'js/dropdown.js') %]
- [% FOREACH asset_url = concatenate_js(starting_js_urls) %]
+ [% FOREACH asset_url = starting_js_urls %]
[% PROCESS format_js_link %]
[% END %]
</script>
[% END %]
- [% FOREACH asset_url = concatenate_js(javascript_urls) %]
+ [% FOREACH asset_url = javascript_urls %]
[% PROCESS format_js_link %]
[% END %]
[% BLOCK format_css_link %]
<link href="[% asset_url FILTER html %]" rel="stylesheet" type="text/css">
+ [% "\n" %]
[% END %]
[% BLOCK format_js_link %]
- <script [% script_nonce FILTER none %] type="text/javascript" src="[% asset_url FILTER mtime FILTER html %]"></script>
+ <script [% script_nonce FILTER none %] type="text/javascript" src="[% asset_url FILTER version FILTER html %]"></script>
+ [% "\n" %]
[% END %]
<head>
<title>[% title FILTER html %]</title>
<base href="[% urlbase FILTER html %]">
- <link href="[% 'skins/standard/buglist.css' FILTER mtime %]"
+ <link href="[% 'skins/standard/buglist.css' FILTER version %]"
rel="stylesheet" type="text/css">
</head>
# Copyright (C) 1998 Netscape Communications Corporation. All
# Rights Reserved.
#
- # Contributor(s):
+ # Contributor(s):
# Gervase Markham <gerv@gerv.net>
# Max Kanat-Alexander <mkanat@bugzilla.org>
#%]
<head>
<title>[% title FILTER html %]</title>
- <link href="[% 'skins/standard/global.css' FILTER mtime %]"
+ <link href="[% 'skins/standard/global.css' FILTER version %]"
rel="stylesheet" type="text/css">
- <link href="[% 'skins/standard/duplicates.css' FILTER mtime %]"
+ <link href="[% 'skins/standard/duplicates.css' FILTER version %]"
rel="stylesheet" type="text/css">
</head>
<html>
<head>
<title>Bugzilla::REST::API</title>
- <link href="[% urlbase FILTER none %][% 'skins/standard/global.css' FILTER mtime %]"
+ <link href="[% urlbase FILTER none %][% 'skins/standard/global.css' FILTER version %]"
rel="stylesheet" type="text/css">
</head>
<body>
#%]
[% PROCESS "global/field-descs.none.tmpl" %]
-
+
[% types = [
"noop",
"equals",
<div class="bz_section_title" id="custom_search_filter">
<div id="custom_search_query_controller" class="arrow">▼</div>
<a id="chart" href="javascript:TUI_toggle_class('custom_search_query')" >
- Custom Search</a> <span class="section_help">Didn't find what
- you're looking for above? This area allows for ANDs, ORs,
+ Custom Search</a> <span class="section_help">Didn't find what
+ you're looking for above? This area allows for ANDs, ORs,
and other more complex searches.</span>
</div>
-<div id="custom_search_filter_section"
+<div id="custom_search_filter_section"
class="bz_search_section custom_search_query">
[% SET indent_level = 0 %]
[% SET cond_num = 0 %]
TUI_alternates['custom_search_advanced'] = "Show Advanced Features";
TUI_hide_default('custom_search_advanced');
</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" src="[% 'js/custom-search.js' FILTER version %]"></script>
+ <script type="text/javascript" src="[% 'js/history.js/native.history.js' FILTER version %]"></script>
<script type="text/javascript">
redirect_html4_browsers();
[%# These are alternative labels for the AND and OR options in and_all_select %]
[% BLOCK one_condition %]
[%# Skip any conditions that don't have a field defined. %]
[% RETURN IF !condition.f %]
-
+
[% IF !top_level_any_shown %]
[% INCLUDE any_all_select
name = "j_top" selected = default.j_top.0
<div class="custom_search_condition"
[% ' style="margin-left: ' _ (indent_level * 2) _ 'em"' IF indent_level %]
[% ' id="custom_search_last_row"' IF with_buttons %]>
-
+
[% IF previous_condition.f == "OP" %]
[% INCLUDE any_all_select
- name = "j" _ (cond_num - 1)
+ name = "j" _ (cond_num - 1)
selected = previous_condition.j %]
[% END %]
</option>
[% END %]
</select>
-
+
[% INCLUDE "search/type-select.html.tmpl"
name = "o${cond_num}", class = "custom_search_form_field"
types = types, selected = condition.o %]
-
+
<input name="v[% cond_num FILTER html %]" title="Value"
class="custom_search_form_field"
onchange="fix_query_string(this)"
value="[% condition.v FILTER html %]">
[% END %]
-
+
[% IF with_buttons %]
<button class="custom_search_add_button" type="button"
id="add_button" title="Add a new row"
</span>
[% END %]
</div>
-
+
[% previous_condition = condition %]
[% END %]
<Directory "/vagrant">
DirectoryIndex index.cgi
Options Indexes FollowSymLinks ExecCGI
- AllowOverride All
+ AllowOverride None
Allow from all
</Directory>
</VirtualHost>