packages feed

gitit 0.6.6 → 0.7

raw patch · 20 files changed

+342/−201 lines, 20 filesdep ~filestoredep ~happstack-serverdep ~happstack-util

Dependency ranges changed: filestore, happstack-server, happstack-util, pandoc, xml

Files

CHANGES view
@@ -1,3 +1,55 @@+Version 0.7 released 20 Dec 2009++* Updated cabal file to allow happstack 0.4.++* Added support for the new mercurial filestore backend.+  (Depending on filestore >= 0.3.4.)++* Depend on xml >= 1.3.5.  This fixes a bug in the display of+  mathml.  Previously the self-closed tags in matrices with empty+  cells confused browsers and caused them to construct the+  DOM incorrectly. The problem is fixed by using xml's new+  ppcElement function to render the MathML without self-closed tags.++* Depend on pandoc >= 1.3.++* Properly handle UTF-8 in config files.++* Moved option parsing code from Config module to main program.+  The Config module now exports getConfigFromFile instead of+  getConfigFromOpts. This should be more useful for those using gitit as+  a library.++* Use wikiTitle config field in default HTML title.++* Improved search results:+  + Highlight search terms in search results.+    Partially resolves Issue #76.+  + Made search results message uniform when no results.+  + Search: don't match page name against empty patterns.+  + Allow search matches on subdirectory part of page name.+  + Search:  catch error status from filestore search.+    Filestore <= 0.3.3 does not properly handle the error status+    returned by later versions of 'git grep' when no match is found.+    The problem has been fixed in darcs filestore.++* CSS tweaks:+  + Removed base-min.css, folded necessary styles into screen.css.+  + Removed 'text-align: left' for th from CSS reset.++* Feed improvements:+  + Modified feed handling so that feeds validate.+  + Perform proper escaping in Feed.hs (thanks to gwern).+  + Don't reveal author email in feeds.+  + Sitewide feed is /_feed/ (with trailing slash).+  + Add "http://" to base-url config option if needed.++* Use + for spaces in URLs linking to wiki pages and folders.++* Updated plugins:+  + Updated Interwiki plugin (gwern).+  + Modified WebArchiver plugin to make Alexa requests (gwern).+ Version 0.6.6 released 06 Nov 2009  * Require filestore >= 0.3.3, which closes a security
Network/Gitit/Config.hs view
@@ -20,9 +20,9 @@ {- | Functions for parsing command line options and reading the config file. -} -module Network.Gitit.Config ( getConfigFromOpts-                            , readMimeTypesFile-                            , getDefaultConfig )+module Network.Gitit.Config ( getConfigFromFile+                            , getDefaultConfig+                            , readMimeTypesFile ) where import Safe import Network.Gitit.Types@@ -32,87 +32,36 @@ import Network.Gitit.Util (parsePageType) import System.Log.Logger (logM, Priority(..)) import qualified Data.Map as M-import System.Environment-import System.Exit-import System.IO (stdout, stderr)-import System.Console.GetOpt-import Data.ConfigFile+import Data.ConfigFile hiding (readfile) import Control.Monad.Error import System.Log.Logger () import Data.List (intercalate) import Data.Char (toLower, toUpper, isDigit)-import Data.Version (showVersion)-import Paths_gitit (getDataFileName, version)+import Paths_gitit (getDataFileName) import Prelude hiding (readFile) import System.IO.UTF8 import System.FilePath ((</>)) import Control.Monad (liftM) import Text.Pandoc -data Opt-    = Help-    | ConfigFile FilePath-    | Port Int-    | Debug-    | Version-    | PrintDefaultConfig-    deriving (Eq)--flags :: [OptDescr Opt]-flags =-   [ Option ['h'] ["help"] (NoArg Help)-        "Print this help message"-   , Option ['v'] ["version"] (NoArg Version)-        "Print version information"-   , Option ['p'] ["port"] (ReqArg (Port . read) "PORT")-        "Specify port"-   , Option [] ["print-default-config"] (NoArg PrintDefaultConfig)-        "Print default configuration"-   , Option ['d'] ["debug"] (NoArg Debug)-        "Print debugging information on each request"-   , Option ['f'] ["config-file"] (ReqArg ConfigFile "FILE")-        "Specify configuration file"-   ]--parseArgs :: [String] -> IO [Opt]-parseArgs argv = do-  progname <- getProgName-  case getOpt Permute flags argv of-    (opts,_,[])  -> return opts-    (_,_,errs)   -> hPutStrLn stderr (concat errs ++ usageInfo (usageHeader progname) flags) >>-                       exitWith (ExitFailure 1)--usageHeader :: String -> String-usageHeader progname = "Usage:  " ++ progname ++ " [opts...]"--copyrightMessage :: String-copyrightMessage = "\nCopyright (C) 2008 John MacFarlane\n" ++-                   "This is free software; see the source for copying conditions.  There is no\n" ++-                   "warranty, not even for merchantability or fitness for a particular purpose."--compileInfo :: String-compileInfo =-#ifdef _PLUGINS-  " +plugins"-#else-  " -plugins"-#endif- forceEither :: Show e => Either e a -> a forceEither = either (error . show) id -handleFlag :: ConfigParser -> Config -> Opt -> IO Config-handleFlag cp conf opt = do-  progname <- getProgName-  case opt of-    Help               -> hPutStrLn stderr (usageInfo (usageHeader progname) flags) >> exitWith ExitSuccess-    Version            -> hPutStrLn stderr (progname ++ " version " ++ showVersion version ++ compileInfo ++ copyrightMessage) >> exitWith ExitSuccess-    PrintDefaultConfig -> getDataFileName "data/default.conf" >>= readFile >>=-                          hPutStrLn stdout >> exitWith ExitSuccess-    Debug              -> return conf{ debugMode = True }-    Port p             -> return conf{ portNumber = p }-    ConfigFile fname   -> readfile cp fname >>= extractConfig . forceEither+-- | Get configuration from config file.+getConfigFromFile :: FilePath -> IO Config+getConfigFromFile fname = do+  cp <- getDefaultConfigParser+  readfile cp fname >>= extractConfig . forceEither +-- | A version of readfile that treats the file as UTF-8.+readfile :: MonadError CPError m+          => ConfigParser+          -> FilePath+          -> IO (m ConfigParser)+readfile cp path' = do+  contents <- readFile path'+  return $ readstring cp contents+ extractConfig :: ConfigParser -> IO Config extractConfig cp = do   config' <- runErrorT $ do@@ -162,9 +111,10 @@       let authMethod = map toLower cfAuthenticationMethod       let stripTrailingSlash = reverse . dropWhile (=='/') . reverse       let repotype' = case map toLower cfRepositoryType of-                        "git"   -> Git-                        "darcs" -> Darcs-                        x       -> error $ "Unknown repository type: " ++ x+                        "git"       -> Git+                        "darcs"     -> Darcs+                        "mercurial" -> Mercurial+                        x           -> error $ "Unknown repository type: " ++ x        return $! Config{           repositoryPath       = cfRepositoryPath@@ -265,16 +215,6 @@ -- | Returns the default gitit configuration. getDefaultConfig :: IO Config getDefaultConfig = getDefaultConfigParser >>= extractConfig---- | Parses command line options and returns configuration--- based on the options (-f FILE specifies a configuration--- file; some settings, such as port number, can be overridden--- by a command line option).-getConfigFromOpts :: IO Config-getConfigFromOpts = do-  cp' <- getDefaultConfigParser-  defaultConfig <- extractConfig cp'-  getArgs >>= parseArgs >>= foldM (handleFlag cp') defaultConfig  -- | Read a file associating mime types with extensions, and return a -- map from extensions to types. Each line of the file consists of a
Network/Gitit/ContentTransformer.hs view
@@ -525,8 +525,9 @@ convertTeXMathToMathML (Math t x) = do   case texMathToMathML t' x of        Left _  -> return $ Math t x-       Right v -> put True >> return (HtmlInline $ ppElement v)+       Right v -> put True >> return (HtmlInline $ showXml v)     where t' = if t == DisplayMath then DisplayBlock else DisplayInline+          showXml = ppcElement (useShortEmptyTags (const False) defaultConfigPP) convertTeXMathToMathML x = return x  -- | Derives a URL from a list of Pandoc Inline elements.
Network/Gitit/Feed.hs view
@@ -22,17 +22,20 @@  module Network.Gitit.Feed (FeedConfig(..), filestoreToXmlFeed) where -import Text.Atom.Feed-import Text.Atom.Feed.Export-import Text.XML.Light-import Data.FileStore.Types-import Data.Maybe-import Data.DateTime-import System.FilePath import Control.Monad+import Data.DateTime import Data.List (intercalate, sortBy)+import Data.Maybe import Data.Ord (comparing)+import Network.URI (isAllowedInURI, escapeURIString)+import System.FilePath +import Data.FileStore.Types++import Text.Atom.Feed+import Text.Atom.Feed.Export+import Text.XML.Light+ data FeedConfig = FeedConfig {     fcTitle    :: String   , fcBaseUrl  :: String@@ -55,7 +58,7 @@                      [] -> []                      (x:_) -> x : init rs   -- so we can get revids for diffs   now <- liftM formatFeedTime getCurrentTime-  return $ Feed { feedId = fcBaseUrl cfg ++ "/" ++ path'+  return $ Feed { feedId = fcBaseUrl cfg ++ "/" ++ escape path'                 , feedTitle = TextString $ fcTitle cfg                 , feedUpdated = now                 , feedAuthors = []@@ -65,7 +68,7 @@                                                 , genVersion = Nothing                                                 , genText = "gitit" }                 , feedIcon = Nothing-                , feedLinks = []+                , feedLinks = [ (nullLink (fcBaseUrl cfg ++ "/_feed/" ++ escape path')) {linkRel = Just (Left "self")} ]                 , feedLogo = Nothing                 , feedRights = Nothing                 , feedSubtitle = Nothing@@ -92,7 +95,9 @@   baseEntry{ entrySummary = Just $ TextString rd            , entryAuthors = [Person { personName = authorName ra                                     , personURI = Nothing -                                    , personEmail = Just $ authorEmail ra+                                    , personEmail = Nothing+                                      -- gitit is set up not to reveal registration emails. To change this:+                                      -- let e = authorEmail ra in if e /= "" then Just e else Nothing                                     , personOther = [] }]            , entryLinks = [diffLink] @@ -115,8 +120,8 @@            -- Enclosure seems to be for conveying media, see            -- https://secure.wikimedia.org/wikipedia/en/wiki/RSS_enclosure         }-    where diffLink = Link{ linkHref = fcBaseUrl cfg ++ "/_diff/" ++ firstpath ++ "?to=" ++ rid ++ fromrev-                         , linkRel = Nothing+    where diffLink = Link{ linkHref = fcBaseUrl cfg ++ "/_diff/" ++ escape firstpath ++ "?to=" ++ rid ++ fromrev+                         , linkRel = Just (Left "alternate")                          , linkType = Nothing                          , linkHrefLang = Nothing                          , linkTitle = Nothing@@ -137,7 +142,7 @@                                    Added f    -> (dePage f, "")                                    Deleted f  -> (dePage f, "&from=" ++ revId prevRevision)                          else (path',"")-          baseEntry = nullEntry (fcBaseUrl cfg ++ "/" ++ path' ++ "?revision=" ++ rid)+          baseEntry = nullEntry (fcBaseUrl cfg ++ "/" ++ escape path' ++ "?revision=" ++ rid)                         (TextString (intercalate ", " $ map showRev rv)) (formatFeedTime rdt)           showRev (Modified f) = dePage f           showRev (Added f)    = "added " ++ dePage f@@ -145,6 +150,9 @@           dePage f = if takeExtension f == ".page"                         then dropExtension f                         else f++escape :: String -> String+escape = escapeURIString isAllowedInURI  formatFeedTime :: DateTime -> String formatFeedTime = formatDateTime "%Y-%m%--%dT%TZ"  -- Why the double hyphen between %m and %d? It works.
Network/Gitit/Framework.hs view
@@ -275,7 +275,9 @@ -- the wiki base. urlForPage :: String -> String urlForPage page = "/" ++-  encString True (\c -> isAscii c && (c `notElem` "?&")) page+  encString True (\c -> isAscii c && (c `notElem` "?&")) (map spaceToPlus page)+    where spaceToPlus ' ' = '+'+          spaceToPlus c   = c -- / and @ are left unescaped so that browsers recognize relative URLs and talk pages correctly  -- | Returns the filestore path of the file containing the page's source.@@ -350,5 +352,6 @@ filestoreFromConfig :: Config -> FileStore filestoreFromConfig conf =   case repositoryType conf of-         Git   -> gitFileStore $ repositoryPath conf-         Darcs -> darcsFileStore $ repositoryPath conf+         Git       -> gitFileStore       $ repositoryPath conf+         Darcs     -> darcsFileStore     $ repositoryPath conf+         Mercurial -> mercurialFileStore $ repositoryPath conf
Network/Gitit/Handlers.hs view
@@ -266,17 +266,21 @@   fs <- getFileStore   matchLines <- if null patterns                    then return []-                   else liftIO $ search fs SearchQuery{-                                            queryPatterns = patterns-                                          , queryWholeWords = True-                                          , queryMatchAll = True-                                          , queryIgnoreCase = True }+                   else liftIO $ catch (search fs SearchQuery{+                                                  queryPatterns = patterns+                                                , queryWholeWords = True+                                                , queryMatchAll = True+                                                , queryIgnoreCase = True })+                                       -- catch error, because newer versions of git+                                       -- return 1 on no match, and filestore <=0.3.3+                                       -- doesn't handle this properly:+                                       (\(_ :: FileStoreError)  -> return [])   let contentMatches = map matchResourceName matchLines   allPages <- liftM (filter isPageFile) $ liftIO $ index fs-  let inPageName pageName' x = x `elem` (words $ map toLower $-                                          dropExtension pageName')-  let matchesPatterns pageName' = all (inPageName pageName') $-                                    map (map toLower) patterns+  let slashToSpace = map (\c -> if c == '/' then ' ' else c)+  let inPageName pageName' x = x `elem` (words $ slashToSpace $ dropExtension pageName')+  let matchesPatterns pageName' = not (null patterns) &&+       all (inPageName (map toLower pageName')) (map (map toLower) patterns)   let pageNameMatches = filter matchesPatterns allPages   let allMatchedFiles = nub $ filter isPageFile contentMatches ++                               pageNameMatches@@ -287,13 +291,10 @@   let relevance (f, ms) = length ms + if f `elem` pageNameMatches                                          then 100                                          else 0-  let preamble = if null matches-                    then h3 << if null patterns-                                  then ["Please enter a search term."]-                                  else ["No matches found for '",-                                         unwords patterns, "':"]-                    else h3 << [(show $ length matches),-                                " matches found for '", unwords patterns, "':"]+  let preamble = if null patterns +                    then h3 << ["Please enter a search term."]+                    else h3 << [ stringToHtml (show (length matches) ++ " matches found for ")+                               , thespan ! [identifier "pattern"] << unwords patterns]   base' <- getWikiBase   let toMatchListItem (file, contents) = li <<         [ anchor ! [href $ base' ++ urlForPage (dropExtension file)] << dropExtension file@@ -663,18 +664,19 @@ fileListToHtml base' prefix files =   let fileLink (FSFile f) | isPageFile f =         li ! [theclass "page"  ] <<-          anchor ! [href $ base' ++ "/" ++ prefix ++ dropExtension f] << dropExtension f+          anchor ! [href $ base' ++ urlForPage (prefix ++ dropExtension f)] <<+            dropExtension f       fileLink (FSFile f) =-        li ! [theclass "upload"] << anchor ! [href $ base' ++ "/" ++ prefix ++ f] << f+        li ! [theclass "upload"] << anchor ! [href $ base' ++ urlForPage (prefix ++ f)] << f       fileLink (FSDirectory f) =         li ! [theclass "folder"] <<-          anchor ! [href $ base' ++ "/" ++ prefix ++ f ++ "/"] << f+          anchor ! [href $ base' ++ urlForPage (prefix ++ f) ++ "/"] << f       updirs = drop 1 $ inits $ splitPath $ '/' : prefix       uplink = foldr (\d accum ->                   concatHtml [ anchor ! [theclass "updir",                                          href $ if length d <= 1                                                    then base' ++ "/_index"-                                                   else base' ++ joinPath d] <<+                                                   else base' ++ urlForPage (joinPath d)] <<                   lastNote "fileListToHtml" d, accum]) noHtml updirs   in uplink +++ ulist ! [theclass "index"] << map fileLink files @@ -721,7 +723,7 @@                 liftM extractCategories . (readFile . (repoPath </>))   base' <- getWikiBase   let toCatLink ctg = li <<-        [ anchor ! [href $ base' ++ "/_category/" ++ ctg] << ctg ]+        [ anchor ! [href $ base' ++ "/_category" ++ urlForPage ctg] << ctg ]   let htmlMatches = ulist << map toCatLink categories   formattedPage defaultPageLayout{                   pgPageName = "Categories",@@ -753,8 +755,10 @@                    mbHost <- getHost                    case mbHost of                         Nothing    -> error "Could not determine base URL"-                        Just hn    -> return ("http://" ++ hn ++ base')-                 else return (baseUrl cfg ++ base')+                        Just hn    -> return $ "http://" ++ hn ++ base'+                 else case baseUrl cfg ++ base' of+                           x@('h':'t':'t':'p':':':'/':'/':_) -> return x+                           y                                 -> return $ "http://" ++ y   let fc = FeedConfig{               fcTitle = wikiTitle cfg             , fcBaseUrl = feedBase
Network/Gitit/Layout.hs view
@@ -81,6 +81,7 @@   let filledTemp = T.render .                    T.setAttribute "base" base' .                    T.setAttribute "feed" (pgLinkToFeed layout) .+                   setStrAttr "wikititle" (wikiTitle cfg) .                    setStrAttr "pagetitle" (pgTitle layout) .                    T.setAttribute "javascripts" javascriptlinks .                    setStrAttr "pagename" page .
Network/Gitit/Types.hs view
@@ -42,7 +42,7 @@ data PageType = Markdown | RST | LaTeX | HTML                 deriving (Read, Show, Eq) -data FileStoreType = Git | Darcs deriving Show+data FileStoreType = Git | Darcs | Mercurial deriving Show  data MathMethod = MathML | JsMathScript | RawTeX                   deriving (Read, Show, Eq)
README.markdown view
@@ -1,13 +1,13 @@ Gitit ===== -Gitit is a wiki program written in Haskell. It uses [Happstack][]-for the web server and [pandoc][] for markup processing. Pages and-uploaded files are stored in a [git][] or [darcs][] repository and may-be modified either by using the VCS's command-line tools or through the-wiki's web interface. By default, pandoc's extended version of markdown-is used as a markup language, but reStructuredText, LaTeX, or HTML-can also be used. Pages can be exported in a number of different+Gitit is a wiki program written in Haskell. It uses [Happstack][] for+the web server and [pandoc][] for markup processing. Pages and uploaded+files are stored in a [git][], [darcs][], or [mercurial][] repository+and may be modified either by using the VCS's command-line tools or+through the wiki's web interface. By default, pandoc's extended version+of markdown is used as a markup language, but reStructuredText, LaTeX,+or HTML can also be used. Pages can be exported in a number of different formats, including LaTeX, RTF, OpenOffice ODT, and MediaWiki markup. Gitit can be configured to display TeX math (using [texmath][]) and highlighted source code (using [highlighting-kate][]).@@ -35,6 +35,7 @@  [git]: http://git.or.cz [darcs]: http://darcs.net+[mercurial]: http://mercurial.selenic.com/ [pandoc]: http://johnmacfarlane.net/pandoc [Happstack]: http://happstack.com [highlighting-kate]: http://johnmacfarlane.net/highlighting-kate/@@ -99,8 +100,8 @@ Running gitit ------------- -To run gitit, you'll need [git][] in your system path. (Or-[darcs][], if you're using darcs to store the wiki data.)+To run gitit, you'll need `git` in your system path. (Or `darcs` or+`hg`, if you're using darcs or mercurial to store the wiki data.)  Gitit assumes that the page files (stored in the git repository) are encoded as UTF-8. Even page names may be UTF-8 if the file system@@ -246,7 +247,7 @@ as well.  It will not be possible to modify these files using the web interface, but they will be modifiable via git. -Using darcs instead of git+Using a VCS other than git --------------------------  By default, gitit will store wiki pages in a git repository in the@@ -255,6 +256,10 @@      repository-type: Darcs +If you'd prefer to use mercurial, add:++    repository-type: Mercurial+ This program may be called "darcsit" instead of "gitit" when a darcs backend is used. @@ -339,22 +344,26 @@ Note also that if you compile gitit for executable profiling, attempts to load plugins will result in "internal error: PAP object entered!" -Accessing the wiki via git or darcs-===================================+Accessing the wiki through git+============================== -All the pages and uploaded files are stored in a git or darcs-repository. By default, this lives in the `wikidata` directory (though-this can be changed through configuration options). So you can interact-with the wiki using git command line tools:+All the pages and uploaded files are stored in a git repository. By+default, this lives in the `wikidata` directory (though this can be+changed through configuration options). So you can interact with the+wiki using git command line tools:      git clone ssh://my.server.edu/path/of/wiki/wikidata     cd wikidata     vim Front\ Page.page  # edit the page     git commit -m "Added message about wiki etiquette" Front\ Page.page-    git push +    git push  If you now look at the Front Page on the wiki, you should see your changes reflected there.  Note that the pages all have the extension `.page`.++If you are using the darcs or mercurial backend, the commands will+be slightly different.  See the documentation for your VCS for+details.  Caching =======
data/default.conf view
@@ -3,9 +3,12 @@ port: 5001 # sets the port on which the web server will run. +wiki-title: Wiki+# the title of the wiki.+ repository-type: Git # specifies the type of repository used for wiki content.-# Options are Git and Darcs.+# Options are Git, Darcs, and Mercurial.  repository-path: wikidata # specifies the path of the repository directory.  If it does not@@ -200,9 +203,6 @@ # request header 'Host'.  For most users, this should be fine, but # if you are proxying a gitit instance to a subdirectory URL, you will # want to set this manually.--wiki-title: Wiki-# the title of the wiki, used in feeds.  feed-days: 14 # number of days to be included in feeds.
− data/static/css/base-min.css
@@ -1,8 +0,0 @@-/*-Copyright (c) 2009, Yahoo! Inc. All rights reserved.-Code licensed under the BSD License:-http://developer.yahoo.net/yui/license.txt-version: 2.7.0-MODIFIED by JM - don't reset list item styles.-*/-body{margin:10px;}h1{font-size:138.5%;}h2{font-size:123.1%;}h3{font-size:108%;}h1,h2,h3{margin:1em 0;}h1,h2,h3,h4,h5,h6,strong,dt{font-weight:bold;}optgroup{font-weight:normal;}abbr,acronym{border-bottom:1px dotted #000;cursor:help;}em{font-style:italic;}del{text-decoration:line-through;}blockquote,ul,ol,dl{margin:1em;}ol,ul,dl{margin-left:2em;}ul li{list-style:disc outside;}dl dd{margin-left:1em;}th,td{border:1px solid #000;padding:.5em;}th{font-weight:bold;text-align:center;}caption{margin-bottom:.5em;text-align:center;}sup{vertical-align:super;}sub{vertical-align:sub;}p,fieldset,table,pre{margin-bottom:1em;}button,input[type="checkbox"],input[type="radio"],input[type="reset"],input[type="submit"]{padding:1px;}
data/static/css/reset-fonts-grids.css view
@@ -3,6 +3,6 @@ Code licensed under the BSD License: http://developer.yahoo.net/yui/license.txt version: 2.7.0-MODIFIED by JM - don't reset list item styles+MODIFIED by JM - don't reset list item styles, removed 'caption,th{text-align:left}' */-html{color:#000;background:#FFF;}body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,button,textarea,p,blockquote,th,td{margin:0;padding:0;}table{border-collapse:collapse;border-spacing:0;}fieldset,img{border:0;}address,caption,cite,code,dfn,em,strong,th,var,optgroup{font-style:inherit;font-weight:inherit;}del,ins{text-decoration:none;}caption,th{text-align:left;}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal;}q:before,q:after{content:'';}abbr,acronym{border:0;font-variant:normal;}sup{vertical-align:baseline;}sub{vertical-align:baseline;}legend{color:#000;}input,button,textarea,select,optgroup,option{font-family:inherit;font-size:inherit;font-style:inherit;font-weight:inherit;}input,button,textarea,select{*font-size:100%;}body{font:13px/1.231 arial,helvetica,clean,sans-serif;*font-size:small;*font:x-small;}select,input,button,textarea,button{font:99% arial,helvetica,clean,sans-serif;}table{font-size:inherit;font:100%;}pre,code,kbd,samp,tt{font-family:monospace;*font-size:108%;line-height:100%;}body{text-align:center;}#doc,#doc2,#doc3,#doc4,.yui-t1,.yui-t2,.yui-t3,.yui-t4,.yui-t5,.yui-t6,.yui-t7{margin:auto;text-align:left;width:57.69em;*width:56.25em;}#doc2{width:73.076em;*width:71.25em;}#doc3{margin:auto 10px;width:auto;}#doc4{width:74.923em;*width:73.05em;}.yui-b{position:relative;}.yui-b{_position:static;}#yui-main .yui-b{position:static;}#yui-main,.yui-g .yui-u .yui-g{width:100%;}.yui-t1 #yui-main,.yui-t2 #yui-main,.yui-t3 #yui-main{float:right;margin-left:-25em;}.yui-t4 #yui-main,.yui-t5 #yui-main,.yui-t6 #yui-main{float:left;margin-right:-25em;}.yui-t1 .yui-b{float:left;width:12.30769em;*width:12.00em;}.yui-t1 #yui-main .yui-b{margin-left:13.30769em;*margin-left:13.05em;}.yui-t2 .yui-b{float:left;width:13.8461em;*width:13.50em;}.yui-t2 #yui-main .yui-b{margin-left:14.8461em;*margin-left:14.55em;}.yui-t3 .yui-b{float:left;width:23.0769em;*width:22.50em;}.yui-t3 #yui-main .yui-b{margin-left:24.0769em;*margin-left:23.62em;}.yui-t4 .yui-b{float:right;width:13.8456em;*width:13.50em;}.yui-t4 #yui-main .yui-b{margin-right:14.8456em;*margin-right:14.55em;}.yui-t5 .yui-b{float:right;width:18.4615em;*width:18.00em;}.yui-t5 #yui-main .yui-b{margin-right:19.4615em;*margin-right:19.125em;}.yui-t6 .yui-b{float:right;width:23.0769em;*width:22.50em;}.yui-t6 #yui-main .yui-b{margin-right:24.0769em;*margin-right:23.62em;}.yui-t7 #yui-main .yui-b{display:block;margin:0 0 1em 0;}#yui-main .yui-b{float:none;width:auto;}.yui-gb .yui-u,.yui-g .yui-gb .yui-u,.yui-gb .yui-g,.yui-gb .yui-gb,.yui-gb .yui-gc,.yui-gb .yui-gd,.yui-gb .yui-ge,.yui-gb .yui-gf,.yui-gc .yui-u,.yui-gc .yui-g,.yui-gd .yui-u{float:left;}.yui-g .yui-u,.yui-g .yui-g,.yui-g .yui-gb,.yui-g .yui-gc,.yui-g .yui-gd,.yui-g .yui-ge,.yui-g .yui-gf,.yui-gc .yui-u,.yui-gd .yui-g,.yui-g .yui-gc .yui-u,.yui-ge .yui-u,.yui-ge .yui-g,.yui-gf .yui-g,.yui-gf .yui-u{float:right;}.yui-g div.first,.yui-gb div.first,.yui-gc div.first,.yui-gd div.first,.yui-ge div.first,.yui-gf div.first,.yui-g .yui-gc div.first,.yui-g .yui-ge div.first,.yui-gc div.first div.first{float:left;}.yui-g .yui-u,.yui-g .yui-g,.yui-g .yui-gb,.yui-g .yui-gc,.yui-g .yui-gd,.yui-g .yui-ge,.yui-g .yui-gf{width:49.1%;}.yui-gb .yui-u,.yui-g .yui-gb .yui-u,.yui-gb .yui-g,.yui-gb .yui-gb,.yui-gb .yui-gc,.yui-gb .yui-gd,.yui-gb .yui-ge,.yui-gb .yui-gf,.yui-gc .yui-u,.yui-gc .yui-g,.yui-gd .yui-u{width:32%;margin-left:1.99%;}.yui-gb .yui-u{*margin-left:1.9%;*width:31.9%;}.yui-gc div.first,.yui-gd .yui-u{width:66%;}.yui-gd div.first{width:32%;}.yui-ge div.first,.yui-gf .yui-u{width:74.2%;}.yui-ge .yui-u,.yui-gf div.first{width:24%;}.yui-g .yui-gb div.first,.yui-gb div.first,.yui-gc div.first,.yui-gd div.first{margin-left:0;}.yui-g .yui-g .yui-u,.yui-gb .yui-g .yui-u,.yui-gc .yui-g .yui-u,.yui-gd .yui-g .yui-u,.yui-ge .yui-g .yui-u,.yui-gf .yui-g .yui-u{width:49%;*width:48.1%;*margin-left:0;}.yui-g .yui-g .yui-u{width:48.1%;}.yui-g .yui-gb div.first,.yui-gb .yui-gb div.first{*margin-right:0;*width:32%;_width:31.7%;}.yui-g .yui-gc div.first,.yui-gd .yui-g{width:66%;}.yui-gb .yui-g div.first{*margin-right:4%;_margin-right:1.3%;}.yui-gb .yui-gc div.first,.yui-gb .yui-gd div.first{*margin-right:0;}.yui-gb .yui-gb .yui-u,.yui-gb .yui-gc .yui-u{*margin-left:1.8%;_margin-left:4%;}.yui-g .yui-gb .yui-u{_margin-left:1.0%;}.yui-gb .yui-gd .yui-u{*width:66%;_width:61.2%;}.yui-gb .yui-gd div.first{*width:31%;_width:29.5%;}.yui-g .yui-gc .yui-u,.yui-gb .yui-gc .yui-u{width:32%;_float:right;margin-right:0;_margin-left:0;}.yui-gb .yui-gc div.first{width:66%;*float:left;*margin-left:0;}.yui-gb .yui-ge .yui-u,.yui-gb .yui-gf .yui-u{margin:0;}.yui-gb .yui-gb .yui-u{_margin-left:.7%;}.yui-gb .yui-g div.first,.yui-gb .yui-gb div.first{*margin-left:0;}.yui-gc .yui-g .yui-u,.yui-gd .yui-g .yui-u{*width:48.1%;*margin-left:0;}.yui-gb .yui-gd div.first{width:32%;}.yui-g .yui-gd div.first{_width:29.9%;}.yui-ge .yui-g{width:24%;}.yui-gf .yui-g{width:74.2%;}.yui-gb .yui-ge div.yui-u,.yui-gb .yui-gf div.yui-u{float:right;}.yui-gb .yui-ge div.first,.yui-gb .yui-gf div.first{float:left;}.yui-gb .yui-ge .yui-u,.yui-gb .yui-gf div.first{*width:24%;_width:20%;}.yui-gb .yui-ge div.first,.yui-gb .yui-gf .yui-u{*width:73.5%;_width:65.5%;}.yui-ge div.first .yui-gd .yui-u{width:65%;}.yui-ge div.first .yui-gd div.first{width:32%;}#hd:after,#bd:after,#ft:after,.yui-g:after,.yui-gb:after,.yui-gc:after,.yui-gd:after,.yui-ge:after,.yui-gf:after{content:".";display:block;height:0;clear:both;visibility:hidden;}#hd,#bd,#ft,.yui-g,.yui-gb,.yui-gc,.yui-gd,.yui-ge,.yui-gf{zoom:1;}+html{color:#000;background:#FFF;}body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,button,textarea,p,blockquote,th,td{margin:0;padding:0;}table{border-collapse:collapse;border-spacing:0;}fieldset,img{border:0;}address,caption,cite,code,dfn,em,strong,th,var,optgroup{font-style:inherit;font-weight:inherit;}del,ins{text-decoration:none;}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal;}q:before,q:after{content:'';}abbr,acronym{border:0;font-variant:normal;}sup{vertical-align:baseline;}sub{vertical-align:baseline;}legend{color:#000;}input,button,textarea,select,optgroup,option{font-family:inherit;font-size:inherit;font-style:inherit;font-weight:inherit;}input,button,textarea,select{*font-size:100%;}body{font:13px/1.231 arial,helvetica,clean,sans-serif;*font-size:small;*font:x-small;}select,input,button,textarea,button{font:99% arial,helvetica,clean,sans-serif;}table{font-size:inherit;font:100%;}pre,code,kbd,samp,tt{font-family:monospace;*font-size:108%;line-height:100%;}body{text-align:center;}#doc,#doc2,#doc3,#doc4,.yui-t1,.yui-t2,.yui-t3,.yui-t4,.yui-t5,.yui-t6,.yui-t7{margin:auto;text-align:left;width:57.69em;*width:56.25em;}#doc2{width:73.076em;*width:71.25em;}#doc3{margin:auto 10px;width:auto;}#doc4{width:74.923em;*width:73.05em;}.yui-b{position:relative;}.yui-b{_position:static;}#yui-main .yui-b{position:static;}#yui-main,.yui-g .yui-u .yui-g{width:100%;}.yui-t1 #yui-main,.yui-t2 #yui-main,.yui-t3 #yui-main{float:right;margin-left:-25em;}.yui-t4 #yui-main,.yui-t5 #yui-main,.yui-t6 #yui-main{float:left;margin-right:-25em;}.yui-t1 .yui-b{float:left;width:12.30769em;*width:12.00em;}.yui-t1 #yui-main .yui-b{margin-left:13.30769em;*margin-left:13.05em;}.yui-t2 .yui-b{float:left;width:13.8461em;*width:13.50em;}.yui-t2 #yui-main .yui-b{margin-left:14.8461em;*margin-left:14.55em;}.yui-t3 .yui-b{float:left;width:23.0769em;*width:22.50em;}.yui-t3 #yui-main .yui-b{margin-left:24.0769em;*margin-left:23.62em;}.yui-t4 .yui-b{float:right;width:13.8456em;*width:13.50em;}.yui-t4 #yui-main .yui-b{margin-right:14.8456em;*margin-right:14.55em;}.yui-t5 .yui-b{float:right;width:18.4615em;*width:18.00em;}.yui-t5 #yui-main .yui-b{margin-right:19.4615em;*margin-right:19.125em;}.yui-t6 .yui-b{float:right;width:23.0769em;*width:22.50em;}.yui-t6 #yui-main .yui-b{margin-right:24.0769em;*margin-right:23.62em;}.yui-t7 #yui-main .yui-b{display:block;margin:0 0 1em 0;}#yui-main .yui-b{float:none;width:auto;}.yui-gb .yui-u,.yui-g .yui-gb .yui-u,.yui-gb .yui-g,.yui-gb .yui-gb,.yui-gb .yui-gc,.yui-gb .yui-gd,.yui-gb .yui-ge,.yui-gb .yui-gf,.yui-gc .yui-u,.yui-gc .yui-g,.yui-gd .yui-u{float:left;}.yui-g .yui-u,.yui-g .yui-g,.yui-g .yui-gb,.yui-g .yui-gc,.yui-g .yui-gd,.yui-g .yui-ge,.yui-g .yui-gf,.yui-gc .yui-u,.yui-gd .yui-g,.yui-g .yui-gc .yui-u,.yui-ge .yui-u,.yui-ge .yui-g,.yui-gf .yui-g,.yui-gf .yui-u{float:right;}.yui-g div.first,.yui-gb div.first,.yui-gc div.first,.yui-gd div.first,.yui-ge div.first,.yui-gf div.first,.yui-g .yui-gc div.first,.yui-g .yui-ge div.first,.yui-gc div.first div.first{float:left;}.yui-g .yui-u,.yui-g .yui-g,.yui-g .yui-gb,.yui-g .yui-gc,.yui-g .yui-gd,.yui-g .yui-ge,.yui-g .yui-gf{width:49.1%;}.yui-gb .yui-u,.yui-g .yui-gb .yui-u,.yui-gb .yui-g,.yui-gb .yui-gb,.yui-gb .yui-gc,.yui-gb .yui-gd,.yui-gb .yui-ge,.yui-gb .yui-gf,.yui-gc .yui-u,.yui-gc .yui-g,.yui-gd .yui-u{width:32%;margin-left:1.99%;}.yui-gb .yui-u{*margin-left:1.9%;*width:31.9%;}.yui-gc div.first,.yui-gd .yui-u{width:66%;}.yui-gd div.first{width:32%;}.yui-ge div.first,.yui-gf .yui-u{width:74.2%;}.yui-ge .yui-u,.yui-gf div.first{width:24%;}.yui-g .yui-gb div.first,.yui-gb div.first,.yui-gc div.first,.yui-gd div.first{margin-left:0;}.yui-g .yui-g .yui-u,.yui-gb .yui-g .yui-u,.yui-gc .yui-g .yui-u,.yui-gd .yui-g .yui-u,.yui-ge .yui-g .yui-u,.yui-gf .yui-g .yui-u{width:49%;*width:48.1%;*margin-left:0;}.yui-g .yui-g .yui-u{width:48.1%;}.yui-g .yui-gb div.first,.yui-gb .yui-gb div.first{*margin-right:0;*width:32%;_width:31.7%;}.yui-g .yui-gc div.first,.yui-gd .yui-g{width:66%;}.yui-gb .yui-g div.first{*margin-right:4%;_margin-right:1.3%;}.yui-gb .yui-gc div.first,.yui-gb .yui-gd div.first{*margin-right:0;}.yui-gb .yui-gb .yui-u,.yui-gb .yui-gc .yui-u{*margin-left:1.8%;_margin-left:4%;}.yui-g .yui-gb .yui-u{_margin-left:1.0%;}.yui-gb .yui-gd .yui-u{*width:66%;_width:61.2%;}.yui-gb .yui-gd div.first{*width:31%;_width:29.5%;}.yui-g .yui-gc .yui-u,.yui-gb .yui-gc .yui-u{width:32%;_float:right;margin-right:0;_margin-left:0;}.yui-gb .yui-gc div.first{width:66%;*float:left;*margin-left:0;}.yui-gb .yui-ge .yui-u,.yui-gb .yui-gf .yui-u{margin:0;}.yui-gb .yui-gb .yui-u{_margin-left:.7%;}.yui-gb .yui-g div.first,.yui-gb .yui-gb div.first{*margin-left:0;}.yui-gc .yui-g .yui-u,.yui-gd .yui-g .yui-u{*width:48.1%;*margin-left:0;}.yui-gb .yui-gd div.first{width:32%;}.yui-g .yui-gd div.first{_width:29.9%;}.yui-ge .yui-g{width:24%;}.yui-gf .yui-g{width:74.2%;}.yui-gb .yui-ge div.yui-u,.yui-gb .yui-gf div.yui-u{float:right;}.yui-gb .yui-ge div.first,.yui-gb .yui-gf div.first{float:left;}.yui-gb .yui-ge .yui-u,.yui-gb .yui-gf div.first{*width:24%;_width:20%;}.yui-gb .yui-ge div.first,.yui-gb .yui-gf .yui-u{*width:73.5%;_width:65.5%;}.yui-ge div.first .yui-gd .yui-u{width:65%;}.yui-ge div.first .yui-gd div.first{width:32%;}#hd:after,#bd:after,#ft:after,.yui-g:after,.yui-gb:after,.yui-gc:after,.yui-gd:after,.yui-ge:after,.yui-gf:after{content:".";display:block;height:0;clear:both;visibility:hidden;}#hd,#bd,#ft,.yui-g,.yui-gb,.yui-gc,.yui-gd,.yui-ge,.yui-gf{zoom:1;}
data/static/css/screen.css view
@@ -1,7 +1,7 @@ @import url("reset-fonts-grids.css");-@import url("base-min.css");  html { background: #f9f9f9; color: black; } +body { margin: 10px; }  fieldset { border: 1px solid #ccc; padding: 1em; } legend { font-weight: bold; margin-left: 1em; padding: 4px; }@@ -27,9 +27,26 @@ h5 { font-size: 108%; margin: 1.6em 0 .8em; } h6 { font-size: 100%; margin: 1.6em 0 .8em; } +strong { font-weight: bold; } ul { list-style-type: square; } dt { font-weight: bold; margin-bottom: .1em; } +optgroup{ font-weight: normal; }+abbr, acronym { border-bottom:1px dotted #000; cursor:help; }+em { font-style: italic; }+del { text-decoration: line-through; }++blockquote, ul, ol, dl { margin: 1em; }+ol, ul, dl { margin-left: 2em; }+dl dd { margin-left: 1em; }+th, td { padding: .5em; }+th { font-weight: bold; }+caption { margin-bottom: .5em; text-align: center; }+sup { vertical-align: super; }+sub { vertical-align: sub; }+p, fieldset, table, pre { margin-bottom: 1em; }+button, input[type="checkbox"], input[type="radio"], input[type="reset"], input[type="submit"] { padding: 1px; }+ blockquote { padding: 0 1.6em; color: #666; }  a:link { text-decoration: underline; color: #36c; }@@ -90,6 +107,8 @@  code { font-size: 93%; } pre.matches { margin: 0; padding: 0; }+#pattern { background-color: yellow; font-weight: bold; }+pre.matches span.highlighted { background-color: yellow; }  .added { background-color: yellow; } .deleted { text-decoration: line-through; color: gray; }
data/static/js/search.js view
@@ -1,5 +1,20 @@+jQuery.fn.highlightPattern = function (patt, className)+{+    // patt is a space separated list of strings - we want to highlight+    // an occurrence of any of these strings as a separate word.+    var regex = new RegExp('\\b(' + patt.replace(/ /, '|') + ')\\b', 'g');++    return this.each(function ()+    {+        this.innerHTML = this.innerHTML.replace(regex,+          '<span class=\'' + className + '\'>' + '$1' + '</span>');+    });+}; function toggleMatches(obj) {-  obj.next('.matches').slideToggle(300);+  var pattern = $('#pattern').text();+  var matches = obj.next('.matches')+  matches.slideToggle(300);+  matches.highlightPattern(pattern, 'highlighted');   if (obj.html() == '[show matches]') {       obj.html('[hide matches]');     } else {@@ -7,7 +22,7 @@     };   } $(function() {-  $('a.showmatch').attr("onClick", "toggleMatches($(this));");+  $('a.showmatch').attr('onClick', 'toggleMatches($(this));');   $('pre.matches').hide();   $('a.showmatch').show();   });
data/templates/page.st view
@@ -4,10 +4,10 @@   <head>     <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />     $if(feed)$-    <link href="$base$/_feed" type="application/atom+xml" rel="alternate" title="Sitewide ATOM Feed" />+    <link href="$base$/_feed/" type="application/atom+xml" rel="alternate" title="Sitewide ATOM Feed" />     <link href="$base$/_feed$pageUrl$" type="application/atom+xml" rel="alternate" title="This page's ATOM Feed" />     $endif$-    <title>Wiki - $pagetitle$</title>+    <title>$wikititle$ - $pagetitle$</title>     $if(printable)$     <link href="$base$/css/print.css" rel="stylesheet" media="all" type= "text/css" />     $else$
data/templates/sitenav.st view
@@ -9,7 +9,7 @@       <li><a href="$base$/_activity">Recent activity</a></li>       <li><a href="$base$/_upload">Upload a file</a></li>       $if(feed)$-      <li><a href="$base$/_feed" type="application/atom+xml" rel="alternate" title="ATOM Feed">Atom feed</a> <img alt="feed icon" src="$base$/img/icons/feed.png"/></li>+      <li><a href="$base$/_feed/" type="application/atom+xml" rel="alternate" title="ATOM Feed">Atom feed</a> <img alt="feed icon" src="$base$/img/icons/feed.png"/></li>       $endif$       <li><a href="$base$/Help">Help</a></li>     </ul>
gitit.cabal view
@@ -1,10 +1,10 @@ name:                gitit-version:             0.6.6+version:             0.7 Cabal-version:       >= 1.2 build-type:          Simple synopsis:            Wiki using happstack, git or darcs, and pandoc.-description:         Gitit is a wiki backed by a git or darcs filestore.-                     Pages and uploaded files can be modified either+description:         Gitit is a wiki backed by a git, darcs, or mercurial+                     filestore.  Pages and uploaded files can be modified either                      directly via the VCS's command-line tools or through                      the wiki's web interface. Pandoc is used for markup                      processing, so pages may be written in@@ -41,10 +41,10 @@ maintainer:          jgm@berkeley.edu bug-reports:         http://code.google.com/p/gitit/issues/list homepage:            http://github.com/jgm/gitit/tree/master-stability:           experimental +stability:           experimental data-files:          data/static/css/screen.css, data/static/css/print.css,                      data/static/css/ie.css, data/static/css/hk-pyg.css,-                     data/static/css/reset-fonts-grids.css, data/static/css/base-min.css,+                     data/static/css/reset-fonts-grids.css,                      data/static/css/custom.css,                      data/static/img/logo.png, data/static/img/icons/feed.png,                      data/static/img/icons/folder.png, data/static/img/icons/page.png,@@ -98,7 +98,7 @@     exposed-modules: Network.Gitit.Interface     build-depends:   ghc, ghc-paths     cpp-options:     -D_PLUGINS-  build-depends:     base >= 3, pandoc >= 1.1, filepath, safe+  build-depends:     base >= 3, pandoc >= 1.3, filepath, safe   ghc-options:       -Wall   ghc-prof-options:  -auto-all -caf-all @@ -109,9 +109,9 @@                      pandoc >= 1.2, process, filepath, directory, mtl, cgi,                      network, old-time, highlighting-kate, bytestring,                      utf8-string, SHA > 1, HTTP, HStringTemplate, random,-                     network >= 2.1.0.0, recaptcha >= 0.1, filestore >= 0.3.3,-                     datetime, zlib, url, happstack-server >= 0.3.3 && < 0.4,-                     happstack-util >= 0.3.2 && < 0.4, xml >= 1.3.4,+                     network >= 2.1.0.0, recaptcha >= 0.1, filestore >= 0.3.4,+                     datetime, zlib, url, happstack-server >= 0.3.3 && < 0.5,+                     happstack-util >= 0.3.2 && < 0.5, xml >= 1.3.5,                      hslogger >= 1 && < 1.1, ConfigFile >= 1, feed >= 0.3.6,                      cautious-file >= 0.1.5 && < 0.2, texmath   if impl(ghc >= 6.10)
gitit.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE CPP #-} {- Copyright (C) 2008 John MacFarlane <jgm@berkeley.edu> @@ -23,22 +24,34 @@ import Network.Gitit.Initialize (createStaticIfMissing, createRepoIfMissing) import Prelude hiding (writeFile, readFile, catch) import System.Directory-import Network.Gitit.Config (getConfigFromOpts)+import Network.Gitit.Config (getConfigFromFile) import Data.Maybe (isNothing) import Control.Monad.Reader import System.Log.Logger (Priority(..), setLevel, setHandlers,         getLogger, saveGlobalLogger) import System.Log.Handler.Simple (fileHandler)-import Data.Char (toLower)+import System.Environment+import System.Exit+import System.IO (stdout, stderr)+import System.Console.GetOpt+import Data.Version (showVersion)+import Prelude hiding (readFile)+import System.IO.UTF8+import Paths_gitit (version, getDataFileName)  main :: IO () main = do    -- parse options to get config file-  conf <- getConfigFromOpts+  opts <- getArgs >>= parseArgs+  defaultConfig <- getDefaultConfig+  conf <- foldM handleFlag defaultConfig opts    -- check for external programs that are needed-  let repoProg = map toLower $ show $ repositoryType conf+  let repoProg = case repositoryType conf of+                       Mercurial   -> "hg"+                       Darcs       -> "darcs"+                       Git         -> "git"   let prereqs = ["grep", repoProg]   forM_ prereqs $ \prog ->     findExecutable prog >>= \mbFind ->@@ -64,8 +77,71 @@   initializeGititState conf'    let serverConf = Conf { validator = Nothing, port = portNumber conf' }+   -- start the server-  simpleHTTP serverConf $ msum [-      wiki conf'-    , dir "_reloadTemplates" reloadTemplates-    ]+  simpleHTTP serverConf $ msum [ wiki conf'+                               , dir "_reloadTemplates" reloadTemplates+                               ]++data Opt+    = Help+    | ConfigFile FilePath+    | Port Int+    | Debug+    | Version+    | PrintDefaultConfig+    deriving (Eq)++flags :: [OptDescr Opt]+flags =+   [ Option ['h'] ["help"] (NoArg Help)+        "Print this help message"+   , Option ['v'] ["version"] (NoArg Version)+        "Print version information"+   , Option ['p'] ["port"] (ReqArg (Port . read) "PORT")+        "Specify port"+   , Option [] ["print-default-config"] (NoArg PrintDefaultConfig)+        "Print default configuration"+   , Option [] ["debug"] (NoArg Debug)+        "Print debugging information on each request"+   , Option ['f'] ["config-file"] (ReqArg ConfigFile "FILE")+        "Specify configuration file"+   ]++parseArgs :: [String] -> IO [Opt]+parseArgs argv = do+  progname <- getProgName+  case getOpt Permute flags argv of+    (opts,_,[])  -> return opts+    (_,_,errs)   -> hPutStrLn stderr (concat errs ++ usageInfo (usageHeader progname) flags) >>+                       exitWith (ExitFailure 1)++usageHeader :: String -> String+usageHeader progname = "Usage:  " ++ progname ++ " [opts...]"++copyrightMessage :: String+copyrightMessage = "\nCopyright (C) 2008 John MacFarlane\n" +++                   "This is free software; see the source for copying conditions.  There is no\n" +++                   "warranty, not even for merchantability or fitness for a particular purpose."++compileInfo :: String+compileInfo =+#ifdef _PLUGINS+  " +plugins"+#else+  " -plugins"+#endif++handleFlag :: Config -> Opt -> IO Config+handleFlag conf opt = do+  progname <- getProgName+  case opt of+    Help               -> hPutStrLn stderr (usageInfo (usageHeader progname) flags) >> exitWith ExitSuccess+    Version            -> hPutStrLn stderr (progname ++ " version " ++ showVersion version ++ compileInfo ++ copyrightMessage) >> exitWith ExitSuccess+    PrintDefaultConfig -> getDataFileName "data/default.conf" >>= readFile >>=+                          hPutStrLn stdout >> exitWith ExitSuccess+    Debug              -> return conf{ debugMode = True }+    Port p             -> return conf{ portNumber = p }+    ConfigFile fname   -> getConfigFromFile fname++
plugins/Interwiki.hs view
@@ -49,12 +49,20 @@     _ -> Link ref (interwiki, article) convertInterwikiLinks x = x -{- | Large table of constants; this is a mapping from shortcuts to a URL. The URL can be used by-   appending to it the article name (suitably URL-escaped, of course).-   The mapping is derived from <https://secure.wikimedia.org/wikipedia/meta/wiki/Interwiki_map>-   as of 11:19 PM, 11 February 2009. -}+-- | Large table of constants; this is a mapping from shortcuts to a URL. The URL can be used by+--   appending to it the article name (suitably URL-escaped, of course). interwikiMap :: M.Map String String-interwikiMap = M.fromList [ ("AbbeNormal", "http://ourpla.net/cgi/pikie?"),+interwikiMap = M.fromList $ wpInterwikiMap ++ customInterwikiMap++wpInterwikiMap, customInterwikiMap :: [(String, String)]+customInterwikiMap = [("Hackage", "http://hackage.haskell.org/package/"),+                      ("Hawiki", "http://haskell.org/haskellwiki/"),+                      ("Hayoo", "http://holumbus.fh-wedel.de/hayoo/hayoo.html#0:"),+                      ("Hoogle", "http://www.haskell.org/hoogle/?hoogle=")]++-- This mapping is derived from <https://secure.wikimedia.org/wikipedia/meta/wiki/Interwiki_map>+-- as of 11:19 PM, 11 February 2009.+wpInterwikiMap = [ ("AbbeNormal", "http://ourpla.net/cgi/pikie?"),                  ("Acronym", "http://www.acronymfinder.com/af-query.asp?String=exact&Acronym="),                  ("Advisory", "http://advisory.wikimedia.org/wiki/"),                  ("Advogato", "http://www.advogato.org/"),
plugins/WebArchiver.hs view
@@ -1,22 +1,27 @@--- | Scans page of Markdown looking for http links. When it finds them, it submits them--- to webcitation.org / https://secure.wikimedia.org/wikipedia/en/wiki/WebCite------ Limitations:--- * Only parses Markdown, not ReST or any other format; this is because 'readMarkdown'--- is hardwired into it.------ By: Gwern Branwen; placed in the public domain+{-| Scans page of Markdown looking for http links. When it finds them, it submits them+to webcitation.org / https://secure.wikimedia.org/wikipedia/en/wiki/WebCite+(It will also submit them to Alexa (the source for the Internet Archive), but Alexa says that+its bots take weeks to visit and may not ever.) +Limitations:+* Only parses Markdown, not ReST or any other format; this is because 'readMarkdown'+is hardwired into it.++By: Gwern Branwen; placed in the public domain -}+ module WebArchiver (plugin) where -import Network.Gitit.Interface (askUser, liftIO, processWithM, uEmail, Plugin(PreCommitTransform), Inline(Link))+import Control.Concurrent (forkIO, ThreadId) import Control.Monad (when)-import Network.URI (isURI)-import Control.Concurrent (forkIO)-import Network.HTTP (getRequest, simpleHTTP)-import Text.Pandoc (defaultParserState, readMarkdown) import Control.Monad.Trans (MonadIO)+import Data.Maybe (fromJust)+import Network.Browser (browse, formToRequest, request, Form(..))+import Network.HTTP (getRequest, rspBody, simpleHTTP, RequestMethod(POST))+import Network.URI (isURI, parseURI, uriPath) +import Network.Gitit.Interface (askUser, liftIO, processWithM, uEmail, Plugin(PreCommitTransform), Inline(Link))+import Text.Pandoc (defaultParserState, readMarkdown)+ plugin :: Plugin plugin = PreCommitTransform archivePage @@ -32,12 +37,20 @@  archiveLinks :: String -> Inline -> IO Inline archiveLinks e x@(Link _ (uln, _)) = checkArchive e uln >> return x-archiveLinks _ x = return x+archiveLinks _ x                   = return x --- | Error check the URL.+-- | Error check the URL and then archive it both ways checkArchive :: (MonadIO m) => String -> String -> m ()-checkArchive e u = when (isURI u) (liftIO $ archiveURL e u)+checkArchive email url = when (isURI url) $ liftIO (webciteArchive email url >> alexaArchive url) -archiveURL :: String -> String -> IO ()-archiveURL eml url = forkIO (openURL ("http://www.webcitation.org/archive?url=" ++ url ++ "&email=" ++ eml) >> return ()) >> return ()+webciteArchive :: String -> String -> IO ThreadId+webciteArchive email url = forkIO (ignore $ openURL ("http://www.webcitation.org/archive?url=" ++ url ++ "&email=" ++ email))    where openURL = simpleHTTP . getRequest+         ignore = fmap $ const ()++alexaArchive :: String -> IO ()+alexaArchive url = do let archiveform = Form POST (fromJust $ parseURI "http://www.alexa.com/help/crawlrequest")+                                                                             [("url", url), ("submit", "")]+                      (uri, resp) <- browse $ request $ formToRequest archiveform+                      when (uriPath uri /= "/help/crawlthanks") $+                           error $ "Request failed! Did Alexa change webpages? Response:" ++ rspBody resp