Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Major reorganization and new wikipedia-like theme.

- Moved javascripts -> js.
- Moved stylesheets -> css.
- Moved images -> img.
- New screen.css, ie.css, and print.css based on bluetrip
  css framework and wikipedia css.
- Moved folder and page icons into img/icons.
- New two-column tabbed layout based on wikipedia's layout.
- Removed old banner; added lambda-bann.png and gitit-dog.png.
  gitit-dog is derived from http://flickr.com/photos/wolfhound/127936545/
  (creative commons 2 attribution license)
- Made gitit-dog the default logo (saved as logo.png in static/img/);
  users can simply replace this with their own logo.png.
- Updated sample config to include metadata for gitit logo.
- Changed wikiBanner -> wikiLogo, now a path rather than raw HTML.
- Better styling for login/register.
- Changed formattedPage to use page layout structure.
  Added page titles and tab selects.
- Don't give error on null email in registration.
- Added revision number below page title.
- Updated print css and added button for "printable version".
- Implemented "random page" button.
  • Loading branch information...
commit 419b973b0dde5322fd2dd6497ab5e7ab282b38a6 1 parent 81b44dc
@jgm authored
Showing with 1,010 additions and 138 deletions.
  1. +314 −0 BLUETRIP-LICENSE
  2. +172 −102 Gitit.hs
  3. +3 −3 Gitit/State.hs
  4. +6 −4 Makefile
  5. +21 −21 TODO
  6. 0  stylesheets/gitit.css → css/gitit-old.css
  7. 0  {stylesheets → css}/hk-pyg.css
  8. +20 −0 css/ie.css
  9. +53 −0 css/print.css
  10. +406 −0 css/screen.css
  11. +2 −2 data/SampleConfig.hs
  12. +13 −6 gitit.cabal
  13. BIN  images/bann.png
  14. BIN  img/gitit-dog.png
  15. BIN  img/icons/cross.png
  16. BIN  img/icons/doc.png
  17. BIN  img/icons/email.png
  18. BIN  img/icons/external.png
  19. BIN  img/icons/feed.png
  20. 0  {stylesheets → img/icons}/folder.png
  21. BIN  img/icons/im.png
  22. BIN  img/icons/key.png
  23. 0  {stylesheets → img/icons}/page.png
  24. BIN  img/icons/pdf.png
  25. BIN  img/icons/tick.png
  26. BIN  img/icons/xls.png
  27. 0  {images → img}/lambda-bann.png
  28. 0  {javascripts → js}/dragdiff.js
  29. 0  {javascripts → js}/folding.js
  30. 0  {javascripts → js}/jquery-ui.packed.js
  31. 0  {javascripts → js}/jquery.min.js
  32. 0  {javascripts → js}/preview.js
  33. 0  {javascripts → js}/search.js
  34. 0  {javascripts → js}/uploadForm.js
View
314 BLUETRIP-LICENSE
@@ -0,0 +1,314 @@
+Blueprint CSS Framework License
+----------------------------------------------------------------
+
+Copyright (c) 2007-2008 Olav Bjorkoy (olav at bjorkoy.com)
+
+The Blueprint CSS Framework is available for use in all personal or
+commercial projects, under both the (modified) MIT and the GPL license. You
+may choose the one that fits your project.
+
+
+The (modified) MIT License
+----------------------------------------------------------------
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice, and every other copyright notice found in this
+software, and all the attributions in every file, and this permission notice
+shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+
+The GPL License
+----------------------------------------------------------------
+
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
View
274 Gitit.hs
@@ -29,6 +29,7 @@ import Control.Exception (bracket)
import Prelude hiding (writeFile, readFile, putStrLn, putStr)
import System.Process
import System.Directory
+import System.Time
import Control.Concurrent
import System.FilePath
import Gitit.Git
@@ -72,9 +73,9 @@ main = do
hPutStrLn stderr $ "Starting server on port " ++ show (portNumber conf)
let debugger = if (debugMode conf) then debugFilter else id
tid <- forkIO $ simpleHTTP (Conf { validator = Nothing, port = portNumber conf }) $ debugger $
- [ dir "stylesheets" [ fileServe [] $ (staticDir conf) </> "stylesheets" ]
- , dir "images" [ fileServe [] $ (staticDir conf) </> "images" ]
- , dir "javascripts" [ fileServe [] $ (staticDir conf) </> "javascripts" ]
+ [ dir "css" [ fileServe [] $ (staticDir conf) </> "css" ]
+ , dir "img" [ fileServe [] $ (staticDir conf) </> "img" ]
+ , dir "js" [ fileServe [] $ (staticDir conf) </> "js" ]
] ++ wikiHandlers
waitForTermination
putStrLn "Shutting down..."
@@ -158,21 +159,29 @@ initializeWiki conf = do
setCurrentDirectory oldDir
staticExists <- doesDirectoryExist staticdir
unless staticExists $ do
- createDirectoryIfMissing True $ staticdir </> "stylesheets"
- let stylesheets = map ("stylesheets" </>) ["gitit.css", "hk-pyg.css", "folder.png", "page.png"]
+ createDirectoryIfMissing True $ staticdir </> "css"
+ let stylesheets = map ("css" </>) ["screen.css", "print.css", "ie.css", "hk-pyg.css"]
stylesheetpaths <- mapM getDataFileName stylesheets
zipWithM copyFile stylesheetpaths (map (staticdir </>) stylesheets)
- createDirectoryIfMissing True $ staticdir </> "javascripts"
+ createDirectoryIfMissing True $ staticdir </> "img" </> "icons"
+ let imgs = map ("img" </>) $ map ("icons" </>)
+ ["cross.png", "doc.png", "email.png", "external.png", "feed.png", "folder.png",
+ "im.png", "key.png", "page.png", "pdf.png", "tick.png", "xls.png"]
+ imgpaths <- mapM getDataFileName imgs
+ zipWithM copyFile imgpaths (map (staticdir </>) imgs)
+ logopath <- getDataFileName $ "img" </> "gitit-dog.png"
+ copyFile logopath (staticdir </> "img" </> "logo.png")
+ createDirectoryIfMissing True $ staticdir </> "js"
let javascripts = ["jquery.min.js", "jquery-ui.packed.js",
"folding.js", "dragdiff.js", "preview.js", "search.js", "uploadForm.js"]
- javascriptpaths <- mapM getDataFileName $ map ("javascripts" </>) javascripts
- zipWithM copyFile javascriptpaths $ map ((staticdir </> "javascripts") </>) javascripts
+ javascriptpaths <- mapM getDataFileName $ map ("js" </>) javascripts
+ zipWithM copyFile javascriptpaths $ map ((staticdir </> "js") </>) javascripts
hPutStrLn stderr $ "Created " ++ staticdir ++ " directory"
- jsMathExists <- doesDirectoryExist (staticdir </> "javascripts" </> "jsMath")
+ jsMathExists <- doesDirectoryExist (staticdir </> "js" </> "jsMath")
unless jsMathExists $ do
hPutStrLn stderr $ replicate 80 '*' ++
"\nWarning: jsMath not found.\n" ++
- "If you want support for math, copy the jsMath directory into " ++ staticdir ++ "/javascripts/\n" ++
+ "If you want support for math, copy the jsMath directory into " ++ staticdir ++ "/js/\n" ++
"jsMath can be obtained from http://www.math.union.edu/~dpvc/jsMath/\n" ++
replicate 80 '*'
@@ -192,6 +201,7 @@ wikiHandlers = [ handlePath "_index" GET indexPage
, handlePath "_logout" GET logoutUser
, handlePath "_upload" GET (ifLoggedIn "" uploadForm)
, handlePath "_upload" POST (ifLoggedIn "" uploadFile)
+ , handlePath "_random" GET randomPage
, withCommand "showraw" [ handlePage GET showRawPage ]
, withCommand "history" [ handlePage GET showPageHistory,
handle (not . isPage) GET showFileHistory ]
@@ -229,6 +239,7 @@ data Params = Params { pUsername :: String
, pFullName :: String
, pAccessCode :: String
, pWikiname :: String
+ , pPrintable :: Bool
, pOverwrite :: Bool
, pFilename :: String
, pFileContents :: B.ByteString
@@ -259,6 +270,7 @@ instance FromData Params where
em <- look "email" `mplus` return ""
na <- look "fullname" `mplus` return ""
wn <- look "wikiname" `mplus` return ""
+ pr <- (look "printable" >> return True) `mplus` return False
ow <- (look "overwrite" >>= return . (== "yes")) `mplus` return False
fn <- (lookInput "file" >>= return . fromMaybe "" . inputFilename) `mplus` return ""
fc <- (lookInput "file" >>= return . inputValue) `mplus` return B.empty
@@ -285,6 +297,7 @@ instance FromData Params where
, pEmail = em
, pFullName = na
, pWikiname = wn
+ , pPrintable = pr
, pOverwrite = ow
, pFilename = fn
, pFileContents = fc
@@ -435,6 +448,17 @@ showFileAsText file params = do
Nothing -> error "Unable to retrieve page contents."
Just c -> ok $ setContentType "text/plain; charset=utf-8" $ toResponse c
+randomPage :: String -> Params -> Web Response
+randomPage _ _ = do
+ files <- gitLsTree "HEAD" >>= return . map (unwords . drop 3 . words) . lines
+ let pages = map dropExtension $ filter (\f -> takeExtension f == ".page") files
+ if null pages
+ then error "No pages found!"
+ else do
+ TOD _ picosecs <- liftIO getClockTime
+ let newPage = pages !! ((fromIntegral picosecs `div` 1000000) `mod` length pages)
+ seeOther (urlForPage newPage) $ toResponse $ p << "Redirecting to a random page"
+
showPage :: String -> Params -> Web Response
showPage "" params = do
cfg <- query GetConfig
@@ -451,7 +475,7 @@ showPage page params = do
(if revision == "HEAD"
then ""
else "&" ++ urlEncodeVars [("logMsg", "Revert to " ++ revision)]) ++ "';")] << cont
- formattedPage [] ["jsMath/easy/load.js"] page params cont'
+ formattedPage (defaultPageLayout { pgScripts = ["jsMath/easy/load.js"]}) page params cont'
_ -> if revision == "HEAD"
then (unlessNoEdit $ ifLoggedIn "" $ editPage) page params
else error $ "Invalid revision: " ++ revision
@@ -467,7 +491,7 @@ uploadForm _ params = do
let origPath = pFilename params
let wikiname = pWikiname params `orIfNull` takeFileName origPath
let logMsg = pLogMsg params
- let upForm = form ! [X.method "post", enctype "multipart/form-data"] <<
+ let upForm = form ! [X.method "post", enctype "multipart/form-data"] << fieldset <<
[ p << [label << "File to upload:", br, afile "file" ! [value origPath] ]
, p << [label << "Name on wiki, including extension",
noscript << " (leave blank to use the same filename)", stringToHtml ":", br,
@@ -475,7 +499,7 @@ uploadForm _ params = do
primHtmlChar "nbsp", checkbox "overwrite" "yes", label << "Overwrite existing file"]
, p << [label << "Description of content or changes:", br, textfield "logMsg" ! [size "60", value logMsg],
submit "upload" "Upload"] ]
- formattedPage [HidePageControls] ["uploadForm.js"] page params upForm
+ formattedPage (defaultPageLayout { pgScripts = ["uploadForm.js"], pgShowPageTools = False, pgTabs = [], pgTitle = "Upload a file"} ) page params upForm
uploadFile :: String -> Params -> Web Response
uploadFile _ params = do
@@ -490,8 +514,6 @@ uploadFile _ params = do
let email = pEmail params
let overwrite = pOverwrite params
exists <- liftIO $ doesFileExist (repositoryPath cfg </> wikiname)
- -- these are the dangerous extensions in HAppS-Server.Server.HTTP.FileServe.mimeMap.
- -- this list should be updated if mimeMap changes:
let imageExtensions = [".png", ".jpg", ".gif"]
let errors = validate [ (null logMsg, "Description cannot be empty.")
, (null origPath, "File not found.")
@@ -512,7 +534,7 @@ uploadFile _ params = do
liftIO $ createDirectoryIfMissing True ((repositoryPath cfg) </> dir')
liftIO $ B.writeFile ((repositoryPath cfg) </> wikiname) fileContents
gitCommit wikiname (author, email) logMsg
- formattedPage [HidePageControls] [] page params $
+ formattedPage (defaultPageLayout { pgShowPageTools = False, pgTabs = [], pgTitle = "Upload successful" }) page params $
thediv << [ h2 << ("Uploaded " ++ show (B.length fileContents) ++ " bytes")
, if takeExtension wikiname `elem` imageExtensions
then p << "To add this image to a page, use:" +++
@@ -542,7 +564,7 @@ searchResults _ params = do
stringToHtml " ", anchor ! [href "#", theclass "showmatch", thestyle "display: none;"] << "[show matches]",
pre ! [theclass "matches"] << unlines contents])
(reverse $ sortBy (comparing (length . snd)) matches)
- formattedPage [HidePageControls] ["search.js"] page params htmlMatches
+ formattedPage (defaultPageLayout { pgShowPageTools = False, pgTabs = [], pgScripts = ["search.js"], pgTitle = "Search results"}) page params htmlMatches
-- Auxiliary function for searchResults
parseMatchLine :: String -> (String, String)
@@ -586,7 +608,7 @@ showHistory file page params = do
else []) ++
[stringToHtml "]"])]
let contents = ulist ! [theclass "history"] << zipWith versionToHtml hist [(length hist), (length hist - 1)..1]
- formattedPage [] ["dragdiff.js"] page params contents
+ formattedPage (defaultPageLayout { pgScripts = ["dragdiff.js"], pgSelectedTab = HistoryTab, pgTitle = ("Changes to " ++ page) }) page params contents
showActivity :: String -> Params -> Web Response
showActivity _ params = do
@@ -606,7 +628,7 @@ showActivity _ params = do
thespan ! [theclass "subject"] << logSubject entry, stringToHtml " (",
thespan ! [theclass "files"] << filesFor (logFiles entry),
stringToHtml ")"]) hist
- formattedPage [HidePageControls] [] page params (heading +++ contents)
+ formattedPage (defaultPageLayout { pgShowPageTools = False, pgTabs = [], pgTitle = "Recent changes" }) page params (heading +++ contents)
showPageDiff :: String -> Params -> Web Response
showPageDiff page params = showDiff (pathForPage page) page params
@@ -625,7 +647,7 @@ showDiff file page params = do
_ -> thespan << [tail l, "\n"]
let formattedDiff = thespan ! [theclass "detail"] << ("Changes from " ++ from) +++
pre ! [theclass "diff"] << map diffLineToHtml (drop 5 $ lines rawDiff)
- formattedPage [] [] page (params { pRevision = to }) formattedDiff
+ formattedPage defaultPageLayout page (params { pRevision = to }) formattedDiff
editPage :: String -> Params -> Web Response
editPage page params = do
@@ -647,13 +669,14 @@ editPage page params = do
let sha1Box = textfield "sha1" ! [thestyle "display: none", value sha1]
let editForm = gui (urlForPage page) ! [identifier "editform"] <<
[sha1Box,
- textarea ! [cols "80", name "editedText", identifier "editedText"] << contents,
+ textarea ! [cols "80", name "editedText", identifier "editedText"] << contents, br,
label << "Description of changes:", br,
textfield "logMsg" ! [size "76", value logMsg],
submit "update" "Save", primHtmlChar "nbsp",
submit "cancel" "Discard", br,
thediv ! [ identifier "previewpane" ] << noHtml ]
- formattedPage [HidePageControls, HideNavbar] ["preview.js"] page (params {pMessages = messages'}) editForm
+ formattedPage (defaultPageLayout { pgShowPageTools = False, pgSelectedTab = EditTab,
+ pgScripts = ["preview.js"], pgTitle = ("Editing " ++ page) }) page (params {pMessages = messages'}) editForm
confirmDelete :: String -> Params -> Web Response
confirmDelete page params = do
@@ -663,7 +686,7 @@ confirmDelete page params = do
, stringToHtml " "
, submit "cancel" "No, keep it!"
, br ]
- formattedPage [HidePageControls] [] page params confirmForm
+ formattedPage defaultPageLayout page params confirmForm
deletePage :: String -> Params -> Web Response
deletePage page params = do
@@ -728,7 +751,7 @@ indexPage _ params = do
let revision = pRevision params
files <- gitLsTree revision >>= return . map (unwords . drop 3 . words) . lines
let htmlIndex = fileListToHtml "/" $ map splitPath $ sort files
- formattedPage [HidePageControls] ["folding.js"] page params htmlIndex
+ formattedPage (defaultPageLayout { pgShowPageTools = False, pgTabs = [], pgScripts = ["folding.js"], pgTitle = "All pages" }) page params htmlIndex
-- | Map a list of nonempty lists onto a list of pairs of list heads and list of tails.
-- e.g. [[1,2],[1],[2,1]] -> [(1,[[2],[]]), (2,[[1]])]
@@ -769,77 +792,122 @@ pandocToHtml :: MonadIO m => Pandoc -> m Html
pandocToHtml pandocContents = do
cfg <- query GetConfig
return $ writeHtml (defaultWriterOptions { writerStandalone = False
- , writerHTMLMathMethod = JsMath (Just "/javascripts/jsMath/easy/load.js")
+ , writerHTMLMathMethod = JsMath (Just "/js/jsMath/easy/load.js")
, writerTableOfContents = tableOfContents cfg
}) $ processPandoc convertWikiLinks pandocContents
-data PageOption = HidePageControls | HideNavbar deriving (Eq, Show)
+-- | Abstract representation of page layout (tabs, scripts, etc.)
+data PageLayout = PageLayout
+ { pgTitle :: String
+ , pgScripts :: [String]
+ , pgShowPageTools :: Bool
+ , pgTabs :: [Tab]
+ , pgSelectedTab :: Tab
+ }
+
+data Tab = ViewTab | EditTab | HistoryTab deriving (Eq, Show)
+
+defaultPageLayout :: PageLayout
+defaultPageLayout = PageLayout
+ { pgTitle = ""
+ , pgScripts = []
+ , pgShowPageTools = True
+ , pgTabs = [ViewTab, EditTab, HistoryTab]
+ , pgSelectedTab = ViewTab
+ }
-- | Returns formatted page
-formattedPage :: [PageOption] -> [String] -> String -> Params -> Html -> Web Response
-formattedPage opts scripts page params htmlContents = do
+formattedPage :: PageLayout -> String -> Params -> Html -> Web Response
+formattedPage layout page params htmlContents = do
let revision = pRevision params
user <- getLoggedInUser params
cfg <- (query GetConfig)
- let stylesheetlinks = thelink ! [href "/stylesheets/gitit.css", rel "stylesheet",
- strAttr "media" "screen", thetype "text/css"] << noHtml +++
- thelink ! [href "/stylesheets/hk-pyg.css", rel "stylesheet",
- strAttr "media" "screen", thetype "text/css"] << noHtml +++
- thelink ! [href "/stylesheets/gitit-print.css", rel "stylesheet",
- strAttr "media" "print", thetype "text/css"] << noHtml
- let javascriptlinks = if null scripts
+ let stylesheetlinks =
+ if pPrintable params
+ then thelink ! [href "/css/print.css", rel "stylesheet", strAttr "media" "all", thetype "text/css"] << noHtml
+ else thelink ! [href "/css/screen.css", rel "stylesheet",
+ strAttr "media" "screen, projection", thetype "text/css"] << noHtml +++
+ primHtml "<!--[if IE]>" +++
+ thelink ! [href "/css/ie.css", rel "stylesheet",
+ strAttr "media" "screen, projection", thetype "text/css"] << noHtml +++
+ primHtml "<![endif]-->" +++
+ thelink ! [href "/css/hk-pyg.css", rel "stylesheet",
+ strAttr "media" "screen, projection", thetype "text/css"] << noHtml +++
+ thelink ! [href "/css/print.css", rel "stylesheet", strAttr "media" "print", thetype "text/css"] << noHtml
+ let javascriptlinks = if null (pgScripts layout)
then noHtml
else concatHtml $ map
- (\x -> script ! [src ("/javascripts/" ++ x), thetype "text/javascript"] << noHtml)
- (["jquery.min.js", "jquery-ui.packed.js"] ++ scripts)
- let title' = thetitle << (wikiTitle cfg ++ " - " ++ dropWhile (=='_') page)
+ (\x -> script ! [src ("/js/" ++ x), thetype "text/javascript"] << noHtml)
+ (["jquery.min.js", "jquery-ui.packed.js"] ++ pgScripts layout)
+ let pageTitle' = pgTitle layout `orIfNull` page
+ let title' = thetitle << (wikiTitle cfg ++ " - " ++ pageTitle')
let head' = header << [title', stylesheetlinks, javascriptlinks]
+ let searchbox = gui ("/_search") ! [identifier "searchform"] <<
+ [ textfield "patterns"
+ , submit "search" "Search" ]
let sitenav = thediv ! [theclass "sitenav"] <<
- gui ("/_search") ! [identifier "searchform"] <<
- (intersperse (primHtmlChar "bull")
- [ anchor ! [href "/", theclass "nav_link"] << "front"
- , anchor ! [href "/_index", theclass "nav_link"] << "index"
- , anchor ! [href "/_upload", theclass "nav_link"] << "upload"
- , anchor ! [href "/_activity", theclass "nav_link"] << "activity"
- , anchor ! [href "/Help", theclass "nav_link"] << "help" ] ++
- [ primHtmlChar "nbsp"
- , textfield "patterns" ! [theclass "search_field search_term"]
- , submit "search" "Search" ])
- let rawButton = anchor ! [href $ urlForPage page ++ "?revision=" ++ revision ++ "&showraw", theclass "nav_link"] << "raw"
- let delButton = anchor ! [href $ urlForPage page ++ "?delete", theclass "nav_link"] << "delete"
- let histButton = anchor ! [href $ urlForPage page ++ "?revision=" ++ revision ++ "&history", theclass "nav_link"] << "history"
- let editButton = anchor ! [href $ urlForPage page ++ "?edit&revision=" ++ revision ++
- if revision == "HEAD" then "" else "&" ++ urlEncodeVars [("logMsg", "Revert to " ++ revision)],
- theclass "nav_link"] << if revision == "HEAD" then "edit" else "revert"
- let buttons = intersperse (primHtmlChar "bull") $
- (if isPage page then [rawButton] else []) ++ [delButton, histButton] ++
- (if isPage page then [editButton] else [])
- let userbox = thediv ! [identifier "userbox"] <<
- case user of
- Just u -> anchor ! [href ("/_logout?" ++ urlEncodeVars [("destination", page)]), theclass "nav_link"] <<
- ("logout " ++ u)
- Nothing -> (anchor ! [href ("/_login?" ++ urlEncodeVars [("destination", page)]), theclass "nav_link"] <<
- "login") +++ primHtmlChar "bull" +++
- anchor ! [href ("/_register?" ++ urlEncodeVars [("destination", page)]), theclass "nav_link"] <<
- "register"
- let sitenavVis = if HideNavbar `elem` opts then "hidden" else "visible"
- let pageinfoVis = if HidePageControls `elem` opts then "none" else "show"
+ fieldset <<
+ [ legend << "Navigation"
+ , ulist << (map li
+ [ anchor ! [href "/"] << "Front page"
+ , anchor ! [href "/_index"] << "All pages"
+ , anchor ! [href "/_random"] << "Random page"
+ , anchor ! [href "/_activity"] << "Recent activity"
+ , anchor ! [href "/_upload"] << "Upload a file"
+ , anchor ! [href "/Help"] << "Help" ])
+ , searchbox ]
+ let tools = if pgShowPageTools layout
+ then thediv ! [theclass "pageTools"] <<
+ fieldset <<
+ [ legend << "Actions"
+ , ulist << (map li
+ [ anchor ! [href $ urlForPage page ++ "?revision=" ++ revision ++ "&showraw"] << "View page source"
+ , anchor ! [href $ urlForPage page ++ "?revision=" ++ revision ++ "&printable"] << "Printable version"
+ , anchor ! [href $ urlForPage page ++ "?delete"] << "Delete this page" ])
+ , exportBox page params ]
+ else noHtml
+ let tabli tab = if tab == pgSelectedTab layout
+ then li ! [theclass "selected"]
+ else li
+ let linkForTab HistoryTab = Just $ tabli HistoryTab << anchor ! [href $ urlForPage page ++ "?revision=" ++ revision ++ "&history"] << "history"
+ linkForTab ViewTab = Just $ tabli ViewTab << anchor ! [href $ urlForPage page ++ if revision == "HEAD" then "" else "?revision=" ++ revision] << "view"
+ linkForTab EditTab = if isPage page
+ then Just $ tabli EditTab << anchor ! [href $ urlForPage page ++ "?edit&revision=" ++ revision ++
+ if revision == "HEAD" then "" else "&" ++ urlEncodeVars [("logMsg", "Revert to " ++ revision)]] <<
+ if revision == "HEAD" then "edit" else "revert"
+ else Nothing
+ let buttons = ulist ! [theclass "tabs"] << mapMaybe linkForTab (pgTabs layout)
+ let userbox = case user of
+ Just u -> anchor ! [href ("/_logout?" ++ urlEncodeVars [("destination", page)])] <<
+ ("Logout " ++ u)
+ Nothing -> (anchor ! [href ("/_login?" ++ urlEncodeVars [("destination", page)])] <<
+ "Login") +++ primHtml "&nbsp;&bull;&nbsp;" +++
+ anchor ! [href ("/_register?" ++ urlEncodeVars [("destination", page)])] <<
+ "Get an account"
let messages = pMessages params
let htmlMessages = if null messages
then noHtml
else ulist ! [theclass "messages"] << map (li <<) messages
let body' = body << thediv ! [identifier "container"] <<
- [ thediv ! [identifier "banner"] << primHtml (wikiBanner cfg)
- , thediv ! [identifier "navbar", thestyle $ "visibility: " ++ sitenavVis] << [userbox, sitenav]
- , case page of
- '_':_ -> noHtml
- _ -> thediv ! [identifier "pageTitle"] << [ anchor ! [href $ urlForPage page] << (h1 << page) ]
- , thediv ! [identifier "content"] << [htmlMessages, htmlContents]
- , thediv ! [identifier "pageinfo", thestyle $ "display: " ++ pageinfoVis] <<
- [ thediv ! [theclass "pageControls"] <<
- ((thespan ! [theclass "details"] << revision) : buttons)
- , if isPage page then exportBox page params else noHtml]
- , thediv ! [identifier "footer"] << primHtml (wikiFooter cfg)
+ [ thediv ! [identifier "userbox"] << userbox
+ , thediv ! [identifier "sidebar"] <<
+ [ thediv ! [identifier "logo"] <<
+ anchor ! [href "/", title "Go to top page"] <<
+ case wikiLogo cfg of
+ Nothing -> noHtml
+ Just f -> image ! [src $ "/" ++ f]
+ , sitenav
+ , tools ]
+ , thediv ! [identifier "maincol"] <<
+ [ thediv ! [theclass "pageControls"] << buttons
+ , thediv ! [identifier "content"] <<
+ [ anchor ! [href $ urlForPage page] << (h1 ! [theclass "pageTitle"] << pageTitle')
+ , if revision == "HEAD"
+ then noHtml
+ else h2 ! [theclass "revision"] << ("Revision " ++ revision)
+ , htmlMessages
+ , htmlContents ]
+ , thediv ! [identifier "footer"] << primHtml (wikiFooter cfg) ]
]
ok $ toResponse $ head' +++ body'
@@ -847,16 +915,16 @@ formattedPage opts scripts page params htmlContents = do
loginForm :: Params -> Html
loginForm params =
let destination = pDestination params
- in gui "" ! [identifier "loginForm"] <<
+ in gui "" ! [identifier "loginForm"] << fieldset <<
[ textfield "sha1" ! [thestyle "display: none", value destination]
, label << "Username ", textfield "username" ! [size "15"], stringToHtml " "
, label << "Password ", X.password "password" ! [size "15"], stringToHtml " "
- , submit "login" "Login"
- , p << [ stringToHtml "If you do not have an account, "
- , anchor ! [href ("/_register?" ++ urlEncodeVars [("destination", destination)])] << "click here to register." ]]
+ , submit "login" "Login"] +++
+ p << [ stringToHtml "If you do not have an account, "
+ , anchor ! [href ("/_register?" ++ urlEncodeVars [("destination", destination)])] << "click here to get one." ]
loginUserForm :: String -> Params -> Web Response
-loginUserForm _ params = formattedPage [HidePageControls] [] "_login" params $ loginForm params
+loginUserForm _ params = formattedPage (defaultPageLayout { pgShowPageTools = False, pgTabs = [], pgTitle = "Login" }) "_login" params $ loginForm params
loginUser :: String -> Params -> Web Response
loginUser _ params = do
@@ -890,23 +958,23 @@ registerForm = do
Nothing -> noHtml
Just (prompt, _) -> label << prompt +++ br +++
X.password "accessCode" ! [size "15"] +++ br
- return $ gui "" ! [identifier "loginForm"] <<
- [ accessQ,
- label << "Username (at least 3 letters or digits):", br,
- textfield "username" ! [size "20"], stringToHtml " ", br,
- label << "Email (optional, will not be displayed on the Wiki):", br,
- textfield "email" ! [size "20"], br,
- textfield "fullname" ! [size "20", theclass "req"],
- label << "Password (at least 6 characters, including at least one non-letter):", br,
- X.password "password" ! [size "20"], stringToHtml " ", br,
- label << "Confirm Password:", br, X.password "password2" ! [size "20"], stringToHtml " ", br,
- submit "register" "Register" ]
+ return $ gui "" ! [identifier "loginForm"] << fieldset <<
+ [ accessQ
+ , label << "Username (at least 3 letters or digits):", br
+ , textfield "username" ! [size "20"], stringToHtml " ", br
+ , label << "Email (optional, will not be displayed on the Wiki):", br
+ , textfield "email" ! [size "20"], br
+ , textfield "fullname" ! [size "20", theclass "req"]
+ , label << "Password (at least 6 characters, including at least one non-letter):", br
+ , X.password "password" ! [size "20"], stringToHtml " ", br
+ , label << "Confirm Password:", br, X.password "password2" ! [size "20"], stringToHtml " ", br
+ , submit "register" "Register" ]
registerUserForm :: String -> Params -> Web Response
registerUserForm _ params = do
let page = "_register"
regForm <- registerForm
- formattedPage [HidePageControls] [] page params regForm
+ formattedPage (defaultPageLayout { pgShowPageTools = False, pgTabs = [], pgTitle = "Register for an account" }) page params regForm
registerUser :: String -> Params -> Web Response
registerUser _ params = do
@@ -930,7 +998,7 @@ registerUser _ params = do
, (not isValidAccessCode, "Incorrect response to access prompt.")
, (not (isValidUsername uname), "Username must be at least 3 charcaters, all letters or digits.")
, (not (isValidPassword pword), "Password must be at least 6 characters, with at least one non-letter.")
- , (not (isValidEmail email), "Email address appears invalid.")
+ , (not (null email) && not (isValidEmail email), "Email address appears invalid.")
, (pword /= pword2, "Password does not match confirmation.")
, (not (null fakeField), "You do not seem human enough.") ] -- fakeField is hidden in CSS (honeypot)
if null errors
@@ -938,7 +1006,8 @@ registerUser _ params = do
let passwordHash = SHA512.hash $ map c2w $ passwordSalt cfg ++ pword
update $ AddUser uname (User { uUsername = uname, uPassword = passwordHash, uEmail = email })
loginUser "/" (params { pUsername = uname, pPassword = pword })
- else formattedPage [HidePageControls] [] page (params { pMessages = errors }) regForm
+ else formattedPage (defaultPageLayout { pgShowPageTools = False, pgTabs = [], pgTitle = "Register for an account" })
+ page (params { pMessages = errors }) regForm
showHighlightedSource :: String -> Params -> Web Response
showHighlightedSource file params = do
@@ -947,7 +1016,7 @@ showHighlightedSource file params = do
Just source -> let lang' = head $ languagesByExtension $ takeExtension file
in case highlightAs lang' (filter (/='\r') source) of
Left _ -> noHandle
- Right res -> formattedPage [] [] file params $ formatAsXHtml [OptNumberLines] lang' res
+ Right res -> formattedPage defaultPageLayout file params $ formatAsXHtml [OptNumberLines] lang' res
Nothing -> noHandle
defaultRespOptions :: WriterOptions
@@ -1012,12 +1081,13 @@ exportFormats = [ ("LaTeX", respondLaTeX)
, ("RTF", respondRTF) ]
exportBox :: String -> Params -> Html
-exportBox page params =
+exportBox page params | isPage page =
gui (urlForPage page) ! [identifier "exportbox"] <<
- [ submit "export" "Export"
- , textfield "revision" ! [thestyle "display: none;", value (pRevision params)]
+ [ textfield "revision" ! [thestyle "display: none;", value (pRevision params)]
, select ! [name "format"] <<
- map ((\f -> option ! [value f] << ("as " ++ f)) . fst) exportFormats ]
+ map ((\f -> option ! [value f] << f) . fst) exportFormats
+ , submit "export" "Export" ]
+exportBox _ _ = noHtml
rawContents :: String -> Params -> Web (Maybe String)
rawContents file params = do
View
6 Gitit/State.hs
@@ -39,7 +39,7 @@ import Codec.Utils (Octet)
data Config = Config {
repositoryPath :: FilePath, -- path of git repository for pages
staticDir :: FilePath, -- path of static directory
- wikiBanner :: String, -- HTML to be included at top of pages
+ wikiLogo :: Maybe String, -- Nothing, or Just path to an image to be displayed at top left
wikiTitle :: String, -- title of wiki
wikiFooter :: String, -- HTML to be included at bottom of pages
tableOfContents :: Bool, -- should each page have an automatic table of contents?
@@ -59,9 +59,9 @@ defaultConfig :: Config
defaultConfig = Config {
repositoryPath = "wikidata",
staticDir = "static",
- wikiBanner = "",
+ wikiLogo = Just "/img/logo.png",
wikiTitle = "Wiki",
- wikiFooter = "Powered by Gitit",
+ wikiFooter = "powered by <a href=\"http://github.com/jgm/gitit/tree/master/\">gitit</a>",
tableOfContents = True,
maxUploadSize = 100000,
portNumber = 5001,
View
10 Makefile
@@ -3,10 +3,12 @@ gitit: Gitit.hs Gitit/Git.hs Gitit/State.hs
.PHONY: static clean
static:
- mkdir -p static/javascripts
- mkdir -p static/stylesheets
- cp javascripts/*.js static/javascripts/
- cp stylesheets/*.css static/stylesheets/
+ mkdir -p static/js
+ mkdir -p static/css
+ mkdir -p static/img
+ cp js/*.js static/js/
+ cp css/*.css static/css/
+ cp -r img/* static/img/
clean:
rm Gitit.hi Gitit.o Gitit/*.hi Gitit/*.o gitit
View
42 TODO
@@ -1,26 +1,26 @@
-- look at Wikipedia for formatting ideas:
-
- - page names: change spaces to underscores
- - change wikilink generator so that the underscores are inserted
- - add talk pages
- - use tabs for edit, history, discuss, etc.
- - left sidebar: search, export, upload, activity, index, help, front, etc.
- - wikipedia-style banner:
- <a style="background-image: url(http://upload.wikimedia.org/wikipedia/en/b/bc/Wiki.png);" href="/wiki/Main_Page">
- - add: random page? popular pages? pages last visited?
- - banner picture is just upper-left corner
- - login/register on top right
- - add print css
- - add "permanent link" (link to this revision)
- - add "related changes" to history page (like activity, but looks at
- only pages linked from this one)
- - store email with user info, and provide a lost password email
- - paging for long histories, activity lists
+- add talk pages
-- add literate haskell support to pandoc, and to gitit
+- add: popular pages? pages last visited?
+
+- add "permanent link" (link to this revision)
+
+- add "related changes" to history page (like activity, but looks at
+ only pages linked from this one)
+
+- recover lost email link?
+
+- paging for long histories, activity lists
-- a pandoc change: table of contents should come right before
- the first header, after any introductory text.
+- handle logging: see http://groups.google.com/group/HAppS/browse_thread/thread/2b412f24c53f5aa/4ef39b57c6062a87?lnk=gst&q=logging#4ef39b57c6062a87
+
+- ?? change wikilinks so that they are NOT relative (and change note
+ in Help about relative links). That is: insert a leading /
+ in the URL.
+
+- think about using a custom wikilink syntax, say {{Front Page}}.
+ could implement this with generics and a function: [Inline] -> [Inline].
+
+- add literate haskell support to pandoc, and to gitit
- smoother way to migrate when state format is changed?
one approach would be to save user information in a file.
View
0  stylesheets/gitit.css → css/gitit-old.css
File renamed without changes
View
0  stylesheets/hk-pyg.css → css/hk-pyg.css
File renamed without changes
View
20 css/ie.css
@@ -0,0 +1,20 @@
+/* ie.css */
+body {text-align:center;}
+.container {text-align:left;}
+* html .column {overflow-x:hidden;}
+* html legend {margin:-18px -8px 16px 0;padding:0;}
+ol {margin-left:2em;}
+sup {vertical-align:text-top;}
+sub {vertical-align:text-bottom;}
+html>body p code {*white-space:normal;}
+hr {margin:-8px auto 11px;}
+.container ul { list-style: disc outside; margin-left: 2em; } /* IE can't handle :before and :after */
+.container ul li { text-indent: 0; margin-left: 0; }
+.container legend { margin-bottom: 1.6em; } /* IE form margin bug */
+sup, sub { font-size: 100%; } /* IE superscript & subscript bug */
+.container blockquote p, #content blockquote ul, #content blockquote ol, #content blockquote dl, #content blockquote pre, #content blockquote address,
+.container blockquote table, #content blockquote form, #content blockquote h1, #content blockquote h2, #content blockquote h3, #content blockquote h4, #content blockquote h5, #content blockquote h6 { margin-top: .8em; margin-bottom: .8em; } /* IE can't handle :first-child */
+* html .container textarea, * html .container input { padding: 0; } /* IE < 7 form fix */
+.container input[type='submit'], .container input[type='button'] { padding: 0; } /* IE 7 button fix */
+.container legend+* { margin-top: 0; } /* we already added legend margin */
+a abbr, a acronym { text-decoration: underline; } /* IE 7 bug */
View
53 css/print.css
@@ -0,0 +1,53 @@
+/* --------------------------------------------------------------
+ gitit print css
+ borrows heavily from Mike Crittenden's BlueTripCSS framework (GPL)
+ and from Wikipedia's CSS.
+-------------------------------------------------------------- */
+
+body {
+width:100% !important;
+margin:0 !important;
+padding:0 !important;
+line-height: 1.4;
+word-spacing:1.1pt;
+letter-spacing:0.2pt; font-family: "Times New Roman", serif; color: #000; background: none; font-size: 12pt; }
+
+/*Headings */
+h1,h2,h3,h4,h5,h6 { font-family: Helvetica, Arial, sans-serif; }
+h1{font-size:19pt;}
+h2{font-size:17pt;}
+h3{font-size:15pt;}
+h4,h5,h6{font-size:12pt;}
+
+code { font: 10pt Courier, monospace; }
+blockquote { margin: 1.3em; padding: 1em; font-size: 10pt; }
+hr { background-color: #ccc; }
+
+/* Images */
+img { float: left; margin: 1em 1.5em 1.5em 0; }
+a img { border: none; }
+
+/* Links */
+a:link, a:visited { background: transparent; font-weight: 700; text-decoration: underline;color:#333; }
+a:link[href^="http://"]:after, a[href^="http://"]:visited:after { content: " (" attr(href) ") "; font-size: 90%; }
+a[href^="http://"] {color:#000; }
+
+/* Table */
+table { margin: 1px; text-align:left; }
+th { border-bottom: 1px solid #333; font-weight: bold; }
+td { border-bottom: 1px solid #333; }
+th,td { padding: 4px 10px 4px 0; }
+tfoot { font-style: italic; }
+caption { background: #fff; margin-bottom:2em; text-align:left; }
+thead {display: table-header-group;}
+tr {page-break-inside: avoid;}
+
+/*hide various parts from the site*/
+
+#maincol { margin-left: 1em; margin-right: 1em; border: none; }
+#content { border: none; }
+#sidebar, div.pageControls, #userbox {display:none;}
+#toc { display: none; }
+h1 a:link, h2 a:link, h3 a:link, h4 a:link, h5 a:link, h6 a:link { text-decoration: none; }
+h1.pageTitle { font-size: 220%; }
+#footer { border-top: 1px solid black; margin-top: 1em; padding-top: 1em; }
View
406 css/screen.css
@@ -0,0 +1,406 @@
+/* -----------------------------------------------------------------------
+ gitit screen css
+ borrows heavily from Mike Crittenden's BlueTripCSS framework (GPL)
+ and from Wikipedia's CSS.
+----------------------------------------------------------------------- */
+
+/* MEYER RESET */
+html, body, div, span, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, code, del, dfn, em, img, q, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td {margin:0;padding:0;border:0;font-weight:inherit;font-style:inherit;font-size:100%;font-family:inherit;vertical-align:baseline;}
+body {line-height:1.5;}
+table {border-collapse:separate;border-spacing:0;}
+caption, th, td {text-align:left;font-weight:normal;}
+table, td, th {vertical-align:middle;}
+blockquote:before, blockquote:after, q:before, q:after {content:"";}
+blockquote, q {quotes:"" "";}
+a img {border:none;}
+
+/* BASIC TYPOGRAPHY */
+
+html { font-family: helvetica, "microsoft sans serif", arial, sans-serif; }
+strong, th, thead td, h1, h2, h3, h4, h5, h6 { font-weight: bold; }
+cite, em, dfn { font-style: italic; }
+code, kbd, samp, pre, tt, var, textarea { font-family: monospace; }
+del { text-decoration: line-through; color: #666; }
+ins, dfn { border-bottom: 1px solid #ccc; }
+small, sup, sub { font-size: 85%; }
+abbr, acronym { text-transform: uppercase; font-size: 85%; letter-spacing: .1em; }
+a abbr, a acronym { border: none; }
+abbr[title], acronym[title], dfn[title] { cursor: help; border-bottom: 1px solid #ccc; }
+sup { vertical-align: super; }
+sub { vertical-align: sub; }
+
+/* QUOTES */
+
+blockquote { border-top: 1px solid #ccc; border-bottom: 1px solid #ccc; color: #666; }
+blockquote *:first-child:before { content: "\201C"; }
+blockquote *:first-child:after { content: "\201D"; }
+
+/* FORMS */
+
+fieldset { padding:1.4em; margin: 0 0 1.5em 0; border: 1px solid #ccc; }
+legend { font-weight: bold; font-size:1.2em; }
+textarea, input[type='text'], input[type='password'], select { border: 1px solid #ccc; background: #fff; }
+textarea:hover, input[type='text']:hover, input[type='password']:hover, select:hover { border-color: #aaa; }
+textarea:focus, input[type='text']:focus, input[type='password']:focus, select:focus { border-color: #888; outline: 2px solid #ffffaa; }
+input, select { cursor: pointer; }
+input[type='text'] { cursor: text; }
+
+/* BASE SIZES */
+
+body { font-size: 1em; line-height: 1.6em; }
+
+hr {
+ height: 1px;
+ color: #aaa;
+ background-color: #aaa;
+ border: 0;
+ margin: .2em 0 .2em 0;
+}
+
+h1, h2, h3, h4, h5, h6 {
+ color: black;
+ background: none;
+ font-weight: normal;
+ margin: 0;
+ padding-top: .5em;
+ padding-bottom: .17em;
+ border-bottom: 1px solid #aaa;
+}
+
+h1.pageTitle { font-size: 220%; margin: 0.2em 0 .5em; }
+h1 { font-size: 150%; margin: 1.07em 0 .535em; }
+h2 { font-size: 132%; margin: 1.14em 0 .57em; }
+h3 { font-size: 116%; margin: 1.23em 0 .615em; }
+h4 { font-size: 100%; margin: 1.33em 0 .67em; }
+h5 { font-size: 88%; margin: 1.6em 0 .8em; }
+h6 { font-size: 80%; margin: 1.6em 0 .8em; }
+
+ul {
+ line-height: 1.5em;
+ list-style-type: square;
+ margin: .3em 0 0 1.5em;
+ padding: 0;
+ list-style-image: url(bullet.gif);
+}
+ol {
+ line-height: 1.5em;
+ margin: .3em 0 0 3.2em;
+ padding: 0;
+ list-style-image: none;
+}
+li {
+ margin-bottom: .1em;
+}
+dt {
+ font-weight: bold;
+ margin-bottom: .1em;
+}
+dl {
+ margin-top: .2em;
+ margin-bottom: .5em;
+}
+dd {
+ line-height: 1.5em;
+ margin-left: 2em;
+ margin-bottom: .1em;
+}
+
+/* TABLES */
+
+table { border-top: 1px solid #ccc; border-left: 1px solid #ccc; }
+th, td { border-bottom: 1px solid #ddd; border-right: 1px solid #ccc; }
+
+/* MARGINS & PADDINGS */
+
+blockquote *:first-child { margin: .8em 0; }
+hr, p, ul, ol, dl, blockquote, address, table, pre, form { margin-bottom: 1.4em; }
+/* NOTE: Calulate header margins: TOP: 1.6em/size, BOTTOM: 1.6em/size/2 */
+th, td { padding: .8em; }
+caption { padding-bottom: .8em; } /* padding instead of margin for IE */
+blockquote { padding: 0 1em; margin: 1.6em 0; }
+legend { padding-left: .8em; padding-right: .8em; }
+legend+* { margin-top: 1em; } /* compensates for the opera margin bug */
+textarea, input { padding: .3em .4em .15em .4em; }
+select { padding: .1em .2em 0 .2em; }
+option { padding: 0 .4em; }
+a { position: relative; padding: 0.3em 0 .1em 0; } /* for larger click-area */
+dt { margin-top: .8em; margin-bottom: .4em; }
+ul { margin-left: 1.5em; }
+ol { margin-left: 2.35em; }
+ol ol, ul ol { margin-left: 2.5em; }
+form div { margin-bottom: .8em; }
+
+/* COLORS */
+
+a:link { text-decoration: underline; color: #36c; }
+a:visited { text-decoration: underline; color: #99c; }
+a:hover { text-decoration: underline; color: #c33; }
+a:active, a:focus { text-decoration: underline; color: #000; }
+/* code, pre { color: #c33; } /* very optional, but still useful. W3C uses about the same colors for codes */
+
+/* TEXT CLASSES */
+
+.small {font-size:.8em;margin-bottom:1.875em;line-height:1.875em;}
+.large {font-size:1.2em;line-height:2.5em;margin-bottom:1.25em;}
+.hide {display:none;}
+.quiet {color:#666;}
+.loud {color:#000;}
+.highlight {background:#ff0;}
+.top {margin-top:0;padding-top:0;}
+.bottom {margin-bottom:0;padding-bottom:0;}
+.thin {font-weight: lighter;}
+.error, .notice, .success {padding:.8em;margin-bottom:1.6em;border:2px solid #ddd;}
+.error {background:#FBE3E4;color:#8a1f11;border-color:#FBC2C4;}
+.notice {background:#FFF6BF;color:#514721;border-color:#FFD324;}
+.success {background:#E6EFC2;color:#264409;border-color:#C6D880;}
+.error a {color:#8a1f11; background:none; padding:0; margin:0; }
+.notice a {color:#514721; background:none; padding:0; margin:0; }
+.success a {color:#264409; background:none; padding:0; margin:0; }
+.center {text-align: center;}
+
+/*---------STYLES FOR BUTTONS----------*/
+/* Demo: particletree.com/features/rediscovering-the-button-element */
+/*
+ <button type="submit" class="button positive">
+ <img src="css/blueprint/plugins/buttons/icons/tick.png" alt=""/> Save
+ </button>
+
+ <a class="button" href="/password/reset/">
+ <img src="css/blueprint/plugins/buttons/icons/key.png" alt=""/> Change Password
+ </a>
+
+ <a href="#" class="button negative">
+ <img src="css/blueprint/plugins/buttons/icons/cross.png" alt=""/> Cancel
+ </a>
+*/
+
+a.button, button {
+ margin:0 0.583em 0.667em 0;
+ padding:5px 10px 5px 7px; /* Links */
+ border:1px solid #dedede;
+ border-top:1px solid #eee;
+ border-left:1px solid #eee;
+ background-color:#f5f5f5;
+ font-family:"Lucida Grande", Tahoma, Arial, Verdana, sans-serif;
+ font-size:100%;
+ line-height:130%;
+ text-decoration:none;
+ font-weight:bold;
+ color:#565656;
+ cursor:pointer;
+}
+button {
+ width:auto;
+ overflow:visible;
+ padding:4px 10px 3px 7px; /* IE6 */
+}
+button[type] {
+ padding:4px 10px 4px 7px; /* Firefox */
+ line-height:17px; /* Safari */
+}
+
+*:first-child+html button[type] {
+ padding:4px 10px 3px 7px; /* IE7 */
+}
+
+button img, a.button img{
+ margin:0 3px -3px 0 !important;
+ padding:0;
+ border:none;
+ width:16px;
+ height:16px;
+ float:none;
+}
+
+input.search_term { width: 95% }
+
+/* Standard Buttons */
+button:hover, a.button:hover{
+ background-color:#dff4ff;
+ border:1px solid #c2e1ef;
+ color:#336699;
+}
+a.button:active, button:active{
+ background-color:#6299c5;
+ border:1px solid #6299c5;
+ color:#fff;
+}
+
+h1 > a:link, h1 > a:active, h1 > a:hover, h1 > a:focus, h1 > a:visited,
+h2 > a:link, h2 > a:active, h2 > a:hover, h2 > a:focus, h2 > a:visited,
+h3 > a:link, h3 > a:active, h3 > a:hover, h3 > a:focus, h3 > a:visited,
+h4 > a:link, h4 > a:active, h4 > a:hover, h4 > a:focus, h4 > a:visited,
+h5 > a:link, h5 > a:active, h5 > a:hover, h5 > a:focus, h5 > a:visited,
+h6 > a:link, h6 > a:active, h6 > a:hover, h6 > a:focus, h6 > a:visited {
+ color: black;
+ text-decoration: none;
+}
+body {
+ font: x-small sans-serif;
+ background: #f9f9f9;
+ color: black;
+ margin: 0;
+ padding: 0;
+}
+#container {
+ font-size: 120%;
+ margin: 0 ;
+ padding: 0;
+}
+
+#userbox { float: right; font-weight: bold; margin: 1em; }
+#logo { min-height: 50px; }
+#sidebar { width: 12em; float: left; margin-right: 10px; }
+#sidebar fieldset {
+ background-color: white;
+ margin-bottom: 0.3em;
+ padding: 0;
+}
+#sidebar fieldset, #sidebar fieldset legend {
+ font-weight: normal;
+ font-size: 95%;
+}
+#sidebar ul {
+ padding: 0;
+ margin: 0;
+ margin-left: 1.6em;
+ line-height: 1.5em;
+}
+#sidebar ul li { color: #888; }
+#maincol { position: absolute; margin-left: 13em; margin-top: 1em; padding-top: 2em; margin-right: 0; }
+#content { border: 1px solid #ccc; background-color: #fff; padding: 1em; font-size: 110%; line-height: 150%; }
+#pageControls { }
+div#toc {
+ float: right;
+ background-color: #f9f9f9;
+ border: 10px solid white;
+ margin: 0.8em;
+ margin-right: 0;
+ padding: 0.4em;
+}
+#toc ul {
+ margin: 0;
+ padding-left: 1em;
+ list-style: none;
+}
+#toc > ul {
+ margin-right: 1em;
+}
+/* .req is used to hide a honeypot in a form */
+.req {
+ display: none;
+}
+ul.messages > li {
+ color: red;
+ list-style: square;
+ font-weight: bold;
+}
+ul.tabs {
+ padding: 0;
+ margin: 0;
+}
+ul.tabs li {
+ display: inline;
+ border: 1px solid #ccc;
+ border-bottom: none;
+ border-collapse: collapse;
+ padding: 0 0.6em 0 0.6em;
+ margin: 0 0 0 1.2em;
+ overflow: visible;
+ background: white;
+ line-height: 1.2em;
+}
+ul.tabs li.selected {
+ border-bottom: 1px solid white;
+}
+ul.tabs li a {
+ text-decoration: none;
+ font-size: 95%;
+ font-weight: bold;
+ margin: 0;
+ z-index: 0;
+ color: #36c;
+}
+
+.folding ul {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+}
+.folding li {
+ list-style: none;
+ background-position: 0 1px;
+ background-repeat: no-repeat;
+ padding-left: 20px;
+}
+.folding li.page {
+ background-image: url(/img/icons/page.png);
+}
+.folding li.folder {
+ background-image: url(/img/icons/folder.png);
+}
+
+.folding a {
+ color: #000000;
+ cursor: pointer;
+ text-decoration: none;
+}
+.folding a:hover {
+ text-decoration: underline;
+}
+#sidebar input, #sidebar select {
+ font-size: 95%;
+ padding: 0.1em;
+}
+#exportbox select {
+ width: 8em;
+ border: 1px solid #ccc;
+ padding: 0;
+}
+#exportbox {
+ margin: 0.3em 0 0.5em 0.4em;
+ padding: 0;
+}
+#sidebar input[type='submit'] {
+ border: none;
+ background-color: #ccc;
+ color: white;
+}
+#searchform {
+ padding: 0;
+ margin: 0.3em 0 0.5em 0.4em;
+}
+#searchform input[type='text'] {
+ width: 7.5em;
+ border: 1px solid #ccc;
+}
+.search_result { margin-bottom: 15px; }
+.search_result .match {
+ line-height: 1em;
+ margin-bottom: 15px;
+}
+pre.matches {
+ font-size: .85em;
+ margin: 0;
+ padding: 0;
+}
+#editform textarea {
+ height: 25em;
+ width: 98%;
+}
+.added { background-color: yellow; }
+.deleted { text-decoration: line-through; color: gray; }
+h2.revision {
+ font-size: 100%;
+ color: #ccc;
+ font-style: italic;
+ border: none;
+ margin: 0 0 0.5em 0;
+ padding: 0;
+}
+#footer {
+ padding: 1em;
+ color: #888;
+ text-align: center;
+ font-size: 95%;
+}
View
4 data/SampleConfig.hs
@@ -1,9 +1,9 @@
Config {
repositoryPath = "wikidata",
staticDir = "static",
-wikiBanner = "<img src=\"/images/bann.png\" alt=\"banner\"",
+wikiLogo = Just "/img/logo.png",
wikiTitle = "Wiki",
-wikiFooter = "Powered by Gitit",
+wikiFooter = "Powered by Gitit\n<!-- Logo courtesy of http://flickr.com/photos/wolfhound/127936545/, licensed under Creative Commons Attribution license -->",
tableOfContents = False,
maxUploadSize = 100000,
portNumber = 5001,
View
19 gitit.cabal
@@ -20,12 +20,19 @@ license: GPL
license-file: LICENSE
author: John MacFarlane
maintainer: jgm@berkeley.edu
-data-files: stylesheets/gitit.css, stylesheets/hk-pyg.css,
- stylesheets/folder.png, stylesheets/page.png,
- javascripts/dragdiff.js, javascripts/folding.js,
- javascripts/jquery.min.js, javascripts/uploadForm.js,
- javascripts/jquery-ui-personalized-1.6rc2.min.js,
- javascripts/preview.js, javascripts/search.js,
+data-files: css/screen.css, css/print.css, css/ie.css,
+ css/hk-pyg.css,
+ img/gitit-dog.png,
+ img/icons/folder.png, img/icons/page.png,
+ img/icons/cross.png, img/icons/doc.png, img/icons/email.png,
+ img/icons/external.png, img/icons/external.png, img/icons/feed.png,
+ img/icons/folder.png, img/icons/im.png, img/icons/key.png,
+ img/icons/page.png, img/icons/pdf.png, img/icons/tick.png,
+ img/icons.xls.png, img/grid.png,
+ js/dragdiff.js, js/folding.js,
+ js/jquery.min.js, js/uploadForm.js,
+ js/jquery-ui.packed.js,
+ js/preview.js, js/search.js,
data/post-update, data/FrontPage.page, data/Help.page,
README.markdown, data/SampleConfig.hs
View
BIN  images/bann.png
Deleted file not rendered
View
BIN  img/gitit-dog.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  img/icons/cross.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  img/icons/doc.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  img/icons/email.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  img/icons/external.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  img/icons/feed.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
0  stylesheets/folder.png → img/icons/folder.png
File renamed without changes
View
BIN  img/icons/im.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  img/icons/key.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
0  stylesheets/page.png → img/icons/page.png
File renamed without changes
View
BIN  img/icons/pdf.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  img/icons/tick.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  img/icons/xls.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
0  images/lambda-bann.png → img/lambda-bann.png
File renamed without changes
View
0  javascripts/dragdiff.js → js/dragdiff.js
File renamed without changes
View
0  javascripts/folding.js → js/folding.js
File renamed without changes
View
0  javascripts/jquery-ui.packed.js → js/jquery-ui.packed.js
File renamed without changes
View
0  javascripts/jquery.min.js → js/jquery.min.js
File renamed without changes
View
0  javascripts/preview.js → js/preview.js
File renamed without changes
View
0  javascripts/search.js → js/search.js
File renamed without changes
View
0  javascripts/uploadForm.js → js/uploadForm.js
File renamed without changes
Please sign in to comment.
Something went wrong with that request. Please try again.