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 +39/−0
- Network/Gitit.hs +27/−24
- Network/Gitit/Authentication.hs +86/−11
- Network/Gitit/Config.hs +16/−1
- Network/Gitit/ContentTransformer.hs +27/−28
- Network/Gitit/Export.hs +5/−5
- Network/Gitit/Framework.hs +42/−48
- Network/Gitit/Handlers.hs +27/−26
- Network/Gitit/Initialize.hs +1/−1
- Network/Gitit/Layout.hs +5/−5
- Network/Gitit/Page.hs +14/−14
- Network/Gitit/Rpxnow.hs +81/−0
- Network/Gitit/Server.hs +3/−3
- Network/Gitit/Types.hs +21/−15
- Network/Gitit/Util.hs +2/−2
- data/default.conf +19/−6
- data/static/css/screen.css +16/−1
- data/static/js/preview.js +5/−0
- data/static/js/uploadForm.js +4/−1
- expireGititCache.hs +1/−1
- gitit.cabal +10/−8
- gitit.hs +26/−2
- plugins/Interwiki.hs +149/−161
- plugins/PigLatin.hs +1/−1
- plugins/Subst.hs +3/−3
- plugins/WebArchiver.hs +11/−28
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 << ("")@@ -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