├── .gitignore
├── LICENSE
├── README.md
├── branding
├── nte-colors.svg
├── nte-monochrome.svg
└── powered-by-nte.png
├── changelog.md
├── engine.nix
├── example
├── base.css
├── default.nix
├── index.nix
├── posts
│ ├── index.css
│ ├── index.nix
│ ├── post.css
│ └── test.nix
└── templates
│ ├── base.nix
│ └── post.nix
├── flake.lock
├── flake.nix
├── nte-drv.nix
└── stdlib.nix
/.gitignore:
--------------------------------------------------------------------------------
1 | result
2 | */result
3 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2024-2025 poz
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
nte
5 |
6 |
7 | nix template engine - takes some templates, entries and applies the templates to the entries
8 |
9 | nte's main repository is on [my forgejo](https://git.poz.pet/poz/nte) instance
10 |
11 | mirrors are available on [github](https://github.com/imnotpoz/nte) and [codeberg](https://codeberg.org/poz/nte), I accept contributions from anywhere
12 |
13 | # sites written in nte
14 | https://poz.pet
15 |
16 | https://nixwebr.ing
17 |
18 | https://mewoocat.github.io/
19 |
20 | if your site (or anything else) is written in nte, let me know and I'll add you to this list
21 |
22 | you can also use this button on your site and link to one of the repos
23 |
24 | [](https://git.poz.pet/poz/nte)
25 |
26 | # examples
27 |
28 | check `example/` for a static website written in nte
29 |
30 | build and run it using
31 | ```sh
32 | nix shell nixpkgs#darkhttpd --command sh -c "nix build -L .#examples.x86_64-linux.default && darkhttpd ./result"
33 | ```
34 | the site will be available at http://localhost:8080
35 |
36 | the example is a cut down version of my own website
37 |
38 | # usage
39 |
40 | first add nte as an input in your project's flake
41 |
42 | ```nix
43 | nte = {
44 | url = "git+https://git.poz.pet/poz/nte";
45 | # or one of the mirrors
46 | #url = "git+https://codeberg.org/poz/nte";
47 | #url = "github:imnotpoz/nte";
48 | inputs.nixpkgs.follows = "nixpkgs";
49 | };
50 | ```
51 |
52 | then use the `mkNteDerivation` wrapper over `stdenv.mkDerivation` available under
53 | ```nix
54 | inputs.nte.functions.${system}.mkNteDerivation
55 | ```
56 | it accepts an attrset of:
57 | - `name`, `version`, `src` - passthrough to `stdenv.mkDerivation`
58 | - `extraArgs` - an attrset of additional arguments passed to all entries and templates
59 | - `entries` - a list of all entry files to be processed
60 | - `templates` - a list of all template files to be applied
61 | - `extraFiles` - a list of either:
62 | - a string containing the path to the source file - will be copied to `$out` in the `installPhase`
63 | - attrset of `source` and `destination`:
64 | - `source` - a string containing a path, if relative `$PWD` is `$src` in the `installPhase`
65 | - `destination` - a string containing a path, never absolute, appended to `$out` in the `installPhase`
66 | - `preBuild` - passthrough to `stdenv.mkDerivation`
67 | - `postBuild` - passthrough to `stdenv.mkDerivation`
68 | - `preInstall` - passthrough to `stdenv.mkDerivation`
69 | - `postInstall` - passthrough to `stdenv.mkDerivation`
70 |
71 | make sure not to use nix paths in `extraFiles` if you want the names of the files to match up
72 |
73 | example usage of the wrapper function:
74 | ```nix
75 | mkNteDerivation {
76 | name = "nte-example";
77 | version = "0.1";
78 | src = ./.;
79 |
80 | extraArgs = {
81 | foo = 2137;
82 | bar = "dupa";
83 | baz = arg1: arg2: ''
84 | here's arg1: ${arg1}
85 | and here's arg2: ${arg2}
86 | '';
87 | };
88 |
89 | entries = [
90 | ./entry1.nix
91 | ./foo/entry2.nix
92 | ./foo/entry3.nix
93 | ./bar/entry4.nix
94 | ./bar/entry5.nix
95 | ./bar/entry6.nix
96 | ];
97 |
98 | templates = [
99 | ./template1.nix
100 | ./template2.nix
101 | ];
102 |
103 | extraFiles = [
104 | "./data.txt" # equivalent to { source = ./data.txt; destination = "/"; }
105 | { source = "./image.png"; destination = "/assets/"; }
106 | { source = "./image2.png"; destination = "/assets/dupa.png"; }
107 | { source = "./data/*"; destination = "/assets/data/"; }
108 | { source = fetchurl { ... }; destination = "/"; }
109 | ];
110 | }
111 | ```
112 |
113 | nte will handle creating directories if your source file structure isn't flat
114 |
115 | if the `mkNteDerivation` wrapper isn't enough for you, you can do things the old way - by putting the output of `inputs.nte.functions.${system}.engine` in a derivation's `buildPhase`:
116 | ```nix
117 | mkDerivation {
118 | # ...
119 |
120 | buildPhase = ''
121 | runHook preBuild
122 |
123 | ${engine {inherit extraArgs entries templates;}}
124 |
125 | runHook postBuild
126 | '';
127 | }
128 | ```
129 |
130 | in that case if you wish to replicate the functionality of `extraFiles` you can use the derivation's `installPhase`, manually `mkdir` the needed directories and `cp` your files into `$out`
131 |
132 | nte offers a standard library that contains:
133 | - `nixpkgs`
134 | - `getEntry` - a function that gives you access to the entry's attributes
135 | - `applyTemplate` - a function that allows you to manually apply a template to an entry
136 | - utility functions found in [stdlib.nix](./stdlib.nix)
137 |
138 | ## templates
139 |
140 | a template can take an arbitrary number of arguments and returns `{ name, format, output }`:
141 |
142 | - `name` - used as a template ID for the entries
143 | - `format` - the extension of the output file (ignored if an entry defines `file`)
144 | - `output` - string if in a base template, entry to another template otherwise
145 |
146 | example template:
147 | ```nix
148 | {
149 | name,
150 | location,
151 | info,
152 | ...
153 | }: {
154 | name = "greeting";
155 | format = "txt";
156 |
157 | output = ''
158 | Hello ${name}! Welcome to ${location}.
159 |
160 | Here's some more information:
161 | ${info}
162 | '';
163 | }
164 | ```
165 |
166 | a template's output can also be an entry to another template:
167 | ```nix
168 | {
169 | name,
170 | location,
171 | date,
172 | time,
173 | ...
174 | }: {
175 | name = "greeting-with-date";
176 | output = {
177 | template = "greeting";
178 |
179 | inherit name location;
180 |
181 | info = ''
182 | You're visiting ${location} on ${date} at ${time}!
183 | '';
184 | };
185 | }
186 | ```
187 | a template that's inherited from a different template also inherits its format - no need to define it again
188 |
189 | ## entries
190 |
191 | an entry can take an arbitrary number of arguments and returns `{ template, ... }`, the `...` being the desired template's arguments (sans `extraArgs`, those are passed either way)
192 |
193 | there's a built-in `passthrough` template, which (as the name might suggest) takes in a `format` and `output` and passes them through to the template with no changes
194 |
195 | this is useful if you're using nte to create a single file - you won't have to create a boilerplate template
196 |
197 | example entries (using the previous example templates):
198 | ```nix
199 | _: {
200 | template = "greeting";
201 |
202 | name = "Jacek";
203 | location = "Wrocław";
204 | info = ''
205 | As of 2023, the official population of Wrocław is 674132 making it the third largest city in Poland.
206 | '';
207 | }
208 | ```
209 | an entry using the stdlib:
210 | ```nix
211 | {
212 | run,
213 | ...
214 | }: {
215 | template = "greeting-with-date";
216 |
217 | name = "Rafał";
218 | location = "Osieck";
219 | date = run "date +%F";
220 | time = run "date +%T";
221 | }
222 | ```
223 | if a binary isn't in `$PATH`, remember that each entry gets `pkgs`:
224 | ```nix
225 | {
226 | pkgs,
227 | run,
228 | ...
229 | }: let
230 | date = "${pkgs.coreutils-full}/bin/date";
231 | in {
232 | # ...
233 | date = run "${date} +%F";
234 | time = run "${date} +%T";
235 | }
236 | ```
237 |
238 | nte by default will follow your source file structure, if you want to specify the output location yourself use `file`:
239 | ```nix
240 | _: {
241 | # ...
242 | file = "foo/bar.txt";
243 | }
244 | ```
245 | in this example the output of this entry will end up at `$out/foo/bar.txt` instead of the default location - a base template's `format` will also be ignored
246 |
247 | # thanks
248 |
249 | [raf](https://notashelf.dev/) for helping me out with some of the nix and setting up mirrors
250 |
251 | # license
252 | MIT
253 |
--------------------------------------------------------------------------------
/branding/nte-colors.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
314 |
--------------------------------------------------------------------------------
/branding/nte-monochrome.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
306 |
--------------------------------------------------------------------------------
/branding/powered-by-nte.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imnotpoz/nte/986ab4440f3c55ba3f4e673932b39d9b5a06864d/branding/powered-by-nte.png
--------------------------------------------------------------------------------
/changelog.md:
--------------------------------------------------------------------------------
1 | # 0.1.0
2 | - made this
3 |
4 | # 0.2.0
5 | - made `file` in entries optional
6 |
7 | # 0.3.0
8 | - set nixpkgs input as unstable by default
9 | - added `github:nix-systems/default` as an input
10 | - moved the following outputs:
11 | - `engines.${system}.default` -> `functions.${system}.engine`
12 | - introduced `mkNteDerivation` helper function (`functions.${system}.mkNteDerivation`)
13 |
14 | # 0.3.1
15 | - improved engine code readability a bit
16 | - exposed `applyTemplate` so that users can do templating inside a single file
17 |
18 | # 0.3.2
19 | - allowed the use of raw paths alongside attrsets in `extraFiles`
20 |
21 | # 0.3.3
22 | - made `applyTemplate` work on a raw entry and return a string
23 | and added an internal `applyTemplateFile` that works as the former did before
24 | - added built-in `passthrough` template
25 |
26 | # 0.3.4
27 | - fixed an issue where `file` wasn't available to the base template if another one inherited from it
28 |
29 | # 0.3.5
30 | - added `{pre,post}{Build,Install}` as passthrough parameters to `mkNteDerivation`
31 |
32 | # 0.3.6
33 | - fixed the above mentioned hooks not having any effect
34 |
35 | # 0.3.7
36 | - added passthrough `meta` parameter to `mkNteDerivation`
37 |
--------------------------------------------------------------------------------
/engine.nix:
--------------------------------------------------------------------------------
1 | pkgs: src: {extraArgs, entries, templates}: let
2 | inherit (pkgs) lib;
3 |
4 | inherit (builtins) abort baseNameOf dirOf toString;
5 | inherit (lib.attrsets) hasAttr;
6 | inherit (lib.lists) forEach findFirst;
7 | inherit (lib.path) removePrefix;
8 | inherit (lib.strings) concatMapStrings concatStrings hasSuffix isString removeSuffix;
9 | inherit (lib.trivial) functionArgs;
10 |
11 | inherit (pkgs) writeText;
12 |
13 | templates' = templates ++ [
14 | (pkgs.writeText "passthrough.nix" /*nix*/''
15 | { format, output, ... }: {
16 | name = "passthrough";
17 | inherit format output;
18 | }
19 | '')
20 | ];
21 |
22 | args = {inherit pkgs getEntry applyTemplate;}
23 | // (import ./stdlib.nix pkgs)
24 | // extraArgs;
25 |
26 | isBaseTemplate = template:
27 | isString template.output;
28 |
29 | findTemplateFn = entry: let
30 | template = findFirst (templateFile: let
31 | templateFn = import templateFile;
32 | template' = templateFn (functionArgs templateFn);
33 | in
34 | template'.name == entry.template)
35 | null
36 | templates';
37 | in
38 | if template == null then
39 | abort "unknown template `${entry.template}`"
40 | else
41 | (import template);
42 |
43 | applyTemplate = templateFn: entry: let
44 | template = templateFn (args // entry);
45 | in
46 | if isBaseTemplate template then
47 | template.output
48 | else let
49 | newEntry = template.output // {inherit (entry) file;};
50 | foundTemplateFn = findTemplateFn newEntry;
51 | in
52 | applyTemplate foundTemplateFn newEntry;
53 |
54 | applyTemplateFn = templateFn: entry: {
55 | inherit (entry) file;
56 | output = applyTemplate templateFn entry;
57 | };
58 |
59 | replaceSuffix = from: to: string:
60 | if !(hasSuffix from string) then
61 | abort "invalid suffix `${from}` for string `${string}`"
62 | else
63 | concatStrings [ (removeSuffix from string) to ];
64 |
65 | getTemplateFormat = entry: templateFn: let
66 | # getEntry needs to go down to the base template for the format
67 | # but any template through the way can ask for a file
68 | # so we just give it a placeholder - an empty string here
69 | # since the output of this thing doesn't matter
70 | template = templateFn (args // entry // { file = ""; });
71 | in
72 | if isBaseTemplate template then
73 | template.format
74 | else let
75 | newEntry = template.output;
76 | foundTemplateFn = findTemplateFn newEntry;
77 | in
78 | getTemplateFormat newEntry foundTemplateFn;
79 |
80 | getEntry = entryFile: let
81 | sourceFile = toString (removePrefix src entryFile);
82 | entry = (import entryFile) args;
83 | foundTemplateFn = findTemplateFn entry;
84 | entryFormat = getTemplateFormat entry foundTemplateFn;
85 | in
86 | if !(hasAttr "file" entry) then
87 | entry // {
88 | file = replaceSuffix ".nix" ".${entryFormat}" sourceFile;
89 | }
90 | else
91 | entry;
92 |
93 | processEntryFile = entryFile: let
94 | foundTemplateFn = findTemplateFn entry;
95 | entry = getEntry entryFile;
96 | in
97 | applyTemplateFn foundTemplateFn entry;
98 |
99 | in /*sh*/''
100 | ${concatMapStrings
101 | (result: /*sh*/''
102 | mkdir -p $out/${dirOf result.file}
103 | cat ${writeText (baseNameOf result.file) result.output} > $out/${result.file}
104 | '')
105 | (forEach entries processEntryFile)
106 | }
107 | ''
108 |
--------------------------------------------------------------------------------
/example/base.css:
--------------------------------------------------------------------------------
1 | * {
2 | margin: 0;
3 | padding: 0;
4 | }
5 |
6 | body {
7 | background-color: #EEEEEE;
8 | color: #222222;
9 | font-size: 1.15rem;
10 | display: flex;
11 | flex-direction: column;
12 | align-items: center;
13 | margin: 0;
14 | height: 100%;
15 | }
16 |
17 | hr {
18 | margin: 1em auto 1em;
19 | width: 90%;
20 | }
21 |
22 | #navigation {
23 | padding-top: 1em;
24 | display: flex;
25 | flex-direction: row;
26 | font-size: 1.5rem;
27 | }
28 |
29 | #navigation:has(hr) hr {
30 | margin: auto 1em auto;
31 | height: 75%;
32 | width: auto;
33 | }
34 |
--------------------------------------------------------------------------------
/example/default.nix:
--------------------------------------------------------------------------------
1 | {
2 | lib,
3 | mkNteDerivation,
4 | ...
5 | }: let
6 | inherit (lib.attrsets) listToAttrs;
7 | inherit (lib.lists) map;
8 | inherit (lib.strings) replaceStrings toLower;
9 |
10 | extraArgs' = {
11 | h = n: content: let
12 | id = replaceStrings [" " ";"] ["-" "-"] (toLower content);
13 | in /*html*/''
14 | # ${content}
15 | '';
16 | };
17 | in mkNteDerivation {
18 | name = "nte-example";
19 | version = "0.1.0";
20 | src = ./.;
21 |
22 | extraArgs = extraArgs'
23 | // listToAttrs (map (n: {
24 | name = "h${toString n}";
25 | value = text: extraArgs'.h n text;
26 | }) [ 1 2 3 4 5 6 ]
27 | );
28 |
29 | entries = [
30 | ./index.nix
31 | ./posts/index.nix
32 | ./posts/test.nix
33 | ];
34 |
35 | templates = [
36 | ./templates/base.nix
37 | ./templates/post.nix
38 | ];
39 |
40 | extraFiles = [
41 | { source = "./*.css"; destination = "/"; }
42 | { source = "./posts/*.css"; destination = "/posts"; }
43 | ];
44 | }
45 |
--------------------------------------------------------------------------------
/example/index.nix:
--------------------------------------------------------------------------------
1 | _: {
2 | template = "base";
3 |
4 | head = /*html*/''
5 | nte example
6 | '';
7 |
8 | body = /*html*/''
9 |
12 | This is a test post on an example nte site. Feel free to read the lorem ipsum below. Its heading was generated using an additional helper function added to extraArgs
13 |
14 | ${h2 "Lorem ipsum"}
15 |
16 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas a lobortis augue. Suspendisse finibus turpis massa, eget aliquam justo ultricies eu. Aliquam augue elit, vulputate ut elit a, porta imperdiet nisi. Sed suscipit sed eros a maximus. Nullam congue sit amet metus non porta. Nunc iaculis euismod orci, sed cursus eros pellentesque sed. Sed lacinia purus nec magna mattis ullamcorper. In aliquet neque sed est pharetra posuere. Donec mauris turpis, tempus at tempor eget, pellentesque nec nibh. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Sed volutpat orci eget justo auctor bibendum. Sed elementum odio et odio vulputate congue at eu tortor.
17 |
18 |
19 |
20 | Morbi eget volutpat justo. Maecenas erat ante, interdum non odio nec, sagittis tristique orci. Ut pulvinar sem vitae odio dictum facilisis. Vestibulum sit amet tristique eros. Phasellus ac tortor arcu. In hac habitasse platea dictumst. Fusce laoreet fringilla facilisis. Vestibulum feugiat luctus elit sit amet rhoncus. Vivamus sed orci quis elit pharetra pretium eu a libero. In sed pretium ante, eget dictum augue.
21 |
22 |
23 |
24 | Maecenas odio ligula, vehicula nec eros id, rhoncus auctor ante. Sed sodales est eu sapien sollicitudin placerat. Maecenas consequat est ac condimentum faucibus. Aenean vel augue sed nunc dapibus sollicitudin. Phasellus sed nisi vel nulla aliquet pellentesque. Etiam euismod euismod felis a cursus. Morbi consequat aliquet ex, nec auctor odio varius non. Etiam finibus sem at orci semper, in volutpat odio sagittis. Etiam id mauris sit amet orci sodales tristique eget ut mauris.
25 |
26 |
27 |
28 | Vestibulum magna sem, tempor et ultricies vitae, cursus ut nunc. Ut eleifend dignissim augue, eu feugiat urna maximus nec. Praesent eget pulvinar velit, a condimentum purus. Nunc dapibus, ligula sed efficitur mattis, velit justo pharetra ipsum, eu malesuada erat erat sed orci. Integer orci orci, ultrices in ipsum et, interdum vestibulum dolor. Mauris varius tellus eu erat lobortis, et viverra ante volutpat. Nam nunc est, posuere eget bibendum tincidunt, posuere et sem. Integer egestas aliquam nibh eu viverra.
29 |
30 |
31 |
32 | Morbi in dignissim erat. Aenean non diam at libero eleifend gravida nec et lorem. Ut nibh sapien, blandit sit amet dui id, tincidunt tempor velit. Aliquam cursus libero lacus, eget pretium ligula scelerisque eget. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Integer tristique metus massa, nec auctor purus imperdiet a. Proin eu turpis quis augue mattis tempus at at dolor. Vestibulum eu justo tincidunt nibh eleifend sollicitudin at ac massa. Praesent sit amet ultricies lorem. Sed eleifend laoreet elit, ac auctor diam finibus vel. Nam iaculis felis neque, eu hendrerit ligula pellentesque in. Sed gravida lacus tempus dictum scelerisque. Ut commodo nec tellus sit amet consequat.
33 |
34 |
35 |
36 | Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Cras eleifend sit amet arcu id euismod. Suspendisse vel bibendum diam, ut posuere nunc. Cras eget purus sit amet ex consequat laoreet quis eget sapien. Pellentesque vel lacus mollis, fringilla lectus tristique, condimentum justo. Donec orci augue, efficitur eget dolor vitae, venenatis malesuada orci. Interdum et malesuada fames ac ante ipsum primis in faucibus. Nunc eros enim, viverra sed semper ac, aliquam et odio. Curabitur a venenatis est. Praesent lacinia non enim quis gravida.
37 |
38 |
39 |
40 | Nunc porta risus a nisl commodo, in tempor est tempor. Quisque facilisis varius nunc, ac ullamcorper magna pharetra et. Aenean lectus nulla, blandit quis mi ac, bibendum convallis nunc. Proin scelerisque porttitor turpis, sed pharetra velit ullamcorper quis. Vivamus hendrerit diam ac neque commodo ornare. Curabitur hendrerit a nulla ut auctor. Nulla sed laoreet lorem. Morbi ullamcorper ipsum mollis, lobortis lectus ut, tempus lacus. Proin lobortis dui sapien, eu facilisis dui molestie at. Fusce ut suscipit nisl, nec mollis ex. Curabitur ut cursus mauris.
41 |
42 |
43 |
44 | Nulla vulputate eget sapien eget congue. Proin et gravida urna. In hendrerit posuere dolor, eget efficitur ex aliquam a. Sed tristique lacus sit amet pulvinar dignissim. Quisque volutpat mi ac posuere feugiat. Etiam elementum molestie tortor ut rhoncus. Integer at orci enim. Pellentesque malesuada, neque faucibus aliquet vehicula, ligula neque maximus mauris, non scelerisque nisl libero sit amet nibh. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
45 |
46 |
47 |
48 | Vivamus at mattis augue, non ullamcorper diam. Ut euismod sed erat ut efficitur. Sed mollis dui sit amet ultricies rutrum. Cras sit amet justo nisl. Aliquam at orci velit. Vestibulum vel dolor ut lectus fermentum scelerisque ut ullamcorper risus. Sed diam nibh, tempor id arcu at, eleifend placerat turpis.
49 |
50 |
51 |
52 | Morbi ut bibendum mi, id finibus odio. Suspendisse commodo leo a nisi porta, sed imperdiet turpis posuere. Mauris interdum neque sit amet metus tristique semper vitae tincidunt ipsum. Phasellus bibendum nulla venenatis nisi aliquam condimentum ac eget metus. Quisque dignissim viverra interdum. Mauris non semper magna. Nam ultricies dui at lacinia fermentum. Quisque sed diam vestibulum, mollis libero eu, sollicitudin elit. Nam quis hendrerit urna, a interdum tortor. Mauris ac augue non neque malesuada eleifend. Etiam accumsan ligula hendrerit, accumsan ante id, faucibus mi.
53 |