git.fiddlerwoaroof.com
demo.lisp
cd3f5f95
 (in-package :cl-user)
b5a8e489
 (ql:quickload :clack-middleware-postmodern)
 
703fc701
 (ql:quickload '(:fwoar.lisputils :araneus :cl-markup :colors :lquery :plump :postmodern
                 :sxql :clack-middleware-postmodern :dexador :spinneret :ubiquitous :iterate
cccada9e
                 :jonathan :cl-actors :simple-tasks :cl-oid-connect :fwoar.lisputils
                 :serapeum))
89bed873
 
17e50f7b
 (declaim (optimize (speed 0) (safety 3) (debug 2)))
cd3f5f95
 
703fc701
 (define-modify-macro aconsf (key value)
                      (lambda (place k v)
                        (acons k v place)))
 
 (aconsf drakma:*text-content-types*
         "application" "rdf+xml")
 (aconsf drakma:*text-content-types*
         "application" "rss+xml")
 (aconsf drakma:*text-content-types*
         "text" "rss+xml")
cd3f5f95
 
b99dd5be
 ;(load "utils.lisp")
83295250
 (load "package.lisp")
89bed873
 (load "rss.lisp")
 
 (in-package plump-dom)
cd3f5f95
 
89bed873
 (defmethod serialize-object :around ((node element))
   (when (string= (tag-name node) "iframe")
     (make-text-node node))
   (call-next-method))
cd3f5f95
 
 
89bed873
 (in-package :whitespace)
c5c94d7d
 (use-package :fwoar.lisputils)
 (use-package :araneus)
89bed873
 (ubiquitous:restore :whitespace)
 
 
 (defparameter *app* (make-instance 'ningle:<app>))
 
 (handler-bind ((warning #'sb-ext::muffle-warning))
   (let* ((feed-urls (ubiquitous:value 'feed 'urls))
          (plump-parser:*tag-dispatchers* plump:*xml-tags*)
          (docs (map 'vector (lambda (x) (plump:parse (drakma:http-request x))) feed-urls)))
     (defparameter *feeds* (map 'vector (lambda (x) (make-rss-feed x)) docs))))
cd3f5f95
 
c5c94d7d
 (defparameter *db-connection-info* (ubiquitous:value 'db))
 
b99dd5be
 (defmacro with-whitespace-db (&body body)
   `(postmodern:with-connection *db-connection-info*
      ,@body))
c5c94d7d
 
b99dd5be
 (defmacro wc (&body body)
c5c94d7d
   "Utility function for the REPL"
b99dd5be
   `(with-whitespace-db ,@body))
 
 (defmacro with-xml-tags (&body body)
   `(let ((plump:*tag-dispatchers* plump:*xml-tags*))
      ,@body))
cd3f5f95
 
67a3d329
 (load "base-template.lisp")
89bed873
 
 (defmacro defun-from-value (name value)
   `(setf (symbol-function ',name) ,value))
 
 (defun-from-value jsonapi-encoder
                   (jonathan.helper:compile-encoder () (success result)
                     (list :|success| success
                           :|result| result)))
 
 ; ; ;  Ultimately, this will only serialize the feed if the client
 (cl-oid-connect:def-route ("/feeds/add" (params) :method :post :app *app*)
b5a8e489
   (ningle.context:with-context-variables (session)
67a3d329
     (let ((user-info (gethash :app-user session))
3ef36f73
           (result '(302 (:location "/")))
b5a8e489
           (api (string= (cl-oid-connect:assoc-cdr "api" params 'string=) "yes"))
           (url (cl-oid-connect:assoc-cdr "url" params 'string=))
3ef36f73
           (plump-parser:*tag-dispatchers* plump-parser:*xml-tags*))
       (cl-oid-connect:require-login
89bed873
         (when (neither-null params user-info)
           (handler-case
3ef36f73
             (let* ((doc (plump:parse (drakma:http-request url)))
67a3d329
                    (uid (slot-value user-info 'id)))
b5a8e489
               (multiple-value-bind (added-feed dao-feed) (store-feed doc)
67a3d329
                 (subscribe-to-feed uid (slot-value dao-feed 'id))
                 (when api
703fc701
                   (setf result `(200 (:Content-Type "application/json")
                                  (,(jsonapi-encoder t added-feed)))))))
3ef36f73
             (cl-postgres-error:unique-violation
               ()
               (when api
                 (setf result
703fc701
                       `(400 () (,(jsonapi-encoder nil "Feed already saved")))))))))
3ef36f73
       result)))
 
89bed873
 ;;; TODO: add needs to return the new content, so that angular can append it
 
0b3c568e
 (defparameter *userasdfs* nil)
 #|(cl-oid-connect:require-login
     ))|#
 
e09b98ca
 ;;; this will be bound by calls to with-palette
 ;;; probably should be refactored out
 (defparameter *palette* nil)
 
 (defparameter *colorscheme* (make-instance 'colors:colorscheme))
 
 (defun get-theme-css ()
   (colors:with-palette (*palette*)
     (flet ((combine-unit-q (quant unit) (format nil "~d~a" quant unit)))
       (let* ((header-height 9)
89bed873
              (main-right-margin (* 0.618 (- 100 header-height)))
e09b98ca
              (height-units "vh")
              (ss (lass:compile-and-write
                    `(* :color ,(colors:colorscheme-fg *colorscheme*))
 
89bed873
                    `(body
                       :background-color ,(colors:colorscheme-bg *colorscheme*))
e09b98ca
 
                    `((:or h1 h2 h3)
                      :color ,(colors:colorscheme-fg-highlight *colorscheme*))
                    `(.feed-header
                       :background-color ,(colors:colorscheme-bg-highlight *colorscheme*))
 
                    `((:or h4 h5 h6) :color ,(colors:colorscheme-fg-highlight *colorscheme*))
 
                    `(header
                       :border-bottom "thin" "solid" ,(colors:colorscheme-accent *colorscheme*)
                       :height ,(combine-unit-q header-height height-units)
                       :font-size ,(combine-unit-q (* 0.75 header-height) height-units)
                       :line-height ,(combine-unit-q header-height height-units)
                       (.flip-button
                         :background-color ,(colors:colorscheme-fg *colorscheme*)
                         :color ,(colors:colorscheme-bg *colorscheme*))
                       ((:and .flip-button :focus)
                        :outline none)
                       ((:and .flip-button :hover)
89bed873
                        :font-size ,(combine-unit-q (* 0.25 header-height) height-units)))
e09b98ca
 
                    `(main
67a3d329
                       :border-left medium solid ,(colors:colorscheme-accent *colorscheme*)
89bed873
                       :height ,(combine-unit-q (- 100 header-height) height-units)
67a3d329
                       ("#add-form"
                         :box-shadow "0em" "0em" "0.2em" "0.2em" ,(colors:colorscheme-accent *colorscheme*)
89bed873
                         ((:or input button)
67a3d329
                          :background-color ,(colors:colorscheme-bg *colorscheme*)
                          :color ,(colors:colorscheme-fg *colorscheme*))
89bed873
                         )
                       )
e09b98ca
 
                    `((:or a (:and a :visited) (:and a :active) code.url)
                      :color ,(colors:colorscheme-fg-highlight *colorscheme*))
 
                    `(section#sidebar
89bed873
                       :transition opacity "0.5s" ease
e09b98ca
                       (ul.menu
                         ((li + li)
                          :border-top "thin" "solid" ,(colors:colorscheme-fg-highlight *colorscheme*))
                         ((:and li :hover)
                          :background-color ,(colors:colorscheme-hover-highlight *colorscheme*)
                          :color ,(colors:colorscheme-fg-highlight *colorscheme*))))
 
                    `(.feed
                       :border-bottom thick solid ,(colors:colorscheme-accent *colorscheme*)
                       :border-left none)
 
                    `(.link-header :background-color ,(colors:colorscheme-bg-highlight *colorscheme*))
                    `(.link
                       :border-top thin solid ,(colors:colorscheme-fg *colorscheme*)
                       :border-bottom none
 
 
                       (.link-info
                         :color ,(colors:colorscheme-fg-deemph *colorscheme*)
                         :border-bottom "thin" "solid" ,(colors:colorscheme-fg *colorscheme*)
                         ((:or a span)
                          :color inherit)
                         ((:and a :hover)
                          :color ,(colors:colorscheme-fg *colorscheme*))
                         ))
89bed873
 
e09b98ca
                    `((:and .feed-header :hover)
                      :background-color ,(colors:colorscheme-hover-highlight *colorscheme*))
 
                    `((.link.closed .link-header)
                      :background-color ,(colors:colorscheme-bg *colorscheme*))
 
                    `((:or (:and .link-header :hover) (.link.closed (:and .link-header :hover)))
                      :background-color ,(colors:colorscheme-hover-highlight *colorscheme*)))))
b99dd5be
         (declare (ignorable main-right-margin)) ; TODO: use this!!!
c5c94d7d
         `(200 (:content-type "text/css") (,ss))))))
e09b98ca
 
703fc701
 (load "route-atoms.lisp")
 
 (define-view json-feed (the-feeds)
   `(200 (:content-type "application/json" :cache-control "private, max-age=300")
     (,(jsonapi-encoder t the-feeds))))
 
 (define-controller json-feed (params)
   (ningle.context:with-context-variables (session)
     (let* ((user-info (gethash :app-user session))
            (*feeds* (if user-info (deserialize user-info) *feeds*)))
       (setf *userasdfs* user-info)
       *feeds*)) )
 
 (cl-oid-connect:def-route ("/demo" (params) :app *app*)
   (base-template-f t))
c5c94d7d
 
 (defroutes *app*
703fc701
   (("/feeds/json") (araneus:as-route 'json-feed))
c5c94d7d
   (("/theme/:scheme.css") (araneus::compose-route (css) css))
   (("/userinfo.json") (araneus::compose-route (id) userinfo))
   (("/login") (araneus::compose-route (id) login))
703fc701
   (("/logout") (araneus::compose-route (logout) redirect))
c5c94d7d
   (("/") (araneus:as-route 'root)))
b5a8e489
 
 (defun assoc-cdr-alternatives (alist alt1 alt2 &optional (test #'eql))
   (aif (cl-oid-connect:assoc-cdr alt1 alist test)
     it
     (cl-oid-connect:assoc-cdr alt2 alist test)))
 
 (cl-oid-connect::setup-oid-connect *app* (userinfo &rest args)
   (declare (ignore args) (optimize (speed 0) (safety 3) (debug 3)))
   (flet ((get-received-id (userinfo) (assoc-cdr-alternatives userinfo :id :sub))
c5c94d7d
          (get-db-user (received-id) (car (postmodern:select-dao 'reader_user (:= :foreign-id received-id))))
b5a8e489
          (get-first-name (userinfo) (assoc-cdr-alternatives userinfo :first--name :given--name))
          (get-last-name (userinfo) (assoc-cdr-alternatives userinfo :last--name :family--name))
          (get-link (userinfo) (assoc-cdr-alternatives userinfo :link :profile)))
 
     (postmodern:with-transaction ()
       (let ((received-id (get-received-id userinfo)))
         (aif (get-db-user received-id) it
           (postmodern:make-dao
             'reader_user
             :foreign-id received-id
             :first-name (get-first-name userinfo)
             :last-name (get-last-name userinfo)
             :name (cl-oid-connect:assoc-cdr :name userinfo)
             :email (cl-oid-connect:assoc-cdr :email userinfo)
             :gender (cl-oid-connect:assoc-cdr :gender userinfo)
             :link (get-link userinfo)
             :locale (cl-oid-connect:assoc-cdr :locale userinfo)))))))
89bed873
 
b99dd5be
 (defun update-feed (url)
7f1ede8b
   (with-whitespace-db
b99dd5be
     (postmodern:with-transaction ()
       (upsert-feed (make-rss-feed (with-xml-tags (plump:parse (drakma:http-request url))))))))
 
7f1ede8b
 (defmacro amapcar-with-body (list &body forms)
   (alexandria:once-only (list)
     `(mapcar (lambda (it) ,@forms)
              ,list)))
 
b99dd5be
 (defun update-all-feeds ()
7f1ede8b
   (with-whitespace-db
b99dd5be
     (let ((urls (postmodern:query (:select 'fetch-url :from 'rss-feed-store))))
7f1ede8b
       (amapcar-with-body urls
         (restart-case
           (apply #'update-feed it)
           (continue-updates () (warn (format nil "Skipping feed with fetch-url: ~s" it)))
           (use-value (v) (update-feed v)))))))
b99dd5be
 
 (defun minutes (minutes) (* minutes 60))
 
7f1ede8b
 (defun continue-updates (e)
   (declare (ignore e))
   (let ((restart (find-restart 'continue-updates)))
     (when restart
       (format t "continuing")
       (invoke-restart restart))))
 
b99dd5be
 (let (update-thread stop)
   (defun start-update-thread ()
     (setf update-thread
           (bordeaux-threads:make-thread
             (lambda ()
               (loop
7f1ede8b
                 (handler-bind ((drakma:parameter-error #'continue-updates))
                   (update-all-feeds))
b99dd5be
                 (sleep (ubiquitous:value 'update-frequency))
                 (when stop
                   (return-from nil nil))))
             :name "Whitespace Update Thread")))
   (defun stop-update-thread ()
     (setf stop t)
     (setf update-thread nil)))
 
c06fa69a
 (let ((handler nil))
   (defun stop () (clack:stop (pop handler)))
 
   (defun start (&optional tmp)
b5a8e489
     (cl-oid-connect:initialize-oid-connect
       (ubiquitous:value 'facebook 'secrets)
       (ubiquitous:value 'google 'secrets))
c06fa69a
     (let ((server (if (> (length tmp) 1)
                     (intern (string-upcase (elt tmp 1)) 'keyword)
                     :hunchentoot)))
       (push (clack:clackup
               (lack.builder:builder
                 :backtrace
                 :session
                 ;:csrf
                 (lambda (app) (lambda (env)
                                 (postmodern:with-connection *db-connection-info*
                                                             (funcall app env))))
                 (:static :path "/static/" :root #p"./static/")
                 *app*) :port 9090 :server server)
             handler)))
 
   (defun restart-clack ()
     (do () ((null handler)) (stop))
     (start)))
b5a8e489
 
c06fa69a