Ability to make notes public or shared

This commit is contained in:
Yann Esposito (Yogsototh) 2019-09-15 15:43:03 +02:00
parent 8ed3965b7e
commit 0dbad04c35
Signed by untrusted user who does not match committer: yogsototh
GPG key ID: 7B19A4C650D59646
10 changed files with 98 additions and 71 deletions

View file

@ -48,19 +48,23 @@ nlist st' =
render st@{ notes } = render st@{ notes } =
HH.div_ (map renderNote notes) HH.div_ (map renderNote notes)
where where
renderNote bm = renderNote note =
div [ id_ (show bm.id) , class_ ("note w-100 mw7 pa1 mb2")] $ div [ id_ (show note.id)
, class_ ("note w-100 mw7 pa1 mb2"
<> if note.shared then "" else " private")] $
[ div [ class_ "display" ] $ [ div [ class_ "display" ] $
[ a [ href (linkToFilterSingle bm.slug), class_ ("link f5 lh-title")] [ a [ href (linkToFilterSingle note.slug), class_ ("link f5 lh-title")]
[ text $ if S.null bm.title then "[no title]" else bm.title ] [ text $ if S.null note.title then "[no title]" else note.title ]
, br_ , br_
, div [ class_ "description mt1 mid-gray" ] (toTextarea (S.take 200 bm.text)) , div [ class_ "description mt1 mid-gray" ] (toTextarea (S.take 200 note.text))
, a [ class_ "link f7 dib gray w4", title (maybe bm.created snd (mmoment bm)) , href (linkToFilterSingle bm.slug) ] , a [ class_ "link f7 dib gray w4"
[ text (maybe " " fst (mmoment bm)) ] , title (maybe note.created snd (mmoment note))
, href (linkToFilterSingle note.slug)]
[text (maybe " " fst (mmoment note))]
] ]
] ]
mmoment bm = mmoment8601 bm.created mmoment note = mmoment8601 note.created
linkToFilterSingle slug = fromNullableStr app.userR <> "/notes/" <> slug linkToFilterSingle slug = fromNullableStr app.userR <> "/notes/" <> slug
toTextarea input = toTextarea input =
S.split (Pattern "\n") input S.split (Pattern "\n") input

View file

@ -51,11 +51,12 @@ _edit_note = lens _.edit_note (_ { edit_note = _ })
_edit :: Lens' NState Boolean _edit :: Lens' NState Boolean
_edit = lens _.edit (_ { edit = _ }) _edit = lens _.edit (_ { edit = _ })
-- | FormField Edits -- | FormField Edits
data EditField data EditField
= Etitle String = Etitle String
| Etext String | Etext String
| EisMarkdown Boolean | EisMarkdown Boolean
| Eshared Boolean
_markdown = SProxy :: SProxy "markdown" _markdown = SProxy :: SProxy "markdown"
@ -99,8 +100,13 @@ nnote st' =
, if note.isMarkdown , if note.isMarkdown
then div [ class_ "description mt1" ] [ HH.slot _markdown unit Markdown.component note.text absurd ] then div [ class_ "description mt1" ] [ HH.slot _markdown unit Markdown.component note.text absurd ]
else div [ class_ "description mt1 mid-gray" ] (toTextarea note.text) else div [ class_ "description mt1 mid-gray" ] (toTextarea note.text)
, div [ class_ "link f7 dib gray w4", title (maybe note.created snd (mmoment note)) ] , div [ class_ "link f7 dib gray w4"]
[ text (maybe " " fst (mmoment note)) ] [ span [title (maybe note.created snd (mmoment note))]
[text (maybe " " fst (mmoment note))]
, text " - "
, span [ class_ ("gray")]
[ text $ if note.shared then "public" else "private" ]
]
] ]
] ]
<> -- | Render Action Links <> -- | Render Action Links
@ -111,7 +117,7 @@ nnote st' =
, span ([ class_ ("confirm red" <> guard (not st.deleteAsk) " dn") ] ) , span ([ class_ ("confirm red" <> guard (not st.deleteAsk) " dn") ] )
[ button [ type_ ButtonButton, onClick \_ -> Just (NDeleteAsk false)] [ text "cancel / " ] [ button [ type_ ButtonButton, onClick \_ -> Just (NDeleteAsk false)] [ text "cancel / " ]
, button [ type_ ButtonButton, onClick \_ -> Just NDestroy, class_ "red" ] [ text "destroy" ] , button [ type_ ButtonButton, onClick \_ -> Just NDestroy, class_ "red" ] [ text "destroy" ]
] ]
] ]
] ]
] ]
@ -134,9 +140,20 @@ nnote st' =
, label [ for "edit_ismarkdown" , class_ "mr2" ] [ text "use markdown?" ] , label [ for "edit_ismarkdown" , class_ "mr2" ] [ text "use markdown?" ]
, br_ , br_
] ]
, input [ type_ InputSubmit , class_ "mr1 pv1 ph2 dark-gray ba b--moon-gray bg-near-white pointer rdim" , value "save" ] , div [ class_ "edit_form_checkboxes mb3"]
[ input [ type_ InputCheckbox , class_ "is-markdown pointer" , id_ "edit_shared", name "shared"
, checked (edit_note.shared) , onChecked (editField Eshared) ]
, text " "
, label [ for "edit_shared" , class_ "mr2" ] [ text "public?" ]
, br_
]
, input [ type_ InputSubmit
, class_ "mr1 pv1 ph2 dark-gray ba b--moon-gray bg-near-white pointer rdim"
, value "save" ]
, text " " , text " "
, input [ type_ InputReset , class_ "pv1 ph2 dark-gray ba b--moon-gray bg-near-white pointer rdim" , value "cancel" , input [ type_ InputReset
, class_ "pv1 ph2 dark-gray ba b--moon-gray bg-near-white pointer rdim"
, value "cancel"
, onClick \_ -> Just (NEdit false) , onClick \_ -> Just (NEdit false)
] ]
] ]
@ -161,6 +178,7 @@ nnote st' =
Etitle e -> _ { title = e } Etitle e -> _ { title = e }
Etext e -> _ { text = e } Etext e -> _ { text = e }
EisMarkdown e -> _ { isMarkdown = e } EisMarkdown e -> _ { isMarkdown = e }
Eshared e -> _ { shared = e }
-- | Delete -- | Delete
handleAction (NDeleteAsk e) = do handleAction (NDeleteAsk e) = do

View file

@ -34,6 +34,7 @@ type Note =
, text :: String , text :: String
, length :: Int , length :: Int
, isMarkdown :: Boolean , isMarkdown :: Boolean
, shared :: Boolean
, created :: String , created :: String
, updated :: String , updated :: String
} }

View file

@ -14,43 +14,37 @@ module Application
, db , db
) where ) where
import Control.Monad.Logger (liftLoc, runLoggingT) import Control.Monad.Logger (liftLoc, runLoggingT)
import Database.Persist.Sqlite import Database.Persist.Sqlite (createSqlitePool, runSqlPool, sqlDatabase, sqlPoolSize)
(createSqlitePool, sqlDatabase, sqlPoolSize) import Import
import Import import Language.Haskell.TH.Syntax (qLocation)
import Yesod.Auth (getAuth) import Lens.Micro
import Language.Haskell.TH.Syntax (qLocation) import Network.HTTP.Client.TLS
import Lens.Micro import Network.Wai (Middleware)
import Network.HTTP.Client.TLS import Network.Wai.Handler.Warp (Settings, defaultSettings, defaultShouldDisplayException, runSettings, setHost, setOnException, setPort, getPort)
import Network.Wai (Middleware) import Network.Wai.Middleware.AcceptOverride
import Network.Wai.Middleware.Autohead import Network.Wai.Middleware.Autohead
import Network.Wai.Middleware.AcceptOverride import Network.Wai.Middleware.Gzip
import Network.Wai.Middleware.Gzip import Network.Wai.Middleware.MethodOverride
import Network.Wai.Middleware.MethodOverride import Network.Wai.Middleware.RequestLogger (Destination(Logger), IPAddrSource(..), OutputFormat(..), destination, mkRequestLogger, outputFormat)
import Network.Wai.Handler.Warp import System.Log.FastLogger (defaultBufSize, newStdoutLoggerSet, toLogStr)
(Settings, defaultSettings, defaultShouldDisplayException, import Yesod.Auth (getAuth)
runSettings, setHost, setOnException, setPort, getPort)
import Network.Wai.Middleware.RequestLogger
(Destination(Logger), IPAddrSource(..), OutputFormat(..),
destination, mkRequestLogger, outputFormat)
import System.Log.FastLogger
(defaultBufSize, newStdoutLoggerSet, toLogStr)
import qualified Control.Monad.Metrics as MM import qualified Control.Monad.Metrics as MM
import qualified Network.Wai.Metrics as WM import qualified Network.Wai.Metrics as WM
import qualified System.Metrics as EKG import qualified System.Metrics as EKG
import qualified System.Remote.Monitoring as EKG import qualified System.Remote.Monitoring as EKG
-- Import all relevant handler modules here. -- Import all relevant handler modules here.
-- Don't forget to add new modules to your cabal file! -- Don't forget to add new modules to your cabal file!
import Handler.Common import Handler.Common
import Handler.Home import Handler.Home
import Handler.User import Handler.User
import Handler.AccountSettings import Handler.AccountSettings
import Handler.Add import Handler.Add
import Handler.Edit import Handler.Edit
import Handler.Notes import Handler.Notes
import Handler.Docs import Handler.Docs
mkYesodDispatch "App" resourcesApp mkYesodDispatch "App" resourcesApp
@ -74,9 +68,9 @@ makeFoundation appSettings = do
createSqlitePool createSqlitePool
(sqlDatabase (appDatabaseConf appSettings)) (sqlDatabase (appDatabaseConf appSettings))
(sqlPoolSize (appDatabaseConf appSettings)) (sqlPoolSize (appDatabaseConf appSettings))
-- runLoggingT runLoggingT
-- (runSqlPool runMigrations pool) (runSqlPool runMigrations pool)
-- logFunc logFunc
return (mkFoundation pool) return (mkFoundation pool)
makeApplication :: App -> IO Application makeApplication :: App -> IO Application

View file

@ -11,6 +11,7 @@ import qualified Text.Blaze.Html5 as H
getNotesR :: UserNameP -> Handler Html getNotesR :: UserNameP -> Handler Html
getNotesR unamep@(UserNameP uname) = do getNotesR unamep@(UserNameP uname) = do
muserid <- maybeAuthId
(limit', page') <- lookupPagingParams (limit', page') <- lookupPagingParams
let queryp = "query" :: Text let queryp = "query" :: Text
mquery <- lookupGetParam queryp mquery <- lookupGetParam queryp
@ -19,7 +20,8 @@ getNotesR unamep@(UserNameP uname) = do
mqueryp = fmap (\q -> (queryp, q)) mquery mqueryp = fmap (\q -> (queryp, q)) mquery
(bcount, notes) <- runDB $ do (bcount, notes) <- runDB $ do
Entity userId _ <- getBy404 (UniqueUserName uname) Entity userId _ <- getBy404 (UniqueUserName uname)
getNoteList userId mquery limit page let sharedp = if muserid == Just userId then SharedAll else SharedPublic
getNoteList userId mquery sharedp limit page
req <- getRequest req <- getRequest
mroute <- getCurrentRoute mroute <- getCurrentRoute
defaultLayout $ do defaultLayout $ do
@ -58,7 +60,7 @@ getAddNoteViewR :: UserNameP -> Handler Html
getAddNoteViewR unamep@(UserNameP uname) = do getAddNoteViewR unamep@(UserNameP uname) = do
userId <- requireAuthId userId <- requireAuthId
let renderEl = "note" :: Text let renderEl = "note" :: Text
note <- liftIO $ Entity (NoteKey 0) <$> _toNote userId (NoteForm Nothing Nothing Nothing Nothing Nothing Nothing Nothing) note <- liftIO $ Entity (NoteKey 0) <$> _toNote userId (NoteForm Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing)
defaultLayout $ do defaultLayout $ do
$(widgetFile "note") $(widgetFile "note")
toWidgetBody [julius| toWidgetBody [julius|
@ -107,6 +109,7 @@ data NoteForm = NoteForm
, _title :: Maybe Text , _title :: Maybe Text
, _text :: Maybe Textarea , _text :: Maybe Textarea
, _isMarkdown :: Maybe Bool , _isMarkdown :: Maybe Bool
, _shared :: Maybe Bool
, _created :: Maybe UTCTimeStr , _created :: Maybe UTCTimeStr
, _updated :: Maybe UTCTimeStr , _updated :: Maybe UTCTimeStr
} deriving (Show, Eq, Read, Generic) } deriving (Show, Eq, Read, Generic)
@ -129,6 +132,7 @@ _toNote userId NoteForm {..} = do
(fromMaybe "" _title) (fromMaybe "" _title)
(maybe "" unTextarea _text) (maybe "" unTextarea _text)
(fromMaybe False _isMarkdown) (fromMaybe False _isMarkdown)
(fromMaybe False _shared)
(fromMaybe time (fmap unUTCTimeStr _created)) (fromMaybe time (fmap unUTCTimeStr _created))
(fromMaybe time (fmap unUTCTimeStr _updated)) (fromMaybe time (fmap unUTCTimeStr _updated))
@ -150,7 +154,7 @@ getNotesFeedR unamep@(UserNameP uname) = do
page = maybe 1 fromIntegral page' page = maybe 1 fromIntegral page'
(bcount, notes) <- runDB $ do (bcount, notes) <- runDB $ do
Entity userId _ <- getBy404 (UniqueUserName uname) Entity userId _ <- getBy404 (UniqueUserName uname)
getNoteList userId mquery limit page getNoteList userId mquery SharedPublic limit page
let (descr :: Html) = toHtml $ H.text (uname <> " notes") let (descr :: Html) = toHtml $ H.text (uname <> " notes")
let entries = map (noteToRssEntry unamep) notes let entries = map (noteToRssEntry unamep) notes
updated <- case maximumMay (map feedEntryUpdated entries) of updated <- case maximumMay (map feedEntryUpdated entries) of

View file

@ -26,7 +26,7 @@ import qualified Data.Map.Strict as MS
import ModelCustom import ModelCustom
share [mkPersist sqlSettings, mkDeleteCascade sqlSettings, mkMigrate "migrateSchema"] [persistLowerCase| share [mkPersist sqlSettings, mkDeleteCascade sqlSettings, mkMigrate "migrateSchema"] [persistLowerCase|
User json User json
Id Int64 Id Int64
name Text name Text
@ -72,6 +72,7 @@ Note json
title Text title Text
text Text text Text
isMarkdown Bool isMarkdown Bool
shared Bool default=False
created UTCTime created UTCTime
updated UTCTime updated UTCTime
deriving Show Eq Typeable Ord deriving Show Eq Typeable Ord
@ -151,7 +152,7 @@ authenticatePassword username password = do
if validatePasswordHash (userPasswordHash (entityVal dbuser)) password if validatePasswordHash (userPasswordHash (entityVal dbuser)) password
then return (Just dbuser) then return (Just dbuser)
else return Nothing else return Nothing
getUserByName :: UserNameP -> DB (Maybe (Entity User)) getUserByName :: UserNameP -> DB (Maybe (Entity User))
getUserByName (UserNameP uname) = do getUserByName (UserNameP uname) = do
selectFirst [UserName ==. uname] [] selectFirst [UserName ==. uname] []
@ -187,7 +188,7 @@ bookmarksQuery userId sharedp filterp tags mquery limit' page =
expr &&. (exists $ -- each tag becomes an exists constraint expr &&. (exists $ -- each tag becomes an exists constraint
from $ \t -> from $ \t ->
where_ (t ^. BookmarkTagBookmarkId E.==. b ^. BookmarkId &&. where_ (t ^. BookmarkTagBookmarkId E.==. b ^. BookmarkId &&.
(t ^. BookmarkTagTag `E.like` val tag)))) (t ^. BookmarkTagTag `E.like` val tag))))
(b ^. BookmarkUserId E.==. val userId) (b ^. BookmarkUserId E.==. val userId)
tags tags
case sharedp of case sharedp of
@ -242,7 +243,7 @@ parseSearchQuery toExpr =
termE = toExpr <$> (fieldTerm <|> quotedTerm <|> simpleTerm) termE = toExpr <$> (fieldTerm <|> quotedTerm <|> simpleTerm)
fieldTerm = concat <$> sequence [simpleTerm, P.string ":", quotedTerm <|> simpleTerm] fieldTerm = concat <$> sequence [simpleTerm, P.string ":", quotedTerm <|> simpleTerm]
quotedTerm = PC.between (P.char '"') (P.char '"') (P.takeWhile1 (/= '"')) quotedTerm = PC.between (P.char '"') (P.char '"') (P.takeWhile1 (/= '"'))
simpleTerm = P.takeWhile1 (\c -> not (isSpace c) && c /= ':' && c /= '|') simpleTerm = P.takeWhile1 (\c -> not (isSpace c) && c /= ':' && c /= '|')
parseTimeText :: (TI.ParseTime t, Monad m, Alternative m) => Text -> m t parseTimeText :: (TI.ParseTime t, Monad m, Alternative m) => Text -> m t
parseTimeText t = parseTimeText t =
@ -267,13 +268,13 @@ withTags key = selectList [BookmarkTagBookmarkId ==. key] [Asc BookmarkTagSeq]
-- Note List Query -- Note List Query
getNote :: Key User -> NtSlug -> DB (Maybe (Entity Note)) getNote :: Key User -> NtSlug -> DB (Maybe (Entity Note))
getNote userKey slug = getNote userKey slug =
selectFirst [NoteUserId ==. userKey, NoteSlug ==. slug] [] selectFirst [NoteUserId ==. userKey, NoteSlug ==. slug] []
getNoteList :: Key User -> Maybe Text -> Limit -> Page -> DB (Int, [Entity Note]) getNoteList :: Key User -> Maybe Text -> SharedP -> Limit -> Page -> DB (Int, [Entity Note])
getNoteList key mquery limit' page = getNoteList key mquery sharedp limit' page =
(,) -- total count (,) -- total count
<$> fmap (sum . fmap E.unValue) <$> fmap (sum . fmap E.unValue)
(select $ (select $
@ -292,6 +293,10 @@ getNoteList key mquery limit' page =
where_ $ (b ^. NoteUserId E.==. val key) where_ $ (b ^. NoteUserId E.==. val key)
-- search -- search
sequenceA_ (parseSearchQuery (toLikeExpr b) =<< mquery) sequenceA_ (parseSearchQuery (toLikeExpr b) =<< mquery)
case sharedp of
SharedAll -> pure ()
SharedPublic -> where_ (b ^. NoteShared E.==. val True)
SharedPrivate -> where_ (b ^. NoteShared E.==. val False)
toLikeExpr :: E.SqlExpr (Entity Note) -> Text -> E.SqlExpr (E.Value Bool) toLikeExpr :: E.SqlExpr (Entity Note) -> Text -> E.SqlExpr (E.Value Bool)
toLikeExpr b term = fromRight p_allFields (P.parseOnly p_onefield term) toLikeExpr b term = fromRight p_allFields (P.parseOnly p_onefield term)
@ -415,6 +420,7 @@ fileNoteToNote user (FileNote {..} ) = do
fileNoteTitle fileNoteTitle
fileNoteText fileNoteText
False False
False
fileNoteCreatedAt fileNoteCreatedAt
fileNoteUpdatedAt fileNoteUpdatedAt
@ -425,19 +431,19 @@ insertDirFileNotes userId noteDirectory = do
Left e -> print e Left e -> print e
Right fnotes -> do Right fnotes -> do
notes <- liftIO $ mapM (fileNoteToNote userId) fnotes notes <- liftIO $ mapM (fileNoteToNote userId) fnotes
void $ mapM insertUnique notes void $ mapM insertUnique notes
where where
readFileNotes :: MonadIO m => FilePath -> m (Either String [FileNote]) readFileNotes :: MonadIO m => FilePath -> m (Either String [FileNote])
readFileNotes fdir = do readFileNotes fdir = do
files <- liftIO (listDirectory fdir) files <- liftIO (listDirectory fdir)
noteBSS <- mapM (readFile . (fdir </>)) files noteBSS <- mapM (readFile . (fdir </>)) files
pure (mapM (A.eitherDecode' . fromStrict) noteBSS) pure (mapM (A.eitherDecode' . fromStrict) noteBSS)
-- AccountSettingsForm -- AccountSettingsForm
data AccountSettingsForm = AccountSettingsForm data AccountSettingsForm = AccountSettingsForm
{ _privateDefault :: Bool { _privateDefault :: Bool
, _archiveDefault :: Bool , _archiveDefault :: Bool
, _privacyLock :: Bool , _privacyLock :: Bool
} deriving (Show, Eq, Read, Generic) } deriving (Show, Eq, Read, Generic)
instance FromJSON AccountSettingsForm where parseJSON = A.genericParseJSON gDefaultFormOptions instance FromJSON AccountSettingsForm where parseJSON = A.genericParseJSON gDefaultFormOptions
@ -479,7 +485,7 @@ instance FromJSON BookmarkForm where parseJSON = A.genericParseJSON gDefaultForm
instance ToJSON BookmarkForm where toJSON = A.genericToJSON gDefaultFormOptions instance ToJSON BookmarkForm where toJSON = A.genericToJSON gDefaultFormOptions
gDefaultFormOptions :: A.Options gDefaultFormOptions :: A.Options
gDefaultFormOptions = A.defaultOptions { A.fieldLabelModifier = drop 1 } gDefaultFormOptions = A.defaultOptions { A.fieldLabelModifier = drop 1 }
toBookmarkFormList :: [Entity Bookmark] -> [Entity BookmarkTag] -> [BookmarkForm] toBookmarkFormList :: [Entity Bookmark] -> [Entity BookmarkTag] -> [BookmarkForm]
toBookmarkFormList bs as = do toBookmarkFormList bs as = do
@ -533,7 +539,7 @@ upsertBookmark:: Maybe (Key Bookmark) -> Bookmark -> [Text] -> DB (UpsertResult,
upsertBookmark mbid bm tags = do upsertBookmark mbid bm tags = do
res <- case mbid of res <- case mbid of
Just bid -> do Just bid -> do
get bid >>= \case get bid >>= \case
Just prev_bm -> replaceBookmark bid prev_bm Just prev_bm -> replaceBookmark bid prev_bm
_ -> fail "not found" _ -> fail "not found"
Nothing -> do Nothing -> do
@ -542,7 +548,7 @@ upsertBookmark mbid bm tags = do
_ -> (Created,) <$> insert bm _ -> (Created,) <$> insert bm
insertTags (bookmarkUserId bm) (snd res) insertTags (bookmarkUserId bm) (snd res)
pure res pure res
where where
prepareReplace prev_bm = do prepareReplace prev_bm = do
if (bookmarkHref bm /= bookmarkHref prev_bm) if (bookmarkHref bm /= bookmarkHref prev_bm)
then bm { bookmarkArchiveHref = Nothing } then bm { bookmarkArchiveHref = Nothing }
@ -567,7 +573,7 @@ upsertNote:: Maybe (Key Note) -> Note -> DB (UpsertResult, Key Note)
upsertNote mnid bmark@Note{..} = do upsertNote mnid bmark@Note{..} = do
case mnid of case mnid of
Just nid -> do Just nid -> do
get nid >>= \case get nid >>= \case
Just _ -> do Just _ -> do
replace nid bmark replace nid bmark
pure (Updated, nid) pure (Updated, nid)

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.