diff of 442d9b810f5406c431b9f55a356e6c857bfa7284

442d9b810f5406c431b9f55a356e6c857bfa7284
diff --git a/elm-frontti/src/Ajax_cmds.elm b/elm-frontti/src/Ajax_cmds.elm
index 75d976b..016e786 100644
--- a/elm-frontti/src/Ajax_cmds.elm
+++ b/elm-frontti/src/Ajax_cmds.elm
@@ -112,3 +112,14 @@ loadPostVersion post_id_int version_id_int =
     Http.get
         { url = "/api/posts/post/" ++ post_id ++ "/version/" ++ version_id
         , expect = Http.expectJson GotOldPost Article.articleDecoder}
+
+generateNewPost =
+    Http.request
+        { method = "POST"
+        , headers = []
+        , url = "/api/posts/new_post"
+        , body = emptyBody
+        , expect = Http.expectJson NewPostGenerated Json.int
+        , timeout = Nothing
+        , tracker = Nothing
+        }
diff --git a/elm-frontti/src/Article.elm b/elm-frontti/src/Article.elm
index 565c4b4..86b6c12 100644
--- a/elm-frontti/src/Article.elm
+++ b/elm-frontti/src/Article.elm
@@ -46,6 +46,8 @@ type alias Article =
     , versions: Maybe (List Int)
     , version : Maybe Int
     , created_at: Maybe Time.Posix
+    , hidden : Bool
+    , unlisted : Bool
     }
 
 -- encoder
@@ -62,6 +64,8 @@ encode article =
         , ( "id", (maybe int) article.id)
         , ( "version", (maybe int) article.version)
         , ( "created_at", (maybe iso8601) article.created_at)
+        , ( "hidden", bool article.hidden)
+        , ( "unlisted", bool article.unlisted)
         ]
 
 
@@ -77,7 +81,9 @@ idDecoder = Decode.maybe ( Decode.field "id" Decode.int)
 versionsDecoder = Decode.maybe (Decode.field "versions" (Decode.list Decode.int))
 versionDecoder = Decode.maybe (Decode.field "version" Decode.int)
 created_atDecoder = Decode.field "created_at" (Decode.maybe Extra.iso8601)
-creator_Decoder = Decode.field "creator" creatorDecoder                    
+creator_Decoder = Decode.field "creator" creatorDecoder
+hiddenDecoder = Decode.field "hidden" Decode.bool
+unlistedDecoder = Decode.field "unlisted" Decode.bool
 
 -- |> == clojure's ->>
 articleDecoder : Decoder Article                    
@@ -92,6 +98,8 @@ articleDecoder =
         |> decodeApply versionsDecoder
         |> decodeApply versionDecoder
         |> decodeApply created_atDecoder
+        |> decodeApply hiddenDecoder
+        |> decodeApply unlistedDecoder
 
 type alias Title =
     { title : String
diff --git a/elm-frontti/src/Main.elm b/elm-frontti/src/Main.elm
index 4dc3561..8b647ce 100644
--- a/elm-frontti/src/Main.elm
+++ b/elm-frontti/src/Main.elm
@@ -62,8 +62,7 @@ main =
 subscriptions : Model -> Sub Msg
 subscriptions _ = Sub.batch 
                   [ tags ReceivedTag
-                  , aceStateUpdate AceStateUpdate
-                  , fromLocalStorage PostFromLocalStorage]
+                  , aceStateUpdate AceStateUpdate]
 
 initialModel url key viewstate = Model viewstate Nothing False False [] Nothing LoggedOut key url Nothing Time.utc Nothing
     
@@ -100,11 +99,6 @@ viewStatePerUrl url =
                                                    , getSettings
                                                    , getTitles
                                                    , loadTaggedPosts tags_])
-        RouteParser.NewPost ->
-            (PostEditor, [ getSettings
-                         , getTitles
-                         , getSession
-                         , loadPostFromLocalStorage ()])
 
         RouteParser.PostVersion post_id version_id -> (Loading, [ getSession
                                                                 , getSettings
@@ -130,9 +124,10 @@ port alert : String -> Cmd msg
 port tags : (String -> msg) -> Sub msg
 port aceStateUpdate : (String -> msg) -> Sub msg
 
-port savePostToLocalStorage: String -> Cmd msg
-port loadPostFromLocalStorage: () -> Cmd msg                         
-port fromLocalStorage: (String -> msg) -> Sub msg
+toggleHidden article =
+    { article | hidden = not article.hidden}
+toggleUnlisted article =
+    { article | unlisted = not article.unlisted}                      
 
 update : Msg -> Model -> (Model, Cmd Msg)
 update msg model =
@@ -203,11 +198,7 @@ update msg model =
                 Ok user ->
                     if model.view_state == PostEditor then
                         ({ model | loginState = LoggedIn user 
-                         , postEditorSettings = Just (PostEditorSettings
-                                                          (Maybe.withDefault 
-                                                               (Article.Article (C.Creator user.username user.nickname user.img_location) [""] "" Nothing "New post" Nothing (Just []) Nothing Nothing)
-                                                               model.postFromLocalStorage)
-                                                          "" False)}
+                         , postEditorSettings = Nothing}
                         , Cmd.none)
                     else 
                         ({model | loginState = LoggedIn user}, Cmd.none)
@@ -264,7 +255,7 @@ update msg model =
                         article = { old_article | tags = tag :: settings.article.tags} in
                     ({ model | postEditorSettings = Just
                            { settings | article = article}}
-                    , savePostToLocalStorage (Json.Encode.encode 0 (Article.encode article)))
+                    , Cmd.none)
                 Nothing -> (model, alert "ReceivedTag called even though postEditorSettings is nil")
         DropTag tag ->
             case model.postEditorSettings of
@@ -273,7 +264,7 @@ update msg model =
                         article = { old_article | tags = List.filter ((/=) settings.selected_tag) old_article.tags} in
                     ({ model | postEditorSettings = Just
                            { settings | article = article}}
-                    , savePostToLocalStorage (Json.Encode.encode 0 (Article.encode article)))
+                    , Cmd.none)
                 Nothing -> (model, alert "DropTag called even though postEditorSettings is nil")
         HttpIgnoreResponse result ->
             (model, Cmd.none)
@@ -294,7 +285,7 @@ update msg model =
                     ({ model | postEditorSettings = Just
                            { settings | article =
                                  { article | content = content}}}
-                    , savePostToLocalStorage (Json.Encode.encode 0 (Article.encode article)))
+                    , Cmd.none)
                 Nothing -> (model, alert "AceStateUpdate called even though postEditorSettings is nil")
                     
         ChangeTitle new_title ->
@@ -304,7 +295,7 @@ update msg model =
                     ({ model | postEditorSettings = Just
                            { settings | article =
                                  { article | title = new_title}}}
-                    , savePostToLocalStorage (Json.Encode.encode 0 (Article.encode article)))
+                    , Cmd.none)
                 Nothing -> (model, alert "ChangeTitle called even though postEditorSettings is nil")            
         HttpManagerGetListOfImages _ -> (model, getListOfImages True)                                  
         GetListOfImages -> ( { model | showImageModal = True }
@@ -416,27 +407,29 @@ update msg model =
                     , Cmd.none)
                 Err err ->
                     (model , alert ("Error loading post version " ++ Debug.toString err))
-        PostFromLocalStorage post_json ->
-            case (Decode.decodeString Article.articleDecoder post_json) of
-                Ok saved_article ->
-                    ({ model | postFromLocalStorage = Just saved_article} 
-                    , Cmd.none)
-                Err err ->
+        GenNewPost ->
+            ( model
+            , generateNewPost)
+        NewPostGenerated new_post_id ->
+            case new_post_id of
+                Ok id -> 
                     ( model
-                    , alert ("json decoding failed" ++ Debug.toString err))
-        ClearLocalStorage ->
-            case model.loginState of
-                LoggedIn user ->
-                    ({ model | postEditorSettings = Just (PostEditorSettings
-                                                              (Maybe.withDefault 
-                                                                   (Article.Article (C.Creator user.username user.nickname user.img_location) [""] "" Nothing "New post" Nothing (Just []) Nothing Nothing)
-                                                                   model.postFromLocalStorage)
-                                                              "" False)}
-                    , clearPostFromLS ())
-                _ -> (model, Cmd.none)
-            
-            
-                  
+                    , Cmd.batch
+                        [ ( Nav.pushUrl model.key ("/blog/post/edit/" ++ String.fromInt id))
+                        , getPostEditorData id])
+                Err error ->
+                    ( model
+                    , alert ("ERROR: " ++ (Debug.toString error)))
+        ToggleArticleHidden ->
+            ({ model | postEditorSettings = Maybe.map (\settings ->
+                                                           {settings | article = toggleHidden settings.article})
+                   model.postEditorSettings}
+            , Cmd.none)
+        ToggleArticleUnlisted ->
+            ({ model | postEditorSettings = Maybe.map (\settings ->
+                                                           {settings | article = toggleUnlisted settings.article})
+                   model.postEditorSettings}
+            , Cmd.none)
             
 doGoHome_ model other_cmds =
     (model, Cmd.batch (List.append [ getSettings
diff --git a/elm-frontti/src/Message.elm b/elm-frontti/src/Message.elm
index 1dfee98..81a2c0b 100644
--- a/elm-frontti/src/Message.elm
+++ b/elm-frontti/src/Message.elm
@@ -119,8 +119,10 @@ type Msg
   | GotTaggedPosts  (Result Http.Error (List Article.Article))
   | ToggleArticlePreview
   | GotOldPost (Result Http.Error Article.Article)
-  | PostFromLocalStorage String
-  | ClearLocalStorage 
+  | GenNewPost 
+  | NewPostGenerated (Result Http.Error Int)
+  | ToggleArticleUnlisted
+  | ToggleArticleHidden
   
 
 
diff --git a/elm-frontti/src/PostEditor.elm b/elm-frontti/src/PostEditor.elm
index 2364142..af3f1d3 100644
--- a/elm-frontti/src/PostEditor.elm
+++ b/elm-frontti/src/PostEditor.elm
@@ -36,7 +36,7 @@ hijack msg =
 
 optionize tag = option [value tag] [text tag]
 
-tagView post selectedTag = div [class "tagview"]
+tagView post selectedTag = div [class "tagview editor-grouper"]
                            [ select [ multiple True
                                     , class "tag-select"
                                     , id "tag-select"
@@ -48,12 +48,6 @@ tagView post selectedTag = div [class "tagview"]
                            , murja_button [ onClick (DropTag selectedTag)
                                           , attribute "data-testid" "remove-tag"]
                                [text "Remove selected tag"]]
-
-third_column = div [class "tagview" ]
-               [ murja_button [ onClick ClearLocalStorage
-                              , attribute "data-testid" "clear-editor" ]
-                     [ text "Clear post in the editor" ] ]
-
 editor params =
     node "ace-editor"
     (  params
@@ -66,35 +60,46 @@ filesDecoder =
   D.at ["target","files"] (D.list File.decoder)
       
 postEditor post tag showImageModal loadedImages draggingImages editorSettings app_settings tz loginState
-    = [ div [ id "editor-buttons"]
-            [ input [ name "title"
-                    , id "editor-post-title"
-                    , value post.title
-                    , onInput ChangeTitle] []
-            , murja_button [ id "editor-post-save"
-                           , onClick (SavePost post) ] [text "Save version"]
-            , label [ for "file-pictures-input"
-                    , class "murja-button"] [ text "Add pictures from device"]
-            , input [ type_ "file"
-                    , multiple False
-                    , style "display" "none"
-                    , id "file-pictures-input"
-                    , on "change" (D.map GotInputFiles filesDecoder)] []
-            , murja_button [ id "image-insert-btn"
-                           , onClick GetListOfImages]
-                  [text "Insert image"]
-            , label [for "show-preview-cb"]
-                [text "Show article preview"]
-            , input [ type_ "checkbox"
-                    , id "show-preview-cb"
-                    , checked editorSettings.show_preview
-                    , onClick ToggleArticlePreview] []]
-            
-      , tagView post tag
-      , third_column
+    = [ div [ class "editor-top" ]
+            [ div [ id "editor-buttons"
+                  , class "editor-grouper"]
+                  [ input [ name "title"
+                          , id "editor-post-title"
+                          , value post.title
+                          , onInput ChangeTitle] []
+                  , murja_button [ id "editor-post-save"
+                                 , onClick (SavePost post) ] [text "Save version"]
+                  , label [ for "file-pictures-input"
+                          , class "murja-button"] [ text "Add pictures from device"]
+                  , input [ type_ "file"
+                          , multiple False
+                          , style "display" "none"
+                          , id "file-pictures-input"
+                          , on "change" (D.map GotInputFiles filesDecoder)] []
+                  , murja_button [ id "image-insert-btn"
+                                 , onClick GetListOfImages]
+                        [text "Insert image"]]
+            , tagView post tag
+            , div [ class "editor-grouper" ]
+                [ label [ for "hidden"]
+                      [ text "Hidden article"]
+                , input [ type_ "checkbox"
+                        , id "hidden"
+                        , checked post.hidden
+                        , onClick ToggleArticleHidden ] []
+                , label [ for "unlisted"]
+                    [ text "Unlisted article"]
+                , input [ type_ "checkbox"
+                        , id "unlisted"
+                        , checked post.unlisted
+                        , onClick ToggleArticleUnlisted] []
+                , label [for "show-preview-cb"]
+                    [text "Show article preview"]
+                , input [ type_ "checkbox"
+                        , id "show-preview-cb"
+                        , checked editorSettings.show_preview
+                        , onClick ToggleArticlePreview] []]]
       , if showImageModal then imageSelector loadedImages else div [] []
-      , div [ attribute "data-testid" "article-id" ] [ text ("Article: " ++ (Maybe.withDefault "No id" (Maybe.map String.fromInt post.id)))]
-
       , if editorSettings.show_preview then
             case loginState of
                 LoggedIn user ->
diff --git a/elm-frontti/src/RouteParser.elm b/elm-frontti/src/RouteParser.elm
index f60d76e..0a0dbb4 100644
--- a/elm-frontti/src/RouteParser.elm
+++ b/elm-frontti/src/RouteParser.elm
@@ -7,7 +7,6 @@ import String exposing (fromInt)
 type Route
     = Page Int
     | Post Int
-    | NewPost 
     | PostAdmin
     | MediaManager
     | PostEditor Int
@@ -25,7 +24,6 @@ routeParser =
         , map Post (s "blog" </> (s "post" </> int))
         , map PostEditor (s "blog" </> (s "post" </> (s "edit" </> int)))
         , map MediaManager (s "blog" </> (s "mediamanager"))
-        , map NewPost (s "blog" </> (s "new_post"))
         , map TaggedPosts (s "blog" </> (s "tags" </> string))
         , map PostAdmin (s "blog" </> (s "postadmin"))]
 
diff --git a/elm-frontti/src/Topbar.elm b/elm-frontti/src/Topbar.elm
index fc70da6..af39075 100644
--- a/elm-frontti/src/Topbar.elm
+++ b/elm-frontti/src/Topbar.elm
@@ -18,8 +18,13 @@ topbar state =
         LoggedIn user ->                               
             div [class "left-sidebar"] [ span [] [text ("Welcome, " ++ user.nickname)]
                                        , User.user_avatar user
-                                       , ul [] [ li [] [ murja_button [ onClick GoHome, attribute "data-testid" "home"] [text "Home"]]
-                                               , li [] [ murja_button [ onClick (PushUrl "/blog/postadmin"), attribute "data-testid" "manage-posts-btn" ] [text "Manage posts"]]
-                                               , li [] [ murja_button [ onClick (PushUrl "/blog/mediamanager")] [text "Manage media"]]
-                                               , li [] [ murja_button [ onClick (PushUrl "/blog/new_post"), attribute "data-testid" "new-post-btn" ] [text "New post!"]]]]
+                                       , ul [] [ li [] [ murja_button [ onClick GoHome, attribute "data-testid" "home"]
+                                                             [text "Home"]]
+                                               , li [] [ murja_button [ onClick (PushUrl "/blog/postadmin"), attribute "data-testid" "manage-posts-btn" ]
+                                                             [text "Manage posts"]]
+                                               , li [] [ murja_button [ onClick (PushUrl "/blog/mediamanager")]
+                                                             [text "Manage media"]]
+                                               , li [] [ murja_button [ onClick GenNewPost
+                                                                      , attribute "data-testid" "new-post-btn" ]
+                                                             [text "New post!"]]]]
         _ -> div [] []
diff --git a/playwright-tests/tests/basic-tests.spec.ts b/playwright-tests/tests/basic-tests.spec.ts
index 9a01b8f..3401a17 100644
--- a/playwright-tests/tests/basic-tests.spec.ts
+++ b/playwright-tests/tests/basic-tests.spec.ts
@@ -18,10 +18,8 @@ const password = 'p4ssw0rd';
 
 async function postPost(page, title, post, tag, append_img = false) {
     await page.getByTestId('new-post-btn').click();
-    await page.getByTestId('clear-editor').click();
 
     console.log(`Posting post with title ${title}, tag ${tag}`);
-    await expect(page.getByTestId('article-id')).toContainText('Article: No id');
     
     await page.locator('#editor-post-title').fill(title);
     
@@ -70,6 +68,9 @@ async function postPost(page, title, post, tag, append_img = false) {
 	await expect(page.locator('#editor-post-content')).toContainText('<img');
     }
 
+    
+    await page.locator("#hidden").setChecked(false);
+
     // save the post
 
     await page.locator('#editor-post-save').click();
@@ -119,13 +120,14 @@ test('basic testing', async ({ page, browser }) => {
     await expect(page.locator('.post')).toBeVisible();
     await expect(page.locator('.post')).toContainText(post);
     await expect(page.locator('.tag')).toHaveText(tag);
-    /*
+
     // edit the post
     for(let x = 0; x < 10; x++) {
-	console.log('x: ' + x);
-	await page.getByTestId('edit-post-btn').click();
-
-	await expect(page.getByTestId('article-id')).not.toContainText('Article: No id');
+	const hidden = x % 2 == 0;
+	await page.goto('http://localhost:3010/blog/postadmin');
+	await expect(page.getByTestId('manager-edit-post-btn')).toBeVisible();
+    
+	await page.getByTestId('manager-edit-post-btn').click();
 
 	await expect(page.locator('#editor-post-content')).toBeVisible();
 
@@ -135,24 +137,27 @@ test('basic testing', async ({ page, browser }) => {
 	    console.log('success');
 	});
 
+	await page.locator("#hidden").setChecked(hidden);
+
 	await page.locator('#editor-post-save').click();
 	await page.goto('http://localhost:3010');
 
-	await expect(page.locator('.post')).toContainText("edited article");
-	await expect(page.locator('.post')).not.toContainText(post);
+	if (hidden) {
+	    await expect(page.locator('.post')).toBeHidden()
+	} else {
+	    await expect(page.locator('.post')).toContainText("edited article");
+	    await expect(page.locator('.post')).not.toContainText(post);
+	}
     }
 
-    await expect(page.locator('.meta')).toContainText('1, 2, 3, 4, 5, 6, 7, 8, 9, 10');*/
+    await expect(page.locator('.meta')).toContainText('2, 4, 6, 8, 10');
 
     // hide the post
 
     await page.getByTestId('edit-post-btn').click();
     
     await expect(page.locator('#editor-post-content')).toBeVisible();
-
-    tag = 'hidden';
-    await page.locator('#new-tag-btn').click();
-    await page.locator('#tag-select').selectOption('hidden');
+    await page.locator("#hidden").setChecked(false);
     await page.locator('#editor-post-save').click();
     await page.goto('http://localhost:3010');
 
@@ -163,9 +168,7 @@ test('basic testing', async ({ page, browser }) => {
     await expect(page.getByTestId('manager-edit-post-btn')).toBeVisible();
     
     await page.getByTestId('manager-edit-post-btn').click();
-    await page.locator('#tag-select').selectOption('hidden');
-
-    await page.getByTestId('remove-tag').click();
+    await page.locator("#hidden").setChecked(false);
     await page.locator('#editor-post-title').fill('Latest test post');
     
     await page.locator('#editor-post-save').click();
@@ -184,12 +187,12 @@ test('basic testing', async ({ page, browser }) => {
 
     const new_ctx = await browser.newContext();
     // Create a new page inside context.
-    /* const anon_page = await new_ctx.newPage();
+    const anon_page = await new_ctx.newPage();
     await anon_page.goto('http://localhost:3010');
 
-    await expect(anon_page.locator('.meta')).toContainText('1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13'); 
-
-    await anon_page.close(); */
+    await expect(anon_page.locator('.meta')).toContainText('2, 4, 6, 8, 10, 12, 13, 14');
+    
+    await anon_page.close(); 
     // make sure editor saves 
     await page.getByText('Edit this post').click();
 
@@ -213,7 +216,7 @@ test('basic testing', async ({ page, browser }) => {
     // make sure the basic post view opens
     await page.getByRole('link', { name: 'really badly edited test post' }).click();
     // await expect(page.getByText('LOADING')).toBeHidden();
-    await expect(page.getByText('jeejee')).toBeVisible();
+    await expect(page.getByText('jeejee')).toBeVisible(); 
     await expect(page.getByText('random content')).toBeHidden();
 
     // test images
diff --git a/resources/css/murja.css b/resources/css/murja.css
index ea04e21..6e5bac7 100644
--- a/resources/css/murja.css
+++ b/resources/css/murja.css
@@ -6,6 +6,17 @@ html, body {
     flex: 1 1;	    
 }
 
+.editor-grouper {
+    
+}
+
+.editor-top {
+    flex: auto;
+    flex-direction: row;
+    display: flex;
+    justify-content: space-around;
+}
+
 .tagview {
     display: table;
 }
diff --git a/resources/js/murja-helper.js b/resources/js/murja-helper.js
index 5b9660d..902cc54 100644
--- a/resources/js/murja-helper.js
+++ b/resources/js/murja-helper.js
@@ -33,7 +33,7 @@ app.ports.addImgToAce.subscribe(img_id => {
 	editor.insert('<img src="/api/pictures/' + img_id +'" />');
 
     } else alert("Didn't find ace editor");
-})
+});
 
 Object.defineProperty(HTMLElement.prototype, "dangerouslySetInnerHTML", {
     get () {
@@ -42,21 +42,4 @@ Object.defineProperty(HTMLElement.prototype, "dangerouslySetInnerHTML", {
     set (value) {
         this.innerHTML = value
     }
-})
-
-app.ports.savePostToLocalStorage.subscribe( v => {
-    localStorage.setItem("post", v)
-});
-
-app.ports.loadPostFromLocalStorage.subscribe( () => {
-    const post = localStorage.getItem("post");
-    if (post) 
-	app.ports.fromLocalStorage.send(post);
-});
-
-app.ports.clearPostFromLS.subscribe( () => {
-    if (window.confirm("Are you sure to clear the editor?")) {
-	localStorage.removeItem("post");
-	location.reload();
-    }
 });