1 { config, lib, pkgs, ... }:
7 cfg = config.services.gitit;
9 homeDir = "/var/lib/gitit";
11 toYesNo = b: if b then "yes" else "no";
13 gititShared = with cfg.haskellPackages; gitit + "/share/" + ghc.targetPrefix + ghc.haskellCompilerName + "/" + gitit.pname + "-" + gitit.version;
15 gititWithPkgs = hsPkgs: extras: hsPkgs.ghcWithPackages (self: with self; [ gitit ] ++ (extras self));
17 gititSh = hsPkgs: extras: with pkgs; let
18 env = gititWithPkgs hsPkgs extras;
19 in writeScript "gitit" ''
22 export NIX_GHC="${env}/bin/ghc"
23 export NIX_GHCPKG="${env}/bin/ghc-pkg"
24 export NIX_GHC_DOCDIR="${env}/share/doc/ghc/html"
25 export NIX_GHC_LIBDIR=$( $NIX_GHC --print-libdir )
26 ${env}/bin/gitit -f ${configFile}
34 description = lib.mdDoc "Enable the gitit service.";
37 haskellPackages = mkOption {
38 default = pkgs.haskellPackages;
39 defaultText = literalExpression "pkgs.haskellPackages";
40 example = literalExpression "pkgs.haskell.packages.ghc784";
41 description = lib.mdDoc "haskellPackages used to build gitit and plugins.";
44 extraPackages = mkOption {
45 type = types.functionTo (types.listOf types.package);
47 example = literalExpression ''
52 description = lib.mdDoc ''
53 Extra packages available to ghc when running gitit. The
54 value must be a function which receives the attrset defined
55 in {var}`haskellPackages` as the sole argument.
62 description = lib.mdDoc "IP address on which the web server will listen.";
68 description = lib.mdDoc "Port on which the web server will run.";
71 wikiTitle = mkOption {
74 description = lib.mdDoc "The wiki title.";
77 repositoryType = mkOption {
78 type = types.enum ["git" "darcs" "mercurial"];
80 description = lib.mdDoc "Specifies the type of repository used for wiki content.";
83 repositoryPath = mkOption {
85 default = homeDir + "/wiki";
86 description = lib.mdDoc ''
87 Specifies the path of the repository directory. If it does not
88 exist, gitit will create it on startup.
92 requireAuthentication = mkOption {
93 type = types.enum [ "none" "modify" "read" ];
95 description = lib.mdDoc ''
96 If 'none', login is never required, and pages can be edited
97 anonymously. If 'modify', login is required to modify the wiki
98 (edit, add, delete pages, upload files). If 'read', login is
99 required to see any wiki pages.
103 authenticationMethod = mkOption {
104 type = types.enum [ "form" "http" "generic" "github" ];
106 description = lib.mdDoc ''
107 'form' means that users will be logged in and registered using forms
108 in the gitit web interface. 'http' means that gitit will assume that
109 HTTP authentication is in place and take the logged in username from
110 the "Authorization" field of the HTTP request header (in addition,
111 the login/logout and registration links will be suppressed).
112 'generic' means that gitit will assume that some form of
113 authentication is in place that directly sets REMOTE_USER to the name
114 of the authenticated user (e.g. mod_auth_cas on apache). 'rpx' means
115 that gitit will attempt to log in through https://rpxnow.com. This
116 requires that 'rpx-domain', 'rpx-key', and 'base-url' be set below,
117 and that 'curl' be in the system path.
121 userFile = mkOption {
123 default = homeDir + "/gitit-users";
124 description = lib.mdDoc ''
125 Specifies the path of the file containing user login information. If
126 it does not exist, gitit will create it (with an empty user list).
127 This file is not used if 'http' is selected for
128 authentication-method.
132 sessionTimeout = mkOption {
135 description = lib.mdDoc ''
136 Number of minutes of inactivity before a session expires.
140 staticDir = mkOption {
142 default = gititShared + "/data/static";
143 description = lib.mdDoc ''
144 Specifies the path of the static directory (containing javascript,
145 css, and images). If it does not exist, gitit will create it and
146 populate it with required scripts, stylesheets, and images.
150 defaultPageType = mkOption {
151 type = types.enum [ "markdown" "rst" "latex" "html" "markdown+lhs" "rst+lhs" "latex+lhs" ];
152 default = "markdown";
153 description = lib.mdDoc ''
154 Specifies the type of markup used to interpret pages in the wiki.
155 Possible values are markdown, rst, latex, html, markdown+lhs,
156 rst+lhs, and latex+lhs. (the +lhs variants treat the input as
157 literate Haskell. See pandoc's documentation for more details.) If
158 Markdown is selected, pandoc's syntax extensions (for footnotes,
159 delimited code blocks, etc.) will be enabled. Note that pandoc's
160 restructuredtext parser is not complete, so some pages may not be
161 rendered correctly if rst is selected. The same goes for latex and
167 type = types.enum [ "mathml" "raw" "mathjax" "jsmath" "google" ];
169 description = lib.mdDoc ''
170 Specifies how LaTeX math is to be displayed. Possible values are
171 mathml, raw, mathjax, jsmath, and google. If mathml is selected,
172 gitit will convert LaTeX math to MathML and link in a script,
173 MathMLinHTML.js, that allows the MathML to be seen in Gecko browsers,
174 IE + mathplayer, and Opera. In other browsers you may get a jumble of
175 characters. If raw is selected, the LaTeX math will be displayed as
176 raw LaTeX math. If mathjax is selected, gitit will link to the
177 remote mathjax script. If jsMath is selected, gitit will link to the
178 script /js/jsMath/easy/load.js, and will assume that jsMath has been
179 installed into the js/jsMath directory. This is the most portable
180 solution. If google is selected, the google chart API is called to
181 render the formula as an image. This requires a connection to google,
182 and might raise a technical or a privacy problem.
186 mathJaxScript = mkOption {
188 default = "https://d3eoax9i5htok0.cloudfront.net/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML";
189 description = lib.mdDoc ''
190 Specifies the path to MathJax rendering script. You might want to
191 use your own MathJax script to render formulas without Internet
192 connection or if you want to use some special LaTeX packages. Note:
193 path specified there cannot be an absolute path to a script on your
194 hdd, instead you should run your (local if you wish) HTTP server
195 which will serve the MathJax.js script. You can easily (in four lines
196 of code) serve MathJax.js using
197 http://happstack.com/docs/crashcourse/FileServing.html Do not forget
198 the "http://" prefix (e.g. http://localhost:1234/MathJax.js).
202 showLhsBirdTracks = mkOption {
205 description = lib.mdDoc ''
206 Specifies whether to show Haskell code blocks in "bird style", with
207 "> " at the beginning of each line.
211 templatesDir = mkOption {
213 default = gititShared + "/data/templates";
214 description = lib.mdDoc ''
215 Specifies the path of the directory containing page templates. If it
216 does not exist, gitit will create it with default templates. Users
217 may wish to edit the templates to customize the appearance of their
218 wiki. The template files are HStringTemplate templates. Variables to
219 be interpolated appear between $\'s. Literal $\'s must be
226 default = homeDir + "/gitit.log";
227 description = lib.mdDoc ''
228 Specifies the path of gitit's log file. If it does not exist, gitit
229 will create it. The log is in Apache combined log format.
233 logLevel = mkOption {
234 type = types.enum [ "DEBUG" "INFO" "NOTICE" "WARNING" "ERROR" "CRITICAL" "ALERT" "EMERGENCY" ];
236 description = lib.mdDoc ''
237 Determines how much information is logged. Possible values (from
238 most to least verbose) are DEBUG, INFO, NOTICE, WARNING, ERROR,
239 CRITICAL, ALERT, EMERGENCY.
243 frontPage = mkOption {
245 default = "Front Page";
246 description = lib.mdDoc ''
247 Specifies which wiki page is to be used as the wiki's front page.
248 Gitit creates a default front page on startup, if one does not exist
253 noDelete = mkOption {
255 default = "Front Page, Help";
256 description = lib.mdDoc ''
257 Specifies pages that cannot be deleted through the web interface.
258 (They can still be deleted directly using git or darcs.) A
259 comma-separated list of page names. Leave blank to allow every page
267 description = lib.mdDoc ''
268 Specifies pages that cannot be edited through the web interface.
269 Leave blank to allow every page to be edited.
273 defaultSummary = mkOption {
276 description = lib.mdDoc ''
277 Specifies text to be used in the change description if the author
278 leaves the "description" field blank. If default-summary is blank
279 (the default), the author will be required to fill in the description
284 tableOfContents = mkOption {
287 description = lib.mdDoc ''
288 Specifies whether to print a tables of contents (with links to
289 sections) on each wiki page.
294 type = with types; listOf str;
295 default = [ (gititShared + "/plugins/Dot.hs") ];
296 description = lib.mdDoc ''
297 Specifies a list of plugins to load. Plugins may be specified either
298 by their path or by their module name. If the plugin name starts
299 with Gitit.Plugin., gitit will assume that the plugin is an installed
300 module and will not try to find a source file.
304 useCache = mkOption {
307 description = lib.mdDoc ''
308 Specifies whether to cache rendered pages. Note that if use-feed is
309 selected, feeds will be cached regardless of the value of use-cache.
313 cacheDir = mkOption {
315 default = homeDir + "/cache";
316 description = lib.mdDoc "Path where rendered pages will be cached.";
319 maxUploadSize = mkOption {
322 description = lib.mdDoc ''
323 Specifies an upper limit on the size (in bytes) of files uploaded
324 through the wiki's web interface. To disable uploads, set this to
325 0K. This will result in the uploads link disappearing and the
326 _upload url becoming inactive.
330 maxPageSize = mkOption {
333 description = lib.mdDoc "Specifies an upper limit on the size (in bytes) of pages.";
336 debugMode = mkOption {
339 description = lib.mdDoc "Causes debug information to be logged while gitit is running.";
342 compressResponses = mkOption {
345 description = lib.mdDoc "Specifies whether HTTP responses should be compressed.";
348 mimeTypesFile = mkOption {
350 default = "/etc/mime/types.info";
351 description = lib.mdDoc ''
352 Specifies the path of a file containing mime type mappings. Each
353 line of the file should contain two fields, separated by whitespace.
354 The first field is the mime type, the second is a file extension.
359 If the file is not found, some simple defaults will be used.
363 useReCaptcha = mkOption {
366 description = lib.mdDoc ''
367 If true, causes gitit to use the reCAPTCHA service
368 (http://recaptcha.net) to prevent bots from creating accounts.
372 reCaptchaPrivateKey = mkOption {
373 type = with types; nullOr str;
375 description = lib.mdDoc ''
376 Specifies the private key for the reCAPTCHA service. To get
377 these, you need to create an account at http://recaptcha.net.
381 reCaptchaPublicKey = mkOption {
382 type = with types; nullOr str;
384 description = lib.mdDoc ''
385 Specifies the public key for the reCAPTCHA service. To get
386 these, you need to create an account at http://recaptcha.net.
390 accessQuestion = mkOption {
392 default = "What is the code given to you by Ms. X?";
393 description = lib.mdDoc ''
394 Specifies a question that users must answer when they attempt to
399 accessQuestionAnswers = mkOption {
401 default = "RED DOG, red dog";
402 description = lib.mdDoc ''
403 Specifies a question that users must answer when they attempt to
404 create an account, along with a comma-separated list of acceptable
405 answers. This can be used to institute a rudimentary password for
406 signing up as a user on the wiki, or as an alternative to reCAPTCHA.
408 access-question: What is the code given to you by Ms. X?
409 access-question-answers: RED DOG, red dog
413 rpxDomain = mkOption {
414 type = with types; nullOr str;
416 description = lib.mdDoc ''
417 Specifies the domain and key of your RPX account. The domain is just
418 the prefix of the complete RPX domain, so if your full domain is
419 'https://foo.rpxnow.com/', use 'foo' as the value of rpx-domain.
424 type = with types; nullOr str;
426 description = lib.mdDoc "RPX account access key.";
429 mailCommand = mkOption {
431 default = "sendmail %s";
432 description = lib.mdDoc ''
433 Specifies the command to use to send notification emails. '%s' will
434 be replaced by the destination email address. The body of the
435 message will be read from stdin. If this field is left blank,
436 password reset will not be offered.
440 resetPasswordMessage = mkOption {
443 > From: gitit@$hostname$
445 > Subject: Wiki password reset
449 > To reset your password, please follow the link below:
450 > http://$hostname$:$port$$resetlink$
454 description = lib.mdDoc ''
455 Gives the text of the message that will be sent to the user should
456 she want to reset her password, or change other registration info.
457 The lines must be indented, and must begin with '>'. The initial
458 spaces and '> ' will be stripped off. $username$ will be replaced by
459 the user's username, $useremail$ by her email address, $hostname$ by
460 the hostname on which the wiki is running (as returned by the
461 hostname system call), $port$ by the port on which the wiki is
462 running, and $resetlink$ by the relative path of a reset link derived
463 from the user's existing hashed password. If your gitit wiki is being
464 proxied to a location other than the root path of $port$, you should
465 change the link to reflect this: for example, to
466 http://$hostname$/path/to/wiki$resetlink$ or
467 http://gitit.$hostname$$resetlink$
474 description = lib.mdDoc ''
475 Specifies whether an ATOM feed should be enabled (for the site and
476 for individual pages).
481 type = with types; nullOr str;
483 description = lib.mdDoc ''
484 The base URL of the wiki, to be used in constructing feed IDs and RPX
485 token_urls. Set this if useFeed is false or authentication-method
490 absoluteUrls = mkOption {
493 description = lib.mdDoc ''
494 Make wikilinks absolute with respect to the base-url. So, for
495 example, in a wiki served at the base URL '/wiki', on a page
496 Sub/Page, the wikilink `[Cactus]()` will produce a link to
497 '/wiki/Cactus' if absoluteUrls is true, and a relative link to
498 'Cactus' (referring to '/wiki/Sub/Cactus') if absolute-urls is 'no'.
502 feedDays = mkOption {
505 description = lib.mdDoc "Number of days to be included in feeds.";
508 feedRefreshTime = mkOption {
511 description = lib.mdDoc "Number of minutes to cache feeds before refreshing.";
514 pdfExport = mkOption {
517 description = lib.mdDoc ''
518 If true, PDF will appear in export options. PDF will be created using
519 pdflatex, which must be installed and in the path. Note that PDF
520 exports create significant additional server load.
524 pandocUserData = mkOption {
525 type = with types; nullOr path;
527 description = lib.mdDoc ''
528 If a directory is specified, this will be searched for pandoc
529 customizations. These can include a templates/ directory for custom
530 templates for various export formats, an S5 directory for custom S5
531 styles, and a reference.odt for ODT exports. If no directory is
532 specified, $HOME/.pandoc will be searched. See pandoc's README for
537 xssSanitize = mkOption {
540 description = lib.mdDoc ''
541 If true, all HTML (including that produced by pandoc) is filtered
542 through xss-sanitize. Set to no only if you trust all of your users.
546 oauthClientId = mkOption {
547 type = with types; nullOr str;
549 description = lib.mdDoc "OAuth client ID";
552 oauthClientSecret = mkOption {
553 type = with types; nullOr str;
555 description = lib.mdDoc "OAuth client secret";
558 oauthCallback = mkOption {
559 type = with types; nullOr str;
561 description = lib.mdDoc "OAuth callback URL";
564 oauthAuthorizeEndpoint = mkOption {
565 type = with types; nullOr str;
567 description = lib.mdDoc "OAuth authorize endpoint";
570 oauthAccessTokenEndpoint = mkOption {
571 type = with types; nullOr str;
573 description = lib.mdDoc "OAuth access token endpoint";
576 githubOrg = mkOption {
577 type = with types; nullOr str;
579 description = lib.mdDoc "Github organization";
583 configFile = pkgs.writeText "gitit.conf" ''
584 address: ${cfg.address}
585 port: ${toString cfg.port}
586 wiki-title: ${cfg.wikiTitle}
587 repository-type: ${cfg.repositoryType}
588 repository-path: ${cfg.repositoryPath}
589 require-authentication: ${cfg.requireAuthentication}
590 authentication-method: ${cfg.authenticationMethod}
591 user-file: ${cfg.userFile}
592 session-timeout: ${toString cfg.sessionTimeout}
593 static-dir: ${cfg.staticDir}
594 default-page-type: ${cfg.defaultPageType}
596 mathjax-script: ${cfg.mathJaxScript}
597 show-lhs-bird-tracks: ${toYesNo cfg.showLhsBirdTracks}
598 templates-dir: ${cfg.templatesDir}
599 log-file: ${cfg.logFile}
600 log-level: ${cfg.logLevel}
601 front-page: ${cfg.frontPage}
602 no-delete: ${cfg.noDelete}
603 no-edit: ${cfg.noEdit}
604 default-summary: ${cfg.defaultSummary}
605 table-of-contents: ${toYesNo cfg.tableOfContents}
606 plugins: ${concatStringsSep "," cfg.plugins}
607 use-cache: ${toYesNo cfg.useCache}
608 cache-dir: ${cfg.cacheDir}
609 max-upload-size: ${cfg.maxUploadSize}
610 max-page-size: ${cfg.maxPageSize}
611 debug-mode: ${toYesNo cfg.debugMode}
612 compress-responses: ${toYesNo cfg.compressResponses}
613 mime-types-file: ${cfg.mimeTypesFile}
614 use-recaptcha: ${toYesNo cfg.useReCaptcha}
615 recaptcha-private-key: ${toString cfg.reCaptchaPrivateKey}
616 recaptcha-public-key: ${toString cfg.reCaptchaPublicKey}
617 access-question: ${cfg.accessQuestion}
618 access-question-answers: ${cfg.accessQuestionAnswers}
619 rpx-domain: ${toString cfg.rpxDomain}
620 rpx-key: ${toString cfg.rpxKey}
621 mail-command: ${cfg.mailCommand}
622 reset-password-message: ${cfg.resetPasswordMessage}
623 use-feed: ${toYesNo cfg.useFeed}
624 base-url: ${toString cfg.baseUrl}
625 absolute-urls: ${toYesNo cfg.absoluteUrls}
626 feed-days: ${toString cfg.feedDays}
627 feed-refresh-time: ${toString cfg.feedRefreshTime}
628 pdf-export: ${toYesNo cfg.pdfExport}
629 pandoc-user-data: ${toString cfg.pandocUserData}
630 xss-sanitize: ${toYesNo cfg.xssSanitize}
633 oauthclientid: ${toString cfg.oauthClientId}
634 oauthclientsecret: ${toString cfg.oauthClientSecret}
635 oauthcallback: ${toString cfg.oauthCallback}
636 oauthauthorizeendpoint: ${toString cfg.oauthAuthorizeEndpoint}
637 oauthaccesstokenendpoint: ${toString cfg.oauthAccessTokenEndpoint}
638 github-org: ${toString cfg.githubOrg}
645 options.services.gitit = gititOptions;
647 config = mkIf cfg.enable {
649 users.users.gitit = {
650 group = config.users.groups.gitit.name;
651 description = "Gitit user";
654 uid = config.ids.uids.gitit;
657 users.groups.gitit.gid = config.ids.gids.gitit;
659 systemd.services.gitit = let
660 uid = toString config.ids.uids.gitit;
661 gid = toString config.ids.gids.gitit;
663 description = "Git and Pandoc Powered Wiki";
664 after = [ "network.target" ];
665 wantedBy = [ "multi-user.target" ];
666 path = with pkgs; [ curl ]
667 ++ optional cfg.pdfExport texlive.combined.scheme-basic
668 ++ optional (cfg.repositoryType == "darcs") darcs
669 ++ optional (cfg.repositoryType == "mercurial") mercurial
670 ++ optional (cfg.repositoryType == "git") git;
673 gm = "gitit@${config.networking.hostName}";
676 chown ${uid}:${gid} -R ${homeDir}
677 for dir in ${repositoryPath} ${staticDir} ${templatesDir} ${cacheDir}
682 find $dir -type d -exec chmod 0750 {} +
683 find $dir -type f -exec chmod 0640 {} +
688 if repositoryType == "darcs" then
693 echo "${gm}" > _darcs/prefs/email
695 else if repositoryType == "mercurial" then
700 cat >> .hg/hgrc <<NAMED
702 username = gitit ${gm}
710 git config user.email "${gm}"
711 git config user.name "gitit"
713 chown ${uid}:${gid} -R ${repositoryPath}
719 User = config.users.users.gitit.name;
720 Group = config.users.groups.gitit.name;
721 ExecStart = with cfg; gititSh haskellPackages extraPackages;