add firefox bookmark import (Resolves #15)
This commit is contained in:
parent
a0b65ca84a
commit
4a3cb641bc
10
README.md
10
README.md
|
@ -43,12 +43,18 @@ see https://github.com/jonschoning/espial-docker
|
|||
stack exec migration -- createuser --conn espial.sqlite3 --userName myusername --userPassword myuserpassword
|
||||
```
|
||||
|
||||
5. Import a bookmark file for a user (optional)
|
||||
5a. Import a pinboard bookmark file for a user (optional)
|
||||
|
||||
```
|
||||
stack exec migration -- importbookmarks --conn espial.sqlite3 --userName myusername --bookmarkFile sample-bookmarks.json
|
||||
```
|
||||
|
||||
5b. Import a firefox bookmark file for a user (optional)
|
||||
|
||||
```
|
||||
stack exec migration -- importfirefoxbookmarks --conn espial.sqlite3 --userName myusername --bookmarkFile firefox-bookmarks.json
|
||||
```
|
||||
|
||||
6. Start a production server:
|
||||
|
||||
```
|
||||
|
@ -78,7 +84,7 @@ ssl: use reverse proxy
|
|||
|
||||
- See `purs/` folder
|
||||
|
||||
## Import Bookmark file format
|
||||
## Import Bookmark file format (pinboard compatible format)
|
||||
|
||||
see `sample-bookmarks.json`, which contains a JSON array, each line containing a `FileBookmark` object.
|
||||
|
||||
|
|
|
@ -26,6 +26,9 @@ data MigrationOpts
|
|||
| ImportBookmarks { conn :: Text
|
||||
, userName :: Text
|
||||
, bookmarkFile :: FilePath }
|
||||
| ImportFirefoxBookmarks { conn :: Text
|
||||
, userName :: Text
|
||||
, bookmarkFile :: FilePath }
|
||||
| ExportBookmarks { conn :: Text
|
||||
, userName :: Text
|
||||
, bookmarkFile :: FilePath }
|
||||
|
@ -72,13 +75,6 @@ main = do
|
|||
P.deleteCascade uid
|
||||
pure () :: DB ()
|
||||
|
||||
ImportBookmarks {..} ->
|
||||
P.runSqlite conn $ do
|
||||
muser <- P.getBy (UniqueUserName userName)
|
||||
case muser of
|
||||
Just (P.Entity uid _) -> insertFileBookmarks uid bookmarkFile
|
||||
Nothing -> liftIO (print (userName ++ "not found"))
|
||||
|
||||
ExportBookmarks {..} ->
|
||||
P.runSqlite conn $ do
|
||||
muser <- P.getBy (UniqueUserName userName)
|
||||
|
@ -86,9 +82,36 @@ main = do
|
|||
Just (P.Entity uid _) -> exportFileBookmarks uid bookmarkFile
|
||||
Nothing -> liftIO (print (userName ++ "not found"))
|
||||
|
||||
ImportBookmarks {..} ->
|
||||
P.runSqlite conn $ do
|
||||
muser <- P.getBy (UniqueUserName userName)
|
||||
case muser of
|
||||
Just (P.Entity uid _) -> do
|
||||
result <- insertFileBookmarks uid bookmarkFile
|
||||
case result of
|
||||
Left e -> liftIO (print e)
|
||||
Right n -> liftIO (print (show n ++ " bookmarks imported."))
|
||||
Nothing -> liftIO (print (userName ++ "not found"))
|
||||
|
||||
|
||||
ImportFirefoxBookmarks {..} ->
|
||||
P.runSqlite conn $ do
|
||||
muser <- P.getBy (UniqueUserName userName)
|
||||
case muser of
|
||||
Just (P.Entity uid _) -> do
|
||||
result <- insertFFBookmarks uid bookmarkFile
|
||||
case result of
|
||||
Left e -> liftIO (print e)
|
||||
Right n -> liftIO (print (show n ++ " bookmarks imported."))
|
||||
Nothing -> liftIO (print (userName ++ "not found"))
|
||||
|
||||
ImportNotes {..} ->
|
||||
P.runSqlite conn $ do
|
||||
muser <- P.getBy (UniqueUserName userName)
|
||||
case muser of
|
||||
Just (P.Entity uid _) -> insertDirFileNotes uid noteDirectory
|
||||
Just (P.Entity uid _) -> do
|
||||
result <- insertDirFileNotes uid noteDirectory
|
||||
case result of
|
||||
Left e -> liftIO (print e)
|
||||
Right n -> liftIO (print (show n ++ " notes imported."))
|
||||
Nothing -> liftIO (print (userName ++ "not found"))
|
||||
|
|
84
src/Model.hs
84
src/Model.hs
|
@ -10,6 +10,7 @@ import qualified Data.Attoparsec.Text as P
|
|||
import qualified Control.Monad.Combinators as PC
|
||||
import qualified Data.List.NonEmpty as NE
|
||||
import qualified Data.Time.ISO8601 as TI
|
||||
import qualified Data.Time.Clock.POSIX as TI
|
||||
import qualified Database.Esqueleto as E
|
||||
import Database.Esqueleto.Internal.Sql as E
|
||||
import qualified Data.Time as TI
|
||||
|
@ -349,12 +350,69 @@ bookmarkTofileBookmark (Bookmark {..}) tags =
|
|||
bookmarkArchiveHref
|
||||
tags
|
||||
|
||||
data FFBookmarkNode = FFBookmarkNode
|
||||
{ firefoxBookmarkChildren :: Maybe [FFBookmarkNode]
|
||||
, firefoxBookmarkDateAdded :: !TI.POSIXTime
|
||||
, firefoxBookmarkGuid :: !Text
|
||||
, firefoxBookmarkIconUri :: !(Maybe Text)
|
||||
, firefoxBookmarkId :: !Int
|
||||
, firefoxBookmarkIndex :: !Int
|
||||
, firefoxBookmarkLastModified :: !TI.POSIXTime
|
||||
, firefoxBookmarkRoot :: !(Maybe Text)
|
||||
, firefoxBookmarkTitle :: !Text
|
||||
, firefoxBookmarkType :: !Text
|
||||
, firefoxBookmarkTypeCode :: !Int
|
||||
, firefoxBookmarkUri :: !(Maybe Text)
|
||||
} deriving (Show, Eq, Typeable, Ord)
|
||||
|
||||
instance FromJSON FFBookmarkNode where
|
||||
parseJSON (Object o) =
|
||||
FFBookmarkNode <$>
|
||||
(o A..:? "children") <*>
|
||||
(o .: "dateAdded") <*>
|
||||
o .: "guid" <*>
|
||||
(o A..:? "iconUri") <*>
|
||||
o .: "id" <*>
|
||||
o .: "index" <*>
|
||||
(o .: "lastModified") <*>
|
||||
(o A..:? "root") <*>
|
||||
(o .: "title") <*>
|
||||
(o .: "type") <*>
|
||||
(o .: "typeCode") <*>
|
||||
(o A..:? "uri")
|
||||
parseJSON _ = A.parseFail "bad parse"
|
||||
|
||||
insertFileBookmarks :: Key User -> FilePath -> DB ()
|
||||
firefoxBookmarkNodeToBookmark :: UserId -> FFBookmarkNode -> IO [Bookmark]
|
||||
firefoxBookmarkNodeToBookmark user (FFBookmarkNode {..}) = do
|
||||
case firefoxBookmarkTypeCode of
|
||||
1 -> do
|
||||
slug <- mkBmSlug
|
||||
pure $
|
||||
[ Bookmark
|
||||
user
|
||||
slug
|
||||
(fromMaybe "" firefoxBookmarkUri)
|
||||
firefoxBookmarkTitle
|
||||
""
|
||||
(TI.posixSecondsToUTCTime (firefoxBookmarkDateAdded / 1000000))
|
||||
True
|
||||
False
|
||||
False
|
||||
Nothing
|
||||
]
|
||||
2 ->
|
||||
join <$>
|
||||
mapM
|
||||
(firefoxBookmarkNodeToBookmark user)
|
||||
(fromMaybe [] firefoxBookmarkChildren)
|
||||
_ -> pure []
|
||||
|
||||
|
||||
insertFileBookmarks :: Key User -> FilePath -> DB (Either String Int)
|
||||
insertFileBookmarks userId bookmarkFile = do
|
||||
mfmarks <- liftIO $ readFileBookmarks bookmarkFile
|
||||
case mfmarks of
|
||||
Left e -> print e
|
||||
Left e -> pure $ Left e
|
||||
Right fmarks -> do
|
||||
bmarks <- liftIO $ mapM (fileBookmarkToBookmark userId) fmarks
|
||||
mbids <- mapM insertUnique bmarks
|
||||
|
@ -366,13 +424,30 @@ insertFileBookmarks userId bookmarkFile = do
|
|||
(\mbid tags -> ((, tags) <$> mbid))
|
||||
mbids
|
||||
(extractTags <$> fmarks)
|
||||
pure $ Right (length bmarks)
|
||||
|
||||
where
|
||||
extractTags = words . fileBookmarkTags
|
||||
|
||||
insertFFBookmarks :: Key User -> FilePath -> DB (Either String Int)
|
||||
insertFFBookmarks userId bookmarkFile = do
|
||||
mfmarks <- liftIO $ readFFBookmarks bookmarkFile
|
||||
case mfmarks of
|
||||
Left e -> pure $ Left e
|
||||
Right fmarks -> do
|
||||
bmarks <- liftIO $ firefoxBookmarkNodeToBookmark userId fmarks
|
||||
_ <- mapM insertUnique bmarks
|
||||
pure $ Right (length bmarks)
|
||||
|
||||
|
||||
readFileBookmarks :: MonadIO m => FilePath -> m (Either String [FileBookmark])
|
||||
readFileBookmarks fpath =
|
||||
pure . A.eitherDecode' . fromStrict =<< readFile fpath
|
||||
|
||||
readFFBookmarks :: MonadIO m => FilePath -> m (Either String FFBookmarkNode)
|
||||
readFFBookmarks fpath =
|
||||
pure . A.eitherDecode' . fromStrict =<< readFile fpath
|
||||
|
||||
exportFileBookmarks :: Key User -> FilePath -> DB ()
|
||||
exportFileBookmarks user fpath = do
|
||||
liftIO . A.encodeFile fpath =<< getFileBookmarks user
|
||||
|
@ -519,14 +594,15 @@ fileNoteToNote user (FileNote {..} ) = do
|
|||
fileNoteCreatedAt
|
||||
fileNoteUpdatedAt
|
||||
|
||||
insertDirFileNotes :: Key User -> FilePath -> DB ()
|
||||
insertDirFileNotes :: Key User -> FilePath -> DB (Either String Int)
|
||||
insertDirFileNotes userId noteDirectory = do
|
||||
mfnotes <- liftIO $ readFileNotes noteDirectory
|
||||
case mfnotes of
|
||||
Left e -> print e
|
||||
Left e -> pure $ Left e
|
||||
Right fnotes -> do
|
||||
notes <- liftIO $ mapM (fileNoteToNote userId) fnotes
|
||||
void $ mapM insertUnique notes
|
||||
pure $ Right (length notes)
|
||||
where
|
||||
readFileNotes :: MonadIO m => FilePath -> m (Either String [FileNote])
|
||||
readFileNotes fdir = do
|
||||
|
|
Loading…
Reference in a new issue