1
0
Fork 0
mirror of https://github.com/nix-community/home-manager.git synced 2025-03-31 04:04:32 +00:00

firefox: refactor bookmarks into a submodule & require force (#6402)

This splits the bookmarks submodule into a seperate file, to make it easier to maintain (like how the search module was previously split out in #5697).

This also refactors bookmarks to require a new force option, to be more explicit about overriding existing bookmarks.
This commit is contained in:
Kira Bruneau 2025-03-19 14:37:13 -04:00 committed by GitHub
parent 1727f417b7
commit 9d554281e0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 317 additions and 231 deletions

View file

@ -56,10 +56,10 @@ let
else
builtins.toJSON pref);
mkUserJs = prePrefs: prefs: extraPrefs: bookmarks: extensions:
mkUserJs = prePrefs: prefs: extraPrefs: bookmarksFile: extensions:
let
prefs' = lib.optionalAttrs ([ ] != bookmarks) {
"browser.bookmarks.file" = toString (browserBookmarksFile bookmarks);
prefs' = lib.optionalAttrs (bookmarksFile != null) {
"browser.bookmarks.file" = toString bookmarksFile;
"browser.places.importBookmarksHTML" = true;
} // lib.optionalAttrs (extensions != { }) {
"extensions.webextensions.ExtensionStorageIDB.enabled" = false;
@ -112,59 +112,6 @@ let
}}
'';
browserBookmarksFile = bookmarks:
let
indent = level:
lib.concatStringsSep "" (map (lib.const " ") (lib.range 1 level));
bookmarkToHTML = indentLevel: bookmark:
''
${indent indentLevel}<DT><A HREF="${
escapeXML bookmark.url
}" ADD_DATE="1" LAST_MODIFIED="1"${
lib.optionalString (bookmark.keyword != null)
" SHORTCUTURL=\"${escapeXML bookmark.keyword}\""
}${
lib.optionalString (bookmark.tags != [ ])
" TAGS=\"${escapeXML (concatStringsSep "," bookmark.tags)}\""
}>${escapeXML bookmark.name}</A>'';
directoryToHTML = indentLevel: directory: ''
${indent indentLevel}<DT>${
if directory.toolbar then
''
<H3 ADD_DATE="1" LAST_MODIFIED="1" PERSONAL_TOOLBAR_FOLDER="true">Bookmarks Toolbar''
else
''<H3 ADD_DATE="1" LAST_MODIFIED="1">${escapeXML directory.name}''
}</H3>
${indent indentLevel}<DL><p>
${allItemsToHTML (indentLevel + 1) directory.bookmarks}
${indent indentLevel}</DL><p>'';
itemToHTMLOrRecurse = indentLevel: item:
if item ? "url" then
bookmarkToHTML indentLevel item
else
directoryToHTML indentLevel item;
allItemsToHTML = indentLevel: bookmarks:
lib.concatStringsSep "\n"
(map (itemToHTMLOrRecurse indentLevel) bookmarks);
bookmarkEntries = allItemsToHTML 1 bookmarks;
in pkgs.writeText "${packageName}-bookmarks.html" ''
<!DOCTYPE NETSCAPE-Bookmark-file-1>
<!-- This is an automatically generated file.
It will be read and overwritten.
DO NOT EDIT! -->
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">
<TITLE>Bookmarks</TITLE>
<H1>Bookmarks Menu</H1>
<DL><p>
${bookmarkEntries}
</DL>
'';
mkNoDuplicateAssertion = entities: entityKind:
(let
# Return an attribute set with entity IDs as keys and a list of
@ -334,6 +281,8 @@ in {
profiles = mkOption {
inherit visible;
type = types.attrsOf (types.submodule ({ config, name, ... }: {
imports = [ (pkgs.path + "/nixos/modules/misc/assertions.nix") ];
options = {
name = mkOption {
type = types.str;
@ -430,104 +379,32 @@ in {
};
bookmarks = mkOption {
type = (with types;
coercedTo (listOf anything) (bookmarks:
warn ''
${cfg.name} bookmarks have been refactored into a submodule that now explicitly require a 'force' option to be enabled.
Replace:
${moduleName}.profiles.${name}.bookmarks = [ ... ];
With:
${moduleName}.profiles.${name}.bookmarks = {
force = true;
settings = [ ... ];
};
'' {
force = true;
settings = bookmarks;
}) (submodule ({ config, ... }:
import ./profiles/bookmarks.nix {
inherit config lib pkgs;
modulePath = modulePath ++ [ "profiles" name "bookmarks" ];
})));
default = { };
internal = !enableBookmarks;
type = let
bookmarkSubmodule = types.submodule ({ config, name, ... }: {
options = {
name = mkOption {
type = types.str;
default = name;
description = "Bookmark name.";
};
tags = mkOption {
type = types.listOf types.str;
default = [ ];
description = "Bookmark tags.";
};
keyword = mkOption {
type = types.nullOr types.str;
default = null;
description = "Bookmark search keyword.";
};
url = mkOption {
type = types.str;
description = "Bookmark url, use %s for search terms.";
};
};
}) // {
description = "bookmark submodule";
};
bookmarkType = types.addCheck bookmarkSubmodule (x: x ? "url");
directoryType = types.submodule ({ config, name, ... }: {
options = {
name = mkOption {
type = types.str;
default = name;
description = "Directory name.";
};
bookmarks = mkOption {
type = types.listOf nodeType;
default = [ ];
description = "Bookmarks within directory.";
};
toolbar = mkOption {
type = types.bool;
default = false;
description = ''
Make this the toolbar directory. Note, this does _not_
mean that this directory will be added to the toolbar,
this directory _is_ the toolbar.
'';
};
};
}) // {
description = "directory submodule";
};
nodeType = types.either bookmarkType directoryType;
in with types;
coercedTo (attrsOf nodeType) attrValues (listOf nodeType);
default = [ ];
example = literalExpression ''
[
{
name = "wikipedia";
tags = [ "wiki" ];
keyword = "wiki";
url = "https://en.wikipedia.org/wiki/Special:Search?search=%s&go=Go";
}
{
name = "kernel.org";
url = "https://www.kernel.org";
}
{
name = "Nix sites";
toolbar = true;
bookmarks = [
{
name = "homepage";
url = "https://nixos.org/";
}
{
name = "wiki";
tags = [ "wiki" "nix" ];
url = "https://wiki.nixos.org/";
}
];
}
]
'';
description = ''
Preloaded bookmarks. Note, this may silently overwrite any
previously existing bookmarks!
'';
description = "Declarative bookmarks.";
};
path = mkOption {
@ -763,6 +640,26 @@ in {
'';
};
};
config = {
assertions = [
(mkNoDuplicateAssertion config.containers "container")
{
assertion = config.extensions.settings == { }
|| config.extensions.force;
message = ''
Using '${
lib.showAttrPath (modulePath
++ [ "profiles" profileName "extensions" "settings" ])
}' will override all previous extensions settings.
Enable '${
lib.showAttrPath (modulePath
++ [ "profiles" profileName "extensions" "force" ])
}' to acknowledge this.
'';
}
] ++ config.bookmarks.assertions;
};
}));
default = { };
description = "Attribute set of ${appName} profiles.";
@ -826,22 +723,7 @@ in {
}
(mkNoDuplicateAssertion cfg.profiles "profile")
] ++ (mapAttrsToList
(_: profile: mkNoDuplicateAssertion profile.containers "container")
cfg.profiles) ++ (mapAttrsToList (profileName: profile: {
assertion = profile.extensions.settings == { }
|| profile.extensions.force;
message = ''
Using '${
lib.showAttrPath
(modulePath ++ [ "profiles" profileName "extensions" "settings" ])
}' will override all previous extensions settings.
Enable '${
lib.showAttrPath
(modulePath ++ [ "profiles" profileName "extensions" "force" ])
}' to acknowledge this.
'';
}) cfg.profiles);
] ++ (concatMap (profile: profile.assertions) (attrValues cfg.profiles));
warnings = optional (cfg.enableGnomeExtensions or false) ''
Using '${moduleName}.enableGnomeExtensions' has been deprecated and
@ -883,10 +765,10 @@ in {
"${profilesPath}/${profile.path}/user.js" = mkIf (profile.preConfig
!= "" || profile.settings != { } || profile.extraConfig != ""
|| profile.bookmarks != [ ]) {
|| profile.bookmarks.configFile != null) {
text =
mkUserJs profile.preConfig profile.settings profile.extraConfig
profile.bookmarks profile.extensions.settings;
profile.bookmarks.configFile profile.extensions.settings;
};
"${profilesPath}/${profile.path}/containers.json" =

View file

@ -0,0 +1,203 @@
{ config, lib, pkgs, modulePath }:
with lib;
let
bookmarkSubmodule = types.submodule ({ name, ... }: {
options = {
name = mkOption {
type = types.str;
default = name;
description = "Bookmark name.";
};
tags = mkOption {
type = types.listOf types.str;
default = [ ];
description = "Bookmark tags.";
};
keyword = mkOption {
type = types.nullOr types.str;
default = null;
description = "Bookmark search keyword.";
};
url = mkOption {
type = types.str;
description = "Bookmark url, use %s for search terms.";
};
};
}) // {
description = "bookmark submodule";
};
bookmarkType = types.addCheck bookmarkSubmodule (x: x ? "url");
directoryType = types.submodule ({ name, ... }: {
options = {
name = mkOption {
type = types.str;
default = name;
description = "Directory name.";
};
bookmarks = mkOption {
type = types.listOf nodeType;
default = [ ];
description = "Bookmarks within directory.";
};
toolbar = mkOption {
type = types.bool;
default = false;
description = ''
Make this the toolbar directory. Note, this does _not_
mean that this directory will be added to the toolbar,
this directory _is_ the toolbar.
'';
};
};
}) // {
description = "directory submodule";
};
nodeType = types.either bookmarkType directoryType;
bookmarksFile = bookmarks:
let
indent = level:
lib.concatStringsSep "" (map (lib.const " ") (lib.range 1 level));
bookmarkToHTML = indentLevel: bookmark:
''
${indent indentLevel}<DT><A HREF="${
escapeXML bookmark.url
}" ADD_DATE="1" LAST_MODIFIED="1"${
lib.optionalString (bookmark.keyword != null)
" SHORTCUTURL=\"${escapeXML bookmark.keyword}\""
}${
lib.optionalString (bookmark.tags != [ ])
" TAGS=\"${escapeXML (concatStringsSep "," bookmark.tags)}\""
}>${escapeXML bookmark.name}</A>'';
directoryToHTML = indentLevel: directory: ''
${indent indentLevel}<DT>${
if directory.toolbar then
''
<H3 ADD_DATE="1" LAST_MODIFIED="1" PERSONAL_TOOLBAR_FOLDER="true">Bookmarks Toolbar''
else
''<H3 ADD_DATE="1" LAST_MODIFIED="1">${escapeXML directory.name}''
}</H3>
${indent indentLevel}<DL><p>
${allItemsToHTML (indentLevel + 1) directory.bookmarks}
${indent indentLevel}</DL><p>'';
itemToHTMLOrRecurse = indentLevel: item:
if item ? "url" then
bookmarkToHTML indentLevel item
else
directoryToHTML indentLevel item;
allItemsToHTML = indentLevel: bookmarks:
lib.concatStringsSep "\n"
(map (itemToHTMLOrRecurse indentLevel) bookmarks);
bookmarkEntries = allItemsToHTML 1 bookmarks;
in pkgs.writeText "bookmarks.html" ''
<!DOCTYPE NETSCAPE-Bookmark-file-1>
<!-- This is an automatically generated file.
It will be read and overwritten.
DO NOT EDIT! -->
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">
<TITLE>Bookmarks</TITLE>
<H1>Bookmarks Menu</H1>
<DL><p>
${bookmarkEntries}
</DL>
'';
in {
imports = [
(pkgs.path + "/nixos/modules/misc/assertions.nix")
(pkgs.path + "/nixos/modules/misc/meta.nix")
];
# We're currently looking for a maintainer who actively uses bookmarks!
meta.maintainers = with maintainers; [ kira-bruneau ];
options = {
enable = mkOption {
type = with types; bool;
default = config.settings != [ ];
internal = true;
};
force = mkOption {
type = with types; bool;
default = false;
description = ''
Whether to force override existing custom bookmarks.
'';
};
settings = mkOption {
type = with types;
coercedTo (attrsOf nodeType) attrValues (listOf nodeType);
default = [ ];
example = literalExpression ''
[
{
name = "wikipedia";
tags = [ "wiki" ];
keyword = "wiki";
url = "https://en.wikipedia.org/wiki/Special:Search?search=%s&go=Go";
}
{
name = "kernel.org";
url = "https://www.kernel.org";
}
{
name = "Nix sites";
toolbar = true;
bookmarks = [
{
name = "homepage";
url = "https://nixos.org/";
}
{
name = "wiki";
tags = [ "wiki" "nix" ];
url = "https://wiki.nixos.org/";
}
];
}
]
'';
description = ''
Custom bookmarks.
'';
};
configFile = mkOption {
type = with types; nullOr path;
default = if config.enable then bookmarksFile config.settings else null;
description = ''
Configuration file to define custom bookmarks.
'';
};
};
config = {
assertions = [{
assertion = config.enable -> config.force;
message = ''
Using '${
lib.showAttrPath (modulePath ++ [ "settings" ])
}' will override all previous bookmarks.
Enable ${
lib.showAttrPath (modulePath ++ [ "force" ])
}' to acknowledge this.
'';
}];
};
}

View file

@ -7,12 +7,6 @@ let
firefoxMockOverlay = import ../../setup-firefox-mock-overlay.nix modulePath;
withName = path:
pkgs.substituteAll {
src = path;
name = cfg.wrappedPackageName;
};
in {
imports = [ firefoxMockOverlay ];
@ -20,52 +14,56 @@ in {
enable = true;
profiles.bookmarks = {
settings = { "general.smoothScroll" = false; };
bookmarks = [
{
toolbar = true;
bookmarks = [{
name = "Home Manager";
url = "https://wiki.nixos.org/wiki/Home_Manager";
}];
}
{
name = "wikipedia";
tags = [ "wiki" ];
keyword = "wiki";
url = "https://en.wikipedia.org/wiki/Special:Search?search=%s&go=Go";
}
{
name = "kernel.org";
url = "https://www.kernel.org";
}
{
name = "Nix sites";
bookmarks = [
{
name = "homepage";
url = "https://nixos.org/";
}
{
name = "wiki";
tags = [ "wiki" "nix" ];
url = "https://wiki.nixos.org/";
}
{
name = "Nix sites";
bookmarks = [
{
name = "homepage";
url = "https://nixos.org/";
}
{
name = "wiki";
url = "https://wiki.nixos.org/";
}
];
}
];
}
];
bookmarks = {
force = true;
settings = [
{
toolbar = true;
bookmarks = [{
name = "Home Manager";
url = "https://wiki.nixos.org/wiki/Home_Manager";
}];
}
{
name = "wikipedia";
tags = [ "wiki" ];
keyword = "wiki";
url =
"https://en.wikipedia.org/wiki/Special:Search?search=%s&go=Go";
}
{
name = "kernel.org";
url = "https://www.kernel.org";
}
{
name = "Nix sites";
bookmarks = [
{
name = "homepage";
url = "https://nixos.org/";
}
{
name = "wiki";
tags = [ "wiki" "nix" ];
url = "https://wiki.nixos.org/";
}
{
name = "Nix sites";
bookmarks = [
{
name = "homepage";
url = "https://nixos.org/";
}
{
name = "wiki";
url = "https://wiki.nixos.org/";
}
];
}
];
}
];
};
};
} // {
nmt.script = ''
@ -74,7 +72,7 @@ in {
assertFileContent \
$bookmarksUserJs \
${withName ./expected-bookmarks-user.js}
${./expected-bookmarks-user.js}
bookmarksFile="$(sed -n \
'/browser.bookmarks.file/ {s|^.*\(/nix/store[^"]*\).*|\1|;p}' \

View file

@ -2,7 +2,7 @@
user_pref("browser.bookmarks.file", "/nix/store/00000000000000000000000000000000-@name@-bookmarks.html");
user_pref("browser.bookmarks.file", "/nix/store/00000000000000000000000000000000-bookmarks.html");
user_pref("browser.places.importBookmarksHTML", true);
user_pref("general.smoothScroll", false);

View file

@ -12,13 +12,16 @@ in {
main = {
isDefault = true;
id = 1;
bookmarks = [{
toolbar = true;
bookmarks = [{
name = "Home Manager";
url = "https://wiki.nixos.org/wiki/Home_Manager";
bookmarks = {
force = true;
settings = [{
toolbar = true;
bookmarks = [{
name = "Home Manager";
url = "https://wiki.nixos.org/wiki/Home_Manager";
}];
}];
}];
};
containers = {
"shopping" = {
icon = "circle";