merge master branch

This commit is contained in:
Luite Stegeman 2012-03-13 11:02:33 +01:00
commit 00b9acd98d
43 changed files with 530 additions and 493 deletions

View File

@ -95,9 +95,12 @@ virthualenv --name=yesod
#### individual cabal packages
~~~ { .bash }
# install and test all packages
# install and test all packages in a repo
./scripts/install
# If things seem weird, you may need to do a clean.
./scripts/install --clean
# move to the individual package you are working on
cd shakespeare-text
@ -109,17 +112,12 @@ cabal test
#### cabal-dev
cabal-dev works very well if you are working on a single package, but it can be very cumbersome to work on multiple packages at once.
cabal-dev works very well if you are working on a single package.
For working on multiple packages at once (installing Yesod), you need to use the shared sandbox feature.
### Use your development version of Yesod in your application
Note that we have recommended to you to install Yesod into a sandboxed virthualenv environment.
This is great for development, but when you want to use these development versions in your application that means they are not available through your user/global cabal database for your application.
You should just continue to use your yesod virthualenv shell for your application.
You can also use `cabal-dev install` to retrieve these packages.
cd to your application directory, and the reference the source list.
~~~ { .bash }
cabal-dev install /path/to/yesodweb/yesod/*(/)
~~~
You can also use the same`cabal-dev shared sandbox.

@ -1 +1 @@
Subproject commit d4cb555ca5fd6bc67f7da484a63d1fcdb149eac9
Subproject commit 6a95e0a8dbc1b3d26d58c1eaadf24d1bdaa48c8b

View File

@ -1,10 +1,11 @@
yesod-core
yesod-json
yesod-static
yesod-persistent
yesod-newsfeed
yesod-form
yesod-auth
yesod-sitemap
yesod-default
yesod
./yesod-routes
./yesod-core
./yesod-json
./yesod-static
./yesod-persistent
./yesod-newsfeed
./yesod-form
./yesod-auth
./yesod-sitemap
./yesod-default
./yesod

View File

@ -1,25 +0,0 @@
The following license covers this documentation, and the source code, except
where otherwise indicated.
Copyright 2010, Michael Snoyman. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -1,8 +0,0 @@
#!/usr/bin/env runhaskell
> module Main where
> import Distribution.Simple
> import System.Cmd (system)
> main :: IO ()
> main = defaultMain

View File

@ -1,123 +0,0 @@
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE OverloadedStrings #-}
-- | In-built kerberos authentication for Yesod.
--
-- Please note that all configuration should have been done
-- manually on the machine prior to running the code.
--
-- On linux machines the configuration might be in /etc/krb5.conf.
-- It's worth checking if the Kerberos service provider (e.g. your university)
-- already provide a complete configuration file.
--
-- Be certain that you can manually login from a shell by typing
--
-- > kinit username
--
-- If you fill in your password and the program returns no error code,
-- then your kerberos configuration is setup properly.
-- Only then can this module be of any use.
module Yesod.Auth.Kerberos
( authKerberos,
genericAuthKerberos,
KerberosConfig(..),
defaultKerberosConfig
) where
#include "qq.h"
import Yesod.Auth
import Yesod.Auth.Message
import Web.Authenticate.Kerberos
import Data.Text (Text)
import qualified Data.Text as T
import Text.Hamlet
import Yesod.Handler
import Yesod.Widget
import Control.Monad.IO.Class (liftIO)
import Yesod.Form
import Control.Applicative ((<$>), (<*>))
data KerberosConfig = KerberosConfig {
-- | When a user gives username x, f(x) will be passed to Kerberos
usernameModifier :: Text -> Text
-- | When a user gives username x, f(x) will be passed to Yesod
, identifierModifier :: Text -> Text
}
-- | A configuration where the username the user provides is the one passed
-- to both kerberos and yesod
defaultKerberosConfig :: KerberosConfig
defaultKerberosConfig = KerberosConfig id id
-- | A configurable version of 'authKerberos'
genericAuthKerberos :: YesodAuth m => KerberosConfig -> AuthPlugin m
genericAuthKerberos config = AuthPlugin "kerberos" dispatch $ \tm -> addHamlet
[QQ(hamlet)|
<div id="header">
<h1>Login
<div id="login">
<form method="post" action="@{tm login}">
<table>
<tr>
<th>Username:
<td>
<input id="x" name="username" autofocus="" required>
<tr>
<th>Password:
<td>
<input type="password" name="password" required>
<tr>
<td>&nbsp;
<td>
<input type="submit" value="Login">
<script>
if (!("autofocus" in document.createElement("input"))) {
document.getElementById("x").focus();
}
|]
where
dispatch "POST" ["login"] = postLoginR config >>= sendResponse
dispatch _ _ = notFound
login :: AuthRoute
login = PluginR "kerberos" ["login"]
-- | Kerberos with 'defaultKerberosConfig'
authKerberos :: YesodAuth m => AuthPlugin m
authKerberos = genericAuthKerberos defaultKerberosConfig
-- | Handle the login form
postLoginR :: (YesodAuth y) => KerberosConfig -> GHandler Auth y ()
postLoginR config = do
(mu,mp) <- runInputPost $ (,)
<$> iopt textField "username"
<*> iopt textField "password"
let errorMessage (message :: Text) = do
setMessage [QQ(shamlet)|Error: #{message}|]
toMaster <- getRouteToMaster
redirect $ toMaster LoginR
case (mu,mp) of
(Nothing, _ ) -> do
mr <- getMessageRender
errorMessage $ mr PleaseProvideUsername
(_ , Nothing) -> do
mr <- getMessageRender
errorMessage $ mr PleaseProvidePassword
(Just u , Just p ) -> do
result <- liftIO $ loginKerberos (usernameModifier config u) p
case result of
Ok -> do
let creds = Creds
{ credsIdent = identifierModifier config u
, credsPlugin = "Kerberos"
, credsExtra = []
}
setCreds True creds
kerberosError -> errorMessage (T.pack $ show kerberosError)

View File

@ -1,10 +0,0 @@
-- CPP macro which choses which quasyquotes syntax to use depending
-- on GHC version.
--
-- QQ stands for quasyquote.
#if GHC7
# define QQ(x) x
#else
# define QQ(x) $x
#endif

View File

@ -1,39 +0,0 @@
name: yesod-auth-kerberos
version: 0.8.0
license: BSD3
license-file: LICENSE
author: Arash Rouhani
maintainer: Arash Rouhani
synopsis: Kerberos Authentication for Yesod.
category: Web, Yesod
stability: Stable
cabal-version: >= 1.6.0
build-type: Simple
homepage: http://www.yesodweb.com/
extra-source-files: include/qq.h
description: Kerberos Authentication for Yesod.
flag ghc7
library
if flag(ghc7)
build-depends: base >= 4.3 && < 5
cpp-options: -DGHC7
else
build-depends: base >= 4 && < 4.3
build-depends: authenticate-kerberos >= 1.0 && < 1.1
, bytestring >= 0.9.1.4 && < 0.10
, yesod-core >= 0.10 && < 0.11
, yesod-auth >= 0.8 && < 0.9
, text >= 0.7 && < 0.12
, hamlet >= 0.10 && < 0.11
, yesod-form >= 0.4 && < 0.5
, transformers >= 0.2.2 && < 0.3
exposed-modules: Yesod.Auth.Kerberos
ghc-options: -Wall
include-dirs: include
source-repository head
type: git
location: https://github.com/yesodweb/yesod

View File

@ -5,6 +5,7 @@ module Yesod.Auth.OAuth
, oauthUrl
, authTwitter
, twitterUrl
, module Web.Authenticate.OAuth
) where
#include "qq.h"
@ -16,71 +17,86 @@ import Yesod.Widget
import Text.Hamlet (shamlet)
import Web.Authenticate.OAuth
import Data.Maybe
import Data.String
import Data.ByteString.Char8 (pack)
import Control.Arrow ((***))
import Data.Text (Text, unpack)
import Data.Text (Text)
import qualified Data.Text as T
import Data.Text.Encoding (encodeUtf8, decodeUtf8With)
import Data.Text.Encoding.Error (lenientDecode)
import Data.ByteString (ByteString)
import Control.Applicative ((<$>), (<*>))
import Data.Conduit
oauthUrl :: Text -> AuthRoute
oauthUrl name = PluginR name ["forward"]
authOAuth :: YesodAuth m =>
Text -- ^ Service Name
-> String -- ^ OAuth Parameter Name to use for identify
-> String -- ^ Request URL
-> String -- ^ Access Token URL
-> String -- ^ Authorize URL
-> String -- ^ Consumer Key
-> String -- ^ Consumer Secret
authOAuth :: YesodAuth m
=> OAuth -- ^ 'OAuth' data-type for signing.
-> (Credential -> IO (Creds m)) -- ^ How to extract ident.
-> AuthPlugin m
authOAuth name ident reqUrl accUrl authUrl key sec = AuthPlugin name dispatch login
authOAuth oauth mkCreds = AuthPlugin name dispatch login
where
name = T.pack $ oauthServerName oauth
url = PluginR name []
oauth = OAuth { oauthServerName = unpack name, oauthRequestUri = reqUrl
, oauthAccessTokenUri = accUrl, oauthAuthorizeUri = authUrl
, oauthSignatureMethod = HMACSHA1
, oauthConsumerKey = fromString key, oauthConsumerSecret = fromString sec
, oauthCallback = Nothing
, oauthRealm = Nothing
}
lookupTokenSecret = bsToText . fromMaybe "" . lookup "oauth_token_secret" . unCredential
oauthSessionName = "__oauth_token_secret"
dispatch "GET" ["forward"] = do
render <- getUrlRender
tm <- getRouteToMaster
let oauth' = oauth { oauthCallback = Just $ encodeUtf8 $ render $ tm url }
master <- getYesod
tok <- lift $ getTemporaryCredential oauth' (authHttpManager master)
setSession oauthSessionName $ lookupTokenSecret tok
redirect $ authorizeUrl oauth' tok
dispatch "GET" [] = do
(verifier, oaTok) <- runInputGet $ (,)
<$> ireq textField "oauth_verifier"
<*> ireq textField "oauth_token"
let reqTok = Credential [ ("oauth_verifier", encodeUtf8 verifier), ("oauth_token", encodeUtf8 oaTok)
reqTok <-
if oauthVersion oauth == OAuth10
then do
oaTok <- runInputGet $ ireq textField "oauth_token"
tokSec <- fromJust <$> lookupSession oauthSessionName
deleteSession oauthSessionName
return $ Credential [ ("oauth_token", encodeUtf8 oaTok)
, ("oauth_token_secret", encodeUtf8 tokSec)
]
master <- getYesod
accTok <- lift $ getAccessToken oauth reqTok (authHttpManager master)
let crId = decodeUtf8With lenientDecode $ fromJust $ lookup (pack ident) $ unCredential accTok
creds = Creds name crId $ map (bsToText *** bsToText ) $ unCredential accTok
setCreds True creds
else do
(verifier, oaTok) <-
runInputGet $ (,) <$> ireq textField "oauth_verifier"
<*> ireq textField "oauth_token"
tokSec <- fromJust <$> lookupSession oauthSessionName
deleteSession oauthSessionName
return $ Credential [ ("oauth_verifier", encodeUtf8 verifier)
, ("oauth_token", encodeUtf8 oaTok)
, ("oauth_token_secret", encodeUtf8 tokSec)
]
master <- getYesod
accTok <- lift $ getAccessToken oauth reqTok (authHttpManager master)
creds <- resourceLiftBase $ mkCreds accTok
setCreds True creds
dispatch _ _ = notFound
login tm = do
render <- lift getUrlRender
let oaUrl = render $ tm $ oauthUrl name
addHtml
[QQ(shamlet)| <a href=#{oaUrl}>Login with #{name} |]
[QQ(shamlet)| <a href=#{oaUrl}>Login via #{name} |]
authTwitter :: YesodAuth m =>
String -- ^ Consumer Key
-> String -- ^ Consumer Secret
authTwitter :: YesodAuth m
=> ByteString -- ^ Consumer Key
-> ByteString -- ^ Consumer Secret
-> AuthPlugin m
authTwitter = authOAuth "twitter"
"screen_name"
"http://twitter.com/oauth/request_token"
"http://twitter.com/oauth/access_token"
"http://twitter.com/oauth/authorize"
authTwitter key secret = authOAuth
(newOAuth { oauthServerName = "twitter"
, oauthRequestUri = "https://api.twitter.com/oauth/request_token"
, oauthAccessTokenUri = "https://api.twitter.com/oauth/access_token"
, oauthAuthorizeUri = "https://api.twitter.com/oauth/authorize"
, oauthSignatureMethod = HMACSHA1
, oauthConsumerKey = key
, oauthConsumerSecret = secret
, oauthVersion = OAuth10a
})
extractCreds
where
extractCreds (Credential dic) = do
let crId = decodeUtf8With lenientDecode $ fromJust $ lookup "screen_name" dic
return $ Creds "twitter" crId $ map (bsToText *** bsToText ) dic
twitterUrl :: AuthRoute
twitterUrl = oauthUrl "twitter"

View File

@ -1,5 +1,5 @@
name: yesod-auth-oauth
version: 0.8.0
version: 0.8.1
license: BSD3
license-file: LICENSE
author: Hiromi Ishii
@ -21,12 +21,13 @@ library
cpp-options: -DGHC7
else
build-depends: base >= 4 && < 4.3
build-depends: authenticate-oauth >= 1.0 && < 1.1
build-depends: authenticate-oauth >= 1.1 && < 1.2
, bytestring >= 0.9.1.4 && < 0.10
, yesod-core >= 0.10 && < 0.11
, yesod-auth >= 0.8 && < 0.9
, text >= 0.7 && < 0.12
, hamlet >= 0.10 && < 0.11
, conduit >= 0.2 && < 0.3
, yesod-form >= 0.4 && < 0.5
exposed-modules: Yesod.Auth.OAuth

View File

@ -18,7 +18,6 @@ module Yesod.Auth.GoogleEmail
import Yesod.Auth
import qualified Web.Authenticate.OpenId as OpenId
import Yesod.Form
import Yesod.Handler
import Yesod.Widget
import Yesod.Request
@ -31,45 +30,36 @@ import Control.Exception.Lifted (try, SomeException)
forwardUrl :: AuthRoute
forwardUrl = PluginR "googleemail" ["forward"]
googleIdent :: Text
googleIdent = "https://www.google.com/accounts/o8/id"
authGoogleEmail :: YesodAuth m => AuthPlugin m
authGoogleEmail =
AuthPlugin "googleemail" dispatch login
where
complete = PluginR "googleemail" ["complete"]
name = "openid_identifier"
login tm = do
[whamlet|
<form method=get action=@{tm forwardUrl}>
<input type=hidden name=openid_identifier value=https://www.google.com/accounts/o8/id>
<input type=submit value=_{Msg.LoginGoogle}>
|]
login tm =
[whamlet|<a href=@{tm forwardUrl}>_{Msg.LoginGoogle}|]
dispatch "GET" ["forward"] = do
roid <- runInputGet $ iopt textField name
case roid of
Just oid -> do
render <- getUrlRender
toMaster <- getRouteToMaster
let complete' = render $ toMaster complete
master <- getYesod
eres <- lift $ try $ OpenId.getForwardUrl oid complete' Nothing
[ ("openid.ax.type.email", "http://schema.openid.net/contact/email")
, ("openid.ns.ax", "http://openid.net/srv/ax/1.0")
, ("openid.ns.ax.required", "email")
, ("openid.ax.mode", "fetch_request")
, ("openid.ax.required", "email")
, ("openid.ui.icon", "true")
] (authHttpManager master)
either
(\err -> do
setMessage $ toHtml $ show (err :: SomeException)
redirect $ toMaster LoginR
)
redirect
eres
Nothing -> do
toMaster <- getRouteToMaster
setMessageI Msg.NoOpenID
render <- getUrlRender
toMaster <- getRouteToMaster
let complete' = render $ toMaster complete
master <- getYesod
eres <- lift $ try $ OpenId.getForwardUrl googleIdent complete' Nothing
[ ("openid.ax.type.email", "http://schema.openid.net/contact/email")
, ("openid.ns.ax", "http://openid.net/srv/ax/1.0")
, ("openid.ns.ax.required", "email")
, ("openid.ax.mode", "fetch_request")
, ("openid.ax.required", "email")
, ("openid.ui.icon", "true")
] (authHttpManager master)
either
(\err -> do
setMessage $ toHtml $ show (err :: SomeException)
redirect $ toMaster LoginR
)
redirect
eres
dispatch "GET" ["complete", ""] = dispatch "GET" ["complete"] -- compatibility issues
dispatch "GET" ["complete"] = do
rr <- getRequest

View File

@ -6,6 +6,8 @@ module Yesod.Auth.Message
-- * All languages
, englishMessage
, portugueseMessage
, swedishMessage
, germanMessage
) where
import Data.Monoid (mappend)
@ -113,3 +115,71 @@ portugueseMessage NowLoggedIn = "Você acaba de entrar no site com sucesso!"
portugueseMessage LoginTitle = "Entrar no site"
portugueseMessage PleaseProvideUsername = "Por favor digite seu nome de usuário"
portugueseMessage PleaseProvidePassword = "Por favor digite sua senha"
swedishMessage :: AuthMessage -> Text
swedishMessage NoOpenID = "Fann ej OpenID identifierare"
swedishMessage LoginOpenID = "Logga in via OpenID"
swedishMessage LoginGoogle = "Logga in via Google"
swedishMessage LoginYahoo = "Logga in via Yahoo"
swedishMessage Email = "Epost"
swedishMessage Password = "Lösenord"
swedishMessage Register = "Registrera"
swedishMessage RegisterLong = "Registrera ett nytt konto"
swedishMessage EnterEmail = "Skriv in din epost nedan så kommer ett konfirmationsmail skickas till adressen."
swedishMessage ConfirmationEmailSentTitle = "Konfirmationsmail skickat"
swedishMessage (ConfirmationEmailSent email) =
"Ett konfirmationsmeddelande har skickats till" `mappend`
email `mappend`
"."
swedishMessage AddressVerified = "Adress verifierad, vänligen välj nytt lösenord"
swedishMessage InvalidKeyTitle = "Ogiltig verifikationsnyckel"
swedishMessage InvalidKey = "Tyvärr, du angav en ogiltig verifimationsnyckel."
swedishMessage InvalidEmailPass = "Ogiltig epost/lösenord kombination"
swedishMessage BadSetPass = "Du måste vara inloggad för att ange ett lösenord"
swedishMessage SetPassTitle = "Ange lösenord"
swedishMessage SetPass = "Ange nytt lösenord"
swedishMessage NewPass = "Nytt lösenord"
swedishMessage ConfirmPass = "Godkänn"
swedishMessage PassMismatch = "Lösenorden matcha ej, vänligen försök igen"
swedishMessage PassUpdated = "Lösenord updaterades"
swedishMessage Facebook = "Logga in med Facebook"
swedishMessage LoginViaEmail = "Logga in via epost"
swedishMessage InvalidLogin = "Ogiltigt login"
swedishMessage NowLoggedIn = "Du är nu inloggad"
swedishMessage LoginTitle = "Logga in"
swedishMessage PleaseProvideUsername = "Vänligen fyll i användarnamn"
swedishMessage PleaseProvidePassword = "Vänligen fyll i lösenord"
germanMessage :: AuthMessage -> Text
germanMessage NoOpenID = "Kein OpenID-Identifier gefunden"
germanMessage LoginOpenID = "Login via OpenID"
germanMessage LoginGoogle = "Login via Google"
germanMessage LoginYahoo = "Login via Yahoo"
germanMessage Email = "Email"
germanMessage Password = "Passwort"
germanMessage Register = "Registrieren"
germanMessage RegisterLong = "Neuen Account registrieren"
germanMessage EnterEmail = "Bitte die e-Mail Adresse angeben, eine Bestätigungsmail wird verschickt."
germanMessage ConfirmationEmailSentTitle = "Bestätigung verschickt."
germanMessage (ConfirmationEmailSent email) =
"Eine Bestätigung wurde an " `mappend`
email `mappend`
"versandt."
germanMessage AddressVerified = "Adresse bestätigt, bitte neues Passwort angeben"
germanMessage InvalidKeyTitle = "Ungültiger Bestätigungsschlüssel"
germanMessage InvalidKey = "Das war leider ein ungültiger Bestätigungsschlüssel"
germanMessage InvalidEmailPass = "Ungültiger Nutzername oder Passwort"
germanMessage BadSetPass = "Um das Passwort zu ändern muss man eingeloggt sein"
germanMessage SetPassTitle = "Passwort angeben"
germanMessage SetPass = "Neues Passwort angeben"
germanMessage NewPass = "Neues Passwort"
germanMessage ConfirmPass = "Bestätigen"
germanMessage PassMismatch = "Die Passwörter stimmten nicht überein"
germanMessage PassUpdated = "Passwort überschrieben"
germanMessage Facebook = "Login über Facebook"
germanMessage LoginViaEmail = "Login via e-Mail"
germanMessage InvalidLogin = "Ungültiger Login"
germanMessage NowLoggedIn = "Login erfolgreich"
germanMessage LoginTitle = "Login"
germanMessage PleaseProvideUsername = "Bitte Nutzername angeben"
germanMessage PleaseProvidePassword = "Bitte Passwort angeben"

View File

@ -1,41 +0,0 @@
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE MultiParamTypeClasses #-}
import Yesod
import Yesod.Auth
import Yesod.Form
import Yesod.Auth.Kerberos
data Kerberos = Kerberos
mkYesod "Kerberos" [parseRoutes|
/ RootR GET
/after AfterLoginR GET
/auth AuthR Auth getAuth
|]
getRootR :: Handler ()
getRootR = redirect RedirectTemporary $ AuthR LoginR
getAfterLoginR :: Handler RepHtml
getAfterLoginR = defaultLayout $ return ()
instance Yesod Kerberos where
approot _ = "http://localhost:3000"
instance YesodAuth Kerberos where
type AuthId Kerberos = String
loginDest _ = AfterLoginR
logoutDest _ = AuthR LoginR
getAuthId _ = do
liftIO $ putStrLn "getAuthId"
return $ Just "foo"
authPlugins = [authKerberos]
instance RenderMessage Kerberos FormMessage where
renderMessage _ _ = defaultFormMessage
main :: IO ()
main = warpDebug 3000 Kerberos

View File

@ -1,5 +1,5 @@
name: yesod-auth
version: 0.8.1
version: 0.8.1.1
license: BSD3
license-file: LICENSE
author: Michael Snoyman, Patrick Brisbin

View File

@ -19,6 +19,7 @@ module Yesod.Content
, typeJpeg
, typePng
, typeGif
, typeSvg
, typeJavascript
, typeCss
, typeFlv
@ -196,6 +197,9 @@ typePng = "image/png"
typeGif :: ContentType
typeGif = "image/gif"
typeSvg :: ContentType
typeSvg = "image/svg+xml"
typeJavascript :: ContentType
typeJavascript = "text/javascript; charset=utf-8"

View File

@ -33,6 +33,10 @@ module Yesod.Core
, clientSessionBackend
, saveClientSession
, loadClientSession
-- * JS loaders
, loadJsYepnope
, ScriptLoadPosition (..)
, BottomOfHeadAsync
-- * Misc
, yesodVersion
, yesodRender

View File

@ -31,6 +31,10 @@ module Yesod.Internal.Core
, clientSessionBackend
, saveClientSession
, loadClientSession
-- * jsLoader
, ScriptLoadPosition (..)
, BottomOfHeadAsync
, loadJsYepnope
-- * Misc
, yesodVersion
, yesodRender
@ -321,11 +325,23 @@ class RenderRoute a => Yesod a where
gzipSettings :: a -> GzipSettings
gzipSettings _ = def
-- | Location of yepnope.js, if any. If one is provided, then all
-- | Deprecated. Use 'jsloader'. To use yepnope: jsLoader = BottomOfHeadAsync (loadJsYepnope eyn)
-- Location of yepnope.js, if any. If one is provided, then all
-- Javascript files will be loaded asynchronously.
yepnopeJs :: a -> Maybe (Either Text (Route a))
yepnopeJs _ = Nothing
-- | Where to Load sripts from. We recommend changing this to 'BottomOfBody'
-- Alternatively use the built in async yepnope loader:
--
-- > BottomOfHeadAsync $ loadJsYepnope $ Right $ StaticR js_modernizr_js
--
-- Or write your own async js loader: see 'loadJsYepnope'
jsLoader :: a -> ScriptLoadPosition a
jsLoader y = case yepnopeJs y of
Nothing -> BottomOfHeadBlocking
Just eyn -> BottomOfHeadAsync (loadJsYepnope eyn)
-- | Create a session backend. Returning `Nothing' disables sessions.
makeSessionBackend :: a -> IO (Maybe (SessionBackend a))
makeSessionBackend a = do
@ -578,7 +594,17 @@ widgetToPageContent w = do
-- modernizr should be at the end of the <head> http://www.modernizr.com/docs/#installing
-- the asynchronous loader means your page doesn't have to wait for all the js to load
let (mcomplete, ynscripts) = ynHelper render scripts jscript jsLoc
let (mcomplete, asyncScripts) = asyncHelper render scripts jscript jsLoc
regularScriptLoad = [HAMLET|
$forall s <- scripts
^{mkScriptTag s}
$maybe j <- jscript
$maybe s <- jsLoc
<script src="#{s}">
$nothing
<script>^{jelper j}
|]
headAll = [HAMLET|
\^{head'}
$forall s <- stylesheets
@ -594,31 +620,22 @@ $forall s <- css
<style media=#{media}>#{content}
$nothing
<style>#{content}
$maybe eyn <- yepnopeJs master
$maybe yn <- left eyn
<script src=#{yn}>
$maybe yn <- right eyn
<script src=@{yn}>
$maybe complete <- mcomplete
<script>yepnope({load:#{ynscripts},complete:function(){^{complete}}})
$nothing
<script>yepnope({load:#{ynscripts}})
$nothing
$forall s <- scripts
^{mkScriptTag s}
$maybe j <- jscript
$maybe s <- jsLoc
<script src="#{s}">
$nothing
<script>^{jelper j}
$case jsLoader master
$of BottomOfBody
$of BottomOfHeadAsync asyncJsLoader
^{asyncJsLoader asyncScripts mcomplete}
$of BottomOfHeadBlocking
^{regularScriptLoad}
|]
let bodyScript = [HAMLET|
^{body}
^{regularScriptLoad}
|]
return $ PageContent title headAll body
where
left (Left x) = Just x
left _ = Nothing
right (Right x) = Just x
right _ = Nothing
return $ PageContent title headAll (case jsLoader master of
BottomOfBody -> bodyScript
_ -> body)
where
renderLoc' render' (Local url) = render' url []
renderLoc' _ (Remote s) = s
@ -632,13 +649,48 @@ $nothing
: attrs
)
ynHelper :: (url -> [x] -> Text)
data ScriptLoadPosition master
= BottomOfBody
| BottomOfHeadBlocking
| BottomOfHeadAsync (BottomOfHeadAsync master)
type BottomOfHeadAsync master
= [Text] -- ^ urls to load asynchronously
-> Maybe (HtmlUrl (Route master)) -- ^ widget of js to run on async completion
-> (HtmlUrl (Route master)) -- ^ widget to insert at the bottom of <head>
left :: Either a b -> Maybe a
left (Left x) = Just x
left _ = Nothing
right :: Either a b -> Maybe b
right (Right x) = Just x
right _ = Nothing
jsonArray :: [Text] -> Html
jsonArray = unsafeLazyByteString . encode . Array . Vector.fromList . map String
-- | For use with setting 'jsLoader' to 'BottomOfHeadAsync'
loadJsYepnope :: Yesod master => Either Text (Route master) -> [Text] -> Maybe (HtmlUrl (Route master)) -> (HtmlUrl (Route master))
loadJsYepnope eyn scripts mcomplete =
[HAMLET|
$maybe yn <- left eyn
<script src=#{yn}>
$maybe yn <- right eyn
<script src=@{yn}>
$maybe complete <- mcomplete
<script>yepnope({load:#{jsonArray scripts},complete:function(){^{complete}}});
$nothing
<script>yepnope({load:#{jsonArray scripts}});
|]
asyncHelper :: (url -> [x] -> Text)
-> [Script (url)]
-> Maybe (JavascriptUrl (url))
-> Maybe Text
-> (Maybe (HtmlUrl (url)), Html)
ynHelper render scripts jscript jsLoc =
(mcomplete, unsafeLazyByteString $ encode $ Array $ Vector.fromList $ map String scripts'')
-> (Maybe (HtmlUrl url), [Text])
asyncHelper render scripts jscript jsLoc =
(mcomplete, scripts'')
where
scripts' = map goScript scripts
scripts'' =

View File

@ -10,6 +10,7 @@ import YesodCoreTest.InternalRequest
import YesodCoreTest.ErrorHandling
import YesodCoreTest.Cache
import qualified YesodCoreTest.Redirect as Redirect
import qualified YesodCoreTest.JsLoader as JsLoader
import Test.Hspec
@ -25,4 +26,5 @@ specs =
, errorHandlingTest
, cacheTest
, Redirect.specs
, JsLoader.specs
]

View File

@ -0,0 +1,40 @@
{-# LANGUAGE QuasiQuotes, TypeFamilies, TemplateHaskell, MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE FlexibleInstances #-}
module YesodCoreTest.JsLoader (specs) where
import YesodCoreTest.JsLoaderSites.HeadAsync (HA(..))
import YesodCoreTest.JsLoaderSites.Bottom (B(..))
import Test.Hspec
import Test.Hspec.HUnit ()
import Yesod.Core hiding (Request)
import Network.Wai.Test
data H = H
mkYesod "H" [parseRoutes|
/ HeadR GET
|]
instance Yesod H
getHeadR :: Handler RepHtml
getHeadR = defaultLayout $ addScriptRemote "load.js"
specs :: [Spec]
specs = describe "Test.Links" [
it "link from head" $ runner H $ do
res <- request defaultRequest
assertBody "<!DOCTYPE html>\n<html><head><title></title><script src=\"load.js\"></script></head><body></body></html>" res
, it "link from head async" $ runner HA $ do
res <- request defaultRequest
assertBody "<!DOCTYPE html>\n<html><head><title></title><script src=\"yepnope.js\"></script><script>yepnope({load:[\"load.js\"]});</script></head><body></body></html>" res
, it "link from bottom" $ runner B $ do
res <- request defaultRequest
assertBody "<!DOCTYPE html>\n<html><head><title></title></head><body><script src=\"load.js\"></script></body></html>" res
]
runner :: (YesodDispatch master master, Yesod master) => master -> Session () -> IO ()
runner app f = toWaiApp app >>= runSession f

View File

@ -0,0 +1,17 @@
{-# LANGUAGE QuasiQuotes, TypeFamilies, TemplateHaskell, MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE FlexibleInstances #-}
module YesodCoreTest.JsLoaderSites.Bottom (B(..)) where
import Yesod.Core
data B = B
mkYesod "B" [parseRoutes|
/ BottomR GET
|]
instance Yesod B where
jsLoader _ = BottomOfBody
getBottomR :: Handler RepHtml
getBottomR = defaultLayout $ addScriptRemote "load.js"

View File

@ -0,0 +1,16 @@
{-# LANGUAGE QuasiQuotes, TypeFamilies, TemplateHaskell, MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE FlexibleInstances #-}
module YesodCoreTest.JsLoaderSites.HeadAsync (HA(..)) where
import Yesod.Core
data HA = HA
mkYesod "HA" [parseRoutes|
/ HeadAsyncR GET
|]
instance Yesod HA where
jsLoader _ = BottomOfHeadAsync $ loadJsYepnope $ Left "yepnope.js"
getHeadAsyncR :: Handler RepHtml
getHeadAsyncR = defaultLayout $ addScriptRemote "load.js"

View File

@ -1,5 +1,5 @@
name: yesod-core
version: 0.10.1
version: 0.10.2.2
license: BSD3
license-file: LICENSE
author: Michael Snoyman <michael@snoyman.com>
@ -49,13 +49,13 @@ library
build-depends: time >= 1.1.4
, yesod-routes >= 0.0.1 && < 0.1
, wai >= 1.1 && < 1.2
, wai-extra >= 1.1 && < 1.2
, wai-extra >= 1.1 && < 1.3
, bytestring >= 0.9.1.4 && < 0.10
, text >= 0.7 && < 0.12
, template-haskell
, path-pieces >= 0.1 && < 0.2
, hamlet >= 0.10.7 && < 0.11
, shakespeare >= 0.10 && < 0.11
, shakespeare >= 0.10 && < 0.12
, shakespeare-js >= 0.11 && < 0.12
, shakespeare-css >= 0.10.5 && < 0.11
, shakespeare-i18n >= 0.0 && < 0.1

View File

@ -7,6 +7,7 @@ module Yesod.Default.Util
, globFile
, widgetFileNoReload
, widgetFileReload
, widgetFileJsCss
) where
import Control.Monad.IO.Class (liftIO)
@ -72,8 +73,21 @@ widgetFileReload x = do
let l = whenExists x "lucius" luciusFileReload
[|$h >> addCassius $c >> addJulius $j >> addLucius $l|]
widgetFileJsCss :: (String, FilePath -> Q Exp) -- ^ Css file extenstion and loading function. example: ("cassius", cassiusFileReload)
-> (String, FilePath -> Q Exp) -- ^ Css file extenstion and loading function. example: ("julius", juliusFileReload)
-> FilePath -> Q Exp
widgetFileJsCss (jsExt, jsLoad) (csExt, csLoad) x = do
let h = whenExists x "hamlet" whamletFile
let c = whenExists x csExt csLoad
let j = whenExists x jsExt jsLoad
[|$h >> addCassius $c >> addJulius $j|]
whenExists :: String -> String -> (FilePath -> Q Exp) -> Q Exp
whenExists x glob f = do
whenExists = warnUnlessExists False
warnUnlessExists :: Bool -> String -> String -> (FilePath -> Q Exp) -> Q Exp
warnUnlessExists shouldWarn x glob f = do
let fn = globFile glob x
e <- qRunIO $ doesFileExist fn
unless (shouldWarn && e) $ qRunIO $ putStrLn $ "widget file not found: " ++ fn
if e then f fn else [|mempty|]

View File

@ -21,7 +21,7 @@ library
, yesod-core >= 0.10.1&& < 0.11
, warp >= 1.1 && < 1.2
, wai >= 1.1 && < 1.2
, wai-extra >= 1.1 && < 1.2
, wai-extra >= 1.1 && < 1.3
, bytestring >= 0.9.1.4
, transformers >= 0.2.2 && < 0.3
, text >= 0.9

View File

@ -173,7 +173,7 @@ htmlField = Field
}
where showVal = either id (pack . renderHtml)
-- | A newtype wrapper around a 'String' that converts newlines to HTML
-- | A newtype wrapper around a 'Text' that converts newlines to HTML
-- br-tags.
newtype Textarea = Textarea { unTextarea :: Text }
deriving (Show, Read, Eq, PersistField)

View File

@ -297,17 +297,17 @@ $forall view <- views
--
-- Sample Hamlet:
--
-- > <form method=post action=@{ActionR} enctype=#{formEnctype}>
-- > <fieldset>
-- > <legend>_{MsgLegend}
-- > $case result
-- > $of FormFailure reasons
-- > $forall reason <- reasons
-- > <div .alert-message .error>#{reason}
-- > $of _
-- > ^{formWidget}
-- > <div .actions>
-- > <input .btn .primary type=submit value=_{MsgSubmit}>
-- > <form .form-horizontal method=post action=@{ActionR} enctype=#{formEnctype}>
-- > <fieldset>
-- > <legend>_{MsgLegend}
-- > $case result
-- > $of FormFailure reasons
-- > $forall reason <- reasons
-- > <div .alert .alert-error>#{reason}
-- > $of _
-- > ^{formWidget}
-- > <div .form-actions>
-- > <input .btn .primary type=submit value=_{MsgSubmit}>
renderBootstrap :: FormRender sub master a
renderBootstrap aform fragment = do
(res, views') <- aFormToForm aform
@ -317,9 +317,9 @@ renderBootstrap aform fragment = do
let widget = [whamlet|
\#{fragment}
$forall view <- views
<div .clearfix :fvRequired view:.required :not $ fvRequired view:.optional :has $ fvErrors view:.error>
<label for=#{fvId view}>#{fvLabel view}
<div.input>
<div .control-group .clearfix :fvRequired view:.required :not $ fvRequired view:.optional :has $ fvErrors view:.error>
<label .control-label for=#{fvId view}>#{fvLabel view}
<div .controls .input>
^{fvInput view}
$maybe tt <- fvTooltip view
<span .help-block>#{tt}

View File

@ -16,7 +16,7 @@ germanFormMessage (MsgInvalidEmail t) = "Ungültige e-Mail Adresse: " `mappend`
germanFormMessage (MsgInvalidHour t) = "Ungültige Stunde: " `mappend` t
germanFormMessage (MsgInvalidMinute t) = "Ungültige Minute: " `mappend` t
germanFormMessage (MsgInvalidSecond t) = "Ungültige Sekunde: " `mappend` t
germanFormMessage MsgCsrfWarning = "Bitte bestätigen Sie ihre Eingabe, als Schutz gegen Cross-Site Forgery Angriffen"
germanFormMessage MsgCsrfWarning = "Bitte bestätigen Sie ihre Eingabe, als Schutz gegen Cross-Site Forgery Angriffe"
germanFormMessage MsgValueRequired = "Wert wird benötigt"
germanFormMessage (MsgInputNotFound t) = "Eingabe nicht gefunden: " `mappend` t
germanFormMessage MsgSelectNone = "<Nichts>"

View File

@ -11,7 +11,7 @@ module Yesod.Form.Nic
) where
import Yesod.Handler
import Yesod.Core (Route, yepnopeJs, Yesod)
import Yesod.Core (Route, ScriptLoadPosition(..), jsLoader, Yesod)
import Yesod.Form
import Yesod.Widget
import Text.HTML.SanitizeXSS (sanitizeBalance)
@ -43,8 +43,8 @@ nicHtmlField = Field
addScript' urlNicEdit
master <- lift getYesod
addJulius $
case yepnopeJs master of
Nothing ->
case jsLoader master of
BottomOfHeadBlocking ->
#if __GLASGOW_HASKELL__ >= 700
[julius|
#else
@ -52,7 +52,7 @@ nicHtmlField = Field
#endif
bkLib.onDomLoaded(function(){new nicEditor({fullPanel:true}).panelInstance("#{theId}")});
|]
Just _ ->
_ ->
#if __GLASGOW_HASKELL__ >= 700
[julius|
#else

View File

@ -1,5 +1,5 @@
name: yesod-form
version: 0.4.1
version: 0.4.2.1
license: BSD3
license-file: LICENSE
author: Michael Snoyman <michael@snoyman.com>
@ -14,7 +14,7 @@ description: Form handling support for Yesod Web Framework
library
build-depends: base >= 4 && < 5
, yesod-core >= 0.10.1 && < 0.11
, yesod-core >= 0.10.2 && < 0.11
, yesod-persistent >= 0.3.1 && < 0.4
, time >= 1.1.4
, hamlet >= 0.10 && < 0.11

View File

@ -26,7 +26,7 @@ library
, conduit >= 0.2 && < 0.3
, transformers >= 0.2.2 && < 0.3
, wai >= 1.1 && < 1.2
, wai-extra >= 1.1 && < 1.2
, wai-extra >= 1.1 && < 1.3
, bytestring >= 0.9 && < 0.10
, safe >= 0.2 && < 0.4
exposed-modules: Yesod.Json

View File

@ -2,6 +2,7 @@
module Yesod.Routes.TH.RenderRoute
( -- ** RenderRoute
mkRenderRouteInstance
, mkRenderRouteInstance'
, mkRouteCons
, mkRenderRouteClauses
) where
@ -89,12 +90,19 @@ mkRenderRouteClauses =
-- | Generate the 'RenderRoute' instance.
--
-- This includes both the 'Route' associated type and the 'renderRoute' method.
-- This function uses both 'mkRouteCons' and 'mkRenderRouteClasses'.
-- This includes both the 'Route' associated type and the
-- 'renderRoute' method. This function uses both 'mkRouteCons' and
-- 'mkRenderRouteClasses'.
mkRenderRouteInstance :: Type -> [Resource Type] -> Q Dec
mkRenderRouteInstance typ ress = do
mkRenderRouteInstance = mkRenderRouteInstance' []
-- | A more general version of 'mkRenderRouteInstance' which takes an
-- additional context.
mkRenderRouteInstance' :: Cxt -> Type -> [Resource Type] -> Q Dec
mkRenderRouteInstance' cxt typ ress = do
cls <- mkRenderRouteClauses ress
return $ InstanceD [] (ConT ''RenderRoute `AppT` typ)
return $ InstanceD cxt (ConT ''RenderRoute `AppT` typ)
[ DataInstD [] ''Route [typ] (mkRouteCons ress) clazzes
, FunD (mkName "renderRoute") cls
]

View File

@ -138,8 +138,12 @@ determineHamletDeps x = do
<|> (A.string "$(widgetFile " >> return Hamlet)
<|> (A.string "$(Settings.hamletFile " >> return Hamlet)
<|> (A.string "$(Settings.widgetFile " >> return Hamlet)
<|> (A.string "$(persistFile " >> return Verbatim)
<|> (A.string "$(parseRoutesFile " >> return Verbatim)
<|> (A.string "$(persistFile " >> return Verbatim)
<|> (
A.string "$(persistFileWith " >>
A.many1 (A.satisfy (/= '"')) >>
return Verbatim)
<|> (do
_ <- A.string "\nmkMessage \""
A.skipWhile (/= '"')

View File

@ -12,31 +12,27 @@ import qualified Distribution.Simple.Utils as D
import qualified Distribution.Verbosity as D
import qualified Distribution.PackageDescription.Parse as D
import qualified Distribution.PackageDescription as D
import qualified Distribution.ModuleName as D
import Control.Concurrent (forkIO, threadDelay)
import qualified Control.Exception as Ex
import Control.Monad (forever)
import Control.Monad (forever, when)
import Data.Char (isUpper, isNumber)
import qualified Data.List as L
import qualified Data.Map as Map
import Data.Maybe (listToMaybe)
import qualified Data.Set as Set
import System.Directory (createDirectoryIfMissing, removeFile,
getDirectoryContents)
import System.Exit (exitFailure, exitSuccess)
import System.Directory
import System.Exit (exitFailure, exitSuccess, ExitCode (..))
import System.FilePath (splitDirectories, dropExtension, takeExtension)
import System.Posix.Types (EpochTime)
import System.PosixCompat.Files (modificationTime, getFileStatus)
import System.Process (runCommand, terminateProcess,
import System.Process (createProcess, proc, terminateProcess, readProcess,
waitForProcess, rawSystem)
import Build (recompDeps, getDeps,findHaskellFiles)
#if __GLASGOW_HASKELL__ >= 700
#define ST st
#else
#define ST $st
#endif
lockFile :: FilePath
lockFile = "dist/devel-terminate"
@ -48,8 +44,11 @@ writeLock = do
removeLock :: IO ()
removeLock = try_ (removeFile lockFile)
devel :: Bool -> IO ()
devel isCabalDev = do
devel :: Bool -> [String] -> IO ()
devel isCabalDev passThroughArgs = do
checkDevelFile
writeLock
putStrLn "Yesod devel server. Press ENTER to quit"
@ -59,71 +58,68 @@ devel isCabalDev = do
checkCabalFile gpd
_ <- if isCabalDev
then rawSystem "cabal-dev"
[ "configure"
, "--cabal-install-arg=-fdevel" -- legacy
, "--cabal-install-arg=-flibrary-only"
, "--disable-library-profiling"
]
else rawSystem "cabal"
[ "configure"
, "-fdevel" -- legacy
, "-flibrary-only"
, "--disable-library-profiling"
]
_<- rawSystem cmd args
mainLoop isCabalDev
mainLoop
_ <- getLine
writeLock
exitSuccess
where
cmd | isCabalDev == True = "cabal-dev"
| otherwise = "cabal"
diffArgs | isCabalDev == True = [
"--cabal-install-arg=-fdevel" -- legacy
, "--cabal-install-arg=-flibrary-only"
]
| otherwise = [
"-fdevel" -- legacy
, "-flibrary-only"
]
args = "configure":diffArgs ++ ["--disable-library-profiling" ]
mainLoop :: IO ()
mainLoop = do
ghcVer <- ghcVersion
forever $ do
putStrLn "Rebuilding application..."
mainLoop :: Bool -> IO ()
mainLoop isCabalDev = forever $ do
putStrLn "Rebuilding application..."
recompDeps
recompDeps
list <- getFileList
exit <- rawSystem cmd ["build"]
list <- getFileList
_ <- if isCabalDev
then rawSystem "cabal-dev" ["build"]
else rawSystem "cabal" ["build"]
removeLock
pkg <- pkgConfigs isCabalDev
let start = concat ["runghc ", pkg, " devel.hs"]
putStrLn $ "Starting development server: " ++ start
ph <- runCommand start
watchTid <- forkIO . try_ $ do
watchForChanges list
putStrLn "Stopping development server..."
writeLock
threadDelay 1000000
putStrLn "Terminating development server..."
terminateProcess ph
ec <- waitForProcess ph
putStrLn $ "Exit code: " ++ show ec
Ex.throwTo watchTid (userError "process finished")
watchForChanges list
case exit of
ExitFailure _ -> putStrLn "Build failure, pausing..."
_ -> do
removeLock
let pkg = pkgConfigs isCabalDev ghcVer
let dev_args = pkg ++ ["devel.hs"] ++ passThroughArgs
putStrLn $ "Starting development server: runghc " ++ L.intercalate " " dev_args
(_,_,_,ph) <- createProcess $ proc "runghc" dev_args
watchTid <- forkIO . try_ $ do
watchForChanges list
putStrLn "Stopping development server..."
writeLock
threadDelay 1000000
putStrLn "Terminating development server..."
terminateProcess ph
ec <- waitForProcess ph
putStrLn $ "Exit code: " ++ show ec
Ex.throwTo watchTid (userError "process finished")
watchForChanges list
try_ :: forall a. IO a -> IO ()
try_ x = (Ex.try x :: IO (Either Ex.SomeException a)) >> return ()
pkgConfigs :: Bool -> IO String
pkgConfigs isDev
| isDev = do
devContents <- getDirectoryContents "cabal-dev"
let confs = filter isConfig devContents
return . unwords $ inplacePkg :
map ("-package-confcabal-dev/"++) confs
| otherwise = return inplacePkg
pkgConfigs :: Bool -> String -> [String]
pkgConfigs isCabalDev ghcVer
| isCabalDev = ["-package-confcabal-dev/packages-" ++ ghcVer ++ ".conf", inplacePkg]
| otherwise = [inplacePkg]
where
inplacePkg = "-package-confdist/package.conf.inplace"
isConfig dir = "packages-" `L.isPrefixOf` dir &&
".conf" `L.isSuffixOf` dir
type FileList = Map.Map FilePath EpochTime
@ -143,29 +139,69 @@ watchForChanges list = do
then return ()
else threadDelay 1000000 >> watchForChanges list
checkDevelFile :: IO ()
checkDevelFile = do
e <- doesFileExist "devel.hs"
when (not e) $ failWith "file devel.hs not found"
checkCabalFile :: D.GenericPackageDescription -> IO ()
checkCabalFile gpd = case D.condLibrary gpd of
Nothing -> do
putStrLn "Error: incorrect cabal file, no library"
exitFailure
Nothing -> failWith "incorrect cabal file, no library"
Just ct ->
case lookupDevelLib ct of
Nothing -> do
putStrLn "Error: no development flag found in your configuration file. Expected a 'library-only' flag or the older 'devel' flag"
exitFailure
Just dLib ->
Nothing ->
failWith "no development flag found in your configuration file. Expected a 'library-only' flag or the older 'devel' flag"
Just dLib -> do
case (D.hsSourceDirs . D.libBuildInfo) dLib of
[] -> return ()
["."] -> return ()
_ ->
putStrLn $ "WARNING: yesod devel may not work correctly with " ++
"custom hs-source-dirs"
fl <- getFileList
let unlisted = checkFileList fl dLib
when (not . null $ unlisted) $ do
putStrLn "WARNING: the following source files are not listed in exposed-modules or other-modules:"
mapM_ putStrLn unlisted
when (D.fromString "Application" `notElem` D.exposedModules dLib) $ do
putStrLn "WARNING: no exposed module Application"
failWith :: String -> IO a
failWith msg = do
putStrLn $ "ERROR: " ++ msg
exitFailure
checkFileList :: FileList -> D.Library -> [FilePath]
checkFileList fl lib = filter isUnlisted . filter isSrcFile $ sourceFiles
where
al = allModules lib
-- a file is only a possible 'module file' if all path pieces start with a capital letter
sourceFiles = filter isSrcFile . map fst . Map.toList $ fl
isSrcFile file = let dirs = filter (/=".") $ splitDirectories file
in all (isUpper . head) dirs && (takeExtension file `elem` [".hs", ".lhs"])
isUnlisted file = not (toModuleName file `Set.member` al)
toModuleName = L.intercalate "." . filter (/=".") . splitDirectories . dropExtension
allModules :: D.Library -> Set.Set String
allModules lib = Set.fromList $ map toString $ D.exposedModules lib ++ (D.otherModules . D.libBuildInfo) lib
where
toString = L.intercalate "." . D.components
ghcVersion :: IO String
ghcVersion = fmap getNumber $ readProcess "runghc" ["--numeric-version", "0"] []
where
getNumber = filter (\x -> isNumber x || x == '.')
lookupDevelLib :: D.CondTree D.ConfVar c a -> Maybe a
lookupDevelLib ct = listToMaybe . map (\(_,x,_) -> D.condTreeData x) .
filter isDevelLib . D.condTreeComponents $ ct
lookupDevelLib ct | found = Just (D.condTreeData ct)
| otherwise = Nothing
where
found = not . null . map (\(_,x,_) -> D.condTreeData x) .
filter isDevelLib . D.condTreeComponents $ ct
isDevelLib ((D.Var (D.Flag (D.FlagName f))), _, _) = f `elem` ["library-only", "devel"]
isDevelLib _ = False

View File

@ -35,7 +35,7 @@ main = do
"build":rest -> touch >> build rest >>= exitWith
["touch"] -> touch
#endif
["devel"] -> devel isDev
"devel":rest -> devel isDev rest
["version"] -> putStrLn yesodVersion
"configure":rest -> rawSystem cmd ("configure":rest) >>= exitWith
_ -> do

View File

@ -1 +1,2 @@
:set -i.:config:dist/build/autogen
:set -XCPP -XTemplateHaskell -XQuasiQuotes -XTypeFamilies -XFlexibleContexts -XGADTs -XOverloadedStrings -XMultiParamTypeClasses -XGeneralizedNewtypeDeriving -XEmptyDataDecls

View File

@ -6,7 +6,6 @@ module Application
import Import
import Settings
import Settings.StaticFiles (staticSite)
import Yesod.Auth
import Yesod.Default.Config
import Yesod.Default.Main

View File

@ -15,7 +15,6 @@ module Foundation
import Prelude
import Yesod
import Yesod.Static
import Settings.StaticFiles
import Yesod.Auth
import Yesod.Auth.BrowserId
import Yesod.Auth.GoogleEmail
@ -124,8 +123,8 @@ instance Yesod ~sitearg~ where
-- users receiving stale content.
addStaticContent = addStaticContentExternal minifym base64md5 Settings.staticDir (StaticR . flip StaticRoute [])
-- Enable Javascript async loading
yepnopeJs _ = Just $ Right $ StaticR js_modernizr_js
-- Place Javascript at bottom of the body tag so the rest of the page loads first
jsLoader _ = BottomOfBody
-- How to run database actions.
instance YesodPersist ~sitearg~ where

View File

@ -2,19 +2,25 @@ module Import
( module Prelude
, module Yesod
, module Foundation
, (<>)
, Text
, module Settings.StaticFiles
, module Data.Monoid
, module Control.Applicative
, Text
#if __GLASGOW_HASKELL__ < 704
, (<>)
#endif
) where
import Prelude hiding (writeFile, readFile)
import Prelude hiding (writeFile, readFile, head, tail, init, last)
import Yesod hiding (Route(..))
import Foundation
import Data.Monoid (Monoid (mappend, mempty, mconcat))
import Control.Applicative ((<$>), (<*>), pure)
import Data.Text (Text)
import Settings.StaticFiles
#if __GLASGOW_HASKELL__ < 704
infixr 5 <>
(<>) :: Monoid m => m -> m -> m
(<>) = mappend
#endif

View File

@ -42,12 +42,12 @@ library
OverloadedStrings
NoImplicitPrelude
CPP
OverloadedStrings
MultiParamTypeClasses
TypeFamilies
GADTs
GeneralizedNewtypeDeriving
FlexibleContexts
EmptyDataDecls
executable ~project~
if flag(library-only)
@ -72,6 +72,7 @@ executable ~project~
GADTs
GeneralizedNewtypeDeriving
FlexibleContexts
EmptyDataDecls
build-depends: base >= 4 && < 5
, yesod >= 0.10 && < 0.11

File diff suppressed because one or more lines are too long

View File

@ -31,10 +31,14 @@
$maybe analytics <- extraAnalytics $ appExtra $ settings master
<script>
if(!window.location.href.match(/localhost/)){
window._gaq = [['_setAccount','#{analytics}'],['_trackPageview'],['_trackPageLoadTime']];
YepNope.load({
\ load: ('https:' == location.protocol ? '//ssl' : '//www') + '.google-analytics.com/ga.js'
});
(function() {
\ var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
\ ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
\ var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
}
\<!-- Prompt IE 6 users to install Chrome Frame. Remove this if you want to support IE 6. chromium.org/developers/how-tos/chrome-frame-getting-started -->
\<!--[if lt IE 7 ]>
<script src="//ajax.googleapis.com/ajax/libs/chrome-frame/1.0.3/CFInstall.min.js">

View File

@ -98,5 +98,5 @@ instance Yesod ~sitearg~ where
-- users receiving stale content.
addStaticContent = addStaticContentExternal (const $ Left ()) base64md5 Settings.staticDir (StaticR . flip StaticRoute [])
-- Enable Javascript async loading
yepnopeJs _ = Just $ Right $ StaticR js_modernizr_js
-- Place Javascript at bottom of the body tag so the rest of the page loads first
jsLoader _ = BottomOfBody

View File

@ -1,5 +1,5 @@
name: yesod
version: 0.10.1
version: 0.10.1.4
license: BSD3
license-file: LICENSE
author: Michael Snoyman <michael@snoyman.com>
@ -81,7 +81,7 @@ library
, monad-control >= 0.3 && < 0.4
, transformers >= 0.2.2 && < 0.3
, wai >= 1.1 && < 1.2
, wai-extra >= 1.1 && < 1.2
, wai-extra >= 1.1 && < 1.3
, wai-logger >= 0.1.2
, hamlet >= 0.10 && < 0.11
, shakespeare-js >= 0.11 && < 0.12
@ -101,7 +101,7 @@ executable yesod
cpp-options: -DWINDOWS
build-depends: parsec >= 2.1 && < 4
, text >= 0.11 && < 0.12
, shakespeare-text >= 0.10 && < 0.11
, shakespeare-text >= 0.10 && < 0.12
, bytestring >= 0.9.1.4 && < 0.10
, time >= 1.1.4
, template-haskell