yesod how to solve this error? - haskell

I am just started with Yesod, I am following this tutorial:http://yannesposito.com/Scratch/en/blog/Yesod-tutorial-for-newbies/
I get this error:
Handler/Blog.hs:32:17:
Couldn't match type `handler' with `GHandler App App'
`handler' is a rigid type variable bound by
the type signature for postBlogR :: handler RepHtml
at Handler/Blog.hs:29:14
Expected type: handler [Entity Article]
Actual type: GHandler App App [Entity Article]
In a stmt of a 'do' block:
articles <- runDB $ selectList [] [Desc ArticleTitle]
In the expression:
do { articles <- runDB $ selectList [] [Desc ArticleTitle];
(articleWidget, enctype) <- generateFormPost entryForm;
defaultLayout $ do { $(widgetFile "articles") } }
In an equation for `postBlogR':
postBlogR
= do { articles <- runDB $ selectList [] [Desc ArticleTitle];
(articleWidget, enctype) <- generateFormPost entryForm;
defaultLayout $ do { ... } }
this is my Blog.hs:
module Handler.Blog
( getBlogR
, postBlogR
)
where
import Import
-- to use Html into forms
import Yesod.Form.Nic (YesodNic, nicHtmlField)
instance YesodNic App
entryForm :: Form Article
entryForm = renderDivs $ Article
<$> areq textField "Title" Nothing
<*> areq nicHtmlField "Content" Nothing
-- The view showing the list of articles
getBlogR :: Handler Html
getBlogR = do
-- Get the list of articles inside the database.
articles <- runDB $ selectList [] [Desc ArticleTitle]
-- We'll need the two "objects": articleWidget and enctype
-- to construct the form (see templates/articles.hamlet).
(articleWidget, enctype) <- generateFormPost entryForm
defaultLayout $ do
$(widgetFile "articles")
postBlogR :: handler RepHtml
postBlogR = do
-- Get the list of articles inside the database.
articles <- runDB $ selectList [] [Desc ArticleTitle]
-- We'll need the two "objects": articleWidget and enctype
-- to construct the form (see templates/articles.hamlet).
(articleWidget, enctype) <- generateFormPost entryForm
defaultLayout $ do
$(widgetFile "articles")
my routes:
/static StaticR Static getStatic
/auth AuthR Auth getAuth
/favicon.ico FaviconR GET
/robots.txt RobotsR GET
/ HomeR GET POST
/echo/#Text EchoR GET
/mirror MirrorR GET POST
/blog BlogR GET POST
/blog/#ArticleId ArticleR GET
and my models:
User
ident Text
password Text Maybe
UniqueUser ident
Email
email Text
user UserId Maybe
verkey Text Maybe
UniqueEmail email
Article
title Text
content Html
deriving
-- By default this file is used in Model.hs (which is imported by Foundation.hs)

I think, you just have to fix your type signature for postBlogR to Handler RepHtml. Names starting with lower case letters are reserved for type variables in type signatures, so it cannot be deduced right here.

Related

How can I create a Yesod form where I can select from a dropdown list where the list is passed as an argument?

I would like to create a form in which I pass a [a] an an argument and get back an MForm Handler (FormResult a, Widget).
I have attempted to implement this using functions such as selectFieldList but have not been able to find a solution. I have not been able to find an answer anywhere on google. I have found many examples where dropdown lists have been used as individual fields in a larger form but none where the dropdown list is the entire form itself.
Edit:
I have managed to make a form that compiles. Unfortunately I am unable to tell if it works the way I would like because I cannot get it to render.
Here are the key functions I am working with (they all compile):
mkCityStringM :: [PG.DbCity] -> [(T.Text, PG.DbCity)]
mkCityStringM xs = zip (map (T.pack . showDbCity) xs) xs
data ContainCity =
ContainCity
{ getCity :: PG.DbCity
}
deriving (Show)
ambiguityForm :: [PG.DbCity] -> AForm Handler ContainCity
ambiguityForm cities = ContainCity
<$> areq (selectFieldList cityMap) "City" Nothing
where
cityMap :: [(T.Text, PG.DbCity)]
cityMap = W.mkCityStringM cities
The problem is when I try to actually use this table I run into issues. Here is code that works just fine for me:
locationForm :: Html -> MForm Handler (FormResult BasicLocation, Widget)
locationForm = renderDivs $ BasicLocation
<$> areq textField "City:" Nothing
<*> areq textField "Country:" (Just "United States")
postAmbiguityR :: [PG.DbCity] -> Handler Html
postAmbiguityR cs = do
(widget, enctype) <- generateFormPost locationForm
defaultLayout $ do
[whamlet|
<p>who cares
|]
On the other hand I have this code:
postAmbiguityR :: [PG.DbCity] -> Handler Html
postAmbiguityR cs = do
(widget, enctype) <- generateFormPost (ambiguityForm cs) --only difference
defaultLayout $ do
[whamlet|
<p> WHATEVER
|]
which throws me this error:
• Couldn't match expected type ‘blaze-markup-0.8.2.5:Text.Blaze.Internal.Markup
-> MForm (HandlerFor Base) (FormResult a0, xml0)’
with actual type ‘AForm Handler ContainCity’
• Possible cause: ‘ambiguityForm’ is applied to too many arguments
In the first argument of ‘generateFormPost’, namely
‘(ambiguityForm cs)’
In a stmt of a 'do' block:
(widget, enctype) <- generateFormPost (ambiguityForm cs)
In the expression:
do (widget, enctype) <- generateFormPost (ambiguityForm cs)
defaultLayout
$ do (do (asWidgetT . toWidget)
((blaze-markup-0.8.2.5:Text.Blaze.Internal.preEscapedText . T.pack)
"<html><header></header>
<form method="post" action="")
....)
|
132 | (widget, enctype) <- generateFormPost (ambiguityForm cs)
| ^^^^^^^^^^^^^^^^
This error message does not make sense to me. It says a possible cause is that ambiguity form is applied to too many arguments but does not seem to be the case as the form takes a list of PG.DbCity's and that is exactly what I supplied it.
The problem is that ambiguityForm is an AForm, but generateFormPost expects an MForm. To convert it to an MForm suitable for rendering, you need to use renderDivs, like you did in your locationForm example. You can either do this in the definition of ambiguityForm, which will change its type/defintion to look something like:
ambiguityForm :: [PG.DbCity] -> Html -> MForm Handler (FormResult ContainCity, Widget)
ambiguityForm cities = renderDivs $ ContainCity ...
or you can leave ambiguityForm unchanged and add renderDivs at the usage site:
postAmbiguityR :: [PG.DbCity] -> Handler Html
postAmbiguityR cs = do
(widget, enctype) <- generateFormPost (renderDivs (ambiguityForm cs))
defaultLayout $ do
[whamlet|
<p> WHATEVER
|]

Having trouble compiling yesod route after introducing a parameter

I am running a modified example of
http://www.yesodweb.com/book/blog-example-advanced
that previously did not use a parameter id of type DBEntitySchemaId.
Since introducing it, I am getting
Handler/Entity.hs:146:34:
Couldn't match type ‘DBEntitySchemaId -> Route App’
with ‘Route (HandlerSite (WidgetT App IO))’
Expected type: WidgetT
App IO ((DBEntitySchemaId -> Route App) -> [(Text, Text)] -> Text)
Actual type: WidgetT
App
IO
(Route (HandlerSite (WidgetT App IO)) -> [(Text, Text)] -> Text)
In the first argument of ‘(>>=)’, namely ‘getUrlRenderParams’
In a stmt of a 'do' block:
(getUrlRenderParams
>>=
(\ urender_akdM
-> (asWidgetT GHC.Base.. toWidget)
(toHtml (\ u_akdN -> urender_akdM u_akdN [] EntityR))))
which refers to the line using the defaultLayout.
The relevant code is:
getEntityR :: DBEntitySchemaId -> Handler Html
getEntityR id = do
oldEntities <- runDB $ selectList [DBEntityDbentitySchemaId ==. id] []
fields <- runDB $ selectList [DBFieldDbentitySchemaId ==. id, DBFieldFieldTypeId ==. (toSqlKey 1)] []
mauthId <- maybeAuthId
((res, widget), enctype) <- runFormPost (form fields)
case (mauthId,res) of
(Just userId, FormSuccess (attributeValues)) -> do
setMessageI $ MsgSubmittedKarma
let entity = DBEntity "Test" id
entId <- runDB $ insert entity
let fieldsNVals = zip fields attributeValues
let einfuegen field attributeValue = case (field, attributeValue) of
((Entity fldId _), (FormSuccess val)) -> do
a <- runDB $ insert (Attribute {attributeEntityId = entId, attributeDbfieldId = fldId, attributeStringValue = val})
return (Just a)
_ -> return Nothing
_ <- forM fieldsNVals (\(field, attributeValue) -> einfuegen field attributeValue)
redirect $ (EntityR id)
_ -> do
--render <- getUrlRenderParams
--return $ renderHtml $ [hamlet|
defaultLayout [whamlet|
$if null oldEntities
<p>There are no entities
$else
<ul>
$forall Entity entityId entity <- oldEntities
<li>
<p>#{dBEntityName entity}
$if mauthId == Nothing
<form method=post action=#{EntityR} enctype=#{enctype}>
<input type=submit value=_{MsgPleaseLogin}>
$else
<form method=post action=#{EntityR} enctype=#{enctype}>
^{widget}
<input type=submit>
|]
What I already tried was changing whamlet to hamlet and
adding some lines of code from here (commented out)
Trying to send an email in yesod using hamlet
and
changing
[whamlet|
to
[whamlet|#{getEntityR}/#{id}|
and
[whamlet||#?{urlParams id}|
and some variations of this inspired by
Render url with query parameters
https://groups.google.com/forum/#!topic/yesodweb/q1gtkOgM32I
I assume there is only a small modification to make. However my understanding of whamlet/hamlet is not too great. So I am posting this in case it is a trivial problem. I fully understand that I need to put in more work, I am just having trouble googling my problem. So please bear with me.
After modifying your route you forgot to pass newly added parameter into #{EntityR} interpolation, something like this: #{EntityR param}.

Why is my Yesod app throwing a TlsNotSupported exception when I try to log in?

I'm trying to follow along with Yesod's cookbook for a blog. I've changed a few things such as switching to a PostgreSQL database, adding a link for GoogleEmail authentication, and moving some of the Shakespearean templates to separate files.
My problem is that when I run the app and try to authenticate, I get returned a TlsNotSupported exception and I have no idea what's causing it or how to find out. I've used both forms of authentication in a separate app and both have worked fine.
My code is below. Any help would be greatly appreciated.
{-# LANGUAGE OverloadedStrings, TypeFamilies, QuasiQuotes,
TemplateHaskell, GADTs, FlexibleContexts,
MultiParamTypeClasses, DeriveDataTypeable #-}
import Yesod
import Yesod.Auth
import Yesod.Form.Nic (YesodNic, nicHtmlField)
import Yesod.Auth.BrowserId (authBrowserId, def)
import Yesod.Auth.GoogleEmail (authGoogleEmail)
import Data.Text (Text)
import Network.HTTP.Client (defaultManagerSettings)
import Network.HTTP.Conduit (Manager, newManager)
import Database.Persist.Postgresql
( ConnectionString, ConnectionPool, SqlPersistT, runSqlPool, runMigration
, withPostgresqlPool, runSqlPersistMPool
)
import Data.Time (UTCTime, getCurrentTime)
import Control.Applicative ((<$>), (<*>), pure)
import Data.Typeable (Typeable)
import Text.Hamlet (hamletFile)
import Text.Lucius (luciusFile)
share [mkPersist sqlOnlySettings, mkMigrate "migrateAll"]
[persistLowerCase|
User
email Text
UniqueUser email
deriving Typeable
Entry
title Text
posted UTCTime
content Html
Comment
entry EntryId
posted UTCTime
user UserId
name Text
text Textarea
|]
data Blog = Blog
{ connPool :: ConnectionPool
, httpManager :: Manager
}
mkMessage "Blog" "blog-messages" "en"
mkYesod "Blog" [parseRoutes|
/ HomeR GET
/blog BlogR GET POST
/blog/#EntryId EntryR GET POST
/auth AuthR Auth getAuth
|]
instance Yesod Blog where
approot = ApprootStatic "http://localhost:3000"
isAuthorized BlogR True = do
mauth <- maybeAuth
case mauth of
Nothing -> return AuthenticationRequired
Just (Entity _ user)
| isAdmin user -> return Authorized
| otherwise -> unauthorizedI MsgNotAnAdmin
isAuthorized (EntryR _) True = do
mauth <- maybeAuth
case mauth of
Nothing -> return AuthenticationRequired
Just _ -> return Authorized
isAuthorized _ _ = return Authorized
authRoute _ = Just (AuthR LoginR)
defaultLayout inside = do
mmsg <- getMessage
pc <- widgetToPageContent $ do
toWidget $(luciusFile "template.lucius")
inside
giveUrlRenderer $(hamletFile "template.hamlet")
isAdmin :: User -> Bool
isAdmin user = userEmail user == "xxxxx#gmail.com"
instance YesodPersist Blog where
type YesodPersistBackend Blog = SqlPersistT
runDB f = do
master <- getYesod
let pool = connPool master
runSqlPool f pool
type Form x = Html -> MForm Handler (FormResult x, Widget)
instance RenderMessage Blog FormMessage where
renderMessage _ _ = defaultFormMessage
instance YesodNic Blog
instance YesodAuth Blog where
type AuthId Blog = UserId
loginDest _ = HomeR
logoutDest _ = HomeR
authHttpManager = httpManager
authPlugins _ = [ authBrowserId def
, authGoogleEmail
]
getAuthId creds = do
let email = credsIdent creds
user = User email
res <- runDB $ insertBy user
return $ Just $ either entityKey id res
getHomeR :: Handler Html
getHomeR = defaultLayout $ do
setTitleI MsgHomepageTitle
[whamlet|
<p>_{MsgWelcomeHomepage}
<p>
<a href=#{BlogR}>_{MsgSeeArchive}
|]
entryForm :: Form Entry
entryForm = renderDivs $ Entry
<$> areq textField (fieldSettingsLabel MsgNewEntryTitle) Nothing
<*> lift (liftIO getCurrentTime)
<*> areq nicHtmlField (fieldSettingsLabel MsgNewEntryContent) Nothing
getBlogR :: Handler Html
getBlogR = do
muser <- maybeAuth
entries <- runDB $ selectList [] [Desc EntryPosted]
(entryWidget, enctype) <- generateFormPost entryForm
defaultLayout $ do
setTitleI MsgBlogArchiveTitle
$(whamletFile "blog.hamlet")
postBlogR :: Handler Html
postBlogR = do
((res, entryWidget), enctype) <- runFormPost entryForm
case res of
FormSuccess entry -> do
entryId <- runDB $ insert entry
setMessageI $ MsgEntryCreated $ entryTitle entry
redirect $ EntryR entryId
_ -> defaultLayout $ do
setTitleI MsgPleaseCorrectEntry
[whamlet|
<form method=post enctype=#{enctype}>
^{entryWidget}
<div>
<input type=submit value=_{MsgNewEntry}>
|]
commentForm :: EntryId -> Form Comment
commentForm entryId = renderDivs $ Comment
<$> pure entryId
<*> lift (liftIO getCurrentTime)
<*> lift requireAuthId
<*> areq textField (fieldSettingsLabel MsgCommentName) Nothing
<*> areq textareaField (fieldSettingsLabel MsgCommentText) Nothing
getEntryR :: EntryId -> Handler Html
getEntryR entryId = do
(entry, comments) <- runDB $ do
entry <- get404 entryId
comments <- selectList [CommentEntry ==. entryId] [Asc CommentPosted]
return (entry, map entityVal comments)
muser <- maybeAuth
(commentWidget, enctype) <- generateFormPost (commentForm entryId)
defaultLayout $ do
setTitleI $ MsgEntryTitle $ entryTitle entry
$(whamletFile "entry.hamlet")
postEntryR :: EntryId -> Handler Html
postEntryR entryId = do
((res, commentWidget), enctype) <- runFormPost (commentForm entryId)
case res of
FormSuccess comment -> do
_ <- runDB $ insert comment
setMessageI MsgCommentAdded
redirect $ EntryR entryId
_ -> defaultLayout $ do
setTitleI MsgPleaseCorrectComment
[whamlet|
<form method=post enctype=#{enctype}>
^{commentWidget}
<div>
<input type=submit value=_{MsgAddCommentButton}>
|]
openConnectionCount :: Int
openConnectionCount = 10
connStr :: ConnectionString
connStr = "host=localhost dbname=postgres user=postgres password=postgres port=5432"
main :: IO ()
main = withPostgresqlPool connStr openConnectionCount $ \pool -> do
runSqlPersistMPool (runMigration migrateAll) pool
manager <- newManager defaultManagerSettings
warp 3000 $ Blog pool manager
edit: My platform is Arch Linux.
import Network.HTTP.Client (defaultManagerSettings)
You need to use tlsManagerSettings from Network.HTTP.Client.TLS instead.

Lookup query parameters in Yesod

I just initialized a Yesod project (no database) using yesod init.
My HomeR GET handler looks like this:
getHomeR :: Handler Html
getHomeR = do
(formWidget, formEnctype) <- generateFormPost sampleForm
let submission = Nothing :: Maybe (FileInfo, Text)
handlerName = "getHomeR" :: Text
defaultLayout $ do
aDomId <- newIdent
setTitle "Welcome To Yesod!"
$(widgetFile "homepage")
When using yesod devel, I can access the default homepage at http://localhost:3000/.
How can I modify the handler listed above to retrieve (and display) a HTTP GET query parameter like id=abc123 when accessing this URL:
http://localhost:3000/?id=abc123
Note: This question was answered Q&A-style and therefore intentionally doesn't show research effort!
I'll show two different methods to achieve this. For both, you'll need to add this code to your template, e.g. in homepage.hamlet:
Note that it is not guaranteed there is any id parameter present when accessing the URL, therefore the type resulting from both methods is Maybe Text. See the Shakespearean template docs for a detailed explanation of the template parameters.
Method 1: lookupGetParam
The easiest way you can do this is using lookupGetParam like this:
idValueMaybe <- lookupGetParam "id"
When using the default setting as generated by yesod init, idValueMaybe needs to be defined in both getHomeR and postHomeR if idValueMaybe is used in the template.
Your HomeR GET handler could look like this:
getHomeR :: Handler Html
getHomeR = do
idValueMaybe <- lookupGetParam "id"
(formWidget, formEnctype) <- generateFormPost sampleForm
let submission = Nothing :: Maybe (FileInfo, Text)
handlerName = "getHomeR" :: Text
defaultLayout $ do
aDomId <- newIdent
setTitle "Welcome To Yesod!"
$(widgetFile "homepage")
Method 2: reqGetParams
Instead of looking up the query parameters by name, you can also retrieve a list of query key/value pairs using reqGetParams. This can be advantageous in certain situations, e.g. if you don't know all possible keys in advance. Using the standard lookup function you can easily lookup a certain key in that list.
The relevant part of your code could look like this:
getParameters <- reqGetParams <$> getRequest
let idValueMaybe = lookup "id" getParameters :: Maybe Text
Your getHomeR could look like this:
getHomeR :: Handler Html
getHomeR = do
getParameters <- reqGetParams <$> getRequest
let idValueMaybe = lookup "id" getParameters :: Maybe Text
(formWidget, formEnctype) <- generateFormPost sampleForm
let submission = Nothing :: Maybe (FileInfo, Text)
handlerName = "getHomeR" :: Text
defaultLayout $ do
aDomId <- newIdent
setTitle "Welcome To Yesod!"
$(widgetFile "homepage")

How to render Html algebraic data type in Yesod

I'm trying to use CKEditor in my Yesod application. Data from CKEditor is returned to the server via Textarea, I then store it as Html in database. My problem is I do know know how to display the Html algebraic data type once I retrieve it from database in the handler. I've been reading this tutorial, but it will only display the Html as a big long string, not as markup.
Note: titleA and contextA are the variable that I want to display in article-local-display.
contextA is the Html algebraic data type
PS: Do I need to transform Html to hamlet in order to render?
module Handler.Article where
import Import
import Data.Text (unpack)
import Data.Time (getCurrentTime)
import Data.String (fromString)
getArticleR :: Handler RepHtml
getArticleR = do
defaultLayout $ do
setTitle "Search For Article"
$(widgetFile "header")
$(widgetFile "article")
postArticleR :: Handler RepHtml
postArticleR = do
redirect ArticleR
getArticleLocalR :: Handler RepHtml
getArticleLocalR = do
articles <- runDB $ selectList ([] :: [Filter Article]) [Desc ArticleTime]
defaultLayout $ do
setTitle "Local Article"
$(widgetFile "header")
$(widgetFile "article-local")
getArticleLocalDisplayR :: ArticleId -> Handler RepHtml
getArticleLocalDisplayR articleId = do
article <- runDB $ get404 articleId
let titleA = articleTitle article
contextA = articleContext article
defaultLayout $ do
setTitle "Article"
$(widgetFile "header")
$(widgetFile "article-local-display")
getArticleLocalCreateR :: Handler RepHtml
getArticleLocalCreateR = do
defaultLayout $ do
setTitle "Create article"
addScript $ StaticR ckeditor_ckeditor_js
$(widgetFile "header")
$(widgetFile "article-local-create")
postArticleLocalCreateR :: Handler RepHtml
postArticleLocalCreateR = do
articleForm <- runInputPost $ ArticleForm <$> ireq textField "title" <*> ireq textareaField "editor1"
now <- liftIO getCurrentTime
let titleA = title articleForm
html = toHtml $ unTextarea $ context articleForm
_ <- runDB $ insert $ Article titleA html now
redirect ArticleLocalR
data ArticleForm = ArticleForm {
title :: Text,
context :: Textarea
}
deriving Show
models file:
Article
title Text
context Html
time UTCTime
deriving
article-local-display.hamlet
<h1>#{titleA}
<article>#{contexA}
So I changed the context from Html to Text.
Article
title Text
context Text
time UTCTime
deriving
Then added preEscapedText when using the value.
let contextA = preEscapedText $ articleContext article
Now it's displaying properly.

Resources