diff of ede920d982a21cf320dd825bf2f1b7de60ca55bd

ede920d982a21cf320dd825bf2f1b7de60ca55bd
diff --git a/aggressive-murja.asd b/aggressive-murja.asd
index d454b01..7f7c47b 100644
--- a/aggressive-murja.asd
+++ b/aggressive-murja.asd
@@ -21,7 +21,11 @@
 	       "xml-emitter"
 	       "drakma"
 	       "xmls"
-	       "cl-date-time-parser")
+	       ;; works in cl universal time (epoch at 1900)
+	       "cl-date-time-parser"
+	       ;; transforms those to unix time
+	       "cl-epoch"
+	       )
   :description "A rewrite of the <a href=\"https://github.com/feuery/murja-blog/\">murja blogging engine</a> in lisp"
   :components ((:module "src"
 		:components
diff --git a/elm-frontti/elm.json b/elm-frontti/elm.json
index f1094e1..d6285c7 100644
--- a/elm-frontti/elm.json
+++ b/elm-frontti/elm.json
@@ -22,7 +22,8 @@
             "elm-community/dict-extra": "2.4.0",
             "elm-community/string-extra": "4.0.1",
             "mhoare/elm-stack": "3.1.2",
-            "waratuman/json-extra": "1.0.2"
+            "waratuman/json-extra": "1.0.2",
+            "waratuman/time-extra": "1.1.0"
         },
         "indirect": {
             "TSFoster/elm-bytes-extra": "1.3.0",
@@ -35,8 +36,7 @@
             "elm/virtual-dom": "1.0.2",
             "justinmimbs/timezone-data": "2.1.4",
             "rtfeldman/elm-hex": "1.0.0",
-            "rtfeldman/elm-iso8601-date-strings": "1.1.3",
-            "waratuman/time-extra": "1.1.0"
+            "rtfeldman/elm-iso8601-date-strings": "1.1.3"
         }
     },
     "test-dependencies": {
diff --git a/elm-frontti/src/FeedView.elm b/elm-frontti/src/FeedView.elm
index fd5115c..3713a7e 100644
--- a/elm-frontti/src/FeedView.elm
+++ b/elm-frontti/src/FeedView.elm
@@ -2,16 +2,26 @@ module FeedView exposing (..)
 
 import DateFormat as Df
 import Feeds exposing (NewFeed)
+import Time
 import Message exposing (..)
 import Html exposing (..)
 import Html.Attributes exposing (..)
 import Html.Events exposing (onInput, onClick)
 import Button exposing (murja_button)
 import UUID
+import Article_view exposing (formatDateTime)
 
 import Random
 
-feeds fs new_feed  =
+feed_item time_format zone item =
+    li [] [ h1 [] [ text item.title]
+          , h4 [] [ text (formatDateTime time_format zone item.pubdate)]
+          , div [ class "feed-author"] [ text <| "By " ++ item.author]
+          , div [ class "feed-item"
+                , dangerouslySetInnerHTML item.description] []]
+                        
+
+feeds settings zone fs new_feed  =
     let new_feed_state = Maybe.withDefault (NewFeed "" "") new_feed
     in 
     div [] 
@@ -19,7 +29,12 @@ feeds fs new_feed  =
               (List.map (\feed ->
                              li [ class "feed" ]
                              [ header [] [ text feed.name ]
-                             , a [ href feed.url ] [ text feed.url ]]) fs)
+                             , a [ href feed.url ] [ text feed.url ]
+                             , ul [ class "feed-items" ]
+                                 (feed.items
+                                 |> List.sortBy (Time.posixToMillis << .pubdate)
+                                 |> List.reverse
+                                 |> List.map (feed_item settings.time_format zone))]) fs)
         , h3 [] [ text "Add new feed?"]
         , div []
             [ label [ for "name" ] [ text "Feed name" ]
diff --git a/elm-frontti/src/Feeds.elm b/elm-frontti/src/Feeds.elm
index dd97bcb..7c70631 100644
--- a/elm-frontti/src/Feeds.elm
+++ b/elm-frontti/src/Feeds.elm
@@ -5,17 +5,29 @@ import Json.Encode.Extra exposing (..)
 import Json.Decode as Decode exposing (Decoder, succeed)
 import Json.Decode.Pipeline exposing (required)
 import Json.Decode.Extra as Extra
+import Time
 
 import UUID exposing (UUID)
 
 import Article exposing (decodeApply)
 import Creator exposing (Creator)
 
+    
+type alias Item =
+    { id: UUID
+    , fetched: Time.Posix 
+    , title: String
+    , description: String 
+    , link: String
+    , author: String
+    , pubdate: Time.Posix} 
+
 type alias Feed =
     { id: UUID
     , name: String 
     , url: String
-    , creator: Creator}
+    , creator: Creator
+    , items: List Item}
 
 type alias NewFeed =
     { name: String 
@@ -26,6 +38,27 @@ urlDecoder = Decode.field "url" Decode.string
 nameDecoder = (Decode.field "name" Decode.string)
 idDecoder = (Decode.field "id" UUID.jsonDecoder)
 
+-- itemDecoder
+fetchedDecoder = Decode.field "fetched" Extra.iso8601
+titleDecoder = Decode.field "title" Decode.string
+descriptionDecoder = Decode.field "description" Decode.string
+linkDecoder = Decode.field "link" Decode.string
+authorDecoder = Decode.field "author" Decode.string
+pubdateDecoder = Decode.field "pubdate" Extra.iso8601
+
+itemDecoder =
+    Decode.succeed Item
+        |> decodeApply idDecoder
+        |> decodeApply fetchedDecoder
+        |> decodeApply titleDecoder
+        |> decodeApply descriptionDecoder
+        |> decodeApply linkDecoder
+        |> decodeApply authorDecoder
+        |> decodeApply pubdateDecoder
+
+itemsDecoder =
+    Decode.field "items" (Decode.list itemDecoder)
+           
 feedDecoder: Decoder Feed
 feedDecoder =
     Decode.succeed Feed
@@ -33,6 +66,7 @@ feedDecoder =
         |> decodeApply nameDecoder
         |> decodeApply urlDecoder
         |> decodeApply creatorDecoder
+        |> decodeApply itemsDecoder
 
 newFeedDecoder: Decoder NewFeed
 newFeedDecoder =
diff --git a/elm-frontti/src/Main.elm b/elm-frontti/src/Main.elm
index 475ed4a..406c3c5 100644
--- a/elm-frontti/src/Main.elm
+++ b/elm-frontti/src/Main.elm
@@ -693,7 +693,7 @@ view model =
                                                    Nothing -> [ div [] [ text "No post loaded" ]]
                                            MediaList -> [ medialist model.loadedImages model.medialist_state ]
                                            SettingsEditor -> [ SettingsEditor.editor settings]
-                                           Feeds feeds -> [ FeedView.feeds feeds model.new_feed ])
+                                           Feeds feeds -> [ FeedView.feeds settings model.zone feeds model.new_feed ])
                         , div [id "sidebar"] [ User.loginView model.loginState
                                              , (sidebarHistory model.titles )
                                              , (case model.view_state of
diff --git a/resources/css/murja.css b/resources/css/murja.css
index c3e3c10..43237f5 100644
--- a/resources/css/murja.css
+++ b/resources/css/murja.css
@@ -299,6 +299,23 @@ header {
     display: block;
 }
 
+.feed-items {
+    height: 500px;
+    overflow-y: scroll;
+}
+
+.feed-items li {
+    list-style: none;
+}
+
+.feed-author {
+    margin-left: 1em;
+}
+
+.feed-item {
+    margin-left: 2em;
+}
+
 @media only screen and (max-device-width:480px)
 {
     body {
diff --git a/resources/sql/reader-fns.sql b/resources/sql/reader-fns.sql
index 5bd6af1..7c058e1 100644
--- a/resources/sql/reader-fns.sql
+++ b/resources/sql/reader-fns.sql
@@ -6,10 +6,13 @@ SELECT fs.id, fs.name, fs.url,
 			  'nickname',
 			  u.Nickname,
 			  'img_location',
-			  u.Img_location) as "creator"
+			  u.Img_location) as "creator",
+       json_agg(row_to_json(fi.*)) as "items"
 FROM blog.feed_subscription fs
 JOIN blog.Users u ON u.ID = fs.owner
-WHERE owner = $1;
+JOIN blog.feed_item fi ON fs.id = fi.feed
+WHERE owner = $1
+GROUP BY fs.id, u.username, u.nickname, u.img_location;
 
 -- name: get-all-feeds 
 -- returns: :array-hash 
diff --git a/src/routes/rss-reader-routes.lisp b/src/routes/rss-reader-routes.lisp
index 25ce0b7..f316875 100644
--- a/src/routes/rss-reader-routes.lisp
+++ b/src/routes/rss-reader-routes.lisp
@@ -32,7 +32,8 @@
     ""))
 
 ;; This will be called by cron/curl
-(defroute update-feeds-rotue ("/api/rss/update" :method :get) ()
+(defroute update-feeds-rotue ("/api/rss/update" :method :get
+						:decorators (@transaction)) ()
   (update-feeds)
   (setf (hunchentoot:return-code*) 204)
     "")
diff --git a/src/rss/reader-db.lisp b/src/rss/reader-db.lisp
index 39b4d7b..650dd17 100644
--- a/src/rss/reader-db.lisp
+++ b/src/rss/reader-db.lisp
@@ -1,7 +1,7 @@
 (defpackage murja.rss.reader-db
   (:use :cl :postmodern :binding-arrows)
   (:import-from :halisql :defqueries)
-  (:import-from :lisp-fixup :partial)
+  (:import-from :lisp-fixup :partial :compose)
   (:import-from :cl-date-time-parser :parse-date-time)
   (:export :get-user-feeds :subscribe-to-feed))
 	
@@ -16,7 +16,9 @@
 
 (defun get-user-feeds (user-id)
   (let ((feeds (coerce (get-user-feeds* user-id) 'list)))
-    (mapcar (partial #'parse "creator") feeds)))
+    (mapcar (compose (partial #'parse "items")
+		     (partial #'parse "creator"))
+	    feeds)))
 
 (defun subscribe-to-feed (feed-name feed-url owner)
   (insert-feed feed-name feed-url (gethash "id" owner)))
@@ -59,7 +61,11 @@ pipes it through trivial-utf-8:utf-8-bytes-to-string"
 		     (get-child-item-value "author" (xmls:node-children item))
 		     ;; author seems to be optional value, let's get title from <channel> if missing
 		     (get-child-item-value "title" (xmls:node-children channel))))
-	    (pubDate (parse-date-time (get-child-item-value "pubDate" (xmls:node-children item)))))
+	    (pubDate (cl-epoch:universal->unix-time
+		      (parse-date-time (get-child-item-value "pubDate" (xmls:node-children item))))))
+	(log:info "Parsed ~a as ~a"
+		  (get-child-item-value "pubDate" (xmls:node-children item))
+		  pubdate)
 	(insert-feed-item title link description author pubDate feed-id)))))
 
 (defun current-hour ()
@@ -73,9 +79,11 @@ pipes it through trivial-utf-8:utf-8-bytes-to-string"
 (defvar *last-updated* nil)
 
 (defun update-feeds ()
+  (setf *last-updated* nil)
   (when (or (not *last-updated*)
 	    ;; hourly rate limit
 	    (> (- (current-hour) *last-updated*) 1))
+    (log:info "Updating all feeds")
     (dolist (feed (coerce (get-all-feeds) 'list))
       (update-feed feed))