khal: fix contact integration (#4836)

* khal: fix contact integration

- Add tests for contact+khal
- Make options `color`/`priority` available for contact accounts

* khal: add separate calendar for each contact collection

A contact account may have multiple VCARD collections, but Khal doesn't
search recursively. Collection folder names must be hardcoded, and each
has its own calendar.

- Add khal.collections option for contact accounts
- Default to previous setup for accounts with a single collection
- Add tests

* khal: specify how priority is defined by Khal

See https://khal.readthedocs.io/en/latest/configure.html
This commit is contained in:
Felipe Silva 2024-03-03 14:12:42 -03:00 committed by GitHub
parent 4de84265d7
commit d579633ff9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 120 additions and 38 deletions

View file

@ -126,6 +126,7 @@ in {
contactOpts contactOpts
(import ../programs/vdirsyncer-accounts.nix) (import ../programs/vdirsyncer-accounts.nix)
(import ../programs/khal-accounts.nix) (import ../programs/khal-accounts.nix)
(import ../programs/khal-contact-accounts.nix)
]); ]);
default = { }; default = { };
description = "List of contacts."; description = "List of contacts.";

View file

@ -13,5 +13,39 @@ with lib;
Keep khal from making any changes to this account. Keep khal from making any changes to this account.
''; '';
}; };
color = mkOption {
type = types.nullOr (types.enum [
"black"
"white"
"brown"
"yellow"
"dark gray"
"dark green"
"dark blue"
"light gray"
"light green"
"light blue"
"dark magenta"
"dark cyan"
"dark red"
"light magenta"
"light cyan"
"light red"
]);
default = null;
description = ''
Color in which events in this calendar are displayed.
'';
example = "light green";
};
priority = mkOption {
type = types.int;
default = 10;
description = ''
Priority of a calendar used for coloring (calendar with highest priority is preferred).
'';
};
}; };
} }

View file

@ -20,39 +20,5 @@ with lib;
type is set to discover. type is set to discover.
''; '';
}; };
color = mkOption {
type = types.nullOr (types.enum [
"black"
"white"
"brown"
"yellow"
"dark gray"
"dark green"
"dark blue"
"light gray"
"light green"
"light blue"
"dark magenta"
"dark cyan"
"dark red"
"light magenta"
"light cyan"
"light red"
]);
default = null;
description = ''
Color in which events in this calendar are displayed.
'';
example = "light green";
};
priority = mkOption {
type = types.int;
default = 10;
description = ''
Priority of a calendar used for coloring.
'';
};
}; };
} }

View file

@ -0,0 +1,15 @@
{ config, lib, ... }:
with lib;
{
options.khal = {
collections = mkOption {
type = types.nullOr (types.listOf types.str);
default = null;
description = ''
VCARD collections to be searched for contact birthdays.
'';
};
};
}

View file

@ -12,13 +12,25 @@ let
khalCalendarAccounts = khalCalendarAccounts =
filterAttrs (_: a: a.khal.enable) config.accounts.calendar.accounts; filterAttrs (_: a: a.khal.enable) config.accounts.calendar.accounts;
khalContactAccounts = mapAttrs (_: v: v // { type = "birthdays"; }) # a contact account may have multiple collections, each a separate calendar
(filterAttrs (_: a: a.khal.enable) config.accounts.contact.accounts); expandContactAccount = name: acct:
if acct.khal.collections != null then
listToAttrs (map (c: {
name = "${name}-${c}";
value = recursiveUpdate acct { khal.thisCollection = c; };
}) acct.khal.collections)
else {
${name} = acct;
};
khalContactAccounts = concatMapAttrs expandContactAccount
(mapAttrs (_: v: recursiveUpdate v { khal.type = "birthdays"; })
(filterAttrs (_: a: a.khal.enable) config.accounts.contact.accounts));
khalAccounts = khalCalendarAccounts // khalContactAccounts; khalAccounts = khalCalendarAccounts // khalContactAccounts;
primaryAccount = findSingle (a: a.primary) null null primaryAccount = findSingle (a: a.primary) null null
(mapAttrsToList (n: v: v // { name = n; }) khalAccounts); (mapAttrsToList (n: v: v // { name = n; }) khalCalendarAccounts);
definedAttrs = filterAttrs (_: v: !isNull v); definedAttrs = filterAttrs (_: v: !isNull v);
@ -30,6 +42,9 @@ let
"path = ${ "path = ${
value.local.path + "/" value.local.path + "/"
+ (optionalString (value.khal.type == "discover") value.khal.glob) + (optionalString (value.khal.type == "discover") value.khal.glob)
+ (optionalString
(value.khal.type == "birthdays" && value.khal ? thisCollection)
value.khal.thisCollection)
}" }"
] ++ optional (value.khal.readOnly) "readonly = True" ++ [ ] ++ optional (value.khal.readOnly) "readonly = True" ++ [
(toKeyValueIfDefined (getAttrs [ "type" "color" "priority" ] value.khal)) (toKeyValueIfDefined (getAttrs [ "type" "color" "priority" ] value.khal))
@ -153,7 +168,7 @@ in {
locale = mkOption { locale = mkOption {
type = lib.types.submodule { options = localeOptions; }; type = lib.types.submodule { options = localeOptions; };
description = '' description = ''
khal locale settings. khal locale settings.
''; '';
default = { }; default = { };
}; };

View file

@ -32,6 +32,36 @@
}; };
}; };
accounts.contact = {
basePath = "$XDG_CONFIG_HOME/card";
accounts = {
testcontacts = {
khal = {
enable = true;
collections = [ "default" "automaticallyCollected" ];
};
local.type = "filesystem";
local.fileExt = ".vcf";
name = "testcontacts";
remote = {
type = "http";
url = "https://example.com/contacts.vcf";
};
};
testcontactsNoCollections = {
khal.enable = true;
local.type = "filesystem";
local.fileExt = ".vcf";
name = "testcontactsNoCollections";
remote = {
type = "http";
url = "https://example.com/contacts.vcf";
};
};
};
};
test.stubs = { khal = { }; }; test.stubs = { khal = { }; };
nmt.script = '' nmt.script = ''

View file

@ -7,6 +7,27 @@ type=calendar
[[testcontacts-automaticallyCollected]]
path = /home/hm-user/$XDG_CONFIG_HOME/card/testcontacts/automaticallyCollected
priority=10
type=birthdays
[[testcontacts-default]]
path = /home/hm-user/$XDG_CONFIG_HOME/card/testcontacts/default
priority=10
type=birthdays
[[testcontactsNoCollections]]
path = /home/hm-user/$XDG_CONFIG_HOME/card/testcontactsNoCollections/
priority=10
type=birthdays
[default] [default]
default_calendar=test default_calendar=test
highlight_event_days=true highlight_event_days=true