thunderbird: support declaration of calendars (#5484)

Adds calendar support using the `accounts.calendar.accounts.*` options.
This commit is contained in:
DerRockWolf 2025-06-16 02:21:02 +02:00 committed by GitHub
parent cef3e0adc0
commit 66523b0efe
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 256 additions and 20 deletions

View file

@ -0,0 +1,8 @@
{ config, ... }:
{
time = "2025-06-04T18:00:00+00:00";
condition = config.programs.thunderbird.enable;
message = ''
'programs.thunderbird' now supports declaration of calendars using 'accounts.calendar.accounts'.
'';
}

View file

@ -32,11 +32,14 @@ let
moduleName = "programs.thunderbird";
enabledAccounts = attrValues (
lib.filterAttrs (_: a: a.thunderbird.enable) config.accounts.email.accounts
);
filterEnabled = accounts: attrValues (lib.filterAttrs (_: a: a.thunderbird.enable) accounts);
addId = map (a: a // { id = builtins.hashString "sha256" a.name; });
enabledAccountsWithId = map (a: a // { id = builtins.hashString "sha256" a.name; }) enabledAccounts;
enabledEmailAccounts = filterEnabled config.accounts.email.accounts;
enabledEmailAccountsWithId = addId enabledEmailAccounts;
enabledCalendarAccounts = filterEnabled config.accounts.calendar.accounts;
enabledCalendarAccountsWithId = addId enabledCalendarAccounts;
thunderbirdConfigPath = if isDarwin then "Library/Thunderbird" else ".thunderbird";
@ -159,6 +162,36 @@ let
)
// account.thunderbird.settings id;
toThunderbirdCalendar =
calendar: _:
let
inherit (calendar) id;
in
{
"calendar.registry.calendar_${id}.name" = calendar.name;
"calendar.registry.calendar_${id}.calendar-main-in-composite" = true;
"calendar.registry.calendar_${id}.cache.enabled" = true;
}
// optionalAttrs (calendar.remote == null) {
"calendar.registry.calendar_${id}.type" = "storage";
"calendar.registry.calendar_${id}.uri" = "moz-storage-calendar://";
}
// optionalAttrs (calendar.remote != null) {
"calendar.registry.calendar_${id}.type" =
if (calendar.remote.type == "http") then "ics" else calendar.remote.type;
"calendar.registry.calendar_${id}.uri" = calendar.remote.url;
"calendar.registry.calendar_${id}.username" = calendar.remote.userName;
}
// optionalAttrs calendar.primary {
"calendar.registry.calendar_${id}.calendar-main-default" = true;
}
// optionalAttrs calendar.thunderbird.readOnly {
"calendar.registry.calendar_${id}.readOnly" = true;
}
// optionalAttrs (calendar.thunderbird.color != "") {
"calendar.registry.calendar_${id}.color" = calendar.thunderbird.color;
};
toThunderbirdFeed =
feed: profile:
let
@ -212,7 +245,7 @@ let
''
+ lib.concatStrings (map (f: mkFilterToIniString f) filters);
getEmailAccountsForProfile =
getAccountsForProfile =
profileName: accounts:
(filter (
a: a.thunderbird.profiles == [ ] || lib.any (p: p == profileName) a.thunderbird.profiles
@ -346,6 +379,26 @@ in
]
'';
};
calendarAccountsOrder = mkOption {
type = types.listOf types.str;
default = [ ];
description = ''
Custom ordering of calendar accounts. The accounts are specified
by their name. For declarative accounts, it must be the name
of their attribute in `config.accounts.calendar.accounts`.
Enabled accounts that aren't listed here appear in an arbitrary
order after the ordered accounts.
'';
example = ''
[
"my-awesome-account"
"private"
"work"
"holidays"
/* Other accounts in arbitrary order */
]
'';
};
withExternalGnupg = mkOption {
type = types.bool;
@ -608,6 +661,41 @@ in
)
);
};
accounts.calendar.accounts = mkOption {
type =
with types;
attrsOf (submodule {
options.thunderbird = {
enable = lib.mkEnableOption "the Thunderbird mail client for this account";
profiles = mkOption {
type = with types; listOf str;
default = [ ];
example = literalExpression ''
[ "profile1" "profile2" ]
'';
description = ''
List of Thunderbird profiles for which this account should be
enabled. If this list is empty (the default), this account will
be enabled for all declared profiles.
'';
};
readOnly = mkOption {
type = bool;
default = false;
description = "Mark calendar as read only";
};
color = mkOption {
type = str;
default = "";
example = "#dc8add";
description = "Display color of the calendar in hex";
};
};
});
};
};
config = mkIf cfg.enable {
@ -635,7 +723,9 @@ in
(
let
profiles = lib.catAttrs "name" profilesWithId;
selectedProfiles = lib.concatMap (a: a.thunderbird.profiles) enabledAccounts;
selectedProfiles = lib.concatMap (a: a.thunderbird.profiles) (
enabledEmailAccounts ++ enabledCalendarAccounts
);
in
{
assertion = (lib.intersectLists profiles selectedProfiles) == selectedProfiles;
@ -647,6 +737,27 @@ in
+ (concatStringsSep "," selectedProfiles);
}
)
(
let
foundCalendars = filter (
a: a.remote != null && a.remote.type == "google_calendar"
) enabledCalendarAccounts;
in
{
assertion = length foundCalendars == 0;
message =
'''accounts.calendar.accounts.<name>.remote.type = "google_calendar";' is not directly supported by Thunderbird, ''
+ "but declared for these calendars: "
+ (concatStringsSep ", " (lib.catAttrs "name" foundCalendars))
+ "\n"
+ ''
To use google calendars in Thunderbird choose 'type = "caldav"' instead.
The 'url' will be "https://apidata.googleusercontent.com/caldav/v2/ID/events/", replace ID with the "Calendar ID".
The ID can be found in the Google Calendar web app: Settings > Settings for my calendars > scroll to "Integrate calendar" > copy the "Calendar ID".
'';
}
)
];
home.packages = [
@ -677,14 +788,14 @@ in
"${thunderbirdProfilesPath}/${name}/user.js" =
let
emailAccounts = getEmailAccountsForProfile name enabledAccountsWithId;
emailAccounts = getAccountsForProfile name enabledEmailAccountsWithId;
calendarAccounts = getAccountsForProfile name enabledCalendarAccountsWithId;
smtp = filter (a: a.smtp != null) emailAccounts;
feedAccounts = map (f: f // { id = builtins.hashString "sha256" f.name; }) (
attrValues profile.feedAccounts
);
feedAccounts = addId (attrValues profile.feedAccounts);
# NOTE: `calendarAccounts` not added here as calendars are not part of the 'Mail' view
accounts = emailAccounts ++ feedAccounts;
orderedAccounts =
@ -707,6 +818,23 @@ in
lib.optionals (accounts != [ ]) (
accountsOrderIds ++ (lib.lists.subtractLists accountsOrderIds enabledAccountsIds)
);
orderedCalendarAccounts =
let
accountNameToId = builtins.listToAttrs (
map (a: {
name = a.name;
value = "calendar_${a.id}";
}) calendarAccounts
);
accountsOrderIds = map (a: accountNameToId."${a}" or a) profile.calendarAccountsOrder;
enabledAccountsIds = (lib.attrsets.mapAttrsToList (name: value: value) accountNameToId);
in
lib.optionals (calendarAccounts != [ ]) (
accountsOrderIds ++ (lib.lists.subtractLists accountsOrderIds enabledAccountsIds)
);
in
{
text = mkUserJs (builtins.foldl' (a: b: a // b) { } (
@ -717,6 +845,10 @@ in
"mail.accountmanager.accounts" = concatStringsSep "," orderedAccounts;
})
(optionalAttrs (length orderedCalendarAccounts != 0) {
"calendar.list.sortOrder" = concatStringsSep " " orderedCalendarAccounts;
})
(optionalAttrs (length smtp != 0) {
"mail.smtpservers" = concatStringsSep "," (map (a: "smtp_${a.id}") smtp);
})
@ -726,6 +858,7 @@ in
profile.settings
]
++ (map (a: toThunderbirdAccount a profile) emailAccounts)
++ (map (c: toThunderbirdCalendar c profile) calendarAccounts)
++ (map (f: toThunderbirdFeed f profile) feedAccounts)
)) profile.extraConfig;
};
@ -755,7 +888,7 @@ in
let
emailAccountsWithFilters = (
filter (a: a.thunderbird.messageFilters != [ ]) (
getEmailAccountsForProfile name enabledAccountsWithId
getAccountsForProfile name enabledEmailAccountsWithId
)
);
in

View file

@ -1,5 +1,20 @@
// Generated by Home Manager.
user_pref("calendar.list.sortOrder", "calendar_5152790e278eb89039f8bfaa354b944ec1b44c5d3fc144edc5720c3edc045c73 imperative_cal calendar_474c12b01f4f765680ac3bb3e0b670b7ac817c9f717997577cac3f12f1b5013a");
user_pref("calendar.registry.calendar_474c12b01f4f765680ac3bb3e0b670b7ac817c9f717997577cac3f12f1b5013a.cache.enabled", true);
user_pref("calendar.registry.calendar_474c12b01f4f765680ac3bb3e0b670b7ac817c9f717997577cac3f12f1b5013a.calendar-main-in-composite", true);
user_pref("calendar.registry.calendar_474c12b01f4f765680ac3bb3e0b670b7ac817c9f717997577cac3f12f1b5013a.name", "holidays");
user_pref("calendar.registry.calendar_474c12b01f4f765680ac3bb3e0b670b7ac817c9f717997577cac3f12f1b5013a.readOnly", true);
user_pref("calendar.registry.calendar_474c12b01f4f765680ac3bb3e0b670b7ac817c9f717997577cac3f12f1b5013a.type", "ics");
user_pref("calendar.registry.calendar_474c12b01f4f765680ac3bb3e0b670b7ac817c9f717997577cac3f12f1b5013a.uri", "https://www.thunderbird.net/media/caldata/autogen/GermanHolidays.ics");
user_pref("calendar.registry.calendar_474c12b01f4f765680ac3bb3e0b670b7ac817c9f717997577cac3f12f1b5013a.username", null);
user_pref("calendar.registry.calendar_5152790e278eb89039f8bfaa354b944ec1b44c5d3fc144edc5720c3edc045c73.cache.enabled", true);
user_pref("calendar.registry.calendar_5152790e278eb89039f8bfaa354b944ec1b44c5d3fc144edc5720c3edc045c73.calendar-main-default", true);
user_pref("calendar.registry.calendar_5152790e278eb89039f8bfaa354b944ec1b44c5d3fc144edc5720c3edc045c73.calendar-main-in-composite", true);
user_pref("calendar.registry.calendar_5152790e278eb89039f8bfaa354b944ec1b44c5d3fc144edc5720c3edc045c73.name", "calendar");
user_pref("calendar.registry.calendar_5152790e278eb89039f8bfaa354b944ec1b44c5d3fc144edc5720c3edc045c73.type", "caldav");
user_pref("calendar.registry.calendar_5152790e278eb89039f8bfaa354b944ec1b44c5d3fc144edc5720c3edc045c73.uri", "https://my.caldav.server/calendar");
user_pref("calendar.registry.calendar_5152790e278eb89039f8bfaa354b944ec1b44c5d3fc144edc5720c3edc045c73.username", "testuser");
user_pref("general.useragent.override", "");
user_pref("mail.account.account_bcd3ace52bed41febb6cdc2fb1303aebaa573e0d993872da503950901bb6c6fc.identities", "id_bcd3ace52bed41febb6cdc2fb1303aebaa573e0d993872da503950901bb6c6fc");
user_pref("mail.account.account_bcd3ace52bed41febb6cdc2fb1303aebaa573e0d993872da503950901bb6c6fc.server", "server_bcd3ace52bed41febb6cdc2fb1303aebaa573e0d993872da503950901bb6c6fc");

View file

@ -1,5 +1,20 @@
// Generated by Home Manager.
user_pref("calendar.list.sortOrder", "calendar_5152790e278eb89039f8bfaa354b944ec1b44c5d3fc144edc5720c3edc045c73 imperative_cal calendar_474c12b01f4f765680ac3bb3e0b670b7ac817c9f717997577cac3f12f1b5013a");
user_pref("calendar.registry.calendar_474c12b01f4f765680ac3bb3e0b670b7ac817c9f717997577cac3f12f1b5013a.cache.enabled", true);
user_pref("calendar.registry.calendar_474c12b01f4f765680ac3bb3e0b670b7ac817c9f717997577cac3f12f1b5013a.calendar-main-in-composite", true);
user_pref("calendar.registry.calendar_474c12b01f4f765680ac3bb3e0b670b7ac817c9f717997577cac3f12f1b5013a.name", "holidays");
user_pref("calendar.registry.calendar_474c12b01f4f765680ac3bb3e0b670b7ac817c9f717997577cac3f12f1b5013a.readOnly", true);
user_pref("calendar.registry.calendar_474c12b01f4f765680ac3bb3e0b670b7ac817c9f717997577cac3f12f1b5013a.type", "ics");
user_pref("calendar.registry.calendar_474c12b01f4f765680ac3bb3e0b670b7ac817c9f717997577cac3f12f1b5013a.uri", "https://www.thunderbird.net/media/caldata/autogen/GermanHolidays.ics");
user_pref("calendar.registry.calendar_474c12b01f4f765680ac3bb3e0b670b7ac817c9f717997577cac3f12f1b5013a.username", null);
user_pref("calendar.registry.calendar_5152790e278eb89039f8bfaa354b944ec1b44c5d3fc144edc5720c3edc045c73.cache.enabled", true);
user_pref("calendar.registry.calendar_5152790e278eb89039f8bfaa354b944ec1b44c5d3fc144edc5720c3edc045c73.calendar-main-default", true);
user_pref("calendar.registry.calendar_5152790e278eb89039f8bfaa354b944ec1b44c5d3fc144edc5720c3edc045c73.calendar-main-in-composite", true);
user_pref("calendar.registry.calendar_5152790e278eb89039f8bfaa354b944ec1b44c5d3fc144edc5720c3edc045c73.name", "calendar");
user_pref("calendar.registry.calendar_5152790e278eb89039f8bfaa354b944ec1b44c5d3fc144edc5720c3edc045c73.type", "caldav");
user_pref("calendar.registry.calendar_5152790e278eb89039f8bfaa354b944ec1b44c5d3fc144edc5720c3edc045c73.uri", "https://my.caldav.server/calendar");
user_pref("calendar.registry.calendar_5152790e278eb89039f8bfaa354b944ec1b44c5d3fc144edc5720c3edc045c73.username", "testuser");
user_pref("general.useragent.override", "");
user_pref("mail.account.account_bcd3ace52bed41febb6cdc2fb1303aebaa573e0d993872da503950901bb6c6fc.identities", "id_bcd3ace52bed41febb6cdc2fb1303aebaa573e0d993872da503950901bb6c6fc");
user_pref("mail.account.account_bcd3ace52bed41febb6cdc2fb1303aebaa573e0d993872da503950901bb6c6fc.server", "server_bcd3ace52bed41febb6cdc2fb1303aebaa573e0d993872da503950901bb6c6fc");

View file

@ -1,5 +1,18 @@
// Generated by Home Manager.
user_pref("calendar.list.sortOrder", "calendar1 calendar_474c12b01f4f765680ac3bb3e0b670b7ac817c9f717997577cac3f12f1b5013a calendar_25bf8e1a2393f1108d37029b3df5593236c755742ec93465bbafa9b290bddcf6");
user_pref("calendar.registry.calendar_25bf8e1a2393f1108d37029b3df5593236c755742ec93465bbafa9b290bddcf6.cache.enabled", true);
user_pref("calendar.registry.calendar_25bf8e1a2393f1108d37029b3df5593236c755742ec93465bbafa9b290bddcf6.calendar-main-in-composite", true);
user_pref("calendar.registry.calendar_25bf8e1a2393f1108d37029b3df5593236c755742ec93465bbafa9b290bddcf6.name", "local");
user_pref("calendar.registry.calendar_25bf8e1a2393f1108d37029b3df5593236c755742ec93465bbafa9b290bddcf6.type", "storage");
user_pref("calendar.registry.calendar_25bf8e1a2393f1108d37029b3df5593236c755742ec93465bbafa9b290bddcf6.uri", "moz-storage-calendar://");
user_pref("calendar.registry.calendar_474c12b01f4f765680ac3bb3e0b670b7ac817c9f717997577cac3f12f1b5013a.cache.enabled", true);
user_pref("calendar.registry.calendar_474c12b01f4f765680ac3bb3e0b670b7ac817c9f717997577cac3f12f1b5013a.calendar-main-in-composite", true);
user_pref("calendar.registry.calendar_474c12b01f4f765680ac3bb3e0b670b7ac817c9f717997577cac3f12f1b5013a.name", "holidays");
user_pref("calendar.registry.calendar_474c12b01f4f765680ac3bb3e0b670b7ac817c9f717997577cac3f12f1b5013a.readOnly", true);
user_pref("calendar.registry.calendar_474c12b01f4f765680ac3bb3e0b670b7ac817c9f717997577cac3f12f1b5013a.type", "ics");
user_pref("calendar.registry.calendar_474c12b01f4f765680ac3bb3e0b670b7ac817c9f717997577cac3f12f1b5013a.uri", "https://www.thunderbird.net/media/caldata/autogen/GermanHolidays.ics");
user_pref("calendar.registry.calendar_474c12b01f4f765680ac3bb3e0b670b7ac817c9f717997577cac3f12f1b5013a.username", null);
user_pref("general.useragent.override", "");
user_pref("mail.account.account_bcd3ace52bed41febb6cdc2fb1303aebaa573e0d993872da503950901bb6c6fc.identities", "id_bcd3ace52bed41febb6cdc2fb1303aebaa573e0d993872da503950901bb6c6fc");
user_pref("mail.account.account_bcd3ace52bed41febb6cdc2fb1303aebaa573e0d993872da503950901bb6c6fc.server", "server_bcd3ace52bed41febb6cdc2fb1303aebaa573e0d993872da503950901bb6c6fc");

View file

@ -1,5 +1,18 @@
// Generated by Home Manager.
user_pref("calendar.list.sortOrder", "calendar1 calendar_474c12b01f4f765680ac3bb3e0b670b7ac817c9f717997577cac3f12f1b5013a calendar_25bf8e1a2393f1108d37029b3df5593236c755742ec93465bbafa9b290bddcf6");
user_pref("calendar.registry.calendar_25bf8e1a2393f1108d37029b3df5593236c755742ec93465bbafa9b290bddcf6.cache.enabled", true);
user_pref("calendar.registry.calendar_25bf8e1a2393f1108d37029b3df5593236c755742ec93465bbafa9b290bddcf6.calendar-main-in-composite", true);
user_pref("calendar.registry.calendar_25bf8e1a2393f1108d37029b3df5593236c755742ec93465bbafa9b290bddcf6.name", "local");
user_pref("calendar.registry.calendar_25bf8e1a2393f1108d37029b3df5593236c755742ec93465bbafa9b290bddcf6.type", "storage");
user_pref("calendar.registry.calendar_25bf8e1a2393f1108d37029b3df5593236c755742ec93465bbafa9b290bddcf6.uri", "moz-storage-calendar://");
user_pref("calendar.registry.calendar_474c12b01f4f765680ac3bb3e0b670b7ac817c9f717997577cac3f12f1b5013a.cache.enabled", true);
user_pref("calendar.registry.calendar_474c12b01f4f765680ac3bb3e0b670b7ac817c9f717997577cac3f12f1b5013a.calendar-main-in-composite", true);
user_pref("calendar.registry.calendar_474c12b01f4f765680ac3bb3e0b670b7ac817c9f717997577cac3f12f1b5013a.name", "holidays");
user_pref("calendar.registry.calendar_474c12b01f4f765680ac3bb3e0b670b7ac817c9f717997577cac3f12f1b5013a.readOnly", true);
user_pref("calendar.registry.calendar_474c12b01f4f765680ac3bb3e0b670b7ac817c9f717997577cac3f12f1b5013a.type", "ics");
user_pref("calendar.registry.calendar_474c12b01f4f765680ac3bb3e0b670b7ac817c9f717997577cac3f12f1b5013a.uri", "https://www.thunderbird.net/media/caldata/autogen/GermanHolidays.ics");
user_pref("calendar.registry.calendar_474c12b01f4f765680ac3bb3e0b670b7ac817c9f717997577cac3f12f1b5013a.username", null);
user_pref("general.useragent.override", "");
user_pref("mail.account.account_bcd3ace52bed41febb6cdc2fb1303aebaa573e0d993872da503950901bb6c6fc.identities", "id_bcd3ace52bed41febb6cdc2fb1303aebaa573e0d993872da503950901bb6c6fc");
user_pref("mail.account.account_bcd3ace52bed41febb6cdc2fb1303aebaa573e0d993872da503950901bb6c6fc.server", "server_bcd3ace52bed41febb6cdc2fb1303aebaa573e0d993872da503950901bb6c6fc");

View file

@ -58,6 +58,37 @@
};
};
accounts.calendar.accounts = {
calendar = {
thunderbird = {
enable = true;
profiles = [ "first" ];
};
primary = true;
remote = {
type = "caldav";
url = "https://my.caldav.server/calendar";
userName = "testuser";
};
};
holidays = {
thunderbird = {
enable = true;
readOnly = true;
};
remote = {
type = "http";
url = "https://www.thunderbird.net/media/caldata/autogen/GermanHolidays.ics";
};
};
local = {
thunderbird = {
enable = true;
profiles = [ "second" ];
};
};
};
programs.thunderbird = {
enable = true;
package = config.lib.test.mkStubPackage {
@ -86,17 +117,25 @@
"imperative_account"
"hm-account"
];
};
second.settings = {
"second.setting" = "some-test-setting";
second.nested.evenFurtherNested = [
1
2
3
calendarAccountsOrder = [
"calendar"
"imperative_cal"
"holidays"
];
};
second.accountsOrder = [ "account1" ];
second = {
settings = {
"second.setting" = "some-test-setting";
second.nested.evenFurtherNested = [
1
2
3
];
};
accountsOrder = [ "account1" ];
calendarAccountsOrder = [ "calendar1" ];
};
};
settings = {