packages feed

gitit 0.7.3.12 → 0.8

raw patch · 26 files changed

+641/−395 lines, 26 filesdep +jsondep ~happstack-serverdep ~happstack-utilPVP ok

version bump matches the API change (PVP)

Dependencies added: json

Dependency ranges changed: happstack-server, happstack-util

API changes (from Hackage documentation)

- Network.Gitit.Framework: requireUser :: Handler -> Handler
- Network.Gitit.Framework: requireUserThat :: (User -> Bool) -> Handler -> Handler
- Network.Gitit.Framework: withInput :: ServerMonad m => String -> String -> m a -> m a
- Network.Gitit.Interface: rqInputs :: Request -> [(String, Input)]
- Network.Gitit.Types: pFileContents :: Params -> ByteString
+ Network.Gitit.Authentication: formAuthHandlers :: [Handler]
+ Network.Gitit.Authentication: httpAuthHandlers :: [Handler]
+ Network.Gitit.Authentication: instance FromData RPars
+ Network.Gitit.Authentication: instance Read ValidationType
+ Network.Gitit.Authentication: instance Show RPars
+ Network.Gitit.Authentication: instance Show ValidationType
+ Network.Gitit.Authentication: loginUserForm :: Handler
+ Network.Gitit.Authentication: rpxAuthHandlers :: [Handler]
+ Network.Gitit.Framework: authenticate :: AuthenticationLevel -> Handler -> Handler
+ Network.Gitit.Framework: authenticateUserThat :: (User -> Bool) -> AuthenticationLevel -> Handler -> Handler
+ Network.Gitit.Interface: requireAuthentication :: Config -> AuthenticationLevel
+ Network.Gitit.Interface: rpxDomain :: Config -> String
+ Network.Gitit.Interface: rpxKey :: Config -> String
+ Network.Gitit.Interface: rqInputsBody :: Request -> MVar [(String, Input)]
+ Network.Gitit.Interface: rqInputsQuery :: Request -> [(String, Input)]
+ Network.Gitit.Types: ForModify :: AuthenticationLevel
+ Network.Gitit.Types: ForRead :: AuthenticationLevel
+ Network.Gitit.Types: Never :: AuthenticationLevel
+ Network.Gitit.Types: data AuthenticationLevel
+ Network.Gitit.Types: instance Eq AuthenticationLevel
+ Network.Gitit.Types: instance Ord AuthenticationLevel
+ Network.Gitit.Types: instance Read AuthenticationLevel
+ Network.Gitit.Types: instance Show AuthenticationLevel
+ Network.Gitit.Types: pFilePath :: Params -> FilePath
+ Network.Gitit.Types: requireAuthentication :: Config -> AuthenticationLevel
+ Network.Gitit.Types: rpxDomain :: Config -> String
+ Network.Gitit.Types: rpxKey :: Config -> String
- Network.Gitit.Interface: Config :: FilePath -> FileStoreType -> PageType -> MathMethod -> Bool -> Bool -> (Handler -> Handler) -> Handler -> FilePath -> Int -> FilePath -> FilePath -> Priority -> FilePath -> [String] -> Bool -> Integer -> Integer -> Int -> Bool -> String -> [String] -> [String] -> String -> Maybe (String, [String]) -> Bool -> String -> String -> Bool -> Bool -> FilePath -> Map String String -> String -> String -> String -> Bool -> String -> String -> Integer -> Integer -> Bool -> Maybe FilePath -> Config
+ Network.Gitit.Interface: Config :: FilePath -> FileStoreType -> PageType -> MathMethod -> Bool -> Bool -> (Handler -> Handler) -> AuthenticationLevel -> Handler -> FilePath -> Int -> FilePath -> FilePath -> Priority -> FilePath -> [String] -> Bool -> Integer -> Integer -> Int -> Bool -> String -> [String] -> [String] -> String -> Maybe (String, [String]) -> Bool -> String -> String -> String -> String -> Bool -> Bool -> FilePath -> Map String String -> String -> String -> String -> Bool -> String -> String -> Integer -> Integer -> Bool -> Maybe FilePath -> Config
- Network.Gitit.Interface: Request :: Method -> [String] -> String -> String -> [(String, Input)] -> [(String, Cookie)] -> Version -> Headers -> RqBody -> Host -> Request
+ Network.Gitit.Interface: Request :: Method -> [String] -> String -> String -> [(String, Input)] -> MVar [(String, Input)] -> [(String, Cookie)] -> HttpVersion -> Headers -> MVar RqBody -> Host -> Request
- Network.Gitit.Interface: rqBody :: Request -> RqBody
+ Network.Gitit.Interface: rqBody :: Request -> MVar RqBody
- Network.Gitit.Interface: rqVersion :: Request -> Version
+ Network.Gitit.Interface: rqVersion :: Request -> HttpVersion
- Network.Gitit.Types: Config :: FilePath -> FileStoreType -> PageType -> MathMethod -> Bool -> Bool -> (Handler -> Handler) -> Handler -> FilePath -> Int -> FilePath -> FilePath -> Priority -> FilePath -> [String] -> Bool -> Integer -> Integer -> Int -> Bool -> String -> [String] -> [String] -> String -> Maybe (String, [String]) -> Bool -> String -> String -> Bool -> Bool -> FilePath -> Map String String -> String -> String -> String -> Bool -> String -> String -> Integer -> Integer -> Bool -> Maybe FilePath -> Config
+ Network.Gitit.Types: Config :: FilePath -> FileStoreType -> PageType -> MathMethod -> Bool -> Bool -> (Handler -> Handler) -> AuthenticationLevel -> Handler -> FilePath -> Int -> FilePath -> FilePath -> Priority -> FilePath -> [String] -> Bool -> Integer -> Integer -> Int -> Bool -> String -> [String] -> [String] -> String -> Maybe (String, [String]) -> Bool -> String -> String -> String -> String -> Bool -> Bool -> FilePath -> Map String String -> String -> String -> String -> Bool -> String -> String -> Integer -> Integer -> Bool -> Maybe FilePath -> Config
- Network.Gitit.Types: Params :: String -> String -> String -> Maybe String -> String -> Maybe String -> Maybe UTCTime -> String -> Int -> [String] -> String -> String -> Maybe String -> [String] -> Maybe String -> Maybe String -> String -> String -> String -> String -> String -> String -> String -> Bool -> Bool -> String -> ByteString -> Bool -> Maybe SessionKey -> Recaptcha -> String -> Params
+ Network.Gitit.Types: Params :: String -> String -> String -> Maybe String -> String -> Maybe String -> Maybe UTCTime -> String -> Int -> [String] -> String -> String -> Maybe String -> [String] -> Maybe String -> Maybe String -> String -> String -> String -> String -> String -> String -> String -> Bool -> Bool -> String -> FilePath -> Bool -> Maybe SessionKey -> Recaptcha -> String -> Params

Files

CHANGES view
@@ -1,3 +1,42 @@+Version 0.8 released 13 May 2011++* Uses happstack 6.++* Added textile and org export formats, textile page format.++* Added support for RPXNow authentication, based on a patch from+  Pasqualino Titto Assini.++* Added `authentication-required` field in config.++    * If set to 'modify', authentication is required to modify the wiki.+    * If set to 'read', atuhentication is required to view the wiki.+    * If set to 'none', authentication is never required, and pages can+      be edited anonymously.++    API changes:++    * currentUser moved to Authentication module+    * requireAuthentication added to Config+    * Added AuthenticationLevel type+    * requireUser renamed authenticate, parameter for AuthenticationLevel+      added; requireUserThat renamed authenticateUserThat++* MathML and jsMath now work in the preview pane (Sean Seefried).++* Use footnotes.js for fancy footnote styling (gwern).++* Added a `--listen` parameter to specify the listen device+  (Timo B. Hübel).++* Removed withInput.++* Replaced fileContents with filePath in Params.++* Fixed bug in uploadForm.js which caused a prefix to be added in the+  default wikiname.++ Version 0.7.3.12 released 01 Feb 2011  * Use pandoc 1.8.
Network/Gitit.hs view
@@ -22,7 +22,7 @@  > import Network.Gitit > import Happstack.Server.SimpleHTTP-> +> > main = do >   conf <- getDefaultConfig >   createStaticIfMissing conf@@ -38,12 +38,12 @@ > import Control.Monad > import Text.XHtml hiding (dir) > import Happstack.Server.SimpleHTTP-> +> > type WikiSpec = (String, FileStoreType, PageType)-> +> > wikis = [ ("markdownWiki", Git, Markdown) >         , ("latexWiki", Darcs, LaTeX) ]-> +> > -- custom authentication > myWithUser :: Handler -> Handler > myWithUser handler = do@@ -66,7 +66,7 @@ > indexPage = ok $ toResponse $ >   (p << "Wiki index") +++ >   ulist << map (\(path', _, _) -> li << hotlink (path' ++ "/") << path') wikis-> +> > main = do >   conf <- getDefaultConfig >   let conf' = conf{authHandler = myAuthHandler, withUser = myWithUser}@@ -125,31 +125,37 @@ import Prelude hiding (readFile) import qualified Data.ByteString.Char8 as B import System.FilePath ((</>))+import System.Directory (getTemporaryDirectory) import Safe  -- | Happstack handler for a gitit wiki. wiki :: Config -> ServerPart Response wiki conf = do+  tempDir <- liftIO getTemporaryDirectory+  let maxSize = fromIntegral $ maxUploadSize conf+  decodeBody $ defaultBodyPolicy tempDir maxSize maxSize maxSize   let static = staticDir conf   defaultStatic <- liftIO $ getDataFileName $ "data" </> "static"   -- if file not found in staticDir, we check also in the data/static   -- directory, which contains defaults   let staticHandler = withExpiresHeaders $-        fileServeStrict' [] static `mplus` fileServeStrict' [] defaultStatic-  let handlers = [debugHandler | debugMode conf] ++ (authHandler conf : wikiHandlers)+        serveDirectory' static `mplus` serveDirectory' defaultStatic+  let debugHandler' = msum [debugHandler | debugMode conf]+  let handlers = debugHandler' `mplus` authHandler conf `mplus`+                 authenticate ForRead (msum wikiHandlers)   let fs = filestoreFromConfig conf   let ws = WikiState { wikiConfig = conf, wikiFileStore = fs }   if compressResponses conf      then compressedResponseFilter      else return ""-  staticHandler `mplus` runHandler ws (withUser conf $ msum handlers)+  staticHandler `mplus` runHandler ws (withUser conf handlers) --- | Like 'fileServeStrict', but if file is not found, fail instead of+-- | Like 'serveDirectory', but if file is not found, fail instead of -- returning a 404 error.-fileServeStrict' :: [FilePath] -> FilePath -> ServerPart Response-fileServeStrict' ps p = do+serveDirectory' :: FilePath -> ServerPart Response+serveDirectory' p = do   rq <- askRq-  resp' <- fileServeStrict ps p+  resp' <- serveDirectory EnableBrowsing [] p   if rsCode resp' == 404 || lastNote "fileServeStrict'" (rqUri rq) == '/'      then mzero  -- pass through if not found or directory index      else do@@ -163,13 +169,12 @@   [ -- redirect /wiki -> /wiki/ when gitit is being served at /wiki     -- so that relative wikilinks on the page will work properly:     guardBareBase >> getWikiBase >>= \b -> movedPermanently (b ++ "/") (toResponse ())-  , dir "_user"     currentUser   , dir "_activity" showActivity   , dir "_go"       goToPage   , dir "_search"   searchResults   , dir "_upload"   $  do guard =<< return . uploadsAllowed =<< getConfig-                          msum [ methodOnly GET  >> requireUser uploadForm -                                 , methodOnly POST >> requireUser uploadFile ]+                          msum [ methodOnly GET  >> authenticate ForModify uploadForm+                                 , methodOnly POST >> authenticate ForModify uploadFile ]   , dir "_random"   $ methodOnly GET  >> randomPage   , dir "_index"    indexPage   , dir "_feed"     feedHandler@@ -182,22 +187,22 @@   , dir "_history"  $ msum       [ showPageHistory       , guardPath isSourceCode >> showFileHistory ]-  , dir "_edit" $ requireUser (unlessNoEdit editPage showPage)+  , dir "_edit" $ authenticate ForModify (unlessNoEdit editPage showPage)   , dir "_diff" $ msum       [ showPageDiff       , guardPath isSourceCode >> showFileDiff ]   , dir "_discuss" discussPage   , dir "_delete" $ msum       [ methodOnly GET  >>-          requireUser (unlessNoDelete confirmDelete showPage)+          authenticate ForModify (unlessNoDelete confirmDelete showPage)       , methodOnly POST >>-          requireUser (unlessNoDelete deletePage showPage) ]+          authenticate ForModify (unlessNoDelete deletePage showPage) ]   , dir "_preview" preview   , guardIndex >> indexPage   , guardCommand "export" >> exportPage   , methodOnly POST >> guardCommand "cancel" >> showPage   , methodOnly POST >> guardCommand "update" >>-      requireUser (unlessNoEdit updatePage showPage)+      authenticate ForModify (unlessNoEdit updatePage showPage)   , showPage   , guardPath isSourceCode >> methodOnly GET >> showHighlightedSource   , handleAny@@ -214,8 +219,6 @@ runHandler :: WikiState -> Handler -> ServerPart Response runHandler = mapServerPartT . unpackReaderT -unpackReaderT:: (Monad m)-    => c -    -> (ReaderT c m) (Maybe ((Either b a), FilterFun b))-    -> m (Maybe ((Either b a), FilterFun b))-unpackReaderT st handler = runReaderT handler st+unpackReaderT :: s -> UnWebT (ReaderT s IO) a -> UnWebT IO a+unpackReaderT st uw = runReaderT uw st+
Network/Gitit/Authentication.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE ScopedTypeVariables, StandaloneDeriving #-} {- Copyright (C) 2009 John MacFarlane <jgm@berkeley.edu>,                    Henry Laxen <nadine.and.henry@pobox.com>@@ -20,7 +21,10 @@ {- Handlers for registering and authenticating users. -} -module Network.Gitit.Authentication (formAuthHandlers, httpAuthHandlers, loginUserForm) where+module Network.Gitit.Authentication ( loginUserForm+                                    , formAuthHandlers+                                    , httpAuthHandlers+                                    , rpxAuthHandlers) where  import Network.Gitit.State import Network.Gitit.Types@@ -32,18 +36,21 @@ import Text.XHtml hiding ( (</>), dir, method, password, rev ) import qualified Text.XHtml as X ( password ) import System.Process (readProcessWithExitCode)-import Control.Monad (unless, liftM)+import Control.Monad (unless, liftM, mplus) import Control.Monad.Trans (MonadIO(), liftIO) import System.Exit import System.Log.Logger (logM, Priority(..)) import Data.Char (isAlphaNum, isAlpha, isAscii)+import qualified Data.Map as M import Text.Pandoc.Shared (substitute)-import Data.Maybe (isJust, fromJust)+import Data.Maybe (isJust, fromJust, isNothing, fromMaybe) import Network.URL (encString, exportURL, add_param, importURL) import Network.BSD (getHostName) import qualified Text.StringTemplate as T-import Network.HTTP (urlEncodeVars)-import Codec.Binary.UTF8.String (encodeString) +import Network.HTTP (urlEncodeVars, urlDecode, urlEncode)+import Codec.Binary.UTF8.String (encodeString)+import Data.ByteString.UTF8 (toString)+import Network.Gitit.Rpxnow as R  data ValidationType = Register                     | ResetPassword@@ -111,7 +118,7 @@                     response     else registerForm >>=          formattedPage defaultPageLayout{-                         pgMessages = errors, +                         pgMessages = errors,                          pgShowPageTools = False,                          pgTabs = [],                          pgTitle = "Register for an account"@@ -155,7 +162,7 @@   let errors = case (knownUser, resetCodeMatches) of                      (True, True)   -> []                      (True, False)  -> ["Your reset code is invalid"]-                     (False, _)     -> ["User " ++ uname ++ " is not known"] +                     (False, _)     -> ["User " ++ uname ++ " is not known"]   if null errors      then postValidate (fromJust user)      else registerForm >>=@@ -366,7 +373,7 @@   if allowed     then do       key <- newSession (SessionData uname)-      addCookie (sessionTimeout cfg) (mkCookie "sid" (show key))+      addCookie (MaxAge $ sessionTimeout cfg) (mkCookie "sid" (show key))       seeOther (encUrl destination) $ toResponse $ p << ("Welcome, " ++ uname)     else       withMessages ["Invalid username or password."] loginUserForm@@ -383,8 +390,7 @@   case key of        Just k  -> do          delSession k-         -- make cookie expire immediately, effectively deleting it-         addCookie 0 (mkCookie "sid" "-1")+         expireCookie "sid"        Nothing -> return ()   seeOther (encUrl dest) $ toResponse "You have been logged out." @@ -407,6 +413,7 @@   , dir "_resetPassword"   $ methodSP POST $ withData resetPasswordRequest   , dir "_doResetPassword" $ methodSP GET  $ withData resetPassword   , dir "_doResetPassword" $ methodSP POST $ withData doResetPassword+  , dir "_user" currentUser   ]  loginUserHTTP :: Params -> Handler@@ -421,4 +428,72 @@ httpAuthHandlers :: [Handler] httpAuthHandlers =   [ dir "_logout" $ logoutUserHTTP-  , dir "_login"  $ withData loginUserHTTP ]+  , dir "_login"  $ withData loginUserHTTP+  , dir "_user" currentUser ]++-- Login using RPX (see RPX development docs at https://rpxnow.com/docs)+loginRPXUser :: RPars  -- ^ The parameters passed by the RPX callback call (after authentication has taken place+             -> Handler+loginRPXUser params = do+  cfg <- getConfig+  ref <- getReferer+  let mtoken = rToken params+  if isNothing mtoken+     then do+       let url = baseUrl cfg ++ "/_login?destination=" +++                  (fromMaybe ref $ rDestination params)+       if null (rpxDomain cfg)+          then error "rpx-domain is not set."+          else do+             let rpx = "https://" ++ rpxDomain cfg +++                       ".rpxnow.com/openid/v2/signin?token_url=" +++                       urlEncode url+             see rpx+     else do -- We got an answer from RPX, this might also return an exception.+       uid' :: Either String R.Identifier <- liftIO $+                      R.authenticate (rpxKey cfg) $ fromJust mtoken+       uid <- case uid' of+                   Right u -> return u+                   Left err -> error err+       liftIO $ logM "gitit.loginRPXUser" DEBUG $ "uid:" ++ show uid+       -- We need to get an unique identifier for the user+       -- The 'identifier' is always present but can be rather cryptic+       -- The 'verifiedEmail' is also unique and is a more readable choice+       -- so we use it if present.+       let userId = R.userIdentifier uid+       let email  = prop "verifiedEmail" uid+       user <- liftIO $ mkUser (fromMaybe userId email) (fromMaybe "" email) "none"+       updateGititState $ \s -> s { users = M.insert userId user (users s) }+       key <- newSession (SessionData userId)+       addCookie (MaxAge $ sessionTimeout cfg) (mkCookie "sid" (show key))+       see $ fromJust $ rDestination params+      where+        prop pname info = lookup pname $ R.userData info+        see url = seeOther (encUrl url) $ toResponse noHtml++-- The parameters passed by the RPX callback call.+data RPars = RPars { rToken       :: Maybe String+                   , rDestination :: Maybe String }+                   deriving Show++instance FromData RPars where+     fromData = do+         vtoken <- liftM Just (look "token") `mplus` return Nothing+         vDestination <- liftM (Just . urlDecode) (look "destination") `mplus`+                           return Nothing+         return RPars { rToken = vtoken+                      , rDestination = vDestination }++rpxAuthHandlers :: [Handler]+rpxAuthHandlers =+  [ dir "_logout" $ methodSP GET $ withData logoutUser+  , dir "_login"  $ withData loginRPXUser+  , dir "_user" currentUser ]++-- | Returns username of logged in user or null string if nobody logged in.+currentUser :: Handler+currentUser = do+  req <- askRq+  ok $ toResponse $ maybe "" toString (getHeader "REMOTE_USER" req)++
Network/Gitit/Config.hs view
@@ -27,7 +27,7 @@ import Network.Gitit.Types import Network.Gitit.Server (mimeTypes) import Network.Gitit.Framework-import Network.Gitit.Authentication (formAuthHandlers, httpAuthHandlers)+import Network.Gitit.Authentication (formAuthHandlers, rpxAuthHandlers, httpAuthHandlers) import Network.Gitit.Util (parsePageType, readFileUTF8) import System.Log.Logger (logM, Priority(..)) import qualified Data.Map as M@@ -66,6 +66,7 @@       cfDefaultPageType <- get cp "DEFAULT" "default-page-type"       cfMathMethod <- get cp "DEFAULT" "math"       cfShowLHSBirdTracks <- get cp "DEFAULT" "show-lhs-bird-tracks"+      cfRequireAuthentication <- get cp "DEFAULT" "require-authentication"       cfAuthenticationMethod <- get cp "DEFAULT" "authentication-method"       cfUserFile <- get cp "DEFAULT" "user-file"       cfSessionTimeout <- get cp "DEFAULT" "session-timeout"@@ -88,6 +89,8 @@       cfUseRecaptcha <- get cp "DEFAULT" "use-recaptcha"       cfRecaptchaPublicKey <- get cp "DEFAULT" "recaptcha-public-key"       cfRecaptchaPrivateKey <- get cp "DEFAULT" "recaptcha-private-key"+      cfRPXDomain <- get cp "DEFAULT" "rpx-domain"+      cfRPXKey <- get cp "DEFAULT" "rpx-key"       cfCompressResponses <- get cp "DEFAULT" "compress-responses"       cfUseCache <- get cp "DEFAULT" "use-cache"       cfCacheDir <- get cp "DEFAULT" "cache-dir"@@ -115,6 +118,8 @@                         "darcs"     -> Darcs                         "mercurial" -> Mercurial                         x           -> error $ "Unknown repository type: " ++ x+      when (authMethod == "rpx" && cfRPXDomain == "") $+         liftIO $ logM "gitit" WARNING $ "rpx-domain is not set"        return $! Config{           repositoryPath       = cfRepositoryPath@@ -130,10 +135,18 @@         , withUser             = case authMethod of                                       "form"     -> withUserFromSession                                       "http"     -> withUserFromHTTPAuth+                                      "rpx"      -> withUserFromSession                                       _          -> id+        , requireAuthentication = case map toLower cfRequireAuthentication of+                                       "none"    -> Never+                                       "modify"  -> ForModify+                                       "read"    -> ForRead+                                       _         -> ForModify+         , authHandler          = case authMethod of                                       "form"     -> msum formAuthHandlers                                       "http"     -> msum httpAuthHandlers+                                      "rpx"      -> msum rpxAuthHandlers                                       _          -> mzero         , userFile             = cfUserFile         , sessionTimeout       = readNumber "session-timeout" cfSessionTimeout * 60  -- convert minutes -> seconds@@ -162,6 +175,8 @@         , useRecaptcha         = cfUseRecaptcha         , recaptchaPublicKey   = cfRecaptchaPublicKey         , recaptchaPrivateKey  = cfRecaptchaPrivateKey+        , rpxDomain            = cfRPXDomain+        , rpxKey               = cfRPXKey         , compressResponses    = cfCompressResponses         , useCache             = cfUseCache         , cacheDir             = cfCacheDir
Network/Gitit/ContentTransformer.hs view
@@ -68,31 +68,30 @@   ) where -import Prelude hiding (catch)-import Network.Gitit.Server+import Control.Exception (throwIO, catch)+import Control.Monad.State+import Data.Maybe (isNothing, mapMaybe)+import Network.Gitit.Cache (lookupCache, cacheContents)+import Network.Gitit.Export (exportFormats) import Network.Gitit.Framework-import Network.Gitit.State-import Network.Gitit.Types import Network.Gitit.Layout-import Network.Gitit.Export (exportFormats) import Network.Gitit.Page (stringToPage)-import Network.Gitit.Cache (lookupCache, cacheContents)-import qualified Data.FileStore as FS-import Data.Maybe (mapMaybe)+import Network.Gitit.Server+import Network.Gitit.State+import Network.Gitit.Types+import Network.URI (isUnescapedInURI)+import Network.URL (encString)+import Prelude hiding (catch)+import System.FilePath+import Text.HTML.SanitizeXSS (sanitizeBalance)+import Text.Highlighting.Kate import Text.Pandoc hiding (MathML, WebTeX)-import qualified Text.Pandoc as Pandoc import Text.Pandoc.Shared (ObfuscationMethod(..)) import Text.XHtml hiding ( (</>), dir, method, password, rev )-import Text.Highlighting.Kate-import Data.Maybe (isNothing)-import System.FilePath-import Control.Monad.State-import Control.Exception (throwIO, catch)-import qualified Data.ByteString as S (concat) +import qualified Data.ByteString as S (concat) import qualified Data.ByteString.Lazy as L (toChunks, fromChunks)-import Network.URL (encString)-import Network.URI (isUnescapedInURI)-import Text.HTML.SanitizeXSS (sanitizeBalance)+import qualified Data.FileStore as FS+import qualified Text.Pandoc as Pandoc -- -- ContentTransformer runners --@@ -100,7 +99,7 @@ runTransformer :: ToMessage a                => (String -> String)                -> ContentTransformer a-               -> GititServerPart a +               -> GititServerPart a runTransformer pathFor xform = withData $ \params -> do   page <- getPage   cfg <- getConfig@@ -122,7 +121,7 @@ -- specialized to wiki pages. runPageTransformer :: ToMessage a                    => ContentTransformer a-                   -> GititServerPart a +                   -> GititServerPart a runPageTransformer = runTransformer pathForPage  -- | Converts a @ContentTransformer@ into a @GititServerPart@;@@ -150,7 +149,7 @@ showPage = runPageTransformer htmlViaPandoc  -- | Responds with page exported into selected format.-exportPage :: Handler +exportPage :: Handler exportPage = runPageTransformer exportViaPandoc  -- | Responds with highlighted source code.@@ -184,7 +183,7 @@ rawTextResponse = rawContents >>= textResponse  -- | Responds with a wiki page in the format specified--- by the @format@ parameter. +-- by the @format@ parameter. exportViaPandoc :: ContentTransformer Response exportViaPandoc = rawContents >>=                   maybe mzero return >>=@@ -212,7 +211,7 @@ highlightRawSource :: ContentTransformer Response highlightRawSource =   cachedHtml `mplus`-    (updateLayout (\l -> l { pgTabs = [ViewTab,HistoryTab] }) >> +    (updateLayout (\l -> l { pgTabs = [ViewTab,HistoryTab] }) >>      rawContents >>=      highlightSource >>=      applyWikiTemplate >>=@@ -223,8 +222,8 @@ --  -- | Caches a response (actually just the response body) on disk,--- unless the context indicates that the page is not cacheable. -cacheHtml :: Response -> ContentTransformer Response +-- unless the context indicates that the page is not cacheable.+cacheHtml :: Response -> ContentTransformer Response cacheHtml resp' = do   params <- getParams   file <- getFileName@@ -283,7 +282,7 @@ mimeResponse c mimeType =   return . setContentType mimeType . toResponse $ c --- | Converts Pandoc to response using format specified in parameters. +-- | Converts Pandoc to response using format specified in parameters. exportPandoc :: Pandoc -> ContentTransformer Response exportPandoc doc = do   params <- getParams@@ -403,7 +402,7 @@   return result'  -- | Applies all the page transform plugins to a Pandoc document.-applyPageTransforms :: Pandoc -> ContentTransformer Pandoc +applyPageTransforms :: Pandoc -> ContentTransformer Pandoc applyPageTransforms c = do   xforms <- getPageTransforms   foldM applyTransform c (wikiLinksTransform : xforms)@@ -488,7 +487,7 @@ -- Pandoc and wiki content conversion support -- -readerFor :: PageType -> Bool -> (String -> Pandoc)+readerFor :: PageType -> Bool -> String -> Pandoc readerFor pt lhs =   let defPS = defaultParserState{ stateSmart = True                                 , stateLiterateHaskell = lhs }
Network/Gitit/Export.hs view
@@ -57,7 +57,7 @@ respond mimetype ext fn page doc = liftIO (fn doc) >>=   ok . setContentType mimetype .   (if null ext then id else setFilename (page ++ "." ++ ext)) .-  toResponseBS (B.empty)+  toResponseBS B.empty  respondX :: String -> String -> String           -> (WriterOptions -> Pandoc -> IO L.ByteString)@@ -225,7 +225,7 @@               -- run pdflatex twice to get the references and toc right               let cmd = "pdflatex"               oldEnv <- getEnvironment-              let env = Just $ ("TEXINPUTS",".:" ++ +              let env = Just $ ("TEXINPUTS",".:" ++                                escapeStringUsing [(' ',"\\ "),('"',"\\\"")]                                (curdir </> repositoryPath cfg) ++ ":") : oldEnv               let opts = ["-interaction=batchmode", "-no-shell-escape", tempfile]@@ -257,13 +257,13 @@ fixURLs pndc = do     curdir <- liftIO getCurrentDirectory     cfg <- getConfig-    +     let go (Image ils (url, title)) = Image ils (fixURL url, title)         go x                        = x-        +         fixURL ('/':url) = curdir </> staticDir cfg </> url         fixURL url       = url-    +     return $ bottomUp go pndc  exportFormats :: Config -> [(String, String -> Pandoc -> Handler)]
Network/Gitit/Framework.hs view
@@ -18,11 +18,11 @@ -}  module Network.Gitit.Framework (-                               -- * Combinators for dealing with users +                               -- * Combinators for dealing with users                                  withUserFromSession                                , withUserFromHTTPAuth-                               , requireUserThat-                               , requireUser+                               , authenticateUserThat+                               , authenticate                                , getLoggedInUser                                -- * Combinators to exclude certain actions                                , unlessNoEdit@@ -46,7 +46,6 @@                                , isSourceCode                                -- * Combinators that change the request locally                                , withMessages-                               , withInput                                -- * Miscellaneous                                , urlForPage                                , pathForPage@@ -61,11 +60,11 @@ import Network.Gitit.Types import Data.FileStore import Data.Char (toLower)-import Control.Monad (mzero, liftM, MonadPlus)+import Control.Monad (mzero, liftM, unless, MonadPlus) import qualified Data.Map as M import Data.ByteString.UTF8 (toString) import Data.ByteString.Lazy.UTF8 (fromString)-import Data.Maybe (fromJust)+import Data.Maybe (fromJust, fromMaybe) import Data.List (intercalate, isPrefixOf, isInfixOf) import System.FilePath ((<.>), takeExtension, takeFileName) import Text.Highlighting.Kate@@ -75,24 +74,27 @@ import Happstack.Crypto.Base64 (decode) import Network.HTTP (urlEncodeVars) --- | Run the handler if a user is logged in, otherwise redirect+-- | Require a logged in user if the authentication level demands it.+-- Run the handler if a user is logged in, otherwise redirect -- to login page.-requireUser :: Handler -> Handler -requireUser = requireUserThat (const True)+authenticate :: AuthenticationLevel -> Handler -> Handler+authenticate = authenticateUserThat (const True) --- | Run the handler if a user satisfying the predicate is logged in.--- Redirect to login if nobody logged in; raise error if someone is--- logged in but doesn't satisfy the predicate.-requireUserThat :: (User -> Bool) -> Handler -> Handler-requireUserThat predicate handler = do-  mbUser <- getLoggedInUser-  rq <- askRq-  let url = rqUri rq ++ rqQuery rq-  case mbUser of-       Nothing   -> tempRedirect ("/_login?" ++ urlEncodeVars [("destination", url)]) $ toResponse ()-       Just u    -> if predicate u-                       then handler-                       else error "Not authorized."+-- | Like 'authenticate', but with a predicate that the user must satisfy.+authenticateUserThat :: (User -> Bool) -> AuthenticationLevel -> Handler -> Handler+authenticateUserThat predicate level handler = do+  cfg <- getConfig+  if level <= requireAuthentication cfg+     then do+       mbUser <- getLoggedInUser+       rq <- askRq+       let url = rqUri rq ++ rqQuery rq+       case mbUser of+            Nothing   -> tempRedirect ("/_login?" ++ urlEncodeVars [("destination", url)]) $ toResponse ()+            Just u    -> if predicate u+                            then handler+                            else error "Not authorized."+     else handler  -- | Run the handler after setting @REMOTE_USER@ with the user from -- the session.@@ -103,7 +105,7 @@   mbUser <- case mbSd of             Nothing    -> return Nothing             Just sd    -> do-              addCookie (sessionTimeout cfg) (mkCookie "sid" (show $ fromJust sk))  -- refresh timeout+              addCookie (MaxAge $ sessionTimeout cfg) (mkCookie "sid" (show $ fromJust sk))  -- refresh timeout               getUser $! sessionUser sd   let user = maybe "" uUsername mbUser   localRq (setHeader "REMOTE_USER" user) handler@@ -113,7 +115,7 @@ withUserFromHTTPAuth :: Handler -> Handler withUserFromHTTPAuth handler = do   req <- askRq-  let user = case (getHeader "authorization" req) of+  let user = case getHeader "authorization" req of               Nothing         -> ""               Just authHeader -> case parse pAuthorizationHeader "" (toString authHeader) of                                   Left _  -> ""@@ -137,14 +139,14 @@  pDigestHeader :: GenParser Char st String pDigestHeader = do-  string "Digest username=\""+  _ <- string "Digest username=\""   result' <- many (noneOf "\"")-  char '"'+  _ <- char '"'   return result'  pBasicHeader :: GenParser Char st String pBasicHeader = do-  string "Basic "+  _ <- string "Basic "   result' <- many (noneOf " \t\n")   return $ takeWhile (/=':') $ decode result' @@ -237,7 +239,7 @@   let (next, rest) = break (==c) cs   in case rest of          []     -> [next]-         (_:rs) -> next : splitOn c rs +         (_:rs) -> next : splitOn c rs  -- | Returns path portion of URI, without initial @\/@. -- Consecutive spaces are collapsed.  We don't want to distinguish@@ -271,8 +273,7 @@ -- | Returns encoded URL path for the page with the given name, relative to -- the wiki base. urlForPage :: String -> String-urlForPage page = "/" ++-  encString False isUnescapedInURI page+urlForPage page = '/' : encString False isUnescapedInURI page  -- | Returns the filestore path of the file containing the page's source. pathForPage :: String -> FilePath@@ -282,9 +283,8 @@ getMimeTypeForExtension :: String -> GititServerPart String getMimeTypeForExtension ext = do   mimes <- liftM mimeMap getConfig-  return $ case M.lookup (dropWhile (=='.') $ map toLower ext) mimes of-                Nothing -> "application/octet-stream"-                Just t  -> t+  return $ fromMaybe "application/octet-stream"+    (M.lookup (dropWhile (== '.') $ map toLower ext) mimes)  -- | Simple helper for validation of forms. validate :: [(Bool, String)]   -- ^ list of conditions and error messages@@ -307,9 +307,8 @@   base <- getWikiBase   uri' <- liftM rqUri askRq   let localpath = drop (length base) uri'-  if length localpath > 1 && lastNote "guardIndex" uri' == '/'-     then return ()-     else mzero+  unless (length localpath > 1 && lastNote "guardIndex" uri' == '/')+    mzero  -- Guard against a path like @\/wiki@ when the wiki is being -- served at @\/wiki@.@@ -317,29 +316,24 @@ guardBareBase = do   base' <- getWikiBase   uri' <- liftM rqUri askRq-  if not (null base') && base' == uri'-     then return ()-     else mzero+  unless (not (null base') && base' == uri')+    mzero  -- | Runs a server monad in a local context after setting -- the "messages" request header. withMessages :: ServerMonad m => [String] -> m a -> m a-withMessages = withInput "messages" . show---- | Runs a server monad in a local context after setting--- request header.-withInput :: ServerMonad m => String -> String -> m a -> m a-withInput name val handler = do+withMessages messages handler = do   req <- askRq-  let inps = filter (\(n,_) -> n /= name) $ rqInputs req-  let newInp = (name, Input { inputValue = fromString val+  let inps = filter (\(n,_) -> n /= "messages") $ rqInputsQuery req+  let newInp = ("messages", Input {+                              inputValue = Right $ fromString $ show messages                             , inputFilename = Nothing                             , inputContentType = ContentType {                                     ctType = "text"                                   , ctSubtype = "plain"                                   , ctParameters = [] }                             })-  localRq (\rq -> rq{ rqInputs = newInp : inps }) handler+  localRq (\rq -> rq{ rqInputsQuery = newInp : inps }) handler  -- | Returns a filestore object derived from the -- repository path and filestore type specified in configuration.
Network/Gitit/Handlers.hs view
@@ -48,7 +48,6 @@                       , deletePage                       , confirmDelete                       , showHighlightedSource-                      , currentUser                       , expireCache                       , feedHandler                       )@@ -65,7 +64,6 @@         exportPage, showHighlightedSource, preview, applyPreCommitPlugins) import Network.Gitit.Page (extractCategories) import Control.Exception (throwIO, catch, try)-import Data.ByteString.UTF8 (toString) import System.Time import System.FilePath import Prelude hiding (catch)@@ -102,7 +100,7 @@                                   (ok $ setContentType mimetype $                                     (toResponse noHtml) {rsBody = contents})                                     -- ugly hack-                Left NotFound  -> anyRequest mzero+                Left NotFound  -> mzero                 Left e         -> error (show e)  debugHandler :: Handler@@ -186,13 +184,13 @@ uploadFile :: Handler uploadFile = withData $ \(params :: Params) -> do   let origPath = pFilename params-  let fileContents = pFileContents params+  let filePath = pFilePath params   let wikiname = pWikiname params `orIfNull` takeFileName origPath   let logMsg = pLogMsg params   cfg <- getConfig   mbUser <- getLoggedInUser   (user, email) <- case mbUser of-                        Nothing -> fail "User must be logged in to delete page."+                        Nothing -> return ("Anonymous", "")                         Just u  -> return (uUsername u, uEmail u)   let overwrite = pOverwrite params   fs <- getFileStore@@ -214,17 +212,17 @@                  , (not overwrite && exists, "A file named '" ++ wikiname ++                     "' already exists in the repository: choose a new name " ++                     "or check the box to overwrite the existing file.")-                 , (B.length fileContents > fromIntegral (maxUploadSize cfg),-                    "File exceeds maximum upload size.")                  , (isPageFile wikiname,                     "This file extension is reserved for wiki pages.")                  ]   if null errors      then do        expireCachedFile wikiname `mplus` return ()+       fileContents <- liftIO $ B.readFile filePath+       let len = B.length fileContents        liftIO $ save fs wikiname (Author user email) logMsg fileContents        let contents = thediv <<-             [ h2 << ("Uploaded " ++ show (B.length fileContents) ++ " bytes")+             [ h2 << ("Uploaded " ++ show len ++ " bytes")              , if takeExtension wikiname `elem` imageExtensions                   then p << "To add this image to a page, use:" +++                        pre << ("![alt text](/" ++ wikiname ++ ")")@@ -259,11 +257,11 @@                                        Just m  -> seeOther (base' ++ urlForPage m) $                                                   toResponse $ "Redirecting" ++                                                     " to partial match"-                                       Nothing -> withInput "patterns" gotopage searchResults+                                       Nothing -> searchResults  searchResults :: Handler searchResults = withData $ \(params :: Params) -> do-  let patterns = pPatterns params+  let patterns = pPatterns params `orIfNull` [pGotoPage params]   fs <- getFileStore   matchLines <- if null patterns                    then return []@@ -292,7 +290,7 @@   let relevance (f, ms) = length ms + if f `elem` pageNameMatches                                          then 100                                          else 0-  let preamble = if null 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]@@ -448,7 +446,7 @@   from' <- case (from, to) of               (Just _, _)        -> return from               (Nothing, Nothing) -> return from-              (Nothing, Just t)  -> do +              (Nothing, Just t)  -> do                 pageHist <- liftIO $ history fs [file]                                      (TimeRange Nothing Nothing)                 let (_, upto) = break (\r -> idsMatch fs (revId r) t)@@ -484,7 +482,10 @@            pre ! [theclass "diff"] << map diffLineToHtml rawDiff  editPage :: Handler-editPage = withData $ \(params :: Params) -> do+editPage = withData editPage'++editPage' :: Params -> Handler+editPage' params = do   let rev = pRevision params  -- if this is set, we're doing a revert   fs <- getFileStore   page <- getPage@@ -537,6 +538,11 @@                               value "Preview" ]                    , thediv ! [ identifier "previewpane" ] << noHtml                    ]+  let pgScripts' = ["preview.js"]+  let pgScripts'' = case mathMethod cfg of+       JsMathScript -> "jsMath/easy/load.js" : pgScripts'+       MathML       -> "MathMLinHTML.js" : pgScripts'+       _            -> pgScripts'   formattedPage defaultPageLayout{                   pgPageName = page,                   pgMessages = messages,@@ -545,7 +551,7 @@                   pgShowSiteNav = False,                   pgMarkupHelp = Just $ markupHelp cfg,                   pgSelectedTab = EditTab,-                  pgScripts = ["preview.js"],+                  pgScripts = pgScripts'',                   pgTitle = ("Editing " ++ page)                   } editForm @@ -585,12 +591,12 @@   let file = pFileToDelete params   mbUser <- getLoggedInUser   (user, email) <- case mbUser of-                        Nothing -> fail "User must be logged in to delete."+                        Nothing -> return ("Anonymous", "")                         Just u  -> return (uUsername u, uEmail u)   let author = Author user email   let descrip = "Deleted using web interface."   base' <- getWikiBase-  if pConfirm params && (file == page || file == page <.> "page") +  if pConfirm params && (file == page || file == page <.> "page")      then do        fs <- getFileStore        liftIO $ delete fs file author descrip@@ -603,7 +609,7 @@   cfg <- getConfig   mbUser <- getLoggedInUser   (user, email) <- case mbUser of-                        Nothing -> fail "User must be logged in to delete page."+                        Nothing -> return ("Anonymous", "")                         Just u  -> return (uUsername u, uEmail u)   editedText <- case pEditedText params of                      Nothing -> error "No body text in POST request"@@ -639,9 +645,10 @@                       if conflicts                          then "Please resolve conflicts and Save."                          else "Please review and Save."-               withMessages [mergeMsg] $-                 withInput "editedText" mergedText $-                   withInput "sha1" (revId mergedWithRev) editPage+               editPage' $+                 params{ pEditedText = Just mergedText,+                         pSHA1       = revId mergedWithRev,+                         pMessages   = [mergeMsg] }  indexPage :: Handler indexPage = do@@ -733,12 +740,6 @@                   pgTabs = [],                   pgScripts = ["search.js"],                   pgTitle = "Categories" } htmlMatches---- | Returns username of logged in user or null string if nobody logged in.-currentUser :: Handler-currentUser = do-  req <- askRq-  ok $ toResponse $ maybe "" toString (getHeader "REMOTE_USER" req)  expireCache :: Handler expireCache = do
Network/Gitit/Initialize.hs view
@@ -172,7 +172,7 @@       logM "gitit" WARNING $ "Created " ++ (cssdir </> f)      {--    let icondir = staticdir </> "img" </> "icons" +    let icondir = staticdir </> "img" </> "icons"     createDirectoryIfMissing True icondir     iconDataDir <- getDataFileName $ "data" </> "static" </> "img" </> "icons"     iconFiles <- liftM (filter (\f -> takeExtension f == ".png")) $ getDirectoryContents iconDataDir
Network/Gitit/Layout.hs view
@@ -71,11 +71,11 @@  -- | Returns a page template with gitit variables filled in. filledPageTemplate :: String -> Config -> PageLayout -> Html ->-                      T.StringTemplate String -> T.StringTemplate String -filledPageTemplate base' cfg layout htmlContents templ = +                      T.StringTemplate String -> T.StringTemplate String+filledPageTemplate base' cfg layout htmlContents templ =   let rev  = pgRevision layout       page = pgPageName layout-      scripts  = ["jquery.min.js", "jquery-ui.packed.js"] ++ pgScripts layout+      scripts  = ["jquery.min.js", "jquery-ui.packed.js", "footnotes.js"] ++ pgScripts layout       scriptLink x = script ! [src (base' ++ "/js/" ++ x),         thetype "text/javascript"] << noHtml       javascriptlinks = renderHtmlFragment $ concatHtml $ map scriptLink scripts@@ -105,7 +105,7 @@                    T.setAttribute "tabs" (renderHtmlFragment tabs) .                    T.setAttribute "messages" (pgMessages layout) .                    T.setAttribute "usecache" (useCache cfg) .-                   T.setAttribute "content" (renderHtmlFragment htmlContents) . +                   T.setAttribute "content" (renderHtmlFragment htmlContents) .                    setBoolAttr "wikiupload" ( uploadsAllowed cfg) $                    templ @@ -118,7 +118,7 @@          value (fromJust rev)] | isJust rev ] ++      [ select ! [name "format"] <<          map ((\f -> option ! [value f] << f) . fst) (exportFormats cfg)-     , primHtmlChar "nbsp" +     , primHtmlChar "nbsp"      , submit "export" "Export" ]) exportBox _ _ _ _ = noHtml 
Network/Gitit/Page.hs view
@@ -21,7 +21,7 @@ -  which looks like this (it is valid YAML): - -  > -----  > title: Custom Title +-  > title: Custom Title -  > format: markdown+lhs -  > toc: yes -  > categories: foo bar baz@@ -32,7 +32,7 @@ -  text as markdown with literate haskell, to include a table of -  contents, and to include the page in the categories foo, bar, -  and baz.-- +- -  The metadata block may be omitted entirely, and any particular line -  may be omitted. The categories in the @categories@ field should be -  separated by spaces. Commas will be treated as spaces.@@ -52,7 +52,7 @@ import Network.Gitit.Util (trim, splitCategories, parsePageType) import Text.ParserCombinators.Parsec import Data.Char (toLower)-import Data.List (intercalate)+import Data.List (isPrefixOf) import Data.Maybe (fromMaybe)  parseMetadata :: String -> ([(String, String)], String)@@ -63,11 +63,11 @@  pMetadataBlock :: GenParser Char st ([(String, String)], String) pMetadataBlock = try $ do-  string "---"-  pBlankline+  _ <- string "---"+  _ <- pBlankline   ls <- many pMetadataLine-  string "..."-  pBlankline+  _ <- string "..."+  _ <- pBlankline   skipMany pBlankline   rest <- getInput   return (ls, rest)@@ -79,11 +79,11 @@ pMetadataLine = try $ do   ident <- many1 letter   skipMany (oneOf " \t")-  char ':'+  _ <- char ':'   rawval <- many $ noneOf "\n\r"                  <|> (try $ newline >> notFollowedBy pBlankline >>                             skipMany1 (oneOf " \t") >> return ' ')-  newline+  _ <- newline   return (ident, trim rawval)  -- | Read a string (the contents of a page file) and produce a Page@@ -91,7 +91,7 @@ stringToPage :: Config -> String -> String -> Page stringToPage conf pagename raw =   let (ls, rest) = parseMetadata raw-      page' = Page { pageName        = pagename +      page' = Page { pageName        = pagename                    , pageFormat      = defaultPageType conf                    , pageLHS         = defaultLHS conf                    , pageTOC         = tableOfContents conf@@ -106,7 +106,7 @@ adjustPage ("format", val) page' = page' { pageFormat = pt, pageLHS = lhs }     where (pt, lhs) = parsePageType val adjustPage ("toc", val) page' = page' {-  pageTOC = (map toLower val) `elem` ["yes","true"] }+  pageTOC = map toLower val `elem` ["yes","true"] } adjustPage ("categories", val) page' =    page' { pageCategories = splitCategories val ++ pageCategories page' } adjustPage (_, _) page' = page'@@ -126,7 +126,7 @@                        else "") ++                    (if pageformat /= defaultPageType conf ||                           pagelhs /= defaultLHS conf-                       then "!format: " ++ +                       then "!format: " ++                             map toLower (show pageformat) ++                             if pagelhs then "+lhs\n" else "\n"                        else "") ++@@ -135,12 +135,12 @@                             (if pagetoc then "yes" else "no") ++ "\n"                        else "") ++                    (if not (null pagecats)-                       then "!categories: " ++ intercalate " " pagecats ++ "\n"+                       then "!categories: " ++ unwords pagecats ++ "\n"                        else "")   in  metadata' ++ (if null metadata' then "" else "\n") ++ pageText page'  extractCategories :: String -> [String]-extractCategories s | take 3 s == "---" = +extractCategories s | "---" `isPrefixOf` s =   let (md,_) = parseMetadata s   in  splitCategories $ fromMaybe "" $ lookup "categories" md extractCategories _ = []
+ Network/Gitit/Rpxnow.hs view
@@ -0,0 +1,81 @@+-- Modified from Michael Snoyman's BSD3 authenticate-0.0.1+-- and http-wget-0.0.1.+-- Facilitates authentication with "http://rpxnow.com/".++module Network.Gitit.Rpxnow+    ( Identifier (..)+    , authenticate+    ) where++import Text.JSON+import Data.Maybe (isJust, fromJust)+import System.Process+import System.Exit+import System.IO+import Network.HTTP (urlEncodeVars)++-- | Make a post request with parameters to the URL and return a response.+curl :: Monad m+     => String             -- ^ URL+     -> [(String, String)] -- ^ Post parameters+     -> IO (m String)      -- ^ Response body+curl url params = do+    (Nothing, Just hout, Just herr, phandle) <- createProcess $ (proc "curl"+        [url, "-d", urlEncodeVars params]+        ) { std_out = CreatePipe, std_err = CreatePipe }+    exitCode <- waitForProcess phandle+    case exitCode of+        ExitSuccess -> hGetContents hout >>= return . return+        _           -> hGetContents herr >>= return . fail++++-- | Information received from Rpxnow after a valid login.+data Identifier = Identifier+    { userIdentifier  :: String+    , userData        :: [(String, String)]+    }+    deriving Show++-- | Attempt to log a user in.+authenticate :: Monad m+             => String -- ^ API key given by RPXNOW.+             -> String -- ^ Token passed by client.+             -> IO (m Identifier)+authenticate apiKey token = do+    body <- curl+                "https://rpxnow.com/api/v2/auth_info"+                [ ("apiKey", apiKey)+                , ("token", token)+                ]+    case body of+        Left s -> return $ fail $ "Unable to connect to rpxnow: " ++ s+        Right b ->+          case decode b >>= getObject of+            Error s -> return $ fail $ "Not a valid JSON response: " ++ s+            Ok o ->+              case valFromObj "stat" o of+                Error _ -> return $ fail "Missing 'stat' field"+                Ok "ok" -> return $ parseProfile o+                Ok stat -> return $ fail $ "Login not accepted: " ++ stat++parseProfile :: Monad m => JSObject JSValue -> m Identifier+parseProfile v = do+    profile <- resultToMonad $ valFromObj "profile" v >>= getObject+    ident <- resultToMonad $ valFromObj "identifier" profile+    let pairs = fromJSObject profile+        pairs' = filter (\(k, _) -> k /= "identifier") pairs+        pairs'' = map fromJust . filter isJust . map takeString $ pairs'+    return $ Identifier ident pairs''++takeString :: (String, JSValue) -> Maybe (String, String)+takeString (k, JSString v) = Just (k, fromJSString v)+takeString _ = Nothing++getObject :: Monad m => JSValue -> m (JSObject JSValue)+getObject (JSObject o) = return o+getObject _ = fail "Not an object"++resultToMonad :: Monad m => Result a -> m a+resultToMonad (Ok x) = return x+resultToMonad (Error s) = fail s
Network/Gitit/Server.hs view
@@ -35,10 +35,10 @@           ) where import Happstack.Server-import Happstack.Server.Parts (compressedResponseFilter)+import Happstack.Server.Compression (compressedResponseFilter) import Network.Socket (getAddrInfo, defaultHints, addrAddress) import Control.Monad.Reader-import Data.ByteString.UTF8 as U+import Data.ByteString.UTF8 as U hiding (lines)  withExpiresHeaders :: ServerMonad m => m Response -> m Response withExpiresHeaders = liftM (setHeader "Cache-Control" "max-age=21600")@@ -57,7 +57,7 @@   if null addrs      then return Nothing      else return $ Just $ takeWhile (/=':') $ show $ addrAddress $ case addrs of -- head addrs-                                                                     [] -> error $ "lookupIPAddr, no addrs"+                                                                     [] -> error "lookupIPAddr, no addrs"                                                                      (x:_) -> x getHost :: ServerMonad m => m (Maybe String) getHost = liftM (maybe Nothing (Just . U.toString)) $ getHeaderM "Host"
Network/Gitit/Types.hs view
@@ -29,13 +29,10 @@ import System.Log.Logger (Priority(..)) import Text.Pandoc.Definition (Pandoc) import Text.XHtml (Html)-import qualified Data.ByteString.Lazy.UTF8 as L (ByteString)-import qualified Data.ByteString.Lazy as L (empty) import qualified Data.Map as M import Data.List (intersect) import Data.Time (parseTime) import System.Locale (defaultTimeLocale)-import Data.Maybe (fromMaybe) import Data.FileStore.Types import Network.Gitit.Server import Text.Pandoc.CharacterReferences (decodeCharacterReferences)@@ -48,6 +45,9 @@ data MathMethod = MathML | JsMathScript | WebTeX String | RawTeX                   deriving (Read, Show, Eq) +data AuthenticationLevel = Never | ForModify | ForRead+                  deriving (Read, Show, Eq, Ord)+ -- | Data structure for information read from config file. data Config = Config {   -- | Path of repository containing filestore@@ -65,6 +65,8 @@   -- | Combinator to set @REMOTE_USER@ request header   withUser             :: Handler -> Handler,   -- | Handler for login, logout, register, etc.+  requireAuthentication :: AuthenticationLevel,+  -- | Specifies which actions require authentication.   authHandler          :: Handler,   -- | Path of users database   userFile             :: FilePath,@@ -108,6 +110,9 @@   useRecaptcha         :: Bool,   recaptchaPublicKey   :: String,   recaptchaPrivateKey  :: String,+  -- | RPX domain and key+  rpxDomain            :: String,+  rpxKey               :: String,   -- | Should responses be compressed?   compressResponses    :: Bool,   -- | Should responses be cached?@@ -221,7 +226,7 @@   { pgPageName       :: String   , pgRevision       :: Maybe String   , pgPrintable      :: Bool-  , pgMessages       :: [String] +  , pgMessages       :: [String]   , pgTitle          :: String   , pgScripts        :: [String]   , pgShowPageTools  :: Bool@@ -273,8 +278,8 @@                      , pPrintable    :: Bool                      , pOverwrite    :: Bool                      , pFilename     :: String-                     , pFileContents :: L.ByteString-                     , pConfirm      :: Bool +                     , pFilePath     :: FilePath+                     , pConfirm      :: Bool                      , pSessionKey   :: Maybe SessionKey                      , pRecaptcha    :: Recaptcha                      , pResetCode    :: String@@ -298,7 +303,7 @@          pa <- look' "patterns"       `mplus` return ""          gt <- look' "gotopage"       `mplus` return ""          ft <- look' "filetodelete"   `mplus` return ""-         me <- lookRead "messages"   `mplus` return [] +         me <- lookRead "messages"   `mplus` return []          fm <- liftM Just (look' "from") `mplus` return Nothing          to <- liftM Just (look' "to")   `mplus` return Nothing          et <- liftM (Just . filter (/='\r')) (look' "editedText")@@ -311,9 +316,10 @@          wn <- look' "wikiname"       `mplus` return ""          pr <- (look' "printable" >> return True) `mplus` return False          ow <- liftM (=="yes") (look' "overwrite") `mplus` return False-         fn <- liftM (fromMaybe "" . inputFilename) (lookInput "file")-                 `mplus` return ""-         fc <- liftM inputValue (lookInput "file") `mplus` return L.empty+         fileparams <- liftM Just (lookFile "file") `mplus` return Nothing+         let (fp, fn) = case fileparams of+                             Just (x,y,_) -> (x,y)+                             Nothing      -> ("","")          ac <- look' "accessCode"     `mplus` return ""          cn <- (look' "confirm" >> return True) `mplus` return False          sk <- liftM Just (readCookieValue "sid") `mplus` return Nothing@@ -336,16 +342,16 @@                          , pFrom         = fm                          , pTo           = to                          , pEditedText   = et-                         , pFormat       = fo +                         , pFormat       = fo                          , pSHA1         = sh                          , pLogMsg       = lm                          , pEmail        = em-                         , pFullName     = na +                         , pFullName     = na                          , pWikiname     = wn-                         , pPrintable    = pr +                         , pPrintable    = pr                          , pOverwrite    = ow                          , pFilename     = fn-                         , pFileContents = fc+                         , pFilePath     = fp                          , pAccessCode   = ac                          , pConfirm      = cn                          , pSessionKey   = sk@@ -366,7 +372,7 @@                where commandList = ["update", "cancel", "export"]  -- | State for a single wiki.-data WikiState = WikiState { +data WikiState = WikiState {                      wikiConfig    :: Config                    , wikiFileStore :: FileStore                    }
Network/Gitit/Util.hs view
@@ -49,11 +49,11 @@   setCurrentDirectory d   result <- action   setCurrentDirectory w-  return result +  return result  -- | Perform a function in a temporary directory and clean up. withTempDir :: FilePath -> (FilePath -> IO a) -> IO a-withTempDir baseName = bracket (createTempDir 0 baseName) (removeDirectoryRecursive)+withTempDir baseName = bracket (createTempDir 0 baseName) removeDirectoryRecursive  -- | Create a temporary directory with a unique name. createTempDir :: Integer -> FilePath -> IO FilePath
data/default.conf view
@@ -14,6 +14,12 @@ # specifies the path of the repository directory.  If it does not # exist, gitit will create it on startup. +require-authentication: modify+# if 'none', login is never required, and pages can be edited anonymously.+# if 'modify', login is required to modify the wiki (edit, add, delete+# pages, upload files).+# if 'read', login is required to see any wiki pages.+ authentication-method: form # 'form' means that users will be logged in and registered # using forms in the gitit web interface.  'http' means@@ -24,7 +30,10 @@ # suppressed).  'generic' means that gitit will assume that # some form of authentication is in place that directly # sets REMOTE_USER to the name of the authenticated user-# (e.g. mod_auth_cas on apache).+# (e.g. mod_auth_cas on apache).  'rpx' means that gitit+# will attempt to log in through https://rpxnow.com.+# This requires that 'rpx-domain', 'rpx-key', and 'base-url'+# be set below, and that 'curl' be in the system path.  user-file: gitit-users # specifies the path of the file containing user login information.@@ -170,6 +179,12 @@ # access-question:  What is the code given to you by Ms. X? # access-question-answers:  RED DOG, red dog +rpx-domain:+rpx-key:+# Specifies the domain and key of your RPX account.  The domain is+# just the prefix of the complete RPX domain, so if your full domain+# is 'https://foo.rpxnow.com/', use 'foo' as the value of rpx-domain.+ mail-command: sendmail %s # specifies the command to use to send notification emails. # '%s' will be replaced by the destination email address.@@ -209,11 +224,9 @@ # individual pages)  base-url:-# the base URL of the wiki, to be used in constructing feed IDs.-# If this field is left blank, gitit will get the base URL from the-# 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.+# the base URL of the wiki, to be used in constructing feed IDs+# and RPX token_urls.  Set this if use-feed is 'yes' or+# authentication-method is 'rpx'.  feed-days: 14 # number of days to be included in feeds.
data/static/css/screen.css view
@@ -1,6 +1,6 @@ @import url("reset-fonts-grids.css"); -html { background: #f9f9f9; color: black; } +html { background: #f9f9f9; color: black; } body { margin: 10px; }  fieldset { border: 1px solid #ccc; padding: 1em; }@@ -44,6 +44,7 @@ caption { margin-bottom: .5em; text-align: center; } sup { vertical-align: super; } sub { vertical-align: sub; }+sub, sup { line-height: 0.3em; } p, fieldset, table, pre { margin-bottom: 1em; } button, input[type="checkbox"], input[type="radio"], input[type="reset"], input[type="submit"] { padding: 1px; } @@ -138,3 +139,17 @@ div.markupHelp pre { font-size: 77%; overflow: auto; }  .login { display: none; }++/* Style the footnotes; from gwern.net */+#footnotediv div {+background-color: white;+padding: 3px;+padding: 12px;+max-width: 800px;+border: 1px solid #CDBBB5;+box-shadow: #555 0 0 10px;+-webkit-box-shadow: #555 0 0 10px;+-moz-box-shadow: 0 0 10px #555;+}+/* Deal with multiple footnotes one after another; Charuru */+sup + sup { margin-left: 2px; }
data/static/js/preview.js view
@@ -6,10 +6,15 @@       {"raw" : $("#editedText").attr("value")},       function(data) {         $('#previewpane').html(data);+        // Process any mathematics if we're using MathML         if (typeof(convert) == 'function') { convert(); }+        // Process any mathematics if we're using jsMath+        if (typeof(jsMath) == 'object')    { jsMath.ProcessBeforeShowing(); }       },       "html");+     $('#previewpane').fadeIn(1000);+ }; $(document).ready(function(){     $("#previewButton").show();
data/static/js/uploadForm.js view
@@ -1,3 +1,6 @@ $(document).ready(function(){-    $("#file").change(function () { $("#wikiname").val($(this).val()); });+    $("#file").change(function () {+      var fn = $(this).val().replace(/.*\\/,"");+      $("#wikiname").val(fn);+    });   });
expireGititCache.hs view
@@ -43,7 +43,7 @@              Nothing -> do                hPutStrLn stderr ("Could not parse URI " ++ uriString)                exitWith (ExitFailure 5)-  forM_ files (expireFile uri)  +  forM_ files (expireFile uri)  usageMessage :: IO () usageMessage = do
gitit.cabal view
@@ -1,5 +1,5 @@ name:                gitit-version:             0.7.3.12+version:             0.8 Cabal-version:       >= 1.2 build-type:          Simple synopsis:            Wiki using happstack, git or darcs, and pandoc.@@ -41,7 +41,7 @@ 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,@@ -91,11 +91,12 @@   exposed-modules:   Network.Gitit, Network.Gitit.ContentTransformer,                      Network.Gitit.Types, Network.Gitit.Framework,                      Network.Gitit.Initialize, Network.Gitit.Config,-                     Network.Gitit.Layout+                     Network.Gitit.Layout, Network.Gitit.Authentication   other-modules:     Network.Gitit.Cache, Network.Gitit.State,                      Paths_gitit, Network.Gitit.Server, Network.Gitit.Export,-                     Network.Gitit.Util, Network.Gitit.Handlers, Network.Gitit.Plugins,-                     Network.Gitit.Authentication, Network.Gitit.Page, Network.Gitit.Feed+                     Network.Gitit.Util, Network.Gitit.Handlers,+                     Network.Gitit.Plugins, Network.Gitit.Rpxnow,+                     Network.Gitit.Page, Network.Gitit.Feed   if flag(plugins)     exposed-modules: Network.Gitit.Interface     build-depends:   ghc, ghc-paths@@ -138,13 +139,14 @@                      filestore >= 0.4.0.2 && < 0.5,                      zlib >= 0.5 && < 0.6,                      url >= 2.1 && < 2.2,-                     happstack-server >= 0.5 && < 0.6,-                     happstack-util >= 0.5 && < 0.6,+                     happstack-server >= 6.0 && < 6.1,+                     happstack-util >= 6.0 && < 6.1,                      xml >= 1.3.5,                      hslogger >= 1 && < 1.2,                      ConfigFile >= 1 && < 1.1,                      feed >= 0.3.6 && < 0.4,-                     xss-sanitize >= 0.2 && < 0.3+                     xss-sanitize >= 0.2 && < 0.3,+                     json >= 0.4 && < 0.5   if impl(ghc >= 6.10)     build-depends:   base >= 4, syb   if flag(plugins)
gitit.hs view
@@ -32,6 +32,8 @@ import System.Exit import System.IO (stderr) import System.Console.GetOpt+import Network.Socket hiding (Debug)+import Network.URI import Data.Version (showVersion) import qualified Data.ByteString as B import Data.ByteString.UTF8 (fromString)@@ -74,10 +76,18 @@   -- initialize state   initializeGititState conf' -  let serverConf = Conf { validator = Nothing, port = portNumber conf' }+  let serverConf = Conf { validator = Nothing, port = portNumber conf',+                          timeout = 20, logAccess = Nothing } +  -- open the requested interface+  sock <- socket AF_INET Stream defaultProtocol+  setSocketOption sock ReuseAddr 1+  device <- inet_addr (getListenOrDefault opts)+  bindSocket sock (SockAddrInet (toEnum (portNumber conf')) device)+  listen sock 10+   -- start the server-  simpleHTTP serverConf $ msum [ wiki conf'+  simpleHTTPWithSocket sock serverConf $ msum [ wiki conf'                                , dir "_reloadTemplates" reloadTemplates                                ] @@ -85,6 +95,7 @@     = Help     | ConfigFile FilePath     | Port Int+	| Listen String     | Debug     | Version     | PrintDefaultConfig@@ -98,6 +109,8 @@         "Print version information"    , Option ['p'] ["port"] (ReqArg (Port . read) "PORT")         "Specify port"+   , Option ['l'] ["listen"] (ReqArg (Listen . checkListen) "INTERFACE")+        "Specify interface to listen on"    , Option [] ["print-default-config"] (NoArg PrintDefaultConfig)         "Print default configuration"    , Option [] ["debug"] (NoArg Debug)@@ -106,6 +119,16 @@         "Specify configuration file"    ] +checkListen :: String -> String+checkListen l | isIPv6address l = l+              | isIPv4address l = l+			  | otherwise         = error "Gitit.checkListen: Not a valid interface name"++getListenOrDefault :: [Opt] -> String+getListenOrDefault [] = "127.0.0.1"+getListenOrDefault ((Listen l):_) = l+getListenOrDefault (_:os) = getListenOrDefault os+ parseArgs :: [String] -> IO [Opt] parseArgs argv = do   progname <- getProgName@@ -139,6 +162,7 @@     Debug              -> return conf{ debugMode = True }     Port p             -> return conf{ portNumber = p }     ConfigFile fname   -> getConfigFromFile fname+    Listen _           -> return conf  putErr :: ExitCode -> String -> IO a putErr c s = B.hPutStrLn stderr (fromString s) >> exitWith c
plugins/Interwiki.hs view
@@ -61,106 +61,84 @@                       ("Hoogle", "http://www.haskell.org/hoogle/?hoogle=")]  -- This mapping is derived from <https://secure.wikimedia.org/wikipedia/meta/wiki/Interwiki_map>--- as of 3:16 PM, 7 February 2010.+-- as of 11:30 AM, 6 February 2011. wpInterwikiMap = [ ("AbbeNormal", "http://ourpla.net/cgi/pikie?"),+                 ("AIWiki", "http://www.ifi.unizh.ch/ailab/aiwiki/aiw.cgi?"),                  ("Acronym", "http://www.acronymfinder.com/af-query.asp?String=exact&Acronym="),                  ("Advisory", "http://advisory.wikimedia.org/wiki/"),                  ("Advogato", "http://www.advogato.org/"),                  ("Aew", "http://wiki.arabeyes.org/"),                  ("Airwarfare", "http://airwarfare.com/mediawiki-1.4.5/index.php?"),-                 ("AIWiki", "http://www.ifi.unizh.ch/ailab/aiwiki/aiw.cgi?"),                  ("AllWiki", "http://allwiki.com/index.php/"),                  ("Appropedia", "http://www.appropedia.org/"),                  ("AquariumWiki", "http://www.theaquariumwiki.com/"),-                 ("arXiv", "http://arxiv.org/abs/"),                  ("AspieNetWiki", "http://aspie.mela.de/index.php/"),                  ("AtmWiki", "http://www.otterstedt.de/wiki/index.php/"),-                 ("BattlestarWiki", "http://en.battlestarwiki.org/wiki/"),                  ("BEMI", "http://bemi.free.fr/vikio/index.php?"),+                 ("BLW", "http://britainloveswikipedia.org/wiki/"),+                 ("BattlestarWiki", "http://en.battlestarwiki.org/wiki/"),                  ("BenefitsWiki", "http://www.benefitslink.com/cgi-bin/wiki.cgi?"),-                 ("betawiki", "http://translatewiki.net/wiki/"),                  ("BibleWiki", "http://bible.tmtm.com/wiki/"),                  ("BluWiki", "http://www.bluwiki.org/go/"),-                 ("BLW", "http://britainloveswikipedia.org/wiki/"),                  ("Botwiki", "http://botwiki.sno.cc/wiki/"),                  ("Boxrec", "http://www.boxrec.com/media/index.php?"),-                 ("BrickWiki", "http://brickwiki.org/index.php?title="),-                 ("BridgesWiki", "http://c2.com:8000/"),-                 ("bugzilla", "https://bugzilla.wikimedia.org/show_bug.cgi?id="),-                 ("bulba", "http://bulbapedia.bulbagarden.net/wiki/"),-                 ("buzztard", "http://buzztard.org/index.php/"),+                 ("BrickWiki", "http://lego.wikia.com/index.php?title="),                  ("Bytesmiths", "http://www.Bytesmiths.com/wiki/"),                  ("C2", "http://c2.com/cgi/wiki?"),                  ("C2find", "http://c2.com/cgi/wiki?FindPage&value="),+                 ("CANWiki", "http://www.can-wiki.info/"),+                 ("CKWiss", "http://ck-wissen.de/ckwiki/index.php?title="),+                 ("CNDbName", "http://cndb.com/actor.html?name="),+                 ("CNDbTitle", "http://cndb.com/movie.html?title="),                  ("Cache", "http://www.google.com/search?q=cache:"),                  ("CanyonWiki", "http://www.canyonwiki.com/wiki/index.php/"),-                 ("CANWiki", "http://www.can-wiki.info/"),-                 ("ĈEJ", "http://esperanto.blahus.cz/cxej/vikio/index.php/"),                  ("CellWiki", "http://cell.wikia.com/wiki/"),-                 ("CentralWikia", "http://www.wikia.com/wiki/"),+                 ("CentralWikia", "http://community.wikia.com/wiki/"),                  ("ChEJ", "http://esperanto.blahus.cz/cxej/vikio/index.php/"),                  ("ChoralWiki", "http://www.cpdl.org/wiki/index.php/"),-                 ("Ciscavate", "http://ciscavate.org/index.php/"),                  ("Citizendium", "http://en.citizendium.org/wiki/"),-                 ("CKWiss", "http://ck-wissen.de/ckwiki/index.php?title="),-                 ("CNDbName", "http://cndb.com/actor.html?name="),-                 ("CNDbTitle", "http://cndb.com/movie.html?title="),-                 ("CoLab", "http://colab.info"),                  ("Comixpedia", "http://www.comixpedia.org/index.php/"),-                 ("comcom", "http://comcom.wikimedia.org/wiki/"),                  ("Commons", "http://commons.wikimedia.org/wiki/"),                  ("CommunityScheme", "http://community.schemewiki.org/?c=s&key="),-                 ("comune", "http://rete.comuni-italiani.it/wiki/"),-                 ("Consciousness", "http://teadvus.inspiral.org/index.php/"),                  ("CorpKnowPedia", "http://corpknowpedia.org/wiki/index.php/"),                  ("CrazyHacks", "http://www.crazy-hacks.org/wiki/index.php?title="),-                 ("Creative Commons", "http://www.creativecommons.org/licenses/"),+                 ("CreativeCommons", "http://www.creativecommons.org/licenses/"),+                 ("CreativeCommonsWiki", "http://wiki.creativecommons.org/"),                  ("CreaturesWiki", "http://creatures.wikia.com/wiki/"),                  ("CxEJ", "http://esperanto.blahus.cz/cxej/vikio/index.php/"),-                 ("DAwiki", "http://www.dienstag-abend.de/wiki/index.php/"),-                 ("Dcc", "http://www.dccwiki.com/"),-                 ("DCDatabase", "http://www.dcdatabaseproject.com/wiki/"),+                 ("DCDatabase", "http://dc.wikia.com/"),                  ("DCMA", "http://www.christian-morgenstern.de/dcma/"),+                 ("DOI", "http://dx.doi.org/"),+                 ("DRAE", "http://buscon.rae.es/draeI/SrvltGUIBusUsual?LEMA="),+                 ("DWJWiki", "http://www.suberic.net/cgi-bin/dwj/wiki.cgi?"),+                 ("Dcc", "http://www.dccwiki.com/"),                  ("DejaNews", "http://www.deja.com/=dnc/getdoc.xp?AN="),-                 ("Delicious", "http://del.icio.us/tag/"),+                 ("Delicious", "http://www.delicious.com/tag/"),                  ("Demokraatia", "http://wiki.demokraatia.ee/index.php/"),                  ("Devmo", "http://developer.mozilla.org/en/docs/"),-                 ("Dictionary", "http://www.dict.org/bin/Dict?Database=*&Form=Dict1&Strategy=*&Query="),                  ("Dict", "http://www.dict.org/bin/Dict?Database=*&Form=Dict1&Strategy=*&Query="),+                 ("Dictionary", "http://www.dict.org/bin/Dict?Database=*&Form=Dict1&Strategy=*&Query="),                  ("Disinfopedia", "http://www.sourcewatch.org/wiki.phtml?title="),-                 ("distributedproofreaders", "http://www.pgdp.net/wiki/"),-                 ("distributedproofreadersca", "http://www.pgdpcanada.net/wiki/index.php/"),-                 ("dmoz", "http://www.dmoz.org/"),-                 ("dmozs", "http://www.dmoz.org/cgi-bin/search?search="),                  ("DocBook", "http://wiki.docbook.org/topic/"),-                 ("DOI", "http://dx.doi.org/"),-                 ("doom_wiki", "http://doom.wikia.com/wiki/"),-                 ("download", "http://download.wikimedia.org/"),-                 ("dbdump", "http://download.wikimedia.org//latest/"),-                 ("DRAE", "http://buscon.rae.es/draeI/SrvltGUIBusUsual?LEMA="),                  ("Dreamhost", "http://wiki.dreamhost.com/index.php/"),                  ("DrumCorpsWiki", "http://www.drumcorpswiki.com/index.php/"),-                 ("DWJWiki", "http://www.suberic.net/cgi-bin/dwj/wiki.cgi?"),-                 ("EĉeI", "http://www.ikso.net/cgi-bin/wiki.pl?"),+                 ("ELibre", "http://enciclopedia.us.es/index.php/"),                  ("EcheI", "http://www.ikso.net/cgi-bin/wiki.pl?"),                  ("EcoReality", "http://www.EcoReality.org/wiki/"),                  ("EcxeI", "http://www.ikso.net/cgi-bin/wiki.pl?"),-                 ("EfnetCeeWiki", "http://purl.net/wiki/c/"),-                 ("EfnetCppWiki", "http://purl.net/wiki/cpp/"),-                 ("EfnetPythonWiki", "http://purl.net/wiki/python/"),-                 ("EfnetXmlWiki", "http://purl.net/wiki/xml/"),-                 ("ELibre", "http://enciclopedia.us.es/index.php/"),                  ("EmacsWiki", "http://www.emacswiki.org/cgi-bin/wiki.pl?"),-                 ("EnergieWiki", "http://www.netzwerk-energieberater.de/wiki/index.php/"),                  ("Encyc", "http://encyc.org/wiki/"),+                 ("EnergieWiki", "http://www.netzwerk-energieberater.de/wiki/index.php/"),                  ("EoKulturCentro", "http://esperanto.toulouse.free.fr/nova/wikini/wakka.php?wiki="),                  ("Ethnologue", "http://www.ethnologue.com/show_language.asp?code="),                  ("EvoWiki", "http://wiki.cotch.net/index.php/"),                  ("Exotica", "http://www.exotica.org.uk/wiki/"),+                 ("EĉeI", "http://www.ikso.net/cgi-bin/wiki.pl?"),                  ("FanimutationWiki", "http://wiki.animutationportal.com/index.php/"),                  ("FinalEmpire", "http://final-empire.sourceforge.net/cgi-bin/wiki.pl?"),                  ("FinalFantasy", "http://finalfantasy.wikia.com/wiki/"),                  ("Finnix", "http://www.finnix.org/"),+                 ("FlickrPhoto", "http://www.flickr.com/photo.gne?id="),                  ("FlickrUser", "http://www.flickr.com/people/"),                  ("FloralWIKI", "http://www.floralwiki.co.uk/wiki/"),                  ("FlyerWiki-de", "http://de.flyerwiki.net/index.php/"),@@ -168,16 +146,17 @@                  ("ForthFreak", "http://wiki.forthfreak.net/index.cgi?"),                  ("Foundation", "http://wikimediafoundation.org/wiki/"),                  ("FoxWiki", "http://fox.wikis.com/wc.dll?Wiki~"),-                 ("FreeBio", "http://freebiology.org/wiki/"),                  ("FreeBSDman", "http://www.FreeBSD.org/cgi/man.cgi?apropos=1&query="),+                 ("FreeBio", "http://freebiology.org/wiki/"),                  ("FreeCultureWiki", "http://wiki.freeculture.org/index.php/"),-                 ("Freedomdefined", "http://freedomdefined.org/"),                  ("FreeFeel", "http://freefeel.org/wiki/"),+                 ("Freedomdefined", "http://freedomdefined.org/"),                  ("FreekiWiki", "http://wiki.freegeek.org/index.php/"),                  ("Freenode", "http://ganfyd.org/index.php?title="),+                 ("Gardenology", "http://www.gardenology.org/wiki/"),                  ("GaussWiki", "http://gauss.ffii.org/"),-                 ("Gentoo-Wiki", "http://gentoo-wiki.com/"),                  ("GenWiki", "http://wiki.genealogy.net/index.php/"),+                 ("Gentoo-Wiki", "http://gentoo-wiki.com/"),                  ("GlobalVoices", "http://cyber.law.harvard.edu/dyn/globalvoices/wiki/"),                  ("GlossarWiki", "http://glossar.hs-augsburg.de/"),                  ("GlossaryWiki", "http://glossary.hs-augsburg.de/"),@@ -189,35 +168,29 @@                  ("GreatLakesWiki", "http://greatlakeswiki.org/index.php/"),                  ("GuildWarsWiki", "http://www.wiki.guildwars.com/wiki/"),                  ("Guildwiki", "http://guildwars.wikia.com/wiki/"),-                 ("gutenberg", "http://www.gutenberg.org/etext/"),-                 ("gutenbergwiki", "http://www.gutenberg.org/wiki/"),                  ("H2Wiki", "http://halowiki.net/p/"),+                 ("HRFWiki", "http://fanstuff.hrwiki.org/index.php/"),+                 ("HRWiki", "http://www.hrwiki.org/index.php/"),                  ("HammondWiki", "http://www.dairiki.org/HammondWiki/index.php3?"),-                 ("heroeswiki", "http://heroeswiki.com/"),                  ("HerzKinderWiki", "http://www.herzkinderinfo.de/Mediawiki/index.php/"),-                 ("HKMule", "http://www.hkmule.com/wiki/"),-                 ("HolshamTraders", "http://www.holsham-traders.de/wiki/index.php/"),-                 ("HRWiki", "http://www.hrwiki.org/index.php/"),-                 ("HRFWiki", "http://fanstuff.hrwiki.org/index.php/"),-                 ("HumanCell", "http://www.humancell.org/index.php/"),                  ("HupWiki", "http://wiki.hup.hu/index.php/"),+                 ("IMDbCharacter", "http://www.imdb.com/character/ch/"),+                 ("IMDbCompany", "http://www.imdb.com/company/co/"),                  ("IMDbName", "http://www.imdb.com/name/nm/"),                  ("IMDbTitle", "http://www.imdb.com/title/tt/"),-                 ("IMDbCompany", "http://www.imdb.com/company/co/"),-                 ("IMDbCharacter", "http://www.imdb.com/character/ch/"),+                 ("IRC", "http://www.sil.org/iso639-3/documentation.asp?id="),                  ("Incubator", "http://incubator.wikimedia.org/wiki/"),-                 ("infoAnarchy", "http://www.infoanarchy.org/en/"),-                 ("Infosecpedia", "http://www.infosecpedia.org/pedia/index.php/"),+                 ("Infosecpedia", "http://infosecpedia.org/wiki/"),                  ("Infosphere", "http://theinfosphere.org/"),-                 ("IRC", "http://www.sil.org/iso639-3/documentation.asp?id="),                  ("Iuridictum", "http://iuridictum.pecina.cz/w/"),+                 ("JEFO", "http://esperanto-jeunes.org/wiki/"),+                 ("JSTOR", "http://www.jstor.org/journals/"),                  ("JamesHoward", "http://jameshoward.us/"),                  ("JavaNet", "http://wiki.java.net/bin/view/Main/"),                  ("Javapedia", "http://wiki.java.net/bin/view/Javapedia/"),-                 ("JEFO", "http://esperanto-jeunes.org/wiki/"),                  ("JiniWiki", "http://www.cdegroot.com/cgi-bin/jini?"),+                 ("Jira", "https://jira.toolserver.org/browse/"),                  ("JspWiki", "http://www.ecyrd.com/JSPWiki/Wiki.jsp?page="),-                 ("JSTOR", "http://www.jstor.org/journals/"),                  ("Kamelo", "http://kamelopedia.mormo.org/index.php/"),                  ("Karlsruhe", "http://ka.stadtwiki.net/"),                  ("KerimWiki", "http://wiki.oxus.net/"),@@ -226,25 +199,26 @@                  ("KontuWiki", "http://kontu.merri.net/wiki/"),                  ("KoslarWiki", "http://wiki.koslar.de/index.php/"),                  ("Kpopwiki", "http://www.kpopwiki.com/"),+                 ("LISWiki", "http://liswiki.org/wiki/"),+                 ("LQWiki", "http://wiki.linuxquestions.org/wiki/"),                  ("LinguistList", "http://linguistlist.org/forms/langs/LLDescription.cfm?code="),                  ("LinuxWiki", "http://www.linuxwiki.de/"),                  ("LinuxWikiDe", "http://www.linuxwiki.de/"),-                 ("LISWiki", "http://liswiki.org/wiki/"),                  ("LiteratePrograms", "http://en.literateprograms.org/"),                  ("Livepedia", "http://www.livepedia.gr/index.php?title="),                  ("Lojban", "http://www.lojban.org/tiki/tiki-index.php?page="),                  ("Lostpedia", "http://lostpedia.wikia.com/wiki/"),-                 ("LQWiki", "http://wiki.linuxquestions.org/wiki/"),                  ("LugKR", "http://lug-kr.sourceforge.net/cgi-bin/lugwiki.pl?"),                  ("Luxo", "http://toolserver.org/~luxo/contributions/contributions.php?user="),-                 ("lyricwiki", "http://lyrics.wikia.com/"),+                 ("MW", "http://www.mediawiki.org/wiki/"),+                 ("MWOD", "http://www.merriam-webster.com/cgi-bin/dictionary?book=Dictionary&va="),+                 ("MWOT", "http://www.merriam-webster.com/cgi-bin/thesaurus?book=Thesaurus&va="),                  ("Mail", "https://lists.wikimedia.org/mailman/listinfo/"),-                 ("mailarchive", "http://lists.wikimedia.org/pipermail/"),                  ("Mariowiki", "http://www.mariowiki.com/"),                  ("MarvelDatabase", "http://www.marveldatabase.com/wiki/index.php/"),-                 ("MeatBall", "http://www.usemod.com/cgi-bin/mb.pl?"),+                 ("MeatBall", "http://meatballwiki.org/wiki/"),                  ("MediaZilla", "https://bugzilla.wikimedia.org/"),-                 ("MemoryAlpha", "http://memory-alpha.org/en/wiki/"),+                 ("MemoryAlpha", "http://memory-alpha.org/wiki/"),                  ("MetaWiki", "http://sunir.org/apps/meta.pl?"),                  ("MetaWikiPedia", "http://meta.wikimedia.org/wiki/"),                  ("Mineralienatlas", "http://www.mineralienatlas.de/lexikon/index.php/"),@@ -255,62 +229,48 @@                  ("MozillaWiki", "http://wiki.mozilla.org/"),                  ("MozillaZineKB", "http://kb.mozillazine.org/"),                  ("MusicBrainz", "http://musicbrainz.org/doc/"),-                 ("MW", "http://www.mediawiki.org/wiki/"),-                 ("MWOD", "http://www.merriam-webster.com/cgi-bin/dictionary?book=Dictionary&va="),-                 ("MWOT", "http://www.merriam-webster.com/cgi-bin/thesaurus?book=Thesaurus&va="),-                 ("NetVillage", "http://www.netbros.com/?"),                  ("NKcells", "http://www.nkcells.info/wiki/index.php/"),                  ("NoSmoke", "http://no-smok.net/nsmk/"),                  ("Nost", "http://nostalgia.wikipedia.org/wiki/"),-                 ("OEIS", "http://www.research.att.com/~njas/sequences/"),-                 ("OldWikisource", "http://wikisource.org/wiki/"),+                 ("OEIS", "http://oeis.org/"),                  ("OLPC", "http://wiki.laptop.org/go/"),+                 ("OSI", "http://wiki.tigma.ee/index.php/"),+                 ("OTRS", "https://ticket.wikimedia.org/otrs/index.pl?Action=AgentTicketZoom&TicketID="),+                 ("OTRSwiki", "http://otrs-wiki.wikimedia.org/wiki/"),+                 ("OldWikisource", "http://wikisource.org/wiki/"),                  ("OneLook", "http://www.onelook.com/?ls=b&w="),                  ("OpenFacts", "http://openfacts.berlios.de/index-en.phtml?title="),-                 ("Openstreetmap", "http://wiki.openstreetmap.org/wiki/"),                  ("OpenWetWare", "http://openwetware.org/wiki/"),                  ("OpenWiki", "http://openwiki.com/?"),+                 ("Openlibrary", "http://openlibrary.org/"),+                 ("Openstreetmap", "http://wiki.openstreetmap.org/wiki/"),                  ("Opera7Wiki", "http://operawiki.info/"),                  ("OrganicDesign", "http://www.organicdesign.co.nz/"),-                 ("OrgPatterns", "http://www.bell-labs.com/cgi-user/OrgPatterns/OrgPatterns?"),                  ("OrthodoxWiki", "http://orthodoxwiki.org/"),-                 ("OSI", "http://wiki.tigma.ee/index.php/"),-                 ("OTRS", "https://ticket.wikimedia.org/otrs/index.pl?Action=AgentTicketZoom&TicketID="),-                 ("OTRSwiki", "http://otrs-wiki.wikimedia.org/wiki/"),                  ("OurMedia", "http://www.socialtext.net/ourmedia/index.cgi?"),+                 ("Outreach", "http://outreach.wikimedia.org/wiki/"),                  ("OutreachWiki", "http://outreach.wikimedia.org/wiki/"),-                 ("PaganWiki", "http://www.paganwiki.org/wiki/index.php?title="),-                 ("Panawiki", "http://wiki.alairelibre.net/wiki/"),-                 ("PangalacticOrg", "http://www.pangalactic.org/Wiki/"),+                 ("PHWiki", "http://wiki.pocketheaven.com/"),+                 ("PMEG", "http://www.bertilow.com/pmeg/.php"),+                 ("Panawiki", "http://wiki.alairelibre.net/index.php?title="),                  ("PatWIKI", "http://gauss.ffii.org/"),-                 ("PerlConfWiki", "http://perl.conf.hu/index.php/"),                  ("PerlNet", "http://perl.net.au/wiki/"),-                 ("PersonalTelco", "http://www.personaltelco.net/index.cgi/"),-                 ("PHWiki", "http://wiki.pocketheaven.com/"),+                 ("PersonalTelco", "http://www.personaltelco.net/"),                  ("PhpWiki", "http://phpwiki.sourceforge.net/phpwiki/index.php?"),                  ("PlanetMath", "http://planetmath.org/?op=getobj&from=objects&id="),-                 ("PMEG", "http://www.bertilow.com/pmeg/.php"),-                 ("PMWiki", "http://old.porplemontage.com/wiki/index.php/"),-                 ("PurlNet", "http://purl.oclc.org/NET/"),-                 ("pyrev", "http://svn.wikimedia.org/viewvc/pywikipedia?view=rev&revision="),+                 ("PyWiki", "http://c2.com/cgi/wiki?"),                  ("PythonInfo", "http://www.python.org/cgi-bin/moinmoin/"),                  ("PythonWiki", "http://www.pythonwiki.de/"),-                 ("PyWiki", "http://c2.com/cgi/wiki?"),-                 ("psycle", "http://psycle.sourceforge.net/wiki/"),-                 ("qcwiki", "http://wiki.quantumchemistry.net/index.php/"),                  ("Quality", "http://quality.wikimedia.org/wiki/"),-                 ("Qwiki", "http://qwiki.caltech.edu/wiki/"),-                 ("r3000", "http://prinsig.se/weekee/"),-                 ("RakWiki", "http://rakwiki.no-ip.info/"),-                 ("Raec", "http://www.raec.clacso.edu.ar:8080/raec/Members/raecpedia/"),-                 ("rev", "http://www.mediawiki.org/wiki/Special:Code/MediaWiki/"),-                 ("ReVo", "http://purl.org/NET/voko/revo/art/.html"),                  ("RFC", "http://tools.ietf.org/html/rfc"),-                 ("RheinNeckar", "http://wiki.rhein-neckar.de/index.php/"),-                 ("RoboWiki", "http://robowiki.net/?"),+                 ("ReVo", "http://purl.org/NET/voko/revo/art/.html"),                  ("ReutersWiki", "http://glossary.reuters.com/index.php/"),+                 ("RheinNeckar", "http://rhein-neckar-wiki.de/"),                  ("RoWiki", "http://wiki.rennkuckuck.de/index.php/"),-                 ("rtfm", "http://s23.org/wiki/"),+                 ("RoboWiki", "http://robowiki.net/?"),+                 ("SLWiki", "http://wiki.secondlife.com/wiki/"),+                 ("SMikipedia", "http://www.smiki.de/"),+                 ("SVGWiki", "http://wiki.svg.org/index.php/"),                  ("Scholar", "http://scholar.google.com/scholar?q="),                  ("SchoolsWP", "http://schools-wikipedia.org/wiki/"),                  ("Scores", "http://imslp.org/wiki/"),@@ -319,118 +279,89 @@                  ("SeaPig", "http://www.seapig.org/"),                  ("SeattleWiki", "http://seattlewiki.org/wiki/"),                  ("SeattleWireless", "http://seattlewireless.net/?"),-                 ("SLWiki", "http://wiki.secondlife.com/wiki/"),                  ("SenseisLibrary", "http://senseis.xmp.net/?"),-                 ("silcode", "http://www.sil.org/iso639-3/documentation.asp?id="),                  ("Slashdot", "http://slashdot.org/article.pl?sid="),-                 ("SMikipedia", "http://www.smiki.de/"),                  ("SourceForge", "http://sourceforge.net/"),-                 ("spcom", "http://spcom.wikimedia.org/wiki/"),                  ("Species", "http://species.wikimedia.org/wiki/"),                  ("Squeak", "http://wiki.squeak.org/squeak/"),-                 ("stable", "http://stable.toolserver.org/"),                  ("Strategy", "http://strategy.wikimedia.org/wiki/"),                  ("StrategyWiki", "http://strategywiki.org/wiki/"),                  ("Sulutil", "http://toolserver.org/~vvv/sulutil.php?user="),-                 ("Susning", "http://www.susning.nu/"),-                 ("Swtrain", "http://train.spottingworld.com/"),-                 ("svn", "http://svn.wikimedia.org/viewvc/mediawiki/?view=log"),-                 ("SVGWiki", "http://www.protocol7.com/svg-wiki/default.asp?"),                  ("SwinBrain", "http://mercury.it.swin.edu.au/swinbrain/index.php/"),                  ("SwingWiki", "http://www.swingwiki.org/"),+                 ("Swtrain", "http://train.spottingworld.com/"),+                 ("TESOLTaiwan", "http://www.tesol-taiwan.org/wiki/index.php/"),+                 ("TMBW", "http://tmbw.net/wiki/"),+                 ("TMwiki", "http://www.EasyTopicMaps.com/?page="),+                 ("TVIV", "http://tviv.org/wiki/"),+                 ("TVtropes", "http://www.tvtropes.org/pmwiki/pmwiki.php/Main/"),+                 ("TWiki", "http://twiki.org/cgi-bin/view/"),                  ("TabWiki", "http://www.tabwiki.com/index.php/"),-                 ("Takipedia", "http://www.takipedia.org/wiki/"),                  ("Tavi", "http://tavi.sourceforge.net/"),                  ("TclersWiki", "http://wiki.tcl.tk/"),                  ("Technorati", "http://www.technorati.com/search/"),-                 ("TEJO", "http://www.tejo.org/vikio/"),-                 ("TESOLTaiwan", "http://www.tesol-taiwan.org/wiki/index.php/"),+                 ("Tenwiki", "http://ten.wikipedia.org/wiki/"),                  ("Testwiki", "http://test.wikipedia.org/wiki/"),                  ("Thelemapedia", "http://www.thelemapedia.org/index.php/"),                  ("Theopedia", "http://www.theopedia.com/"),                  ("ThinkWiki", "http://www.thinkwiki.org/wiki/"),                  ("TibiaWiki", "http://tibia.erig.net/"),                  ("Ticket", "https://ticket.wikimedia.org/otrs/index.pl?Action=AgentTicketZoom&TicketNumber="),-                 ("TMBW", "http://tmbw.net/wiki/"),                  ("TmNet", "http://www.technomanifestos.net/?"),-                 ("TMwiki", "http://www.EasyTopicMaps.com/?page="),-                 ("TokyoNights", "http://wiki.tokyo-nights.com/wiki/"),                  ("Tools", "http://toolserver.org/"),-                 ("tswiki", "http://wiki.toolserver.org/view/"),-                 ("translatewiki", "http://translatewiki.net/wiki/"),                  ("Trash!Italia", "http://trashware.linux.it/wiki/"),                  ("Turismo", "http://www.tejo.org/turismo/"),-                 ("TVIV", "http://tviv.org/wiki/"),-                 ("TVtropes", "http://www.tvtropes.org/pmwiki/pmwiki.php/Main/"),-                 ("TWiki", "http://twiki.org/cgi-bin/view/"),-                 ("TwistedWiki", "http://purl.net/wiki/twisted/"),                  ("TyvaWiki", "http://www.tyvawiki.org/wiki/"),+                 ("USEJ", "http://www.tejo.org/usej/"),                  ("Uncyclopedia", "http://uncyclopedia.org/wiki/"),                  ("Unreal", "http://wiki.beyondunreal.com/wiki/"),                  ("Urbandict", "http://www.urbandictionary.com/define.php?term="),-                 ("USEJ", "http://www.tejo.org/usej/"),                  ("UseMod", "http://www.usemod.com/cgi-bin/wiki.pl?"),-                 ("usability", "http://usability.wikimedia.org/wiki/"),+                 ("VKoL", "http://kol.coldfront.net/thekolwiki/index.php/"),+                 ("VLOS", "http://www.thuvienkhoahoc.com/tusach/"),                  ("ValueWiki", "http://www.valuewiki.com/w/"),                  ("Veropedia", "http://en.veropedia.com/a/"),                  ("Vinismo", "http://vinismo.com/en/"),-                 ("VLOS", "http://www.thuvienkhoahoc.com/tusach/"),-                 ("VKoL", "http://kol.coldfront.net/thekolwiki/index.php/"),                  ("VoIPinfo", "http://www.voip-info.org/wiki/view/"),-                 ("WarpedView", "http://www.warpedview.com/mediawiki/index.php/"),-                 ("WebDevWikiNL", "http://www.promo-it.nl/WebDevWiki/index.php?page="),+                 ("WLUG", "http://www.wlug.org.nz/"),+                 ("WMF", "http://wikimediafoundation.org/wiki/"),                  ("Webisodes", "http://www.webisodes.org/"),-                 ("WebSeitzWiki", "http://webseitz.fluxent.com/wiki/"),-                 ("wg", "http://wg.en.wikipedia.org/wiki/"),                  ("Wiki", "http://c2.com/cgi/wiki?"),+                 ("WikiChristian", "http://www.wikichristian.org/index.php?title="),+                 ("WikiF1", "http://www.wikif1.org/"),+                 ("WikiFur", "http://en.wikifur.com/wiki/"),+                 ("WikiIndex", "http://wikiindex.org/"),+                 ("WikiLemon", "http://wiki.illemonati.com/"),+                 ("WikiMac-de", "http://apfelwiki.de/wiki/Main/"),+                 ("WikiSkripta", "http://www.wikiskripta.eu/index.php/"),+                 ("WikiTI", "http://wikiti.denglend.net/index.php?title="),+                 ("WikiTravel", "http://wikitravel.org/en/"),+                 ("WikiTree", "http://wikitree.org/index.php?title="),+                 ("WikiWeet", "http://wikiweet.nl/wiki/"),+                 ("WikiWikiWeb", "http://c2.com/cgi/wiki?"),                  ("Wikia", "http://www.wikia.com/wiki/c:"),                  ("WikiaSite", "http://www.wikia.com/wiki/c:"),-                 ("Wikianso", "http://www.ansorena.de/mediawiki/wiki/"),-                 ("Wikible", "http://wikible.org/en/"),                  ("Wikibooks", "http://en.wikibooks.org/wiki/"),                  ("Wikichat", "http://www.wikichat.org/"),-                 ("WikiChristian", "http://www.wikichristian.org/index.php?title="),                  ("Wikicities", "http://www.wikia.com/wiki/"),                  ("Wikicity", "http://www.wikia.com/wiki/c:"),-                 ("WikiF1", "http://www.wikif1.org/"),-                 ("WikiFur", "http://en.wikifur.com/wiki/"),-                 ("wikiHow", "http://www.wikihow.com/"),-                 ("WikiIndex", "http://wikiindex.com/"),-                 ("WikiLemon", "http://wiki.illemonati.com/"),                  ("Wikilivres", "http://wikilivres.info/wiki/"),-                 ("WikiMac-de", "http://apfelwiki.de/wiki/Main/"),-                 ("WikiMac-fr", "http://www.wikimac.org/index.php/"),                  ("Wikimedia", "http://wikimediafoundation.org/wiki/"),                  ("Wikinews", "http://en.wikinews.org/wiki/"),                  ("Wikinfo", "http://www.wikinfo.org/index.php/"),-                 ("Wikinurse", "http://wikinurse.org/media/index.php?title="),                  ("Wikinvest", "http://www.wikinvest.com/"),                  ("Wikipaltz", "http://www.wikipaltz.com/wiki/"),                  ("Wikipedia", "http://en.wikipedia.org/wiki/"),                  ("WikipediaWikipedia", "http://en.wikipedia.org/wiki/Wikipedia:"),                  ("Wikiquote", "http://en.wikiquote.org/wiki/"),-                 ("Wikireason", "http://wikireason.net/wiki/"),                  ("Wikischool", "http://www.wikischool.de/wiki/"),-                 ("wikisophia", "http://wikisophia.org/index.php?title="),                  ("Wikisource", "http://en.wikisource.org/wiki/"),                  ("Wikispecies", "http://species.wikimedia.org/wiki/"),                  ("Wikispot", "http://wikispot.org/?action=gotowikipage&v="),-                 ("Wikitech", "https://wikitech.wikimedia.org/view/"),-                 ("WikiTI", "http://wikiti.denglend.net/index.php?title="),-                 ("WikiTravel", "http://wikitravel.org/en/"),-                 ("WikiTree", "http://wikitree.org/index.php?title="),+                 ("Wikitech", "http://wikitech.wikimedia.org/view/"),                  ("Wikiversity", "http://en.wikiversity.org/wiki/"),-                 ("betawikiversity", "http://beta.wikiversity.org/wiki/"),-                 ("WikiWikiWeb", "http://c2.com/cgi/wiki?"),                  ("Wiktionary", "http://en.wiktionary.org/wiki/"),                  ("Wipipedia", "http://www.londonfetishscene.com/wipi/index.php/"),-                 ("WLUG", "http://www.wlug.org.nz/"),-                 ("wmau", "http://wikimedia.org.au/wiki/"),-                 ("wmcz", "http://meta.wikimedia.org/wiki/Wikimedia_Czech_Republic/"),-                 ("wmno", "http://no.wikimedia.org/wiki/"),-                 ("wmrs", "http://rs.wikimedia.org/wiki/"),-                 ("wmse", "http://se.wikimedia.org/wiki/"),-                 ("wmuk", "http://uk.wikimedia.org/wiki/"),                  ("Wm2005", "http://wikimania2005.wikimedia.org/wiki/"),                  ("Wm2006", "http://wikimania2006.wikimedia.org/wiki/"),                  ("Wm2007", "http://wikimania2007.wikimedia.org/wiki/"),@@ -438,16 +369,73 @@                  ("Wm2009", "http://wikimania2009.wikimedia.org/wiki/"),                  ("Wm2010", "http://wikimania2010.wikimedia.org/wiki/"),                  ("Wm2011", "http://wikimania2011.wikimedia.org/wiki/"),+                 ("Wm2011", "http://wikimania2011.wikimedia.org/wiki/"),                  ("Wmania", "http://wikimania.wikimedia.org/wiki/"),-                 ("WMF", "http://wikimediafoundation.org/wiki/"),+                 ("Wmteam", "http://wikimaniateam.wikimedia.org/wiki/"),+                 ("WoWWiki", "http://www.wowwiki.com/"),                  ("Wookieepedia", "http://starwars.wikia.com/wiki/"),                  ("World66", "http://www.world66.com/"),-                 ("WoWWiki", "http://www.wowwiki.com/"),                  ("Wqy", "http://wqy.sourceforge.net/cgi-bin/index.cgi?"),                  ("WurmPedia", "http://www.wurmonline.com/wiki/index.php/"),-                 ("WZNAN", "http://www.wikiznanie.ru/wiki/article/"),-                 ("Xboxic", "http://wiki.xboxic.com/"),                  ("ZRHwiki", "http://www.zrhwiki.ch/wiki/"),                  ("ZUM", "http://wiki.zum.de/"),                  ("ZWiki", "http://www.zwiki.org/"),+                 ("arXiv", "http://arxiv.org/abs/"),+                 ("betawiki", "http://translatewiki.net/wiki/"),+                 ("betawikiversity", "http://beta.wikiversity.org/wiki/"),+                 ("bugzilla", "https://bugzilla.wikimedia.org/show_bug.cgi?id="),+                 ("bulba", "http://bulbapedia.bulbagarden.net/wiki/"),+                 ("buzztard", "http://buzztard.org/index.php/"),+                 ("comune", "http://rete.comuni-italiani.it/wiki/"),+                 ("dbdump", "http://download.wikimedia.org//latest/"),+                 ("distributedproofreaders", "http://www.pgdp.net/wiki/"),+                 ("distributedproofreadersca", "http://www.pgdpcanada.net/wiki/index.php/"),+                 ("dmoz", "http://www.dmoz.org/"),+                 ("dmozs", "http://www.dmoz.org/cgi-bin/search?search="),+                 ("doom_wiki", "http://doom.wikia.com/wiki/"),+                 ("download", "http://download.wikimedia.org/"),+                 ("gutenberg", "http://www.gutenberg.org/etext/"),+                 ("gutenbergwiki", "http://www.gutenberg.org/wiki/"),+                 ("heroeswiki", "http://heroeswiki.com/"),+                 ("infoAnarchy", "http://www.infoanarchy.org/en/"),+                 ("lyricwiki", "http://lyrics.wikia.com/"),+                 ("mailarchive", "http://lists.wikimedia.org/pipermail/"),+                 ("psycle", "http://psycle.sourceforge.net/wiki/"),+                 ("pyrev", "http://svn.wikimedia.org/viewvc/pywikipedia?view=rev&revision="),+                 ("qcwiki", "http://wiki.quantumchemistry.net/index.php/"),+                 ("rev", "http://www.mediawiki.org/wiki/Special:Code/MediaWiki/"),+                 ("rtfm", "http://s23.org/wiki/"),+                 ("semantic-mw", "http://www.semantic-mediawiki.org/wiki/"),+                 ("silcode", "http://www.sil.org/iso639-3/documentation.asp?id="),+                 ("spcom", "http://spcom.wikimedia.org/wiki/"),+                 ("stable", "http://stable.toolserver.org/"),+                 ("svn", "http://svn.wikimedia.org/viewvc/mediawiki/?view=log"),+                 ("translatewiki", "http://translatewiki.net/wiki/"),+                 ("tswiki", "http://wiki.toolserver.org/view/"),+                 ("usability", "http://usability.wikimedia.org/wiki/"),+                 ("wg", "http://wg.en.wikipedia.org/wiki/"),+                 ("wikiHow", "http://www.wikihow.com/"),+                 ("wikisophia", "http://wikisophia.org/index.php?title="),+                 ("wmar", "http://www.wikimedia.org.ar/wiki/"),+                 ("wmau", "http://wikimedia.org.au/wiki/"),+                 ("wmca", "http://wikimedia.ca/index.php/"),+                 ("wmch", "http://www.wikimedia.ch/"),+                 ("wmcz", "http://meta.wikimedia.org/wiki/Wikimedia_Czech_Republic/"),+                 ("wmde", "http://wikimedia.de/wiki/"),+                 ("wmfi", "http://fi.wikimedia.org/wiki/"),+                 ("wmhk", "http://wikimedia.hk/index.php/"),+                 ("wmhu", "http://wiki.media.hu/wiki/"),+                 ("wmid", "http://www.wikimedia.or.id/wiki/"),+                 ("wmil", "http://www.wikimedia.org.il/"),+                 ("wmin", "http://wikimedia.in/wiki/"),+                 ("wmit", "http://wikimedia.it/index.php/"),+                 ("wmnl", "http://nl.wikimedia.org/wiki/"),+                 ("wmno", "http://no.wikimedia.org/wiki/"),+                 ("wmpl", "http://pl.wikimedia.org/wiki/"),+                 ("wmrs", "http://rs.wikimedia.org/wiki/"),+                 ("wmru", "http://ru.wikimedia.org/wiki/"),+                 ("wmse", "http://se.wikimedia.org/wiki/"),+                 ("wmtw", "http://wikimedia.tw/wiki/index.php5/"),+                 ("wmuk", "http://uk.wikimedia.org/wiki/"),+                 ("ĈEJ", "http://esperanto.blahus.cz/cxej/vikio/index.php/"),                  ("ZZZ", "http://wiki.zzz.ee/index.php/") ]
plugins/PigLatin.hs view
@@ -25,7 +25,7 @@ pigLatinStr x       = x  isConsonant :: Char -> Bool-isConsonant c = c `notElem` "aeiouAEIOU" +isConsonant c = c `notElem` "aeiouAEIOU"  capitalize :: String -> String capitalize "" = ""
plugins/Subst.hs view
@@ -17,11 +17,11 @@ plugin = mkPageTransformM substituteIntoBlock  substituteIntoBlock :: [Block] -> PluginM [Block]-substituteIntoBlock ((Para [Link ref ("!subst", _)]):xs) = -     do let target = inlinesToString ref +substituteIntoBlock ((Para [Link ref ("!subst", _)]):xs) =+     do let target = inlinesToString ref         cfg <- askConfig         let fs = filestoreFromConfig cfg-        article <- try $ liftIO (retrieve fs (target ++ ".page") Nothing) +        article <- try $ liftIO (retrieve fs (target ++ ".page") Nothing)         case article :: Either FileStoreError String of           Left  _    -> let txt = Str ("[" ++ target ++ "](!subst)")                             alt = "'" ++ target ++ "' doesn't exist. Click here to create it."
plugins/WebArchiver.hs view
@@ -3,55 +3,38 @@ (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.) +This module employs the archiver daemon <http://hackage.haskell.org/package/archiver> as a library; `cabal install archiver` will install it.+ Limitations: * Only parses Markdown, not ReST or any other format; this is because 'readMarkdown' is hardwired into it.+* No rate limitation or choking; will fire off all requests as fast as possible.+  If pages have more than 20 external links or so, this may result in your IP being temporarily+  banned by WebCite. To avoid this, you can use WebArchiverBot.hs instead, which will parse & dump+  URLs into a file processed by the archiver daemon (which *is* rate-limited).  By: Gwern Branwen; placed in the public domain -}  module WebArchiver (plugin) where  import Control.Concurrent (forkIO)-import Control.Monad (when)-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 Network.URL.Archiver as A (checkArchive)+import Network.Gitit.Interface (askUser, bottomUpM, liftIO, uEmail, Plugin(PreCommitTransform), Inline(Link)) import Text.Pandoc (defaultParserState, readMarkdown)  plugin :: Plugin plugin = PreCommitTransform archivePage --- archivePage :: (MonadIO m) => String -> ReaderT (Config, Maybe User) (StateT IO) String+-- archivePage :: String -> ReaderT PluginData (StateT Context IO) String archivePage x = do mbUser <- askUser                    let email = case mbUser of                         Nothing -> "nobody@mailinator.com"                         Just u  -> uEmail u                    let p = readMarkdown defaultParserState x                    -- force evaluation and archiving side-effects-                   _p' <- liftIO $ processWithM (archiveLinks email) p+                   _p' <- liftIO $ bottomUpM (archiveLinks email) p                    return x -- note: this is read-only - don't actually change page!  archiveLinks :: String -> Inline -> IO Inline-archiveLinks e x@(Link _ (uln, _)) = forkIO (checkArchive e uln) >> return x+archiveLinks e x@(Link _ (uln, _)) = forkIO (A.checkArchive e uln) >> return x archiveLinks _ x                   = return x---- | Error check the URL and then archive it both ways-checkArchive :: (MonadIO m) => String -> String -> m ()-checkArchive email url = when (isURI url) $ liftIO (webciteArchive email url >> alexaArchive url)--webciteArchive :: String -> String -> IO ()-webciteArchive email url = 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