forked from Fediversity/fediversity.eu
Compare commits
No commits in common. "e0bb83610f1452a8a51448e7c7c3e457d8fbc27c" and "c1dd3bdfab3115d2c5dc25b8d513d22b2eef2d25" have entirely different histories.
e0bb83610f
...
c1dd3bdfab
|
@ -3,11 +3,11 @@ let
|
||||||
inherit (config) pages;
|
inherit (config) pages;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
imports = lib.nixFiles ./.;
|
imports = with lib.fileset; toList (difference (fileFilter ({ hasExt, ... }: hasExt "nix") ./.) ./default.nix);
|
||||||
|
|
||||||
collections.news.type = config.content-types.article;
|
collections.news.type = config.content-types.article;
|
||||||
|
|
||||||
pages.index = { link, ... }: {
|
pages.index = {
|
||||||
title = "Fediversity";
|
title = "Fediversity";
|
||||||
locations = [
|
locations = [
|
||||||
"index.html"
|
"index.html"
|
||||||
|
@ -21,13 +21,13 @@ in
|
||||||
|
|
||||||
${pages.fediversity.summary}
|
${pages.fediversity.summary}
|
||||||
|
|
||||||
[Learn more about Fediversity](${link pages.fediversity})
|
[Learn more about Fediversity](${pages.fediversity})
|
||||||
|
|
||||||
# Fediversity grants
|
# Fediversity grants
|
||||||
|
|
||||||
${pages.grants.summary}
|
${pages.grants.summary}
|
||||||
|
|
||||||
[Learn more about Fediversity grants](${link pages.grants})
|
[Learn more about Fediversity grants](${pages.grants})
|
||||||
|
|
||||||
# Consortium
|
# Consortium
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ in
|
||||||
|
|
||||||
${partner.description}
|
${partner.description}
|
||||||
|
|
||||||
[Read more about ${partner.title}](${link partner})
|
[Read more about ${partner.title}](${partner})
|
||||||
'') (with pages; [ nlnet oid tweag nordunet ]))}
|
'') (with pages; [ nlnet oid tweag nordunet ]))}
|
||||||
|
|
||||||
# News
|
# News
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
{ config, lib, ... }:
|
{ config, ... }:
|
||||||
let
|
let
|
||||||
inherit (config) pages;
|
inherit (config) pages;
|
||||||
in
|
in
|
||||||
|
@ -11,21 +11,11 @@ in
|
||||||
menu.items = map (page: { inherit page; }) (with pages; [ nlnet oid tweag nordunet ]);
|
menu.items = map (page: { inherit page; }) (with pages; [ nlnet oid tweag nordunet ]);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
menu.label = "News";
|
page = pages.fediversity;
|
||||||
menu.items =
|
}
|
||||||
let
|
{
|
||||||
sorted = with lib; reverseList (sortOn (entry: entry.date) config.collections.news.entry);
|
page = pages.grants;
|
||||||
in
|
|
||||||
map
|
|
||||||
(page: {
|
|
||||||
page = lib.recursiveUpdate page {
|
|
||||||
title = "${page.date}: ${page.title}";
|
|
||||||
};
|
|
||||||
})
|
|
||||||
(lib.take 3 sorted);
|
|
||||||
}
|
}
|
||||||
{ page = pages.fediversity; }
|
|
||||||
{ page = pages.grants; }
|
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
40
lib.nix
40
lib.nix
|
@ -36,45 +36,7 @@ rec {
|
||||||
splitLines = s: with builtins; filter (x: !isList x) (split "\n" s);
|
splitLines = s: with builtins; filter (x: !isList x) (split "\n" s);
|
||||||
|
|
||||||
indent = prefix: s:
|
indent = prefix: s:
|
||||||
with lib.lists;
|
join "\n" (map (x: if x == "" then x else "${prefix}${x}") (splitLines s));
|
||||||
let
|
|
||||||
lines = splitLines s;
|
|
||||||
in
|
|
||||||
join "\n" (
|
|
||||||
[ (head lines) ]
|
|
||||||
++
|
|
||||||
(map (x: if x == "" then x else "${prefix}${x}") (tail lines))
|
|
||||||
);
|
|
||||||
|
|
||||||
relativePath = path1': path2':
|
|
||||||
let
|
|
||||||
inherit (lib.path) subpath;
|
|
||||||
inherit (lib) lists;
|
|
||||||
|
|
||||||
path1 = subpath.components path1';
|
|
||||||
path2 = subpath.components path2';
|
|
||||||
|
|
||||||
commonPrefixLength = with lists;
|
|
||||||
findFirstIndex (i: i.fst != i.snd)
|
|
||||||
{ fst = null; snd = null; }
|
|
||||||
(zipLists path1 path2);
|
|
||||||
|
|
||||||
relativeComponents = with lists;
|
|
||||||
[ "." ] ++ (replicate (length path1 - commonPrefixLength - 1) "..") ++
|
|
||||||
(drop commonPrefixLength path2);
|
|
||||||
in
|
|
||||||
join "/" relativeComponents;
|
|
||||||
|
|
||||||
/**
|
|
||||||
Recursively list all Nix files from a directory, except the top-level `default.nix`
|
|
||||||
|
|
||||||
Useful for module system `imports` from a top-level module.
|
|
||||||
**/
|
|
||||||
nixFiles = dir: with lib.fileset;
|
|
||||||
toList (difference
|
|
||||||
(fileFilter ({ hasExt, ... }: hasExt "nix") dir)
|
|
||||||
(dir + "/default.nix")
|
|
||||||
);
|
|
||||||
|
|
||||||
types = rec {
|
types = rec {
|
||||||
collection = elemType:
|
collection = elemType:
|
||||||
|
|
|
@ -1,35 +0,0 @@
|
||||||
{ config, options, lib, ... }:
|
|
||||||
let
|
|
||||||
inherit (lib)
|
|
||||||
mkOption
|
|
||||||
types
|
|
||||||
;
|
|
||||||
cfg = config;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
content-types. article = { config, collection, ... }: {
|
|
||||||
imports = [ cfg.content-types.page ];
|
|
||||||
options = {
|
|
||||||
collection = mkOption {
|
|
||||||
description = "Collection this article belongs to";
|
|
||||||
type = options.collections.type.nestedTypes.elemType;
|
|
||||||
default = collection;
|
|
||||||
};
|
|
||||||
date = mkOption {
|
|
||||||
description = "Publication date";
|
|
||||||
type = with types; str;
|
|
||||||
default = null;
|
|
||||||
};
|
|
||||||
author = mkOption {
|
|
||||||
description = "Page author";
|
|
||||||
type = with types; either str (nonEmptyListOf str);
|
|
||||||
default = null;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
config.name = lib.slug config.title;
|
|
||||||
# TODO: this should be covered by the TBD `link` function instead,
|
|
||||||
# taking a historical list of collection names into account
|
|
||||||
config.outPath = "${collection.name}/${lib.head config.locations}";
|
|
||||||
config.outputs.html = lib.mkForce (cfg.templates.html.article cfg config);
|
|
||||||
};
|
|
||||||
}
|
|
150
structure/content-types.nix
Normal file
150
structure/content-types.nix
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
{ config, lib, options, ... }:
|
||||||
|
let
|
||||||
|
inherit (lib)
|
||||||
|
mkOption
|
||||||
|
types
|
||||||
|
;
|
||||||
|
cfg = config;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options = {
|
||||||
|
content-types = mkOption {
|
||||||
|
description = "Content types";
|
||||||
|
type = with types; attrsOf deferredModule;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
config.content-types = {
|
||||||
|
document = { name, config, ... }: {
|
||||||
|
options = {
|
||||||
|
name = mkOption {
|
||||||
|
description = "Symbolic name, used as a human-readable identifier";
|
||||||
|
type = types.str;
|
||||||
|
default = name;
|
||||||
|
};
|
||||||
|
# TODO: reconsider using `page.outPath` and what to put into `locations`.
|
||||||
|
# maybe we can avoid having ".html" suffixes there.
|
||||||
|
# since templates can output multiple files, `html` is merely one of many things we *could* produce.
|
||||||
|
# TODO: make `apply` configurable so one can programmatically modify locations
|
||||||
|
locations = mkOption {
|
||||||
|
description = ''
|
||||||
|
List of historic output locations for the resulting file
|
||||||
|
|
||||||
|
The first element is the canonical location.
|
||||||
|
All other elements are used to create redirects to the canonical location.
|
||||||
|
'';
|
||||||
|
type = with types; nonEmptyListOf str;
|
||||||
|
};
|
||||||
|
link = mkOption {
|
||||||
|
description = "Helper function for transparent linking to other pages";
|
||||||
|
type = with types; functionTo str;
|
||||||
|
default = target: "TODO: compute the relative path based on `locations`";
|
||||||
|
};
|
||||||
|
# TODO: may not need it when using `link`; could repurpose it to render the default template
|
||||||
|
outPath = mkOption {
|
||||||
|
description = ''
|
||||||
|
Location of the page, used for transparently creating links
|
||||||
|
'';
|
||||||
|
type = types.str;
|
||||||
|
default = lib.head config.locations;
|
||||||
|
};
|
||||||
|
outputs = mkOption {
|
||||||
|
description = ''
|
||||||
|
Representations of the document in different formats
|
||||||
|
'';
|
||||||
|
type = with types; attrsOf str;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
page = { name, config, ... }: {
|
||||||
|
imports = [ cfg.content-types.document ];
|
||||||
|
options = {
|
||||||
|
title = mkOption {
|
||||||
|
description = "Page title";
|
||||||
|
type = types.str;
|
||||||
|
default = name;
|
||||||
|
};
|
||||||
|
description = mkOption {
|
||||||
|
description = ''
|
||||||
|
One-sentence description of page contents
|
||||||
|
'';
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
summary = mkOption {
|
||||||
|
description = ''
|
||||||
|
One-paragraph summary of page contents
|
||||||
|
'';
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
body = mkOption {
|
||||||
|
description = ''
|
||||||
|
Page contents in CommonMark
|
||||||
|
'';
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
config.outputs.html = cfg.templates.html.page cfg config;
|
||||||
|
};
|
||||||
|
|
||||||
|
article = { config, collection, ... }: {
|
||||||
|
imports = [ cfg.content-types.page ];
|
||||||
|
options = {
|
||||||
|
collection = mkOption {
|
||||||
|
description = "Collection this article belongs to";
|
||||||
|
type = options.collections.type.nestedTypes.elemType;
|
||||||
|
default = collection;
|
||||||
|
};
|
||||||
|
date = mkOption {
|
||||||
|
description = "Publication date";
|
||||||
|
type = with types; str;
|
||||||
|
default = null;
|
||||||
|
};
|
||||||
|
author = mkOption {
|
||||||
|
description = "Page author";
|
||||||
|
type = with types; either str (nonEmptyListOf str);
|
||||||
|
default = null;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
config.name = lib.slug config.title;
|
||||||
|
# TODO: this should be covered by the TBD `link` function instead,
|
||||||
|
# taking a historical list of collection names into account
|
||||||
|
config.outPath = "${collection.name}/${lib.head config.locations}";
|
||||||
|
config.outputs.html = lib.mkForce (cfg.templates.html.article cfg config);
|
||||||
|
};
|
||||||
|
|
||||||
|
named-link = { ... }: {
|
||||||
|
options = {
|
||||||
|
label = mkOption {
|
||||||
|
description = "Link label";
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
url = mkOption {
|
||||||
|
description = "Link URL";
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
navigation = { name, ... }: {
|
||||||
|
options = {
|
||||||
|
name = mkOption {
|
||||||
|
description = "Symbolic name, used as a human-readable identifier";
|
||||||
|
type = types.str;
|
||||||
|
default = name;
|
||||||
|
};
|
||||||
|
label = mkOption {
|
||||||
|
description = "Menu label";
|
||||||
|
type = types.str;
|
||||||
|
default = name;
|
||||||
|
};
|
||||||
|
items = mkOption {
|
||||||
|
description = "List of menu items";
|
||||||
|
type = with types; listOf (attrTag {
|
||||||
|
menu = mkOption { type = submodule cfg.content-types.navigation; };
|
||||||
|
page = mkOption { type = submodule cfg.content-types.page; };
|
||||||
|
link = mkOption { type = submodule cfg.content-types.named-link; };
|
||||||
|
});
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
|
@ -7,12 +7,7 @@ let
|
||||||
cfg = config;
|
cfg = config;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
imports = lib.nixFiles ./.;
|
imports = [ ./content-types.nix ];
|
||||||
|
|
||||||
options.content-types = mkOption {
|
|
||||||
description = "Content types";
|
|
||||||
type = with types; attrsOf deferredModule;
|
|
||||||
};
|
|
||||||
|
|
||||||
# TODO: enable i18n, e.g. via a nested attribute for language-specific content
|
# TODO: enable i18n, e.g. via a nested attribute for language-specific content
|
||||||
options.pages = mkOption {
|
options.pages = mkOption {
|
||||||
|
|
|
@ -1,51 +0,0 @@
|
||||||
{ lib, ... }:
|
|
||||||
let
|
|
||||||
inherit (lib)
|
|
||||||
mkOption
|
|
||||||
types
|
|
||||||
;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
content-types.document = { name, config, link, ... }: {
|
|
||||||
config._module.args.link = config.link;
|
|
||||||
options = {
|
|
||||||
name = mkOption {
|
|
||||||
description = "Symbolic name, used as a human-readable identifier";
|
|
||||||
type = types.str;
|
|
||||||
default = name;
|
|
||||||
};
|
|
||||||
# TODO: reconsider using `page.outPath` and what to put into `locations`.
|
|
||||||
# maybe we can avoid having ".html" suffixes there.
|
|
||||||
# since templates can output multiple files, `html` is merely one of many things we *could* produce.
|
|
||||||
# TODO: make `apply` configurable so one can programmatically modify locations
|
|
||||||
locations = mkOption {
|
|
||||||
description = ''
|
|
||||||
List of historic output locations for the resulting file
|
|
||||||
|
|
||||||
The first element is the canonical location.
|
|
||||||
All other elements are used to create redirects to the canonical location.
|
|
||||||
'';
|
|
||||||
type = with types; nonEmptyListOf str;
|
|
||||||
};
|
|
||||||
link = mkOption {
|
|
||||||
description = "Helper function for transparent linking to other pages";
|
|
||||||
type = with types; functionTo str;
|
|
||||||
default = target: with lib; relativePath (head config.locations) (head target.locations);
|
|
||||||
};
|
|
||||||
# TODO: may not need it when using `link`; could repurpose it to render the default template
|
|
||||||
outPath = mkOption {
|
|
||||||
description = ''
|
|
||||||
Location of the page, used for transparently creating links
|
|
||||||
'';
|
|
||||||
type = types.str;
|
|
||||||
default = lib.head config.locations;
|
|
||||||
};
|
|
||||||
outputs = mkOption {
|
|
||||||
description = ''
|
|
||||||
Representations of the document in different formats
|
|
||||||
'';
|
|
||||||
type = with types; attrsOf str;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
{ config, lib, ... }:
|
|
||||||
let
|
|
||||||
inherit (lib)
|
|
||||||
mkOption
|
|
||||||
types
|
|
||||||
;
|
|
||||||
cfg = config;
|
|
||||||
subtype = baseModule: types.submodule [
|
|
||||||
baseModule
|
|
||||||
{ _module.freeformType = types.attrs; }
|
|
||||||
];
|
|
||||||
in
|
|
||||||
{
|
|
||||||
content-types.named-link = { ... }: {
|
|
||||||
options = {
|
|
||||||
label = mkOption {
|
|
||||||
description = "Link label";
|
|
||||||
type = types.str;
|
|
||||||
};
|
|
||||||
url = mkOption {
|
|
||||||
description = "Link URL";
|
|
||||||
type = types.str;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
content-types.navigation = { name, ... }: {
|
|
||||||
options = {
|
|
||||||
name = mkOption {
|
|
||||||
description = "Symbolic name, used as a human-readable identifier";
|
|
||||||
type = types.str;
|
|
||||||
default = name;
|
|
||||||
};
|
|
||||||
label = mkOption {
|
|
||||||
description = "Menu label";
|
|
||||||
type = types.str;
|
|
||||||
default = name;
|
|
||||||
};
|
|
||||||
items = mkOption {
|
|
||||||
description = "List of menu items";
|
|
||||||
type = with types; listOf (attrTag {
|
|
||||||
menu = mkOption { type = submodule cfg.content-types.navigation; };
|
|
||||||
page = mkOption { type = subtype cfg.content-types.page; };
|
|
||||||
link = mkOption { type = submodule cfg.content-types.named-link; };
|
|
||||||
});
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
{ config, lib, ... }:
|
|
||||||
let
|
|
||||||
inherit (lib)
|
|
||||||
mkOption
|
|
||||||
types
|
|
||||||
;
|
|
||||||
cfg = config;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
content-types.page = { name, config, ... }: {
|
|
||||||
imports = [ cfg.content-types.document ];
|
|
||||||
options = {
|
|
||||||
title = mkOption {
|
|
||||||
description = "Page title";
|
|
||||||
type = types.str;
|
|
||||||
default = name;
|
|
||||||
};
|
|
||||||
description = mkOption {
|
|
||||||
description = ''
|
|
||||||
One-sentence description of page contents
|
|
||||||
'';
|
|
||||||
type = types.str;
|
|
||||||
};
|
|
||||||
summary = mkOption {
|
|
||||||
description = ''
|
|
||||||
One-paragraph summary of page contents
|
|
||||||
'';
|
|
||||||
type = types.str;
|
|
||||||
};
|
|
||||||
body = mkOption {
|
|
||||||
description = ''
|
|
||||||
Page contents in CommonMark
|
|
||||||
'';
|
|
||||||
type = types.str;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
config.outputs.html = cfg.templates.html.page cfg config;
|
|
||||||
};
|
|
||||||
}
|
|
Reference in a new issue