at 15.09-beta 24 kB view raw
1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 7 cfg = config.services.gitit; 8 9 homeDir = "/var/lib/gitit"; 10 11 toYesNo = b: if b then "yes" else "no"; 12 13 gititShared = with cfg.haskellPackages; gitit + "/share/" + pkgs.stdenv.system + "-" + ghc.name + "/" + gitit.pname + "-" + gitit.version; 14 15 gititWithPkgs = hsPkgs: extras: hsPkgs.ghcWithPackages (self: with self; [ gitit ] ++ (extras self)); 16 17 gititSh = hsPkgs: extras: with pkgs; let 18 env = gititWithPkgs hsPkgs extras; 19 in writeScript "gitit" '' 20 #!${stdenv.shell} 21 cd $HOME 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} 27 ''; 28 29 gititOptions = { 30 31 enable = mkOption { 32 type = types.bool; 33 default = false; 34 description = "Enable the gitit service."; 35 }; 36 37 haskellPackages = mkOption { 38 defaultText = "pkgs.haskellPackages"; 39 example = literalExample "pkgs.haskell.packages.ghc784"; 40 description = "haskellPackages used to build gitit and plugins."; 41 }; 42 43 extraPackages = mkOption { 44 default = self: []; 45 example = literalExample '' 46 haskellPackages: [ 47 haskellPackages.wreq 48 ] 49 ''; 50 description = '' 51 Extra packages available to ghc when running gitit. The 52 value must be a function which receives the attrset defined 53 in <varname>haskellPackages</varname> as the sole argument. 54 ''; 55 }; 56 57 address = mkOption { 58 type = types.str; 59 default = "0.0.0.0"; 60 description = "IP address on which the web server will listen."; 61 }; 62 63 port = mkOption { 64 type = types.int; 65 default = 5001; 66 description = "Port on which the web server will run."; 67 }; 68 69 wikiTitle = mkOption { 70 type = types.str; 71 default = "Gitit!"; 72 description = "The wiki title."; 73 }; 74 75 repositoryType = mkOption { 76 type = types.enum ["git" "darcs" "mercurial"]; 77 default = "git"; 78 description = "Specifies the type of repository used for wiki content."; 79 }; 80 81 repositoryPath = mkOption { 82 type = types.path; 83 default = homeDir + "/wiki"; 84 description = '' 85 Specifies the path of the repository directory. If it does not 86 exist, gitit will create it on startup. 87 ''; 88 }; 89 90 requireAuthentication = mkOption { 91 type = types.enum [ "none" "modify" "read" ]; 92 default = "modify"; 93 description = '' 94 If 'none', login is never required, and pages can be edited 95 anonymously. If 'modify', login is required to modify the wiki 96 (edit, add, delete pages, upload files). If 'read', login is 97 required to see any wiki pages. 98 ''; 99 }; 100 101 authenticationMethod = mkOption { 102 type = types.enum [ "form" "http" "generic"]; 103 default = "form"; 104 description = '' 105 'form' means that users will be logged in and registered using forms 106 in the gitit web interface. 'http' means that gitit will assume that 107 HTTP authentication is in place and take the logged in username from 108 the "Authorization" field of the HTTP request header (in addition, 109 the login/logout and registration links will be suppressed). 110 'generic' means that gitit will assume that some form of 111 authentication is in place that directly sets REMOTE_USER to the name 112 of the authenticated user (e.g. mod_auth_cas on apache). 'rpx' means 113 that gitit will attempt to log in through https://rpxnow.com. This 114 requires that 'rpx-domain', 'rpx-key', and 'base-url' be set below, 115 and that 'curl' be in the system path. 116 ''; 117 }; 118 119 userFile = mkOption { 120 type = types.path; 121 default = homeDir + "/gitit-users"; 122 description = '' 123 Specifies the path of the file containing user login information. If 124 it does not exist, gitit will create it (with an empty user list). 125 This file is not used if 'http' is selected for 126 authentication-method. 127 ''; 128 }; 129 130 sessionTimeout = mkOption { 131 type = types.int; 132 default = 60; 133 description = '' 134 Number of minutes of inactivity before a session expires. 135 ''; 136 }; 137 138 staticDir = mkOption { 139 type = types.path; 140 description = '' 141 Specifies the path of the static directory (containing javascript, 142 css, and images). If it does not exist, gitit will create it and 143 populate it with required scripts, stylesheets, and images. 144 ''; 145 }; 146 147 defaultPageType = mkOption { 148 type = types.enum [ "markdown" "rst" "latex" "html" "markdown+lhs" "rst+lhs" "latex+lhs" ]; 149 default = "markdown"; 150 description = '' 151 Specifies the type of markup used to interpret pages in the wiki. 152 Possible values are markdown, rst, latex, html, markdown+lhs, 153 rst+lhs, and latex+lhs. (the +lhs variants treat the input as 154 literate Haskell. See pandoc's documentation for more details.) If 155 Markdown is selected, pandoc's syntax extensions (for footnotes, 156 delimited code blocks, etc.) will be enabled. Note that pandoc's 157 restructuredtext parser is not complete, so some pages may not be 158 rendered correctly if rst is selected. The same goes for latex and 159 html. 160 ''; 161 }; 162 163 math = mkOption { 164 type = types.enum [ "mathml" "raw" "mathjax" "jsmath" "google" ]; 165 default = "mathml"; 166 description = '' 167 Specifies how LaTeX math is to be displayed. Possible values are 168 mathml, raw, mathjax, jsmath, and google. If mathml is selected, 169 gitit will convert LaTeX math to MathML and link in a script, 170 MathMLinHTML.js, that allows the MathML to be seen in Gecko browsers, 171 IE + mathplayer, and Opera. In other browsers you may get a jumble of 172 characters. If raw is selected, the LaTeX math will be displayed as 173 raw LaTeX math. If mathjax is selected, gitit will link to the 174 remote mathjax script. If jsMath is selected, gitit will link to the 175 script /js/jsMath/easy/load.js, and will assume that jsMath has been 176 installed into the js/jsMath directory. This is the most portable 177 solution. If google is selected, the google chart API is called to 178 render the formula as an image. This requires a connection to google, 179 and might raise a technical or a privacy problem. 180 ''; 181 }; 182 183 mathJaxScript = mkOption { 184 type = types.str; 185 default = "https://d3eoax9i5htok0.cloudfront.net/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"; 186 description = '' 187 Specifies the path to MathJax rendering script. You might want to 188 use your own MathJax script to render formulas without Internet 189 connection or if you want to use some special LaTeX packages. Note: 190 path specified there cannot be an absolute path to a script on your 191 hdd, instead you should run your (local if you wish) HTTP server 192 which will serve the MathJax.js script. You can easily (in four lines 193 of code) serve MathJax.js using 194 http://happstack.com/docs/crashcourse/FileServing.html Do not forget 195 the "http://" prefix (e.g. http://localhost:1234/MathJax.js). 196 ''; 197 }; 198 199 showLhsBirdTracks = mkOption { 200 type = types.bool; 201 default = false; 202 description = '' 203 Specifies whether to show Haskell code blocks in "bird style", with 204 "> " at the beginning of each line. 205 ''; 206 }; 207 208 templatesDir = mkOption { 209 type = types.path; 210 description = '' 211 Specifies the path of the directory containing page templates. If it 212 does not exist, gitit will create it with default templates. Users 213 may wish to edit the templates to customize the appearance of their 214 wiki. The template files are HStringTemplate templates. Variables to 215 be interpolated appear between $\'s. Literal $\'s must be 216 backslash-escaped. 217 ''; 218 }; 219 220 logFile = mkOption { 221 type = types.path; 222 default = homeDir + "/gitit.log"; 223 description = '' 224 Specifies the path of gitit's log file. If it does not exist, gitit 225 will create it. The log is in Apache combined log format. 226 ''; 227 }; 228 229 logLevel = mkOption { 230 type = types.enum [ "DEBUG" "INFO" "NOTICE" "WARNING" "ERROR" "CRITICAL" "ALERT" "EMERGENCY" ]; 231 default = "ERROR"; 232 description = '' 233 Determines how much information is logged. Possible values (from 234 most to least verbose) are DEBUG, INFO, NOTICE, WARNING, ERROR, 235 CRITICAL, ALERT, EMERGENCY. 236 ''; 237 }; 238 239 frontPage = mkOption { 240 type = types.str; 241 default = "Front Page"; 242 description = '' 243 Specifies which wiki page is to be used as the wiki's front page. 244 Gitit creates a default front page on startup, if one does not exist 245 already. 246 ''; 247 }; 248 249 noDelete = mkOption { 250 type = types.str; 251 default = "Front Page, Help"; 252 description = '' 253 Specifies pages that cannot be deleted through the web interface. 254 (They can still be deleted directly using git or darcs.) A 255 comma-separated list of page names. Leave blank to allow every page 256 to be deleted. 257 ''; 258 }; 259 260 noEdit = mkOption { 261 type = types.str; 262 default = "Help"; 263 description = '' 264 Specifies pages that cannot be edited through the web interface. 265 Leave blank to allow every page to be edited. 266 ''; 267 }; 268 269 defaultSummary = mkOption { 270 type = types.str; 271 default = ""; 272 description = '' 273 Specifies text to be used in the change description if the author 274 leaves the "description" field blank. If default-summary is blank 275 (the default), the author will be required to fill in the description 276 field. 277 ''; 278 }; 279 280 tableOfContents = mkOption { 281 type = types.bool; 282 default = true; 283 description = '' 284 Specifies whether to print a tables of contents (with links to 285 sections) on each wiki page. 286 ''; 287 }; 288 289 plugins = mkOption { 290 type = with types; listOf str; 291 description = '' 292 Specifies a list of plugins to load. Plugins may be specified either 293 by their path or by their module name. If the plugin name starts 294 with Gitit.Plugin., gitit will assume that the plugin is an installed 295 module and will not try to find a source file. 296 ''; 297 }; 298 299 useCache = mkOption { 300 type = types.bool; 301 default = false; 302 description = '' 303 Specifies whether to cache rendered pages. Note that if use-feed is 304 selected, feeds will be cached regardless of the value of use-cache. 305 ''; 306 }; 307 308 cacheDir = mkOption { 309 type = types.path; 310 default = homeDir + "/cache"; 311 description = "Path where rendered pages will be cached."; 312 }; 313 314 maxUploadSize = mkOption { 315 type = types.str; 316 default = "1000K"; 317 description = '' 318 Specifies an upper limit on the size (in bytes) of files uploaded 319 through the wiki's web interface. To disable uploads, set this to 320 0K. This will result in the uploads link disappearing and the 321 _upload url becoming inactive. 322 ''; 323 }; 324 325 maxPageSize = mkOption { 326 type = types.str; 327 default = "1000K"; 328 description = "Specifies an upper limit on the size (in bytes) of pages."; 329 }; 330 331 debugMode = mkOption { 332 type = types.bool; 333 default = false; 334 description = "Causes debug information to be logged while gitit is running."; 335 }; 336 337 compressResponses = mkOption { 338 type = types.bool; 339 default = true; 340 description = "Specifies whether HTTP responses should be compressed."; 341 }; 342 343 mimeTypesFile = mkOption { 344 type = types.path; 345 default = "/etc/mime/types.info"; 346 description = '' 347 Specifies the path of a file containing mime type mappings. Each 348 line of the file should contain two fields, separated by whitespace. 349 The first field is the mime type, the second is a file extension. 350 For example: 351<programlisting> 352video/x-ms-wmx wmx 353</programlisting> 354 If the file is not found, some simple defaults will be used. 355 ''; 356 }; 357 358 useReCaptcha = mkOption { 359 type = types.bool; 360 default = false; 361 description = '' 362 If true, causes gitit to use the reCAPTCHA service 363 (http://recaptcha.net) to prevent bots from creating accounts. 364 ''; 365 }; 366 367 reCaptchaPrivateKey = mkOption { 368 type = with types; nullOr str; 369 default = null; 370 description = '' 371 Specifies the private key for the reCAPTCHA service. To get 372 these, you need to create an account at http://recaptcha.net. 373 ''; 374 }; 375 376 reCaptchaPublicKey = mkOption { 377 type = with types; nullOr str; 378 default = null; 379 description = '' 380 Specifies the public key for the reCAPTCHA service. To get 381 these, you need to create an account at http://recaptcha.net. 382 ''; 383 }; 384 385 accessQuestion = mkOption { 386 type = types.str; 387 default = "What is the code given to you by Ms. X?"; 388 description = '' 389 Specifies a question that users must answer when they attempt to 390 create an account 391 ''; 392 }; 393 394 accessQuestionAnswers = mkOption { 395 type = types.str; 396 default = "RED DOG, red dog"; 397 description = '' 398 Specifies a question that users must answer when they attempt to 399 create an account, along with a comma-separated list of acceptable 400 answers. This can be used to institute a rudimentary password for 401 signing up as a user on the wiki, or as an alternative to reCAPTCHA. 402 Example: 403 access-question: What is the code given to you by Ms. X? 404 access-question-answers: RED DOG, red dog 405 ''; 406 }; 407 408 rpxDomain = mkOption { 409 type = with types; nullOr str; 410 default = null; 411 description = '' 412 Specifies the domain and key of your RPX account. The domain is just 413 the prefix of the complete RPX domain, so if your full domain is 414 'https://foo.rpxnow.com/', use 'foo' as the value of rpx-domain. 415 ''; 416 }; 417 418 rpxKey = mkOption { 419 type = with types; nullOr str; 420 default = null; 421 description = "RPX account access key."; 422 }; 423 424 mailCommand = mkOption { 425 type = types.str; 426 default = "sendmail %s"; 427 description = '' 428 Specifies the command to use to send notification emails. '%s' will 429 be replaced by the destination email address. The body of the 430 message will be read from stdin. If this field is left blank, 431 password reset will not be offered. 432 ''; 433 }; 434 435 resetPasswordMessage = mkOption { 436 type = types.lines; 437 default = '' 438 > From: gitit@$hostname$ 439 > To: $useremail$ 440 > Subject: Wiki password reset 441 > 442 > Hello $username$, 443 > 444 > To reset your password, please follow the link below: 445 > http://$hostname$:$port$$resetlink$ 446 > 447 > Regards 448 ''; 449 description = '' 450 Gives the text of the message that will be sent to the user should 451 she want to reset her password, or change other registration info. 452 The lines must be indented, and must begin with '>'. The initial 453 spaces and '> ' will be stripped off. $username$ will be replaced by 454 the user's username, $useremail$ by her email address, $hostname$ by 455 the hostname on which the wiki is running (as returned by the 456 hostname system call), $port$ by the port on which the wiki is 457 running, and $resetlink$ by the relative path of a reset link derived 458 from the user's existing hashed password. If your gitit wiki is being 459 proxied to a location other than the root path of $port$, you should 460 change the link to reflect this: for example, to 461 http://$hostname$/path/to/wiki$resetlink$ or 462 http://gitit.$hostname$$resetlink$ 463 ''; 464 }; 465 466 useFeed = mkOption { 467 type = types.bool; 468 default = false; 469 description = '' 470 Specifies whether an ATOM feed should be enabled (for the site and 471 for individual pages). 472 ''; 473 }; 474 475 baseUrl = mkOption { 476 type = with types; nullOr str; 477 default = null; 478 description = '' 479 The base URL of the wiki, to be used in constructing feed IDs and RPX 480 token_urls. Set this if useFeed is false or authentication-method 481 is 'rpx'. 482 ''; 483 }; 484 485 absoluteUrls = mkOption { 486 type = types.bool; 487 default = false; 488 description = '' 489 Make wikilinks absolute with respect to the base-url. So, for 490 example, in a wiki served at the base URL '/wiki', on a page 491 Sub/Page, the wikilink '[Cactus]()' will produce a link to 492 '/wiki/Cactus' if absoluteUrls is true, and a relative link to 493 'Cactus' (referring to '/wiki/Sub/Cactus') if absolute-urls is 'no'. 494 ''; 495 }; 496 497 feedDays = mkOption { 498 type = types.int; 499 default = 14; 500 description = "Number of days to be included in feeds."; 501 }; 502 503 feedRefreshTime = mkOption { 504 type = types.int; 505 default = 60; 506 description = "Number of minutes to cache feeds before refreshing."; 507 }; 508 509 pdfExport = mkOption { 510 type = types.bool; 511 default = false; 512 description = '' 513 If true, PDF will appear in export options. PDF will be created using 514 pdflatex, which must be installed and in the path. Note that PDF 515 exports create significant additional server load. 516 ''; 517 }; 518 519 pandocUserData = mkOption { 520 type = with types; nullOr path; 521 default = null; 522 description = '' 523 If a directory is specified, this will be searched for pandoc 524 customizations. These can include a templates/ directory for custom 525 templates for various export formats, an S5 directory for custom S5 526 styles, and a reference.odt for ODT exports. If no directory is 527 specified, $HOME/.pandoc will be searched. See pandoc's README for 528 more information. 529 ''; 530 }; 531 532 xssSanitize = mkOption { 533 type = types.bool; 534 default = true; 535 description = '' 536 If true, all HTML (including that produced by pandoc) is filtered 537 through xss-sanitize. Set to no only if you trust all of your users. 538 ''; 539 }; 540 }; 541 542 configFile = pkgs.writeText "gitit.conf" '' 543 address: ${cfg.address} 544 port: ${toString cfg.port} 545 wiki-title: ${cfg.wikiTitle} 546 repository-type: ${cfg.repositoryType} 547 repository-path: ${cfg.repositoryPath} 548 require-authentication: ${cfg.requireAuthentication} 549 authentication-method: ${cfg.authenticationMethod} 550 user-file: ${cfg.userFile} 551 session-timeout: ${toString cfg.sessionTimeout} 552 static-dir: ${cfg.staticDir} 553 default-page-type: ${cfg.defaultPageType} 554 math: ${cfg.math} 555 mathjax-script: ${cfg.mathJaxScript} 556 show-lhs-bird-tracks: ${toYesNo cfg.showLhsBirdTracks} 557 templates-dir: ${cfg.templatesDir} 558 log-file: ${cfg.logFile} 559 log-level: ${cfg.logLevel} 560 front-page: ${cfg.frontPage} 561 no-delete: ${cfg.noDelete} 562 no-edit: ${cfg.noEdit} 563 default-summary: ${cfg.defaultSummary} 564 table-of-contents: ${toYesNo cfg.tableOfContents} 565 plugins: ${concatStringsSep "," cfg.plugins} 566 use-cache: ${toYesNo cfg.useCache} 567 cache-dir: ${cfg.cacheDir} 568 max-upload-size: ${cfg.maxUploadSize} 569 max-page-size: ${cfg.maxPageSize} 570 debug-mode: ${toYesNo cfg.debugMode} 571 compress-responses: ${toYesNo cfg.compressResponses} 572 mime-types-file: ${cfg.mimeTypesFile} 573 use-recaptcha: ${toYesNo cfg.useReCaptcha} 574 recaptcha-private-key: ${toString cfg.reCaptchaPrivateKey} 575 recaptcha-public-key: ${toString cfg.reCaptchaPublicKey} 576 access-question: ${cfg.accessQuestion} 577 access-question-answers: ${cfg.accessQuestionAnswers} 578 rpx-domain: ${toString cfg.rpxDomain} 579 rpx-key: ${toString cfg.rpxKey} 580 mail-command: ${cfg.mailCommand} 581 reset-password-message: ${cfg.resetPasswordMessage} 582 use-feed: ${toYesNo cfg.useFeed} 583 base-url: ${toString cfg.baseUrl} 584 absolute-urls: ${toYesNo cfg.absoluteUrls} 585 feed-days: ${toString cfg.feedDays} 586 feed-refresh-time: ${toString cfg.feedRefreshTime} 587 pdf-export: ${toYesNo cfg.pdfExport} 588 pandoc-user-data: ${toString cfg.pandocUserData} 589 xss-sanitize: ${toYesNo cfg.xssSanitize} 590 ''; 591 592in 593 594{ 595 596 options.services.gitit = gititOptions; 597 598 config = mkIf cfg.enable { 599 600 services.gitit = { 601 haskellPackages = mkDefault pkgs.haskellPackages; 602 staticDir = gititShared + "/data/static"; 603 templatesDir = gititShared + "/data/templates"; 604 plugins = [ ]; 605 }; 606 607 users.extraUsers.gitit = { 608 group = config.users.extraGroups.gitit.name; 609 description = "Gitit user"; 610 home = homeDir; 611 createHome = true; 612 uid = config.ids.uids.gitit; 613 }; 614 615 users.extraGroups.gitit.gid = config.ids.gids.gitit; 616 617 systemd.services.gitit = let 618 uid = toString config.ids.uids.gitit; 619 gid = toString config.ids.gids.gitit; 620 in { 621 description = "Git and Pandoc Powered Wiki"; 622 after = [ "network.target" ]; 623 wantedBy = [ "multi-user.target" ]; 624 path = with pkgs; [ curl ] 625 ++ optional cfg.pdfExport texLiveFull 626 ++ optional (cfg.repositoryType == "darcs") darcs 627 ++ optional (cfg.repositoryType == "mercurial") mercurial 628 ++ optional (cfg.repositoryType == "git") git; 629 630 preStart = let 631 gm = "gitit@${config.networking.hostName}"; 632 in 633 with cfg; '' 634 chown ${uid}:${gid} -R ${homeDir} 635 for dir in ${repositoryPath} ${staticDir} ${templatesDir} ${cacheDir} 636 do 637 if [ ! -d $dir ] 638 then 639 mkdir -p $dir 640 find $dir -type d -exec chmod 0750 {} + 641 find $dir -type f -exec chmod 0640 {} + 642 fi 643 done 644 cd ${repositoryPath} 645 ${ 646 if repositoryType == "darcs" then 647 '' 648 if [ ! -d _darcs ] 649 then 650 ${pkgs.darcs}/bin/darcs initialize 651 echo "${gm}" > _darcs/prefs/email 652 '' 653 else if repositoryType == "mercurial" then 654 '' 655 if [ ! -d .hg ] 656 then 657 ${pkgs.mercurial}/bin/hg init 658 cat >> .hg/hgrc <<NAMED 659[ui] 660username = gitit ${gm} 661NAMED 662 '' 663 else 664 '' 665 if [ ! -d .git ] 666 then 667 ${pkgs.git}/bin/git init 668 ${pkgs.git}/bin/git config user.email "${gm}" 669 ${pkgs.git}/bin/git config user.name "gitit" 670 ''} 671 chown ${uid}:${gid} -R ${repositoryPath} 672 fi 673 cd - 674 ''; 675 676 serviceConfig = { 677 User = config.users.extraUsers.gitit.name; 678 Group = config.users.extraGroups.gitit.name; 679 ExecStart = with cfg; gititSh haskellPackages extraPackages; 680 }; 681 }; 682 }; 683} 684