fix endorsement ui, refactor code

fix endorsement ui, refactor code

--- src/cljs/salava/badge/ui/endorsement.cljs
+++ src/cljs/salava/badge/ui/endorsement.cljs
@@ -1,6 +1,6 @@
 (ns salava.badge.ui.endorsement
   (:require [salava.core.i18n :refer [t]]
-            [reagent.core :refer [atom cursor create-class]]
+            [reagent.core :refer [atom cursor create-class dom-node]]
             [salava.core.time :refer [unix-time date-from-unix-time]]
             [salava.core.ui.ajax-utils :as ajax]
             [salava.core.ui.helper :refer [path-for private?]]
@@ -10,7 +10,8 @@
             [salava.user.ui.helper :refer [profile-picture]]
             [reagent-modals.modals :as m]
             [salava.core.ui.layout :as layout]
-            [salava.core.ui.error :as err]))
+            [salava.core.ui.error :as err]
+            [salava.core.ui.grid :as g]))

 (defn endorsement-row [endorsement]
@@ -41,19 +42,24 @@
                          :show-content "none"
                          :show-endorsement-status "block")))}))

-(defn user-badge-endorsement-content [badge-id]
+(defn user-badge-endorsement-content [badge-id badge-endorsements]
   (let [state (atom {:id badge-id})]
     (init-user-badge-endorsement state)
     (fn []
-      (let [endorsements (:user-badge-endorsements @state)]
+      (let [endorsements (:user-badge-endorsements @state)
+            badge-endorsements? (pos? (count @badge-endorsements))]
         (when (seq endorsements)
           [:div;.row {:id "badge-contents"}
            ;[:div.col-xs-12
            ;[:h4 {:style {:margin-bottom "20px"}} (t :badge/BadgeEndorsedBy)]
+           (if badge-endorsements? [:hr.line])
+           [:h4 {:style {:margin-bottom "20px"}} (t :badge/BadgeEndorsedByIndividuals)]
            (reduce (fn [r endorsement]
                      (let [{:keys [id user_badge_id image_file name content first_name last_name profile_picture endorser_id mtime]} endorsement]
                        (conj r [:div {:style {:margin-bottom "20px"}}
+
                                 [:h5
+                                 [:img {:src (profile-picture profile_picture) :style {:width "55px" :height "auto" :padding "7px"}}]
                                  [:a {:href "#"
                                       :on-click #(do (.preventDefault %) (mo/set-new-view [:user :profile] {:user-id endorser_id}))
                                       } (str first_name " " last_name)]
@@ -70,17 +76,21 @@
         user-badge-id (:id param)]
     (init-badge-endorsements endorsements badge-id)
     (fn []
-      [:div.row {:id "badge-contents"}
-       [:div.col-xs-12
-        [:h4 {:style {:margin-bottom "20px"}} (t :badge/BadgeEndorsedBy)]
-        (into [:div]
-              (for [endorsement @endorsements]
-                (endorsement-row endorsement)))]
-       (when user-badge-id
-         ;(if (seq @endorsements) [:hr])
-         [:div.col-xs-12
-          [user-badge-endorsement-content user-badge-id]]
-         )])))
+      (let [endorsement-count (count @endorsements)]
+        [:div.row {:id "badge-contents"}
+         (when (seq @endorsements)
+           [:div.col-xs-12
+            [:h4 {:style {:margin-bottom "20px"}} (t :badge/BadgeEndorsedByOrganizations)]
+            (into [:div]
+                  (for [endorsement @endorsements]
+                    (endorsement-row endorsement)))])
+         (when user-badge-id
+           ;(if (seq @endorsements) [:hr])
+           [:div.col-xs-12
+            [user-badge-endorsement-content user-badge-id endorsements]]
+           )]))))
+
+

 ;; User Badge Endorsements

@@ -92,7 +102,8 @@
                                 :initializing false
                                 :permission "success"
                                 :show "all"
-                                :search ""))
+                                :search ""
+                                :order "mtime"))
                 )}
     (fn [] (swap! state assoc :permission "error"))))

@@ -112,7 +123,6 @@
               :user_badge_id badge-id}
      :handler (fn [data]
                 (when (= "success" (:status data))
-                  (prn data)
                   ))}))

 (defn save-endorsement [state]
@@ -143,40 +153,117 @@
                   (when reload-fn (reload-fn state))))}))

+
+
+(def simplemde-toolbar (array "bold" "italic" "heading-3"
+                              "quote" "unordered-list" "ordered-list"
+                              "link" "horizontal-rule"
+                              "preview"))
+
+(defn init-editor [atom element-id]
+  (let [editor (js/SimpleMDE. (js-obj "element" (.getElementById js/document element-id)
+                                      "toolbar" simplemde-toolbar
+                                      "autosave" "true"
+                                      "forceSync" "true"))]
+
+    (js/setTimeout (fn [] (.value editor @atom)) 200) ;;delay for editor to load
+    (.codemirror.on editor "change" (fn [] (reset! atom (.value editor))))))
+
+(defn markdown-editor [value]
+  (create-class {:component-did-mount #(init-editor value (str "editor" (-> (session/get :user) :id)))
+                 :reagent-render (fn []
+                                   [:div.form-group
+                                    [:textarea {:class "form-control"
+                                                :id (str "editor" (-> (session/get :user) :id))
+                                                ;:value @atom
+                                                :on-change #(reset! value (.-target.value %))
+                                                }]]) }))
+
+(defn test-editor [value]
+  (let [instance (atom nil)]
+    (create-class {:component-did-mount (fn []
+                                          (let [editor (js/SimpleMDE. (js-obj "element" (.getElementById js/document (str "editor" (-> (session/get :user) :id)))
+                                                                              "toolbar" simplemde-toolbar
+                                                                              "autosave" "true"
+                                                                              "forceSync" "true"))]
+                                            (-> editor
+                                                .-codemirror
+                                                (.refresh)
+                                                #_(.setValue @value))
+                                            (.value editor @value)
+
+                                            (.codemirror.on editor "scroll" (fn [] (.value editor @value)))
+                                            (.codemirror.on editor "mousedown" (fn [] (.value editor @value)))
+                                            (.codemirror.on editor "change" (fn [] (reset!  value (.value editor))))
+                                            )
+
+                                          )
+                   :component-did-update (fn [this editor]
+                                           (prn "TEST"))
+                   :reagent-render (fn []
+                                     @value
+                                     [:div.form-group
+                                      [:textarea {:class "form-control"
+                                                  :id (str "editor" (-> (session/get :user) :id))
+                                                  :defaultValue @value
+                                                  :on-change #(reset! value (.-target.value %))
+                                                  }]])})))
+
 (defn process-text [s state]
   (let [text (-> js/document
                  (.getElementById (str "editor" (-> (session/get :user) :id)))
                  (.-innerHTML))
         endorsement-claim (str text (if (blank? text) "" "\n\n") "* " s)]
-    (prn text)
-    (reset! (cursor state [:endorsement-comment]) endorsement-claim)))
-
-(def simplemde-toolbar (array "bold" "italic" "heading-3"
-                              "quote" "unordered-list" "ordered-list"
-                              "link" "horizontal-rule"
-                              "preview"))
-
-(defn init-editor [atom element-id]
-  (let [editor (js/SimpleMDE. (js-obj "element" (.getElementById js/document element-id)
-                                      "toolbar" simplemde-toolbar
-                                      "autosave" "true"
-                                      "forceSync" "true"))]
-    (js/setTimeout (.refresh editor) 10)
-    (js/setTimeout (fn [] (.value editor @atom)) 500) ;;delay for editor to load
-    (.codemirror.on editor "change" (fn [] (reset! atom (.value editor))))
-    (.codemirror.on editor "mousedown" (fn [] (.value editor @atom)))))
-
-(defn markdown-editor [atom]
-  (create-class {:component-did-mount #(init-editor atom (str "editor" (-> (session/get :user) :id)))
-                 :reagent-render (fn []
-                                   [:div.form-group
-                                    [:textarea {:class "form-control"
-                                                :id (str "editor" (-> (session/get :user) :id))
-                                                :value @atom
-                                                :on-change #(reset! atom (.-target.value %))
-                                                }]])}))
+    ; (prn text)
+    (reset! (cursor state [:endorsement-comment]) endorsement-claim))
+  #_(init-editor (cursor state [:endorsement-comment] (-> (session/get :user) :id))))
+
+
+#_(defn editor [value-atom]
+    (let [cm (atom nil)]
+      (create-class
+        {:component-did-mount
+         (fn [this]
+           (let [el (dom-node this)
+                 editor (js/SimpleMDE.
+                          (clj->js
+                            {:toolbar simplemde-toolbar
+                             :autofocus    true
+                             :spellChecker true
+                             :placeholder  ""
+                             :forceSync    true
+                             :element      @cm
+                             :initialValue @value-atom
+                             :value        @value-atom
+                             }))]
+
+             (reset! cm editor)
+             (.codemirror.on editor "change" (fn [] (reset! value-atom (.value editor))))
+             #_(.on editor "change"
+                    (fn []
+                      (let [value (.value editor)]
+                        (when-not (= value @value-atom)
+                          (reset! value-atom value)))))
+             ))
+
+         :component-did-update
+         (fn [this old-argv]
+           (when-not (= @value-atom (.value cm))
+             (.setValue @cm @value-atom)
+             ;; reset the cursor to the end of the text, if the text was changed externally
+             (let [last-line (.lastLine @cm)
+                   last-ch (count (.getLine @cm last-line))]
+               (.setCursor @cm last-line last-ch))))
+
+         :reagent-render
+         (fn [_ _ _]
+           ;@atom
+           [:textarea {:id (str "editor" (-> (session/get :user) :id)) :defaultValue @value-atom}])})))
+
+

 (defn endorse-badge-content [state]
+
   (fn []
     [:div {:style {:display @(cursor state [:show-content])}}
      [:hr]
@@ -194,7 +281,6 @@
         [:a.list-group-item {:href "#" :on-click #(do
                                                     (.preventDefault %)
                                                     (process-text (t :badge/Endorsephrase1) state)
-                                                   (prn  (.getElementsByClassName js/document "CodeMirror"))
                                                     )} [:i.fa.fa-plus-circle](t :badge/Endorsephrase1)]
         [:a.list-group-item {:href "#" :on-click #(do
                                                     (.preventDefault %)
@@ -215,7 +301,7 @@

      [:div.form-group
       [:label {:for "claim"} (str (t :badge/Composeyourendorsement) ":") ]
-      [:div.editor [markdown-editor (cursor state [:endorsement-comment])]]
+      [:div.editor #_[editor (cursor state [:endorsement-comment])] [test-editor (cursor state [:endorsement-comment])]]
       #_[:textarea.form-control {:id "claim"
                                  :rows "10"
                                  :cols "50"
@@ -275,7 +361,7 @@
                            [:div.thumbnail
                             [:div.endorser.col-md-12
                              [profile-link-inline endorser_id first_name last_name profile_picture id]
-                             [:hr.border]
+                             [:hr.line]
                              ]
                             [:div.caption.row.flip
                              [:div.position-relative.badge-image.col-md-3
@@ -286,26 +372,26 @@
                              ]

                             [:div.caption.card-footer.text-center
-                             [:hr]
-                             #_[:button.btn.btn-primary {:href "#"
-                                                         :on-click #(do
-                                                                      (.preventDefault %)
-                                                                      (update-status id "accepted" user_badge_id state init-pending-endorsements)
-                                                                      )} (t :badge/Acceptendorsement)]
-                             #_[:button.btn.btn-warning.cancel {:href "#"
-                                                                :on-click #(do
-                                                                             (.preventDefault %)
-                                                                             (update-status id "declined" user_badge_id state init-pending-endorsements))} (t :badge/Declineendorsement)]
-                             [:ul.list-inline.buttons
-                              [:li {:style {:margin "10px"}} [:a.button {:href "#"
-                                                                         :on-click #(do
-                                                                                (.preventDefault %)
-                                                                                (update-status id "accepted" user_badge_id state init-pending-endorsements)
-                                                                                )} [:i.fa.fa-check] (t :badge/Acceptendorsement)]]
-                              [:li.cancel {:style {:margin "10px"}}[:a.button {:href "#"
-                                                                               :on-click #(do
-                                                                                (.preventDefault %)
-                                                                                (update-status id "declined" user_badge_id state init-pending-endorsements))} [:i.fa.fa-remove] (t :badge/Declineendorsement)]]]]]]]))) [:div.row] @(cursor state [:pending]))
+                             [:hr.line]
+                             [:button.btn.btn-primary {:href "#"
+                                                       :on-click #(do
+                                                                    (.preventDefault %)
+                                                                    (update-status id "accepted" user_badge_id state init-pending-endorsements)
+                                                                    )} (t :badge/Acceptendorsement)]
+                             [:button.btn.btn-warning.cancel {:href "#"
+                                                              :on-click #(do
+                                                                           (.preventDefault %)
+                                                                           (update-status id "declined" user_badge_id state init-pending-endorsements))} (t :badge/Declineendorsement)]
+                             #_[:ul.list-inline.buttons
+                                [:li {:style {:margin "10px"}} [:a.button {:href "#"
+                                                                           :on-click #(do
+                                                                                (.preventDefault %)
+                                                                                (update-status id "accepted" user_badge_id state init-pending-endorsements)
+                                                                                )} [:i.fa.fa-check] (t :badge/Acceptendorsement)]]
+                                [:li.cancel {:style {:margin "10px"}}[:a.button {:href "#"
+                                                                                :on-click #(do
+                                                                                (.preventDefault %)
+                                                                                (update-status id "declined" user_badge_id state init-pending-endorsements))} [:i.fa.fa-remove] (t :badge/Declineendorsement)]]]]]]]))) [:div.row] @(cursor state [:pending]))
        ])))

 (defn endorse-badge [badge-id]
@@ -372,7 +458,7 @@
                     ) [:div] @(cursor state [:user-badge-endorsements]))]]))))

 (defn profile [element-data]
-  (let [{:keys [id first_name last_name profile_picture status header]} element-data
+  (let [{:keys [id first_name last_name profile_picture status label]} element-data
         current-user (session/get-in [:user :id])]
     [:div.endorsement-profile.panel-default
      [:a {:href "#" :on-click #(mo/open-modal [:user :profile] {:user-id id})}
@@ -384,7 +470,7 @@
            :alt (str first_name " " last_name)}]]]
        [:div.col-md-8
         [:h4 (str first_name " " last_name)]
-        (when (= status "pending") [:p [:span.label.label-info (t :social/Pending)]])]]]]))
+        (when (= status "pending") [:p [:span.label.label-info label]])]]]]))

 (defn user-endorsement-content [params]
   (fn []
@@ -399,16 +485,24 @@
          [:div (if endorsee_id (t :badge/Manageendorsementtext1) (t :badge/Manageendorsementtext2))]
          [:hr.line]
          [:div.row
-          [:div.col-md-4.col-md-push-8 (when-not (and endorser_id (= "pending" status))
-                                         [:div.row
-                                          [:div.col-xs-12.delete-btn {:style {:margin "5px 0px" :cursor "pointer" :width "100%"}}
-                                           [:a {:style {:line-height "4"}
-                                                :on-click #(do
-                                                             (.preventDefault %)
-                                                             (delete-endorsement id user_badge_id nil nil))
-                                                :data-dismiss "modal"} [:i.fa.fa-trash] (t :badge/Deleteendorsement)]
-                                           ]])]
-          [:div.col-md-8.col-md-pull-4 [profile {:id (or endorsee_id endorser_id) :profile_picture profile_picture :first_name first_name :last_name last_name :status status}]]]
+          [:div.col-md-4.col-md-push-8  " "#_(when-not (and endorser_id (= "pending" status))
+                                               [:div.row
+                                                [:div.col-xs-12.delete-btn {:style {:margin "5px 0px" :cursor "pointer" :width "100%"}}
+                                                 [:a {:style {:line-height "4"}
+                                                      :on-click #(do
+                                                                   (.preventDefault %)
+                                                                   (delete-endorsement id user_badge_id nil nil))
+                                                      :data-dismiss "modal"} [:i.fa.fa-trash] (t :badge/Deleteendorsement)]
+                                                 ]])]
+          [:div.col-md-8.col-md-pull-4 [profile {:id (or endorsee_id endorser_id)
+                                                 :profile_picture profile_picture
+                                                 :first_name first_name
+                                                 :last_name last_name
+                                                 :status status
+                                                 :label (if endorser_id
+                                                          (t :badge/pendingreceived)
+                                                          (t :badge/pendinggiven)
+                                                          )}]]]

          (if endorsee_id
            [:div {:style {:margin-top "15px"}}
@@ -430,38 +524,47 @@
                                        :disabled (blank? @(cursor params [:endorsement :content]))
                                        :data-dismiss "modal"

-                                       } (t :core/Save)]]]
+                                       } (t :core/Save)]
+             [:button.btn.btn-warning.cancel {:data-dismiss "modal"} (t :core/Cancel)]
+             ;[:div.col-xs-12.delete-btn {:style {:margin "10px 0px 10px 0px" :cursor "pointer" :width "100%"}}
+             [:a.delete-btn.pull-right {:style {:line-height "4" :cursor "pointer"}
+                                        :on-click #(do
+                                                     (.preventDefault %)
+                                                     (delete-endorsement id user_badge_id nil nil))
+                                        :data-dismiss "modal"} [:i.fa.fa-trash] (t :badge/Deleteendorsement)]
+             ]]
+           ;]

            [:div {:style {:margin-top "15px"}}
             [:div {:dangerouslySetInnerHTML {:__html content}}]
             (when (= "pending" status)
               [:div.caption
                [:hr.line]
-               [:div.text-center
-                #_[:button.btn.btn-primary {:href "#"
-                                            :on-click #(do
-                                                         (.preventDefault %)
-                                                         (update-status id "accepted" user_badge_id state init-pending-endorsements)
-                                                         )
-                                            :data-dismiss "modal"}  (t :badge/Acceptendorsement)]
-                #_[:button.btn.btn-warning.cancel {:href "#"
-                                                   :on-click #(do
-                                                                (.preventDefault %)
-                                                                (update-status id "declined" user_badge_id state init-pending-endorsements))
-                                                   :data-dismiss "modal"} (t :badge/Declineendorsement)]
+               [:div.buttons
+                [:button.btn.btn-primary {:href "#"
+                                          :on-click #(do
+                                                       (.preventDefault %)
+                                                       (update-status id "accepted" user_badge_id state init-pending-endorsements)
+                                                       )
+                                          :data-dismiss "modal"}  (t :badge/Acceptendorsement)]
+                [:button.btn.btn-warning.cancel {:href "#"
+                                                 :on-click #(do
+                                                              (.preventDefault %)
+                                                              (update-status id "declined" user_badge_id state init-pending-endorsements))
+                                                 :data-dismiss "modal"} (t :badge/Declineendorsement)]
                 ;[:ul.list-inline.buttons
-                [:ul.list-inline.buttons
-                 [:li [:a.button {:href "#"
-                                  :on-click #(do
-                                               (.preventDefault %)
-                                               (update-status id "accepted" user_badge_id nil nil)
-                                               )
-                                  :data-dismiss "modal"}  [:i.fa.fa-check] (t :badge/Acceptendorsement)]]
-                 [:li.cancel [:a.button {:href "#"
-                                         :on-click #(do
-                                                      (.preventDefault %)
-                                                      (update-status id "declined" user_badge_id nil nil ))
-                                         :data-dismiss "modal"} [:i.fa.fa-remove] (t :badge/Declineendorsement)]]]]
+                #_[:ul.list-inline.buttons
+                   [:li [:a.button {:href "#"
+                                    :on-click #(do
+                                                 (.preventDefault %)
+                                                 (update-status id "accepted" user_badge_id nil nil)
+                                                 )
+                                    :data-dismiss "modal"}  [:i.fa.fa-check] (t :badge/Acceptendorsement)]]
+                   [:li.cancel [:a.button {:href "#"
+                                           :on-click #(do
+                                                        (.preventDefault %)
+                                                        (update-status id "declined" user_badge_id nil nil ))
+                                           :data-dismiss "modal"} [:i.fa.fa-remove] (t :badge/Declineendorsement)]]]]
                ]
               )
             ])]]])))
@@ -479,11 +582,26 @@
                                               (re-find (re-pattern (str "(?i)" @(cursor state [:search]))) (:first_name %))
                                               (re-find (re-pattern (str "(?i)" @(cursor state [:search]))) (:last_name %))
                                               (re-find (re-pattern (str "(?i)" @(cursor state [:search]))) (str (:first_name %) " " (:last_name %))))
-                                         endorsements))]
+                                         endorsements))
+        order (keyword  @(cursor state [:order]))
+        endorsements (case order
+                       (:mtime) (sort-by order > processed-endorsements)
+                       (:name) (sort-by (comp clojure.string/upper-case str order) processed-endorsements)
+                       (:user) (sort-by #(str (:first_name %) " " (:last_name %)) processed-endorsements)
+                       processed-endorsements
+                       )]
+
     [:div.panel
      [:div.panel-heading
       [:h3
-       (str (t :badge/Endorsements) ) [:span.badge {:style {:vertical-align "text-top"}}  (count processed-endorsements)]]]
+       (str (t :badge/Endorsements) ) #_[:span.badge {:style {:vertical-align "text-top"}}  (count processed-endorsements)]]
+      [:br]
+      [:div (case @(cursor state [:show])
+              "all" (t :badge/Allendorsementstext)
+              "given" (t :badge/Givenendorsementstext)
+              "received" (t :badge/Receivedendorsementstext)
+              (t :badge/Allendorsementstext))]
+      ]
      [:div.panel-body
       [:div.table  {:summary (t :badge/Endorsements)}
        (reduce (fn [r endorsement]
@@ -495,10 +613,17 @@
                                                         (mo/open-modal [:badge :userendorsement] (atom {:endorsement endorsement :state state}) {:hidden (fn [] (init-user-endorsements state))} ))}
                              [:div.col-md-4.col-md-push-8 [:small.pull-right [:i (date-from-unix-time (* 1000 mtime) "days")]]]
                              [:div.col-md-8.col-md-pull-4 [:div.media
-                                                           [:div.row
+                                                           [:div;.row
                                                             [:div.labels
-                                                             [:span.label.label-success (if endorser_id (t :badge/Endorsedyou) (t :badge/Youendorsed))]
-                                                             (if (= "pending" status) [:span.label.label-info (t :social/Pending)])]
+                                                             (if endorser_id
+                                                               [:span.label.label-success (t :badge/Endorsedyou)]
+                                                               [:span.label.label-primary (t :badge/Youendorsed)])
+                                                             (if (= "pending" status)
+                                                               [:span.label.label-info
+                                                                (if endorser_id
+                                                                  (t :badge/pendingreceived)
+                                                                  (t :badge/pendinggiven)
+                                                                  )])]
                                                             ]
                                                            [:div.media-left.media-top.list-item-body
                                                             [:img.main-img.media-object {:src (str "/" image_file)}]
@@ -510,27 +635,25 @@
                                                                                [:img.media-object.small-img {:src (profile-picture profile_picture)}]]
                                                               [:div.media-body
                                                                [:p endorser]]
-                                                              ]]]]]]]))) [:div] processed-endorsements)]]]))
+                                                              ]]]]]]]))) [:div] endorsements)]]]))
+(defn order-opts []
+  [{:value "mtime" :id "radio-date" :label (t :badge/bydate)}
+   {:value "name" :id "radio-name" :label (t :core/byname)}
+   {:value "user" :id "radio-issuer" :label (t :badge/byuser)}])

 (defn user-endorsements-content [state]
   [:div
    [m/modal-window]
    [:div#badge-stats
-    [:h1.uppercase-header (t :badge/Myendorsements)]
-    [:div {:style {:margin-bottom "15px"}} (t :badge/Endorsementpageinfo)]
+    #_[:h1.uppercase-header (t :badge/Myendorsements)]
+    #_[:div {:style {:margin-bottom "15px"}} (t :badge/Endorsementpageinfo)]

     (if (or (seq @(cursor state [:received]) ) (seq @(cursor state [:given])))
       [:div
        [:div.form-horizontal {:id "grid-filter"}
-        [:div.form-group
-         [:div.col-sm-12
-          [:input.form-control
-           {:type"text"
-            :placeholder (t :badge/Filterbybadgenameoruser)
-            :value @(cursor state [:search])
-            :on-change #(reset! (cursor state [:search]) (.-target.value %) )}]]
-         ]
+        [g/grid-search-field (t :core/Search ":")  "endorsementsearch" (t :badge/Filterbybadgenameoruser) :search state]
         [:div.form-group.wishlist-buttons
+         [:legend {:class "control-label col-sm-2"} (str (t :core/Show) ":")]
          [:div.col-md-10
           [:div.buttons
            [:button {:class (str "btn btn-default " (when (= "all" @(cursor state [:show])) "btn-active"))
@@ -544,7 +667,8 @@
            [:button {:class (str "btn btn-default " (when (= "given" @(cursor state [:show])) "btn-active"))
                      :id "btn-all"
                      :on-click #(do (swap! state assoc :show "given"))}
-            (t :badge/Iendorsed)]]] ]]
+            (t :badge/Iendorsed)]]] ]
+        [g/grid-radio-buttons (t :core/Order ":") "order" (order-opts) :order state]]

        (endorsements state)]
       [:div (t :badge/Youhavenoendorsements)])]])
@@ -554,7 +678,8 @@

 (defn handler [site-navi]
   (let [state (atom {:initializing true
-                     :permission "initial"})
+                     :permission "initial"
+                     :order :mtime})
         user (session/get :user)]
     (init-user-endorsements state)
     (fn []


remove usages of expensive (fail?)

remove usages of expensive (fail?)


continue to isolate/encapsulate join internal rep

--- dada2-core/src/test/clojure/org/dada2/test_join_model.clj
+++ dada2-core/src/test/clojure/org/dada2/test_join_model.clj
@@ -41,19 +41,21 @@
   "return seq of lhses indexed by i/fk"
   ((nth lhs-indeces (dec i)) fk))	;TODO - don't like dec here

-(defn- lhs-assoc [old-indeces fk-keys lhs]
+(defn- lhs-assoc [old-indeces lhs-fk-keys lhs]
   "associate a new lhs with existing index"
-  (let [[old-lhs-index & _] old-indeces
-	new-lhs-index (mapv (fn [lhs-index fk-key] (group lhs-index (fk-key lhs) lhs)) old-lhs-index fk-keys)
+  (let [new-lhs-index (mapv
+		       (fn [lhs-index lhs-fk-key] (group lhs-index (lhs-fk-key lhs) lhs))
+		       (first old-indeces)
+		       lhs-fk-keys)
 	new-indeces (assoc old-indeces 0 new-lhs-index)]
     new-indeces))

 ;;--------------------------------------------------------------------------------
 ;; this layer encapsulates access to rhs indeces

-(defn- rhses-get [rhs-indeces fk-keys lhs]
+(defn- rhses-get [rhs-indeces lhs-fk-keys lhs]
   "return a lazy seq of seqs of rhses indexed by their respective fks"
-  (map (fn [rhs-index fk-key] (rhs-index (fk-key lhs))) rhs-indeces fk-keys))
+  (map (fn [rhs-index lhs-fk-key] (rhs-index (lhs-fk-key lhs))) rhs-indeces lhs-fk-keys))

 (defn- rhs-assoc [old-indeces i fk rhs]
   "associate a new rhs with existing index"
@@ -68,16 +70,16 @@
 ;;--------------------------------------------------------------------------------
 ;; this layer encapsulates access to both lhs and rhs at the same time...

-(defn- derive-joins [lhs rhs-indeces keys join-fn]
+(defn- derive-joins [rhs-indeces lhs-fk-keys lhs join-fn]
   (map
    (fn [args] (apply join-fn lhs args))
-   (permute [[]] (rhses-get rhs-indeces keys lhs))))
-
-;;--------------------------------------------------------------------------------
-
-(defn- lhs-upsert [old-indeces keys lhs join-fn joined-model]
-  (let [new-indeces (lhs-assoc old-indeces keys lhs)
-	joins (derive-joins lhs (rest new-indeces) keys join-fn)]
+   (permute [[]] (rhses-get rhs-indeces lhs-fk-keys lhs))))
+
+;;--------------------------------------------------------------------------------
+
+(defn- lhs-upsert [old-indeces lhs-fk-keys lhs join-fn joined-model]
+  (let [new-indeces (lhs-assoc old-indeces lhs-fk-keys lhs)
+	joins (derive-joins (rest new-indeces) lhs-fk-keys lhs join-fn)]
     [new-indeces (fn [_] (on-upserts joined-model joins))]))

 ;;; TODO
@@ -85,15 +87,15 @@
 (defn- lhs-upserts [old-indeces i ks vs join-fn])
 (defn- lhs-deletes [old-indeces i ks vs join-fn])

-(defn- lhs-view [indeces i keys join-fn joined-model]
+(defn- lhs-view [indeces i lhs-fk-keys join-fn joined-model]
   (reify
    View
    ;; singleton changes
-   (on-upsert [_ upsertion] ((swap*! indeces lhs-upsert keys upsertion join-fn joined-model) []) nil) ;TODO - pass in views to notifier
-   (on-delete [_ deletion]  (swap! indeces lhs-delete keys deletion join-fn) nil)
+   (on-upsert [_ upsertion] ((swap*! indeces lhs-upsert lhs-fk-keys upsertion join-fn joined-model) []) nil) ;TODO - pass in views to notifier
+   (on-delete [_ deletion]  (swap! indeces lhs-delete lhs-fk-keys deletion join-fn) nil)
    ;; batch changes
-   (on-upserts [_ upsertions] (swap! indeces lhs-upserts keys upsertions join-fn) nil)
-   (on-deletes [_ deletions]  (swap! indeces lhs-deletes keys deletions join-fn) nil)))
+   (on-upserts [_ upsertions] (swap! indeces lhs-upserts lhs-fk-keys upsertions join-fn) nil)
+   (on-deletes [_ deletions]  (swap! indeces lhs-deletes lhs-fk-keys deletions join-fn) nil)))

 ;; TODO: return a pair - first is notification fn, second is new-indeces - use swap*! to apply

@@ -103,7 +105,7 @@
 	lhs-indeces (first old-indeces)
 	lhses (lhses-get lhs-indeces i fk)
 	rhs-indeces (rest new-indeces)
-	joins (mapcat (fn [lhs] (derive-joins lhs rhs-indeces lhs-fk-keys join-fn)) lhses)
+	joins (mapcat (fn [lhs] (derive-joins rhs-indeces lhs-fk-keys lhs join-fn)) lhses)
 	]
     [new-indeces (fn [_] (on-upserts joined-model joins))]
     ))


Making read-response-value safer by just using str when read-string throws an Exception

--- src/ipython_clojure/messaging/execute.clj
+++ src/ipython_clojure/messaging/execute.clj
@@ -15,9 +15,18 @@
   {:execution_count execution-count
    :code (get-in message [:content :code])})

+(defn safe-read-response-value
+  [{:keys [value] :as msg}]
+  (if-not (string? value)
+    msg
+    (try
+      (assoc msg :value (read-string value))
+      (catch Exception e
+        (assoc msg :value (str value))))))
+
 (defn prepare-resp
   [resp]
-  (->> resp (map repl/read-response-value) repl/combine-responses))
+  (->> resp (map safe-read-response-value) repl/combine-responses))

 (defn execute [repl-client request]
   (let [resp (repl/message repl-client {:op "eval" :code (get-in request [:content :code])})


Cleanup inspection logic.

Cleanup inspection logic.

--- src/main/clojure/clara/tools/inspect.clj
+++ src/main/clojure/clara/tools/inspect.clj
@@ -74,15 +74,26 @@
   "Prints a human-readable explanation of the facts and conditions that created the Rete token."
   ([token] (explain-token token ""))
   ([token prefix]
-      (doseq [[fact {:keys [type constraints]}] (:matches token) ]
-        (println prefix fact)
-        (println prefix "  is a" type)
-        (println prefix "  where" constraints))))
+     (doseq [[fact condition] (:matches token)]
+       (if (:from condition)
+         ;; Explain why the accumulator matched.
+         (let [{:keys [accumulator from]} condition]
+           (println prefix fact)
+           (println prefix "  accumulated with" accumulator)
+           (println prefix "  from" (:type from))
+           (println prefix "  where" (:constraints from)))
+
+         ;; Explain why a condition matched.
+         (let [{:keys [type constraints]} condition]
+           (println prefix fact)
+           (println prefix "  is a" type)
+           (println prefix "  where" constraints))))))

 (defn explain-activations
   "Prints a human-friend explanation of why rules and queries matched in the given session."
   [session]
-  (doseq [[rule tokens] (:rule-matches (inspect session))]
+  (doseq [[rule tokens] (:rule-matches (inspect session))
+          :when (seq tokens)]
     (println "rule"  (or (:name rule) (str "<" (:lhs rule) ">")))
     (println "  executed")
     (println "   " (:rhs rule))
@@ -91,7 +102,8 @@
       (explain-token token "    "))
     (println))

-  (doseq [[rule tokens] (:query-matches (inspect session))]
+  (doseq [[rule tokens] (:query-matches (inspect session))
+          :when (seq tokens)]
     (println "query"  (or (:name rule) (str "<" (:lhs rule) ">")))
     (doseq [token tokens]
       (println "  qualified because")


add el jueves 18 rule for ES

add el jueves 18 rule for ES

--- resources/duckling/rules/es.time.clj
+++ resources/duckling/rules/es.time.clj
@@ -201,6 +201,10 @@

   "<named-month> <day-of-month>" ; mayo 5 in Latin America mostly (this rule removes latency)
   [{:form :month} (integer 1 31)]
+  (intersect %1 (day-of-month (:value %2)))
+
+  "<day-of-week> <day-of-month>" ; vendredi 13
+  [{:form :day-of-week} (integer 1 31)]
   (intersect %1 (day-of-month (:value %2)))

   ;; hours and minutes (absolute time)


Re-factoring so that make-handlers and make-routes are separate. This is to prepare for the change to re-using the same Liberator code for multiple routes

--- src/kixi/hecuba/web.clj
+++ src/kixi/hecuba/web.clj
@@ -34,7 +34,12 @@
 (defn readings [req]
   {:status 200 :body (slurp (io/resource "reading.html"))})

-(defn make-routes [producer-config querier commander project projects]
+(defn create-handlers [commander querier]
+  (let [project (project-resource querier)]
+    {:project project
+     :projects (projects-resource querier commander project)}))
+
+(defn make-routes [producer-config querier commander handlers]
   ["/"
    [["" (->Redirect 307 tables)]
     ["index.html" index]
@@ -47,11 +52,10 @@
     (kixi.hecuba.web.property/create-routes querier commander)
     (kixi.hecuba.web.messages/create-routes querier commander)

-
     ;; Projects
-    [["project/" :id] project]
-    ["projects/" projects]
-    ["projects" (->Redirect 307 projects)]
+    [["project/" :id] (:project handlers)]
+    ["projects/" (:projects handlers)]
+    ["projects" (->Redirect 307 (:projects handlers))]

     ["hecuba-js/react.js" (->Resources {:prefix "sb-admin/"})]
     ["" (->Resources {:prefix "sb-admin/"})]
@@ -64,17 +68,13 @@
   Lifecycle
   (init [_ system] system)
   (start [_ system]
-    (let [querier (:querier system)
-          commander (:commander system)
-          project (project-resource querier)
-          projects (projects-resource querier commander project)]
+    (let [commander (:commander system)
+          querier (:querier system)
+          handlers (create-handlers commander querier)]
       (-> system
           (add-bidi-routes
            config
            (make-routes (first (:kixi.hecuba.kafka/producer-config (:hecuba/kafka system)))
-                        querier
-                        commander
-                        project
-                        projects))
-          (update-in [:handlers] merge {:project project :projects projects}))))
+                        querier commander handlers))
+          (update-in [:handlers] merge handlers))))
   (stop [_ system] system))


Nil punning

Nil punning

--- src/tick/core.clj
+++ src/tick/core.clj
@@ -384,7 +384,11 @@

   LocalTime
   (time [i] i)
-  (local? [i] true))
+  (local? [i] true)
+
+  nil
+  (time [_] nil)
+  (local? [_] nil))

 (extend-protocol ITimeRange
   String
@@ -417,7 +421,11 @@

   LocalDateTime
   (start [time] time)
-  (end [end] end))
+  (end [end] end)
+
+  nil
+  (start [_] nil)
+  (end [_] nil))

 (defn on [^LocalTime time ^LocalDate date]
   (.atTime date time))


Style tweaks

Style tweaks

--- src/datview/core.cljs
+++ src/datview/core.cljs
@@ -433,7 +433,7 @@
 (def default-pull-data-view-style
   (merge h-box-styles
          default-box-style
-         {:padding "10px 15px"
+         {:padding "8px 15px"
           :width "100%"}))


minor code gen tweak

minor code gen tweak

--- src/main/shadow/build/targets/esm.clj
+++ src/main/shadow/build/targets/esm.clj
@@ -311,32 +311,29 @@
          :or {runtime :browser}}
         config]

-    (str ;; closure accesses these defines via goog.global.CLOSURE_DEFINES
-      "globalThis.CLOSURE_DEFINES = " (output/closure-defines-json state) ";\n"
-      "globalThis.CLOSURE_NO_DEPS = true;\n"
-      ;; the global must be overriden in goog/base.js since it contains some
-      ;; goog.define(...) which would otherwise be exported to "this"
-      ;; but we need it on $CLJS
-      (-> (data/get-output! state {:resource-id output/goog-base-id})
-          (get :js)
-          ;; FIXME: this is using the compiled variant of goog/base.js
-          ;; don't use goog-global-snippet, that is used for uncompiled goog/base.js
-          ;; keeping both variants for now until I can make this cleaner
-          (str/replace "goog.global = this || self;" "goog.global = globalThis;"))
-
-      "globalThis.goog = goog;\n"
-      "globalThis.shadow$provide = {};"
-
-
-      "globalThis.shadow_esm_import = function(x) { return import(x.startsWith(\"./\") ? \".\" + x : x); }\n"
-
-      "let $CLJS = globalThis.$CLJS = globalThis;"
-      (slurp (io/resource "shadow/boot/esm.js"))
-
-      (when (seq polyfill-js)
-        (str polyfill-js "\n"
-             "globalThis.$jscomp = $jscomp;\n"))
-      )))
+    (->> ["globalThis.CLOSURE_DEFINES = " (output/closure-defines-json state) ";"
+          "globalThis.CLOSURE_NO_DEPS = true;"
+          ;; the global must be overriden in goog/base.js since it contains some
+          ;; goog.define(...) which would otherwise be exported to "this"
+          ;; but we need it on $CLJS
+          (-> (data/get-output! state {:resource-id output/goog-base-id})
+              (get :js)
+              ;; FIXME: this is using the compiled variant of goog/base.js
+              ;; don't use goog-global-snippet, that is used for uncompiled goog/base.js
+              ;; keeping both variants for now until I can make this cleaner
+              (str/replace "goog.global = this || self;" "goog.global = globalThis;"))
+
+          "globalThis.goog = goog;"
+          "globalThis.shadow$provide = {};"
+          "globalThis.shadow_esm_import = function(x) { return import(x.startsWith(\"./\") ? \".\" + x : x); }"
+          "let $CLJS = globalThis.$CLJS = globalThis;"
+          (slurp (io/resource "shadow/boot/esm.js"))
+
+          (when (seq polyfill-js)
+            (str polyfill-js "\n"
+                 "globalThis.$jscomp = $jscomp;\n"))]
+         (remove nil?)
+         (str/join "\n"))))

 (defn flush-dev [{::build/keys [config] :keys [build-modules] :as state}]
   (when-not (seq build-modules)


RS & AB | #0 | Remove debug print statement

--- src/objective8/handlers/front_end.clj
+++ src/objective8/handlers/front_end.clj
@@ -37,7 +37,6 @@
   (error-404-response request))

 (defn index [request]
-  (prn "SCHEME: " (:scheme request))
   {:status 200
    :headers {"Content-Type" "text/html"}
    :body (views/index "index" request)})


Removed an unnecessary TODO.

Removed an unnecessary TODO.

--- src/plugboard/webfunction/plugboards.clj
+++ src/plugboard/webfunction/plugboards.clj
@@ -141,7 +141,6 @@
 ;; This is almost identical to
 ;; plugboard.webfunction.response/webfn-matches-path? but doesn't
 ;; count functions that don't have paths.
-;; TODO: Try to re-factor so that only one such function is needed?
 (defn webfn-matches-path? [path webfn]
   (let [p (get (meta webfn) web/path)]
     (cond


Uses better names

Uses better names

--- scheduler/src/cook/authorization.clj
+++ scheduler/src/cook/authorization.clj
@@ -145,22 +145,22 @@
                      "is not allowed to perform" verb "on" (str object ",") "denying.")
            false))))

-(defn admins-open-gets-auth
-  "Allows admins to do anything to any object, and if whitelisted-user is truthy,
+(defn admins-open-gets-whitelisted-users-auth
+  "Allows admins to do anything to any object, and if user-is-whitelisted? is truthy,
   allows non-admins to manipulate objects that they own and to read (get) any object."
-  [admins user verb {:keys [owner] :as object} whitelisted-user]
-  (let [svo {:user user :verb verb :object object :whitelisted-user? whitelisted-user}]
+  [admins user verb {:keys [owner] :as object} user-is-whitelisted?]
+  (let [svo {:user user :verb verb :object object :user-is-whitelisted? user-is-whitelisted?}]
     (cond (contains? admins user)
           (do
             (log/debug "User" user "is an admin, allowing." (assoc svo :authorized? true))
             true)

-          (and (= verb :get) whitelisted-user)
+          (and (= verb :get) user-is-whitelisted?)
           (do
             (log/debug "Verb was get, allowing." (assoc svo :authorized? true))
             true)

-          (and (= owner user) whitelisted-user)
+          (and (= owner user) user-is-whitelisted?)
           (do
             (log/debug "Object is owned by user, allowing." (assoc svo :authorized? true))
             true)
@@ -184,7 +184,7 @@
     {:keys [owner item] :as object}]
    (log/debug "Checking whether user" user "may perform" verb "on object" (str object)
               ". Admins are:" admins "and whitelist is:" whitelist)
-   (admins-open-gets-auth admins user verb object (contains? whitelist user))))
+   (admins-open-gets-whitelisted-users-auth admins user verb object (contains? whitelist user))))

 (defn configfile-admins-auth-open-gets
   "This authorization function consults the set of usernames specified
@@ -205,7 +205,7 @@
               "may perform" verb
               "on object" (str object) "."
               "Admins are:" admins)
-   (admins-open-gets-auth admins user verb object true)))
+   (admins-open-gets-whitelisted-users-auth admins user verb object true)))

 (defn is-authorized?
   "Determines whether the given user can perform the given operation


Add comments

Add comments

--- src/yada/core.clj
+++ src/yada/core.clj
@@ -345,14 +345,14 @@
                                   :body body
                                   ::http-response true})))))))

-              ;; TODO Not implemented (if unknown Content-* header)
-
-              ;; TODO Unsupported media type
-
-              ;; TODO Request entity too large - shouldn't we do this later,
+              ;; TODO: Not implemented (if unknown Content-* header)
+
+              ;; TODO: Unsupported media type
+
+              ;; TODO: Request entity too large - shouldn't we do this later,
               ;; when we determine we actually need to read the request body?

-              ;; TODO OPTIONS
+              ;; TODO: OPTIONS

               ;; Prior to conneg we do a pre-fetch, so the resource has
               ;; a chance to load any metadata it may need to answer the
@@ -379,6 +379,14 @@
                                                            (or (service/produces produces ctx)
                                                                (res/produces resource ctx))))]

+                  ;; Check to see if the charset is recognized
+                  ;; (registered with IANA). If it isn't we throw a 500,
+                  ;; as this is a server error. It might be necessary to
+                  ;; disable this check in future but a balance should
+                  ;; be struck between giving the developer complete
+                  ;; control to dictate charsets, and error-proofing. It
+                  ;; might be possible to disable this check for
+                  ;; advanced users if a reasonable case is made.
                   (when-let [bad-charset
                              (some (fn [mt] (when-let [charset (some-> mt :parameters (get "charset"))]
                                              (when-not (charset/valid-charset? charset) charset)))


(maint) fix -F/-C/-R options for benchmark tool

This fixes an issue that caused the -F, -C, and -R flags to have the same
effect as the default case.

--- src/puppetlabs/puppetdb/cli/benchmark.clj
+++ src/puppetlabs/puppetdb/cli/benchmark.clj
@@ -346,7 +346,7 @@
     (let [options (-> (ks/cli! args supported-cli-options required-cli-options)
                       first
                       (validate-options #(System/exit 1)))]
-      (if (empty? (select-keys [:facts :reports :catalogs] options))
+      (if (empty? (select-keys options [:facts :reports :catalogs]))
         (default-options options)
         options))


Fix CBOR codec types issue.

Fix CBOR codec types issue.

--- src/merkle_db/graph.clj
+++ src/merkle_db/graph.clj
@@ -19,18 +19,22 @@
   "Map of codec type information that can be used with MerkleDAG stores."
   {'inst
    {:description "An instant in time."
-    :reader #(Instant/parse ^String %)
-    :writers {Instant #(.format DateTimeFormatter/ISO_INSTANT ^Instant %)}}
+    :edn/reader #(Instant/parse ^String %)
+    :edn/writers {Instant #(.format DateTimeFormatter/ISO_INSTANT ^Instant %)}}

    'merkle-db/key
    {:description "Record key byte data."
     :reader key/parse
-    :writers {Key key/hex}}
+    :writers {Key key/hex}
+    :cbor/tag 567
+    :cbor/reader key/create
+    :cbor/writers {Key key/get-bytes}}

    'merkle-db/bloom-filter
    {:description "Probablistic Bloom filter."
     :reader bloom/form->filter
-    :writers {BloomFilter bloom/filter->form}}})
+    :writers {BloomFilter bloom/filter->form}
+    :cbor/tag 568}})


Avoid shadowing min-key and max-key.

Avoid shadowing min-key and max-key.

--- src/merkle_db/index.clj
+++ src/merkle_db/index.clj
@@ -252,20 +252,20 @@
   "Read a lazy sequence of key/map tuples which contain the field data for the
   records whose keys lie in the given range, inclusive. A nil boundary includes
   all records in that range direction."
-  [store node fields min-key max-key]
+  [store node fields min-k max-k]
   (when node
     (case (:data/type node)
       :merkle-db/index
         (->
           (child-boundaries node)
           (cond->>
-            min-key
+            min-k
               (drop-while #(if-let [last-key (nth % 2)]
-                             (key/before? last-key min-key)
+                             (key/before? last-key min-k)
                              false))
-            max-key
+            max-k
               (take-while #(if-let [first-key (nth % 1)]
-                             (not (key/after? first-key max-key))
+                             (not (key/after? first-key max-k))
                              true)))
           (->>
             (map first)
@@ -273,10 +273,10 @@
               (fn read-child
                 [child-link]
                 (let [child (graph/get-link! store node child-link)]
-                  (read-range store child fields min-key max-key))))))
+                  (read-range store child fields min-k max-k))))))

       :merkle-db/partition
-        (part/read-range store node fields min-key max-key)
+        (part/read-range store node fields min-k max-k)

       (throw (ex-info (str "Unsupported index-tree node type: "
                            (pr-str (:data/type node)))


Little bit of cleanup. Make sure we reset the *loaded-libs*.

--- src/prod/clj/com/potetm/tire_iron.clj
+++ src/prod/clj/com/potetm/tire_iron.clj
@@ -26,8 +26,42 @@
                       identity
                       repl-opts))

-(defn ns-parts [munged-ns]
-  (let [split (str/split (str munged-ns) #"\.")
+(defn eval-script [repl-env script]
+  (let [{:keys [status value] :as ret}
+        (repl/-evaluate repl-env
+                        "<cljs repl>"
+                        1
+                        script)]
+    (case status
+      :error (let [e (ex-info "Error executing script"
+                              {:type :js-eval-error
+                               :error ret})]
+               (repl/err-out (pr e))
+               (throw e))
+      :exception (let [e (ex-info "Error executing script"
+                                  {:type :js-eval-exception
+                                   :error ret})]
+                   (repl/err-out (pr e))
+                   (throw e))
+      :success value)))
+
+(defn require-lib [repl-env analyze-env repl-opts target-ns]
+  (let [is-self-require? (= target-ns ana/*cljs-ns*)
+        [in-ns restore-ns]
+        (if is-self-require?
+          ['cljs.user ana/*cljs-ns*]
+          [ana/*cljs-ns* nil])]
+    (binding [ana/*reload-macros* true]
+      (eval-form repl-env
+                 analyze-env
+                 repl-opts
+                 `(~'ns ~in-ns
+                    (:require [~target-ns]))))
+    (when is-self-require?
+      (set! ana/*cljs-ns* restore-ns))))
+
+(defn object-path-parts [munged-path]
+  (let [split (str/split (str munged-path) #"\.")
         part-count (count split)]
     (loop [parts []
            n 1]
@@ -37,12 +71,39 @@
                (inc n))
         parts))))

-(defn compile-ns-exists-check [ns]
+(defn parts-exist? [parts]
   (str/join " && "
-            (map (fn [ns-part]
-                   (str "typeof " ns-part " !== 'undefined' "
-                        "&& " ns-part " !== null "))
-                 (ns-parts ns))))
+            (map (fn [part]
+                   (str "typeof " part " !== 'undefined' "
+                        "&& " part " !== null "))
+                 parts)))
+
+(defn object-path-exists? [munged-path]
+  (parts-exist? (object-path-parts munged-path)))
+
+(defn call-sym-script [sym]
+  (when-not (str/blank? (str sym))
+    (str "if (" (object-path-exists? sym) ") {\n"
+         "  " sym ".call(null);\n"
+         "} else {\n"
+         "  throw new Error(\"Cannot resolve symbol: " sym "\");\n"
+         "}\n")))
+
+(defn set-ti-state-from-sym-script [state-sym]
+  (when-not (str/blank? (str state-sym))
+    (str "if (" (object-path-exists? state-sym) ") {\n"
+         "  goog.tire_iron_state_ = " state-sym ";\n"
+         "} else {\n"
+         "  throw new Error(\"Cannot resolve :state symbol: " state-sym "\");\n"
+         "}\n")))
+
+(defn set-sym-from-ti-state-script [state-sym]
+  (when-not (str/blank? (str state-sym))
+    (str "if (" (parts-exist? (butlast (object-path-parts state-sym))) ") {\n"
+         "  " state-sym " = goog.tire_iron_state_;\n"
+         "} else {\n"
+         "  throw new Error(\"Cannot resolve :state symbol: " state-sym "\");\n"
+         "}\n")))

 (defn compile-unload-ns [ns]
   (let [ns (comp/munge ns)
@@ -54,7 +115,7 @@
          "goog.object.remove(goog.dependencies_.visited, path);\n"
          "goog.object.remove(goog.dependencies_.written, path);\n"
          "goog.object.remove(goog.dependencies_.written, goog.basePath + path);\n"
-         "if (" (compile-ns-exists-check ns) ") {\n"
+         "if (" (object-path-exists? ns) ") {\n"
          "  ns = " ns ";\n"
          "  for (key in ns) {\n"
          "    if (!ns.hasOwnProperty(key)) { continue; }\n"
@@ -84,52 +145,25 @@
          loaded-libs " = cljs.core.disj.call(null, " loaded-libs ", ns_string);\n"
          "})();")))

-(defn compile-unload-nss [nss]
+(defn unload-nss-script [nss]
   (str/join "\n"
             (map compile-unload-ns
                  nss)))

-(defn compile-load-nss [nss]
-  (str "goog.net.jsloader.loadMany([\n"
-       (str/join ",\n"
-                 (map (comp #(str "goog.basePath + goog.dependencies_.nameToPath[\"" % "\"]")
-                            comp/munge)
-                      nss))
-       "]);\n"))
-
-(defn eval-script [repl-env script]
-  (let [{:keys [status value] :as ret}
-        (repl/-evaluate repl-env
-                        "<cljs repl>"
-                        1
-                        script)]
-    (case status
-      :error (let [e (ex-info "Error executing script"
-                              {:type :js-eval-error
-                               :error ret})]
-               (repl/err-out (pr e))
-               (throw e))
-      :exception (let [e (ex-info "Error executing script"
-                                  {:type :js-eval-exception
-                                   :error ret})]
-                   (repl/err-out (pr e))
-                   (throw e))
-      :success value)))
-
-(defn require-lib [repl-env analyze-env repl-opts target-ns]
-  (let [is-self-require? (= target-ns ana/*cljs-ns*)
-        [in-ns restore-ns]
-        (if is-self-require?
-          ['cljs.user ana/*cljs-ns*]
-          [ana/*cljs-ns* nil])]
-    (binding [ana/*reload-macros* true]
-      (eval-form repl-env
-                 analyze-env
-                 repl-opts
-                 `(~'ns ~in-ns
-                    (:require [~target-ns]))))
-    (when is-self-require?
-      (set! ana/*cljs-ns* restore-ns))))
+(defn load-nss-browser-async [nss]
+  (let [loaded-libs (comp/munge 'cljs.core/*loaded-libs*)
+        nss-array (str "["
+                       (str/join ",\n"
+                                 (map (comp #(str "goog.basePath + goog.dependencies_.nameToPath[\"" % "\"]")
+                                            comp/munge)
+                                      nss))
+                       "]")]
+    (str "(function() {\n"
+         "var nss = " nss-array ";"
+         "return goog.net.jsloader.loadMany(nss).addCallback(function() {"
+         "  cljs.core.apply.call(null, cljs.core.conj, " loaded-libs ", nss);"
+         "  });"
+         "})();")))

 (defn remove-disabled [tracker]
   (-> tracker
@@ -138,6 +172,8 @@
       (update-in [::track/load] (partial remove disabled-load-namespaces))))

 (defn refresh* [{:keys [repl-env
+                        analyzer-env
+                        repl-opts
                         source-dirs
                         before
                         after
@@ -153,41 +189,17 @@
   (prn :requesting-reload (::track/load refresh-tracker))
   (eval-script repl-env
                (str "(function() {\n"
-                    "if(typeof goog.net.jsloader == 'undefined') {\n"
-                    "  throw new Error(\"Tire iron not initialized\");"
-                    "}\n"
-                    (when before
-                      (let [b (comp/munge before)]
-                        (str "if (typeof " b " !== 'undefined') {\n"
-                             "  " b ".call(null);\n"
-                             "} else {\n"
-                             "  throw new Error(\"Cannot resolve :before symbol: " b "\");\n"
-                             "}\n")))
-                    (when state
-                      (let [s (comp/munge state)]
-                        (str "if (typeof " s " !== 'undefined') {\n"
-                             "  goog.tire_iron_state_ = " s ";\n"
-                             "} else {\n"
-                             "  throw new Error(\"Cannot resolve :state symbol: " s "\");\n"
-                             "}\n")))
-                    (compile-unload-nss (::track/unload refresh-tracker))
-                    "var d = " (compile-load-nss (::track/load refresh-tracker)) ";\n"
-                    (str "d.addCallback(function(res) {\n"
-                         (when state
-                           (let [s (comp/munge state)]
-                             (str "if (typeof " s " !== 'undefined') {\n"
-                                  "  " s " = goog.tire_iron_state_;\n"
-                                  "} else {\n"
-                                  "  throw new Error(\"Cannot resolve :state symbol: " s "\");\n"
-                                  "}\n")))
-                         (when after
-                           (let [a (comp/munge after)]
-                             (str "if (typeof " a " !== 'undefined') {\n"
-                                  "  " a ".call(null);\n"
-                                  "} else {\n"
-                                  "  throw new Error(\"Cannot resolve :before symbol: " a "\");\n"
-                                  "}\n")))
-                         "})\n")
+                    (call-sym-script (comp/munge before))
+                    (set-ti-state-from-sym-script (comp/munge state))
+                    (unload-nss-script (map comp/munge
+                                            (::track/unload refresh-tracker)))
+                    "var d = " (load-nss-browser-async (map comp/munge
+                                                            (::track/load refresh-tracker)))
+                    ";\n"
+                    "d.addCallback(function(ret) {\n"
+                    (set-sym-from-ti-state-script (comp/munge state))
+                    (call-sym-script (comp/munge after))
+                    "  });\n"
                     "})();"))
   (alter-var-root #'refresh-tracker (fn [tracker]
                                       (assoc tracker
@@ -213,13 +225,14 @@
     (second symbol-or-list)
     symbol-or-list))

-(defn disable-unload!
-  ([ns]
-   (alter-var-root #'disabled-unload-namespaces conj (maybe-quoted->symbol ns))))
-
-(defn disable-reload!
-  ([ns]
-   (alter-var-root #'disabled-load-namespaces conj (maybe-quoted->symbol ns))))
+(defn disable-unload! [ns]
+  (alter-var-root #'disabled-unload-namespaces conj (maybe-quoted->symbol ns)))
+
+(defn disable-reload! [ns]
+  (alter-var-root #'disabled-load-namespaces conj (maybe-quoted->symbol ns)))
+
+(defn clear! []
+  (alter-var-root #'refresh-tracker (constantly (track/tracker))))

 (defn print-disabled []
   (prn "disabled unload" disabled-unload-namespaces)
@@ -282,6 +295,13 @@
                                :analyzer-env analyzer-env
                                :repl-opts repl-opts})))))

+   'init
+   (wrap (fn [repl-env analyzer-env _ repl-opts]
+           (require-lib repl-env
+                        analyzer-env
+                        repl-opts
+                        'goog.net.jsloader)))
+
    'print-tis
    (wrap (fn [repl-env analyzer-env _ repl-opts]
            (print-state repl-env
@@ -308,14 +328,6 @@
    (wrap (fn [_ _ [_ my-ns] _]
            (disable-reload! my-ns)))

-   'init
-   (wrap (fn [repl-env analyzer-env _ repl-opts]
-           (require-lib repl-env
-                        analyzer-env
-                        repl-opts
-                        'goog.net.jsloader)))
-
    'clear
    (wrap (fn [_ _ _ _]
-           (alter-var-root #'refresh-tracker
-                           (constantly (track/tracker)))))})
+           (clear!)))})


(TK-64) Fix TK failures from Slingshot Exceptions

Fix IllegalArgumentException that occurs when catching a
Slingshot exception in the TK main function by adding an
else clause to the case statement.

--- src/puppetlabs/trapperkeeper/core.clj
+++ src/puppetlabs/trapperkeeper/core.clj
@@ -1,6 +1,6 @@
 (ns puppetlabs.trapperkeeper.core
   (:require [clojure.tools.logging :as log]
-            [slingshot.slingshot :refer [try+]]
+            [slingshot.slingshot :refer [try+ throw+]]
             [puppetlabs.kitchensink.core :refer [without-ns]]
             [puppetlabs.trapperkeeper.services :as services]
             [puppetlabs.trapperkeeper.app :as app]
@@ -160,9 +160,11 @@
         (internal/parse-cli-args!)
         (run))
     (catch map? m
-      (println (:message m))
       (case (without-ns (:type m))
-        :cli-error (System/exit 1)
-        :cli-help  (System/exit 0)))
+        :cli-error ((println (:message m))
+                    (System/exit 1))
+        :cli-help ((println (:message m))
+                   (System/exit 0))
+        (throw+ m)))
     (finally
       (shutdown-agents))))


(TK-202) Update build-app to take a map or a function

An earlier commit had changed the signature of build-app in a backward
incompatible manner. This commit updates build-app to handle the
previous behavior, where config-data was a map, as well as the new
behavior, where config-data is a function.

--- src/puppetlabs/trapperkeeper/core.clj
+++ src/puppetlabs/trapperkeeper/core.clj
@@ -32,13 +32,14 @@
   (`init`, `start`, `stop`) as you see fit;  if you'd like to have the trapperkeeper
   framework block the main thread to wait for a shutdown event, call `init`,
   `start`, and then `run-app`."
-  [services config-data-fn]
+  [services config-data]
   {:pre  [(sequential? services)
           (every? #(satisfies? services/ServiceDefinition %) services)
-          (ifn? config-data-fn)]
+          (ifn? config-data)]
    :post [(satisfies? app/TrapperkeeperApp %)]}
-  (config/initialize-logging! (config-data-fn))
-  (internal/build-app* services config-data-fn (promise)))
+  (let [config-data-fn (if (map? config-data) (constantly config-data) config-data)]
+    (config/initialize-logging! (config-data-fn))
+    (internal/build-app* services config-data-fn (promise))))

 (defn boot-services-with-cli-data
   "Given a list of ServiceDefinitions and a map containing parsed cli data, create


Repair error calculating pumped volume

Repair error calculating pumped volume

--- src/test_utils/fracture_diagnostics.cljc
+++ src/test_utils/fracture_diagnostics.cljc
@@ -539,7 +539,7 @@
    (let [volume-magnitude (condp = volume-unit
                             :bbl (tuc/draw-normal 6986.70 1992.30)
                             :m3  (tuc/draw-normal 813.47 286.89))]
-     [volume-magnitude volume-unit])))
+     (make-measurement volume-magnitude volume-unit))))

 (defn typical-proppant-mass
   ([] (let [mass-unit (rand-mass-unit)]
@@ -661,15 +661,8 @@
         measurement (generate-measurement-f unit)]
     [measurement (measurement-as-f measurement (first (remove (partial = unit) units)))]))

-(generate-measurement-pair (set (quantity-unit-map :volume))
-                           rand-volume-unit typical-pumped-volume volume-as)
-
 (defn rand-measurement
   ([]
    (rand-measurement (rand-physical-quantity)))
   ([physical-quantity]
    ((quantity-generate-measurement-map physical-quantity) (rand-unit physical-quantity))))
-
-((quantity-generate-measurement-map :duration))
-((quantity-generate-measurement-map :duration) (rand-unit :duration))
-(rand-measurement :duration)


fix #1469 chat: location is shown as plaintext

--- src/status_im/chat/handlers/commands.cljs
+++ src/status_im/chat/handlers/commands.cljs
@@ -43,8 +43,10 @@
                 callback #(let [result (get-in % [:result :returned])
                                 result' (if (:markup result)
                                          (update result :markup cu/generate-hiccup)
-                                         result)]
-                            (dispatch [:set-in [:message-data data-type message-id] result'])
+                                         result)]
+                            ;; don't fill message data with nil results
+                            (when result'
+                              (dispatch [:set-in [:message-data data-type message-id] result']))
                             (when (and result (= :preview data-type))
                               ;; update message in realm with serialized preview
                               (messages/update {:message-id message-id


Fix parens on intro screens

Fix parens on intro screens

Fixes #10979

Signed-off-by: Churikova Tetiana <df59d4ef10565572befc1f25db27f19bd387b29a@gmail.com>

--- src/status_im/ui/screens/intro/views.cljs
+++ src/status_im/ui/screens/intro/views.cljs
@@ -167,7 +167,7 @@
                           :generate-key     :t/generate-a-key
                           :recovery-success :t/re-encrypt-key
                           :intro-wizard-title6)]
-           [react/view (:style (assoc styles/bottom-button :margin-bottom 16))
+           [react/view {:style (assoc styles/bottom-button :margin-bottom 16)}
             [quo/button
              {:disabled            existing-account?
               :on-press            #(re-frame/dispatch [forward-action])


Adds rempick

Adds rempick

--- src/the_little_clojurian/chapter4.clj
+++ src/the_little_clojurian/chapter4.clj
@@ -184,3 +184,16 @@
   (is (= (pick 1 '(apple)) 'apple))
   (is (= (pick 2 '(apple bananas)) 'bananas))
   (is (= (pick 4 '(apple)) nil)))
+
+(with-test
+  (def rempick
+    (fn [n lat]
+      (cond (zero? n) nil
+            (zero? (sub1 n)) (cdr lat)
+            :else (cons (car lat)
+                        (rempick (sub1 n)
+                                 (cdr lat))))))
+
+  (is (= (rempick 0 '()) nil))
+  (is (= (rempick 1 '(apple)) '()))
+  (is (= (rempick 2 '(apple bananas gin)) '(apple gin))))


Minor refactor on projects page (ui components).

--- src/uxbox/main/ui/dashboard/projects.cljs
+++ src/uxbox/main/ui/dashboard/projects.cljs
@@ -6,24 +6,21 @@
 ;; Copyright (c) 2015-2016 Juan de la Cruz <delacruzgarciajuan@gmail.com>

 (ns uxbox.main.ui.dashboard.projects
-  (:require [sablono.core :as html :refer-macros [html]]
-            [rum.core :as rum]
-            [lentes.core :as l]
+  (:require [lentes.core :as l]
             [cuerdas.core :as str]
+            [uxbox.main.state :as st]
+            [uxbox.main.data.projects :as dp]
+            [uxbox.main.data.lightbox :as udl]
+            [uxbox.main.ui.icons :as i]
+            [uxbox.main.ui.dashboard.header :refer (header)]
+            [uxbox.main.ui.lightbox :as lbx]
+            [uxbox.main.ui.messages :as uum]
             [uxbox.util.i18n :as t :refer (tr)]
             [uxbox.util.router :as r]
             [uxbox.util.rstore :as rs]
-            [uxbox.main.state :as s]
-            [uxbox.main.data.dashboard :as dd]
-            [uxbox.main.data.projects :as dp]
-            [uxbox.main.data.workspace :as dw]
-            [uxbox.main.data.lightbox :as udl]
-            [uxbox.main.ui.icons :as i]
+            [uxbox.util.data :refer (read-string)]
             [uxbox.util.dom :as dom]
-            [uxbox.main.ui.dashboard.header :refer (header)]
-            [uxbox.main.ui.lightbox :as lbx]
-            [uxbox.main.ui.messages :as uum]
-            [uxbox.util.mixins :as mx]
+            [uxbox.util.mixins :as mx :include-macros true]
             [uxbox.util.datetime :as dt]))

 ;; --- Helpers & Constants
@@ -60,100 +57,94 @@
   {:name "ds.project-ordering.by-name"
    :created "ds.project-ordering.by-creation-date"})

-;; --- Lenses
-
-(def projects-by-id-ref
-  (as-> (l/key :projects-by-id) $
-    (l/derive $ s/state)))
+;; --- Refs
+
+(def projects-map-ref
+  (-> (l/key :projects-by-id)
+      (l/derive st/state)))
+
+(def dashboard-ref
+  (-> (l/in [:dashboard :projects])
+      (l/derive st/state)))

 (def project-ordering-ref
-  (as-> (l/in [:dashboard :project-order]) $
-    (l/derive $ s/state)))
+  (-> (l/key :project-order)
+      (l/derive dashboard-ref)))

 (def project-filtering-ref
-  (as-> (l/in [:dashboard :project-filter]) $
-    (l/derive $ s/state)))
-
-;; --- Sort Widget
-
-(defn sort-widget-render
-  []
-  (let [ordering (mx/react project-ordering-ref)
-        on-change #(rs/emit! (dp/set-project-ordering
-                              (keyword (.-value (.-target %)))))]
-    (html
-     [:div
-      [:span (tr "ds.project-ordering")]
-      [:select.input-select
-       {:on-change on-change
-        :value (name ordering)}
-       (for [option (keys +ordering-options+)
-             :let [option-id (get +ordering-options+ option)
-                   option-value (name option)
-                   option-text (tr option-id)]]
-         [:option
-          {:key option-id
-           :value option-value}
-          option-text])]])))
-
-(def ^:private sort-widget
-  (mx/component
-   {:render sort-widget-render
-    :name "sort-widget-render"
-    :mixins [mx/reactive mx/static]}))
-
-;; --- Filtering Widget
-
-(defn search-widget-render
-  []
-  (letfn [(on-term-change [event]
-            (-> (dom/get-target event)
-                (dom/get-value)
-                (dp/set-project-filtering)
-                (rs/emit!)))
-          (on-clear [event]
-            (rs/emit! (dp/clear-project-filtering)))]
-    (html
-     [:form.dashboard-search
-      [:input.input-text
-       {:type "text"
-        :on-change on-term-change
-        :auto-focus true
-        :placeholder (tr "ds.project-search.placeholder")
-        :value (mx/react project-filtering-ref)}]
-      [:div.clear-search
-       {:on-click on-clear}
-       i/close]])))
-
-(def ^:private search-widget
-  (mx/component
-   {:render search-widget-render
-    :name "search-widget"
-    :mixins [mx/reactive mx/static]}))
-
-;; --- Sort & Search Menu
-
-(defn menu-render
-  []
-  (let [projects (mx/react projects-by-id-ref)
-        pcount (count projects)]
-    (html
-     [:section.dashboard-bar
-      [:div.dashboard-info
-       [:span.dashboard-projects (tr "ds.num-projects" (t/c pcount))]
-       (sort-widget)
-       (search-widget)]])))
-
-(def menu
-  (mx/component
-   {:render menu-render
-    :name "menu"
-    :mixins [mx/reactive mx/static]}))
+  (-> (l/in [:project-filter])
+      (l/derive dashboard-ref)))
+
+;; --- Helpers
+
+(defn sort-projects-by
+  [ordering projs]
+  (case ordering
+    :name (sort-by :name projs)
+    :created (reverse (sort-by :created-at projs))
+    projs))
+
+(defn contains-term?
+  [phrase term]
+  (let [term (name term)]
+    (str/includes? (str/lower phrase) (str/trim (str/lower term)))))
+
+(defn filter-projects-by
+  [term projs]
+  (if (str/blank? term)
+    projs
+    (filter #(contains-term? (:name %) term) projs)))
+
+;; --- Menu (Filter & Sort)
+
+(mx/defc menu
+  {:mixins [mx/static]}
+  [state projects]
+  (let [ordering (:order state :name)
+        filtering (:filter state "")
+        count (count projects)]
+    (letfn [(on-term-change [event]
+              (let [term (-> (dom/get-target event)
+                             (dom/get-value))]
+                (rs/emit! (dp/update-opts :filter term))))
+            (on-ordering-change [event]
+              (let [value (dom/event->value event)
+                    value (read-string value)]
+                (rs/emit! (dp/update-opts :order value))))
+            (on-clear [event]
+              (rs/emit! (dp/update-opts :filter "")))]
+      [:section.dashboard-bar
+       [:div.dashboard-info
+
+        ;; Counter
+        [:span.dashboard-images (tr "ds.num-projects" (t/c count))]
+
+        ;; Sorting
+        [:div
+         [:span (tr "ds.project-ordering")]
+         [:select.input-select
+          {:on-change on-ordering-change
+           :value (pr-str ordering)}
+          (for [[key value] (seq +ordering-options+)
+                :let [ovalue (pr-str key)
+                      olabel (tr value)]]
+            [:option {:key ovalue :value ovalue} olabel])]]
+        ;; Search
+        [:form.dashboard-search
+         [:input.input-text
+          {:key :images-search-box
+           :type "text"
+           :on-change on-term-change
+           :auto-focus true
+           :placeholder (tr "ds.project-search.placeholder")
+           :value (or filtering "")}]
+         [:div.clear-search {:on-click on-clear} i/close]]]])))

 ;; --- Grid Item

-(defn grid-item-render
-  [own project]
+(mx/defc  grid-item
+  {:mixins [mx/static]}
+  [project]
   (letfn [(on-navigate [event]
             (rs/emit! (dp/go-to (:id project))))
           (delete []
@@ -161,135 +152,115 @@
           (on-delete [event]
             (dom/stop-propagation event)
             (udl/open! :confirm {:on-accept delete}))]
-    (html
-     [:div.grid-item.project-th {:on-click on-navigate
-                                 :key (:id project)}
-      [:h3 (:name project)]
-      [:span.project-th-update
-       (str "Updated " (dt/timeago (:modified-at project)))]
-      [:div.project-th-actions
-       [:div.project-th-icon.pages
-        i/page
-        [:span (:total-pages project)]]
-       #_[:div.project-th-icon.comments
-        i/chat
+    [:div.grid-item.project-th {:on-click on-navigate}
+     [:h3 (:name project)]
+     [:span.project-th-update
+      (str "Updated " (dt/timeago (:modified-at project)))]
+     [:div.project-th-actions
+      [:div.project-th-icon.pages
+       i/page
+       [:span (:total-pages project)]]
+      #_[:div.project-th-icon.comments
+         i/chat
         [:span "0"]]
-       #_[:div.project-th-icon.edit
-          i/pencil]
-       [:div.project-th-icon.delete
-        {:on-click on-delete}
-        i/trash]]])))
-
-(def ^:private grid-item
-  (mx/component
-   {:render grid-item-render
-    :name "grid-item"
-    :mixins [rum/static]}))
+      #_[:div.project-th-icon.edit
+         i/pencil]
+      [:div.project-th-icon.delete
+       {:on-click on-delete}
+       i/trash]]]))

 ;; --- Grid

-(defn grid-render
-  [own]
-  (let [projects (mx/react projects-by-id-ref)
-        ordering (mx/react project-ordering-ref)
-        filtering (mx/react project-filtering-ref)]
+(mx/defc grid
+  {:mixins [mx/static]}
+  [state projects]
+  {:pre [(map? projects)]}
+  (let [ordering (:order state :name)
+        filtering (:filter state "")
+        projects (->> (vals projects)
+                      (filter-projects-by filtering)
+                      (sort-projects-by ordering))]
     (letfn [(on-click [e]
               (dom/prevent-default e)
               (udl/open! :new-project))]
-      (html
-       [:section.dashboard-grid
-        [:h2 "Your projects"]
-         [:div.dashboard-grid-content
-          [:div.dashboard-grid-row
-            [:div.grid-item.add-project
-             {:on-click on-click}
-             [:span "+ New project"]]
-            (for [item (->> (vals projects)
-                            (dp/filter-projects-by filtering)
-                            (dp/sort-projects-by ordering))]
-              (-> (grid-item item)
-                  (rum/with-key (:id item))))]]]))))
-
-(def grid
-  (mx/component
-   {:render grid-render
-    :name "grid"
-    :mixins [mx/reactive]}))
-
-;; --- Page
-
-(defn projects-page-render
-  [own]
-  (html
-   [:main.dashboard-main
-    (uum/messages)
-    (header)
-    [:section.dashboard-content
-     (menu)
-     (grid)]]))
+      [:section.dashboard-grid
+       [:h2 "Your projects"]
+       [:div.dashboard-grid-content
+        [:div.dashboard-grid-row
+         [:div.grid-item.add-project
+          {:on-click on-click}
+          [:span "+ New project"]]
+         (for [item projects]
+           (-> (grid-item item)
+               (mx/with-key (:id item))))]]])))
+
+;; --- Projects Page

 (defn projects-page-will-mount
   [own]
-  (rs/emit! (dd/initialize :dashboard/projects)
-            (dp/initialize))
+  (rs/emit! (dp/initialize))
   own)

 (defn projects-page-did-remount
-  [old-state state]
-  (rs/emit! (dd/initialize :dashboard/projects))
-  state)
-
-(def projects-page
-  (mx/component
-   {:render projects-page-render
-    :will-mount projects-page-will-mount
-    :did-remount projects-page-did-remount
-    :name "projects-page"
-    :mixins [rum/static]}))
+  [old-own own]
+  (rs/emit! (dp/initialize))
+  own)
+
+(mx/defc projects-page
+  {:will-mount projects-page-will-mount
+   :did-remount projects-page-did-remount
+   :mixins [mx/static mx/reactive]}
+  []
+  (let [state (mx/react dashboard-ref)
+        projects-map (mx/react projects-map-ref)]
+    [:main.dashboard-main
+     (uum/messages)
+     (header)
+     [:section.dashboard-content
+      (menu state projects-map)
+      (grid state projects-map)]]))

 ;; --- Lightbox: Layout input

-(defn- layout-input
+(mx/defc layout-input
   [local layout-id]
   (let [layout (get-in +layouts+ [layout-id])
         id (:id layout)
         name (:name layout)
         width (:width layout)
         height (:height layout)]
-    (html
-     [:div
-      [:input
-       {:type "radio"
-        :key id
-        :id id
-        :name "project-layout"
-        :value name
-        :checked (= layout-id (:layout @local))
-        :on-change #(swap! local merge {:layout layout-id
-                                        :width width
-                                        :height height})}]
-      [:label {:value (:name @local) :for id} name]])))
+    [:div
+     [:input
+      {:type "radio"
+       :key id
+       :id id
+       :name "project-layout"
+       :value name
+       :checked (= layout-id (:layout @local))
+       :on-change #(swap! local merge {:layout layout-id
+                                       :width width
+                                       :height height})}]
+     [:label {:value (:name @local) :for id} name]]))

 ;; --- Lightbox: Layout selector

-(defn- layout-selector
+(mx/defc layout-selector
   [local]
-  (html
-   [:div.input-radio.radio-primary
-    (layout-input local "mobile")
-    (layout-input local "tablet")
-    (layout-input local "notebook")
-    (layout-input local "desktop")]))
+  [:div.input-radio.radio-primary
+   (layout-input local "mobile")
+   (layout-input local "tablet")
+   (layout-input local "notebook")
+   (layout-input local "desktop")])

 ;; -- New Project Lightbox

-(defn- new-project-lightbox-render
+(mx/defcs new-project-lightbox
+  {:mixins [(mx/local +project-defaults+)]}
   [own]
   (let [local (:rum/local own)
         name (:name @local)
         width (:width @local)
         height (:height @local)]
-   (html
     [:div.lightbox-body
      [:h3 "New project"]
      [:form {:on-submit (constantly nil)}
@@ -333,13 +304,7 @@
      [:a.close {:href "#"
                 :on-click #(do (dom/prevent-default %)
                                (udl/close!))}
-      i/close]])))
-
-(def new-project-lightbox
-  (mx/component
-   {:render new-project-lightbox-render
-    :name "new-project-lightbox"
-    :mixins [(rum/local +project-defaults+)]}))
+      i/close]]))

 (defmethod lbx/render-lightbox :new-project
   [_]


on server put get-results into a function

So that we can reuse get-results for the realtime queries as well

--- src/webapp/framework/server/systemfns.clj
+++ src/webapp/framework/server/systemfns.clj
@@ -408,6 +408,32 @@

+(defn get-results [& {:keys [db-table where order start end params]}]
+(sql
+    (str
+        "select id from "
+        db-table " "
+        (if (-> where count pos?) (str "where " where " "))
+        (if (-> order count pos?) (str "order by " order " "))
+        (cond
+            (= *database-type* "postgres" )
+            (str
+                (if start (str " offset " (- start 1) " "))
+                (if end (str " limit " (+ 1 (- end start)) " ") "limit 2")
+                )
+
+            (= *database-type* "oracle" )
+            ""
+            )
+
+        ;questions_answered_count is not null
+        ;order by
+        ;questions_answered_count desc"
+        ) params)
+        )
+
+
+

 (defn !get-query-results-v2 [{:keys [
                                      db-table
@@ -469,27 +495,13 @@

         results

-        (sql
-				 (str
-				  "select id from "
-				  db-table " "
-				  (if (-> where count pos?) (str "where " where " "))
-          (if (-> order count pos?) (str "order by " order " "))
-          (cond
-            (= *database-type* "postgres" )
-            (str
-              (if start (str " offset " (- start 1) " "))
-              (if end (str " limit " (+ 1 (- end start)) " ") "limit 2")
-              )
-
-            (= *database-type* "oracle" )
-            ""
-            )
-
-				  ;questions_answered_count is not null
-				  ;order by
-				  ;questions_answered_count desc"
-				  ) params)
+       (get-results   :db-table db-table
+                      :where    where
+                      :order    order
+                      :start    start
+                      :end      end
+                      :params   params
+        )

 		result-id-vector
 		(into [] (map :id results))


every 10 seconds send a record update for processing

This is the second stage of getting the real time changes back to the
client

--- src/webapp/framework/server/systemfns.clj
+++ src/webapp/framework/server/systemfns.clj
@@ -16,6 +16,7 @@
   [:use [webapp.framework.server.db-helper]]
   [:use [webapp.framework.server.neo4j-helper]]
   [:use [webapp.framework.server.globals]]
+  (use [overtone.at-at])
   )

@@ -444,8 +445,8 @@

 (defn create-realtime-trigger [& {:keys [:table-name]}]
-(let [coils-trigger      (korma.core/exec-raw
-                                   [" select * from coils_triggers where table_name = ?" [table-name]]
+(let [coils-trigger      (korma.core/exec-raw
+                                   [" select * from coils_triggers where table_name = ?" [table-name]]
                                    :results)

         coils-trigger-exists   (pos? (count coils-trigger))
@@ -483,8 +484,8 @@

 (defn make-admin-table []
-  (let [coils-admin-tables      (korma.core/exec-raw
-                                   [" select * from pg_tables where schemaname='public' and tablename='coils_triggers'" []]
+  (let [coils-admin-tables      (korma.core/exec-raw
+                                   [" select * from pg_tables where schemaname='public' and tablename='coils_triggers'" []]
                                    :results)

         coils-admin-table-exists   (pos? (count coils-admin-tables))
@@ -518,8 +519,8 @@

 (defn make-log-table []
-  (let [coils-admin-tables      (korma.core/exec-raw
-                                   [" select * from pg_tables where schemaname='public' and tablename='coils_realtime_log'" []]
+  (let [coils-admin-tables      (korma.core/exec-raw
+                                   [" select * from pg_tables where schemaname='public' and tablename='coils_realtime_log'" []]
                                    :results)

         coils-admin-table-exists   (pos? (count coils-admin-tables))
@@ -698,7 +699,7 @@

   (cond
        realtime
-
+
        (let [
              query-key   (create-query-key
                             :db-table  db-table
@@ -824,3 +825,23 @@
 ;(sql-1 "select count(id) from table_name " [])

+
+(def my-pool (mk-pool))
+(every 10000 (fn []
+              (let [sql (str "update coils_realtime_log"
+                            "      set record_status = 'PROCESSING'"
+                            "where "
+                            "      id in ( SELECT "
+                            "                    id"
+                            "              FROM "
+                            "                    coils_realtime_log"
+                            "              WHERE "
+                            "                   record_status='WAIITING' LIMIT 1 ) "
+                    )]
+                     (do
+                        (println "Checking pool!" )
+                        (korma.core/exec-raw [sql []])
+                 ))) my-pool )
+
+
+


Fix unexpected exception on count function of padding.

When checking not padded byte-array with pkcs7 padding.

--- src/buddy/core/padding.clj
+++ src/buddy/core/padding.clj
@@ -60,8 +60,11 @@
   "Get the padding size found on given byte array."
   ([^bytes input] (count input :pkcs7))
   ([^bytes input ^Keyword alg]
-   (let [engine (padding-engine alg)]
-     (.padCount engine input))))
+   (try
+     (let [engine (padding-engine alg)]
+       (.padCount engine input))
+     (catch org.bouncycastle.crypto.InvalidCipherTextException e
+       0))))

 (defn padded?
   "Check if given byte array has padding using specified


Use larger salt in compact message signing impl.

--- src/buddy/sign/compact.clj
+++ src/buddy/sign/compact.clj
@@ -116,8 +116,8 @@
   [data key & [{:keys [alg compress]
                 :or {alg :hs256 compress true}}]]
   (let [input (serialize data compress)
-        salt (nonce/random-nonce 8)
-        stamp (codecs/long->bytes (util/timestamp))
+        salt (nonce/random-bytes 12)
+        stamp (codecs/long->bytes (util/now))
         signature (-> (bytes/concat input salt stamp)
                       (calculate-signature key alg))]
     (str/join "." [(bytes->base64str input)


use defn versions of get-uri-match-end-index and assoc-uri-match-end-index

--- src/calfpath/internal.cljc
+++ src/calfpath/internal.cljc
@@ -83,43 +83,24 @@
 (def ^:const uri-match-end-index :calfpath/uri-match-end-index)

-(defmacro get-uri-match-end-index
-  [request]
-  ;; In CLJS `defmacro` is called by ClojureJVM, hence reader conditionals always choose :clj -
-  ;; so we discover the environment using a hack (:ns &env), which returns truthy for CLJS.
-  ;; Reference: https://groups.google.com/forum/#!topic/clojure/DvIxYnO1QLQ
-  ;; Reference: https://dev.clojure.org/jira/browse/CLJ-1750
-  (let [vsym (gensym)]
-    `(if-some [~vsym (get ~request uri-match-end-index)]
-       ~(if (:ns &env)
-          ;; CLJS
-          `(deref ~vsym)
-          ;; CLJ
-          `(VolatileInt/deref ~vsym))
-       0)))
-
-
-(defmacro assoc-uri-match-end-index
-  [request end-index]
-  ;; In CLJS `defmacro` is called by ClojureJVM, hence reader conditionals always choose :clj -
-  ;; so we discover the environment using a hack (:ns &env), which returns truthy for CLJS.
-  ;; Reference: https://groups.google.com/forum/#!topic/clojure/DvIxYnO1QLQ
-  ;; Reference: https://dev.clojure.org/jira/browse/CLJ-1750
-  (let [vsym (gensym)]
-    `(let [request# ~request]
-       (if-some [~vsym (get request# uri-match-end-index)]
-         (do
-           ~(if (:ns &env)
-              ;; CLJS
-              `(vreset! ~vsym ~end-index)
-              ;; CLJ
-              `(VolatileInt/reset ~vsym ~end-index))
-           request#)
-         (dassoc request# uri-match-end-index ~(if (:ns &env)
-                                                 ;; CLJS
-                                                 `(volatile! ~end-index)
-                                                 ;; CLJ
-                                                 `(VolatileInt/create ~end-index)))))))
+(defn get-uri-match-end-index
+  ^long [request]
+  (if-some [vol (get request uri-match-end-index)]
+    #?(:cljs (deref vol)
+        :clj (VolatileInt/deref vol))
+    0))
+
+
+(defn assoc-uri-match-end-index
+  [request ^long end-index]
+  (if-let [vol (get request uri-match-end-index)]
+    (do
+      #?(:cljs (vreset! vol end-index)
+         :clj (VolatileInt/reset vol end-index))
+      request)
+    (dassoc request uri-match-end-index #?(:cljs (volatile! end-index)
+                                            :clj (VolatileInt/create end-index)))))
+

 (def valid-method-keys #{:get :head :options :patch :put :post :delete})


Try to find the optimum with spacing the headlines

- See page 75 of "XSL-FO: Making XML Look Good in Print" by Dave
  Pawson (https://books.google.ch/books?id=27VkuPGRDG0C)

--- src/catalog/layout/fop.clj
+++ src/catalog/layout/fop.clj
@@ -19,22 +19,22 @@
 (def ^:private default-stylesheet
   {:font {:font-family "Verdana" :font-size "11pt"}
    :block {:text-align "start" :hyphenate "false"}
-   :h1 {:font-weight "bold" :keep-with-next "always" :space-before "11pt" :space-after "11pt"
+   :h1 {:font-weight "bold" :keep-with-next "always" :space-after "25pt"
         :break-before "page" :font-size "25pt" :role "H1"}
-   :h2 {:font-weight "bold" :keep-with-next "always" :space-before "26pt" :space-after "11pt"
+   :h2 {:font-weight "bold" :keep-with-next "always" :space-before "25pt" :space-after "11pt"
         :font-size "17pt" :role "H2"}
-   :h3 {:font-weight "bold" :keep-with-next "always" :space-before "17pt" :space-after "11pt"
+   :h3 {:font-weight "bold" :keep-with-next "always" :space-before "11pt" :space-after "14pt"
         :font-size "14pt" :role "H3"}
    :header {:text-align-last "justify" :font-weight "bold" :border-bottom "thin solid"}})

 (def ^:private large-print-stylesheet
   {:font {:font-family "Tiresias" :font-size "17pt"}
    :block {:text-align "start" :hyphenate "false"}
-   :h1 {:font-weight "bold" :keep-with-next "always" :space-before "17pt" :space-after "17pt"
-        :break-before "page" :font-size "36pt" :role "H1" :line-height "110%"}
-   :h2 {:font-weight "bold" :keep-with-next "always" :space-before "36pt" :space-after "17pt"
-        :font-size "36pt" :role "H2" :line-height "110%"}
-   :h3 {:font-weight "bold" :keep-with-next "always" :space-before "25pt" :space-after "17pt"
+   :h1 {:font-weight "bold" :keep-with-next "always" :space-after "25pt"
+        :break-before "odd-page" :font-size "36pt" :role "H1" :line-height "110%"}
+   :h2 {:font-weight "bold" :keep-with-next "always" :space-after "25pt"
+        :break-before "odd-page" :font-size "36pt" :role "H2" :line-height "110%"}
+   :h3 {:font-weight "bold" :keep-with-next "always" :space-before "30pt" :space-after "25pt"
         :font-size "25pt" :role "H3"}
    :header {:text-align-last "justify" :font-weight "bold" :border-bottom "thin solid"}})


Simplify the menu

Simplify the menu

- Remove "Catalog" from menu items
- Move Prev and Next into a dropdown
- Move the issue number to the right

--- src/catalog/web/layout.clj
+++ src/catalog/web/layout.clj
@@ -66,14 +66,16 @@
       (dropdown-menu "Editorials" [[(format "/%s/%s/editorial/grossdruck" year issue) "Grossdruck"]
                                    [(format "/%s/%s/editorial/braille" year issue) "Braille"]
                                    [(format "/%s/%s/editorial/hörbuch" year issue) "Hörbuch"]])
-      (menu-item (format "/%s/%s/full-catalogs" year issue) "Full Catalogs")
-      (menu-item (format "/custom") "Custom Catalogs")
-      (dropdown-menu "Older Catalogs"
+      (menu-item (format "/%s/%s/full" year issue) "Full")
+      (menu-item (format "/%s/%s/custom" year issue) "Custom")
+      (dropdown-menu "Previous"
                      [(let [[year issue] (issue/prev-issue year issue)]
                         [(format "/%s/%s" year issue) "Previous"])
                       ["/" "Current"]
                       (let [[year issue] (issue/next-issue year issue)]
-                        [(format "/%s/%s" year issue) "Next"])])]]]])
+                        [(format "/%s/%s" year issue) "Next"])])]
+     [:ul.nav.navbar-nav.navbar-right
+      [:li [:a (format "%s/%s" year issue)]]]]]])

 (defn common
   "Display a page using the bootstrap css"