Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
6d81875
Bug 1983391: Add a donation banner to the home page
justdave Jul 1, 2026
5c38e97
Bug 1983391: Dismiss the next-upgrade donation reminder from the Dona…
mrenvoize Jul 3, 2026
690fd83
Bug 1983391: Hide internal donation settings from the generic Setting…
mrenvoize Jul 3, 2026
e977585
Bug 1983391: Remove the unused Donation user-setting subclass
mrenvoize Jul 3, 2026
0021b98
Bug 1983391: Do not let the donation banner hide the new-release notice
mrenvoize Jul 3, 2026
959aa7f
Bug 1983391: Share common banner styling between release and donation…
mrenvoize Jul 3, 2026
1057bc0
Bug 1983391: Use a stateless hash token for the donation banner dismiss
mrenvoize Jul 3, 2026
0690f65
Bug 1983391: Stop stretching the Buggie donation image
mrenvoize Jul 3, 2026
e7a1b50
Bug 1983391: Only show the reminder date input for 'specific date'
mrenvoize Jul 3, 2026
d4b9357
Bug 1983391: Improve donation banner contrast in dark mode
mrenvoize Jul 3, 2026
189a2f5
Bug 1983391: Constrain home banners to the page content width
mrenvoize Jul 3, 2026
7781ac7
Bug 1983391: Default the reminder date picker to today, not the epoch
mrenvoize Jul 3, 2026
535265b
Bug 1983391: Replace donation banner placeholder image with buggie_wa…
mrenvoize Aug 3, 2026
38e1012
Merge branch 'main' into bug-1983391-harmony
justdave Aug 26, 2026
f159236
Convert the entire thing to an extension.
justdave Aug 26, 2026
9f73fa6
Put the dismissal UI in a disclosure widget for less noise
justdave Aug 26, 2026
d2f2210
Show the banner on the user preference page as well.
justdave Aug 26, 2026
6d15147
Use the good SVG file instead of the PNG mockup.
justdave Aug 26, 2026
db56c57
Let users know how to change their mind later
justdave Aug 26, 2026
d049371
brighter background behind the image
justdave Aug 26, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions extensions/BugzillaDonationBanner/Config.pm
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# 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::Extension::BugzillaDonationBanner;

use 5.14.0;
use strict;
use warnings;

use constant NAME => 'BugzillaDonationBanner';

__PACKAGE__->NAME;
124 changes: 124 additions & 0 deletions extensions/BugzillaDonationBanner/Extension.pm
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
# 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::Extension::BugzillaDonationBanner;

use 5.14.0;
use strict;
use warnings;
use base qw(Bugzilla::Extension);

use Bugzilla::Constants;
use Bugzilla::Install::Filesystem;
use Bugzilla::User::Setting qw(add_setting);
use Bugzilla::Extension::BugzillaDonationBanner::Donation;

our $VERSION = '1.0';

sub config_add_panels {
my ($self, $args) = @_;
$args->{panel_modules}->{Donation}
= 'Bugzilla::Extension::BugzillaDonationBanner::Config';
}

sub install_before_final_checks {
add_setting({
name => 'donate_banner_pref',
options => ['next_upgrade', 'specific_date', 'never'],
default => 'next_upgrade',
category => 'Bugzilla.org',
});
add_setting({
name => 'donate_banner_last_version',
default => '0.0',
category => 'Bugzilla.org',
});
add_setting({
name => 'donate_banner_reminder_date',
default => '1970-01-01',
category => 'Bugzilla.org',
});
}

sub install_filesystem {
my ($self, $args) = @_;
my $files = $args->{files};
my $extension_dir = bz_locations()->{extensionsdir} . '/'
. __PACKAGE__->NAME;

$files->{"$extension_dir/bin/donate.cgi"}
= {perms => Bugzilla::Install::Filesystem::WS_EXECUTE};
$files->{"$extension_dir/web/buggie_watering.svg"}
= {perms => Bugzilla::Install::Filesystem::WS_SERVE};
$files->{"$extension_dir/web/donation-banner.css"}
= {perms => Bugzilla::Install::Filesystem::WS_SERVE};
$files->{"$extension_dir/web/donation-banner.js"}
= {perms => Bugzilla::Install::Filesystem::WS_SERVE};
}

sub app_startup {
my ($self, $args) = @_;
my $app = $args->{app};
my $r = $app->routes;

Bugzilla::App::CGI->load_one(
'bugzilla_donation_banner_cgi',
'extensions/BugzillaDonationBanner/bin/donate.cgi'
);
$r->post('/extensions/BugzillaDonationBanner/bin/donate.cgi')
->to('CGI#bugzilla_donation_banner_cgi');
}

sub template_before_process {
my ($self, $args) = @_;
my ($file, $vars) = @$args{qw(file vars)};

if ($file =~ m{(?:^|/)index\.html\.tmpl$}) {
$vars->{donation}
= Bugzilla::Extension::BugzillaDonationBanner::Donation::get_banner();
}
}

sub user_preferences_settings {
my ($self, $args) = @_;
my $skip_settings = $args->{skip_settings};
$skip_settings->{donate_banner_pref} = 1;
$skip_settings->{donate_banner_last_version} = 1;
$skip_settings->{donate_banner_reminder_date} = 1;
}

sub user_preferences {
my ($self, $args) = @_;
return unless $args->{current_tab} eq 'donate';

my $vars = $args->{vars};
my $user = Bugzilla->user;
my $cgi = Bugzilla->cgi;

if ($args->{save_changes}) {
my $pref = $cgi->param('donate_banner_pref') || 'next_upgrade';
my $date = $cgi->param('donate_banner_reminder_date') || '';
Bugzilla::Extension::BugzillaDonationBanner::Donation::save_preference(
$pref, $date);
$vars->{changes_saved} = 1;
}

$vars->{settings} = $user->settings(1);
$vars->{donation}
= Bugzilla::Extension::BugzillaDonationBanner::Donation::get_banner(1);
$vars->{donate_pref} = $user->setting('donate_banner_pref');
$vars->{today}
= Bugzilla::Extension::BugzillaDonationBanner::Donation::today();
my $reminder_date = $user->setting('donate_banner_reminder_date');
$vars->{reminder_date}
= !$reminder_date || $reminder_date eq '1970-01-01'
? $vars->{today}
: $reminder_date;
${$args->{handled}} = 1;
}

__PACKAGE__->NAME;
29 changes: 29 additions & 0 deletions extensions/BugzillaDonationBanner/bin/donate.cgi
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/usr/bin/env perl

use 5.14.0;
use strict;
use warnings;

use lib qw(../../.. ../../../lib);

use Bugzilla;
use Bugzilla::Constants;
use Bugzilla::Error;
use Bugzilla::Token qw(check_hash_token);
use Bugzilla::Extension::BugzillaDonationBanner::Donation;

my $cgi = Bugzilla->cgi;
if ($cgi->request_method ne 'POST') {
ThrowCodeError('illegal_request_method',
{method => $cgi->request_method, accepted => ['POST']});
}

Bugzilla->login(LOGIN_REQUIRED) unless Bugzilla->user->id;
check_hash_token($cgi->param('token'), ['donation_banner']);

Bugzilla::Extension::BugzillaDonationBanner::Donation::dismiss(
scalar $cgi->param('donate_action'),
scalar $cgi->param('donate_banner_reminder_date'),
);

$cgi->base_redirect();
28 changes: 28 additions & 0 deletions extensions/BugzillaDonationBanner/lib/Config.pm
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# 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::Extension::BugzillaDonationBanner::Config;

use 5.14.0;
use strict;
use warnings;

use Bugzilla::Config::Common;

our $sortkey = 175;

use constant get_param_list => (
{
name => 'donation_banner_visibility',
type => 's',
choices => ['admins_only', 'end_users', 'disabled'],
default => 'admins_only',
checker => \&check_multi,
},
);

1;
130 changes: 130 additions & 0 deletions extensions/BugzillaDonationBanner/lib/Donation.pm
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
# 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::Extension::BugzillaDonationBanner::Donation;

use 5.14.0;
use strict;
use warnings;

use Bugzilla::Constants;
use Bugzilla::Error;
use Bugzilla::Token qw(issue_hash_token);
use Bugzilla::User::Setting qw(clear_settings_cache);
use Bugzilla::Util qw(validate_date);

use DateTime;

use constant BANNER_URL => 'https://bugzilla.org/donate';
use constant BANNER_MESSAGES => (
'Help us make Bugzilla better more often.',
'A small donation helps keep Bugzilla moving forward.',
'Support the people who keep Bugzilla running.',
'Even a little funding helps Bugzilla improve more quickly.',
'If Bugzilla helps your team, consider helping Bugzilla too.',
);

sub today {
return DateTime->now(time_zone => Bugzilla->local_timezone)->ymd;
}

sub get_banner {
my ($ignore_preference) = @_;
my $user = Bugzilla->user;
return undef if !$user->id;

my $visibility = Bugzilla->params->{donation_banner_visibility}
|| 'admins_only';
return undef if $visibility eq 'disabled';
return undef if $visibility eq 'admins_only' && !$user->in_group('admin');

my $settings = $user->settings;
my $pref = $settings->{donate_banner_pref}->{value};
my $last_version = $settings->{donate_banner_last_version}->{value} || '0.0';
my $reminder_date
= $settings->{donate_banner_reminder_date}->{value} || '1970-01-01';
my $current_version = BUGZILLA_VERSION;

my ($show, $show_thanks) = (0, 0);
if ($pref eq 'next_upgrade') {
$show = $last_version ne $current_version;
$show_thanks = $show
&& $user->in_group('admin')
&& $last_version ne '0'
&& $last_version ne '0.0';
}
elsif ($pref eq 'specific_date') {
$show = $reminder_date le today();
}

return undef if !$show && !$ignore_preference;

my @messages = BANNER_MESSAGES;
my $data = {
url => BANNER_URL,
message => $messages[int(rand(@messages))],
show_thanks => $show_thanks,
token => issue_hash_token(['donation_banner']),
today => today(),
settings_link => 'editparams.cgi?section=donation#donation_banner_visibility_desc',
};

if ($visibility eq 'admins_only') {
$data->{visibility_note}
= 'This message is only shown to logged-in users with admin privs.';
}
elsif ($user->in_group('admin')) {
$data->{visibility_note}
= 'This message is visible to all logged-in users.';
}

return $data;
}

sub save_preference {
my ($pref, $date) = @_;
my $user = Bugzilla->user;
my $settings = $user->settings;

if ($pref eq 'specific_date') {
validate_date($date)
|| ThrowUserError('illegal_date', {date => $date, format => 'YYYY-MM-DD'});
$settings->{donate_banner_reminder_date}->set($date);
}
elsif ($pref ne 'next_upgrade' && $pref ne 'never') {
ThrowUserError('invalid_parameter', {
name => 'donate_banner_pref',
err => "must be one of 'next_upgrade', 'specific_date', or 'never'",
});
}

$settings->{donate_banner_pref}->set($pref);
$settings->{donate_banner_last_version}->set(BUGZILLA_VERSION);
clear_settings_cache($user->id);
}

sub dismiss {
my ($action, $date) = @_;
my $settings = Bugzilla->user->settings;

if ($action eq 'next_upgrade') {
save_preference('next_upgrade', '');
}
elsif ($action eq 'week' || $action eq 'month') {
my $dt = DateTime->now(time_zone => Bugzilla->local_timezone);
$dt->add(days => $action eq 'week' ? 7 : 30);
save_preference('specific_date', $dt->ymd);
}
elsif ($action eq 'date') {
save_preference('specific_date', $date);
}
elsif ($action eq 'never') {
save_preference('never', '');
}
}

1;
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
[%# 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/.
#%]

[% PROCESS "donation/banner.html.tmpl" show_dismissal=0 %]

<p>Choose when you want to be reminded about supporting Bugzilla on the home page:</p>

<table>
<tr>
<td><label for="donate_banner_pref">Reminder:</label></td>
<td>
<select id="donate_banner_pref" name="donate_banner_pref">
<option value="next_upgrade" [% ' selected="selected"' IF donate_pref == 'next_upgrade' %]>
Remind me after the next Bugzilla upgrade
</option>
<option value="specific_date" [% ' selected="selected"' IF donate_pref == 'specific_date' %]>
Remind me on a specific date
</option>
<option value="never" [% ' selected="selected"' IF donate_pref == 'never' %]>
Never show this again
</option>
</select>
</td>
</tr>
<tr id="donate_date_row"[% ' style="display: none"' UNLESS donate_pref == 'specific_date' %]>
<td><label for="donate_banner_reminder_date">Date:</label></td>
<td>
<input type="date" id="donate_banner_reminder_date"
name="donate_banner_reminder_date"
value="[% reminder_date FILTER html %]">
<div class="hint">Used only when a specific date is selected.</div>
</td>
</tr>
</table>
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[%# 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/.
#%]

[% title = 'Donation';
desc = 'Configure the Bugzilla donation banner';
param_descs = {
donation_banner_visibility => 'Controls which logged-in users can see the donation banner on the Bugzilla home page.'
}
%]
Loading
Loading