packages feed

xing-api-0.1.1: demos/yesod-demo.hs

-- This demo shows how to use Yesod with the XING API.
--
-- The OAuth handshake is implemented without using yesod-auth.
-- Have a look at the yesod-auth-xing package for a simpler
-- solution to authenticate users using the XING API.
--
-- Make sure to set the environment variables XING_CONSUMER_KEY and
-- XING_CONSUMER_SECRET before trying this demo.

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeFamilies #-}

module Main where

-- TODO: Web.XING.Types.User.FullUser also exports languages. Find a better name.
import           Yesod.Core hiding (languages)
import           Network.Wai.Handler.Warp (run)
import           Text.Hamlet (hamlet)
import           Web.XING
import           Network.HTTP.Conduit (newManager, def)
import           Data.Maybe (fromJust, isJust, fromMaybe)
import qualified Data.Map as M
import           Helper.YesodHelper ( bootstrapLayout, bootstrapCDN
                                    , writeTokenToSession, getTokenFromSession
                                    , deleteTokenFromSession )
import qualified Data.ByteString.Char8 as BS
import           Data.Monoid (mappend)
import qualified Data.Text.Encoding as E
import           Data.Time
import qualified Data.Text as T
import           System.Environment (getEnv)

data HelloXING = HelloXING {
    httpManager   :: Manager
  , oAuthConsumer :: OAuth
}

instance Yesod HelloXING where
  defaultLayout = bootstrapLayout

mkYesod "HelloXING" [parseRoutes|
  /          HomeR      GET
  /handshake HandshakeR POST
  /callback  CallbackR  GET
  /logout    LogoutR    POST
|]

postHandshakeR :: Handler RepHtml
postHandshakeR = do
  yesod <- getYesod
  let oa = oAuthConsumer yesod
  let manager = httpManager yesod

  (requestToken, url) <- getRequestToken oa manager
  writeTokenToSession "request" requestToken

  redirect $ E.decodeUtf8 url

getCallbackR :: Handler RepHtml
getCallbackR = do
  yesod <- getYesod
  let oa = oAuthConsumer yesod
  let manager = httpManager yesod

  maybeRequestToken <- getTokenFromSession "request"

  maybeVerifier <- lookupGetParam "oauth_verifier"
  let verifier = E.encodeUtf8 (fromMaybe "" maybeVerifier)

  if isJust maybeRequestToken
    then do
      (accessToken, _) <- getAccessToken (fromJust maybeRequestToken) verifier oa manager
      writeTokenToSession "access" accessToken
    else return ()

  redirect HomeR

postLogoutR :: Handler RepHtml
postLogoutR = do
  deleteTokenFromSession "access"
  deleteTokenFromSession "request"
  redirect HomeR

daysUntilBirthday
  :: Maybe BirthDate
  -> Day
  -> Integer
daysUntilBirthday (Just (DayOnly birthMonth birthDay)) today    = calcDays birthMonth birthDay today
daysUntilBirthday (Just (FullDate _ birthMonth birthDay)) today = calcDays birthMonth birthDay today
daysUntilBirthday Nothing _                                     = 0

calcDays
  :: Int
  -> Int
  -> Day
  -> Integer
calcDays birthMonth birthDay today
  = if (birthDayThisYear >= 0)
      then birthDayThisYear
      else birthDayNextYear
  where
    (year, _, _)     = toGregorian today
    birthDayThisYear = diffDays (fromGregorian (year    ) birthMonth birthDay) today
    birthDayNextYear = diffDays (fromGregorian (year + 1) birthMonth birthDay) today

getHomeR :: Handler RepHtml
getHomeR = do
  maybeAccessToken <- getTokenFromSession "access"

  widget <- case maybeAccessToken of
    Just accessToken -> do
      yesod <- getYesod
      users <- getUsers (oAuthConsumer yesod) (httpManager yesod) accessToken ["me"]
      today <- liftIO $ getCurrentTime
      let firstUser = head $ unUserList users
      let birthDayInDays = daysUntilBirthday (birthDate firstUser) (utctDay today)
      return $ whoAmI firstUser birthDayInDays

    Nothing -> return pleaseLogIn

  defaultLayout $ do
    addStylesheetRemote $ bootstrapCDN `mappend` "/css/bootstrap-combined.min.css"
    [whamlet|
      <h1>Welcome to the XING API demo
      ^{widget}
    |]

pleaseLogIn :: Widget
pleaseLogIn =
  toWidget [hamlet|
    <img src="https://www.xing.com/img/n/nobody_m.png">
    <p>Hello unknown user. Please log-in.
    <form method=POST action=@{HandshakeR}>
      <input type=submit value="Login with XING">
  |]

whoAmI
  :: FullUser
  -> Integer
  -> Widget
whoAmI user birthDayInDays = do
  toWidget [hamlet|
    <img src=#{fromMaybe "" $ M.lookup "large" (photoUrls user)}>
    <p>
      <a href=#{permalink user}>#{displayName user}
    <p>
      Hey #{firstName user}! Welcome to this demo.<br>


    <p>
      Your next birthday is in #{show birthDayInDays} days.

    <p>
      Did you know? In Germany you would be greeted with "Guten Tag 
      $if (gender user) == Male
        Herr
      $else
        Frau
      \ #{lastName user}".

    $if (length (M.keys (languages user)) > 1)
      <p>
        Impressive! You are speaking #{length (M.keys (languages user))} languages:
        \  #{T.intercalate ", " (M.keys (languages user))}.

    $maybe mail <- activeEmail user
      <p>Your active email address is <a href=mailto:#{mail}>#{mail}</a>.

    <p>Here is a list of your premium services
    <ul>
      $forall service <- premiumServices user
        <li>#{service}

    $if null $ badges user
      You have no badges.
    $else
      <p>Your badges:
      <ul>
        $forall badge <- badges user
          <li>#{badge}

    <form method=POST action=@{LogoutR}>
      <input type=submit value="Logout">
  |]

main :: IO ()
main = do
  manager <- newManager def
  let port = 3000
  consumer_key    <- getEnv "XING_CONSUMER_KEY"
  consumer_secret <- getEnv "XING_CONSUMER_SECRET"
  let xingConsumer = consumer (BS.pack consumer_key) (BS.pack consumer_secret)
  let xingConsumer' = xingConsumer{
    oauthCallback = Just $ "http://localhost:" `mappend` (BS.pack.show) port `mappend` "/callback"
  }
  putStrLn $ "Starting on port " ++ show port
  run port =<< toWaiApp (HelloXING manager xingConsumer')