Merge branch 'master' into uni2work
This commit is contained in:
commit
cb75192e0c
4
.github/workflows/tests.yml
vendored
4
.github/workflows/tests.yml
vendored
@ -21,6 +21,8 @@ jobs:
|
|||||||
- "--resolver lts-12"
|
- "--resolver lts-12"
|
||||||
- "--resolver lts-11"
|
- "--resolver lts-11"
|
||||||
- "--stack-yaml stack-persistent-211.yaml"
|
- "--stack-yaml stack-persistent-211.yaml"
|
||||||
|
- "--stack-yaml stack-persistent-212.yaml"
|
||||||
|
- "--stack-yaml stack-persistent-213.yaml"
|
||||||
# Bugs in GHC make it crash too often to be worth running
|
# Bugs in GHC make it crash too often to be worth running
|
||||||
exclude:
|
exclude:
|
||||||
- os: windows-latest
|
- os: windows-latest
|
||||||
@ -29,6 +31,8 @@ jobs:
|
|||||||
args: "--resolver lts-16"
|
args: "--resolver lts-16"
|
||||||
- os: windows-latest
|
- os: windows-latest
|
||||||
args: "--stack-yaml stack-persistent-211.yaml"
|
args: "--stack-yaml stack-persistent-211.yaml"
|
||||||
|
- os: windows-latest
|
||||||
|
args: "--stack-yaml stack-persistent-212.yaml"
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Clone project
|
- name: Clone project
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@ -25,3 +25,4 @@ tarballs/
|
|||||||
|
|
||||||
# OS X
|
# OS X
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
*.yaml.lock
|
||||||
|
|||||||
@ -21,7 +21,7 @@ data Wiki = Wiki
|
|||||||
}
|
}
|
||||||
|
|
||||||
-- | A typeclass that all master sites that want a Wiki must implement. A
|
-- | A typeclass that all master sites that want a Wiki must implement. A
|
||||||
-- master must be able to render form messages, as we use yesod-forms for
|
-- master must be able to render form messages, as we use yesod-form for
|
||||||
-- processing user input.
|
-- processing user input.
|
||||||
class (RenderMessage master FormMessage, Yesod master) => YesodWiki master where
|
class (RenderMessage master FormMessage, Yesod master) => YesodWiki master where
|
||||||
-- | Write protection. By default, no protection.
|
-- | Write protection. By default, no protection.
|
||||||
|
|||||||
44
flake.lock
Normal file
44
flake.lock
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"flake-utils": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1619345332,
|
||||||
|
"narHash": "sha256-qHnQkEp1uklKTpx3MvKtY6xzgcqXDsz5nLilbbuL+3A=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "2ebf2558e5bf978c7fb8ea927dfaed8fefab2e28",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"ref": "master",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1620323686,
|
||||||
|
"narHash": "sha256-+gfcE3YTGl+Osc8HzOUXSFO8/0PAK4J8ZxCXZ4hjXHI=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "dfacb8329b2236688b9a1e705116203a213b283a",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "master",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": "flake-utils",
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
||||||
30
flake.nix
Normal file
30
flake.nix
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
inputs = {
|
||||||
|
nixpkgs = {
|
||||||
|
type = "github";
|
||||||
|
owner = "NixOS";
|
||||||
|
repo = "nixpkgs";
|
||||||
|
ref = "master";
|
||||||
|
};
|
||||||
|
flake-utils = {
|
||||||
|
type = "github";
|
||||||
|
owner = "numtide";
|
||||||
|
repo = "flake-utils";
|
||||||
|
ref = "master";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = { self, nixpkgs, flake-utils, ... }: flake-utils.lib.eachDefaultSystem
|
||||||
|
(system:
|
||||||
|
let pkgs = import nixpkgs {
|
||||||
|
inherit system;
|
||||||
|
config.allowUnfree = true;
|
||||||
|
};
|
||||||
|
in {
|
||||||
|
devShell = pkgs.mkShell {
|
||||||
|
name = "uni2work-yesod";
|
||||||
|
nativeBuildInputs = with pkgs.haskellPackages; [ stack ];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
8
nixpkgs.nix
Normal file
8
nixpkgs.nix
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import (
|
||||||
|
let
|
||||||
|
lock = builtins.fromJSON (builtins.readFile ./flake.lock);
|
||||||
|
in fetchTarball {
|
||||||
|
url = "https://api.github.com/repos/NixOS/nixpkgs/tarball/${lock.nodes.nixpkgs.locked.rev}";
|
||||||
|
sha256 = lock.nodes.nixpkgs.locked.narHash;
|
||||||
|
}
|
||||||
|
)
|
||||||
20
stack-persistent-212.yaml
Normal file
20
stack-persistent-212.yaml
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
resolver: nightly-2021-03-31
|
||||||
|
packages:
|
||||||
|
- ./yesod-core
|
||||||
|
- ./yesod-static
|
||||||
|
- ./yesod-persistent
|
||||||
|
- ./yesod-newsfeed
|
||||||
|
- ./yesod-form
|
||||||
|
- ./yesod-form-multi
|
||||||
|
- ./yesod-auth
|
||||||
|
- ./yesod-auth-oauth
|
||||||
|
- ./yesod-sitemap
|
||||||
|
- ./yesod-test
|
||||||
|
- ./yesod-bin
|
||||||
|
- ./yesod
|
||||||
|
- ./yesod-eventsource
|
||||||
|
- ./yesod-websockets
|
||||||
|
extra-deps:
|
||||||
|
- persistent-2.12.0.1
|
||||||
|
- persistent-template-2.12.0.0
|
||||||
|
- persistent-sqlite-2.12.0.0
|
||||||
23
stack-persistent-213.yaml
Normal file
23
stack-persistent-213.yaml
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
resolver: nightly-2021-03-31
|
||||||
|
packages:
|
||||||
|
- ./yesod-core
|
||||||
|
- ./yesod-static
|
||||||
|
- ./yesod-persistent
|
||||||
|
- ./yesod-newsfeed
|
||||||
|
- ./yesod-form
|
||||||
|
- ./yesod-form-multi
|
||||||
|
- ./yesod-auth
|
||||||
|
- ./yesod-auth-oauth
|
||||||
|
- ./yesod-sitemap
|
||||||
|
- ./yesod-test
|
||||||
|
- ./yesod-bin
|
||||||
|
- ./yesod
|
||||||
|
- ./yesod-eventsource
|
||||||
|
- ./yesod-websockets
|
||||||
|
extra-deps:
|
||||||
|
- lift-type-0.1.0.1
|
||||||
|
- persistent-2.13.0.0
|
||||||
|
- persistent-mysql-2.13.0.0
|
||||||
|
- persistent-sqlite-2.13.0.0
|
||||||
|
- persistent-postgresql-2.13.0.0
|
||||||
|
- persistent-template-2.12.0.0
|
||||||
14
stack.nix
Normal file
14
stack.nix
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
{ ghc, nixpkgs ? import ./nixpkgs.nix }:
|
||||||
|
|
||||||
|
let
|
||||||
|
# haskellPackages = import ./stackage.nix { inherit nixpkgs; };
|
||||||
|
haskellPackages = pkgs.haskellPackages;
|
||||||
|
inherit (nixpkgs {}) pkgs;
|
||||||
|
in pkgs.haskell.lib.buildStackProject {
|
||||||
|
inherit ghc;
|
||||||
|
inherit (haskellPackages) stack;
|
||||||
|
name = "stackenv";
|
||||||
|
buildInputs = with pkgs;
|
||||||
|
[ zlib
|
||||||
|
];
|
||||||
|
}
|
||||||
@ -1,7 +1,10 @@
|
|||||||
nix:
|
nix:
|
||||||
packages: [zlib]
|
packages: []
|
||||||
|
pure: false
|
||||||
|
shell-file: ./stack.nix
|
||||||
|
add-gc-roots: true
|
||||||
|
|
||||||
resolver: lts-15.5
|
resolver: lts-16.31
|
||||||
packages:
|
packages:
|
||||||
- ./yesod-core
|
- ./yesod-core
|
||||||
- ./yesod-static
|
- ./yesod-static
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
packages: []
|
packages: []
|
||||||
snapshots:
|
snapshots:
|
||||||
- completed:
|
- completed:
|
||||||
size: 491372
|
size: 534126
|
||||||
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/15/5.yaml
|
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/16/31.yaml
|
||||||
sha256: 1b549cfff328040c382a70a84a2087aac8dab6d778bf92f32a93a771a1980dfc
|
sha256: 637fb77049b25560622a224845b7acfe81a09fdb6a96a3c75997a10b651667f6
|
||||||
original: lts-15.5
|
original: lts-16.31
|
||||||
|
|||||||
@ -1,3 +1,9 @@
|
|||||||
|
# ChangeLog for yesod-auth-oauth
|
||||||
|
|
||||||
|
## 1.6.0.3
|
||||||
|
|
||||||
|
* Allow yesod-form 1.7
|
||||||
|
|
||||||
## 1.6.0.2
|
## 1.6.0.2
|
||||||
|
|
||||||
* Remove unnecessary deriving of Typeable
|
* Remove unnecessary deriving of Typeable
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
cabal-version: >= 1.10
|
cabal-version: >= 1.10
|
||||||
name: yesod-auth-oauth
|
name: yesod-auth-oauth
|
||||||
version: 1.6.0.2
|
version: 1.6.0.3
|
||||||
license: BSD3
|
license: BSD3
|
||||||
license-file: LICENSE
|
license-file: LICENSE
|
||||||
author: Hiromi Ishii
|
author: Hiromi Ishii
|
||||||
@ -22,7 +22,7 @@ library
|
|||||||
, unliftio
|
, unliftio
|
||||||
, yesod-auth >= 1.6 && < 1.7
|
, yesod-auth >= 1.6 && < 1.7
|
||||||
, yesod-core >= 1.6 && < 1.7
|
, yesod-core >= 1.6 && < 1.7
|
||||||
, yesod-form >= 1.6 && < 1.7
|
, yesod-form >= 1.6 && < 1.8
|
||||||
exposed-modules: Yesod.Auth.OAuth
|
exposed-modules: Yesod.Auth.OAuth
|
||||||
ghc-options: -Wall
|
ghc-options: -Wall
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,13 @@
|
|||||||
# ChangeLog for yesod-auth
|
# ChangeLog for yesod-auth
|
||||||
|
|
||||||
|
## 1.6.10.3
|
||||||
|
|
||||||
|
* Relax bounds for yesod-form 1.7
|
||||||
|
|
||||||
|
## 1.6.10.2
|
||||||
|
|
||||||
|
* Relax bounds for persistent 2.12
|
||||||
|
|
||||||
## 1.6.10.1
|
## 1.6.10.1
|
||||||
|
|
||||||
* Add support for Persistent 2.11 [#1701](https://github.com/yesodweb/yesod/pull/1701)
|
* Add support for Persistent 2.11 [#1701](https://github.com/yesodweb/yesod/pull/1701)
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
cabal-version: >=1.10
|
cabal-version: >=1.10
|
||||||
name: yesod-auth
|
name: yesod-auth
|
||||||
version: 1.6.10.1
|
version: 1.6.10.3
|
||||||
license: MIT
|
license: MIT
|
||||||
license-file: LICENSE
|
license-file: LICENSE
|
||||||
author: Michael Snoyman, Patrick Brisbin
|
author: Michael Snoyman, Patrick Brisbin
|
||||||
@ -44,7 +44,7 @@ library
|
|||||||
, http-types
|
, http-types
|
||||||
, memory
|
, memory
|
||||||
, nonce >= 1.0.2 && < 1.1
|
, nonce >= 1.0.2 && < 1.1
|
||||||
, persistent >= 2.8 && < 2.12
|
, persistent >= 2.8
|
||||||
, random >= 1.0.0.2
|
, random >= 1.0.0.2
|
||||||
, safe
|
, safe
|
||||||
, shakespeare
|
, shakespeare
|
||||||
@ -57,7 +57,7 @@ library
|
|||||||
, unordered-containers
|
, unordered-containers
|
||||||
, wai >= 1.4
|
, wai >= 1.4
|
||||||
, yesod-core >= 1.6 && < 1.7
|
, yesod-core >= 1.6 && < 1.7
|
||||||
, yesod-form >= 1.6 && < 1.7
|
, yesod-form >= 1.6 && < 1.8
|
||||||
, yesod-persistent >= 1.6
|
, yesod-persistent >= 1.6
|
||||||
|
|
||||||
if flag(network-uri)
|
if flag(network-uri)
|
||||||
|
|||||||
@ -1,5 +1,22 @@
|
|||||||
# ChangeLog for yesod-core
|
# ChangeLog for yesod-core
|
||||||
|
|
||||||
|
## 1.6.20.2
|
||||||
|
|
||||||
|
* Fix compatibility with template-haskell 2.17 [#1729](https://github.com/yesodweb/yesod/pull/1729)
|
||||||
|
|
||||||
|
## 1.6.20.1
|
||||||
|
|
||||||
|
* Throw an error in `breadcrumbs` if the trail of breadcrumbs is circular. [#1727](https://github.com/yesodweb/yesod/issues/1727)
|
||||||
|
|
||||||
|
## 1.6.20
|
||||||
|
|
||||||
|
* Generate CSRF tokens using a secure entropy source [#1726](https://github.com/yesodweb/yesod/pull/1726)
|
||||||
|
* Change semantics of `yreGen` and `defaultGen`
|
||||||
|
|
||||||
|
## 1.6.19.0
|
||||||
|
|
||||||
|
* Change order of priority in `languages`[#1721](https://github.com/yesodweb/yesod/pull/1721)
|
||||||
|
|
||||||
## 1.6.18.8
|
## 1.6.18.8
|
||||||
|
|
||||||
* Fix test suite for wai-extra change around vary header
|
* Fix test suite for wai-extra change around vary header
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
{-# LANGUAGE FlexibleContexts #-}
|
||||||
module Yesod.Core.Class.Breadcrumbs where
|
module Yesod.Core.Class.Breadcrumbs where
|
||||||
|
|
||||||
import Yesod.Core.Handler
|
import Yesod.Core.Handler
|
||||||
@ -15,7 +16,7 @@ class YesodBreadcrumbs site where
|
|||||||
|
|
||||||
-- | Gets the title of the current page and the hierarchy of parent pages,
|
-- | Gets the title of the current page and the hierarchy of parent pages,
|
||||||
-- along with their respective titles.
|
-- along with their respective titles.
|
||||||
breadcrumbs :: YesodBreadcrumbs site => HandlerFor site (Text, [(Route site, Text)])
|
breadcrumbs :: (YesodBreadcrumbs site, Show (Route site), Eq (Route site)) => HandlerFor site (Text, [(Route site, Text)])
|
||||||
breadcrumbs = do
|
breadcrumbs = do
|
||||||
x <- getCurrentRoute
|
x <- getCurrentRoute
|
||||||
case x of
|
case x of
|
||||||
@ -26,6 +27,8 @@ breadcrumbs = do
|
|||||||
return (title, z)
|
return (title, z)
|
||||||
where
|
where
|
||||||
go back Nothing = return back
|
go back Nothing = return back
|
||||||
go back (Just this) = do
|
go back (Just this)
|
||||||
(title, next) <- breadcrumb this
|
| this `elem` map fst back = error $ "yesod-core: infinite recursion in breadcrumbs at " ++ show this
|
||||||
go ((this, title) : back) next
|
| otherwise = do
|
||||||
|
(title, next) <- breadcrumb this
|
||||||
|
go ((this, title) : back) next
|
||||||
|
|||||||
@ -46,6 +46,7 @@ import qualified Network.Wai as W
|
|||||||
|
|
||||||
import Data.ByteString.Lazy.Char8 ()
|
import Data.ByteString.Lazy.Char8 ()
|
||||||
|
|
||||||
|
import Data.Bits ((.|.), finiteBitSize, shiftL)
|
||||||
import Data.Text (Text)
|
import Data.Text (Text)
|
||||||
import qualified Data.ByteString as S
|
import qualified Data.ByteString as S
|
||||||
import qualified Data.ByteString.Lazy as BL
|
import qualified Data.ByteString.Lazy as BL
|
||||||
@ -59,7 +60,7 @@ import Yesod.Core.Class.Dispatch
|
|||||||
import Yesod.Core.Internal.Run
|
import Yesod.Core.Internal.Run
|
||||||
import Text.Read (readMaybe)
|
import Text.Read (readMaybe)
|
||||||
import System.Environment (getEnvironment)
|
import System.Environment (getEnvironment)
|
||||||
import qualified System.Random as Random
|
import System.Entropy (getEntropy)
|
||||||
import Control.AutoUpdate (mkAutoUpdate, defaultUpdateSettings, updateAction, updateFreq)
|
import Control.AutoUpdate (mkAutoUpdate, defaultUpdateSettings, updateAction, updateFreq)
|
||||||
import Yesod.Core.Internal.Util (getCurrentMaxExpiresRFC1123)
|
import Yesod.Core.Internal.Util (getCurrentMaxExpiresRFC1123)
|
||||||
|
|
||||||
@ -92,8 +93,19 @@ toWaiAppPlain site = do
|
|||||||
, yreGetMaxExpires = getMaxExpires
|
, yreGetMaxExpires = getMaxExpires
|
||||||
}
|
}
|
||||||
|
|
||||||
|
-- | Generate a random number uniformly distributed in the full range
|
||||||
|
-- of 'Int'.
|
||||||
|
--
|
||||||
|
-- Note: Before 1.6.20, this generates pseudo-random number in an
|
||||||
|
-- unspecified range. The range size may not be a power of 2. Since
|
||||||
|
-- 1.6.20, this uses a secure entropy source and generates in the full
|
||||||
|
-- range of 'Int'.
|
||||||
defaultGen :: IO Int
|
defaultGen :: IO Int
|
||||||
defaultGen = Random.getStdRandom Random.next
|
defaultGen = bsToInt <$> getEntropy bytes
|
||||||
|
where
|
||||||
|
bits = finiteBitSize (undefined :: Int)
|
||||||
|
bytes = div (bits + 7) 8
|
||||||
|
bsToInt = S.foldl' (\v i -> shiftL v 8 .|. fromIntegral i) 0
|
||||||
|
|
||||||
-- | Pure low level function to construct WAI application. Usefull
|
-- | Pure low level function to construct WAI application. Usefull
|
||||||
-- when you need not standard way to run your app, or want to embed it
|
-- when you need not standard way to run your app, or want to embed it
|
||||||
|
|||||||
@ -1226,10 +1226,10 @@ cacheBySet key value = do
|
|||||||
-- Languages are determined based on the following (in descending order
|
-- Languages are determined based on the following (in descending order
|
||||||
-- of preference):
|
-- of preference):
|
||||||
--
|
--
|
||||||
-- * The _LANG user session variable.
|
|
||||||
--
|
|
||||||
-- * The _LANG get parameter.
|
-- * The _LANG get parameter.
|
||||||
--
|
--
|
||||||
|
-- * The _LANG user session variable.
|
||||||
|
--
|
||||||
-- * The _LANG cookie.
|
-- * The _LANG cookie.
|
||||||
--
|
--
|
||||||
-- * Accept-Language HTTP header.
|
-- * Accept-Language HTTP header.
|
||||||
@ -1238,11 +1238,12 @@ cacheBySet key value = do
|
|||||||
-- If a matching language is not found the default language will be used.
|
-- If a matching language is not found the default language will be used.
|
||||||
--
|
--
|
||||||
-- This is handled by parseWaiRequest (not exposed).
|
-- This is handled by parseWaiRequest (not exposed).
|
||||||
|
--
|
||||||
|
-- __NOTE__: Before version @1.6.19.0@, this function prioritized the session
|
||||||
|
-- variable above all other sources.
|
||||||
|
--
|
||||||
languages :: MonadHandler m => m [Text]
|
languages :: MonadHandler m => m [Text]
|
||||||
languages = do
|
languages = reqLangs <$> getRequest
|
||||||
mlang <- lookupSession langKey
|
|
||||||
langs <- reqLangs <$> getRequest
|
|
||||||
return $ maybe id (:) mlang langs
|
|
||||||
|
|
||||||
lookup' :: Eq a => a -> [(a, b)] -> [b]
|
lookup' :: Eq a => a -> [(a, b)] -> [b]
|
||||||
lookup' a = map snd . filter (\x -> a == fst x)
|
lookup' a = map snd . filter (\x -> a == fst x)
|
||||||
|
|||||||
@ -107,9 +107,9 @@ mkYesodDispatch name = fmap snd . mkYesodWithParser name False return
|
|||||||
-- | Get the Handler and Widget type synonyms for the given site.
|
-- | Get the Handler and Widget type synonyms for the given site.
|
||||||
masterTypeSyns :: [Name] -> Type -> [Dec] -- FIXME remove from here, put into the scaffolding itself?
|
masterTypeSyns :: [Name] -> Type -> [Dec] -- FIXME remove from here, put into the scaffolding itself?
|
||||||
masterTypeSyns vs site =
|
masterTypeSyns vs site =
|
||||||
[ TySynD (mkName "Handler") (fmap PlainTV vs)
|
[ TySynD (mkName "Handler") (fmap plainTV vs)
|
||||||
$ ConT ''HandlerFor `AppT` site
|
$ ConT ''HandlerFor `AppT` site
|
||||||
, TySynD (mkName "Widget") (fmap PlainTV vs)
|
, TySynD (mkName "Widget") (fmap plainTV vs)
|
||||||
$ ConT ''WidgetFor `AppT` site `AppT` ConT ''()
|
$ ConT ''WidgetFor `AppT` site `AppT` ConT ''()
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@ -196,7 +196,13 @@ data YesodRunnerEnv site = YesodRunnerEnv
|
|||||||
, yreSite :: !site
|
, yreSite :: !site
|
||||||
, yreSessionBackend :: !(Maybe SessionBackend)
|
, yreSessionBackend :: !(Maybe SessionBackend)
|
||||||
, yreGen :: !(IO Int)
|
, yreGen :: !(IO Int)
|
||||||
-- ^ Generate a random number
|
-- ^ Generate a random number uniformly distributed in the full
|
||||||
|
-- range of 'Int'.
|
||||||
|
--
|
||||||
|
-- Note: Before 1.6.20, the default value generates pseudo-random
|
||||||
|
-- number in an unspecified range. The range size may not be a power
|
||||||
|
-- of 2. Since 1.6.20, the default value uses a secure entropy source
|
||||||
|
-- and generates in the full range of 'Int'.
|
||||||
, yreGetMaxExpires :: !(IO Text)
|
, yreGetMaxExpires :: !(IO Text)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import YesodCoreTest.InternalRequest
|
|||||||
import YesodCoreTest.ErrorHandling
|
import YesodCoreTest.ErrorHandling
|
||||||
import YesodCoreTest.Cache
|
import YesodCoreTest.Cache
|
||||||
import YesodCoreTest.ParameterizedSite
|
import YesodCoreTest.ParameterizedSite
|
||||||
|
import YesodCoreTest.Breadcrumb
|
||||||
import qualified YesodCoreTest.WaiSubsite as WaiSubsite
|
import qualified YesodCoreTest.WaiSubsite as WaiSubsite
|
||||||
import qualified YesodCoreTest.Redirect as Redirect
|
import qualified YesodCoreTest.Redirect as Redirect
|
||||||
import qualified YesodCoreTest.JsLoader as JsLoader
|
import qualified YesodCoreTest.JsLoader as JsLoader
|
||||||
@ -61,3 +62,4 @@ specs = do
|
|||||||
Ssl.sslOnlySpec
|
Ssl.sslOnlySpec
|
||||||
Ssl.sameSiteSpec
|
Ssl.sameSiteSpec
|
||||||
Csrf.csrfSpec
|
Csrf.csrfSpec
|
||||||
|
breadcrumbTest
|
||||||
|
|||||||
58
yesod-core/test/YesodCoreTest/Breadcrumb.hs
Normal file
58
yesod-core/test/YesodCoreTest/Breadcrumb.hs
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
{-# LANGUAGE FlexibleInstances #-}
|
||||||
|
{-# LANGUAGE MultiParamTypeClasses #-}
|
||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
{-# LANGUAGE QuasiQuotes #-}
|
||||||
|
{-# LANGUAGE Rank2Types #-}
|
||||||
|
{-# LANGUAGE TemplateHaskell #-}
|
||||||
|
{-# LANGUAGE TypeFamilies #-}
|
||||||
|
|
||||||
|
module YesodCoreTest.Breadcrumb
|
||||||
|
( breadcrumbTest,
|
||||||
|
)
|
||||||
|
where
|
||||||
|
|
||||||
|
import qualified Data.ByteString.Lazy.Char8 as L8
|
||||||
|
import Data.Text (Text)
|
||||||
|
import Data.Typeable (Typeable)
|
||||||
|
import Network.Wai
|
||||||
|
import Network.Wai.Test
|
||||||
|
import Test.Hspec
|
||||||
|
import UnliftIO.IORef
|
||||||
|
import Yesod.Core
|
||||||
|
|
||||||
|
data A = A
|
||||||
|
|
||||||
|
mkYesod
|
||||||
|
"A"
|
||||||
|
[parseRoutes|
|
||||||
|
/ RootR GET
|
||||||
|
/loop LoopR GET
|
||||||
|
|]
|
||||||
|
|
||||||
|
instance Yesod A
|
||||||
|
|
||||||
|
instance YesodBreadcrumbs A where
|
||||||
|
breadcrumb r = case r of
|
||||||
|
RootR -> pure ("Root", Nothing)
|
||||||
|
LoopR -> pure ("Loop", Just LoopR) -- Purposefully a loop
|
||||||
|
|
||||||
|
getRootR :: Handler Text
|
||||||
|
getRootR = fst <$> breadcrumbs
|
||||||
|
|
||||||
|
getLoopR :: Handler Text
|
||||||
|
getLoopR = fst <$> breadcrumbs
|
||||||
|
|
||||||
|
breadcrumbTest :: Spec
|
||||||
|
breadcrumbTest =
|
||||||
|
describe "Test.Breadcrumb" $ do
|
||||||
|
it "can fetch the root which contains breadcrumbs" $
|
||||||
|
runner $ do
|
||||||
|
res <- request defaultRequest
|
||||||
|
assertStatus 200 res
|
||||||
|
it "gets a 500 for a route with a looping breadcrumb" $
|
||||||
|
runner $ do
|
||||||
|
res <- request defaultRequest {pathInfo = ["loop"]}
|
||||||
|
assertStatus 500 res
|
||||||
|
|
||||||
|
runner :: Session () -> IO ()
|
||||||
|
runner f = toWaiApp A >>= runSession f
|
||||||
@ -1,5 +1,5 @@
|
|||||||
name: yesod-core
|
name: yesod-core
|
||||||
version: 1.6.18.8
|
version: 1.6.20.2
|
||||||
license: MIT
|
license: MIT
|
||||||
license-file: LICENSE
|
license-file: LICENSE
|
||||||
author: Michael Snoyman <michael@snoyman.com>
|
author: Michael Snoyman <michael@snoyman.com>
|
||||||
@ -39,6 +39,7 @@ library
|
|||||||
, containers >= 0.2
|
, containers >= 0.2
|
||||||
, cookie >= 0.4.3 && < 0.5
|
, cookie >= 0.4.3 && < 0.5
|
||||||
, deepseq >= 1.3
|
, deepseq >= 1.3
|
||||||
|
, entropy
|
||||||
, fast-logger >= 2.2
|
, fast-logger >= 2.2
|
||||||
, http-types >= 0.7
|
, http-types >= 0.7
|
||||||
, memory
|
, memory
|
||||||
|
|||||||
@ -1,5 +1,9 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 1.7.0.2
|
||||||
|
|
||||||
|
* Allow yesod-form 1.7
|
||||||
|
|
||||||
## 1.7.0.1
|
## 1.7.0.1
|
||||||
|
|
||||||
[#1716](https://github.com/yesodweb/yesod/pull/1716)
|
[#1716](https://github.com/yesodweb/yesod/pull/1716)
|
||||||
@ -23,4 +27,4 @@
|
|||||||
|
|
||||||
[#1601](https://github.com/yesodweb/yesod/pull/1601)
|
[#1601](https://github.com/yesodweb/yesod/pull/1601)
|
||||||
|
|
||||||
* Added `Yesod.Form.MultiInput` which supports multi-input forms without needing to submit the form to add an input field
|
* Added `Yesod.Form.MultiInput` which supports multi-input forms without needing to submit the form to add an input field
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
name: yesod-form-multi
|
name: yesod-form-multi
|
||||||
version: 1.7.0.1
|
version: 1.7.0.2
|
||||||
license: MIT
|
license: MIT
|
||||||
license-file: LICENSE
|
license-file: LICENSE
|
||||||
author: James Burton <jamesejburton@gmail.com>
|
author: James Burton <jamesejburton@gmail.com>
|
||||||
@ -26,7 +26,7 @@ library
|
|||||||
, text >= 0.9
|
, text >= 0.9
|
||||||
, transformers >= 0.2.2
|
, transformers >= 0.2.2
|
||||||
, yesod-core >= 1.6 && < 1.7
|
, yesod-core >= 1.6 && < 1.7
|
||||||
, yesod-form >= 1.6 && < 1.7
|
, yesod-form >= 1.6 && < 1.8
|
||||||
|
|
||||||
if flag(network-uri)
|
if flag(network-uri)
|
||||||
build-depends: network-uri >= 2.6
|
build-depends: network-uri >= 2.6
|
||||||
|
|||||||
@ -1,5 +1,9 @@
|
|||||||
# ChangeLog for yesod-form
|
# ChangeLog for yesod-form
|
||||||
|
|
||||||
|
## 1.7.0
|
||||||
|
|
||||||
|
* Extended `OptionList` by `OptionListGrouped` and implemented grouped select fields (`<select>` with `<optgroup>`) [#1722](https://github.com/yesodweb/yesod/pull/1722)
|
||||||
|
|
||||||
## 1.6.7
|
## 1.6.7
|
||||||
|
|
||||||
* Added equivalent version of `mreqMsg` for `areq` and `wreq` correspondingly [#1628](https://github.com/yesodweb/yesod/pull/1628)
|
* Added equivalent version of `mreqMsg` for `areq` and `wreq` correspondingly [#1628](https://github.com/yesodweb/yesod/pull/1628)
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
Form handling for Yesod, in the same style as formlets. See [the forms
|
Form handling for Yesod, in the same style as formlets. See [the forms
|
||||||
chapter](http://www.yesodweb.com/book/forms) of the Yesod book.
|
chapter](http://www.yesodweb.com/book/forms) of the Yesod book.
|
||||||
|
|
||||||
This package provies a set of basic form inputs such as text, number, time,
|
This package provides a set of basic form inputs such as text, number, time,
|
||||||
checkbox, select, textarea, and etc. via `Yesod.Form.Fields` module. Also,
|
checkbox, select, textarea, and etc. via `Yesod.Form.Fields` module. Also,
|
||||||
there is `Yesod.Form.Nic` module providing richtext field using Nic editor.
|
there is `Yesod.Form.Nic` module providing richtext field using Nic editor.
|
||||||
However, this module is grandfathered now and Nic editor is not actively
|
However, this module is grandfathered now and Nic editor is not actively
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
{-# LANGUAGE TypeFamilies #-}
|
{-# LANGUAGE TypeFamilies #-}
|
||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
|
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
|
||||||
|
{-# LANGUAGE ScopedTypeVariables #-}
|
||||||
{-# LANGUAGE FlexibleContexts #-}
|
{-# LANGUAGE FlexibleContexts #-}
|
||||||
{-# LANGUAGE CPP #-}
|
{-# LANGUAGE CPP #-}
|
||||||
-- | Field functions allow you to easily create and validate forms, cleanly handling the uncertainty of parsing user input.
|
-- | Field functions allow you to easily create and validate forms, cleanly handling the uncertainty of parsing user input.
|
||||||
@ -45,6 +46,7 @@ module Yesod.Form.Fields
|
|||||||
, selectFieldHelper
|
, selectFieldHelper
|
||||||
, selectField
|
, selectField
|
||||||
, selectFieldList
|
, selectFieldList
|
||||||
|
, selectFieldListGrouped
|
||||||
, radioField
|
, radioField
|
||||||
, radioFieldList
|
, radioFieldList
|
||||||
, checkboxesField
|
, checkboxesField
|
||||||
@ -54,9 +56,11 @@ module Yesod.Form.Fields
|
|||||||
, Option (..)
|
, Option (..)
|
||||||
, OptionList (..)
|
, OptionList (..)
|
||||||
, mkOptionList
|
, mkOptionList
|
||||||
|
, mkOptionListGrouped
|
||||||
, optionsPersist
|
, optionsPersist
|
||||||
, optionsPersistKey
|
, optionsPersistKey
|
||||||
, optionsPairs
|
, optionsPairs
|
||||||
|
, optionsPairsGrouped
|
||||||
, optionsEnum
|
, optionsEnum
|
||||||
) where
|
) where
|
||||||
|
|
||||||
@ -80,7 +84,7 @@ import Database.Persist (Entity (..), SqlType (SqlString), PersistRecordBackend,
|
|||||||
import Database.Persist (Entity (..), SqlType (SqlString), PersistEntity, PersistQuery, PersistEntityBackend)
|
import Database.Persist (Entity (..), SqlType (SqlString), PersistEntity, PersistQuery, PersistEntityBackend)
|
||||||
#endif
|
#endif
|
||||||
import Text.HTML.SanitizeXSS (sanitizeBalance)
|
import Text.HTML.SanitizeXSS (sanitizeBalance)
|
||||||
import Control.Monad (when, unless)
|
import Control.Monad (when, unless, forM_)
|
||||||
import Data.Either (partitionEithers)
|
import Data.Either (partitionEithers)
|
||||||
import Data.Maybe (listToMaybe, fromMaybe)
|
import Data.Maybe (listToMaybe, fromMaybe)
|
||||||
|
|
||||||
@ -172,7 +176,7 @@ timeField = timeFieldTypeTime
|
|||||||
--
|
--
|
||||||
-- Add the @time@ package and import the "Data.Time.LocalTime" module to use this function.
|
-- Add the @time@ package and import the "Data.Time.LocalTime" module to use this function.
|
||||||
--
|
--
|
||||||
-- Since 1.4.2
|
-- @since 1.4.2
|
||||||
timeFieldTypeTime :: Monad m => RenderMessage (HandlerSite m) FormMessage => Field m TimeOfDay
|
timeFieldTypeTime :: Monad m => RenderMessage (HandlerSite m) FormMessage => Field m TimeOfDay
|
||||||
timeFieldTypeTime = timeFieldOfType "time"
|
timeFieldTypeTime = timeFieldOfType "time"
|
||||||
|
|
||||||
@ -182,7 +186,7 @@ timeFieldTypeTime = timeFieldOfType "time"
|
|||||||
--
|
--
|
||||||
-- Add the @time@ package and import the "Data.Time.LocalTime" module to use this function.
|
-- Add the @time@ package and import the "Data.Time.LocalTime" module to use this function.
|
||||||
--
|
--
|
||||||
-- Since 1.4.2
|
-- @since 1.4.2
|
||||||
timeFieldTypeText :: Monad m => RenderMessage (HandlerSite m) FormMessage => Field m TimeOfDay
|
timeFieldTypeText :: Monad m => RenderMessage (HandlerSite m) FormMessage => Field m TimeOfDay
|
||||||
timeFieldTypeText = timeFieldOfType "text"
|
timeFieldTypeText = timeFieldOfType "text"
|
||||||
|
|
||||||
@ -362,7 +366,7 @@ $newline never
|
|||||||
|
|
||||||
-- | Creates an input with @type="email"@ with the <http://w3c.github.io/html/sec-forms.html#the-multiple-attribute multiple> attribute; browsers might implement this as taking a comma separated list of emails. Each email address is validated as described in 'emailField'.
|
-- | Creates an input with @type="email"@ with the <http://w3c.github.io/html/sec-forms.html#the-multiple-attribute multiple> attribute; browsers might implement this as taking a comma separated list of emails. Each email address is validated as described in 'emailField'.
|
||||||
--
|
--
|
||||||
-- Since 1.3.7
|
-- @since 1.3.7
|
||||||
multiEmailField :: Monad m => RenderMessage (HandlerSite m) FormMessage => Field m [Text]
|
multiEmailField :: Monad m => RenderMessage (HandlerSite m) FormMessage => Field m [Text]
|
||||||
multiEmailField = Field
|
multiEmailField = Field
|
||||||
{ fieldParse = parseHelper $
|
{ fieldParse = parseHelper $
|
||||||
@ -427,7 +431,15 @@ selectFieldList :: (Eq a, RenderMessage site FormMessage, RenderMessage site msg
|
|||||||
-> Field (HandlerFor site) a
|
-> Field (HandlerFor site) a
|
||||||
selectFieldList = selectField . optionsPairs
|
selectFieldList = selectField . optionsPairs
|
||||||
|
|
||||||
-- | Creates a @\<select>@ tag for selecting one option. Example usage:
|
-- | Creates a @\<select>@ tag with @\<optgroup>@s for selecting one option.
|
||||||
|
--
|
||||||
|
-- @since 1.7.0
|
||||||
|
selectFieldListGrouped :: (Eq a, RenderMessage site FormMessage, RenderMessage site msg)
|
||||||
|
=> [(msg, [(msg, a)])]
|
||||||
|
-> Field (HandlerFor site) a
|
||||||
|
selectFieldListGrouped = selectField . optionsPairsGrouped
|
||||||
|
|
||||||
|
-- | Creates a @\<select>@ tag with optional @\<optgroup>@s for selecting one option. Example usage:
|
||||||
--
|
--
|
||||||
-- > areq (selectField $ optionsPairs [(MsgValue1, "value1"),(MsgValue2, "value2")]) "Which value?" Nothing
|
-- > areq (selectField $ optionsPairs [(MsgValue1, "value1"),(MsgValue2, "value2")]) "Which value?" Nothing
|
||||||
selectField :: (Eq a, RenderMessage site FormMessage)
|
selectField :: (Eq a, RenderMessage site FormMessage)
|
||||||
@ -446,6 +458,9 @@ $newline never
|
|||||||
$newline never
|
$newline never
|
||||||
<option value=#{value} :isSel:selected>#{text}
|
<option value=#{value} :isSel:selected>#{text}
|
||||||
|]) -- inside
|
|]) -- inside
|
||||||
|
(Just $ \label -> [whamlet|
|
||||||
|
<optgroup label=#{label}>
|
||||||
|
|]) -- group label
|
||||||
|
|
||||||
-- | Creates a @\<select>@ tag for selecting multiple options.
|
-- | Creates a @\<select>@ tag for selecting multiple options.
|
||||||
multiSelectFieldList :: (Eq a, RenderMessage site msg)
|
multiSelectFieldList :: (Eq a, RenderMessage site msg)
|
||||||
@ -531,6 +546,7 @@ $newline never
|
|||||||
<input id=#{theId}-#{value} type=radio name=#{name} value=#{value} :isSel:checked *{attrs}>
|
<input id=#{theId}-#{value} type=radio name=#{name} value=#{value} :isSel:checked *{attrs}>
|
||||||
\#{text}
|
\#{text}
|
||||||
|])
|
|])
|
||||||
|
Nothing
|
||||||
|
|
||||||
-- | Creates a group of radio buttons to answer the question given in the message. Radio buttons are used to allow differentiating between an empty response (@Nothing@) and a no response (@Just False@). Consider using the simpler 'checkBoxField' if you don't need to make this distinction.
|
-- | Creates a group of radio buttons to answer the question given in the message. Radio buttons are used to allow differentiating between an empty response (@Nothing@) and a no response (@Just False@). Consider using the simpler 'checkBoxField' if you don't need to make this distinction.
|
||||||
--
|
--
|
||||||
@ -598,15 +614,31 @@ $newline never
|
|||||||
showVal = either (\_ -> False)
|
showVal = either (\_ -> False)
|
||||||
|
|
||||||
-- | A structure holding a list of options. Typically you can use a convenience function like 'mkOptionList' or 'optionsPairs' instead of creating this directly.
|
-- | A structure holding a list of options. Typically you can use a convenience function like 'mkOptionList' or 'optionsPairs' instead of creating this directly.
|
||||||
data OptionList a = OptionList
|
--
|
||||||
|
-- Extended by 'OptionListGrouped' in 1.7.0.
|
||||||
|
data OptionList a
|
||||||
|
= OptionList
|
||||||
{ olOptions :: [Option a]
|
{ olOptions :: [Option a]
|
||||||
, olReadExternal :: Text -> Maybe a -- ^ A function mapping from the form's value ('optionExternalValue') to the selected Haskell value ('optionInternalValue').
|
, olReadExternal :: Text -> Maybe a -- ^ A function mapping from the form's value ('optionExternalValue') to the selected Haskell value ('optionInternalValue').
|
||||||
}
|
}
|
||||||
|
| OptionListGrouped
|
||||||
|
{ olOptionsGrouped :: [(Text, [Option a])]
|
||||||
|
, olReadExternalGrouped :: Text -> Maybe a -- ^ A function mapping from the form's value ('optionExternalValue') to the selected Haskell value ('optionInternalValue').
|
||||||
|
}
|
||||||
|
|
||||||
-- | Since 1.4.6
|
-- | Convert grouped 'OptionList' to a normal one.
|
||||||
|
--
|
||||||
|
-- @since 1.7.0
|
||||||
|
flattenOptionList :: OptionList a -> OptionList a
|
||||||
|
flattenOptionList (OptionListGrouped os re) = OptionList (concatMap snd os) re
|
||||||
|
flattenOptionList ol = ol
|
||||||
|
|
||||||
|
-- | @since 1.4.6
|
||||||
instance Functor OptionList where
|
instance Functor OptionList where
|
||||||
fmap f (OptionList options readExternal) =
|
fmap f (OptionList options readExternal) =
|
||||||
OptionList ((fmap.fmap) f options) (fmap f . readExternal)
|
OptionList ((fmap.fmap) f options) (fmap f . readExternal)
|
||||||
|
fmap f (OptionListGrouped options readExternal) =
|
||||||
|
OptionListGrouped (map (\(g, os) -> (g, (fmap.fmap) f os)) options) (fmap f . readExternal)
|
||||||
|
|
||||||
-- | Creates an 'OptionList', using a 'Map' to implement the 'olReadExternal' function.
|
-- | Creates an 'OptionList', using a 'Map' to implement the 'olReadExternal' function.
|
||||||
mkOptionList :: [Option a] -> OptionList a
|
mkOptionList :: [Option a] -> OptionList a
|
||||||
@ -615,13 +647,22 @@ mkOptionList os = OptionList
|
|||||||
, olReadExternal = flip Map.lookup $ Map.fromList $ map (optionExternalValue &&& optionInternalValue) os
|
, olReadExternal = flip Map.lookup $ Map.fromList $ map (optionExternalValue &&& optionInternalValue) os
|
||||||
}
|
}
|
||||||
|
|
||||||
|
-- | Creates an 'OptionList', using a 'Map' to implement the 'olReadExternalGrouped' function.
|
||||||
|
--
|
||||||
|
-- @since 1.7.0
|
||||||
|
mkOptionListGrouped :: [(Text, [Option a])] -> OptionList a
|
||||||
|
mkOptionListGrouped os = OptionListGrouped
|
||||||
|
{ olOptionsGrouped = os
|
||||||
|
, olReadExternalGrouped = flip Map.lookup $ Map.fromList $ map (optionExternalValue &&& optionInternalValue) $ concatMap snd os
|
||||||
|
}
|
||||||
|
|
||||||
data Option a = Option
|
data Option a = Option
|
||||||
{ optionDisplay :: Text -- ^ The user-facing label.
|
{ optionDisplay :: Text -- ^ The user-facing label.
|
||||||
, optionInternalValue :: a -- ^ The Haskell value being selected.
|
, optionInternalValue :: a -- ^ The Haskell value being selected.
|
||||||
, optionExternalValue :: Text -- ^ The representation of this value stored in the form.
|
, optionExternalValue :: Text -- ^ The representation of this value stored in the form.
|
||||||
}
|
}
|
||||||
|
|
||||||
-- | Since 1.4.6
|
-- | @since 1.4.6
|
||||||
instance Functor Option where
|
instance Functor Option where
|
||||||
fmap f (Option display internal external) = Option display (f internal) external
|
fmap f (Option display internal external) = Option display (f internal) external
|
||||||
|
|
||||||
@ -637,6 +678,30 @@ optionsPairs opts = do
|
|||||||
}
|
}
|
||||||
return $ mkOptionList (zipWith mkOption [1 :: Int ..] opts)
|
return $ mkOptionList (zipWith mkOption [1 :: Int ..] opts)
|
||||||
|
|
||||||
|
-- | Creates an 'OptionList' from a list of (display-value, internal value) pairs.
|
||||||
|
--
|
||||||
|
-- @since 1.7.0
|
||||||
|
optionsPairsGrouped
|
||||||
|
:: forall m msg a. (MonadHandler m, RenderMessage (HandlerSite m) msg)
|
||||||
|
=> [(msg, [(msg, a)])] -> m (OptionList a)
|
||||||
|
optionsPairsGrouped opts = do
|
||||||
|
mr <- getMessageRender
|
||||||
|
let mkOption (external, (display, internal)) =
|
||||||
|
Option { optionDisplay = mr display
|
||||||
|
, optionInternalValue = internal
|
||||||
|
, optionExternalValue = pack $ show external
|
||||||
|
}
|
||||||
|
opts' = enumerateSublists opts :: [(msg, [(Int, (msg, a))])]
|
||||||
|
opts'' = map (\(x, ys) -> (mr x, map mkOption ys)) opts'
|
||||||
|
return $ mkOptionListGrouped opts''
|
||||||
|
|
||||||
|
-- | Helper to enumerate sublists with one consecutive index.
|
||||||
|
enumerateSublists :: forall a b. [(a, [b])] -> [(a, [(Int, b)])]
|
||||||
|
enumerateSublists xss =
|
||||||
|
let yss :: [(Int, (a, [b]))]
|
||||||
|
yss = snd $ foldl (\(i, res) xs -> (i + (length.snd) xs, res ++ [(i, xs)])) (1, []) xss
|
||||||
|
in map (\(i, (x, ys)) -> (x, zip [i :: Int ..] ys)) yss
|
||||||
|
|
||||||
-- | Creates an 'OptionList' from an 'Enum', using its 'Show' instance for the user-facing value.
|
-- | Creates an 'OptionList' from an 'Enum', using its 'Show' instance for the user-facing value.
|
||||||
optionsEnum :: (MonadHandler m, Show a, Enum a, Bounded a) => m (OptionList a)
|
optionsEnum :: (MonadHandler m, Show a, Enum a, Bounded a) => m (OptionList a)
|
||||||
optionsEnum = optionsPairs $ map (\x -> (pack $ show x, x)) [minBound..maxBound]
|
optionsEnum = optionsPairs $ map (\x -> (pack $ show x, x)) [minBound..maxBound]
|
||||||
@ -692,7 +757,7 @@ optionsPersist filts ords toDisplay = fmap mkOptionList $ do
|
|||||||
-- | An alternative to 'optionsPersist' which returns just the 'Key' instead of
|
-- | An alternative to 'optionsPersist' which returns just the 'Key' instead of
|
||||||
-- the entire 'Entity'.
|
-- the entire 'Entity'.
|
||||||
--
|
--
|
||||||
-- Since 1.3.2
|
-- @since 1.3.2
|
||||||
#if MIN_VERSION_persistent(2,5,0)
|
#if MIN_VERSION_persistent(2,5,0)
|
||||||
optionsPersistKey
|
optionsPersistKey
|
||||||
:: (YesodPersist site
|
:: (YesodPersist site
|
||||||
@ -731,7 +796,7 @@ optionsPersistKey filts ords toDisplay = fmap mkOptionList $ do
|
|||||||
}) pairs
|
}) pairs
|
||||||
|
|
||||||
-- |
|
-- |
|
||||||
-- A helper function for constucting 'selectField's. You may want to use this when you define your custom 'selectField's or 'radioField's.
|
-- A helper function for constucting 'selectField's with optional option groups. You may want to use this when you define your custom 'selectField's or 'radioField's.
|
||||||
--
|
--
|
||||||
-- @since 1.6.2
|
-- @since 1.6.2
|
||||||
selectFieldHelper
|
selectFieldHelper
|
||||||
@ -739,23 +804,26 @@ selectFieldHelper
|
|||||||
=> (Text -> Text -> [(Text, Text)] -> WidgetFor site () -> WidgetFor site ()) -- ^ Outermost part of the field
|
=> (Text -> Text -> [(Text, Text)] -> WidgetFor site () -> WidgetFor site ()) -- ^ Outermost part of the field
|
||||||
-> (Text -> Text -> Bool -> WidgetFor site ()) -- ^ An option for None if the field is optional
|
-> (Text -> Text -> Bool -> WidgetFor site ()) -- ^ An option for None if the field is optional
|
||||||
-> (Text -> Text -> [(Text, Text)] -> Text -> Bool -> Text -> WidgetFor site ()) -- ^ Other options
|
-> (Text -> Text -> [(Text, Text)] -> Text -> Bool -> Text -> WidgetFor site ()) -- ^ Other options
|
||||||
|
-> (Maybe (Text -> WidgetFor site ())) -- ^ Group headers placed inbetween options
|
||||||
-> HandlerFor site (OptionList a)
|
-> HandlerFor site (OptionList a)
|
||||||
-> Field (HandlerFor site) a
|
-> Field (HandlerFor site) a
|
||||||
selectFieldHelper outside onOpt inside opts' = Field
|
selectFieldHelper outside onOpt inside grpHdr opts' = Field
|
||||||
{ fieldParse = \x _ -> do
|
{ fieldParse = \x _ -> do
|
||||||
opts <- opts'
|
opts <- fmap flattenOptionList opts'
|
||||||
return $ selectParser opts x
|
return $ selectParser opts x
|
||||||
, fieldView = \theId name attrs val isReq -> do
|
, fieldView = \theId name attrs val isReq -> do
|
||||||
opts <- fmap olOptions $ handlerToWidget opts'
|
|
||||||
outside theId name attrs $ do
|
outside theId name attrs $ do
|
||||||
unless isReq $ onOpt theId name $ not $ render opts val `elem` map optionExternalValue opts
|
optsFlat <- fmap (olOptions.flattenOptionList) $ handlerToWidget opts'
|
||||||
flip mapM_ opts $ \opt -> inside
|
unless isReq $ onOpt theId name $ render optsFlat val `notElem` map optionExternalValue optsFlat
|
||||||
theId
|
opts'' <- handlerToWidget opts'
|
||||||
name
|
case opts'' of
|
||||||
((if isReq then (("required", "required"):) else id) attrs)
|
OptionList{} -> constructOptions theId name attrs val isReq optsFlat
|
||||||
(optionExternalValue opt)
|
OptionListGrouped{olOptionsGrouped=grps} -> do
|
||||||
((render opts val) == optionExternalValue opt)
|
forM_ grps $ \(grp, opts) -> do
|
||||||
(optionDisplay opt)
|
case grpHdr of
|
||||||
|
Just hdr -> hdr grp
|
||||||
|
Nothing -> return ()
|
||||||
|
constructOptions theId name attrs val isReq opts
|
||||||
, fieldEnctype = UrlEncoded
|
, fieldEnctype = UrlEncoded
|
||||||
}
|
}
|
||||||
where
|
where
|
||||||
@ -768,6 +836,14 @@ selectFieldHelper outside onOpt inside opts' = Field
|
|||||||
x -> case olReadExternal opts x of
|
x -> case olReadExternal opts x of
|
||||||
Nothing -> Left $ SomeMessage $ MsgInvalidEntry x
|
Nothing -> Left $ SomeMessage $ MsgInvalidEntry x
|
||||||
Just y -> Right $ Just y
|
Just y -> Right $ Just y
|
||||||
|
constructOptions theId name attrs val isReq opts =
|
||||||
|
forM_ opts $ \opt -> inside
|
||||||
|
theId
|
||||||
|
name
|
||||||
|
((if isReq then (("required", "required"):) else id) attrs)
|
||||||
|
(optionExternalValue opt)
|
||||||
|
(render opts val == optionExternalValue opt)
|
||||||
|
(optionDisplay opt)
|
||||||
|
|
||||||
-- | Creates an input with @type="file"@.
|
-- | Creates an input with @type="file"@.
|
||||||
fileField :: Monad m
|
fileField :: Monad m
|
||||||
@ -864,7 +940,7 @@ prependZero t0 = if T.null t1
|
|||||||
then "-0." `T.append` (T.drop 2 t1)
|
then "-0." `T.append` (T.drop 2 t1)
|
||||||
else t1
|
else t1
|
||||||
|
|
||||||
where t1 = T.dropWhile ((==) ' ') t0
|
where t1 = T.dropWhile (==' ') t0
|
||||||
|
|
||||||
-- $optionsOverview
|
-- $optionsOverview
|
||||||
-- These functions create inputs where one or more options can be selected from a list.
|
-- These functions create inputs where one or more options can be selected from a list.
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
|
cabal-version: >= 1.10
|
||||||
name: yesod-form
|
name: yesod-form
|
||||||
version: 1.6.7
|
version: 1.7.0
|
||||||
license: MIT
|
license: MIT
|
||||||
license-file: LICENSE
|
license-file: LICENSE
|
||||||
author: Michael Snoyman <michael@snoyman.com>
|
author: Michael Snoyman <michael@snoyman.com>
|
||||||
@ -7,7 +8,6 @@ maintainer: Michael Snoyman <michael@snoyman.com>
|
|||||||
synopsis: Form handling support for Yesod Web Framework
|
synopsis: Form handling support for Yesod Web Framework
|
||||||
category: Web, Yesod
|
category: Web, Yesod
|
||||||
stability: Stable
|
stability: Stable
|
||||||
cabal-version: >= 1.8
|
|
||||||
build-type: Simple
|
build-type: Simple
|
||||||
homepage: http://www.yesodweb.com/
|
homepage: http://www.yesodweb.com/
|
||||||
description: API docs and the README are available at <http://www.stackage.org/package/yesod-form>. Third-party packages which you can find useful: <http://hackage.haskell.org/package/yesod-form-richtext yesod-form-richtext> - richtext form fields (currntly it provides only Summernote support).
|
description: API docs and the README are available at <http://www.stackage.org/package/yesod-form>. Third-party packages which you can find useful: <http://hackage.haskell.org/package/yesod-form-richtext yesod-form-richtext> - richtext form fields (currntly it provides only Summernote support).
|
||||||
@ -19,6 +19,7 @@ flag network-uri
|
|||||||
default: True
|
default: True
|
||||||
|
|
||||||
library
|
library
|
||||||
|
default-language: Haskell2010
|
||||||
build-depends: base >= 4.10 && < 5
|
build-depends: base >= 4.10 && < 5
|
||||||
, aeson
|
, aeson
|
||||||
, attoparsec >= 0.10
|
, attoparsec >= 0.10
|
||||||
@ -70,6 +71,7 @@ library
|
|||||||
ghc-options: -Wall
|
ghc-options: -Wall
|
||||||
|
|
||||||
test-suite test
|
test-suite test
|
||||||
|
default-language: Haskell2010
|
||||||
type: exitcode-stdio-1.0
|
type: exitcode-stdio-1.0
|
||||||
main-is: main.hs
|
main-is: main.hs
|
||||||
hs-source-dirs: test
|
hs-source-dirs: test
|
||||||
|
|||||||
@ -1,5 +1,13 @@
|
|||||||
# ChangeLog for yesod-persistent
|
# ChangeLog for yesod-persistent
|
||||||
|
|
||||||
|
## 1.6.0.7
|
||||||
|
|
||||||
|
* Add support for persistent 2.13. [#1723](https://github.com/yesodweb/yesod/pull/1723)
|
||||||
|
|
||||||
|
## 1.6.0.6
|
||||||
|
|
||||||
|
* Add support for persistent 2.12
|
||||||
|
|
||||||
## 1.6.0.5
|
## 1.6.0.5
|
||||||
|
|
||||||
* Add support for Persistent 2.11 [#1701](https://github.com/yesodweb/yesod/pull/1701)
|
* Add support for Persistent 2.11 [#1701](https://github.com/yesodweb/yesod/pull/1701)
|
||||||
|
|||||||
@ -25,6 +25,7 @@ module Yesod.Persist.Core
|
|||||||
import Database.Persist
|
import Database.Persist
|
||||||
import Control.Monad.Trans.Reader (ReaderT, runReaderT)
|
import Control.Monad.Trans.Reader (ReaderT, runReaderT)
|
||||||
|
|
||||||
|
import Data.Foldable (toList)
|
||||||
import Yesod.Core
|
import Yesod.Core
|
||||||
import Data.Conduit
|
import Data.Conduit
|
||||||
import Blaze.ByteString.Builder (Builder)
|
import Blaze.ByteString.Builder (Builder)
|
||||||
@ -33,6 +34,9 @@ import Control.Monad.Trans.Resource
|
|||||||
import Control.Exception (throwIO)
|
import Control.Exception (throwIO)
|
||||||
import Yesod.Core.Types (HandlerContents (HCError))
|
import Yesod.Core.Types (HandlerContents (HCError))
|
||||||
import qualified Database.Persist.Sql as SQL
|
import qualified Database.Persist.Sql as SQL
|
||||||
|
#if MIN_VERSION_persistent(2,13,0)
|
||||||
|
import qualified Database.Persist.SqlBackend.Internal as SQL
|
||||||
|
#endif
|
||||||
|
|
||||||
unSqlPersistT :: a -> a
|
unSqlPersistT :: a -> a
|
||||||
unSqlPersistT = id
|
unSqlPersistT = id
|
||||||
@ -196,7 +200,15 @@ insert400 datum = do
|
|||||||
conflict <- checkUnique datum
|
conflict <- checkUnique datum
|
||||||
case conflict of
|
case conflict of
|
||||||
Just unique ->
|
Just unique ->
|
||||||
|
#if MIN_VERSION_persistent(2, 12, 0)
|
||||||
|
-- toList is called here because persistent-2.13 changed this
|
||||||
|
-- to a nonempty list. for versions of persistent prior to 2.13, toList
|
||||||
|
-- will be a no-op. for persistent-2.13, it'll convert the NonEmptyList to
|
||||||
|
-- a List.
|
||||||
|
badRequest' $ map (unFieldNameHS . fst) $ toList $ persistUniqueToFieldNames unique
|
||||||
|
#else
|
||||||
badRequest' $ map (unHaskellName . fst) $ persistUniqueToFieldNames unique
|
badRequest' $ map (unHaskellName . fst) $ persistUniqueToFieldNames unique
|
||||||
|
#endif
|
||||||
Nothing -> insert datum
|
Nothing -> insert datum
|
||||||
|
|
||||||
-- | Same as 'insert400', but doesn’t return a key.
|
-- | Same as 'insert400', but doesn’t return a key.
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
cabal-version: >= 1.10
|
cabal-version: >= 1.10
|
||||||
name: yesod-persistent
|
name: yesod-persistent
|
||||||
version: 1.6.0.5
|
version: 1.6.0.7
|
||||||
license: MIT
|
license: MIT
|
||||||
license-file: LICENSE
|
license-file: LICENSE
|
||||||
author: Michael Snoyman <michael@snoyman.com>
|
author: Michael Snoyman <michael@snoyman.com>
|
||||||
@ -17,8 +17,8 @@ library
|
|||||||
default-language: Haskell2010
|
default-language: Haskell2010
|
||||||
build-depends: base >= 4.10 && < 5
|
build-depends: base >= 4.10 && < 5
|
||||||
, yesod-core >= 1.6 && < 1.7
|
, yesod-core >= 1.6 && < 1.7
|
||||||
, persistent >= 2.8 && < 2.12
|
, persistent >= 2.8
|
||||||
, persistent-template >= 2.1 && < 2.10
|
, persistent-template >= 2.1
|
||||||
, transformers >= 0.2.2
|
, transformers >= 0.2.2
|
||||||
, blaze-builder
|
, blaze-builder
|
||||||
, conduit
|
, conduit
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
cabal-version: 1.10
|
cabal-version: >=1.10
|
||||||
name: yesod-websockets
|
name: yesod-websockets
|
||||||
version: 0.3.0.3
|
version: 0.3.0.3
|
||||||
synopsis: WebSockets support for Yesod
|
synopsis: WebSockets support for Yesod
|
||||||
|
|||||||
@ -1,3 +1,9 @@
|
|||||||
|
# ChangeLog for yesod
|
||||||
|
|
||||||
|
## 1.6.1.1
|
||||||
|
|
||||||
|
* Allow yesod-form 1.7
|
||||||
|
|
||||||
## 1.6.1.0
|
## 1.6.1.0
|
||||||
|
|
||||||
* `widgetFileReload` and `widgetFileNoReload` now use absolute paths via the new `globFilePackage` Q Exp which can provide absolute templates paths within the project [#1691](https://github.com/yesodweb/yesod/pull/1691)
|
* `widgetFileReload` and `widgetFileNoReload` now use absolute paths via the new `globFilePackage` Q Exp which can provide absolute templates paths within the project [#1691](https://github.com/yesodweb/yesod/pull/1691)
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
name: yesod
|
name: yesod
|
||||||
version: 1.6.1.0
|
version: 1.6.1.1
|
||||||
license: MIT
|
license: MIT
|
||||||
license-file: LICENSE
|
license-file: LICENSE
|
||||||
author: Michael Snoyman <michael@snoyman.com>
|
author: Michael Snoyman <michael@snoyman.com>
|
||||||
@ -38,7 +38,7 @@ library
|
|||||||
, warp >= 1.3
|
, warp >= 1.3
|
||||||
, yaml >= 0.8.17
|
, yaml >= 0.8.17
|
||||||
, yesod-core >= 1.6 && < 1.7
|
, yesod-core >= 1.6 && < 1.7
|
||||||
, yesod-form >= 1.6 && < 1.7
|
, yesod-form >= 1.6 && < 1.8
|
||||||
, yesod-persistent >= 1.6 && < 1.7
|
, yesod-persistent >= 1.6 && < 1.7
|
||||||
|
|
||||||
exposed-modules: Yesod
|
exposed-modules: Yesod
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user