diff of cc81f1e668c372c866660c886ad0e8a861d466f5
cc81f1e668c372c866660c886ad0e8a861d466f5
diff --git a/elm-frontti/src/Ajax_cmds.elm b/elm-frontti/src/Ajax_cmds.elm
index a697a05..e260121 100644
--- a/elm-frontti/src/Ajax_cmds.elm
+++ b/elm-frontti/src/Ajax_cmds.elm
@@ -127,3 +127,9 @@ saveSettings settings =
, expect = Http.expectWhatever SettingsSaved
, timeout = Nothing
, tracker = Nothing}
+
+searchPreviouslyPosts search_term =
+ Http.post
+ { url = "/api/posts/search-previously"
+ , body = Http.stringBody "application/json" search_term
+ , expect = Http.expectJson PreviouslySearchResult (Json.list Article.previouslyDocDecoder)}
diff --git a/elm-frontti/src/Article.elm b/elm-frontti/src/Article.elm
index 533a048..31c6f4c 100644
--- a/elm-frontti/src/Article.elm
+++ b/elm-frontti/src/Article.elm
@@ -24,7 +24,9 @@ import Time
-- "id": 1,
-- "versions": [],
-- "version": null,
--- "created_at": "2020-10-16T07:52:59Z"
+-- "created_at": "2020-10-16T07:52:59Z",
+-- "previosly": [{"id": 666,
+-- "title": "Titteli"}]
-- }
import Creator exposing (Creator, creatorDecoder)
@@ -33,6 +35,9 @@ decodeApply : Decode.Decoder a -> Decode.Decoder (a -> b) -> Decode.Decoder b
decodeApply value partial =
Decode.andThen (\p -> Decode.map p value) partial
+type alias PreviousArticle =
+ { id: Int
+ , title: String}
type alias Article =
{ creator : Creator
@@ -48,9 +53,16 @@ type alias Article =
, created_at: Maybe Time.Posix
, hidden : Bool
, unlisted : Bool
+ , previously: List PreviousArticle
}
-- encoder
+
+encodePreviously prev =
+ object
+ [ ( "id", int prev.id)
+ , ( "title", string prev.title)]
+
encode : Article -> Json.Value
encode article =
object
@@ -66,6 +78,7 @@ encode article =
, ( "created_at", (maybe iso8601) article.created_at)
, ( "hidden", bool article.hidden)
, ( "unlisted", bool article.unlisted)
+ , ( "previously", list encodePreviously article.previously)
]
@@ -85,6 +98,13 @@ creator_Decoder = Decode.field "creator" creatorDecoder
hiddenDecoder = Decode.field "hidden" Decode.bool
unlistedDecoder = Decode.field "unlisted" Decode.bool
+previouslyDocDecoder =
+ Decode.succeed PreviousArticle
+ |> decodeApply idDecoder
+ |> decodeApply titleDecoder
+
+previouslyDecoder = Decode.field "previously" (Decode.list previouslyDocDecoder)
+
-- |> == clojure's ->>
articleDecoder : Decoder Article
articleDecoder =
@@ -100,6 +120,7 @@ articleDecoder =
|> decodeApply created_atDecoder
|> decodeApply hiddenDecoder
|> decodeApply unlistedDecoder
+ |> decodeApply previouslyDecoder
type alias Title =
{ title : String
diff --git a/elm-frontti/src/Article_view.elm b/elm-frontti/src/Article_view.elm
index 0a61041..09c9a6e 100644
--- a/elm-frontti/src/Article_view.elm
+++ b/elm-frontti/src/Article_view.elm
@@ -42,4 +42,9 @@ articleView settings loginstate zone the_actual_post =
|> List.filter ((/=) "")
|> List.map ( \tag -> span [] [ a [ href ("/blog/tags/" ++ tag)
, class "tag" ] [text tag]
- , text ", "]))]
+ , text ", "]))
+ , div [ class "previously" ]
+ ( the_actual_post.previously
+ |> List.map (\prev -> span [] [ a [ href ("/blog/post/" ++ (String.fromInt prev.id))]
+ [ text settings.previously_label ]
+ , text ", "]))]
diff --git a/elm-frontti/src/Main.elm b/elm-frontti/src/Main.elm
index d6ce68f..7aeb21a 100644
--- a/elm-frontti/src/Main.elm
+++ b/elm-frontti/src/Main.elm
@@ -65,7 +65,7 @@ subscriptions _ = Sub.batch
[ tags ReceivedTag
, aceStateUpdate AceStateUpdate]
-initialModel url key viewstate = Model viewstate Nothing False False [] Nothing LoggedOut key url Nothing Time.utc []
+initialModel url key viewstate = Model viewstate Nothing False False [] Nothing LoggedOut key url Nothing Time.utc [] []
viewStatePerUrl : Url.Url -> (ViewState, List (Cmd Msg))
viewStatePerUrl url =
@@ -125,6 +125,8 @@ init _ url key =
-- PORTS --
port prompt : String -> Cmd msg
port alert : String -> Cmd msg
+port showPreviousPostsModal: (() -> Cmd msg)
+port closePreviousPostsModal: (() -> Cmd msg)
port tags : (String -> msg) -> Sub msg
port aceStateUpdate : (String -> msg) -> Sub msg
@@ -481,7 +483,66 @@ update msg model =
Err http_error ->
( model
- , alert ("Error saving settings " ++ Debug.toString http_error))
+ , alert ("Error saving settings " ++ Debug.toString http_error))
+ ShowPreviousPostsModal ->
+ ( model
+ , showPreviousPostsModal ())
+ ClosePreviousPostsModel ->
+ ( model
+ , closePreviousPostsModal ())
+ PreviouslySearchInput search_term ->
+ ( model
+ , searchPreviouslyPosts search_term)
+ PreviouslySearchResult result ->
+ case result of
+ Ok previously_posts ->
+ let article_previouslies = case model.postEditorSettings of
+ Just settings -> settings.article.previously
+ Nothing -> []
+ in
+ ({ model |
+ searchedPosts = List.filter (\p -> not (List.member p article_previouslies)) previously_posts}
+ , Cmd.none)
+ Err error ->
+ ( model
+ , alert (errToString error))
+ SelectPreviouslyPost selectedPost ->
+ case model.postEditorSettings of
+ Just editorSettings ->
+ let new_posts = List.filter ((/=) selectedPost) model.searchedPosts
+ article = editorSettings.article
+ previously = article.previously
+ in
+ ({ model
+ | searchedPosts = new_posts
+ , postEditorSettings = Just { editorSettings
+ | article = { article
+ | previously = selectedPost :: previously}}}
+ , Cmd.none)
+ Nothing ->
+ ( model
+ , Cmd.none)
+ DropPreviously previous_post ->
+ case model.postEditorSettings of
+ Just editorSettings ->
+ let article = editorSettings.article
+ previously = article.previously
+ in
+ ({ model
+ | postEditorSettings = Just
+ { editorSettings
+ | article =
+ { article
+ | previously = List.filter ((/=) previous_post) previously}}}
+ , Cmd.none)
+ Nothing ->
+ ( model
+ , Cmd.none)
+ SetPreviouslyLabel label ->
+ ({ model | settings = Maybe.map (\settings ->
+ { settings | previously_label = label})
+ model.settings}
+ , Cmd.none)
doGoHome_ model other_cmds =
(model, Cmd.batch (List.append [ getSettings
@@ -565,7 +626,7 @@ view model =
Just editorSettings ->
let post = editorSettings.article
tag_index = editorSettings.selected_tag in
- PostEditor.postEditor post tag_index model.showImageModal model.loadedImages model.draggingImages editorSettings settings model.zone model.loginState
+ PostEditor.postEditor post tag_index model.showImageModal model.loadedImages model.draggingImages editorSettings settings model.zone model.loginState model.searchedPosts
Nothing -> [ div [] [ text "No post loaded" ]]
MediaList -> [ medialist model.loadedImages model.medialist_state ]
SettingsEditor -> [ SettingsEditor.editor settings])
diff --git a/elm-frontti/src/Message.elm b/elm-frontti/src/Message.elm
index 8eb44b0..9c39456 100644
--- a/elm-frontti/src/Message.elm
+++ b/elm-frontti/src/Message.elm
@@ -72,7 +72,8 @@ type alias Model =
, url : Url.Url
, postEditorSettings: Maybe PostEditorSettings
, zone : Time.Zone
- , titles : List Article.Title }
+ , titles : List Article.Title
+ , searchedPosts : List Article.PreviousArticle}
type Msg
= PageReceived (Result Http.Error P.Page)
@@ -130,6 +131,13 @@ type Msg
| SetPageSize String
| SaveSettings
| SettingsSaved (Result Http.Error ())
+ | ShowPreviousPostsModal
+ | ClosePreviousPostsModel
+ | PreviouslySearchInput String
+ | PreviouslySearchResult (Result Http.Error (List Article.PreviousArticle))
+ | SelectPreviouslyPost Article.PreviousArticle
+ | DropPreviously Article.PreviousArticle
+ | SetPreviouslyLabel String
-- ports
port reallySetupAce : String -> Cmd msg
diff --git a/elm-frontti/src/PostEditor.elm b/elm-frontti/src/PostEditor.elm
index af3f1d3..98c99c7 100644
--- a/elm-frontti/src/PostEditor.elm
+++ b/elm-frontti/src/PostEditor.elm
@@ -58,8 +58,25 @@ editor params =
filesDecoder : D.Decoder (List File)
filesDecoder =
D.at ["target","files"] (D.list File.decoder)
+
+previouslyButtons post loadedPosts =
+ div [ class "previously-buttons" ]
+ [ murja_button [ onClick ShowPreviousPostsModal ] [ text "Link previous posts"]
+ , ul []
+ ( post.previously
+ |> List.map (\p -> li [] [ text p.title, button [ onClick (DropPreviously p)] [ text "X"]]))
+ , node "dialog" [ id "previouslyModal" ]
+ [ div [ class "dialog" ]
+ [ header [ class "previouslyHeader" ] [ button [ onClick ClosePreviousPostsModel ] [ text "X"]]
+ , select [ multiple True
+ , class "previouslyPostResult"]
+ (List.map (\prev_post ->
+ option [ onClick (SelectPreviouslyPost prev_post) ] [ text prev_post.title ]) loadedPosts)
+ , input [ type_ "text"
+ , placeholder "Search for posts"
+ , onInput PreviouslySearchInput] []]]]
-postEditor post tag showImageModal loadedImages draggingImages editorSettings app_settings tz loginState
+postEditor post tag showImageModal loadedImages draggingImages editorSettings app_settings tz loginState searchedPosts
= [ div [ class "editor-top" ]
[ div [ id "editor-buttons"
, class "editor-grouper"]
@@ -80,6 +97,7 @@ postEditor post tag showImageModal loadedImages draggingImages editorSettings ap
, onClick GetListOfImages]
[text "Insert image"]]
, tagView post tag
+ , previouslyButtons post searchedPosts
, div [ class "editor-grouper" ]
[ label [ for "hidden"]
[ text "Hidden article"]
diff --git a/elm-frontti/src/Settings.elm b/elm-frontti/src/Settings.elm
index 900aa19..d5daad3 100644
--- a/elm-frontti/src/Settings.elm
+++ b/elm-frontti/src/Settings.elm
@@ -8,15 +8,19 @@ import Json.Encode as Json exposing (..)
type alias Settings =
{ time_format : String
, blog_title : String
- , recent_post_count : Int}
+ , recent_post_count : Int
+ , previously_label: String
+ }
-settingsDecoder = Decode.map3 Settings
+settingsDecoder = Decode.map4 Settings
(Decode.field "time-format" Decode.string)
(Decode.field "blog-title" Decode.string)
(Decode.field "recent-post-count" Decode.int)
+ (Decode.field "previously_label" Decode.string)
encodeSettings settings =
object
[ ( "time-format", string settings.time_format )
, ( "blog-title", string settings.blog_title)
- , ( "recent-post-count", int settings.recent_post_count)]
+ , ( "recent-post-count", int settings.recent_post_count)
+ , ( "previously_label", string settings.previously_label) ]
diff --git a/elm-frontti/src/SettingsEditor.elm b/elm-frontti/src/SettingsEditor.elm
index 226aa6c..4e8d11a 100644
--- a/elm-frontti/src/SettingsEditor.elm
+++ b/elm-frontti/src/SettingsEditor.elm
@@ -28,5 +28,11 @@ editor settings =
, value (String.fromInt settings.recent_post_count)
, type_ "number"] []
+ , label [ for "previously_label" ]
+ [ text "Previously link label" ]
+ , input [ id "previously"
+ , onInput SetPreviouslyLabel
+ , value settings.previously_label] []
+
, button [ onClick SaveSettings ]
[ text "Save settings"]]
diff --git a/resources/css/murja.css b/resources/css/murja.css
index 2e5eb73..094259f 100644
--- a/resources/css/murja.css
+++ b/resources/css/murja.css
@@ -265,6 +265,14 @@ header {
grid-column: 2;
}
+.previouslyHeader {
+ display: flex;
+ justify-content: flex-end;
+}
+
+.previouslyPostResult {
+ display: block;
+}
@media only screen and (max-device-width:480px)
{
diff --git a/resources/js/murja-helper.js b/resources/js/murja-helper.js
index 902cc54..0006998 100644
--- a/resources/js/murja-helper.js
+++ b/resources/js/murja-helper.js
@@ -43,3 +43,12 @@ Object.defineProperty(HTMLElement.prototype, "dangerouslySetInnerHTML", {
this.innerHTML = value
}
});
+
+app.ports.showPreviousPostsModal.subscribe(_ => {
+ document.getElementById('previouslyModal').showModal();
+});
+
+
+app.ports.closePreviousPostsModal.subscribe(_ => {
+ document.getElementById('previouslyModal').close();
+});
diff --git a/resources/sql/018-previously.sql b/resources/sql/018-previously.sql
new file mode 100644
index 0000000..b7fc8a5
--- /dev/null
+++ b/resources/sql/018-previously.sql
@@ -0,0 +1,17 @@
+CREATE TABLE IF NOT EXISTS blog.Previously_Link
+(
+ referencee_id INT NOT NULL,
+ referenced_id INT NOT NULL,
+ PRIMARY KEY (referencee_id, referenced_id),
+ FOREIGN KEY (referencee_id) REFERENCES blog.Post(id) ON UPDATE CASCADE ON DELETE CASCADE,
+ FOREIGN KEY (referenced_id) REFERENCES blog.Post(id) ON UPDATE CASCADE ON DELETE CASCADE
+);
+
+CREATE OR REPLACE VIEW blog.Previously_Link_Titles AS
+ SELECT pl.referencee_id,
+ pl.referenced_id AS id,
+ p.Title AS title
+ FROM blog.Previously_Link pl
+ JOIN blog.Post p on pl.referenced_id = p.id;
+
+INSERT INTO blog.Settings VALUES ('previously_label', '"Previously"') ON CONFLICT DO NOTHING;
diff --git a/resources/sql/post-fns.sql b/resources/sql/post-fns.sql
index 9d29e8c..3eec83b 100644
--- a/resources/sql/post-fns.sql
+++ b/resources/sql/post-fns.sql
@@ -35,9 +35,11 @@ SELECT p.ID,
'img_location',
u.Img_location) as "creator",
(SELECT MAX(version) + 1 FROM blog.Post_History phh WHERE (phh.id = p.id AND ((not phh.unlisted) OR $2) AND ((not phh.hidden) OR $2))) AS version,
- json_agg(DISTINCT version) as "versions", p.hidden, p.unlisted
+ json_agg(DISTINCT version) as "versions", p.hidden, p.unlisted,
+ json_agg(to_jsonb (pl.*) - 'referencee_id') as previously
FROM blog.Post p
JOIN blog.Users u ON u.ID = p.creator_id
+LEFT JOIN blog.Previously_Link_Titles pl ON p.id = pl.referencee_id
LEFT JOIN blog.Post_History ph ON (ph.id = p.id AND ((not ph.unlisted) OR $2) AND ((not ph.hidden) OR $2))
WHERE p.ID = $1 AND (NOT p.hidden OR (p.hidden AND $2))
GROUP BY p.ID, u.ID;
@@ -52,19 +54,23 @@ SELECT p.ID, p.Title, p.created_at, p.Content, p.tags, p.version, 0 AS "amount-o
u.Nickname,
'img_location',
u.Img_location) as "creator",
- json_agg(DISTINCT ph.version) as "versions", p.hidden, p.unlisted
+ json_agg(DISTINCT ph.version) as "versions", p.hidden, p.unlisted,
+ json_agg(to_jsonb (pl.*) - 'referencee_id') as previously
FROM blog.Post_History p
JOIN blog.Users u ON u.ID = p.creator_id
LEFT JOIN blog.Post_History ph ON (ph.id = p.id AND (not ph.unlisted) AND (not ph.hidden))
+LEFT JOIN blog.Previously_Link_Titles pl ON p.id = pl.referencee_id
WHERE p.ID = $1 AND p.version = $2 AND not p.hidden
GROUP BY p.ID, p.Title, p.created_at, p.Content, p.tags, p.version, "amount-of-comments", u.username, u.nickname, u.img_location;
-- name: get-all*
-SELECT p.id, p.Title, p.Content, p.created_at, p.tags, u.Username, u.Nickname, u.Img_location, COUNT(c.ID) AS "amount-of-comments", p.hidden, p.unlisted
+SELECT p.id, p.Title, p.Content, p.created_at, p.tags, u.Username, u.Nickname, u.Img_location, COUNT(c.ID) AS "amount-of-comments", p.hidden, p.unlisted,
+ json_agg(to_jsonb (pl.*) - 'referencee_id') as previously
FROM blog.Post p
JOIN blog.Users u ON u.ID = p.creator_id
LEFT JOIN blog.Comment c ON c.parent_post_id = p.ID
+LEFT JOIN blog.Previously_Link_Titles pl ON p.id = pl.referencee_id
WHERE NOT p.hidden
GROUP BY p.ID, u.ID
ORDER BY p.created_at DESC
@@ -78,11 +84,13 @@ ORDER BY p.created_at DESC
-- $3 == show-hidden?
-- name: get-page*
-- returns: :array-hash
-SELECT p.ID, p.Title, p.Content, p.created_at, p.tags, COUNT(c.ID) AS "amount-of-comments", json_build_object('username', u.Username, 'nickname', u.Nickname, 'img_location', u.Img_location) as "creator", json_agg(DISTINCT version) as "versions", p.hidden, p.unlisted
+SELECT p.ID, p.Title, p.Content, p.created_at, p.tags, COUNT(c.ID) AS "amount-of-comments", json_build_object('username', u.Username, 'nickname', u.Nickname, 'img_location', u.Img_location) as "creator", json_agg(DISTINCT version) as "versions", p.hidden, p.unlisted,
+ json_agg(to_jsonb (pl.*) - 'referencee_id') as previously
FROM blog.Post p
JOIN blog.Users u ON u.ID = p.creator_id
LEFT JOIN blog.Comment c ON c.parent_post_id = p.ID
LEFT JOIN blog.Post_History ph ON (ph.id = p.id AND ((not ph.unlisted) OR $3) AND ((not ph.hidden) OR $3))
+LEFT JOIN blog.Previously_Link_Titles pl ON p.id = pl.referencee_id
WHERE ((NOT p.unlisted) OR $3)
AND ((NOT p.hidden) OR $3)
GROUP BY p.ID, u.ID
@@ -163,3 +171,10 @@ where id = $6;
update blog.post
set hidden = $2
where id = $1;
+
+-- name: link-previously
+INSERT INTO blog.Previously_Link VALUES ($1, $2);
+
+-- name: search-posts
+-- returns: :array-hash
+select post.id, post.title from blog.Post where (title ilike '%'||$1||'%' or content ilike '%'||$1||'%') and not unlisted and not hidden;
diff --git a/src/migration-list.lisp b/src/migration-list.lisp
index 740fbf7..896af6e 100644
--- a/src/migration-list.lisp
+++ b/src/migration-list.lisp
@@ -22,6 +22,7 @@
(defmigration "015-image-post-pairing-view.up")
(defmigration "016-hardcoded-hidden-unlisted")
(defmigration "017-settings-in-db")
+(defmigration "018-previously")
(defun prepare-e2e-migration ()
(postmodern:execute "DELETE FROM blog.Users")
diff --git a/src/posts/post-db.lisp b/src/posts/post-db.lisp
index 42d8cf7..dacda5d 100644
--- a/src/posts/post-db.lisp
+++ b/src/posts/post-db.lisp
@@ -3,7 +3,7 @@
(:import-from :com.inuoe.jzon :parse)
(:import-from :halisql :defqueries)
(:import-from :lisp-fixup :fix-timestamp)
- (:export :get-tagged :get-post-version :get-page :get-titles-by-year :insert-post :update-post :get-post))
+ (:export :search-posts :link-previously :get-tagged :get-post-version :get-page :get-titles-by-year :insert-post :update-post :get-post))
(in-package :murja.posts.post-db)
@@ -20,11 +20,16 @@
(get-titles-by-year* allow-hidden?) 'list)))
(defun fix-post (post)
- (dolist (key (list "creator" "tags" "versions"))
+ (dolist (key (list "creator" "tags" "versions" "previously"))
(when (gethash key post)
(setf (gethash key post)
(parse (gethash key post)))))
+ (setf (gethash "previously" post)
+ (or
+ (remove-if-not #'hash-table-p (coerce (gethash "previously" post) 'list))
+ #()))
+
(setf (gethash "created_at" post)
(fix-timestamp (gethash "created_at" post)))
post)
@@ -51,4 +56,4 @@
(log:info "Tag ~a returns posts ~a~%" tag (mapcar #'alexandria:hash-table-alist posts))
(when posts
(mapcar #'fix-post
- posts))))
+ posts))))
diff --git a/src/routes/post-routes.lisp b/src/routes/post-routes.lisp
index 739e35d..ec72136 100644
--- a/src/routes/post-routes.lisp
+++ b/src/routes/post-routes.lisp
@@ -85,6 +85,14 @@
(let ((creator-id (gethash "id" *user*)))
(prin1-to-string (caar (murja.posts.post-db:insert-post "New title" "New post" creator-id "[]" t nil)))))
+(defroute search-prev ("/api/posts/search-previously" :method :post
+ :decorators (@json
+ @transaction
+ @authenticated
+ (@can? "create-post"))) ()
+ (let* ((search-body (hunchentoot:raw-post-data :force-text t)))
+ (stringify (murja.posts.post-db:search-posts search-body))))
+
(defroute post-update-route ("/api/posts/post" :method :put
:decorators (@json
@transaction
@@ -99,9 +107,13 @@
(gethash "tags" request-body) 'list))
#())))
(post-id (gethash "id" request-body))
+ (previously-links (coerce (gethash "previously" request-body) 'list))
(hidden (gethash "hidden" request-body))
(unlisted (gethash "unlisted" request-body)))
(log:info "updating post ~d" post-id)
(murja.posts.post-db:update-post title content tags hidden unlisted post-id)
+ (dolist (link previously-links)
+ (let ((id (gethash "id" link)))
+ (murja.posts.post-db:link-previously post-id id)))
""))