Browse code
Basic functionality down.
Working prototype stage
fiddlerwoaroof authored on 23/01/2016 01:26:20Showing 9 changed files
- README.txt
- lyangulus.asd
- lyangulus.lisp
- package.lisp
- static/css/login.css
- static/css/main.css
- static/index.html
- static/index.mustache.html
- static/login.html
0 | 2 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,20 @@ |
1 |
+;;;; lyangulus.asd |
|
2 |
+ |
|
3 |
+(asdf:defsystem #:lyangulus |
|
4 |
+ :description "A simple link-submission-to-rss service" |
|
5 |
+ :author "socraticum" |
|
6 |
+ :license "MIT" |
|
7 |
+ :depends-on (#:alimenta |
|
8 |
+ #:cl-oid-connect |
|
9 |
+ #:cl-mustache |
|
10 |
+ #:parenscript |
|
11 |
+ #:alexandria |
|
12 |
+ #:anaphora |
|
13 |
+ #:ironclad |
|
14 |
+ #:ubiquitous |
|
15 |
+ #:ningle) |
|
16 |
+ :serial t |
|
17 |
+ :components ((:file "package") |
|
18 |
+ (:file "lyangulus"))) |
|
19 |
+ |
|
20 |
+;; vim: set ft=lisp: |
0 | 21 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,214 @@ |
1 |
+(declaim (optimize (debug 3) (speed 0 ) (safety 3)) ) |
|
2 |
+;;;; lyangulus.lisp |
|
3 |
+ |
|
4 |
+(in-package #:lyangulus) |
|
5 |
+(defparameter *submissions* nil) |
|
6 |
+(defparameter *by-distinct* (make-hash-table :test #'equalp)) |
|
7 |
+(defparameter *users* (make-hash-table :test #'equalp)) |
|
8 |
+ |
|
9 |
+;;; "lyangulus" goes here. Hacks and glory await! |
|
10 |
+ |
|
11 |
+(defun current-date-string () |
|
12 |
+ "Returns current date as a string." |
|
13 |
+ (multiple-value-bind (sec min hr day mon yr dow dst-p tz) |
|
14 |
+ (get-decoded-time) |
|
15 |
+ (declare (ignore dow dst-p)) |
|
16 |
+ (format nil "~4,'0d-~2,'0d-~2,'0d ~2,'0d:~2,'0d:~2,'0d ~2,'0d" yr mon day hr min sec tz))) |
|
17 |
+ |
|
18 |
+ |
|
19 |
+(defclass user () |
|
20 |
+ ((uid :initarg :uid) |
|
21 |
+ (email :initarg :email) |
|
22 |
+ (name :initarg :name) |
|
23 |
+ (moderator :initarg :moderator :initform nil))) |
|
24 |
+ |
|
25 |
+(defun user-alist (user) |
|
26 |
+ (with-slots (email name moderator) user |
|
27 |
+ `(("name" . ,name) |
|
28 |
+ ("email" . ,email) |
|
29 |
+ ("moderator" . ,moderator)))) |
|
30 |
+ |
|
31 |
+(defclass submission () |
|
32 |
+ ((headline :initarg :headline :initform "" :accessor s-headline) |
|
33 |
+ (url :initarg :url :initform "" :accessor s-url) |
|
34 |
+ (date :initarg :date :initform "" :accessor s-date) |
|
35 |
+ (approved :initarg :approved :initform "" :accessor s-approved))) |
|
36 |
+ |
|
37 |
+(defmethod print-object ((obj user) s) |
|
38 |
+ (print-unreadable-object (obj s :type t :identity t) |
|
39 |
+ (with-slots (uid email name moderator) obj |
|
40 |
+ (format s "U: ~s E: ~s N: ~s M: ~s" uid email name moderator)))) |
|
41 |
+ |
|
42 |
+(defmethod print-object ((obj submission) s) |
|
43 |
+ (print-unreadable-object (obj s :type t :identity t) |
|
44 |
+ (with-slots (headline url approved) obj |
|
45 |
+ (format s "H: ~s U: ~s A: ~s" headline url approved)))) |
|
46 |
+ |
|
47 |
+(defun make-submission (headline url &key (approved "")) |
|
48 |
+ (make-instance 'submission :headline headline :url url :approved approved |
|
49 |
+ :date )) |
|
50 |
+ |
|
51 |
+(defun submission-alist (submission) |
|
52 |
+ `(("headline". ,(s-headline submission)) |
|
53 |
+ ("url" . ,(s-url submission)) |
|
54 |
+ ("date" . ,(s-url submission)) |
|
55 |
+ ("approved" . ,(s-approved submission)))) |
|
56 |
+ |
|
57 |
+(defun alist-submission (alist &key nil-if-exists (modify t)) |
|
58 |
+ (let* ((result (make-submission (cdr (assoc :headline alist :test #'string-equal)) |
|
59 |
+ (cdr (assoc :url alist :test #'string-equal)) |
|
60 |
+ :approved (aif (cdr (assoc :approved alist :test #'string-equal)) it ""))) |
|
61 |
+ (key (cons (s-headline result) (s-url result)))) |
|
62 |
+ (aif (gethash key *by-distinct*) |
|
63 |
+ (progn |
|
64 |
+ (when modify |
|
65 |
+ (setf (s-url it) (s-url result) |
|
66 |
+ (s-headline it) (s-headline result))) |
|
67 |
+ (if nil-if-exists nil it)) |
|
68 |
+ (progn |
|
69 |
+ (setf (gethash key *by-distinct*) result) |
|
70 |
+ result)))) |
|
71 |
+ |
|
72 |
+ |
|
73 |
+(defun get-moderated (feeds) |
|
74 |
+ (remove-if (lambda (x) (not (equal x "approved"))) feeds :key #'s-approved)) |
|
75 |
+ |
|
76 |
+(defparameter *app* (make-instance 'ningle:<app>)) |
|
77 |
+ |
|
78 |
+(defmacro i-lambda ((&rest args) &body body) |
|
79 |
+ `(lambda (,@args) |
|
80 |
+ (declare (ignorable ,@args)) |
|
81 |
+ ,@body)) |
|
82 |
+ |
|
83 |
+(defun render-mustache (fn data) |
|
84 |
+ (with-open-file (s (truename fn)) |
|
85 |
+ (let ((template (make-string (file-length s)))) |
|
86 |
+ (read-sequence template s) |
|
87 |
+ (mustache:render* template data)))) |
|
88 |
+ |
|
89 |
+(setf (ningle:route *app* "/") |
|
90 |
+ (i-lambda (params) |
|
91 |
+ (ningle.context:with-context-variables (session) |
|
92 |
+ (handler-case |
|
93 |
+ (cl-oid-connect.utils:ensure-logged-in |
|
94 |
+ (cl-oid-connect.utils:redirect-if-necessary session |
|
95 |
+ (render-mustache #p"static/index.mustache.html" |
|
96 |
+ `((:links . ,(mapcar #'submission-alist *submissions*)) |
|
97 |
+ (:user . ,(user-alist (gethash :app-user session))))))) |
|
98 |
+ (cl-oid-connect.utils:user-not-logged-in (c) (render-mustache #p"static/index.mustache.html" |
|
99 |
+ nil)))))) |
|
100 |
+ |
|
101 |
+(defun submit (params) |
|
102 |
+ (awhen (alist-submission params :nil-if-exists t) |
|
103 |
+ (format t "~s <<<" it) |
|
104 |
+ (push it *submissions*))) |
|
105 |
+ |
|
106 |
+(defun get-by-key (headline url) |
|
107 |
+ (gethash (cons headline url) *by-distinct*)) |
|
108 |
+ |
|
109 |
+(setf (ningle:route *app* "/murmuro" :method :POST) |
|
110 |
+ (i-lambda (params) |
|
111 |
+ (sleep 0.01) |
|
112 |
+ (submit params) |
|
113 |
+ '(302 (:location "/") ("Done")))) |
|
114 |
+ |
|
115 |
+(setf (ningle:route *app* "/curo" :method :POST) |
|
116 |
+ (i-lambda (params) |
|
117 |
+ (cl-oid-connect.utils:require-login |
|
118 |
+ (alet (alist-submission params :modify nil) |
|
119 |
+ (let ((approval (string-downcase (cdr (assoc "approved" params :test #'equalp))))) |
|
120 |
+ (setf (s-approved it) |
|
121 |
+ (if (equal approval "+") "approved" |
|
122 |
+ (if (equal approval "-") "rejected")))))) |
|
123 |
+ '(302 (:location "/") ("Done")))) |
|
124 |
+ |
|
125 |
+(setf (ningle:route *app* "/login" :method :GET) |
|
126 |
+ (i-lambda (params) |
|
127 |
+ `(200 () |
|
128 |
+ (,(cl-who:with-html-output-to-string (s) |
|
129 |
+ (:html |
|
130 |
+ (:head |
|
131 |
+ (:title "Login") |
|
132 |
+ (:link :rel "stylesheet" :href "/static/css/login.css")) |
|
133 |
+ (:body |
|
134 |
+ (:h1 "In Angulis") |
|
135 |
+ (:div :class "login-buttons" |
|
136 |
+ (:a :class "facebook" :href "/login/facebook" "Login With Facebook"))))))))) |
|
137 |
+ |
|
138 |
+(cl-oid-connect:def-route ("/logout" (params) :app *app*) |
|
139 |
+ (declare (ignore params)) |
|
140 |
+ (ningle:with-context-variables (session) |
|
141 |
+ (setf (gethash :userinfo session) nil) |
|
142 |
+ '(302 (:location "/")))) |
|
143 |
+ |
|
144 |
+(defun get-feed-guid (item) |
|
145 |
+ (with-slots (alimenta:title alimenta:link) item |
|
146 |
+ (let ((hasher (ironclad:make-digest 'ironclad:sha256))) |
|
147 |
+ (ironclad:update-digest hasher (ironclad:ascii-string-to-byte-array alimenta:title)) |
|
148 |
+ (ironclad:update-digest hasher (ironclad:ascii-string-to-byte-array alimenta:link)) |
|
149 |
+ (ironclad:byte-array-to-hex-string (ironclad:produce-digest hasher))))) |
|
150 |
+ |
|
151 |
+(setf (ningle:route *app* "/feed" :method :GET) |
|
152 |
+ (i-lambda (params) |
|
153 |
+ (let ((feed (alimenta::make-feed :title "In Angulis" :link "http://srv2.elangley.org:9090/feed" |
|
154 |
+ :description "Locus in quo sunt illi qui murmurant in angulis"))) |
|
155 |
+ (loop for submission in (reverse (get-moderated *submissions*)) |
|
156 |
+ do (alimenta::add-item-to-feed feed |
|
157 |
+ :title (s-headline submission) |
|
158 |
+ :link (s-url submission) |
|
159 |
+ :date (current-date-string) |
|
160 |
+ :next-id #'get-feed-guid |
|
161 |
+ :content "")) |
|
162 |
+ `(200 (:content-type "application/rss+xml") (,(plump:serialize (alimenta:generate-xml feed) nil)))))) |
|
163 |
+ |
|
164 |
+(setf (ningle:route *app* "/firehose" :method :GET) |
|
165 |
+ (i-lambda (params) |
|
166 |
+ (let ((feed (alimenta::make-feed :title "In Angulis" :link "http://srv2.elangley.org:9090/feed" |
|
167 |
+ :description "Locus in quo sunt illi qui murmurant in angulis"))) |
|
168 |
+ (loop for submission in (reverse *submissions*) |
|
169 |
+ do (alimenta::add-item-to-feed feed |
|
170 |
+ :title (s-headline submission) |
|
171 |
+ :link (s-url submission) |
|
172 |
+ :date (current-date-string) |
|
173 |
+ :next-id #'get-feed-guid |
|
174 |
+ :content "")) |
|
175 |
+ `(200 (:content-type "application/rss+xml") (,(plump:serialize (alimenta:generate-xml feed) nil)))))) |
|
176 |
+ |
|
177 |
+(cl-oid-connect::setup-oid-connect *app* (userinfo &rest args) |
|
178 |
+ (declare (ignore args)) |
|
179 |
+ (let ((id (cdr (assoc :id userinfo)))) |
|
180 |
+ (unless (gethash id *users*) |
|
181 |
+ (setf (gethash id *users*) |
|
182 |
+ (alet (make-instance 'user) |
|
183 |
+ (with-slots (uid name email) it |
|
184 |
+ (prog1 it |
|
185 |
+ (setf uid id |
|
186 |
+ name (cdr (assoc :name userinfo)) |
|
187 |
+ email (cdr (assoc :email userinfo)))))))) |
|
188 |
+ (gethash id *users*))) |
|
189 |
+ |
|
190 |
+(let ((handler nil)) |
|
191 |
+ (ubiquitous:restore :whitespace) |
|
192 |
+ (defun stop () (clack:stop (pop handler))) |
|
193 |
+ |
|
194 |
+ (defun start (&optional tmp) |
|
195 |
+ (cl-oid-connect:initialize-oid-connect |
|
196 |
+ (ubiquitous:value 'facebook 'secrets) |
|
197 |
+ (ubiquitous:value 'google 'secrets)) |
|
198 |
+ (let ((server (if (> (length tmp) 1) |
|
199 |
+ (intern (string-upcase (elt tmp 1)) 'keyword) |
|
200 |
+ :hunchentoot))) |
|
201 |
+ (push (clack:clackup |
|
202 |
+ (lack.builder:builder |
|
203 |
+ :session |
|
204 |
+ (:static :path "/static/" :root #p"./static/") |
|
205 |
+ :backtrace |
|
206 |
+ *app* |
|
207 |
+ ) |
|
208 |
+ :port 9090 |
|
209 |
+ :server server) |
|
210 |
+ handler))) |
|
211 |
+ |
|
212 |
+ (defun restart-clack () |
|
213 |
+ (do () ((null handler)) (stop)) |
|
214 |
+ (start))) |
0 | 6 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,65 @@ |
1 |
+@import url(https://fonts.googleapis.com/css?family=Alegreya+Sans+SC|MedievalSharp&subset=latin,latin-ext); |
|
2 |
+ |
|
3 |
+* { |
|
4 |
+ box-sizing: border-box; |
|
5 |
+ margin: 0px; |
|
6 |
+ padding: 0px; |
|
7 |
+ -webkit-font-feature-settings: 'kern' 1, 'liga' 1; |
|
8 |
+ -moz-font-feature-settings: 'kern' 1; |
|
9 |
+ -o-font-feature-settings: 'kern' 1; |
|
10 |
+ text-rendering: geometricPrecision; |
|
11 |
+ transition: color 0.2s ease-in-out, |
|
12 |
+ background-color 0.2s ease-in-out; |
|
13 |
+} |
|
14 |
+ |
|
15 |
+body { |
|
16 |
+ position: relative; |
|
17 |
+ font-size: 14px; |
|
18 |
+ font-family: 'Alegreya Sans SC', sans-serif; |
|
19 |
+ padding-bottom: 6em; |
|
20 |
+} |
|
21 |
+ |
|
22 |
+h1 { |
|
23 |
+ letter-spacing: 6px; |
|
24 |
+ padding: 0.5em; |
|
25 |
+ margin: 0px; |
|
26 |
+ margin-top: 0.5em; |
|
27 |
+ margin-bottom: 1em; |
|
28 |
+ font-size: 3em; |
|
29 |
+ background: white; |
|
30 |
+ width: 100%; |
|
31 |
+ z-index: 100; |
|
32 |
+ text-align: center; |
|
33 |
+ font-variant: small-caps; |
|
34 |
+ font-family: 'MedievalSharp' |
|
35 |
+} |
|
36 |
+ |
|
37 |
+ |
|
38 |
+.login-buttons { |
|
39 |
+ text-align: center; |
|
40 |
+} |
|
41 |
+ |
|
42 |
+.login-buttons a.facebook { |
|
43 |
+ border: 3px double #888; |
|
44 |
+ font-size: 1.5em; |
|
45 |
+ padding: 1em; |
|
46 |
+ display: inline-block; |
|
47 |
+ text-decoration: none; |
|
48 |
+ color: white; |
|
49 |
+ text-shadow: 0em 0em 0.1em black; |
|
50 |
+ |
|
51 |
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAABUCAMAAACfvxb8AAAAeFBMVEVOaaJMZ6FIZJ89W5lBXps8WplDYJxNaKFKZqBGYp5HY54/XJpCX5xFYZ07WZg+XJpLZqBAXZtJZZ9EYZ1NaKI8Wpg9WplMaKFNaaJJZJ9LZ6FEYJ1FYp1DX5xAXptJZaBAXZo+W5k+W5pKZaBGY55LZ6A8WZg/XZpJqQD4AAAAVklEQVR4Xk3CBQoDABADwU3d3d37/x8WAqEHI/jboZV1Y2sqDuiNZtaLJ7qihe1RoxjEC/XjhMaxtCPqxBmNolVc0Nxu6IuGMbUHuqNmsbF2rO2DJhg/RwEE6AR0V3kAAAAASUVORK5CYII=); |
|
52 |
+ background-repeat: repeat-x; |
|
53 |
+ background-size: 1px 100%; |
|
54 |
+ background-position: 0 0; |
|
55 |
+ background-color: #3a5795; |
|
56 |
+ |
|
57 |
+ letter-spacing: 2px; |
|
58 |
+ font-variant: small-caps; |
|
59 |
+ font-weight: bold; |
|
60 |
+} |
|
61 |
+ |
|
62 |
+.login-button + * { |
|
63 |
+ margin-top: 1em; |
|
64 |
+} |
|
65 |
+ |
... | ... |
@@ -1,6 +1,6 @@ |
1 | 1 |
/*@import(url(/static/css/baseline.css));*/ |
2 | 2 |
/*@import url("/css/formalize.css");*/ |
3 |
-@import url(https://fonts.googleapis.com/css?family=Alegreya|MedievalSharp&subset=latin,latin-ext); |
|
3 |
+@import url(https://fonts.googleapis.com/css?family=Alegreya+Sans|MedievalSharp&subset=latin,latin-ext); |
|
4 | 4 |
|
5 | 5 |
* { |
6 | 6 |
box-sizing: border-box; |
... | ... |
@@ -10,8 +10,7 @@ |
10 | 10 |
-moz-font-feature-settings: 'kern' 1; |
11 | 11 |
-o-font-feature-settings: 'kern' 1; |
12 | 12 |
text-rendering: geometricPrecision; |
13 |
- transition: color 0.2s ease-in-out, |
|
14 |
- background-color 0.2s ease-in-out; |
|
13 |
+ transition: color 0.2s ease-in-out, background-color 0.2s ease-in-out; |
|
15 | 14 |
} |
16 | 15 |
|
17 | 16 |
body { |
... | ... |
@@ -22,6 +21,7 @@ body { |
22 | 21 |
} |
23 | 22 |
|
24 | 23 |
h1 { |
24 |
+ letter-spacing: 6px; |
|
25 | 25 |
padding: 0.5em; |
26 | 26 |
margin: 0px; |
27 | 27 |
margin-top: 0.5em; |
... | ... |
@@ -151,6 +151,8 @@ ul#submissions li + li { |
151 | 151 |
|
152 | 152 |
ul#submissions a { |
153 | 153 |
display: block; |
154 |
+ padding: 0.75em; |
|
155 |
+ border-radius: 0.25em; |
|
154 | 156 |
} |
155 | 157 |
|
156 | 158 |
ul#submissions li { |
... | ... |
@@ -174,6 +176,63 @@ ul#submissions li.submission:first-child { |
174 | 176 |
margin-top: 3.33em; |
175 | 177 |
} |
176 | 178 |
|
179 |
+li.submission a.rejected { |
|
180 |
+ background-color: hsl(0,25%,75%); |
|
181 |
+} |
|
182 |
+ |
|
183 |
+li.submission a.approved { |
|
184 |
+ background-color: hsl(120,25%,75%); |
|
185 |
+} |
|
186 |
+ |
|
177 | 187 |
a { color: inherit; text-decoration: none;} |
178 | 188 |
a:visited { color: hsl(0, 50%, 25%); } |
179 | 189 |
a:active, a:hover { text-decoration: underline; } |
190 |
+ |
|
191 |
+.moderation { |
|
192 |
+ position: absolute; |
|
193 |
+ bottom: 50%; |
|
194 |
+ transform: translateY(50%); |
|
195 |
+ left: -5em; |
|
196 |
+ width: 4em; |
|
197 |
+ background: #ddd; |
|
198 |
+ padding: 0.2em; |
|
199 |
+ text-align: center; |
|
200 |
+ border-radius: 0.2em; |
|
201 |
+} |
|
202 |
+ |
|
203 |
+.moderation button { |
|
204 |
+ display: inline-block; |
|
205 |
+ width: 1.5em; |
|
206 |
+ height: 1.5em; |
|
207 |
+ line-height: 1.5em; |
|
208 |
+ |
|
209 |
+} |
|
210 |
+ |
|
211 |
+.moderation button + button { |
|
212 |
+ margin-left: 0.2em; |
|
213 |
+} |
|
214 |
+ |
|
215 |
+.userinfo { |
|
216 |
+ background-color: #eee; |
|
217 |
+ padding: 0.2em; |
|
218 |
+ border-radius: 0.2em; |
|
219 |
+ border: 3px double #888; |
|
220 |
+ position: absolute; |
|
221 |
+ font-size: 0.9em; |
|
222 |
+ right: 1em; |
|
223 |
+ top: 1em; |
|
224 |
+ text-align: center; |
|
225 |
+} |
|
226 |
+ |
|
227 |
+.userinfo .user { |
|
228 |
+ font-size: 1.5em; |
|
229 |
+} |
|
230 |
+.userinfo .email { |
|
231 |
+ color: #888; |
|
232 |
+} |
|
233 |
+body > div.userinfo > a#logout { |
|
234 |
+ width: 100%; |
|
235 |
+ display: block; |
|
236 |
+ margin-top: 0.5em; |
|
237 |
+ font-size: 1.25em; |
|
238 |
+} |
... | ... |
@@ -16,14 +16,20 @@ |
16 | 16 |
</form> |
17 | 17 |
|
18 | 18 |
<ul id="submissions"> |
19 |
- <li class="in-progress" v-if="newLink.headline !== undefined || newLink.url !== undefined"> |
|
19 |
+ <li class="in-progress " v-if="newLink.headline !== undefined || newLink.url !== undefined"> |
|
20 | 20 |
<a href="#"> |
21 | 21 |
<h2>{{ newLink.headline }}</h2> |
22 | 22 |
<p>{{ newLink.url }}</p> |
23 | 23 |
</a> |
24 | 24 |
</li> |
25 | 25 |
<li class="submission" v-for="link in links"> |
26 |
- <a href="{{ link.url }}"> |
|
26 |
+ <div class="moderation"> |
|
27 |
+ <form action="/curo" method="POST" v-on:submit="moderate"> |
|
28 |
+ <button name="curo" value="+" type="submit">+</button> |
|
29 |
+ <button name="curo" value="-" type="submit">-</button> |
|
30 |
+ </form> |
|
31 |
+ </div > |
|
32 |
+ <a href="{{ link.url }}" class="{{ link.approved || '' }}"> |
|
27 | 33 |
<h2>{{ link.headline }}</h2> |
28 | 34 |
<p>{{ link.url }}</p> |
29 | 35 |
</a> |
... | ... |
@@ -52,11 +58,13 @@ new Vue({ |
52 | 58 |
}, |
53 | 59 |
links: [ |
54 | 60 |
{ headline: "Against the axiom of nature’s infinite precision", |
55 |
- url: "https://thomism.wordpress.com/2016/01/19/against-the-axiom-of-natures-infinite-precision/"}, |
|
61 |
+ url: "https://thomism.wordpress.com/2016/01/19/against-the-axiom-of-natures-infinite-precision/", |
|
62 |
+ approved: "approved"}, |
|
56 | 63 |
{ headline: "The opposition between \"strongly held intuitions\" and science", |
57 | 64 |
url: "https://thomism.wordpress.com/2010/07/10/the-battle-between-strongly-held-intuitions-and-science/"}, |
58 | 65 |
{ headline: "Against the axiom of nature’s infinite precision", |
59 |
- url: "https://thomism.wordpress.com/2016/01/19/against-the-axiom-of-natures-infinite-precision/"}, |
|
66 |
+ url: "https://thomism.wordpress.com/2016/01/19/against-the-axiom-of-natures-infinite-precision/", |
|
67 |
+ approved: "rejected"}, |
|
60 | 68 |
{ headline: "The opposition between \"strongly held intuitions\" and science", |
61 | 69 |
url: "https://thomism.wordpress.com/2010/07/10/the-battle-between-strongly-held-intuitions-and-science/"}, |
62 | 70 |
{ headline: "Against the axiom of nature’s infinite precision", |
63 | 71 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,77 @@ |
1 |
+<!DOCTYPE html> |
|
2 |
+<html lang="en"> |
|
3 |
+ <head> |
|
4 |
+ <meta charset="UTF-8"> |
|
5 |
+ <title>In Angulis</title> |
|
6 |
+ <!--<link rel="stylesheet" href="/css/baseline.css">--> |
|
7 |
+ <link rel="stylesheet" href="/static/css/main.css"> |
|
8 |
+ </head> |
|
9 |
+ <body> |
|
10 |
+ <h1>In Angulis</h1> |
|
11 |
+ {{#user}} |
|
12 |
+ <div class="userinfo"> |
|
13 |
+ <div class="user">{{name}}</div> |
|
14 |
+ <div class="email">{{email}}</div> |
|
15 |
+ <div class="mod">{{moderator}}</div> |
|
16 |
+ <a id="logout" href="/logout">[Logout]</a> |
|
17 |
+ </div> |
|
18 |
+ {{/user}} |
|
19 |
+ <main id="app"> |
|
20 |
+ <form action="/murmuro" id="submission" name="submission" method="POST" v-on:submit="submit"> |
|
21 |
+ <input type="text" name="headline" placeholder="Headline" v-model="newLink.headline"> |
|
22 |
+ <input type="text" name="url" placeholder="URL" v-model="newLink.url"> |
|
23 |
+ <input type="submit" value="Go" title='Murmuro'> |
|
24 |
+ </form> |
|
25 |
+ |
|
26 |
+ <ul id="submissions"> |
|
27 |
+ {{#links}} |
|
28 |
+ <li class="submission" v-for="link in links"> |
|
29 |
+ {{#user}} |
|
30 |
+ {{#moderator}} |
|
31 |
+ <div class="moderation"> |
|
32 |
+ <form action="/curo" method="POST"> |
|
33 |
+ <button name="approved" value="+" type="submit">+</button> |
|
34 |
+ <button name="approved" value="-" type="submit">-</button> |
|
35 |
+ <input type="hidden" name="headline" value="{{headline}}" /> |
|
36 |
+ <input type="hidden" name="url" value="{{url}}" /> |
|
37 |
+ </form> |
|
38 |
+ </div> |
|
39 |
+ {{/moderator}} |
|
40 |
+ {{/user}} |
|
41 |
+ <a href="{{ url }}" class="{{ approved }}"> |
|
42 |
+ <h2>{{ headline }}</h2> |
|
43 |
+ <p>{{ url }}</p> |
|
44 |
+ </a> |
|
45 |
+ </li> |
|
46 |
+ {{/links}} |
|
47 |
+ </ul> |
|
48 |
+ </main> |
|
49 |
+ |
|
50 |
+ <script src="/static/js/jquery.js"></script> |
|
51 |
+ <script src="/static/js/jquery.formalize.js"></script> |
|
52 |
+<!-- |
|
53 |
+ - <script src="/static/js/vue.js"></script> |
|
54 |
+ - <script type="text/javascript"> |
|
55 |
+ -new Vue({ |
|
56 |
+ - el: '#app', |
|
57 |
+ - |
|
58 |
+ - methods: { |
|
59 |
+ - submit: function(e) { |
|
60 |
+ - e.preventDefault(); |
|
61 |
+ - this.links.unshift(0,Object.create(this.newLink)); |
|
62 |
+ - this.newLink = {}; |
|
63 |
+ - return false; |
|
64 |
+ - } |
|
65 |
+ - }, |
|
66 |
+ - |
|
67 |
+ - data: { |
|
68 |
+ - newLink: { |
|
69 |
+ - }, |
|
70 |
+ - links: [ {{ jsondata }} ] |
|
71 |
+ - } |
|
72 |
+ -}); |
|
73 |
+ - </script> |
|
74 |
+ --> |
|
75 |
+ </body> |
|
76 |
+</html> |
|
77 |
+ |