packages feed

clckwrks 0.26.4 → 0.27.1

raw patch · 12 files changed

+167/−42 lines, 12 filesdep ~happstack-authenticatePVP ok

version bump matches the API change (PVP)

Dependency ranges changed: happstack-authenticate

API changes (from Hackage documentation)

- Clckwrks.Authenticate.Plugin: AcidStateAuthenticate :: AcidState AuthenticateState -> AcidStateAuthenticate
- Clckwrks.Authenticate.Plugin: [acidStateAuthenticate] :: AcidStateAuthenticate -> AcidState AuthenticateState
- Clckwrks.Authenticate.Plugin: instance (GHC.Base.Functor m, Control.Monad.IO.Class.MonadIO m) => Clckwrks.Acid.GetAcidState (Clckwrks.Monad.ClckT url m) Happstack.Authenticate.Core.AuthenticateState
- Clckwrks.Authenticate.Plugin: newtype AcidStateAuthenticate
+ Clckwrks.Authenticate.Monad: AcidStateAuthenticate :: AcidState AuthenticateState -> AcidState PasswordState -> AcidStateAuthenticate
+ Clckwrks.Authenticate.Monad: [acidStateAuthenticate] :: AcidStateAuthenticate -> AcidState AuthenticateState
+ Clckwrks.Authenticate.Monad: [acidStatePassword] :: AcidStateAuthenticate -> AcidState PasswordState
+ Clckwrks.Authenticate.Monad: data AcidStateAuthenticate
+ Clckwrks.Authenticate.Monad: instance (GHC.Base.Functor m, Control.Monad.IO.Class.MonadIO m) => Clckwrks.Acid.GetAcidState (Clckwrks.Monad.ClckT url m) Happstack.Authenticate.Core.AuthenticateState
+ Clckwrks.Authenticate.Monad: instance (GHC.Base.Functor m, Control.Monad.IO.Class.MonadIO m) => Clckwrks.Acid.GetAcidState (Clckwrks.Monad.ClckT url m) Happstack.Authenticate.Password.Core.PasswordState
+ Clckwrks.Authenticate.Page.ViewUsers: viewUsers :: ClckT ClckURL (ServerPartT IO) Response
+ Clckwrks.Authenticate.URL: ViewUsers :: AuthURL
+ Clckwrks.ProfileData.EditProfileDataFor: passwordForFormlet :: UserId -> ClckForm ProfileDataURL ()

Files

Clckwrks/Admin/Route.hs view
@@ -15,3 +15,4 @@       EditNavBar        -> editNavBar       NavBarPost        -> navBarPost       SystemEmails      -> systemEmailsPage (Admin url)+
Clckwrks/Authenticate/API.hs view
@@ -11,7 +11,8 @@ import Control.Monad                (join) import Control.Monad.State          (get) import Control.Monad.Trans          (liftIO)-import Clckwrks.Authenticate.Plugin (AcidStateAuthenticate(..), authenticatePlugin)+import Clckwrks.Authenticate.Plugin (authenticatePlugin)+import Clckwrks.Authenticate.Monad  (AcidStateAuthenticate(..)) import Data.Acid as Acid            (AcidState, query, update) import Data.Maybe                   (maybe) import Data.Monoid                  (mempty)@@ -23,7 +24,7 @@ getUser :: UserId -> Clck url (Maybe User) getUser uid =   do p <- plugins <$> get-     ~(Just (AcidStateAuthenticate authenticateState)) <- getPluginState p (pluginName authenticatePlugin)+     ~(Just (AcidStateAuthenticate authenticateState _)) <- getPluginState p (pluginName authenticatePlugin)      liftIO $ Acid.query authenticateState (GetUserByUserId uid)  -- | Update an existing 'User'. Must already have a valid 'UserId'.@@ -33,7 +34,7 @@ insecureUpdateUser :: User -> Clck url () insecureUpdateUser user =   do p <- plugins <$> get-     ~(Just (AcidStateAuthenticate authenticateState)) <- getPluginState p (pluginName authenticatePlugin)+     ~(Just (AcidStateAuthenticate authenticateState _)) <- getPluginState p (pluginName authenticatePlugin)      liftIO $ Acid.update authenticateState (UpdateUser user)  getUsername :: UserId -> Clck url (Maybe Username)
+ Clckwrks/Authenticate/Monad.hs view
@@ -0,0 +1,30 @@+{-# LANGUAGE DeriveDataTypeable, RecordWildCards, FlexibleContexts, Rank2Types, OverloadedStrings, MultiParamTypeClasses #-}+module Clckwrks.Authenticate.Monad where++import Clckwrks.Acid               (GetAcidState(..), GetCoreState(..), GetEnableOpenId(..), acidCore, acidProfileData, coreFromAddress, coreReplyToAddress, coreSendmailPath, getAcidState)+import Clckwrks.Monad+import Control.Monad.State         (get)+import Control.Monad.Trans         (MonadIO, lift)+import Data.Acid as Acid           (AcidState, query)+import Data.Typeable               (Typeable)+import Happstack.Authenticate.Core (AuthenticateState, AuthenticateConfig(..), getToken, tokenUser, userId, usernamePolicy)+import Happstack.Authenticate.Password.Core (PasswordState)+import Web.Plugins.Core            (getPluginState)++data AcidStateAuthenticate = AcidStateAuthenticate+  { acidStateAuthenticate :: AcidState AuthenticateState+  , acidStatePassword     :: AcidState PasswordState                                                }+  deriving Typeable++instance (Functor m, MonadIO m) => GetAcidState (ClckT url m) AuthenticateState where+    getAcidState =+      do p <- plugins <$> get+         ~(Just (AcidStateAuthenticate authenticateState _)) <- getPluginState p "authenticate"+         pure authenticateState++instance (Functor m, MonadIO m) => GetAcidState (ClckT url m) PasswordState where+    getAcidState =+      do p <- plugins <$> get+         ~(Just (AcidStateAuthenticate _ passwordState)) <- getPluginState p "authenticate"+         pure passwordState+
Clckwrks/Authenticate/Page/ChangePassword.hs view
@@ -9,7 +9,7 @@  changePasswordPanel :: ClckT ClckURL (ServerPartT IO) Response changePasswordPanel =-    do template "Change Password" () $ [hsx|+  do template "Change Password" () $ [hsx|         <%>          <h2>Change Password</h2>          <div ng-controller="UsernamePasswordCtrl">
+ Clckwrks/Authenticate/Page/ViewUsers.hs view
@@ -0,0 +1,37 @@+{-# LANGUAGE FlexibleContexts, TypeFamilies, QuasiQuotes, OverloadedStrings #-}+module Clckwrks.Authenticate.Page.ViewUsers where++import Clckwrks.Admin.Template (template)+import Clckwrks.Monad+import Clckwrks.URL             (ClckURL(..))+import Clckwrks.Authenticate.Monad ()+import Clckwrks.ProfileData.URL(ProfileDataURL(..))+import Data.Maybe               (maybe)+import Data.Foldable                 (toList)+import qualified Data.Text      as Text+import Happstack.Server         (Response, ServerPartT, ok, toResponse)+import Happstack.Authenticate.Core (Email(..), GetUsers(..), User(..), UserId(..), Username(..))+import Language.Haskell.HSX.QQ (hsx)+import Web.Plugins.Core            (Plugin(..), getPluginState)+import Web.Routes (showURL)++viewUsers :: ClckT ClckURL (ServerPartT IO) Response+viewUsers =+  do us <- query GetUsers+     template "View Users" () $ [hsx|+        <div>+          <h2>Users</h2>+          <table class="table">+           <thead>+            <tr><th>UserId</th><th>Username</th><th>Email</th></tr>+           </thead>+           <tbody>+             <% mapM mkRow (toList us) %>+           </tbody>+          </table>+        </div> |]+         where+           mkRow u =+             do epdf <- showURL (Profile (EditProfileDataFor (_userId u)))+                [hsx| <tr><td><a href=(Text.unpack epdf)><% show $ _unUserId $ _userId u %></a></td><td><% _unUsername $ _username u %></td><td><% maybe (Text.empty) _unEmail (_email u) %></td></tr> |]+
Clckwrks/Authenticate/Plugin.hs view
@@ -3,6 +3,7 @@  import Clckwrks.Monad import Clckwrks.Acid               (GetAcidState(..), GetCoreState(..), GetEnableOpenId(..), acidCore, acidProfileData, coreFromAddress, coreReplyToAddress, coreSendmailPath, getAcidState)+import Clckwrks.Authenticate.Monad (AcidStateAuthenticate(..)) import Clckwrks.Authenticate.Route (routeAuth) import Clckwrks.Authenticate.URL   (AuthURL(..)) import Clckwrks.ProfileData.Acid   (HasRole(..))@@ -14,7 +15,7 @@ import Control.Monad.Reader        (ask) import Control.Monad.State         (get) import Control.Monad.Trans         (MonadIO, lift)-import Data.Acid as Acid           (AcidState, query)+import Data.Acid as Acid           (AcidState, query, openLocalStateFrom) import Data.Maybe                  (isJust) import Data.Monoid                 ((<>)) import qualified Data.Set          as Set@@ -26,17 +27,14 @@ import Data.UserId                  (UserId) import Happstack.Authenticate.Core  (AuthenticateState, AuthenticateConfig(..), getToken, tokenUser, userId, usernamePolicy) import Happstack.Authenticate.Route (initAuthentication)-import Happstack.Authenticate.Password.Core (PasswordConfig(..))-import Happstack.Authenticate.Password.Route (initPassword)+import Happstack.Authenticate.Password.Core (PasswordConfig(..), initialPasswordState)+import Happstack.Authenticate.Password.Route (initPassword') import Happstack.Authenticate.OpenId.Route (initOpenId) import Happstack.Server-import System.FilePath             ((</>))+import System.FilePath             ((</>), combine) import Web.Plugins.Core            (Plugin(..), When(Always), addCleanup, addHandler, addPluginState, getConfig, getPluginRouteFn, getPluginState, getPluginsSt, initPlugin) import Web.Routes -newtype AcidStateAuthenticate = AcidStateAuthenticate { acidStateAuthenticate :: AcidState AuthenticateState }-    deriving Typeable- authenticateHandler   :: (AuthenticateURL -> RouteT AuthenticateURL (ServerPartT IO) Response)   -> (AuthURL -> [(Text, Maybe Text)] -> Text)@@ -60,9 +58,10 @@ addAuthAdminMenu =     do p <- plugins <$> get        ~(Just authShowURL) <- getPluginRouteFn p (pluginName authenticatePlugin)-       addAdminMenu ("Authentication", [(Set.fromList [Visitor]      , "Change Password", authShowURL ChangePassword [])])-       addAdminMenu ("Authentication", [(Set.fromList [Administrator], "OpenId Realm"   , authShowURL OpenIdRealm    [])])-       addAdminMenu ("Authentication", [(Set.fromList [Administrator], "Authentication Modes", authShowURL AuthModes [])])+       addAdminMenu ("Authentication", [(Set.fromList [Visitor]      , "Change Password"     , authShowURL ChangePassword [])])+       addAdminMenu ("Authentication", [(Set.fromList [Administrator], "OpenId Realm"        , authShowURL OpenIdRealm    [])])+       addAdminMenu ("Authentication", [(Set.fromList [Administrator], "Authentication Modes", authShowURL AuthModes      [])])+       addAdminMenu ("Authentication", [(Set.fromList [Administrator], "View Users"          , authShowURL ViewUsers      [])])  authenticateInit   :: ClckPlugins@@ -92,10 +91,11 @@                           , _passwordAcceptable = const Nothing                           } +     passwordState <- openLocalStateFrom (combine basePath "password") initialPasswordState      (authCleanup, routeAuthenticate, authenticateState) <- initAuthentication (Just basePath) authenticateConfig-        ((initPassword passwordConfig) : if True then [ initOpenId ] else [])+        ((initPassword' passwordConfig passwordState) : if True then [ initOpenId ] else [])      addHandler     plugins (pluginName authenticatePlugin) (authenticateHandler routeAuthenticate authShowFn)-     addPluginState plugins (pluginName authenticatePlugin) (AcidStateAuthenticate authenticateState)+     addPluginState plugins (pluginName authenticatePlugin) (AcidStateAuthenticate authenticateState passwordState)      addCleanup plugins Always authCleanup      return Nothing {-@@ -133,14 +133,9 @@ getUserId :: (Happstack m) => ClckT url m (Maybe UserId) getUserId =   do p <- plugins <$> get-     ~(Just (AcidStateAuthenticate authenticateState)) <- getPluginState p (pluginName authenticatePlugin)+     ~(Just (AcidStateAuthenticate authenticateState _)) <- getPluginState p (pluginName authenticatePlugin)      mToken <- getToken authenticateState      case mToken of        Nothing       -> return Nothing        (Just (token, _)) -> return $ Just (token ^. tokenUser ^. userId) -instance (Functor m, MonadIO m) => GetAcidState (ClckT url m) AuthenticateState where-    getAcidState =-      do p <- plugins <$> get-         ~(Just (AcidStateAuthenticate authenticateState)) <- getPluginState p (pluginName authenticatePlugin)-         pure authenticateState
Clckwrks/Authenticate/Route.hs view
@@ -9,6 +9,7 @@ import Clckwrks.Authenticate.Page.ChangePassword (changePasswordPanel) import Clckwrks.Authenticate.Page.ResetPassword  (resetPasswordPage) import Clckwrks.Authenticate.Page.OpenIdRealm    (openIdRealmPanel)+import Clckwrks.Authenticate.Page.ViewUsers      (viewUsers) import Clckwrks.ProfileData.API    (Role(..), requiresRole_) import Clckwrks.URL                (ClckURL) -- import Clckwrks.Plugin             (clckPlugin)@@ -40,6 +41,7 @@        ChangePassword -> withClckURL changePasswordPanel        OpenIdRealm    -> withClckURL openIdRealmPanel        AuthModes      -> authModesPage u+       ViewUsers      -> withClckURL viewUsers  checkAuth :: (Happstack m, Monad m) =>              AuthURL@@ -55,3 +57,4 @@        AuthModes      -> requiresRole (Set.fromList [Administrator]) url        ChangePassword -> requiresRole (Set.fromList [Visitor]) url        OpenIdRealm    -> requiresRole (Set.fromList [Administrator]) url+       ViewUsers      -> requiresRole (Set.fromList [Administrator]) url
Clckwrks/Authenticate/URL.hs view
@@ -16,6 +16,7 @@   | ChangePassword   | OpenIdRealm   | AuthModes+  | ViewUsers   deriving (Eq, Ord, Data, Typeable, Generic, Read, Show)  derivePathInfo ''AuthURL
Clckwrks/ProfileData/EditNewProfileData.hs view
@@ -5,7 +5,8 @@ import Clckwrks import Clckwrks.Monad              (getRedirectCookie) import Clckwrks.Admin.Template     (emptyTemplate)-import Clckwrks.Authenticate.Plugin (AcidStateAuthenticate(..), authenticatePlugin)+import Clckwrks.Authenticate.Plugin (authenticatePlugin)+import Clckwrks.Authenticate.Monad (AcidStateAuthenticate(..)) import Clckwrks.ProfileData.Acid   (GetProfileData(..), SetProfileData(..)) import Clckwrks.ProfileData.EditProfileData(profileDataFormlet) import Control.Monad.State         (get)@@ -34,7 +35,7 @@          (Just uid) ->              do -- pd <- query (GetProfileData uid)                 p <- plugins <$> get-                ~(Just (AcidStateAuthenticate authenticateState)) <- getPluginState p (pluginName authenticatePlugin)+                ~(Just (AcidStateAuthenticate authenticateState _)) <- getPluginState p (pluginName authenticatePlugin)                 ~(Just user) <- liftIO $ Acid.query authenticateState (GetUserByUserId uid)                 pd <- query (GetProfileData uid)                 action <- showURL here
Clckwrks/ProfileData/EditProfileData.hs view
@@ -4,7 +4,8 @@ import Clckwrks import Clckwrks.Monad              (plugins) import Clckwrks.Admin.Template     (template)-import Clckwrks.Authenticate.Plugin (AcidStateAuthenticate(..), authenticatePlugin)+import Clckwrks.Authenticate.Plugin (authenticatePlugin)+import Clckwrks.Authenticate.Monad (AcidStateAuthenticate(..)) import Clckwrks.ProfileData.Acid   (GetProfileData(..), SetProfileData(..)) import Control.Monad.State         (get) import Control.Monad.Trans         (liftIO)@@ -33,7 +34,7 @@          (Just uid) ->              do -- pd <- query (GetProfileData uid)                 p <- plugins <$> get-                ~(Just (AcidStateAuthenticate authenticateState)) <- getPluginState p (pluginName authenticatePlugin)+                ~(Just (AcidStateAuthenticate authenticateState _)) <- getPluginState p (pluginName authenticatePlugin)                 ~(Just user) <- liftIO $ Acid.query authenticateState (GetUserByUserId uid)                 pd <- query (GetProfileData uid)                 action <- showURL here@@ -70,7 +71,7 @@       do let user = u { _email       = if Text.null eml then Nothing else (Just (Email eml))                       }          p <- plugins <$> get-         ~(Just (AcidStateAuthenticate authenticateState)) <- getPluginState p (pluginName authenticatePlugin)+         ~(Just (AcidStateAuthenticate authenticateState _)) <- getPluginState p (pluginName authenticatePlugin)          liftIO $ Acid.update authenticateState  (UpdateUser user)          pd <- query (GetProfileData _userId)          update (SetProfileData (pd { displayName = if Text.null dn then Nothing else Just (DisplayName dn) }))
Clckwrks/ProfileData/EditProfileDataFor.hs view
@@ -1,45 +1,98 @@-{-# LANGUAGE RecordWildCards, OverloadedStrings #-}-{-# OPTIONS_GHC -F -pgmFhsx2hs #-}+{-# LANGUAGE RecordWildCards, OverloadedStrings, QuasiQuotes #-} module Clckwrks.ProfileData.EditProfileDataFor where  import Clckwrks import Clckwrks.Admin.Template  (template) import Clckwrks.ProfileData.Acid (GetProfileData(..), SetProfileData(..))+import Clckwrks.Authenticate.Monad () import Data.Maybe               (fromMaybe) import Data.Set                 as Set-import Data.Text                (Text, pack)+import Data.Text.Lazy           (Text)+import qualified Data.Text.Lazy as LT import qualified Data.Text      as Text import Data.UserId              (UserId)+import Language.Haskell.HSX.QQ  (hsx)+import Happstack.Authenticate.Core (Email(..), GetUserByUserId(..), User(..), UserId(..), Username(..))+import Happstack.Authenticate.Password.Core (SetPassword(..), mkHashedPass) import HSP.XMLGenerator import HSP.XML-import Text.Reform              ((++>), transformEitherM)+import Text.Reform              ((++>), mapView, transformEitherM) import Text.Reform.Happstack    (reform)-import Text.Reform.HSP.Text     (inputCheckboxes, inputText, labelText, inputSubmit, fieldset, ol, li, form)+import Text.Reform.HSP.Text     (inputCheckboxes, inputPassword, inputText, labelText, inputSubmit, fieldset, ol, li, form, setAttrs)  editProfileDataForPage :: ProfileDataURL -> UserId -> Clck ProfileDataURL Response editProfileDataForPage here uid =     do pd <- query (GetProfileData uid)-       action <- showURL here-       template "Edit Profile Data" () $-         <% reform (form action) "epd" updated Nothing (profileDataFormlet pd) %>-+       mu  <- query (GetUserByUserId uid)+       case mu of+         Nothing ->+           template "Edit Profile Data" () $+             [hsx|+               <div>Invalid UserId <% show uid  %></div>+             |]+         (Just u) ->+           do action <- showURL here+              template "Edit Profile Data" () $ [hsx|+                <div>+                 <h2>User Info</h2>+                 <dl>+                  <dt>UserId</dt>  <dd><% show $ _unUserId $ _userId u %></dd>+                  <dt>Username</dt><dd><% _unUsername $ _username u %></dd>+                  <dt>Email</dt>   <dd><% maybe Text.empty _unEmail  (_email u) %></dd>+                 </dl>+                 <h2>Roles</h2>+                 <% reform (form action) "epd" updated Nothing (profileDataFormlet pd) %>+                 <h2>Update User's Password</h2>+                 <% reform (form action) "pf"  updated Nothing (passwordForFormlet uid) %>+               </div>+               |]     where       updated :: () -> Clck ProfileDataURL Response       updated () =           do seeOtherURL here +passwordForFormlet :: UserId -> ClckForm ProfileDataURL ()+passwordForFormlet userid =+    (fieldset $+      (divControlGroup $+        (divControls (label' "new password" ++> inputPassword))+        )+      <* (divControlGroup $ divControls $ inputSubmit "Change Password"  `setAttrs` [("class" := "btn") :: Attr Text Text])+      ) `transformEitherM` updatePassword+    where+      label' :: Text -> ClckForm ProfileDataURL ()+      label' str      = (labelText str `setAttrs` [("class":="control-label") :: Attr Text Text])+--       divHorizontal   = mapView (\xml -> [[hsx|<div class="form-horizontal"><% xml %></div>|]])+      divControlGroup = mapView (\xml -> [[hsx|<div class="control-group"><% xml %></div>|]])+      divControls     = mapView (\xml -> [[hsx|<div class="controls"><% xml %></div>|]])++      updatePassword :: Text.Text -> Clck ProfileDataURL (Either ClckFormError ())+      updatePassword newPw+        | Text.null newPw = pure (Right ())+        | otherwise =+            do hp <- mkHashedPass newPw+               update (SetPassword userid hp)+               pure (Right ())+ profileDataFormlet :: ProfileData -> ClckForm ProfileDataURL () profileDataFormlet pd@ProfileData{..} =     (fieldset $-      ol $-       ((li $ labelText "roles:")            ++> (li $ inputCheckboxes [ (r, show r) | r <- [minBound .. maxBound]] (\r -> Set.member r roles))-             <*  inputSubmit (pack "update")-       )+      (divControlGroup $+        (divControls (inputCheckboxes [ (r, show r) | r <- [minBound .. maxBound]] (\r -> Set.member r roles)) `setAttrs` (("class" := "form-check") :: Attr Text Text)))+      <* (divControlGroup $ divControls $ inputSubmit "Update Roles"  `setAttrs` [("class" := "btn") :: Attr Text Text])     ) `transformEitherM` updateProfileData     where+      label' :: Text -> ClckForm ProfileDataURL ()+      label' str      = (labelText str `setAttrs` [("class":="control-label") :: Attr Text Text])+--       divHorizontal   = mapView (\xml -> [[hsx|<div class="form-horizontal"><% xml %></div>|]])+      divControlGroup = mapView (\xml -> [[hsx|<div class="control-group"><% xml %></div>|]])+      divControls     = mapView (\xml -> [[hsx|<div class="controls"><% xml %></div>|]])+       updateProfileData :: [Role] -> Clck ProfileDataURL (Either ClckFormError ())       updateProfileData roles' =         do let newPd = pd { roles    = Set.fromList roles'                           }            update (SetProfileData newPd)            pure (Right ())++--       ((li $ labelText "roles:")            ++> ((li $ inputCheckboxes [ (r, show r) | r <- [minBound .. maxBound]] (\r -> Set.member r roles)) `setAttrs` (("class" := "form-check") :: Attr Text Text))
clckwrks.cabal view
@@ -1,5 +1,5 @@ Name:                clckwrks-Version:             0.26.4+Version:             0.27.1 Synopsis:            A secure, reliable content management system (CMS) and blogging platform Description:         clckwrks (pronounced, clockworks) aims to compete                      directly with popular PHP-based blogging and CMS@@ -46,11 +46,13 @@                    Clckwrks.Admin.EditSettings                    Clckwrks.Admin.SystemEmails                    Clckwrks.Authenticate.API+                   Clckwrks.Authenticate.Monad                    Clckwrks.Authenticate.Page.AuthModes                    Clckwrks.Authenticate.Page.ChangePassword                    Clckwrks.Authenticate.Page.Login                    Clckwrks.Authenticate.Page.OpenIdRealm                    Clckwrks.Authenticate.Page.ResetPassword+                   Clckwrks.Authenticate.Page.ViewUsers                    Clckwrks.Authenticate.Plugin                    Clckwrks.Authenticate.Route                    Clckwrks.Authenticate.URL@@ -105,7 +107,7 @@      containers                   >= 0.4  && < 0.7,      directory                    >= 1.1  && < 1.4,      filepath                     >= 1.2  && < 1.5,-     happstack-authenticate       >= 2.4  && < 2.5,+     happstack-authenticate       >= 2.4.2  && < 2.5,      happstack-hsp                == 7.3.*,      happstack-jmacro             >= 7.0  && < 7.1,      happstack-server             >= 7.0  && < 7.8,