Browse code
Updating userinfo and the feed reading interface
fiddlerwoaroof authored on 04/09/2015 19:02:23
Showing 12 changed files
Showing 12 changed files
- .gitignore
- .gitmodules
- cl-oid-connect.lisp
- colors
- colors/README.txt
- colors/colors.asd
- colors/colors.lisp
- colors/package.lisp
- demo.lisp
- lass/main.lass
- static/css/main.css
- tables.lisp
... | ... |
@@ -1,4 +1,5 @@ |
1 | 1 |
;;;; cl-oid-connect.lisp |
2 |
+;;;; TODO: Need to refactor out server names!!! |
|
2 | 3 |
#| |
3 | 4 |
|Copyright (c) 2015 Edward Langley |
4 | 5 |
|All rights reserved. |
... | ... |
@@ -45,6 +46,7 @@ |
45 | 46 |
:properties '((auth-endpoint nil :accessor auth-endpoint) |
46 | 47 |
(token-endpoint nil :accessor token-endpoint) |
47 | 48 |
(userinfo-endpoint nil :accessor t) |
49 |
+ (auth-scope "openid profile email" :accessor t) |
|
48 | 50 |
(redirect-uri nil :accessor t)))) |
49 | 51 |
(sheeple:defmessage get-user-info (a b)) |
50 | 52 |
(sheeple:defmessage get-access-token (a b)) |
... | ... |
@@ -67,6 +69,7 @@ |
67 | 69 |
((auth-endpoint "https://www.facebook.com/dialog/oauth") |
68 | 70 |
(token-endpoint "https://graph.facebook.com/v2.3/oauth/access_token") |
69 | 71 |
(userinfo-endpoint "https://graph.facebook.com/v2.3/me") |
72 |
+ (auth-scope "email") |
|
70 | 73 |
(redirect-uri "http://srv2.elangley.org:9090/oidc_callback/facebook"))) |
71 | 74 |
|
72 | 75 |
(sheeple:defreply get-access-token ((endpoint-schema *fbook-endpoint-schema*) (code sheeple:=string=)) |
... | ... |
@@ -97,6 +100,7 @@ |
97 | 100 |
|
98 | 101 |
(setf (auth-endpoint schema) (assoc-cdr :authorization--endpoint discovery-document)) |
99 | 102 |
(setf (token-endpoint schema) (assoc-cdr :token--endpoint discovery-document)) |
103 |
+ (setf (userinfo-endpoint schema) (assoc-cdr :userinfo--endpoint discovery-document)) |
|
100 | 104 |
|
101 | 105 |
(if gui-p (sheeple:defreply get-user-info ((a schema)) (funcall gui a))) |
102 | 106 |
(if gat-p (sheeple:defreply get-access-token ((a schema) (b sheeple:=string=)) |
... | ... |
@@ -116,7 +120,7 @@ |
116 | 120 |
:parameters `(("client_id" . ,(client-id endpoint-schema)) |
117 | 121 |
("app_id" . ,(client-id endpoint-schema)) |
118 | 122 |
("response_type" . "code") |
119 |
- ("scope" . "email") |
|
123 |
+ ("scope" . ,(auth-scope endpoint-schema)) |
|
120 | 124 |
("redirect_uri" . ,(redirect-uri endpoint-schema)) |
121 | 125 |
("state" . ,state)))) |
122 | 126 |
|
... | ... |
@@ -163,7 +167,7 @@ |
163 | 167 |
(let* ((data (yason:parse fbook-info)) |
164 | 168 |
(client-id (gethash "client-id" data)) |
165 | 169 |
(secret (gethash "secret" data))) |
166 |
- (setf (client-id *FBOOK-INFO*) client-id) |
|
170 |
+ (setf (client-id *FBOOK-INFO*) client-id) |
|
167 | 171 |
(setf (secret *FBOOK-INFO*) secret)))) |
168 | 172 |
|
169 | 173 |
(defun load-google-info (loadfrom) |
... | ... |
@@ -191,6 +195,15 @@ |
191 | 195 |
:gat #'goog-get-access-token) |
192 | 196 |
(setf (redirect-uri *goog-endpoint-schema*) "http://srv2.elangley.org:9090/oidc_callback/google")) |
193 | 197 |
|
198 |
+(sheeple:defreply get-user-info ((endpoint-schema *goog-endpoint-schema*) (access-token sheeple:=string=)) |
|
199 |
+ (format t "getting user data: ~a~%" "blarg") |
|
200 |
+ (let ((endpoint (userinfo-endpoint endpoint-schema))) |
|
201 |
+ (cl-json:decode-json-from-string |
|
202 |
+ (drakma:http-request endpoint |
|
203 |
+ :parameters `(("alt" . "json") |
|
204 |
+ ("access_token" . ,access-token)) |
|
205 |
+ )))) |
|
206 |
+ |
|
194 | 207 |
|
195 | 208 |
(defun oauth2-login-middleware (&key google-info facebook-info) |
196 | 209 |
(let ((clack-env nil)) |
... | ... |
@@ -200,16 +213,16 @@ |
200 | 213 |
(load-goog-endpoint-schema) |
201 | 214 |
(load-google-info google-info) |
202 | 215 |
|
203 |
- (def-route ("/login/google" (params) :app app) |
|
204 |
- (with-context-variables (session) |
|
205 |
- (let ((state (gen-state 36))) |
|
206 |
- (setf (gethash :state session) state) |
|
207 |
- (with-endpoints *goog-endpoint-schema* |
|
208 |
- (setf (gethash :endpoint-schema session) *goog-endpoint-schema*) |
|
209 |
- (multiple-value-bind (content rcode headers) (do-auth-request *goog-endpoint-schema* state) |
|
210 |
- (if (< rcode 400) |
|
211 |
- `(302 (:location ,(cdr (assoc :location headers)))) |
|
212 |
- content)))))) |
|
216 |
+ (def-route ("/login/google" (params) :app app) |
|
217 |
+ (with-context-variables (session) |
|
218 |
+ (let ((state (gen-state 36))) |
|
219 |
+ (setf (gethash :state session) state) |
|
220 |
+ (with-endpoints *goog-endpoint-schema* |
|
221 |
+ (setf (gethash :endpoint-schema session) *goog-endpoint-schema*) |
|
222 |
+ (multiple-value-bind (content rcode headers) (do-auth-request *goog-endpoint-schema* state) |
|
223 |
+ (if (< rcode 400) |
|
224 |
+ `(302 (:location ,(cdr (assoc :location headers)))) |
|
225 |
+ content)))))) |
|
213 | 226 |
|
214 | 227 |
|
215 | 228 |
(def-route ("/login/facebook" (params) :app app) |
... | ... |
@@ -231,10 +244,14 @@ |
231 | 244 |
(with-context-variables (session) |
232 | 245 |
(with-endpoints *goog-endpoint-schema* |
233 | 246 |
(let* ((a-t (get-access-token *goog-endpoint-schema* code)) |
247 |
+ (access-token (assoc-cdr :access--token a-t)) ;; Argh |
|
234 | 248 |
(id-token (assoc-cdr :id--token a-t)) |
235 | 249 |
(decoded (cljwt:decode id-token :fail-if-unsupported nil))) |
236 |
- (setf (gethash :userinfo session) decoded) |
|
237 |
- '(302 (:location "/"))))) |
|
250 |
+ (setf (gethash :idtoken session) (get-user-info *goog-endpoint-schema* id-token)) |
|
251 |
+ (setf (gethash :accesstoken session) (get-user-info *goog-endpoint-schema* access-token)) |
|
252 |
+ (setf (gethash :userinfo session) (get-user-info *goog-endpoint-schema* access-token)) |
|
253 |
+ '(302 (:location "/")) |
|
254 |
+ ))) |
|
238 | 255 |
'(403 '() "Out, vile imposter!")))) |
239 | 256 |
|
240 | 257 |
|
... | ... |
@@ -252,7 +269,7 @@ |
252 | 269 |
|
253 | 270 |
(def-route ("/userinfo.json" (params) :app app) |
254 | 271 |
(with-context-variables (session) |
255 |
- (require-login |
|
272 |
+ (require-login |
|
256 | 273 |
(with-endpoints (gethash :endpoint-schema session) |
257 | 274 |
(cl-json:encode-json-to-string (gethash :userinfo session)))))) |
258 | 275 |
|
10 | 0 |
deleted file mode 100644 |
... | ... |
@@ -1,111 +0,0 @@ |
1 |
-;;;; colors.lisp |
|
2 |
-(defpackage #:colors |
|
3 |
- (:use #:cl) |
|
4 |
- (:export #:colorscheme #:palette *palette* |
|
5 |
- #:invert-palette |
|
6 |
- #:let-palette #:with-palette |
|
7 |
- #:accentize |
|
8 |
- #:colorscheme-bg #:colorscheme-bg-highlight |
|
9 |
- #:colorscheme-fg-deemph #:colorscheme-fg #:colorscheme-fg-highlight |
|
10 |
- #:colorscheme-accent |
|
11 |
- #:colorscheme-hover-highlight)) |
|
12 |
- |
|
13 |
-(in-package #:colors) |
|
14 |
-(declaim (optimize (debug 2) (safety 2) (speed 0))) |
|
15 |
- |
|
16 |
-;;; Generic utility macro TODO: separate these out!!! |
|
17 |
-(defmacro initialize-to (obj1-v obj2-v &body slot-swaps) |
|
18 |
- (alexandria:with-gensyms (obj1 obj2) |
|
19 |
- `(let* ((,obj1 ,obj1-v) |
|
20 |
- (,obj2 ,obj2-v)) |
|
21 |
- ,@(loop for (to from) in slot-swaps |
|
22 |
- collect `(setf (,to ,obj1) (,from ,obj2)))))) |
|
23 |
- |
|
24 |
-;;; This macro connects the "-" prefixed slots in the colorscheme class |
|
25 |
-;;; To the appropriate palette |
|
26 |
-(defmacro def-palette-accessor (scheme-slot scheme palette ) |
|
27 |
- `(progn |
|
28 |
- (defgeneric ,scheme-slot (,scheme)) |
|
29 |
- (defmethod ,scheme-slot ((,scheme colorscheme)) |
|
30 |
- (slot-value ,palette (,(intern (concatenate 'string "-" (symbol-name scheme-slot))) ,scheme))))) |
|
31 |
- |
|
32 |
- |
|
33 |
-;; &group interfaces |
|
34 |
-;;; Palette class and methods &group |
|
35 |
- |
|
36 |
-(defclass palette () ; solarized http://ethanschoonover.com/solarized |
|
37 |
- ((base03 :accessor palette-base03 :initform "#002b36") |
|
38 |
- (base02 :accessor palette-base02 :initform "#073642") |
|
39 |
- (base01 :accessor palette-base01 :initform "#586e75") |
|
40 |
- (base00 :accessor palette-base00 :initform "#657b83") |
|
41 |
- (base0 :accessor palette-base0 :initform "#839496") |
|
42 |
- (base1 :accessor palette-base1 :initform "#93a1a1") |
|
43 |
- (base2 :accessor palette-base2 :initform "#eee8d5") |
|
44 |
- (base3 :accessor palette-base3 :initform "#fdf6e3") |
|
45 |
- (yellow :accessor palette-yellow :initform "#b58900") |
|
46 |
- (orange :accessor palette-orange :initform "#cb4b16") |
|
47 |
- (red :accessor palette-red :initform "#dc322f") |
|
48 |
- (magenta :accessor palette-magenta :initform "#d33682") |
|
49 |
- (violet :accessor palette-violet :initform "#6c71c4") |
|
50 |
- (blue :accessor palette-blue :initform "#268bd2") |
|
51 |
- (cyan :accessor palette-cyan :initform "#2aa198") |
|
52 |
- (green :accessor palette-green :initform "#859900"))) |
|
53 |
- |
|
54 |
-(defgeneric invert-palette (palette)) |
|
55 |
- |
|
56 |
-;;; The palette var: this defaults to the solarized palette defined |
|
57 |
-;;; above, but can (and should) be temporarily rebound via the |
|
58 |
-;;; with-palette macro below. |
|
59 |
-(defparameter *palette* (make-instance 'palette)) |
|
60 |
- |
|
61 |
-(defmacro let-palette (palette &body body) |
|
62 |
- "Set custom palette in end-user code" |
|
63 |
- `(let ((*palette* ,palette)) |
|
64 |
- ,@body)) |
|
65 |
- |
|
66 |
-(defmacro with-palette ((place) &body body) |
|
67 |
- "Access the current palette" |
|
68 |
- `(let ((,place *palette*)) |
|
69 |
- ,@body)) |
|
70 |
- |
|
71 |
-;;; &endgroup |
|
72 |
-;;; &group Color scheme |
|
73 |
-(defclass colorscheme () |
|
74 |
- ((bg :accessor -colorscheme-bg :initform 'base03) |
|
75 |
- (bg-highlight :accessor -colorscheme-bg-highlight :initform 'base02) |
|
76 |
- (fg-deemph :accessor -colorscheme-fg-deemph :initform 'base01) |
|
77 |
- (fg :accessor -colorscheme-fg :initform 'base0 ) |
|
78 |
- (fg-highlight :accessor -colorscheme-fg-highlight :initform 'base1 ) |
|
79 |
- (hover-highlight :accessor -colorscheme-hover-highlight :initform 'base3 ) |
|
80 |
- (accent :accessor -colorscheme-accent :initform 'violet))) |
|
81 |
- |
|
82 |
-(defgeneric accentize (colorscheme accent)) |
|
83 |
- |
|
84 |
-(def-palette-accessor colorscheme-bg scheme *palette*) |
|
85 |
-(def-palette-accessor colorscheme-bg-highlight scheme *palette*) |
|
86 |
-(def-palette-accessor colorscheme-fg-deemph scheme *palette*) |
|
87 |
-(def-palette-accessor colorscheme-fg scheme *palette*) |
|
88 |
-(def-palette-accessor colorscheme-fg-highlight scheme *palette*) |
|
89 |
-(def-palette-accessor colorscheme-accent scheme *palette*) |
|
90 |
-(def-palette-accessor colorscheme-hover-highlight scheme *palette*) |
|
91 |
- |
|
92 |
-;;; &endgroup |
|
93 |
-;; &endgroup |
|
94 |
- |
|
95 |
-(defmethod invert-palette ((palette palette)) |
|
96 |
- (let ((result (make-instance 'palette))) |
|
97 |
- (initialize-to result palette |
|
98 |
- (palette-base03 palette-base3) |
|
99 |
- (palette-base02 palette-base2) |
|
100 |
- (palette-base01 palette-base1) |
|
101 |
- (palette-base00 palette-base0) |
|
102 |
- (palette-base0 palette-base00) |
|
103 |
- (palette-base1 palette-base01) |
|
104 |
- (palette-base2 palette-base02) |
|
105 |
- (palette-base3 palette-base03)) |
|
106 |
- result)) |
|
107 |
- |
|
108 |
-(defmethod accentize ((colorscheme colorscheme) accent) |
|
109 |
- (setf (colorscheme-accent colorscheme) (funcall accent colorscheme))) |
|
110 |
- |
|
111 |
-; vim: foldmethod=marker foldmarker=&group,&endgroup foldlevel=0 : |
... | ... |
@@ -1,25 +1,39 @@ |
1 | 1 |
(in-package :cl-user) |
2 |
-(ql:quickload :plump) |
|
3 |
-(ql:quickload :lquery) |
|
4 | 2 |
(ql:quickload :cl-markup) |
5 | 3 |
(ql:quickload :cl-oid-connect) |
6 | 4 |
(ql:quickload :colors) |
5 |
+(ql:quickload :lquery) |
|
6 |
+(ql:quickload :plump) |
|
7 |
+(ql:quickload :postmodern) |
|
7 | 8 |
|
8 | 9 |
(push (cons "application" "rdf+xml") drakma:*text-content-types*) |
9 | 10 |
(push (cons "application" "rss+xml") drakma:*text-content-types*) |
10 | 11 |
(push (cons "text" "rss+xml") drakma:*text-content-types*) |
11 | 12 |
|
13 |
+(lquery:define-lquery-list-function tag-name (nodes &rest tags) |
|
14 |
+ "Manipulate elements on the basis of there tag-name. |
|
15 |
+ With no arguments, return their names else return |
|
16 |
+ the corresponding tags." |
|
17 |
+ (if (null tags) |
|
18 |
+ (map 'vector #'plump:tag-name nodes) |
|
19 |
+ (apply #'vector |
|
20 |
+ (loop for node across nodes |
|
21 |
+ if (find (plump:tag-name node) tags :test #'string=) |
|
22 |
+ collect node)))) |
|
23 |
+ |
|
12 | 24 |
(defparameter *app* (make-instance 'ningle:<app>)) |
13 | 25 |
|
14 | 26 |
(defclass rss-feed () |
15 |
- ((feed :accessor rss-feed-feed |
|
16 |
- :initarg :feed) |
|
27 |
+ ((feed :accessor rss-feed-feed :initarg :feed) |
|
17 | 28 |
(channel :accessor rss-feed-channel) |
18 | 29 |
(title :accessor rss-feed-title) |
19 | 30 |
(link :accessor rss-feed-link) |
20 | 31 |
(description :accessor rss-feed-description) |
21 | 32 |
(items :accessor rss-feed-items))) |
22 | 33 |
|
34 |
+(defgeneric serialize (cls)) |
|
35 |
+ |
|
36 |
+ |
|
23 | 37 |
(defclass rss-item () |
24 | 38 |
((item :accessor rss-item-item :initarg :item) |
25 | 39 |
(title :accessor rss-item-title) |
... | ... |
@@ -33,6 +47,10 @@ |
33 | 47 |
(pub-date :accessor rss-item-pub-date) |
34 | 48 |
(source :accessor rss-item-source))) |
35 | 49 |
|
50 |
+(defmethod serialize ((obj rss-feed)) |
|
51 |
+ (postmodern:make-dao 'rss) |
|
52 |
+ ) |
|
53 |
+ |
|
36 | 54 |
(defmacro get-elements (feed &optional (filter nil)) |
37 | 55 |
(let ((feed-sym (gensym)) |
38 | 56 |
(filter-lis `(lambda (x) (and (plump-dom:element-p x) ,@(loop for x in filter |
... | ... |
@@ -44,11 +62,7 @@ |
44 | 62 |
`(get-elements ,feed ((lambda (x) (string= ,tagname (plump:tag-name x)))))) |
45 | 63 |
|
46 | 64 |
(defmacro extract-text (selector &optional (default "")) |
47 |
- (alexandria:with-gensyms (selector-s) |
|
48 |
- `(let ((,selector-s ,selector)) |
|
49 |
- (if (not (equalp #() (lquery:$ ,selector-s))) |
|
50 |
- (lquery:$ ,selector-s (text) (node)) |
|
51 |
- ,default)))) |
|
65 |
+ `(or (lquery:$ ,selector (text) (node)) ,default)) |
|
52 | 66 |
|
53 | 67 |
(defun make-rss-item (item) |
54 | 68 |
(lquery:initialize item) |
... | ... |
@@ -98,7 +112,7 @@ |
98 | 112 |
(defun make-rss-feed (feed) |
99 | 113 |
(lquery:initialize feed) |
100 | 114 |
(let* ((result (make-instance 'rss-feed :feed feed)) |
101 |
- (channel (lquery:$ "channel" (text) (node))) |
|
115 |
+ (channel (lquery:$ "channel" (node))) |
|
102 | 116 |
(title (lquery:$ "title" (text) (node))) |
103 | 117 |
(link (lquery:$ "link" (text) (node))) |
104 | 118 |
(description (lquery:$ "description" (text) (node))) |
... | ... |
@@ -125,17 +139,6 @@ |
125 | 139 |
:class "login-button google" |
126 | 140 |
(:a :href "/login/google" "Google")))))) |
127 | 141 |
|
128 |
-(lquery:define-lquery-list-function tag-name (nodes &rest tags) |
|
129 |
- "Manipulate elements on the basis of there tag-name. |
|
130 |
- With no arguments, return their names else return |
|
131 |
- the corresponding tags." |
|
132 |
- (if (null tags) |
|
133 |
- (map 'vector #'plump:tag-name nodes) |
|
134 |
- (apply #'vector |
|
135 |
- (loop for node across nodes |
|
136 |
- if (find (plump:tag-name node) tags :test #'string=) |
|
137 |
- collect node)))) |
|
138 |
- |
|
139 | 142 |
(defparameter *feed-urls* |
140 | 143 |
#( |
141 | 144 |
"http://www.reddit.com/r/lisp.rss" |
... | ... |
@@ -157,109 +160,6 @@ |
157 | 160 |
|
158 | 161 |
(defparameter *feeds* (map 'vector (lambda (x) (unwind-protect (make-rss-feed x))) *docs*)) |
159 | 162 |
|
160 |
-;;; this will be bound by calls to with-palette |
|
161 |
-;;; probably should be refactored out |
|
162 |
-(defparameter *palette* nil) |
|
163 |
- |
|
164 |
-(defparameter *colorscheme* (make-instance 'colors:colorscheme)) |
|
165 |
- |
|
166 |
-(cl-oid-connect:def-route ("/theme/dark.css" (params) :app *app*) |
|
167 |
- (colors:let-palette (make-instance 'colors:palette) |
|
168 |
- (eval '(get-theme-css)))) |
|
169 |
- |
|
170 |
-(cl-oid-connect:def-route ("/theme/light.css" (params) :app *app*) |
|
171 |
- (colors:let-palette (colors:invert-palette (make-instance 'colors:palette)) |
|
172 |
- (eval '(get-theme-css)))) |
|
173 |
- |
|
174 |
-(defun get-theme-css () |
|
175 |
- (colors:with-palette (*palette*) |
|
176 |
- (flet ((combine-unit-q (quant unit) (format nil "~d~a" quant unit))) |
|
177 |
- (let* ((header-height 9) |
|
178 |
- (height-units "vh") |
|
179 |
- (ss (lass:compile-and-write |
|
180 |
- `(* :color ,(colors:colorscheme-fg *colorscheme*)) |
|
181 |
- |
|
182 |
- `(body :background-color ,(colors:colorscheme-bg *colorscheme*)) |
|
183 |
- |
|
184 |
- `((:or h1 h2 h3) |
|
185 |
- :color ,(colors:colorscheme-fg-highlight *colorscheme*)) |
|
186 |
- `(.feed-header |
|
187 |
- :background-color ,(colors:colorscheme-bg-highlight *colorscheme*)) |
|
188 |
- |
|
189 |
- `((:or h4 h5 h6) :color ,(colors:colorscheme-fg-highlight *colorscheme*)) |
|
190 |
- |
|
191 |
- `(header |
|
192 |
- :border-bottom "thin" "solid" ,(colors:colorscheme-accent *colorscheme*) |
|
193 |
- :height ,(combine-unit-q header-height height-units) |
|
194 |
- :font-size ,(combine-unit-q (* 0.75 header-height) height-units) |
|
195 |
- :line-height ,(combine-unit-q header-height height-units) |
|
196 |
- (.flip-button |
|
197 |
- :float right |
|
198 |
- :width "3em" |
|
199 |
- :height "3em" |
|
200 |
- :padding-left "1em" |
|
201 |
- :padding-bottom "1em" |
|
202 |
- :border-bottom-left-radius "100%" |
|
203 |
- :border none |
|
204 |
- :transition "all 0.5s ease" |
|
205 |
- :background-color ,(colors:colorscheme-fg *colorscheme*) |
|
206 |
- :color ,(colors:colorscheme-bg *colorscheme*)) |
|
207 |
- ((:and .flip-button :focus) |
|
208 |
- :outline none) |
|
209 |
- ((:and .flip-button :hover) |
|
210 |
- :width "4em" |
|
211 |
- :height "4em" |
|
212 |
- :padding-left "2em" |
|
213 |
- :padding-bottom "2em") |
|
214 |
- ) |
|
215 |
- |
|
216 |
- `(main |
|
217 |
- :border-left thin solid ,(colors:colorscheme-accent *colorscheme*) |
|
218 |
- :height ,(combine-unit-q (- 100 header-height) height-units)) |
|
219 |
- |
|
220 |
- `((:or a (:and a :visited) (:and a :active) code.url) |
|
221 |
- :color ,(colors:colorscheme-fg-highlight *colorscheme*)) |
|
222 |
- |
|
223 |
- `(section#sidebar |
|
224 |
- (ul.menu |
|
225 |
- ((li + li) |
|
226 |
- :border-top "thin" "solid" ,(colors:colorscheme-fg-highlight *colorscheme*)) |
|
227 |
- ((:and li :hover) |
|
228 |
- :background-color ,(colors:colorscheme-hover-highlight *colorscheme*) |
|
229 |
- :color ,(colors:colorscheme-fg-highlight *colorscheme*)))) |
|
230 |
- |
|
231 |
- `(.feed |
|
232 |
- :border-bottom thin solid ,(colors:colorscheme-fg *colorscheme*) |
|
233 |
- :border-left none) |
|
234 |
- |
|
235 |
- `(.link-header :background-color ,(colors:colorscheme-bg-highlight *colorscheme*)) |
|
236 |
- `(.link |
|
237 |
- :border-top thin solid ,(colors:colorscheme-fg *colorscheme*) |
|
238 |
- :border-bottom none |
|
239 |
- |
|
240 |
- |
|
241 |
- (.link-info |
|
242 |
- :color ,(colors:colorscheme-fg-deemph *colorscheme*) |
|
243 |
- :border-bottom "thin" "solid" ,(colors:colorscheme-fg *colorscheme*) |
|
244 |
- ((:or a span) |
|
245 |
- :color inherit) |
|
246 |
- ((:and a :hover) |
|
247 |
- :color ,(colors:colorscheme-fg *colorscheme*)) |
|
248 |
- )) |
|
249 |
- `((:and .feed-header :hover) |
|
250 |
- :background-color ,(colors:colorscheme-hover-highlight *colorscheme*)) |
|
251 |
- `((.link.closed .link-header) |
|
252 |
- :background-color ,(colors:colorscheme-bg *colorscheme*)) |
|
253 |
- |
|
254 |
- `((:or (:and .link-header :hover) (.link.closed (:and .link-header))) |
|
255 |
- :background-color ,(colors:colorscheme-hover-highlight *colorscheme*)) |
|
256 |
- `(blah |
|
257 |
- :a ,(colors:colorscheme-fg-highlight *colorscheme*) |
|
258 |
- :a ,(colors:colorscheme-hover-highlight *colorscheme*) |
|
259 |
- :a ,(colors:colorscheme-bg-highlight *colorscheme*) |
|
260 |
- ) |
|
261 |
- ))) |
|
262 |
- `(200 (:content-type "text/css") ,ss))))) |
|
263 | 163 |
|
264 | 164 |
(defmacro item-markup (item) |
265 | 165 |
(alexandria:with-gensyms (item-s) |
... | ... |
@@ -313,7 +213,7 @@ |
313 | 213 |
(:link :rel "stylesheet" :href "/theme/light.css")) |
314 | 214 |
(:body |
315 | 215 |
(:header |
316 |
- (:button :class "flip-button" ">") |
|
216 |
+ (:button :class "flip-button" ">") |
|
317 | 217 |
(:h1 "What?") |
318 | 218 |
) |
319 | 219 |
(:section :id "content" |
... | ... |
@@ -354,10 +254,113 @@ |
354 | 254 |
(cl-oid-connect:def-route ("/" (params) :app *app*) |
355 | 255 |
(ningle.context:with-context-variables (session) |
356 | 256 |
;(cl-oid-connect:require-login |
357 |
- (let ((*feeds* (gethash :feeds session *feeds*))) |
|
358 |
- (base-template-f));) |
|
257 |
+ (cl-oid-connect:redirect-if-necessary session |
|
258 |
+ (let ((*feeds* (gethash :feeds session *feeds*))) |
|
259 |
+ (base-template-f)));) |
|
359 | 260 |
)) |
360 | 261 |
|
262 |
+;;; this will be bound by calls to with-palette |
|
263 |
+;;; probably should be refactored out |
|
264 |
+(defparameter *palette* nil) |
|
265 |
+ |
|
266 |
+(defparameter *colorscheme* (make-instance 'colors:colorscheme)) |
|
267 |
+ |
|
268 |
+(cl-oid-connect:def-route ("/theme/dark.css" (params) :app *app*) |
|
269 |
+ (colors:let-palette (make-instance 'colors:palette) |
|
270 |
+ (eval '(get-theme-css)))) |
|
271 |
+ |
|
272 |
+(cl-oid-connect:def-route ("/theme/light.css" (params) :app *app*) |
|
273 |
+ (colors:let-palette (colors:invert-palette (make-instance 'colors:palette)) |
|
274 |
+ (eval '(get-theme-css)))) |
|
275 |
+ |
|
276 |
+ |
|
277 |
+ |
|
278 |
+(defun get-theme-css () |
|
279 |
+ (colors:with-palette (*palette*) |
|
280 |
+ (flet ((combine-unit-q (quant unit) (format nil "~d~a" quant unit))) |
|
281 |
+ (let* ((header-height 9) |
|
282 |
+ (height-units "vh") |
|
283 |
+ (ss (lass:compile-and-write |
|
284 |
+ `(* :color ,(colors:colorscheme-fg *colorscheme*)) |
|
285 |
+ |
|
286 |
+ `(body :background-color ,(colors:colorscheme-bg *colorscheme*)) |
|
287 |
+ |
|
288 |
+ `((:or h1 h2 h3) |
|
289 |
+ :color ,(colors:colorscheme-fg-highlight *colorscheme*)) |
|
290 |
+ `(.feed-header |
|
291 |
+ :background-color ,(colors:colorscheme-bg-highlight *colorscheme*)) |
|
292 |
+ |
|
293 |
+ `((:or h4 h5 h6) :color ,(colors:colorscheme-fg-highlight *colorscheme*)) |
|
294 |
+ |
|
295 |
+ `(header |
|
296 |
+ :border-bottom "thin" "solid" ,(colors:colorscheme-accent *colorscheme*) |
|
297 |
+ :height ,(combine-unit-q header-height height-units) |
|
298 |
+ :font-size ,(combine-unit-q (* 0.75 header-height) height-units) |
|
299 |
+ :line-height ,(combine-unit-q header-height height-units) |
|
300 |
+ (.flip-button |
|
301 |
+ :float right |
|
302 |
+ :width "3em" |
|
303 |
+ :height "3em" |
|
304 |
+ :padding-left "1em" |
|
305 |
+ :padding-bottom "1em" |
|
306 |
+ :border-bottom-left-radius "100%" |
|
307 |
+ :border none |
|
308 |
+ :transition "all 2s cubic-bezier(0.175, 0.885, 0.32, 1.275)" |
|
309 |
+ :background-color ,(colors:colorscheme-fg *colorscheme*) |
|
310 |
+ :color ,(colors:colorscheme-bg *colorscheme*)) |
|
311 |
+ ((:and .flip-button :focus) |
|
312 |
+ :outline none) |
|
313 |
+ ((:and .flip-button :hover) |
|
314 |
+ :width "6em" |
|
315 |
+ :height "6em" |
|
316 |
+ :padding-left "4em" |
|
317 |
+ :padding-bottom "3em") |
|
318 |
+ ) |
|
319 |
+ |
|
320 |
+ `(main |
|
321 |
+ :border-left thin solid ,(colors:colorscheme-accent *colorscheme*) |
|
322 |
+ :height ,(combine-unit-q (- 100 header-height) height-units)) |
|
323 |
+ |
|
324 |
+ `((:or a (:and a :visited) (:and a :active) code.url) |
|
325 |
+ :color ,(colors:colorscheme-fg-highlight *colorscheme*)) |
|
326 |
+ |
|
327 |
+ `(section#sidebar |
|
328 |
+ (ul.menu |
|
329 |
+ ((li + li) |
|
330 |
+ :border-top "thin" "solid" ,(colors:colorscheme-fg-highlight *colorscheme*)) |
|
331 |
+ ((:and li :hover) |
|
332 |
+ :background-color ,(colors:colorscheme-hover-highlight *colorscheme*) |
|
333 |
+ :color ,(colors:colorscheme-fg-highlight *colorscheme*)))) |
|
334 |
+ |
|
335 |
+ `(.feed |
|
336 |
+ :border-bottom thick solid ,(colors:colorscheme-accent *colorscheme*) |
|
337 |
+ :border-left none) |
|
338 |
+ |
|
339 |
+ `(.link-header :background-color ,(colors:colorscheme-bg-highlight *colorscheme*)) |
|
340 |
+ `(.link |
|
341 |
+ :border-top thin solid ,(colors:colorscheme-fg *colorscheme*) |
|
342 |
+ :border-bottom none |
|
343 |
+ |
|
344 |
+ |
|
345 |
+ (.link-info |
|
346 |
+ :color ,(colors:colorscheme-fg-deemph *colorscheme*) |
|
347 |
+ :border-bottom "thin" "solid" ,(colors:colorscheme-fg *colorscheme*) |
|
348 |
+ ((:or a span) |
|
349 |
+ :color inherit) |
|
350 |
+ ((:and a :hover) |
|
351 |
+ :color ,(colors:colorscheme-fg *colorscheme*)) |
|
352 |
+ )) |
|
353 |
+ |
|
354 |
+ `((:and .feed-header :hover) |
|
355 |
+ :background-color ,(colors:colorscheme-hover-highlight *colorscheme*)) |
|
356 |
+ |
|
357 |
+ `((.link.closed .link-header) |
|
358 |
+ :background-color ,(colors:colorscheme-bg *colorscheme*)) |
|
359 |
+ |
|
360 |
+ `((:or (:and .link-header :hover) (.link.closed (:and .link-header :hover))) |
|
361 |
+ :background-color ,(colors:colorscheme-hover-highlight *colorscheme*))))) |
|
362 |
+ `(200 (:content-type "text/css") ,ss))))) |
|
363 |
+ |
|
361 | 364 |
(defvar *handler* nil) |
362 | 365 |
|
363 | 366 |
(defun stop () |
... | ... |
@@ -11,6 +11,7 @@ |
11 | 11 |
:padding "0px") |
12 | 12 |
|
13 | 13 |
(body |
14 |
+ :transition "background-color 0.25s ease" |
|
14 | 15 |
:font-family "Lato") |
15 | 16 |
|
16 | 17 |
(((:or main "#sidebar" .feed) > (:or ul ol)) |
... | ... |
@@ -53,13 +54,14 @@ |
53 | 54 |
:font-variant small-caps |
54 | 55 |
(li |
55 | 56 |
(a |
56 |
- :width "100%" |
|
57 |
+ :color inherit |
|
57 | 58 |
:display block |
59 |
+ :font-size "125%" |
|
60 |
+ :font-weight 700 |
|
58 | 61 |
:padding "0.5em" |
59 |
- :color inherit |
|
60 | 62 |
:text-decoration none |
61 |
- :font-weight 700 |
|
62 |
- :font-size "125%" |
|
63 |
+ :transition "background-color 0.25s ease" |
|
64 |
+ :width "100%" |
|
63 | 65 |
)))) |
64 | 66 |
|
65 | 67 |
(main |
... | ... |
@@ -73,6 +75,7 @@ |
73 | 75 |
((:or h2 h3) :padding "0.62em") |
74 | 76 |
(h2 :padding-bottom "0.38em") |
75 | 77 |
(h3 :padding-top "0.38em") |
78 |
+ :transition "background-color 0.25s ease" |
|
76 | 79 |
:padding-bottom "0em") |
77 | 80 |
|
78 | 81 |
((.link.closed .link-content) |
... | ... |
@@ -91,6 +94,7 @@ |
91 | 94 |
:padding "1em" |
92 | 95 |
:padding-bottom "0em" |
93 | 96 |
:cursor pointer |
97 |
+ :transition "background-color 0.25s ease" |
|
94 | 98 |
(h4 |
95 | 99 |
:margin-bottom "0.5em" |
96 | 100 |
:display "inline-block")) |
... | ... |
@@ -1 +1 @@ |
1 |
-@import url(https://fonts.googleapis.com/css?family=Lato:400,100,300,400italic,300italic,700,700italic,900&subset=latin,latin-ext);@import url(https://fonts.googleapis.com/css?family=Caudex);.feed,main{-webkit-backface-visibility:hidden;-webkit-transform:translateZ(0);}*{box-sizing:border-box;margin:0px;padding:0px;}body{font-family:Lato;}main > ul,main > ol,#sidebar > ul,#sidebar > ol,.feed > ul,.feed > ol{list-style:none;margin:0px;}ul{margin:1em;}h1,h2{font-size:153.9%;}:h3{font-size:146.5%;}h4,h5,h6{font-size:138.5%;}ul + h1,ul + h2,ul + h3,ul + h4,ul + h5,ul + h6{width:initial;}header{color:white;}header h1{font-family:Caudex;font-size:inherit;margin-top:0em;padding-left:10vw;font-weight:200;}#sidebar,main{border-top:none;}section#sidebar{width:38vw;height:90vh;position:fixed;overflow:auto;}section#sidebar ul.menu{text-align:right;font-variant:small-caps;}section#sidebar ul.menu li a{width:100%;display:block;padding:0.5em;color:inherit;text-decoration:none;font-weight:700;font-size:125%;}main{width:62vw;float:right;clear:right;overflow-x:hidden;overflow-y:scroll;}.feed-header{padding-bottom:0em;}.feed-header h2,.feed-header h3{padding:0.62em;}.feed-header h2{padding-bottom:0.38em;}.feed-header h3{padding-top:0.38em;}.link.closed .link-content{max-height:0px;padding:0em;}.link.closed{padding-bottom:0em;}.link{text-decoration:none;display:block;overflow:hidden;font-size:0.8em;}.link .link-header{padding:1em;padding-bottom:0em;cursor:pointer;}.link .link-header h4{margin-bottom:0.5em;display:inline-block;}.link .link-info{margin-left:-1em;margin-right:-1em;padding-left:1em;padding-right:1em;padding-bottom:0.32em;}.link .link-info .link-url{float:left;}.link .link-info .link-date{float:right;display:block;}.link .link-info:after{content:" ";display:block;clear:both;}.link .link-content{-moz-transition:max-height 0.5s ease;-o-transition:max-height 0.5s ease;-webkit-transition:max-height 0.5s ease;-ms-transition:max-height 0.5s ease;transition:max-height 0.5s ease;}.link .link-content > div{padding:1em;}.feed.closed .post-list{max-height:0px;padding:0em;}.feed:first-child{border-top:none;}.feed{overflow:hidden;}.feed .post-list{-moz-transition:max-height 0.5s ease;-o-transition:max-height 0.5s ease;-webkit-transition:max-height 0.5s ease;-ms-transition:max-height 0.5s ease;transition:max-height 0.5s ease;} |
|
2 | 1 |
\ No newline at end of file |
2 |
+@import url(https://fonts.googleapis.com/css?family=Lato:400,100,300,400italic,300italic,700,700italic,900&subset=latin,latin-ext);@import url(https://fonts.googleapis.com/css?family=Caudex);.feed,main{-webkit-backface-visibility:hidden;-webkit-transform:translateZ(0);}*{box-sizing:border-box;margin:0px;padding:0px;}body{-moz-transition:background-color 0.25s ease;-o-transition:background-color 0.25s ease;-webkit-transition:background-color 0.25s ease;-ms-transition:background-color 0.25s ease;transition:background-color 0.25s ease;font-family:Lato;}main > ul,main > ol,#sidebar > ul,#sidebar > ol,.feed > ul,.feed > ol{list-style:none;margin:0px;}ul{margin:1em;}h1,h2{font-size:153.9%;}:h3{font-size:146.5%;}h4,h5,h6{font-size:138.5%;}ul + h1,ul + h2,ul + h3,ul + h4,ul + h5,ul + h6{width:initial;}header{color:white;}header h1{font-family:Caudex;font-size:inherit;margin-top:0em;padding-left:10vw;font-weight:200;}#sidebar,main{border-top:none;}section#sidebar{width:38vw;height:90vh;position:fixed;overflow:auto;}section#sidebar ul.menu{text-align:right;font-variant:small-caps;}section#sidebar ul.menu li a{color:inherit;display:block;font-size:125%;font-weight:700;padding:0.5em;text-decoration:none;-moz-transition:background-color 0.25s ease;-o-transition:background-color 0.25s ease;-webkit-transition:background-color 0.25s ease;-ms-transition:background-color 0.25s ease;transition:background-color 0.25s ease;width:100%;}main{width:62vw;float:right;clear:right;overflow-x:hidden;overflow-y:scroll;}.feed-header{-moz-transition:background-color 0.25s ease;-o-transition:background-color 0.25s ease;-webkit-transition:background-color 0.25s ease;-ms-transition:background-color 0.25s ease;transition:background-color 0.25s ease;padding-bottom:0em;}.feed-header h2,.feed-header h3{padding:0.62em;}.feed-header h2{padding-bottom:0.38em;}.feed-header h3{padding-top:0.38em;}.link.closed .link-content{max-height:0px;padding:0em;}.link.closed{padding-bottom:0em;}.link{text-decoration:none;display:block;overflow:hidden;font-size:0.8em;}.link .link-header{padding:1em;padding-bottom:0em;cursor:pointer;-moz-transition:background-color 0.25s ease;-o-transition:background-color 0.25s ease;-webkit-transition:background-color 0.25s ease;-ms-transition:background-color 0.25s ease;transition:background-color 0.25s ease;}.link .link-header h4{margin-bottom:0.5em;display:inline-block;}.link .link-info{margin-left:-1em;margin-right:-1em;padding-left:1em;padding-right:1em;padding-bottom:0.32em;}.link .link-info .link-url{float:left;}.link .link-info .link-date{float:right;display:block;}.link .link-info:after{content:" ";display:block;clear:both;}.link .link-content{-moz-transition:max-height 0.5s ease;-o-transition:max-height 0.5s ease;-webkit-transition:max-height 0.5s ease;-ms-transition:max-height 0.5s ease;transition:max-height 0.5s ease;}.link .link-content > div{padding:1em;}.feed.closed .post-list{max-height:0px;padding:0em;}.feed:first-child{border-top:none;}.feed{overflow:hidden;}.feed .post-list{-moz-transition:max-height 0.5s ease;-o-transition:max-height 0.5s ease;-webkit-transition:max-height 0.5s ease;-ms-transition:max-height 0.5s ease;transition:max-height 0.5s ease;} |
|
3 | 3 |
\ No newline at end of file |
4 | 4 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,50 @@ |
1 |
+(ql:quickload :postmodern) |
|
2 |
+ |
|
3 |
+(defclass rss-feed-store () |
|
4 |
+ ((id :col-type serial :initarg :id :accessor rfs-id) |
|
5 |
+ (title :col-type text :initarg :title :accessor rss-feed-title :col-default "") |
|
6 |
+ (link :col-type text :initarg :link :accessor rss-feed-line :col-default "") |
|
7 |
+ (description :col-type text :initarg :description :accessor rss-feed-description :col-default "")) |
|
8 |
+ (:metaclass postmodern:dao-class) |
|
9 |
+ (:table-name "rssFeed") |
|
10 |
+ (:unique link) |
|
11 |
+ (:keys id)) |
|
12 |
+ |
|
13 |
+(postmodern:deftable rss-feed-store |
|
14 |
+ (postmodern:!dao-def)) |
|
15 |
+ |
|
16 |
+; (postmodern:create-table 'rss-feed-store) |
|
17 |
+ |
|
18 |
+(defclass rss-item-store () |
|
19 |
+ ((id :col-type serial :initarg :id :accessor ris-id) |
|
20 |
+ (title :col-type text :initarg :title :accessor ris-title :col-default "") |
|
21 |
+ (link :col-type text :initarg :link :accessor ris-link :col-default "") |
|
22 |
+ (description :col-type text :initarg :description :accessor ris-description :col-default "") |
|
23 |
+ (comments :col-type text :initarg :comments :accessor ris-comments :col-default "") |
|
24 |
+ (enclosure :col-type text :initarg :enclosure :accessor ris-enclosure :col-default "") |
|
25 |
+ (guid :col-type text :initarg :guid :accessor ris-guid :col-default "") |
|
26 |
+ (pub-date :col-type text :initarg :pub-date :accessor ris-pub-date :col-default "") |
|
27 |
+ (source :col-type text :initarg :source :accessor ris-source :col-default "") |
|
28 |
+ (feed :col-type integer :initarg :feed :accessor ris-feed)) |
|
29 |
+ (:metaclass postmodern:dao-class) |
|
30 |
+ (:keys id)) |
|
31 |
+ |
|
32 |
+(postmodern:deftable rss-item-store |
|
33 |
+ (postmodern:!dao-def) |
|
34 |
+ (postmodern:!foreign "rssfeed" "feed" "id" :on-delete :cascade :on-update :cascade)) |
|
35 |
+ |
|
36 |
+ |
|
37 |
+; (postmodern:create-table 'rss-item-store) |
|
38 |
+ |
|
39 |
+(defclass user () |
|
40 |
+ ((id) |
|
41 |
+ (foreign-id) |
|
42 |
+ (email) |
|
43 |
+ (first-name) |
|
44 |
+ (gender) |
|
45 |
+ (last-name) |
|
46 |
+ (link) |
|
47 |
+ (locale) |
|
48 |
+ |
|
49 |
+ ) |
|
50 |
+ ) |