1 | ;;+----------------------------------------------------------------------------- |
---|
2 | ;;+ Isidorus |
---|
3 | ;;+ (c) 2008-2010 Marc Kuester, Christoph Ludwig, Lukas Georgieff |
---|
4 | ;;+ |
---|
5 | ;;+ Isidorus is freely distributable under the LLGPL license. |
---|
6 | ;;+ You can find a detailed description in trunk/docs/LLGPL-LICENSE.txt and |
---|
7 | ;;+ trunk/docs/LGPL-LICENSE.txt. |
---|
8 | ;;+----------------------------------------------------------------------------- |
---|
9 | |
---|
10 | (in-package :rest-interface) |
---|
11 | |
---|
12 | (defparameter *gdl-get-fragment* "/gdl/fragment/(.+)$") |
---|
13 | (defparameter *gdl-get-schema* "/gdl/schema/?$") |
---|
14 | (defparameter *gdl-commit-fragment* "/gdl/commit/?") |
---|
15 | (defparameter *gdl-delete-fragment* "/gdl/delete/?") |
---|
16 | (defparameter *gdl-host-address-hash-object* "/hash-object") |
---|
17 | (defparameter *gdl-host-address-environment* "/environment") |
---|
18 | (defparameter *gdl-base-path* "anaToMia/hosted_files/") |
---|
19 | (defparameter *gdl-host-file* (concat *gdl-base-path* "GDL_Widgets.html")) |
---|
20 | (defparameter *gdl-tm-id* "http://textgrid.org/serviceregistry/gdl-frontend/gdl-tm") |
---|
21 | (defparameter *gdl-sparql* "/gdl/tm-sparql/?$") |
---|
22 | |
---|
23 | |
---|
24 | (defun set-up-gdl-interface (&key (get-fragment *gdl-get-fragment*) |
---|
25 | (get-schema *gdl-get-schema*) |
---|
26 | (commit-fragment *gdl-commit-fragment*) |
---|
27 | (delete-fragment *gdl-delete-fragment*) |
---|
28 | (gdl-sparql *gdl-sparql*) |
---|
29 | (base-path *gdl-base-path*) |
---|
30 | (host-address-hash-object *gdl-host-address-hash-object*) |
---|
31 | (host-address-environment *gdl-host-address-environment*) |
---|
32 | (host-file *gdl-host-file*)) |
---|
33 | (declare (String get-fragment get-schema commit-fragment |
---|
34 | delete-fragment host-address-hash-object |
---|
35 | host-address-environment host-file)) |
---|
36 | |
---|
37 | ;(init-cache nil) |
---|
38 | ;(format t "~%") |
---|
39 | (init-fragments nil) |
---|
40 | |
---|
41 | ;; registers the http-code 500 for an internal server error to the standard |
---|
42 | ;; return codes. so there won't be attached a hunchentoot default message, |
---|
43 | ;; this is necessary to be able to send error messages in an individual way/syntax |
---|
44 | ;; e.g. a json error-message. |
---|
45 | (push hunchentoot:+http-internal-server-error+ hunchentoot:*approved-return-codes*) |
---|
46 | |
---|
47 | (init-hosted-files :host-address-hash-object host-address-hash-object |
---|
48 | :host-address-environment host-address-environment |
---|
49 | :host-file host-file :base-path base-path) |
---|
50 | |
---|
51 | (push |
---|
52 | (create-regex-dispatcher get-fragment |
---|
53 | (if (> *use-http-authentication* 2) |
---|
54 | (lambda(&optional param) |
---|
55 | (with-http-authentication |
---|
56 | (return-json-fragment-handler param))) |
---|
57 | #'return-json-fragment-handler)) |
---|
58 | hunchentoot:*dispatch-table*) |
---|
59 | |
---|
60 | (push |
---|
61 | (create-regex-dispatcher get-schema |
---|
62 | (if (> *use-http-authentication* 2) |
---|
63 | (lambda() |
---|
64 | (with-http-authentication |
---|
65 | (return-gdl-schema-handler))) |
---|
66 | #'return-gdl-schema-handler)) |
---|
67 | hunchentoot:*dispatch-table*) |
---|
68 | |
---|
69 | (push |
---|
70 | (create-regex-dispatcher commit-fragment |
---|
71 | (if (> *use-http-authentication* 1) |
---|
72 | (lambda() |
---|
73 | (with-http-authentication |
---|
74 | (commit-fragment-handler))) |
---|
75 | #'commit-fragment-handler)) |
---|
76 | hunchentoot:*dispatch-table*) |
---|
77 | |
---|
78 | (push |
---|
79 | (create-regex-dispatcher delete-fragment |
---|
80 | (if (> *use-http-authentication* 1) |
---|
81 | (lambda() |
---|
82 | (with-http-authentication |
---|
83 | (delete-handler))) |
---|
84 | #'delete-handler)) |
---|
85 | hunchentoot:*dispatch-table*) |
---|
86 | |
---|
87 | (push |
---|
88 | (create-regex-dispatcher gdl-sparql |
---|
89 | (if (> *use-http-authentication* 2) |
---|
90 | (lambda(&optional param) |
---|
91 | (with-http-authentication |
---|
92 | (gdl-sparql-handler param))) |
---|
93 | #'gdl-sparql-handler)) |
---|
94 | hunchentoot:*dispatch-table*)) |
---|
95 | |
---|
96 | |
---|
97 | (defun init-hosted-files (&key (host-address-hash-object *gdl-host-address-hash-object*) |
---|
98 | (host-address-environment *gdl-host-address-environment*) |
---|
99 | (host-file *gdl-host-file*) |
---|
100 | (base-path *gdl-base-path*)) |
---|
101 | "Adds handlers for the css, html and js files needed by the frontend." |
---|
102 | (declare (String host-address-hash-object host-address-environment |
---|
103 | host-file base-path)) |
---|
104 | ;; add the actual html file |
---|
105 | (let ((full-host-path |
---|
106 | (concat (namestring |
---|
107 | (asdf:component-pathname constants:*isidorus-system*)) |
---|
108 | host-file)) |
---|
109 | (absolute-base-path |
---|
110 | (concat |
---|
111 | (namestring |
---|
112 | (asdf:component-pathname constants:*isidorus-system*)) |
---|
113 | base-path))) |
---|
114 | (if (> *use-http-authentication* 0) |
---|
115 | (progn |
---|
116 | (define-easy-handler (isidorus-ui :uri host-address-hash-object |
---|
117 | :default-request-type :get) |
---|
118 | () |
---|
119 | (with-http-authentication |
---|
120 | (serve-file full-host-path "text/html"))) |
---|
121 | (define-easy-handler (isidorus-ui :uri host-address-environment |
---|
122 | :default-request-type :get) |
---|
123 | () |
---|
124 | (with-http-authentication |
---|
125 | (serve-file full-host-path "text/html")))) |
---|
126 | (progn |
---|
127 | (push |
---|
128 | (create-static-file-dispatcher-and-handler |
---|
129 | host-address-hash-object full-host-path "text/html") |
---|
130 | hunchentoot:*dispatch-table*) |
---|
131 | (push |
---|
132 | (create-static-file-dispatcher-and-handler |
---|
133 | host-address-environment full-host-path "text/html") |
---|
134 | hunchentoot:*dispatch-table*))) |
---|
135 | ; add all additional files |
---|
136 | (let ((absolute-base-path-len (length absolute-base-path))) |
---|
137 | (com.gigamonkeys.pathnames:walk-directory |
---|
138 | "anaToMia/hosted_files" |
---|
139 | (lambda(item) |
---|
140 | (unless (or (search "/.svn/" (namestring item) :test #'string=) |
---|
141 | (string= full-host-path (namestring item))) |
---|
142 | (let* ((rel-addr (subseq (namestring item) absolute-base-path-len)) |
---|
143 | (content-type (generate-content-type (file-namestring item))) |
---|
144 | (rel-uri (concat "/" rel-addr))) |
---|
145 | (push |
---|
146 | (create-static-file-dispatcher-and-handler |
---|
147 | rel-uri item content-type) |
---|
148 | hunchentoot:*dispatch-table*)))))))) |
---|
149 | |
---|
150 | |
---|
151 | (defun generate-content-type(file-name) |
---|
152 | "Returns a mime-type that corresponds to the passed |
---|
153 | file-ending, note currently onle a fey types are supported!" |
---|
154 | (declare (String file-name)) |
---|
155 | (cond ((string-ends-with file-name "png" :ignore-case t) |
---|
156 | "image/png") |
---|
157 | ((string-ends-with file-name "html" :ignore-case t) |
---|
158 | "text/html") |
---|
159 | ((string-ends-with file-name "js" :ignore-case t) |
---|
160 | "application/json") |
---|
161 | ((string-ends-with file-name "css" :ignore-case t) |
---|
162 | "text/css") |
---|
163 | ((string-ends-with file-name "gif" :ignore-case t) |
---|
164 | "image/gif") |
---|
165 | (t |
---|
166 | "text/plain"))) |
---|
167 | |
---|
168 | |
---|
169 | (defun delete-handler() |
---|
170 | "marks the corresponding construct(s) as deleted" |
---|
171 | (let ((http-method (hunchentoot:request-method*))) |
---|
172 | (if (or (eq http-method :DELETE) |
---|
173 | (eq http-method :POST)) |
---|
174 | (let ((external-format |
---|
175 | (flexi-streams:make-external-format :UTF-8 :eol-style :LF))) |
---|
176 | (let ((json-data |
---|
177 | (hunchentoot:raw-post-data :external-format external-format |
---|
178 | :force-text t))) |
---|
179 | (with-writer-lock |
---|
180 | (let* ((rev (d:get-revision)) |
---|
181 | (result (jtm-delete-interface:mark-as-deleted-from-jtm |
---|
182 | json-data :revision rev))) |
---|
183 | (let ((tops |
---|
184 | (remove-null |
---|
185 | (cond ((or (typep result 'OccurrenceC) |
---|
186 | (typep result 'NameC)) |
---|
187 | (let ((top (parent result :revision (1- rev)))) |
---|
188 | (when top (list top)))) |
---|
189 | ((typep result 'VariantC) |
---|
190 | (let ((name (parent result :revision (1- rev)))) |
---|
191 | (when name |
---|
192 | (let ((top (parent name :revision (1- rev)))) |
---|
193 | (when top (list top)))))) |
---|
194 | ((typep result 'AssociationC) |
---|
195 | (map 'list (lambda(role) |
---|
196 | (player role :revision (1- rev))) |
---|
197 | (roles result :revision (1- rev)))) |
---|
198 | ((typep result 'TopicC) |
---|
199 | (let ((assocs |
---|
200 | (remove-null |
---|
201 | (map 'list (lambda(role) |
---|
202 | (parent role :revision (1- rev))) |
---|
203 | (player-in-roles result :revision (1- rev))))) |
---|
204 | (frags |
---|
205 | (elephant:get-instances-by-value |
---|
206 | 'd:FragmentC 'd:topic result))) |
---|
207 | (map nil #'elephant:drop-instance frags) |
---|
208 | (loop for assoc in assocs |
---|
209 | append (map 'list (lambda(role) |
---|
210 | (player role :revision (1- rev))) |
---|
211 | (roles assoc :revision (1- rev)))))))))) |
---|
212 | (map nil (lambda(top) |
---|
213 | (let ((frags |
---|
214 | (elephant:get-instances-by-value 'd:FragmentC 'd:topic top))) |
---|
215 | (map nil #'elephant:drop-instance frags)) |
---|
216 | (create-latest-fragment-of-topic top)) |
---|
217 | (if (typep result 'd:TopicC) |
---|
218 | (delete result tops) |
---|
219 | tops))) |
---|
220 | (unless result |
---|
221 | (setf (hunchentoot:return-code*) hunchentoot:+http-not-found+) |
---|
222 | (format nil "object not found")))))) |
---|
223 | (setf (hunchentoot:return-code*) hunchentoot:+http-bad-request+)))) |
---|
224 | |
---|
225 | |
---|
226 | (defun commit-fragment-handler () |
---|
227 | "handles commits in the JTM 1.1 format." |
---|
228 | (let ((http-method (hunchentoot:request-method*))) |
---|
229 | (if (or (eq http-method :PUT) |
---|
230 | (eq http-method :POST)) |
---|
231 | (let ((external-format |
---|
232 | (flexi-streams:make-external-format :UTF-8 :eol-style :LF))) |
---|
233 | (let ((json-data |
---|
234 | (hunchentoot:raw-post-data :external-format external-format |
---|
235 | :force-text t))) |
---|
236 | (with-writer-lock |
---|
237 | (jtm-importer:import-construct-from-jtm-string |
---|
238 | json-data :revision (get-revision) :tm-id *gdl-tm-id* |
---|
239 | :create-fragments t)))) |
---|
240 | (setf (hunchentoot:return-code*) hunchentoot:+http-bad-request+)))) |
---|
241 | |
---|
242 | |
---|
243 | (defun return-gdl-schema-handler() |
---|
244 | "Currently the entore topic map is returned. |
---|
245 | To emerge the efficiency it will be necessary |
---|
246 | to structure the data as GDL-Fragments, so each view or schema |
---|
247 | can be served separately." |
---|
248 | (let ((http-method (hunchentoot:request-method*))) |
---|
249 | (if (eq http-method :GET) |
---|
250 | (progn (setf (hunchentoot:content-type*) "application/json") |
---|
251 | (jtm-exporter:export-as-jtm-string :revision 0)) |
---|
252 | (setf (hunchentoot:return-code*) hunchentoot:+http-bad-request+)))) |
---|
253 | |
---|
254 | |
---|
255 | (defun return-json-fragment-handler(&optional psi) |
---|
256 | "returns the json-fragmen belonging to the psi passed by the parameter psi" |
---|
257 | (assert psi) |
---|
258 | (let ((http-method (hunchentoot:request-method*))) |
---|
259 | (if (eq http-method :GET) |
---|
260 | (let ((identifier (hunchentoot:url-decode psi))) |
---|
261 | (setf (hunchentoot:content-type*) "application/json") ;RFC 4627 |
---|
262 | (let ((fragment |
---|
263 | (with-reader-lock |
---|
264 | (get-latest-fragment-of-topic identifier)))) |
---|
265 | (if fragment |
---|
266 | (with-reader-lock |
---|
267 | (jtm-exporter:export-construct-as-jtm-string |
---|
268 | fragment :revision 0)) |
---|
269 | (progn |
---|
270 | (setf (hunchentoot:return-code*) hunchentoot:+http-not-found+) |
---|
271 | (setf (hunchentoot:content-type*) "text") |
---|
272 | (format nil "Topic \"~a\" not found" psi))))) |
---|
273 | (setf (hunchentoot:return-code*) hunchentoot:+http-bad-request+)))) |
---|
274 | |
---|
275 | |
---|
276 | (defun gdl-sparql-handler(&optional param) |
---|
277 | "Returns a JSON object representing a SPARQL response." |
---|
278 | (declare (Ignorable param)) |
---|
279 | (if (eql (hunchentoot:request-method*) :POST) |
---|
280 | (let ((external-format (flexi-streams:make-external-format |
---|
281 | :UTF-8 :eol-style :LF))) |
---|
282 | (let ((sparql-request (hunchentoot:raw-post-data |
---|
283 | :external-format external-format |
---|
284 | :force-text t))) |
---|
285 | (export-construct-as-isidorus-json-string |
---|
286 | (make-instance 'SPARQL-Query :query sparql-request |
---|
287 | :revision 0)))) |
---|
288 | (setf (hunchentoot:return-code*) hunchentoot:+http-bad-request+))) |
---|